【问题标题】:Even with tiny data: Error in mcfork() : unable to fork, possible reason: Cannot allocate memory即使数据很小:mcfork() 中的错误:无法分叉,可能的原因:无法分配内存
【发布时间】:2018-02-02 01:34:21
【问题描述】:

我一直在研究处理大型语料库的功能。在其中我使用 doparallel 包。在 50 - 100k 个文档上一切正常。我在 1M 的文档上进行了测试,并收到了上述错误。

但是,当我返回到我之前正在处理的语料库大小时,我仍然遇到同样的错误。我什至尝试过低至 1k 个文档。在控制台调用函数时,一按回车就会产生错误。

虽然我有 15 个核心,但我只测试了两个核心 - 同样的问题。

我还尝试使用rm(list = ls()) 重新启动会话并清除环境

代码:

clean_corpus <- function(corpus, n = 1000) { # n is length of each peice in parallel processing

  # split the corpus into pieces for looping to get around memory issues with transformation
  nr <- length(corpus)
  pieces <- split(corpus, rep(1:ceiling(nr/n), each=n, length.out=nr))
  lenp <- length(pieces)

  rm(corpus) # save memory

  # save pieces to rds files since not enough RAM
  tmpfile <- tempfile() 
  for (i in seq_len(lenp)) {
    saveRDS(pieces[[i]],
            paste0(tmpfile, i, ".rds"))
  }

  rm(pieces) # save memory

  # doparallel
  registerDoParallel(cores = 14)
  pieces <- foreach(i = seq_len(lenp)) %dopar% {
    # update spelling
    piece <- readRDS(paste0(tmpfile, i, ".rds"))
    # spelling update based on lut
    piece <- tm_map(piece, function(i) stringi_spelling_update(i, spellingdoc))
    # regular transformations
    piece <- tm_map(piece, removeNumbers)
    piece <- tm_map(piece, content_transformer(removePunctuation), preserve_intra_word_dashes = T)
    piece <- tm_map(piece, content_transformer(function(x, ...) 
      qdap::rm_stopwords(x, stopwords = tm::stopwords("english"), separate = F)))
    saveRDS(piece, paste0(tmpfile, i, ".rds"))
    return(1) # hack to get dopar to forget the piece to save memory since now saved to rds
  }

  # combine the pieces back into one corpus
  corpus <- list()
  corpus <- foreach(i = seq_len(lenp)) %do% {
    corpus[[i]] <- readRDS(paste0(tmpfile, i, ".rds"))
  }
  corpus <- do.call(function(...) c(..., recursive = TRUE), corpus)
  return(corpus)

} # end clean_corpus function

然后当我运行它时,即使是在一个小语料库上:

> mini_cleancorp <- clean_corpus(mini_corpus, n = 1000) # mini_corpus is a 10k corpus
 Show Traceback

 Rerun with Debug
 Error in mcfork() : 
  unable to fork, possible reason: Cannot allocate memory 

在我尝试运行该功能之前,这是终端中 top 的一些屏幕截图。

【问题讨论】:

  • 我总是会明确地创建集群并在使用后关闭它。您可以尝试使用stopImplicitCluster
  • 感谢您的提示,在函数中添加它的适当位置是否就在 dopar 块中关闭 } 之后?
  • 是的。但是,您的问题也可能是打开的文件连接过多。我真的不明白为什么要在同一个函数调用中导出到文件并再次导入。是因为记忆的原因吗?不能用foreach.combine参数吗?
  • 是的,内存问题。我一直在努力克服内存限制,这就是我这样做的原因。是的,尝试了 .combine 但达到了内存限制。将每次迭代保存到临时 RDS 文件中,然后删除迭代的存储空间 (return(1)) 似乎确实完成了工作,尽管可能比其他方式慢
  • 您在使用少数内核运行时遇到错误,并尝试通过向其添加更多内核来解决此问题?嗯,没有。首先尝试了解错误。无论如何,随着内核数量的增加对内存使用和速度进行基准测试(对于非平凡的任务,您应该始终这样做)。

标签: r parallel-processing tm doparallel


【解决方案1】:

当您在 Unix 系统上使用 registerDoParallel(cores) 时,您最终会获得主 R 会话的 分叉 进程的工作人员。错误消息中的“mcfork()”也确认了这一点。

现在,当使用 forked 并行处理时,工作人员“共享”主 R 会话中的内存。这对你有利。但是,在分叉时(即当您调用 foreach() 时)不在主 R 会话中的任何新对象都将在 worker 中分配新内存,因此会增加整体内存消耗。这也适用于加载的包。

例如,在您的第一个foreach() 循环中,您调用qdap::rm_stopwords()tm::stopwords()。这意味着,如果包 qdaptm 没有在主 R 会话中加载,则 14 个分叉进程中的每一个都将独立加载它们(及其依赖项),从而占用 14 次这些包所需的内存。因此,在新的 R 会话中,比较使用和不使用的总体内存使用情况:

 loadNamespace("qdap")
 loadNamespace("tm")

我做了一个非常粗略的检查,它看起来像qdap,它的依赖项消耗了大约 3 GiB 的 RAM。因此,在 14 个内核(= worker)中独立加载,将消耗 42 GiB 的 RAM。如果您在调用 foreach() 之前加载它,您的内存总消耗应该保持在 3 GiB 左右。

【讨论】:

  • 感谢您回答这对我来说是新信息。实际上,我在前面加上 qdap:: 和 tm:: 的原因是,由于某种原因,在我的 linux 托管的 RStudio 上,每当我调用包函数时都会出错。我必须在每个已安装的包函数调用前加上 packagename::functionname()。在这种情况下, qdap 和 tm 都已经加载到父会话中,即使我引用了 foreach 中的库
猜你喜欢
  • 2013-03-18
  • 2015-05-17
  • 1970-01-01
  • 2013-08-24
  • 2011-11-30
  • 1970-01-01
  • 2016-11-16
  • 2013-09-16
相关资源
最近更新 更多