如何在OpenMP并行for循环中调用非线程安全的ARPACK?
问题描述
我有一个使用ARPACK求解特征问题的Fortran函数,需要从C文件中调用并置于并行环境下。尝试如下OpenMP并行for循环调用方式:
#pragma omp parallel for for (int i=0; i < N; i++) { double *var1 ... ; my_arpack_function(var1 ...); // Function call, with only vars local to the block passed }
该函数本身未并行化,每个线程独立调用,但此方式执行失败。改用带ordered子句的方式可运行,但完全失去并行优势:
#pragma omp parallel for ordered for (int i=0; i < N; i++) { double *var1 ... ; #pragma omp ordered { my_arpack_function(var1 ...); // Function call, with only vars local to the block passed } }
已知ARPACK非线程安全,这应为失败原因。请问是否可强制函数使用的所有变量均为私有实例?
解决方案分析
ARPACK的线程不安全源于它依赖全局/静态变量(比如内部工作数组、状态标记等),这些变量是线程共享的,这才是并行调用时冲突的核心。你没法直接通过OpenMP的私有变量声明强制ARPACK内部所有变量变为线程私有——因为这些变量是库内部维护的,不在你的代码控制范围内。
可行的解决思路如下:
- 使用线程安全的ARPACK衍生版本:比如ARPACK-NG提供了线程安全接口,通过传递上下文结构体替代全局状态,每个线程可独立创建专属上下文,避免共享冲突。
- 手动封装线程私有上下文:若只能用原版ARPACK,可自行封装调用层,给每个线程分配独立的ARPACK工作空间和状态变量,修改Fortran函数将原全局变量改为传入参数或模块内线程私有变量。但此方法需要修改ARPACK源码,实现难度较高。
- 标记Fortran模块变量为线程私有:如果能修改ARPACK的模块代码,可对其中的全局变量添加
!$OMP THREADPRIVATE指令,让每个线程拥有独立副本。同时要确保C调用Fortran时,每个线程的私有变量能正确初始化。 - 改用进程级并行:如果线程级并行难以实现,可使用MPI启动多个独立进程,每个进程处理部分任务,彻底避免线程间状态共享。此方法无需修改ARPACK,但进程间通信开销比线程大。
注意:ordered子句本质是将函数调用串行化,完全浪费并行资源,绝非最优解。
内容的提问来源于stack exchange,提问作者DJames
相关产品推荐
相关产品推荐

