37

私たちのコーディング ガイドラインconst_iteratorでは、通常の に比べて少し高速であるため、 を優先していますiterator。を使用すると、コンパイラがコードを最適化するようですconst_iterator

これは本当に正しいですか?はいの場合、内部で実際に何が起こってconst_iterator高速化されますか?.

編集:私はconst_iteratorvsをチェックするための小さなテストを書きiterator、さまざまな結果を見つけました:

10,000 個のオブジェクトconst_teratorを反復する場合、数ミリ秒 (約 16 ミリ秒) 短縮されました。しかし、常にではありません。両方が等しい反復がありました。

4

11 に答える 11

82

const_iterator コードを読んでいる人に、「含まれているオブジェクトをいじるのではなく、このコンテナーを反復処理しているだけです」と通知するためです。

これは大きな勝利です。パフォーマンスの違いは気にしないでください。

于 2009-04-16T09:23:35.563 に答える
26

私たちが使用するガイドラインは次のとおりです。

常に非定数よりも定数を好む

const オブジェクトを使用する傾向がある場合は、取得したオブジェクトに対して定数操作のみを使用することに慣れており、可能な限りconst_iteratorを使用しています。

Constness にはウイルス性があります。使い始めると、すべてのコードに伝播します。変化しないメソッドは定数になり、属性に対して定数操作のみを使用し、定数参照を渡す必要があり、それ自体が定数操作のみを強制します...

私にとって、非定数イテレータよりも定数イテレータを使用することのパフォーマンス上の利点 (あるとしても) は、コード自体の改善ほど重要ではありません。変化しないように意図された (設計された) 操作一定です。

于 2009-04-16T11:02:43.133 に答える
19

それらは重要なコンテナ/イテレータ用です。習慣を正せば、重要なときにパフォーマンスを失うことはありません。

また、const_iterator を優先する理由はいくつかあります。

  1. const の使用は、コードの意図を表します (つまり、読み取り専用で、これらのオブジェクトの変更はありません)。
  2. const(_iterator) を使用すると、データが誤って変更されるのを防ぐことができます。(上記を参照)
  3. 一部のライブラリは const の欠如を使用begin()してデータをダーティとしてフラグ付けし (OpenSG など)、同期時に他のスレッドやネットワーク経由で送信するため、パフォーマンスに実際の影響があります。
  4. また、非 const メンバー関数へのアクセスを許可すると、共有データからコピー オン ライト コンテナーを切り離すなど、(上記とほぼ同じ方法で) 意図しない副作用が生じる可能性があります。Qt は、まさにそれを行います。

上記の最後のポイントの例として、Qt の qmap.h からの抜粋を次に示します。

inline iterator begin() { detach(); return iterator(e->forward[0]); }
inline const_iterator begin() const { return const_iterator(e->forward[0]); }

iterator と const_iterator が ( を除いてconst) 実質的に同等detach()であっても、データを使用するオブジェクトが 2 つ以上ある場合は、データの新しいコピーを作成します。を使用して示すデータを読み取るだけの場合、これはまったく役に立ちませんconst_iterator

もちろん、反対方向のデータ ポイントもあります。

  1. STL コンテナーや多くのシンプル コピー セマンティック コンテナーの場合、パフォーマンスは問題になりません。コード同等です。ただし、明確なコードを記述してバグを回避できることが勝ちです。
  2. const はバイラルであるため、const が十分に実装されていない (または単に実装されていない) レガシー コード ベースで作業している場合は、非 const イテレータを使用する必要がある場合があります。
  3. どうやら、一部の C++0x 以前の STL には、const_iterators を使用してコンテナーから要素を erase() できないというバグがあります。
于 2009-04-16T20:13:32.423 に答える
16

なぜそうなるのかわかりません- constness はコンパイル時のチェックです。しかし、明白な答えは、テストを書くことです。

編集:ここに私のテストがあります-それは私のマシンで同じタイミングを与えます:

#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;


int main() {
    vector <int> v;
    const int BIG = 10000000;
    for ( int i = 0; i < BIG; i++ ) {
        v.push_back( i );
    }
    cout << "begin\n";
    int n = 0;
    time_t now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::iterator it = v.begin(); it != v.end(); ++it ) {
            n += *it;
        }
    }
    cout << time(0) - now << "\n";
    now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::const_iterator cit = v.begin(); cit != v.end(); ++cit ) {
            n += *cit;
        }
    }
    cout << time(0) - now << "\n";;

    return n != 0;

}
于 2009-04-16T09:23:39.227 に答える
7

使用するコンテナと実装によって異なります。

はい、パフォーマンスが向上const_iterator する可能性があります。

一部のコンテナでは、定数イテレータと可変イテレータの実装が異なる場合があります。私が考えることができる最初の例は、SGIのSTLロープコンテナです。可変イテレータには、更新をサポートするために親ロープへの追加のポインタがあります。これは、参照カウントの更新と親ロープへのポインタのメモリに無駄な追加のリソースを意味します。こちらの実装ノートを参照してください。

ただし、他の人が言ったように、コンパイラはconstそれ自体で最適化を行うことはできません。const不変であると言うのではなく、参照されるオブジェクトへの読み取り専用アクセスを許可するだけです。のようなコンテナの場合std::vector、そのイテレータは通常、単純なポインタとして実装されますが、違いはありません。

于 2011-04-19T10:43:57.443 に答える
6

私たちのコーディングガイドラインは const_iterator を好むと言っています

ここで Scott Meyers によるこの記事をご覧ください。彼は、なぜ const_iterator よりも iterator を好むべきかを説明しています。

于 2009-04-16T09:31:26.650 に答える
4

constness はコンパイル時のチェックであるため、それらは同一である必要があります。

癖がないことを自分自身に証明するために、anon のコードを使用するように変更し、clock_gettimeキャッシング バイアスを回避するために外側のループを追加し、何度も実行しました。結果は驚くほど一貫性がありませんでした - 上下 20% (利用可能なアイドル ボックスはありません) - しかし、両方の最小時間iteratorconst_iterator実質的に同じでした。

次に、コンパイラ (GCC 4.5.2 -O3) を使用してアセンブリ出力を生成し、2 つのループを視覚的に比較しました:同一です(いくつかのレジスタ ロードの順序が逆になっていることを除いて)。

iteratorループ

    call    clock_gettime
    movl    56(%esp), %esi
    movl    $10, %ecx
    movl    60(%esp), %edx
    .p2align 4,,7
    .p2align 3
.L35:
    cmpl    %esi, %edx
    je  .L33
    movl    %esi, %eax    .p2align 4,,7
    .p2align 3
.L34:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L34
.L33:
    subl    $1, %ecx
    jne .L35
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)

const_iteratorループ:

    movl    60(%esp), %edx
    movl    $10, %ecx
    movl    56(%esp), %esi
    .p2align 4,,7
    .p2align 3
.L38:
    cmpl    %esi, %edx
    je  .L36
    movl    %esi, %eax
    .p2align 4,,7
    .p2align 3
.L37:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L37
.L36:
    subl    $1, %ecx
    jne .L38
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)
于 2011-04-28T03:37:14.380 に答える
2

これらのいずれかをベンチマークするときは、適切な最適化レベルを使用するようにしてください。「-O0」と「-O」などを使用すると、タイミングが大きく異なります。

于 2011-04-28T03:03:03.427 に答える
1

container<T>::const_iterator::operator*const T&の代わりにa を返すT&ので、コンパイラは const オブジェクトに対して通常の最適化を行うことができます。

于 2009-04-16T09:23:24.953 に答える
1

アクセス制限 (public、protected、private) のような「一貫性」は、最適化を支援する以上にプログラマーに利益をもたらします。
コンパイラは、多くの理由 (const_cast、変更可能なデータ メンバー、ポインター/参照のエイリアシングなど) により、const を含む多くの状況を実際に最適化することはできません。ただし、ここで最も重要な理由は、 const_iterator が参照するデータを変更できないからといって、そのデータを他の手段で変更できないという意味ではないということです。また、コンパイラがデータが読み取り専用であることを判断できない場合、非定数イテレータの場合よりも多くの最適化を行うことはできません。
詳細と例は、次の場所にあります。http://www.gotw.ca/gotw/081.htm

于 2009-04-16T11:57:31.347 に答える
0

私の経験では、const イテレータを使用する場合、コンパイラは測定可能な最適化を行いません。「可能である」というステートメントは真実であり、参照は標準でポインターとして定義されていませんが。

ただし、同じオブジェクトへの参照を 2 つ持つことができるため、1 つを const に、もう 1 つを非 const にすることができます。次に、制限ポインターに関するこのスレッドの回答が適用されると思います。コンパイラーは、オブジェクトが別のスレッドによって変更されたかどうか、たとえば、割り込み処理コードによって変更されたかどうかを認識できません。

于 2009-04-16T09:30:48.757 に答える