使用循环缓冲区的cuda核心外实现

使用循环缓冲区的cuda核心外实现,cuda,Cuda,我正在尝试在GPU内存和CPU内存之间做内核外的工作。例如,我有每个1GB的数据块,我需要按顺序处理1000个这样的数据块,每个数据块都由内核启动完成。假设处理必须一个接一个地完成,因为第n次内核启动需要使用(n-1)次内核生成的结果,该结果存储在(n-1)次块中,第一次内核启动除外。因此,我考虑在GPU上使用循环缓冲区来存储最近的5个块,并使用事件在数据流和任务流之间进行同步。数据流准备数据,任务流启动内核。代码如下所示 const int N_CBUF = 5, N_TASK = 1000;

我正在尝试在GPU内存和CPU内存之间做内核外的工作。例如,我有每个1GB的数据块,我需要按顺序处理1000个这样的数据块,每个数据块都由内核启动完成。假设处理必须一个接一个地完成,因为第n次内核启动需要使用(n-1)次内核生成的结果,该结果存储在(n-1)次块中,第一次内核启动除外。因此,我考虑在GPU上使用循环缓冲区来存储最近的5个块,并使用事件在数据流和任务流之间进行同步。数据流准备数据,任务流启动内核。代码如下所示

const int N_CBUF = 5, N_TASK = 1000;

// Each pointer points to a data block of 1GB
float* d_cir_buf[N_CBUF];
float* h_data_blocks[N_TASK];

// The data stream for transfering data from host to device.
// The task stream for launching kernels to process the data.
cudaStream_t s_data, s_task;

// The data events for the completion of each data transfer.
// The task events for the completion of each kernel execution.
cudaEvent_t e_data[N_TASK], e_task[N_TASK];

// ... code for creating the streams and events.

for (int i = 0; i < N_TASK; i++) {
  // Data transfer should not overwritten the data needed by the kernels.
  if (i >= N_CBUF) {
    cudaStreamWaitEvent(s_data, e_task[i-N_CBUF+1]);
  }
  cudaMemcpyAsync(d_cir_buf[i % N_CBUF], h_data_blocks[i], ..., cudaMemcpyHostToDevice, s_data);
  cudaEventRecord(e_data[i], s_data);

  cudaStreamWaitEvent(s_task, e_data[i]);

  // Pass the current and the last data block to the kernel.
  my_kernel<<<..., s_task>>>(d_cir_buf[i % N_CBUF], 
    i == 0 ? 0 : d_cir_buf[(i+N_CBUF-1)%N_CBUF]);
  cudaEventRecord(e_task[i], s_task);
}
const int N_CBUF=5,N_TASK=1000;
//每个指针指向1GB的数据块
浮动*d_cir_buf[N_CBUF];
浮点*h_数据块[N_任务];
//用于将数据从主机传输到设备的数据流。
//用于启动内核以处理数据的任务流。
cudaStream_t s_数据,s_任务;
//完成每次数据传输的数据事件。
//完成每个内核执行的任务事件。
cudaEvent_t e_数据[N_任务]、e_任务[N_任务];
// ... 用于创建流和事件的代码。
for(int i=0;i=N_CBUF){
cudaStreamWaitEvent(s_数据,e_任务[i-N_CBUF+1]);
}
cudaMemcpyAsync(d_cir_buf[i%N_CBUF],h_数据块[i],…,cudamemcpyhostodevice,s_数据);
cudaEventRecord(e_数据[i],s_数据);
cudaStreamWaitEvent(s_任务,e_数据[i]);
//将当前和最后一个数据块传递给内核。
我的内核(d_cir_buf[i%N_CBUF],
i==0?0:d_cir_buf[(i+N_CBUF-1)%N_CBUF]);
cudaEventRecord(e_任务[i],s_任务);
}

我想知道这是否是一个有效的想法,或者有什么完全错误的吗?此外,《CUDA编程指南》提到,如果有memcpy从两个不同的主机内存地址到同一个设备地址,那么就不会有并发执行,这在我的情况下重要吗?特别是,如果
d_cir_buf
的内存被分配为一个完整的大块,然后分为5个部分,那么这会算作“设备中相同的内存地址”,导致并发失败吗?此外,在我的例子中,(n+5)次数据传输将转到与第n次数据传输相同的地址,但是,考虑到所需的同步,不会有两次这样的传输同时执行。这样可以吗?

我觉得您的问题最适合双缓冲:

  • 两河
  • 在stream1中上载数据1
  • 在stream1中的data1上运行内核
  • 在stream2中上载数据2
  • 在stream2中的data2上运行内核
。。。等等


stream2中的内核可以与strezm 1中的数据传输重叠,反之亦然

谢谢,当将
N_CBUF
设置为2时,双缓冲是我程序的一个特例,但我需要它至少为3,因为每次内核启动都需要使用2个连续块。我的问题是,这种实现双/三/N元组缓冲的方法正确吗?或者是否存在任何会禁用并发性的问题?据我所知,如果一次可以运行一个内核,则双缓冲是唯一的选择。重叠计算和内存传输是目标,因此您可以让两个内核获得两个以上的元素/块,这是肯定的。这也是双重缓冲。唯一非常重要的是,在为下一步准备数据时,不要触摸正在运行的内核所使用的队列块。我明白了,你是对的,在这种情况下,我不打算让不同的内核同时运行,但我错过了同步,以防发生这种情况。