将 std::vector<int> 的每个值重置为 0 的最快方法

Posted

技术标签:

【中文标题】将 std::vector<int> 的每个值重置为 0 的最快方法【英文标题】:Fastest way to reset every value of std::vector<int> to 0 【发布时间】:2012-02-09 12:55:40 【问题描述】:

std::vector&lt;int&gt; 的每个值重置为 0 并保持向量初始大小的最快方法是什么?

带有 [] 运算符的 for 循环?

【问题讨论】:

std::fill “最快”的性能?还是最容易实施/维护? 【参考方案1】:
std::fill(v.begin(), v.end(), 0);

【讨论】:

查看汇编输出,gcc 实际上将这个循环展开为使用 mmx 寄存器一次转储 16 个字节,直到接近结束。我会说这非常快。 memset 版本跳转到 memset,我猜它的速度差不多。我会用你的方法。 但是,跳转到 memset 是一条指令,所以使用它会导致二进制文件更小。 这不是 OP 所要求的,但在我的机器上,简单地将你的向量重新分配给一个相同大小的新向量 (v = std::vector&lt;int&gt;(vec_size,0)) 似乎比 fill 稍快 这是最惯用的做法,比使用assign更惯用。 是否将它分配给一个新的向量做堆分配?然后丢弃现有向量的分配?我可以看到它比 memset 等人慢【参考方案2】:

当您询问最快时,一如既往:测量!使用上述方法(在 Mac 上使用 Clang):

Method      |  executable size  |  Time Taken (in sec) |
            |  -O0    |  -O3    |  -O0      |  -O3     |  
------------|---------|---------|-----------|----------|
1. memset   | 17 kB   | 8.6 kB  | 0.125     | 0.124    |
2. fill     | 19 kB   | 8.6 kB  | 13.4      | 0.124    |
3. manual   | 19 kB   | 8.6 kB  | 14.5      | 0.124    |
4. assign   | 24 kB   | 9.0 kB  | 1.9       | 0.591    |

在 10000 个整数的向量上使用 100000 次迭代。

编辑:如果更改此数字可能会更改结果时间,您可以一些相信人工基准测试没有这样做(不如检查最终汇编代码)完全被优化掉了。当然,最好在真实条件下弄乱性能。 结束编辑

参考使用的代码:

#include <vector>

#define TEST_METHOD 1
const size_t TEST_ITERATIONS = 100000;
const size_t TEST_ARRAY_SIZE = 10000;

int main(int argc, char** argv) 

   std::vector<int> v(TEST_ARRAY_SIZE, 0);

   for(size_t i = 0; i < TEST_ITERATIONS; ++i) 
   #if TEST_METHOD == 1 
      memset(&v[0], 0, v.size() * sizeof v[0]);
   #elif TEST_METHOD == 2
      std::fill(v.begin(), v.end(), 0);
   #elif TEST_METHOD == 3
      for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) 
         *it = 0;
      
   #elif TEST_METHOD == 4
      v.assign(v.size(),0);
   #endif
   

   return EXIT_SUCCESS;

结论:使用std::fill(因为,正如其他人所说的最惯用的)!

【讨论】:

+1。这个特定的基准测试不是决定性的,但这一点是绝对正确的,您应该编写替代方案的性能测试,因为它们实际上将被使用。如果没有性能差异,则使用最简单的来源。 "... 没有定论 ..." IMO 这种不确定性本身已经是进行基准测试的一个好点,优化器通常已经在这种情况下做得很好OP询问。我会将您的最后一句话修改为“如果没有显着性能差异......” UPDATE 使用 Nonius 进行基准测试:clang3.6-libc++-c++1y-O3、gcc4.9-c++1y-O3 和 gcc5-c++1y-O3 - TL;DRassign 是较慢,除了libc++ 上的小容量。代码coliru/paste 另外,哇,如果您关心没有优化的速度(如果您在“调试”模式下部署,这可能是合理的,某些团队会这样做),fill 看起来很糟糕。在这个测试中它慢了 两个数量级 @KyleStrand:并不是 fill 很糟糕,它是一个模板,代码是在翻译单元中使用 -O0 生成的。当你使用 memset 时,你使用的是用 -O3 编译的 libc 代码(即使你用 -O0 编译你的代码)。如果您关心调试速度并使用模板,则必须在使用 -O3 编译的单独文件中使用显式模板实例化【参考方案3】:

assign 成员函数怎么样?

some_vector.assign(some_vector.size(), 0);

【讨论】:

OP 想要重置现有值,但是当想要调整 重置值时,您的答案会更好。谢谢!【参考方案4】:

如果它只是一个整数向量,我会先尝试:

memset(&my_vector[0], 0, my_vector.size() * sizeof my_vector[0]);

它不是很 C++,所以我相信有人会提供正确的方法来做到这一点。 :)

【讨论】:

由于标准 (2003 TC1) 保证 std::vector 在内存中是连续的,这应该没问题。如果您的 c++ 库不符合 2003 TC1,请不要使用它。 @Mario:我不会发布这个,除非这是真的并且假设是众所周知的,当然。 :) 但是谢谢。 我检查了组件。 ::std::fill 方法扩展为非常快的东西,虽然在代码方面有点臃肿,因为它都是内联的。不过我还是会使用它,因为它更易于阅读。 你最好添加检查向量是否为空,在这种情况下什么都不做。为空向量计算 &buf[0] 可以在 STL 代码中生成断言。【参考方案5】:

我有同样的问题,但vector&lt;bool&gt; 很短(afaik 标准允许在内部以不同的方式实现它,而不仅仅是一个连续的布尔元素数组)。因此,我重复了 Fabio Fracassi 稍作修改的测试。结果如下(次,以秒为单位):

            -O0       -O3
         --------  --------
memset     0.666     1.045
fill      19.357     1.066
iterator  67.368     1.043
assign    17.975     0.530
for i     22.610     1.004

显然对于这些尺寸,vector&lt;bool&gt;::assign() 更快。用于测试的代码:

#include <vector>
#include <cstring>
#include <cstdlib>

#define TEST_METHOD 5
const size_t TEST_ITERATIONS = 34359738;
const size_t TEST_ARRAY_SIZE = 200;

using namespace std;

int main(int argc, char** argv) 

    std::vector<int> v(TEST_ARRAY_SIZE, 0);

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) 
#if TEST_METHOD == 1
        memset(&v[0], false, v.size() * sizeof v[0]);
#elif TEST_METHOD == 2
        std::fill(v.begin(), v.end(), false);
   #elif TEST_METHOD == 3
        for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) 
            *it = 0;
        
   #elif TEST_METHOD == 4
      v.assign(v.size(),false);
   #elif TEST_METHOD == 5
      for (size_t i = 0; i < TEST_ARRAY_SIZE; i++) 
          v[i] = false;
      
#endif
    

    return EXIT_SUCCESS;

我在 Ubuntu 17.10 上使用了 GCC 7.2.0 编译器。编译命令行:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp

【讨论】:

【参考方案6】:

试试

std::fill

还有

std::size siz = vec.size();
//no memory allocating
vec.resize(0);
vec.resize(siz, 0);

【讨论】:

调整大小非常好 我对@9​​87654323@进行了计时,发现使用-O3它的性能与memset相同。

以上是关于将 std::vector<int> 的每个值重置为 0 的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

如何将 std::vector<thrust::device_vector<int>> 转换为 int**?

将 std::vector<int> 设置为一个范围

将 std::vector<int> 的每个值重置为 0 的最快方法

将 std::stack 复制到 std::vector

将 std::vector<int> 从原始内存转换为数组[重复]

我可以制作一个线程安全的 std::atomic<vector<int>> 吗?