web-dev-qa-db-ja.com

C ++ 11 vector <bool>のパフォーマンスの問題(コード例付き)

次のコードを実行すると、vectorはbool配列よりもはるかに遅いことに気付きました。

int main() 
{
    int count = 0;
    int n = 1500000;
    // slower with c++ vector<bool>
    /*vector<bool> isPrime;
    isPrime.reserve(n);
    isPrime.assign(n, true);
    */
    // faster with bool array 
    bool* isPrime = new bool[n];

    for (int i = 0; i < n; ++i)
        isPrime[i] = true;


    for (int i = 2; i< n; ++i) {
        if (isPrime[i])
            count++;
        for (int j =2; i*j < n; ++j )
            isPrime[i*j] = false;
    }

    cout <<  count << endl;
    return 0;
}

vector<bool>を高速化する方法はありますか?ところで、std::vector::Push_backstd::vector::emplace_backはどちらもstd::vector::assignよりもさらに遅いです。

21
guoqing

std::vector<bool>には、さまざまなパフォーマンスの問題が発生する可能性があります(たとえば、 https://isocpp.org/blog/2012/11/on-vectorbool を参照してください)。

一般的に、次のことができます。

  • 使用する std::vector<std::uint8_t> の代わりに std::vector<bool>(試してみてください std::valarray<bool> また)。

    これはより多くのメモリを必要とし、キャッシュフレンドリーではありませんが、単一の値にアクセスするためのオーバーヘッド(ビット操作の形で)がないため、より適切に機能する状況があります(結局のところ、boolの配列のようです)ただし、メモリ管理の煩わしさはありません)

  • 使用する - std::bitset コンパイル時にブール配列がどれだけ大きくなるかがわかっている場合(または少なくとも妥当な上限を確立できる場合)
  • boostがオプションの場合は、試してください boost::dynamic_bitset (サイズは実行時に指定できます)

しかし、速度を最適化するには、テストする必要があります...

あなたの特定の例では、最適化がオフになっている場合にのみパフォーマンスの違いを確認できます(もちろんこれは方法ではありません)。

IntelXeonシステムでのg ++​​ v4.8.3およびclang ++ v3.4.5を使用した一部のテスト(-O3最適化レベル)別の図を示します:

                    time (ms)
                 G++      CLANG++
array of bool    3103     3010
vector<bool>     2835     2420    // not bad!
vector<char>     3136     3031    // same as array of bool
bitset           2742     2388    // marginally better

(回答のコードを100回実行して経過した時間)

std::vector<bool>それほど悪くはありません(ソースコード ここ )。

15
manlio

vector<bool>にはテンプレートの特殊化があり、スペースを節約するためにビット配列を使用して実装できます。ビットを抽出して保存し、それを/からboolに変換すると、観察しているパフォーマンスが低下する可能性があります。 std::vector::Push_backを使用すると、ベクトルのサイズが変更され、パフォーマンスがさらに低下します。次のパフォーマンスキラーはassign(最悪の複雑さ:最初の引数の線形)である可能性があり、代わりにoperator [](複雑さ:定数)を使用します。

一方、bool []boolの配列であることが保証されています。

また、未定義の動作を回避するために、n-1ではなくnにサイズ変更する必要があります。

10
Mohit Jain

_vector<bool>_ can高性能である必要はありませんが、そうである必要はありません。 _vector<bool>_を効率的にするには、一度に多くのboolを操作する必要があります(例:isPrime.assign(n, true))、and実装者はそれに愛情を込めて注意を払う必要があります。 _vector<bool>_の個々のboolのインデックス作成は遅いです。

これは、_vector<bool>_とclang + libc ++(libc ++の部分が重要)を使用してしばらく前に書いた素数のFinderです。

_#include <algorithm>
#include <chrono>
#include <iostream>
#include <vector>

std::vector<bool>
init_primes()
{
    std::vector<bool> primes(0x80000000, true);
    primes[0] = false;
    primes[1] = false;
    const auto pb = primes.begin();
    const auto pe = primes.end();
    const auto sz = primes.size();
    size_t i = 2;
    while (true)
    {
        size_t j = i*i;
        if (j >= sz)
            break;
        do
        {
            primes[j] = false;
            j += i;
        } while (j < sz);
        i = std::find(pb + (i+1), pe, true) - pb;
    }
    return primes;
}

int
main()
{
    using namespace std::chrono;
    using dsec = duration<double>;
    auto t0 = steady_clock::now();
    auto p = init_primes();
    auto t1 = steady_clock::now();
    std::cout << dsec(t1-t0).count() << "\n";
}
_

これは約28秒(-O3)で実行されます。代わりに_vector<char>_を返すように変更すると、実行時間が約44秒に増加します

他のstd :: libを使用してこれを実行すると、おそらくこの傾向は見られません。 libc ++では、_std::find_などのアルゴリズムは、一度に1ビットずつではなく、一度に1ワードのビットを検索するように最適化されています。

ベンダーが最適化できるstdアルゴリズムの詳細については、 http://howardhinnant.github.io/onvectorbool.html を参照してください。

5
Howard Hinnant