提升R语言程序性能

1.       性能测评

时间测定方法

R中提供的测量时间最简单的方法是system.time函数。

system.time(expr, gcFirst=TRUE)

这个函数会在不降低程序运行性能的情况下,执行表达式expr,gcFrist则是指定程序运行前是否先执行垃圾回收。

do.stuff <- function(){
a <- 1:100000
for(i in 1:100000){
           a[i] <- a[i]^2
}
a
}
system.time(do.stuff())
技术分享

监控内存方法

R中函数gc(),有两个功能,一是立即执行一次垃圾清理,二是显示剩余内存的统计信息。

gc()
技术分享

used是当前使用情况,gc trigger是会触发垃圾回收的值,max used是上次gc()操作或者是此次启动R后使用最大值。(Mb)是Ncells和Vcells的大小转换为Mb单位时的值。

Ncells即cons cells,32位R中占28B,64位R中占56B,我是用的32位的R,所以2616689*28/(1024^2) = 69.9。

Vcells即vector cells,占8B,所以63817864*8/(1024^2) = 486.9。

不是很懂Ncells和Vcells分别指的是R中的什么对象,网上也没找到很确切的说法,所以不知道应该怎么去翻译它们,有知道的朋友希望能告知,谢谢!

 

R中object.size()函数可以查看每个对象占用内存数。

object.size(1)
object.size(train)
技术分享

 

R中memory.profile()函数可以查看不同对象类型的内存占用情况。

memory.profile()
技术分享

不过memory.profile()展示的是Ncells的统计量,可以看到gc()中查到的Ncells使用数跟memory.profiles()的总量非常接近。

技术分享


R中memory.size()函数,可以查看到R使用的内存大小,还可以设置参数max=TRUE,来查看上次gc()操作或者是此次启动R后使用的最大的内存数。

memory.size()
memory.size(max=TRUE)
技术分享


时间性能分析

R中有Rprof()方法,能监控R语言程序中每一个操作语句的耗时。

Rprof(filenames=”Rprof.out”,append=FLASE,interval=0.02,memory.profiling=FALSE)

filenames 输出文件路径

append 向已存在文件追加内容还是覆盖已存在文件

interval 采用时间间隔

memory.profiling 是否将内存信息写入文件

启动性能监控是Rprof(filename)

停止性能监控时Rprof()或者Rprof(NULL)

 

summaryRprof()方法可以查看Rprof()性能采集的结果。

summaryRprof(filenames=”Rprof.out”,chunksize=5000,memory=c(“none”,”both”,”tseries”,”stats”),index=2,diff=TRUE,exclude=NULL)

filenames 输出文件路径

chunksize 一次读取的行数

memory 如何显示内存消耗信息,分别是不显示,时间和内存信息都显示,一时间序列的方式显示,显示内存消耗统计量。

index 是否将内存的信息写入文件

diff 在内存统计量中是否显示内存使用的变化,或者总的内存消耗

exclude 指定排除在统计结果之外的函数

 

这个部分不给出详细的例子了,可以看这篇文章里面讲到的性能监控的例子,使用的就是这两个函数:http://blog.fens.me/r-perform-rprof-profr/

 

内存性能分析

R中有Rprofmen

Rprofmem(filename = "Rprofmem.out", append =FALSE, threshold = 0)

filenames 输出文件路径

append 向已存在文件追加内容还是覆盖已存在文件

threshold 内存分配大于这个值的才会被记录,单位字节

启动性能监控是Rprofmem (filename)

停止性能监控时Rprofmem ()或者Rprofmem (NULL)

 

查看运行结果,直接读取filename就行。下面的例子,是函数说明文档中的例子:

Rprofmem("Rprofmem.out", threshold = 1000)
example(glm)
Rprofmem(NULL)
noquote(readLines("Rprofmem.out", n = 5))
技术分享

技术分享

2.       优化R代码

使用向量操作

R的一个很大的特点就是能进行向量操作,相比循环迭代的方法而言,向量操作的效率更高。

square.two <- function(n){
v <- numeric(0)
length(v) <- n
for(i in 1:n){
           v[i] <- i^2
}
v
}
square.two (10)
system.time(square.two (10000))
system.time(square.two (100000))
system.time(square.two (1000000))

结果如下图,可以看出来,消耗的时间随向量长度线性增长。

技术分享

用向量来实现平方运算这个函数,代码如下:

better. square <- function(n){
	(1:n)^2
}
better.square (10)
system.time(better.square (10000))
system.time(better.square (100000))
system.time(better.square (1000000))

技术分享

可以看到,相比前面循环实现方式而言,向量运算快了很多。

 

使用内置函数

大部分情况下,内置函数要比自己写的代码性能更好。R中的内置函数经常由是其他语言(通常是C和Fortran),实现的编译过的代码,这些函数的运行效率比解释性的R程序强很多。其实上面的向量操作也可以看做是使用R的内置函数,不举更多的例子了。

 

内存预分配

频繁地申请内存会加大程序的耗时,这个在很多编程语言中都是这样。R语言操作对象不需要提前分配内存,但提前分配好内存能加快运行速度。还是平方运算的例子,提前分配内存和不提前分配内存在数据量大时,运行时间性能上差异很大。当然,我们已经知道了一个更加快的方式——向量操作。

square.one <- function(n){
v <- numeric(0)
for(i in 1:n){
           v[i] <- i^2
}
v
}
 
square.two <- function(n){
v <- numeric(0)
length(v) <- n
for(i in 1:n){
           v[i] <- i^2
}
v
}
技术分享

 

查找性能

R语言中,向量查找的方式有很多。下标查找,名字标签单括号查找,名字标签双括号精确查找,名字标签双括号模糊查找等。

这几种查找方式的时间复杂度:

下标查找:时间复杂度1;

名字标签单括号查找:时间复杂度n;

字标签双括号精确查找(默认):时间复杂度1;

名字标签双括号模糊查找:时间复杂度1。

这个也是在写R程序过程中,可能提升程序效率的一个地方。

精确查找和模糊查找的说明:

diary <- list(milk="1 gallon",butter="1 pound",eggs=12)
diary[["milk"]]
diary[["mil"]]
diary[["mil",exact=FALSE]]
技术分享

 

参考:《R in a Nutshell》,这本书用来做入门书籍很合适。

有任何问题建议欢迎指出,转载请注明出处,谢谢!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。