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

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

こんな感じ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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());

結果は、

1
2
3
4
5
6
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/CUDA/CUDA1