{"id":19644856,"url":"https://github.com/uscbiostats/r-parallel-benchmark","last_synced_at":"2025-08-11T06:22:03.060Z","repository":{"id":93430412,"uuid":"266915794","full_name":"USCbiostats/r-parallel-benchmark","owner":"USCbiostats","description":"Using Rcpp with OpenMP (parfor and SIMD)","archived":false,"fork":false,"pushed_at":"2020-07-29T22:20:48.000Z","size":55,"stargazers_count":6,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-14T12:37:57.963Z","etag":null,"topics":["benchmark","hpc","openmp","parallel-computing","rcpp","rstats","simd"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/USCbiostats.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-26T01:27:28.000Z","updated_at":"2024-11-05T20:01:03.000Z","dependencies_parsed_at":"2023-03-10T08:32:16.579Z","dependency_job_id":null,"html_url":"https://github.com/USCbiostats/r-parallel-benchmark","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/USCbiostats/r-parallel-benchmark","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/USCbiostats%2Fr-parallel-benchmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/USCbiostats%2Fr-parallel-benchmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/USCbiostats%2Fr-parallel-benchmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/USCbiostats%2Fr-parallel-benchmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/USCbiostats","download_url":"https://codeload.github.com/USCbiostats/r-parallel-benchmark/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/USCbiostats%2Fr-parallel-benchmark/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269838580,"owners_count":24483241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmark","hpc","openmp","parallel-computing","rcpp","rstats","simd"],"created_at":"2024-11-11T14:30:26.347Z","updated_at":"2025-08-11T06:22:03.033Z","avatar_url":"https://github.com/USCbiostats.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n# Rcpp + OpenMP\n\nThis repository shows how much speed gains can be obtained from using\n[OpenMP](https://openmp.org), and in particular, the `omp simd` and `parallel for`\ninstructions.\n\nThe test consists on computing the pair-wise distances between rows in a matrix\nof size `N` by `M`. The equivalent function in R is `dist()`, and here we \nredefined it using [`Rcpp`](https://cran.r-project.org/package=Rcpp) (checkout\nthe benchmark computing matrix product [here](matrix.md)).\n\nThe file [norm.cpp](norm.cpp) contains the C++ source code for the dist functions.\nThe compiles function are:\n\n- `dist_omp_simd` Using the pragma directives `parallel for` and `simd`.\n\n- `dist_omp_simd_ptr` Same as above, but instead of creating a copy of the input matrix, it uses a `const double *` (a pointer) to access the data.\n\n- `dist_omp` Using the pragma `parallel for`.\n\n- `dist_simd` Using the pragma `simd`.\n\n- `dist_for` no directives.\n\n- `dist_for_arma2` Using Armadillo with vectorized functions.\n\n- `dist_for_arma1` Armadillo implementation with for-loops.\n\n## Speed benchmark\n\n```{r execution, cache=TRUE}\n# Notice that the -fopenmp flag is already included in the norm.cpp file\nSys.setenv(\"PKG_CXXFLAGS\" = \"-O2 -mavx2 -march=core-avx2 -mtune=core-avx2 -DARMA_USE_OPENMP\")\nRcpp::sourceCpp(\"norm.cpp\")\n\nlibrary(microbenchmark)\nset.seed(718243)\nN \u003c- 500\nM \u003c- 1000\nx \u003c- matrix(runif(N * M), nrow = N)\nxt \u003c- t(x)\n\n(ans_bm \u003c- microbenchmark(\n  `SIMD + parfor`      = dist_omp_simd(x, N, M, 2),\n  `SIMD + parfor (ptr)`= dist_omp_simd_ptr(xt, N, M, 2),\n  `parfor`             = dist_omp(x, N, M, 2),\n  `SIMD`               = dist_simd(x, N, M),\n  `serial`             = dist_for(x, N, M),\n  `arma sugar`         = dist_for_arma2(x,N,M),\n  `arma`               = dist_for_arma1(x,N,M),\n  R                    = as.matrix(dist(x)),\n  times                = 10,\n  unit                 = \"relative\"\n))\n```\n\nAs a reference, the elapsed time in ms for R and SIMD + parfor is\n\n```{r print-as-ms, echo=FALSE}\nlibrary(microbenchmark)\nprint(ans_bm[ans_bm$expr %in% c(\"R\", \"SIMD + parfor\"),], unit = \"ms\")\n```\n\nOverall, in my machine, the SIMD+parfor combo outperforms all the others (notice\nthat when it comes to compute matrix products, [Armadillo is the fastest](matrix.md)).\nLet's see if the results are equivalent. At the very least, we should only\nobserve small differences (if any) b/c of precision:\n\n```{r Comparing-results, cache=TRUE}\nRcpp::sourceCpp(\"norm.cpp\")\nans0 \u003c- as.matrix(dist(x))\nans_a \u003c- dist_omp_simd(x, N, M)\nans_b \u003c- dist_omp(x, N, M)\nans_c \u003c- dist_simd(x, N, M)\nans_d \u003c- dist_for(x, N, M)\nans_e \u003c- dist_omp_simd_ptr(t(x), N, M)\nrange(ans0 - ans_b)\nrange(ans_a - ans_b)\nrange(ans_b - ans_c)\nrange(ans_c - ans_d)\nrange(ans_d - ans_e)\n```\n\nThe programs were compiled on a machine with an \n[Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz processor](https://ark.intel.com/content/www/us/en/ark/products/95443/intel-core-i5-7200u-processor-3m-cache-up-to-3-10-ghz.html) which works with [AVX2 instructions](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#CPUs_with_AVX2), i.e. we can literally vectorize 4 double precision operations at a time (512/64 = 4, on top of multi-threading). One important thing to consider is that for this to work we had to generate a copy of the R matrix into a double vector so that elements were contiguous (which is important for SIMD).\n\nFinally, the [`microbenchmark`](https://cran.r-project.org/package=microbenchmark) R package offers a nice viz with boxplot comparing all the methods:\n\n```{r viz, dependson='execution'}\nop \u003c- par(mai = par(\"mai\") * c(2,1,1,1))\nboxplot(ans_bm, las = 2, xlab = \"\")\npar(op)\n```\n\n## Session info\n\nThe programs were compiled on a machine with an \n[Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz processor](https://ark.intel.com/content/www/us/en/ark/products/95443/intel-core-i5-7200u-processor-3m-cache-up-to-3-10-ghz.html)\n\n```{r}\nsessionInfo()\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuscbiostats%2Fr-parallel-benchmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fuscbiostats%2Fr-parallel-benchmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuscbiostats%2Fr-parallel-benchmark/lists"}