OpenMP并行化LU分解嵌套循环遇问题:collapse失效且性能下降
LU分解OpenMP并行化问题解决方案
错误原因及修复
1. 嵌套工作共享区域报错
你最初在外层k循环加了#pragma omp parallel for,又尝试在内层循环用collapse,这就造成了两层工作共享区域嵌套——OpenMP明确禁止工作共享区域(比如parallel for)紧密嵌套在其他工作共享、临界区、任务区等结构里,这就是报错的根源。
2. Collapsed循环不完美嵌套报错
collapse指令要求被折叠的循环必须是完美嵌套的,也就是循环之间不能插入任何非循环代码。你原代码中k循环内有两个独立的i循环,没法直接对k和i循环做折叠;如果想对内层循环用collapse,也得保证目标循环是连续嵌套、无额外代码干扰的。
正确并行化改造
首先必须明确:LU分解的k循环存在严格依赖——第k步的计算完全依赖第k-1步的结果,外层k循环绝对不能直接并行,这也是你线程越多越慢的核心原因:强行并行会导致线程间大量等待、数据竞争,同步开销远超并行收益,甚至会出现计算错误。
正确的做法是并行化内层无依赖的循环,具体代码如下:
for(k = 0; k < n - 1; ++k) { // 并行计算L矩阵的当前列 #pragma omp parallel for shared(A,n,k) private(i) schedule(static) num_threads(threads) for(i = k + 1; i < n; i++) { A[i][k] /= A[k][k]; } // 并行更新U矩阵的子块 #pragma omp parallel for shared(A,n,k) private(i,j) schedule(static) num_threads(threads) for(i = k + 1; i < n; i++) { const double Aik = A[i][k]; for(j = k + 1; j < n; j++) { A[i][j] -= Aik * A[k][j]; } } }
如果想尝试用collapse优化内层嵌套循环,可以针对i和j循环做折叠(注意这两个循环是完美嵌套的):
for(k = 0; k < n - 1; ++k) { #pragma omp parallel for shared(A,n,k) private(i) schedule(static) num_threads(threads) for(i = k + 1; i < n; i++) { A[i][k] /= A[k][k]; } // 折叠i和j循环,提升并行粒度 #pragma omp parallel for collapse(2) shared(A,n,k) private(i,j,Aik) schedule(static) num_threads(threads) for(i = k + 1; i < n; i++) { const double Aik = A[i][k]; for(j = k + 1; j < n; j++) { A[i][j] -= Aik * A[k][j]; } } }
线程数越多越慢的优化建议
- 放弃外层k循环并行:如前所述,k循环有依赖,强行并行只会增加开销。
- 匹配物理核心数:线程数不要超过CPU的物理核心数(超线程核心的收益有限),比如8核CPU设8线程即可,过多线程会导致频繁上下文切换。
- 设置串行阈值:当矩阵尺寸
n较小时(比如小于200),并行的开销可能超过计算收益,此时可以加判断,直接串行执行。
内容的提问来源于stack exchange,提问作者Rayan Ali
相关产品推荐
相关产品推荐

