相同内存使用不同大小的矩阵

DJa*_*ack 21 memory size r matrix

当我发现一些奇怪的东西时,我对R中矩阵的内存使用感兴趣.在循环中,我使矩阵的列数增长,并为每个步骤计算对象大小,如下所示:

x <- 10
size <- matrix(1:x, x, 2)

for (i in 1:x){
  m  <- matrix(1, 2, i)
  size[i,2] <- object.size(m)
}
Run Code Online (Sandbox Code Playgroud)

这使

plot(size[,1], size[,2], xlab="n columns", ylab="memory")
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

似乎具有2行和5,6,7或8列的矩阵使用完全相同的内存.我们怎么解释呢?

had*_*ley 35

要了解这里发生了什么,你需要了解与R中的对象相关的内存开销.每个对象,甚至是没有数据的对象,都有40个字节的数据与之关联:

x0 <- numeric()
object.size(x0)
# 40 bytes
Run Code Online (Sandbox Code Playgroud)

此内存用于存储对象的类型(由返回typeof())以及内存管理所需的其他元数据.

忽略此开销后,您可能会期望向量的内存使用量与向量的长度成比例.让我们看看几个情节:

sizes <- sapply(0:50, function(n) object.size(seq_len(n)))
plot(c(0, 50), c(0, max(sizes)), xlab = "Length", ylab = "Bytes", 
  type = "n")
abline(h = 40, col = "grey80")
abline(h = 40 + 128, col = "grey80")
abline(a = 40, b = 4, col = "grey90", lwd = 4)
lines(sizes, type = "s")
Run Code Online (Sandbox Code Playgroud)

向量的内存使用情况

看起来内存使用大致与向量的长度成比例,但是在168字节处存在很大的不连续性,并且每几步都存在小的不连续性.最大的不连续性是因为R有两个向量存储池:由R管理的小向量和由OS管理的大向量(这是一种性能优化,因为分配大量少量内存非常昂贵).小向量只能是8,16,32,48,64或128字节长,一旦我们删除40字节开销,正是我们所看到的:

sizes - 40
#  [1]   0   8   8  16  16  32  32  32  32  48  48  48  48  64  64  64  64 128 128 128 128
# [22] 128 128 128 128 128 128 128 128 128 128 128 128 136 136 144 144 152 152 160 160 168
# [43] 168 176 176 184 184 192 192 200 200
Run Code Online (Sandbox Code Playgroud)

从64到128的步骤导致了一大步,然后一旦我们进入大矢量池,矢量被分配为8个字节的块(存储器以一定大小为单位,R不能要求半个单元):

# diff(sizes)
#  [1]  8  0  8  0 16  0  0  0 16  0  0  0 16  0  0  0 64  0  0  0  0  0  0  0  0  0  0  0
# [29]  0  0  0  0  8  0  8  0  8  0  8  0  8  0  8  0  8  0  8  0  8  0
Run Code Online (Sandbox Code Playgroud)

那么这种行为如何与你用矩阵看到的一致?好吧,首先我们需要查看与矩阵相关的开销:

xv <- numeric()
xm <- matrix(xv)

object.size(xm)
# 200 bytes

object.size(xm) - object.size(xv)
# 160 bytes
Run Code Online (Sandbox Code Playgroud)

因此,与向量相比,矩阵需要额外的160字节存储空间.为什么160字节?这是因为矩阵的dim属性包含两个整数,属性存储在pairlist(旧版本list())中:

object.size(pairlist(dims = c(1L, 1L)))
# 160 bytes
Run Code Online (Sandbox Code Playgroud)

如果我们使用矩阵而不是向量重新绘制先前的绘图,并将y轴上的所有常量增加160,则可以看到不连续性与从小向量池到大向量池的跳转完全对应:

msizes <- sapply(0:50, function(n) object.size(as.matrix(seq_len(n))))
plot(c(0, 50), c(160, max(msizes)), xlab = "Length", ylab = "Bytes", 
  type = "n")
abline(h = 40 + 160, col = "grey80")
abline(h = 40 + 160 + 128, col = "grey80")
abline(a = 40 + 160, b = 4, col = "grey90", lwd = 4)
lines(msizes, type = "s")
Run Code Online (Sandbox Code Playgroud)

矩阵的内存使用情况

  • +1有趣而且很棒的答案.谢谢哈德利 (5认同)

Sco*_*hie 7

这似乎只发生在小端的非常特定的列范围内.查看1-100列的矩阵,我看到以下内容:

在此输入图像描述

我没有看到任何其他的高原,即使我增加列数,10000:

在此输入图像描述

好奇,我进一步研究了一下,把你的代码放在一个函数中:

sizes <- function(nrow, ncol) {
  size=matrix(1:ncol,ncol,2)
  for (i in c(1:ncol)){
    m = matrix(1,nrow, i)
    size[i,2]=object.size(m)
  } 
  plot(size[,1], size[,2])
  size
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,我们仍然看到这个高原和直线低的数字,如果我们增加的行数,与高原收缩和向后移动,最后调整到由我们打的时间直线前nrow=8:

具有3-8行,10列的矩阵的大小:

指示这发生在矩阵中的单元数量的非常特定的范围内; 9-16.

内存分配

正如@Hadley在他的评论中指出的那样,在向量的内存分配方面存在类似的线程.提出了公式:40 + 8 * floor(n / 2)对于numeric大小的矢量n.

对于矩阵,开销略有不同,并且步进关系不成立(如我的图中所示).相反,我提出了公式208 + 8 * n字节,其中n是矩阵(nrow * ncol)中的单元格数,除了n9到16之间的位置:

矩阵大小 - "double"矩阵为208字节,1行,1-20列:

> sapply(1:20, function(x) { object.size(matrix(1, 1, x)) })-208
 [1]   0   8  24  24  40  40  56  56 120 120 120 120 120 120 120 120 128 136 144
[20] 152
Run Code Online (Sandbox Code Playgroud)

然而.如果我们将矩阵的类型更改为Integer或Logical,我们会看到上面线程中描述的内存分配中的逐步行为:

矩阵大小 - 对于"integer"矩阵1行,1-20列,208字节:

> sapply(1:20, function(x) { object.size(matrix(1L, 1, x)) })-208
 [1]   0   0   8   8  24  24  24  24  40  40  40  40  56  56  56  56 120 120 120
[20] 120
Run Code Online (Sandbox Code Playgroud)

对于"logical"矩阵也是如此:

> sapply(1:20, function(x) { object.size(matrix(1L, 1, x)) })-208
 [1]   0   0   8   8  24  24  24  24  40  40  40  40  56  56  56  56 120 120 120
[20] 120
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,我们没有看到类型矩阵的相同行为double,因为它只是附加"numeric"dim属性的向量(R lang规范).

我们在内存分配中看到的重要一步来自于R有两个内存池,一个用于小向量,一个用于大向量,而这恰好是跳转的位置.Hadley Wickham在他的回答中详细解释了这一点.


Ban*_*you 5

看看大小从1到20的数字向量,我得到了这个数字.

x=20
size=matrix(1:x,x,2)
for (i in c(1:x)){
   m = rep(1, i)
   size[i,2]=object.size(m)
}

plot(size[,1],size[,2])
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述