在不实例化局部变量的情况下将结构传递给函数的效率

Efficiency of passing a struct to a function without instantiating a local variable

我最近了解到我可以通过将结构传递给 C++ 中的函数来执行以下操作:

(很抱歉没有在标题中为这个 "feature" 使用更合适的名称,请随时纠正我)

#include <iostream>

typedef struct mystruct{
    int data1;
    int data2;
} MYSTRUCT;

void myfunction( MYSTRUCT _struct ){
    std::cout << _struct.data1 << _struct.data2;
}

int main(){
    //This is what I recently learned
    myfunction( MYSTRUCT{2,3} );
    return 0;
}

这让我想知道这是否比实例化本地 MYSTRUCT 成本更低 并将其按值传递给函数?或者它只是一种方便的方法来做同样的事情,只是在之后立即删除临时变量?

例如添加这一行#define KBIG 10000000,是这样的:

std::vector<MYSTRUCT> myvector1;
for (long long i = 0; i < KBIG; i++) {
    myvector1.push_back(MYSTRUCT{ 1,1 });
}

始终比这更快:

std::vector<MYSTRUCT> myvector2;
for (long long i = 0; i < KBIG; i++) {
    MYSTRUCT localstruct = { 1,1 };
    myvector2.push_back(localstruct);
}

我尝试对其进行测试,但结果非常不一致,每次都在 9-12 秒左右徘徊。有时第一个会更快,有时则不会。当然,这可能是我测试时所有后台进程的原因。

如果速度有任何问题,请测量复制与采用 const ref(即 const MYSTRUCT& _struct)。进行测量时,请确保先执行 <1> <2>,然后执行 <2> <1> 以补偿缓存效应。

建议:避免使用_作为参数的第一个字符,因为一些保留字以它开头;另外,不要大写struct。

如果你想加速你的代码,我建议你通过 const 引用传递结构,如下所示:

void myfunction (const MYSTRUCT& _struct)
{
    std::cout << _struct.data1 << _struct.data2;
}

这将比按值传递快得多,因为它不会复制整个结构,而只会传递其地址。

(好的,在这种情况下不会有太大差异,因为你的结构只包含 2 个整数,但如果你有 >1000 字节(例如)那么会有显着差异)

此外,我建议您使用 std::vector<T>::emplace_back 而不是 std::vector<T>::push_back

std::vector<MYSTRUCT> myvector1;
for (long long i = 0; i < KBIG; i++)
{
    myvector1.emplace_back (1, 1);
}

emplace_back 将其参数转发给 mystruct 的构造函数,因此编译器不会创建无用的副本。

稍微简化并编译为汇编程序:

extern void emit(int);

typedef struct mystruct{
    int data1;
    int data2;
} MYSTRUCT;

__attribute__((noinline))
void myfunction( MYSTRUCT _struct ){
  emit(_struct.data1);
  emit(_struct.data2);
}

int main(){
    //This is what I recently learned
    myfunction( MYSTRUCT{2,3} );
    return 0;
}

使用 -O2 产量:

myfunction(mystruct):
        pushq   %rbx
        movq    %rdi, %rbx
        call    emit(int)
        sarq    , %rbx
        movq    %rbx, %rdi
        popq    %rbx
        jmp     emit(int)
main:
        movabsq 884901890, %rdi
        subq    , %rsp
        call    myfunction(mystruct)
        xorl    %eax, %eax
        addq    , %rsp
        ret

发生了什么事?

编译器意识到整个结构适合一个寄存器并以这种方式按值传递它。

故事的寓意:表达意图。让编译器操心细节。

如果你需要一份,你需要一份。故事结束。