我试图在CUDA的帮助下减少65536个元素数组(计算其中元素的总和)。内核看起来如下(请忽略* dev_distanceFloats和索引参数)
__global__ void kernel_calcSum(float *d, float *dev_distanceFloats, int index) {
int tid = threadIdx.x;
float mySum = 0;
for (int e = 0; e < 256; e++) {
mySum += d[tid + e];
}
}
ant它作为一个具有256个线程的块启动:
kernel_calcSum <<<1, 256 >>>(dev_spFloats1, dev_distanceFloats, index);
到目前为止,非常好,每个256个线程从全局内存中获取256个元素,并在局部变量mySum中计算它的总和。内核执行时间约为45毫秒。 下一步是在块中的256个线程之间引入共享内存(以计算mySum的总和),因此内核变为如下:
__global__ void kernel_calcSum(float *d, float *dev_distanceFloats, int index) {
__shared__ float sdata[256];
int tid = threadIdx.x;
float mySum = 0;
for (int e = 0; e < 256; e++) {
mySum += d[tid + e];
}
sdata[tid] = mySum;
}
我刚刚添加了对共享内存的写入,但执行时间从45毫秒增加到258毫秒(我在NVidia Visual Profiler 5.0.0的帮助下对此进行了检查)。 我发现在写入sdata变量时,每个线程都有8个存储体冲突(我在GTX670上有32个存储区的能力3.0)。作为一个实验 - 我试图在启动内核时将线程减少到32 - 但时间仍然是258毫秒。
问题1:为什么写共享内存需要花费很长时间? 问题2:是否有任何工具,详细显示了“执行计划”(内存访问时间,冲突等)?
感谢您的建议。
更新 玩内核 - 我为每个线程设置了一个常量的sdata:
__global__ void kernel_calcSum(float *d, float *dev_distanceFloats, int index) {
__shared__ float sdata[256];
int tid = threadIdx.x;
float mySum = 0;
for (int e = 0; e < 256; e++) {
mySum += d[tid + e];
}
sdata[tid] = 111;
}
时间又恢复到48毫秒。 所以,改变 sdata [tid] = mySum; 至 sdata [tid] = 111; 做了这个。
这个编译器是否优化(可能只是删除了这一行?)或者由于某种原因从本地内存(寄存器?)复制到共享需要很长时间?
答案 0 :(得分:0)
共享内存不是正确的。你需要的是warp原子操作,在warp中总结,然后在warp之间传递中间结果。这里有一些示例代码,演示了使用CUDA进行此次运送。
总结元素是大量并行化无济于事的任务之一,GPU实际上可以胜过CPU。
答案 1 :(得分:0)
两个内核都没有做任何事情,因为它们不会将结果写出到内核完成后仍然可以访问的内存。
在第一种情况下,编译器足够聪明地注意到这一点并优化掉整个计算。 在涉及共享内存的第二种情况下,编译器不会注意到这一点,因为通过共享内存的信息流将更难以跟踪。因此,它将计算结果。
传入一个指向全局内存的指针(就像你已经做的那样)并通过这个指针写出结果。