我正在编写一个尝试32位数字的每个组合的程序,看它是否满足某些条件并返回那些。从示例程序中我一直看到数组的大小总是(元素数* sizeof())。
这个数字看起来太大了,而且大多数数字也会被拒绝,所以我不需要2 ^ 32阵列。我知道结果的数量将远远少于2 ^ 32,但我不确切知道会有多少。
此外,每个线程在尝试数字时都会循环,因此线程可能有多个正结果。
那么我该如何进行内存分配以及如何存储接受的值?
答案 0 :(得分:4)
一种方法是尝试分配尽可能多的内存或认为您需要存储内核输出,然后使用原子递增的计数器来跟踪输出缓冲区中的下一个空闲位置,其中任何给定的线程可以存储结果
例如,如果你定义一个这样的辅助结构:
struct counter
{
unsigned int * _val;
__host__ __device__
counter(unsigned int * value) : _val(value) {};
__device__
unsigned int next() {
return atomicAdd(_val, 1);
};
}
然后在主机代码中执行类似
的操作unsigned int * array_index;
const unsigned int zero = 0;
cudaMalloc((void **)&array_index, sizeof(unsigned int*));
cudaMemcpy(array_index, &zero, sizeof(unsigned int), cudaMemcpyHostToDevice);
counter mycounter(array_index);
您的初始化设备内存计数器为零,可以通过重复调用next()
方法在设备代码中安全地读取和递增。
在内核中看起来像:
__global__ void kernel(Type * buffer, counter mycounter)
{
// Calculate and find a match...
buffer[mycounter.next()] = match;
}
[强烈警告:所有使用浏览器编写的代码,未经编译或测试,可能会使您的GPU着火,使用风险自负]
然后,您的内核可以为每个线程发出尽可能多的输出,这与您的算法设计非常吻合。将我上面说明的设计模式扩展到包括对数组的边界检查是明智的。您还应该注意可以检索内核发出的输出总数,如下所示:
unsigned int N;
cudaMemcpy(&N, array_index, sizeof(unsigned int), cudaMemcpyDeviceToHost);
当内核的输出相当“稀疏”时,该解决方案可能是最有用的,即相对于线程数或输入数量的输出数量相当小。如果你的问题更“密集”,即内核将相对于线程数或输入数发出大量输出,那么原子内存事务可能代表显着的性能损失。在这种情况下,最好将线程存储到“稀疏”输出缓冲区中,然后使用流压缩传递来消除内核输出缓冲区中的少量空条目。