OpenMP并行区调用dynamics函数比临界区慢的原因排查
关于OpenMP并行调用dynamics函数的线程安全与耗时问题
背景与代码说明
我有一个函数void dynamics(A a, std::vector<double> &, std::vector<double> &, std::vector<double>),通过OpenMP创建的线程调用,所有输入都是并行块内创建的线程私有变量。
基础代码如下:
#include <iostream> #include <vector> #include <chrono> using namespace std; class A { // 内部实现代码 }; int main(void) { vector<double> a (12,0.0); vector<double> b (12,0.0); #pragma omp parallel for shared(a,b) for(int id = 0; id < 6; id++) { vector<double> a_private (2,0.0); vector<double> b_private (2,0.0); vector<double> c_private (2,(double)id); A d; // 记录线程开始时间(chrono实现) dynamics(d,a_private,b_private,c_private); // 记录线程结束时间并计算耗时 #pragma omp critical { for(int i = 0; i < 2; i++) a[i+(2*id)] = a_private[i]; for(int i = 0; i < 2; i++) b[i+(2*id)] = b_private[i]; } } print(a); print(b); return 0; }
为避免竞态条件,我将a_private、b_private赋值给全局a、b的操作放在了临界区中。但发现并行执行dynamics时,各线程的单任务耗时比把dynamics放在临界区中串行执行的耗时更长。
临界区执行dynamics的代码片段:
#pragma omp critical { // 记录线程开始时间(chrono实现) dynamics(d,a_private,b_private,c_private); // 记录线程结束时间并计算耗时 for(int i = 0; i < 2; i++) a[i+(2*id)] = a_private[i]; for(int i = 0; i < 2; i++) b[i+(2*id)] = b_private[i]; }
两种执行方式下,a和b的输出完全一致,多次运行结果也相同。
线程安全相关疑问
- 我认为
dynamics是线程安全的,但不确定是否真的安全? - 所有输入都是并行块内创建的变量,这些变量是否确实是线程私有?
耗时差异相关疑问
- 为何并行执行
dynamics时,各线程的单任务耗时比临界区中串行执行的耗时更长?我排除了线程创建管理开销的可能,因为两种情况都会创建线程。虽然并行总时间比串行短(有加速比),但单线程耗时差异极大。 - 会不会即使输入输出是线程私有,
dynamics内部仍存在竞态条件? - 我没有使用
omp_get_num_threads和omp_get_thread_num,这会不会是问题所在?
耗时数据对比
并行区执行dynamics时
ID = 3, Dynamics Time = 410233 ID = 2, Dynamics Time = 447835 ID = 5, Dynamics Time = 532967 ID = 1, Dynamics Time = 545017 ID = 4, Dynamics Time = 576783 ID = 0, Dynamics Time = 624855
临界区执行dynamics时
ID = 0, Dynamics Time = 331579 ID = 2, Dynamics Time = 303294 ID = 5, Dynamics Time = 307622 ID = 1, Dynamics Time = 340489 ID = 3, Dynamics Time = 303066 ID = 4, Dynamics Time = 293090
注:无法提供dynamics的最小复现代码,因为它是教授的专有代码。
内容的提问来源于stack exchange,提问作者Siddhant Kadwe
相关产品推荐
相关产品推荐

