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ść.
To interesujące pytanie, nawet jeśli ** jest ** mikro-optymalizacją. – jpm
, 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. –
@Gene Zgadzam się i mam nawyk używania pre-inkrementacji, kiedy tylko mogę. Jestem tylko ciekawy :) –