CUDA로 작성한 RGB2RGBA 성능
지난 글에서,
C# - OpenCvSharp 사용 시 C/C++을 이용한 속도 향상 (for 루프 연산)
; https://www.sysnet.pe.kr/2/0/11422
OpenCV의 CvtColor(ColorConversionCodes.BGR2BGRA) 호출에 대해 C++/parallel_for로 성능을 유사하게 구현한 적이 있습니다. 마찬가지로, SIMD를 이용해 OpenCV의 erode 연산을 해보기도 했습니다.
내가 만든 코드보다 OpenCV의 속도가 월등히 빠른 이유
; https://www.sysnet.pe.kr/2/0/11423
아쉽게도 SIMD 연산의 경우 RGB2RGBA 연산에는 적용할 수 없었는데요. CUDA의 경우 kernel 함수가 SIMD보다는 더 유연하기 때문에 RGB2RGBA 같은 연산을 구현하는 것이 가능한데, 아래의 코드가 바로 그것입니다.
__global__ void rgb2rgba(int n, BYTE *srcPtr, BYTE *dstPtr)
{
int tid = threadIdx.x + blockIdx.x * blockDim.x;
while (tid < n)
{
int srcPos = tid * 3;
int dstPos = tid * 4;
dstPtr[dstPos + 0] = srcPtr[srcPos + 0];
dstPtr[dstPos + 1] = srcPtr[srcPos + 1];
dstPtr[dstPos + 2] = srcPtr[srcPos + 2];
dstPtr[dstPos + 3] = 0xff;
tid += (blockDim.x * gridDim.x);
}
}
위의 kernel 함수를 C#에서 호출할 수 있도록 다음과 같이 export 함수를 하나 만들어 주고,
__declspec(dllexport) BOOL RGB2RGBA_Cuda(BYTE *srcPtr, BYTE *dstPtr, int width, int height)
{
BYTE *cudaSrc = nullptr;
BYTE *cudaDst = nullptr;
int srcSize = width * height * 3; // RGB 3bytes
int dstSize = width * height * 4; // RGBA 4bytes
BOOL ret = FALSE;
do
{
cudaError_t cudaStatus = cudaMalloc((void **)&cudaSrc, srcSize);
if (cudaStatus != cudaSuccess)
{
break;
}
cudaStatus = cudaMalloc((void **)&cudaDst, dstSize);
if (cudaStatus != cudaSuccess)
{
break;
}
cudaStatus = cudaMemcpy(cudaSrc, srcPtr, srcSize, cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess)
{
break;
}
rgb2rgba<<<64, 64>>>(width * height, cudaSrc, cudaDst);
cudaStatus = cudaGetLastError();
if (cudaStatus != cudaSuccess)
{
break;
}
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess)
{
break;
}
cudaStatus = cudaMemcpy(dstPtr, cudaDst, dstSize, cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess)
{
break;
}
ret = TRUE;
} while (false);
if (cudaSrc != nullptr)
{
cudaFree(cudaSrc);
}
if (cudaDst != nullptr)
{
cudaFree(cudaDst);
}
return ret;
}
테스트해 보면, 100회 연산에 2초 넘는 시간이 걸립니다. 즉, "
C# - OpenCvSharp 사용 시 C/C++을 이용한 속도 향상 (for 루프 연산)" 글에서 성능 테스트한 것 중에 (C# 제외하고) 가장 안 좋은 기록이 나온 것입니다. (아직 제가 CUDA 초보자라 더 빠르게 할 수 있는 방법이 있는지는 모르겠습니다.)
성능이 낮은 이유는, RAM에 있는 데이터를 GPU의 메모리로 복사하고 그 결과를 다시 RAM으로 복사하는 오버헤드가 있기 때문입니다.
따라서, CUDA를 이용해 성능 향상을 이루고 싶다면 메모리 복사에 따른 오버헤드를 극복할 정도의 복잡한 kernel 연산이거나, 아니면 CPU를 쉬게 하면서 GPU에 다중으로 작업을 맡기는 경우에만 쓰는 것이 좋겠습니다.
(
첨부 파일은 "
C# - OpenCvSharp 사용 시 C/C++을 이용한 속도 향상 (for 루프 연산)" 글의 예제에 CUDA 테스트를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]