OPENCL:可以将模板对象作为内核参数,并使用boost :: Compute



我的内核的功能签名如下:

template< size_t S, typename Field, typename Type1, typename Type2>
void kernel(const Type1 arg1, const Type2 arg2, Field *results) {
// S is known at compile time
// Field might be float or double
// Type1 is an object holding data and also methods
// Type2 is an object holding data and also methods
// The computation start here
}

我知道,可以使用C 功能的一个子集使用AMD实现OPENCL实现的扩展名来编写内核,但所得代码仅限于AMD卡上运行。

2.0之前版本的OpenCL语言的标准规范限制了程序员使用C99编写内核,我相信版本2.1和2.2尚未广泛用于Linux发行版。但是,我在这里发现Boost :: Compute在某种程度上允许在内核规范中使用C 功能的子集。但是,尚不清楚是否可以使用Boos :: Compute在上面的代码段中实现内核签名。在多大程度上可以实施这样的内核?代码示例将不胜感激。

tl; dr:是和否。确实可以在某种程度上写模板的内核,但是这些内核并不像它们的Cuda对应物那么强大。

我知道,可以使用C 功能的子集使用AMD实现的OpenCL实现的扩展名来编写内核,但是所得代码仅限于AMD卡上运行。

不限于仅在AMD卡上运行。仅在AMD的OpenCL实现上汇编。例如,只要它在AMD的实现中编译。

,它应该在英特尔CPU上运行。

我在这里发现Boost :: Compute在某种程度上允许在内核规范中使用C 特征的子集。但是,尚不清楚是否可以使用Boos :: Compute。

boost.com pupute本质上是在Opencl c API上方的精美抽象层,以使其更可口,并且不太乏味,但是它仍然使您可以完全访问基础的C API。这意味着,如果C API可行,那么从理论上讲,它也应该从boost.com上可行。


由于在运行时在单独的通过中编译了OpenCL代码,因此您将无法自动执行模板实例化,就像CUDA在编译时这样做的方式。CUDA编译器可以看到主机和设备代码,并且可以在整个呼叫图上进行正确的模板Intplate Intplate Instatiation,就好像它是单个翻译单元一样。这在opencl中是不可能的。

1。您将必须手动实例化所需的所有可能的模板构造,拧紧它们的名称并派遣到适当的实例化。

2。模板实例化中使用的所有类型也必须在OpenCL代码中定义。

这种限制使OpenCL模板内核并非完全没有用,但与CUDA相比不是很实用。他们的主要目的是避免代码重复。

此设计的另一个结果是,内核模板模板参数列表中不允许使用非类型的模板参数(至少据我所知,但我会 emake at Inly 想在这个!(。这意味着您必须将内核模板的非类型模板参数降低到一个 graborments 之一的非类型模板参数中。换句话说,转换看起来像这样的东西:

template<std::size_t Size, typename Thing>
void kernel(Thing t);

这样的东西:

template<typename Size, typename Thing>
void kernel(Size* s, Thing t);

,然后通过使用与std::integral_constant<std::size_t, 512>相似的东西(或任何其他可以在整数常数上模板(作为第一个参数来区分不同的实例。这里的指针只是避免需要主机端定义大小类型的诀窍(因为我们不在乎它(。


免责声明:我的系统不支持OpenCL,因此我无法测试以下代码。可能需要进行一些调整才能按预期工作。但是,它确实编译了。

auto source = R"_cl_source_(
    // Type that holds a compile-time size.
    template<std::size_t Size>
    struct size_constant {
        static const std::size_t value = Size;
    };
    // Those should probably be defined somewhere else since
    // the host needs to know about them too.
    struct Thing1 {};
    struct Thing2 {};
    // Primary template, this is where you write your general code.
    template<typename Size, typename Field, typename Type1, typename Type2>
    kernel void generic_kernel(Size*, const Type1 arg1, const Type2 arg2, Field *results) {
        // S is known at compile time
        // Field might be float or double
        // Type1 is an object holding data and also methods
        // Type2 is an object holding data and also methods
        // The computation start here
        // for (std::size_t s = 0; s < Size::value; ++s)
        // ...
    }
    // Instantiate the template as many times as needed.
    // As you can see, this can very quickly become explosive in number of combinations.
    template __attribute__((mangled_name(kernel_512_float_thing1_thing2)))
    kernel void generic_kernel(size_constant<512>*, const Thing1, const Thing2, float*);
    template __attribute__((mangled_name(kernel_1024_float_thing1_thing2)))
    kernel void generic_kernel(size_constant<1024>*, const Thing1, const Thing2, float*);
    template __attribute__((mangled_name(kernel_1024_double_thing1_thing2)))
    kernel void generic_kernel(size_constant<1024>*, const Thing1, const Thing2, double*);
)_cl_source_";
namespace compute = boost::compute;
auto device = compute::system::default_device();
auto context = compute::context { device };
auto queue = compute::command_queue { context, device };
// Build the program.
auto program = compute::program::build_with_source(source, context, "-x clc++");
// Retrieve the kernel entry points.
auto kernel_512_float_thing1_thing2 = program.create_kernel("kernel_512_float_thing1_thing2");
auto kernel_1024_float_thing1_thing2 = program.create_kernel("kernel_1024_float_thing1_thing2");
auto kernel_1024_double_thing1_thing2 = program.create_kernel("kernel_1024_double_thing1_thing2");
// Now you can call these kernels like any other kernel.
// Remember: the first argument is just a dummy.
kernel_512_float_thing1_thing2.set_arg(0, sizeof(std::nullptr_t), nullptr);
// TODO: Set other arguments (not done in this example)
// Finally submit the kernel to the command queue.
auto global_work_size = 512;
auto local_work_size = 64;
queue.enqueue_1d_range_kernel(kernel_512_float_thing1_thing2, 0, global_work_size, local_work_size);

祝您好运,随时用您的更改编辑这篇文章,以便其他人可以从中受益!

最新更新