CUDA를 활용하면 GPU의 성능을 최대한 활용할 수 있습니다. CUDA에서 함수 호출 방식은 세 가지로 구분됩니다:
__global__: CPU에서 호출되며, GPU에서 실행됩니다 (비동기).__device__: GPU 내부에서 호출되며, GPU에서 실행됩니다.__host__: CPU에서 호출되며, CPU에서 실행됩니다 (동기).
함수 선언 및 호출 방법
CUDA에서는 다음과 같이 함수를 선언합니다:
__global__ void FuncA(float input)
{
// GPU 작업 수행
}
__device__ void FuncB(int input)
{
// GPU 내부 작업 수행
}
__host__ void FuncC(char input)
{
// CPU 작업 수행
}
GPU 코어는 다중 스레드 환경에서 작동하므로, 각 스레드가 어떤 위치에 있는지 확인해야 합니다. 이를 위해 CUDA에서는 grid-block-thread 구조를 사용합니다.
Grid와 Block 정의
예를 들어 4x4 크기의 grid와 block을 생성하는 방법은 다음과 같습니다:
dim3 grid(4, 4);
dim3 block(4, 4);
각 thread의 위치를 결정하는 코드는 다음과 같습니다:
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
데이터 전송과 메모리 관리
CPU와 GPU 간 데이터 전송은 cudaMalloc과 cudaMemcpy를 사용하여 수행됩니다:
float *gpu_input;
size_t size = N * sizeof(float);
cudaMalloc((void**)&gpu_input, size);
cudaMemcpy(gpu_input, cpu_input, size, cudaMemcpyHostToDevice);
dim3 blockSize(16, 16);
dim3 gridSize(16, 16);
FuncA<<<gridSize, blockSize>>>(gpu_input);
결과를 CPU로 다시 가져오는 과정도 유사합니다:
float *gpu_output;
cudaMalloc((void**)&gpu_output, size);
FuncA<<<gridSize, blockSize>>>(gpu_input, gpu_output);
cudaMemcpy(cpu_output, gpu_output, size, cudaMemcpyDeviceToHost);
CUDA 라이브러리 사용
CUDA는 다양한 수학 및 행렬 연산 라이브러리를 제공합니다. 예를 들어, sigmoid 함수는 다음과 같이 작성할 수 있습니다:
template <typename T>
__device__ __forceinline__ T Sigmoid(T z) {
return 1.0 / (1.0 + exp(-z));
}
행렬 연산은 cuBLAS 라이브러리를 통해 쉽게 처리할 수 있습니다:
cublasHandle_t handle;
cublasCreate(&handle);
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, M, N, K, &alpha, A, lda, B, ldb, &beta, C, ldc);
이러한 기능들을 활용하면 복잡한 신경망 계산도 효율적으로 수행할 수 있습니다.