0

当编写一个嵌套循环时,外部循环需要是顺序的,而内部循环通过一个非常简单的对象依赖于先前的迭代,有没有办法减少会话之间多次通信的开销?

这是一个使用doMCand的示例foreach

library(doMC)
library(tictoc)

registerDoMC(3)

tic()
a <- runif(100)
for(i in seq_len(333)){
  a <- foreach(j = 1:100, .combine = c) %dopar% {
    sqrt(a[j])*sqrt(max(a))
  } 
}
toc()
#> 7.669 sec elapsed

tic()
a <- runif(100)
for(i in seq_len(333)){
  a <- foreach(j = 1:100, .combine = c) %do% {
    sqrt(a[j])*sqrt(max(a))
  } 
}
toc()
#> 5.224 sec elapsed

reprex 包(v0.3.0)于 2019-07-22 创建

因为内部循环中的计算非常简单,所以创建多个会话和传输数据所引入的开销使得并行版本比顺序版本慢。

在分析代码后,正如预期的那样,时间差主要来自mcfork,但由于计算非常简单并且仅由于a向量而在调用之间有所不同,我想知道是否有一种方法可以使会话在foreach调用之间持续存在并且只发送每次调用之间的a向量(我认为这应该比分叉整个会话要快得多)。

有没有办法通过会话分叉结构来实现doMC?是否有另一个并行后端能够提供这种解决方案(使会话在任务之间发生微小变化时持续存在)?

4

1 回答 1

0

我不确定并行计算是否可以递归。因此,如果 resultn+1依赖于 result n,那么如果不重新构建算法就无法并行。

对于您的特定问题,它不是真正的递归 when max(a) > 0. 在哪里a[j] == max(a),您的内部循环将评估为max(a). 那是:

sqrt(a[which.max(a)]) * sqrt(max(a)) == max(a)

所以对于每个循环,sqrt(max(a))max(a) > 0. 此外,我们可以致力于简化循环的其他部分:

#i = 1
sqrt(a[1]) * sqrt(max(a))
#i = 2
sqrt(sqrt(a[1]) * sqrt(max(a))) * sqrt(max(a))
#simplifies to
a[1]^(1/4) * max(a) ^ (3/4)

这个循环可以翻译成:

a^(1 / 2^n) * max(a)^((2^n - 1) / (2^n))

我知道这并不能直接回答您的问题,但它支持@F。Privé 的评论:试着重新思考这个问题,看看它是否可以被矢量化。例如,即使您的示例循环也可以矢量化:

for(i in seq_len(n)){
  a <- sqrt(a) * sqrt(max(a)) 
  }

对于seq_len(5),这是方法的性能:

Unit: microseconds
             expr      min       lq      mean   median       uq      max neval
  do_par_original 125089.5 128144.6 128089.30 128462.3 129352.3 129397.8     5
  inner_as_vector   3095.4   3183.0   9754.70   3288.2   3307.9  35899.0     5
 fully_vectorized     17.7     24.8     25.76     28.1     28.1     30.1     5
于 2019-07-23T02:37:19.270 回答