2011-06-21 13 views
11

Sé que las versiones de posfijo de los operadores de incremento/disminución generalmente serán optimizadas por el compilador para los tipos incorporados (es decir, no se realizará ninguna copia), pero ¿es este el caso para iterator s?¿Se optimizará la ineficacia de los operadores postfix ++/- para los iteradores STL?

Básicamente son operadores sobrecargados, y podrían implementarse de varias maneras, pero dado que su comportamiento está estrictamente definido, pueden ser optimizados, y si es así, ¿son por compiladores any/many?

#include <vector> 

void foo(std::vector<int>& v){ 
    for (std::vector<int>::iterator i = v.begin(); 
     i!=v.end(); 
     i++){ //will this get optimised by the compiler? 
    *i += 20; 
    } 
} 
+0

Esta es una pregunta interesante, incluso si ** es ** una micro-optimización. – jpm

+0

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. –

+0

@Gene Estoy de acuerdo, y tengo el hábito de usar el pre-incremento cada vez que puedo. Solo tengo curiosidad :) –

Respuesta

9

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.

+0

Buen análisis. Espero que cualquier buen compilador de optimización realice el mismo. –

+0

Gracias por esa respuesta. Sospecho que @Mark tiene razón, aunque tengo curiosidad sobre otros compiladores. –

+0

No dude en probar con otros compiladores también :) – bdonlan

Cuestiones relacionadas