r %dopar% 嵌套循环不是 运行 并行

r %dopar% nested loop not running in parallel

我正在 运行 通过使用 %dopar% 生成一个嵌套循环来生成用于体验目的的虚拟数据集。 参考文献 link:

示例数据集

set.seed(123)
n = 10000 #number of unique IDs (10k as trial) , real data consits of 50k unique IDs
ID <- paste(LETTERS[1:8],sample(n),sep = "")
year <- c('2015','2016','2017','2018')
month <- c('1','2','3','4','5','6','7','8','9','10','11','12')

预定义库

library(foreach)  
library(data.table)
library(doParallel)

# parallel processing setting
cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)

测试 1:%dopar% 脚本

system.time(
  output_table <- foreach(i = seq_along(ID), .combine=rbind, .packages="data.table") %:%
    foreach(j = seq_along(year), .combine=rbind, .packages="data.table") %:%
    foreach(k = seq_along(month), .combine=rbind, .packages="data.table") %dopar% {

    data.table::data.table(
      mbr_code = ID[i],
      year = year[j],
      month = month[k]
    )
  }
)
stopCluster(cl)

#---------#
# runtime #
#---------#
>    user  system elapsed 
> 1043.31   66.83 1171.08

测试 2:%do% 脚本

system.time(
  output_table <- foreach(i = seq_along(ID), .combine=rbind, .packages="data.table") %:%
    foreach(j = seq_along(year), .combine=rbind, .packages="data.table") %:%
    foreach(k = seq_along(month), .combine=rbind, .packages="data.table") %do% {

    data.table::data.table(
      mbr_code = ID[i],
      year = year[j],
      month = month[k]
    )
  }
)
stopCluster(cl)

#---------#
# runtime #
#---------#
> user  system elapsed 
> 1101.85    1.02 1110.55 

预期输出结果

> view(output_table)

问题

当我 运行 在 %dopar% 上时,我确实使用 Resource Monitor 监控我的机器的 CPU 性能,我注意到 CPU 没有得到充分利用。

问题

我确实在我的 i5、4 核机器上尝试 运行 以上脚本(test1 和 test2)。但似乎 %do%%dopar% 的 运行 时间彼此关闭。这是我的脚本设计问题?我的真实数据由 50k 个唯一 ID 组成,这意味着如果在 %do% 中 运行 将花费很长时间,我如何才能充分利用我的机器 CPU 来减少 运行时间?

我相信您看到了 foreach 包的初始开销,因为它会正确复制和设置 运行 每个循环所需的任何内容。在 运行 使用您的代码大约 30 - 60 秒后,我的 cpu 在代码最终完成之前的所有利用率都达到了最高水平。

也就是说,它并不能解释为什么您的代码与 %do% 循环相比如此慢。我相信这里的问题在于当您尝试跨所有 foreach 循环访问数据时如何应用 foreach 循环。基本上,如果您不导出所需的数据,它将尝试在多个并行会话中访问相同的数据,并且每个会话都必须等待其他会话完成访问自己的数据。 这可能可以通过在 foreach 中使用 .export 参数导出数据来缓解。我个人使用其他包来执行我的大部分并行化,所以如果这是你想要的,我建议测试它。然而,这会带来更大的开销。

更快的方法:

现在当您尝试创建一个虚拟数据集时,某些列的所有组合都被组合在一起,有更快的方法来获得它。快速搜索 'cross-join' 将 lead you to posts like this one.

对于 data.table 包,使用 'CJ' 函数可以非常高效和快速地完成。简直

output <- CJ(ID, year, month)

将产生您的嵌套循环试图创建的结果,执行该任务仅需大约 0.07 秒。