r%dopar%嵌套循环未并行运行

时间:2019-02-19 07:21:39

标签: r nested-loops doparallel parallel-foreach

我正在使用%dopar%运行嵌套循环,以生成用于经验目的的虚拟数据集。 参考链接: R nested foreach %dopar% in outer loop and %do% in inner loop

样本数据集

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)

enter image description here

问题

当我在%dopar%上运行时,确实使用Resource Monitor监视了计算机的CPU性能,并且我发现CPU没有得到充分利用。 enter image description here

问题

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

1 个答案:

答案 0 :(得分:1)

我相信您会看到foreach软件包的初始开销,因为它复制并设置了正确运行每个循环所需的内容。在运行您的代码大约30至60秒后,我的CPU的利用率都太高了,直到代码完成为止。

也就是说,它不能解释为什么您的代码与%do%循环相比如此之慢。我认为,当您尝试访问所有foreach循环中的数据时,最主要的问题是如何应用foreach循环。基本上,如果您不导出所需的数据,它将尝试在多个并行会话中访问相同的数据,并且每个会话将不得不等待其他会话完成其自身数据的访问。 通过使用foreach中的.export参数导出数据,可以缓解这种情况。就我个人而言,我使用其他程序包来执行大部分的并行处理,因此,如果您要这样做,我建议您对此进行测试。但是,这会带来更大的开销。

更快的方法:

现在,当您尝试创建一个虚拟数据集时,将某些列的所有组合都合并在一起,因此有更快的方法来获取此数据集。快速搜索“交叉连接”将lead you to posts like this one.

对于data.table包,可以使用'CJ'功能非常高效和快速地完成。只是

output <- CJ(ID, year, month)

将产生您的嵌套循环尝试创建的结果,只需花费约0.07秒的时间即可完成任务。