R语言foreach并行循环中无法调用eigen()函数问题咨询
问题原因
该问题由多线程线性代数库与fork模式并行的冲突导致:
非Windows系统(Linux、macOS)下的R默认会搭载OpenBLAS、Intel MKL等多线程版本的BLAS/LAPACK底层计算库,eigen()函数底层直接调用LAPACK接口,默认会启用多线程加速。而doParallel在非Windows系统默认采用fork模式创建子进程,fork生成的子进程会继承父进程的多线程BLAS状态,子进程内部的BLAS多线程调度和foreach的进程级并行会产生资源竞争甚至死锁,最终导致程序卡住无响应。
注释掉eigen()相关代码后没有调用多线程BLAS接口,不会触发冲突,因此可以正常运行。
可行解决方案
以下方案均可解决该问题,按实现成本从低到高排序:
- 方案1:限制BLAS/LAPACK的线程数为1,避免子进程内部多线程和进程级并行冲突。
可通过RhpcBLASctl包统一设置,修改后的代码如下:
由于已经使用进程级并行,单线程BLAS不会降低整体计算效率,反而会避免资源浪费,计算速度更快。library(doParallel) library(RhpcBLASctl) # 关闭BLAS和OpenMP的多线程,避免和进程并行冲突 blas_set_num_threads(1) omp_set_num_threads(1) registerDoParallel(12) res <- foreach(s = 1:50, .combine = c)%dopar%{ M <- matrix(rnorm(10*10), 10, 10) M <- M+t(M) E <- eigen(M)$values return(E) } - 方案2:手动切换为PSOCK集群模式,避免fork模式的线程继承问题。
非Windows系统也可以手动指定使用PSOCK集群,不存在多线程继承冲突,修改注册集群的代码即可:cl <- makeCluster(12, type = "PSOCK") registerDoParallel(cl) # 原有计算逻辑不变 # 计算结束后关闭集群 stopCluster(cl) - 方案3:使用
RSpectra包的特征值计算函数替代原生eigen()。RSpectra包的eigs_sym()(针对对称矩阵)、eigs()(针对普通矩阵)底层不依赖系统多线程BLAS,并行环境适配性更好,仅需要部分特征值时效率远高于原生eigen(),示例修改如下:# 提前安装RSpectra包:install.packages("RSpectra") library(RSpectra) # 循环内部替换eigen调用 E <- eigs_sym(M, k = 10)$values # k为需要提取的特征值数量,此处10为全量提取
内容的提问来源于stack exchange,提问作者user3037237
相关产品推荐
相关产品推荐

