在OpenMP'for'中使用rand_r在2个线程中较慢

时间:2017-10-16 00:13:57

标签: multithreading openmp

以下代码使用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,一切都按预期工作。

1 个答案:

答案 0 :(得分:4)

这是虚假共享的教科书示例。通过使用每个线程占用一个元素的种子数组,可以强制逻辑上的私有变量在内存中彼此相邻。因此,它们都在同一个缓存行中。这意味着虽然没有线程尝试修改某个其他线程的种子,但每次迭代时每个线程都会修改缓存行本身。实际的问题是系统无法检测变量对缓存一致性的修改,只能检测缓存行修改。因此,在每个线程的每次迭代中,缓存行已被另一个线程修改,并且从系统的角度来看不再有效。它必须从内存重新加载(好吧,很可能是来自这里的共享L3缓存),导致代码速度变慢。

试试这个(未经测试):

TRIM