OpenMP任务执行时间差异:Pragma指令位置影响排查
OpenMP任务并行中指令位置导致的性能差异分析
问题背景
我正尝试结合OpenMP与MPI,通过任务机制并行化SSOR迭代的代码。核心代码结构如下:
主函数调用片段
double t_copy = 0, t_forward = 0, t_backward = 0, t_diag = 0; void pc_ssor_poisson3d(int N, void *data, double *restrict Ax, double *restrict x) { // clocks for timing #define COPY_CLOCK 20 #define FW_SSOR_CLOCK 21 #define DIAG_SSOR_CLOCK 22 #define BW_SSOR_CLOCK 23 pc_ssor_p3d_t *ssor_data = (pc_ssor_p3d_t *)data; int n = ssor_data->n; double w = ssor_data->omega; tic(COPY_CLOCK); #ifdef PAR_PC parallel_copy(N, Ax, x); #else memcpy(Ax, x, N * sizeof(double)); #endif t_copy += toc(COPY_CLOCK); tic(FW_SSOR_CLOCK); #ifdef PAR_PC // 场景1:并行指令放在此处(标记--1--) #pragma omp parallel #pragma omp single { parallel_ssor_forward_sweep(n, 0, n, 0, n, 0, n, Ax, w); } #else // 串行实现 ... #endif t_forward += toc(FW_SSOR_CLOCK); }
parallel_ssor_forward_sweep函数
void parallel_ssor_forward_sweep(int n, int i1, int i2, int j1, int j2, int k1, int k2, double *restrict Ax, double w) { char *dep_matrix = (char *)malloc(sizeof(char) * (i2 - i1) / BS * (j2 - j1) / BS * (k2 - k1) / BS); // 场景2:并行指令放在此处(标记--2--) #pragma omp parallel #pragma omp single { for (int k = 0; k < (k2 - k1) / BS; k++) { for (int j = 0; j < (j2 - j1) / BS; j++) { for (int i = 0; i < (i2 - i1) / BS; i++) { ssor_forward_sweep_pwrap(n, i1, i2, j1, j2, k1, k2, Ax, w, dep_matrix, i, j, k); } } } } free(dep_matrix); }
任务创建函数ssor_forward_sweep_pwrap
void ssor_forward_sweep_pwrap(int n, int i1, int i2, int j1, int j2, int k1, int k2, double *restrict Ax, double w, char *dep_matrix, int i, int j, int k) { #define dep_mat(i, j, k) (dep_matrix[(k * (j2 - j1) + j) * (i2 - i1) + i]) char *top_dep = k - 1 >= 0 ? &dep_mat(i, j, k - 1) : NULL; char *left_dep = j - 1 >= 0 ? &dep_mat(i, j - 1, k) : NULL; char *back_dep = i - 1 >= 0 ? &dep_mat(i - 1, j, k) : NULL; char *out_dep = &dep_mat(i, j, k); #pragma omp task depend(in \ : *top_dep, *left_dep, *back_dep) depend(inout \ : *out_dep) { ssor_forward_sweep(n, i1 + i * BS, i1 + (i + 1) * BS, j1 + j * BS, j1 + (j + 1) * BS, k1 + k * BS, k1 + (k + 1) * BS, Ax, w); } #undef dep_mat }
疑惑点
两种场景下输出结果一致,但性能差异明显:
- 场景1耗时约9.6-9.7秒
- 场景2耗时约12.7-12.8秒
线程数固定为6,环境无干扰。我困惑的是:omp single区域内仅单线程执行,malloc的位置为何会导致这么大的性能差异?
原因分析
你看到的性能差异并非由malloc的位置直接导致,核心是两种场景下OpenMP并行区域的范围和任务执行的重叠程度不同:
并行区域启动时机与线程就绪度
- 场景1中,并行区域在
parallel_ssor_forward_sweep调用前就已启动,所有线程提前进入等待状态。当主线程在single区域内执行malloc、循环创建任务时,其他线程可以立即窃取并执行已创建的任务,任务的创建和执行是高度重叠的。 - 场景2中,malloc是在并行区域外串行执行的,之后才启动并行区域。OpenMP启动并行区域存在一定的线程初始化开销(即使使用线程池,首次启动或切换也有延迟),主线程开始创建任务时,其他线程可能还未完全就绪,导致任务无法被及时执行,整体执行的重叠度大幅降低。
- 场景1中,并行区域在
任务生命周期的调度效率
- 场景1中,整个
parallel_ssor_forward_sweep的执行都在并行区域内,任务从创建到完成的调度全程在已就绪的线程池中进行,调度开销极小。 - 场景2中,并行区域仅覆盖任务创建的循环部分,线程需要在串行(malloc)和并行模式间切换,且任务创建完成后,线程还要退出并行区域,额外增加了调度和线程管理的开销。
- 场景1中,整个
内存缓存的间接影响
虽然malloc本身的开销可以忽略,但场景1中,dep_matrix是在并行区域的主线程中分配的,后续任务线程访问依赖矩阵时,缓存亲和性更好(主线程分配的内存大概率在其缓存行中,任务线程窃取任务后访问时缓存命中率更高);而场景2中,malloc在串行线程执行,后续并行线程访问时可能需要重新加载缓存,带来微小的额外开销,叠加后也会影响整体性能。
内容的提问来源于stack exchange,提问作者Alberto Perugini
相关产品推荐
相关产品推荐

