6

(明らかな構文の違いは別として)オブジェクトの複数のインスタンス(同じタイプ)を含むクラスと、そのタイプのオブジェクトの固定サイズの配列との間に効率の違いがあるかどうか疑問に思いました。

コード内:

struct A {
  double x;
  double y;
  double z;
};

struct B {
  double xvec[3];
};

実際には、Cスタイルの配列よりも優れたC++の代替であるboost::arraysを使用します。

これらのクラスは、メンバー関数の1つを一度呼び出すだけで作成されることが多いため、私は主に構築/破棄とそのようなdoubleの読み取り/書き込みに関心があります。

あなたの助け/提案をありがとう。

4

6 に答える 6

8

通常、これら2つの構造体の表現はまったく同じです。ただし、ユースケースに対して間違ったものを選択すると、パフォーマンスが低下する可能性があります。

たとえば、配列を使用してループ内の各要素にアクセスする必要がある場合は、次のように実行できます。

for (int i = 0; i < 3; i++)
    dosomething(xvec[i]);

ただし、配列がない場合は、コードを複製する必要があります。

dosomething(x);
dosomething(y);
dosomething(z);

これはコードの重複を意味します-どちらの方向にも進むことができます。一方では、ループコードが少なくなります。一方、最近のプロセッサでは、非常にタイトなループが非常に高速になる可能性があり、コードの重複によってIキャッシュが吹き飛ばされる可能性があります。

もう1つのオプションはスイッチです。

for (int i = 0; i < 3; i++) {
    int *r;
    switch(i) {
        case 0: r = &x; break;
        case 1: r = &y; break;
        case 1: r = &z; break;
    }
    dosomething(*r); // assume this is some big inlined code
}

これにより、i-cacheのフットプリントが大きくなる可能性は回避されますが、パフォーマンスに大きな悪影響があります。これをしないでください。

一方、コンパイラがあまり賢くない場合は、原則として、配列アクセスが遅くなる可能性があります。

xvec[0] = xvec[1] + 1;
dosomething(xvec[1]);

xvec[0]とxvec[1]は別個のものであるため、原則として、コンパイラーはxvec [1]の値をレジスターに保持できる必要があり、次の行で値を再ロードする必要はありません。ただし、一部のコンパイラは、xvec[0]とxvec[1]がエイリアスしないことに気付くほど賢くない可能性があります。この場合、個別のフィールドを使用すると、非常に高速になる可能性があります。

要するに、それはどちらかがすべての場合に速いということではありません。それはあなたがそれをどのように使うかと表現を一致させることについてです。

個人的には、xvecで動作するコードを最も自然にするものなら何でも使用することをお勧めします。せいぜい、マイクロベンチマークでしか捉えられないほど小さなパフォーマンスの違いしか生じないようなことを心配するのに、多くの人的時間を費やす価値はありません。

于 2011-01-30T15:51:49.800 に答える
5

MVC ++ 2010は、例のように2つのPOD構造体から読み取り/書き込みを行うためにまったく同じコードを生成しました。読み取り/書き込みのオフセットはコンパイル時に計算可能であるため、これは驚くべきことではありません。建設と破壊についても同じことが言えます。

実際のパフォーマンスに関しては、一般的なルールが適用されます。重要な場合はプロファイルを作成し、重要でない場合はプロファイルを作成します。なぜ気にするのでしょうか。

配列メンバーへのインデックス作成は、構造体のユーザーにとっては少し手間がかかるかもしれませんが、繰り返しになりますが、要素をより簡単に繰り返すことができます。

于 2011-01-30T15:42:59.557 に答える
2

決定できず、オプションを開いたままにしておきたい場合は、匿名のユニオンを使用できます。

struct Foo
{
    union
    {
        struct
        {
            double x;
            double y;
            double z;
        } xyz;
        double arr[3];
    };
};

int main()
{
    Foo a;
    a.xyz.x = 42;
    std::cout << a.arr[0] << std::endl;
}

一部のコンパイラは匿名構造体もサポートしています。その場合、そのxyz部分を省略できます。

于 2011-01-30T16:57:07.753 に答える
1

場合によります。たとえば、あなたが与えた例は、「昔ながらの」配列を支持する古典的な例です:数学の点/ベクトル(または行列)

  • 要素の数は固定されています
  • データ自体は通常、オブジェクト内でプライベートに保たれます
  • (もし?)それはインターフェースとしてクラスを持っているので、コンストラクターでそれらを適切に初期化することができます(そうでなければ、古典的な配列の初期化は私が本当に好きではないものです、構文的に)

このような場合(数学ベクトル/行列の例を使用)、各コンポーネントのコピー/貼り付けコードを記述する代わりにループできるため、常に内部でCスタイルの配列を使用することになりました。

しかし、これは特殊なケースです。私にとって、C++の最近の配列==STLベクトルでは、高速であり、nuthinについて心配する必要はありません':)

于 2011-01-30T15:24:56.520 に答える
1

違いは、変数をメモリに格納することです。最初の例では、コンパイラーはデータを整列させるためにパディングを追加できます。しかし、あなたの特定のケースでは、それは問題ではありません。

于 2011-01-30T19:14:46.857 に答える
-1

生の配列は、提示されているように、c ++配列よりも優れたキャッシュ局所性を提供しますが、配列の例の複数のオブジェクトに対する唯一の利点は、要素を反復処理できることです。

もちろん、本当の答えは、テストケースを作成して測定することです。

于 2011-01-30T15:25:10.693 に答える