我正在编写CUDA代码而我正在使用GForce 9500 GT显卡。
我正在尝试处理20000000个整数元素的数组,而我使用的线程数是256
warp大小为32.计算能力为1.1
这是硬件http://www.geforce.com/hardware/desktop-gpus/geforce-9500-gt/specifications
现在,块数= 20000000/256 = 78125?
这个声音不正确。如何计算块编号? 任何帮助将不胜感激。
我的CUDA内核功能如下。这个想法是每个块将计算其总和,然后通过添加每个块的总和来计算最终总和。
__global__ static void calculateSum(int * num, int * result, int DATA_SIZE)
{
extern __shared__ int shared[];
const int tid = threadIdx.x;
const int bid = blockIdx.x;
shared[tid] = 0;
for (int i = bid * THREAD_NUM + tid; i < DATA_SIZE; i += BLOCK_NUM * THREAD_NUM) {
shared[tid] += num[i];
}
__syncthreads();
int offset = THREAD_NUM / 2;
while (offset > 0) {
if (tid < offset) {
shared[tid] += shared[tid + offset];
}
offset >>= 1;
__syncthreads();
}
if (tid == 0) {
result[bid] = shared[0];
}
}
我将此功能称为
calculateSum <<<BLOCK_NUM, THREAD_NUM, THREAD_NUM * sizeof(int)>>> (gpuarray, result, size);
THREAD_NUM = 256 和gpu数组大小为20000000.
这里我只使用块号为16但不确定它是否正确? 如何确保实现最大并行度?
这是我的CUDA占用计算器的输出。它说当块号为8时我将占用100%。这意味着当块号= 8且线程号= 256时,我将获得最大效率。这是对的吗?
谢谢
答案 0 :(得分:2)
如果每个thred进程一个元素,并且每个块有256个线程,则应运行20000000个线程,从而产生78125个块。这是完全有效的数字。
然而,有一点问题。我手边没有CC1.1设备,但在CC1.3:
Maximum sizes of each dimension of a grid: 65535 x 65535 x 1
所以你应该为不同的数据部分运行内核几次,或者制作2D网格,只需将线程的2D地址转换为数组元素的1D地址。
答案 1 :(得分:2)
您发布的内核代码可以处理任何输入数据大小,与您选择启动的块数无关。选择应该只是降低性能。
根据经验,对于这种内核,您需要在单个多处理器上并发运行的块数乘以卡上多处理器的数量。第一个数字可以使用CUDA工具包中附带的CUDA占用电子表格获得,但每个多处理器的上限为8个块,而第二个数字为您拥有的设备的4个。这意味着实现最大可能并行性所需的块数不会超过32个,但要回答的确需要访问我目前没有的编译器。
您还可以使用基准测试来实验确定最佳块数,使用4,8,12,16,20,24,28或32个块中的一个(4的倍数,因为这是卡上多处理器的数量) )。
答案 2 :(得分:1)
在您的情况下,线程总数(20000000)按每个块的线程数(256)均匀划分,因此您可以使用该数字(78125)。如果数字不均匀分配,则常规的整数除法会将其向下舍入,最终会得到比您需要的更少的线程。因此,在这种情况下,您需要使用如下函数对除法结果进行舍入:
int DivUp(int a, int b) {
return ((a % b) != 0) ? (a / b + 1) : (a / b);
}
由于此函数可能会为您提供比元素更多的线程,因此您还需要在内核中添加一个测试以中止最后几个线程的计算:
int i(blockIdx.x * blockDim.x + threadIdx.x);
if (i >= n_items) {
return;
}
然而,还有一个障碍。您的硬件在网格中的每个维度中限制为最多65535个块,并且限制为二维(x和y)。因此,如果在使用DivUp()之后,最终得到的计数高于此值,则有两种选择。您可以将工作负载分开并多次运行内核,也可以使用两个维度。
要使用两个维度,您可以选择两个数字,每个数字都低于硬件限制,并且在乘以时,将成为您需要的实际块数。然后,在内核顶部添加代码,将两个维度(x和y)合并为一个索引。
答案 3 :(得分:1)
您只在内核中使用网格的x-Dimension。因此,使用cc 1.1限制为65535个块。
20000000/256 = 78125是正确的!
所以你肯定需要超过1个区块。
内核:
//get unique block index
const unsigned int blockId = blockIdx.x //1D
+ blockIdx.y * gridDim.x //2D
//terminate unnecessary blocks
if(blockId >= 78124)
return;
//... rest of kernel
最简单的方法是在内核中使用两个y块并检查块id。
dim3 gridDim = dim3(65535, 2);
这会使超过52945个块无用,我不知道什么是开销,但填充第一个x然后y和z维度可以创建很多未使用的块,特别是如果达到z维度!
(Nvidia应该提供一个实用程序函数,以获得内核中唯一块使用的最佳网格用法,就像这里的情况一样)
对于这个简单的例子,如何使用x和y以及计算根。
grid(280, 280) = 78400 blocks //only 275 blocks overhead, less is not possible
这是计算能力3.0的一大优势。每个块上的32位范围使生活变得更加容易。 为什么它被限制在65535我从来不理解。
但我仍然倾向于向下兼容。
我也会测试@talonmies变种。