Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do variable references (alias) incure runtime cost?

Maybe this is a compiler specific thing. If so, how about for gcc (g++)? If you use a variable reference/alias like this:

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

Does it actually require more cycles than if we didn't use the reference.

int x = 5;
x += 10;

In other words, does the machine code change, or does the "alias" happen only at the compiler level?

This may seem like a dumb question, but I am curious. Especially in the case where maybe it would be convenient to temporarily rename some member variables just so that the math code is a little easier to read. Sure, we're not exactly talking about a bottleneck here... but it's something that I'm doing and so I'm just wondering if there is any 'actual' difference... or if it's only cosmetic.

like image 856
cheshirekow Avatar asked Apr 15 '10 00:04

cheshirekow


5 Answers

It may be treated as an alias, but not in terms of efficiency. Underneath the hood, a reference is a pointer with a nicer syntax and higher safety guarantees. Hence you've got a "dereference" operation runtime penalty. UNLESS the compiler optimizes it, but I wouldn't count on that usually.

In case of the question of whether the compiler will optimize it or not, there's no other way than to look at the generated assembly.

like image 139
Kornel Kisielewicz Avatar answered Oct 11 '22 22:10

Kornel Kisielewicz


It is true that in most cases references implement the concept of "alias", an alternative name for the object they are bound to.

However, in general case references are implemented through pointers. Nevertheless, a good compiler will only use an actual pointer to implement reference in situations when the actual binding is determined at run-time. If the binding is known at compile time (and the types match), the compiler will normally implement reference as an alternative name for the same object, in which case there will be no performance penalty for accessing the object through the reference (compared to accessing it through its original name).

Your example is one of those when you should expect no performance penalty from the reference.

like image 42
AnT Avatar answered Oct 11 '22 22:10

AnT


I compared 2 programs on Gnu/Linux. Only GCC output is shown below, but clang results lead to identical conclusions.

GCC version: 4.9.2

Clang version: 3.4.2

The programs

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

The test

Attempt 1: No optimizations

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

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

1.cpp's resulting assembly was shorter.

Attempt 2: Optimizations on

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

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

The resulting assembly was completely identical.

The assembly output

1.cpp, no optimization

    .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, no optimization

    .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, with optimizations

    .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, with optimizations

    .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

Conclusion

There is no runtime cost when it comes to optimized GCC output. Same goes with clang (tested with version 3.4.2): when optimizations are on, the generated assembly code is identical in both programs.

like image 22
Hello World Avatar answered Oct 11 '22 21:10

Hello World


The only way to know for sure is to compile it and examine the output of the compiler. Generally, the overhead for a reference is similar to the overhead for a pointer, because pointers are usually how references are implemented. However, given the simple case you're showing, I believe the reference would be optimized away.

like image 45
Billy ONeal Avatar answered Oct 11 '22 21:10

Billy ONeal


Both of these functions compile to exactly the same code in g++, even just using -O1. (I added the return statement to ensure that the calculation wasn't eliminated entirely.)

There is no pointer, only a reference. In this trivial example there was no performance difference. That's no guarantee that this would always be the case (no performance difference) for all uses of reference, though.

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

.

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

Assembler:

movl    $15, %eax
ret
like image 38
CB Bailey Avatar answered Oct 11 '22 20:10

CB Bailey