我使用R的optim-
程序(使用BFGS)进行最大似然优化。
每次评估目标函数时,都会发生一对矢量矩阵乘法,其中相同的设计矩阵将与变化参数的矢量进行后复制。
如果我只是使用parallel
包在核心之间分配作业,那么每次迭代中的分发时间基本上会导致计算时间,如果与普通矩阵矢量产品相比,它甚至需要更长的并行版本。
我想要做的是在一次核心之间分配矩阵的片段,然后对片段进行乘法,因为矩阵在迭代之间不会改变。 基本上我不希望每次迭代都会分发相同的对象。
到目前为止我所做的是
nc <- detectCores()
cl <- makeCluster(rep("localhost", nc))
matprod.par <- function(cl, A, B){
idx <- splitIndices(nrow(A), length(cl))
Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
ans <- clusterApply(cl, Alist, get("%*%"), B)
do.call(rbind, ans)
}
此处,clusterApply
- 函数在核心之间分发A
,即Alist
。是否有可能在核心之间分配Alist,然后对分布式碎片进行乘法并通过clusterApply
将它们重新组合在一起?
/修改
我将Steve Weston的clusterCall方法与简单的foreach-%dopar%-approch进行了比较:
matprod.par1 <-function(Alist,B,nc){
par <- foreach(i=1:nc, .combine=rbind) %dopar%{
Alist[[i]]%*%B
}
}
以及clusterExport-approach,与clusterCall方法不同,它将所有内容复制到每个spawn。
matprod.par2 <-function(obj1,obj2,nc){
return( do.call(rbind, sfClusterApplyLB(1:nc, function(i) eval(as.name(obj1))[[i]]%*%eval(as.name(obj2)))) )
}
我将运行两个1000x1000矩阵的矩阵乘法,在具有30GB RAM的8核群集上运行100次
设置为
nr <- 1000
A <- matrix(round(rnorm(nr^2),1),nr=nr)
B <- t(A) + 4
ordinary <- A %*% B
这就是我所看到的
在y轴上,我们以秒为单位看到时间。
显然,sfCluster-Approach表现最好但如果矩阵非常大(在我的情况下它们是),则不可行。因此,即使foreach更好,使用clusterCall方法也是明智的吗?我不确定机器人是不会将所有内容复制到正确的位置?
答案 0 :(得分:1)
我会使用clusterApply将A的子矩阵分发给worker,然后使用clusterCall在这些子矩阵上重复执行操作。例如:
A <- matrix(rnorm(16), 4)
idx <- splitIndices(nrow(A), length(cl))
Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
clusterApply(cl, Alist, function(a) { subA <<- a; NULL })
matprod.par <- function(cl, B) {
do.call(rbind, clusterCall(cl, function(b) subA %*% b, B))
}
AB <- matprod.par(cl, matrix(1:16, 4))
AC <- matprod.par(cl, matrix(rnorm(16), 4))
与clusterExport不同,clusterApply可以导出变量的不同值&#34; subA&#34;对于每个集群工作者。
clusterCall函数在此上下文中非常有用,因为它允许您迭代已经分发给工作者的数据,并且您仍然可以传递&#34; B&#34;以及每项任务。
更新
首先,我应该注意到我的例子有三个假设:
&#34; foreach&#34;您提供的示例并未预先分发&#34; A&#34;,因此它无法重复使用&#34; A&#34;如果它被多次调用。 &#34; foreach&#34;和&#34; sfClusterApplyLB&#34;示例复制了所有&#34; A&#34;对于所有使用更多记忆的工人,正如你所指出的那样。
如果你打算打电话给#34; matprod.par&#34;多次,您可以使用:
matprod.par <- function(Alist, B) {
foreach(a=Alist, .combine=rbind) %dopar% {
a %*% B
}
}
避免复制所有&#34; A&#34;对所有的工人。这相当于:
matprod.par <- function(cl, Alist, B) {
do.call(rbind, clusterApply(cl, Alist, get('%*%'), B))
}
它的运行速度比foreach版本快一些,因为它的开销较小。
如果 要多次调用matprod.par,预分发时间就变得无关紧要了,并且&#34; clusterCall&#34;例子会更快,因为没有&#34; A&#34;被再次发送给工人。交叉发生的位置取决于调用matprod.par的次数,&#34; A&#34;的大小,以及工人的数量。