Skip to content

Commit aa84a57

Browse files
committed
add test for downstream use of the scalable allocator
generates a small package using scalable_malloc and scalable_free via the RcppParallel::CxxFlags() / RcppParallelLibs() flags, installs it, and confirms the allocator can be used. on windows, this exercises the tbbmalloc re-export from RcppParallel.dll; elsewhere, the run-time resolution of tbbmalloc symbols loaded by RcppParallel::.onLoad(). the test builds a package, so it runs only on CI (or with NOT_CRAN), and only when the tbb backend is enabled.
1 parent 9df8885 commit aa84a57

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

tests/scalable-allocator.R

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
2+
# Check that packages using TBB's scalable allocator can be compiled, linked,
3+
# and loaded against this installation of RcppParallel.
4+
#
5+
# On Windows, downstream packages link only '-lRcppParallel', and so this
6+
# requires RcppParallel.dll to re-export the tbbmalloc API on their behalf.
7+
# On other platforms, the symbols are left undefined at link time, and
8+
# resolved from the tbbmalloc library loaded when RcppParallel is loaded.
9+
#
10+
# https://github.com/RcppCore/RcppParallel/pull/262
11+
12+
library(RcppParallel)
13+
14+
# building the test package requires a toolchain, so keep this test
15+
# off of CRAN's machines
16+
ci <- nzchar(Sys.getenv("CI")) || identical(Sys.getenv("NOT_CRAN"), "true")
17+
if (!ci) {
18+
writeLines("Not running on CI; skipping scalable allocator test.")
19+
quit(save = "no")
20+
}
21+
22+
# the scalable allocator is only available with the TBB backend
23+
if (!RcppParallel:::TBB_ENABLED) {
24+
writeLines("TBB is not enabled; skipping scalable allocator test.")
25+
quit(save = "no")
26+
}
27+
28+
# generate the test package
29+
pkgRoot <- file.path(tempdir(), "scalabletest")
30+
dir.create(file.path(pkgRoot, "src"), recursive = TRUE, showWarnings = FALSE)
31+
32+
writeLines(con = file.path(pkgRoot, "DESCRIPTION"), c(
33+
"Package: scalabletest",
34+
"Type: Package",
35+
"Title: Test Linking to the TBB Scalable Allocator",
36+
"Version: 0.1.0",
37+
"Author: RcppParallel Authors",
38+
"Maintainer: RcppParallel Authors <noreply@example.com>",
39+
"Description: Confirms that packages can use the TBB scalable allocator.",
40+
"License: GPL-2",
41+
"Imports: RcppParallel"
42+
))
43+
44+
writeLines(con = file.path(pkgRoot, "NAMESPACE"), c(
45+
"useDynLib(scalabletest)",
46+
"importFrom(RcppParallel, RcppParallelLibs)"
47+
))
48+
49+
writeLines(con = file.path(pkgRoot, "src", "Makevars"), c(
50+
'PKG_CXXFLAGS = $(shell "${R_HOME}/bin/Rscript" -e "RcppParallel::CxxFlags()")',
51+
'PKG_LIBS = $(shell "${R_HOME}/bin/Rscript" -e "RcppParallel::RcppParallelLibs()")'
52+
))
53+
54+
writeLines(con = file.path(pkgRoot, "src", "Makevars.win"), c(
55+
'PKG_CXXFLAGS = $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "RcppParallel::CxxFlags()")',
56+
'PKG_LIBS = $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "RcppParallel::RcppParallelLibs()")'
57+
))
58+
59+
writeLines(con = file.path(pkgRoot, "src", "scalable.cpp"), c(
60+
"#include <tbb/scalable_allocator.h>",
61+
"",
62+
"#include <R.h>",
63+
"#include <Rinternals.h>",
64+
"",
65+
'extern "C" SEXP scalable_roundtrip(SEXP sizeSEXP)',
66+
"{",
67+
" int size = Rf_asInteger(sizeSEXP);",
68+
"",
69+
" double* data = (double*) scalable_malloc(size * sizeof(double));",
70+
" if (data == NULL)",
71+
" return Rf_ScalarLogical(FALSE);",
72+
"",
73+
" for (int i = 0; i < size; i++)",
74+
" data[i] = i;",
75+
"",
76+
" double total = 0.0;",
77+
" for (int i = 0; i < size; i++)",
78+
" total += data[i];",
79+
"",
80+
" scalable_free(data);",
81+
"",
82+
" double expected = (double) size * (size - 1) / 2;",
83+
" return Rf_ScalarLogical(total == expected);",
84+
"}"
85+
))
86+
87+
# install it, making sure child processes resolve this RcppParallel
88+
libDir <- file.path(tempdir(), "library")
89+
dir.create(libDir, recursive = TRUE, showWarnings = FALSE)
90+
Sys.setenv(R_LIBS = paste(.libPaths(), collapse = .Platform$path.sep))
91+
92+
rExe <- file.path(R.home("bin"), if (.Platform$OS.type == "windows") "R.exe" else "R")
93+
args <- c("CMD", "INSTALL", "--no-multiarch", paste0("--library=", shQuote(libDir)), shQuote(pkgRoot))
94+
95+
output <- suppressWarnings(system2(rExe, args, stdout = TRUE, stderr = TRUE))
96+
writeLines(output)
97+
98+
status <- attr(output, "status")
99+
if (is.numeric(status) && status != 0L)
100+
stop("error installing test package (status code ", status, ")")
101+
102+
# load it, and check that the scalable allocator can be used
103+
invisible(loadNamespace("scalabletest", lib.loc = libDir))
104+
ok <- .Call("scalable_roundtrip", 1000L, PACKAGE = "scalabletest")
105+
writeLines(paste("scalable allocator round trip:", if (isTRUE(ok)) "OK" else "FAILED"))
106+
stopifnot(isTRUE(ok))

0 commit comments

Comments
 (0)