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:

• Modifying the big common data structure:
• Won’t be seen by other processes,
• But will blow up the memory requirements
• You can only use one machine’s processors
• Won’t work on Windows
• mc.cores is a lie. It’s the number of tasks, not cores.

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

• Use clusterExport for functions and data that will be needed by everyone.
• Communicating data is slow, but much faster than having every worker read the same data from a file.
• Use clusterApplyLB if the tasks vary greatly in runtime.
• Use clusterApply if each task requires an enormous amount of data.

# 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==