【问题标题】:Generating permutations with CUDA使用 CUDA 生成排列
【发布时间】:2013-11-25 13:21:18
【问题描述】:

我正在阅读有关 CUDA 的内容,我尝试实现一个简单的代码来创建 array {a,b,c,d} 的所有可能排列,但我不知道如何实现 CUDA 方式(因为我阅读的所有示例都是形成a[blockIdx.x] = b[blockIdx.x] + c[blockIdx.x])。

任何帮助将不胜感激。

【问题讨论】:

  • 如果你已经安装了 cuda sdk。它在示例目录中有各种示例。尝试学习和实施,如果遇到任何问题,可以在这里提出问题的完整复制者。
  • 我用的是 ubuntu,当我 CUDA 时它没有创建任何文件夹
  • 默认路径为/usr/local/cuda/。你可以在/usr/local/cuda/samples中找到samples
  • 作为 Sagar Masuri 建议的替代方案(或补充方案),您不妨看看 CUDA By Example 这本书,它将顺利地向您介绍 GPU 上的并行编程主题.
  • 听起来你想枚举所有permutations 的 n=4 件事,一次采取 k=4 (在这种情况下有 n!/(n-k)! 即 24 个独特的结果)是正确的?或者是其他东西?也许您可以勾勒出您期望的结果的简要轮廓。我描述的代码 here 可能会引起人们的兴趣。

标签: c arrays cuda


【解决方案1】:

下面是 CUDA 中并行排列生成器的一个有点幼稚的实现。该示例旨在生成ABCD 的所有可能排列。

由于可以通过将第一个符号固定为 X 并附加其余符号的所有可能排列来生成所有可能的排列,然后将第一个符号更改为 Y 并再次执行上述过程,代码背后的简单想法是指定4线程来完成这项工作,每个线程引用不同的初始符号。

第一个符号之后的排列以规范的方式进行评估,即通过递归。

显然,下面的代码可以变得更通用,或许还可以改进,但它应该会给你一个初步的大致思路。

#include <stdio.h>

inline void GPUassert(cudaError_t code, char * file, int line, bool Abort=true)
{
    if (code != 0) {
        fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code),file,line);
        if (Abort) exit(code);
    }       
}

#define GPUerrchk(ans) { GPUassert((ans), __FILE__, __LINE__); }

__host__ __device__ void swap(char *x, char *y)
{
    char temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

__device__ void permute_device(char *a, int i, int n, int tid, int* count) 
{
    if (i == n) { 
        char b[4]; char* c = a - 1; 
        b[0] = c[0]; b[1] = c[1]; b[2] = c[2]; b[3] = c[3];
        printf("Permutation nr. %i from thread nr. %i is equal to %s\n", count[0], tid, c); count[0] = count[0] + 1; 
    }
    else
    {
        for (int j = i; j <= n; j++)
        {
            swap((a+i), (a+j));
            permute_device(a, i+1, n, tid, count);
            swap((a+i), (a+j)); //backtrack
        }
    }
} 

__global__ void permute_kernel(char* d_A, int size) {

    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    int count[1]; count[0] = 0;

    char local_array[4];

    for (int i=0; i<size; i++) local_array[i] = d_A[i];

    swap(local_array+threadIdx.x,local_array);
    permute_device(local_array+1,0,2,tid,count);

}

int main()
{
    char h_a[] = "ABCD";

    char* d_a; cudaMalloc((void**)&d_a,sizeof(h_a));
    GPUerrchk(cudaMemcpy(d_a, h_a, sizeof(h_a), cudaMemcpyHostToDevice));

    printf("\n\n Permutations on GPU\n");
    permute_kernel<<<1,4>>>(d_a, 4);
    GPUerrchk(cudaPeekAtLastError());
    GPUerrchk(cudaDeviceSynchronize());

    getchar();
    return 0;
}

【讨论】:

  • 谢谢,这就是我一直在寻找的想法
  • 在 GPU 上只运行 4 个线程没有多大意义(它会比在 CPU 上运行慢得多)。由于排列数为n!,因此无法计算足够大到线程数会使 GPU 饱和的数组的排列数。在 16 个符号(只有半个线程)的情况下,您已经有 20 万亿个排列。
  • 我认为您需要考虑为排列实现一个unranking 函数。用于排列的 unranking 函数采用一个整数(这将是 CUDA 的线程索引)并将其转换为唯一的排列。因为 unranking 函数很昂贵,您可能希望考虑将它与迭代方法结合起来。因此,您将搜索空间划分为足够多的部分,每个部分使用一个线程会使 GPU 饱和。然后,您以 unranking 开始每个线程以设置初始状态。
【解决方案2】:

我想知道你的组合是不是成对的。

  1. 想想数组的长度。(n)
  2. 想想组合复合体。 (n^2,成对)
  3. 选择一种并行计算的方法。 (以block id为add offset,thread id为base one.array(threadid) + array(threadid+offset)为例)

所以你的内核应该这样写:

template<typename T>
__global__ void 
combination(T* in, T* out) {
  int tid = threadId.x;
  int bid = blockId.x+1;
  out[tid+bid*blockDim.x] = in[tid]+in[(tid+bid)%arrlen];     //0+1, 1+2,..., 0+2, 1+3
}

您可以使用gridDim(arrlen)blockDim(arrlen) 调用内核。

【讨论】:

  • 对不起我的粗心。
  • 有n个!数组的排列不是 n^2。这是不正确的
  • 好吧,我在答案的第一行将组合定义为成对组合。对于排列问题Use CUDA to compute all possible combinations of words?,可以找到很多答案。
  • 您的内核返回序数值的总和,而不是排列/组合。没有办法根据该内核返回的结果来推断排列或组合是什么。不管我们是不是成对说话,这都是真的。
猜你喜欢
  • 2012-09-21
  • 2019-10-22
  • 2011-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多