Introduction
R is designed to only use one cpu (or core) when running tasks.
However, a computer may have more than one core that can be used to run
tasks. The use of more than one core is known as parallel computing in
R. The goal of this tutorial is to provide the basics of using the
parallel
package and utilizing more cores in a
computer.
For more information about parallel processing in R, visit the
following sites:
Background
The parallel
package allows you to use multiple cpus at
the same time to complete repetitive tasks. For example, if you have to
run a simulation study with 100 data sets, you can task 4 cpus to
analyze 25 data sets at the same time. This ideally will take a forth of
the time it will take 1 cpu to complete 100 data sets.
The only caveaut with using the parallel
package is that
each process needs to be independent of all processes. If you are
running a for loop that depends on the previous iteration, the cpu
cannot process the task because it has missing information. Therefore,
you will need identify the tasks that are not dependent of one another
and parallelize it.
With the parallel package you can use two family of functions:
mc*()
and par*()
functions. The main
difference between these functions is that mc*()
uses a
forking method to parallelize your code, and the par*()
uses a socket method to parallelize your code. Both methods has their
strengths and weaknesses. When using the HPCC cluster, I recommend using
the mc*()
functions when possible. There are brief
descriptions for each method below.
Forking
The forking method is implemented when using the mc*()
functions. Below is a list of pros and cons:
Pros:
Easier to implement
Your environment is copied to the cpu
Has the potential to use less resources
Wraps USER function with the try()
function
Cons:
Only works on POSIX systems (Mac or Linux)
Does not work with multiple nodes
May cause cross-contamination because the environment may
change
Generally speaking, R copies its environment to a cpu and runs the
process.
Socket
The socket method is implemented when using the par*()
functions. Below is a list of pros and cons:
Pros:
Works on any system (Windows, Mac, and Linux)
Each process is unique
No cross-contamination
Can be used when multiple nodes are involved
Cons:
The entire environment (including loaded functions) must be
specified
More resource intensive
try()
function is not implemented, when one cpu
fails, the entire cluster fails
Generally speaking, R creates a new environment on each cpu.
Where to parallelize?
Parallel computing requires repetitive tasks to be independent of
each other. The results from one task must not be used as input for
another task. Therefore, identify blocks of code where repetition is
involved. This can be either for
loops or
*apply()
functions.
Other locations to parallelize your code are bottlenecks. Having
multiple cores processing bottlenecks helps speed up your code. For more
information about bottlenecks or code efficiency, visit the Advanced R
website: adv-r.hadley.nz.
Identifying the location to parallelize may be challenging due to so
many options. My advice is to try many things and see what provides the
best result.
Simulation Example
To demonstrate how to use the parallel
package in R, we
conduct a simple simulation study showing how the estimates from the
ordinary least squares estimator leads to unbiased results.
\[
Y \sim N(\beta_0+X_1\beta_1+X_4\beta_5+X_3\beta_3,\sigma^2)
\]
Simulation Functions
The function below generates data from the model above and returns a
data frame.
data_sim <- function(seed, nobs, beta, sigma, xmeans, xsigs){
set.seed(seed)
xrn <- rmvnorm(nobs, mean = xmeans, sigma = xsigs)
xped <- cbind(rep(1,nobs),xrn)
y <- xped %*% beta + rnorm(nobs ,0, sigma)
df <- data.frame(x=xrn, y=y)
return(df)
}
The function needs the following arguments:
seed
: the value to set for the random number
generator
nobs
: number of observations
beta
: a vector specifying the true values for the
regression coefficients (\(\beta_0\),
\(\beta_1\), \(\beta_2\), \(\beta_3\))
sigma
: the variance for the model above
xmeans
: a vector of means used to generate the values
for \(X_1\), \(X_2\), and \(X_3\)
xsigs
: a matrix for the covariance for \(X_1\), \(X_2\), and \(X_3\)
The function below takes the data generated from the
data_sim()
and fits a linear regression model. The function
wraps around the lm()
function and returns estimated
regression coefficients.
parallel_lm <- function(data){
lm_res <- lm(y ~ x.1 + x.2 + x.3, data = data)
return(coef(lm_res))
}
Loading R Packages
For this tutorial, you will need the parallel
and
mvtnorm
packages.
library(parallel)
library(mvtnorm)
Setting Parameters
N <- 10000
nobs <- 200
beta <- c(5, 4, -5, -3)
xmeans <- c(-2, 0, 2)
xsigs <-diag(rep(1, 3))
sig <- 3
Simulating Data
start <- Sys.time()
parallel_data <- lapply(c(1:N), data_sim,
nobs = nobs, beta = beta, sigma = sig, xmeans = xmeans, xsigs = xsigs)
Sys.time()-start
Time difference of 2.903658 secs
Parallelization
Detecting Cores
The parallel
package has a function that can detect the
number of cores available on your computer. Use the
detectCores()
function to find how many cpus your computer
has.
detectCores()
[1] 8
For this example, I will use 8 cpus. You can change the number of
cpus based on your computer.
ncores <- 8
Using lapply()
Before we parallelize, use the lapply()
function to run
the simulation on one cpu.
start <- Sys.time()
lapply_results <- lapply(parallel_data, parallel_lm)
(lapply_time <- Sys.time()-start)
Time difference of 5.015617 secs
Using mclapply()
The easiest way to parallelize is to use the mclapply()
function. All you need to do is change the lapply()
function to mclapply()
and add the argument
mc.cores = ncores
as the last argument. The argument
mc.cores
specifies the number of cores for
parallelization.
start <- Sys.time()
mclapply_results <- mclapply(parallel_data, parallel_lm, mc.cores = ncores)
(mclapply_time <- Sys.time()-start)
Time difference of 1.710228 secs
Using parLapply()
Functions
Using parLapply()
function requires setting up the
cluster. There are two functions needed to set up the cluster:
makeCluster()
and clusterExport()
. Once your
analysis is finished, you will need to close the cluster using the
stopCluster()
function.
makeCluster()
The makeCluster()
function sets up the cluster in your
computer. You only need to specify the number of cores to use
(ncores
) and store it in an R object (cl
)
clusterExport()
The clusterExport()
function is needed to export
user-created functions, package functions, or data to the cluster. You
will need to specify the cluster (cl
) to export the
information and the name of the functions or data in a character
vector.
parLapply()
The parLapply()
is the parallel computing component. It
is used the same way as the lapply()
function. The only
difference is that you will need to specify the cluster
(cl
) as the first argument. The second argument goes as the
lapply()
function.
stopCluster()
The stopCluster()
function closes the cluster down.
Implementation
The code below implements the parLapply()
function:
start <- Sys.time()
cl <- makeCluster(ncores)
clusterExport(cl, c("parallel_lm"))
parLapply_results<-parLapply(cl,parallel_data,parallel_lm)
stopCluster(cl)
(parLapply_time <- Sys.time()-start)
Time difference of 2.937489 secs
Results
Since all the results are in a list, we will first convert them into
a matrix:
lapply_mat <- matrix(unlist(lapply_results), nrow = 4)
mclapply_mat <- matrix(unlist(mclapply_results), nrow = 4)
parLapply_mat <- matrix(unlist(parLapply_results), nrow = 4)
Use the rowMeans()
on all objects to show each method
provides consistent estimates of the regression parameters.
rowMeans(lapply_mat)
[1] 4.999838 4.000874 -4.999656
[4] -2.996343
rowMeans(mclapply_mat)
[1] 4.999838 4.000874 -4.999656
[4] -2.996343
rowMeans(parLapply_mat)
[1] 4.999838 4.000874 -4.999656
[4] -2.996343
Notes
Speed Gains
When parallelizing your code, the speed gains may not be noticeable.
When using the parallel
package, R assigns elements of a
list or vector into different cores, known as overhead, which takes
time. The time it takes to load the data may be longer than processing
the data. Therefore, it may not be faster or take longer than using the
*apply()
functions. It may be best to use one core or split
up the data on your own. However, when processing your data takes much
longer than your overhead time, the speed gains are noticeable.
Hyper-threading
Certain computer processors have cores that are multi-threaded
(hyper-threading). This means the computer views 1 physical core as 2
logical cores. While you certainly specify double the number of physical
cores for your cluster, the speed gains is not equivalent as using
proper physical cores. In my experience, it is not worth using the
logical cores and just specify the number of physical cores in your
computer.
LS0tCnRpdGxlOiAiUGFyYWxsZWwgQ29tcHV0aW5nIGluIFIgd2l0aCBgcGFyYWxsZWxgIgphdXRob3I6ICJJc2FhYyBRdWludGFuaWxsYSBTYWxpbmFzIgpkZXNjcmlwdGlvbjogVHV0b3JpYWwgZm9yIHBhcmFsbGVsIHByb2Nlc3NpbmcgdXNpbmcgdGhlIFIgcGFja2FnZSBwYXJhbGxlbC4KZGF0ZTogIjEyLTE5LTIwMjAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gVFJVRSkKYGBgCgojIEludHJvZHVjdGlvbgoKUiBpcyBkZXNpZ25lZCB0byBvbmx5IHVzZSBvbmUgY3B1IChvciBjb3JlKSB3aGVuIHJ1bm5pbmcgdGFza3MuIEhvd2V2ZXIsIGEgY29tcHV0ZXIgbWF5IGhhdmUgbW9yZSB0aGFuIG9uZSBjb3JlIHRoYXQgY2FuIGJlIHVzZWQgdG8gcnVuIHRhc2tzLiBUaGUgdXNlIG9mIG1vcmUgdGhhbiBvbmUgY29yZSBpcyBrbm93biBhcyBwYXJhbGxlbCBjb21wdXRpbmcgaW4gUi4gVGhlIGdvYWwgb2YgdGhpcyB0dXRvcmlhbCBpcyB0byBwcm92aWRlIHRoZSBiYXNpY3Mgb2YgdXNpbmcgdGhlIGBwYXJhbGxlbGAgcGFja2FnZSBhbmQgdXRpbGl6aW5nIG1vcmUgY29yZXMgaW4gYSBjb21wdXRlci4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHBhcmFsbGVsIHByb2Nlc3NpbmcgaW4gUiwgdmlzaXQgdGhlIGZvbGxvd2luZyBzaXRlczoKCi0gICA8aHR0cHM6Ly9uY2Vhcy5naXRodWIuaW8vb3NzLWxlc3NvbnMvcGFyYWxsZWwtY29tcHV0aW5nLWluLXIvcGFyYWxsZWwtY29tcHV0aW5nLWluLXIuaHRtbD4KLSAgIDxodHRwczovL2RlcHQuc3RhdC5sc2EudW1pY2guZWR1L35qZXJyaWNrL2NvdXJzZXMvc3RhdDcwMS9ub3Rlcy9wYXJhbGxlbC5odG1sPgotICAgPGh0dHBzOi8vcHN1LXBzeWNob2xvZ3kuZ2l0aHViLmlvL3ItYm9vdGNhbXAtMjAxOC90YWxrcy9wYXJhbGxlbF9yLmh0bWw+CgojIEJhY2tncm91bmQKClRoZSBgcGFyYWxsZWxgIHBhY2thZ2UgYWxsb3dzIHlvdSB0byB1c2UgbXVsdGlwbGUgY3B1cyBhdCB0aGUgc2FtZSB0aW1lIHRvIGNvbXBsZXRlIHJlcGV0aXRpdmUgdGFza3MuIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSB0byBydW4gYSBzaW11bGF0aW9uIHN0dWR5IHdpdGggMTAwIGRhdGEgc2V0cywgeW91IGNhbiB0YXNrIDQgY3B1cyB0byBhbmFseXplIDI1IGRhdGEgc2V0cyBhdCB0aGUgc2FtZSB0aW1lLiBUaGlzIGlkZWFsbHkgd2lsbCB0YWtlIGEgZm9ydGggb2YgdGhlIHRpbWUgaXQgd2lsbCB0YWtlIDEgY3B1IHRvIGNvbXBsZXRlIDEwMCBkYXRhIHNldHMuCgpUaGUgb25seSBjYXZlYXV0IHdpdGggdXNpbmcgdGhlIGBwYXJhbGxlbGAgcGFja2FnZSBpcyB0aGF0IGVhY2ggcHJvY2VzcyBuZWVkcyB0byBiZSBpbmRlcGVuZGVudCBvZiBhbGwgcHJvY2Vzc2VzLiBJZiB5b3UgYXJlIHJ1bm5pbmcgYSBmb3IgbG9vcCB0aGF0IGRlcGVuZHMgb24gdGhlIHByZXZpb3VzIGl0ZXJhdGlvbiwgdGhlIGNwdSBjYW5ub3QgcHJvY2VzcyB0aGUgdGFzayBiZWNhdXNlIGl0IGhhcyBtaXNzaW5nIGluZm9ybWF0aW9uLiBUaGVyZWZvcmUsIHlvdSB3aWxsIG5lZWQgaWRlbnRpZnkgdGhlIHRhc2tzIHRoYXQgYXJlIG5vdCBkZXBlbmRlbnQgb2Ygb25lIGFub3RoZXIgYW5kIHBhcmFsbGVsaXplIGl0LgoKV2l0aCB0aGUgcGFyYWxsZWwgcGFja2FnZSB5b3UgY2FuIHVzZSB0d28gZmFtaWx5IG9mIGZ1bmN0aW9uczogYG1jKigpYCBhbmQgYHBhciooKWAgZnVuY3Rpb25zLiBUaGUgbWFpbiBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgZnVuY3Rpb25zIGlzIHRoYXQgYG1jKigpYCB1c2VzIGEgZm9ya2luZyBtZXRob2QgdG8gcGFyYWxsZWxpemUgeW91ciBjb2RlLCBhbmQgdGhlIGBwYXIqKClgIHVzZXMgYSBzb2NrZXQgbWV0aG9kIHRvIHBhcmFsbGVsaXplIHlvdXIgY29kZS4gQm90aCBtZXRob2RzIGhhcyB0aGVpciBzdHJlbmd0aHMgYW5kIHdlYWtuZXNzZXMuIFdoZW4gdXNpbmcgdGhlIEhQQ0MgY2x1c3RlciwgSSByZWNvbW1lbmQgdXNpbmcgdGhlIGBtYyooKWAgZnVuY3Rpb25zIHdoZW4gcG9zc2libGUuIFRoZXJlIGFyZSBicmllZiBkZXNjcmlwdGlvbnMgZm9yIGVhY2ggbWV0aG9kIGJlbG93LgoKIyMgRm9ya2luZwoKVGhlIGZvcmtpbmcgbWV0aG9kIGlzIGltcGxlbWVudGVkIHdoZW4gdXNpbmcgdGhlIGBtYyooKWAgZnVuY3Rpb25zLiBCZWxvdyBpcyBhIGxpc3Qgb2YgcHJvcyBhbmQgY29uczoKCiMjIyBQcm9zOgoKLSAgIEVhc2llciB0byBpbXBsZW1lbnQKCi0gICBZb3VyIGVudmlyb25tZW50IGlzIGNvcGllZCB0byB0aGUgY3B1CgotICAgSGFzIHRoZSBwb3RlbnRpYWwgdG8gdXNlIGxlc3MgcmVzb3VyY2VzCgotICAgV3JhcHMgVVNFUiBmdW5jdGlvbiB3aXRoIHRoZSBgdHJ5KClgIGZ1bmN0aW9uCgojIyMgQ29uczoKCi0gICBPbmx5IHdvcmtzIG9uIFBPU0lYIHN5c3RlbXMgKE1hYyBvciBMaW51eCkKCi0gICBEb2VzIG5vdCB3b3JrIHdpdGggbXVsdGlwbGUgbm9kZXNbXjFdCgotICAgTWF5IGNhdXNlIGNyb3NzLWNvbnRhbWluYXRpb24gYmVjYXVzZSB0aGUgZW52aXJvbm1lbnQgbWF5IGNoYW5nZVteMl0KClteMV06IFlvdSBjYW4gdGhpbmsgb2YgYXMgbm9kZXMgYXMgdGhlIHByb2Nlc3Nvci4gRWFjaCBwcm9jZXNzb3IgaGFzIGEgc2V0IG51bWJlciBvZiBjcHVzLCBpZiB5b3UgbmVlZCBtb3JlIGNwdXMsIHlvdSB3aWxsIG5lZWQgdG8gdXNlIGFub3RoZXIgbm9kZS4KClteMl06IE1vc3RseSBvY2N1cnMgd2l0aCByYW5kb20gbnVtYmVyIGdlbmVyYXRpb247IGhvd2V2ZXIsIHRoaXMgaXMgZ2VuZXJhbGx5IG5vdCBhIHByb2JsZW0uCgpHZW5lcmFsbHkgc3BlYWtpbmcsIFIgY29waWVzIGl0cyBlbnZpcm9ubWVudCB0byBhIGNwdSBhbmQgcnVucyB0aGUgcHJvY2Vzcy4KCiMjIFNvY2tldAoKVGhlIHNvY2tldCBtZXRob2QgaXMgaW1wbGVtZW50ZWQgd2hlbiB1c2luZyB0aGUgYHBhciooKWAgZnVuY3Rpb25zLiBCZWxvdyBpcyBhIGxpc3Qgb2YgcHJvcyBhbmQgY29uczoKCiMjIyBQcm9zOgoKLSAgIFdvcmtzIG9uIGFueSBzeXN0ZW0gKFdpbmRvd3MsIE1hYywgYW5kIExpbnV4KQoKLSAgIEVhY2ggcHJvY2VzcyBpcyB1bmlxdWUKCi0gICBObyBjcm9zcy1jb250YW1pbmF0aW9uCgotICAgQ2FuIGJlIHVzZWQgd2hlbiBtdWx0aXBsZSBub2RlcyBhcmUgaW52b2x2ZWQKCiMjIyBDb25zOgoKLSAgIFRoZSBlbnRpcmUgZW52aXJvbm1lbnQgKGluY2x1ZGluZyBsb2FkZWQgZnVuY3Rpb25zKSBtdXN0IGJlIHNwZWNpZmllZAoKLSAgIE1vcmUgcmVzb3VyY2UgaW50ZW5zaXZlCgotICAgYHRyeSgpYCBmdW5jdGlvbiBpcyBub3QgaW1wbGVtZW50ZWQsIHdoZW4gb25lIGNwdSBmYWlscywgdGhlIGVudGlyZSBjbHVzdGVyIGZhaWxzCgpHZW5lcmFsbHkgc3BlYWtpbmcsIFIgY3JlYXRlcyBhIG5ldyBlbnZpcm9ubWVudCBvbiBlYWNoIGNwdS4KCiMjIFdoZXJlIHRvIHBhcmFsbGVsaXplPwoKUGFyYWxsZWwgY29tcHV0aW5nIHJlcXVpcmVzIHJlcGV0aXRpdmUgdGFza3MgdG8gYmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4gVGhlIHJlc3VsdHMgZnJvbSBvbmUgdGFzayBtdXN0IG5vdCBiZSB1c2VkIGFzIGlucHV0IGZvciBhbm90aGVyIHRhc2suIFRoZXJlZm9yZSwgaWRlbnRpZnkgYmxvY2tzIG9mIGNvZGUgd2hlcmUgcmVwZXRpdGlvbiBpcyBpbnZvbHZlZC4gVGhpcyBjYW4gYmUgZWl0aGVyIGBmb3JgIGxvb3BzIG9yIGAqYXBwbHkoKWAgZnVuY3Rpb25zLgoKT3RoZXIgbG9jYXRpb25zIHRvIHBhcmFsbGVsaXplIHlvdXIgY29kZSBhcmUgYm90dGxlbmVja3MuIEhhdmluZyBtdWx0aXBsZSBjb3JlcyBwcm9jZXNzaW5nIGJvdHRsZW5lY2tzIGhlbHBzIHNwZWVkIHVwIHlvdXIgY29kZS4gRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgYm90dGxlbmVja3Mgb3IgY29kZSBlZmZpY2llbmN5LCB2aXNpdCB0aGUgQWR2YW5jZWQgUiB3ZWJzaXRlOiBbYWR2LXIuaGFkbGV5Lm56XShodHRwczovL2Fkdi1yLmhhZGxleS5uei8pLgoKSWRlbnRpZnlpbmcgdGhlIGxvY2F0aW9uIHRvIHBhcmFsbGVsaXplIG1heSBiZSBjaGFsbGVuZ2luZyBkdWUgdG8gc28gbWFueSBvcHRpb25zLiBNeSBhZHZpY2UgaXMgdG8gdHJ5IG1hbnkgdGhpbmdzIGFuZCBzZWUgd2hhdCBwcm92aWRlcyB0aGUgYmVzdCByZXN1bHQuCgojIFNpbXVsYXRpb24gRXhhbXBsZQoKVG8gZGVtb25zdHJhdGUgaG93IHRvIHVzZSB0aGUgYHBhcmFsbGVsYCBwYWNrYWdlIGluIFIsIHdlIGNvbmR1Y3QgYSBzaW1wbGUgc2ltdWxhdGlvbiBzdHVkeSBzaG93aW5nIGhvdyB0aGUgZXN0aW1hdGVzIGZyb20gdGhlIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgZXN0aW1hdG9yIGxlYWRzIHRvIHVuYmlhc2VkIHJlc3VsdHMuCgokJApZIFxzaW0gTihcYmV0YV8wK1hfMVxiZXRhXzErWF80XGJldGFfNStYXzNcYmV0YV8zLFxzaWdtYV4yKQokJAoKIyMgU2ltdWxhdGlvbiBGdW5jdGlvbnMKClRoZSBmdW5jdGlvbiBiZWxvdyBnZW5lcmF0ZXMgZGF0YSBmcm9tIHRoZSBtb2RlbCBhYm92ZSBhbmQgcmV0dXJucyBhIGRhdGEgZnJhbWUuCgpgYGB7cn0KZGF0YV9zaW0gPC0gZnVuY3Rpb24oc2VlZCwgbm9icywgYmV0YSwgc2lnbWEsIHhtZWFucywgeHNpZ3MpewogIHNldC5zZWVkKHNlZWQpCiAgeHJuIDwtIHJtdm5vcm0obm9icywgbWVhbiA9IHhtZWFucywgc2lnbWEgPSB4c2lncykKICB4cGVkIDwtIGNiaW5kKHJlcCgxLG5vYnMpLHhybikKICB5IDwtIHhwZWQgJSolIGJldGEgKyBybm9ybShub2JzICwwLCBzaWdtYSkKICBkZiA8LSBkYXRhLmZyYW1lKHg9eHJuLCB5PXkpCiAgcmV0dXJuKGRmKQp9CmBgYAoKVGhlIGZ1bmN0aW9uIG5lZWRzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOgoKLSAgIGBzZWVkYDogdGhlIHZhbHVlIHRvIHNldCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yCi0gICBgbm9ic2A6IG51bWJlciBvZiBvYnNlcnZhdGlvbnMKLSAgIGBiZXRhYDogYSB2ZWN0b3Igc3BlY2lmeWluZyB0aGUgdHJ1ZSB2YWx1ZXMgZm9yIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAoJFxiZXRhXzAkLCAkXGJldGFfMSQsICRcYmV0YV8yJCwgJFxiZXRhXzMkKQotICAgYHNpZ21hYDogdGhlIHZhcmlhbmNlIGZvciB0aGUgbW9kZWwgYWJvdmUKLSAgIGB4bWVhbnNgOiBhIHZlY3RvciBvZiBtZWFucyB1c2VkIHRvIGdlbmVyYXRlIHRoZSB2YWx1ZXMgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCi0gICBgeHNpZ3NgOiBhIG1hdHJpeCBmb3IgdGhlIGNvdmFyaWFuY2UgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCgpUaGUgZnVuY3Rpb24gYmVsb3cgdGFrZXMgdGhlIGRhdGEgZ2VuZXJhdGVkIGZyb20gdGhlIGBkYXRhX3NpbSgpYCBhbmQgZml0cyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUaGUgZnVuY3Rpb24gd3JhcHMgYXJvdW5kIHRoZSBgbG0oKWAgZnVuY3Rpb24gYW5kIHJldHVybnMgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLgoKYGBge3J9CnBhcmFsbGVsX2xtIDwtIGZ1bmN0aW9uKGRhdGEpewogIGxtX3JlcyA8LSBsbSh5IH4geC4xICsgeC4yICsgeC4zLCBkYXRhID0gZGF0YSkKICByZXR1cm4oY29lZihsbV9yZXMpKQp9CmBgYAoKIyMjIExvYWRpbmcgUiBQYWNrYWdlcwoKRm9yIHRoaXMgdHV0b3JpYWwsIHlvdSB3aWxsIG5lZWQgdGhlIGBwYXJhbGxlbGAgYW5kIGBtdnRub3JtYCBwYWNrYWdlcy4KCmBgYHtyfQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KG12dG5vcm0pCmBgYAoKIyMjIFNldHRpbmcgUGFyYW1ldGVycwoKYGBge3J9Ck4gPC0gMTAwMDAKbm9icyA8LSAyMDAKYmV0YSA8LSBjKDUsIDQsIC01LCAtMykKeG1lYW5zIDwtIGMoLTIsIDAsIDIpCnhzaWdzIDwtZGlhZyhyZXAoMSwgMykpCnNpZyA8LSAzCmBgYAoKIyMjIFNpbXVsYXRpbmcgRGF0YQoKYGBge3J9CnN0YXJ0IDwtIFN5cy50aW1lKCkKcGFyYWxsZWxfZGF0YSA8LSBsYXBwbHkoYygxOk4pLCBkYXRhX3NpbSwKICAgICAgICAgICAgICAgICAgICAgICAgICBub2JzID0gbm9icywgYmV0YSA9IGJldGEsIHNpZ21hID0gc2lnLCB4bWVhbnMgPSB4bWVhbnMsIHhzaWdzID0geHNpZ3MpClN5cy50aW1lKCktc3RhcnQKYGBgCgojIFBhcmFsbGVsaXphdGlvbgoKIyMgRGV0ZWN0aW5nIENvcmVzCgpUaGUgYHBhcmFsbGVsYCBwYWNrYWdlIGhhcyBhIGZ1bmN0aW9uIHRoYXQgY2FuIGRldGVjdCB0aGUgbnVtYmVyIG9mIGNvcmVzIGF2YWlsYWJsZSBvbiB5b3VyIGNvbXB1dGVyLiBVc2UgdGhlIGBkZXRlY3RDb3JlcygpYCBmdW5jdGlvbiB0byBmaW5kIGhvdyBtYW55IGNwdXMgeW91ciBjb21wdXRlciBoYXNbXjNdLgoKW14zXTogVGhlIGZ1bmN0aW9uIHByb3ZpZGVzIHRoZSBudW1iZXIgb2YgbG9naWNhbCBjb3JlcyBhbmQgbm90IHBoeXNpY2FsIGNvcmVzLiBUaGlzIGlzIGR1ZSB0byBtdWx0aXRocmVhZGluZy4KCmBgYHtyfQpkZXRlY3RDb3JlcygpCmBgYAoKRm9yIHRoaXMgZXhhbXBsZSwgSSB3aWxsIHVzZSA4IGNwdXMuIFlvdSBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2YgY3B1cyBiYXNlZCBvbiB5b3VyIGNvbXB1dGVyLgoKYGBge3J9Cm5jb3JlcyA8LSA4CmBgYAoKIyMgVXNpbmcgYGxhcHBseSgpYAoKQmVmb3JlIHdlIHBhcmFsbGVsaXplLCB1c2UgdGhlIGBsYXBwbHkoKWAgZnVuY3Rpb24gdG8gcnVuIHRoZSBzaW11bGF0aW9uIG9uIG9uZSBjcHUuCgpgYGB7cn0Kc3RhcnQgPC0gU3lzLnRpbWUoKQpsYXBwbHlfcmVzdWx0cyA8LSBsYXBwbHkocGFyYWxsZWxfZGF0YSwgcGFyYWxsZWxfbG0pCihsYXBwbHlfdGltZSA8LSBTeXMudGltZSgpLXN0YXJ0KQpgYGAKCiMjIFVzaW5nIGBtY2xhcHBseSgpYAoKVGhlIGVhc2llc3Qgd2F5IHRvIHBhcmFsbGVsaXplIGlzIHRvIHVzZSB0aGUgYG1jbGFwcGx5KClgIGZ1bmN0aW9uLiBBbGwgeW91IG5lZWQgdG8gZG8gaXMgY2hhbmdlIHRoZSBgbGFwcGx5KClgIGZ1bmN0aW9uIHRvIGBtY2xhcHBseSgpYCBhbmQgYWRkIHRoZSBhcmd1bWVudCBgbWMuY29yZXMgPSAgbmNvcmVzYCBhcyB0aGUgbGFzdCBhcmd1bWVudC4gVGhlIGFyZ3VtZW50IGBtYy5jb3Jlc2Agc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgY29yZXMgZm9yIHBhcmFsbGVsaXphdGlvbi4KCmBgYHtyfQpzdGFydCA8LSBTeXMudGltZSgpCm1jbGFwcGx5X3Jlc3VsdHMgPC0gbWNsYXBwbHkocGFyYWxsZWxfZGF0YSwgcGFyYWxsZWxfbG0sIG1jLmNvcmVzID0gbmNvcmVzKQoobWNsYXBwbHlfdGltZSA8LSBTeXMudGltZSgpLXN0YXJ0KQpgYGAKCiMjIFVzaW5nIGBwYXJMYXBwbHkoKWAKCiMjIyBGdW5jdGlvbnMKClVzaW5nIGBwYXJMYXBwbHkoKWAgZnVuY3Rpb24gcmVxdWlyZXMgc2V0dGluZyB1cCB0aGUgY2x1c3Rlci4gVGhlcmUgYXJlIHR3byBmdW5jdGlvbnMgbmVlZGVkIHRvIHNldCB1cCB0aGUgY2x1c3RlcjogYG1ha2VDbHVzdGVyKClgIGFuZCBgY2x1c3RlckV4cG9ydCgpYC4gT25jZSB5b3VyIGFuYWx5c2lzIGlzIGZpbmlzaGVkLCB5b3Ugd2lsbCBuZWVkIHRvIGNsb3NlIHRoZSBjbHVzdGVyIHVzaW5nIHRoZSBgc3RvcENsdXN0ZXIoKWAgZnVuY3Rpb24uCgojIyMjIGBtYWtlQ2x1c3RlcigpYAoKVGhlIGBtYWtlQ2x1c3RlcigpYCBmdW5jdGlvbiBzZXRzIHVwIHRoZSBjbHVzdGVyIGluIHlvdXIgY29tcHV0ZXIuIFlvdSBvbmx5IG5lZWQgdG8gc3BlY2lmeSB0aGUgbnVtYmVyIG9mIGNvcmVzIHRvIHVzZSAoYG5jb3Jlc2ApIGFuZCBzdG9yZSBpdCBpbiBhbiBSIG9iamVjdCAoYGNsYCkKCiMjIyMgYGNsdXN0ZXJFeHBvcnQoKWAKClRoZSBgY2x1c3RlckV4cG9ydCgpYCBmdW5jdGlvbiBpcyBuZWVkZWQgdG8gZXhwb3J0IHVzZXItY3JlYXRlZCBmdW5jdGlvbnMsIHBhY2thZ2UgZnVuY3Rpb25zLCBvciBkYXRhIHRvIHRoZSBjbHVzdGVyLiBZb3Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgdGhlIGNsdXN0ZXIgKGBjbGApIHRvIGV4cG9ydCB0aGUgaW5mb3JtYXRpb24gYW5kIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbnMgb3IgZGF0YSBpbiBhIGNoYXJhY3RlciB2ZWN0b3IuCgojIyMjIGBwYXJMYXBwbHkoKWAKClRoZSBgcGFyTGFwcGx5KClgIGlzIHRoZSBwYXJhbGxlbCBjb21wdXRpbmcgY29tcG9uZW50LiBJdCBpcyB1c2VkIHRoZSBzYW1lIHdheSBhcyB0aGUgYGxhcHBseSgpYCBmdW5jdGlvbi4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IHlvdSB3aWxsIG5lZWQgdG8gc3BlY2lmeSB0aGUgY2x1c3RlciAoYGNsYCkgYXMgdGhlIGZpcnN0IGFyZ3VtZW50LiBUaGUgc2Vjb25kIGFyZ3VtZW50IGdvZXMgYXMgdGhlIGBsYXBwbHkoKWAgZnVuY3Rpb24uCgojIyMjIGBzdG9wQ2x1c3RlcigpYAoKVGhlIGBzdG9wQ2x1c3RlcigpYCBmdW5jdGlvbiBjbG9zZXMgdGhlIGNsdXN0ZXIgZG93bi4KCiMjIyBJbXBsZW1lbnRhdGlvbgoKVGhlIGNvZGUgYmVsb3cgaW1wbGVtZW50cyB0aGUgYHBhckxhcHBseSgpYCBmdW5jdGlvbjoKCmBgYHtyfQpzdGFydCA8LSBTeXMudGltZSgpCmNsIDwtIG1ha2VDbHVzdGVyKG5jb3JlcykKY2x1c3RlckV4cG9ydChjbCwgYygicGFyYWxsZWxfbG0iKSkKcGFyTGFwcGx5X3Jlc3VsdHM8LXBhckxhcHBseShjbCxwYXJhbGxlbF9kYXRhLHBhcmFsbGVsX2xtKQpzdG9wQ2x1c3RlcihjbCkKKHBhckxhcHBseV90aW1lIDwtIFN5cy50aW1lKCktc3RhcnQpCmBgYAoKIyBSZXN1bHRzCgpTaW5jZSBhbGwgdGhlIHJlc3VsdHMgYXJlIGluIGEgbGlzdCwgd2Ugd2lsbCBmaXJzdCBjb252ZXJ0IHRoZW0gaW50byBhIG1hdHJpeDoKCmBgYHtyfQpsYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QobGFwcGx5X3Jlc3VsdHMpLCBucm93ID0gNCkKbWNsYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QobWNsYXBwbHlfcmVzdWx0cyksIG5yb3cgPSA0KQpwYXJMYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QocGFyTGFwcGx5X3Jlc3VsdHMpLCBucm93ID0gNCkKYGBgCgpVc2UgdGhlIGByb3dNZWFucygpYCBvbiBhbGwgb2JqZWN0cyB0byBzaG93IGVhY2ggbWV0aG9kIHByb3ZpZGVzIGNvbnNpc3RlbnQgZXN0aW1hdGVzIG9mIHRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMuCgpgYGB7cn0Kcm93TWVhbnMobGFwcGx5X21hdCkKcm93TWVhbnMobWNsYXBwbHlfbWF0KQpyb3dNZWFucyhwYXJMYXBwbHlfbWF0KQpgYGAKCiMgTm90ZXMKCiMjIFNwZWVkIEdhaW5zCgpXaGVuIHBhcmFsbGVsaXppbmcgeW91ciBjb2RlLCB0aGUgc3BlZWQgZ2FpbnMgbWF5IG5vdCBiZSBub3RpY2VhYmxlLiBXaGVuIHVzaW5nIHRoZSBgcGFyYWxsZWxgIHBhY2thZ2UsIFIgYXNzaWducyBlbGVtZW50cyBvZiBhIGxpc3Qgb3IgdmVjdG9yIGludG8gZGlmZmVyZW50IGNvcmVzLCBrbm93biBhcyBvdmVyaGVhZCwgd2hpY2ggdGFrZXMgdGltZS4gVGhlIHRpbWUgaXQgdGFrZXMgdG8gbG9hZCB0aGUgZGF0YSBtYXkgYmUgbG9uZ2VyIHRoYW4gcHJvY2Vzc2luZyB0aGUgZGF0YS4gVGhlcmVmb3JlLCBpdCBtYXkgbm90IGJlIGZhc3RlciBvciB0YWtlIGxvbmdlciB0aGFuIHVzaW5nIHRoZSBgKmFwcGx5KClgIGZ1bmN0aW9ucy4gSXQgbWF5IGJlIGJlc3QgdG8gdXNlIG9uZSBjb3JlIG9yIHNwbGl0IHVwIHRoZSBkYXRhIG9uIHlvdXIgb3duLiBIb3dldmVyLCB3aGVuIHByb2Nlc3NpbmcgeW91ciBkYXRhIHRha2VzIG11Y2ggbG9uZ2VyIHRoYW4geW91ciBvdmVyaGVhZCB0aW1lLCB0aGUgc3BlZWQgZ2FpbnMgYXJlIG5vdGljZWFibGUuCgojIyBIeXBlci10aHJlYWRpbmcKCkNlcnRhaW4gY29tcHV0ZXIgcHJvY2Vzc29ycyBoYXZlIGNvcmVzIHRoYXQgYXJlIG11bHRpLXRocmVhZGVkIChoeXBlci10aHJlYWRpbmcpLiBUaGlzIG1lYW5zIHRoZSBjb21wdXRlciB2aWV3cyAxIHBoeXNpY2FsIGNvcmUgYXMgMiBsb2dpY2FsIGNvcmVzLiBXaGlsZSB5b3UgY2VydGFpbmx5IHNwZWNpZnkgZG91YmxlIHRoZSBudW1iZXIgb2YgcGh5c2ljYWwgY29yZXMgZm9yIHlvdXIgY2x1c3RlciwgdGhlIHNwZWVkIGdhaW5zIGlzIG5vdCBlcXVpdmFsZW50IGFzIHVzaW5nIHByb3BlciBwaHlzaWNhbCBjb3Jlcy4gSW4gbXkgZXhwZXJpZW5jZSwgaXQgaXMgbm90IHdvcnRoIHVzaW5nIHRoZSBsb2dpY2FsIGNvcmVzIGFuZCBqdXN0IHNwZWNpZnkgdGhlIG51bWJlciBvZiBwaHlzaWNhbCBjb3JlcyBpbiB5b3VyIGNvbXB1dGVyLgo=