6

配列アクセスのパフォーマンスを std::vector のパフォーマンスと比較するテスト プログラムをセットアップしました。同様の質問をいくつか見つけましたが、私の特定の懸念に対処しているようには見えません。配列アクセスがベクトル アクセスよりも 6 倍高速であるように思われる理由について、私はしばらく頭を悩ませていました。結局のところ、これは Intel コンパイラ (v12) と最適化 (-O1 を超えるもので発生) の機能のようです。なぜなら、gcc v4.1.2 を使用すると std::vector でパフォーマンスが向上し、配列にgcc v4.4.4 で 2 倍の利点。Xeon X5355 コアを搭載した RHEL 5.8 マシンでテストを実行しています。余談ですが、イテレータは要素アクセスよりも高速であることがわかりました。

次のコマンドでコンパイルしています。

icpc -fast test.cc
g++44 -O3 test.cc

速度が劇的に向上した理由を説明できる人はいますか?

#include <vector>
#include <iostream>

using namespace std;

int main() {
  int sz = 100;
  clock_t start,stop;
  int ncycle=1000;
  float temp  = 1.1;

  // Set up and initialize vector
  vector< vector< vector<float> > > A(sz, vector< vector<float> >(sz,  vector<float>(sz, 1.0)));

  // Set up and initialize array
  float*** a = new float**[sz];
  for( int i=0; i<sz; ++i) {
    a[i] = new float*[sz];
    for( int j=0; j<sz; ++j) {
      a[i][j] = new float[sz]();
      for( int k=0; k<sz; ++k)
        a[i][j][k] = 1.0;
    }
  }

  // Time the array
  start = clock();
  for( int n=0; n<ncycle; ++n )
    for( int i=0; i<sz; ++i )
      for( int j=0; j<sz; ++j )
        for( int k=0; k<sz; ++k )
          a[i][j][k] *= temp;

  stop = clock();
  std::cout << "STD ARRAY: " << double((stop - start)) / CLOCKS_PER_SEC << " seconds"     << std::endl;

  // Time the vector
      start = clock();
  /*
  */
  for( int n=0; n < ncycle; ++n )
    for (vector<vector<vector<float> > >::iterator it1 = A.begin(); it1 != A.end();     ++it1)
      for (vector<vector<float> >::iterator it2 = it1->begin(); it2 != it1->end();     ++it2)
        for (vector<float>::iterator it3 =it2->begin(); it3 != it2->end(); ++it3)
          *it3 *= temp;
  /*
     for( int n=0; n < ncycle; ++n )
       for( int i=0; i < sz; ++i )
         for( int j=0; j < sz; ++j )
           for( int k=0; k < sz; ++k )
             A[i][j][k] *= temp;
  */

  stop = clock();
  std::cout << "VECTOR: " << double((stop - start)) / CLOCKS_PER_SEC << " seconds" <<     std::endl;


  for( int i=0; i<100; ++i) {
    for( int j=0; j<100; ++j)
      delete[] a[i][j];
  }
  for( int i=0; i<100; ++i) {
    delete[] a[i];
  }
  delete[] a;
  return 0;
}

解決した

コンパイラはループについて「すべてを知っている」ため、ベクトルの場合よりもループを最適化できるという Bo の指摘に注目した後、「temp」による乗算を「rand()」の呼び出しによる乗算に置き換えました。これにより競技場が平準化され、実際、 std::vector がわずかにリードしているようです。さまざまなシナリオのタイミングは次のとおりです。

ARRAY (flat): 111.15 seconds
ARRAY (flat): 0.011115 seconds per cycle
ARRAY (3d): 111.73 seconds
ARRAY (3d): 0.011173 seconds per cycle
VECTOR (flat): 110.51 seconds
VECTOR (flat): 0.011051 seconds per cycle
VECTOR (3d): 118.05 seconds
VECTOR (3d): 0.011805 seconds per cycle
VECTOR (flat iterator): 108.55 seconds
VECTOR (flat iterator): 0.010855 seconds per cycle
VECTOR (3d iterator): 111.93 seconds
VECTOR (3d iterator): 0.011193 seconds per cycle

要点は、ベクトルは配列と同じくらい高速であり、フラット化 (連続メモリ) してイテレータで使用するとわずかに高速であるように思われます。私の実験では、平均して 10,000 回以上の反復しか行われなかったので、これらはすべてほぼ同等であり、どちらを使用するかは、最も使いやすい方で決定する必要があると言えます。私の場合、それは「3d イテレータ」の場合です。

4

3 に答える 3

3

ここには黒魔術はありません。コンパイラがここでそれを確認するのは簡単すぎます

for( int n=0; n<ncycle; ++n )
   for( int i=0; i<sz; ++i )
     for( int j=0; j<sz; ++j )
       for( int k=0; k<sz; ++k )
          a[i][j][k] *= temp;

すべてがコンパイル時に認識されます。ループを簡単に展開して高速化できます。

于 2012-08-23T15:14:28.160 に答える
0

入れ子になった配列内のすべての要素は連続したメモリ位置にあるため、コンパイラが形式の式に遭遇すると、整数の乗算と加算のみを含む実際のインデックスを計算a[x][y][z]するコードを生成します。一方、ネストされた は実際にネストされています。式には、さらに 2 つのレベルの間接参照が含まれます。実際には、実際のデータを含むベクトルの配列へのポインターを含むオブジェクトが存在するためです。std::vectorv[x][y][z]v[x]std::vector

于 2013-04-30T10:06:48.173 に答える