内核调用期间仅用于设备计算的CUDA内存(类型)(计算1.1或1.2)

时间:2013-04-08 09:52:11

标签: c++ c memory cuda

我正在学习CUDA,我的算法必须根据一些输入数据进行一些繁重的计算。这些计算是在循环中进行的,最多可以旋转1024轮。只要每个内核有少量线程(<100'000),一切正常,但如果我想利用更多线程,内核将被windows中断,因为它需要很长时间才能完成。

我的解决方案是在几个内核调用中分解繁重的计算:

  1. 内核,用于准备输入数据并计算前x轮(循环展开)。每次输入只会调用一次。
  2. 工作内核,执行下一轮x循环(循环展开)。这将根据需要经常调用,以计算所有必需的回合。
  3. 在每次内核调用(一个,多个工作)之间,我必须保存16个长度字节的数据,这将用于下次调用(长度)是输入的长度,它是根据调用固定的。 main 内核将初始写入这些字节, work 内核将读取它们,运行下一次计算并使用新结果写入原始数据。 我只需要设备上的那些数据,不需要主机访问。我必须使用哪种内存?至少它必须是全局内存,因为它是内核调用期间唯一可写的内存,不是吗?但那么,什么? 你能否告诉我如何继续使用正确的记忆(以及最佳表现)?

    在“伪代码”中它可能如下所示:

    prepare memory to hold threads * (16 + length) bytes
    
    for length = 1 to x step 1
      call mainKernel
      rounds = 1024 - rounds_done_in_main
      for rounds to 0 step rounds_done_in_work
        call workKernel
      end for
    end for
    
    cleanup memory
    
    --------
    
    template <unsigned char length> __global__ mainKernel() {
      unsigned char input[length];
      unsigned char output[16];
      const int tid = ...;
    
      devPrepareInput<length>(input);
    
      calc round 1: doSomething<length>(output, input)
      calc round 2: doSomething<length>(output, output + input) // '+' == append
    
      write data to memory based on tid // data == output + input
    }
    
    template <unsigned char length, remaining rounds> __global__ workKernel() {
      unsigned char *input;
      unsigned char *output;
      const int tid = ...;
    
      read data from memory based on tid
      ouput = data
      input = data+16
    
      if rounds >= 1
        calc round x  : doSomething<length>(output, output + input)
      if rounds >= 2
        calc round x+1: doSomething<length>(output, output + input) // '+' == append
    
      if rounds == x // x is the number of rounds in the last work call
        do final steps on output
      else
        write ouput + input to memory based on tid (for next call)
    }
    

1 个答案:

答案 0 :(得分:1)

是的,您可以使用设备内存执行此操作。用__device__声明的变量提供了一个缓冲区的静态声明,可以由内核直接使用,无需任何cudaMemcpy操作,也不需要将指针显式传递给内核。由于它具有lifetime of the application,因此其中的数据将从一个内核调用持续到下一个内核。

#define NUM_THREADS 1024
#define DATA_PER_THREAD 16
__device__ int temp_data[NUM_THREADS*DATA_PER_THREAD];

__global__ my_kernel1(...){
  int my_data[DATA_PER_THREAD] = {0};
  int idx = threadIdx.x + blockDim.x * blockIdx.x;
  // perform calculations

  // write out temp data
  for (int i = 0; i < DATA_PER_THREAD; i++) temp_data[i + (idx * DATA_PER_THREAD)] = my_data[i];
  }

__global__ my_kernel2(...){
  int my_data[DATA_PER_THREAD];
  // read in temp data
  for (int i = 0; i < DATA_PER_THREAD; i++) my_data[i] = temp_data[i + (idx * DATA_PER_THREAD)];
  // perform calculations

  }

您可以通过多种方式根据内核中的使用模式对此进行优化。不需要将数据传输到my_data或从temp_data传输数据。显然,您的内核代码可以直接代替my_data访问for,并使用适当的索引。

如果您决定加载/存储它,您可以交错数据以允许在{{1}}循环读取和写入数据期间进行合并访问。