[R] snow/Rmpi without MPI.spawn?

Jim Leek leek2 at llnl.gov
Thu Sep 4 17:36:21 CEST 2014


Ah, now it's working.  Thanks.  Now I just need to figure out how to get 
snow doing this...

Jim

On 09/04/2014 05:03 AM, Martin Morgan wrote:
> On 09/03/2014 10:24 PM, Leek, Jim wrote:
>> Thanks for the tips.  I'll take a look around for for loops in the 
>> morning.
>>
>> I think the example you provided worked for OpenMPI.  (The default on 
>> our machine is MPICH2, but it gave the same error about calling 
>> spawn.)  Anyway, with OpenMPI I got this:
>>
>>> # salloc -n 12 orterun -n 1 R -f spawn.R
>>> library(Rmpi)
>>> ## Recent Rmpi bug -- should be mpi.universe.size() nWorkers <- 
>>> mpi.universe.size()
>
> (the '## Recent Rmpi bug' comment should have been removed, it's a 
> holdover from when the script was written several years ago)
>
>>> nslaves = 4
>>> mpi.spawn.Rslaves(nslaves)
>
> The argument needs to be named
>
>   mpi.spawn.Rslaves(nslaves=4)
>
> otherwise R matches unnamed arguments by position, and '4' is 
> associated with the 'Rscript' argument.
>
> Martin
>
>> Reported: 2 (out of 2) daemons - 4 (out of 4) procs
>>
>> Then it hung there.  So things spawned anyway, which is progress.  
>> I'm just not sure is that expected behavior for parSupply or not.
>>
>> Jim
>>
>> -----Original Message-----
>> From: Martin Morgan [mailto:mtmorgan at fhcrc.org]
>> Sent: Wednesday, September 03, 2014 5:08 PM
>> To: Leek, Jim; r-help at r-project.org
>> Subject: Re: [R] snow/Rmpi without MPI.spawn?
>>
>> On 09/03/2014 03:25 PM, Jim Leek wrote:
>>> I'm a programmer at a high-performance computing center.  I'm not very
>>> familiar with R, but I have used MPI from C, C++, and Python. I have
>>> to run an R code provided by a guy who knows R, but not MPI. So, this
>>> fellow used the R snow library to parallelize his R code
>>> (theoretically, I'm not actually sure what he did.)  I need to get
>>> this code running on our machines.
>>>
>>> However, Rmpi and snow seem to require mpi spawn, which our computing
>>> center doesn't support.  I even tried building Rmpi with MPICH1
>>> instead of 2, because Rmpi has that option, but it still tries to 
>>> use spawn.
>>>
>>> I can launch plenty of processes, but I have to launch them all at
>>> once at the beginning. Is there any way to convince Rmpi to just use
>>> those processes rather than trying to spawn its own?  I haven't found
>>> any documentation on this issue, although I would've thought it 
>>> would be quite common.
>>
>> This script
>>
>> spawn.R
>> =======
>> # salloc -n 12 orterun -n 1 R -f spawn.R
>> library(Rmpi)
>> ## Recent Rmpi bug -- should be mpi.universe.size() nWorkers <- 
>> mpi.universe.size()
>> mpi.spawn.Rslaves(nslaves=nWorkers)
>> mpiRank <- function(i)
>>     c(i=i, rank=mpi.comm.rank())
>> mpi.parSapply(seq_len(2*nWorkers), mpiRank)
>> mpi.close.Rslaves()
>> mpi.quit()
>>
>> can be run like the comment suggests
>>
>>      salloc -n 12 orterun -n 1 R -f spawn.R
>>
>> uses slurm (or whatever job manager) to allocate resources for 12 
>> tasks and spawn within that allocation. Maybe that's 'good enough' -- 
>> spawning within the assigned allocation? Likely this requires minimal 
>> modification of the current code.
>>
>> More extensive is to revise the manager/worker-style code to 
>> something more like single instruction, multiple data
>>
>>
>> simd.R
>> ======
>> ## salloc -n 4 orterun R --slave -f simd.R
>> sink("/dev/null") # don't capture output -- more care needed here
>> library(Rmpi)
>>
>> TAGS = list(FROM_WORKER=1L)
>> .comm = 0L
>>
>> ## shared `work', here just determine rank and host
>> work = c(rank=mpi.comm.rank(.comm),
>>            host=system("hostname", intern=TRUE))
>>
>> if (mpi.comm.rank(.comm) == 0) {
>>       ## manager
>>       mpi.barrier(.comm)
>>       nWorkers = mpi.comm.size(.comm)
>>       res = list(nWorkers)
>>       for (i in seq_len(nWorkers - 1L)) {
>>           res[[i]] <- mpi.recv.Robj(mpi.any.source(), TAGS$FROM_WORKER,
>>                                     comm=.comm)
>>       }
>>       res[[nWorkers]] = work
>>       sink() # start capturing output
>>       print(do.call(rbind, res))
>> } else {
>>       ## worker
>>       mpi.barrier(.comm)
>>       mpi.send.Robj(work, 0L, TAGS$FROM_WORKER, comm=.comm)
>> }
>> mpi.quit()
>>
>> but this likely requires some serious code revision; if going this 
>> route then
>> http://r-pbd.org/ might be helpful (and from a similar HPC environment).
>>
>> It's always worth asking whether the code is written to be efficient 
>> in R -- a
>> typical 'mistake' is to write R-level explicit 'for' loops that
>> "copy-and-append" results, along the lines of
>>
>>      len <- 100000
>>      result <- NULL
>>      for (i in seq_len(len))
>>          ## some complicated calculation, then...
>>          result <- c(result, sqrt(i))
>>
>> whereas it's much better to "pre-allocate and fill"
>>
>>       result <- integer(len)
>>       for (i in seq_len(len))
>>           result[[i]] = sqrt(i)
>>
>> or
>>
>>       lapply(seq_len(len), sqrt)
>>
>> and very much better still to 'vectorize'
>>
>>       result <- sqrt(seq_len(len))
>>
>> (timing for me are about 1 minute for "copy-and-append", .2 s for 
>> "pre-allocate
>> and fill", and .002s for "vectorize").
>>
>> Pushing back on the guy providing the code (grep for "for" loops, and 
>> look for
>> that copy-and-append pattern) might save you from having to use parallel
>> evaluation at all.
>>
>> Martin
>>
>>>
>>> Thanks,
>>> Jim
>>>
>>> ______________________________________________
>>> R-help at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-help
>>> PLEASE do read the posting guide 
>>> http://www.R-project.org/posting-guide.html
>>> and provide commented, minimal, self-contained, reproducible code.
>>
>>
>
>



More information about the R-help mailing list