我正在运行一个大型并行R函数,该函数包含要在每个内核上执行的for循环。该功能开始运行良好,但是随着循环的进行,计算机的内存使用量也会不断增加,直到最终RAM用完并且该过程失败为止。我不明白为什么会这样,因为该循环既不会创建对象也不会增长对象,并且不希望有任何指导来解决该问题。
我正在使用具有16个超线程CPU内核和128 GB RAM的Linux系统(Ubuntu 19.04)。使用doParallel包,我创建了一个分叉的集群,并使用foreach分发了任务。在每个内核上的每次循环迭代中,我都会让每个从属进程将其总内存使用情况打印到控制台(使用pryr :: mem_used())。我还打印了应用于本地环境中所有对象的object.size()命令的总和(使用environment()访问),以及应用于所有对象的object.size()命令的总和的同一图全局环境中对流程可见的对象。
在我开始调用foreach时,如htop所示(与终端分开调用)显示的内存使用总量几乎是我预期的两倍。全球环境中大约有12 GB的对象。我的31个逻辑核心中的每个核心都包含约1 GB的对象(根据object.size()的总和);但是htop报告的内存使用量约为70 GB。从每个内核调用的mem_used()报告每个内核看到约8.5 GB的内存使用情况-我假设每个内核上大约有1 GB的对象,再加上全局环境中约7GB的对内核可见的对象
我在整个循环中定期调用gc(),并且随着每个内核进入其循环,由object_size()和mem_used()报告的内存使用率始终保持在同一范围内。但是,htop报告的内存使用量随着时间的推移稳步增加,直到最终达到126 GB并崩溃。
这是调用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 GB(除非将全局环境中可见的7 GB复制到每个逻辑核心,但这将更像200 GB的使用量,我将看到所有使用情况)就在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 GB左右。然后,我得到以下错误,脚本中止。
Error in unserialize(socklist[[n]]) : error reading from connection
Calls: SimWrapper ... recvOneData -> recvOneData.SOCKcluster -> unserialize
Execution halted