如何在 R 中嵌套 foreach 循环的内循环和外循环之间添加代码

问题描述

我了解到在 R 中执行嵌套 foreach 循环的正确方法是通过嵌套运算符 %:%(例如 https://cran.r-project.org/web/packages/foreach/vignettes/nested.html)。

但是,使用这种方法时,不能在内循环和外循环之间添加代码——请参见下面的示例。

有没有办法创建嵌套的、并行的 foreach 循环,以便可以在内循环和外循环之间添加代码

更一般地说,我想到的显而易见的方法有什么问题,即简单地使用 %dopar% 运算符而不是 %:% 运算符嵌套两个 foreach 循环?请参阅下面的简单示例。

library(foreach)

# Set up backend
cl = makeCluster(6)
registerDoParallel(cl)
on.exit(stopCluster(cl))

# Run nested loop with '%:%' operator. Breaks if adding code between the inner and outer loops 
foreach(i=1:2) %:% 
  # a = 1 #trivial example of running code between outer and inner loop -- throws error 
  foreach(j = 1:3) %dopar% {
    i * j
  }

# Run nested loop using 2 '%dopar%' statements -- is there anything wrong with this?
foreach(i=1:2,.packages = 'foreach') %dopar% {
  a = 1 #trivial example of running code between outer and inner loop
  foreach(j = 1:3) %dopar% {
    i * j
  }
}

解决方法

provided 文档中的“将 open (F,$file) or die; ### open the file or end the analyze while(my $one = <F>) {### readline => F chomp $one; my @cols = split(/\s+/,$one); ### split columns next unless (scalar @cols) == 8; ### the line must have 8 columns to add to the array my $val = $cols[5]; if ($val >= 0.5) { my $position = $cols[3]; my $id_list = $cols[0]; $id_list =~ s/^\s*([^_]+)_([0-9]+)_([a-zA-Z0-9]+)/$1.$2|$3/; if (exists($fastadata{$id_list})) { my $new_seq = $fastadata{$id_list}; my $subresidues = substr($new_seq,$position -3,5); print $subresidues,"\n"; } } } %:% 一起使用”一章给出了有用的提示:

所有的任务都是完全独立的,所以它们都可以并行执行

%dopar% 运算符将多个 foreach 循环变成一个循环这就是上例中只有一个 %:% 运算符的原因。当我们通过将 %do% 更改为 %do% 来并行化嵌套的 foreach 循环时,我们正在创建可以并行执行的单个任务流。

当您组合两个 %dopar% 并测量执行时间时,您会看到只有外循环是并行执行的,这可能不是您要找的:

%dopar%

这段经过的时间反映了:

system.time(
foreach(i=1:2,.packages = 'foreach') %dopar% {
  # Outer calculation
  Sys.sleep(.5)
  foreach(j = 1:3) %dopar% {
    # Inner calculation
    Sys.sleep(1)
  }
})
#  user      system     elapsed 
#  0.00        0.00        3.52 

如果外层计算不是太长,放入内循环实际上更快,因为使用了您示例的6个worker:

parallel[ outer(0.5s) + sequential [3 * inner(1s)] ] ~ 3.5s

如果外层计算太长,并且您的内循环比外循环多得多,您可以并行预先计算外循环。然后您可以在 system.time(res <- foreach(i=1:2,.packages = 'foreach') %:% foreach(j = 1:3) %dopar% { # Outer calculation Sys.sleep(.5) # Inner calculation Sys.sleep(1) }) # user system elapsed # 0.02 0.02 1.52 中使用结果:

%:%

比 :

system.time({
  precalc <- foreach(i=1:2) %dopar% {
    # Outer pre-calculation
    Sys.sleep(2)
    i
  }
  foreach(i=1:2,.packages = 'foreach') %:%
    foreach(j = 1:12) %dopar% {
      # Inner calculation
      Sys.sleep(1)
      precalc[[i]]*j
    }
})
#   user  system elapsed 
#   0.11    0.00    5.25