Here are the two different for-each ranges to compare:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int main() { constexpr unsigned numElts = 7; unsigned values[numElts]{2, 4, 6, 8, 12, 21, 101}; for(unsigned idx = 0; idx < numElts; ++idx) { ++values[idx]; } for(auto & v : values) { ++v; } } |
The first for-each range is a classical loop to access one-by-one all the elements of a list and perform a basic modification to each element. The second for-each range mimics the exact same processing, but it's written using the new C++11 for-each range writing.
Now use C++ Insights [3] to see the differences between the two implementations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | int main() { constexpr const unsigned int numElts = 7; unsigned int values[7] = {2, 4, 6, 8, 12, 21, 101}; for(unsigned int idx = 0; idx < numElts; ++idx) { ++values[idx]; } { unsigned int (&__range1)[7] = values; for(unsigned int * __begin1 = __range1, *__end1 = __range1 + 7l; __begin1 != __end1; ++__begin1) { unsigned int & v = *__begin1; ++v; } } } |
Note that the C++98 does not change (i.e. only a minor change to clearly define the idx variable type).
Below is the assembly of the C++98 for-loop (i.e. use the online compiler godbolt [4]):
1 2 3 4 5 6 7 8 9 10 11 12 | mov DWORD PTR [rbp-4], 0 .L3: cmp DWORD PTR [rbp-4], 6 ja .L2 mov eax, DWORD PTR [rbp-4] mov eax, DWORD PTR [rbp-80+rax*4] lea edx, [rax+1] mov eax, DWORD PTR [rbp-4] mov DWORD PTR [rbp-80+rax*4], edx add DWORD PTR [rbp-4], 1 jmp .L3 .L2: |
Note how the gcc converts the C++11 for-each range to a classical C++98 for loop using pointer arithmetic.
Below is the assembly of the C++11 for-each range (i.e. use the online compiler godbolt [4]):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | lea rax, [rbp-80] mov QWORD PTR [rbp-32], rax mov rax, QWORD PTR [rbp-32] mov QWORD PTR [rbp-16], rax mov rax, QWORD PTR [rbp-32] add rax, 28 mov QWORD PTR [rbp-40], rax .L5: mov rax, QWORD PTR [rbp-16] cmp rax, QWORD PTR [rbp-40] je .L4 mov rax, QWORD PTR [rbp-16] mov QWORD PTR [rbp-48], rax mov rax, QWORD PTR [rbp-48] mov eax, DWORD PTR [rax] lea edx, [rax+1] mov rax, QWORD PTR [rbp-48] mov DWORD PTR [rax], edx add QWORD PTR [rbp-16], 4 jmp .L5 .L4: |
The C++11 implementation needs 9 assembly instructions for the C++98 implementation. But it requires 12 assembly instructions for the C++11 implementation. If you now consider all the loop (i.e. the loop content plus all declarations) then the difference is now 10 vs. 19.
References:
[1] C++98 for loop https://en.cppreference.com/w/cpp/language/for
[2] C++11 for-each range https://en.cppreference.com/w/cpp/language/range-for
[3] Andreas Fertig is the creator of C++ Insight, https://cppinsights.io/
[4] The online compiler from Matt Godbolt, https://godbolt.org/
[keywords: for loop, for-each range, c++98, c++11]
No comments:
Post a Comment