13

私はこのようなクラスを持っています:

//Array of Structures
class Unit
{
  public:
    float v;
    float u;
    //And similarly many other variables of float type, upto 10-12 of them.
    void update()
    {
       v+=u;
       v=v*i*t;
       //And many other equations
    }
};

Unit 型のオブジェクトの配列を作成します。そして、それらの update を呼び出します。

int NUM_UNITS = 10000;
void ProcessUpdate()
{
  Unit *units = new Unit[NUM_UNITS];
  for(int i = 0; i < NUM_UNITS; i++)
  {
    units[i].update();
  }
}

処理を高速化し、おそらくループを自動ベクトル化するために、AoS を配列の構造に変換しました。

//Structure of Arrays:
class Unit
{
  public:
  Unit(int NUM_UNITS)
  {
    v = new float[NUM_UNITS];
  }
  float *v;
  float *u;
  //Mnay other variables
  void update()
  {
    for(int i = 0; i < NUM_UNITS; i++)
    {
      v[i]+=u[i];
      //Many other equations
    }
  }
};

ループの自動ベクトル化に失敗すると、配列の構造のパフォーマンスが非常に低下します。50 ユニットの場合、SoA の更新は AoS よりわずかに高速ですが、100 ユニット以降では、SoA は AoS よりも遅くなります。300 ユニットでは、SoA はほぼ 2 倍悪くなります。100K ユニットでは、SoA は AoS よりも 4 倍遅くなります。キャッシュは SoA の問題かもしれませんが、パフォーマンスの違いがこれほど大きくなるとは思っていませんでした。cachegrind でプロファイリングすると、両方のアプローチで同様の数のミスが示されます。Unit オブジェクトのサイズは 48 バイトです。L1 キャッシュは 256K、L2 は 1MB、L3 は 8MB です。ここで何が欠けていますか?これは本当にキャッシュの問題ですか?

編集: gcc 4.5.2 を使用しています。コンパイラ オプションは -o3 -msse4 -ftree-vectorize です。

SoA で別の実験を行いました。配列を動的に割り当てる代わりに、コンパイル時に「v」と「u」を割り当てました。100K ユニットの場合、動的に割り当てられた配列を使用する SoA よりも 10 倍高速なパフォーマンスが得られます。ここで何が起こっているのですか?静的メモリと動的に割り当てられたメモリにこのようなパフォーマンスの違いがあるのはなぜですか?

4

4 に答える 4

10

この場合、配列の構造はキャッシュに適していません。

両方uv一緒に使用しますが、それらに 2 つの異なる配列がある場合、それらは 1 つのキャッシュ ラインに同時にロードされず、キャッシュ ミスによりパフォーマンスが大幅に低下します。

_mm_prefetchAoS表現をさらに高速化するために使用できます。

于 2012-07-23T16:56:59.060 に答える
1

プリフェッチは、実行時間のほとんどをデータの表示待ちに費やすコードにとって重要です。最新のフロント サイド バスには十分な帯域幅があり、プログラムが現在の負荷のセットよりもはるかに進んでいない限り、プリフェッチを安全に行うことができます。

さまざまな理由により、構造体とクラスは C++ で多数のパフォーマンスの問題を引き起こす可能性があり、許容レベルのパフォーマンスを得るにはさらに調整が必要になる場合があります。コードが大きい場合は、オブジェクト指向プログラミングを使用します。データが大きい (そしてパフォーマンスが重要な) 場合は、そうしないでください。

float v[N];
float u[N];
    //And similarly many other variables of float type, up to 10-12 of them.
//Either using an inlined function or just adding this text in main()
       v[j] += u[j];
       v[j] = v[j] * i[j] * t[j];
于 2013-11-02T18:23:48.360 に答える
0

確かに、ベクトル化を達成しなければ、SoA 変換を行うインセンティブはあまりありません。

__RESTRICT が事実上かなり広く受け入れられていることに加えて、gcc 4.9 では#pragma GCC ivdep想定されたエイリアシングの依存関係を解消することが採用されています。

明示的なプリフェッチの使用に関しては、それが有用である場合、もちろん、SoA ではさらに多くのプリフェッチが必要になる場合があります。主なポイントは、ページを先にフェッチすることで DTLB ミスの解決を加速することである可能性があります。これにより、アルゴリズムがより多くのキャッシュを消費する可能性があります。

OSに関する詳細を含め、詳細がなければ、「コンパイル時」の割り当てと呼ばれるものについて、インテリジェントなコメントを行うことはできないと思います。高いレベルで割り当てを行い、その割り当てを再利用するという伝統が重要であることは間違いありません。

于 2014-02-08T11:49:03.130 に答える