CUDA实例练习:多个cuda流

Posted Jason&Hymer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CUDA实例练习:多个cuda流相关的知识,希望对你有一定的参考价值。

  1 #include <stdio.h>
  2 #include <cuda_runtime.h>
  3 #include <device_launch_parameters.h>
  4 #include "book.h"
  5 
  6 #define N (1024*1024)
  7 #define FULL_DATA_SIZE (N * 20)
  8 
  9 __global__ void kernel(int *a, int *b, int *c){
 10     int idx = threadIdx.x + blockIdx.x * blockDim.x;
 11     if (idx < N){
 12         int idx1 = (idx + 1) % 256;
 13         int idx2 = (idx + 2) % 256;
 14         float as = (a[idx] + a[idx1] + a[idx2]) / 3.0f;
 15         float bs = (b[idx] + b[idx1] + b[idx2]) / 3.0f;
 16         c[idx] = (as + bs) / 2;
 17     }
 18 }
 19 
 20 int main(void){
 21     cudaDeviceProp prop;
 22     int whichDevice;
 23     HANDLE_ERROR(cudaGetDevice(&whichDevice));
 24     HANDLE_ERROR(cudaGetDeviceProperties(&prop, whichDevice));
 25     if (!prop.deviceOverlap){
 26         printf("Device will not handle overlaps, so no speed up from streams\\n");
 27         return 0;
 28     }
 29     cudaEvent_t start, stop;
 30     float elapsedTime;
 31 
 32     //启动计时器
 33     HANDLE_ERROR(cudaEventCreate(&start));
 34     HANDLE_ERROR(cudaEventCreate(&stop));
 35     HANDLE_ERROR(cudaEventRecord(start, 0));
 36 
 37     //初始化流
 38     cudaStream_t stream0, stream1;
 39     HANDLE_ERROR(cudaStreamCreate(&stream0));
 40     HANDLE_ERROR(cudaStreamCreate(&stream1));
 41 
 42     int *host_a, *host_b, *host_c;
 43     int *dev_a0, *dev_b0, *dev_c0;//为第0个流分配的GPU内存
 44     int *dev_a1, *dev_b1, *dev_c1;//为第1个流分配的GPU内存
 45 
 46     //在GPU上分配内存
 47     HANDLE_ERROR(cudaMalloc((void **)&dev_a0, N * sizeof(int)));
 48     HANDLE_ERROR(cudaMalloc((void **)&dev_b0, N * sizeof(int)));
 49     HANDLE_ERROR(cudaMalloc((void **)&dev_c0, N * sizeof(int)));
 50     HANDLE_ERROR(cudaMalloc((void **)&dev_a1, N * sizeof(int)));
 51     HANDLE_ERROR(cudaMalloc((void **)&dev_b1, N * sizeof(int)));
 52     HANDLE_ERROR(cudaMalloc((void **)&dev_c1, N * sizeof(int)));
 53 
 54     //分配在流中使用的页锁定内存
 55     HANDLE_ERROR(cudaHostAlloc((void **)&host_a, FULL_DATA_SIZE * sizeof(int),
 56         cudaHostAllocDefault));
 57     HANDLE_ERROR(cudaHostAlloc((void **)&host_b, FULL_DATA_SIZE * sizeof(int),
 58         cudaHostAllocDefault));
 59     HANDLE_ERROR(cudaHostAlloc((void **)&host_c, FULL_DATA_SIZE * sizeof(int),
 60         cudaHostAllocDefault));
 61 
 62     for (int i = 0; i < FULL_DATA_SIZE; i++){
 63         host_a[i] = rand();
 64         host_b[i] = rand();
 65     }
 66 
 67     //在整体数据上循环,每个数据块的大小为N
 68     for (int i = 0; i < FULL_DATA_SIZE; i += N * 2){
 69         //将锁定内存以异步方式复制到设备上
 70         HANDLE_ERROR(cudaMemcpyAsync(dev_a0, host_a + i, N * sizeof(int),
 71             cudaMemcpyHostToDevice, stream0));
 72         HANDLE_ERROR(cudaMemcpyAsync(dev_b0, host_b + i, N * sizeof(int),
 73             cudaMemcpyHostToDevice, stream0));
 74         kernel << <N / 256, 256, 0, stream0 >> >(dev_a0, dev_b0, dev_c0);
 75 
 76         //将数据从设备复制回锁定内存
 77         HANDLE_ERROR(cudaMemcpyAsync(host_c + i, dev_c0, N * sizeof(int),
 78             cudaMemcpyDeviceToHost, stream0));
 79 
 80         //将锁定内存以异步方式复制到设备上
 81         HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a + i + N, N* sizeof(int),
 82             cudaMemcpyHostToDevice, stream1));
 83         HANDLE_ERROR(cudaMemcpyAsync(dev_b1, host_b + i + N, N * sizeof(int),
 84             cudaMemcpyHostToDevice, stream1));
 85         kernel << <N / 256, 256, 0, stream1 >> >(dev_a1, dev_b1, dev_c1);
 86 
 87         //将数据从设备复制回到锁定内存
 88         HANDLE_ERROR(cudaMemcpyAsync(host_c + i + N, dev_c1, N * sizeof(int),
 89             cudaMemcpyDeviceToHost, stream1));
 90     }
 91 
 92     //在停止应用程序的计时器之前,首先将两个流进行同步
 93     HANDLE_ERROR(cudaStreamSynchronize(stream0));
 94     HANDLE_ERROR(cudaStreamSynchronize(stream1));
 95     HANDLE_ERROR(cudaEventRecord(stop, 0));
 96     HANDLE_ERROR(cudaEventSynchronize(stop));
 97     HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime, start, stop));
 98     printf("Time taken: %3.1f ms\\n", elapsedTime);
 99 
100     //释放流和内存
101     HANDLE_ERROR(cudaFreeHost(host_a));
102     HANDLE_ERROR(cudaFreeHost(host_b));
103     HANDLE_ERROR(cudaFreeHost(host_c));
104     HANDLE_ERROR(cudaFree(dev_a0));
105     HANDLE_ERROR(cudaFree(dev_b0));
106     HANDLE_ERROR(cudaFree(dev_c0));
107     HANDLE_ERROR(cudaFree(dev_a1));
108     HANDLE_ERROR(cudaFree(dev_b1));
109     HANDLE_ERROR(cudaFree(dev_c1));
110     HANDLE_ERROR(cudaStreamDestroy(stream0));
111     HANDLE_ERROR(cudaStreamDestroy(stream1));
112 
113     return 0;
114 
115 
116 
117 }

 

如果同时调度某个流的所有操作,那么很容易在无意中阻塞另一个流的复制操作或者核函数执行。要解决这个问题,在将操作放入流的队列时应采用宽度优先方式,而非深度优先方式。

  1 #include <stdio.h>
  2 #include <cuda_runtime.h>
  3 #include <device_launch_parameters.h>
  4 #include "book.h"
  5 
  6 #define N (1024*1024)
  7 #define FULL_DATA_SIZE (N * 20)
  8 
  9 __global__ void kernel(int *a, int *b, int *c){
 10     int idx = threadIdx.x + blockIdx.x * blockDim.x;
 11     if (idx < N){
 12         int idx1 = (idx + 1) % 256;
 13         int idx2 = (idx + 2) % 256;
 14         float as = (a[idx] + a[idx1] + a[idx2]) / 3.0f;
 15         float bs = (b[idx] + b[idx1] + b[idx2]) / 3.0f;
 16         c[idx] = (as + bs) / 2;
 17     }
 18 }
 19 
 20 int main(void){
 21     cudaDeviceProp prop;
 22     int whichDevice;
 23     HANDLE_ERROR(cudaGetDevice(&whichDevice));
 24     HANDLE_ERROR(cudaGetDeviceProperties(&prop, whichDevice));
 25     if (!prop.deviceOverlap){
 26         printf("Device will not handle overlaps, so no speed up from streams\\n");
 27         return 0;
 28     }
 29     cudaEvent_t start, stop;
 30     float elapsedTime;
 31 
 32     //启动计时器
 33     HANDLE_ERROR(cudaEventCreate(&start));
 34     HANDLE_ERROR(cudaEventCreate(&stop));
 35     HANDLE_ERROR(cudaEventRecord(start, 0));
 36 
 37     //初始化流
 38     cudaStream_t stream0, stream1;
 39     HANDLE_ERROR(cudaStreamCreate(&stream0));
 40     HANDLE_ERROR(cudaStreamCreate(&stream1));
 41 
 42     int *host_a, *host_b, *host_c;
 43     int *dev_a0, *dev_b0, *dev_c0;//为第0个流分配的GPU内存
 44     int *dev_a1, *dev_b1, *dev_c1;//为第1个流分配的GPU内存
 45 
 46     //在GPU上分配内存
 47     HANDLE_ERROR(cudaMalloc((void **)&dev_a0, N * sizeof(int)));
 48     HANDLE_ERROR(cudaMalloc((void **)&dev_b0, N * sizeof(int)));
 49     HANDLE_ERROR(cudaMalloc((void **)&dev_c0, N * sizeof(int)));
 50     HANDLE_ERROR(cudaMalloc((void **)&dev_a1, N * sizeof(int)));
 51     HANDLE_ERROR(cudaMalloc((void **)&dev_b1, N * sizeof(int)));
 52     HANDLE_ERROR(cudaMalloc((void **)&dev_c1, N * sizeof(int)));
 53 
 54     //分配在流中使用的页锁定内存
 55     HANDLE_ERROR(cudaHostAlloc((void **)&host_a, FULL_DATA_SIZE * sizeof(int),
 56         cudaHostAllocDefault));
 57     HANDLE_ERROR(cudaHostAlloc((void **)&host_b, FULL_DATA_SIZE * sizeof(int),
 58         cudaHostAllocDefault));
 59     HANDLE_ERROR(cudaHostAlloc((void **)&host_c, FULL_DATA_SIZE * sizeof(int),
 60         cudaHostAllocDefault));
 61 
 62     for (int i = 0; i < FULL_DATA_SIZE; i++){
 63         host_a[i] = rand();
 64         host_b[i] = rand();
 65     }
 66 
 67     //在整体数据上循环,每个数据块的大小为N
 68     for (int i = 0; i<FULL_DATA_SIZE; i += N * 2) {
 69         // enqueue copies of a in stream0 and stream1
 70         HANDLE_ERROR(cudaMemcpyAsync(dev_a0, host_a + i,
 71             N * sizeof(int),
 72             cudaMemcpyHostToDevice,
 73             stream0));
 74         HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a + i + N,
 75             N * sizeof(int),
 76             cudaMemcpyHostToDevice,
 77             stream1));
 78         // enqueue copies of b in stream0 and stream1
 79         HANDLE_ERROR(cudaMemcpyAsync(dev_b0, host_b + i,
 80             N * sizeof(int),
 81             cudaMemcpyHostToDevice,
 82             stream0));
 83         HANDLE_ERROR(cudaMemcpyAsync(dev_b1, host_b + i + N,
 84             N * sizeof(int),
 85             cudaMemcpyHostToDevice,
 86             stream1));
 87 
 88         // enqueue kernels in stream0 and stream1   
 89         kernel << <N / 256, 256, 0, stream0 >> >(dev_a0, dev_b0, dev_c0);
 90         kernel << <N / 256, 256, 0, stream1 >> >(dev_a1, dev_b1, dev_c1);
 91 
 92         // enqueue copies of c from device to locked memory
 93         HANDLE_ERROR(cudaMemcpyAsync(host_c + i, dev_c0,
 94             N * sizeof(int),
 95             cudaMemcpyDeviceToHost,
 96             stream0));
 97         HANDLE_ERROR(cudaMemcpyAsync(host_c + i + N, dev_c1,
 98             N * sizeof(int),
 99             cudaMemcpyDeviceToHost,
100             stream1));
101     }
102 
103 
104     //在停止应用程序的计时器之前,首先将两个流进行同步
105     HANDLE_ERROR(cudaStreamSynchronize(stream0));
106     HANDLE_ERROR(cudaStreamSynchronize(stream1));
107     HANDLE_ERROR(cudaEventRecord(stop, 0));
108     HANDLE_ERROR(cudaEventSynchronize(stop));
109     HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime, start, stop));
110     printf("Time taken: %3.1f ms\\n", elapsedTime);
111 
112     //释放流和内存
113     HANDLE_ERROR(cudaFreeHost(host_a));
114     HANDLE_ERROR(cudaFreeHost(host_b));
115     HANDLE_ERROR(cudaFreeHost(host_c));
116     HANDLE_ERROR(cudaFree(dev_a0));
117     HANDLE_ERROR(cudaFree(dev_b0));
118     HANDLE_ERROR(cudaFree(dev_c0));
119     HANDLE_ERROR(cudaFree(dev_a1));
120     HANDLE_ERROR(cudaFree(dev_b1));
121     HANDLE_ERROR(cudaFree(dev_c1));
122     HANDLE_ERROR(cudaStreamDestroy(stream0));
123     HANDLE_ERROR(cudaStreamDestroy(stream1));
124 
125     return 0;
126 
127 
128 
129 }

 

以上是关于CUDA实例练习:多个cuda流的主要内容,如果未能解决你的问题,请参考以下文章

CUDA实例练习:零拷贝内存

CUDA实例练习:页锁定主机内存

在 cuda 中同步多个设备

流处理器和CUDA

cudaGraph:多线程流捕获仅在 cuda-memcheck 中运行时才会导致错误

cuda 与 cupy 和 tensorRT 的流同步问题