cuda 시작하기

  • 최초 작성일: 2023년 8월 22일(화)

목차

[TOC]


CUDA 프로그래밍 준비 사항

1. 하드웨어 준비:

  • NVIDIA GPU: CUDA는 NVIDIA의 GPU를 대상으로 한 프로그래밍이므로, 호환되는 NVIDIA 그래픽 카드가 설치된 시스템이 필요하다.
  • 시스템 요구 사항: 메모리, CPU, 저장 공간 등의 요구 사항도 고려해야 한다. 특히 복잡한 CUDA 애플리케이션의 경우 더 많은 리소스가 필요할 수 있다.

2. 소프트웨어 설치:

  • CUDA Toolkit: NVIDIA의 공식 웹사이트에서 적절한 버전의 CUDA Toolkit을 다운로드 받아 설치.
  • GPU 드라이버: CUDA Toolkit과 호환되는 NVIDIA GPU 드라이버를 설치.
  • cuDNN: 딥러닝을 위한 CUDA용 딥 뉴럴 네트워크 라이브러리. 필요한 경우 다운로드 받아 설치하면 된다.
  • IDE: NVIDIA는 Nsight라는 통합 개발 환경도 제공한다. Visual Studio와 같은 다른 IDE와 함께 CUDA 개발을 할 수도 있다.

3. 프로그래밍 지식:

  • C/C++ 지식: CUDA는 C/C++을 기반으로 한 프로그래밍 모델이므로 기본적인 C/C++ 프로그래밍 지식이 필요.
  • 병렬 프로그래밍: CUDA는 GPU의 병렬 처리 능력을 최대한 활용하기 위한 프로그래밍 모델입니다. 따라서 병렬 프로그래밍에 대한 이해도 중요하다.

4. 개발 환경 설정:

  • 환경 변수 설정: CUDA 라이브러리와 바이너리에 쉽게 접근하기 위해 시스템의 환경 변수를 설정해야 할 수도 있다.
  • 프로젝트 설정: 개발하려는 IDE나 빌드 시스템에서 CUDA를 지원하기 위한 설정이 필요하다.

5. 학습 자료:

  • 공식 문서: NVIDIA의 공식 CUDA 문서는 매우 유용하다.
  • 텍스트북 및 튜토리얼: 여러 텍스트북과 온라인 튜토리얼이 CUDA 프로그래밍에 대한 입문자나 고급 사용자를 위해 제공된다.

6. 도구 및 라이브러리:

  • NVIDIA Visual Profiler: GPU의 성능을 분석하고 최적화하는 데 도움을 주는 도구이다.
  • Thrust, CUB, NCCL 등: CUDA와 함께 사용할 수 있는 여러 라이브러리와 도구들이 있다. 이들은 특정 작업을 더 효과적으로 수행할 수 있게 도와준다.


설치된 그래픽 카드 확인

아래와 같이 장치 관리자를 통해 본인의 PC에 설치된 그래픽 카드를 확인할 수 있다.

image


그리고 구글에 CUDA capable GPU list 를 검색해서 NVIDIA CUDA zone에 들어가면 내 그래픽 카드가 리스트에 있다면 CUDA 프로그래밍이 가능한 것이다.

image


CUDA 툴킷 toolkit 다운로드

구글에서 “CUDA download” 검색해서 CUDA 툴킷을 다운로드한다.

image

image


CUDA 프로젝트 새로 만들기

“Visual Studio” 를 켜고 새 프로젝트 생성(Create a new project)를 클릭하면, 아래의 창이 보일텐데 본인이 설치한 CUDA 툴킷을 찾아 생성한다.

image


새로운 CUDA 프로젝트를 생성하면, 아래와 같이 임의로 간단한 예제 코드가 입력되어 있을건데 그냥 한번 F5를 눌러 실행해서 결과를 확인해보고 넘어가자.

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

#include <stdio.h>

cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);

__global__ void addKernel(int *c, const int *a, const int *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

int main()
{
    const int arraySize = 5;
    const int a[arraySize] = { 1, 2, 3, 4, 5 };
    const int b[arraySize] = { 10, 20, 30, 40, 50 };
    int c[arraySize] = { 0 };

    // Add vectors in parallel.
    cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "addWithCuda failed!");
        return 1;
    }

    printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
        c[0], c[1], c[2], c[3], c[4]);

    // cudaDeviceReset must be called before exiting in order for profiling and
    // tracing tools such as Nsight and Visual Profiler to show complete traces.
    cudaStatus = cudaDeviceReset();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceReset failed!");
        return 1;
    }

    return 0;
}

// Helper function for using CUDA to add vectors in parallel.
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
    int *dev_a = 0;
    int *dev_b = 0;
    int *dev_c = 0;
    cudaError_t cudaStatus;

    // Choose which GPU to run on, change this on a multi-GPU system.
    cudaStatus = cudaSetDevice(0);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

    // Allocate GPU buffers for three vectors (two input, one output)    .
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }

    // Copy input vectors from host memory to GPU buffers.
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    // Launch a kernel on the GPU with one thread for each element.
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);

    // Check for any errors launching the kernel
    cudaStatus = cudaGetLastError();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
        goto Error;
    }
    
    // cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch.
    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

    // Copy output vector from GPU buffer to host memory.
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

Error:
    cudaFree(dev_c);
    cudaFree(dev_a);
    cudaFree(dev_b);
    
    return cudaStatus;
}


아래와 같이 정상적으로 출력되는 걸 볼 수 있다.

image