整数乗算のパフォーマンスを比較する

この記事のテストプログラムの作成を開始したとき、同じ名前のコンパイラがVisual Studioを争わずに倒すのと同じように、IntelのCPUが両方のブレードにAMDを搭載することを内部的に予想していました。 しかし、すべてがそれほど単純なわけではありません。ソフトウェアテストの選択が影響したのでしょうか。



テストでは、2つの128ビット数の整数乗算を使用して、256ビットの結果を取得しました。 テストは10億回繰り返され、12〜85秒しかかかりませんでした。 使用したプロセッサは、AMD FX-8150 3.60GHzおよびIntel Core i5 2500 3.30GHzでした。 マルチスレッド、オーバークロックなし。



使用されたコンパイラーは、Intel Parallel Studio XEバージョン12.0.0.104ビルド20101006、その新しい生まれ変わり12.1.5.344ビルド20120612、Visual Studio 2010 SP1、および最新のVisual Studio 2012(MetroインターフェイスとCAPSLOCKメニュー付き)であり、C ++ 11.0リリース候補でもあります。 -O2オプションは忘れないでください。VisualStudioで有効になっています。 Intelではこれは必要ありません。デフォルトでは-O2で最適化され、Intelでは-O3オプションが有効になっています。



テスト自体を行います。 64ビットコードの場合、BN_WORDを__int64に等しくし、BN_DWORDを下位部分と上位部分に分割し、このエコノミーを乗算するには、これらのコンパイラでサポートされる_mul128という組み込み関数を使用する必要があることに同意します。 これらはすべて計画に含まれており、後で行われることになっています。 この記事の目的は、最適化コンパイラを比較することですが、32ビットと64ビットの乗算の速度を比較することではなく、1つの神話を解くことです。



#include <stdio.h> #include <windows.h> #define QUANTITY 4 typedef unsigned int BN_WORD; typedef unsigned __int64 BN_DWORD; void Mul(BN_WORD *C, BN_WORD *A, BN_WORD *B ) { BN_WORD Carry = 0; BN_WORD h = *(B++); int i, j; union { BN_DWORD sd; BN_WORD sw[2]; } s; for( i = QUANTITY; i > 0; --i) { s.sd = (BN_DWORD) *(A++) * h + Carry; *C++ = s.sw[0]; Carry = s.sw[1]; } *C = Carry; for ( j = QUANTITY-1; j > 0; --j ) { A -= QUANTITY; h = *(B++); C -= QUANTITY-1; Carry = 0; for( i = QUANTITY; i > 0; --i ) { s.sd = (BN_DWORD) *(A++) * h + *C + Carry; *C++ = s.sw[0]; Carry = s.sw[1]; } *C = Carry; } } typedef void (*my_proc)(BN_WORD*, BN_WORD*, BN_WORD*); void put_addr(void) { FILE *f=fopen("tmp.$$$", "wb"); my_proc proc = Mul; fwrite(&proc, 1, sizeof(proc), f); fclose(f); } my_proc get_addr(void) { FILE *f=fopen("tmp.$$$", "rb"); my_proc proc = NULL; fread(&proc, 1, sizeof(proc), f); fclose(f); return proc; } int main(void) { int i,j; LARGE_INTEGER lFrequency, lStart, lEnd; double dfTime1; BN_WORD A[QUANTITY], B[QUANTITY], C[QUANTITY*2]; BN_WORD RES[QUANTITY*2]={0xd7a44a41, 0xf6e4895c, 0x1624c878, 0x35650795, 0xa55cb22f, 0x861c7313, 0x66dc33f7, 0x479bf4db }; // ,   inline    Mul   main void (*mul)( BN_WORD *C, BN_WORD *A, BN_WORD *B ); put_addr(); mul = get_addr(); for( i=0; i<QUANTITY; ++i) { A[i] = B[i] = 0x87654321; } QueryPerformanceFrequency(&lFrequency); QueryPerformanceCounter(&lStart); for( i=0; i<1000; ++i) { for( j=0; j<1000000; ++j) { mul(C, A, B); } if (memcmp(RES, C, sizeof(RES))!=0) { printf("Something wrong!\n"); } } QueryPerformanceCounter(&lEnd); dfTime1 = (double)(lEnd.QuadPart - lStart.QuadPart) / (double)lFrequency.QuadPart; printf("Time = %g sec\n", dfTime1); }
      
      







結果を表に示します。

AMD FX-8150 3.60GHz 64ビット AMD FX-8150 3.60GHz 32ビット Core i5-2500 3.30GHz 64ビット Core i5-2500 3.30GHz 32ビット
Intel Parallel Studio XE 12.0.0.104 Build 20101006 22.6235秒 25.913秒 13.0921秒 23.1986秒
Intel Parallel Studio XE 12.1.5.344ビルド20120612 22.2398秒 26.0347秒 12.9242秒 23.1603秒
Visual Studio 2010 C ++ 10.0 SP1 22.5853秒 84.1714秒 12.4991秒 53.633秒
Visual Studio 2012 C ++ 11.0リリース候補 22.2952秒 72.8279秒 12.6212秒 47.1136秒




64ビットコードでは、3つすべてのコンパイラでほぼ同じ結果が得られます。



Intelは32ビットで大幅に勝ち、VS2010はそれに遅れをとり、最新のVS2012は着実に成長していますが、Intelからはほど遠いものです。



AMDとCore i5の作業速度を比較することも興味深いです。 7000ルーブルの同様の価格で、プロセッサーはIntelコンパイラーの32ビットアプリケーションで同様のパフォーマンスを示します。 シングルスレッドテストでは、常にCore i5の利点が期待されていました。 8個のAMDコアのフルパワーを使用するマルチスレッドテストを作成する計画があります。 そして、彼は勝つ可能性があります。彼は8つの整数演算コア(ただし4つの浮動小数点コア)とCore i5の4つのコアを持っているため、マルチスレッドは(私の)Core i5をサポートしません。



別の重要な結論は、コンパイラー製造業者が同様の結果を達成しながら、最適化64ビットコンパイラーの作成に全力を注いできたことを示唆しています。 プロセッサメーカーもすべての力を64ビットプラットフォームに投入しましたが、IntelはAMDを大きく上回りました。



もう1つの興味深い事実は、IntelコンパイラーがIntelでのみ正常に動作するコードを作成し、AMDで嘆かわしいパフォーマンスを示す(2倍以上遅い)という神話が暴かれていることです。 32ビットコードのIntelコンパイラーは、AMDからIntel CPUに切り替えたときにほぼ同じ結果をもたらすことは簡単にわかりますが、VSコンパイラーは増加しますが、VSのコードを使用する場合、Core i5の大幅な高速化はこれまでのところ不可能でした。 (本当に、なぜ?)



UPD1。 この記事の第1版では、巧妙な最適化によりVS2010が乗算を実行せず、コードが5.5倍速くなったという事実により、エラーが入り込みました。 これで、ソースが修正され(f-ju Mulのmulポインターが導入され、コンパイラーを欺くためにポインターがファイルに書き込まれる/読み取られる)、結果が更新されます。 また、_WORDは最初のコメントから判断して、明確にするためにBN_WORDに修正されています。



All Articles