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.
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.
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.
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
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;
}
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.
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
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With