Will the inefficiency of the postfix ++/-- operators be optimised away for STL iterators?
I know that the postfix versions of the increment/decrement operators will generally be optimised by the compiler for built-in types (i.e. no copy will be made), but is this the case for iterator
s?
They are essentially just overloaded operators, and could be implemented in any number of ways, but since their behaviour is strictly defined, can they be optimised, and if so, are they by any/many compilers?
#include <vector>
void foo(std::vector开发者_如何学Python<int>& v){
for (std::vector<int>::iterator i = v.begin();
i!=v.end();
i++){ //will this get optimised by the compiler?
*i += 20;
}
}
In the specific case of std::vector
on GNU GCC's STL implementation (version 4.6.1), I don't think there would be a performance difference on sufficiently high optimization levels.
The implementation for forward iterators on vector
is provided by __gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>
. Let's look at its constructor and postfix ++
operator:
explicit
__normal_iterator(const _Iterator& __i) : _M_current(__i) { }
__normal_iterator
operator++(int)
{ return __normal_iterator(_M_current++); }
And its instantiation in vector
:
typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;
As you can see, it internally performs a postfix increment on an ordinary pointer, then passes the original value through its own constructor, which saves it to a local member. This code should be trivial to eliminate through dead value analysis.
But is it optimized really? Let's find out. Test code:
#include <vector>
void test_prefix(std::vector<int>::iterator &it)
{
++it;
}
void test_postfix(std::vector<int>::iterator &it)
{
it++;
}
Output assembly (on -Os
):
.file "test.cpp"
.text
.globl _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.type _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function
_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE:
.LFB442:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $4, (%eax)
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
.cfi_endproc
.LFE442:
.size _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.globl _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.type _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function
_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE:
.LFB443:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $4, (%eax)
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
.cfi_endproc
.LFE443:
.size _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.ident "GCC: (Debian 4.6.0-10) 4.6.1 20110526 (prerelease)"
.section .note.GNU-stack,"",@progbits
As you can see, exactly the same assembly is output in both cases.
Of course, this may not necessarily be the case for custom iterators, or more complex data types. But it appears that, for vector
specifically, prefix and postfix (without capturing the postfix return value) have identical performance.
精彩评论