25

重複の可能性:
基本データ型の値渡しと参照渡しのどちらが良いですか?
参照によって単純な型を渡さない理由は?

2 つのシナリオがあり、それぞれに 2 つの同一の関数があり、1 つは参照によってパラメーターを渡し、もう 1 つは値によって渡されるといういくつかのテストを行いました。文字列を使用したシナリオでは、大幅なパフォーマンスの向上が見られました (文字列のコピーが作成され、コンストラクターが呼び出されるため) が、long を使用したテストでは、参照によって値を渡すときにパフォーマンスの向上は見られませんでした。実際、パフォーマンスが悪化することもありました。

これはプリミティブ型で予想されますか? それらを参照渡しする意味はありませんか?

参照によって使用されていないときにプリミティブ型のコピーが作成されることを期待していたため、パフォーマンスがわずかに向上することが期待されていました。

4

7 に答える 7

40

プリミティブ型を値で渡すと、最高のパフォーマンスが得られます。それの訳は:

  • プリミティブは blitable であるため、コピーのコストはサイズによって異なります
  • プリミティブは小さく、ほとんどの環境で参照よりも大きくなりdoubleますlong long
  • pass-by-value はエイリアシングを回避し、オプティマイザーが実際にそのことを実行できるようにします

この最後の点は見過ごされがちですが、大きな違いを生む可能性があります。

于 2012-12-23T17:46:10.563 に答える
11

はい、それは予想される動作です。参照によってパラメーターを渡す場合、実際には変数のアドレスを渡しています (ポインターの場合と同様)。通常、アドレスは 4 バイトまたは 8 バイトの整数であるため、プリミティブ型がそれよりも大きくない限り、パフォーマンスの向上は得られません (さらに大きくても、おそらく向上しないでしょう)。

于 2012-12-23T17:45:20.213 に答える
8

現代のコンパイラは非常に巧妙なので、関数が「隠されている」(つまり、コードの生成時にコンパイラが認識できないものの一部である) 場合、まったく違いがない可能性があります。ただし、コンパイラが指示に従う場合、単純な型を参照として渡すことで大きな違いが生じる可能性があります。特に、値がコード内で何度も更新される場合。

私が働いていた場所で、次のようなコードを見ました。

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     len = 0;
     while (list)
     {
         len++;
         list = list->next;
     }
 }

コードを次のように変更します。

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     int tempLen = 0;
     while (list)
     {
         tempLen++;
         list = list->next;
     }
     len = tempLen;
 }

コード全体が約 30% 速く実行され、多くの場所から呼び出されました (途中で if 条件があったと思うので、単に長さを追跡することはできませんでした)。また、これは API 関数の一部であるため、関数のシグネチャを変更することはできませんでした。

参照を使用すると速度が低下した理由は、メモリからレジスタへのロード、レジスタのインクリメント、およびメモリへのストア レジスタである、参照値が更新されるたびにコンパイラが参照値に書き込むためです。このtempLenソリューションを使用すると、コンパイラーはレジスターを使用できるようになり、はるかに高速になります。

于 2012-12-23T18:16:34.533 に答える
4

C++ では、参照はポインターを使用する便利な方法です。ポインタを使用すると、追加の間接化が追加されます。プリミティブ型のコピーは、ポインターのコピーと同じくらい安価です。そのため、参照によって渡されるプリミティブ型は少し遅くなります

于 2012-12-23T17:44:45.433 に答える
2

参照によって値を渡す場合、関数は値を取得するために逆参照する必要があります。値を変更するたびに、メモリの場所に書き込むため、逆参照も発生する必要があります。コンパイラーは、何かが参照場所に戻されない場合を理解できると思います。そのため、値はレジスター上で変更され、必要なときに戻されますが、これがどれほど強力かはわかりません。

そのため、パラメーターを値で渡すときに存在しない間接ステップがあり、パフォーマンスが低下する可能性がありますが、コンパイラーの最適化が行われるため、実際にはぼんやりしています。ポインタを渡すという事実について考えてみてください。値が必要になるたびに、スタックからポインタをフェッチしてから、ポイントされた値をフェッチする必要があります (したがって、2 つのアクセスがあります) が、通常のパラメータでは 1 つしかありません。

いずれにせよ、パフォーマンスとは明らかに異なる目的で参照が使用されます。

于 2012-12-23T17:46:02.060 に答える
1

タグを使用したためc、ポインターについて話していると思います (C++ からの明示的な参照ではありません)。

ポインターを使用すると、ポインターとポイントされた値の 2 つのメモリ アクセスが可能になります。そのため、パフォーマンスの特別な向上はありません。さらに、コンパイラは値を使ってより多くの最適化を行うことができます。たとえば、エイリアシングの問題はありません。

于 2012-12-23T17:45:24.153 に答える
1

これはプリミティブ型で予想されますか?

私は絶対に言うでしょう。コンストラクターがないため、呼び出す必要はありません。

それらを参照渡しする意味はありませんか?

あります: 出力パラメーターが必要な場合、C++ では、ポインターを渡すよりも参照渡しの方が適切であると見なされます。

参照によって使用されていないときにプリミティブ型のコピーが作成されることを期待していたため、パフォーマンスがわずかに向上することが期待されていました。

参照渡しは通常、ポインターを使用して実装されるため、コンパイラーは、値または値へのポインターのいずれかをスタックにプッシュするコードを発行する必要があります。

于 2012-12-23T17:46:04.230 に答える