First, here is the assembly of using the multiplication operator (I added the comments):
.LC0:
.string " "
main:
pushq %rbp
movl $10, %ebp // Part of loop
pushq %rbx
movl $1, %ebx // Part of loop
subq $8, %rsp
.L2:
movl %ebx, %esi
movl std::cout, %edi
addl %ebx, %ebx // Part of loop
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
movl $1, %edx
movl $.LC0, %esi
movq %rax, %rdi
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
subl $1, %ebp // Part of loop
jne .L2 // Part of loop
addq $8, %rsp
xorl %eax, %eax
popq %rbx
popq %rbp
ret
subq $8, %rsp
movl std::__ioinit, %edi
call std::ios_base::Init::Init()
movl $__dso_handle, %edx
movl std::__ioinit, %esi
movl std::ios_base::Init::~Init(), %edi
addq $8, %rsp
jmp __cxa_atexit
And here is the assembly using the shift operator:
.LC0:
.string " "
main:
pushq %rbp
movl $10, %ebp
pushq %rbx
movl $1, %ebx
subq $8, %rsp
.L2:
movl %ebx, %esi
movl std::cout, %edi
addl %ebx, %ebx
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
movl $1, %edx
movl $.LC0, %esi
movq %rax, %rdi
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
subl $1, %ebp
jne .L2
addq $8, %rsp
xorl %eax, %eax
popq %rbx
popq %rbp
ret
subq $8, %rsp
movl std::__ioinit, %edi
call std::ios_base::Init::Init()
movl $__dso_handle, %edx
movl std::__ioinit, %esi
movl std::ios_base::Init::~Init(), %edi
addq $8, %rsp
jmp __cxa_atexit
Which is exactly the same (I only compiled with GCC 5.3 for x86 using -O3
, so this may not be the case with other compilers and other architectures).
After looking at the link posted in the comments by Chris, the automatic highlighting makes it seems as if the shift operator requires additional instructions:
subl $1, %ebp
jne .L2
movl $10, %ebp // Actually not part of loop
movl $1, %ebx // Actually not part of loop
Compared to the multiplication, which is:
subl $1, %ebp
jne .L3
Which is different to the original assembly I posted (since both were exactly the same).
As mentioned in the comments by Revolver_Ocelot, the instructions
movl $10, %ebp
movl $1, %ebx
Are just setting the counters for the next loop, so the loop components actually compile to same assembly (for this compiler and architecture), as was shown by the separate comparison.
Update: Based on Flikk's comment saying the assembly will be different, here is an unoptimised version, which shows that they are the same.