11

インクリメント/デクリメント演算子の後置バージョンは、通常、組み込み型用にコンパイラによって最適化される (つまり、コピーは作成されない) ことは知っていますが、これはiterators の場合ですか?

それらは本質的にオーバーロードされた演算子であり、さまざまな方法で実装できますが、それらの動作は厳密に定義されているため、最適化できますか?もしそうなら、それらは任意/多くのコンパイラによって行われますか?

#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;
  }
}
4

1 に答える 1

9

GNU GCC の STL 実装 (バージョン 4.6.1)の特定のケースではstd::vector、十分に高い最適化レベルでパフォーマンスの違いがあるとは思いません。

の前方イテレータの実装vectorは、 によって提供され__gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>ます。++そのコンストラクタと後置演算子を見てみましょう:

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

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

そして、そのインスタンス化vector:

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

ご覧のとおり、通常のポインターに対して後置インクリメントを内部的に実行し、独自のコンストラクターを介して元の値を渡し、それをローカル メンバーに保存します。このコードは、死んだ値の分析によって排除するのが簡単なはずです。

しかし、それは本当に最適化されていますか? 確認してみましょう。テストコード:

#include <vector>

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

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

出力アセンブリ (上-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

ご覧のとおり、どちらの場合もまったく同じアセンブリが出力されます。

もちろん、カスタム イテレータやより複雑なデータ型の場合は、必ずしもそうであるとは限りません。ただし、vector具体的には、プレフィックスとポストフィックス (ポストフィックスの戻り値をキャプチャしない) のパフォーマンスは同じであるようです。

于 2011-06-21T02:19:06.763 に答える