Скорость передачи параметра в функцию по ссылке или глобально

Подскажите пожалуйста, должна ли теоретически существовать разница в скорости передачи параметров в функцию по ссылке(или указателю) или через глобальную переменную ?

Этот код показывает, что разница на уровне погрешности и даже по ссылке всегда чуть быстрее: запускал на 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 шт):

Автор решения: Alex Guteniev

Передача по ссылке лучше подлежит оптимизации, когда компилятор может отследить, что переменная не используется больше нигде, и применить no-aliasing оптимизации. В этом плане ещё эффективнее передача по значению.

Передача по ссылке, скорее всего, займёт место в регистре или на стеке. Однако, достать глобальную переменную может быть дороже (хотя это касается в основном Linux с position-independent code).

Но это всё мелочи. Главное, почему стоит избегать глобальные переменные -- потокобезопасность, реентрабельность, поддерживаемость. С первым может помочь замена глобальной переменной на thread-local, но thread local не очень эффективны.

→ Ссылка
Автор решения: Serge3leo

Да, должна. Зависит от используемого соглашения о вызовах и/или ключей оптимизации.

Если без глобальной оптимизации или inline-оптимизации и соглашениях о вызовах x64:

  • Передача по ссылке первых 4 аргументов будет производится на регистрах, которые в вашем тесте могут быть постоянно заполнены, т.е. затраты собственно на передачу аргумента будут равны 0. И, дополнительно, обращение к этим аргументам будет осуществляться короткой командой, типа ADD EAX, [RCХ];
  • Передача через глобальные переменные имеет тоже нулевые затраты. Но при обращении к ним, либо потребуется использовать более длинную (медленную) команду, типа ADD EAX, [local_int2], либо загружать её адрес в регистр (заметим, возможно, потребуется предварительно этот регистр сохранить и восстановить).

В части возможных неглобальных оптимизаций, компилятор не может глобальные переменные полностью удалить и перенести на регистры, согласно стандарта он вынужден иногда обновлять их значение в памяти.

P.S. При передаче 5 и 6 аргументов ситуация обратная.

→ Ссылка