我有一个简单的扫描内核,它可以计算循环中几个块的扫描。我注意到当get_local_id()存储在局部变量中而不是在循环内调用它时,性能会有所上升。所以总结一下代码,这个:
__kernel void LocalScan_v0(__global const int *p_array, int n_array_size, __global int *p_scan)
{
const int n_group_offset = get_group_id(0) * SCAN_BLOCK_SIZE;
p_array += n_group_offset;
p_scan += n_group_offset;
// calculate group offset
const int li = get_local_id(0); // *** local id cached ***
const int gn = get_num_groups(0);
__local int p_workspace[SCAN_BLOCK_SIZE];
for(int i = n_group_offset; i < n_array_size; i += SCAN_BLOCK_SIZE * gn) {
LocalScan_SingleBlock(p_array, p_scan, p_workspace, li);
p_array += SCAN_BLOCK_SIZE * gn;
p_scan += SCAN_BLOCK_SIZE * gn;
}
// process all the blocks in the array (each block size SCAN_BLOCK_SIZE)
}
GTX-780的吞吐量为74 GB / s,而这个:
__kernel void LocalScan_v0(__global const int *p_array, int n_array_size, __global int *p_scan)
{
const int n_group_offset = get_group_id(0) * SCAN_BLOCK_SIZE;
p_array += n_group_offset;
p_scan += n_group_offset;
// calculate group offset
const int gn = get_num_groups(0);
__local int p_workspace[SCAN_BLOCK_SIZE];
for(int i = n_group_offset; i < n_array_size; i += SCAN_BLOCK_SIZE * gn) {
LocalScan_SingleBlock(p_array, p_scan, p_workspace, get_local_id(0));
// *** local id polled inside the loop ***
p_array += SCAN_BLOCK_SIZE * gn;
p_scan += SCAN_BLOCK_SIZE * gn;
}
// process all the blocks in the array (each block size SCAN_BLOCK_SIZE)
}
在同一硬件上只有70 GB / s。唯一的区别是对get_local_id()的调用是在循环内部还是外部。 LocalScan_SingleBlock()中的代码在this GPU Gems article中有很多描述。
现在这带来了一些问题。我总是想象线程id存储在某个寄存器中,对它的访问速度与任何线程局部变量一样快。似乎并非如此。我一直习惯于在一个变量中缓存本地id,而不愿意在一个循环中不调用函数的旧“C”程序员,如果他希望它每次返回相同的值,但我没有'认真思考它会有所作为。
关于为什么会出现这种情况的任何想法?我没有检查编译的二进制代码。有没有人有相同的经历?与CUDA中的threadIdx.x
相同吗? ATI平台怎么样?这种行为是在某处描述的吗?我很快通过CUDA最佳实践进行了扫描,但没有找到任何结果。
答案 0 :(得分:5)
这只是猜测,但根据Khronos页面
http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/get_local_id.html
get_local_id()未定义为返回常量值(仅为size_t)。这可能意味着,就编译器而言,与常量local_id相比,可能不允许执行某些优化,因为函数值的返回可能会在编译器的眼中发生变化(即使它不是每个线程)