CUDA-埃拉托什尼筛分为若干部分

CUDA-埃拉托什尼筛分为若干部分,c,cuda,parallel-processing,primes,sieve-of-eratosthenes,C,Cuda,Parallel Processing,Primes,Sieve Of Eratosthenes,我正在GPU上编写Eratosthenes()筛的实现。但是没有这样的事情- 方法: 创建具有默认值0/1(0-prime,1-no)的n元素数组并将其传递给GPU(我知道可以直接在内核中完成,但目前还没有问题) 块中的每个线程检查单个数字的倍数。每个区块检查总sqrt(n)可能性。每个块==不同的间隔 将倍数标记为1并将数据传回主机 代码: #包括 #包括 #定义线程1024 __全局无效内核(int*全局,int线程){ 外部共享内部缓存[]; inttid=threadIdx.x+1; i

我正在GPU上编写Eratosthenes()筛的实现。但是没有这样的事情-

方法:

  • 创建具有默认值0/1(0-prime,1-no)的n元素数组并将其传递给GPU(我知道可以直接在内核中完成,但目前还没有问题)
  • 块中的每个线程检查单个数字的倍数。每个区块检查总sqrt(n)可能性。每个块==不同的间隔
  • 将倍数标记为1并将数据传回主机
  • 代码:

    #包括
    #包括
    #定义线程1024
    __全局无效内核(int*全局,int线程){
    外部共享内部缓存[];
    inttid=threadIdx.x+1;
    int offset=blockIdx.x*blockDim.x;
    整数=偏移量+tid;
    cache[tid-1]=全局[number];
    __同步线程();
    int start=偏移量+1;
    int end=偏移量+螺纹;
    
    对于(int i=start;i,我认为有几个问题,但这里有一个指向实际问题的指针:Eratosthenes的筛选以迭代方式移除已经遇到的素数的倍数,并且您希望将工作负载分离到线程块中,其中每个线程块在一块共享内存(在您的示例中为缓存)上运行。但是,线程块通常独立于所有其他线程块,并且不能轻松地相互通信。举例说明此问题:索引为0的线程块中的索引为0的线程会删除2的倍数。索引为>0的线程块无法知道这一点。

    此代码有各种问题,在我看来

    >p>基本上是访问范围之外的项。考虑内核中的这个序列:

    int tid = threadIdx.x + 1;
    int offset = blockIdx.x * blockDim.x;
    int number = offset + tid;
    
    cache[tid - 1] = global[number];
    
    你(在某些情况下——见下文)已启动一个大小与您的
    全局
    数组完全相同的线程数组。那么,当编号最高的线程运行上述代码时会发生什么情况?
    number
    =
    threadIdx.x+1+blockIdx.x*blockDim.x
    。此
    number
    索引将超出数组的末尾。对于
    n
    。如果您使用或使用
    cuda memcheck
    运行代码,这个问题对您来说是显而易见的。当您在使用cuda代码时遇到问题,并且在向他人寻求帮助之前,您应该始终执行这些操作

  • 只有当输入
    n
    是一个完美的正方形时,代码才有机会正常工作。原因包括以下代码行(以及内核中的依赖项):

    (请注意,这里正确的函数应该是
    atoi
    而不是
    atol
    ,但我离题了…)除非
    n
    是一个完美的正方形,否则得到的
    n\u sqrt
    将略小于
    n
    的实际平方根。这将导致您计算小于必要大小的总线程数组。(如果你现在不相信我,那没关系。运行下面我将发布的代码,输入1025这样的大小,然后查看threads*块的数量是否足以覆盖1025个数组。)

  • 正如你所说:

    每个区块检查总sqrt(n)可能性

    希望这也指出了非完美正方形
    n
    的危险,但我们现在必须问“如果
    n
    大于最大螺纹块大小(1024)的平方会怎样?”答案是代码在许多情况下无法正常工作-您选择的10240000输入,尽管是完美正方形,但超过了1024^2(1048576)由于这个原因,它不起作用。您的算法(我声称它不是一个Eratosthenes的筛子)要求每个块都能够检查
    sqrt(n)
    可能性,正如您在问题中所述。当由于每个块的线程数限制而无法实现时,您的算法开始崩溃

  • 下面是一段代码,它试图修复上面的问题1,并至少解释了与#2和#3相关的故障:

    #包括
    #包括
    #定义线程1024
    #定义最大值10240000
    #定义cudaCheckErrors(msg)\
    做{\
    cudaError\u t\u err=cudaGetLastError()\
    如果(_err!=cudaSuccess){\
    fprintf(标准,“致命错误:%s(%s位于%s:%d)\n”\
    msg,cudaGetErrorString(_err)\
    __文件(行)\
    fprintf(stderr,“***失败-中止\n”)\
    出口(1)\
    } \
    }而(0)
    __全局无效内核(int*全局,int线程){
    外部共享内部缓存[];
    inttid=threadIdx.x+1;
    int offset=blockIdx.x*blockDim.x;
    整数=偏移量+tid;
    if((blockIdx.x!=(gridDim.x-1))| |(threadIdx.x!=(blockDim.x-1))){
    cache[tid-1]=全局[number];
    __同步线程();
    int start=偏移量+1;
    int end=偏移量+螺纹;
    
    对于(int i=开始;我将标记赔率的倍数,而不是素数,帮助(或2-3-5…共数,即车轮)?另一种处理方法可能是分段工作,并使用已知的素数筛选下一个块。@Peter Klenner,thx,但查看了我编辑的文章。我添加了我想要实现的示例。我认为线程块之间不需要通信。每个线程块在不同的间隔上工作。@Will Ness,我不标记素数只有倍数。当n=64时,要检查的是sqrt(64)=8个数字。我希望每个线程块在不同的间隔上工作,threadIdx==multiple.block 1实际上覆盖了间隔10-17。您的循环
    for(int I=start;I
    不适用于检测素数的倍数。例如,数字10被打折,因为它是此线程块中的第一个条目。您需要使用模运算符并将循环扩展到每个线程的整个段上。从我的h
    int tid = threadIdx.x + 1;
    int offset = blockIdx.x * blockDim.x;
    int number = offset + tid;
    
    cache[tid - 1] = global[number];
    
    int n = atol(argv[1]);
    int n_sqrt = floor(sqrt((double)n));
    ...
    int threads = min(n_sqrt, THREADS);
    int blocks = n / threads;
    
    #include <stdio.h>
    #include <stdlib.h>
    #define THREADS 1024
    #define MAX 10240000
    
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    
    __global__ void kernel(int *global, int threads) {
        extern __shared__ int cache[];
    
        int tid = threadIdx.x + 1;
        int offset = blockIdx.x * blockDim.x;
        int number = offset + tid;
    
        if ((blockIdx.x != (gridDim.x-1)) || (threadIdx.x != (blockDim.x-1))){
          cache[tid - 1] = global[number];
          __syncthreads();
    
          int start = offset + 1;
          int end = offset + threads;
    
          for (int i = start; i <= end; i++) {
            if ((i != tid) && (tid != 1) && (i % tid == 0)) {
                cache[i - offset - 1] = 1;
            }
          }
          __syncthreads();
          global[number] = cache[tid - 1];}
    }
    
    
    int cpu_sieve(int n){
        int limit = floor(sqrt(n));
        int *test_arr = (int *)malloc(n*sizeof(int));
        if (test_arr == NULL) return -1;
        memset(test_arr, 0, n*sizeof(int));
        for (int i = 2; i < limit; i++)
          if (!test_arr[i]){
            int j = i*i;
            while (j <= n){
              test_arr[j] = 1;
              j += i;}}
        int count = 0;
        for (int i = 2; i < n; i++)
          if (!test_arr[i]) count++;
        return count;
    }
    
    int main(int argc, char *argv[]) {
        int *array, *dev_array;
        if (argc != 2) {printf("must supply n as command line parameter\n"); return 1;}
        int n = atoi(argv[1]);
        if ((n < 1) || (n > MAX)) {printf("n out of range %d\n", n); return 1;}
        int n_sqrt = floor(sqrt((double)n));
    
        size_t array_size = n * sizeof(int);
        array = (int*) malloc(n * sizeof(int));
        array[0] = 1;
        array[1] = 1;
        for (int i = 2; i < n; i++) {
            array[i] = 0;
        }
    
        cudaMalloc((void**)&dev_array, array_size);
        cudaMemcpy(dev_array, array, array_size, cudaMemcpyHostToDevice);
    
        int threads = min(n_sqrt, THREADS);
        int blocks = n / threads;
        int shared = threads * sizeof(int);
        printf("threads = %d, blocks = %d\n", threads, blocks);
        kernel<<<blocks, threads, shared>>>(dev_array, threads);
        cudaMemcpy(array, dev_array, array_size, cudaMemcpyDeviceToHost);
        cudaCheckErrors("some error");
        int count = 0;
        for (int i = 0; i < n; i++) {
            if (array[i] == 0) {
                count++;
            }
        }
        printf("Count: %d\n", count);
        printf("CPU Sieve: %d\n", cpu_sieve(n));
        return 0;
    }