微前端架构如何改变企业的开发模式与效率提升
764
2022-10-08
RNote101---多线程处理
R的循环效率比较慢,通常可以使用apply族函数进行加速,那么多线程怎么整?只介绍方法,原理不是很懂。
R会给出程序运行时间,供参考:
用户:是消耗在应用程序(非操作系统部分)执行的时间系统:是底层操作系统执行(例如磁盘读写等)部分的时间流逝:是经过的总时间(可以认为是前两者的总和)
snow包
先查看电脑有几个core 我现有电脑配置是6核,逻辑处理器是12
parallel::detectCores()
12
# 不设置多线程 vec <- 1:10000000cat("-------- vapply执行 --------")system.time(vapply(vec,function(x) x+2,FUN.VALUE=0))# 开启多进程cat("-------- Core设置为5 --------")clus <- snow::makeCluster(5)system.time(snow::parLapply(clus,vec, function(x){x+2}))cat("-------- Core设置为10 --------")clus <- snow::makeCluster(10)system.time(snow::parLapply(clus,vec, function(x){x+2}))# 关闭多进程snow::stopCluster(clus)
-------- vapply执行 -------- user system elapsed 4.50 0.03 4.54 -------- Core设置为5 -------- user system elapsed 2.14 0.44 6.58 -------- Core设置为10 -------- user system elapsed 2.11 0.25 4.99
**可以看到做了多线程之后,user时间变少了。**但是很多时候我们的处理过程并不是简单的向量apply操作,比如做分析时,会有多个SQL需要查询,这些SQL本身独立,也可多线程的操作,该如果处理呢?看个Demo:
有一个数据框,两列(name,value)要求做groupby操作进行sum求和,name作为参数多线程计算
构造数据
demoDf <- data.frame(name=sample(letters,100000,replace = TRUE),value=seq(1,100000))
进行groupby的操作
demofunction <- function(name,df=demoDf){ subDf <- subset(df,name==name) return(data.frame(name=name,sum=sum(subDf$value))) }
system.time(expr = { clus <- snow::makeCluster(10) # 提交多线程用到的变量&函数 snow::clusterExport(clus, c("demofunction", "demoDf")) res <- snow::parLapply(clus, letters, function(x) demofunction(name = x, df = demoDf)) # 关闭多进程 snow::stopCluster(clus) resDf <- do.call(rbind, res)})
user system elapsed 0.01 0.00 1.69
head(resDf,3)
A data.frame: 3 × 2
name | sum |
<fct> | <dbl> |
a | 5000050000 |
b | 5000050000 |
c | 5000050000 |
foreach和doParallel
snow包的用法像是apply族的并行化,foreach和doParallel更像是对for循环的并行化。还是那句话原理不是很懂,看代码~
library(foreach)# library(doParallel)eig <- function(n, p){x <- matrix(rnorm(100000), ncol=100)r <- cor(x)eigen(r)$values}n <- 1000000p <- 100k <- 500cat("-------- 不并行 --------")# 不并行system.time(x <- foreach(i=1:k, .combine=rbind) %do% eig(n, p))cat("-------- Core设置为2 --------")cl<- snow::makeCluster(2) # 注册线程,可以是detectCores()返回的核数。## 登记内核数量 doParallel::registerDoParallel(cl)system.time(y <- foreach(i=1:k, .combine=rbind) %dopar% eig(n, p))snow::stopCluster(cl) # 结束集群cat("-------- Core设置为5 --------")cl<- snow::makeCluster(5) # 不过R中能运行满线程 ,即detectCores()返回的核数。## 登记内核数量 doParallel::registerDoParallel(cl)system.time(y <- foreach(i=1:k, .combine=rbind) %dopar% eig(n, p))snow::stopCluster(cl) cat("-------- Core设置为10 --------")cl<- snow::makeCluster(10) # 不过R中能运行满线程 ,即detectCores()返回的核数。## 登记内核数量 doParallel::registerDoParallel(cl)system.time(y <- foreach(i=1:k, .combine=rbind) %dopar% eig(n, p))snow::stopCluster(cl)
Warning message:"package 'foreach' was built under R version 3.6.3"-------- 不并行 -------- user system elapsed 6.22 0.00 6.21 -------- Core设置为2 -------- user system elapsed 0.14 0.06 3.38 -------- Core设置为5 -------- user system elapsed 0.06 0.02 1.47 -------- Core设置为10 -------- user system elapsed 0.20 0.03 1.04
从系统总耗时看,所耗费时间是降低的。再看一个简单的例子:
system.time(expr = { foreach(i = 1:4) %do% { Sys.sleep(5) i }})
user system elapsed 0.00 0.00 20.03
cl<- snow::makeCluster(5) # 不过R中能运行满线程 ,即detectCores()返回的核数。## 登记内核数量 doParallel::registerDoParallel(cl)system.time(expr = { foreach(i = 1:4) %dopar% { Sys.sleep(5) i }})snow::stopCluster(cl)
user system elapsed 0.02 0.00 5.03
当不并行时,sleep5s串行了4次,共计20s;并行之后相当于在不同线程同时sleep一次,5s;
snow和foreach
system.time(expr = { clus <- snow::makeCluster(5) # 提交多线程用到的变量&函数 snow::clusterExport(clus, c("eig", "k","p")) res <- snow::parLapply(clus, 1:k, function(x) eig(n, p)) # 关闭多进程 snow::stopCluster(clus) resDf <- do.call(rbind, res)})
user system elapsed 0.01 0.02 2.40
head(resDf)
A matrix: 6 × 100 of type dbl
1.656100 | 1.640637 | 1.616798 | 1.586866 | 1.566930 | 1.538374 | 1.523015 | 1.495004 | 1.488897 | 1.468040 | ... | 0.5862909 | 0.5762028 | 0.5741423 | 0.5644911 | 0.5384094 | 0.5363696 | 0.5235116 | 0.5159777 | 0.4964601 | 0.4866267 |
1.691235 | 1.658395 | 1.609777 | 1.575012 | 1.558377 | 1.539480 | 1.522883 | 1.496592 | 1.489386 | 1.458325 | ... | 0.5896276 | 0.5873841 | 0.5737916 | 0.5722187 | 0.5611733 | 0.5579417 | 0.5285259 | 0.5217362 | 0.5153633 | 0.4782889 |
1.686425 | 1.653121 | 1.628179 | 1.598551 | 1.579656 | 1.557433 | 1.537228 | 1.498753 | 1.489975 | 1.460616 | ... | 0.5952069 | 0.5823180 | 0.5720212 | 0.5681074 | 0.5551917 | 0.5450221 | 0.5319406 | 0.5268110 | 0.5173417 | 0.5001969 |
1.713829 | 1.676294 | 1.613605 | 1.579971 | 1.567225 | 1.545787 | 1.519495 | 1.494373 | 1.471482 | 1.467215 | ... | 0.5946169 | 0.5853068 | 0.5719170 | 0.5502586 | 0.5498509 | 0.5435140 | 0.5358413 | 0.5112063 | 0.4937276 | 0.4767415 |
1.677933 | 1.615744 | 1.594095 | 1.577702 | 1.561510 | 1.544389 | 1.499172 | 1.491493 | 1.471160 | 1.449174 | ... | 0.6070764 | 0.6024897 | 0.5899197 | 0.5707263 | 0.5591482 | 0.5421557 | 0.5243488 | 0.5172421 | 0.5034002 | 0.4930905 |
1.659066 | 1.629982 | 1.612570 | 1.582731 | 1.570251 | 1.535848 | 1.517782 | 1.507463 | 1.494994 | 1.487952 | ... | 0.5987651 | 0.5832903 | 0.5745790 | 0.5577187 | 0.5494924 | 0.5432500 | 0.5378630 | 0.5128477 | 0.5062455 | 0.4900256 |
# library(doParallel)cl<- snow::makeCluster(5) # 不过R中能运行满线程 ,即detectCores()返回的核数。## 登记内核数量 doParallel::registerDoParallel(cl)system.time(expr={x <- foreach(i=1:1000)%dopar%function(x){x+2}})doParallel::stopImplicitCluster()snow::stopCluster(cl)
user system elapsed 0.87 0.31 1.19
上面简单的测试,发现向量化的简单操作snow似乎优势更大,foreach循环次数到10000000电脑基本卡住了。如果是做复杂计算,似乎foreach更合适些。实际应用可以做个测试看看~ 但是snow有个挺麻烦的事儿,需要把用到的变量和函数clusterExport,这个操作挺不人性化的。实际中用过来,感觉还是foreach的并行化处理更方便些。
Ref
[1] R语言实战第二版P444
[2] 于南京市江宁区九龙湖
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~