R语言内存管理

1 minute read

Published:

人,最困难的莫过于认识自己。如果能对自己有清楚的定位,那么就能有价值地活在世上。同样,作为一门工具,我们如果能对其利弊都了如指掌,将会更好地发挥工具自身的优势。本文将简要分析一下,在用R编程过程推荐和不推荐的操作,使编写的R程序更加高效。

关于内存管理

  1. 在循环中尽量少用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的运行当中,当内存不够时系统会自动清理垃圾。这里只是为了对比实验结果所用。

关于矩阵化运算

  1. 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

总结

上面给出的例子样本量都比较小,但是也能说明问题。只有平时我们就要养成良好的习惯,才能使自己的程序高效地为大数据集服务。

参考资料

R的内存管理和垃圾清理