3

这是我觉得很难理解的一点:

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 被评估。现在最奇怪的事情发生在第三个函数中,除了让 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(),但是当有很多对象要导出时会很麻烦。有没有更好的办法?


4

1 回答 1

5

要理解这一点,您必须意识到每个函数都有一个关联的环境,而该环境是什么取决于函数是如何创建的。在脚本中简单创建的函数与全局环境相关联,但由另一个函数创建的函数与创建函数的本地环境相关联。在您的示例中,pmult创建mult,因此与关联的环境mult包含形式参数clax

第一种情况的问题在于parLapply它不知道任何关于的内容x:它只是一个未评估的形式参数,被序列化为 by 环境的mult一部分parLapply。由于在序列化并发送到集群工作人员x时未评估,因此在工作人员执行时会导致错误,因为在该上下文中不可用。换句话说,等到评估的时候,已经太晚了。multmultdatmultx

第二种情况有效,因为在被序列化x之前进行评估mult,因此 的实际值x与 的环境一起被序列化mult。如果您知道闭包但不知道惰性参数评估,它会满足您的期望。

第三种情况有效,因为您可以parLapply处理x。根本没有什么诡计。

我应该警告您,在所有这些情况下,a正在评估(由parLapply)并与mult. parLapply也是拆分a成块并将这些块发送给每个工作人员,因此完全不需要a环境中的副本。mult它不会导致错误,但可能会损害性能,因为它会mult在每个任务对象中发送给工作人员。幸运的是,这不是问题parLapply,因为每个工人只有一个任务。如果clusterApply任务clusterApplyLB数等于a.

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

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

这是将全局环境中的所有功能导出到集群工作人员的简单方法:

ex <- Filter(function(x) is.function(get(x, .GlobalEnv)), ls(.GlobalEnv))
clusterExport(cl, ex)
于 2013-05-31T18:35:13.783 回答