C++ では、プリミティブ型を値で返すのではなく、参照で渡すことで効率が向上しますか?
3 に答える
[...] プリミティブ型を値で返す代わりに参照で渡すと、効率が向上しますか?
ありそうもない。まず第一に、別の方法をとるべき理由を示すデータがプロファイラーから得られない限り、プログラムを設計する際にパフォーマンスの問題について心配する必要はありません。最もシンプルなデザインと、意図を最もよく伝えるデザインを選択してください。
さらに、プリミティブ型は通常、安価にコピーできるため、これがアプリケーションのボトルネックになることはほとんどありません。また、これは最も単純なオプションであり、関数のインターフェイスを最も明確にするオプションであるため、値で渡す必要があります。
署名を見るだけで、次のような機能があることは明らかです。
void foo(int);
引数への参照を保存しません (その結果、ダングリング参照やポインターなどの問題が発生しません)、呼び出し元に表示される方法で引数を変更しません。
上記のいずれも、次のような関数シグネチャからは推測できません。
void f(int&); // May modify the argument! Will it? Who knows...
あるいは:
void f(int const&); // May store a reference! Will it? Who knows...
さらに、値による受け渡しは、潜在的なエイリアスが防止する最適化をコンパイラが実行できるようにすることで、パフォーマンスを向上させることさえあります。
もちろん、これはすべて、関数が戻った後にその引数の副作用が呼び出し元に表示されるように、関数内の引数を実際に変更する必要がないことを前提としています-またはその参照を保存します口論。
その場合は、もちろん、参照によって渡し、適切なconst
修飾子を使用する必要があります。
より広範な議論については、この Q&A on StackOverflowも参照してください。
一般に、パフォーマンス上の利点はなく、パフォーマンス コストが発生する可能性があります。次のコードを検討してください。
void foo(const int& a, const int& b, int& res) {
res = a + b;
res *= a;
}
int a = 1, b = 2;
foo(a, b, a);
コンパイラが add() のような関数に遭遇した場合、呼び出し例のように a と res が別名である可能性があると想定する必要があるため、グローバルな最適化を行わない場合、a をロードし、b をロードしてから a + b の結果を格納するコードを生成する必要があります。 res を再度ロードして乗算を実行してから、結果を res に格納します。
代わりに、次のように関数を記述した場合:
int foo(int a, int b) {
int res = a + b;
res *= a;
return res;
}
int a = 1, b = 2;
int c = foo(a, b);
次に、コンパイラは a と b をレジスタにロードし (またはそれらをレジスタに直接渡します)、レジスタで加算と乗算を行い、結果を返します (多くの呼び出し規約では、生成されたレジスタに直接返すことができます)。 .
ほとんどの場合、実際には foo の値による受け渡しバージョンのセマンティクスが必要であり、参照による受け渡しバージョンで可能なエイリアシング セマンティクスは実際にはサポートする必要はありません。参照バージョンによるパス/リターンを使用すると、実際のパフォーマンス ペナルティを支払うことになる可能性があります。
Chandler Carruth は、C++ Now でこれに触れた優れた講演を行いました。
これが当てはまるあいまいなアーキテクチャがいくつかあるかもしれませんが、組み込み型を返すことは、参照によって out パラメータを渡すことよりもパフォーマンスが低いことを認識していません。必要に応じて、関連するアセンブリをいつでも調べて比較できます。