这是一个用于多线程向量和的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
写一次,在计算的最后,我不会指望它是限制因素。这是内存带宽问题吗?
答案 0 :(得分:3)
accumulate
对cpu算术单元的使用率很低,但缓存和内存吞吐量很可能是瓶颈,特别是对于10 ^ 7 double或1000万double = 80MB数据,这比你的CPU更多缓存大小。
要克服缓存和内存吞吐量瓶颈,您可能需要enable prefetch -fprefetch-loop-arrays
,甚至手动执行某些程序集。