0

私はパフォーマンスが重要なものを書いており、使用すると違いが生じるかどうかを知りたいと思っていました:

int test( int a, int b, int c )
{
    // Do millions of calculations with a, b, c
}

また

class myStorage
{
public:
  int a, b, c;
};

int test( myStorage values )
{
   // Do millions of calculations with values.a, values.b, values.c
}
  • これは基本的に同様のコードになりますか? クラス メンバーへのアクセスに余分なオーバーヘッドはありますか?

これは C++ の専門家には明らかであるため、現時点では非現実的なベンチマークを作成しようとはしません。

4

8 に答える 8

5

コンパイラはおそらくそれらを等化します。何らかの頭脳がある場合values.a、 、values.b、およびvalues.cをローカル変数またはレジスタにコピーします。これは、単純なケースでも発生します。

関連する格言:

  1. 時期尚早の最適化は、多くの悪の根源です。

  2. 今から 6 か月後の午前 1 時に読んでも、自分が何をしようとしていたかを理解できるように書いてください。

  3. ほとんどの場合、重要な最適化は、変数へのアクセス方法の小さな変更ではなく、アルゴリズムの再構築によってもたらされます。はい、例外があることは承知していますが、これはおそらく例外ではありません。

于 2010-05-19T16:38:39.747 に答える
4

これは時期尚早の最適化のように思えます。

そうは言っても、いくつかの違いと機会がありますが、それらは関数のパフォーマンスではなく、関数への複数の呼び出しに影響します。

まず、2 番目のオプションでは、定数参照として MyStorage を渡すことができます。その結果、コンパイルされたコードは、3 つの個別の値をプッシュするのではなく、(コンテナーにアクセスできるようにするために) 単一の値をスタックにプッシュする可能性があります。(ac に加えて) 追加のフィールドがある場合、MyStorage を参照としてではなく送信すると、コピー コンストラクターを呼び出して基本的にすべての追加フィールドをコピーすることになるため、実際にはより多くのコストがかかる可能性があります。これらはすべて、関数内ではなく、呼び出しごとのコストになります。

関数内で ab と c を使用して大量の計算を行っている場合、それらをどのように転送またはアクセスするかは問題ではありません。参照渡しの場合、初期コストはわずかに高くなる可能性があります (オブジェクトが参照渡しの場合、スタックではなくヒープ上にある可能性があるため)。低コストのアクセスを意味します。オブジェクトを値で渡した場合は、最初から値がスタックの近くにあるため、実際には問題ありません。

あなたが提供したコードについては、これらが唯一のフィールドである場合、おそらく違いはありません。「values.variable」は、「あるオブジェクトを検索してから別のアドレスにアクセスする」とは解釈されず、単にスタック内のオフセットとして解釈されます。

もちろん、これらの引数を購入しない場合は、関数の最初のステップとしてローカル変数を定義し、オブジェクトから値をコピーしてから、これらの変数を使用してください。実際に複数回使用する場合、このコピーの初期費用は問題になりません:)

于 2010-05-19T16:39:27.073 に答える
0

プログラムをプロファイリングできない場合は、コード フラグメントのアセンブリ言語を出力します。

一般に、アセンブリ コードが少ないということは、実行する命令が少なくなることを意味し、パフォーマンスが向上します。これは、プロファイラーが利用できない場合にパフォーマンスの概算を取得するための手法です。

アセンブリ言語のリストにより、実装間に違いがある場合はそれを確認できます。

于 2010-05-19T18:46:05.097 に答える
0

いいえ、CPU は使用する変数を何度もキャッシュします。

于 2010-05-19T16:40:04.593 に答える
0

多少のオーバーヘッドはあると思いますが、それほど多くはないかもしれません。オブジェクトのメモリ アドレスは、ヒープ メモリ オブジェクトを指すスタックに格納されるため、インスタンス変数にアクセスします。

変数 int をスタックに格納すると、値がすでにスタックにあり、マシンはスタックに移動して計算するため、非常に高速になります:)。

また、クラスのインスタンス変数の値をスタックに格納するかどうかにも依存します。test() 内の場合は、次のようにします。

int a = objA.a;
int b = objA.b;
int c = objA.c;

ほぼ同じ性能だと思います

于 2010-05-19T16:41:15.230 に答える
0

パフォーマンスが重要なコードを実際に作成していて、一方のバージョンが他方よりも高速であるべきだと考える場合は、両方のバージョンを作成してタイミングをテストします (適切な最適化スイッチでコンパイルされたコードを使用)。生成されたアセンブリ コードを表示することもできます。レジスタ スピルなど、非常に微妙なコード スニペットの速度に影響を与える可能性があるものは多数あります。

于 2010-05-19T16:42:22.327 に答える
0

関数を開始することもできます

int & a = values.a;
int & b = values.b;

ただし、コンパイラは、舞台裏でそれを行うのに十分なほどスマートである必要があります。一般に、構造体またはクラスを渡すことを好みます。これにより、多くの場合、関数の目的が明確になり、別のパラメーターを考慮に入れるたびにシグネチャを変更する必要がなくなります。

于 2010-05-19T16:43:12.340 に答える
0

以前の同様の質問と同様に、コンパイラとプラットフォームに依存します。違いがあるとしても、それは非常に小さいでしょう。

スタック上の値とオブジェクト内の値はどちらも、一般的にポインター (スタック ポインターまたはthisポインター) とオフセット (関数のスタック フレーム内の場所、またはクラス内の場所) を使用してアクセスされます。

違いが生じる可能性があるいくつかのケースを次に示します。

  • プラットフォームによっては、スタック ポインターが CPU レジスターに保持されている場合と、ポインターが保持されていない場合thisがあります。この場合、this(おそらくスタック上にある) アクセスには、追加のメモリ ルックアップが必要になります。

  • メモリの局所性は異なる場合があります。メモリ内のオブジェクトが 1 つのキャッシュ ラインよりも大きい場合、フィールドは複数のキャッシュ ラインに分散されます。関連する値のみをスタック フレームにまとめると、キャッシュの効率が向上する場合があります。

ただし、ここで「可能性」という言葉を頻繁に使用したことに注意してください。確認する唯一の方法は、それを測定することです。

于 2010-05-19T16:44:20.307 に答える