如何处理CUDA中大小不固定的数据集?
是根据问题集设置块和网格大小,还是值得分配2的因子作为块维度,并有一些内核逻辑来处理溢出?
我可以看出这对块维度很重要,但这对网格维度有多重要呢?据我所知,实际硬件约束仅限于块级别(即分配给具有一组SP的SM的块,因此可以处理特定的warp大小)。
我已经浏览了Kirk的“大规模并行处理器编程”,但它并没有真正涉及这个领域。
如何处理CUDA中大小不固定的数据集?
是根据问题集设置块和网格大小,还是值得分配2的因子作为块维度,并有一些内核逻辑来处理溢出?
我可以看出这对块维度很重要,但这对网格维度有多重要呢?据我所知,实际硬件约束仅限于块级别(即分配给具有一组SP的SM的块,因此可以处理特定的warp大小)。
我已经浏览了Kirk的“大规模并行处理器编程”,但它并没有真正涉及这个领域。
// Fused square root and dscal operation
__global__
void cdivkernel(const int n, double *a)
{
__shared__ double oneondiagv;
int imin = threadIdx.x + blockDim.x * blockIdx.x;
int istride = blockDim.x * gridDim.x;
if (threadIdx.x == 0) {
oneondiagv = rsqrt( a[0] );
}
__syncthreads();
for(int i=imin; i<n; i+=istride) {
a[i] *= oneondiagv;
}
}
启动此内核,执行参数计算如下:
包含执行参数计算和内核启动的结果包装器函数如下:
// Fused the diagonal element root and dscal operation into
// a single "cdiv" operation
void fusedDscal(const int n, double *a)
{
// The semibandwidth (column length) determines
// how many warps are required per column of the
// matrix.
const int warpSize = 32;
const int maxGridSize = 112; // this is 8 blocks per MP for a Telsa C2050
int warpCount = (n / warpSize) + (((n % warpSize) == 0) ? 0 : 1);
int warpPerBlock = max(1, min(4, warpCount));
// For the cdiv kernel, the block size is allowed to grow to
// four warps per block, and the block count becomes the warp count over four
// or the GPU "fill" whichever is smaller
int threadCount = warpSize * warpPerBlock;
int blockCount = min( maxGridSize, max(1, warpCount/warpPerBlock) );
dim3 BlockDim = dim3(threadCount, 1, 1);
dim3 GridDim = dim3(blockCount, 1, 1);
cdivkernel<<< GridDim,BlockDim >>>(n,a);
errchk( cudaPeekAtLastError() );
}
也许这能提供一些关于如何设计针对输入数据大小设置执行参数的"通用"方案的线索。好的,我想我们需要处理两个问题。
1)分配块大小(即线程数)的好方法 这通常取决于您正在处理的数据类型。您正在处理向量吗?您正在处理矩阵吗?建议的方法是保持线程数为32的倍数。因此,在处理向量时,启动256 x 1,512 x 1块可能很好。同样,在处理矩阵时,选择32 x 8、32 x 16等。
2)分配网格大小(即块数)的好方法 这有些棘手。仅仅因为我们可以启动10000个块并不是通常做事情的最佳方式。切换块进入和退出硬件是昂贵的。要考虑的两个因素是每个块使用的共享内存和可用的总SP数量,并求解最优数量。
您可以从thrust找到一个非常好的实现方法。不过可能需要一段时间才能弄清楚代码内部发生了什么。
我认为根据问题集设置块和网格大小通常是最好的选择,特别是为了优化目的。拥有无用的额外线程真的没有意义,可能会使程序的性能变差。
__syncthreads()
,块就越多(这样一个块可以在另一个块等待同步时运行)线程应该是warp大小的倍数(即通常为32)
通常最好选择线程数,使每个块的最大线程数(基于硬件)是线程数的倍数。例如,使用256个线程每块的最大线程数为768,比使用512个线程要好,因为多个线程可以同时在一个块上运行。
max()
的目的是什么:max(1, min(4, warpCount))
? - syntagma