En el caso específico de std::vector
sobre la aplicación STL de GNU GCC (versión 4.6.1), no creo que habría una diferencia de rendimiento en niveles suficientemente altos de optimización.
La implementación de iteradores de reenvío en vector
es proporcionada por __gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>
. Veamos su constructor y Postfix ++
operador:
explicit
__normal_iterator(const _Iterator& __i) : _M_current(__i) { }
__normal_iterator
operator++(int)
{ return __normal_iterator(_M_current++); }
Y su ejemplificación en vector
:
typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;
Como se puede ver, se realiza internamente un incremento de sufijo en un puntero de ordinario, a continuación, pasa el original valor a través de su propio constructor, que lo guarda en un miembro local. Este código debe ser trivial para eliminar a través del análisis de valor muerto.
Pero, ¿está realmente optimizado? Vamos a averiguar. Código de ensayo:
#include <vector>
void test_prefix(std::vector<int>::iterator &it)
{
++it;
}
void test_postfix(std::vector<int>::iterator &it)
{
it++;
}
conjunto de salida (en -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
Como se puede ver, exactamente el mismo conjunto de salida es en ambos casos.
Por supuesto, esto puede no ser necesariamente el caso de iteradores personalizados o tipos de datos más complejos. Pero parece que, para el vector
específicamente, el prefijo y el postfijo (sin capturar el valor de retorno del sufijo postal) tienen un rendimiento idéntico.
Esta es una pregunta interesante, incluso si ** es ** una micro-optimización. – jpm
a menos que las operaciones del iterador tengan efectos colaterales, el compilador puede optimizar la versión de postincrement siguiendo la regla * as-if *. Si lo hace o no depende del compilador. Compilación Indebug, probablemente no será optimizado, ¿por qué hacer que la depuración sea más lenta? No veo el problema de desarrollar un buen hábito de usar postincremento solo cuando realmente lo necesite. –
@Gene Estoy de acuerdo, y tengo el hábito de usar el pre-incremento cada vez que puedo. Solo tengo curiosidad :) –