CUDA 이용하여 행렬 곱셈을 해보자

2018, Jul 03    

CUDA 를 이용해 행렬의 곱셈을 해보자.

kernel.cu

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

#include <Stdio.h>
// 행렬 곱셈 커널 함수를 콜할 호스트 함수
cudaError_t multiWithCuda(float* c, float* a, float* b, unsigned int size);

__global__ void multiKernel(float* c, float* a, float* b, unsigned int size)
{
    int i = threadIdx.x;
    // block 갯수를 지정해 idx를 활용할 것이다.
    // (1차원 배열을 사용할 것이지만)
    // 행렬은 2차원이기 때문이다.
    int j = blockIdx.x;
    
    // 행렬의 곱셈을 구현한다.
    for(int x=0; x<size; x++)
        c[size*j + i] += a[size*j + x] * b[size*x + i];
}

int main()
{
    const int arraySize = 5;
    // 행렬 a,b,c 를 만든다.
    float a[arraySize*arraySize] = {0};
    float b[arraySize*arraySize] = {0};
    float c[arraySize*arraySize] = {0};

    // 알맞은 값으로 초기화 한다.
    for(int i=0; i<arraySize*arraySize; i++)
    {
        a[i] = static_cast<float>(i);
        b[i] = static_cast<float>(i);
    }

    // 작업할 함수를 콜한다.
    cudaError_t cudaStatus = multiWithCuda(c, a, b, arraySize);
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "multiWithCuda failed!");
        return -1;
    }

    // 결과를 출력한다.
    for(int i=0; i<arraySize*arraySize; i++)
    {
        if(i % arraySize == 0) printf("\n");
        printf("%8.1f ",c[i]);
    }
    printf("\n");

    // 모든 작업이 완료되었으므로
    // device 를 reset 한다.
    cudaStatus = cudaDeviceReset();
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr,"cudaDeviceReset, failed!");
        return 1;
    }

    return 0;
}

// 커널함수 호출하는 헬퍼 함수 multiWithCuda를 정의하자
cudaError_t multiWithCuda(float* c, float* a, float* b, unsigned int size)
{
    // gpu에 할당한 메모리 주소값을 저장할 변수를 선언한다.
    float* dev_a = 0;
    float* dev_b = 0;
    float* dev_c = 0;
    cudaError_t cudaStatus;

    cudaStatus = cudaSetDevice(0);
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "CudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
        goto Error;
    }

    // GPU에 메모리를 할당한다.
    // 행렬 크기만큼 할당한다.
    cudaStatus = cudaMalloc((void**)&dev_c, size*size*sizeof(float));
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr,"cudaMalloc failed!");
        goto Error;
    }
    cudaStatus = cudaMalloc((void**)&dev_a, size*size*sizeof(float));
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr,"cudaMalloc failed!");
        goto Error;
    }
    cudaStatus = cudaMalloc((void**)&dev_b, size*size*sizeof(float));
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr,"cudaMalloc failed!");
        goto Error;
    }

    // 호스트 메모리에 있는 값을 디바이스 메모리에 복사한다.
    cudaStatus = cudaMemcpy(dev_a, a, size*size*sizeof(float), cudaMemcpyHostToDevice);
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }
     cudaStatus = cudaMemcpy(dev_b, b, size*size*sizeof(float), cudaMemcpyHostToDevice);
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

    // 커널 함수를 실행한다.
    multiKernel<<<size, size>>>(dev_c, dev_a, dev_b, size);

    // 커널 함수 실행후 에러가 있는지 확인
    cudataStatus = cudaGetLastError();
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "multiKernel launch failed : %s\n", cudaGetErrorString(cudaStatus));
        goto Error;
    }

    // 커널이 모두 종료되었는지 확인
    cudaStatus  = cudaDeviceSynchronize();
    if(cudaStatus!= cudaSuccess)
    {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
        goto Error;
    }

    // 결과를 호스트 메모리에 복사
    cudaStatus = cudaMemcpy(c, dev_c, size*size*sizeof(float), cudaMemcpyDeviceToHost);
    if(cudaStatus != cudaSuccess)
    {
        fprintf(stderr, "cudaMemcpy failed!");
        goto Error;
    }

Error:
// gpu에 할당한 메모리를 반환
    cudaFree(dev_c);
    cudaFree(dev_a);
    cudaFree(dev_b);

    return cudaStatus;
}

결과는 다음과 같다

     150      160      170      180      190  
     400      435      470      505      540  
     650      710      770      830      890  
     900      985     1070     1155     1240  
    1150     1260     1370     1480     1590