Empezaremos viendo cómo usar parallel y las funciones mcparallel/mccollect. Ejecutamos las tareas usando mcparallel y recupermos los resultados usando mccollect.

library(ggplot2)
library(parallel)
library(microbenchmark) # Para evaluar cuanto tarda
epa <- readRDS("./assets/clean-epa.RDS")
epa$works <- epa$trarem == 1

parfits <- function() {
  pfit <- mcparallel(glm(works ~ education, data=epa, family=binomial))
  mccollect(list(pfit))
}
mbm <- microbenchmark("par.boot"=parfits(),
                     "serial.boot"=glm(works ~ education,
                                       data=epa,
                                       family=binomial),
                     times=10)
autoplot(mbm)

Clearly actually forking the processes and waiting for them to rejoin itself takes some time.

Semillas aleatorias y paralalelismo

Usaremos la versión paralelizada de lapply

microbenchmark("serial"=unlist(lapply(1:10, function(x) rnorm(1e3))),
               "par2"=unlist(mclapply(1:10, function(x) rnorm(1e3), mc.cores=2)),
               "par4"=unlist(mclapply(1:10, function(x) rnorm(1e3), mc.cores=4)),
               times=100)
microbenchmark("serial"=unlist(lapply(1:10, function(x) rnorm(1e5))),
               "par2"=unlist(mclapply(1:10, function(x) rnorm(1e5), mc.cores=2)),
               "par4"=unlist(mclapply(1:10, function(x) rnorm(1e5), mc.cores=4)),
               times=100)
cis <- readRDS('./assets/clean-data.RDS')
cis <- cis[, c("economy",
              "econrighttrack",
              "politics",
              "polrighttrack",
              "ideology",
              "goveval")]
cis <- cis[complete.cases(cis), ]
cis <- do.call(cbind, lapply(cis, as.numeric))
system.time(serial.res <- kmeans(cis, centers=3, nstart=20))
serial.res$tot.withinss
do_n_kmeans <- function(n) {
    return(kmeans(cis, centers=7, nstart=n))
}

system.time(list.res <- lapply(runif(4, 1, 100), do_n_kmeans))
res <- sapply(list.res, function(x) x$tot.withinss)
res
system.time(list.res <- mclapply(runif(4, 1, 1000), do_n_kmeans, mc.cores=4))
res <- sapply(list.res, function(x) x$tot.withinss)
res
RNGkind("L'Ecuyer-CMRG")

system.time(list.res <- mclapply(runif(4, 1, 1000),
                                do_n_kmeans,
                                mc.cores=4, mc.set.seed=FALSE))
res <- sapply(list.res, function(x) x$tot.withinss)
res

mcparallel works very well for task parallelism; the mclapply for data parallelism.

Things to watch for:

Multiple computers

library(parallel)
cl <- makeCluster(4)
clusterCall(cl, rnorm, 5)

clusterCall() runs the same function (here, rnorm, with argument 5) on all workers in the cluster. A related helper function is clusterEvalQ() which is handier to use for some setup tasks - eg,

clusterEvalQ(cl, {library(parallel); NULL})
res <- clusterApply(cl, rep(10, 4), do_n_kmeans)
stopCluster(cl)

El error es porque no hemos copiado los datos.

Recall that we aren’t forking here; we are creating processes from scratch. These processes, new to this world, are not familiar with our ways, customs, or datasets. We actually have to ship the data out to the workers:

system.time(clusterExport(cl, "cis"))
res <- clusterApply(cl, rep(10, 4), do_n_kmeans)
res <- sapply(list.res, function(x) x$tot.withinss)
lapply.res <- list.res[[which.min(res)]]
lapply.res$withinss
res

Podemos generar un cluster usando otras máquinas

hosts <- c(rep("localhost", 8), rep("192.168.0.10", 8))
cl <- makePSOCKcluster(names=hosts)
clusterCall(cl, rnorm, 5)
stopCluster(cl)

The cluster routines in parallel are good if you know you will eventually have to move to using multiple computers (nodes in a cluster, or desktops in a lab) for a single computation.

foreach and doparalel

The “master/worker” approach that parallel enables works extremely well for moderately sized problems, and isn’t that difficult to use. It is all based on one form of R iteration, apply, which is well understood.

However, going from serial to parallel requires some re-writing, and even going from one method of parallelism to another (eg, multicore-style to snow-style) requires some modification of code.

The foreach package is based on another style of iterating through data - a for loop - and is designed so that one can go from serial to several forms of parallel relatively easily. There are then a number of tools one can use in the library to improve performance.

for (i in 1:3) print(sqrt(i))
library(foreach)
foreach (i=1:3) %do% sqrt(i)
library(doParallel)
registerDoParallel(3)  # use multicore-style forking
foreach (i=1:3) %dopar% sqrt(i)
stopImplicitCluster()
cl <- makePSOCKcluster(3)
registerDoParallel(cl)  # use the just-made PSOCK cluster
foreach (i=1:3) %dopar% sqrt(i)
foreach (i=1:3, .combine=c) %do% sqrt(i)
foreach (i=1:3, .combine="+") %do% sqrt(i)

%%%%%%%%%%

%%%%%%%%%% %%%%%%%%%% %%%%%%%%%% %%%%%%%%%% %%%%%%%%%% %%%%%%%%%% %%%%%%%%%% %%%%%%%%%%
LS0tIAp0aXRsZTogIkNvbXB1dGFjacOzbiBlbiBwYXJhbGVsbyIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5wYXRoID0gJy4vYXNzZXRzLycpCmBgYAoKRW1wZXphcmVtb3MgdmllbmRvIGPDs21vIHVzYXIgYHBhcmFsbGVsYCB5IGxhcyBmdW5jaW9uZXMKYG1jcGFyYWxsZWwvbWNjb2xsZWN0YC4gRWplY3V0YW1vcyBsYXMgdGFyZWFzIHVzYW5kbyBgbWNwYXJhbGxlbGAgeQpyZWN1cGVybW9zIGxvcyByZXN1bHRhZG9zIHVzYW5kbyBgbWNjb2xsZWN0YC4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGFyYWxsZWwpCmxpYnJhcnkobWljcm9iZW5jaG1hcmspICMgUGFyYSBldmFsdWFyIGN1YW50byB0YXJkYQpgYGAKCmBgYHtyfSAKZXBhIDwtIHJlYWRSRFMoIi4vYXNzZXRzL2NsZWFuLWVwYS5SRFMiKQplcGEkd29ya3MgPC0gZXBhJHRyYXJlbSA9PSAxCgpwYXJmaXRzIDwtIGZ1bmN0aW9uKCkgewogIHBmaXQgPC0gbWNwYXJhbGxlbChnbG0od29ya3MgfiBlZHVjYXRpb24sIGRhdGE9ZXBhLCBmYW1pbHk9Ymlub21pYWwpKQogIG1jY29sbGVjdChsaXN0KHBmaXQpKQp9CmBgYAoKYGBge3J9Cm1ibSA8LSBtaWNyb2JlbmNobWFyaygicGFyLmJvb3QiPXBhcmZpdHMoKSwKICAgICAgICAgICAgICAgICAgICAgInNlcmlhbC5ib290Ij1nbG0od29ya3MgfiBlZHVjYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZXBhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9Ymlub21pYWwpLAogICAgICAgICAgICAgICAgICAgICB0aW1lcz0xMCkKYXV0b3Bsb3QobWJtKQpgYGAKCkNsZWFybHkgYWN0dWFsbHkgZm9ya2luZyB0aGUgcHJvY2Vzc2VzIGFuZCB3YWl0aW5nIGZvciB0aGVtIHRvIHJlam9pbgppdHNlbGYgdGFrZXMgc29tZSB0aW1lLgoKIyBTZW1pbGxhcyBhbGVhdG9yaWFzIHkgcGFyYWxhbGVsaXNtbwoKClVzYXJlbW9zIGxhIHZlcnNpw7NuIHBhcmFsZWxpemFkYSBkZSBgbGFwcGx5YAoKYGBge3J9Cm1pY3JvYmVuY2htYXJrKCJzZXJpYWwiPXVubGlzdChsYXBwbHkoMToxMCwgZnVuY3Rpb24oeCkgcm5vcm0oMWUzKSkpLAogICAgICAgICAgICAgICAicGFyMiI9dW5saXN0KG1jbGFwcGx5KDE6MTAsIGZ1bmN0aW9uKHgpIHJub3JtKDFlMyksIG1jLmNvcmVzPTIpKSwKICAgICAgICAgICAgICAgInBhcjQiPXVubGlzdChtY2xhcHBseSgxOjEwLCBmdW5jdGlvbih4KSBybm9ybSgxZTMpLCBtYy5jb3Jlcz00KSksCiAgICAgICAgICAgICAgIHRpbWVzPTEwMCkKYGBgCgpgYGB7cn0KbWljcm9iZW5jaG1hcmsoInNlcmlhbCI9dW5saXN0KGxhcHBseSgxOjEwLCBmdW5jdGlvbih4KSBybm9ybSgxZTUpKSksCiAgICAgICAgICAgICAgICJwYXIyIj11bmxpc3QobWNsYXBwbHkoMToxMCwgZnVuY3Rpb24oeCkgcm5vcm0oMWU1KSwgbWMuY29yZXM9MikpLAogICAgICAgICAgICAgICAicGFyNCI9dW5saXN0KG1jbGFwcGx5KDE6MTAsIGZ1bmN0aW9uKHgpIHJub3JtKDFlNSksIG1jLmNvcmVzPTQpKSwKICAgICAgICAgICAgICAgdGltZXM9MTAwKQpgYGAKCmBgYHtyfQpjaXMgPC0gcmVhZFJEUygnLi9hc3NldHMvY2xlYW4tZGF0YS5SRFMnKQpgYGAKCmBgYHtyfQpjaXMgPC0gY2lzWywgYygiZWNvbm9teSIsCiAgICAgICAgICAgICAgImVjb25yaWdodHRyYWNrIiwKICAgICAgICAgICAgICAicG9saXRpY3MiLAogICAgICAgICAgICAgICJwb2xyaWdodHRyYWNrIiwKICAgICAgICAgICAgICAiaWRlb2xvZ3kiLAogICAgICAgICAgICAgICJnb3ZldmFsIildCmNpcyA8LSBjaXNbY29tcGxldGUuY2FzZXMoY2lzKSwgXQpjaXMgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGNpcywgYXMubnVtZXJpYykpCmBgYAoKYGBge3J9CnN5c3RlbS50aW1lKHNlcmlhbC5yZXMgPC0ga21lYW5zKGNpcywgY2VudGVycz0zLCBuc3RhcnQ9MjApKQpzZXJpYWwucmVzJHRvdC53aXRoaW5zcwpgYGAKCmBgYHtyfSAKZG9fbl9rbWVhbnMgPC0gZnVuY3Rpb24obikgewogICAgcmV0dXJuKGttZWFucyhjaXMsIGNlbnRlcnM9NywgbnN0YXJ0PW4pKQp9CgpzeXN0ZW0udGltZShsaXN0LnJlcyA8LSBsYXBwbHkocnVuaWYoNCwgMSwgMTAwKSwgZG9fbl9rbWVhbnMpKQpyZXMgPC0gc2FwcGx5KGxpc3QucmVzLCBmdW5jdGlvbih4KSB4JHRvdC53aXRoaW5zcykKcmVzCmBgYAoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gcmVzIC0tPgo8IS0tIGBgYCAtLT4KCmBgYHtyfSAKc3lzdGVtLnRpbWUobGlzdC5yZXMgPC0gbWNsYXBwbHkocnVuaWYoNCwgMSwgMTAwMCksIGRvX25fa21lYW5zLCBtYy5jb3Jlcz00KSkKcmVzIDwtIHNhcHBseShsaXN0LnJlcywgZnVuY3Rpb24oeCkgeCR0b3Qud2l0aGluc3MpCnJlcwpgYGAKCmBgYHtyfQpSTkdraW5kKCJMJ0VjdXllci1DTVJHIikKCnN5c3RlbS50aW1lKGxpc3QucmVzIDwtIG1jbGFwcGx5KHJ1bmlmKDQsIDEsIDEwMDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25fa21lYW5zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzPTQsIG1jLnNldC5zZWVkPUZBTFNFKSkKcmVzIDwtIHNhcHBseShsaXN0LnJlcywgZnVuY3Rpb24oeCkgeCR0b3Qud2l0aGluc3MpCnJlcwpgYGAKCm1jcGFyYWxsZWwgd29ya3MgdmVyeSB3ZWxsIGZvciB0YXNrIHBhcmFsbGVsaXNtOyB0aGUgbWNsYXBwbHkgZm9yIGRhdGEKcGFyYWxsZWxpc20uCgpUaGluZ3MgdG8gd2F0Y2ggZm9yOgoKKiBNb2RpZnlpbmcgdGhlIGJpZyBjb21tb24gZGF0YSBzdHJ1Y3R1cmU6CiAgLSBXb24ndCBiZSBzZWVuIGJ5IG90aGVyIHByb2Nlc3NlcywKICAtIEJ1dCB3aWxsIGJsb3cgdXAgdGhlIG1lbW9yeSByZXF1aXJlbWVudHMKKiBZb3UgY2FuIG9ubHkgdXNlIG9uZSBtYWNoaW5lJ3MgcHJvY2Vzc29ycwoqIFdvbid0IHdvcmsgb24gV2luZG93cyAKKiBtYy5jb3JlcyBpcyBhIGxpZS4gSXQncyB0aGUgbnVtYmVyIG9mIHRhc2tzLCBub3QgY29yZXMuIAoKIyBNdWx0aXBsZSBjb21wdXRlcnMKCmBgYHtyfQpsaWJyYXJ5KHBhcmFsbGVsKQpjbCA8LSBtYWtlQ2x1c3Rlcig0KQpgYGAKCmBgYHtyfQpjbHVzdGVyQ2FsbChjbCwgcm5vcm0sIDUpCmBgYAoKY2x1c3RlckNhbGwoKSBydW5zIHRoZSBzYW1lIGZ1bmN0aW9uIChoZXJlLCBybm9ybSwgd2l0aCBhcmd1bWVudCA1KSBvbgphbGwgd29ya2VycyBpbiB0aGUgY2x1c3Rlci4gQSByZWxhdGVkIGhlbHBlciBmdW5jdGlvbiBpcwpjbHVzdGVyRXZhbFEoKSB3aGljaCBpcyBoYW5kaWVyIHRvIHVzZSBmb3Igc29tZSBzZXR1cCB0YXNrcyAtIGVnLAoKYGBge3J9CmNsdXN0ZXJFdmFsUShjbCwge2xpYnJhcnkocGFyYWxsZWwpOyBOVUxMfSkKYGBgCgpgYGB7cn0KcmVzIDwtIGNsdXN0ZXJBcHBseShjbCwgcmVwKDEwLCA0KSwgZG9fbl9rbWVhbnMpCnN0b3BDbHVzdGVyKGNsKQpgYGAKCkVsIGVycm9yIGVzIHBvcnF1ZSBubyBoZW1vcyBjb3BpYWRvIGxvcyBkYXRvcy4gCgpSZWNhbGwgdGhhdCB3ZSBhcmVuJ3QgZm9ya2luZyBoZXJlOyB3ZSBhcmUgY3JlYXRpbmcgcHJvY2Vzc2VzIGZyb20Kc2NyYXRjaC4gVGhlc2UgcHJvY2Vzc2VzLCBuZXcgdG8gdGhpcyB3b3JsZCwgYXJlIG5vdCBmYW1pbGlhciB3aXRoIG91cgp3YXlzLCBjdXN0b21zLCBvciBkYXRhc2V0cy4gV2UgYWN0dWFsbHkgaGF2ZSB0byBzaGlwIHRoZSBkYXRhIG91dCB0bwp0aGUgd29ya2VyczoKCmBgYHtyfQpzeXN0ZW0udGltZShjbHVzdGVyRXhwb3J0KGNsLCAiY2lzIikpCnJlcyA8LSBjbHVzdGVyQXBwbHkoY2wsIHJlcCgxMCwgNCksIGRvX25fa21lYW5zKQpyZXMgPC0gc2FwcGx5KGxpc3QucmVzLCBmdW5jdGlvbih4KSB4JHRvdC53aXRoaW5zcykKbGFwcGx5LnJlcyA8LSBsaXN0LnJlc1tbd2hpY2gubWluKHJlcyldXQpsYXBwbHkucmVzJHdpdGhpbnNzCnJlcwpgYGAKClBvZGVtb3MgZ2VuZXJhciB1biBjbHVzdGVyIHVzYW5kbyBvdHJhcyBtw6FxdWluYXMKCmBgYHtyfQpob3N0cyA8LSBjKHJlcCgibG9jYWxob3N0IiwgOCksIHJlcCgiMTkyLjE2OC4wLjEwIiwgOCkpCmNsIDwtIG1ha2VQU09DS2NsdXN0ZXIobmFtZXM9aG9zdHMpCmNsdXN0ZXJDYWxsKGNsLCBybm9ybSwgNSkKc3RvcENsdXN0ZXIoY2wpCmBgYAoKVGhlIGNsdXN0ZXIgcm91dGluZXMgaW4gcGFyYWxsZWwgYXJlIGdvb2QgaWYgeW91IGtub3cgeW91IHdpbGwKZXZlbnR1YWxseSBoYXZlIHRvIG1vdmUgdG8gdXNpbmcgbXVsdGlwbGUgY29tcHV0ZXJzIChub2RlcyBpbiBhCmNsdXN0ZXIsIG9yIGRlc2t0b3BzIGluIGEgbGFiKSBmb3IgYSBzaW5nbGUgY29tcHV0YXRpb24uCgotIFVzZSBjbHVzdGVyRXhwb3J0IGZvciBmdW5jdGlvbnMgYW5kIGRhdGEgdGhhdCB3aWxsIGJlIG5lZWRlZCBieSBldmVyeW9uZS4KLSBDb21tdW5pY2F0aW5nIGRhdGEgaXMgc2xvdywgYnV0IG11Y2ggZmFzdGVyIHRoYW4gaGF2aW5nIGV2ZXJ5IHdvcmtlciByZWFkIHRoZSBzYW1lIGRhdGEgZnJvbSBhIGZpbGUuCi0gVXNlIGNsdXN0ZXJBcHBseUxCIGlmIHRoZSB0YXNrcyB2YXJ5IGdyZWF0bHkgaW4gcnVudGltZS4KLSBVc2UgY2x1c3RlckFwcGx5IGlmIGVhY2ggdGFzayByZXF1aXJlcyBhbiBlbm9ybW91cyBhbW91bnQgb2YgZGF0YS4KCiMgZm9yZWFjaCBhbmQgZG9wYXJhbGVsCgpUaGUg4oCcbWFzdGVyL3dvcmtlcuKAnSBhcHByb2FjaCB0aGF0IHBhcmFsbGVsIGVuYWJsZXMgd29ya3MgZXh0cmVtZWx5CndlbGwgZm9yIG1vZGVyYXRlbHkgc2l6ZWQgcHJvYmxlbXMsIGFuZCBpc24ndCB0aGF0IGRpZmZpY3VsdCB0byB1c2UuCkl0IGlzIGFsbCBiYXNlZCBvbiBvbmUgZm9ybSBvZiBSIGl0ZXJhdGlvbiwgYXBwbHksIHdoaWNoIGlzIHdlbGwKdW5kZXJzdG9vZC4KCkhvd2V2ZXIsIGdvaW5nIGZyb20gc2VyaWFsIHRvIHBhcmFsbGVsIHJlcXVpcmVzIHNvbWUgcmUtd3JpdGluZywgYW5kCmV2ZW4gZ29pbmcgZnJvbSBvbmUgbWV0aG9kIG9mIHBhcmFsbGVsaXNtIHRvIGFub3RoZXIgKGVnLAptdWx0aWNvcmUtc3R5bGUgdG8gc25vdy1zdHlsZSkgcmVxdWlyZXMgc29tZSBtb2RpZmljYXRpb24gb2YgY29kZS4KClRoZSBmb3JlYWNoIHBhY2thZ2UgaXMgYmFzZWQgb24gYW5vdGhlciBzdHlsZSBvZiBpdGVyYXRpbmcgdGhyb3VnaApkYXRhIC0gYSBmb3IgbG9vcCAtIGFuZCBpcyBkZXNpZ25lZCBzbyB0aGF0IG9uZSBjYW4gZ28gZnJvbSBzZXJpYWwgdG8Kc2V2ZXJhbCBmb3JtcyBvZiBwYXJhbGxlbCByZWxhdGl2ZWx5IGVhc2lseS4gVGhlcmUgYXJlIHRoZW4gYSBudW1iZXIKb2YgdG9vbHMgb25lIGNhbiB1c2UgaW4gdGhlIGxpYnJhcnkgdG8gaW1wcm92ZSBwZXJmb3JtYW5jZS4KCmBgYHtyfQpmb3IgKGkgaW4gMTozKSBwcmludChzcXJ0KGkpKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGZvcmVhY2gpCmZvcmVhY2ggKGk9MTozKSAlZG8lIHNxcnQoaSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZG9QYXJhbGxlbCkKcmVnaXN0ZXJEb1BhcmFsbGVsKDMpICAjIHVzZSBtdWx0aWNvcmUtc3R5bGUgZm9ya2luZwpmb3JlYWNoIChpPTE6MykgJWRvcGFyJSBzcXJ0KGkpCnN0b3BJbXBsaWNpdENsdXN0ZXIoKQpgYGAKCmBgYHtyfQpjbCA8LSBtYWtlUFNPQ0tjbHVzdGVyKDMpCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkgICMgdXNlIHRoZSBqdXN0LW1hZGUgUFNPQ0sgY2x1c3Rlcgpmb3JlYWNoIChpPTE6MykgJWRvcGFyJSBzcXJ0KGkpCmBgYAoKYGBge3J9CmZvcmVhY2ggKGk9MTozLCAuY29tYmluZT1jKSAlZG8lIHNxcnQoaSkKZm9yZWFjaCAoaT0xOjMsIC5jb21iaW5lPSIrIikgJWRvJSBzcXJ0KGkpCmBgYAoKCgolJSUlJSUlJSUlIAoKXGJlZ2lue2ZyYW1lfXtQYWNrYWdlc30KICBSIGRvZXMge3NvbWV9IHBhcmFsbGVsaXNtIGJ1dCB0aGVyZSBhcmUgcGFja2FnZXMgdGhhdCBleHBsaWNpdGx5IHVzZQogIHBhcmFsbGVsaXNtCgogIEZvciBhIGNvbXBsZXRlIGxpc3QsIHNlZQoKICBcdXJse2h0dHA6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3ZpZXdzL0hpZ2hQZXJmb3JtYW5jZUNvbXB1dGluZy5odG1sfQoKCiAgU29tZSBwYWNrYWdlIHNpbXBsaWZ5IGludGVyZWN0IHdpdGggcGFyYWxsZWw6CgogIENhcmV0CgogIENhcmV0IGlzIGEgd2lkZWx5LXVzZWQgbWFjaGluZSBsZWFybmluZyBwYWNrYWdlLCB0aGF0IHVzZXMgZm9yZWFjaAogICh3aGljaCB3ZSdsbCBsZWFybiBhYm91dCkgdG8gcGFyYWxsZWxpemUgdGhpbmdzIGxpa2UgQ1YtZm9sZHMsIGV0YwpcZW5ke2ZyYW1lfQoKCiUlJSUlJSUlJSUKXGJlZ2lue2ZyYW1lfXtUaGUgUGFyYWxsZWwgUGFja2FnZX0KICBTaW5jZSBSIDIuMTQuMCAobGF0ZSAyMDExKSwgdGhlIHBhcmFsbGVsIHBhY2thZ2UgaGFzIGJlZW4gcGFydCBvZgogIGNvcmUgUi4KCiAgSW5jb3Jwb3JhdGVzIHR3byBvdGhlciBwYWNrYWdlczoKCiAgbXVsdGljb3JlOiBmb3IgdXNpbmcgYWxsIHByb2Nlc3NvcnMgb24gYSBzaW5nbGUgcHJvY2Vzc29yLiBOb3Qgb24KICB3aW5kb3dzLgoKICBzbm93OiBmb3IgdXNpbmcgYW55IGdyb3VwIG9mIHByb2Nlc3NvcnMsIHBvc3NpYmx5IGFjcm9zcyBhIGNsdXN0ZXIuCgogIE1hbnkgcGFja2FnZXMgd2hpY2ggdXNlIHBhcmFsbGVsaXNtIHVzZSBvbmUgb2YgdGhlc2UgdHdvLCBzbyB3b3J0aAogIHVuZGVyc3RhbmRpbmcuCgogIEJvdGggY3JlYXRlIG5ldyBwcm9jZXNzZXMgKG5vdCB0aHJlYWRzKSB0byBydW4gb24gZGlmZmVyZW50CiAgcHJvY2Vzc29yczsgYnV0IGluIGltcG9ydGFudGx5IGRpZmZlcmVudCB3YXlzLgpcZW5ke2ZyYW1lfQoKCiUlJSUlJSUlJSUKXGJlZ2lue2ZyYW1lfXttY3BhcmFsbGVsL21jY29sbGVjdH0KCiAgVGhlIHNpbXBsZXN0IHVzZSBvZiB0aGUgbXVsdGljb3JlIHBhY2thZ2UgaXMgdGhlIHBhaXIgb2YgZnVuY3Rpb25zCiAgbWNwYXJhbGxlbCgpIGFuZCBtY2NvbGxlY3QoKS4KCiAgbWNwYXJhbGxlbCgpIGZvcmtzIGEgdGFzayB0byBydW4gYSBnaXZlbiBmdW5jdGlvbjsgaXQgdGhlbiBydW5zIGluCiAgdGhlIGJhY2tncm91bmQuIG1jY29sbGVjdCgpIHdhaXRzIGZvciBhbmQgZ2V0cyB0aGUgcmVzdWx0LgoKICBMZXQncyBwaWNrIGFuIGV4YW1wbGU6IHJlYWRpbmcgdGhlIGFpcmxpbmVzIGRhdGEgc2V0LCB3ZSB3YW50IC0tLQogIGZvciBhIHBhcnRpY3VsYXIgbW9udGggLS0tIHRvIGtub3cgYm90aCB0aGUgdG90YWwgbnVtYmVyIG9mIHBsYW5lcwogIGluIHRoZSBkYXRhIChieSB0YWlsIG51bWJlcikgYW5kIHRoZSBtZWRpYW4gZWxhcHNlZCBmbGlnaHQgdGltZS4KICBUaGVzZSBhcmUgdHdvIGluZGVwZW5kYW50IGNhbGN1bGF0aW9ucywgYW5kIHNvIGNhbiBiZSBkb25lCiAgaW5kZXBlbmRhbnRseS4KXGVuZHtmcmFtZX0KCiUlJSUlJSUlJSUKXGJlZ2lue2ZyYW1lfXtTZWVkc30KICBQYXJhbGxlbCBSTkcgKHByb2JsZW1zIHdpdGggcmVwcm9kdWNpYmlsaXR5IGJlY2F1c2UgZGlmZmVyZW50CiAgcHJvY2Vzc2VzKQoKICBEZXBlbmRpbmcgb24gd2hhdCB5b3UgYXJlIGRvaW5nLCBpdCBtYXkgYmUgdmVyeSBpbXBvcnRhbnQgdG8gaGF2ZQogIGRpZmZlcmVudCAob3IgdGhlIHNhbWUhKSByYW5kb20gbnVtYmVycyBnZW5lcmF0ZWQgaW4gZWFjaCBwcm9jZXNzLgoKICBIZXJlLCB3ZSBkZWZpbml0ZWx5IHdhbnQgdGhlbSBkaWZmZXJlbnQgLSB0aGUgd2hvbGUgcG9pbnQgaXMgdG8KICBnZW5lcmF0ZSBkaWZmZXJlbnQgcmFuZG9tIHJlYWxpemF0aW9ucy4KCiAgcGFyYWxsZWwgaGFzIGEgZ29vZCBSTkcgc3VpdGFibGUgZm9yIHBhcmFsbGVsIHdvcmsgYmFzZWQgb24gdGhlIHdvcmsKICBvZiBQaWVycmUgTCdFY3V5ZXIgaW4gTW9udHLDqWFsOgpcZW5ke2ZyYW1lfQoKJSUlJSUlJSUlJQpcYmVnaW57ZnJhbWV9e1N1bW1hcnk6IHBhcmFsbGVsL211bHRpY29yZX0KVGhlIG1jKiByb3V0aW5lcyBpbiBwYXJhbGxlbCB3b3JrIHBhcnRpY3VsYXJseSB3ZWxsIHdoZW46CgpZb3Ugd2FudCB0byBtYWtlIGZ1bGwgdXNlIG9mIHRoZSBwcm9jZXNzb3JzIG9uIGEgc2luZ2xlIGNvbXB1dGVyIEVhY2gKdGFzayBvbmx5IHJlYWRzIGZyb20gc29tZSBiaWcgY29tbW9uIGRhdGEgc3RydWN0dXJlIGFuZCBwcm9kdWNlcwptb2Rlc3Qtc2l6ZWQgcmVzdWx0cyBtY3BhcmFsbGVsIHdvcmtzIHZlcnkgd2VsbCBmb3IgdGFzayBwYXJhbGxlbGlzbTsKdGhlIG1jbGFwcGx5IGZvciBkYXRhIHBhcmFsbGVsaXNtLgoKVGhpbmdzIHRvIHdhdGNoIGZvcjoKCk1vZGlmeWluZyB0aGUgYmlnIGNvbW1vbiBkYXRhIHN0cnVjdHVyZTogV29uJ3QgYmUgc2VlbiBieSBvdGhlcgpwcm9jZXNzZXMsIEJ1dCB3aWxsIGJsb3cgdXAgdGhlIG1lbW9yeSByZXF1aXJlbWVudHMgWW91IGNhbiBvbmx5IHVzZSBvbmUKbWFjaGluZSdzIHByb2Nlc3NvcnMgV29uJ3Qgd29yayBvbiBXaW5kb3dzIChidXQgd2hhdCBkb2VzPykKClxlbmR7ZnJhbWV9CgolJSUlJSUlJSUlClxiZWdpbntmcmFtZX17TXVsdGlwbGUgY29tcHV0ZXJzIHdpdGggcGFyYWxsZWwvc25vd30KVGhlIG90aGVyIGhhbGYgb2YgcGFyYWxsZWwsIHJvdXRpbmVzIHRoYXQgd2VyZSBpbiB0aGUgc3RpbGwtYWN0aXZlIHNub3cKcGFja2FnZSwgYWxsb3cgeW91IHRvIGFnYWluIGxhdW5jaCBuZXcgUiBwcm9jZXNzZXMgLS0tIGJ5IGRlZmF1bHQsIG9uCnRoZSBjdXJyZW50IGNvbXB1dGVyLCBidXQgYWxzbyBvbiBhbnkgY29tcHV0ZXIgeW91IGhhdmUgYWNjZXNzIHRvLiAoU05PVwpzdGFuZHMgZm9yIGBgU2ltcGxlIE5ldHdvcmsgb2YgV29ya3N0YXRpb25zJycsIHdoaWNoIHdhcyB0aGUgb3JpZ2luYWwKdXNlIGNhc2UpLgoKVGhlIHJlY2lwZSBmb3IgZG9pbmcgY29tcHV0YXRpb25zIHdpdGggc25vdyBsb29rcyBzb21ldGhpbmcgbGlrZToKCm90aGVyIHRoYW4gdGhlIG1ha2VDbHVzdGVyKCkvc3RvcENsdXN0ZXIoKSwgaXQgbG9va3MgdmVyeSBtdWNoIGxpa2UKbXVsdGljb3JlIGFuZCBtY2xhcHBseS4KClJlY2FsbCB0aGF0IHdlIGFyZW4ndCBmb3JraW5nIGhlcmU7IHdlIGFyZSBjcmVhdGluZyBwcm9jZXNzZXMgZnJvbQpzY3JhdGNoLiBUaGVzZSBwcm9jZXNzZXMsIG5ldyB0byB0aGlzIHdvcmxkLCBhcmUgbm90IGZhbWlsaWFyIHdpdGggb3VyCndheXMsIGN1c3RvbXMsIG9yIGRhdGFzZXRzLiBXZSBhY3R1YWxseSBoYXZlIHRvIHNoaXAgdGhlIGRhdGEgb3V0IHRvIHRoZQp3b3JrZXJzCgoKTm90ZSB0aGF0IHRoZSBjb3N0cyBvZiBzaGlwcGluZyBvdXQgZGF0YSBiYWNrIGFuZCBmb3J0aCwgYW5kIGNyZWF0aW5nCnRoZSBwcm9jZXNzZXMgZnJvbSBzY3JhdGNoLCBpcyByZWxhdGl2ZWx5IGNvc3RseSAtIGJ1dCB0aGlzIGlzIHRoZSBwcmljZQp3ZSBwYXkgZm9yIGJlaW5nIGFibGUgdG8gc3Bhd24gdGhlIHByb2Nlc3NlcyBhbnl3aGVyZS4KClxlbmR7ZnJhbWV9CgoKJSUlJSUlJSUlJQpcYmVnaW57ZnJhbWV9e2ZvcmVhY2ggYW5kIGRvcGFyYWxsZWx9CgogIFRoZSBgYG1hc3Rlci93b3JrZXInJyBhcHByb2FjaCB0aGF0IHBhcmFsbGVsIGVuYWJsZXMgd29ya3MgZXh0cmVtZWx5CiAgd2VsbCBmb3IgbW9kZXJhdGVseSBzaXplZCBwcm9ibGVtcywgYW5kIGlzbid0IHRoYXQgZGlmZmljdWx0IHRvIHVzZS4KICBJdCBpcyBhbGwgYmFzZWQgb24gb25lIGZvcm0gb2YgUiBpdGVyYXRpb24sIGFwcGx5LCB3aGljaCBpcyB3ZWxsCiAgdW5kZXJzdG9vZC4KCiAgSG93ZXZlciwgZ29pbmcgZnJvbSBzZXJpYWwgdG8gcGFyYWxsZWwgcmVxdWlyZXMgc29tZSByZS13cml0aW5nLCBhbmQKICBldmVuIGdvaW5nIGZyb20gb25lIG1ldGhvZCBvZiBwYXJhbGxlbGlzbSB0byBhbm90aGVyIChlZywKICBtdWx0aWNvcmUtc3R5bGUgdG8gc25vdy1zdHlsZSkgcmVxdWlyZXMgc29tZSBtb2RpZmljYXRpb24gb2YgY29kZS4KCiAgVGhlIGZvcmVhY2ggcGFja2FnZSBpcyBiYXNlZCBvbiBhbm90aGVyIHN0eWxlIG9mIGl0ZXJhdGluZyB0aHJvdWdoCiAgZGF0YSAtIGEgZm9yIGxvb3AgLSBhbmQgaXMgZGVzaWduZWQgc28gdGhhdCBvbmUgY2FuIGdvIGZyb20gc2VyaWFsCiAgdG8gc2V2ZXJhbCBmb3JtcyBvZiBwYXJhbGxlbCByZWxhdGl2ZWx5IGVhc2lseS4gVGhlcmUgYXJlIHRoZW4gYQogIG51bWJlciBvZiB0b29scyBvbmUgY2FuIHVzZSBpbiB0aGUgbGlicmFyeSB0byBpbXByb3ZlIHBlcmZvcm1hbmNlLgpcZW5ke2ZyYW1lfQoKJSUlJSUlJSUlJQpcYmVnaW57ZnJhbWV9e1N1bW1hcnk6IGZvcmVhY2h9CiAgCiAgRm9yZWFjaCBpcyBhIHdyYXBwZXIgZm9yIHRoZSBvdGhlciBwYXJhbGxlbCBtZXRob2RzIHdlJ3ZlIHNlZW4sIHNvCiAgaXQgaW5oZXJpdHMgc29tZSBvZiB0aGUgYWR2YW50YWdlcyBhbmQgZHJhd2JhY2tzIG9mIGVhY2guCgogIFVzZSBmb3JlYWNoIGlmOgoKICBZb3VyIGNvZGUgYWxyZWFkeSByZWx5cyBvbiBmb3Itc3R5bGUgaXRlcmF0aW9uOyB0cmFuc2l0aW9uIGlzIGVhc3kKICBZb3UgZG9uJ3Qga25vdyBpZiB5b3Ugd2FudCBtdWx0aWNvcmUgdnMuIHNub3cgc3R5bGUgcGFyYWxsZWwgdXNlIChvcgogIG90aGVyIGtpbmRzLCBsaWtlIGJhdGNoIGpvYnMpOiB5b3UgY2FuIHN3aXRjaCBqdXN0IGJ5IHJlZ2lzdGVyaW5nIGEKICBkaWZmZXJlbnQgYmFja2VuZCEgWW91IHdhbnQgdG8gYmUgYWJsZSB0byBpbmNyZW1lbnRhbGx5IGltcHJvdmUgdGhlCiAgcGVyZm9ybWFuY2Ugb2YgeW91ciBjb2RlLgoKICBOb3RlIHRoYXQgeW91IGNhbiBoYXZlIHBvcnRpb25zIG9mIHlvdXIgYW5hbHlzaXMgY29kZSB1c2UgZm9yZWFjaAogIHdpdGggcGFyYWxsZWwgYW5kIHBvcnRpb25zIHVzaW5nIHRoZSBiYWNrZW5kIHdpdGggYXBwbHktc3R5bGUKICBwYXJhbGxlbGlzbTsgaXQgZG9lc24ndCBoYXZlIHRvIGJlIGFsbCBvbmUgb3IgdGhlIG90aGVyLgpcZW5ke2ZyYW1lfQoKJSUlJSUlJSUlJQpcYmVnaW57ZnJhbWV9e1N1bW1hcnl9ClIgY29tZXMgd2l0aCBhbiBpbmNyZWFzaW5nbHkgcmljaCBzZXQgb2YgdG9vbHMgZm9yIHRha2luZyBhZHZhbnRhZ2Ugb2YKbW9yZSBjb21wdXRlIHBvd2VyOgoKcGFyYWxsZWwgZm9yZWFjaC9kb1BhcmFsbGVsCgpLZWVwIGluIG1pbmQgd2hhdCB3ZSB0YWxrZWQgYWJvdXQgaW4gdGVybXMgb2Ygb3ZlcmhlYWQsIGFuZDoKCkRvbid0IHJlaW52ZW50IHdoZWVscyBCaWcgY2h1bmtzIGFyZSBiZXR0ZXIgdGhhbiBsaXR0bGUgY2h1bmtzClBhcmFsbGVsaXNtIGdpdmVzIHlvdSBtb3JlIGNvbXB1dGUsIG5vdCBJL08gT25lIHRhc2sgcGVyIGNvcmUgRG9uJ3QgdHJpcApvdmVyIHlvdXIgb3duIGZlZXQKXGVuZHtmcmFtZX0KCg==