C++11 vector<bool> 性能问题(附代码示例)

Posted

技术标签:

【中文标题】C++11 vector<bool> 性能问题(附代码示例)【英文标题】:C++11 vector<bool> performance issue (with code example) 【发布时间】:2016-08-24 06:44:56 【问题描述】:

我注意到在运行以下代码时,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&lt;bool&gt; 更快吗?顺便说一句,std::vector::push_backstd::vector::emplace_back 都比 std::vector::assign 慢。

【问题讨论】:

你正在访问isPrime,它应该是new bool[n] 如果您非常关心性能,请不要使用vector&lt;bool&gt;。标准要求它非常节省空间,但这会带来性能成本。 您所说的减速有多大?您可能需要添加一些时间示例,以使这个问题更具吸引力。 您是否在启用优化的情况下进行编译? 【参考方案1】:

std::vector&lt;bool&gt; 可能存在各种性能问题(例如查看https://isocpp.org/blog/2012/11/on-vectorbool)。

一般来说你可以:

使用std::vector&lt;std::uint8_t&gt; 而不是std::vector&lt;bool&gt;(也可以试试std::valarray&lt;bool&gt;)。

这需要更多内存并且对缓存不太友好,但访问单个值没有开销(以位操作的形式),因此在某些情况下它工作得更好(毕竟它就像你的bool 的数组,但没有内存管理的麻烦)

如果您在编译时知道布尔数组的大小(或者如果您至少可以建立一个合理的上限),请使用 std::bitset 如果 Boost 是一个选项,请尝试 boost::dynamic_bitset(大小可以在运行时指定)

但对于速度优化,您必须进行测试...

通过您的具体示例,我只能在关闭优化时确认性能差异(当然这不是要走的路)。

在英特尔至强系统(-O3 优化级别)上使用 g++ v4.8.3 和 clang++ v3.4.5 进行的一些测试给出了不同的结果:

                    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&lt;bool&gt; 看起来还不错(源代码here)。

【讨论】:

“至强”可以是从 P4 到 Skylake 的任何东西。说 which Xeon(例如 Haswell Xeon 或 Exxxx v3)会提供更多信息。这些也是现代硬件的相当老的编译器版本(如果你不自动矢量化或使用-march=native,这没什么大不了的)。 @PeterCordes 你是对的。它是 Xeon e3-1230v3(使用 -march=native 开关)。在我的辩护中,我要补充一点,这并不是一个完整的考试,但我会尽快添加测试代码和更多结果。 您不必为此花很多时间。每当您发布任何性能数字时,特定的微架构和编译器版本 + 选项都是必不可少的。例如Haswell Xeon 上的 clang++ 3.4.5,-O3 -march=native 将涵盖它。顺便说一句,就 asm 的质量而言,clang 3.7.1(当前稳定)对 AVX2 的自动矢量化明显优于 3.4。 IDK 多久会产生一次性能差异,因为内存瓶颈通常会隐藏 CPU 瓶颈。 llvm.org/apt 拥有适用于基于 debian 的 Linux 发行版的当前 clang 版本。 我确认std::vector&lt;bool&gt; 的性能优势不仅可以在 x86 处理器上观察到,而且在我的带有 gcc 8.3 的树莓派上也可以观察到,只需一个简单的-O2 开关。这就像 0.5s 和 2.5s 的差异,std::vector&lt;bool&gt; 是 0.5s。【参考方案2】:

vector&lt;bool&gt; 可能具有模板特化,并且可以使用位数组来实现以节省空间。提取和保存一点并将其从 / 转换为 bool 可能会导致您观察到的性能下降。如果您使用std::vector::push_back,您正在调整向量的大小,这将导致更差的性能。下一个性能杀手可能是assign(最差复杂性:第一个参数的线性),而不是使用operator [](复杂性:常数)。

另一方面,bool [] 保证是bool 的数组。

并且您应该将大小调整为 n 而不是 n-1 以避免未定义的行为。

【讨论】:

它不仅“可能”有这样的专业化,这实际上是在标准中! 一种解决方法是使用deque&lt;bool&gt;。或vector&lt;My_bool&gt;。 :) @Mohit Jain:模板特化应该发生在编译时。它应该会影响运行时性能,不是吗? @guoqing 专业化的含义会影响运行时性能(无论好坏)。在这种情况下,它可能对空间要求有利,对执行时间不利。 @guoqing 你在编译前在vectorlist 之间进行选择,不是吗?但它会影响运行时性能。【参考方案3】:

vector&lt;bool&gt; 可以高性能,但不是必须的。为了使vector&lt;bool&gt; 高效,它需要一次对多个布尔值进行操作(例如isPrime.assign(n, true)),并且实施者必须对其进行细心呵护。在 vector&lt;bool&gt; 中索引单个布尔值很慢。

这是我不久前使用vector&lt;bool&gt; 和clang + libc++(libc++ 部分很重要)写的一个主要查找器:

#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&lt;char&gt; 时,执行时间上升到大约 44 秒。

如果您使用其他一些 std::lib 运行它,您可能不会看到这种趋势。在 libc++ 上,std::find 等算法已被优化为一次搜索一个字,而不是一次搜索。

有关您的供应商可以优化哪些标准算法的更多详细信息,请参阅http://howardhinnant.github.io/onvectorbool.html

【讨论】:

以上是关于C++11 vector<bool> 性能问题(附代码示例)的主要内容,如果未能解决你的问题,请参考以下文章

vector<bool>的特殊性

C++ - 将 char 引用转换为 bool 引用(std::vector<bool>)

为啥 std::vector<bool> 没有 .data()?

通过 mpi 发送一个 c++ std::vector<bool>

将向量归零的最快方法<vector<bool>>

运算符 |= 在 std::vector<bool> 上