「C++とC# unsafeで速度差を計測してみる」という記事があって、ここではC++と
C#では速度がたいして変わらないということが書かれていて、いやホンマかな?と思ったので少々検証してみることに。
ちなみに「「C# が C++ の速度を凌駕している」らしいので、C++側を高速化してみた」というのもあって、SSEにしてみたり色々とやっているようでやっぱC++の方が速い的な感じになってますが。
そもそもC#側のソースを見た感じ、ガベージコレクション(GC)が働くので全く同じようなコードは書けない気はするんですよね。いや、同じような処理はかけるとは思うのですが、同じように処理時間を比較するのが難しいというか。
C#側は、ソースコード上では毎回配列を用意しているような雰囲気ですが、実際動作時には本当に毎度確保しているわけではないように思えたり、C++側では毎回deleteしているタイムが入っていますが、C#はGCが計測後に動くような気がするので同じプロセスを測定してはいない気はします。
ってことで、まずは私の環境でこちらのソースコードを測定してみたところ
- C#
- 2868 msec
- C#(unsafe)
- 2506 msec
- C++
- 2813 msec
この時点で、C++は遅いのですが、さっき書いたようにGCの関係上同じものを比較しているようには感じられないってことで、ソースコードを修正
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 |
byte *a = NULL; static void test2() { int w = 4321; int stride = (w + 3) & ~3; int h = 6789; if( !a ) { a = new byte[stride * h]; } memset(a,0,sizeof(byte)*stride * h ); byte* p0 = a; for (int y = 0; y < h; y++) { byte* p = p0 + y * stride; for (int x = 0; x < w; x++) { p[x] = (byte)(x ^ y); } } } void time(void (*action)(), int count = 100) { DWORD start = GetTickCount(); for (int i = 0; i < count; i++) action(); printf("%u\n", GetTickCount() - start); } int main() { time(test2); delete[] a; } |
1回だけ確保はカウントするけど最後の削除はカウントしないし、配列も使い回すという感じ。これだと、2016 msecに。
さらに改良するなら、ompで並列化して
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 |
byte *a = NULL; static void test2() { int w = 4321; const int stride = (w + 3) & ~3; int h = 6789; if( !a ) { a = new byte[stride * h]; } byte* p0 = a; #pragma omp parallel for for (int y = 0; y < h; y++) { byte* p = p0 + y * stride; for (int x = 0; x < w; x++) { p[x] = (byte)(x ^ y); } } } void time(void (*action)(), int count = 100) { DWORD start = GetTickCount(); for (int i = 0; i < count; i++) action(); printf("%u\n", GetTickCount() - start); } int main() { time(test2); delete[] a; } |
これだと625 msecとかになる。memsetも基本的には、処理外の部分の結果なのでクリアする必要がないということで使ってない。
ということで、単純に比較しても結局意味ない気はしてくる。C++ならC++なりの最適化なり高速化が使えるわけだし。
ちなみにC#の方も並列化すると、それぞれ1388 msec, 1082 msecとなった。(C#の並列化はtest関数外での並列化にしてる。というのも、unsafeの中ではなんかだめっぽい?、unsafeじゃないやつはロジック外の方が速かった(スレッドのコストが高い?
- C#
- 1388 msec
- C#(unsafe)
- 1082 msec
- C++
- 625 msec
という感じで、C++の利点は必要じゃない処理を結構省けるというところかもしれない。
ちなみに、「C#はunsafeの方が速いという幻想」によると、unsafeは境界値チェックをしないがために速いとのこと。なるほどね。