我在OpenCL中看到了一个smallpt实现,作者在内核中使用了自己的向量类型和prepocessor宏函数。
typedef struct { float x, y, z; } Vec;
#define vinit(v, a, b, c) { (v).x = a; (v).y = b; (v).z = c; }
#define vsmul(v, a, b) { float k = (a); vinit(v, k * (b).x, k * (b).y, k * (b).z) }
#define vdot(a, b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
#define vnorm(v) { float l = 1.f / sqrt(vdot(v, v)); vsmul(v, l, v); }
and much more...
我测试了代码并用标准的内置float3类型替换了用户定义的向量类型和操作。令我感到惊讶的是,作者的原始实现比内置float3的变体快10帧。之后我用英特尔OpenCL应用程序内核生成器测试了一下情况,似乎可以确认内置函数速度较慢。
任何想法为什么?供应商推荐内置的矢量类型:/
答案 0 :(得分:3)
用户定义的向量是一个真正的3元素向量,使用3个浮点数的大小。
但是,OpenCL float3
向量实际上使用的是float4
向量,如cl_platform.h
中所示:
/* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */
typedef cl_float4 cl_float3;
最初使用向量是OpenCL的推荐编程技术。由于与普通的非矢量代码相比,开发了完整的SIMD架构。
但随着OpenCL编译器的发展,现在编译器足够聪明,可以在内部向量化用户代码。 有时候(我已经在其他内核中看到了它,即使是我自己的),最好用普通元素编写代码并让编译器重新对其进行矢量化,而不是使用默认的矢量类型。因此,我建议除非矢量简化编程任务并且更容易阅读(例如2D处理等),否则现在不要使用OpenCL矢量。
用户定义vector3
可能正在使用更少的寄存器,而且更少的溢出。或者可能是另一种向量排列对于算法更好,并且编译器可以在用户定义的向量情形中自由地重构代码。
应该使用已编译的PTX代码对正在发生的事情进行深入分析。
答案 1 :(得分:1)
添加一点。对于NVIDIA GPU(我不知道AMD),有32位(LD.32),64位(LD.64)和128位(LD.128)的加载指令,但没有96位加载。从DRAM加载一个真正的float3实现为两个单独的指令 - 一个LD.32和一个LD.64。如果你在一个warp中的连续线程中加载连续的float3,就像你应该为float2,double或float4加载一样,你实际上是在两个单独的指令中以96位步幅访问数据,从而产生重放。商店说明也是如此。我猜这是选择将它们定义为float4的原因。