[초보 자 는 매일 CUDAC]여러 개의 CUDA 흐름 을 사용 하여 프로그램의 실행 효율 을 높 인 다.
cudaMemcpy Async 함수 의 기능 은 GPU 와 호스트 사이 에서 데 이 터 를 복사 하 는 것 입 니 다.이것 은 비동기 함수 입 니 다.즉,함수 가 호출 된 후에 하나의 요청 만 설치 하여 스 트림 에서 메모리 복사 작업 을 수행 하 는 것 을 표시 합 니 다.함수 가 되 돌 아 왔 을 때 복사 작업 이 반드시 시작 되 거나 끝 날 필 요 는 없습니다.이 작업 은 실행 대기 열 에 넣 고 다음 스 트림 에 넣 기 전에 실 행 됩 니 다.
실험 은 한 조 의 데 이 터 를 GPU 로 나 누 어 실행 결 과 를 되 돌려 줌 으로 써 cuda 흐름 을 사용 하면 프로그램의 실행 효율 을 높 일 수 있다 는 것 을 설명 한다.원 리 는 주로 데이터 복제 작업 과 핵 함수 실행 작업 을 교차 집행 하 게 하 는 것 이다.첫 번 째 핵 함수 실행 이 끝 날 때 까지 기 다 렸 다가 두 번 째 데이터 복 제 를 시작 하지 않 아 도 순서 집행 에 따 른 지연 을 줄 일 수 있다(컴 파일 에서 파이프라인 을 사용 하여 충돌 을 해결 하 는 전제 에서 효율 을 높이 는 것 과 유사 하 다).
프로그램 코드 는 다음 과 같 습 니 다:
#include "cuda_runtime.h"
#include "cutil_inline.h"
#include <stdio.h>
#include <math.h>
static void HandleError( cudaError_t err,const char *file,int line )
{
if (err != cudaSuccess)
{
printf( "%s in %s at line %d
", cudaGetErrorString( err ),
file, line );
exit( EXIT_FAILURE );
}
}
#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))
#define N (1024*1024)
#define FULL_DATA_SIZE N*20
__global__ void kernel(int* a, int *b, int*c)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int offset = gridDim.x * blockDim.x;
if (idx < N)
{
int idx1 = (idx + 1) % 256;
int idx2 = (idx + 2) % 256;
float as = (a[idx] + a[idx1] + a[idx2]) / 3;
float bs = (b[idx] + b[idx1] + b[idx2]) / 3;
c[idx] = (as + bs) / 2;
}
}
int main()
{
cudaDeviceProp prop;
int devID;
HANDLE_ERROR(cudaGetDevice(&devID));
HANDLE_ERROR(cudaGetDeviceProperties(&prop, devID));
if (!prop.deviceOverlap)
{
printf("No device will handle overlaps. so no speed up from stream.
");
return 0;
}
cudaEvent_t start, stop;
float elapsedTime;
HANDLE_ERROR(cudaEventCreate(&start));
HANDLE_ERROR(cudaEventCreate(&stop));
HANDLE_ERROR(cudaEventRecord(start, 0));
cudaStream_t stream0;
cudaStream_t stream1;
HANDLE_ERROR(cudaStreamCreate(&stream0));
HANDLE_ERROR(cudaStreamCreate(&stream1));
int *host_a, *host_b, *host_c;
int *dev_a0, *dev_b0, *dev_c0;
int *dev_a1, *dev_b1, *dev_c1;
HANDLE_ERROR(cudaMalloc((void**)&dev_a0, N*sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_b0, N*sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_c0, N*sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_a1, N*sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_b1, N*sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_c1, N*sizeof(int)));
HANDLE_ERROR(cudaHostAlloc((void**)&host_a, FULL_DATA_SIZE*sizeof(int), cudaHostAllocDefault));
HANDLE_ERROR(cudaHostAlloc((void**)&host_b, FULL_DATA_SIZE*sizeof(int), cudaHostAllocDefault));
HANDLE_ERROR(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();
}
// tasks are put into stack for gpu execution
for (int i=0; i<FULL_DATA_SIZE; i+=2*N)
{
HANDLE_ERROR(cudaMemcpyAsync(dev_a0, host_a+i, N*sizeof(int), cudaMemcpyHostToDevice, stream0));
HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a+i+N, N*sizeof(int), cudaMemcpyHostToDevice, stream1));
HANDLE_ERROR(cudaMemcpyAsync(dev_b0, host_b+i, N*sizeof(int), cudaMemcpyHostToDevice, stream0));
HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a+i+N, N*sizeof(int), cudaMemcpyHostToDevice, stream1));
kernel<<<N/256, 256, 0, stream0>>>(dev_a0, dev_b0, dev_c0);
kernel<<<N/256, 256, 0, stream1>>>(dev_a1, dev_b1, dev_c1);
HANDLE_ERROR(cudaMemcpyAsync(host_c+i, dev_c0, N*sizeof(int), cudaMemcpyDeviceToHost, stream0));
HANDLE_ERROR(cudaMemcpyAsync(host_c+i+N, dev_c1, N*sizeof(int), cudaMemcpyDeviceToHost, stream1));
// HANDLE_ERROR(cudaMemcpyAsync(dev_a0, host_a+i, N*sizeof(int), cudaMemcpyHostToDevice, stream0));
// HANDLE_ERROR(cudaMemcpyAsync(dev_b0, host_b+i, N*sizeof(int), cudaMemcpyHostToDevice, stream0));
// kernel<<<N/256, 256, 0, stream0>>>(dev_a0, dev_b0, dev_c0);
// HANDLE_ERROR(cudaMemcpyAsync(host_c+i, dev_c0, N*sizeof(int), cudaMemcpyDeviceToHost, stream0));
//
// HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a+i+N, N*sizeof(int), cudaMemcpyHostToDevice, stream1));
// HANDLE_ERROR(cudaMemcpyAsync(dev_a1, host_a+i+N, N*sizeof(int), cudaMemcpyHostToDevice, stream1));
// kernel<<<N/256, 256, 0, stream1>>>(dev_a1, dev_b1, dev_c1);
// HANDLE_ERROR(cudaMemcpyAsync(host_c+i+N, dev_c1, N*sizeof(int), cudaMemcpyDeviceToHost, stream1));
}
// wait until gpu execution finish
HANDLE_ERROR(cudaStreamSynchronize(stream0));
HANDLE_ERROR(cudaStreamSynchronize(stream1));
HANDLE_ERROR(cudaEventRecord(stop, 0));
HANDLE_ERROR(cudaEventSynchronize(stop));
HANDLE_ERROR(cudaEventElapsedTime(&elapsedTime, start, stop));
printf("Time taken: %3.1f ms
", elapsedTime);
// free stream and mem
HANDLE_ERROR(cudaFreeHost(host_a));
HANDLE_ERROR(cudaFreeHost(host_b));
HANDLE_ERROR(cudaFreeHost(host_c));
HANDLE_ERROR(cudaFree(dev_a0));
HANDLE_ERROR(cudaFree(dev_b0));
HANDLE_ERROR(cudaFree(dev_c0));
HANDLE_ERROR(cudaFree(dev_a1));
HANDLE_ERROR(cudaFree(dev_b1));
HANDLE_ERROR(cudaFree(dev_c1));
HANDLE_ERROR(cudaStreamDestroy(stream0));
HANDLE_ERROR(cudaStreamDestroy(stream1));
return 0;
}
순서대로 실행 하 는 것 에 비해 두 개의 cuda 흐름 을 사용 하여 프로그램의 실행 시간 을 20ms 줄 였 습 니 다(데이터 양 이 많 지 않 기 때문에 흐름 을 사용 하 는 장점 이 뚜렷 하지 않 습 니 다).
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Windows10에서 Cuda9.1+Chainer4.1의 환경을 가장 빠르게 만든다최근에는 라이브러리(특히 Python에서)가 충실하고 있어, GPU를 이용한 기계 학습이 가까워지고 있습니다만, Windows에서의 환경 구축은 역시 어렵네요. 요전날, 연구실의 Windows 머신에서 Cuda+Ch...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.