use*_*936 6 c++ 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
}
Run Code Online (Sandbox Code Playgroud)
我知道可以使用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 的实现上编译的,它就应该在 Intel CPU 上运行得很好。
我在这里发现 Boost::compute 在某种程度上允许在内核规范中使用 C++ 功能的子集。然而,尚不清楚是否可以使用 Boos::compute 实现上述代码片段中的内核签名。
Boost.Compute 本质上是 OpenCL C API 之上的一个奇特的抽象层,使其更易于使用且使用起来更简单,但它仍然允许您完全访问底层 C API。这意味着,如果某件事在 C API 中可行,那么理论上它在 Boost.Compute 中也应该可行。
由于 OpenCL 代码是在运行时编译的,在单独的过程中,您将无法像 CUDA 在编译时那样自动进行模板实例化。CUDA 编译器可以看到主机和设备代码,并且可以在整个调用图中进行正确的模板实例化,就好像它是单个翻译单元一样。从设计上来说,这在 OpenCL 中是不可能的。
1. 您必须手动实例化您需要的所有可能的模板实例,修改它们的名称,然后分派到正确的实例化。
2. 模板实例化中使用的所有类型也必须在 OpenCL 代码中定义。
这一限制使得 OpenCL 模板化内核并非完全无用,但与 CUDA 内核相比也不太实用。它们的主要目的是避免代码重复。
这种设计的另一个结果是,内核模板模板参数列表中不允许使用非类型模板参数(至少据我所知,但我真的很想在这个问题上犯错!)。这意味着您必须将内核模板的非类型模板参数降低为参数之一的非类型模板参数。换句话说,转换看起来像这样的东西:
template<std::size_t Size, typename Thing>
void kernel(Thing t);
Run Code Online (Sandbox Code Playgroud)
变成这样的事情:
template<typename Size, typename Thing>
void kernel(Size* s, Thing t);
Run Code Online (Sandbox Code Playgroud)
然后通过使用本质上类似的东西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);
Run Code Online (Sandbox Code Playgroud)
祝你好运,并随时编辑这篇文章并进行更改,以便其他人从中受益!