脚本 foreach_Sparklyr 1.2支持foreach函数了

本文探讨了如何使用R语言的foreach在Spark环境中进行文件合并统计,对比了R的FORK并行与Spark foreach的性能,发现Spark在数据读写操作中的效率较低。作者还分享了单机Standalone模式下优化CPU资源的策略,通过多应用实例最大化worker利用率。
摘要由CSDN通过智能技术生成

刚才在R-blogger上才看到这篇文章,垂死病中惊坐起,马上来试试。


众所周知,foreach常见于R里头的并行程序,我之前也写过相关的文章。

微笑牛油果:R语言多文件合并统计:foreach的高级用法​zhuanlan.zhihu.com
zhihu-card-default.svg

这次就按照上面文章中的用法来测试spark集群的foreach。

p. s. 虽然说是集群,但我只有一个服务器,当做单机测试看看就行。

首先在R里头读入要合并的文件位置,然后看下内容:

> a<-list.files('/home/testfiles',
+               recursive = T, full.names = T, pattern = '.csv')[1:5]
> fread(a[1], nrows = 5)
         V1 V2     V3     V4 V5   V6        V7         V8       V9
1: 12123.31  9 350000 350700 NA 1132 1.6174643 0.53193917 5.893926
2: 12172.31 19 310000 310115  6 1141 1.3583580 0.18948141 4.310760
3: 12173.31 19 310000 310115  6 1141 0.5696340 0.07945995 1.807738
4: 12173.31 19 310000 310115  6 1141 0.3696276 0.06254695 1.253260
5: 12173.31 19 310000 310115  6 1141 1.1088829 0.18764085 3.759780
           V10       V11
1: 0.056843234  222.0000
2: 0.029994972 1151.2273
3: 0.012578537  482.7727
4: 0.008010572  318.2500
5: 0.024031717  954.7500 
> nrow(fread(a[1]))
[1] 8741292

这次只抓5个文件来测试,对这些文件合并统计,以V3来分组统计V9及V10的总和,写一个自定义的合并函数:

> bind_fun<-function(...){
+   data<-rbind(...)[,.(V9 = sum(V9, na.rm = T),
+                       V10 = sum(V10, na.rm = T)),
+                    by = c('V3')]
+ }

按照blog的方法,注册spark app,注册并行后端,开始跑foreach:

> conf <- spark_config()
> conf$spark.executor.memory <- "28GB"
> conf$spark.memory.fraction <- 0.9
> conf$spark.executor.cores <- 4
> conf$spark.cores.max<-8  
> conf$spark.dynamicAllocation.enabled <- "false"
> 
> sc <- spark_connect(master="spark://127.0.1.7:7077", 
+                     version = "2.3.2",
+                     config = conf,
+                     spark_home = "/home/spark-2.3.2-bin-hadoop2.7/")
> 
> registerDoSpark(sc, nocompile = F)
> 
> raw<-foreach(i = a,
+              .packages = c('data.table','dplyr','stringr'),
+              .inorder = F,
+              .combine = 'bind_fun',
+              .multicombine = T,
+              .maxcombine = 3) %dopar% {
+                data<-fread(i)[,.(V9 = sum(V9, na.rm = T),
+                                 V10 = sum(V10, na.rm = T)),
+                              by = c('V3')]
+                return(data)
+              }

注册后端的函数目前只有nocompile ,就这个案例来看,设置为T或F,运行时间差不多都是1.2m。

再来看看FORK的并行:

> cl<-makeCluster(8, type = 'FORK')
> registerDoParallel(cl)
> 
> system.time({
+   raw<-foreach(i = a,
+                .packages = c('data.table','dplyr','stringr'),
+                .inorder = F,
+                .combine = 'bind_fun',
+                .multicombine = T,
+                .maxcombine = 3) %dopar% {
+                  data<-fread(i)[,.(V9 = sum(V9, na.rm = T),
+                                    V10 = sum(V10, na.rm = T)),
+                                 by = c('V3')]
+                  return(data)
+                }
+ })
   user  system elapsed 
  0.013   0.024  11.699 
> 
> stopCluster(cl)
> stopImplicitCluster()

用时12秒,这就很尴尬了。

其实从job的log不难看出这个案例里spark跑foreach慢的原因:

5dc321fe67c5b091ca7f270fd86cb824.png

出现了好几次collect。看起来spark跑foreach程序不适合用来读写数据。


最后分享一下单机standalone模式下一个有效利用CPU资源跑循环的方法。一般来说,Standalone模式可以在一台机子上配置多个worker。Standalone的设置可以参考我之前的文章。比方说这是我机子上worker的配置:

585d7b5cfc3a99c24aace97bd05e8feb.png
手动码一下ip

但实际运行中,在程序各阶段不是所有worker都在工作,这就造成CPU资源闲置。因此我们可以通过后台运行方式来申请多个app,最大限度的压榨每个work的资源。

我们先准备nohup要调用的脚本:

library(sparklyr)
library(data.table)

conf <- spark_config()
conf$spark.executor.memory <- "28GB"
conf$spark.memory.fraction <- 0.9
conf$spark.executor.cores <- 4
conf$spark.cores.max<-8  
conf$spark.dynamicAllocation.enabled <- "false"
#conf$spark.shuffle.service.enabled<-'true'

sc <- spark_connect(master="spark://127.0.1.7:7077", 
                    version = "2.3.2",
                    config = conf,
                    spark_home = "/home/spark-2.3.2-bin-hadoop2.7/")

<你的代码>

将上面的代码存成脚本,放在工作目录下,比方说/home/script.R

解释一下,spark.executor.cores设置为4,因为我的worker的cores也是4spark.cores.max设置为8,则这个app(就是下面的sc)最多用到8个cores。

因此,我们nohup执行script.R之后,worker的状态会显示:

d08aad2f3d38709e96c7b314a80b92bf.png

App栏则会出现:

21430a848d7bebe4faaf267f07a78ad4.png

可以看到,只有2个worker的cores被调用。然后我们按照同样的方法再nohup一次刚才的脚本,新的app就注册上了:

fab717824c4877459c14282a28cd7ddd.png

查看app工作的端口是按照注册顺序来编号的,第一个app就是localip:4040,第二个就是localip:4041,以此类推。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值