OpenCL:是否可以使用模板化对象作为 Boost::compute 的内核参数?
OpenCL: Is possible to use templated objects as kernel arguments with 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++ 特性的一个子集来使用 extension 将内核写入 AMD 的 OpenCL 实现,但生成的代码仅限于 运行仅限 AMD 卡。
2.0 之前版本的 OpenCL 语言标准规范限制程序员使用 C99 编写内核,我相信 2.1 和 2.2 版本尚未广泛用于 Linux 发行版。但是,我发现 here Boost::compute 在某种程度上允许在内核规范中使用 c++ 功能的子集。但是,尚不清楚是否可以使用 Boos::compute 实现上述代码片段中的内核签名。在多大程度上可以实现这样的内核?代码示例将不胜感激。
TL;DR:是也不是。在某种程度上确实可以编写模板化内核,但它们的功能远不如 CUDA 内核。
I know that is possible to use a subset of the features of c++ to write the kernel using an extension to the implementations of OpenCL from AMD but the resulting code is restricted to run on AMD cards only.
它不仅限于 运行 在 AMD 卡上。它被限制只能在 AMD 的 OpenCL 实现上编译。例如,它应该 运行 在 Intel CPU 上就好了,只要它是在 AMD 的实现上编译的。
I found here that Boost::compute allows to some extent to use a subset of c++ features in the specification of the kernels. However is not clear if it is possible to implement a kernel signature as in the code snippet above using Boos::compute.
Boost.Compute 本质上是 OpenCL C API 之上的奇特抽象层,使其更易于使用且使用起来不那么乏味,但它仍然使您可以完全访问底层 C API。这意味着如果某些东西在 C API 中是可行的,理论上它在 Boost.Compute.
中也应该是可行的
由于 OpenCL 代码是在 运行时间 编译的,因此您将无法像 CUDA 在编译时间。 CUDA 编译器同时查看主机和设备代码,并且可以在整个调用图中执行适当的模板实例化,就好像它是一个翻译单元一样。按照设计,这在 OpenCL 中是不可能的。
1.您将必须手动实例化您需要的所有可能的模板实例化,破坏它们的名称,然后分派到正确的实例化。
2。模板实例化中使用的所有类型也必须在 OpenCL 代码中定义。
这个限制使得 OpenCL 模板化内核并非完全无用,但与 CUDA 内核相比也不是很实用。它们的主要目的是避免代码重复。
这种设计的另一个后果是 non-type 模板参数在内核模板模板参数列表中是不允许的(至少据我所知,但我 真的 想在这个问题上犯错!)。这意味着您必须将内核模板的 non-type 模板参数降低为 参数 之一的 non-type 模板参数。换句话说,转换看起来像这样的东西:
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>
的东西(或任何其他可以在整数常量上模板化的类型)作为第一个参数来区分不同的实例化。这里的指针只是一个技巧,可以避免需要 host-side 大小类型的定义(因为我们不关心它)。
Disclaimer: my system doesn't support OpenCL, so I could not test the below code. It probably requires some tweaking to work as expected. It does compile, however.
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);
祝您好运,随时编辑此 post 您的更改,以便其他人可以从中受益!
我的内核函数签名如下:
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++ 特性的一个子集来使用 extension 将内核写入 AMD 的 OpenCL 实现,但生成的代码仅限于 运行仅限 AMD 卡。
2.0 之前版本的 OpenCL 语言标准规范限制程序员使用 C99 编写内核,我相信 2.1 和 2.2 版本尚未广泛用于 Linux 发行版。但是,我发现 here Boost::compute 在某种程度上允许在内核规范中使用 c++ 功能的子集。但是,尚不清楚是否可以使用 Boos::compute 实现上述代码片段中的内核签名。在多大程度上可以实现这样的内核?代码示例将不胜感激。
TL;DR:是也不是。在某种程度上确实可以编写模板化内核,但它们的功能远不如 CUDA 内核。
I know that is possible to use a subset of the features of c++ to write the kernel using an extension to the implementations of OpenCL from AMD but the resulting code is restricted to run on AMD cards only.
它不仅限于 运行 在 AMD 卡上。它被限制只能在 AMD 的 OpenCL 实现上编译。例如,它应该 运行 在 Intel CPU 上就好了,只要它是在 AMD 的实现上编译的。
I found here that Boost::compute allows to some extent to use a subset of c++ features in the specification of the kernels. However is not clear if it is possible to implement a kernel signature as in the code snippet above using Boos::compute.
Boost.Compute 本质上是 OpenCL C API 之上的奇特抽象层,使其更易于使用且使用起来不那么乏味,但它仍然使您可以完全访问底层 C API。这意味着如果某些东西在 C API 中是可行的,理论上它在 Boost.Compute.
中也应该是可行的由于 OpenCL 代码是在 运行时间 编译的,因此您将无法像 CUDA 在编译时间。 CUDA 编译器同时查看主机和设备代码,并且可以在整个调用图中执行适当的模板实例化,就好像它是一个翻译单元一样。按照设计,这在 OpenCL 中是不可能的。
1.您将必须手动实例化您需要的所有可能的模板实例化,破坏它们的名称,然后分派到正确的实例化。
2。模板实例化中使用的所有类型也必须在 OpenCL 代码中定义。
这个限制使得 OpenCL 模板化内核并非完全无用,但与 CUDA 内核相比也不是很实用。它们的主要目的是避免代码重复。
这种设计的另一个后果是 non-type 模板参数在内核模板模板参数列表中是不允许的(至少据我所知,但我 真的 想在这个问题上犯错!)。这意味着您必须将内核模板的 non-type 模板参数降低为 参数 之一的 non-type 模板参数。换句话说,转换看起来像这样的东西:
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>
的东西(或任何其他可以在整数常量上模板化的类型)作为第一个参数来区分不同的实例化。这里的指针只是一个技巧,可以避免需要 host-side 大小类型的定义(因为我们不关心它)。
Disclaimer: my system doesn't support OpenCL, so I could not test the below code. It probably requires some tweaking to work as expected. It does compile, however.
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);
祝您好运,随时编辑此 post 您的更改,以便其他人可以从中受益!