Обозначают ли переменные ссылки (псевдоним) стоимость времени выполнения? - PullRequest
10 голосов
/ 15 апреля 2010

Может быть, это особенность компилятора. Если так, то как насчет gcc (g ++)? Если вы используете переменную ссылку / псевдоним, как это:

int x = 5;
int& y = x;
y += 10;

Требуется ли на самом деле больше циклов, чем если бы мы не использовали ссылку.

int x = 5;
x += 10;

Другими словами, изменяется ли машинный код или «псевдоним» возникает только на уровне компилятора?

Это может показаться глупым вопросом, но мне любопытно. Особенно в том случае, когда, возможно, было бы удобно временно переименовать некоторые переменные-члены просто для того, чтобы математический код был немного легче для чтения. Конечно, мы не говорим о узком месте здесь ... но это то, что я делаю, и поэтому мне просто интересно, есть ли какая-то "реальная" разница ... или это только косметика.

Ответы [ 6 ]

9 голосов
/ 15 апреля 2010

Может рассматриваться как псевдоним, но не с точки зрения эффективности. Под капотом указатель - указатель с более приятным синтаксисом и более высокими гарантиями безопасности. Следовательно, у вас есть штраф за выполнение операции разыменования. Если компилятор не оптимизирует его, но я бы на это обычно не рассчитывал.

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

4 голосов
/ 15 апреля 2010

Обе эти функции компилируются в один и тот же код в g++, даже используя -O1. (Я добавил оператор return, чтобы расчеты не были полностью исключены.)

Нет указателя, только ссылка. В этом тривиальном примере разницы в производительности не было. Тем не менее, это не гарантирует, что так будет всегда (без разницы в производительности) при любом использовании эталона.

int f()
{
    int x = 5;
    x += 10;
    return x;
}

.

int f()
{
    int x = 5;
    int & y = x;
    y += 10;
    return y;
}

Ассемблер:

movl    $15, %eax
ret
4 голосов
/ 15 апреля 2010

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

Однако, в общем случае ссылки реализуются через указатели. Тем не менее, хороший компилятор будет использовать фактический указатель только для реализации ссылки в ситуациях, когда фактическая привязка определяется во время выполнения. Если привязка известна во время компиляции (и типы совпадают), компилятор обычно реализует ссылку как альтернативное имя для того же объекта, и в этом случае не будет никакого снижения производительности для доступа к объекту через ссылку (по сравнению с это через его оригинальное название).

Ваш пример - один из тех, когда от ссылки не следует ожидать снижения производительности.

4 голосов
/ 15 апреля 2010

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

3 голосов
/ 17 июля 2015

Я сравнил 2 программы на Gnu / Linux.Только вывод GCC показан ниже, но результаты лязга приводят к идентичным выводам.

Версия GCC: 4.9.2

Версия Clang: 3.4.2

Программы

1.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    printf("%d\n", x);
    return 0;
}

2.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    int & y = x;
    printf("%d\n", y);
    return 0;
}

Тест

Попытка 1: без оптимизации

gcc -S --std=c++11 1.cpp

gcc -S --std=c++11 2.cpp

1.cpp была получена сборкакороче.

Попытка 2: Оптимизация на

gcc -S -O2 --std=c++11 1.cpp

gcc -S -O2 --std=c++11 2.cpp

Полученная сборка была полностью идентична.

Выход сборки

1.cpp, без оптимизации

    .file   "1.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -4(%rbp)
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp, без оптимизации

    .file   "2.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -12(%rbp)
    leaq    -12(%rbp), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    (%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

1.cpp, с оптимизацией

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp, с оптимизацией

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

Заключение

При работе с оптимизированным выходом GCC не требуется никаких затрат времени выполнения.То же самое относится и к clang (протестировано с версией 3.4.2): когда оптимизация включена, сгенерированный код сборки идентичен в обеих программах.

0 голосов
/ 15 апреля 2010

Да, разыменование указателя за ссылкой влечет за собой дополнительные затраты времени выполнения, но, вероятно, незначительно.Напишите код любым удобным для чтения способом и четко сформулируйте семантику, к которой вы стремитесь, а затем запустите его в профилировщике, если производительность является проблемой (узкое место редко бывает таким, как вы думаете).Если вы используете MacOS, Shark - это просто фантастика.

...