C++ 执行少量插入/移位的并行算法

C++ 执行少量插入/移位的并行算法,c++,algorithm,cuda,parallel-processing,gpu,C++,Algorithm,Cuda,Parallel Processing,Gpu,假设我有一个由8个数字组成的数组a,我有另一个由数字组成的数组B来确定a中的数字应该向右移动多少个位置 A 3,6,7,8,1,2,3,5 b0,1,0,0,0,0,0,0,0,0 0表示有效,1表示此数字应在后1位,输出数组应在后3位之间插入0,输出数组C应为: C:3,0,6,7,8,1,2,3 是否插入0或其他内容并不重要,关键是3之后的所有数字都移动了一个位置。出站号码将不再在数组中 另一个例子: A 3,6,7,8,1,2,3,5 b0,1,0,0,2,0,0,0,0 c3,0,6,7

假设我有一个由8个数字组成的数组a,我有另一个由数字组成的数组B来确定a中的数字应该向右移动多少个位置

A 3,6,7,8,1,2,3,5

b0,1,0,0,0,0,0,0,0,0

0表示有效,1表示此数字应在后1位,输出数组应在后3位之间插入0,输出数组C应为:

C:3,0,6,7,8,1,2,3

是否插入0或其他内容并不重要,关键是3之后的所有数字都移动了一个位置。出站号码将不再在数组中

另一个例子:

A 3,6,7,8,1,2,3,5

b0,1,0,0,2,0,0,0,0

c3,0,6,7,8,0,1,2

A 3,6,7,8,1,2,3,5

b0,1,0,0,1,0,0,0

c3,0,6,7,8,1,2,3

我正在考虑使用扫描/前缀和或类似的方法来解决这个问题。而且这个阵列很小,我应该能够在一个扭曲中适应阵列(一种可能的方法)

由于移位的模糊性(
0,1,0,1
0,1,1,1
0,1,0,0
都会产生相同的数据偏移模式,例如)不可能只创建移位模式的前缀和来生成每个位置的相对偏移。但是,我们可以观察到,如果移位模式中的每个零都被其左侧的第一个非零移位值替换,则将创建有效的偏移模式:

0, 1, 0, 0   (shift pattern)
0, 1, 1, 1   (offset pattern)

那么如何做到这一点呢?假设我们有第二个测试用例移位模式:

      0, 1, 0, 0, 2, 0, 0, 0
我们希望的偏移模式是:

      0, 1, 1, 1, 2, 2, 2, 2
  • 对于给定的移位模式,创建一个二进制值,如果移位模式对应索引处的值为零,则每个位为一,否则为零。我们可以使用名为
    \u ballot()
    的指令来实现此目的。每个车道将从选票中获得相同的值:

      1  0  1  1  0  1  1  1  (this is a single binary 8-bit value in this case)
    
  • 现在,每个扭曲通道将采用该值,并向其添加一个值,该值在扭曲通道位置有一个1位。在本示例的其余部分使用通道1:

    + 0  0  0  0  0  0  1  0  (the only 1 bit in this value will be at the lane index)
    = 1  0  1  1  1  0  0  1
    
  • 我们现在使用步骤2的结果,并使用步骤1的结果进行按位异或:

    = 0  0  0  0  1  1  1  0
    
  • 现在我们计算该值中的1位数(该值有一个
    \uuu popc()
    ),并从结果中减去1。因此,对于上面的车道1示例,此步骤的结果将是
    2
    ,因为设置了3位。这将使用到左侧第一个值的距离,该值在原始换档模式中为非零。因此对于车道1示例,车道1左侧的第一个非零值高出2车道,i、 东三巷

  • 对于每个车道,我们使用步骤4的结果获取该车道的适当偏移值。我们可以使用
    \uuu shfl\u down()
    指令一次处理所有车道

      0, 1, 1, 1, 2, 2, 2, 2
    
    从而产生我们想要的“偏移模式”

  • 一旦获得了所需的偏移模式,让每个扭曲通道使用其偏移值来适当移动其数据项的过程就很简单了

    下面是一个使用您的3个测试用例的完整示例。上面的步骤1-4包含在
    \uuuu device\uuuu
    函数
    mydelta
    中。内核的其余部分将执行步骤5洗牌,适当地索引到数据中,并复制数据。由于使用了warp shuffle指令,我们必须编译它对于cc3.0或更高版本的GPU。(但是,用其他索引代码替换warp shuffle指令并不困难,这样可以在cc2.0或更高版本的设备上运行。)此外,由于使用了各种内部函数,此函数不能用于32个以上的数据项,但这是问题中所述的先决条件

    $ cat t475.cu
    #include <stdio.h>
    #define DSIZE 8
    
    #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)
    
    
    __device__ int mydelta(const int shift){
      unsigned nz = __ballot(shift == 0);
      unsigned mylane = (threadIdx.x & 31);
      unsigned lanebit = 1<<mylane;
      unsigned temp = nz + lanebit;
      temp = nz ^ temp;
      unsigned delta = __popc(temp);
      return delta-1;
    }
    __global__ void mykernel(const int *data, const unsigned *shift, int *result, const int limit){ // limit <= 32
      if (threadIdx.x < limit){
        unsigned lshift = shift[(limit - 1) - threadIdx.x];
        unsigned delta = mydelta(lshift);
        unsigned myshift = __shfl_down(lshift, delta);
        myshift = __shfl(myshift, ((limit -1) - threadIdx.x)); // reverse offset pattern
        result[threadIdx.x] = 0;
        if ((myshift + threadIdx.x) < limit)
        result[threadIdx.x + myshift] = data[threadIdx.x];
      }
    }
    
    int main(){
      int A[DSIZE]         = {3, 6, 7, 8, 1, 2, 3, 5};
      unsigned tc1B[DSIZE] = {0, 1, 0, 0, 0, 0, 0, 0};
      unsigned tc2B[DSIZE] = {0, 1, 0, 0, 2, 0, 0, 0};
      unsigned tc3B[DSIZE] = {0, 1, 0, 0, 1, 0, 0, 0};
    
      int *d_data, *d_result, *h_result;
      unsigned *d_shift;
      h_result = (int *)malloc(DSIZE*sizeof(int));
      if (h_result == NULL) { printf("malloc fail\n"); return 1;}
      cudaMalloc(&d_data, DSIZE*sizeof(int));
      cudaMalloc(&d_shift, DSIZE*sizeof(unsigned));
      cudaMalloc(&d_result, DSIZE*sizeof(int));
      cudaCheckErrors("cudaMalloc fail");
      cudaMemcpy(d_data, A, DSIZE*sizeof(int), cudaMemcpyHostToDevice);
      cudaMemcpy(d_shift, tc1B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("index: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", i);
      printf("\nA:     ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", A[i]);
      printf("\ntc1 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc1B[i]);
      printf("\ntc1 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      cudaMemcpy(d_shift, tc2B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("\ntc2 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc2B[i]);
      printf("\ntc2 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      cudaMemcpy(d_shift, tc3B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("\ntc3 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc3B[i]);
      printf("\ntc2 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      printf("\n");
      return 0;
    }
    $ nvcc -arch=sm_35 -o t475 t475.cu
    $ ./t475
    index: 0, 1, 2, 3, 4, 5, 6, 7,
    A:     3, 6, 7, 8, 1, 2, 3, 5,
    tc1 B: 0, 1, 0, 0, 0, 0, 0, 0,
    tc1 C: 3, 0, 6, 7, 8, 1, 2, 3,
    tc2 B: 0, 1, 0, 0, 2, 0, 0, 0,
    tc2 C: 3, 0, 6, 7, 8, 0, 1, 2,
    tc3 B: 0, 1, 0, 0, 1, 0, 0, 0,
    tc2 C: 3, 0, 6, 7, 8, 1, 2, 3,
    $
    
    $cat t475.cu
    #包括
    #定义DSIZE 8
    #定义cudaCheckErrors(msg)\
    做{\
    cudaError\u t\u err=cudaGetLastError()\
    如果(_err!=cudaSuccess){\
    fprintf(标准,“致命错误:%s(%s位于%s:%d)\n”\
    msg,cudaGetErrorString(_err)\
    __文件(行)\
    fprintf(stderr,“***失败-中止\n”)\
    出口(1)\
    } \
    }而(0)
    __设备_uuint mydelta(常数int移位){
    未签署的新西兰=无记名投票(shift=0);
    无符号mylane=(threadIdx.x&31);
    
    unsigned lanebit=1I我认为您的示例中存在一些歧义。假设在上一个示例中,我使用了
    b0,1,0,0,0,0
    ,是输出(
    C
    )有什么不同吗?如果没有,对于
    b0,2,1,0,0,0,0,0,0
    ,输出数组应该是什么样子?当然没有我建议的模糊性,这个问题很容易用
    B
    上的包含前缀和来解决。第一个问题:没有区别,第二个问题我忘了提到。不会发生的,数组A是“排序”的为了保证这一点。我觉得前缀和是一个解决方案,但我搞不懂。我不习惯考虑这种并行算法。非常感谢你的时间和努力!
    $ cat t475.cu
    #include <stdio.h>
    #define DSIZE 8
    
    #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)
    
    
    __device__ int mydelta(const int shift){
      unsigned nz = __ballot(shift == 0);
      unsigned mylane = (threadIdx.x & 31);
      unsigned lanebit = 1<<mylane;
      unsigned temp = nz + lanebit;
      temp = nz ^ temp;
      unsigned delta = __popc(temp);
      return delta-1;
    }
    __global__ void mykernel(const int *data, const unsigned *shift, int *result, const int limit){ // limit <= 32
      if (threadIdx.x < limit){
        unsigned lshift = shift[(limit - 1) - threadIdx.x];
        unsigned delta = mydelta(lshift);
        unsigned myshift = __shfl_down(lshift, delta);
        myshift = __shfl(myshift, ((limit -1) - threadIdx.x)); // reverse offset pattern
        result[threadIdx.x] = 0;
        if ((myshift + threadIdx.x) < limit)
        result[threadIdx.x + myshift] = data[threadIdx.x];
      }
    }
    
    int main(){
      int A[DSIZE]         = {3, 6, 7, 8, 1, 2, 3, 5};
      unsigned tc1B[DSIZE] = {0, 1, 0, 0, 0, 0, 0, 0};
      unsigned tc2B[DSIZE] = {0, 1, 0, 0, 2, 0, 0, 0};
      unsigned tc3B[DSIZE] = {0, 1, 0, 0, 1, 0, 0, 0};
    
      int *d_data, *d_result, *h_result;
      unsigned *d_shift;
      h_result = (int *)malloc(DSIZE*sizeof(int));
      if (h_result == NULL) { printf("malloc fail\n"); return 1;}
      cudaMalloc(&d_data, DSIZE*sizeof(int));
      cudaMalloc(&d_shift, DSIZE*sizeof(unsigned));
      cudaMalloc(&d_result, DSIZE*sizeof(int));
      cudaCheckErrors("cudaMalloc fail");
      cudaMemcpy(d_data, A, DSIZE*sizeof(int), cudaMemcpyHostToDevice);
      cudaMemcpy(d_shift, tc1B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("index: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", i);
      printf("\nA:     ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", A[i]);
      printf("\ntc1 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc1B[i]);
      printf("\ntc1 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      cudaMemcpy(d_shift, tc2B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("\ntc2 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc2B[i]);
      printf("\ntc2 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      cudaMemcpy(d_shift, tc3B, DSIZE*sizeof(unsigned), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMempcyH2D fail");
      mykernel<<<1,32>>>(d_data, d_shift, d_result, DSIZE);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(h_result, d_result, DSIZE*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMempcyD2H fail");
      printf("\ntc3 B: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", tc3B[i]);
      printf("\ntc2 C: ");
      for (int i = 0; i < DSIZE; i++)
        printf("%d, ", h_result[i]);
      printf("\n");
      return 0;
    }
    $ nvcc -arch=sm_35 -o t475 t475.cu
    $ ./t475
    index: 0, 1, 2, 3, 4, 5, 6, 7,
    A:     3, 6, 7, 8, 1, 2, 3, 5,
    tc1 B: 0, 1, 0, 0, 0, 0, 0, 0,
    tc1 C: 3, 0, 6, 7, 8, 1, 2, 3,
    tc2 B: 0, 1, 0, 0, 2, 0, 0, 0,
    tc2 C: 3, 0, 6, 7, 8, 0, 1, 2,
    tc3 B: 0, 1, 0, 0, 1, 0, 0, 0,
    tc2 C: 3, 0, 6, 7, 8, 1, 2, 3,
    $