影响 OpenMP 并行效率的方面
Aspects that affects the efficiency of OpenMP parallelism
我想使用 OpenMP 并行一个大循环以提高其效率。这是玩具代码的主要部分:
vector<int> config;
config.resize(indices.size());
omp_set_num_threads(2);
#pragma omp parallel for schedule(static, 5000) firstprivate(config)
for (int i = 0; i < 10000; ++i) { // the outer loop that I would like to parallel
#pragma omp simd
for (int j = 0; j < indices.size(); ++j) { // pick some columns from a big ref_table
config[j] = ref_table[i][indices[j]];
}
int index = GetIndex(config); // do simple computations on the picked values to get the index
#pragma omp atomic
result[index]++;
}
然后我发现如果我使用2、4或8个线程都无法提高效率。并行版本的执行时间通常大于顺序版本的执行时间。外循环有 10000 次迭代,它们是独立的,所以我希望多个线程并行执行这些迭代。
我猜性能下降的原因可能包括:config
的私有副本?或者,随机访问 ref_table
?或者,昂贵的原子操作?那么性能下降的具体原因是什么?更重要的是,我怎样才能获得更短的执行时间?
config
的私有副本或者ref_tables
的随机访问没有问题,我认为工作量很小,有 2 个潜在的问题阻碍了高效的并行化:
- 原子操作太昂贵了。
- 开销大于工作负载(这只是意味着不值得与 OpenMP 并行化)
我不知道哪一个对你的情况更重要,所以值得尝试摆脱原子操作。有2种情况:
a) 如果 results
数组是零初始化的,你必须使用:
#pragma omp parallel for reduction(+:result[0:N]) schedule(static, 5000) firstprivate(config)
其中 N
是 result
数组的大小并删除 #pragma omp atomic
。请注意,这适用于 OpenMP 4.5 或更高版本。对于 2-10 次迭代的循环,删除 #parama omp simd
也是值得的。因此,您的代码应如下所示:
#pragma omp parallel for reduction(+:result[0:N]) schedule(static, 5000) firstprivate(config)
for (int i = 0; i < 10000; ++i) { // the outer loop that I would like to parallel
for (int j = 0; j < indices.size(); ++j) { // pick some columns from a big ref_table
config[j] = ref_table[i][indices[j]];
}
int index = GetIndex(config); // do simple computations on the picked values to get the index
result[index]++;
}
b) 如果 result
数组未进行零初始化,解决方案非常相似,但在循环中使用一个临时的零初始化数组,然后将其添加到 result
数组。
如果速度不会提高,那么您的代码不值得在您的硬件上与 OpenMP 并行化。
我想使用 OpenMP 并行一个大循环以提高其效率。这是玩具代码的主要部分:
vector<int> config;
config.resize(indices.size());
omp_set_num_threads(2);
#pragma omp parallel for schedule(static, 5000) firstprivate(config)
for (int i = 0; i < 10000; ++i) { // the outer loop that I would like to parallel
#pragma omp simd
for (int j = 0; j < indices.size(); ++j) { // pick some columns from a big ref_table
config[j] = ref_table[i][indices[j]];
}
int index = GetIndex(config); // do simple computations on the picked values to get the index
#pragma omp atomic
result[index]++;
}
然后我发现如果我使用2、4或8个线程都无法提高效率。并行版本的执行时间通常大于顺序版本的执行时间。外循环有 10000 次迭代,它们是独立的,所以我希望多个线程并行执行这些迭代。
我猜性能下降的原因可能包括:config
的私有副本?或者,随机访问 ref_table
?或者,昂贵的原子操作?那么性能下降的具体原因是什么?更重要的是,我怎样才能获得更短的执行时间?
config
的私有副本或者ref_tables
的随机访问没有问题,我认为工作量很小,有 2 个潜在的问题阻碍了高效的并行化:
- 原子操作太昂贵了。
- 开销大于工作负载(这只是意味着不值得与 OpenMP 并行化)
我不知道哪一个对你的情况更重要,所以值得尝试摆脱原子操作。有2种情况:
a) 如果 results
数组是零初始化的,你必须使用:
#pragma omp parallel for reduction(+:result[0:N]) schedule(static, 5000) firstprivate(config)
其中 N
是 result
数组的大小并删除 #pragma omp atomic
。请注意,这适用于 OpenMP 4.5 或更高版本。对于 2-10 次迭代的循环,删除 #parama omp simd
也是值得的。因此,您的代码应如下所示:
#pragma omp parallel for reduction(+:result[0:N]) schedule(static, 5000) firstprivate(config)
for (int i = 0; i < 10000; ++i) { // the outer loop that I would like to parallel
for (int j = 0; j < indices.size(); ++j) { // pick some columns from a big ref_table
config[j] = ref_table[i][indices[j]];
}
int index = GetIndex(config); // do simple computations on the picked values to get the index
result[index]++;
}
b) 如果 result
数组未进行零初始化,解决方案非常相似,但在循环中使用一个临时的零初始化数组,然后将其添加到 result
数组。
如果速度不会提高,那么您的代码不值得在您的硬件上与 OpenMP 并行化。