【问题标题】:Sorting many small arrays in CUDA在 CUDA 中对许多小数组进行排序
【发布时间】:2014-04-15 21:41:21
【问题描述】:

我正在 CUDA 中实现中值滤波器。对于一个特定的像素,我提取了与该像素周围的一个窗口相对应的邻居,比如一个N x N (3 x 3) 窗口,现在有一个N x N 元素数组。我不打算为我的应用程序使用超过10 x 10 元素的窗口。

这个数组现在本地存在于内核中,并且已经加载到设备内存中。从我读过的以前的 SO 帖子中,最常见的排序算法是由 Thrust 实现的。但是,只能从主机调用 Thrust。线程 - Thrust inside user written kernels

有没有一种快速有效的方法来对内核中的一小部分N x N 元素进行排序?

【问题讨论】:

  • 您可以调整cub block sort。排序本身可能可以直接使用。你必须想出自己的例程来将数据复制到排序输入数组,然后适当地处理输出。
  • 或者该函数只是将数组window的排序元素存储在同一个数组中?

标签: sorting cuda cub


【解决方案1】:

如果您使用的是 CUDA 5.X,则可以使用动态并行。您可以在过滤器内核中创建一些子内核来完成排序工作。至于如何按CUDA排序,可以使用一些归纳技巧。

【讨论】:

    【解决方案2】:

    如果元素数量固定且很少,可以使用排序网络(http://pages.ripco.net/~jgamble/nw.html)。它为固定数量的元素提供固定数量的比较/交换操作(例如,8 个元素的 19 次比较/交换迭代)。

    【讨论】:

      【解决方案3】:

      您的问题是在 CUDA 中对许多小数组进行排序

      根据 Robert 在评论中的建议,CUB 提供了解决此问题的可能解决方案。下面我报告一个围绕罗伯特的代码构建的示例cub BlockRadixSort: how to deal with large tile size or sort multiple tiles?

      这个想法是将要排序的小数组分配给不同的线程块,然后使用cub::BlockRadixSort 对每个数组进行排序。提供了两个版本,一个加载,一个加载小数组到共享内存。

      最后让我注意到,您关于 CUDA Thrust 不能从内核中调用的说法不再正确。您链接到的帖子 Thrust inside user written kernels 已更新为其他答案。

      #include <cub/cub.cuh>
      #include <stdio.h>
      #include <stdlib.h>
      
      #include "Utilities.cuh"
      
      using namespace cub;
      
      /**********************************/
      /* CUB BLOCKSORT KERNEL NO SHARED */
      /**********************************/
      template <int BLOCK_THREADS, int ITEMS_PER_THREAD>
      __global__ void BlockSortKernel(int *d_in, int *d_out)
      {
          // --- Specialize BlockLoad, BlockStore, and BlockRadixSort collective types
          typedef cub::BlockLoad      <int*, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_LOAD_TRANSPOSE>   BlockLoadT;
          typedef cub::BlockStore     <int*, BLOCK_THREADS, ITEMS_PER_THREAD, BLOCK_STORE_TRANSPOSE>  BlockStoreT;
          typedef cub::BlockRadixSort <int , BLOCK_THREADS, ITEMS_PER_THREAD>                         BlockRadixSortT;
      
          // --- Allocate type-safe, repurposable shared memory for collectives
          __shared__ union {
              typename BlockLoadT     ::TempStorage load;
              typename BlockStoreT    ::TempStorage store;
              typename BlockRadixSortT::TempStorage sort;
          } temp_storage;
      
          // --- Obtain this block's segment of consecutive keys (blocked across threads)
          int thread_keys[ITEMS_PER_THREAD];
          int block_offset = blockIdx.x * (BLOCK_THREADS * ITEMS_PER_THREAD);
      
          BlockLoadT(temp_storage.load).Load(d_in + block_offset, thread_keys);
          __syncthreads(); 
      
          // --- Collectively sort the keys
          BlockRadixSortT(temp_storage.sort).Sort(thread_keys);
          __syncthreads(); 
      
          // --- Store the sorted segment
          BlockStoreT(temp_storage.store).Store(d_out + block_offset, thread_keys);
      
      }
      
      /*******************************/
      /* CUB BLOCKSORT KERNEL SHARED */
      /*******************************/
      template <int BLOCK_THREADS, int ITEMS_PER_THREAD>
      __global__ void shared_BlockSortKernel(int *d_in, int *d_out)
      {
          // --- Shared memory allocation
          __shared__ int sharedMemoryArray[BLOCK_THREADS * ITEMS_PER_THREAD];
      
          // --- Specialize BlockStore and BlockRadixSort collective types
          typedef cub::BlockRadixSort <int , BLOCK_THREADS, ITEMS_PER_THREAD> BlockRadixSortT;
      
          // --- Allocate type-safe, repurposable shared memory for collectives
          __shared__ typename BlockRadixSortT::TempStorage temp_storage;
      
          int block_offset = blockIdx.x * (BLOCK_THREADS * ITEMS_PER_THREAD);
      
          // --- Load data to shared memory
          for (int k = 0; k < ITEMS_PER_THREAD; k++) sharedMemoryArray[threadIdx.x * ITEMS_PER_THREAD + k]  = d_in[block_offset + threadIdx.x * ITEMS_PER_THREAD + k];
          __syncthreads();
      
          // --- Collectively sort the keys
          BlockRadixSortT(temp_storage).Sort(*static_cast<int(*)[ITEMS_PER_THREAD]>(static_cast<void*>(sharedMemoryArray + (threadIdx.x * ITEMS_PER_THREAD))));
          __syncthreads();
      
          // --- Write data to shared memory
          for (int k = 0; k < ITEMS_PER_THREAD; k++) d_out[block_offset + threadIdx.x * ITEMS_PER_THREAD + k] = sharedMemoryArray[threadIdx.x * ITEMS_PER_THREAD + k];
      
      }
      
      /********/
      /* MAIN */
      /********/
      int main() {
      
          const int numElemsPerArray  = 8;
          const int numArrays         = 4;
          const int N                 = numArrays * numElemsPerArray;
          const int numElemsPerThread = 4;
      
          const int RANGE             = N * numElemsPerThread;
      
          // --- Allocating and initializing the data on the host
          int *h_data = (int *)malloc(N * sizeof(int));
          for (int i = 0 ; i < N; i++) h_data[i] = rand() % RANGE;
      
          // --- Allocating the results on the host
          int *h_result1 = (int *)malloc(N * sizeof(int));
          int *h_result2 = (int *)malloc(N * sizeof(int));
      
          // --- Allocating space for data and results on device
          int *d_in;      gpuErrchk(cudaMalloc((void **)&d_in,   N * sizeof(int)));
          int *d_out1;    gpuErrchk(cudaMalloc((void **)&d_out1, N * sizeof(int)));
          int *d_out2;    gpuErrchk(cudaMalloc((void **)&d_out2, N * sizeof(int)));
      
          // --- BlockSortKernel no shared
          gpuErrchk(cudaMemcpy(d_in, h_data, N*sizeof(int), cudaMemcpyHostToDevice));
          BlockSortKernel<N / numArrays / numElemsPerThread, numElemsPerThread><<<numArrays, numElemsPerArray / numElemsPerThread>>>(d_in, d_out1); 
          gpuErrchk(cudaMemcpy(h_result1, d_out1, N*sizeof(int), cudaMemcpyDeviceToHost));
      
          printf("BlockSortKernel no shared\n\n");
          for (int k = 0; k < numArrays; k++) 
              for (int i = 0; i < numElemsPerArray; i++)
                  printf("Array nr. %i; Element nr. %i; Value %i\n", k, i, h_result1[k * numElemsPerArray + i]);
      
          // --- BlockSortKernel with shared
          gpuErrchk(cudaMemcpy(d_in, h_data, N*sizeof(int), cudaMemcpyHostToDevice));
          shared_BlockSortKernel<N / numArrays / numElemsPerThread, numElemsPerThread><<<numArrays, numElemsPerArray / numElemsPerThread>>>(d_in, d_out2); 
          gpuErrchk(cudaMemcpy(h_result2, d_out2, N*sizeof(int), cudaMemcpyDeviceToHost));
      
          printf("\n\nBlockSortKernel with shared\n\n");
          for (int k = 0; k < numArrays; k++) 
              for (int i = 0; i < numElemsPerArray; i++)
                  printf("Array nr. %i; Element nr. %i; Value %i\n", k, i, h_result2[k * numElemsPerArray + i]);
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 2013-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-16
        • 2017-11-09
        • 2023-03-23
        • 2013-03-11
        • 1970-01-01
        相关资源
        最近更新 更多