【问题标题】:Same memory usage of matrices with different size [duplicate]具有不同大小的矩阵的相同内存使用[重复]
【发布时间】:2013-08-30 05:02:07
【问题描述】:

当我观察到一些奇怪的事情时,我对 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)
}

这给了

plot(size[,1], size[,2], xlab="n columns", ylab="memory")

似乎具有 2 行和 5、6、7 或 8 列的矩阵使用完全相同的内存。我们该如何解释呢?

【问题讨论】:

  • 这与矩阵无关(无论如何都是 R 中的向量)。看sizes &lt;- rep(NA_integer_,100); for (i in 1:100) sizes[i] &lt;- object.size(rep(1,i)); plot(sizes)
  • 看看字符 m &lt;- matrix('a',2,i) 和整数 m &lt;- matrix(1L,2,i)m &lt;- matrix(TRUE,2,i) -- 或者更有趣的是 @Roland 建议的内容(对于字符、整数和逻辑向量)。
  • @SimonO101 Hadley 在聊天中解释了它。我希望他有时间写一个正确的答案。例如,阅读this section in R-exts
  • @Roland 谢谢,我只是saw it! 有趣。

标签: r memory matrix size


【解决方案1】:

要了解这里发生了什么,您需要了解一点与 R 中的对象相关的内存开销。每个对象,即使是没有数据的对象,都有 40 字节的数据与之相关:

x0 <- numeric()
object.size(x0)
# 40 bytes

此内存用于存储对象的类型(由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")

看起来内存使用量与向量的长度大致成正比,但在 168 字节处有一个很大的不连续性,每隔几步就会有一个小的不连续性。最大的不连续性是因为 R 有两个用于向量的存储池:由 R 管理的小向量和由操作系统管理的大向量(这是一种性能优化,因为分配大量少量内存很昂贵)。小向量只能是 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

从 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

那么,这种行为与您在矩阵中看到的情况有何对应?好吧,首先我们需要看看与矩阵相关的开销:

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

object.size(xm)
# 200 bytes

object.size(xm) - object.size(xv)
# 160 bytes

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

object.size(pairlist(dims = c(1L, 1L)))
# 160 bytes

如果我们使用矩阵而不是向量重新绘制之前的图,并将 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")

【讨论】:

  • +1 有趣且很棒的答案。谢谢哈德利
  • 谢谢哈德利。这是一个完美的答案。
【解决方案2】:

这似乎只发生在小端的非常特定范围的列中。查看具有 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
}

有趣的是,如果我们增加行数,我们仍然会看到这个高原和直线的数量很少,高原缩小并向后移动,直到我们点击nrow=8时最终调整为直线:

表明这种情况发生在矩阵中单元格数量的非常特定范围内; 9-16。

内存分配

正如@Hadley 在他的评论中指出的那样,有一个类似的thread on memory allocation of vectors。这得出了公式:40 + 8 * floor(n / 2) 表示 numeric 大小为 n 的向量。

对于矩阵,开销略有不同,步进关系不成立(如我的图中所示)。相反,我提出了公式208 + 8 * n 字节,其中n 是矩阵中的单元格数(nrow * ncol),除非n 介于 9 和 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

但是。如果我们将矩阵的类型更改为整数或逻辑,我们确实会看到上面线程中描述的内存分配中的逐步行为:

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

> 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

"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

令人惊讶的是,我们在double 类型的矩阵中没有看到相同的行为,因为它只是一个带有dim 属性的"numeric" 向量 (R lang specification)。

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

【讨论】:

    【解决方案3】:

    看看大小从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])
    

    【讨论】:

    • 哈,这是一种更聪明的挖掘方式!
    猜你喜欢
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-14
    相关资源
    最近更新 更多