在set <int>与vector <bool>和vector <boolean_t>之间选择用作位图(bitset / bit array)</boolean_t> </bool> </int>

时间:2010-11-11 15:10:53

标签: c++ performance

给定一系列索引(标识符),我想将每个索引映射到一个布尔值,即:

// interface pseudocode
interface bitmap {
  bool identifier_is_set(unsigned int id_idx) const;
  void set_identifier(unsigned int id_idx, bool val) const;
};

这样我可以设置和查询每个ID(索引)是否已设置,您希望用什么来实现它?

我认为这称为位数组或位图或bitset,如果我错了,请纠正我。

假设最大标识符是预定的并且不大于1e6(1m),可能小得多(10k - 100k)。(这意味着sizeof(int)* maximum_id_idx使用的大小很容易适合进入记忆。)

到目前为止我看到的可能的解决方案:

  • std::set<size_t> - 根据需要将标识符添加或删除。只要我们有稀疏位图,这将允许任意大的标识符。
  • std::vector<bool> - 调整为适当的最大值,为每个id_idx存储true或false。
  • std::vector<char> - 同样的事情,但没有遭遇奇怪的std::vector<bool>问题。使用的内存少于vector<int>
  • std::vector<int> - 使用int作为布尔标志,使用机器的自然字大小来容纳容器。 (如果可以产生影响,则不知道。)

请根据上面引用的最大ID限制,特别是考虑查询位图的性能方面,回答您更喜欢的容器类型及其原因(插入性能无关紧要) )。

注意:vectorset的界面使用无关紧要,因为无论如何它都会隐藏在它的包装类后面。

编辑:添加到关于std :: bitset的讨论中:std :: bitset将整个数组大小合并到对象中,即sizeof(std :: bitset&lt; 1m&gt;)将是大约1的大小/ 8兆字节,这使得一个巨大的单个对象成为一个你不能再堆叠的东西(可能相关或不相关)。

6 个答案:

答案 0 :(得分:3)

在不知道运行此代码的平台和您的访问模式的情况下,很难说vector<bool>是否会快于vector<char>(或vector<int>)甚至{{1 }或set<int>

例如,如果您有一个非常稀疏的数组,那么仅包含索引集的unordered_set<int>的线性搜索可能是最佳答案。 (See Mike Abrash's article on optimizing Pixomatic for x86.

另一方面,您可能有一个有点稀疏的数组。有点稀疏,我的意思是设置元素的数量远大于L1或L2。在这种情况下,更多低级细节开始发挥作用,以及您的实际访问模式。

例如,在某些平台上,可变位移动非常昂贵。因此,如果您要查询一组随机标识符,则执行此操作的频率越高,vector<int>vector<char>变得比vector<int>bitset<...>更好。 (后两者使用位移位来查找位。)另一方面,如果按顺序迭代稀疏位向量并且只想设置位,则可以优化该迭代以消除变量移位的开销。

此时,您可能还想知道稀疏标识符的实际分布情况。如果它们聚集在一起,您需要知道最佳内存读取大小和一次读取字符之间的权衡。这将决定是否更频繁地访问缓存将抵消非原生大小数据的读取。

如果标识符分散,您可以通过使用哈希集(vector<bool>)而不是位向量来获得显着的胜利。然而,这取决于负载。

答案 1 :(得分:2)

答案 2 :(得分:2)

假设最大标识符是预先确定的且不大于1e6(1m)

如果您有硬限制,请使用std::bitset

std::bitset<1000000> bits;
bits.set(1000);

答案 3 :(得分:1)

如果按性能表示最快查找的那个,那么std :: bitset可能足够快,因为它的查找是常量时间。将所有位设置为零有一个初始开销。矢量&lt;诠释&GT;可能会明显更快,并且设置比特会有更大的开销,因为在32位系统中它们的数量是32倍。

矢量&lt; BOOL&GT;就像你的实现中的bitset一样,如果你需要的话,它具有可调整大小的优点,虽然通常我会避免使用vector,如果我需要调整大小,可以使用boost的dynamic_bitset。

在查找和插入/删除中,

std :: set将是O(log N),尽管它在内存使用中是最具可扩展性的,如果该集合不是特别满,则占用较少。 std :: set不受范围限制。

如果您的数据更稀疏,某种形式的散列也是一种选择,通常是O(1)设置和查找,尽管可能存在一些碰撞处理的开销。

答案 4 :(得分:0)

最快的似乎是使用位掩码。你应该构造一个std::vector<int>,并使其大小足够(N除以sizeof(int)* 8,向上舍入)。

对于大型数据集,这似乎比std::vector<bool>(或类似)更快。因为实际上使用的内存要少得多,因此缓存利用率更高

答案 5 :(得分:0)

你总是可以拥有一个std::vector<std::bitset<sizeof(size_t)> >,然后你的查找就是简单的计算(虽然模运算相对较慢),但你有这个优势可以增长...我会冒这个空间明智的,以上可能也是最优的...