Скорость передачи параметра в функцию по ссылке или глобально
Подскажите пожалуйста, должна ли теоретически существовать разница в скорости передачи параметров в функцию по ссылке(или указателю) или через глобальную переменную ?
Этот код показывает, что разница на уровне погрешности и даже по ссылке всегда чуть быстрее: запускал на VS2019, release/O2, x86/x64
#include <iostream>
#include <string>
int global_int1;
int global_int2;
size_t global_size_t3;
unsigned long long global_uulong4;
std::string global_my_string1;
std::string global_my_string2;
void my_func_local(int& local_int1, int& local_int2, size_t& local_size_t3, unsigned long long& local_uulong4, std::string& local_my_string1, std::string& local_my_string2)
{
local_int1 = local_int2 + 10;
local_size_t3 = local_int1 + local_int2;
local_uulong4 = local_size_t3 + local_uulong4 + local_int1;
local_uulong4 = atoi(local_my_string1.c_str()) + atoi(local_my_string2.c_str()) + local_size_t3;
}
void my_func_global()
{
global_int1 = global_int2 + 10;
global_size_t3 = global_int1 + global_int2;
global_uulong4 = global_size_t3 + global_uulong4 + global_int1;
global_uulong4 = atoi(global_my_string1.c_str()) + atoi(global_my_string2.c_str()) + global_size_t3;
}
int main()
{
int clock1;
int clock2;
size_t cntr = 99999999;
int local_int1;
int local_int2;
size_t local_size_t3;
unsigned long long local_uulong4;
std::string local_my_string1;
std::string local_my_string2;
clock1 = clock();
for (size_t i = 0; i < cntr; i++)
{
local_int1 = i;
local_int2 = i;
local_size_t3 = i;
local_uulong4 = i;
local_my_string1 = std::to_string(i);
local_my_string2 = std::to_string(i);
my_func_local(local_int1, local_int2, local_size_t3, local_uulong4, local_my_string1, local_my_string2);
}
clock2 = clock();
std::cout << local_uulong4 << std::endl;
std::cout << "time_local:" << clock2 - clock1 << std::endl;
clock1 = clock();
for (size_t i = 0; i < cntr; i++)
{
global_int1 = i;
global_int2 = i;
global_size_t3 = i;
global_uulong4 = i;
global_my_string1 = std::to_string(i);
global_my_string2 = std::to_string(i);
my_func_global();
}
clock2 = clock();
std::cout << global_uulong4 << std::endl;
std::cout << "time_global:" << clock2 - clock1 << std::endl;
}
Ответы (2 шт):
Передача по ссылке лучше подлежит оптимизации, когда компилятор может отследить, что переменная не используется больше нигде, и применить no-aliasing оптимизации. В этом плане ещё эффективнее передача по значению.
Передача по ссылке, скорее всего, займёт место в регистре или на стеке. Однако, достать глобальную переменную может быть дороже (хотя это касается в основном Linux с position-independent code).
Но это всё мелочи. Главное, почему стоит избегать глобальные переменные -- потокобезопасность, реентрабельность, поддерживаемость. С первым может помочь замена глобальной переменной на thread-local, но thread local не очень эффективны.
Да, должна. Зависит от используемого соглашения о вызовах и/или ключей оптимизации.
Если без глобальной оптимизации или inline-оптимизации и соглашениях о вызовах x64:
- Передача по ссылке первых 4 аргументов будет производится на регистрах, которые в вашем тесте могут быть постоянно заполнены, т.е. затраты собственно на передачу аргумента будут равны 0. И, дополнительно, обращение к этим аргументам будет осуществляться короткой командой, типа
ADD EAX, [RCХ]; - Передача через глобальные переменные имеет тоже нулевые затраты. Но при обращении к ним, либо потребуется использовать более длинную (медленную) команду, типа
ADD EAX, [local_int2], либо загружать её адрес в регистр (заметим, возможно, потребуется предварительно этот регистр сохранить и восстановить).
В части возможных неглобальных оптимизаций, компилятор не может глобальные переменные полностью удалить и перенести на регистры, согласно стандарта он вынужден иногда обновлять их значение в памяти.
P.S. При передаче 5 и 6 аргументов ситуация обратная.