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 nodes1

  • May cause cross-contamination because the environment may change2

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 has3.

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.


  1. You can think of as nodes as the processor. Each processor has a set number of cpus, if you need more cpus, you will need to use another node.↩︎

  2. Mostly occurs with random number generation; however, this is generally not a problem.↩︎

  3. The function provides the number of logical cores and not physical cores. This is due to multithreading.↩︎

LS0tCnRpdGxlOiAiUGFyYWxsZWwgQ29tcHV0aW5nIGluIFIgd2l0aCBgcGFyYWxsZWxgIgphdXRob3I6ICJJc2FhYyBRdWludGFuaWxsYSBTYWxpbmFzIgpkZXNjcmlwdGlvbjogVHV0b3JpYWwgZm9yIHBhcmFsbGVsIHByb2Nlc3NpbmcgdXNpbmcgdGhlIFIgcGFja2FnZSBwYXJhbGxlbC4KZGF0ZTogIjEyLTE5LTIwMjAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gVFJVRSkKYGBgCgojIEludHJvZHVjdGlvbgoKUiBpcyBkZXNpZ25lZCB0byBvbmx5IHVzZSBvbmUgY3B1IChvciBjb3JlKSB3aGVuIHJ1bm5pbmcgdGFza3MuIEhvd2V2ZXIsIGEgY29tcHV0ZXIgbWF5IGhhdmUgbW9yZSB0aGFuIG9uZSBjb3JlIHRoYXQgY2FuIGJlIHVzZWQgdG8gcnVuIHRhc2tzLiBUaGUgdXNlIG9mIG1vcmUgdGhhbiBvbmUgY29yZSBpcyBrbm93biBhcyBwYXJhbGxlbCBjb21wdXRpbmcgaW4gUi4gVGhlIGdvYWwgb2YgdGhpcyB0dXRvcmlhbCBpcyB0byBwcm92aWRlIHRoZSBiYXNpY3Mgb2YgdXNpbmcgdGhlIGBwYXJhbGxlbGAgcGFja2FnZSBhbmQgdXRpbGl6aW5nIG1vcmUgY29yZXMgaW4gYSBjb21wdXRlci4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHBhcmFsbGVsIHByb2Nlc3NpbmcgaW4gUiwgdmlzaXQgdGhlIGZvbGxvd2luZyBzaXRlczoKCi0gICA8aHR0cHM6Ly9uY2Vhcy5naXRodWIuaW8vb3NzLWxlc3NvbnMvcGFyYWxsZWwtY29tcHV0aW5nLWluLXIvcGFyYWxsZWwtY29tcHV0aW5nLWluLXIuaHRtbD4KLSAgIDxodHRwczovL2RlcHQuc3RhdC5sc2EudW1pY2guZWR1L35qZXJyaWNrL2NvdXJzZXMvc3RhdDcwMS9ub3Rlcy9wYXJhbGxlbC5odG1sPgotICAgPGh0dHBzOi8vcHN1LXBzeWNob2xvZ3kuZ2l0aHViLmlvL3ItYm9vdGNhbXAtMjAxOC90YWxrcy9wYXJhbGxlbF9yLmh0bWw+CgojIEJhY2tncm91bmQKClRoZSBgcGFyYWxsZWxgIHBhY2thZ2UgYWxsb3dzIHlvdSB0byB1c2UgbXVsdGlwbGUgY3B1cyBhdCB0aGUgc2FtZSB0aW1lIHRvIGNvbXBsZXRlIHJlcGV0aXRpdmUgdGFza3MuIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSB0byBydW4gYSBzaW11bGF0aW9uIHN0dWR5IHdpdGggMTAwIGRhdGEgc2V0cywgeW91IGNhbiB0YXNrIDQgY3B1cyB0byBhbmFseXplIDI1IGRhdGEgc2V0cyBhdCB0aGUgc2FtZSB0aW1lLiBUaGlzIGlkZWFsbHkgd2lsbCB0YWtlIGEgZm9ydGggb2YgdGhlIHRpbWUgaXQgd2lsbCB0YWtlIDEgY3B1IHRvIGNvbXBsZXRlIDEwMCBkYXRhIHNldHMuCgpUaGUgb25seSBjYXZlYXV0IHdpdGggdXNpbmcgdGhlIGBwYXJhbGxlbGAgcGFja2FnZSBpcyB0aGF0IGVhY2ggcHJvY2VzcyBuZWVkcyB0byBiZSBpbmRlcGVuZGVudCBvZiBhbGwgcHJvY2Vzc2VzLiBJZiB5b3UgYXJlIHJ1bm5pbmcgYSBmb3IgbG9vcCB0aGF0IGRlcGVuZHMgb24gdGhlIHByZXZpb3VzIGl0ZXJhdGlvbiwgdGhlIGNwdSBjYW5ub3QgcHJvY2VzcyB0aGUgdGFzayBiZWNhdXNlIGl0IGhhcyBtaXNzaW5nIGluZm9ybWF0aW9uLiBUaGVyZWZvcmUsIHlvdSB3aWxsIG5lZWQgaWRlbnRpZnkgdGhlIHRhc2tzIHRoYXQgYXJlIG5vdCBkZXBlbmRlbnQgb2Ygb25lIGFub3RoZXIgYW5kIHBhcmFsbGVsaXplIGl0LgoKV2l0aCB0aGUgcGFyYWxsZWwgcGFja2FnZSB5b3UgY2FuIHVzZSB0d28gZmFtaWx5IG9mIGZ1bmN0aW9uczogYG1jKigpYCBhbmQgYHBhciooKWAgZnVuY3Rpb25zLiBUaGUgbWFpbiBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgZnVuY3Rpb25zIGlzIHRoYXQgYG1jKigpYCB1c2VzIGEgZm9ya2luZyBtZXRob2QgdG8gcGFyYWxsZWxpemUgeW91ciBjb2RlLCBhbmQgdGhlIGBwYXIqKClgIHVzZXMgYSBzb2NrZXQgbWV0aG9kIHRvIHBhcmFsbGVsaXplIHlvdXIgY29kZS4gQm90aCBtZXRob2RzIGhhcyB0aGVpciBzdHJlbmd0aHMgYW5kIHdlYWtuZXNzZXMuIFdoZW4gdXNpbmcgdGhlIEhQQ0MgY2x1c3RlciwgSSByZWNvbW1lbmQgdXNpbmcgdGhlIGBtYyooKWAgZnVuY3Rpb25zIHdoZW4gcG9zc2libGUuIFRoZXJlIGFyZSBicmllZiBkZXNjcmlwdGlvbnMgZm9yIGVhY2ggbWV0aG9kIGJlbG93LgoKIyMgRm9ya2luZwoKVGhlIGZvcmtpbmcgbWV0aG9kIGlzIGltcGxlbWVudGVkIHdoZW4gdXNpbmcgdGhlIGBtYyooKWAgZnVuY3Rpb25zLiBCZWxvdyBpcyBhIGxpc3Qgb2YgcHJvcyBhbmQgY29uczoKCiMjIyBQcm9zOgoKLSAgIEVhc2llciB0byBpbXBsZW1lbnQKCi0gICBZb3VyIGVudmlyb25tZW50IGlzIGNvcGllZCB0byB0aGUgY3B1CgotICAgSGFzIHRoZSBwb3RlbnRpYWwgdG8gdXNlIGxlc3MgcmVzb3VyY2VzCgotICAgV3JhcHMgVVNFUiBmdW5jdGlvbiB3aXRoIHRoZSBgdHJ5KClgIGZ1bmN0aW9uCgojIyMgQ29uczoKCi0gICBPbmx5IHdvcmtzIG9uIFBPU0lYIHN5c3RlbXMgKE1hYyBvciBMaW51eCkKCi0gICBEb2VzIG5vdCB3b3JrIHdpdGggbXVsdGlwbGUgbm9kZXNbXjFdCgotICAgTWF5IGNhdXNlIGNyb3NzLWNvbnRhbWluYXRpb24gYmVjYXVzZSB0aGUgZW52aXJvbm1lbnQgbWF5IGNoYW5nZVteMl0KClteMV06IFlvdSBjYW4gdGhpbmsgb2YgYXMgbm9kZXMgYXMgdGhlIHByb2Nlc3Nvci4gRWFjaCBwcm9jZXNzb3IgaGFzIGEgc2V0IG51bWJlciBvZiBjcHVzLCBpZiB5b3UgbmVlZCBtb3JlIGNwdXMsIHlvdSB3aWxsIG5lZWQgdG8gdXNlIGFub3RoZXIgbm9kZS4KClteMl06IE1vc3RseSBvY2N1cnMgd2l0aCByYW5kb20gbnVtYmVyIGdlbmVyYXRpb247IGhvd2V2ZXIsIHRoaXMgaXMgZ2VuZXJhbGx5IG5vdCBhIHByb2JsZW0uCgpHZW5lcmFsbHkgc3BlYWtpbmcsIFIgY29waWVzIGl0cyBlbnZpcm9ubWVudCB0byBhIGNwdSBhbmQgcnVucyB0aGUgcHJvY2Vzcy4KCiMjIFNvY2tldAoKVGhlIHNvY2tldCBtZXRob2QgaXMgaW1wbGVtZW50ZWQgd2hlbiB1c2luZyB0aGUgYHBhciooKWAgZnVuY3Rpb25zLiBCZWxvdyBpcyBhIGxpc3Qgb2YgcHJvcyBhbmQgY29uczoKCiMjIyBQcm9zOgoKLSAgIFdvcmtzIG9uIGFueSBzeXN0ZW0gKFdpbmRvd3MsIE1hYywgYW5kIExpbnV4KQoKLSAgIEVhY2ggcHJvY2VzcyBpcyB1bmlxdWUKCi0gICBObyBjcm9zcy1jb250YW1pbmF0aW9uCgotICAgQ2FuIGJlIHVzZWQgd2hlbiBtdWx0aXBsZSBub2RlcyBhcmUgaW52b2x2ZWQKCiMjIyBDb25zOgoKLSAgIFRoZSBlbnRpcmUgZW52aXJvbm1lbnQgKGluY2x1ZGluZyBsb2FkZWQgZnVuY3Rpb25zKSBtdXN0IGJlIHNwZWNpZmllZAoKLSAgIE1vcmUgcmVzb3VyY2UgaW50ZW5zaXZlCgotICAgYHRyeSgpYCBmdW5jdGlvbiBpcyBub3QgaW1wbGVtZW50ZWQsIHdoZW4gb25lIGNwdSBmYWlscywgdGhlIGVudGlyZSBjbHVzdGVyIGZhaWxzCgpHZW5lcmFsbHkgc3BlYWtpbmcsIFIgY3JlYXRlcyBhIG5ldyBlbnZpcm9ubWVudCBvbiBlYWNoIGNwdS4KCiMjIFdoZXJlIHRvIHBhcmFsbGVsaXplPwoKUGFyYWxsZWwgY29tcHV0aW5nIHJlcXVpcmVzIHJlcGV0aXRpdmUgdGFza3MgdG8gYmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4gVGhlIHJlc3VsdHMgZnJvbSBvbmUgdGFzayBtdXN0IG5vdCBiZSB1c2VkIGFzIGlucHV0IGZvciBhbm90aGVyIHRhc2suIFRoZXJlZm9yZSwgaWRlbnRpZnkgYmxvY2tzIG9mIGNvZGUgd2hlcmUgcmVwZXRpdGlvbiBpcyBpbnZvbHZlZC4gVGhpcyBjYW4gYmUgZWl0aGVyIGBmb3JgIGxvb3BzIG9yIGAqYXBwbHkoKWAgZnVuY3Rpb25zLgoKT3RoZXIgbG9jYXRpb25zIHRvIHBhcmFsbGVsaXplIHlvdXIgY29kZSBhcmUgYm90dGxlbmVja3MuIEhhdmluZyBtdWx0aXBsZSBjb3JlcyBwcm9jZXNzaW5nIGJvdHRsZW5lY2tzIGhlbHBzIHNwZWVkIHVwIHlvdXIgY29kZS4gRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgYm90dGxlbmVja3Mgb3IgY29kZSBlZmZpY2llbmN5LCB2aXNpdCB0aGUgQWR2YW5jZWQgUiB3ZWJzaXRlOiBbYWR2LXIuaGFkbGV5Lm56XShodHRwczovL2Fkdi1yLmhhZGxleS5uei8pLgoKSWRlbnRpZnlpbmcgdGhlIGxvY2F0aW9uIHRvIHBhcmFsbGVsaXplIG1heSBiZSBjaGFsbGVuZ2luZyBkdWUgdG8gc28gbWFueSBvcHRpb25zLiBNeSBhZHZpY2UgaXMgdG8gdHJ5IG1hbnkgdGhpbmdzIGFuZCBzZWUgd2hhdCBwcm92aWRlcyB0aGUgYmVzdCByZXN1bHQuCgojIFNpbXVsYXRpb24gRXhhbXBsZQoKVG8gZGVtb25zdHJhdGUgaG93IHRvIHVzZSB0aGUgYHBhcmFsbGVsYCBwYWNrYWdlIGluIFIsIHdlIGNvbmR1Y3QgYSBzaW1wbGUgc2ltdWxhdGlvbiBzdHVkeSBzaG93aW5nIGhvdyB0aGUgZXN0aW1hdGVzIGZyb20gdGhlIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgZXN0aW1hdG9yIGxlYWRzIHRvIHVuYmlhc2VkIHJlc3VsdHMuCgokJApZIFxzaW0gTihcYmV0YV8wK1hfMVxiZXRhXzErWF80XGJldGFfNStYXzNcYmV0YV8zLFxzaWdtYV4yKQokJAoKIyMgU2ltdWxhdGlvbiBGdW5jdGlvbnMKClRoZSBmdW5jdGlvbiBiZWxvdyBnZW5lcmF0ZXMgZGF0YSBmcm9tIHRoZSBtb2RlbCBhYm92ZSBhbmQgcmV0dXJucyBhIGRhdGEgZnJhbWUuCgpgYGB7cn0KZGF0YV9zaW0gPC0gZnVuY3Rpb24oc2VlZCwgbm9icywgYmV0YSwgc2lnbWEsIHhtZWFucywgeHNpZ3MpewogIHNldC5zZWVkKHNlZWQpCiAgeHJuIDwtIHJtdm5vcm0obm9icywgbWVhbiA9IHhtZWFucywgc2lnbWEgPSB4c2lncykKICB4cGVkIDwtIGNiaW5kKHJlcCgxLG5vYnMpLHhybikKICB5IDwtIHhwZWQgJSolIGJldGEgKyBybm9ybShub2JzICwwLCBzaWdtYSkKICBkZiA8LSBkYXRhLmZyYW1lKHg9eHJuLCB5PXkpCiAgcmV0dXJuKGRmKQp9CmBgYAoKVGhlIGZ1bmN0aW9uIG5lZWRzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOgoKLSAgIGBzZWVkYDogdGhlIHZhbHVlIHRvIHNldCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yCi0gICBgbm9ic2A6IG51bWJlciBvZiBvYnNlcnZhdGlvbnMKLSAgIGBiZXRhYDogYSB2ZWN0b3Igc3BlY2lmeWluZyB0aGUgdHJ1ZSB2YWx1ZXMgZm9yIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAoJFxiZXRhXzAkLCAkXGJldGFfMSQsICRcYmV0YV8yJCwgJFxiZXRhXzMkKQotICAgYHNpZ21hYDogdGhlIHZhcmlhbmNlIGZvciB0aGUgbW9kZWwgYWJvdmUKLSAgIGB4bWVhbnNgOiBhIHZlY3RvciBvZiBtZWFucyB1c2VkIHRvIGdlbmVyYXRlIHRoZSB2YWx1ZXMgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCi0gICBgeHNpZ3NgOiBhIG1hdHJpeCBmb3IgdGhlIGNvdmFyaWFuY2UgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCgpUaGUgZnVuY3Rpb24gYmVsb3cgdGFrZXMgdGhlIGRhdGEgZ2VuZXJhdGVkIGZyb20gdGhlIGBkYXRhX3NpbSgpYCBhbmQgZml0cyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUaGUgZnVuY3Rpb24gd3JhcHMgYXJvdW5kIHRoZSBgbG0oKWAgZnVuY3Rpb24gYW5kIHJldHVybnMgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLgoKYGBge3J9CnBhcmFsbGVsX2xtIDwtIGZ1bmN0aW9uKGRhdGEpewogIGxtX3JlcyA8LSBsbSh5IH4geC4xICsgeC4yICsgeC4zLCBkYXRhID0gZGF0YSkKICByZXR1cm4oY29lZihsbV9yZXMpKQp9CmBgYAoKIyMjIExvYWRpbmcgUiBQYWNrYWdlcwoKRm9yIHRoaXMgdHV0b3JpYWwsIHlvdSB3aWxsIG5lZWQgdGhlIGBwYXJhbGxlbGAgYW5kIGBtdnRub3JtYCBwYWNrYWdlcy4KCmBgYHtyfQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KG12dG5vcm0pCmBgYAoKIyMjIFNldHRpbmcgUGFyYW1ldGVycwoKYGBge3J9Ck4gPC0gMTAwMDAKbm9icyA8LSAyMDAKYmV0YSA8LSBjKDUsIDQsIC01LCAtMykKeG1lYW5zIDwtIGMoLTIsIDAsIDIpCnhzaWdzIDwtZGlhZyhyZXAoMSwgMykpCnNpZyA8LSAzCmBgYAoKIyMjIFNpbXVsYXRpbmcgRGF0YQoKYGBge3J9CnN0YXJ0IDwtIFN5cy50aW1lKCkKcGFyYWxsZWxfZGF0YSA8LSBsYXBwbHkoYygxOk4pLCBkYXRhX3NpbSwKICAgICAgICAgICAgICAgICAgICAgICAgICBub2JzID0gbm9icywgYmV0YSA9IGJldGEsIHNpZ21hID0gc2lnLCB4bWVhbnMgPSB4bWVhbnMsIHhzaWdzID0geHNpZ3MpClN5cy50aW1lKCktc3RhcnQKYGBgCgojIFBhcmFsbGVsaXphdGlvbgoKIyMgRGV0ZWN0aW5nIENvcmVzCgpUaGUgYHBhcmFsbGVsYCBwYWNrYWdlIGhhcyBhIGZ1bmN0aW9uIHRoYXQgY2FuIGRldGVjdCB0aGUgbnVtYmVyIG9mIGNvcmVzIGF2YWlsYWJsZSBvbiB5b3VyIGNvbXB1dGVyLiBVc2UgdGhlIGBkZXRlY3RDb3JlcygpYCBmdW5jdGlvbiB0byBmaW5kIGhvdyBtYW55IGNwdXMgeW91ciBjb21wdXRlciBoYXNbXjNdLgoKW14zXTogVGhlIGZ1bmN0aW9uIHByb3ZpZGVzIHRoZSBudW1iZXIgb2YgbG9naWNhbCBjb3JlcyBhbmQgbm90IHBoeXNpY2FsIGNvcmVzLiBUaGlzIGlzIGR1ZSB0byBtdWx0aXRocmVhZGluZy4KCmBgYHtyfQpkZXRlY3RDb3JlcygpCmBgYAoKRm9yIHRoaXMgZXhhbXBsZSwgSSB3aWxsIHVzZSA4IGNwdXMuIFlvdSBjYW4gY2hhbmdlIHRoZSBudW1iZXIgb2YgY3B1cyBiYXNlZCBvbiB5b3VyIGNvbXB1dGVyLgoKYGBge3J9Cm5jb3JlcyA8LSA4CmBgYAoKIyMgVXNpbmcgYGxhcHBseSgpYAoKQmVmb3JlIHdlIHBhcmFsbGVsaXplLCB1c2UgdGhlIGBsYXBwbHkoKWAgZnVuY3Rpb24gdG8gcnVuIHRoZSBzaW11bGF0aW9uIG9uIG9uZSBjcHUuCgpgYGB7cn0Kc3RhcnQgPC0gU3lzLnRpbWUoKQpsYXBwbHlfcmVzdWx0cyA8LSBsYXBwbHkocGFyYWxsZWxfZGF0YSwgcGFyYWxsZWxfbG0pCihsYXBwbHlfdGltZSA8LSBTeXMudGltZSgpLXN0YXJ0KQpgYGAKCiMjIFVzaW5nIGBtY2xhcHBseSgpYAoKVGhlIGVhc2llc3Qgd2F5IHRvIHBhcmFsbGVsaXplIGlzIHRvIHVzZSB0aGUgYG1jbGFwcGx5KClgIGZ1bmN0aW9uLiBBbGwgeW91IG5lZWQgdG8gZG8gaXMgY2hhbmdlIHRoZSBgbGFwcGx5KClgIGZ1bmN0aW9uIHRvIGBtY2xhcHBseSgpYCBhbmQgYWRkIHRoZSBhcmd1bWVudCBgbWMuY29yZXMgPSAgbmNvcmVzYCBhcyB0aGUgbGFzdCBhcmd1bWVudC4gVGhlIGFyZ3VtZW50IGBtYy5jb3Jlc2Agc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgY29yZXMgZm9yIHBhcmFsbGVsaXphdGlvbi4KCmBgYHtyfQpzdGFydCA8LSBTeXMudGltZSgpCm1jbGFwcGx5X3Jlc3VsdHMgPC0gbWNsYXBwbHkocGFyYWxsZWxfZGF0YSwgcGFyYWxsZWxfbG0sIG1jLmNvcmVzID0gbmNvcmVzKQoobWNsYXBwbHlfdGltZSA8LSBTeXMudGltZSgpLXN0YXJ0KQpgYGAKCiMjIFVzaW5nIGBwYXJMYXBwbHkoKWAKCiMjIyBGdW5jdGlvbnMKClVzaW5nIGBwYXJMYXBwbHkoKWAgZnVuY3Rpb24gcmVxdWlyZXMgc2V0dGluZyB1cCB0aGUgY2x1c3Rlci4gVGhlcmUgYXJlIHR3byBmdW5jdGlvbnMgbmVlZGVkIHRvIHNldCB1cCB0aGUgY2x1c3RlcjogYG1ha2VDbHVzdGVyKClgIGFuZCBgY2x1c3RlckV4cG9ydCgpYC4gT25jZSB5b3VyIGFuYWx5c2lzIGlzIGZpbmlzaGVkLCB5b3Ugd2lsbCBuZWVkIHRvIGNsb3NlIHRoZSBjbHVzdGVyIHVzaW5nIHRoZSBgc3RvcENsdXN0ZXIoKWAgZnVuY3Rpb24uCgojIyMjIGBtYWtlQ2x1c3RlcigpYAoKVGhlIGBtYWtlQ2x1c3RlcigpYCBmdW5jdGlvbiBzZXRzIHVwIHRoZSBjbHVzdGVyIGluIHlvdXIgY29tcHV0ZXIuIFlvdSBvbmx5IG5lZWQgdG8gc3BlY2lmeSB0aGUgbnVtYmVyIG9mIGNvcmVzIHRvIHVzZSAoYG5jb3Jlc2ApIGFuZCBzdG9yZSBpdCBpbiBhbiBSIG9iamVjdCAoYGNsYCkKCiMjIyMgYGNsdXN0ZXJFeHBvcnQoKWAKClRoZSBgY2x1c3RlckV4cG9ydCgpYCBmdW5jdGlvbiBpcyBuZWVkZWQgdG8gZXhwb3J0IHVzZXItY3JlYXRlZCBmdW5jdGlvbnMsIHBhY2thZ2UgZnVuY3Rpb25zLCBvciBkYXRhIHRvIHRoZSBjbHVzdGVyLiBZb3Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgdGhlIGNsdXN0ZXIgKGBjbGApIHRvIGV4cG9ydCB0aGUgaW5mb3JtYXRpb24gYW5kIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbnMgb3IgZGF0YSBpbiBhIGNoYXJhY3RlciB2ZWN0b3IuCgojIyMjIGBwYXJMYXBwbHkoKWAKClRoZSBgcGFyTGFwcGx5KClgIGlzIHRoZSBwYXJhbGxlbCBjb21wdXRpbmcgY29tcG9uZW50LiBJdCBpcyB1c2VkIHRoZSBzYW1lIHdheSBhcyB0aGUgYGxhcHBseSgpYCBmdW5jdGlvbi4gVGhlIG9ubHkgZGlmZmVyZW5jZSBpcyB0aGF0IHlvdSB3aWxsIG5lZWQgdG8gc3BlY2lmeSB0aGUgY2x1c3RlciAoYGNsYCkgYXMgdGhlIGZpcnN0IGFyZ3VtZW50LiBUaGUgc2Vjb25kIGFyZ3VtZW50IGdvZXMgYXMgdGhlIGBsYXBwbHkoKWAgZnVuY3Rpb24uCgojIyMjIGBzdG9wQ2x1c3RlcigpYAoKVGhlIGBzdG9wQ2x1c3RlcigpYCBmdW5jdGlvbiBjbG9zZXMgdGhlIGNsdXN0ZXIgZG93bi4KCiMjIyBJbXBsZW1lbnRhdGlvbgoKVGhlIGNvZGUgYmVsb3cgaW1wbGVtZW50cyB0aGUgYHBhckxhcHBseSgpYCBmdW5jdGlvbjoKCmBgYHtyfQpzdGFydCA8LSBTeXMudGltZSgpCmNsIDwtIG1ha2VDbHVzdGVyKG5jb3JlcykKY2x1c3RlckV4cG9ydChjbCwgYygicGFyYWxsZWxfbG0iKSkKcGFyTGFwcGx5X3Jlc3VsdHM8LXBhckxhcHBseShjbCxwYXJhbGxlbF9kYXRhLHBhcmFsbGVsX2xtKQpzdG9wQ2x1c3RlcihjbCkKKHBhckxhcHBseV90aW1lIDwtIFN5cy50aW1lKCktc3RhcnQpCmBgYAoKIyBSZXN1bHRzCgpTaW5jZSBhbGwgdGhlIHJlc3VsdHMgYXJlIGluIGEgbGlzdCwgd2Ugd2lsbCBmaXJzdCBjb252ZXJ0IHRoZW0gaW50byBhIG1hdHJpeDoKCmBgYHtyfQpsYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QobGFwcGx5X3Jlc3VsdHMpLCBucm93ID0gNCkKbWNsYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QobWNsYXBwbHlfcmVzdWx0cyksIG5yb3cgPSA0KQpwYXJMYXBwbHlfbWF0IDwtIG1hdHJpeCh1bmxpc3QocGFyTGFwcGx5X3Jlc3VsdHMpLCBucm93ID0gNCkKYGBgCgpVc2UgdGhlIGByb3dNZWFucygpYCBvbiBhbGwgb2JqZWN0cyB0byBzaG93IGVhY2ggbWV0aG9kIHByb3ZpZGVzIGNvbnNpc3RlbnQgZXN0aW1hdGVzIG9mIHRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMuCgpgYGB7cn0Kcm93TWVhbnMobGFwcGx5X21hdCkKcm93TWVhbnMobWNsYXBwbHlfbWF0KQpyb3dNZWFucyhwYXJMYXBwbHlfbWF0KQpgYGAKCiMgTm90ZXMKCiMjIFNwZWVkIEdhaW5zCgpXaGVuIHBhcmFsbGVsaXppbmcgeW91ciBjb2RlLCB0aGUgc3BlZWQgZ2FpbnMgbWF5IG5vdCBiZSBub3RpY2VhYmxlLiBXaGVuIHVzaW5nIHRoZSBgcGFyYWxsZWxgIHBhY2thZ2UsIFIgYXNzaWducyBlbGVtZW50cyBvZiBhIGxpc3Qgb3IgdmVjdG9yIGludG8gZGlmZmVyZW50IGNvcmVzLCBrbm93biBhcyBvdmVyaGVhZCwgd2hpY2ggdGFrZXMgdGltZS4gVGhlIHRpbWUgaXQgdGFrZXMgdG8gbG9hZCB0aGUgZGF0YSBtYXkgYmUgbG9uZ2VyIHRoYW4gcHJvY2Vzc2luZyB0aGUgZGF0YS4gVGhlcmVmb3JlLCBpdCBtYXkgbm90IGJlIGZhc3RlciBvciB0YWtlIGxvbmdlciB0aGFuIHVzaW5nIHRoZSBgKmFwcGx5KClgIGZ1bmN0aW9ucy4gSXQgbWF5IGJlIGJlc3QgdG8gdXNlIG9uZSBjb3JlIG9yIHNwbGl0IHVwIHRoZSBkYXRhIG9uIHlvdXIgb3duLiBIb3dldmVyLCB3aGVuIHByb2Nlc3NpbmcgeW91ciBkYXRhIHRha2VzIG11Y2ggbG9uZ2VyIHRoYW4geW91ciBvdmVyaGVhZCB0aW1lLCB0aGUgc3BlZWQgZ2FpbnMgYXJlIG5vdGljZWFibGUuCgojIyBIeXBlci10aHJlYWRpbmcKCkNlcnRhaW4gY29tcHV0ZXIgcHJvY2Vzc29ycyBoYXZlIGNvcmVzIHRoYXQgYXJlIG11bHRpLXRocmVhZGVkIChoeXBlci10aHJlYWRpbmcpLiBUaGlzIG1lYW5zIHRoZSBjb21wdXRlciB2aWV3cyAxIHBoeXNpY2FsIGNvcmUgYXMgMiBsb2dpY2FsIGNvcmVzLiBXaGlsZSB5b3UgY2VydGFpbmx5IHNwZWNpZnkgZG91YmxlIHRoZSBudW1iZXIgb2YgcGh5c2ljYWwgY29yZXMgZm9yIHlvdXIgY2x1c3RlciwgdGhlIHNwZWVkIGdhaW5zIGlzIG5vdCBlcXVpdmFsZW50IGFzIHVzaW5nIHByb3BlciBwaHlzaWNhbCBjb3Jlcy4gSW4gbXkgZXhwZXJpZW5jZSwgaXQgaXMgbm90IHdvcnRoIHVzaW5nIHRoZSBsb2dpY2FsIGNvcmVzIGFuZCBqdXN0IHNwZWNpZnkgdGhlIG51bWJlciBvZiBwaHlzaWNhbCBjb3JlcyBpbiB5b3VyIGNvbXB1dGVyLgo=