R:Foreach并行化

时间:2013-11-18 22:55:09

标签: r parallel-processing

我想要运行一次100次的功能。该函数本身包含一个需要运行4000次的for循环。我将我的代码放在EC2上以在多个内核上运行它但我不确定我是否正确地执行它,因为它没有显示它是否实际使用了所有内核。下面的代码是否有意义?

#arbitrary function:
x = function() {
    y=c()
    for(i in 1:4000){
        y=c(y,i)
    }
    return(y)
}

#helper Function
loop.helper<-function(n.times){
    results = list()
    for(i in 1:n.times){
        results[[i]] = x()
    }
    return(results)
}

#Parallel
require(foreach)
require(parallel)
require(doParallel)

cores = detectCores() #32
cl<-makeCluster(cores) #register cores
registerDoParallel(cl, cores = cores)

这是我的问题,我不确定它是否应该是这样的:

out <- foreach(i=1:cores) %dopar% {
     helper(n.times = 100)
} 

或应该是这样的:

out <- foreach(i=1:100) %dopar% {
     x()
} 

它们都有效,但我不确定第一个是否会将任务分配给我拥有的32个核心,或者是否会在第二个foreach循环实现中自动执行该任务。

感谢

2 个答案:

答案 0 :(得分:3)

out <- foreach(i=1:100) %dopar% {
     x()
} 

这是正确的方法吗? foreach包将自动在已注册的核心中分配100个任务(在您的情况下为32个核心)。

如果您阅读了包文档,您可以阅读一些示例,并且应该对您更加清楚。

编辑:

回复@ user1234440的评论:

一些注意事项:

设置和管理并行任务需要一些时间(例如,设置多个作业同时运行,然后在最后组合结果)。对于一些简单的任务或小型作业,有时运行并行进程比简单的顺序循环花费的时间更长,因为设置并行进程所花费的时间比保存时间长。但是,对于大多数需要进行一些非平凡计算的任务,您可能会体验到速度的提升。

另外,根据我的阅读,当您使用更多内核时,您会看到收益递减(例如,使用8个内核可能不一定比使用4个内核快2倍,但可能只快1.5倍)。此外,根据我的个人经验,使用我系统上的所有可用内核会导致性能下降。我认为这是因为我将所有系统资源都用于并行作业,这会减慢我的其他系统进程。

话虽这么说,在使用foreach函数提供的并行处理能力时,我几乎总能体验到速度的提升。对于运行具有32个核心的100个作业的示例,4个核心将接收4个作业,而其他28个核心将接收3个作业。现在,好像32台计算机正在运行迷你for循环,迭代分配给每个核心的4或3个作业。每个循环完成后,结果将合并并返回给您。

如果使用简单的for循环比使用并行foreach循环更快地完成100个任务的运行,那么在常规for循环中运行这100个任务将会更快而不是在并行foreach循环中运行100次任务4000次。

答案 1 :(得分:1)

由于你想要执行100次函数“x”,你可以用:

out <- foreach(i=1:100) %dopar% {
    x()
}

这正确返回100个向量的列表。您的其他解决方案是错误的,因为它将执行“x”cores * 100次函数,返回100个向量的cores列表列表。

您可能会感到困惑,因为编写对每个核心使用一次迭代的并行循环是很常见的。例如,您也可以像这样执行“x”100次:

out <- foreach(i=1:cores, .combine='c') %dopar% {
  results <- vector('list', 25)
  for (j in 1:25) {
    results[[j]] <- x()
  }
  results
}

这也返回了100个向量的列表,它会更有效率。这种技术称为“任务分块”,当任务很短时,它可以提供明显更好的性能。你的第二个解决方案几乎就是这样,除了辅助函数应该执行更少的迭代,结果列表应该合并,我使用c作为组合函数。

重要的是要意识到你无法控制通过foreach循环中的迭代变量使用的核心数量:这是通过registerDoParallel函数控制的。但是大多数并行后端(包括doParallel)会将cores任务映射到cores工作者。同样重要的是要意识到您并未真正控制cores工作进程将使用的核心数。您可以控制在调用makeCluster时为执行任务而创建的进程数,但最终由操作系统来调度CPU核心上的进程,因此“核心”参数是用词不当。

另请注意,对于您的示例,您应将registerDoParallel称为:

registerDoParallel(cl)

由于您为cl参数指定了一个值,因此忽略cores参数,但文档并未明确说明。