我有一个长期运行的模拟程序,我计划使用OpenMP来并行一些代码以加速。我是OpenMP的新手,有以下问题。
鉴于模拟是一个随机模拟,我有以下数据结构,我需要捕获特定年龄的种子代理[编辑:编辑一些代码]:
class CAgent {
int ageGroup;
bool isSeed;
/* some other stuff */
};
class Simulator {
std::vector<int> seed_by_age;
std::vector<CAgent> agents;
void initEnv();
/* some other stuff */
};
void Simulator::initEnv() {
std::fill(seed_by_age.begin(), seed_by_age.end(), 0);
#pragma omp parallel
{
#pragma omp for
for (size_t i = 0; i < agents.size(); i++)
{
agents[i].setup(); // (a)
if (someRandomCondition())
{
agents[i].isSeed = true;
/* (b) */
seed_by_age[0]++; // index = 0 -> overall
seed_by_age[ agents[i].ageGroup - 1 ]++;
}
}
} // end #parallel
} // end Simulator::initEnv()
由于变量seed_by_age
是跨线程共享的,我知道我必须正确地保护它。所以在(b)中,我使用了#pragma omp flush(seed_by_age[agents[i].ageGroup])
但是编译器在'''token'之前抱怨“错误:期望')'
我没有做减少,如果可能的话,我会尽量避免“关键”指令。那么,我在这里错过了一些东西吗?如何正确保护载体的特定元素?
非常感谢,我感谢任何建议。
答案 0 :(得分:2)
您只能将flush
用于变量,而不能使用数组的元素,绝对不能使用C ++容器类的元素。 std::vector
的索引运算符会调用operator[]
,这是一个内联函数,但仍然是函数。
因为在您的情况下std::vector::operator[]
返回对简单标量类型的引用,您可以使用atomic update
构造来保护更新:
#pragma omp atomic update
seed_by_age[0]++; // index = 0 -> overall
#pragma omp atomic update
seed_by_age[ agents[i].ageGroup - 1 ]++;
至于不使用简化,当满足循环内的条件时,每个线程都会触及seed_by_age[0]
,从而使所有其他核心中的同一缓存行无效。访问其他向量元素也会导致相互缓存失效,但假设代理在年龄组中或多或少地平均分配,则不会像向量中的第一个元素那样严重。因此我建议你做一些像:
int total_seed_by_age = 0;
#pragma omp parallel for schedule(static) reduction(+:total_seed_by_age)
for (size_t i = 0; i < agents.size(); i++)
{
agents[i].setup(); // (a)
if (someRandomCondition())
{
agents[i].isSeed = true;
/* (b) */
total_seed_by_age++;
#pragma omp atomic update
seed_by_age[ agents[i].ageGroup - 1 ]++;
}
}
seed_by_age[0] = total_seed_by_age;
答案 1 :(得分:1)
#pragma omp flush(seed_by_age[agents[i]].ageGroup)
尝试关闭所有括号,它将修复编译错误。
答案 2 :(得分:0)
我担心你的#pragma omp flush语句不足以保护你的数据并阻止竞争条件。 如果someRandomCondition()仅在极少数情况下为真,则可以使用临界区来更新向量而不会失去太多速度。或者,如果向量seed_by_age的大小不是太大(我假设),那么在离开并行块之前合并的每个线程都有一个私有版本的向量可能是有效的。