9

これは私のベクトル構造体です:struct Vector{ float x, y; };

  • 値またはとして関数に渡す必要がありますconst Vector&か?
4

4 に答える 4

10

値渡しの場合、関数は、呼び出し元に影響を与えずにローカルで変更できるコピーを取得します。

const-reference で渡す場合、関数は読み取り専用参照のみを取得します。コピーは必要ありませんが、呼び出された関数はそれをローカルで変更できません。

構造体のサイズを考えると、コピーのオーバーヘッドは非常に小さくなります。したがって、呼び出された関数に最適/最も簡単なものを選択してください。

于 2011-03-12T17:05:27.227 に答える
7

このような小さな構造の場合、プラットフォームとコンパイラによっては、どちらかが最も効率的である可能性があります。

(C++ のvectorは通常 "動的配列" を意味するため、他のプログラマーには名前が少し紛らわしいかもしれません。どうVector2Dですか?)

于 2011-03-12T17:03:36.260 に答える
3

プラットフォームとコンパイラ、および関数がインラインかどうかに大きく依存します。

参照渡しの場合、構造体はコピーされず、そのアドレスのみがスタックに格納され、コンテンツは格納されません。値渡しの場合、内容がコピーされます。64 ビット プラットフォームでは、構造体のサイズは構造体へのポインターと同じです (より一般的な状況と思われる 64 ビット ポインターを想定しています)。そのため、参照渡しのメリットは、ここではあまり明確ではありません。

ただし、考慮すべき点がもう 1 つあります。構造体に float 値が含まれています。Intel アーキテクチャでは、関数の呼び出し前に FPU または SIMD レジスタに格納できます。そのような状況で、関数が参照によってパラメーターを取得する場合は、パラメーターをメモリにスピルし、このメモリへのアドレスを関数に渡す必要があります。これは非常に遅くなる可能性があります。それらが値で渡された場合、メモリへのコピーは必要ありません (より高速です)。また、一部のプラットフォーム (PS3) では、インライン関数の場合でも、コンパイラはこれらのスピルを削除するほどスマートではありません。

実際、マイクロ最適化のすべての質問と同様に、「良い答え」はありません。それはすべて、関数をどのように使用するか、およびコンパイラ/プラットフォームが何を望んでいるかに依存します。最善の方法は、測定 (またはツールを使用してアセンブリを分析) して、プラットフォームとコンパイラの組み合わせに最適なものを確認することです。

最後に、Q-Game の Jaymin Kessler の言葉を引用して締めくくりたいと思います。

2) 型がレジスタに収まる場合は、値で渡します。参照によってベクトル型を渡さないでください。特に、const 参照です。関数が最終的にインライン化された場合、GCC は参照にヒットしたときにメモリに移動することがあります。もう一度言います。使用している型がレジスタ (float、int、または vector) に収まる場合は、値以外で関数に渡さないでください。Visual Studio for x86 のような正常でないコンパイラの場合、スタック上のオブジェクトの位置合わせを維持できないため、align ディレクティブを持つオブジェクトは、参照によって関数に渡す必要があります。これは、修正されるか、Xbox 360 である可能性があります。マルチプラットフォームの場合、最小公分母に対応する必要がないように、typedef を渡すパラメーターを作成することをお勧めします。

次のコードを検討してください。

struct Vector { float x, y; };
extern Vector DoSomething1(Vector v);
extern Vector DoSomething2(const Vector& v);

void Test1()
{
    Vector v0 = { 1., 2. };
    Vector v1 = DoSomething1(v0);
}

void Test2()
{
    Vector v0 = { 1., 2. };
    Vector v1 = DoSomething2(v0);
}

コードの観点からすると、 と の唯一の違いは、Test1とが構造体を受け取るためにTest2使用する呼び出し規約です。(バージョン 4.2、アーキテクチャ x86_64) でコンパイルすると、生成されるコードは次のようになります。DoSomething1DoSomething2Vectorg++

.globl __Z5Test1v
__Z5Test1v:
LFB2:
    movabsq $4611686019492741120, %rax
    movd    %rax, %xmm0
    jmp __Z12DoSomething16Vector
LFE2:
.globl __Z5Test2v
__Z5Test2v:
LFB3:
    subq    $24, %rsp
LCFI0:
    movl    $0x3f800000, (%rsp)
    movl    $0x40000000, 4(%rsp)
    movq    %rsp, %rdi
    call    __Z12DoSomething2RK6Vector
    addq    $24, %rsp
    ret
LFE3:

Test1の場合、値はメモリからロードされると SIMD レジスタを介して渡されることがわかり%xmm0ます (したがって、前の計算の結果がすでにレジスタにある場合は、それらをロードする必要はありません。メモリー)。一方、 の場合Test2、値はスタックに渡されます (スタックにmovl $0x3f800000, (%rsp)プッシュ1.0f)。そして、それらが前の計算の結果である場合、%xmm0SIMD レジスタからそれらをコピーする必要があります。そして、それは非常に遅くなる可能性があります (値が利用可能になるまでパイプラインが停止する可能性があり、スタックが適切に整列されていない場合、コピーも遅くなります)。

したがって、関数がinlineでない場合は、const-reference ではなくコピーで渡すことをお勧めします。関数が実際にinlineである場合は、決定する前に生成されたコードを確認してください。

于 2011-03-12T17:26:21.260 に答える
0

参考までに。これは、構造体のコピーを作成してそれを渡す (値渡し) よりも効率的です。

唯一の例外は、プラットフォームが構造全体をレジスターに収めることができる場合です。

于 2011-03-12T17:03:48.880 に答える