我应该使用哪种bitset实现以获得最大的性能?

15

我目前正在尝试在即时编译器(JIT)中实现各种算法。许多算法操作的是位图,更常被称为位集。

在C++中,有多种实现位集的方法。作为真正的C++开发人员,我更倾向于使用STL中的内容。最重要的方面是性能。我并不一定需要一个动态可调整大小的位集。

我认为有三个可能的选择:

I. 一种选项是使用已经为空间进行了优化的std::vector<bool>。这也说明数据不必在内存中是连续的。我猜这会降低性能。另一方面,每个布尔值都有一个位,这可能会提高速度,因为它非常缓存友好。

II. 另一个选项是改用std::vector<char>。它保证数据在内存中是连续的,易于访问单个元素。然而,使用这个选择感觉很奇怪,因为它并不打算成为位集。

III. 第三个选项是使用实际的std::bitset。它不具备动态调整大小的功能。

哪一个才是我应该选择的最佳性能?


4
基准测试!相关链接。 - user1203803
3
还有 Boost.Dynamic Bitset 可以考虑。但是,如果不知道使用模式,就无法确定哪种性能最好。例如:如果你的集合很小且经常访问,vector<char> 可能比位集更快,因为它不需要进行位移/掩码操作。但是,当访问较少/更大时,由于更大的内存占用量导致更高的缓存未命中率可能会抵消这种好处。 - Grizzly
冒昧指出可能很明显的一点:std::bitset分配在堆栈上,因此在大多数情况下其最大大小相当有限。然而,我不知道您需要存储的数据量是多少。 - identity
需要多大的空间呢?我的意思是,你能否将它放在一个无符号长整型或类似的数据类型中? - harold
3个回答

8

最好的方法是进行基准测试,因为每种情况都不同。

我不会使用std::vector<bool>。我曾经尝试过,性能非常差。我可以通过简单地改用std::vector<char>来提高应用程序的性能。

我没有真正将std::bitsetstd::vector<char>进行比较,但如果在您的情况下空间不是问题,我会选择std::vector<char>。它使用的空间比位集多8倍,但由于它不必执行位操作来获取或设置数据,因此应该更快。

当然,如果您需要在位集/向量中存储大量数据,则使用位集可能会有益,因为这将适合处理器的缓存。

最简单的方法是使用typedef并隐藏实现。位集和向量都支持[]运算符,因此应该很容易地将一种实现切换到另一种实现。


operator[] 相似,但构造函数不同。 - Mooing Duck
@MooingDuck:没错。我使用typedef来简化从一种类型到另一种类型的迁移,但并不是为了让它毫不费力。我还使用typedef来表示集合,以便隐藏真正的实现(如list、vector、deque等),这样如果我要改变容器类型,真正的代码更改就会减少约90%。 - Patrick

5

我最近在这个论坛回答了一个类似的问题,我推荐使用我的BITSCAN库。我刚刚发布了1.0版本,BITSCAN专门设计用于快速的位扫描操作。

BitBoard类包装了许多不同的实现,用于64位字(也称为bitboards)的典型操作,例如bsfbsrpopcount。BitBoardN、BBIntrin和BBSentinel类将位扫描扩展到位字符串。BITSCAN中的位字符串是一个位板数组。位字符串的基本包装类是BitBoardN。BBIntrin通过使用Windows编译器内核函数来扩展BitBoardN。通过使用适当的asm等效函数,BBIntrin可以在POSIX上实现可移植性。

我已经使用BITSCAN来实现了一些高效的求解器,用于NP组合问题的图形领域。通常,图形的邻接矩阵以及顶点集被编码为位字符串,并使用位掩码执行典型计算。简单位编码图形对象的代码在GRAPH中可用。如何使用BITSCAN和GRAPH的示例也可用。

BITSCAN与STL(bitset)和BOOST(dynamic_bitset)的典型实现的比较可以在这里找到:http://blog.biicode.com/bitscan-efficiency-at-glance/


1

简而言之,本文的结论是:“我们已经证明了boost::dynamic_bitset在执行速度方面比大多数其他实现要高效得多,而使用std::vector<char>的实现在内存效率方面优于其他实现。” - davidhigh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接