Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Environment and scope when using parallel functions

I have the following function:

f1<-function(x){
  iih_data<-...stuff...
  ...more stuff...

  cl <- makeCluster(mc <- getOption("cl.cores", 6))
  clusterExport(cl, c("iih_data"))
  clusterEvalQ(cl, require(lme4))

  Tstar<-parCapply(cl, ystar, function(x){
     ostar=glmer(x ~ GENO + RACE + (1|GROUP), family="binomial",data=iih_data,nAGQ=1)
     fixef(ostar)[2]/sqrt(vcov(ostar)[2,2])
  })

  stopCluster(cl)

  ...more stuff...
}

But I get this error:

Error in get(name, envir = envir) : object 'iih_data' not found

I am guessing it has to do with the fact that I am trying to run parallel apply inside of a function. Can you guys help me sort this out? Thanks

like image 622
bdeonovic Avatar asked Aug 03 '13 17:08

bdeonovic


People also ask

How does mclapply work?

mclapply is a parallelized version of lapply , it returns a list of the same length as X , each element of which is the result of applying FUN to the corresponding element of X . It relies on forking and hence is not available on Windows unless mc. cores = 1 .

What is parallel programming in R?

Parallel processing (in the extreme) means that all the f# processes start simultaneously and run to completion on their own. If we have a single computer at our disposal and have to run n models, each taking s seconds, the total running time will be n*s .

Does R support parallel computing?

There are various packages in R which allow parallelization. “parallel” Package The parallel package in R can perform tasks in parallel by providing the ability to allocate cores to R. The working involves finding the number of cores in the system and allocating all of them or a subset to make a cluster.


1 Answers

As you've figured out, clusterExport looks for the specified variables in .GlobalEnv unless directed otherwise with the envir argument. But in your particular example, iih_data is being serialized along with the unnamed function that you're executing with parCapply, so the copy that you're exporting to the workers via clusterExport won't actually be used. In fact, all of the local variables that are defined in f1 before parCapply is executed will be serialized along with the unnamed worker function and sent to each of the workers.

This technique can be very useful for sending data to the workers (it's actually used by clusterExport itself), but you have to know what you're doing, otherwise it can significantly hurt your performance, especially when using clusterApply and clusterApplyLB, since they don't do the same prescheduling done by parLapply and parCapply.

Here's a simple example that demonstrates this:

library(parallel)
cl <- makePSOCKcluster(3)
f1 <- function() {
  iih_data <- 'foo'
  parLapply(cl, 1:3, function(i) iih_data)
}
f1()

You'd think that you would get an error saying "object 'iih_data' is not found" since you haven't explicitly exported it, but you don't. The odd thing is that this doesn't happen when the function is defined from the global environment, because the global environment is never serialized along with functions.

If you think that's strange, things get stranger when dealing with arguments. Consider this example:

library(parallel)
cl <- makePSOCKcluster(3)
f1 <- function(iih_data) {
  parLapply(cl, 1:3, function(i) iih_data)
}
x <- 'foo'
f1(x)

Given my previous example, you might think that this would work, but instead you get the following error:

Error in checkForRemoteErrors(val) : 
  3 nodes produced errors; first error: object 'x' not found

But why does it say "object 'x' not found" rather than "object 'iih_data' not found"? This is due to R's lazy evaluation of function arguments. The function and its associated environment is serialized and sent to the workers without ever evaluating the argument "iih_data". It's not evaluated until the unnamed worker function is executed on the workers, and that's when it discovers that "x" is not defined in the global environment of the workers.

You can fix this by changing f1 to:

f1 <- function(iih_data) {
  force(iih_data)
  parLapply(cl, 1:3, function(i) iih_data)
}

If instead of calling force you executed clusterExport(cl, 'iih_data', envir=environment()), it would work, but not because you've exported it to the workers. It would work because the argument had been forced, but in a much less efficient way, and the values copied to the global environment of the workers would still not be used. The worker function would still actually use the copy of "iih_data" that was in the local environment that was created by calling f1 that was serialized along with the unnamed worker function.

This may seem like an academic issue, but it comes up in various forms once you start to call parallel functions such as parLapply and clusterApply from inside functions in order to execute unnamed worker functions. I've been bitten many times by this kind of problem.

like image 197
Steve Weston Avatar answered Nov 08 '22 17:11

Steve Weston