C++のテスト
今度はC++で同様のことを試してみましょう。
void generate_randoms( int randoms[] )
{
for( int i = 0; i < N_GENERATED; i++ )
randoms[i] = rand();
}
厳密なことを言えば、Javaで生成される数値はC++で生成される数値と同じではありません。従って、両方の実装で同じ数値セットが使われるという保証はありません。もっとも、両者の差異はそれほどないはずです。
C++での計算処理はJavaと似ています。
void nativeCompute(int randoms[])
{
int result = 0;
for(int i = 2; i < N_MULTIPLY; i++)
{
for( int j = 0; j < N_GENERATED; j++ )
{
result = randoms[j] * i;
result++;
}
}
}
C++には演算の時間をミリ秒単位の精度で測定する標準的な方法がなさそうなので、このコードはWindowsプラットフォームに的を絞っています。QueryPerformanceCounter関数を使用すると、高精度のタイマーにアクセスできます。この関数を使用するため、このコードでは2つのメソッド(StartとStop)を持つ簡素な「ストップウォッチ」クラスを利用しています。この時間測定に基づいて、このクラスは2つのメソッドがそれぞれいつ呼び出されたかを記録し、ミリ秒単位の時間差を返します。
// Stop watch class.
class CStopWatch
{
public:
// Constructor.
CStopWatch()
{
// Ticks per second.
QueryPerformanceFrequency( &liPerfFreq );
}
// Start counter.
void Start()
{
liStart.QuadPart = 0;
QueryPerformanceCounter( &liStart );
}
// Stop counter.
void Stop()
{
liEnd.QuadPart = 0;
QueryPerformanceCounter( &liEnd );
}
// Get duration.
long double GetDuration()
{
return ( (liEnd.QuadPart - liStart.QuadPart) / long double(liPerfFreq.QuadPart) ) * 1000; //return result in milliseconds
}
private:
LARGE_INTEGER liStart;
LARGE_INTEGER liEnd;
LARGE_INTEGER liPerfFreq;
};
同様のパターンを利用して、ストップウォッチクラスでかかった時間の平均値を知ることができます。
long double test_times( long double diffs[] )
{
long double shortest = 65535;
long double longest = -1;
long double total = 0;
for( int i = 0; i < N_ITERATIONS; i++ )
{
if( shortest > diffs[i] )
shortest = diffs[i];
else if( longest < diffs[i] )
longest = diffs[i];
total += diffs[i];
}
total -= shortest;
total -= longest;
total /= ( N_ITERATIONS - 2 );
return total;
}
このパターンでは次の実装が必要です(IntMaths.cに入っています)。
int main(int argc, char* argv[])
{
int randoms[N_GENERATED];
generate_randoms( randoms );
CStopWatch watch;
long double timeNative[N_ITERATIONS];
for( int i = 0; i < N_ITERATIONS; i++ )
{
watch.Start();
nativeCompute(randoms);
watch.Stop();
timeNative[i] = watch.GetDuration();
}
printf( "C computing took %lf\n", test_times(timeNative) );
return 0;
}
上記のコードはサンプルコードに含まれているIntMathsプロジェクトに入っています。筆者のラップトップで実行すると、次の結果が返されます(測定値の単位はミリ秒)。
C computing took 0.001427
つまり、約10,000,000回の算術演算を実行するのに約1/1000ミリ秒を要したことになります。
Javaでは25msであるのに対して、C++では0.001msです。大変な違いがあります。ただし、C++コードのコンパイルではフルコンパイラとリンカで速度の最適化が行われていることを忘れてはなりません。最適化を無効にすれば、次の結果が返されます。
C computing took 70.179901
驚いたことに、Javaバージョンより3倍も遅いではありませんか。つまり、こういうことです。一見したところではC++の方が速いのですが、それはコンパイラがコードをうまく最適化する場合に限られるのです(この点は非常に重要です)。
今日の大多数のC++コンパイラは、生成するコードをかなりうまく最適化します。とはいえ、1/1000ミリ秒になるか70ミリ秒になるかはコンパイラに委ねられています。速さゆえにC++に切り替えようという人は、このことを忘れないでください。
