高效完成R代码
Posted gaowenxingxing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高效完成R代码相关的知识,希望对你有一定的参考价值。
为什么R有时候运行慢?
参考https://www.cnblogs.com/qiaoyihang/p/7779144.html
一、为什么R程序有时候会很慢?
1、计算性能的三个限制条件 cpu ram io R代码本身(个人觉得能控制的是R代码书写的高效
2、R是运行时解释的 在运行时解释并执行R代码
3、R是单线程的 CPU的强大核心并没卵用,R只会只用一个
4、R需要将全部数据加载到内存 处理的最大数据了取决于内存的限制
这里 linux相比于windows有一个优势,当我们试图装载一个可用内存大小的数据集
数据可能会成功装载,不过一旦可用内存耗尽,操作系统会将内存中的数据换到磁盘(交换空间)上
的交换文件,R人为数据全部装载入内存时间上OS正在做内存和磁盘文件的数据交换工作,如此一来,磁盘IO瓶颈对R性能就有着巨大的影响
5、算法设计影响时间和空间复杂度
时间复杂度:运行R程序需要的计算时间和数据规模之间的关系
空间复杂度:运行R程序需要的内存量和数据规模之间的关系
二、衡量代码的性能常用办法
使用system.time() benchmark() microbenchmark()
Rprof() 的工作原理是在运行R表达式的时候 ,观察R的调用栈,并以固定的时间,默认是0.02秒,对调用栈进行快照
,从而确定函数当前正在执行什么操作。通过这些快照,summaryRprof 能够计算每个函数的耗时
要理解R程序各个部分的性能,快速发现瓶颈,Rprof 是个很有用的工具
内存分析 memory.profiling=TRUE 这个指标会有误导,会偏高,因为有些函数内存尚未释放,
三、加快R运行的简单方法
1、尽量向量化运算,避免循环(这个一定要注意,我稀罕循环,然后容易die)
2、使用内置函数。 R中存在很多低级运算符,虽然这些运算符可以组成更加复杂的运算符或者函数,但是和编译性语言相比性能很差,
但是R提供了很多C++编写的计算包。(大量的内置函数可以使用,所以不需要像python一样)百度一下都能解决内置函数问题。。
比如计算 矩阵每一行的和,一般用apply可以解决问题,但是内置函数 rowSums,性能提升11倍,rowSums是使用C预编译的优化函数
开源社区开发了很多函数优化库供R使用,比如 basic linear algebra subprograms (BLAS) 详见:www.netlib.org.blas
给个demo好了
> # Define the previous solution
> app <- function(x) {
apply(x, 1, sum)
}
>
> # Define the new solution
> r_sum <- function(x) {
rowSums(x)
}
>
> # Compare the methods
> microbenchmark(
app_sol = app(rolls),
r_sum_sol = r_sum(rolls)
)
Unit: microseconds
expr min lq mean median uq max neval
app_sol 27.482 28.434 50.85846 29.0660 30.6125 2004.209 100
r_sum_sol 6.709 7.673 27.95393 8.3015 8.7205 1956.351 100
实行的函数功能一样,从运行时间上来看 rowSums()效率更高
3、预分配内存
上回跑了一个pmg模型,我的电脑崩了。。。事先没有进行预估计
为什么要预分配内存呢?主要是因为动态分配内存会拖慢程序的运行速度,每当向量大小发生变化,程序都需要做一些额外的工作
经过测试在一个简单的加法运算中,1000 长度的向量的计算,预分配内存之前为25.373秒,之后为0.577 秒
4、使用简单的数据结构
5、使用更加简单的数据结构
比如 如果可以的话,尽量使用matrix 而不是 dataframe,因为大部分矩阵操作首先要把dataframe强制转换为matrix,才开始计算。额好吧,其实我都是用dataframe的。
mat是一个矩阵,df是一个数据框,比较一下二者的运行速度
> # Which is faster, mat[, 1] or df[, 1]?
> microbenchmark(mat[, 1], df[, 1])
Unit: microseconds
expr min lq mean median uq max neval
mat[, 1] 2.318 2.701 3.21188 2.9760 3.3265 18.359 100
df[, 1] 11.646 13.010 16.67619 13.8965 15.1355 95.352 100
如果数据中存在多种变量类型,不可避免的要使用dataframe的时候,尽量用subset 和 which 筛选子集后再做运算。
6、使用哈希表进行大型数据上的频繁查找
一个包含N个元素的列表上的查找操作的时间复杂度是O(N) 列表越靠后,查找时间越长,随着N的增加,情况越严重。
CRAN 上可用的R 包是hash, 哈希表上查找操作的时间复杂度为O(1)
7、去CRAN上寻找更快的包
例如:fastcluster,princomp,fastmatch,RcppEigen,data.table,dplyr
四、使用编译代码加快运行速度
1、在运行之前编译R代码 因为每次运行前都需要解析和评估代码。这需要话费大量CPU时间,拖慢运行速度。
利用compiler 包,能在一定程度上减少此问题,提前预编译。
cmpfun 编译函数
compile 编译表达式
cmpfile 编译存储在文件中的R表达式
2、即时编译 激活JIT(just in time) compiler 包的enableJIT
3、在R中使用编译语言 在R中嵌入C、C++、OC、OC++(inline包)
4、调用外部编译代码 Rcpp rJava
5、使用编译代码的注意事项
6、创建R对象及垃圾回收
预分配变量内存,PROTECT防止R的垃圾收集器清理对象。 UNPROTECT 解除已分配内存的保护
7、One thing to note is that && only works on single logical values, i.e., logical vectors of length 1 (like you would pass into an if condition), but & also works on vectors of length greater than 1.
从运行时间上面来看确实是&更快一些
> # Example data
> is_double
[1] FALSE TRUE TRUE
>
> # Define the previous solution
> move <- function(is_double) {
if (is_double[1] & is_double[2] & is_double[3]) {
current <- 11 # Go To Jail
}
}
>
> # Define the improved solution
> improved_move <- function(is_double) {
if (is_double[1] && is_double[2] && is_double[3]) {
current <- 11 # Go To Jail
}
}
>
> # microbenchmark both solutions
> # Very occassionally the improved solution is actually a little slower
> # This is just random chance
> microbenchmark(move, improved_move, times = 1e5)
Unit: nanoseconds
expr min lq mean median uq max neval
move 42 52 107.7818 133 141 110084 1e+05
improved_move 39 50 104.0745 130 138 41764 1e+05
五、使用GPU让R运行的更快
这里需要注意的是在计算pearson相关性的时候,GPU是比CPU慢的
1、GPU最大的优势是核心数量巨多,所以最适合数据的并行问题,不适合那些线程之间需要大量同步的任务。
2、GPU的性能主要取决于主内存(RAM)和GPU内存之间的数据传输量,因为RAM和CPU内存之间的连接宽带很低。
优秀的GPU编程应该最小化这种传输
1.查看R 当前的版本
主要是查看配置信息把
# Print the R version details using version
> version
_
platform x86_64-pc-linux-gnu
arch x86_64
os linux-gnu
system x86_64, linux-gnu
status
major 3
minor 4.4
year 2018
month 03
day 15
svn rev 74408
language R
version.string R version 3.4.4 (2018-03-15)
nickname Someone to Lean On
>
> # Assign the variable major to the major component
> major <- version$major
>
> # Assign the variable minor to the minor component
> minor <- version$minor
2.读入数据更高效
Comparing read times of CSV and RDS files
One of the most common tasks we perform is reading in data from CSV files. However, for large CSV files this can be slow. One neat trick is to read in the data and save as an R binary file (rds) using saveRDS(). To read in the rds file, we use readRDS().
Note: Since rds is R‘s native format for storing single objects, you have not introduced any third-party dependencies that may change in the future.
To benchmark the two approaches, you can use system.time(). This function returns the time taken to evaluate any R expression. For example, to time how long it takes to calculate the square root of the numbers from one to ten million, you would write the following:
> # How long does it take to read movies from CSV?
> system.time(read.csv("movies.csv"))
user system elapsed
0.395 0.000 0.395
>
> # How long does it take to read movies from RDS?
> system.time(readRDS("movies.rds"))
user system elapsed
0.042 0.000 0.042
不难看出,rds是更高效的,因为数据是加载到R里面的,比外部导入里面之后加载肯定要节省时间。。(或许这么解释不对,但是真的节省时间)
3.辨析 <-和=
这两个是不一样的
Using the <- operator inside a function call will create a new (or overwrite an existing) object.
<-创建的变量的作用范围可以在整个顶层环境,而=仅仅在一个局部环境。(应该是global和局部的关系)https://www.cnblogs.com/xuanlvshu/p/5493222.html
但要<-创建的变量如果是在函数实参传递的时候创建的,其的作用范围可以在整个顶层环境,有一个前提条件:对应的形参在函数内部被用到了。
4.microbenchmark
> # Load the microbenchmark package
> library(microbenchmark)
>
> # Compare the two functions
> compare <- microbenchmark(read.csv("movies.csv"),
readRDS("movies.csv"),
times = 10)
>
> # Print compare
> compare
Unit: nanoseconds
expr min lq mean median uq max neval
read.csv 145 148 414.4 150 158 2741 10
readRDS 110 114 173.1 116 124 681 10
The benchmarkme package provides a set of benchmarks to help quantify your system. More importantly, it allows you to compare your timings with other systems.
> # Load the benchmarkme package
> library( benchmarkme)
>
> # Assign the variable ram to the amount of RAM on this machine
> ram <- get_ram()
> ram
16.3 GB
>
> # Assign the variable cpu to the cpu specs
> cpu <- get_cpu()
> cpu
$vendor_id
[1] "GenuineIntel"
$model_name
[1] "Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz"
$no_of_cores
[1] 4
get_ram():Attempt to extract the amount of RAM on the current machine.
get_cpu():Attempt to extract the CPU model on the current host
pre_allocate():测试边;变量声明的时间的
The growing() function defined below generates n random standard normal numbers, but grows the size of the vector each time an element is added!
向量化代码有多重要?
profvis:Profvis is a tool for visualizing code profiling data from R. It creates a web page which provides a graphical interface for exploring the data. Live demo.
比如这个是可以在网页上面看到的
How many cores does this machine have?
The parallel package has a function detectCores() that determines the number of cores in a machine.
How many cores does this machine have?
一个改写sapply和parSapply的例子
> # Set the number of games to play
> no_of_games <- 1e5
>
> ## Time serial version
> system.time(serial <- sapply(1:no_of_games, function(i) play()))
user system elapsed
7.003 0.005 7.018
>
> ## Set up cluster
> cl <- makeCluster(4)
> clusterExport(cl, "play")
>
> ## Time parallel version
> system.time(par <- parSapply(cl, 1:no_of_games, function(i) play()))
user system elapsed
0.063 0.004 3.168
>
> ## Stop cluster
> stopCluster(cl)
以上是关于高效完成R代码的主要内容,如果未能解决你的问题,请参考以下文章