我试图在C中使用带有CUDA 6和统一内存的流.我之前的流实现看起来像这样:
for(x=0; x<DSIZE; x+=N*2){
gpuErrchk(cudaMemcpyAsync(array_d0, array_h+x, N*sizeof(char), cudaMemcpyHostToDevice, stream0));
gpuErrchk(cudaMemcpyAsync(array_d1, array_h+x+N, N*sizeof(char), cudaMemcpyHostToDevice, stream1));
gpuErrchk(cudaMemcpyAsync(data_d0, data_h, wrap->size*sizeof(int), cudaMemcpyHostToDevice, stream0));
gpuErrchk(cudaMemcpyAsync(data_d1, data_h, wrap->size*sizeof(int), cudaMemcpyHostToDevice, stream1));
searchGPUModified<<<N/128,128,0,stream0>>>(data_d0, array_d0, out_d0 );
searchGPUModified<<<N/128,128,0,stream1>>>(data_d1, array_d1, out_d1);
gpuErrchk(cudaMemcpyAsync(out_h+x, out_d0 , N * sizeof(int), cudaMemcpyDeviceToHost, stream0));
gpuErrchk(cudaMemcpyAsync(out_h+x+N, out_d1 ,N * sizeof(int), cudaMemcpyDeviceToHost, stream1));
}
Run Code Online (Sandbox Code Playgroud)
但我找不到流和统一内存的例子,使用相同的技术,将数据块发送到GPU.我想知道是否有办法做到这一点?
您应该阅读编程指南的J.2.2节(最好是附录J的全部内容).
对于统一内存,使用的内存cudaMallocManaged默认附加到所有流("全局"),我们必须修改它以便有效地使用流,例如计算/复制重叠.我们可以使用cudaStreamAttachMemAsyncJ.2.2.3节中描述的功能执行此操作.通过以这种方式将每个内存"块"与流相关联,UM子系统可以做出关于何时传输每个数据项的智能决策.
以下示例演示了这一点:
#include <stdio.h>
#include <time.h>
#define DSIZE 1048576
#define DWAIT 100000ULL
#define nTPB 256
#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)
typedef int mytype;
__global__ void mykernel(mytype *data){
int idx = threadIdx.x+blockDim.x*blockIdx.x;
if (idx < DSIZE) data[idx] = 1;
unsigned long long int tstart = clock64();
while (clock64() < tstart + DWAIT);
}
int main(){
mytype *data1, *data2, *data3;
cudaStream_t stream1, stream2, stream3;
cudaMallocManaged(&data1, DSIZE*sizeof(mytype));
cudaMallocManaged(&data2, DSIZE*sizeof(mytype));
cudaMallocManaged(&data3, DSIZE*sizeof(mytype));
cudaCheckErrors("cudaMallocManaged fail");
cudaStreamCreate(&stream1);
cudaStreamCreate(&stream2);
cudaStreamCreate(&stream3);
cudaCheckErrors("cudaStreamCreate fail");
cudaStreamAttachMemAsync(stream1, data1);
cudaStreamAttachMemAsync(stream2, data2);
cudaStreamAttachMemAsync(stream3, data3);
cudaDeviceSynchronize();
cudaCheckErrors("cudaStreamAttach fail");
memset(data1, 0, DSIZE*sizeof(mytype));
memset(data2, 0, DSIZE*sizeof(mytype));
memset(data3, 0, DSIZE*sizeof(mytype));
mykernel<<<(DSIZE+nTPB-1)/nTPB, nTPB, 0, stream1>>>(data1);
mykernel<<<(DSIZE+nTPB-1)/nTPB, nTPB, 0, stream2>>>(data2);
mykernel<<<(DSIZE+nTPB-1)/nTPB, nTPB, 0, stream3>>>(data3);
cudaDeviceSynchronize();
cudaCheckErrors("kernel fail");
for (int i = 0; i < DSIZE; i++){
if (data1[i] != 1) {printf("data1 mismatch at %d, should be: %d, was: %d\n", i, 1, data1[i]); return 1;}
if (data2[i] != 1) {printf("data2 mismatch at %d, should be: %d, was: %d\n", i, 1, data2[i]); return 1;}
if (data3[i] != 1) {printf("data3 mismatch at %d, should be: %d, was: %d\n", i, 1, data3[i]); return 1;}
}
printf("Success!\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的程序创建了一个人工长时间运行的内核clock64(),以便为我们提供计算/复制重叠的模拟机会(模拟计算密集型内核).我们正在启动这个内核的3个实例,每个实例都在一个单独的"数据块"上运行.
当我们对上述程序进行概述时,可以看到以下内容:
首先,注意,第三内核启动以黄色突出显示,它开始立即之后的第二内核启动紫色突出.cudaLaunch启动此第3个内核的实际运行时API事件在运行时API行中由鼠标指针指示,也以黄色突出显示(前面是前2个内核的cudaLaunch事件).由于此启动发生在第一个内核的执行期间,并且从该点到第三个内核的启动之间没有中间的"空白空间",我们可以观察到第三个内核启动(即data3)的数据传输发生在内核1和2正在执行.因此,我们有复制和计算的有效重叠.(我们可以对内核2进行类似的观察).
虽然我没有在这里显示,但是如果省略这些cudaStreamAttachMemAsync行,程序仍然可以编译并正确运行,但是如果我们对它进行分析,我们会观察到cudaLaunch事件和内核之间的不同关系.整个配置文件看起来很相似,并且内核正在执行背靠背,但整个cudaLaunch进程现在在第一个内核开始执行之前开始和结束,并且在内核执行期间没有cudaLaunch事件.这表明(因为所有cudaMallocManaged内存都是全局的)所有数据传输都在第一次内核启动之前进行.程序无法将"全局"分配与任何特定内核相关联,因此所有这样分配的内存必须在第一次内核启动之前传输(即使该内核仅使用data1).
| 归档时间: |
|
| 查看次数: |
2073 次 |
| 最近记录: |