正确地将一个for循环与R并行化

时间:2013-03-02 13:10:03

标签: r loops foreach parallel-processing

我一直致力于为我的主管编写一个简单的函数集合,这些函数将执行一些简单的初始基因组比例统计,这很容易让我的团队快速指出未来的分析可能会花更多的时间 - 例如RDP4或BioC(只是为了解释为什么我还没有直接进入BioConductor)。我想加快一些事情以允许更大的重叠群大小,所以我决定使用doParallel和foreach来编辑一些for循环以允许这个。下面是一个简单的函数,它可以识别某些序列中的基数(存储为矩阵),这些序列是相同的并将它们删除。

strip.invar <- function(x) {
  cat("
          Now removing invariant sites from DNA data matrix, this may take some time...
      ")
  prog <- txtProgressBar(min=0, max=ncol(x), style=3)
  removals<-c()
  for(i in 1:ncol(x)){
    setTxtProgressBar(prog, i)
    if(length(unique(x[,i])) == 1) { 
      removals <- append(removals, i)
    }
  }
  newDnaMatrix <- x[,-removals]
  return(newDnaMatrix)
}

在阅读了doParallel和foreach的介绍之后,我尝试制作一个版本以容纳更多内核 - 在我的mac上这是8 - 四核,每个内核有两个线程--8个虚拟内核:

strip.invar <- function(x, coresnum=detectCores()){
  cat("
          Now removing invariant sites from DNA data matrix, this may take some time...
          ")
  prog <- txtProgressBar(min=0, max=ncol(x), style=3)
  removals<-c()
  if(coresnum > 1) {
    cl <- makeCluster(coresnum)
    registerDoParallel(cl)
    foreach(i=1:ncol(x)) %dopar% {
      setTxtProgressBar(prog, i)
      if(all(x[,i] == x[[1,i]])){
        removals <- append(removals, i)
      }
    }
  } else {
    for(i in 1:ncol(x)){
      setTxtProgressBar(prog, i)
      if(length(unique(x[,i])) == 1) { 
        removals <- append(removals, i)
      }
    }
  }
  newDnaMatrix <- x[,-removals]
  return(newDnaMatrix)
}

但是,如果我运行此核心并将核心数设置为8,我并不完全确定它是否有效 - 我无法看到进度条做任何事情但我已经听说过打印使用R中的并行计算来筛选和涉及图形设备的东西很棘手。但它似乎还需要一些时间,而我的笔记本电脑也非常“非常”。热,所以我不确定我是否已经正确地完成了这个,我在看了几个例子之后尝试过(我在小插图中成功地运行了一个很好的bootstrap示例)但是我已经绑定了打到学习颠簸。顺便说一句,我想我也会问人们的意见,R代码瓶颈的最佳加速是什么,涉及循环或应用 - 并行化,或者Rcpp?

感谢。

2 个答案:

答案 0 :(得分:0)

首先,尝试运行cl <- makeCluster( coresnum-1 )。主R进程已经使用了一个内核,用于从从属作业发送和接收结果,因此您有7个可用于从属作业的内核。我想想你将有效地排队你的一个foreach循环,等到之前的一个循环结束,因此这个工作需要更长的时间才能完成。

其次,您在通常在非并行环境中运行此功能的控制台上看到的内容是打印到控制台,只是每个作业输出都打印到从属进程控制台,因此您将看不到它。但是,您可以将不同foreach循环的输出保存到文本文件中以检查它们。 Here is an example如何保存控制台输出。将代码粘贴在foreach语句中。

您的笔记本电脑将变得非常热,因为您在运行此作业时所有核心都以100%的容量运行。

我发现foreach包提供了一组优秀的函数来提供简单的并行处理。 Rcpp可能(会吗?!)为您提供更高的性能,但是您如何编写C ++代码以及此函数的运行时间以及使用频率如何?我总是先考虑这些事情。

答案 1 :(得分:0)

我的另一个答案是不正确的,因为colmean等于第一个值不足以测试唯一值的数量。所以这是另一个答案:

您可以使用apply优化循环。

set.seed(42)
dat <- matrix(sample(1:5,1e5,replace=TRUE),nrow=2)
res1 <- strip.invar(dat)


strip.invar2 <- function(dat) {
  ix <- apply(dat,2,function(x) length(unique(x))>1)
  dat[,ix]}

res2 <- strip.invar2(dat)

all.equal(res1,res2)
#TRUE
library(microbenchmark)
microbenchmark(strip.invar(dat),strip.invar2(dat),times=10)
#Unit: milliseconds
#             expr       min        lq    median       uq      max neval
#strip.invar(dat)  2514.7995 2529.2827 2547.6751 2678.464 2792.405    10
#strip.invar2(dat)  933.3701  945.5689  974.7564 1008.589 1018.400    10

这可以提高性能,但是如果可以进行矢量化,则不会达到你所能达到的程度。

并行化在这里不会提供更好的性能,因为每次迭代都不需要很多性能,因此并行化开销实际上会增加所需的时间。但是,您可以并行拆分数据和处理块。