diff --git a/_freeze/slides/debugging/index/execute-results/html.json b/_freeze/slides/debugging/index/execute-results/html.json
index ae27743..7384319 100644
--- a/_freeze/slides/debugging/index/execute-results/html.json
+++ b/_freeze/slides/debugging/index/execute-results/html.json
@@ -1,8 +1,8 @@
{
- "hash": "37d1438d65894950f2595fb337b993ac",
+ "hash": "1ddabd08f6a0ad667fdee614a10740dc",
"result": {
"engine": "knitr",
- "markdown": "---\ntitle: \"Debugging, Profiling, and a Bit of Optimization\"\nauthor: \"Marcin Kierczak\"\nimage: \"assets/featured.jpg\"\nformat: revealjs\n---\n\n\n## {visibility=\"hidden\"}\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(tictoc)\nlibrary(DT)\nlibrary(profvis)\n# library(Rgraphviz)\nlibrary(proftools) # depends on \"graph\" and \"Rgraphviz\" packages\nlibrary(profr)\nlibrary(pryr)\nlibrary(microbenchmark)\nlibrary(ggplot2)\n# remotes::install_github(\"hadley/emo\")\nlibrary(emo)\n# remotes::install_github(\"cdeterman/gpuR\")\n#library(gpuR)\n```\n:::\n\n\n## Run Forrest, run!\n:::{.columns}\n:::{.column width=\"50%\"}\n\n
\n\n:::{.incremental}\n- *My code does not run!* -- **debugging**
\n- *Now it does run but... out of memory!* -- **profiling**
\n- *It runs! It says it will finish in 5 ~~minutes~~ years.* -- **optimization**\n:::\n:::\n:::{.column width=\"50%\"}\n\n:::\n::: \n\n## Types of bugs {background-image=\"assets/featured.jpg\"}\n\n- ๐ฃ Syntax errors\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"1|2\"}\nprin(var1) \nmean(sum(seq((x + 2) * (y - 9 * b))))\n```\n:::\n\n\n. . .\n\n- ๐ข Arithmetic \n\n\n::: {.cell}\n\n```{.r .cell-code}\ny <- 7 / 0\n```\n:::\n\n*Not in R though! `y = Inf`*\n\n. . .\n\n- ๐๐ Type \n\n\n::: {.cell}\n\n```{.r .cell-code}\nmean('a')\n```\n:::\n\n\n. . .\n\n- ๐งฉ Logic\n\nEverything works and produces seemingly valid output that is WRONG! \nIMHO those are the hardest ๐ to debug!\n\n## How to avoid bugs\n
\n\n:::{.incremental}\n- Encapsulate your code in smaller units ๐ฑ (functions), you can test.
\n- Use classes and type checking ๐.
\n- Test ๐งช at the boundaries, e.g. loops at min and max value.
\n- Feed your functions with test data ๐พ that should result with a known output.
\n- Use *antibugging* ๐ธ: `stopifnot(y <= 75)`\n:::\n\n## Floating confusion\n\n
So, how to handle this mess?\n\n:::\n:::\n\n## Handling Errors -- `try`\n
\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntry(\n print(\n paste0('Log of ', input, ' is ', log10(as.numeric(input)))\n )\n)\n```\n:::\n\n\n. . .\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Log of 1 is 0\" \n[2] \"Log of 10 is 1\" \n[3] \"Log of -7 is NaN\" \n[4] \"Log of -0.4 is NaN\" \n[5] \"Log of 0 is -Inf\" \n[6] \"Log of char is NA\" \n[7] \"Log of 100 is 2\" \n[8] \"Log of 3.14159265358979 is 0.497149872694133\"\n[9] \"Log of NaN is NaN\" \n```\n\n\n:::\n:::\n\n\n## Handling Errors -- `tryCatch` block:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nresult <- tryCatch(log10(val), \n warning = function(w) { \n print('Warning! Negative argument supplied. Negating.') \n log10(-val) }, \n error = function(e) { \n print('ERROR! Not a number!')\n NaN\n }\n )\n```\n:::\n\n\n. . .\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Log of 1 is 0\"\n[1] \"Log of 10 is 1\"\n[1] \"Warning! Negative argument supplied. Negating.\"\n[1] \"Log of -7 is 0.845098040014257\"\n[1] \"Warning! Negative argument supplied. Negating.\"\n[1] \"Log of -0.4 is -0.397940008672038\"\n[1] \"Log of 0 is -Inf\"\n[1] \"Log of NA is NA\"\n[1] \"Log of 100 is 2\"\n[1] \"Log of 3.14159265358979 is 0.497149872694133\"\n[1] \"Log of NaN is NaN\"\n```\n\n\n:::\n:::\n\n\n## Debugging -- errors and warnings\n\n:::{.incremental}\n- An error in your code will result in a call to the `stop()` function that:\n - Breaks the execution of the program (loop, if-statement, etc.)\n - Performs the action defined by the global parameter `error`.\n- A warning just prints out the warning message (or reports it in another way)\n:::\n\n. . .\n\n- Global parameter `error` defines what R should do when an error occurs.\n\n\n::: {.cell}\n\n```{.r .cell-code}\noptions(error = )\n```\n:::\n\n\n. . .\n\n- You can use `simpleError()` and `simpleWarning()` to generate errors and warnings in your code:\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"4\"}\nf <- function(x) {\n if (x < 0) {\n x <- abs(x)\n w <- simpleWarning(\"Value less than 0. Taking abs(x)\")\n w\n }\n}\n```\n:::\n\n\n## Debugging -- what are my options?\n\n- Old-school debugging: a lot of `print` statements\n - print values of your variables at some checkpoints,\n - sometimes fine but often laborious,\n - need to remove/comment out manually after debugging.\n\n. . .\n\n- Dumping frames\n - on error, R state will be saved to a file,\n - file can be read into debugger,\n - values of all variables can be checked,\n - can debug on another machine, e.g. send dump to your colleague!\n \n. . .\n\n- Traceback\n - a list of the recent function calls with values of their parameters\n \n. . .\n\n- Step-by-step debugging\n - execute code line by line within the debugger\n\n## Option 1: dumping frames\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|4-6\"}\nf <- function(x) { sin(x) }\noptions(error = quote(dump.frames(dumpto = \"assets/testdump\", to.file = T)))\nf('test')\noptions(error = NULL) # reset the behavior\nload('assets/testdump.rda')\n# debugger(testdump)\n```\n:::\n\nHint: Last empty line brings you back to the environments menu.\n\n## Option 2: traceback\n\n\n::: {.cell}\n\n```{.r .cell-code}\nf <- function(x) { \n log10(x) \n}\ng <- function(x) { \n f(x) \n}\ng('test')\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in log10(x): non-numeric argument to mathematical function\n```\n\n\n:::\n:::\n\n\n. . .\n\n```\n> traceback()\n2: f(x) at #2\n1: g(\"test\")\n```\n\n`traceback()` shows what were the function calls and what parameters were passed to them when the error occurred.\n\n## Option 3: step-by-step debugging\n\n:::: {.columns}\n::: {.column width=\"50%\"}\n\nLet us define a new function `h(x, y)`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nh <- function(x, y) { \n f(x) \n f(y) \n}\n```\n:::\n\n\nNow, we can use `debug()` to debug the function in a step-by-step manner:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndebug(h)\nh('text', 7)\nundebug(h)\n```\n:::\n\n\n:::\n\n::: {.column .fragment width=\"50%\"}\n\n![](assets/debug.png)\n\n:::\n::::\n\n## Profiling -- `proc.time()`\n\nProfiling is the process of **identifying memory** and time **bottlenecks** ๐ผ in your code.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nproc.time()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 2.204 1.819 1.519 \n```\n\n\n:::\n:::\n\n\n- `user time` -- CPU time charged for the execution of user instructions of the calling process,\n- `system time` -- CPU time charged for execution by the system on behalf of the calling process,\n- `elapsed time` -- total CPU time elapsed for the currently running R process.\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\npt1 <- proc.time()\ntmp <- runif(n = 10e5)\npt2 <- proc.time()\npt2 - pt1\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 0.014 0.000 0.015 \n```\n\n\n:::\n:::\n\n\n## Profiling -- `system.time()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsystem.time(runif(n = 10e6))\nsystem.time(rnorm(n = 10e6))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 0.167 0.002 0.168 \n user system elapsed \n 0.596 0.002 0.598 \n```\n\n\n:::\n:::\n\n\n. . .\n\nAn alternative approach is to use `tic` and `toc` statements from the `tictoc` package.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(tictoc)\ntic()\ntmp1 <- runif(n = 10e6)\ntoc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n0.135 sec elapsed\n```\n\n\n:::\n:::\n\n\n## Profiling in action\n\nThese 4 functions fill a **large vector** with values supplied by function `f`.\n\n. . .\n\n1 -- loop without memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3-5\"}\nfun_fill_loop1 <- function(n = 10e6, f) {\n result <- NULL\n for (i in 1:n) {\n result <- c(result, eval(call(f, 1)))\n }\n return(result)\n}\n```\n:::\n\n\n. . .\n\n2 -- loop with memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3-5\"}\nfun_fill_loop2 <- function(n = 10e6, f) {\n result <- vector(length = n)\n for (i in 1:n) {\n result[i] <- eval(call(f, 1))\n }\n return(result)\n}\n```\n:::\n\n\n## Profiling in action cted.\n\nBut it is maybe better to use...\n\n. . .\n\nvectorization!\n\n. . .\n\n3 -- vectorized loop without memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3\"}\nfun_fill_vec1 <- function(n = 10e6, f) {\n result <- NULL\n result <- eval(call(f, n))\n return(result)\n}\n```\n:::\n\n\n. . .\n\n4 -- vectorized with memory allocation.\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3\"}\nfun_fill_vec2 <- function(n = 10e6, f) {\n result <- vector(length = n)\n result <- eval(call(f, n))\n return(result)\n}\n```\n:::\n\n\n## Profiling our functions\n \n\n\n::: {.cell}\n\n```{.r .cell-code}\np1 <- system.time(fun_fill_loop1(n = 10e4, \"runif\")) # 1 - loop, no alloc\np2 <- system.time(fun_fill_loop2(n = 10e4, \"runif\")) # 2 - loop, alloc \np3 <- system.time(fun_fill_vec1(n = 10e4, \"runif\")) # 3 - vector, no alloc\np4 <- system.time(fun_fill_vec2(n = 10e4, \"runif\")) # 4 - vector, alloc\n```\n:::\n\n\n|fn | user.self| sys.self| elapsed|\n|:---|---------:|--------:|-------:|\n|fn1 | 7.641| 0.05| 7.692|\n|fn2 | 0.320| 0.00| 0.320|\n|fn3 | 0.001| 0.00| 0.001|\n|fn4 | 0.002| 0.00| 0.002|\n\n\nThe `system.time()` function is not the most accurate though. During the lab, we will experiment with package `microbenchmark`.\n\n## More advanced profiling\n\nWe can also do a bit more advanced profiling, including the memory profiling, using, e.g. `Rprof()` function.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nRprof('profiler_test.out', interval = 0.01, memory.profiling = T)\nfor (i in 1:5) {\n result <- fun_fill_loop2(n = 10e4, \"runif\")\n print(result)\n}\nRprof(NULL)\n```\n:::\n\n\nAnd let us summarise:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsummary <- summaryRprof(\"profiler_test.out\", memory = \"both\")\n#unlink(\"profiler_test.out\")\nknitr::kable(summary$by.self)\n```\n\n::: {.cell-output-display}\n\n\n| | self.time| self.pct| total.time| total.pct| mem.total|\n|:----------------|---------:|--------:|----------:|---------:|---------:|\n|\"runif\" | 2.13| 48.41| 2.13| 48.41| 1899.0|\n|\"eval\" | 1.04| 23.64| 3.29| 74.77| 3334.7|\n|\"print.default\" | 0.85| 19.32| 0.85| 19.32| 72.1|\n|\"fun_fill_loop2\" | 0.24| 5.45| 3.53| 80.23| 3646.3|\n|\"parent.frame\" | 0.06| 1.36| 0.06| 1.36| 64.6|\n|\"is.list\" | 0.05| 1.14| 0.05| 1.14| 82.3|\n|\"is.pairlist\" | 0.01| 0.23| 0.01| 0.23| 23.1|\n|\"mayCallBrowser\" | 0.01| 0.23| 0.01| 0.23| 1.0|\n|\"strsplit\" | 0.01| 0.23| 0.01| 0.23| 0.4|\n\n\n:::\n:::\n\n\n## Profiling -- `profr` package\n\nThere are also packages available that enable even more advanced profiling:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(profr)\nRprof(\"profiler_test2.out\", interval = 0.01)\ntmp <- table(sort(rnorm(1e5)))\nRprof(NULL)\nprofile_df <- parse_rprof('profiler_test2.out')\n```\n:::\n\n\nThis returns a table that can be visualised:\n\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n\n| level| g_id| t_id|f | start| end| n|leaf | time|source |\n|-----:|----:|----:|:-----------------------|-----:|----:|--:|:-----|----:|:------|\n| 1| 1| 1|.main | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 2| 1| 1|execute | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 3| 1| 1|rmarkdown::render | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 4| 1| 1|knitr::knit | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 5| 1| 1|process_file | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 6| 1| 1|handle_error | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 7| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 8| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 9| 1| 1|process_group | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 10| 1| 1|process_group.block | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 11| 1| 1|call_block | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 12| 1| 1|block_exec | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 13| 1| 1|eng_r | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 14| 1| 1|in_input_dir | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 15| 1| 1|in_dir | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 16| 1| 1|evaluate | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 17| 1| 1|evaluate::evaluate | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 18| 1| 1|evaluate_call | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 19| 1| 1|timing_fn | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 20| 1| 1|handle | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 21| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 22| 1| 1|withVisible | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 23| 1| 1|eval_with_user_handlers | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 24| 1| 1|eval | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 25| 1| 1|eval | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 26| 1| 1|table | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 27| 1| 1|factor | 0.00| 0.38| 1|FALSE | 0.38|base |\n| 28| 1| 1|order | 0.00| 0.02| 1|TRUE | 0.02|base |\n| 28| 2| 1|unique | 0.02| 0.22| 1|FALSE | 0.20|base |\n| 29| 1| 1|unique.default | 0.02| 0.22| 1|TRUE | 0.20|base |\n\n\n:::\n:::\n\n\n## Profiling -- `profr` package cted.\n\nWe can also plot the results using -- `proftools` package-\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nlibrary(proftools)\nprofile_df2 <- readProfileData(\"profiler_test2.out\")\nplotProfileCallGraph(profile_df2, style = google.style, score = \"total\")\n```\n\n::: {.cell-output-display}\n![](index_files/figure-revealjs/show_profr_result_plot-1.png){fig-align='center' width=384}\n:::\n:::\n\n\n## Profiling with `profvis`\n\nYet another nice way to profile your code is by using Hadley Wickham's `profvis` package:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(profvis)\nprofvis({fun_fill_loop2(1e4, 'runif')\n fun_fill_vec2(1e4, 'runif')\n})\n```\n:::\n\n\n## Profiling with `profvis` cted.\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n\n\n```\n\n:::\n:::\n\n\n## Optimizing your code\n\n::: {.blockquote}\nWe should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be deluded into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified.\n\n-- Donald Knuth\n:::\n\n:::: {.columns}\n::: {.column width=\"50%\"}\n![](assets/xkcd_automation.png){height=\"350px\"} \n[source: http://www.xkcd/com/1319]{.smaller}\n:::\n\n::: {.column width=\"50%\"}\n![](assets/xkcd_is_it_worth_the_time_2x.png){height=\"350px\"} \n[source: http://www.xkcd/com/1205]{.smaller}\n:::\n::::\n\n## Ways to optimize the code\n\n:::{.incremental}\n- write it in a more efficient way, e.g. use vectorization or `*apply` family instead of loops etc.,\n- allocating memory to avoid copy-on-modify,\n- use package `BLAS` for linear algebra,\n- use `bigmemory` package,\n- GPU computations,\n- multicore support, e.g. `multicore`, `snow`\n- use `futures`\n- use `data.table` or `tibble` instead of `data.frame`\n:::\n\n## Copy-on-modify\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(pryr)\norder <- 1024\nmatrix_A <- matrix(rnorm(order^2), nrow = order)\nmatrix_B <- matrix_A\n```\n:::\n\n\n. . .\n\nCheck where the objects are in the memory:\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\naddress(matrix_A)\naddress(matrix_B)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x7fff977ff010\"\n[1] \"0x7fff977ff010\"\n```\n\n\n:::\n:::\n\n\n. . .\n\nWhat happens if we modify a value in one of the matrices?\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmatrix_B[1,1] <- 1\naddress(matrix_A)\naddress(matrix_B)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x7fff977ff010\"\n[1] \"0x7fff96ffe010\"\n```\n\n\n:::\n:::\n\n\n## Avoid copying by allocating memory\n\nNo memory allocation\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|5\"}\nf1 <- function(to = 3, silent=F) {\n tmp <- c()\n for (i in 1:to) {\n a1 <- address(tmp)\n tmp <- c(tmp, i)\n a2 <- address(tmp)\n if (!silent) { print(paste0(a1, \" --> \", a2)) } \n }\n}\nf1()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x5555555747c0 --> 0x555559c1a5a0\"\n[1] \"0x555559c1a5a0 --> 0x555559bd2a40\"\n[1] \"0x555559bd2a40 --> 0x55555b9a32e8\"\n```\n\n\n:::\n:::\n\n\n## Avoid copying by allocating memory cted.\n\nWith memory allocation\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|5\"}\nf2 <- function(to = 3, silent = FALSE) {\n tmp <- vector(length = to, mode='numeric')\n for (i in 1:to) {\n a1 <- address(tmp)\n tmp[i] <- i\n a2 <- address(tmp)\n if(!silent) { print(paste0(a1, \" --> \", a2)) }\n }\n}\nf2()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x55555d85acb8 --> 0x55555d85acb8\"\n[1] \"0x55555d85acb8 --> 0x55555d85acb8\"\n[1] \"0x55555d85acb8 --> 0x55555d85acb8\"\n```\n\n\n:::\n:::\n\n\n## Allocating memory -- benchmark.\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nlibrary(microbenchmark)\nbenchmrk <- microbenchmark(f1(to = 1e3, silent = T), \n f2(to = 1e3, silent = T), \n times = 100L)\nggplot2::autoplot(benchmrk)\n```\n\n::: {.cell-output-display}\n![](index_files/figure-revealjs/unnamed-chunk-9-1.png){fig-align='center' width=960}\n:::\n:::\n\n\n## GPU\n\n\n::: {.cell}\n\n```{.r .cell-code}\nA = matrix(rnorm(1000^2), nrow=1000) # stored: RAM, computed: CPU\nB = matrix(rnorm(1000^2), nrow=1000) \ngpuA = gpuMatrix(A, type = \"float\") # stored: RAM, computed: GPU\ngpuB = gpuMatrix(B, type = \"float\")\nvclA = vclMatrix(A, type = \"float\") # stored: GPU, computed: GPU\nvclB = vclMatrix(B, type = \"float\")\nbch <- microbenchmark(\n cpu_ram = A %*% B,\n gpu_ram = gpuA %*% gpuB,\n gpu_vcl = vclA %*% vclB, \n times = 10L) \n```\n:::\n\n\n[More on [Charles Determan's Blog](https://www.r-bloggers.com/r-gpu-programming-for-all-with-gpur/).]{.smaller}\n\n## GPU cted.\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nggplot2::autoplot(bch)\n```\n:::\n\n\n![](assets/gpu.png)\n\n## Parallelization using package `parallel`\n\nEasiest to parallelize is `lapply`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nresult <- lapply(1:2, function(x) { c(x, x^2, x^3) })\nresult\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[[1]]\n[1] 1 1 1\n\n[[2]]\n[1] 2 4 8\n```\n\n\n:::\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(parallel)\nnum_cores <- detectCores() - 1\ncl <- makeCluster(num_cores) # Init cluster\nparLapply(cl, 1:2, function(x) { c(x, x^2, x^3)} )\nstopCluster(cl)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[[1]]\n[1] 1 1 1\n\n[[2]]\n[1] 2 4 8\n```\n\n\n:::\n:::\n\n\n## {background-image=\"/assets/images/cover.jpg\"}\n\n### Thank you! Questions?\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n _ \nplatform x86_64-pc-linux-gnu\nos linux-gnu \nmajor 4 \nminor 3.2 \n```\n\n\n:::\n:::\n\n\n[{{< meta current_year >}} โข [SciLifeLab](https://www.scilifelab.se/) โข [NBIS](https://nbis.se/) โข [RaukR](https://nbisweden.github.io/raukr-2024)]{.smaller}\n",
+ "markdown": "---\ntitle: \"Debugging, Profiling, and a Bit of Optimization\"\nauthor: \"Marcin Kierczak\"\nimage: \"assets/featured.jpg\"\nformat: revealjs\n---\n\n\n## {visibility=\"hidden\"}\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(tictoc)\nlibrary(DT)\nlibrary(profvis)\n# library(Rgraphviz)\nlibrary(proftools) # depends on \"graph\" and \"Rgraphviz\" packages\nlibrary(profr)\nlibrary(pryr)\nlibrary(microbenchmark)\nlibrary(ggplot2)\n# remotes::install_github(\"hadley/emo\")\nlibrary(emo)\n# remotes::install_github(\"cdeterman/gpuR\")\n#library(gpuR)\n```\n:::\n\n\n## Run Forrest, run!\n:::{.columns}\n:::{.column width=\"50%\"}\n\n
\n\n:::{.incremental}\n- *My code does not run!* -- **debugging**
\n- *Now it does run but... out of memory!* -- **profiling**
\n- *It runs! It says it will finish in 5 ~~minutes~~ years.* -- **optimization**\n:::\n:::\n:::{.column width=\"50%\"}\n\n:::\n::: \n\n## Types of bugs {background-image=\"assets/featured.jpg\"}\n\n- ๐ฃ Syntax errors\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"1|2\"}\nprin(var1) \nmean(sum(seq((x + 2) * (y - 9 * b))))\n```\n:::\n\n\n. . .\n\n- ๐ข Arithmetic \n\n\n::: {.cell}\n\n```{.r .cell-code}\ny <- 7 / 0\n```\n:::\n\n*Not in R though! `y = Inf`*\n\n. . .\n\n- ๐๐ Type \n\n\n::: {.cell}\n\n```{.r .cell-code}\nmean('a')\n```\n:::\n\n\n. . .\n\n- ๐งฉ Logic\n\nEverything works and produces seemingly valid output that is WRONG! \nIMHO those are the hardest ๐ to debug!\n\n## How to avoid bugs\n
\n\n:::{.incremental}\n- Encapsulate your code in smaller units ๐ฑ (functions), you can test.
\n- Use classes and type checking ๐.
\n- Test ๐งช at the boundaries, e.g. loops at min and max value.
\n- Feed your functions with test data ๐พ that should result with a known output.
\n- Use *antibugging* ๐ธ: `stopifnot(y <= 75)`\n:::\n\n## Floating confusion\n\n
So, how to handle this mess?\n\n:::\n:::\n\n## Handling Errors -- `try`\n
\n\n\n::: {.cell}\n\n```{.r .cell-code}\ntry(\n print(\n paste0('Log of ', input, ' is ', log10(as.numeric(input)))\n )\n)\n```\n:::\n\n\n. . .\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Log of 1 is 0\" \n[2] \"Log of 10 is 1\" \n[3] \"Log of -7 is NaN\" \n[4] \"Log of -0.4 is NaN\" \n[5] \"Log of 0 is -Inf\" \n[6] \"Log of char is NA\" \n[7] \"Log of 100 is 2\" \n[8] \"Log of 3.14159265358979 is 0.497149872694133\"\n[9] \"Log of NaN is NaN\" \n```\n\n\n:::\n:::\n\n\n## Handling Errors -- `tryCatch` block:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nresult <- tryCatch(log10(val), \n warning = function(w) { \n print('Warning! Negative argument supplied. Negating.') \n log10(-val) }, \n error = function(e) { \n print('ERROR! Not a number!')\n NaN\n }\n )\n```\n:::\n\n\n. . .\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"Log of 1 is 0\"\n[1] \"Log of 10 is 1\"\n[1] \"Warning! Negative argument supplied. Negating.\"\n[1] \"Log of -7 is 0.845098040014257\"\n[1] \"Warning! Negative argument supplied. Negating.\"\n[1] \"Log of -0.4 is -0.397940008672038\"\n[1] \"Log of 0 is -Inf\"\n[1] \"Log of NA is NA\"\n[1] \"Log of 100 is 2\"\n[1] \"Log of 3.14159265358979 is 0.497149872694133\"\n[1] \"Log of NaN is NaN\"\n```\n\n\n:::\n:::\n\n\n## Debugging -- errors and warnings\n\n:::{.incremental}\n- An error in your code will result in a call to the `stop()` function that:\n - Breaks the execution of the program (loop, if-statement, etc.)\n - Performs the action defined by the global parameter `error`.\n- A warning just prints out the warning message (or reports it in another way)\n:::\n\n. . .\n\n- Global parameter `error` defines what R should do when an error occurs.\n\n\n::: {.cell}\n\n```{.r .cell-code}\noptions(error = )\n```\n:::\n\n\n. . .\n\n- You can use `simpleError()` and `simpleWarning()` to generate errors and warnings in your code:\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"4\"}\nf <- function(x) {\n if (x < 0) {\n x <- abs(x)\n w <- simpleWarning(\"Value less than 0. Taking abs(x)\")\n w\n }\n}\n```\n:::\n\n\n## Debugging -- what are my options?\n\n- Old-school debugging: a lot of `print` statements\n - print values of your variables at some checkpoints,\n - sometimes fine but often laborious,\n - need to remove/comment out manually after debugging.\n\n. . .\n\n- Dumping frames\n - on error, R state will be saved to a file,\n - file can be read into debugger,\n - values of all variables can be checked,\n - can debug on another machine, e.g. send dump to your colleague!\n \n. . .\n\n- Traceback\n - a list of the recent function calls with values of their parameters\n \n. . .\n\n- Step-by-step debugging\n - execute code line by line within the debugger\n\n## Option 1: dumping frames\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|4-6\"}\nf <- function(x) { sin(x) }\noptions(error = quote(dump.frames(dumpto = \"assets/testdump\", to.file = T)))\nf('test')\noptions(error = NULL) # reset the behavior\nload('assets/testdump.rda')\n# debugger(testdump)\n```\n:::\n\nHint: Last empty line brings you back to the environments menu.\n\n## Option 2: traceback\n\n\n::: {.cell}\n\n```{.r .cell-code}\nf <- function(x) { \n log10(x) \n}\ng <- function(x) { \n f(x) \n}\ng('test')\n```\n\n::: {.cell-output .cell-output-error}\n\n```\nError in log10(x): non-numeric argument to mathematical function\n```\n\n\n:::\n:::\n\n\n. . .\n\n```\n> traceback()\n2: f(x) at #2\n1: g(\"test\")\n```\n\n`traceback()` shows what were the function calls and what parameters were passed to them when the error occurred.\n\n## Option 3: step-by-step debugging\n\n:::: {.columns}\n::: {.column width=\"50%\"}\n\nLet us define a new function `h(x, y)`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nh <- function(x, y) { \n f(x) \n f(y) \n}\n```\n:::\n\n\nNow, we can use `debug()` to debug the function in a step-by-step manner:\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndebug(h)\nh('text', 7)\nundebug(h)\n```\n:::\n\n\n:::\n\n::: {.column .fragment width=\"50%\"}\n\n![](assets/debug.png)\n\n:::\n::::\n\n## Profiling -- `proc.time()`\n\nProfiling is the process of **identifying memory** and time **bottlenecks** ๐พ in your code.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nproc.time()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 1.798 0.708 1.532 \n```\n\n\n:::\n:::\n\n\n- `user time` -- CPU time charged for the execution of user instructions of the calling process,\n- `system time` -- CPU time charged for execution by the system on behalf of the calling process,\n- `elapsed time` -- total CPU time elapsed for the currently running R process.\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\npt1 <- proc.time()\ntmp <- runif(n = 10e5)\npt2 <- proc.time()\npt2 - pt1\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 0.017 0.007 0.025 \n```\n\n\n:::\n:::\n\n\n## Profiling -- `system.time()`\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsystem.time(runif(n = 10e6))\nsystem.time(rnorm(n = 10e6))\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n user system elapsed \n 0.260 0.009 0.268 \n user system elapsed \n 0.433 0.020 0.454 \n```\n\n\n:::\n:::\n\n\n. . .\n\nAn alternative approach is to use `tic` and `toc` statements from the `tictoc` package.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(tictoc)\ntic()\ntmp1 <- runif(n = 10e6)\ntoc()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n0.228 sec elapsed\n```\n\n\n:::\n:::\n\n\n## Profiling in action\n\nThese 4 functions fill a **large vector** with values supplied by function `f`.\n\n. . .\n\n1 -- loop without memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3-5\"}\nfun_fill_loop1 <- function(n = 10e6, f) {\n result <- NULL\n for (i in 1:n) {\n result <- c(result, eval(call(f, 1)))\n }\n return(result)\n}\n```\n:::\n\n\n. . .\n\n2 -- loop with memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3-5\"}\nfun_fill_loop2 <- function(n = 10e6, f) {\n result <- vector(length = n)\n for (i in 1:n) {\n result[i] <- eval(call(f, 1))\n }\n return(result)\n}\n```\n:::\n\n\n## Profiling in action cted.\n\nBut it is maybe better to use...\n\n. . .\n\nvectorization!\n\n. . .\n\n3 -- vectorized loop without memory allocation.\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3\"}\nfun_fill_vec1 <- function(n = 10e6, f) {\n result <- NULL\n result <- eval(call(f, n))\n return(result)\n}\n```\n:::\n\n\n. . .\n\n4 -- vectorized with memory allocation.\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|3\"}\nfun_fill_vec2 <- function(n = 10e6, f) {\n result <- vector(length = n)\n result <- eval(call(f, n))\n return(result)\n}\n```\n:::\n\n\n## Profiling our functions\n \n\n\n::: {.cell}\n\n```{.r .cell-code}\np1 <- system.time(fun_fill_loop1(n = 10e4, \"runif\")) # 1 - loop, no alloc\np2 <- system.time(fun_fill_loop2(n = 10e4, \"runif\")) # 2 - loop, alloc \np3 <- system.time(fun_fill_vec1(n = 10e4, \"runif\")) # 3 - vector, no alloc\np4 <- system.time(fun_fill_vec2(n = 10e4, \"runif\")) # 4 - vector, alloc\n```\n:::\n\n\n|fn | user.self| sys.self| elapsed|\n|:---|---------:|--------:|-------:|\n|fn1 | 11.973| 0.099| 12.073|\n|fn2 | 0.386| 0.032| 0.418|\n|fn3 | 0.002| 0.000| 0.002|\n|fn4 | 0.002| 0.000| 0.002|\n\n\nThe `system.time()` function is not the most accurate though. During the lab, we will experiment with package `microbenchmark`.\n\n## More advanced profiling\n\nWe can also do a bit more advanced profiling, including the memory profiling, using, e.g. `Rprof()` function.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nRprof('profiler_test.out', interval = 0.01, memory.profiling = T)\nfor (i in 1:5) {\n result <- fun_fill_loop2(n = 10e4, \"runif\")\n print(result)\n}\nRprof(NULL)\n```\n:::\n\n\nAnd let us summarise:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nsummary <- summaryRprof(\"profiler_test.out\", memory = \"both\")\nknitr::kable(summary$by.self)\n```\n\n::: {.cell-output-display}\n\n\n| | self.time| self.pct| total.time| total.pct| mem.total|\n|:----------------|---------:|--------:|----------:|---------:|---------:|\n|\"runif\" | 2.13| 48.41| 2.13| 48.41| 1899.0|\n|\"eval\" | 1.04| 23.64| 3.29| 74.77| 3334.7|\n|\"print.default\" | 0.85| 19.32| 0.85| 19.32| 72.1|\n|\"fun_fill_loop2\" | 0.24| 5.45| 3.53| 80.23| 3646.3|\n|\"parent.frame\" | 0.06| 1.36| 0.06| 1.36| 64.6|\n|\"is.list\" | 0.05| 1.14| 0.05| 1.14| 82.3|\n|\"is.pairlist\" | 0.01| 0.23| 0.01| 0.23| 23.1|\n|\"mayCallBrowser\" | 0.01| 0.23| 0.01| 0.23| 1.0|\n|\"strsplit\" | 0.01| 0.23| 0.01| 0.23| 0.4|\n\n\n:::\n:::\n\n\n## Profiling -- `profr` package\n\nThere are also packages available that enable even more advanced profiling:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(profr)\nRprof(\"profiler_test2.out\", interval = 0.01)\ntmp <- table(sort(rnorm(1e5)))\nRprof(NULL)\nprofile_df <- parse_rprof('profiler_test2.out')\n```\n:::\n\n\nThis returns a table that can be visualised:\n\n\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n\n| level| g_id| t_id|f | start| end| n|leaf | time|source |\n|-----:|----:|----:|:-----------------------|-----:|----:|--:|:-----|----:|:------|\n| 1| 1| 1|.main | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 2| 1| 1|execute | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 3| 1| 1|rmarkdown::render | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 4| 1| 1|knitr::knit | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 5| 1| 1|process_file | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 6| 1| 1|handle_error | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 7| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 8| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 9| 1| 1|process_group | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 10| 1| 1|process_group.block | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 11| 1| 1|call_block | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 12| 1| 1|block_exec | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 13| 1| 1|eng_r | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 14| 1| 1|in_input_dir | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 15| 1| 1|in_dir | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 16| 1| 1|evaluate | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 17| 1| 1|evaluate::evaluate | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 18| 1| 1|evaluate_call | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 19| 1| 1|timing_fn | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 20| 1| 1|handle | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 21| 1| 1|withCallingHandlers | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 22| 1| 1|withVisible | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 23| 1| 1|eval_with_user_handlers | 0.00| 0.40| 1|FALSE | 0.40|NA |\n| 24| 1| 1|eval | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 25| 1| 1|eval | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 26| 1| 1|table | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 27| 1| 1|factor | 0.00| 0.40| 1|FALSE | 0.40|base |\n| 28| 1| 1|unique | 0.00| 0.22| 1|TRUE | 0.22|base |\n| 29| 1| 1|unique.default | 0.02| 0.22| 1|TRUE | 0.20|base |\n\n\n:::\n:::\n\n\n## Profiling -- `profr` package cted.\n\nWe can also plot the results using -- `proftools` package-\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nlibrary(proftools)\nprofile_df2 <- readProfileData(\"profiler_test2.out\")\nplotProfileCallGraph(profile_df2, style = google.style, score = \"total\")\n```\n\n::: {.cell-output-display}\n![](index_files/figure-revealjs/show_profr_result_plot-1.png){fig-align='center' width=384}\n:::\n:::\n\n\n## Profiling with `profvis`\n\nYet another nice way to profile your code is by using Hadley Wickham's `profvis` package:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(profvis)\nprofvis({fun_fill_loop2(1e4, 'runif')\n fun_fill_vec2(1e4, 'runif')\n})\n```\n:::\n\n\n## Profiling with `profvis` cted.\n\n\n::: {.cell}\n::: {.cell-output-display}\n\n```{=html}\n\n\n```\n\n:::\n:::\n\n\n## Optimizing your code\n\n::: {.blockquote}\nWe should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be deluded into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified.\n\n-- Donald Knuth\n:::\n\n:::: {.columns}\n::: {.column width=\"50%\"}\n![](assets/xkcd_automation.png){height=\"350px\"} \n[source: http://www.xkcd/com/1319]{.smaller}\n:::\n\n::: {.column width=\"50%\"}\n![](assets/xkcd_is_it_worth_the_time_2x.png){height=\"350px\"} \n[source: http://www.xkcd/com/1205]{.smaller}\n:::\n::::\n\n## Ways to optimize the code\n\n:::{.incremental}\n- write it in a more efficient way, e.g. use vectorization or `*apply` family instead of loops etc.,\n- allocating memory to avoid copy-on-modify,\n- use package `BLAS` for linear algebra,\n- use `bigmemory` package,\n- GPU computations,\n- multicore support, e.g. `multicore`, `snow`\n- use `futures`\n- use `data.table` or `tibble` instead of `data.frame`\n:::\n\n## Copy-on-modify\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(pryr)\norder <- 1024\nmatrix_A <- matrix(rnorm(order^2), nrow = order)\nmatrix_B <- matrix_A\n```\n:::\n\n\n. . .\n\nCheck where the objects are in the memory:\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\naddress(matrix_A)\naddress(matrix_B)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x7fe490ab0010\"\n[1] \"0x7fe490ab0010\"\n```\n\n\n:::\n:::\n\n\n. . .\n\nWhat happens if we modify a value in one of the matrices?\n\n. . .\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmatrix_B[1,1] <- 1\naddress(matrix_A)\naddress(matrix_B)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x7fe490ab0010\"\n[1] \"0x7fe4902af010\"\n```\n\n\n:::\n:::\n\n\n## Avoid copying by allocating memory\n\nNo memory allocation\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|5\"}\nf1 <- function(to = 3, silent=F) {\n tmp <- c()\n for (i in 1:to) {\n a1 <- address(tmp)\n tmp <- c(tmp, i)\n a2 <- address(tmp)\n if (!silent) { print(paste0(a1, \" --> \", a2)) } \n }\n}\nf1()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x561c9d55e560 --> 0x561ca3e80678\"\n[1] \"0x561ca3e80678 --> 0x561ca3e802f8\"\n[1] \"0x561ca3e802f8 --> 0x561ca3e71908\"\n```\n\n\n:::\n:::\n\n\n## Avoid copying by allocating memory cted.\n\nWith memory allocation\n\n\n::: {.cell}\n\n```{.r .cell-code code-line-numbers=\"2|5\"}\nf2 <- function(to = 3, silent = FALSE) {\n tmp <- vector(length = to, mode='numeric')\n for (i in 1:to) {\n a1 <- address(tmp)\n tmp[i] <- i\n a2 <- address(tmp)\n if(!silent) { print(paste0(a1, \" --> \", a2)) }\n }\n}\nf2()\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] \"0x561ca87f50d8 --> 0x561ca87f50d8\"\n[1] \"0x561ca87f50d8 --> 0x561ca87f50d8\"\n[1] \"0x561ca87f50d8 --> 0x561ca87f50d8\"\n```\n\n\n:::\n:::\n\n\n## Allocating memory -- benchmark.\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nlibrary(microbenchmark)\nbenchmrk <- microbenchmark(f1(to = 1e3, silent = T), \n f2(to = 1e3, silent = T), \n times = 100L)\nggplot2::autoplot(benchmrk)\n```\n\n::: {.cell-output-display}\n![](index_files/figure-revealjs/unnamed-chunk-9-1.png){fig-align='center' width=960}\n:::\n:::\n\n\n## GPU\n\n\n::: {.cell}\n\n```{.r .cell-code}\nA = matrix(rnorm(1000^2), nrow=1000) # stored: RAM, computed: CPU\nB = matrix(rnorm(1000^2), nrow=1000) \ngpuA = gpuMatrix(A, type = \"float\") # stored: RAM, computed: GPU\ngpuB = gpuMatrix(B, type = \"float\")\nvclA = vclMatrix(A, type = \"float\") # stored: GPU, computed: GPU\nvclB = vclMatrix(B, type = \"float\")\nbch <- microbenchmark(\n cpu_ram = A %*% B,\n gpu_ram = gpuA %*% gpuB,\n gpu_vcl = vclA %*% vclB, \n times = 10L) \n```\n:::\n\n\n[More on [Charles Determan's Blog](https://www.r-bloggers.com/r-gpu-programming-for-all-with-gpur/).]{.smaller}\n\n## GPU cted.\n\n\n::: {.cell layout-align=\"center\"}\n\n```{.r .cell-code}\nggplot2::autoplot(bch)\n```\n:::\n\n\n![](assets/gpu.png)\n\n## Parallelization using package `parallel`\n\nEasiest to parallelize is `lapply`:\n\n\n::: {.cell}\n\n```{.r .cell-code}\nresult <- lapply(1:2, function(x) { c(x, x^2, x^3) })\nresult\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[[1]]\n[1] 1 1 1\n\n[[2]]\n[1] 2 4 8\n```\n\n\n:::\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nlibrary(parallel)\nnum_cores <- detectCores() - 1\ncl <- makeCluster(num_cores) # Init cluster\nparLapply(cl, 1:2, function(x) { c(x, x^2, x^3)} )\nstopCluster(cl)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[[1]]\n[1] 1 1 1\n\n[[2]]\n[1] 2 4 8\n```\n\n\n:::\n:::\n\n\n## {background-image=\"/assets/images/cover.jpg\"}\n\n### Thank you! Questions?\n\n\n::: {.cell}\n::: {.cell-output .cell-output-stdout}\n\n```\n _ \nplatform x86_64-pc-linux-gnu\nos linux-gnu \nmajor 4 \nminor 3.2 \n```\n\n\n:::\n:::\n\n\n[{{< meta current_year >}} โข [SciLifeLab](https://www.scilifelab.se/) โข [NBIS](https://nbis.se/) โข [RaukR](https://nbisweden.github.io/raukr-2024)]{.smaller}\n",
"supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
diff --git a/_freeze/slides/debugging/index/figure-revealjs/show_profr_result_plot-1.png b/_freeze/slides/debugging/index/figure-revealjs/show_profr_result_plot-1.png
index 284f764..1880042 100644
Binary files a/_freeze/slides/debugging/index/figure-revealjs/show_profr_result_plot-1.png and b/_freeze/slides/debugging/index/figure-revealjs/show_profr_result_plot-1.png differ
diff --git a/_freeze/slides/debugging/index/figure-revealjs/unnamed-chunk-9-1.png b/_freeze/slides/debugging/index/figure-revealjs/unnamed-chunk-9-1.png
index 257d614..ecb8f84 100644
Binary files a/_freeze/slides/debugging/index/figure-revealjs/unnamed-chunk-9-1.png and b/_freeze/slides/debugging/index/figure-revealjs/unnamed-chunk-9-1.png differ
diff --git a/docs/404.html b/docs/404.html
index 9a48fd6..fca983a 100644
--- a/docs/404.html
+++ b/docs/404.html
@@ -572,7 +572,7 @@