R︱并行计算以及提高运算效率的方式(parallel包、clusterExport函数、SupR包简介)
要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗“贝壳”~
———————————————————————————
终于开始攻克并行这一块了,有点小兴奋,来看看网络上R语言并行办法有哪些:
赵鹏老师(R与并行计算)做的总结已经很到位。现在并行可以分为:
隐式并行:隐式计算对用户隐藏了大部分细节,用户不需要知道具体数据分配方式 ,算法的实现或者底层的硬件资源分配。系统会根据当前的硬件资源来自动启动计算核心。显然,这种模式对于大多数用户来说是最喜闻乐见的。
显性并行:显式计算则要求用户能够自己处理算例中数据划分,任务分配,计算以及最后的结果收集。因此,显式计算模式对用户的要求更高,用户不仅需要理解自己的算法,还需要对并行计算和硬件有一定的理解。值得庆幸的是,现有R中的并行计算框架,如parallel (snow,multicores),Rmpi和foreach等采用的是映射式并行模型(Mapping),使用方法简单清晰,极大地简化了编程复杂度。R用户只需要将现有程序转化为*apply或者for的循环形式之后,通过简单的API替换来实现并行计算。
简单总结就是:
隐式并行:OpenBLAS,Intel MKL,NVIDIA cuBLAS,H2O(参考我的博客)等
显性并行:parallel(主打lapply应用)、foreach(主打for循环)、SupR、还有利用GPU的办法(gpuR)
同时并行时对内存的消耗极大,超级容易爆发内存问题,而且R的内存问题一直都是R很难解决的问题,这边笔者也把看到的一些方式列出来。
当然在使用一些高大上的并行包以及框架之前,如果你能够从编码小细节优化,效率也能提高很多,譬如:
方法:速度, nrow(df)/time_taken = n 行每秒 原始方法:1X, 856.2255行每秒(正则化为1) 向量化方法:738X, 631578行每秒 只考虑真值情况:1002X,857142.9行每秒 ifelse:1752X,1500000行每秒 which:8806X,7540364行每秒 Rcpp:13476X,11538462行每秒 apply处理并行
——————————————————————————————————————————————————————
在最后笔者在实践中遇到的问题,进行对应的解决:
应用一:使用parallel包时,能不能clusterExport整个函数呢?
应用二:在使用parallel包时,报错:Error in unserialize(node$con) : error reading from connection
——————————————————————————————————
一、parallel包的使用方法
多数内容参考:R语言并行化基础与提高
parallel是base包,所以不用install.packages就可以直接调用。
原理:是利用CPU的核心进行训练。
应用场景:跟apply族(lapply/sapply效果一致)(
R语言︱数据分组统计函数族——apply族用法与心得
)
1、使用步骤
设置核心数:no_cores <- detectCores() - 1
步骤分群环境:cl <- makeCluster(no_cores)
用到的变量与包复制给不同的核心:clusterEvalQ(包)、clusterExport(变量)
运行算法:clusterApply(cl, c(9,5), get("+"), 3)
关闭集群:
stopCluster(cl)
就OK啦。但是这里面很从前不一样的是,如果有环境里面的外置变量(自己定义)那么需要额外插入,复制到不同核上面,而且如果有不同包里面的函数,都要额外加载、复制多份给不同的电脑核心。
2、案例
library(parallel) cl <- makeCluster(getOption("cl.cores", 2)) clusterApply(cl, c(9,5), get("+"), 3) #加减乘除 parSapply(cl, c(9,5), get("+"), 3)
案例一:c1就是设置的核心数,此时是2核心,然后就可以利用clusterApply/parSapply等函数进行调用。
xx <- 1 clusterExport(cl, "xx") clusterCall(cl, function(y) xx + y, 2)
案例二:这个里面有xx这个变量是额外定义的,所以需要额外加载,需要用clusterExport函数,导入到并行环境中。
3、parallel内存优化与管理
(1)注意数据容量的均匀分布
parLapply <- function (cl = NULL, X, fun, ...) { cl <- defaultCluster(cl) do.call(c, clusterApply(cl, x = splitList(X, length(cl)), fun = lapply, fun, ...), quote = TRUE) }
注意到splitList(X, length(cl)) ,他会将任务分割成多个部分,然后将他们发送到不同的集群中。这里一个问题就是,譬如假设有一个list,里面数据量分别是:
(99,99,99,2,5,2)
如果是两个核数据分为了(99,99,99)、(2,5,2),第一个核分为到了那么多任务,第二个核很少,那么就会空闲,于是乎,效率还是不高,所以数据容量要尽量均匀分布。
(2)集群内存类型:FORK和PSOCK
FORK适用unix/max,实现内存共享以及节省内存,大数据环境下内存问题报错少
PSOCK适用所有(一般window都是这个)
parallel包中通过函数来设置:
makeCluster(4,type="FORK")
FORK对性能提升很显著,但是window下不可适用。
4、parallel万一报错了咋办?
lapply在使用的时候也会出现这样的问题,如果出现问题,那么就白跑了,而且也不可能给你停顿下来。那么如何让lapply运行中跳过报错的办法呢?
R语言相关的报错处理函数可见:R语言-处理异常值或报错的三个示例
用tryCatch跳过:
result = tryCatch( {expr}, warning = function(w) {warning-handler-code}, error = function(e) { error-handler-code}, finally = {cleanup-code} )
出现warning、error时候怎么处理,就可以跳过了。例子:
result = tryCatch( {segmentCN(txt)}, warning = function(w) {"出警告啦"}, error = function(e) { "出错啦"}, )
分词时候,容易因为Lapply中断之后,就不会运行了,这样功亏一篑所以可以用这个办法跳过。
5、parSapply/parLapply函数使用技巧
函数的大体结构是:
parSapply(cl,x,fun)
其中cl是预先设定好的,x是需要循环的变量,而fun是函数。
那么一般来说,fun之中要使用的任何内容都需要用clusterEvalQ(包)、clusterExport(变量)复制到不同的核心之中。
而x则可以不用布置到全局,因为他是在源环境下调用出来,并拆分任务的。
——————————————————————————————————
二、foreach包的使用方法
1、简单使用案例
设计foreach
包的思想可能想要创建一个lapply和for循环的标准,初始化的过程有些不同,你需要register
注册集群:
library(foreach)
library(doParallel)
cl<-makeCluster(no_cores)
registerDoParallel(cl)
要记得最后要结束集群(不是用stopCluster()
):
stopImplicitCluster()
foreach函数可以使用参数.combine
控制你汇总结果的方法:
> foreach(exponent = 2:4,
.combine = c) %dopar%
base^exponent
[1] 4 8 16
> foreach(exponent = 2:4, .combine = rbind) %dopar% base^exponent
[,1]
result.1 4 result.2 8 result.3 16
foreach(exponent = 2:4, .combine = list, .multicombine = TRUE) %dopar% base^exponent
[[1]]
[1] 4 [[2]]
[1] 8 [[3]]
[1] 16
注意到最后list的combine方法是默认的。在这个例子中用到一个.multicombine
参数,他可以帮助你避免嵌套列表。比如说list(list(result.1, result.2), result.3)
:
> foreach(exponent = 2:4,
.combine = list) %dopar%
base^exponent
[[1]]
[[1]][[1]]
[1] 4
[[1]][[2]]
[1] 8
[[2]]
[1] 16
2、变量作用域
在foreach中,变量作用域有些不同,它会自动加载本地的环境到函数中:
> base <- 2
> cl<-makeCluster(2)
> registerDoParallel(cl)
> foreach(exponent = 2:4,
.combine = c) %dopar%
base^exponent
stopCluster(cl)
[1] 4 8 16
但是,对于父环境的变量则不会加载,以下这个例子就会抛出错误:
test <- function (exponent) {
foreach(exponent = 2:4,
.combine = c) %dopar%
base^exponent
}
test()
Error in base^exponent : task 1 failed - "object 'base' not found"
为解决这个问题你可以使用.export
这个参数而不需要使用clusterExport
。注意的是,他可以加载最终版本的变量,在函数运行前,变量都是可以改变的:
base <- 2
cl<-makeCluster(2)
registerDoParallel(cl)
base <- 4
test <- function (exponent) {
foreach(exponent = 2:4,
.combine = c,
.export = "base") %dopar%
base^exponent
}
test()
stopCluster(cl)
[1] 4 8 16
相似的你可以使用.packages
参数来加载包,比如说:.packages = c("rms", "mice")
——————————————————————————————————
三、SupR
通过对现有R 内核的改进实现在单机上的多线程和在集群上的分布式计算功能。SupR目前仍处在内部试用和补充完善阶段。
据说supR很好用,而且小象学院的讲师(游皓麟)已经开始教授这个系统的使用方法,挺好用的。
——————————————————————————————————
四、内存管理
方法有三:
一、升级硬件
二、改进算法
三、修改操作系统分配给R的内存上限, memory.size(T)查看已分配内存
memory.size(F)#查看已使用内存 memory.limit()#查看内存上限 object.size()#看每个变量占多大内存。 memory.size()#查看现在的work space的内存使用 memory.limit()#查看系统规定的内存使用上限。如果现在的内存上限不够用,可以通过memory.limit(newLimit)更改到一个新的上限。注意,在32位的R中,封顶上限为4G,无法在一个程序上使用超过4G (数位上限)。这种时候,可以考虑使用64位的版本。
详情看:R语言︱大数据集下运行内存管理 以及 R语言之内存管理
——————————————————————————————————
应用一:使用parallel包时,能不能clusterExport整个函数呢?
R语言在使用Parallel时候,会出现这样的疑问,一些东西都需要广播给不同的核心,那么在clusterExport步骤怎么办呢?能不能clusterExport一整个函数?然后直接parLapply呢?
答案否定的。笔者在用的时候,怎么样都不能把整个函数加载进去,所以只能另想办法。
既然不能clusterExport整个函数,那就只能改造我们的函数去适应parallel包了。
来看几个函数“被”改造的例子,一般来说有两个办法:
1、方法一:通过.GlobalEnv广播成全局变量
clusterExport(cl=cl, varlist=c("text.var", "ntv", "gc.rate", "pos"), envir=environment())
在函数导入的时候,加入envir变量让其广播给不同的核心,这个可以放在函数之中来使用。
2、方法二:把parLapply嵌套进函数之中
par.test <- function(text.var, gc.rate=10){ require(parallel) pos <- function(i) { paste(sapply(strsplit(tolower(i), " "), nchar), collapse=" | ") } cl <- makeCluster(mc <- getOption("cl.cores", 4)) parLapply(cl, text.var, function(text.vari, gc.rate, pos) { x <- pos(text.vari) if (i%%gc.rate==0) gc() x }, gc.rate, pos) }
可以看到的这个例子,就是把内容嵌套到parLapply之中了。同时也可以学习到,parLapply使用方法也很不错,也可以学习一下。
再来看一个例子:
mainFunction <- function(cl) { fa <- function(x) fb(x) fb <- function(x) fc(x) fc <- function(x) x y <- 7 workerFunction <- function(i) { do.call(functionNames[[i]], list(y)) } environment(workerFunction) <- .GlobalEnv environment(fa) <- .GlobalEnv environment(fb) <- .GlobalEnv environment(fc) <- .GlobalEnv functionNames <- c("fa", "fb", "fc") clusterExport(cl, varlist=c("functionNames", functionNames, "y"), envir=environment()) parLapply(cl, seq_along(functionNames), workerFunction) } library(parallel) cl <- makeCluster(detectCores()) mainFunction(cl) stopCluster(cl)
——————————————————————————————————
应用二:在使用parallel包时,报错:Error in unserialize(node$con) : error reading from connection
在R语言中使用并行算法的时候,会出现报错,无法连接到核心,即使在本来连接上的时候。通过查阅文献看到了,这是因为“调用核心数--计算机内存”的不匹配造成的。
如果你的数据集很大,调用了很多核心,那么你的计算机内存如果不够匹配,就会出现连接不上的不错,甚至还出现卡机,一动不动的情况(当然,只要耐心等待,其实他还是会继续运行的...等待的时候会有点长)
解决办法一:调用FORK,window不能...
FORK适用unix/max,实现内存共享以及节省内存,大数据环境下内存问题报错少
PSOCK适用所有(一般window都是这个)
不过调用FORK也还是治标不治本。
解决办法二:分开并行,小步迭代
譬如10万数据,那么就“2万+2万+2万+2万+2万”的跑,如果还出现脱机,就用之前tryCatch跳过,让损失降低到最小。
最好的办法了。
参考文献:How-to go parallel in R – basics + tips
——————————————————————————————————
参考文献
2、R与并行计算
3、sparklyr包:实现Spark与R的接口,会用dplyr就能玩Spark
6、R用户的福音︱TensorFlow:TensorFlow的R接口
8、碎片︱R语言与深度学习
R︱并行计算以及提高运算效率的方式(parallel包、clusterExport函数、SupR包简介)的更多相关文章
- oracle使用with as提高查询效率
经常在开发过程中会用到视图或组合查询的情况,但由于涉及表数据经常达到千万级别的笛卡尔积,而且一段查询时会反复调用,但结果输出往往不需要那么多,可以使用with将过滤或处理后的结果先缓存到临时表(此处原 ...
- 提升R代码运算效率的11个实用方法——并行、效率
转载于36大数据,原文作者:Selva Prabhakaran 译者:fibears 众所周知,当我们利用R语言处理大型数据集时,for循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率 ...
- 【R】提升R代码运算效率的11个实用方法
低.有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升.本文将介绍几种适用于大数据领域的方法,包括简单的逻辑调整设计.并行处理和Rcpp的运用,利用这些方法你可以轻松地处理 ...
- Android 你可能忽略的提高敲代码效率的方式 (转)
每日推荐 Eyepetizer-in-Kotlin:一款简约的小视频app,带你走进kotlin 作为学习kotlin的一款app,在撸代码的过程中学习kotlin的语法及特性. Eyepetizer ...
- Android 你可能忽略的提高敲代码效率的方式
Android 你可能忽略的提高敲代码效率的方式
- 提升R代码运算效率的11个实用方法
提升R代码运算效率的11个实用方法 众所周知,当我们利用R语言处理大型数据集时,for 循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升.本文将 ...
- Lucene4.6查询时完全跳过打分,提高查询效率的实现方式
由于索引的文件量比较大,而且应用中不需要对文档进行打分,只需要查询出所有满足条件的文档.所以需要跳过打分来提高查询效率.一开始想用ConstantScoreQuery,但是测试发现这个类虽然让所有返回 ...
- 在对文件进行随机读写,RandomAccessFile类,如何提高其效率
花1K内存实现高效I/O的RandomAccessFile类 JAVA的文件随机存取类(RandomAccessFile)的I/O效率较低.通过分析其中原因,提出解决方案.逐步展示如何创建具备缓存读写 ...
- 成吨提高开发效率:Intellij Shortcuts精简子集与思维模式
在线精简cheatsheet备查表:intellij.linesh.twGithub项目:intellij-mac-frequent-keymap Intellij的快捷键多而繁杂,从官方推荐的key ...
随机推荐
- NetCloud——一个网易云音乐评论抓取和分析的Python库
在17的四月份,我曾经写了一篇关于网易云音乐爬虫的文章,还写了一篇关于评论数据可视化的文章.在这大半年的时间里,有时会有一些朋友给我发私信询问一些关于代码方面的问题.所以我最近抽空干脆将原来的代码整理 ...
- java根据模板导出pdf
在网上看了一些Java生成pdf文件的,写的有点乱,有的不支持写入中文字体,有的不支持模板,有的只是随便把数据放里面生成文件,完全不考虑数据怎样放置的以及以后的维护性,想想还是自己总结一个完全版的导出 ...
- Nginx几种负载均衡算法及配置实例
本文装载自: https://yq.aliyun.com/articles/114683 Nginx负载均衡(工作在七层"应用层")功能主要是通过upstream模块实现,Ngin ...
- 从jvm源码看synchronzied
synchronized的使用 synchronized关键字是Java中解决并发问题的一种常用方法,也是最简单的一种方法,其作用有三个:(1)互斥性:确保线程互斥的访问同步代码(2)可见性:保证共享 ...
- BZOJ 3456: 城市规划 [多项式求逆元 组合数学 | 生成函数 多项式求ln]
3456: 城市规划 题意:n个点组成的无向连通图个数 以前做过,今天复习一下 令\(f[n]\)为n个点的无向连通图个数 n个点的完全图个数为\(2^{\binom{n}{2}}\) 和Bell数的 ...
- C 洛谷 P3599 Koishi Loves Construction [构造 打表观察]
题目描述 Koishi决定走出幻想乡成为数学大师! Flandre听说她数学学的很好,就给Koishi出了这样一道构造题: Task1:试判断能否构造并构造一个长度为的的排列,满足其个前缀和在模的意义 ...
- 基于layui和bootstrap搭建极简后台管理框架
年前无聊,想自己搭建一个后台管理框架,对比了easyui.Extjs.H-ui.H+UI.layui几个框架,easyui和Extjs虽然功能强大但是界面实在是接受不了,H+UI和layuiAdmin ...
- 2n皇后问题
此题为蓝桥杯基础练习题. 问题描述 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后.现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行.同一列或同一条对角线上,任意的两个白皇后 ...
- python爬虫登录
python3 urllib.request 网络请求操作 http://www.cnblogs.com/cocoajin/p/3679821.html python实现 爬取twitter用户姓名 ...
- 分布式代码管理系统Git实践
1.1 版本管理工具介绍 什么是版本管理工具 为什么要使用版本管理工具? 老板要求小明写一个可行性报告,当他写完给老板看后,结果-- Git简介 早先linux内核代码托管在B ...