C++ boost::dynamic_位集比std::位集慢,除非重置std::位集
我最近遇到了位集模板,非常想在我当前的项目中使用它们。继续阅读,我发现C++ boost::dynamic_位集比std::位集慢,除非重置std::位集,c++,boost-dynamic-bitset,std-bitset,C++,Boost Dynamic Bitset,Std Bitset,我最近遇到了位集模板,非常想在我当前的项目中使用它们。继续阅读,我发现std::bitset模板必须在编译时确定大小。许多人建议使用boost::dynamic_位集来缓解这一需求 为了比较这两种方法,我决定对set、flip和count方法进行速度比较 结果很奇怪……我想知道是否有人能帮我解释一下 代码在文章的末尾,但我会解释我在这里做什么。我有一个std::bitset对象(称之为bs)和一个boost::dynamic_bitset对象(称之为dynbs)。每个都有n=1000000位。对
std::bitset
模板必须在编译时确定大小。许多人建议使用boost::dynamic_位集
来缓解这一需求
为了比较这两种方法,我决定对set
、flip
和count
方法进行速度比较
结果很奇怪……我想知道是否有人能帮我解释一下
代码在文章的末尾,但我会解释我在这里做什么。我有一个std::bitset
对象(称之为bs
)和一个boost::dynamic_bitset
对象(称之为dynbs
)。每个都有n=1000000
位。对于上面给定的方法,按顺序调用每个n
位上的方法,并重复该R=10000
次
使用std::chrono
库,以下是以纳秒为单位的计时:
set
位集:267纳秒
动态位集:18603174546 nsecs
轻弹
位集:73纳秒
动态位集:18842352867 nsecs
计数
比特集:77纳秒
动态位集:51纳秒
boost::dynamic_位集
对于set
和flip
来说似乎要慢得多
为了让它更有趣,如果在运行这些测试之前对两个对象调用了方法reset
,那么计时是可比较的。这是:
set
位集:19397779399 nsecs
动态位集:18472863864 nsecs
轻弹
位集:18599248629 nsecs
动态位集:18376267939 nsecs
计数
位集:68纳秒
动态位集:61纳秒
现在,两个容器都声明将所有位初始化为0
,因此调用reset
不应更改任何位。在reset
之前和之后转储none
的输出会确认这一点
所以在这之后,我有两个问题:
1) 为什么调用set
和flip
时boost::dynamic_位集
比std::位集
慢得多
2) 为什么调用reset
会对std::bitset
的速度产生巨大的负面影响
这是我的密码:
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
使用名称空间计时;
使用名称空间boost;
int main(){
常量无符号整数n=1000000;
位集bs;
动态位集<>dynbs(n);
//重置();
//dynbs.reset();
无符号整数i,r,r=10000;
高分辨率时钟:时间点滴答滴答;
////////////////////////////////////////////////////////////
//方法:集合
std::cout好吧,看起来T.C.有解释
位集上的所有设置和翻转(以及计数)都已完全优化
出去
装配代码中提供了一个链接,以显示以下内容:
通过返回三种不同方法的值并将它们添加到另一个变量中,成功了,现在看来这是一场公平的斗争(正如T.C.所建议的)
1) 为什么boost::dynamic_位集
比
std::bitset
调用set和flip时
由于std::bitset
不使用动态分配,并且您的bitset
是一个局部变量,因此编译器可以轻松确定所有集
和翻转
和计数
都没有外部可见的效果。因此,它和您的代码基本上是一堆计时和打印ng打电话来
请注意,在上面的程序集中,它甚至没有为位集分配堆栈空间。整个过程基本上消失得无影无踪
boost::dynamic_bitset
使用new
动态分配其缓冲区,最终调用::operator new()
,它可以是在不同翻译单元中定义的任意重载版本。因此,编译器很难证明对返回缓冲区的更改在外部是不可见的
您的count
循环对于std::bitset
和boost::dynamic_bitset
都进行了优化,因为编译器可以很容易地看到count()
不会更改位集中的任何内容,并且您也不会使用返回值
2) 为什么调用reset
会对移动速度产生巨大的负面影响
位集
我在GCC中检查了reset
的源代码,它调用了一个编译器内部\uuuu builtin\u memset
,将一个指向缓冲区的指针传递给它。当您将指向堆栈变量的指针传递给外部函数时,编译器在它可以删除的内容上受到了更大的限制,因为变量中的更改现在可能是外部的servable(例如,被调用的函数可能已将指针的副本存储在某个位置,稍后可以从中窥视).@T.C.那么,是不是我的测试太简单了?我想我不太清楚这意味着什么。我试图阅读汇编代码,但不太明白发生了什么。基本上。微基准很难编写。在这种情况下,如果在每个循环后添加要打印的代码,例如,bs
,那么设置s和>flip
s可能不会被优化掉。为了确保count
调用不会被优化掉,请将返回值添加到一个变量中,然后打印出来。dynamic_bitset
使用堆上的分配,编译器更难证明set
和flip
所做的更改会被优化掉隐形。@T.C.明白了。这很有道理。谢谢你澄清引擎盖下发生的事情。只是添加了一些