python进行并行处理的分为多线程和多进程,多线程使用thread,多进程使用multiprocessing,还有线程池future。
假设要运行linux下的命令:
首先是普通的运行:
def Reconall(sub):
subanat = filedir + "/" + sub + "/anat/" + sub + "_T1w.nii"
Command_recon = "recon-all -s sub-101 -i sub-101_T1w.nii.gz -all"
os.system(Command)
import os, sys
workpath = '/home/clancy/Document'
filedir = os.path.join(workpath, 'test')
subs = os.listdir(filedir)
## run per sub
sub = subs[0]
Recon(sub)
cpu占用率瞬间就100%,到AntsN4BiasFiledCorrectionFs函数。不过是单cpu。
其次试试pool,线程池:
import re
from pathlib import Path
subs = list(set([Path(f).stem for f in Path(fmriprep_dir).glob(f"sub-*") if Path(f).is_dir()]))
subs.sort(key=lambda x: int("".join(re.findall("\d+",x))))
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(max_works=3)
for sub in subs:
pool.submit(Reconall, sub)
pool.shutdown
有2个cpu瞬间达到100%,到AntsN4BiasFiledCorrectionFs函数,第3个cpu落后了一些时间,但是后面也达到了100%。
然后试试多进程:
from multiprocessing import Pool
pool = Pool(processes=3)
for sub in subs:
pool.apply(Reconall, args=(sub, ))
pool.close()
pool.join()
只有1个cpu达到100%,到AntsN4BiasFiledCorrectionFs函数。
所以其实linux命令算是多线程了。
但是当我连接远程hpc,跑单核可以到100%,跑多线程时,hpc每个线程最大只能到10%的cpu负载,可能是hpc进行了某种限制。
至于更复杂的python多进程,全局变量锁,这些在需要的时候再看看。目前这个已经能满足日常使用了。
使用自带的基础parallel包,用到的函数有
parLapply(cl, x, FUN, ...)
mclapply(X, FUN, ..., mc.cores)
parLapply版本如下:
library(parallel)
#指定线程数
clus <- makeCluster(3)
#可以确定当前可用CPU数目
detectCores()
#编写函数用于测试
Run <- function(x){
a <- sample(1:(10000*x), 1000)
sd(a)
}
nums <- 1:1000
# 单线程版
lapply(nums, FUN = Run)
# 多线程版
parLapply(clus, nums ,fun = Run)
也就是在apply家族的函数,加一个par前缀就行。
parLapply无法使用全局变量,应传入后再使用,如下:
st = 10
clus <- makeCluster(3)
# 使用clusterExport传入全局变量
clusterExport(clus,"st",envir = environment())
Run <- function(x){
a <- sample(st:(10000*x), 1000)
sd(a)
}
nums <- 1:1000
system.time(parLapply(clus, nums ,fun = Run))
## 假如要使用全部的cores
clnum<-detectCores()
cl <- makeCluster(getOption("cl.cores", clnum));
# 关闭并行处理
stopCluster(cl)
stopCluster(clus)
mcapply一般仅在linux下使用,window会有一些想不到的bug。mcapply版本如下:
mclapply(mc.preschedule = F, mc.cores = 4, nums, Run)
mc.cores就是要调用的cores数量,mc.preschedule就是保证一组数据出错不会影响另一组数据的计算(lapply如果遇到错误,会直接中止计算)。