我写了我的第一个cuda脚本,并想知道它是如何并行化的。
我有一些变量r0_dev
和f_dev
,每个变量长度为(*fnum_dev) * 3
。在每个块上,它们按顺序读取。然后是我读过的r_dev
和我要并行写的v_dev
,两者都是长度为gnum * 3
的数组。
该程序产生我希望它产生的结果,但复杂性(时间与数据大小的关系)并不是我所期望的。
我的期望是,当数组v_dev
的大小增加时,执行时间保持不变,因为gnum
的值小于某个维度中允许的块数。
现实是不同的。使用以下代码,测量时间。观察到线性复杂性,我将在顺序代码中执行该操作。
dim3 blockGrid(gnum);
cudaEvent_t start, stop;
float time;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
// the actual calculation
stokeslets<<<blockGrid, 1>>>(fnum_dev, r0_dev, f_dev, r_dev, v_dev);
// time measurement
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
问题:
如上所述,我的期望是错的吗? 还有哪些其他考虑因素很重要?
详情
以下显示stokeslet
的实施。也许我在做那些不好的事情?
__device__ void gridPoint(int offset, float* r0, float* f, float* r, float* v) {
int flatInd = 3 * offset;
float dr[3];
float len = 0;
float drf = 0;
for (int i = 0; i < 3; i++) {
dr[i] = r[i] - r0[i + flatInd];
len += dr[i] * dr[i];
drf += dr[i] * f[i + flatInd];
}
len = sqrt(len);
float fak = 1 / (8 * 3.1416 * 0.7);
v[0] += (fak / len) * (f[0 + flatInd] + (dr[0]) * drf / (len * len));
v[1] += (fak / len) * (f[1 + flatInd] + (dr[1]) * drf / (len * len));
v[2] += (fak / len) * (f[2 + flatInd] + (dr[2]) * drf / (len * len));
}
__global__ void stokeslets(int* fnum, float* r0, float* f, float* r, float* v) {
// where are we (which block, which is equivalent to the grid point)?
int idx = blockIdx.x;
// we want to add all force contributions
float rh[3] = {r[3 * idx + 0], r[3 * idx + 1], r[3 * idx + 2]};
float vh[3] = {0, 0, 0};
for (int i=0; i < *fnum; i++) {
gridPoint(i, r0, f, rh, vh);
}
// sum intermediate velocity vh
int flatInd = 3 * idx;
v[0 + flatInd] += vh[0];
v[1 + flatInd] += vh[1];
v[2 + flatInd] += vh[2];
}
答案 0 :(得分:3)
代码的主要问题是您运行的多个块只包含一个线程。
引用CUDA C编程指南
NVIDIA GPU架构围绕可扩展的多线程阵列构建 流式多处理器(SM)。当主机CPU上的CUDA程序调用a时 在内核网格中,网格的块被枚举并分发给多处理器 具有可用的执行能力。线程块的线程并发执行 在一个多处理器上,多个线程块可以在一个上同时执行 多处理器。当线程块终止时,空出的新块将被启动 多处理器。
多处理器旨在同时执行数百个线程。
引用帖子How CUDA Blocks/Warps/Threads map onto CUDA Cores?
的回答程序员将工作划分为线程,将线程划分为线程块,将线程块划分为网格。计算工作分配器将线程块分配给流式多处理器(SM)。一旦将线程块分配给SM,就会分配线程块的资源(warp和共享内存),并将线程划分为32个线程的组,称为warps。一旦分配了warp,它就被称为主动warp。 两个warp调度程序每个周期选择两个活动warp并将warp调度到执行单元。
从两个带有粗体标记的句子开始,每个流多处理器每个时钟周期只运行2个线程。这是您观察与连续情况基本相同的计算复杂度的主要原因。
建议重写代码/内核以承载每个块运行多个线程的可能性。