A certain engineer "COMPLEX"

.NETでGPUPUを試してみる CUDA編 第1回

前回は環境構築をしました。

Introduction


ちょっと余所事を。
どうして.NETでGPUPUをやるかというと、WPFやWinFormsでUIは簡単に作れるからというのがあります。
別に、C++でもできなくはないでしょうが、やっぱり自分の実力も考えるとそれが妥当です。

.NETを使っていてよく言われるのが、

パフォーマンスがねぇ...

というセリフ。
そこは否定しません。NGENで最適化しようが、ポインタをフル活用しようが、どうやったって、ハードに近い部分を触れるC、C++に勝てるはずがありません。
でも開発効率はC#のが断然高いです。
どっちが優れている、というつもりもないです。

言いたいのは、どっちもいいとこどりをしたいんです。

C#側からは、時間のかかる処理はC++に任せたい
C++側からは、リッチなUIをC#に任せたい

というWin&Winな関係を作りたいんです。
正直、このご時世に頑なにMFCを使う理由がわからないんですよ。
デモアプリであっても。

もうちょっと見た目にも気を使ってほしいなあ。って。

といったところです。

Explanation


CUDAことはじめ

インストールが完了し、Visual Studio から 新しいプロジェクトインストール済みを選ぶと、NVIDIA とその下に CUDA X.X が追加されています。
(2010、2012、2013のみ。2015と2008は対象外でした。)

これを選択してプロジェクトを作成すると、C++のプロジェクトができ、kernel.cu というコードが表示されます。
これはサンプルのプログラムです。

まずはビルドしてみます。
バッチビルドを開くと、Win32x64 が対象になっています。
すべて選択してビルドしてみます。

警告がたくさん出ますが、一応ビルドが通ります。

実行すると

となります。
単純に、同一長のint配列の同一のindexの値同士の和を計算するプログラムです。

無事にCUDAが動いていることが確認できました。

CUDAの実力

でも、これだとCUDAがすごいのかがよくわかりません。
私の開発機、

  • Intel Core i7-2600 3.40GHz
  • Windows 7 Ultimate SP1 64bit
  • 16.0GB

なので、最新とは言えないですが、今でもバリバリ現役です。
なので、CPUのがひょっとしたらすごいかもしれません。

なので、試してみることにしました。

サンプルの計算を10000回繰り返して、経過した時間をCUDAと非CUDA、つまりC++の普通の実装で勝負させます。
私のC++のソースが酷いのは、お察しください
どうでもいいけど、ReSharper C++ すごいよーー!!ちゃんとインクルードが足りないと指摘してくれるよ!!しかも C++11

こんな感じ


std::chrono::high_resolution_clock::time_point start, stop;
const int loop = 10000;
cudaError_t cudaStatus;

start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < loop; i++)
{
cudaStatus = addWithCuda(c, a, b, arraySize);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addWithCuda failed!");
return 1;
}
}
stop = std::chrono::high_resolution_clock::now();

printf("CUDA is\n");
printf("\t{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]);

auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
printf("\ttime = {%lld}\n", ms.count());

start = std::chrono::high_resolution_clock::now();

for (size_t i = 0; i < loop; i++)
{
auto pA = &a[0];
auto pB = &b[0];
auto pC = &c[0];

for (size_t j = 0; j < arraySize; j++, pA++, pB++, pC++)
{
*pC = *pA + *pB;
}
}
stop = std::chrono::high_resolution_clock::now();

printf("No CUDA is\n");
printf("\t{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]);

ms = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
printf("\ttime = {%lld}\n", ms.count());

結果は、

[code lang="sh"]
CUDA is
{1,2,3,4,5} + {10,20,30,40,50} = {11,22,33,44,55}
time = {5675}
No CUDA is
{1,2,3,4,5} + {10,20,30,40,50} = {11,22,33,44,55}
time = {0}

CUDA惨敗

はぁ!?

Conclusion


まぁ明らかに演出な結果ですが、ここまでひどいとは思いませんでした。
当初はループ回数10000000回でしたけど、全く終わらなかったくらいですからwww

次回は原因の究明と解決を。

Source Code


https://github.com/takuya-takeuchi/Demo/tree/master/CUDA1

コメントを残す

メールアドレスが公開されることはありません。

%d人のブロガーが「いいね」をつけました。