R语言内存管理
Published:
人,最困难的莫过于认识自己。如果能对自己有清楚的定位,那么就能有价值地活在世上。同样,作为一门工具,我们如果能对其利弊都了如指掌,将会更好地发挥工具自身的优势。本文将简要分析一下,在用R编程过程推荐和不推荐的操作,使编写的R程序更加高效。
关于内存管理
- 在循环中尽量少用cbind, rbind。R当中处理矩阵的时候没有维数的限制,这给我们带来了很大的方便。但是,如果我们对矩阵进行扩充,如用$\text{x=cbind(x,temp)}$时,系统会开辟新的连续空间给最新的x。这样会造成内存的浪费。因此,如果能事先给定最终所得扩充后矩阵的维数,那么系统只需要开辟一个连续的空间。
用cbind合并的例子:
rm(list = ls())
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 330409 17.7 597831 32.0 531268 28.4
## Vcells 564819 4.4 1162592 8.9 1031040 7.9
a = memory.size(F)
N = 1000
x = c()
for(i in 1:1000){
x = cbind(matrix(runif(N),N,1),x);
}
dim(x)
## [1] 1000 1000
b = memory.size(F)
使用内存将增加:
b-a
## [1] 23.03
事先指定X维数的例子
rm(list = ls())
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 331274 17.7 597831 32.0 531268 28.4
## Vcells 565091 4.4 3314577 25.3 3723679 28.5
a = memory.size(F)
N = 1000
x = matrix(0,N,1000);
for(i in 1:1000){
x[,i] = matrix(runif(N),N,1);
}
dim(x)
## [1] 1000 1000
b = memory.size(F)
使用内存将增加:
b-a
## [1] 10.61
有时,确实不能指定需要得到的扩充后矩阵的维数,比如在迭代运算当中。这时,我们可以通过在运算中清除变量和清除内存垃圾的办法。
rm(list = ls())
gc()
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 331304 17.7 597831 32.0 540518 28.9
## Vcells 565363 4.4 2651661 20.3 3723679 28.5
a = memory.size(F)
N = 1000
x = c()
for(i in 1:1000){
x1 = cbind(matrix(runif(N),N,1),x);
rm(x)
gc()
x = x1
rm(x1)
gc()
}
dim(x)
## [1] 1000 1000
b = memory.size(F)
使用内存将增加:
b-a
## [1] 5.05
Remark: $\text{gc()}$函数通常是不需要的,因为在R的运行当中,当内存不够时系统会自动清理垃圾。这里只是为了对比实验结果所用。
关于矩阵化运算
- R和matlab一样,可以进行矩阵运算。如果在编程中有矩阵的思想,将会避免显式循环,大大节省程序的执行时间。下面以计算一个数据集的欧氏距离矩阵为例,进行对比。
一般的显式循环方法
data(iris)
x = as.matrix(iris[,1:4]);
dis = matrix(0, nrow(x), nrow(x));
ptm <- proc.time();
for (i in 1:nrow(x))
for (j in 1:nrow(x)){
dis[i,j] = sqrt(sum((x[i,]-x[j,])^2));
}
proc.time()-ptm
## user system elapsed
## 0.11 0.00 0.11
用矩阵化的思想:
data(iris)
x = as.matrix(iris[,1:4]);
dis1 = matrix(0, nrow(x), nrow(x));
ptm <- proc.time();
for (i in 1:nrow(x)){
mat1 = t(matrix(x[i,],ncol(x),nrow(x)));
dis1[i,] = sqrt(rowSums((x - mat1)^2));
}
proc.time()-ptm
## user system elapsed
## 0.02 0.00 0.02
两者的计算是等价的
sum(dis1!=dis)
## [1] 0
总结
上面给出的例子样本量都比较小,但是也能说明问题。只有平时我们就要养成良好的习惯,才能使自己的程序高效地为大数据集服务。