在 OpenMP 中使用私有版本的全局外部变量

Using private versions of global extern variables with OpenMP

我有两个全局外部变量。我希望每个线程都有自己的私有副本和另一个的共享副本。 objective 是让每个线程都在私有版本上工作,然后将信息组合到第二个中。

这可能看起来有点矫枉过正,但这是我真正在做的事情的 MWE:

外部变量在Globals.h文件中定义:

extern vector<int> TestVector;
extern vector<vector<int>> CombinedTestVector;

并且在 Globals.cpp 文件中:

vector<int> TestVector;
vector<vector<int>> CombinedTestVector;

已执行 MyFunc.cpp(包含许多内容,包括 Globals.h)

void MyFunc(int NumberOfIterations)
{
    int iter;
    SetupTestVector();
    cout << "Test vector initially looks like:\n";
    PrintTestVector();
#   pragma omp parallel for default(shared) private(iter) firstprivate(TestVector)
    for (iter = 0; iter < NumberOfIterations; iter++)
    {
        int MyThreadNum = omp_get_thread_num();
        RemoveOneFromYourThreadIndex(MyThreadNum);
#       pragma omp critical 
        {
            cout << "      Thread " << MyThreadNum << ", TestVector now looks like:\n";
            cout << "      ";
            PrintTestVector();
        }
        CombinedTestVector.push_back(TestVector);
    }
#   pragma omp barrier
    cout << "Combined vector looks like:\n";
    PrintCombinedVector();
}

///////////////////////////////////////////////////////////////////////
void SetupTestVector()
{
    TestVector.push_back(10);
    TestVector.push_back(11);
    TestVector.push_back(12);
    TestVector.push_back(13);
    TestVector.push_back(14);
}

///////////////////////////////////////////////////////////////////////
void PrintTestVector()
{
    for (size_t i = 0; i < TestVector.size(); i++)
    {
        cout << TestVector.at(i) << " ";
    }
    cout << "\n";
}

///////////////////////////////////////////////////////////////////////
void RemoveOneFromYourThreadIndex(int ThreadIndex)
{
    TestVector.at(ThreadIndex) -= 1;
    TestVector.push_back(ThreadIndex);
}

///////////////////////////////////////////////////////////////////////
void PrintCombinedVector()
{
    for (size_t i = 0; i < CombinedTestVector.size(); i++)
    {
        for (size_t j = 0; j < CombinedTestVector.at(i).size(); j++)
        {
            cout << CombinedTestVector.at(i).at(j) << " ";
        }
        cout << "\n";
    }
    cout << "\n";
}

如果我使用 NumberOfIterations = 1 执行此操作,我得到:

Test vector initially looks like:
10 11 12 13 14
     Thread 0, TestVector now looks like:
     9 11 12 13 14 0
Combined vector looks like:
10 11 12 13 14

所以你可以看到组合向量没有得到我要求的结果...如果用 3 个线程执行,情况更糟:

Test vector initially looks like:
10 11 12 13 14      Thread 2, TestVector now looks like:
     9 10 11 13 14 2 0 1
     Thread 0, TestVector now looks like:
     9 10 11 13 14 2 0 1
     Thread 1, TestVector now looks like:
     9 10 11 13 14 2 0 1
Combined vector looks like:
10 11 12 13 14
10 10 11 13 14 2
10 11 12 13 14

如何获得预期的行为,即离开:

Test vector initially looks like:
10 11 12 13 14      Thread 0, TestVector now looks like:
     9 11 12 13 14 0
     Thread 1, TestVector now looks like:
     10 10 12 13 14 1
     Thread 1, TestVector now looks like:
     10 11 11 13 14 2
Combined vector looks like:
9 11 12 13 14 0
10 10 12 13 14 1
10 11 11 13 14 2

这里似乎对 firstprivate 和相关人员的实际工作存在误解。他们做的是神奇地使全局变量在每个线程中引用不同的副本。相反,他们会在本地范围 中为您提供副本(如果您使用firstprivate,则从全局范围复制)。

这样想:

#   pragma omp parallel for default(shared) private(iter) firstprivate(TestVector)
    for (iter = 0; iter < NumberOfIterations; iter++)
    {
        thread_local vector<int> TestVector = ::TestVector; /* OMP MAGIC */

        int MyThreadNum = omp_get_thread_num();
        // etc. ...lots of stuff that completely ignores your local copy
        // (The function calls still operate on the global TestVector!)

        CombinedTestVector.push_back(TestVector);
    }

那么问题就很清楚了:在 RemoveOneFromYourThreadIndex 中,您引用的是 global TestVector,而不是在您的部分范围内的那个firstprivate。您需要将 那个本地实例 传递给 RemoveOneFromYourThreadIndex(以及应该在 TestVector 上运行的所有其他函数)。

此外,CombinedTestVector.push_back(TestVector); 未同步,因此存在竞争条件,您也应该解决该问题。


关于代码质量的其他评论:

  • Don't do using namespace std;.
  • 不要使用全局变量,它们会很快变得一团糟。你有 Globals.h 的事实非常令人担忧。
  • 我鼓励变量和函数以小写字母开头,而不是大写字母。