以下代码使用1个线程比使用2个更好(使用4个线程可以提高速度):
n
在我的笔记本电脑上(2核,HT启用)我得到以下结果:
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int main(int argc, char **argv) {
int n = atoi(argv[1]);
int num_threads = atoi(argv[2]);
omp_set_num_threads(num_threads);
unsigned int *seeds = malloc(num_threads * sizeof(unsigned int));
for (int i = 0; i < num_threads; ++i) {
seeds[i] = 42 + i;
}
unsigned long long sum = 0;
double begin_time = omp_get_wtime();
#pragma omp parallel
{
unsigned int *seedp = &seeds[omp_get_thread_num()];
#pragma omp for reduction(+ : sum)
for (int i = 0; i < n; ++i) {
sum += rand_r(seedp);
}
}
double end_time = omp_get_wtime();
printf("%fs\n", end_time - begin_time);
free(seeds);
return EXIT_SUCCESS;
}
问题持续存在而没有减少,$ gcc -fopenmp test.c && ./a.out 100000000 1
0.821497s
$ gcc -fopenmp test.c && ./a.out 100000000 2
1.096394s
$ gcc -fopenmp test.c && ./a.out 100000000 3
0.933494s
$ gcc -fopenmp test.c && ./a.out 100000000 4
0.748038s
没有带来任何差异,动态调度使事情变得更糟。但是,如果我用不随机连接的东西替换循环体,i。即drand48_r
,一切都按预期工作。
答案 0 :(得分:4)
这是虚假共享的教科书示例。通过使用每个线程占用一个元素的种子数组,可以强制逻辑上的私有变量在内存中彼此相邻。因此,它们都在同一个缓存行中。这意味着虽然没有线程尝试修改某个其他线程的种子,但每次迭代时每个线程都会修改缓存行本身。实际的问题是系统无法检测变量对缓存一致性的修改,只能检测缓存行修改。因此,在每个线程的每次迭代中,缓存行已被另一个线程修改,并且从系统的角度来看不再有效。它必须从内存重新加载(好吧,很可能是来自这里的共享L3缓存),导致代码速度变慢。
试试这个(未经测试):
TRIM