我正在编写一个程序,其中一堆不同的类(都存储在 vector 中)使用公共(public)数据结构对私有(private)成员执行并行操作。我想使用OpenMP将其并行化以用于多个处理器,但是我对代码中的两个操作有两个疑问,在下面的示例注释中指出了这两个问题,它们显示了程序逻辑的简化形式。

#include <omp.h>
#include <iostream>
#include <sys/timeb.h>
#include <vector>

class A {
  private :
    long _i;
  public :
    void add_i(long &i) { _i += i; }
    long get_i() const { return _i; }
};

int main()
{
  timeb then;
  ftime(&then);
  unsigned int BIG = 1000000;
  int N = 4;
  std::vector<long> foo(BIG, 1);
  std::vector<A *> bar;
  for (unsigned int i = 0; i < N; i++)
  {
    bar.push_back(new A());
  }
  #pragma omp parallel num_threads(4)
  {
    for(long i = 0; i < BIG; i++)
    {
      int thread_n = omp_get_thread_num();
      // read a global variable
      long *to_add = &foo[i];
      // write to a private variable
      bar[thread_n]->add_i(*to_add);
    }
  }
  timeb now;
  ftime(&now);
  for (int i = 0; i < N; i++)
  {
    std::cout << bar[i]->get_i() << std::endl;
  }
  std::cout << now.millitm - then.millitm << std::endl;
}

第一条注释涉及从全局foo读取的内容。这是“虚假共享”(或数据晃动)吗?我阅读的大多数资源都谈到了写操作方面的虚假共享,但是我不知道是否同样适用于读操作。

第二条评论涉及对bar中的类的写操作。同样的问题:这是错误的分享吗?它们正在写入具有相同全局数据结构的元素(根据我的阅读,这是晃荡的),但仅作用于元素内的私有(private)数据。

当我用for循环替换OpenMP宏时,该程序的速度提高了大约25%,所以我猜我在做错什么了...

最佳答案

现代内存分配器是线程感知的。为防止在修改class A元素所指向的bar的每个实例时出现错误共享,您应将内存分配移到并行区域内,例如:

const int N = 4;
std::vector<long> foo(BIG, 1);
std::vector<A *> bar(N);
#pragma omp parallel num_threads(N)
{
  int thread_n = omp_get_thread_num();
  bar[thread_n] = new A();
  for(long i = 0; i < BIG; i++)
  {
    // read a global variable
    long *to_add = &foo[i];
    // write to a private variable
    bar[thread_n]->add_i(*to_add);
  }
}

还要注意,在这种情况下,omp_get_thread_num()仅被调用一次,而BIG时间与代码中的调用时间相反。调用一个函数的开销相对较低,但是当您执行多次时,它的开销就会增加。

09-07 10:51