R 为什么并行包比仅仅使用apply慢?

R 为什么并行包比仅仅使用apply慢?,r,parallel-processing,R,Parallel Processing,我正在尝试确定何时使用并行包来加快运行某些分析所需的时间。我需要做的一件事是创建矩阵,比较两个数据帧中不同行数的变量。我问了一个关于如何有效地进行测试的问题,并在我的电脑上写了一些测试。因为我对最好的方法很满意,所以我想通过并行运行来加速这个过程。以下结果基于2ghz i7 Mac和8gb RAM。我感到惊讶的是,parallel包,特别是parSapply函数,比仅仅使用apply函数更糟糕。下面是复制此功能的代码。请注意,我目前只使用我创建的两个列中的一个,但最终希望同时使用这两个列 (来

我正在尝试确定何时使用
并行
包来加快运行某些分析所需的时间。我需要做的一件事是创建矩阵,比较两个数据帧中不同行数的变量。我问了一个关于如何有效地进行测试的问题,并在我的电脑上写了一些测试。因为我对最好的方法很满意,所以我想通过并行运行来加速这个过程。以下结果基于2ghz i7 Mac和8gb RAM。我感到惊讶的是,
parallel
包,特别是
parSapply
函数,比仅仅使用
apply
函数更糟糕。下面是复制此功能的代码。请注意,我目前只使用我创建的两个列中的一个,但最终希望同时使用这两个列


(来源:)

require(并行)
需要(ggplot2)
要求(2)
种子(2112)

结果并行运行作业会产生开销。只有在工作节点上启动的作业花费大量时间时,并行化才能提高总体性能。当单个作业只需几毫秒时,持续启动作业的开销将降低总体性能。诀窍是在节点上分配工作,使作业足够长,比如至少几秒钟。我用它来同时运行六个Fortran模型,效果非常好,但是这些单独的模型运行需要几个小时,几乎抵消了开销的影响


请注意,我没有运行您的示例,但我上面描述的情况通常是并行化比顺序运行花费更长时间的问题。

这些差异可归因于1)通信开销(特别是在跨节点运行时)和2)性能开销(例如,如果与启动并行化相比,您的工作没有那么密集)。通常,如果您正在并行化的任务没有那么耗时,那么您会发现并行化没有太大的影响(在大型数据集上非常明显)

尽管这可能不会直接回答您的基准测试,但我希望这应该非常简单,并且可以与相关。例如,在这里,我使用
1e6
行构建了一个
data.frame
,行中包含
1e4
唯一列
条目和列
val
中的一些值。然后使用运行e> plyr
并行中
使用
doMC
且不并行

df <- data.frame(group = as.factor(sample(1:1e4, 1e6, replace = T)), 
                 val = sample(1:10, 1e6, replace = T))
> head(df)
  group val
# 1  8498   8
# 2  5253   6
# 3  1495   1
# 4  7362   9
# 5  2344   6
# 6  5602   9

> dim(df)
# [1] 1000000       2

require(plyr)
require(doMC)
registerDoMC(20) # 20 processors

# parallelisation using doMC + plyr 
P.PLYR <- function() {
    o1 <- ddply(df, .(group), function(x) sum(x$val), .parallel = TRUE)
}

# no parallelisation
PLYR <- function() {
    o2 <- ddply(df, .(group), function(x) sum(x$val), .parallel = FALSE)
}

require(rbenchmark)
benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed")

      test replications elapsed relative user.self sys.self user.child sys.child
2   PLYR()            2   8.925    1.000     8.865    0.068      0.000     0.000
1 P.PLYR()            2  30.637    3.433    15.841   13.945      8.944    38.858
这里,并行版本比非并行版本快
1.752倍

Edit:在@Paul的评论之后,我刚刚使用
Sys.sleep()
实现了一个小延迟。当然,结果是显而易见的。但为了完整起见,下面是20*2 data.frame上的结果:

df <- data.frame(group=sample(letters[1:5], 20, replace=T), val=sample(20))

# parallelisation using doMC + plyr 
P.PLYR <- function() {
    o1 <- ddply(df, .(group), function(x) {
    Sys.sleep(2)
    median(x$val)
    }, .parallel = TRUE)
}

# no parallelisation
PLYR <- function() {
    o2 <- ddply(df, .(group), function(x) {
        Sys.sleep(2)
        median(x$val)
    }, .parallel = FALSE)
}

> benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed")

#       test replications elapsed relative user.self sys.self user.child sys.child
# 1 P.PLYR()            2   4.116    1.000     0.056    0.056      0.024      0.04
# 2   PLYR()            2  20.050    4.871     0.028    0.000      0.000      0.00

df完全同意@Arun和@PaulHiemestra关于为什么…?这是你问题的一部分的观点

然而,在您的情况下,您似乎可以从
parallel
软件包中获得一些好处(至少在您不使用Windows的情况下)。可能的解决方案是使用
mclappy
而不是依赖于快速分叉和共享内存的
parsply

  tm2 <- system.time({
    tm3 <- system.time({
     df7 <- matrix(unlist(mclapply(df2$var1, FUN=function(x) {x==df1$var1}, mc.cores=8)), nrow=i)
     dimnames(df7) <- list(row.names(df1), row.names(df2))
    })
  })

tm2(+1)很好的公式,我发现它很有趣!(除了blog plug:))对不起,这并不是说它是一个plug:-)只是试图提供我目前拥有的所有信息。+1是示例代码。归根结底,这一切都归结为有效地将你的问题分割成相当大的部分。一个非常简单的例子是使用一个只休眠10秒钟的函数,在那里并行运行确实很好。如果你把一个20小时的工作分成60个部分,平行化将带来重大的改进。例如,每十行像素处理一幅卫星图像比每次只给工人一个像素要有效得多,所以要明智地切片。对于plyr,切换到data.table可能会比并行运行带来更大的改进。因此,选择正确的工具也是一个问题。此外,当从R中并行运行模型时,使用六个工人可以使性能提高5.6倍,但这些运行需要几个小时。谢谢Paul。如果您注意到我实际上跟踪了两次,总共包括
makeCluster
调用,然后是一次仅用于我的执行,以便了解有多少开销。从结果来看,启动线程的开销似乎很小。我对执行过程中的开销感到困惑。在每个工作节点上都有一个R进程,开销包括向每个节点发送指令,告诉它们要做什么,并收集结果。
df <- data.frame(group=sample(letters[1:5], 20, replace=T), val=sample(20))

# parallelisation using doMC + plyr 
P.PLYR <- function() {
    o1 <- ddply(df, .(group), function(x) {
    Sys.sleep(2)
    median(x$val)
    }, .parallel = TRUE)
}

# no parallelisation
PLYR <- function() {
    o2 <- ddply(df, .(group), function(x) {
        Sys.sleep(2)
        median(x$val)
    }, .parallel = FALSE)
}

> benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed")

#       test replications elapsed relative user.self sys.self user.child sys.child
# 1 P.PLYR()            2   4.116    1.000     0.056    0.056      0.024      0.04
# 2   PLYR()            2  20.050    4.871     0.028    0.000      0.000      0.00
  tm2 <- system.time({
    tm3 <- system.time({
     df7 <- matrix(unlist(mclapply(df2$var1, FUN=function(x) {x==df1$var1}, mc.cores=8)), nrow=i)
     dimnames(df7) <- list(row.names(df1), row.names(df2))
    })
  })