如何在OpenCL内核中使用C ++模板?

时间:2010-12-16 20:57:12

标签: c++ templates compiler-construction metaprogramming opencl

我是OpenCL的新手。

我有一个使用模板的算法。它在OpenMP并行化方面运行良好,但现在数据量已经增长,处理它的唯一方法是重写它以使用OpenCL。 我可以轻松地使用MPI为集群构建它,但类似特斯拉的GPU比集群便宜得多:)

有没有办法在OpenCL内核中使用C ++模板?

是否可以通过C ++编译器或某种工具以某种方式扩展模板,之后使用如此改变的内核函数?

EDIT。解决方法的想法是以某种方式从模板中的C ++代码生成与C99兼容的代码。

我发现了关于Comeau的一些信息:

Comeau C ++ 4.3.3是一个完整而真实的编译器,可执行完整的语法检查,完整的语义检查,完整的错误检查以及所有其他编译器职责。输入C ++代码被转换为内部编译器树和符号表,看起来不像C ++或C.同样,它生成一个内部专有的中间形式。但是,Comeau C ++ 4.3.3不是使用专有的后端代码生成器,而是生成C代码作为输出。除了C ++的技术优势之外,Comeau C ++ 4.3.3等产品的C生成方面也被吹捧为C ++成功的原因,因为C编译器的普遍可用性使其能够被带到大量平台。

仅使用C编译器,仅用于获取本机代码。这意味着Comeau C ++专为与各个平台上的特定C编译器一起使用而定制。请注意,要求剪裁必须由Comeau完成。否则,生成的C代码没有意义,因为它绑定到特定平台(其中平台至少包括CPU,OS和C编译器),此外,生成的C代码不是独立的。因此,它不能单独使用(请注意,这在使用Comeau C ++时是技术和法律要求),这就是为什么通常没有选项来查看生成的C代码:它几乎总是无用的和编译过程包括其代,应被视为翻译的内部阶段。

6 个答案:

答案 0 :(得分:14)

有一种旧的方法可以用纯C语言模拟模板。 它基于多次包含单个文件(不包括保护)。 由于OpenCL具有功能齐全的预处理器并允许包含文件,因此可以使用此技巧。

这是一个很好的解释: http://arnold.uthar.net/index.php?n=Work.TemplatesC

它仍然比C ++模板更麻烦:代码必须分成几个部分,你必须显式实例化每个模板实例。此外,您似乎无法做一些有用的事情,例如将factorial实现为递归模板。

代码示例

让我们将这个想法应用于OpenCL。假设我们想通过Newton-Raphson迭代计算逆平方根(通常不是一个好主意)。但是,浮点类型和迭代次数可能会有所不同。

首先,我们需要一个帮助头(“templates.h”):

#ifndef TEMPLATES_H_
#define TEMPLATES_H_

#define CAT(X,Y,Z) X##_##Y##_##Z   //concatenate words
#define TEMPLATE(X,Y,Z) CAT(X,Y,Z)

#endif

然后,我们在“NewtonRaphsonRsqrt.cl”中编写模板函数:

#include "templates.h"

real TEMPLATE(NewtonRaphsonRsqrt, real, iters) (real x, real a) {
    int i;
    for (i = 0; i<iters; i++) {
        x *= ((real)1.5 - (0.5*a)*x*x);
    }
    return x;
}

在主.cl文件中,按如下方式实例化此模板:

#define real float
#define iters 2
#include "NewtonRaphsonRsqrt.cl"  //defining NewtonRaphsonRsqrt_float_2

#define real double
#define iters 3
#include "NewtonRaphsonRsqrt.cl"  //defining NewtonRaphsonRsqrt_double_3

#define real double
#define iters 4
#include "NewtonRaphsonRsqrt.cl"  //defining NewtonRaphsonRsqrt_double_4

然后可以像这样使用它:

double prec = TEMPLATE(NewtonRaphsonRsqrt, double, 4) (1.5, 0.5);
float approx = TEMPLATE(NewtonRaphsonRsqrt, float, 2) (1.5, 0.5);

答案 1 :(得分:5)

我已经为OpenCL C源转换工具编写了一个实验性的C ++。该工具将C ++源代码(甚至一些STL)编译为LLVM字节代码,并使用修改后的LLVM“C”后端版本将字节代码反汇编为OpenCL“C”。

请参阅http://dimitri-christodoulou.blogspot.com/2013/12/writing-opencl-kernels-in-c.html

例如,使用C ++ 11的std :: enable_if的代码可以转换为OpenCL'C',然后在GPU上执行:

#include <type_traits>

template<class T>
T foo(T t, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
    return 1;
}

template<class T>
T foo(T t, typename std::enable_if<std::is_floating_point<T>::value >::type* = 0)
{
    return 0;
}

extern "C" void _Kernel_enable_if_int_argument(int* arg0, int* out)
{
    out[0] = foo(arg0[0]);
}

答案 2 :(得分:3)

您可以查看使用表达式模板生成OpenCL内核的VexCL。您可以了解如何使OpenCL与模板很好地协作。

正在积极处理的另一个库是Boost.Compute,它是OpenCL之上的一个层,允许使用通用C ++代码。

一般的想法是将内核或多或少地创建为C字符串,并将其传递给OpenCL运行时以进行编译和执行。

答案 3 :(得分:2)

如果你真的决定要完成它,你可以重新定位你选择的C ++编译器来生成NVidia PTX(并且Clang很可能很快就能做到)。但是这样你就可以将你的代码绑定到NVidia硬件了。

另一种方法是基于当前的CBE实现LLVM的自定义后端,它将生成纯OpenCL代码而不是C。

答案 4 :(得分:0)

请注意,新的SYCL Khronos标准在OpenCL中对C ++模板提供原生支持。

答案 5 :(得分:-2)

PyOpenCL现在使用Mako作为它的模板引擎。 http://www.makotemplates.org/