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 秒。
我正在 运行 通过使用 %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 秒。