Мониторинг использования памяти в R

Можно ли контролировать объем памяти, который используется или использовался R для вызова функции? Например, у меня есть произвольная функция, например:

smallest.sv <- function(){
  A <- matrix(rnorm(1e6), 1e3);
  mysvd <- svd(A);
  return(tail(mysvd$d, 1));
}

Запуск функции просто возвращает скаляр, но для ее вычисления использовалось много памяти. Теперь мне нужно сделать тест производительности. Время обработки легко:

system.time(x <- smallest.sv())

Однако я также хотел бы знать, сколько памяти было необходимо для этого вызова, без изменения функции (она должна работать для произвольных функций). Есть какой-либо способ сделать это?

Изменить: уточнить немного. Меня больше всего интересует верхняя граница памяти, которая использовалась во время вызова функции, то есть, сколько физической памяти требуется для обработки вызова функции. Я думаю, что во многих случаях это значительно меньше, чем общий объем выделенной памяти.

4 ответа

Решение

R обеспечивает поддержку профилирования памяти, см. Раздел 3.3 Руководства по написанию расширений R:

3.3 Профилирование R-кода для использования памяти

Измерение использования памяти в коде R полезно, когда код занимает больше памяти, чем удобно, или когда распределение памяти и копирование объектов ответственны за медленный код. Существует три способа профилирования использования памяти с течением времени в коде R. Все три требуют, чтобы R был скомпилирован с `--enable-memory-profiling', который не используется по умолчанию, но в настоящее время используется для двоичных дистрибутивов Mac OS X и Windows. Все может вводить в заблуждение по разным причинам.

При понимании профилей памяти полезно узнать немного больше о распределении памяти R. Просмотр результатов `gc()'показывает разделение памяти на`Vcells', используемое для хранения содержимого векторов, и `Ncells', используемый для хранения всего остального, включая все административные издержки для векторов, такие как информация о типе и длине. Фактически векторное содержимое разделено на два пула. Память для маленьких векторов (по умолчанию 128 байт или меньше) получается большими порциями, а затем распределяется по R; память для больших векторов получается непосредственно из операционной системы.

а затем предоставляет еще три раздела.

Одним из вариантов является использование Rprof, Простой подход заключается в следующем:

Rprof(tf <- "rprof.log", memory.profiling=TRUE)

[your code]

Rprof(NULL)
summaryRprof(tf)

Это даст вам некоторую информацию об использовании памяти.

Вы можете получить верхнюю границу памяти, которая используется во время обработки функции и команд, с помощьюgc:

smallest.sv <- function(){
  A <- matrix(rnorm(1e6), 1e3);
  mysvd <- svd(A);
  return(tail(mysvd$d, 1));
}

tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
x <- smallest.sv()
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#62 MB
rm(x)

На эту верхнюю границу влияет сборка мусора, поэтому включение gctortureдаст нижнюю верхнюю границу:

tt <- sum(.Internal(gc(FALSE, TRUE, TRUE))[13:14])
gctorture(on = TRUE)
x <- smallest.sv()
gctorture(on = FALSE)
sum(.Internal(gc(FALSE, FALSE, TRUE))[13:14]) - tt
#53.7 MB

Другие инструменты, такие как Rprof, Rprofmem, profmem::profmem, bench::mark или profvis::profvis может также показать использование памяти.

#Using Rprof (Enable profiling is a compile-time option: ./configure --enable-R-profiling)
gc()
Rprof("Rprof.out", memory.profiling=TRUE)
x <- smallest.sv()
Rprof(NULL)
max(summaryRprof("Rprof.out", memory="both")$by.total$mem.total)
#45.9
#Here at defined intervals the status is checked and so the result depends on if you hit the peak

#Using Rprofmem (Enable momory profiling is a compile-time option: ./configure --enable-memory-profiling)
Rprofmem("Rprofmem.out"); x <- smallest.sv(); Rprofmem(NULL) #Wen first run, there is much more in the log file
gc()
Rprofmem("Rprofmem.out")
x <- smallest.sv()
Rprofmem(NULL)
sum(as.numeric(read.table("Rprofmem.out", comment.char = ":")[,1]), na.rm=TRUE)
#88101752
#Writes out them memory amount when it is allocated

library(profmem) #uses utils::Rprofmem
gc()
total(profmem(x <- smallest.sv()))
#88101752

library(bench) #uses utils::Rprofmem
gc()
mark(x <- smallest.sv())[,"mem_alloc"]
#84MB
#Warning message:
#Some expressions had a GC in every iteration; so filtering is disabled. 

library(profvis) #uses utils::Rprof
gc()
profvis(x <- smallest.sv())
#opens a browser window where you can read under Memory -23.0 | 45.9

Rprofmemпоказывает память, которая была выделена кумулятивно, и не учитывает память, которая была освобождена во время выполнения. Чтобы увеличить вероятностьRprof для достижения пика вы можете выбрать короткий интервал времени или / и повторить процедуру.

max(replicate(10, {
    gc()
    Rprof("Rprof.out", memory.profiling=TRUE, interval = runif(1,.005,0.02))
    x <- smallest.sv()
    Rprof(NULL)
    max(summaryRprof("Rprof.out", memory="both")$by.total$mem.total)
}))
#76.4

Здесь я ценю более высокую, чем получаю от gc, что демонстрирует, что на использование памяти влияет сборка мусора, и верхняя граница памяти, которая используется во время обработки функции, может варьироваться от вызова к вызову, покаgctorture не включается.

Вы можете получить время обработки, а также пиковую память с PeakRAM:

      library(peakRAM)

peakRAM(smallest.sv())

  Function_Call Elapsed_Time_sec Total_RAM_Used_MiB Peak_RAM_Used_MiB
1 smallest.sv()             3.64                  0              61.1 
Другие вопросы по тегам