使用CUDA进行暴力攻击。关于内存分配

时间:2016-03-13 06:02:43

标签: c++ cuda

我正在编写一个尝试32位数字的每个组合的程序,看它是否满足某些条件并返回那些。从示例程序中我一直看到数组的大小总是(元素数* sizeof())。

这个数字看起来太大了,而且大多数数字也会被拒绝,所以我不需要2 ^ 32阵列。我知道结果的数量将远远少于2 ^ 32,但我不确切知道会有多少。

此外,每个线程在尝试数字时都会循环,因此线程可能有多个正结果。

那么我该如何进行内存分配以及如何存储接受的值?

1 个答案:

答案 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);

当内核的输出相当“稀疏”时,该解决方案可能是最有用的,即相对于线程数或输入数量的输出数量相当小。如果你的问题更“密集”,即内核将相对于线程数或输入数发出大量输出,那么原子内存事务可能代表显着的性能损失。在这种情况下,最好将线程存储到“稀疏”输出缓冲区中,然后使用流压缩传递来消除内核输出缓冲区中的少量空条目。