1075: .NETからCUDAを使うひとつの方法

23
.NET から CUDA を使うひとつの方法 C++/CLI による WRAPPER のつくりかた episthmh [email protected] Microsoft MVP for Visual C++ Jan.2004NVIDIA CUDA Ambassador for Apr.20152015-1075 Hall-B1 16:20

Transcript of 1075: .NETからCUDAを使うひとつの方法

.NET から CUDA を使うひとつの方法C++/CLI による WRAPPER のつくりかた

episthmh [email protected]

Microsoft MVP for Visual C++ Jan.2004~NVIDIA CUDA Ambassador for Apr.2015~

2015-1075Hall-B1 16:20~

WINDOWS APPLICATION

CUIConsole (stdin/stdout)

GUIWin API

MFC

Windows Forms

WPF

native : C/C++

managed : .NET (C#,VB etc.)

legacy…

MANAGED は直接 CUDA を呼べない…

host

memorydevice

memory

PCI-bus

CLR

managed app.

MANAGED と NATIVE の仲介役

host

memorydevice

memory

PCI-bus

CLR

managed app.

native assembly

call

C++/CLI で作る「仲介役」

見た目(インタフェース)はmanaged

ナカミ(実装)はnative

native assembly

PCI-bus

サンプル : SAXPY

Y[i] = alpha * X[i] + Y[i] (i = 0..N-1)

C# / Windows Forms app.

絵ヅラはC#, 計算はCUDA

C++/CLIが両者を仲介

WRAPPERのつくりかた 1: CLR クラスライブラリ

WRAPPERのつくりかた 2: ビルド カスタマイズ

※ C++/CLI プロジェクト内で デバイス・コード(~.cu) をコンパイルできます

WRAPPERのつくりかた 3: CUDA RUNTIME

WRAPPERのつくりかた 4: 64BIT PLATFORM

※ CUDA 7.0 以降、多くのCUDAライブラリは 64bit-only です

SAXPY DEVICE/HOST CODE__global__ void kernel_saxpy(float alpha, const float* x, float* y,

unsigned int size) {

unsigned int i = blockDim.x * blockIdx.x + threadIdx.x;

if ( i < size ) { y[i] = alpha * x[i] + y[i]; } // Saxpy!

}

__host__ void device_saxpy(float alpha, const float* x, float* y,

unsigned int size) {

unsigned int block = 256U; unsigned int grid = (size + block -1U)/block;

kernel_saxpy<<<grid,block>>>(alpha, x, y, size);

}※ device-code および それを呼び出すhost-codeは~.cu に記述します

REF CLASS SAXPY : ~.h public部namespace CUDA {

public ref class Saxpy {

private:

public:

Saxpy(); // コンストラクタ

~Saxpy(); // デストラクタ

!Saxpy(); // ファイナライザ

void calculate(float alpha, cli::array<float>^ x, cli::array<float>^ y);

};

} ※ C++/CLI: cli::array<float>^ は C#: float[] に相当します

REF CLASS SAXPY : ~.h private部namespace CUDA {

public ref class Saxpy {

private:

float* dx_; // device-memory X[]

float* dy_; // device-memory Y[]

size_t bsize_; // dx_/dy_の大きさ(bytes)

void allocate(size_t new_bsize); // dx_/dy_を確保する

void deallocate() { // dx_/dy_を解放する

cudaFree(dx_); dx_ = nullptr;

cudaFree(dy_); dy_ = nullptr;

}

}※利用者(C#,VB)に成り代わって device-memory を確保/解放します

namespace CUDA {

Saxpy::Saxpy() : dx_(nullptr), dy_(nullptr), bsize_(0U) {}

Saxpy::~Saxpy() { this->!Saxpy(); } // 明示的にファイナライザを呼ぶ

Saxpy::!Saxpy() { deallocate(); } // device-mem. を解放する

}

※ ファイナライザ : !クラス名() をお忘れなく

REF CLASS SAXPY : ~.cpp

namespace CUDA {

void Saxpy::allocate(size_t new_bsize) {

// 足りないときは一旦解放し、再確保

if ( bsize_ < new_bsize ) {

deallocate();

bsize_ = new_bsize;

float* ptr;

cudaMalloc(&ptr, bsize_); dx_ = ptr;

cudaMalloc(&ptr, bsize_); dy_ = ptr;

}

}

}※ cudaMalloc(&dx_, bsize_) とは書けません

namespace CUDA {

void Saxpy::calculate(float alpha, array<float>^ x, array<float>^ y) {

unsigned int size = x->Length; if ( size != y->Length ) return;

allocate((size_t)(size*sizeof(float)));

// host → device

pin_ptr<float> hx = &x[0];

cudaMemcpy(dx_, hx, size*sizeof(float), cudaMemcpyHostToDevice);

pin_ptr<float> hy = &y[0];

cudaMemcpy(dy_, hy, size*sizeof(float), cudaMemcpyHostToDevice);

// kernel-call

device_saxpy(alpha, dx_, dy_, size);

// device → host

cudaMemcpy(hy, dy_, size*sizeof(float), cudaMemcpyDeviceToHost);

}

}※ pin_ptr<float> でピン留めします

HOW TO USE IN .NET

※ 参照設定をお忘れなく

HOW TO USE IN C# : ~.cspublic partial class Form1 : Form {

private CUDA.Saxpy saxpy = new CUDA.Saxpy(); // 作って

private void btnCalc_Click(object sender, EventArgs e) {

if ( lstX.Items.Count != lstY.Items.Count ) return;

float[] x = …;

float[] y = …;

float alpha = …;

saxpy.calculate(alpha, x, y); // 呼ぶ!

}

※ インスタンスを生成(new)し、メソッドを呼び出すだけ

BITMAPを扱うには?

CUDAで画像処理構造はSaxpyとおなじ。

配列じゃなく、Bitmapの先頭アドレスを

CUDAに渡します。

namespace CUDA {

public ref class SepiaConverter {

public:

SepiaConverter();

~SepiaConverter();

!SepiaConverter();

// image(元画像)をcolor_にコピーし、モノクロ化

void LoadImage(System::IntPtr image, int width, int height, int stride);

// モノクロ画像を色調変換し、imageにコピー

void ConvertSepia(System::IntPtr image, float u, float v);

};

}

※ System::IntPtr が void* に相当します

namespace CUDA {

void SepiaConverter::ConvertSepia(System::IntPtr image, float u, float v) {

// 色調変換し、結果を color_ に

// 結果をコピー

size_t bsize = height_ * stride_;

void* ptr = static_cast<void*>(image);

cudaMemcpy(ptr, color_, bsize, cudaMemcpyDeviceToHost);

}

}

※ System::IntPtr は void* にキャストできます

// セピア変換

private CUDA.SepiaConverter converter_; // C++/CLIで作ったwrapper

private void convert_sepia(float u, float v) {

// Bitmapを生成し、 LockBitsで固定

var bmp = new Bitmap(幅, 高さ);

var data = bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height),

ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);

// 色調変換

converter_.ConvertSepia(data.Scan0, u, v);

bmp.UnlockBits(data);

picOutput.Image = bmp;

picOutput.Invalidate();

}

※ IntPtr BitmapData.Scan0 が画像の先頭アドレスです

HAVE FUN!

BLOG http://blog.zaq.ne.jp/fareastprogramming

https://www.facebook.com/cppepisteme

@epitwit

http://codezine.jp/author/352CodeZine