如何使一个对象可用于雪包中的节点以进行R并行计算

时间:2013-05-31 16:13:50

标签: r parallel-processing snow

我觉得这很难理解:

cl = makeCluster(rep("localhost", 8), "SOCK")

# This will not work, error: dat not found in the nodes
pmult = function(cl, a, x)
{
    mult = function(s) s*x
    parLapply(cl, a, mult)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

# This will work
pmult = function(cl, a, x)
{
    x
    mult = function(s) s*x
    parLapply(cl, a, mult)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

# This will work
pmult = function(cl, a, x)
{
    mult = function(s, x) s*x
    parLapply(cl, a, mult, x)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

由于参数的惰性求值,第一个函数不起作用。但什么是懒惰的评价?执行mult()时,是否需要对x进行求值?第二个是有效的,因为它迫使x被评估。现在最奇怪的事情发生在第三个函数中,什么都没做,但make mult()接收x作为额外的参数,突然一切正常!

另一件事是,如果我不想在调用parLapply()的函数内定义所有变量和函数,我该怎么办?以下肯定不会起作用:

pmult = function(cl)
{
    source("a_x_mult.r")
    parLapply(cl, a, mult, x)
}
scalars = 1:4
dat = rnorm(4)
pmult(cl, scalars, dat)

我可以将所有这些变量和函数作为参数传递:

f1 = function(i)
{
    return(rnorm(i))
}

f2 = function(y)
{
    return(f1(y)^2)
}

f3 = function(v)
{
    return(v- floor(v) + 100)
}

test = function(cl, f1, f2, f3)
{
    x = f2(15)
    parLapply(cl, x, f3)
}

test(cl, f1, f2, f3)

或者我可以使用clusterExport(),但是当有很多对象要导出时会很麻烦。还有更好的方法吗?


1 个答案:

答案 0 :(得分:5)

要理解这一点,您必须意识到存在与每个功能相关联的环境,该环境取决于功能的创建方式。在脚本中简单创建的函数与全局环境相关联,但由另一个函数创建的函数与创建函数的本地环境相关联。在您的示例中,pmult创建了mult,因此与mult相关联的环境包含正式参数clax。< / p>

第一种情况的问题是parLapplyx一无所知:它只是一个未经评估的形式论证,被序列化为mult的环境的一部分parLapply。由于x在序列化并发送给集群工作人员时未评估mult,因此当工作人员执行mult时会导致错误,因为dat不可用那个背景。换句话说,在mult评估x时,为时已晚。

第二种情况有效,因为在x序列化之前评估了mult,因此x的实际值与mult的环境一起被序列化。如果您了解闭包但不了解懒惰的参数评估,它会做您所期望的。

第三种情况有效,因为您正在为parLapply处理x。根本没有任何诡计。

我应警告您,在所有这些情况下,a正在评估parLapply并与mult的环境一起序列化。 parLapply还将a拆分为块并将这些块发送给每个工作人员,因此a环境中mult的副本完全没有必要。它不会导致错误,但可能会影响性能,因为mult会发送给每个任务对象中的worker。幸运的是,parLapply的问题要小得多,因为每个工作者只有一个任务。如果clusterApplyclusterApplyLB的任务数等于a的长度,那将是一个更糟糕的问题。

我在本书的“雪”章节中谈到了与功能和环境有关的一些问题。涉及一些微妙的问题,很容易被烧毁,有时候却没有意识到它已经发生了。

至于你的第二个问题,有各种策略可以将函数导出给工人,但有些人确实使用source来定义工人的功能,而不是使用clusterExport。请记住,source有一个local参数,用于控制解析表达式的计算位置,您可能需要指定脚本的绝对路径。最后,如果您正在使用远程集群工作程序,如果您没有分布式文件系统,则可能需要将脚本scp到工作程序。

以下是将全局环境中的所有函数导出到集群工作者的简单方法:

ex <- Filter(function(x) is.function(get(x, .GlobalEnv)), ls(.GlobalEnv))
clusterExport(cl, ex)