2011-06-21 12 views
11

Wiem, że wersje przyrostowe operatorów inkrementacji/dekrementacji będą zazwyczaj optymalizowane przez kompilator pod względem wbudowanych typów (tzn. Nie będzie wykonywana kopia), ale czy tak jest w przypadku iterator s?Czy nieefektywność postfiksów ++/- operatorów zostanie zoptymalizowana dla iteratorów STL?

Są one w zasadzie tylko przeciążone operatory, i mogą być realizowane w dowolnej liczbie sposobów, ale ponieważ ich zachowanie jest ściśle określona, ​​może one zostać zoptymalizowane, a jeśli tak, to są one żadnymi/wielu kompilatory?

#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

To interesujące pytanie, nawet jeśli ** jest ** mikro-optymalizacją. – jpm

+0

, chyba że operacje iteratora mają efekty uboczne, kompilator może zoptymalizować wersję postincrement po regule * as-if *. Niezależnie od tego, czy to zależy, czy nie, zależy od kompilatora. Kompilacja Indebug, prawdopodobnie nie zostanie zoptymalizowana, więc dlaczego wolisz debugowanie? Nie widzę problemu z rozwijaniem dobrego nawyku korzystania z postincrementu tylko wtedy, gdy faktycznie go potrzebujesz. –

+0

@Gene Zgadzam się i mam nawyk używania pre-inkrementacji, kiedy tylko mogę. Jestem tylko ciekawy :) –

Odpowiedz

9

W konkretnym przypadku std::vector na wdrożenie STL GNU GCC (wersja 4.6.1), nie sądzę, że będzie różnica wydajności na wystarczająco wysokim poziomie optymalizacji.

Implementacja dla przyszłych iteratorów pod numerem vector jest dostarczana przez __gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>. Spójrzmy na jej konstruktora i Postfix ++ operatora:

explicit 
    __normal_iterator(const _Iterator& __i) : _M_current(__i) { } 

    __normal_iterator 
    operator++(int) 
    { return __normal_iterator(_M_current++); } 

I jego instancji w vector:

typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; 

Jak widać, to wewnętrznie wykonuje przyrost postfix na zwykłym wskaźnikiem, a następnie przekazuje oryginał wartość poprzez własny konstruktor, który zapisuje go do lokalnego elementu. Kod ten powinien być trywialny, aby wyeliminować go poprzez analizę wartości bezwzględnej.

Ale czy jest to zoptymalizowane naprawdę? Dowiedzmy Się. Kod Test:

#include <vector> 

void test_prefix(std::vector<int>::iterator &it) 
{ 
    ++it; 
} 

void test_postfix(std::vector<int>::iterator &it) 
{ 
    it++; 
} 

montażowa (na -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 

Jak widać, dokładnie taki sam montaż jest odtwarzany w obu przypadkach.

Oczywiście może się tak nie stać w przypadku niestandardowych iteratorów lub bardziej złożonych typów danych. Wygląda jednak na to, że dla konkretnie vector, prefiks i postfiks (bez przechwytywania wartości zwracanej przyrostkiem Postfix) mają identyczną wydajność.

+0

Niezła analiza. Oczekuję, że każdy dobry kompilator optymalizacyjny wykona to samo. –

+0

Dzięki za tę odpowiedź. Podejrzewam, że @Mark ma rację, chociaż jestem ciekawy innych kompilatorów. –

+0

Zapraszam do testowania także z innymi kompilatorami :) – bdonlan

Powiązane problemy