2011-06-21 7 views
11

増分/減算演算子の後置式の演算子は、一般に組み込み型のコンパイラーによって最適化されます(つまり、コピーは作成されません)が、これはiteratorの場合ですか?postfix ++/- 演算子の非効率性は、STLイテレータに対して最適化されますか?

これらは基本的にオーバーロードされた演算子であり、さまざまな方法で実装できますが、動作は厳密に定義されているため、に最適化されています。 GNU GCCのSTLの実装(バージョン4.6.1)にstd::vectorの特定の場合には

#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

**は**マイクロ最適化であっても興味深い質問です。 – jpm

+0

反復子操作で副作用が発生しない限り、コンパイラは* as-if *規則に従って最適化された更新後のバージョンを使用できます。それがコンパイラに依存するかどうか。 Indebugビルド、それはおそらく最適化されないでしょう、なぜデバッグを遅くするのですか?あなたが実際にそれを必要とするときにだけポストインクリメントを使う良い習慣を開発するという問題はありません。 –

+0

@Gene私は同意します。私はいつでもプリインクリメントを使用する習慣があります。私はちょうど興味があります:) –

答えて

9

、私は十分に高い最適化レベルでのパフォーマンスの違いがあるとは思いません。

vectorのフォワードイテレータの実装は、__gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>で提供されています。のは、そのコンストラクタと後置++オペレータを見てみましょう:

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

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

とそのインスタンス化:

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

あなたが見ることができるように、それは内部的に通常のポインタの後置インクリメントを行い、その後、元を渡します独自のコンストラクタを使用して値をローカルメンバーに保存します。このコードは、デッド・バリュー分析を通じて排除するのは簡単なことです。

本当に最適化されていますか?確認してみましょう。テストコード:(-Os上)

#include <vector> 

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

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

出力アセンブリ:

.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 

あなたが見ることができるように、まったく同じアセンブリは、両方のケースで出力されます。

もちろん、これは必ずしもカスタムイテレータやより複雑なデータ型の場合には当てはまりません。しかし、具体的には、vectorの場合、接頭辞と接尾辞(接尾辞の戻り値を取り込まない)は同じ性能を持つように見えます。

+0

良い分析。私は良い最適化コンパイラが同じように動作することを期待しています。 –

+0

その答えをありがとう。私は@マークが正しいと思うが、私は他のコンパイラが不思議だ。 –

+0

他のコンパイラでも自由にテストできます:) – bdonlan

関連する問題