R ошибка не могу разместить вектор размером

nickleb

Гуру

Сообщения: 961
Зарегистрирован: 22 май 2010, 20:20

Репутация: 152

R Cran: «Ошибка: не могу разместить вектор размером…»

Здравствуйте. R Cran не может разместить в памяти вектор:

Код: Выделить всё

> tpot<-ncvar_get(ncin,"THETA")
Ошибка: не могу разместить вектор размером 197.8 Mb

Windows XP, RAM 4 Gb
Что делать?


Аватара пользователя

rhot

Гуру

Сообщения: 1727
Зарегистрирован: 25 янв 2011, 17:50
Статьи: 1

Репутация: 194
Ваше звание: доктор
Откуда: Архангельск

Re: R Cran: «Ошибка: не могу разместить вектор размером…»

Сообщение

rhot » 10 апр 2015, 17:37

memory.limit()

___________(¯`·.¸(¯`·.¸ Scientia potentia est _/ {SILVA}:::{FOSS}:::{GIS} _ Знание сила ¸.·´¯)¸.·´¯)___________


nickleb

Гуру

Сообщения: 961
Зарегистрирован: 22 май 2010, 20:20

Репутация: 152

Re: R Cran: «Ошибка: не могу разместить вектор размером…»

Сообщение

nickleb » 10 апр 2015, 18:24

rhot писал(а):memory.limit()


nickleb

Гуру

Сообщения: 961
Зарегистрирован: 22 май 2010, 20:20

Репутация: 152

Re: R Cran: «Ошибка: не могу разместить вектор размером…»

Сообщение

nickleb » 10 апр 2015, 19:38

… а package bigmemory?…


Аватара пользователя

rhot

Гуру

Сообщения: 1727
Зарегистрирован: 25 янв 2011, 17:50
Статьи: 1

Репутация: 194
Ваше звание: доктор
Откуда: Архангельск

Re: R Cran: «Ошибка: не могу разместить вектор размером…»

Сообщение

rhot » 10 апр 2015, 19:40

установить memory.limit > 3000 не пробовали?

___________(¯`·.¸(¯`·.¸ Scientia potentia est _/ {SILVA}:::{FOSS}:::{GIS} _ Знание сила ¸.·´¯)¸.·´¯)___________


gamm

Гуру

Сообщения: 3932
Зарегистрирован: 15 окт 2010, 08:33

Репутация: 1029
Ваше звание: программист
Откуда: Казань

Re: R Cran: «Ошибка: не могу разместить вектор размером…»

Сообщение

gamm » 10 апр 2015, 21:21

nickleb писал(а):Что делать?

поставить нормальную х64 систему (Win7) и 16-32 Гб памяти. Выдавать периодически gc() и удалять ненужные объекты. Не использовать data.frame() для больших данных, ограничиться matrix(). Работать с большими объемами данных вне R, готовя выборки. Использовать подкачу данных по мере необходимости, разбив задачу на части (для некоторых случаев есть соответствующие пакеты). Но главное — поставить побольше памяти.


I am running into issues trying to use large objects in R. For example:

> memory.limit(4000)
> a = matrix(NA, 1500000, 60)
> a = matrix(NA, 2500000, 60)
> a = matrix(NA, 3500000, 60)
Error: cannot allocate vector of size 801.1 Mb
> a = matrix(NA, 2500000, 60)
Error: cannot allocate vector of size 572.2 Mb # Can't go smaller anymore
> rm(list=ls(all=TRUE))
> a = matrix(NA, 3500000, 60) # Now it works
> b = matrix(NA, 3500000, 60)
Error: cannot allocate vector of size 801.1 Mb # But that is all there is room for

I understand that this is related to the difficulty of obtaining contiguous blocks of memory (from here):

Error messages beginning cannot
allocate vector of size indicate a
failure to obtain memory, either
because the size exceeded the
address-space limit for a process or,
more likely, because the system was
unable to provide the memory. Note
that on a 32-bit build there may well
be enough free memory available, but
not a large enough contiguous block of
address space into which to map it.

How can I get around this? My main difficulty is that I get to a certain point in my script and R can’t allocate 200-300 Mb for an object… I can’t really pre-allocate the block because I need the memory for other processing. This happens even when I dilligently remove unneeded objects.

EDIT: Yes, sorry: Windows XP SP3, 4Gb RAM, R 2.12.0:

> sessionInfo()
R version 2.12.0 (2010-10-15)
Platform: i386-pc-mingw32/i386 (32-bit)

locale:
[1] LC_COLLATE=English_Caribbean.1252  LC_CTYPE=English_Caribbean.1252   
[3] LC_MONETARY=English_Caribbean.1252 LC_NUMERIC=C                      
[5] LC_TIME=English_Caribbean.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base

Хранение объектов

Для мониторинга используемой памяти можно обращаться к выводу функции gc() (основной её функционал — сбор мусора, garbage collection).

##           used (Mb) gc trigger (Mb) max used (Mb)
## Ncells  636223 34.0    1211135 64.7  1211135 64.7
## Vcells 1166455  8.9    8388608 64.0  1819344 13.9

В R есть некоторое различие в том, как хранятся и мониторятся разные объекты, поэтому в выводе gc() мы и видим две строки. Первая, Ncells, — это так называемые cons cells (пришедшие в R из Lisp и его особенностей хранения объектов в памяти). В R cons cells используются для хранения объектов, относящихся к самому языку: окружения, замыкания, парные списки, названия объектов и так далее. Полную статистику по этим объектам можно посмотреть с помощью функции memory.profile().

Вторая строчка вывода gc(), Vcells, — это, собственно, статистика используемых пользователем объектов. Правда, на данный момент это не выделяемая память (heaps), а блоки по 8 байт.

Группа used (Mb) отображает, сколько ячеек было использовано для хранения всех текущих объектов, а также объём задействованной памяти в мегабайтах. Группа колонок gc trigger (Mb) отражает, при каком количестве ячеек и памяти будет запущен сборщик мусора, группа max used (Mb) — статистики на момент предыдущего запуска сборщика.

При интерпретации вывода gc() в первую очередь надо смотреть на первую колонку (Mb), так как именно сумма значений обеих строк по этой колонке отражает состояние на текущий момент. Притом наибольший фокус должен быть, конечно, на строку Vcells: по сравнению с пользовательскими объектами объекты Ncells намного меньше, и их сложно увеличить до сколько-нибудь значимого объёма.

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

Размер объекта

При работе с большими объектами нередко возникает необходимость контролировать, какой объём оперативной памяти он занимает. Наиболее полезные инструменты здесь — функция object.size() базового пакета utils и функция obj_size() пакета lobstr. Обе функции отдают размер в байтах:

## 8000048 bytes
## 8,000,048 B

Результат функции object.size() можно представить в нужном формате с помощью format() и аргумента units, который позволяет задать единицу измерения (“b”, “Kb”, “Mb”, “Gb” и проч.) и стандарт представления единицы измерения (standard с допустимыми значениями “legacy”, “IEC”, “SI” и “auto”). Стандарты legacy и IEC считают байты по степеням двойки, то есть 1024 байта равны 1 килобайту. Стандарт SI опирается на десятичную шкалу, в которой 1 килобайт равен 1000 байтам.

## [1] "7.6 Mb"
## [1] "7.6 MiB"
## [1] "8 MB"

Оценки размеров объектов в памяти с помощью функции object.size() достаточно грубые, так как, во-первых, не учитывают ни окружение, которому принадлежит объект, ни название объекта, ни ситуации, когда части сложного объекта делятся с другими объектами (характерно для списков и таблиц, подробнее ниже). Во-вторых, размеры объектов могут несколько различаться на 64- и 32-битных системах.

Лимиты памяти

R хранит объекты в оперативной памяти. Как следствие, это накладывает некоторые ограничения на размер объектов в зависимости от операционной системы и её архитектуры, а также архитектуры приложения. Стоит отметить, что использование 32-битных версий R на 64-битных ОС также имеет свои особенности (в частности, на Windows), однако это достаточно редкое сочетание, и мы его не будем рассматривать.

Пользователи чаще всего сталкиваются с таким сообщением об ошибке: cannot allocate vector of size XXX (не могу разместить вектор размером XXX). Размер, который упоминается в этой ошибке, — это необходимое количество памяти сверх уже выделенного (а не сколько всего требуется памяти). В некоторых случаях это ошибка возникает из-за того, что операционная система не может выделить столько памяти под процесс (например, из-за высокой фрагментированности свободной памяти). В основном же эта ошибка возникает, когда размер объекта превышает (2^{31} — 1) байтов по какому-либо измерению (длина вектора, количество элементов в матрице, длина колонки и проч).

Для *nix-систем количество памяти, выделяемое под процесс, зависит от разрядности системы: для 32-битных систем под процесс выделяется 3 Gb (если не учитывать резервируемое под ядро), а в 64-битной системе уже намного больше — 128 Tb.

В Windows ситуация несколько сложнее. Во-первых, в 32-битных системах под процесс выделяется 2 Gb (при определенных настройках — 3 Gb). В 64-битных системах больше, но с ограничением в 8 Tb. Из-за того, что Windows ограничивает размер выделяемой памяти под процесс, в R есть инструменты управлениями этими лимитами — функции memory.size() и memory.limit(). Первая возвращает текущий или максимальный лимит памяти. Вторая возвращает текущий лимит или же увеличивает лимит памяти. Обе функции могут быть использованы только в Windows, в *nix-системах они будут отдавать Inf.

Понимать, какие есть лимиты памяти в операционной системе и в приложении, полезно, но рядовой пользователь с ними редко сталкивается. Скорее, возникновение ошибки cannot allocate vector of size XXX свидетельствует либо о плохо или с ошибкой написанном коде (что чаще), либо же о плохо построенном процессе работы с данными.

Трассировка объектов

Для отслеживания адреса объекта в памяти и его изменения при различных операциях используется функция tracemem():

## [1] "<0x561be4411450>"

При изменении элемента объекта адрес меняется и изменение адресов отображается сразу, без повторного вызова tracemem():

## tracemem[0x561be4411450 -> 0x561be817c1c8]: eval eval withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local

Для отключения трассировки можно воспользоваться функцией untracemem(), только для начала сохраним актуальный адрес объекта:

# сохраняем 
prev_address <- tracemem(x)

# отключаем трассировку и изменяем
untracemem(x)
x[3] <- 99

При желании после отключения трассировки можно сделать какую-нибудь новую операцию (или несколько) и c помощью retracemem() отследить изменение адреса с последнего известного до текущего:

# добавляем ещё одно изменение
x <- 9

# смотрим изменение адреса с предыдущего известного
retracemem(x, previous = prev_address)
## tracemem[<0x561be817c1c8> -> 0x561be7263ea8]: eval eval withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local

Функция tracemem(), использует вывод C-функции duplicate(), которая вызывается при копировании объектов. Как следствие, tracemem() бессмысленна или неприменима к функциям, окружениям и прочим объектам, которые нельзя дуплицировать.

Помимо tracemem() для получения адреса объекта можно использовать более информативный вызов внутренней функции inspect(), которая также возвращает адрес в памяти и C-тип данных, а также функцию lobstr::obj_addr(), которая является оболочкой над inspect():

## [1] "<0x561be7263ea8>"
## @561be7263ea8 14 REALSXP g0c1 [REF(3),TR] (len=1, tl=0) 9
## [1] "0x561be7263ea8"

Хранение разных типов данных

Для лучшего понимания основных типов данных в R рассмотрим их с точки зрения хранения в памяти. Это можно сделать как с помощью tracemem(), так и с помощью функций ref(), obj_addr() и obj_addrs() пакета lobstr, которые возвращают более детальную информацию об объекте и его элементах.

Векторы

Векторы элементов атомарных типов, за исключением строкового, имеют только адрес начала вектора. Со строковыми значениями ситуация несколько иная: в R используется так называемый глобальный кэш строковых значений (global string pooling). Это значит, что каждое новое строковое значение получает свой адрес и попадает в кэш, все же дальнейшие обращения к этой строке используют закэшированное значение (то есть обращаются по тому же адресу). Таким образом, вектор строковых значений будет содержать значения из кэша и какое-то количество ссылок на эти значения, которых может быть больше, чем уникальных строковых значений:

x_char <- sample(c('ab', 'ac', 'cb'), 7, replace = TRUE)
x_char <- sort(x_char)
print(x_char)
## [1] "ab" "ac" "ac" "ac" "cb" "cb" "cb"
lobstr::ref(x_char, character = TRUE)
## █ [1:0x561be66ea228] <chr> 
## ├─[2:0x561be68c0458] <string: "ab"> 
## ├─[3:0x561be68c0500] <string: "ac"> 
## ├─[3:0x561be68c0500] 
## ├─[3:0x561be68c0500] 
## ├─[4:0x561be27fbf98] <string: "cb"> 
## ├─[4:0x561be27fbf98] 
## └─[4:0x561be27fbf98]
## [1] "0x561be68c0458" "0x561be68c0500" "0x561be68c0500" "0x561be68c0500"
## [5] "0x561be27fbf98" "0x561be27fbf98" "0x561be27fbf98"

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

Списки и таблицы

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

# создадим список и посмотрим его адрес и адреса его подсписков
my_list <- list(e1 = 1:3, e2 = letters[1:5], e3 = month.abb[1])
lobstr::ref(my_list)
## █ [1:0x561be759a708] <named list> 
## ├─e1 = [2:0x561be61d8a20] <int> 
## ├─e2 = [3:0x561be7fd24f8] <chr> 
## └─e3 = [4:0x561be6321ad8] <chr>
# создаём новый подсписок
my_list$e4 <- sample(5)
lobstr::ref(my_list)
## █ [1:0x561be7e33108] <named list> 
## ├─e1 = [2:0x561be61d8a20] <int> 
## ├─e2 = [3:0x561be7fd24f8] <chr> 
## ├─e3 = [4:0x561be6321ad8] <chr> 
## └─e4 = [5:0x561be7e331f8] <int>

Аналогично с таблицами:

# создаём таблицу и смотрим её адреса
my_df <- data.frame(col1 = 1:5, col2 = letters[1:5], col3 = month.abb[1:5], stringsAsFactors = FALSE)
lobstr::ref(my_df)
## █ [1:0x561be7fe6f18] <df[,3]> 
## ├─col1 = [2:0x561be5dc3f20] <int> 
## ├─col2 = [3:0x561be809a998] <chr> 
## └─col3 = [4:0x561be809a928] <chr>
# создаём новую колонку и смотрим изменения
my_df$col4 <- sample(5)
lobstr::ref(my_df)
## █ [1:0x561be57f2588] <df[,4]> 
## ├─col1 = [2:0x561be5dc3f20] <int> 
## ├─col2 = [3:0x561be809a998] <chr> 
## ├─col3 = [4:0x561be809a928] <chr> 
## └─col4 = [5:0x561be57f26c8] <int>

Подходы к копированию и изменению объектов

Copy-on-modify

В R реализован вызов по значению (call-by-value). Правда, в несколько нетрадиционном виде: его корректнее было бы назвать вызовом по соиспользованию (call-by-sharing), в R Internals встречается формулировка call by value illusion. Смысл заключается в следующем: когда мы создаём копию объекта, то первоначально адреса исходного объекта и копии совпадают:

# создаём объект
x <- 1:5

# создаём его копию
y <- x

# смотрим адреса обоих объектов
tracemem(x)
## [1] "<0x561be52e0a00>"
## [1] "<0x561be52e0a00>"

То есть до какого-то момента объект и его копия используют один и тот же адрес в памяти. По-настоящему копия создаётся в тот момент, когда над копией производится какое-то действие:

## tracemem[0x561be52e0a00 -> 0x561be74d9da8]: eval eval withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local

Одновременно с механизмом copy-on-modify проявляется и обратная сторона процесса: изменение объекта всегда сопровождается его копированием, в некоторых случаях изменение может быть даже скрыто от пользователя. Например, когда мы хотим добавить к вектору целых значений numeric-значение.

## [1] "integer"
## [1] "<0x561be733eae0>"
## tracemem[0x561be733eae0 -> 0x561be7156318]: eval eval withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local 
## tracemem[0x561be7156318 -> 0x561be756c4c8]: eval eval withVisible withCallingHandlers handle timing_fn evaluate_call <Anonymous> evaluate in_dir eng_r block_exec call_block process_group.block process_group withCallingHandlers process_file <Anonymous> <Anonymous> do.call eval eval eval eval eval.parent local
## [1] "numeric"

Как мы видим, сначала за счёт неявного преобразования типов (integer в numeric) происходит копирование и изменение объекта, а потом — происходит добавление нового элемента, также с копированием и созданием объекта новой длины.

Shallow copy

Полное копирование объекта при его изменении (deep copy) — это достаточно дорогостоящая процедура, особенно при больших объемах данных, так как фактически требует двух- или даже трёхкратного объёма памяти объекта для проведения минимальных операций. В частности, из-за такого механизма R долгое время считался очень медленным языком. Тем не менее, в версии 3.1.0 был введён новый механизм копирования — shallow copy, поверхностное копирование. В первую очередь это касается списков и таблиц.

Основная идея механизма — копировать только тот элемент (подсписок или колонку), которая реально изменяется. Остальные элементы оставлять неизменными до тех пор, пока не возникнет какая-то необходимость в их изменении. По сути, механизм shallow copy — сочетание особенности хранения списков в памяти и механизма copy-on-modify.

# создаём таблицу и смотрим её адреса
my_df <- data.frame(col1 = 1:5, col2 = letters[1:5], col3 = month.abb[1:5], stringsAsFactors = FALSE)
lobstr::ref(my_df)
## █ [1:0x561be7fdde68] <df[,3]> 
## ├─col1 = [2:0x561be6ac3310] <int> 
## ├─col2 = [3:0x561be551a3f8] <chr> 
## └─col3 = [4:0x561be56ff338] <chr>
# создаём копию таблицы
my_df_copy <- my_df
lobstr::ref(my_df_copy)
## █ [1:0x561be7fdde68] <df[,3]> 
## ├─col1 = [2:0x561be6ac3310] <int> 
## ├─col2 = [3:0x561be551a3f8] <chr> 
## └─col3 = [4:0x561be56ff338] <chr>
# создаём новую колонку и смотрим изменения
my_df_copy$col1 <- my_df_copy$col1 * 2
my_df_copy$col4 <- sample(5)
lobstr::ref(my_df_copy)
## █ [1:0x561be80abfa8] <df[,4]> 
## ├─col1 = [2:0x561be704b568] <dbl> 
## ├─col2 = [3:0x561be551a3f8] <chr> 
## ├─col3 = [4:0x561be56ff338] <chr> 
## └─col4 = [5:0x561be80ac098] <int>

При копировании таблицы адреса её и копии, а также колонок в таблице и копии совпадали. После изменения первой колонки и создания четвёртой колонки в копии адрес копии и изменённых/созданных колонок изменился. При этом вторая и третья колонки, которые мы не меняли, остались по тому же адресу, что и в исходной таблице, в совместном использовании.

Как следствие, логичным выводом будет, что если мы изменим строку, то изменятся все колонки. То есть это очень невыгодная операция, особенно при больших размерах таблицы: обновлять надо будет полностью всю таблицу, а не только изменяемые колонки:

# смотрим адреса таблицы и колонок
lobstr::ref(my_df_copy)
## █ [1:0x561be80abfa8] <df[,4]> 
## ├─col1 = [2:0x561be704b568] <dbl> 
## ├─col2 = [3:0x561be551a3f8] <chr> 
## ├─col3 = [4:0x561be56ff338] <chr> 
## └─col4 = [5:0x561be80ac098] <int>
# переприсваиваем в строку её же значение (формально ничего не меняется)
my_df_copy[2, ] <- my_df_copy[2, ]

# смотрим ещё раз адреса
lobstr::ref(my_df_copy)
## █ [1:0x561be7e57fd8] <df[,4]> 
## ├─col1 = [2:0x561be7fbd9d8] <dbl> 
## ├─col2 = [3:0x561be7fbd888] <chr> 
## ├─col3 = [4:0x561be7fbd738] <chr> 
## └─col4 = [5:0x561be7e57da8] <int>

Полное обновление таблицы при добавлении строки — одна из причин, почему неэффективны циклы, использующие rbind() в теле цикла.

Modify-in-place

Помимо copy-on-modify, также есть возможность модификации объектов без копирования, на месте, modify-in-place. Подобные модификации возможны для векторов, а также для окружений — при изменении объекта внутри окружения адрес самого окружения не меняется:

# создаём новое окружение и один объект в нём
my_env <- new.env()
my_env$a <- 1:5
lobstr::ref(my_env)
## █ [1:0x561be6007210] <env> 
## └─a = [2:0x561be5fb87b8] <int>
# меняем объект
my_env$a[6] <- 99
lobstr::ref(my_env)
## █ [1:0x561be6007210] <env> 
## └─a = [2:0x561be80cd458] <dbl>

На практике лучше всего (да и полезней) изменение на месте без копирования реализовано в пакете data.table. При изменении колонки вся таблица не копируется, а меняется только колонка или даже её часть.

## █ [1:0x561be7e622e0] <data.table[,3]> 
## ├─col1 = [2:0x561be720e558] <int> 
## ├─col2 = [3:0x561be756c618] <chr> 
## └─col3 = [4:0x561be756cdf8] <chr>
my_dt[, col1 := col1 * 2]
lobstr::ref(my_dt)
## █ [1:0x561be7e622e0] <data.table[,3]> 
## ├─col1 = [2:0x561be7fbcf58] <dbl> 
## ├─col2 = [3:0x561be756c618] <chr> 
## └─col3 = [4:0x561be756cdf8] <chr>

Однако к data.table также применима логика copy-on-modify, что вызывает казусы: когда стандартным путём создаётся и изменяется копия таблицы, изменения также проявляются и в исходной таблице:

# создаём таблицу и её копию
my_dt <- data.table(col1 = 1:5, col2 = letters[1:5], col3 = month.abb[1:5])
my_dt_copy <- my_dt

# смотрим их адреса
lobstr::ref(my_dt)
## █ [1:0x561be7489b50] <data.table[,3]> 
## ├─col1 = [2:0x561be7ee2918] <int> 
## ├─col2 = [3:0x561be5801eb8] <chr> 
## └─col3 = [4:0x561be5801d68] <chr>
## █ [1:0x561be7489b50] <data.table[,3]> 
## ├─col1 = [2:0x561be7ee2918] <int> 
## ├─col2 = [3:0x561be5801eb8] <chr> 
## └─col3 = [4:0x561be5801d68] <chr>
# изменяем колонку в копии таблицы
my_dt_copy[, col1 := col1 * 2]

# смотрим результат
my_dt_copy
##    col1 col2 col3
## 1:    2    a  Jan
## 2:    4    b  Feb
## 3:    6    c  Mar
## 4:    8    d  Apr
## 5:   10    e  May
##    col1 col2 col3
## 1:    2    a  Jan
## 2:    4    b  Feb
## 3:    6    c  Mar
## 4:    8    d  Apr
## 5:   10    e  May
# проверяем на тождество
all.equal(my_dt, my_dt_copy)
## [1] TRUE
# ещё раз смотрим адреса
lobstr::ref(my_dt)
## █ [1:0x561be7489b50] <data.table[,3]> 
## ├─col1 = [2:0x561be74804a8] <dbl> 
## ├─col2 = [3:0x561be5801eb8] <chr> 
## └─col3 = [4:0x561be5801d68] <chr>
## █ [1:0x561be7489b50] <data.table[,3]> 
## ├─col1 = [2:0x561be74804a8] <dbl> 
## ├─col2 = [3:0x561be5801eb8] <chr> 
## └─col3 = [4:0x561be5801d68] <chr>

Для того чтобы избегать подобных ситуаций, следует пользоваться явной функцией копирования data-table объектов — data.table::copy().

Сборщик мусора

Сборщик мусора в R использует классический алгоритм — подсчёт ссылок на объекты в памяти. То есть подсчитывается, сколько ссылок есть на объект при каждом изменении, и если количество ссылок равно нулю, то объект может быть удалён, а занимаемая им память — возвращена операционной системе.

При этом сборщик мусора работает “поколениями”: исходя из предположения, что на новые объекты ссылки могут чаще меняться, чем на старые, новые объекты просматриваются сборщиком мусора чаще. К сожалению, такой механизм вынужден просматривать весь кэш строковых значений, что в некоторых ситуациях может замедлять работу сборщика мусора.

Сборщик мусора в R работает в автоматическом режиме, однако в некоторых случаях есть смысл форсировать процесс и вызвать сборщик вручную. Основная функция вызова сборщика мусора — уже описанная ранее функция gc(). Аргумент full = TRUE позволяет игнорировать поколения объектов, в результате сборщик мусора просматривает все объекты.

Также есть функция gcinfo(), если её запустить с аргументом verbose = TRUE, то она присутствует в фоновом режиме во время сессии и при автоматическом срабатывании сборщика мусора сообщает пользователю текущие статистики Ncells/Vcells. Функции gctorture() и gctorture2() используются в очень редких случаях (и в основном разработчиками R), так как форсируют сборщик мусора при каждом размещении объекта в памяти, что существенно замедляет работу.

Помимо сборщика мусора, в R есть возможность вручную запускать финализатор во время выполнения какой-либо операции или же при завершении сессии. Тем не менее, функция reg.finalizer() используется достаточно редко, и её применяют обычно только к R6-классам.

January 28 2013, 16:23

Category:

  • История
  • Cancel

Решила поработать с одним из файлов (формат SPSS) международного исследования PISA (http://www.oecd.org/pisa/pisaproducts/pisa2000/) за 2000 г. Файл с оценками по математическим тестам, большой: включает все страны-участницы, по 3 тысячи с лишним кейсов для каждой страны, свыше 400 переменных, в общем, общим весом свыше 150 мегабайт.
После запуска командной строки:

library(foreign)
pisa<-read.spss(«intstud_math.sav», to.data.frame = TRUE)

R выдал следующее:
Ошибка: не могу разместить вектор размером 498 Kb
Вдобавок: Предупреждения
1: In structure(list(message = as.character(message), call = call), :
Reached total allocation of 1535Mb: see help(memory.size)

Если грузить без добавления to.data.frame, то грузится нормально. Получаем объект класса list. Но тут возникает следующая проблема: мне не нужен файл целиком, нужен только кусок по РФ. Есть ли какой-то способ «вытащить» из list российскую подвыборку? У меня пока получается вытаскивать по одной переменной при помощи двойных квадратных скобок.

Буду признательна за помощь. Есть, конечно, простой способ — залезть в SPSS и там «отмотать», но очень хочется слезть с SPSS.

This tutorial shows how to increase or decrease the memory limit in R.

The tutorial contains two examples for the modification of memory limits. To be more specific, the article consists of the following topics:

So let’s take a look at some R codes in action:

The Starting Point: Running Memory Intense Function with Default Specifications

Let’s assume we want to create a huge vector of randomly distributed values using the rnorm function in R. Then we could use the following R code:

x <- rnorm(4000000000)            # Trying to run rnorm function
# Error: cannot allocate vector of size 29.8 Gb

Unfortunately, the RStudio console returns the error message: “cannot allocate vector of size 29.8 Gb”.

Even though there is no general solution to this problem, I’ll show in two examples how you might be able to fix this issue. So keep on reading!

Example 1: Garbage Collection Using gc() Function in R

The first possible solution for this problem is provided by the gc function. The gc function causes a garbage collection and prints memory usage statistics.

Let’s do this in R:

gc()                              # Apply gc() function
#          used (Mb) gc trigger (Mb) max used (Mb)
# Ncells 531230 28.4    1156495 61.8  1156495 61.8
# Vcells 988751  7.6    8388608 64.0  1761543 13.5

Have a look at the previous output of the RStudio console. It shows some memory usage statistics that might be helpful to evaluate your RAM usage.

Perhaps, the call of the gc function solves our memory problems:

x <- rnorm(4000000000)            # Trying to run rnorm function
# Error: cannot allocate vector of size 29.8 Gb

Unfortunately, this is not the case in this example. So let’s try something else…

Example 2: Increase Memory Limit Using memory.limit() Function

Another solution for the error message: “cannot allocate vector of size X Gb” can be the increasing of the memory limit available to R. First, let’s check the memory limit that is currently specified in R. For this, we simply have to call the memory.limit function as shown below:

memory.limit()                    # Check currently set limit 
# [1] 16267

The RStudio console shows that our current memory limit is 16267.

We can also use the memory.limit function to increase (or decrease) memory limits in R. Let’s increase our memory limit to 35000:

memory.limit(size = 35000)        # Increase limit
# [1] 35000

Now, we can run the rnorm function again:

x <- rnorm(4000000000)            # Successfully running rnorm function

Nice, this time it worked (even though it took a very long time to compute)!

Video, Further Resources & Summary

Do you want to learn more about adjusting memory limits? Then you may want to have a look at the following video of my YouTube channel. In the video, I’m explaining the R syntax of this tutorial.

The YouTube video will be added soon.

Note that the examples shown in this tutorial are only two out of many possible solutions for the error message “cannot allocate vector of size X Gb”. Have a look at this thread on Stack Overflow to find additional tips and detailed instructions.

Furthermore, you may want to have a look at the other tutorials on Statistics Globe.

  • Clear RStudio Console
  • Remove All Objects But One from Workspace in R
  • Difference between rm() & rm(list=ls())
  • Clean Up Memory in R
  • Determine Memory Usage of Data Objects
  • What’s the Difference Between the rm & gc Functions?
  • R Programming Tutorials

In summary: At this point you should know how to increase or decrease the memory limit in R programming. If you have additional questions, please let me know in the comments. Besides that, don’t forget to subscribe to my email newsletter in order to get updates on the newest tutorials.

В целом:

Примером простого способа размещения XML-данных и получения ответа (как строки) была бы следующая функция:

public string postXMLData(string destinationUrl, string requestXml)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(destinationUrl);
    byte[] bytes;
    bytes = System.Text.Encoding.ASCII.GetBytes(requestXml);
    request.ContentType = "text/xml; encoding='utf-8'";
    request.ContentLength = bytes.Length;
    request.Method = "POST";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(bytes, 0, bytes.Length);
    requestStream.Close();
    HttpWebResponse response;
    response = (HttpWebResponse)request.GetResponse();
    if (response.StatusCode == HttpStatusCode.OK)
    {
        Stream responseStream = response.GetResponseStream();
        string responseStr = new StreamReader(responseStream).ReadToEnd();
        return responseStr;
    }
    return null;
}

В вашем конкретная ситуация:

Вместо:

request.ContentType = "application/x-www-form-urlencoded";

use:

request.ContentType = "text/xml; encoding='utf-8'";

Также удалите:

string postData = "XMLData=" + Sendingxml;

И замените:

byte[] byteArray = Encoding.UTF8.GetBytes(postData);

на:

byte[] byteArray = Encoding.UTF8.GetBytes(Sendingxml.ToString());

задан Gregor 6 July 2018 в 14:13

поделиться

8 ответов

Подумайте, действительно ли вам нужны все эти данные в явном виде или может быть разрешена матрица? В R есть хорошая поддержка (см. Пакет Matrix для, например,) для разреженных матриц.

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

Если приведенное выше не поможет, получите 64-разрядную машину с таким количеством оперативной памяти, какую вы можете себе позволить, и установите 64-разрядную R.

Если вы не можете этого сделать, онлайн-сервисы для удаленных вычислений.

Если вы не можете сделать это, инструменты для сопоставления памяти, такие как пакет ff (или bigmemory, как упоминается Sascha), помогут вам построить новое решение. В моем ограниченном опыте ff — это более сложный пакет, но вы должны прочитать тему High Performance Computing в представлении задач CRAN.

ответ дан Jaap 16 August 2018 в 10:59

поделиться

Если вы запускаете свой скрипт в среде linux, вы можете использовать эту команду:

bsub -q server_name -R "rusage[mem=requested_memory]" "Rscript script_name.R"

, и сервер будет выделять запрошенную вам память (в соответствии с лимитами сервера, но с хорошим сервером — можно использовать огромные файлы)

ответ дан David Arenburg 16 August 2018 в 10:59

поделиться

Самый простой способ обойти это ограничение — переключиться на 64 бит R.

ответ дан David Heffernan 16 August 2018 в 10:59

поделиться

Я столкнулся с подобной проблемой, и я использовал 2 флеш-накопителя как «ReadyBoost». Эти два диска обеспечили дополнительную память объемом 8 ГБ (для кеша), и она решила проблему, а также увеличила скорость всей системы в целом. Чтобы использовать Readyboost, щелкните правой кнопкой мыши на диске, перейдите к свойствам и выберите «ReadyBoost» и выберите переключатель «использовать это устройство» и нажмите «Применить» или «ОК» для настройки.

ответ дан Kwaku Damoah 16 August 2018 в 10:59

поделиться

ответ дан Sacha Epskamp 16 August 2018 в 10:59

поделиться

Метод сохранения / загрузки, упомянутый выше, работает для меня. Я не уверен, как / if gc() дефрагментирует память, но это, похоже, работает.

# defrag memory 
save.image(file="temp.RData")
rm(list=ls())
load(file="temp.RData")

ответ дан Simon Woodward 16 August 2018 в 10:59

поделиться

Для пользователей Windows следующее помогло мне понять некоторые ограничения памяти:

  • перед открытием R, откройте монитор ресурсов Windows (Ctrl-Alt-Delete / Start Task Manager / Performance вкладка / нажмите на нижнюю кнопку «Монитор ресурсов» / вкладка «Память»)
  • вы увидите, сколько RAM-памяти мы уже использовались до того, как вы откроете R и какие приложения. В моем случае используется 1,6 ГБ всего 4 ГБ. Поэтому я смогу получить только 2,4 ГБ для R, но теперь все хуже …
  • откройте R и создайте набор данных объемом 1,5 ГБ, а затем уменьшите его размер до 0,5 ГБ, монитор ресурсов показывает, что моя оперативная память используется почти на 95%.
  • использовать gc() для сбора мусора => она работает, я вижу, что использование памяти сокращается до 2 ГБ

enter image description here [/g1]

Дополнительные советы, которые работают на моей машине:

  • подготовить функции, сохранить как файл RData, закрыть R, повторно открыть R, и загрузите функции поезда. Диспетчер ресурсов обычно показывает более низкое использование памяти, что означает, что даже gc () не восстанавливает всю возможную память, а закрытие / повторное открытие R лучше всего работает с максимальной доступной памятью.
  • другой трюк состоит в том, чтобы загружать только набор для тренировки (не загружайте тестовый комплект, который обычно может составлять половину размера набора поезда). Фаза тренировки может использовать максимальную память (100%), поэтому все, что доступно, полезно. Все это нужно взять с солью, поскольку я экспериментирую с ограничениями памяти R.

ответ дан tucson 16 August 2018 в 10:59

поделиться

ответ дан Rajib Kumar De 29 October 2018 в 11:55

поделиться

Другие вопросы по тегам:

Похожие вопросы:

my laptop has 4gb of ram still facing such issue even size of total excel file is 322mb,is there
a issue with my ram or rstudio.
Grateful! for your help.

#concat direcrtory to file names
files<-str_c("C:/Users/91932/Downloads/archive (2)/Fitabase Data 4.12.16-5.12.16",files)

# applying function to each element of vector

#map_df(.x = files, .f = read.csv,)

combine.df = csv_to_disk.frame(files)

r2evans's user avatar

r2evans

135k6 gold badges75 silver badges142 bronze badges

asked Dec 20, 2021 at 19:24

1273 _BE's user avatar

4

Some of the things I do when faced with this problem are:

  1. Close other applications that may consume a lot of resources (Chrome and some other browsers tend to consume a lot of RAM).
  2. Execute the command gc() so that R frees memory, if it is the first element of your code can run rm(list = ls()); gc()
  3. And if none of the above works, restart the whole computer and only work with R.

answered Dec 20, 2021 at 20:40

Diego Avila's user avatar

The “cannot allocate vector of size” error message occurs when there isn’t enough available memory (RAM) capacity to allocate a vector , array, or objects in R. You’ll see this error when you are working with large data sets or managing the allocation of large data objects. The good news this is usually isn’t a code error (like other R errors) but rather a system constrain that you’re going to need to maneuver around. 

Why You’re Seeing This R Error

The “cannot allocate vector of size” memory error message occurs when you are creating or loading an extremely large amount of data that takes up a lot of virtual memory usage. When dealing with such large datasets it is possible to actually run out of usable memory. It is most likely to happen when a data set is loaded in from an external source such as a package or another type of file. This is because you cannot control the size of an object contained in a source that you did not create. It is most likely to occur when accessing the function to load or create a vector or other data set. They can also occur with smaller objects if you have an extremely large number of objects in your program.

A List of More Specific Causes of the R Error

The cause of the “cannot allocate vector of size” error message is a virtual memory allocation problem. That being said, here are some specific things to watch out for with this error: 

  • Memory allocation problems due to other applications open on your machine. The total memory usage from the other applications is consuming the available memory capacity.
  • Bumping into the memory size limits for the R application. You can use the memory.limit() function to manage this on Windows systems.
  • Sloppy memory allocation practices and data model design in designing your program; look for things like sparsely populated arrays and matrices, excessive large objects, or loading unused packages and code into the R environment. 
  • Limit vector size, array length, and column definitions to the values that you actually need; this includes being aware of na values, which occupy memory regardless. Consider making performance tradeoffs between holding data in memory and generating values via a function or a code loop.
  • Difficulties recycling unused element(s) in R memory management. If you need them, look to move them into storage on disk or other environment.

Ways To Fix This Error

The “cannot allocate vector of size” memory issue error message has several R code solutions. The best thing about these solutions is that none of them is overly complicated, most are a simple single process that is easy to do in your R script.

  • The simplest solution is to avoid using overly large objects or excessively large numbers of them in one program or R session, for example try removing unneeded objects from your calculation to better fit within your total allocation of memory.
  • When reading in an external file enclose the read() function inside a subset() function resulting in the format of subset(read.(“filename”, header = TRUE), select = c(columns to be kept)) this will reduce the size of individual objects being created by removing unwanted columns.
  • You can clear out unneeded objects using the rm() function.
  • You can clean out address space using the gc() function.
  • You can also test for a limit to the free memory using the memory.limit() and set a higher value with the format of memory.limit(size=number).
  • Shut down any programs you are not using to clear up extra space.

Finding the best process to fix this error may take some trial and error but they are not hard or time-consuming. While this error message is not a coding problem it is still not difficult to fix within your R session.

R Error cannot allocate vector of size

У меня есть вопрос относительно использования памяти в R. Я запускаю Rcode для всей нашей базы данных в R в цикле for. Однако в какой-то момент код останавливается, говоря, что не может выделить вектор размером 325,7 МБ. Когда я посмотрел на диспетчер задач, я увидел, что он использует 28 ГБ ОЗУ на нашем сервере.

Я знаком с функцией gc() в R, но, похоже, это не работает. Например. код перестал работать на 15-й итерации, говоря, что не может выделить вектор. Однако, если я запускаю только 15-ю итерацию (и больше ничего), проблем вообще нет. Более того, для каждой новой итерации я удаляю свой DT, который на сегодняшний день является самым большим объектом в моей среде.

Пример кода:

DT <- data.table() 
items <- as.character(seq(1:10)) 
for (i in items){ 
  DT <- sample(x = 5000,replace = T) 
  write.csv(DT,paste0(i,".csv")) 
  gc() 
  rm(DT) 
} 

У меня такое ощущение, что эта функция gc не работает должным образом в цикле for. Это правильно или есть какие-либо другие возможные проблемы, то есть есть ли причины, по которым моя память заполняется после нескольких итераций?

1 ответ

Просмотрите лимит памяти с помощью команды memory.limit(), а затем расширьте его с помощью memory.limit(size=XXX)

Обратите внимание, что это всего лишь временный подход, и я думаю, что этот urlУправление памятью R / не может выделить вектор размером n Mb дает гораздо лучшее объяснение того, как с этим справиться.


0

mytkavish
10 Окт 2021 в 04:57

Понравилась статья? Поделить с друзьями:
  • R studio ошибка 800
  • R studio ошибка 433
  • R sem6 exit ошибка r
  • R keeper ошибка проверки лицензии
  • R keeper ошибка 7236