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<bool>
更快吗?顺便说一句,std::vector::push_back
和 std::vector::emplace_back
都比 std::vector::assign
慢。
【问题讨论】:
你正在访问isPrime
,它应该是new bool[n]
如果您非常关心性能,请不要使用vector<bool>
。标准要求它非常节省空间,但这会带来性能成本。
您所说的减速有多大?您可能需要添加一些时间示例,以使这个问题更具吸引力。
您是否在启用优化的情况下进行编译?
【参考方案1】:
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
(大小可以在运行时指定)
但对于速度优化,您必须进行测试...
通过您的具体示例,我只能在关闭优化时确认性能差异(当然这不是要走的路)。
在英特尔至强系统(-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<bool>
看起来还不错(源代码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<bool>
的性能优势不仅可以在 x86 处理器上观察到,而且在我的带有 gcc 8.3 的树莓派上也可以观察到,只需一个简单的-O2
开关。这就像 0.5s 和 2.5s 的差异,std::vector<bool>
是 0.5s。【参考方案2】:
vector<bool>
可能具有模板特化,并且可以使用位数组来实现以节省空间。提取和保存一点并将其从 / 转换为 bool
可能会导致您观察到的性能下降。如果您使用std::vector::push_back
,您正在调整向量的大小,这将导致更差的性能。下一个性能杀手可能是assign
(最差复杂性:第一个参数的线性),而不是使用operator []
(复杂性:常数)。
另一方面,bool []
保证是bool
的数组。
并且您应该将大小调整为 n
而不是 n-1
以避免未定义的行为。
【讨论】:
它不仅“可能”有这样的专业化,这实际上是在标准中! 一种解决方法是使用deque<bool>
。或vector<My_bool>
。 :)
@Mohit Jain:模板特化应该发生在编译时。它应该会影响运行时性能,不是吗?
@guoqing 专业化的含义会影响运行时性能(无论好坏)。在这种情况下,它可能对空间要求有利,对执行时间不利。
@guoqing 你在编译前在vector
和list
之间进行选择,不是吗?但它会影响运行时性能。【参考方案3】:
vector<bool>
可以高性能,但不是必须的。为了使vector<bool>
高效,它需要一次对多个布尔值进行操作(例如isPrime.assign(n, true)
),并且实施者必须对其进行细心呵护。在 vector<bool>
中索引单个布尔值很慢。
这是我不久前使用vector<bool>
和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<char>
时,执行时间上升到大约 44 秒。
如果您使用其他一些 std::lib 运行它,您可能不会看到这种趋势。在 libc++ 上,std::find
等算法已被优化为一次搜索一个字,而不是一次搜索。
有关您的供应商可以优化哪些标准算法的更多详细信息,请参阅http://howardhinnant.github.io/onvectorbool.html。
【讨论】:
以上是关于C++11 vector<bool> 性能问题(附代码示例)的主要内容,如果未能解决你的问题,请参考以下文章
C++ - 将 char 引用转换为 bool 引用(std::vector<bool>)
为啥 std::vector<bool> 没有 .data()?