std::vector::operator[] が std::vector::at() より 5 ~ 10 倍高速なのはなぜですか? 質問する

std::vector::operator[] が std::vector::at() より 5 ~ 10 倍高速なのはなぜですか? 質問する

プログラムの最適化中に、ベクトルを反復するループを最適化しようとしたところ、次の事実がわかりました: ::std::vector::at() は、operator[] よりも非常に遅いです。

演算子[]はat()より5~10倍高速ですリリース ビルドとデバッグ ビルドの両方 (VS2008 x86)。

ウェブで少し読んでみると、at() には境界チェック機能があることに気付きました。わかりましたが、操作が最大 10 倍遅くなるのですか?

何か理由があるのでしょうか?境界チェックは単純な数値比較ですが、何か見落としているのでしょうか?
問題は、このパフォーマンス低下の本当の理由が何なのかということです。
さらに、さらに速くする方法はありますか?

他のコード部分(既にカスタム境界チェックが入っています)では、すべての at() 呼び出しを [] に置き換えるつもりです。

コンセプトの証明:

#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <conio.h>

#include <vector>

#define ELEMENTS_IN_VECTOR  1000000

int main()
{
    __int64 freq, start, end, diff_Result;
    if(!::QueryPerformanceFrequency((LARGE_INTEGER*)&freq))
        throw "Not supported!";
    freq /= 1000000; // microseconds!

    ::std::vector<int> vec;
    vec.reserve(ELEMENTS_IN_VECTOR);
    for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
        vec.push_back(i);

    int xyz = 0;

    printf("Press any key to start!");
    _getch();
    printf(" Running speed test..\n");

    { // at()
        ::QueryPerformanceCounter((LARGE_INTEGER*)&start);
        for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
            xyz += vec.at(i);
        ::QueryPerformanceCounter((LARGE_INTEGER*)&end);
        diff_Result = (end - start) / freq;
    }
    printf("Result\t\t: %u\n\n", diff_Result);

    printf("Press any key to start!");
    _getch();
    printf(" Running speed test..\n");

    { // operator []
        ::QueryPerformanceCounter((LARGE_INTEGER*)&start);
        for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
            xyz -= vec[i];
        ::QueryPerformanceCounter((LARGE_INTEGER*)&end);
        diff_Result = (end - start) / freq;
    }

    printf("Result\t\t: %u\n", diff_Result);
    _getch();
    return xyz;
}

編集:
現在、値は「xyz」に割り当てられているため、コンパイラはそれを「消去」しません。

ベストアンサー1

その理由は、チェックなしのアクセスはおそらく 1 つのプロセッサ命令で実行できるからです。チェックありのアクセスでは、メモリからサイズをロードし、それをインデックスと比較し、(範囲内であると仮定して) 条件分岐をスキップしてエラー ハンドラに渡さなければなりません。例外をスローする可能性に対処するために、さらに手間がかかる可能性があります。これは何倍も遅くなりますが、これがまさに両方のオプションがある理由です。

実行時チェックなしでインデックスが範囲内にあることを証明できる場合は、 を使用しますoperator[]。それ以外の場合は、 を使用するat()か、アクセス前に独自のチェックを追加します。 は、operator[]ほぼ可能な限り高速になるはずですが、インデックスが無効な場合は乱雑に爆発します。

おすすめ記事