我最近遇到了bitset模板,并且非常想在我当前的项目中使用它们。继续阅读,我发现std::bitset
模板必须具有在编译时确定的大小。许多人建议使用boost::dynamic_bitset
来缓解这一要求。
为了比较这两者,我决定对set
,flip
和count
方法进行速度比较。
结果很奇怪......我想知道是否有人可以为我阐明它。
代码在帖子的末尾,但我会解释我在这里做的事情。我有一个std::bitset
对象(称之为bs
)和一个boost::dynamic_bitset
对象(称之为dynbs
)。每个都有n=1000000
位。对于上面给定的方法,请按顺序调用每个n
位上的方法,并重复此R=10000
次。
使用std::chrono
库,以下是每纳秒的时间:
set
bitset: 267 nsecs
dyn bitset: 18603174546 nsecs
flip
bitset: 73 nsecs
dyn bitset: 18842352867 nsecs
count
bitset: 77 nsecs
dyn bitset: 51 nsecs
boost::dynamic_bitset
和set
flip
似乎要慢得多。
为了使它更有趣,如果在运行这些测试之前在两个对象上调用方法reset
,那么时序是可比较的。他们在这里:
set
bitset: 19397779399 nsecs
dyn bitset: 18472863864 nsecs
flip
bitset: 18599248629 nsecs
dyn bitset: 18376267939 nsecs
count
bitset: 68 nsecs
dyn bitset: 61 nsecs
现在,两个容器声称将所有位初始化为0
,因此对reset
的调用不应更改任何位。在none
之前和之后转储reset
的输出会确认这一点。
毕竟,我有两个问题:
1)调用boost::dynamic_bitset
和std::bitset
时,为什么set
比flip
慢得多?
2)为什么调用reset
会对std::bitset
的速度产生巨大的负面影响?
这是我的代码:
#include <iostream>
#include <iomanip>
#include <bitset>
#include <boost/dynamic_bitset.hpp>
#include <vector>
#include <chrono>
#include <ctime>
using namespace std;
using namespace chrono;
using namespace boost;
int main(){
const unsigned int n=1000000;
bitset< n > bs;
dynamic_bitset< > dynbs(n);
// bs.reset();
// dynbs.reset();
unsigned int i,r,R=10000;
high_resolution_clock::time_point tick,tock;
////////////////////////////////////////////////////////////
// Method: set
std::cout << "set" << std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
bs.set(i);
tock=high_resolution_clock::now();
cout << setw(16) << "bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
dynbs.set(i);
tock=high_resolution_clock::now();
cout << setw(16) << "dyn bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl << std::endl;
////////////////////////////////////////////////////////////
// Method: flip
std::cout << "flip" << std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
bs.flip(i);
tock=high_resolution_clock::now();
cout << setw(16) << "bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
dynbs.flip(i);
tock=high_resolution_clock::now();
cout << setw(16) << "dyn bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl << std::endl;
////////////////////////////////////////////////////////////
// Method: count
std::cout << "count" << std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
bs.count();
tock=high_resolution_clock::now();
cout << setw(16) << "bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl;
tick=high_resolution_clock::now();
for(r=0; r<R; r++)
for(i=0; i<n; i++)
dynbs.count();
tock=high_resolution_clock::now();
cout << setw(16) << "dyn bitset: "
<< setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
<< std::endl;
return 0;
}
我用
编译了它g++ -O3 -std=c++11 bitset.cpp -o bitset
其中bitset.cpp
是上面插入的代码。
答案 0 :(得分:24)
1)为什么
boost::dynamic_bitset
比...慢得多 调用set和flip时std::bitset
?
由于std::bitset
不使用动态分配,而您的bitset
是局部变量,编译器可以轻松确定所有set
和flip
和count
没有外部可见效果。结果,它optimizes them all away,你的代码基本上最终成为一堆定时和打印电话。
请注意,在上面的程序集中,它甚至不为bitset分配堆栈空间。整个事情基本上消失得无影无踪。
boost::dynamic_bitset
使用new
动态分配其缓冲区,最终调用::operator new()
,这可以是在不同翻译单元中定义的任意重载版本。因此,编译器很难证明返回的缓冲区的更改在外部是不可见的。
count
和std::bitset
的{{1}}循环已经过优化,因为编译器可以很容易地看到boost::dynamic_bitset
不会更改位集中的任何内容而您不会t使用返回值。
2)为什么调用
count()
会对速度产生巨大的负面影响 的std ::位集合?
我检查了GCC中reset
的源代码,并调用了编译器内部reset
,并向其传递了一个指向缓冲区的指针。当您将指向堆栈变量的指针传递给外部函数时,编译器在它可以删除的内容中受到的限制更多,因为变量中的更改现在可以在外部进行观察(例如,调用的函数可能存储了一个副本指针在某个地方,以后可以偷看它。)
答案 1 :(得分:1)
好吧,看起来像T.C.有解释。
bitset上的所有设置和翻转(以及计数)都已完全优化 进行。
为汇总代码提供了一个链接,以显示:assembly code
通过返回三种不同方法中的值并将它们添加到另一个变量中就可以了,这似乎是一场公平的斗争(正如T.C所建议的那样)。