RNote101---多线程处理

网友投稿 764 2022-10-08

RNote101---多线程处理

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小时内删除侵权内容。

上一篇:FreeMina- 兼容微信小程序Mina框架
下一篇:FAutoTest- H5、小程序自动化测试框架(fautotest)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~