web-dev-qa-db-ja.com

const_iteratorsは高速ですか?

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

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

編集:const_iteratoriteratorをチェックするための小さなテストを作成し、さまざまな結果を見つけました。

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

36
aJ.

他に何もないとしても、const_iteratorreadsより良い、コードを読んでいる人に「私はこのコンテナを繰り返し処理しているだけで、含まれているオブジェクトをいじっていない」と伝えるからです。

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

76
unwind

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

常に非constよりもconstを優先します

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

Constnessにはviralプロパティがあります。使用できるようになると、すべてのコードに伝播されます。変化しないメソッドは一定になり、属性に対して一定の操作のみを使用し、一定の参照を渡す必要があり、それ自体が一定の操作のみを強制します...

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

それらは重要なコンテナ/イテレータ用です。あなたの習慣をまっすぐにしてください、そしてそれが重要であるときあなたはパフォーマンスを失うことはありません。

また、何があっても、const_iteratorを好む理由はいくつかあります。

  1. Constの使用は、コードの意図を表します(つまり、読み取り専用であり、これらのオブジェクトの変更はありません)。
  2. Const(_iterator)を使用すると、データが誤って変更されるのを防ぐことができます。 (上記を参照)
  3. 一部のライブラリは、lack-of-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を除く)、それを使用するオブジェクトが2つ以上ある場合、detach()はデータの新しいコピーを作成します。 _const_iterator_を使用して示すデータを読み取るだけの場合、これはまったく役に立ちません。

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

  1. STLコンテナや多くの単純コピーセマンティックコンテナの場合、パフォーマンスは重要ではありません。コードis同等です。ただし、明確なコードを記述してバグを回避できることが重要です。
  2. Constはウイルス性であるため、constが適切に実装されていない(または単に実装されていない)レガシーコードベースで作業している場合は、const以外のイテレーターを使用する必要があります。
  3. どうやら、一部のC++ 0xより前のSTLには、const_iteratorsを使用してコンテナーから要素をerase()できないというバグがあります。
19
Macke

なぜそうなるのかわかりません-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;

}
15
anon

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

はい、const_iteratormayのパフォーマンスが向上します。

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

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

7
ybungalobill

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

これを見てください Scott Meyersによる記事はこちら 。彼は、const_iteratorよりもイテレータを好む理由を説明しています。

6
Shree

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

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

次に、コンパイラ(GCC 4.5.2 -O3)にアセンブリ出力を生成させ、2つのループを視覚的に比較しました:同一(ただし、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)
3
Tony Delroy

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

2
user497611

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

1
mjmt

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

1
tstenner

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

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

0
Manuel