インデックスメモリアドレッシング
ここまでは少量のデータへのアクセスを見てきましたが、インデックス化されたデータ(配列)ではメモリ割り当てとアクセスのモデルがどのように働くのでしょうか。その点を調べるために、多数の要素からなる配列を単純に反復処理して、各要素へのアクセスに要する時間を比較します。ここでのアクセスとは、データを読み取り、それを変数に格納し、それから再び配列に書き込むことを意味します。
実際に測定に使用する関数は、Javaでは次のようになります。
private void javaTraverse()
{
int temp = 0;
for( int i = 0; i < N_ELEMS; i++ )
{
temp = array[i];
array[i] = temp;
}
}
上記のコード(ArraysAccess.java内)を実行すると、次の結果が返されます。
Java traverse took 53
つまり、1千万個の要素からなる配列を反復処理するのに平均で53ミリ秒を要したことになります。これと同等のC++コードの実装(ダウンロードファイル内のArraysAccessプロジェクトに入っています)は、前の例とは少し異なります。なぜなら、C++ではデフォルトで1つの配列に要素を65,535個までしか入れられないからです。この制限に対処するため、この例でもWindows APIを使用し、GlobalAlloc関数を組み込んでいます。これにより、大きなメモリの割り当てが可能になります。
int main(int argc, char* argv[])
{
int * randoms;
HGLOBAL h = GlobalAlloc( GPTR, sizeof(int) * N_GENERATED );
randoms = (int *)h;
generate_randoms( randoms );
CStopWatch watch;
long double timeNative[N_ITERATIONS];
for( int i = 0; i < N_ITERATIONS; i++ )
{
watch.Start();
nativeTraverse(randoms);
watch.Stop();
timeNative[i] = watch.GetDuration();
}
printf( "C traversing took %lf\n", test_times(timeNative) );
GlobalFree( h );
return 0;
}
ご覧のように、GlobalAllocを使って1千万個のintを割り当ててから、これをJavaのときと同じやり方で反復処理しています。この演算の結果の平均は次のようになります。
C traversing took 10.857639
つまり、Javaより約5倍速いということです。ただし、このコードをコンパイルするときに最適化を無効にすると、Javaより2倍から3倍ほど遅くなります。
