Efficiency of passing a struct to a function witho

2019-09-11 22:37发布

问题:

I recently learned that I can do the following with passing a a struct to a function in C++:

(My apologies for not using a more appropriate name for this "feature" in the title, feel free to correct me)

#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;
}

This makes me wonder is this less costly than instantiating a local MYSTRUCT and passing it by value to the function? Or is it just a convenient way to do the same only that the temporary variable is eliminated right afterwards?

For example adding this line #define KBIG 10000000, is this:

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

Consistently faster than this:

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

I tried testing it, but the results were pretty inconsistent, hovering around 9-12 seconds for each. Sometimes the first one would be faster, other times not. Of course, this could be due to all the background processes at the time I was testing.

回答1:

Simplifying slightly and compiling to assembler:

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;
}

with -O2 yields:

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

What happened?

The compiler realised that the entire structure fits into a register and passed it by value that way.

moral of the story: express intent. Let the compiler worry about details.

If you need a copy, you need a copy. End of story.



回答2:

If speed is of any concern, take measurements of copying vs. taking const ref (i.e., const MYSTRUCT& _struct). When you do measurements, make sure you do them <1> <2>, then <2> <1> to compensate for cache effect.

Suggestions: avoid using _ as the first char of parameter, as some reserved words start with it; also, do not capitalize struct.



回答3:

If you want to speed up your code, I suggest you to pass the struct via const reference, as follow:

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

This will be much more faster than passing by value because instead of copying an entire struct, it will pass just its address.

(Ok, there will not be so much difference in this case, since your struct contains only 2 integers, but it you have >1000 bytes (for example) then there will be a notable difference)

Also, i suggest you to use to use std::vector<T>::emplace_back instead std::vector<T>::push_back

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

emplace_back forwards its arguments to the constructor of mystruct, so the compiler will not create an useless copy.