Parallel execution using doRSR for script containing RevoScaleR and foreach constructs
Important
This content is being retired and may not be updated in the future. The support for Machine Learning Server will end on July 1, 2022. For more information, see What's happening to Machine Learning Server?
Machine Learning Server includes the open-source foreach package in case you need to substitute the built-in parallel execution methodology of RevoScaleR with a custom implementation.
To execute code that leverages foreach, you will need a parallel backend engine, similar to NetWorkSpaces, snow, and rmpi. For integration with script leveraging RevoScaleR, you can use the doRSR package. The doRSR package is a parallel backend for RevoScaleR, built on top of rxExec, and included with all RevoScaleR distributions.
Example script using doRSR
To get started with doRSR, load the package and register the backend:
library(doRSR)
registerDoRSR()
The doRSR package uses your current compute context to determine how to run your job. In most cases, the job is run via rxExec, sequentially in the local compute context and in parallel in a distributed compute context. In the special case where you are in the local compute context and have set rxOptions(useDoParallel=TRUE)
, doRSR will pass your foreach jobs to the doParallel package for execution in parallel using multiple cores on your machine.
A simple example is this one from the foreach help file:
foreach(i=1:3) %dopar% sqrt(i)
This returns, as expected, a list containing the square roots of 1, 2, and 3:
[[1]]
[1] 1
[[2]]
[1] 1.414214
[[3]]
[1] 1.732051
Another example is what the help file reports as a “simple (and inefficient) parallel matrix multiply”:
a <- matrix(1:16, 4, 4)
b <- t(a)
foreach(b=iter(b, by='col'), .combine=cbind) %dopar%
(a %*% b)
This returns the multiplied matrix:
[,1] [,2] [,3] [,4]
[1,] 276 304 332 360
[2,] 304 336 368 400
[3,] 332 368 404 440
[4,] 360 400 440 480
Use case: simulation
In Running jobs in parallel, we introduced the simulation function playDice
to simulate a single game of dice rolling, using rxExec to play 10000 games. You can do the same thing with foreach:
z1 <- foreach(i=1:10000, .options.rsr=list(chunkSize=2000)) %dopar% playDice()
table(unlist(z1))
Loss Win
5079 4921
Although outcomes are equivalent in both approaches, the rxExec approach is several times faster because you can call it directly.
Use case: naïve parallelization of the standard R kmeans function
Also in the previous article, we created a function kmeansRSR to perform a naïve parallelization of the standard R kmeans function. We can do the same thing with foreach directly as follows:
kMeansForeach <- function(x, centers=5, iter.max=10, nstart=1)
{
numTimes <- 20
results <- foreach(i=1:numTimes) %dopar% kmeans(x=x, centers=centers, iter.max=iter.max,
nstart=nstart)
best <- 1
bestSS <- sum(results[[1]]$withinss)
for (j in 1:numTimes)
{
jSS <- sum(results[[j]]$withinss)
if (bestSS > jSS)
{
best <- j
bestSS <- jSS
}
}
results[[best]]
}
Recall that the idea was to run a specified number of kmeans fits, then find the best set of results, where “best” is the result with the lowest within-group sum of squares. We can run this function as follows:
x <- matrix(rnorm(250000), nrow = 5000, ncol = 50)
kMeansForeach(x, 10, 35, 20)