[英]A CUDA kernel to optimize
嗨,我最近有一个CUDA内核需要优化。 这是原始的CUDA内核:
__glboal__ void kernel_base( float *data, int x_dim, int y_dim )
{
int ix = blockIdx.x;
int iy = blockIdx.y*blockDim.y + threadIdx.y;
int idx = iy*x_dim + ix;
float tmp = data[idx];
if( ix % 2 )
{
tmp += sqrtf( sinf(tmp) + 1.f );
}
else
{
tmp += sqrtf( cosf(tmp) + 1.f );
}
data[idx] = tmp;
}
dim3 block( 1, 512 );
dim3 grid( 2048/1, 2048/512 );
kernel<<<grid,block>>>( d_data, 2048, 2048 );
这里的基本问题是内存合并和线程分歧的困境。 原始代码在主列中处理数组,因此它跨越了内存访问模式,但是没有差异。 我可以将其更改为行优先,这又有线程发散的问题。
那么,有谁有更好的主意如何最大化性能呢?
与跨内存访问相比,就性能而言,这里的线程分歧并不是一个大问题。 我会合并。 此外,您的数据存储具有隐式AoS顺序。 如果可以将数据重新排序到SoA,则可以解决这两个问题。
因此,我将对该内核进行重新排序,使其首先以行主要方式处理事物。 这解决了合并问题,但引入了翘曲发散。
如果您无法重新排序数据,那么我将考虑通过修改索引方案来消除扭曲差异,以便偶数扭曲处理偶数元素,奇数扭曲处理奇数元素。
这将消除翘曲发散,但会再次破坏完美的合并,但是缓存应有助于解决此问题。 在Fermi的情况下,L1缓存应该可以很好地平滑此模式。 然后,我将把这种情况与翘曲的发散情况进行比较,看哪个更快。
考虑到
sin(x) = cos(x + pi/2)
因此,您可以将if ... else
条件替换为
tmp += sqrtf( cosf(tmp + (ix%2) * pi/2) + 1.f );
避免分支分歧。
如果执行此操作,则可以将块尺寸设置为16 x 16或其他一些具有较低长宽比的形状。 我将使用共享内存来抓取2个块的数据(每个idx从数据中抓取2个元素,可能由blockDim.x元素分隔),然后让每个块执行其分配的“奇数”行,后跟“偶数”行。 您将不得不重新计算ix和iy(可能还有idx),并且将使用1/2的块,但是应该有合并的内存访问,后跟无差异的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.