更快的替代push_back(大小已知)

时间:2017-07-05 18:58:52

标签: c++ performance vector push-back

我有一个浮动矢量。当我处理某些数据时,我会把它推回去。我总是知道在声明向量时大小是多少。

对于最大的案例,它是172,490,752个浮点数。这需要大约11秒来推送所有东西。

是否存在更快的替代方案,例如不同的数据结构或什么?

6 个答案:

答案 0 :(得分:9)

如果您知道最终大小,则在声明向量后reserve()大小。这样它只需要分配一次内存。

此外,您可以尝试使用emplace_back(),但我怀疑它会对float的向量产生任何影响。但是尝试它并对它进行基准测试(当然,使用优化的构建 - 你 使用优化的构建 - 对吧?)。

答案 1 :(得分:2)

预先知道尺寸时加快vector的常用方法是在使用reserve之前在其上调用push_back。这消除了每次填充先前容量时重新分配内存和复制数据的开销。

有时对于要求很高的应用程序来说,这还不够。即使push_back不会重新分配,它仍然需要每次都检查容量。如果没有基准测试,就没有办法知道它有多糟糕,因为现代处理器在总是/从不采用分支时效率非常高。

您可以尝试resize而不是reserve并使用数组索引,但resize强制每个元素的默认初始化;如果你知道你要为每个元素设置一个新值,这是一种浪费。

另一种方法是使用std::unique_ptr<float[]>并自行分配存储空间。

答案 2 :(得分:1)

::boost::container::stable_vector请注意,分配172 * 4 MB的连续块可能很容易失败,并且需要大量的页面抖动。稳定向量本质上是较小向量或合理大小的数组的列表。您可能还想并行填充它。

答案 3 :(得分:1)

我有两个答案:

  1. 正如之前的答案所指出的,使用reserve预先分配存储空间可能非常有用,但是:
  2. push_back(或emplace_back)本身会有性能损失,因为在每次调用期间,他们都必须检查是否必须重新分配该向量。如果您知道将要插入的元素数量,则可以通过使用访问运算符[]
  3. 直接设置元素来避免这种损失。

    所以我推荐的最有效方式是:

    1. 使用&#39; fill&#39; -constructor初始化向量:

      std::vector<float> values(172490752, 0.0f);
      
    2. 使用访问运算符直接设置条目:

      values[i] = some_float;
      ++i;
      

答案 4 :(得分:1)

您可以使用自定义分配器来避免所有元素的默认初始化,如this answer中所述,与普通元素访问一起使用:

std::vector

这避免了(i)默认初始化所有元素,(ii)在每次推送时检查容量,以及(iii)重新分配,但同时保留了使用std::unique_ptr<float[]>的所有便利(而不是{{ 1}})。但是,allocator模板参数不常见,因此您需要使用通用代码而不是std::vector特定代码。

答案 5 :(得分:0)

push_back缓慢的原因是它需要在向量增长时多次复制所有数据,即使它不需要复制数据也需要检查。向量增长得足够快,这种情况不会经常发生,但它仍然会发生。一个粗略的经验法则是每个元素需要平均复制一次或两次;早期的元素需要复制更多,但几乎一半的元素根本不需要复制。

您可以通过在创建向量时调用向量上的reserve来避免复制,而不是检查,确保它有足够的空间。您可以通过从开头创建正确大小,通过向向量构造函数提供元素数量,然后使用索引作为Tobias suggested插入来避免复制和检查。不幸的是,这也通过向量进行了额外的时间来初始化所有内容。

如果你知道编译时浮点数而不仅仅是运行时,你可以使用std::array,这可以避免所有这些问题。如果您只知道运行时的数字,我会在Mark’s suggestion之后使用std::unique_ptr<float[]>。你可以用

创建它
size_t size = /* Number of floats */;
auto floats = unique_ptr<float[]>{new float[size]};

你不需要做任何特别的事情来删除它;当它超出范围时,它将释放内存。在大多数方面,您可以像向量一样使用它,但它不会自动调整大小。