用零并行填充 std::vector

Posted

技术标签:

【中文标题】用零并行填充 std::vector【英文标题】:Parallel fill std::vector with zero 【发布时间】:2017-02-04 20:00:06 【问题描述】:

我想用 openmp 将 std::vector<int> 填充为零。如何快速做到这一点?

我听说循环遍历向量以将每个元素设置为零很慢,而std::fill 要快得多。现在还是这样吗?

Fastest way to reset every value of std::vector<int> to 0

我是否必须手动将std::vector&lt;int&gt; 划分为区域,在每个线程上使用#pragma omp for 循环,然后在循环中使用std::fill

【问题讨论】:

是的,它仍然是真的。是的,如果您使用的是 OpenMP,您必须自己分配作业。无论哪种方式,如果你关心它的速度,你应该测量它。我建议分配 2 个大小的作业的功率(引用的线程说它在 MMX 寄存器中使用了 16 个整数,这可能是最小的可行作业大小)。根据向量的长度,单线程填充可能会更快。一定要测量并找到交叉点。这是一条评论,因为它并不能很好地回答您的问题。这只是你自己可能有过的沉思。 GCC 6.3 和 Clang 3.9.0 都将“循环并在各处分配 0”和 std::fill 编译为对 memset 的(尾)调用。这不是完全相同的代码,但繁重的工作是相同的。 打赌用零填充向量会占用您最少的时间。在您有证据表明这是问题区域之前,请不要担心。 @voo,“我开枪了,没有测试。”编译器。这就是为什么您应该始终进行测试的原因。现在,如果你原谅我,我得再吃点我的脚。 @sbabbi,这只是将实际工作转移到以后。由于局部性,可能很好,但也可能对所有页面错误都不利。 【参考方案1】:

您可以将向量拆分为块,以便每个线程填充std::fill

#pragma omp parallel
   
    auto tid = omp_get_thread_num();
    auto chunksize = v.size() / omp_get_num_threads();
    auto begin = v.begin() + chunksize * tid;
    auto end = (tid == omp_get_num_threads() -1) ? v.end() : begin + chunksize);
    std::fill(begin, end, 0);

您可以通过将chunksize 舍入到最接近的缓存线/内存字大小(128 字节 = 32 ints)来进一步改进它。假设v.data() 的对齐方式类似。这样,您就可以避免任何虚假分享问题。

在双插槽 24 核 Haswell 系统上,我获得了接近 9 倍的加速:1 个线程为 3.6 秒,24 个线程为 0.4 秒,4.8B 整数 = ~48 GB/秒,结果略有不同,并且这不是科学分析。但是离系统的内存带宽也不算太远。

对于一般性能,您应该关注将向量划分为不仅用于此操作,还用于进一步操作(无论是读取还是写入),如果可能的话。这样,如果您需要数据,或者至少在同一个 NUMA 节点上,您就可以增加数据实际在缓存中的机会。

奇怪的是,在我的系统上,std::fill(..., 1); 对于单线程比 std::fill(..., 0) 快,但对于 24 线程则慢。都使用 gcc 6.1.0 和 icc 17.0.1。我想我会把它发布到一个单独的问题中。

【讨论】:

一目了然,当向量的大小不能被线程数整除时,这似乎不是线程之间的最佳计算划分。最后一个线程的工作量可能比其他线程少。感谢omp parallel 的好例子。我不知道我们可以这样使用 tid,并且在我的代码中的每个线程上仍然有 omp 循环... 没有最后一个线程得到更多的工作,但最多nthreads - 1更多,可以忽略不计。或者你可以做chunksize = (v.size() - 1) / nthreads + 1,这更平衡一点。但我会争辩(去编辑那个),对齐块实际上更重要。 对齐块是什么意思? @rxu,你看到我回答的编辑了吗?还可以看看:en.wikipedia.org/wiki/… 这说明清楚了吗? 是的。我现在明白了。

以上是关于用零并行填充 std::vector的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C++11 会从 std::vector 填充构造函数的原型中移除默认值?

从特征数组/矩阵的行中填充 std::vector

为什么c ++用零来初始化std :: vector,而不是std :: array?

在某个阈值后,Std::vector 填充时间从 0ms 变为 16ms?

用零填充缺失的用户输入

用零填充字符串的更好方法[重复]