我正在发布一个深入研究的代码以供审查。我相信它应该可以毫无问题地编译和执行,但由于我排除了所有不相关的部分,我可能犯了一些错误。
struct Users {
double A[96];
double B[32];
double C[32];
};
这是我的带有固定长度数组的用户结构。下面给出主要功能。
int main(int argc, char **argv) {
int numUsers = 10;
Users *users = new Users[numUsers];
double Step[96];
for (int i = 0; i < 32; i++) {
Step[i] = 0.8;
Step[i + 32] = 0.8;
Step[i + 64] = 0.8;
}
for (int usr = 0; usr < numUsers; usr++) {
for (int i = 0; i < 32; i++) {
users[usr].A[i] = 10;
users[usr].A[i + 32] = 20;
users[usr].A[i + 64] = 30;
}
memset(users[usr].B, 0, sizeof(double) * 32);
memset(users[usr].C, 0, sizeof(double) * 32);
}
double *d_Step;
cudaMalloc((void**)&d_Step, sizeof(double) * 96);
cudaMemcpy(d_Step, Step, sizeof(double) * 96, cudaMemcpyHostToDevice);
Users *deviceUsers;
cudaMalloc((void**)&deviceUsers, sizeof(Users) * numUsers);
cudaMemcpy(deviceUsers, users, sizeof(Users) * numUsers, cudaMemcpyHostToDevice);
dim3 grid;
dim3 block;
grid.x = 1;
grid.y = 1;
grid.z = 1;
block.x = 32;
block.y = 10;
block.z = 1;
calc<<<grid, block >>> (deviceUsers, d_Step, numUsers);
delete users;
return 0;
}
请注意,Step 数组是具有 96 个 bin 的 1D 数组,我跨越 10 个 warp(x 方向上的 32 个线程,我的块中有 10 个线程)。每个经纱将访问相同的 Step 数组。这可以在下面的内核中看到。
__global__ void calc(Users *users, double *Step, int numUsers) {
int tId = threadIdx.x + blockIdx.x * blockDim.x;
int uId = threadIdx.y;
while (uId < numUsers) {
double mean00 = users[uId].A[tId] * Step[tId];
double mean01 = users[uId].A[tId + 32] * Step[tId + 32];
double mean02 = users[uId].A[tId + 64] * Step[tId + 64];
users[uId].A[tId] = (mean00 == 0? 0 : 1 / mean00);
users[uId].A[tId + 32] = (mean01 == 0? 0 : 1 / mean01);
users[uId].A[tId + 64] = (mean02 == 0? 0 : 1 / mean02);
uId += 10;
}
}
现在,当我使用 NVIDIA Visual Profiler 时,合并后的检索率为 47%。我进一步调查并发现每个经线正在访问的 Step 数组会导致此问题。如果我用一些常量替换它,则访问是 100% 合并的。
Q1)据我了解,合并访问链接到字节行,即字节行必须是 32 的倍数,无论它们是整数还是双字节行。为什么我没有获得合并访问?
据我所知,cuda 每当在设备全局内存中分配一个内存块时,它都会为其分配一个偶数地址。因此,只要起点 + 32 位置由经线访问,就应该合并访问。我对么?
硬件
Geforce GTX 470,计算能力 2.0