cuda流的使用,結合nsight查看時間線

今天是6月10號,記得自己是去年這個時候開始接觸CUDA的,目前只會寫一點簡單的程序,對順程序的優化設計,還是一個小萌新,我在實驗室的項目裏面,看到的CUDA 程序使用了多個流的設計,但是它的時間線卻讓我匪夷所思,爲什麼不是並行的呢?帶着這個疑惑,我決定在最近這段時間,通過Google,搞明白這個問題,然後分享給大家,希望有朝一日,碰巧會有一個少年,碰巧也是通信CS 領域,碰巧也要用CUDA做加速計算,碰巧看到了我這篇文章,那麼,我的所作所爲就有意義了,幫助他節省了一段時間,最重要的,在他剛開始接觸這個領域的時候,給了他一股學下去的興趣~

1.cuda單個流的應用

話不多說,先看一個Nsight裏面的Timeline

在這裏插入圖片描述
圖中Streams裏面表示了一個流裏面的執行順序,基本上就是Memory和Compute的結合,畢竟,拷貝數據和計算,就是我們想要完成的事;
這是一個完成數組相加的cuda程序,基本的思路是“拷貝a到GPU,拷貝b到GPU,計算,拷貝c到CPU”
在這裏插入圖片描述
核函數如下,每次可以算1024個點,採用4個block,每個block256個thread,一個thread算一個點;

__global__ void kernel(int *a, int *b, int *c)
{
 int idx = threadIdx.x + blockDim.x * blockIdx.x;
 if (idx < N)
 {
  c[idx] = a[idx] + b[idx];
 }
}

然後,現在我把數組的大小設置爲1024*4,就需要核函數執行四次,從而算出結果;這是一個有着先後順序的任所以,將這些任務順序的丟到流裏面,就會按照順序進行下去;
(本人使用的是cuda10.2+vs2015)
第一部分完整代碼:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>
#include <stdlib.h>

#define N 1024*1024
__global__ void kernel(int *a, int *b, int *c)
{
	int idx = threadIdx.x + blockDim.x * blockIdx.x;
	if (idx < N)
	{
		c[idx] = a[idx] + b[idx];
	}
}
int main()
{
	//查詢設備是否支持overlap
	cudaDeviceProp prop;
	int whichDevice;
	cudaGetDevice(&whichDevice);
	cudaGetDeviceProperties(&prop, whichDevice);
	if (!prop.deviceOverlap)
	{
		printf("Devices will not handle overlaps");
		return 0;
	}

	int FULL_DATA_SIZE = N * 4;

	cudaEvent_t start, stop;
	float elapsedTime;
	//啓動計時器
	cudaEventCreate(&start);
	cudaEventCreate(&stop);
	cudaEventRecord(start, 0);
	//初始化流
	cudaStream_t stream;
	cudaStreamCreate(&stream);
	//數據分配操作
	int *host_a, *host_b, *host_c;
	int *dev_a, *dev_b, *dev_c;
	//在 GPU 上分配內存
	cudaMalloc((void**)&dev_a, N * sizeof(int));
	cudaMalloc((void**)&dev_b, N * sizeof(int));
	cudaMalloc((void**)&dev_c, N * sizeof(int));
	//在 CPU 上分配內存,是流使用的頁鎖定內存
	cudaHostAlloc((void**)&host_a, FULL_DATA_SIZE * sizeof(int), cudaHostAllocDefault);
	cudaHostAlloc((void**)&host_b, FULL_DATA_SIZE * sizeof(int), cudaHostAllocDefault);
	cudaHostAlloc((void**)&host_c, FULL_DATA_SIZE * sizeof(int), cudaHostAllocDefault);
	//隨機數填充主機內存
	for (int i = 0; i < FULL_DATA_SIZE; i++)
	{
		host_a[i] = rand();
		host_b[i] = rand();
	}
	//在整體數據上循環,每個數據塊的大小是 N 
	for (int i = 0; i < FULL_DATA_SIZE; i += N)
	{
		//將鎖定內存以異步方式複製到設備上
		cudaMemcpyAsync(dev_a, host_a + i, N * sizeof(int), cudaMemcpyHostToDevice, stream);
		cudaMemcpyAsync(dev_b, host_b + i, N * sizeof(int), cudaMemcpyHostToDevice, stream);
		kernel << <N / 256, 256, 0, stream >> > (dev_a, dev_b, dev_c);
		//將數據複製到鎖定內存中
		cudaMemcpyAsync(host_c + i, dev_c, N * sizeof(int), cudaMemcpyDeviceToHost, stream);
	}
	//同步
	cudaStreamSynchronize(stream);
	//
	cudaEventRecord(stop, 0);
	cudaEventSynchronize(stop);
	cudaEventElapsedTime(&elapsedTime, start, stop);
	//釋放流和內存
	cudaFreeHost(host_a);
	cudaFreeHost(host_b);
	cudaFreeHost(host_c);
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);
	//銷燬流
	cudaStreamDestroy(stream);
	return 0;
}

2020.6.17更新

2.cuda多個流的應用

首先,可以通過cudaSample查詢一下GPU支持幾個流;
打開這個目錄下的vs,
“…\ProgramData\NVIDIA Corporation\CUDA Samples\v10.2\1_Utilities\deviceQuery”
在這裏插入圖片描述
圖中的MultiProcessor代表實際可以使用的流的個數

突然接到了項目任務,這一塊以後再更新吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章