Трюки, чтобы управлять доступной памятью в сеансе R - PullRequest
474 голосов
/ 31 августа 2009

Какие приемы люди используют для управления доступной памятью интерактивного сеанса R? Я использую функции ниже [основанные на публикациях Петра Пикала и Дэвида Хиндса в список r-help в 2004 году], чтобы перечислять (и / или сортировать) самые большие объекты и иногда rm() некоторые из них. Но, безусловно, наиболее эффективным решением было ... работать под 64-битным Linux с достаточным объемом памяти.

Любые другие хорошие уловки люди хотят поделиться? Один на пост, пожалуйста.

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.dim)
    names(out) <- c("Type", "Size", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}
# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

Ответы [ 27 ]

23 голосов
/ 01 февраля 2011

Как для скорости, так и для памяти, при построении большого фрейма данных с помощью некоторой сложной серии шагов я буду периодически сбрасывать его (создаваемый набор данных) на диск, добавляя все, что было до этого, и затем перезапустите его. Таким образом, промежуточные шаги работают только с небольшими фреймами данных (что хорошо, например, rbind значительно замедляется с более крупными объектами). Весь набор данных может быть считан обратно в конце процесса, когда все промежуточные объекты были удалены.

dfinal <- NULL
first <- TRUE
tempfile <- "dfinal_temp.csv"
for( i in bigloop ) {
    if( !i %% 10000 ) { 
        print( i, "; flushing to disk..." )
        write.table( dfinal, file=tempfile, append=!first, col.names=first )
        first <- FALSE
        dfinal <- NULL   # nuke it
    }

    # ... complex operations here that add data to 'dfinal' data frame  
}
print( "Loop done; flushing to disk and re-reading entire data set..." )
write.table( dfinal, file=tempfile, append=TRUE, col.names=FALSE )
dfinal <- read.table( tempfile )
17 голосов
/ 11 октября 2013

Просто отметим, что data.table пакета tables() кажется довольно хорошей заменой пользовательской функции .ls.objects() Дирка (подробно в предыдущих ответах), хотя только для data.frames / таблиц, а не, например матрицы, массивы, списки.

14 голосов
/ 22 мая 2012
  1. Мне повезло, и мои большие наборы данных сохраняются инструментом в виде «кусков» (подмножеств) размером примерно 100 МБ (32-разрядный двоичный файл). Таким образом, я могу последовательно выполнить этапы предварительной обработки (удаление неинформативных частей, уменьшение частоты дискретизации) перед объединением набора данных.

  2. Вызов gc () «от руки» может помочь, если размер данных приблизится к доступной памяти.

  3. Иногда другому алгоритму требуется гораздо меньше памяти.
    Иногда есть компромисс между векторизацией и использованием памяти.
    сравните: split & lapply с петлей for.

  4. Для быстрого и легкого анализа данных я часто сначала работаю с небольшим случайным подмножеством (sample ()) данных. Как только скрипт анализа данных / .Rnw завершит работу, код анализа данных и полные данные отправляются на сервер вычислений для расчета в течение ночи / в выходные дни / ....

11 голосов
/ 06 февраля 2014

Использование сред вместо списков для обработки коллекций объектов, занимающих значительный объем рабочей памяти.

Причина: каждый раз, когда изменяется элемент структуры list, весь список временно дублируется. Это становится проблемой, если требования к хранилищу списка составляют примерно половину доступной рабочей памяти, потому что тогда данные должны быть перенесены на медленный жесткий диск. Среды, с другой стороны, не подвержены такому поведению и могут рассматриваться как списки.

Вот пример:

get.data <- function(x)
{
  # get some data based on x
  return(paste("data from",x))
}

collect.data <- function(i,x,env)
{
  # get some data
  data <- get.data(x[[i]])
  # store data into environment
  element.name <- paste("V",i,sep="")
  env[[element.name]] <- data
  return(NULL)  
}

better.list <- new.env()
filenames <- c("file1","file2","file3")
lapply(seq_along(filenames),collect.data,x=filenames,env=better.list)

# read/write access
print(better.list[["V1"]])
better.list[["V2"]] <- "testdata"
# number of list elements
length(ls(better.list))

В сочетании с такими структурами, как big.matrix или data.table, которые позволяют изменять содержимое на месте, можно добиться очень эффективного использования памяти.

7 голосов
/ 26 января 2014

Функция ll в пакете gData может также отображать использование памяти каждым объектом.

gdata::ll(unit='MB')
6 голосов
/ 20 июля 2014

Если вы действительно хотите избежать утечек, вам следует избегать создания больших объектов в глобальной среде.

Обычно я использую функцию, которая выполняет работу и возвращает NULL - все данные читаются и обрабатываются в этой или других функциях, которые она вызывает.

6 голосов
/ 06 июня 2017

Имея только 4 ГБ ОЗУ (под управлением Windows 10, так что примерно 2 или более реально это 1 ГБ), я должен был быть очень осторожным с распределением.

Я использую data.table почти исключительно.

Функция 'fread' позволяет вам импортировать информацию по именам полей при импорте; импортируйте только те поля, которые действительно нужны для начала. Если вы используете чтение базы R, обнуляйте ложные столбцы сразу после импорта.

Как подсказывает 42- , когда это возможно, я буду помещать их в столбцы сразу после импорта информации.

Я часто rm () объекты из среды, как только они больше не нужны, например, на следующей строке после использования их для подстановки чего-то другого и вызова gc ().

'fread' и 'fwrite' из data.table могут быть очень быстрыми по сравнению с основанием R для чтения и записи.

Как подсказывает kpierce8 , я почти всегда переписываю все из окружения и перезаписываю его обратно, даже с тысячами / сотнями тысяч крошечных файлов, через которые можно пройти. Это не только обеспечивает чистоту среды и низкое распределение памяти, но, возможно, из-за серьезного недостатка доступной оперативной памяти, R имеет склонность к частым сбоям на моем компьютере; действительно часто. Резервное копирование информации на самом диске в процессе выполнения кода на разных этапах означает, что мне не нужно начинать с самого начала, если он падает.

По состоянию на 2017 год, я думаю, самые быстрые твердотельные накопители работают со скоростью несколько ГБ в секунду через порт M2. У меня действительно базовый твердотельный накопитель Kingston V300 (550 МБ / с) емкостью 50 ГБ, который я использую в качестве основного (на нем установлены Windows и R). Я храню всю основную информацию на дешевом 500-гигабайтном диске WD. Я перемещаю наборы данных в SSD, когда начинаю над ними работать. Это, в сочетании с «fread» и «fwrite» все работало замечательно. Я пытался использовать 'FF', но предпочитаю первое. Скорость чтения / записи 4K может создать проблемы с этим; Резервное копирование четверти миллиона файлов размером 1 КБ (стоимостью 250 МБ) с SSD на диск может занять несколько часов. Насколько я знаю, еще нет доступных пакетов R, которые могли бы автоматически оптимизировать процесс «чанкификации»; например посмотрите, сколько у пользователя оперативной памяти, проверьте скорость чтения / записи ОЗУ / всех подключенных дисков, а затем предложите оптимальный протокол «чанкификации». Это может привести к значительным улучшениям рабочего процесса / оптимизации ресурсов; например разделить его на ... МБ для оперативной памяти -> разделить на ... МБ для SSD -> разделить на ... МБ на блюде -> разделить на ... МБ на ленте. Он мог бы забрать наборы данных заранее, чтобы дать ему более реалистичный измерительный стержень для работы.

Множество проблем, над которыми я работал в R, включают в себя формирование пар комбинаций и перестановок, троек и т. Д., Что делает ограничение ограниченного объема ОЗУ более ограничительным, поскольку они часто по крайней мере экспоненциально расширяются какой-то момент. Это заставило меня сосредоточиться на качестве , а не на количестве информации, поступающей в них с самого начала, вместо того, чтобы пытаться очистить ее впоследствии, и на последовательности операции по подготовке информации для начала (начиная с самой простой операции и увеличивая сложность); например подмножество, затем объединить / объединить, затем сформировать комбинации / перестановки и т.д.

Кажется, в некоторых случаях использование чтения и записи базы R имеет некоторые преимущества. Например, обнаружение ошибок в 'fread' настолько хорошо, что может быть трудно попытаться получить действительно грязную информацию в R для начала, чтобы очистить ее. Base R также выглядит намного проще, если вы используете Linux. Кажется, что Base R отлично работает в Linux, Windows 10 использует ~ 20 ГБ дискового пространства, тогда как Ubuntu требуется всего несколько ГБ, а оперативной памяти, необходимой для Ubuntu, немного меньше. Но я заметил большое количество предупреждений и ошибок при установке сторонних пакетов в (L) Ubuntu. Я бы не рекомендовал уходить слишком далеко от (L) Ubuntu или других стандартных дистрибутивов с Linux, так как вы можете потерять столько общей совместимости, что делает процесс практически бессмысленным (я думаю, что единство должно быть отменено в Ubuntu с 2017 ). Я понимаю, что с некоторыми пользователями Linux это не получится, но некоторые пользовательские дистрибутивы не имеют смысла, кроме новизны (я провел годы, используя только Linux).

Надеюсь, некоторые из них могут помочь другим.

5 голосов
/ 26 апреля 2016

Это ничего не добавляет к вышесказанному, но написано в простом и сильно закомментированном стиле, который мне нравится. В результате получается таблица с упорядоченными по размеру объектами, но без некоторых деталей, приведенных в примерах выше:

#Find the objects       
MemoryObjects = ls()    
#Create an array
MemoryAssessmentTable=array(NA,dim=c(length(MemoryObjects),2))
#Name the columns
colnames(MemoryAssessmentTable)=c("object","bytes")
#Define the first column as the objects
MemoryAssessmentTable[,1]=MemoryObjects
#Define a function to determine size        
MemoryAssessmentFunction=function(x){object.size(get(x))}
#Apply the function to the objects
MemoryAssessmentTable[,2]=t(t(sapply(MemoryAssessmentTable[,1],MemoryAssessmentFunction)))
#Produce a table with the largest objects first
noquote(MemoryAssessmentTable[rev(order(as.numeric(MemoryAssessmentTable[,2]))),])
3 голосов
/ 23 февраля 2018

Это более новый ответ на этот превосходный старый вопрос. От продвинутого R Хэдли:

install.packages("pryr")

library(pryr)

object_size(1:10)
## 88 B

object_size(mean)
## 832 B

object_size(mtcars)
## 6.74 kB

(http://adv -r.had.co.nz / memory.html )

3 голосов
/ 14 ноября 2017

Если вы работаете в Linux и хотите использовать несколько процессов и вам нужно только прочитать операции с одним или несколькими большими объектами используйте makeForkCluster вместо makePSOCKcluster. Это также экономит время на отправку большого объекта другим процессам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...