为啥使用多线程更新数组没有加速
Posted
技术标签:
【中文标题】为啥使用多线程更新数组没有加速【英文标题】:Why is there no speedup with multithreading for updating the array为什么使用多线程更新数组没有加速 【发布时间】:2016-10-14 13:39:25 【问题描述】:在这里,我们编写了一个代码,用于测试多线程更新数组 10,000,000 次的加速。在一些具有 2*intel E5-2620v2 CPU、centos 6.5、g++ 4.7.2 的机器上,我们发现 2-tread 比单线程慢得多。 在具有 2*intel E5-2660v2 CPU、centos 6.5、g++ 4.7.2 和 windows 机器的机器上,我们观察到了加速。如果我们将注释 1 的代码替换为“a[j]=i+j;”,我们也可以获得加速。 两类linux机器的内存带宽是一样的。
double a[1000];
void test(long long int number)
for(int i =0;i<number;i++)
for(int j = 0;j<1000;j++)
a[j] +=i; //*1*
int main()
int th = 1;
thread worker[th];
long long int number[th];
for(int i=0; i<th; i++)
number[i] = 10000000/th;
struct timeval start, end;
gettimeofday( &start, NULL );
for(int i=0; i<th; i++)
worker[i] = thread(test,number[i]);
for(int i=0; i<th; i++)
worker[i].join();
gettimeofday( &end, NULL );
double iterate_time =(end.tv_sec-start.tv_sec)+(end.tv_usec-start.tv_usec)/1000000.0;
cout << iterate_time<<endl;
【问题讨论】:
你的多线程任务很琐碎。你需要一个更耗时的任务来进行测试。 迭代一个大小为 1000 的数组非常快。创建/销毁新线程等本身需要很多时间。 附带说明 - 您在未受保护的内存上进行并发读取/写入 - 不要这样做,因为您不能期望您的值不会损坏。 (搜索互斥锁) 首先你有一个竞争条件...... 要并行化,您应该让每个线程在不同的内存上运行不一样。所以就像索引 0 - 2000 上的一个线程和索引 2001 - 3999 上的第二个线程。 【参考方案1】:给定的示例不适合多线程执行,因为任务实际上不能在线程之间拆分。
线程竞争单一共享资源:a[1000]
数组。由于cache coherence 协议,共享内存的争用导致高缓存间流量。详情请见MESI protocol。
争用的实际开销取决于特定的系统、CPU 和内存配置。即使在同一台机器上,您也可能会观察到明显不同的结果,具体取决于程序在哪些 CPU 上执行:
// CPUs within the same NUMA node
$ numactl -C 0,1 ./a.out
24.3272
// CPUs from different NUMA nodes
$ numactl -C 0,6 ./a.out
42.1547
【讨论】:
【参考方案2】:我认为您看到的是优化器正在省略单线程循环。
long long int number[th];
for(int i=0; i<th; i++)
number[i] = 10000000/th;
在这个循环之后,分配给number
的结果永远不会被读取,因此用数字填充number
的可观察效果与什么都不做的可观察效果相同(参见“as-if 规则”)。
你可以用一个小程序来演示:
int main()
int th = 1;
long long int number[th];
for(int i=0; i<th; i++)
number[i] = 10000000/th;
使用 gcc 和 clang 并进行优化,编译为:
main:
xor eax, eax
ret
我怀疑正在发生的事情是在这个简单的示例中添加线程会阻止优化器看到分配的结果从未使用过,因此线程版本实际上填充了一个向量,而单线程版本没有.
演示:https://godbolt.org/g/lewly3
【讨论】:
以上是关于为啥使用多线程更新数组没有加速的主要内容,如果未能解决你的问题,请参考以下文章