ZeroLoom

CUDA プログラミング - サンプルコード集

October 11, 2024
2 min read
Table of Contents

基本的な処理の流れ


  1. データの転送
  • CPU(ホスト)からGPU(デバイス)にデータを転送
  1. カーネルの起動
  • GPUで動作する関数(カーネル)を起動
  1. 結果の取得
  • GPU から CPU に結果を転送
  1. メモリの解放
  • GPU メモリの解放

サンプルコード


配列の要素ごとに足し算

cpp
#include <iostream>
#include <cuda_runtime.h>
 
// CUDA カーネル関数
__global__ void addKernel(int *a, int *b, int *c, int size) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < size) {
        c[i] = a[i] + b[i];
    }
}
 
int main() {
    const int arraySize = 1000;
    int a[arraySize], b[arraySize], c[arraySize];
    int *d_a, *d_b, *d_c;
 
    // ホスト側のデータを初期化
    for (int i = 0; i < arraySize; ++i) {
        a[i] = i;
        b[i] = i * 2;
    }
 
    // GPUメモリを確保
    cudaMalloc((void**)&d_a, arraySize * sizeof(int));
    cudaMalloc((void**)&d_b, arraySize * sizeof(int));
    cudaMalloc((void**)&d_c, arraySize * sizeof(int));
 
    // CPUからGPUにデータを転送
    cudaMemcpy(d_a, a, arraySize * sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, b, arraySize * sizeof(int), cudaMemcpyHostToDevice);
 
    // カーネル起動
    int blockSize = 256;
    int numBlocks = (arraySize + blockSize - 1) / blockSize;
    addKernel<<<numBlocks, blockSize>>>(d_a, d_b, d_c, arraySize);
 
    // GPUからCPUに結果を転送
    cudaMemcpy(c, d_c, arraySize * sizeof(int), cudaMemcpyDeviceToHost);
 
    // 結果の表示
    for (int i = 0; i < 10; ++i) {
        // 部分的に結果を表示
        std::cout << "c[" << i << "] = " << c[i] << std::endl;
    }
 
    // GPUメモリの解放
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);
 
    return 0;
}

画像のグレースケール変換 - 各ピクセルの RGB 値をグレースケールに変換

cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <cuda_runtime.h>
 
// CUDA カーネル関数
__global__ void rgbToGrayKernel(unsigned char* d_input, unsigned char* d_output, int width, int height, int channels) {
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
 
    if (x < width && y < height) {
        int idx = (y * width + x) * channels;
        unsigned char r = d_input[idx];
        unsigned char g = d_input[idx + 1];
        unsigned char b = d_input[idx + 2];
        d_output[y * width + x] = static_cast<unsigned char>(0.299f * r + 0.587f * g + 0.114f * b);
    }
}
 
int main() {
    // 画像の読み込み
    cv::Mat inputImage = cv::imread("input.jpg");
    if (inputImage.empty()) {
        std::cerr << "画像が読み込めませんでした!" << std::endl;
        return -1;
    }
 
    int width = inputImage.cols;
    int height = inputImage.rows;
    int channels = inputImage.channels();
 
    // 出力画像の作成
    cv::Mat outputImage(height, width, CV_8UC1);
 
    // GPUメモリの確保
    unsigned char* d_input;
    unsigned char* d_output;
    cudaMalloc((void**)&d_input, width * height * channels * sizeof(unsigned char));
    cudaMalloc((void**)&d_output, width * height * sizeof(unsigned char));
 
    // データをGPUに転送
    cudaMemcpy(d_input, inputImage.data, width * height * channels * sizeof(unsigned char), cudaMemcpyHostToDevice);
 
    // カーネル起動の設定
    dim3 blockSize(16, 16);
    dim3 gridSize((width + blockSize.x - 1) / blockSize.x, (height + blockSize.y - 1) / blockSize.y);
 
    // カーネルを起動
    rgbToGrayKernel<<<gridSize, blockSize>>>(d_input, d_output, width, height, channels);
 
    // 結果をホストに転送
    cudaMemcpy(outputImage.data, d_output, width * height * sizeof(unsigned char), cudaMemcpyDeviceToHost);
 
    // 結果を表示および保存
    cv::imshow("Original Image", inputImage);
    cv::imshow("Grayscale Image", outputImage);
    cv::imwrite("output.jpg", outputImage);
    cv::waitKey(0);
 
    // GPUメモリの解放
    cudaFree(d_input);
    cudaFree(d_output);
 
    return 0;
}