`Rprof` 的内存分析输出的解释

Interpretation of memory profiling output of `Rprof`

我正在尝试使用分析来查看我的代码的哪一部分对 3GB 内存的最大使用负责(根据 gc() 最大已用内存统计数据 see here how 报告)。我是这样 运行 内存分析的:

Rprof(line.profiling = TRUE, memory.profiling = TRUE)
graf(...) # ... here I run the profiled code
Rprof(NULL)
summaryRprof(lines = "both", memory = "both")

输出如下:

$by.total
                       total.time total.pct mem.total self.time self.pct
"graf"                     299.12     99.69   50814.4      0.02     0.01
#2                         299.12     99.69   50814.4      0.00     0.00
"graf.fit.laplace"         299.06     99.67   50787.2      0.00     0.00
"doTryCatch"               103.42     34.47    4339.2      0.00     0.00
"chol"                     103.42     34.47    4339.2      0.00     0.00
"tryCatch"                 103.42     34.47    4339.2      0.00     0.00
"tryCatchList"             103.42     34.47    4339.2      0.00     0.00
"tryCatchOne"              103.42     34.47    4339.2      0.00     0.00
"chol.default"             101.62     33.87    1087.0    101.62    33.87
graf.fit.laplace.R#46       85.80     28.60    3633.2      0.00     0.00
"backsolve"                 78.82     26.27    1635.2     58.40    19.46

我该如何解读mem.total?它是什么,它的单位是什么?我试图查看文档,即 ?Rprof?summaryRprof,但似乎没有很好的文档记录:-/

编辑: Here 他们说 Rprof "probes the total memory usage of R at regular time intervals"。但这不适合 50GB,这超出了我的内存所能容纳的范围! (现在 8GB 物理 + 12GB 页面文件)。

同样,正如 R Yoda 所指出的,?summaryRprof 表示内存 = "both" 意味着 "the change in total memory"。但它到底是什么(是总内存还是总内存的变化),它如何与 50GB 的数字相符?

编辑:profvis 中完成的相同分析 - 当我将鼠标悬停在 50812 上方时,它显示 "Memory allocation (MB)",并将鼠标悬停在关闭的黑条上到那条垂直线 "Percentage of peak memory allocation and deallocation"。不确定那是什么意思...这大约是 50 GB,这意味着这可能是所有分配的总和(??)...绝对不是峰值内存使用量:

?summaryRprof 说:

If memory = "both" the same list but with memory consumption in Mb in addition to the timings.

所以 mem.total 是 MB

With memory = "both" the change in total memory (truncated at zero) is reported [...]

您有 8 GB RAM + 12 GB 交换空间,但 mem.total 声称您已使用 50 GB?

因为它是两个后续探测之间的聚合增量Rprof定期拍摄的内存使用快照:如果在执行过程中进行探测在函数 f 中,最后一次探测的内存使用增量被添加到 f 的 mem.total)。

内存使用增量可能为负,但我从未见过负 mem.total 值,所以我猜(!)只有正值被添加到 mem.total.

这可以解释您看到的 50 GB 总使用量:它不是单个时间点内分配的内存量,而是整个执行时间内的聚合内存增量。

这也解释了gc只显示3GB为"max used (Mb)":内存分配和freed/deallocated多次所以您不会 运行 进入内存压力,但这会花费很多时间(在 RAM 中移动如此多的数据会使所有缓存无效,因此速度很慢)在 CPU 应用的计算逻辑之上。

此摘要(恕我直言)似乎也隐藏了一个事实,即垃圾收集器 (gc) 在不确定的时间点开始 以清理释放的内存。

由于 gc 启动延迟(不确定),恕我直言,将负内存增量归因于刚刚探测的单个函数是不公平的。

我会将 mem.total 解释为 mem.total.used.during.runtime,这可能是该列的更好标签。

profvis 有更详细的内存使用摘要(正如您在问题的屏幕截图中看到的那样):它还汇总了负内存使用增量(释放的内存),但 profvis documentation还警告缺点:

The code panel also shows memory allocation and deallocation. Interpreting this information can be a little tricky, because it does not necessarily reflect memory allocated and deallcated at that line of code. The sampling profiler records information about memory allocations that happen between the previous sample and the current one. This means that the allocation/deallocation values on that line may have actually occurred in a previous line of code.

更详细的答案需要更多的研究时间(我没有) - 查看 C 和 R 源代码 - 根据Rprof

创建的数据文件理解(复制)summaryRprof的聚合逻辑

Rprof 数据文件 (Rprof.out) 如下所示:

:376447:6176258:30587312:152:1#2 "test" 1#1 "test2"

前四个数字(以冒号分隔)表示(参见?summaryRprof) - R_SmallVallocSize: R堆上小块的向量内存[桶数] - R_LargeVallocSize:大块中的向量内存[桶数](来自 malloc) - R 堆上节点中的内存 - 在时间间隔内调用内部函数的次数 duplicate(用于复制向量,例如,在函数参数的首次写入语义复制的情况下)

字符串是函数调用堆栈。

只有前两个数字与计算当前(向量的)内存使用量相关(以 MB 为单位):

TotalBuckets = R_SmallVallocSize + R_LargeVallocSize
mem.used = TotalBuckets * 8 Bytes / 1024 / 1024
# 50 MB in the above `Rprof` probe line:
# (376447 + 6176258) * 8 / 1024 / 1024

有关 Vcells 的详细信息,请参阅 ?Memory

顺便说一句:我想尝试 summaryRProf(memory = "stats", diff = F) 来获取当前内存摘要,但我在 Ubuntu:

上收到 R3.4.4 64 位的错误消息
Error in tapply(seq_len(1L), list(index = c("1::#File", "\"test2\":1#1",  : 
  arguments must have same length

你能重现这个吗(看起来 "stats" 坏了)?