Я запускаю большую параллельную функцию R, которая содержит цикл for, который должен выполняться на каждом ядре. Функция начинает работать нормально, но по мере выполнения цикла увеличивается и использование памяти компьютера, пока, в конце концов, не закончится ОЗУ и процесс не завершится неудачно. Я не понимаю, почему это происходит, учитывая, что цикл не создает и не увеличивает объекты, и был бы признателен за любые рекомендации по решению проблемы.
Я работаю в системе Linux (Ubuntu 19.04) с 16-ю ядрами с многопоточным процессором и 128 ГБ ОЗУ. Используя пакет doParallel, я создаю разветвленный кластер и распределяю задачи с помощью foreach. На каждой итерации цикла на каждом ядре каждый подчиненный процесс выводит на консоль информацию об общем использовании памяти (используя pryr :: mem_used ()). У меня также есть распечатать сумму команды object.size (), примененной ко всем объектам в локальной среде (доступ к которой осуществляется с помощью environment ()), и ту же цифру для суммы команды object.size (), примененной ко всем объекты в глобальной среде, видимые для процесса.
В начале моего обращения к foreach общий объем использования памяти, как показывает htop (вызывается отдельно от терминала), почти вдвое больше, чем я ожидал. В глобальной среде около 12 ГБ объектов; каждое из моих 31 логических ядер содержит около 1 ГБ объектов (согласно сумме object.size ()); но использование памяти, о котором сообщает htop, составляет около 70 ГБ. Вызванный каждым ядром, mem_used () сообщает о 8,5 ГБ использования памяти, видимой каждым ядром - я предполагаю, что это примерно 1 ГБ объектов на каждом ядре, плюс ~ 7 ГБ объектов в глобальной среде, видимых ядрам. .
Я регулярно вызываю gc () на протяжении всего цикла, и, когда каждое ядро проходит свой цикл, использование памяти, о котором сообщают object_size () и mem_used (), всегда остается в одном диапазоне. Однако использование памяти, о котором сообщает htop, неуклонно увеличивается с течением времени, пока в конечном итоге оно не достигнет 126 ГБ и не вылетит.
Вот суть призыва к foreach:
library(doParallel)
num_cores <- detectCores() - 1
SimWrapper <- function(platform, cores) {
if (platform != 'windows') {
workers <- makeCluster(cores, type= 'FORK', outfile = '')
registerDoParallel(workers)
gc()
return(foreach(x = 1:cores, .packages = c('data.table', 'broom', 'rlang', 'sn', 'zoo', 'stringr', 'pryr')) %dorng% SimTournament(x))
}
}
out <- SimWrapper(.Platform$OS.type, cores = num_cores)
Перед вызовом foreach я уже разбил свои данные на соответствующее количество частей. Каждый из них представляет собой отдельный объект списка под названием CoreXXDT, содержащий четыре data.tables. Распараллеленная функция SimTournament извлекает данные для каждого ядра из глобальной среды следующим образом:
SimTournament <- function(core) {
ThisCoreDTs <- get(paste0('Core', core, 'DTs'), pos = .GlobalEnv)
list2env(setNames(ThisCoreDTs, paste0('ThisCore', names(ThisCoreDTs))), envir = environment())
rm(ThisCoreDTs)
gc()
}
Я проверяю использование памяти для каждого ядра в SimTournament следующим образом. Суммируя более 31 логического ядра, это никогда не приближается к 128 ГБ (если только 7 ГБ, видимых в глобальной среде, не копируются на каждое логическое ядро, но это будет больше похоже на 200 ГБ использования, и я бы увидел все это использование прямо в начале цикла foreach, вместо того чтобы увеличивать его по мере роста цикла).
# Initialize empty data.table of objects in local environment
ThisCoreMemUse <- data.table(Object = ls(environment()), Size = rep(NA_character_, length(ls(environment()))), SizeInMiB = rep(NA_real_,
length(ls(environment()))))
# Populate size of each object
for (i in 1:(nrow(ThisCoreMemUse))) {
set(ThisCoreMemUse, i, 'Size', format(object.size(get(ThisCoreMemUse$Object[i])), units = 'MiB'))
}
ThisCoreMemUse[, SizeInMiB := as.numeric(gsub(' MiB', '', Size))]
setorder(ThisCoreMemUse, -SizeInMiB)
# Repeat for objects in global environment visible to slave process
GlobalEnvMemUse <- data.table(Object = ls(.GlobalEnv), Size = rep(NA_character_, length(ls(.GlobalEnv))), SizeInMiB = rep(NA_real_, length(ls(.GlobalEnv))))
for (i in 1:(nrow(GlobalEnvMemUse))) {
set(GlobalEnvMemUse, i, 'Size', format(object.size(get(GlobalEnvMemUse$Object[i])), units = 'MiB'))
}
GlobalEnvMemUse[, SizeInMiB := as.numeric(gsub(' MiB', '', Size))]
# Calculate total reported memory usage for local and global environments, and combine into one data.table
ThisCoreMemUse <- rbindlist(list(data.table(Object = 'Total on core', Size = NA_character_, SizeInMiB = sum(ThisCoreMemUse$SizeInMiB)), data.table(Object = 'Visible in global env', Size = NA_character_, SizeInMiB = sum(GlobalEnvMemUse$SizeInMiB)), ThisCoreMemUse))
# Print results to console
print(paste0('Core number ', core, ' has total memory use of ', mem_used(), '. Its five largest objects are:'))
print(head(ThisCoreMemUse, 7))
Использование памяти увеличивается, пока не достигнет 125 ГБ или около того. Затем я получаю следующую ошибку, и сценарий прерывается.
Error in unserialize(socklist[[n]]) : error reading from connection
Calls: SimWrapper ... recvOneData -> recvOneData.SOCKcluster -> unserialize
Execution halted