LoginSignup
12
17

More than 5 years have passed since last update.

CUDAで作った関数をDLLライブラリとして使用する

Last updated at Posted at 2017-11-15

こちらでCUDAのインストール方法を紹介しましたが、これではCUDA専用のプロジェクトとなってしまいます。この記事では、実装したCUDA関数をDLLライブラリとしてエクスポートして、他の通常のプロジェクトから呼び出せるようにします。

もっといい方法があればぜひ教えてください。DLLなので呼び出しコストが気になります。

下記動画でも説明しています。

Link to Youtube

環境

  • Windows 10
  • Visual Studio Community 2017
  • CUDA Toolkit 9.0

目的

  • 通常のアプリケーション(この記事ではWindows Console Applicationを使用)から、CUDAで実装した処理を呼び出せるようにする
  • CUDAライブラリ側の独立性を高めて、チューニングやユニットテストをやりやすくする
  • 通常のアプリケーション側ではCUDAに関する設定は一切不要にする

ソリューションとプロジェクトの用意

1つのソリューションの中に、2つのプロジェクトを作成する。一つは通常のアプリケーション、もう一つはCUDAライブラリ用のプロジェクト。

1. ファイル -> 新規作成 -> プロジェクト -> Visual C++ -> Windows デスクトップ -> Windows コンソール アプリケーション
- ここでは、MyAppという名前にする
- MyAppソリューションと、MyAppソリューションの下にMyAppプロジェクトが生成される
2. ソリューションエクスプローラ上でMyAppソリューションで右クリック -> 追加 -> 新しいプロジェクト -> NVIDIA -> CUDA 9.0 -> CUDA 9.0 Runtime
- ここでは、CudaLibという名前にする
- MyAppソリューションの下に、CudaLibプロジェクトが生成される
3. 最終的には下記のような構成になります。

ProjectStructure.jpg

ソリューションの構成設定 (重要)

基本的に使用するのは、Debug/Releaseのx64。ビルド設定や、プロジェクトのプロパティを変更する際には、x64向け設定になっていることを確認する。

複数プロジェクトをまたぐので、スタートアッププロジェクトの設定を変更すると便利かも。

MyAppソリューションで右クリック -> プロパティ -> スタートアッププロジェクト -> 現在の選択  

CudaLib

計算用関数とWrapperの実装

  • CudaLib上で右クリック -> 新しい項目の追加 -> Visual C++ -> C++ファイル
    • main.cpp
  • CudaLib上で右クリック -> 新しい項目の追加 -> Visual C++ -> ヘッダーファイル
    • CudaLib.h
  • デフォルトで作られたkernel.cuはファイルは再利用するが中身は全てデリート

下記の実装で、まずはライブラリ関数単体として動作することを確認する。

CudaLib.h
#pragma once

namespace CudaLib
{
#if 0
}   // indent guard
#endif


#ifdef DLL_EXPORT
__declspec(dllexport) void complexCalcOriginal(int *in, int *out, int n);
__declspec(dllexport) void complexCalcFast(int *hIn, int *hOut, int n);
#else
__declspec(dllimport) void complexCalcOriginal(int *in, int*out, int n);
__declspec(dllimport) void complexCalcFast(int *in, int*out, int n);
#endif

}


main.cpp
/*
* Note:
* To switch output type (dll/exe), Project Configuration Properties -> Configuration Type
* *.exe for unit test
* *.dll for dll library
*/
#include "stdio.h"
#include "stdlib.h"

#include "CudaLib.h"

using namespace CudaLib;

bool unitTest(int n)
{
    int *in = new int[n];
    int *out1 = new int[n];
    int *out2 = new int[n];

    for (int i = 0; i < n; i++) in[i] = rand() % 100;

    complexCalcOriginal(in, out1, n);
    complexCalcFast(in, out2, n);

    int i;
    for (i = 0; i < n; i++) {
        if (out1[i] != out2[i]) {
            printf("error at index = %d\n", i);
            break;
        }
    }

    delete[] in;
    delete[] out1;
    delete[] out2;

    if (i == n) return true;
    return false;
}

int main()
{
    if (unitTest(10)) {
        printf("OK\n");
    } else {
        printf("ERROR\n");
    }


    return 0;
}


kernel.cu
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

#include "CudaLib.h"

namespace CudaLib
{
#if 0
}   // indent guard
#endif


void complexCalcOriginal(int *in, int *out, int n)
{
    for (int i = 0; i < n; i++) {
        out[i] = in[i] * 2;
    }
}


__global__ void complexCalcFastLoop(int *in, int *out, int n)
{
    int i = threadIdx.x;
    if (i < n) {
        out[i] = in[i] * 2;
    }
}

void complexCalcFast(int *hIn, int *hOut, int n)
{
    int *dIn;
    int *dOut;
    cudaMallocHost((void**)&dIn, n * sizeof(int));
    cudaMallocHost((void**)&dOut, n * sizeof(int));
    cudaMemcpy(dIn, hIn, n * sizeof(int), cudaMemcpyHostToDevice);

    complexCalcFastLoop <<<1, n>>> (dIn, dOut, n);
    cudaDeviceSynchronize();

    cudaMemcpy(hOut, dOut, n * sizeof(int), cudaMemcpyDeviceToHost);
    cudaFree(dIn);
    cudaFree(dOut);
}

}

DLLとしてEXPORTする

  1. DLLとしてエクスポートする関数にdllexport指定する
    • CudaLibプロジェクトで右クリック -> C/C++ -> プリプロセッサ -> プリプロセッサの定義に"DLL_EXPORT"を追加する。 (これはヘッダで使用している文字列と一致すればなんでもOK)
    • 注意点
    • Debug x64とRelease x64の両方に追加すること。(デフォルトだとx86になっているかもしれないので注意)
  2. プロジェクトをDLLライブラリ形式にする
    • CudaLibプロジェクトで右クリック -> 全般 -> 構成の種類
    • アプリケーション(.exe)をダイナミックライブラリ(.dll)にする
  3. ビルドする
    • MyApp\x64\Debugの下にCudaLib.dllとCudaLib.libが生成されていることを確認する

MyApp

インクルードパスとライブラリの参照設定

  1. MyAppで右クリック -> C/C++ -> 全般 -> 追加のインクルードパス
    • ../CudaLib
  2. MyAppで右クリック -> リンカー -> 入力 -> 追加の依存ファイル
    • ../x64/Debug/CudaLib.lib
    • ../x64/Release/CudaLib.lib

MyAppからCUDAで実装したライブラリ関数を呼ぶ

通常の関数として呼べる。

MyApp.cpp
#include "stdafx.h"
#include "CudaLib.h"

int main()
{
    int n = 5;
    int *in = new int[n];
    int *out = new int[n];

    for (int i = 0; i < n; i++) in[i] = i;

    CudaLib::complexCalcFast(in, out, n);

    for (int i = 0; i < n; i++) {
        printf("%d -> %d\n", in[i], out[i]);
    }

    delete[] in;
    delete[] out;

    return 0;
}

普段の運用例

アルゴリズムを改良したい

  1. CudaLibプロジェクトをEXE形式にする
    • CudaLibプロジェクトで右クリック -> 全般 -> 構成の種類
      • ダイナミックライブラリ(.dll)をアプリケーション(.exe)に戻す
  2. 新規アルゴリズムを実装する
    • 別プロジェクトになっているので、Nsightによるアナライズやユニットテストがやりやすい!!
  3. 満足する実装が出来たら、再度プロジェクトをDLLライブラリ形式にする
    • CudaLibプロジェクトで右クリック -> 全般 -> 構成の種類
      • アプリケーション(.exe)をダイナミックライブラリ(.dll)にする

実際のプロジェクトファイル

12
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
17