2016-07-05 4 views
0

Недавно я узнал, что я могу сделать следующее с переходом аа-структуру в функцию в C++:Эффективность перехода на структуру к функции без создания экземпляра локальной переменной

(Мои извинения за не используя более подходящее имя для эта «особенность» в названии, не стесняйтесь исправлять меня)

#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 секунд для каждого. Иногда первый был бы быстрее, иногда - нет. Конечно, это может быть связано со всеми фоновыми процессами во время тестирования.

+0

Итак, вывод состоит в том, что нет большой разницы в производительности? Если вы хотите точно знать, постройте с включенной оптимизацией и сравните сгенерированный код для обеих альтернатив. –

ответ

3

Упрощая немного и компиляции на ассемблере:

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 $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 

Что случилось?

Компилятор понял, что вся структура вписывается в регистр и передает его таким образом.

моральный характер: выразить намерение. Пусть компилятор беспокоится о деталях.

Если вам нужна копия, вам нужна копия. Конец истории.

1

Если скорость вызывает какое-либо беспокойство, выполните измерения копирования или принятия const ref (т. Е. const MYSTRUCT& _struct). Когда вы делаете измерения, убедитесь, что вы их делаете < 1> < 2>, затем < 2> < 1> для компенсации эффекта кеша.

Предложения: не используйте _ в качестве первого символа параметра, так как некоторые зарезервированные слова начинаются с него; также не капитализируйте структуру.

+0

неправильный. почему вы считаете, что ссылка быстрее? вы не знаете, как оптимизатор приблизится к коду. Полученный ассемблер часто не имеет никакого отношения к исходному коду. –

+0

Отредактировано: выполнить измерения вместо того, чтобы принимать только const ref. Тем не менее, есть причина, по которой мы склонны принимать const ref для реальных объектов (я считаю, что пример OP был именно таким - пример, а не копия фактической структуры. Возможно, я ошибаюсь - комментарий от OP, безусловно, будет помогите очистить это.). Не утверждая, что он всегда будет быстрее, просто, что это будет первое, что я проверил бы для реальной структуры. – lorro

0

Если вы хотите ускорить свой код, я предлагаю вам пройти-структуру с помощью константной ссылки, следующим образом:

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

Это будет гораздо быстрее, чем передача по значению, потому что вместо копирования всего struct, он будет передавать только его адрес.

(Хорошо, что не будет так много разницы в этом случае, так как ваша структура содержит только два целых числа, но у вас есть> 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, поэтому компилятор не будет создавать бесполезные копии.

+0

неправильный. это не «намного быстрее». вы не можете рассуждать об оптимизации, которые сделает компилятор. выразить намерение и оставить это на этом. –

+0

«Скорее всего, это будет быстрее»? –

+0

Абсолютно нет. –