【问题标题】:How to zero a vector<bool>?如何将向量<bool>归零?
【发布时间】:2013-10-24 09:55:32
【问题描述】:

我有一个vector&lt;bool&gt;,我想将其归零。我需要尺寸保持不变。

通常的方法是遍历所有元素并重置它们。但是,vector&lt;bool&gt; 是一个specially optimized 容器,根据实现,每个元素可能只存储一位。有没有办法利用这一点来有效地清除整个事情?

bitset,固定长度变体,具有set 功能。 vector&lt;bool&gt; 有类似的吗?

【问题讨论】:

  • std::fill(v.begin(), v.end(), 0);
  • @AliAlamiri:这是 OP 隐含提及的正常方式。并且它可能无法利用在操作中设置 8 位。
  • 你计时了吗?在您的标准库实现中很可能是 std::fill 之类的算法是 specialized for std::vector&lt;bool&gt;
  • 一个好方法是不使用vector&lt;bool&gt;,简单来说,它已经坏了。
  • 虽然std::vector&lt;bool&gt; 有一些不幸的问题,但添加它的最初理由正是针对此类问题。那么,“不要使用它”不是适当的评论。

标签: c++ vector


【解决方案1】:

到目前为止发布的答案中似乎有很多猜测,但事实很少,所以也许值得做一些测试。

#include <vector>
#include <iostream>
#include <time.h>

int seed(std::vector<bool> &b) {
    srand(1);
    for (int i = 0; i < b.size(); i++)
        b[i] = ((rand() & 1) != 0);
    int count = 0;
    for (int i = 0; i < b.size(); i++)
    if (b[i])
        ++count;
    return count;
}

int main() {
    std::vector<bool> bools(1024 * 1024 * 32);

    int count1= seed(bools);
    clock_t start = clock();
    bools.assign(bools.size(), false);
    double using_assign = double(clock() - start) / CLOCKS_PER_SEC;

    int count2 = seed(bools);
    start = clock();
    for (int i = 0; i < bools.size(); i++)
        bools[i] = false;
    double using_loop = double(clock() - start) / CLOCKS_PER_SEC;

    int count3 = seed(bools);
    start = clock();
    size_t size = bools.size();
    bools.clear();
    bools.resize(size); 
    double using_clear = double(clock() - start) / CLOCKS_PER_SEC;

    int count4 = seed(bools);
    start = clock();
    std::fill(bools.begin(), bools.end(), false);
    double using_fill = double(clock() - start) / CLOCKS_PER_SEC;


    std::cout << "Time using assign: " << using_assign << "\n";
    std::cout << "Time using loop: " << using_loop << "\n";
    std::cout << "Time using clear: " << using_clear << "\n";
    std::cout << "Time using fill: " << using_fill << "\n";
    std::cout << "Ignore: " << count1 << "\t" << count2 << "\t" << count3 << "\t" << count4 << "\n";
}

所以这会创建一个向量,在其中设置一些随机选择的位,对它们进行计数,然后清除它们(并重复)。设置/计数/打印是为了确保即使进行了积极的优化,编译器也不能/不会优化我们的代码以清除向量。

至少可以说,我发现结果很有趣。首先是 VC++ 的结果:

Time using assign: 0.141
Time using loop: 0.068
Time using clear: 0.141
Time using fill: 0.087
Ignore: 16777216        16777216        16777216        16777216

因此,对于 VC++,最快的方法可能是您最初认为最幼稚的方法——分配给每个单独项目的循环。使用 g++,结果只是 tad 不同:

Time using assign: 0.002
Time using loop: 0.08
Time using clear: 0.002
Time using fill: 0.001
Ignore: 16777216        16777216        16777216        16777216

在这里,循环是(到目前为止)最慢的方法(其他方法基本上是并列的——1 毫秒的速度差异实际上是不可重复的)。

不管怎样,尽管这部分测试显示为使用 g++ 快得多,但总体时间彼此相差不到 1%(VC++ 为 4.944 秒,VC++ 为 4.915 秒g++)。

【讨论】:

  • 这两个平台之间的相对差异令人惊讶。只有fill 两次出现在较快的组中。我认为这里的要点是vector&lt;bool&gt; 不可靠。
  • "unreliable" 表明它不起作用。它在任何地方都有效,而且您显然也可以指望内存高效的实现。您不能指望的一件事是速度。
  • @MSalters:除了它在所有方面都不是完全符合的向量(STL 容器)......但是,+1 简明扼要地总结了大多数人需要知道的内容。
【解决方案2】:

试试

v.assign(v.size(), false);

看看这个链接: http://www.cplusplus.com/reference/vector/vector/assign/

或以下

std::fill(v.begin(), v.end(), 0)

【讨论】:

  • “尝试”和“可能有用”;一个短语中的两个猜测;不是一个非常高质量的答案。尤其是在 C++ 中,这样的答案可能是不可移植的,甚至是危险的。
  • @phresnel by try 我的意思是我相信代码会起作用。第二个代码我不太确定。没有什么值得一票否决:)
  • 注意gcc实现的assign实际上做了一个std::fill gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=libstdc%2B%2B-v3/…
  • @log0 感谢参考
  • @log0 我不是 100% 确定,但在我看来,assign 中的 std::fill 是在容器词之上,而不是单个位。所以这可能是一种优化的方式,至少对于 GCC 而言。 Jerry 的 VC 测试是倒退的。
【解决方案3】:

你运气不好。 std::vector&lt;bool&gt; 是一个专业化,显然甚至不保证连续内存或随机访问迭代器(甚至向前?!),至少基于我对 cppreference 的阅读——解码标准将是下一步。

所以编写实现特定的代码,祈祷并使用一些标准的归零技术,或者不使用类型。我投 3 票。

收到的智慧是这是一个错误,可能会被弃用。如果可能,请使用不同的容器。绝对不要乱搞内部胆量,或者依赖它的包装。检查您的std 库中是否有动态位集,或者在std::vector&lt;unsigned char&gt; 周围滚动您自己的包装器。

【讨论】:

  • 这并不能直接回答问题,但您的选择 3 可能是正确的前进道路。使用一个做同样事情但从一开始就为它制作的容器。 @TemplateRex 的链接显示了正确(和错误)完成时位向量的优点:isocpp.org/blog/2012/11/on-vectorbool
【解决方案4】:

我最近遇到了这个性能问题。我没有尝试在网上寻找答案,但确实发现使用 g++ O3 (Debian 4.7.2-5) 4.7.2 使用构造函数的赋值要快 10 倍。我发现这个问题是因为我想避免额外的malloc。看起来分配和构造函数一样优化,并且在我的基准测试中大约是两倍。

unsigned sz = v.size(); for (unsigned ii = 0; ii != sz; ++ii) v[ii] = false;
v = std::vector(sz, false); // 10x faster
v.assign(sz, false); >      // 20x faster

所以,我不会回避使用vector&lt;bool&gt; 的专业化;只需非常了解位向量表示即可。

【讨论】:

  • 感谢您的解决方案。有人能告诉我为什么assign 比循环快得多吗?无论如何,它必须在内部循环来设置值,对吧?
【解决方案5】:

使用为此目的提供的std::vector&lt;bool&gt;::assign 方法。 如果实现是特定于bool 的,那么assign 很可能也得到了适当的实现。

【讨论】:

    【解决方案6】:

    如果您能够从 vector&lt;bool&gt; 切换到自定义位向量表示,那么您可以使用专为快速清除操作而设计的表示,并获得一些潜在的相当显着的加速(尽管并非没有权衡)。

    诀窍是对每个位向量条目使用整数和单个“滚动阈值”值,该值确定哪些条目实际评估为真。

    然后您可以通过仅增加单个阈值来清除位向量,而无需触及其余数据(直到阈值​​溢出)。

    可以在here 找到关于此的更完整的文章和一些示例代码。

    【讨论】:

      【解决方案7】:

      似乎还没有提到一个不错的选择:

      auto size = v.size();
      v.resize(0);
      v.resize(size);
      

      STL 实现者应该已经选择了最有效的归零方法,因此我们甚至不需要知道可能是哪种特定方法。这也适用于真实向量(想想模板),而不仅仅是std::vector&lt;bool&gt; monstrosity。

      在循环中重用缓冲区(例如筛子等)可能有一个微小的额外优势,您只需将大小调整为当前轮次所需的大小,而不是原始大小。

      【讨论】:

        【解决方案8】:

        作为std::vector&lt;bool&gt; 的替代品,请查看boost::dynamic_bitset (https://www.boost.org/doc/libs/1_72_0/libs/dynamic_bitset/dynamic_bitset.html)。您可以通过调用reset() 成员函数来清零(即,将每个元素设置为false)。

        就像在boost::dynamic_bitset 上清除std::vector&lt;int&gt;reset 一样,也可以编译为memset,而std::vector&lt;bool&gt; 可能无法实现。例如,见https://godbolt.org/z/aqSGCi

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-04-20
          • 2010-10-17
          • 1970-01-01
          • 1970-01-01
          • 2020-03-12
          • 2014-09-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多