doSnow驱动的foreach并行循环中无法识别sp包spDistsN1函数
doSnow并行调用sp函数报找不到函数的排查与修正
问题本质
你遇到的报错是并行计算的典型环境隔离问题:doSNOW创建的每个工作节点是完全独立的R运行会话,默认不会继承主会话的任何环境内容——包括你在主会话通过library(sp)加载的函数、提前定义的全局数据对象。因此节点环境没有加载sp包时,自然无法识别spDistsN1函数。
除此之外原代码还存在潜在报错风险:就算解决了函数找不到的问题,节点环境中也不存在lonlat、intrp两个运算依赖的数据框,后续会直接抛出找不到对应对象的错误。
修正方案
在foreach的参数配置中显式声明并行节点需要的依赖,具体需要补充两个配置项:
.packages:指定每个节点启动后需要加载的R包,这里需要把sp加入参数列表.export:指定需要从主会话传递到节点环境的全局对象,这里需要把运算用到的lonlat、intrp两个对象加入参数列表
修正后的完整可运行代码:
# parallel processing library(foreach) library(doSNOW) library(sp) cl <- makeCluster(4, type="SOCK") # for 4 cores machine registerDoSNOW(cl) system.time({ output <- foreach( i = 1:nrow(intrp), .combine = c, # 让每个并行节点加载sp包 .packages = "sp", # 把运算依赖的全局数据传递到节点环境 .export = c("lonlat", "intrp") ) %dopar% { which.min(spDistsN1(lonlat[, 1:2], cbind(intrp[i,"LON"], intrp[i,"LAT"]),longlat = FALSE)) } }) stopCluster(cl)
补充说明
- 上述配置不是强制必须写在foreach参数里的:你也可以在注册集群后,提前用
clusterEvalQ(cl, library(sp))给所有节点批量加载sp包,用clusterExport(cl, c("lonlat", "intrp"))批量传递全局对象,最终效果和在foreach中写参数完全一致。 - 并行运行时需要注意控制节点间传输的数据量:如果传递的对象体积过大,数据传输的开销会抵消并行计算的性能收益,甚至让整体运行速度比串行更慢。
- 后续如果在并行代码里用到其他自定义函数、其他依赖包,都要按照上面的规则同步到节点环境,不能依赖主会话的环境自动继承。
内容的提问来源于stack exchange,提问作者Abdelouahed BEN MHAMED
相关产品推荐
相关产品推荐

