并行运行时写入全局环境

时间:2011-06-06 11:58:55

标签: r parallel-processing snowfall

我有一个data.frame的单元格,值和坐标。它驻留在全球环境中。

> head(cont.values)
   cell value   x   y
1 11117    NA -34 322
2 11118    NA -30 322
3 11119    NA -26 322
4 11120    NA -22 322
5 11121    NA -18 322
6 11122    NA -14 322

因为我的自定义函数需要几秒钟来计算单个单元格(我需要计算数万个单元格),我不想复制已经有值的单元格的计算。以下解决方案试图避免这种情况。每个单元格可以独立计算,尖叫以便并行执行。

我的函数实际上做的是检查指定的单元格编号是否有值,如果是NA,则计算它并插入它代替NA。

我可以使用应用函数系列运行我的魔术函数(结果为value)对应cell}并且在apply内,我可以读写cont.values没有问题(它在全球环境中)。

现在,我希望并行运行(使用snowfall)并且我无法从单个核心读取或写入此变量。

问题:当并行执行函数时,什么解决方案能够从工作者(核心)内部驻留在全局环境中的动态变量读取/写入。有没有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:4)

这取决于所讨论的功能是什么,当然,但我担心snowfall在那里不会有太多帮助。事实上,您必须将整个数据框导出到不同的核心(请参阅?sfExport)并仍然找到一种方法来组合它。这种方式超越了改变全球环境价值的全部目的,因为您可能希望尽可能降低内存使用率。

您可以深入了解snow的低级功能,让它变得有效。请参阅以下示例:

#Some data
Data <- data.frame(
  cell = 1:10,
  value = sample(c(100,NA),10,TRUE),
  x = 1:10,
  y = 1:10
)
# A sample function
sample.func <- function(){
    id <- which(is.na(Data$value)) # get the NA values

    # this splits up the values from the dataframe in a list
    # which will be passed to clusterApply later on.
    parts <- lapply(clusterSplit(cl,id),function(i)Data[i,c("x","y")])

    # Here happens the magic
    Data$value[id] <<-
    unlist(clusterApply(cl,parts,function(x){
        x$x+x$y
      }
    ))
}
#now we run it
require(snow)
cl <- makeCluster(c("localhost","localhost"), type = "SOCK")
sample.func()
stopCluster(cl)
> Data
   cell value  x  y
1     1   100  1  1
2     2   100  2  2
3     3     6  3  3
4     4     8  4  4
5     5    10  5  5
6     6    12  6  6
7     7   100  7  7
8     8   100  8  8
9     9    18  9  9
10   10    20 10 10

您仍然需要复制(部分)数据才能将其发送到核心。但是,当您在数据框上调用snowfall高级函数时,无论如何都会发生这种情况,因为snowfall无论如何都会使用snow的低级函数。

另外,不应忘记,如果更改数据帧中的一个值,整个数据帧也会复制到内存中。因此,当他们从群集中返回时,您将不会通过逐个添加值来赢得那么多。您可能想尝试一些不同的方法并进行一些内存分析。

答案 1 :(得分:4)

工作人员咨询价值观的中央商店模式在CRAN的rredis包中实施。我们的想法是Redis服务器维护一个键值对的存储(您的全局数据框,重新实现)。工作人员查询服务器以查看是否已计算该值(redisGet),如果不进行计算并将其存储(redisSet),以便其他工作人员可以重复使用它。工人可以是R脚本,因此很容易扩展劳动力。这是一个非常好的替代并行范例。这是一个使用“memoizing”每个结果概念的例子。我们有一个缓慢的功能(睡眠一秒钟)

fun <- function(x) { Sys.sleep(1); x }

我们写了一个'memoizer',它返回fun的变体,首先检查x的值是否已经计算过,如果是,则使用

memoize <-
    function(FUN)
{
    force(FUN) # circumvent lazy evaluation
    require(rredis)
    redisConnect()
    function(x)
    {
        key <- as.character(x)
        val <- redisGet(key)
        if (is.null(val)) {
            val <- FUN(x)
            redisSet(key, val)
        }
        val
    }
}

然后我们记住我们的功能

funmem <- memoize(fun)

然后去

> system.time(res <- funmem(10)); res
   user  system elapsed 
  0.003   0.000   1.082 
[1] 10
> system.time(res <- funmem(10)); res
   user  system elapsed 
  0.001   0.001   0.040 
[1] 10

这确实需要在R外部运行的redis服务器,但非常容易安装;请参阅rredis软件包附带的文档。

内部-R并行版本可能

library(snow)
cl <- makeCluster(c("localhost","localhost"), type = "SOCK")
clusterEvalQ(cl, { require(rredis); redisConnect() })
tasks <- sample(1:5, 100, TRUE)
system.time(res <- parSapply(cl, tasks, funmem))

答案 2 :(得分:1)

我同意Joris您需要将数据复制到其他核心。 从积极的方面来说,您不必担心核心内部NA是否在数据中。 如果您的原始data.frame被称为cont.values

nnaidx<-is.na(cont.values$value) #where is missing data originally
dfrnna<-cont.values[nnaidx,] #subset for copying to other cores
calcValForDfrRow<-function(dfrRow){return(dfrRow$x+dfrRow$y)}#or whatever pleases you
sfExport(dfrnna, calcValForDfrRow) #export what is needed to other cores
cont.values$value[nnaidx]<-sfSapply(seq(dim(dfrnna)[1]), function(i){calcValForDfrRow(dfrnna[i,])}) #sfSapply handles 'reordering', so works exactly as if you had called sapply

应该很好地工作(除非错别字)