9

这是我的向量结构:struct Vector{ float x, y; };

  • 我应该按值还是按方式将它传递给函数const Vector&
4

4 回答 4

10

如果按值传递,函数将获得一个副本,它可以在本地修改而不影响调用者。

如果您通过 const-reference 传递,该函数将仅获得只读引用。不涉及复制,但被调用函数不能在本地修改它。

给定结构的大小,复制开销将非常小。因此,选择对被调用函数最好/最简单的方法。

于 2011-03-12T17:05:27.227 回答
7

对于这样一个小结构,任何一个都可能是最有效的,具体取决于您的平台和编译器。

(这个名字可能会让其他程序员有点困惑,因为C++ 中的向量通常意味着“动态数组”。怎么样Vector2D?)

于 2011-03-12T17:03:36.260 回答
3

我真的取决于你的平台和编译器,以及函数是否内联。

当通过引用传递时,结构不会被复制,只是它的地址存储在堆栈上,而不是内容。当按值传递时,内容被复制。在 64 位平台上,结构的大小与指向结构的指针相同(假设 64 位指针似乎是更常见的情况)。因此,通过引用传递的好处在这里并不十分清楚。

但是,还有另一件事需要考虑。您的结构包含浮点值。在 Intel 架构上,它们可以在调用函数之前存储在 FPU 或 SIMD 寄存器中。在这种情况下,如果函数通过引用获取参数,那么它们将不得不被溢出到内存中,并将该内存的地址传递给函数。这可能真的很慢。如果它们是按值传递的,则不需要复制到内存(更快)。还有一个平台(PS3),即使是内联函数,编译器也不够聪明,无法删除那些溢出。

事实上,就像每一个微优化问题一样,没有“好的答案”,这完全取决于你对函数的用途,以及你的编译器/平台想要什么。最好的办法是测量(或使用工具分析程序集)来检查最适合您的平台/编译器组合的方法。

最后,我将引用Q-Game 中的 Jaymin Kessler的话,他比我更精通这些主题:

2) 如果一个类型适合寄存器,则按值传递。不要通过引用传递向量类型,尤其是常量引用。如果函数最终被内联,GCC 偶尔会在遇到引用时进入内存。我再说一遍:如果您使用的类型适合寄存器(float、int 或 vector),则不要将其传递给函数,而是通过值。对于像 Visual Studio for x86 这样的非正常编译器,它不能保持堆栈上对象的对齐,因此具有对齐指令的对象必须通过引用传递给函数。这可能是固定的或 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从代码的角度来看, and之间的唯一区别是and用于接收结构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,值就会通过%xmm0SIMD 寄存器传递(因此,如果它们是先前计算的结果,它们将已经在寄存器中,并且不需要从记忆)。另一方面,在 的情况下Test2,值在堆栈上传递(movl $0x3f800000, (%rsp)压入1.0f堆栈)。如果它们是先前计算的结果,则需要从%xmm0SIMD 寄存器中复制它们。这可能真的很慢(它很可能会停止管道直到值可用,如果堆栈没有正确对齐,复制也会很慢)。

因此,如果您的函数不是 inline,则更喜欢通过副本而不是 const-reference 传递。如果该函数确实是inline,请在下定决心之前查看生成的代码。

于 2011-03-12T17:26:21.260 回答
0

作为参考。这比复制结构并传递它(即按值传递)更有效。

唯一的例外是您的平台是否可以将整个结构放入寄存器中。

于 2011-03-12T17:03:48.880 回答