多线程向量求和的可扩展性
Posted
技术标签:
【中文标题】多线程向量求和的可扩展性【英文标题】:Scalability of multi-threaded vector sum 【发布时间】:2018-06-18 15:49:57 【问题描述】:这是一段用于多线程向量求和的 C++11 代码。
#include <thread>
template<typename ITER>
void sum_partial(ITER a, ITER b, double & result)
result = std::accumulate(a, b, 0.0);
template<typename ITER>
double sum(ITER begin, ITER end, unsigned int nb_threads)
size_t len = std::distance(begin, end);
size_t size = len/nb_threads;
std::vector<std::thread> thr(nb_threads-1);
std::vector<double> r(nb_threads);
size_t be = 0;
for(size_t i = 0; i < nb_threads-1; i++)
size_t en = be + size;
thr[i] = std::thread(sum_partial<ITER>, begin + be, begin + en, std::ref(r[i]));
be = en;
sum_partial(begin + be, begin + len, r[nb_threads-1]);
for(size_t i = 0; i < nb_threads-1; i++)
thr[i].join();
return std::accumulate(r.begin(), r.end(), 0.0);
典型的用法是 sum(x.begin(), x.end(), n)
和 x
一个双精度向量。
这是一个图表,显示计算时间作为线程数的函数(求和 10⁷ 值的平均时间,在没有其他任何运行的 8 核计算机上 - 我在 32 核计算机上尝试过,行为非常类似)。
为什么可扩展性这么差?可以改进吗?
我的(非常有限的)理解是,为了具有良好的可扩展性,线程应该避免在同一个缓存行中写入。这里所有线程都写入r
一次,在它们计算的最后,我不希望它成为限制因素。是内存带宽问题吗?
【问题讨论】:
需要 y 轴上的单位。 @UKMonkey 只需几秒钟,但我认为这并不重要...... 好吧,如果需要 Milliseconds to create thread: 0.015625 看起来线程创建可能是您的瓶颈 - 如果您的单位错误 @Elvis 这就是单位很重要的原因。 @Elvis 是的,你错了。std::accumulate
被定义为左折叠,矢量化会破坏这一点,因为浮点加法 is not associative。 (根据 fp 严格性设置,库/编译器可能会这样做。)我肯定会反对使用 std::accumulate
以获得最佳性能。
【参考方案1】:
accumulate
在 cpu 算术单元上的利用率很低,但缓存和内存吞吐量很可能是瓶颈,尤其是对于 10^7 double 或 1000 万 double = 80MB 数据,这远远超过 CPU 缓存大小.
要克服缓存和内存吞吐量瓶颈,您可能需要enable prefetch 和-fprefetch-loop-arrays
,甚至手动进行一些组装。
【讨论】:
以上是关于多线程向量求和的可扩展性的主要内容,如果未能解决你的问题,请参考以下文章