并行计算有显性并行和隐式并行,

隐式并行:OpenBLAS,Intel MKL,NVIDIA cuBLAS,H2O(参考我的博客)等

显性并行:parallel(主打lapply应用)、foreach(主打for循环)、SupR、还有利用GPU的办法(gpuR)

介绍可以参考:
https://cosx.org/2016/09/r-and-parallel-computing

parallel是比较基础的,不需要额外安装,直接加载就行,但是不是很稳定

parallel包

paralle包的并行主要靠parLapply()、parSapply()、parApply()、parRapply()、parCapply()函数实现

cl<- makeCluster(detectCores())  
    all.pcg <-  c("httr","jsonlite","magrittr")  
    worker.init <- function(p) sapply(p,library,character.only=TRUE) 
    clusterCall(cl, worker.init, all.pcg)    
    #此句用于将各个子进程的环境全部加载分配到各进程环境中
    mydata2 <- parLapply(
                cl,      #进程环境
                1:16,    #遍历参数
                GETPDF   #测试代码语句
               ) %>% rlist::list.rbind()
  stopCluster(cl)
cl <- makePSOCKcluster(使用核的数量)
clusterExport(cl,输入变量)
clusterEvalQ(cl, 输入包)
x <- parLapply(cl,循环次数,函数)

情形1:
myfun <- function(x){
   x^2
}
这个函数输入就只有x,则并行可以如下
cl <- makePSOCKcluster(2) #使用两个核
out <- parLapply(cl,1:10,myfun) # 这样1:10直接输入到函数myfun里面,作为x的值
情形2:
myfun <- function(x){
   x^2 + y + z
}
这时候函数里除了x还多出来y和z,先给y和z赋值
y <- 1
z <- 2
通过clusterExport把赋值的变量送进函数里面
cl <- makePSOCKcluster(2)
clusterExport(cl,c("y","z")) #传输外部变量
out <- parLapply(cl,1:10,myfun)
情形3:
myfun <- function(x){
   某函数(x^2 + y + z)
}
这个情形需要用到某函数,该函数包含在某包中。这时候需要通过clusterEvalQ将某包传入
cl <- makePSOCKcluster(2)
clusterExport(cl,c("y","z"))
clusterEvalQ(cl,c("某包"))
x <- parLapply(cl,1:10,myfun)
stopCluster(cl)
gc()

foreach包

实例

# foreach
library(foreach)
library(doParallel)

#分配核心数,这里获取了物理核心数
cores <- detectCores(logical=F)
cl <- makeCluster(cores)
registerDoParallel(cl, cores=cores)

system.time(
#.combine是数据的合并方法,如果缺省则返回一个list
  res <- foreach(i=1:20, .combine='rbind', .packages=c('需要的包')) %dopar%
    { 
    	#主要代码区域
      for (j in 1:100000) {
        s=i+j
      }
      
  return(s)
    }
)

#关闭并行
stopImplicitCluster()
stopCluster(cl)

参数介绍

  • %do%: 严格按照顺序执行任务(所以,也就非并行计算),%dopar%并行执行任务,%do%时候就像sapply或lapply,%dopar%就是并行启动器
  • .combine:运算之后结果的显示方式,default是list,“c”返回vector, cbind和rbind返回矩阵,并把数据整合起来.
  • .init:.combine函数的第一个变量
  • .final:返回最后结果
  • .inorder:TRUE则返回和原始输入相同顺序的结果(对结果的顺序要求严格的时候),FALSE返回没有顺序的结果(可以提高运算效率)。这个参数适合于设定对结果顺序没有需求的情况。
  • .muticombine:设定.combine函数的传递参数,default是FALSE表示其参数是2,TRUE可以设定多个参数
  • .maxcombine:设定.combine的最大参数
  • .errorhandling:如果循环中出现错误,对错误的处理方法
  • .packages:指定在%dopar%运算过程中依赖的package(%do%会忽略这个选项),用于并行一些机器学习算法。
  • .export:在编译函数的时候需要预先加载一些内容进去,类似parallel的clusterExport

用法举例:

x <- foreach(ii=1:100,.combine = "c",.export = c("semimetric.pca","quadratic"))%dopar% func(ii)

Snowfall包

另外一个比较稳定的并行包是snowfall依赖snow包和parallel包,可以参考:

可以比较一下运行速度:

library(parallel) #用于并行计算
library(snowfall)  # 载入snowfall包,用于并行计算
system.time(
  for (i in 1:2250) {
    for (j in 1:100000) {
      s=i+j
    }
  }
)

myfun<-function(i){
  for (j in 1:100000) {
    s=i+j
  }
}
system.time(
  # 并行初始化
  sfInit(parallel = TRUE, cpus = detectCores(logical = F) - 1)
  # 进行lapply的并行操作
  s<-sfLapply(1:2250, myfun)
  # 结束并行,返还内存等资源
  sfStop()
)

snowfall的一般框架

library((parallel))
library(snowfall)

#初始化计算群
sfInit(parallel = TRUE, cpus = min(coresmax,detectCores(logical = T)))

sfExport(XXX)#为每个进程载入相关的函数,数据
sfLibrary(XXX)#为每个进程载入相关的包

parlresult<-sfLapply(1:100, fun)
 
sfStop() #结束并行

并行计算的嵌套

对于第一级并行化,必须使用分布式内存技术(如 snow 包中的 makeCluster()),而在第二级并行化中,必须使用共享内存技术(多核包,mclapply())。