From 8baba6c37566ba5aa7c1f7beca1207a28cd0daf2 Mon Sep 17 00:00:00 2001 From: david-cortes Date: Fri, 2 Feb 2024 19:48:22 +0100 Subject: [PATCH 1/4] set OMP threads manually instead of hard-coding --- R-package/DESCRIPTION | 3 ++- R-package/R/xgb.DMatrix.save.R | 1 + R-package/R/xgb.config.R | 10 ++++++++++ R-package/R/xgb.dump.R | 1 + R-package/R/xgb.load.R | 1 + R-package/R/xgb.save.R | 1 + R-package/R/xgb.save.raw.R | 1 + R-package/demo/basic_walkthrough.R | 2 ++ R-package/man/xgb.DMatrix.save.Rd | 1 + R-package/man/xgb.dump.Rd | 1 + R-package/man/xgb.load.Rd | 1 + R-package/man/xgb.save.Rd | 1 + R-package/man/xgb.save.raw.Rd | 1 + R-package/man/xgbConfig.Rd | 12 ++++++++++++ R-package/tests/testthat.R | 1 + R-package/vignettes/xgboostPresentation.Rmd | 3 +++ src/gbm/gbtree_model.cc | 21 ++------------------- 17 files changed, 42 insertions(+), 20 deletions(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 66e2b5692190..02f2d589449f 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -56,7 +56,8 @@ Suggests: testthat, igraph (>= 1.0.1), float, - titanic + titanic, + RhpcBLAStcl Depends: R (>= 4.3.0) Imports: diff --git a/R-package/R/xgb.DMatrix.save.R b/R-package/R/xgb.DMatrix.save.R index ef4599d0ef95..243f43047b07 100644 --- a/R-package/R/xgb.DMatrix.save.R +++ b/R-package/R/xgb.DMatrix.save.R @@ -6,6 +6,7 @@ #' @param fname the name of the file to write. #' #' @examples +#' \dontshow{RhpcBLASctl::omp_set_num_threads(1)} #' data(agaricus.train, package='xgboost') #' dtrain <- with(agaricus.train, xgb.DMatrix(data, label = label, nthread = 2)) #' fname <- file.path(tempdir(), "xgb.DMatrix.data") diff --git a/R-package/R/xgb.config.R b/R-package/R/xgb.config.R index 3f3a9b1a7b3b..06b294019be9 100644 --- a/R-package/R/xgb.config.R +++ b/R-package/R/xgb.config.R @@ -4,7 +4,17 @@ #' values of one or more global-scope parameters. Use \code{xgb.get.config} to fetch the current #' values of all global-scope parameters (listed in #' \url{https://xgboost.readthedocs.io/en/stable/parameter.html}). +#' @details +#' Note that some functions might use a globally-configured number of threads, which is not +#' managed by this configuration. Typically, XGBoost methods accept an `nthreads` parameter +#' controlling the number of parallel threads that will be used, but some functionalities +#' might execute functions before this parameter can be supplied. #' +#' In such cases, the global number of threads is taken from the configured number of OpenMP +#' (OMP) threads, which if not set, will default to the maximum number of available threads. +#' The number of OMP threads in turn can be configured through an environment variable +#' `OMP_NUM_THREADS` (which needs to be set before R is started), or alternatively, can be +#' changed in an R session through package `RhpcBLASctl`, by calling function `RhpcBLASctl::omp_set_num_threads`. #' @rdname xgbConfig #' @title Set and get global configuration #' @name xgb.set.config, xgb.get.config diff --git a/R-package/R/xgb.dump.R b/R-package/R/xgb.dump.R index 3a3d2c7dcbcb..2fa5bcb2f628 100644 --- a/R-package/R/xgb.dump.R +++ b/R-package/R/xgb.dump.R @@ -24,6 +24,7 @@ #' as a \code{character} vector. Otherwise it will return \code{TRUE}. #' #' @examples +#' \dontshow{RhpcBLASctl::omp_set_num_threads(1)} #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') #' train <- agaricus.train diff --git a/R-package/R/xgb.load.R b/R-package/R/xgb.load.R index 7d1eab7e9c34..4985f74b56c6 100644 --- a/R-package/R/xgb.load.R +++ b/R-package/R/xgb.load.R @@ -20,6 +20,7 @@ #' \code{\link{xgb.save}} #' #' @examples +#' \dontshow{RhpcBLASctl::omp_set_num_threads(1)} #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') #' diff --git a/R-package/R/xgb.save.R b/R-package/R/xgb.save.R index e1a61d1965b9..91c545ff76fd 100644 --- a/R-package/R/xgb.save.R +++ b/R-package/R/xgb.save.R @@ -35,6 +35,7 @@ #' \code{\link{xgb.load}} #' #' @examples +#' \dontshow{RhpcBLASctl::omp_set_num_threads(1)} #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') #' diff --git a/R-package/R/xgb.save.raw.R b/R-package/R/xgb.save.raw.R index c124a752b02d..c04f06d9c941 100644 --- a/R-package/R/xgb.save.raw.R +++ b/R-package/R/xgb.save.raw.R @@ -12,6 +12,7 @@ #' } #' #' @examples +#' \dontshow{RhpcBLASctl::omp_set_num_threads(1)} #' data(agaricus.train, package='xgboost') #' data(agaricus.test, package='xgboost') #' diff --git a/R-package/demo/basic_walkthrough.R b/R-package/demo/basic_walkthrough.R index 31f79fb57be4..3dbbe0586f44 100644 --- a/R-package/demo/basic_walkthrough.R +++ b/R-package/demo/basic_walkthrough.R @@ -55,6 +55,8 @@ print(paste("test-error=", err)) # save model to binary local file xgb.save(bst, "xgboost.model") # load binary model to R +# Function doesn't take 'nthreads', but can be set like this: +RhpcBLASctl::omp_set_num_threads(1) bst2 <- xgb.load("xgboost.model") pred2 <- predict(bst2, test$data) # pred2 should be identical to pred diff --git a/R-package/man/xgb.DMatrix.save.Rd b/R-package/man/xgb.DMatrix.save.Rd index d5c0563b37db..51643274d857 100644 --- a/R-package/man/xgb.DMatrix.save.Rd +++ b/R-package/man/xgb.DMatrix.save.Rd @@ -15,6 +15,7 @@ xgb.DMatrix.save(dmatrix, fname) Save xgb.DMatrix object to binary file } \examples{ +\dontshow{RhpcBLASctl::omp_set_num_threads(1)} data(agaricus.train, package='xgboost') dtrain <- with(agaricus.train, xgb.DMatrix(data, label = label, nthread = 2)) fname <- file.path(tempdir(), "xgb.DMatrix.data") diff --git a/R-package/man/xgb.dump.Rd b/R-package/man/xgb.dump.Rd index 2cdb6b16acd8..6f97f69244b9 100644 --- a/R-package/man/xgb.dump.Rd +++ b/R-package/man/xgb.dump.Rd @@ -44,6 +44,7 @@ as a \code{character} vector. Otherwise it will return \code{TRUE}. Dump an xgboost model in text format. } \examples{ +\dontshow{RhpcBLASctl::omp_set_num_threads(1)} data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') train <- agaricus.train diff --git a/R-package/man/xgb.load.Rd b/R-package/man/xgb.load.Rd index 1a687317176f..1fbe0055ed9d 100644 --- a/R-package/man/xgb.load.Rd +++ b/R-package/man/xgb.load.Rd @@ -25,6 +25,7 @@ Note: a model saved as an R-object, has to be loaded using corresponding R-metho not \code{xgb.load}. } \examples{ +\dontshow{RhpcBLASctl::omp_set_num_threads(1)} data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') diff --git a/R-package/man/xgb.save.Rd b/R-package/man/xgb.save.Rd index 0db80a120c84..bcfbd0bb4520 100644 --- a/R-package/man/xgb.save.Rd +++ b/R-package/man/xgb.save.Rd @@ -41,6 +41,7 @@ how to persist models in a future-proof way, i.e. to make the model accessible i releases of XGBoost. } \examples{ +\dontshow{RhpcBLASctl::omp_set_num_threads(1)} data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') diff --git a/R-package/man/xgb.save.raw.Rd b/R-package/man/xgb.save.raw.Rd index 15400bb1450e..6cdafd3d950c 100644 --- a/R-package/man/xgb.save.raw.Rd +++ b/R-package/man/xgb.save.raw.Rd @@ -21,6 +21,7 @@ xgb.save.raw(model, raw_format = "ubj") Save xgboost model from xgboost or xgb.train } \examples{ +\dontshow{RhpcBLASctl::omp_set_num_threads(1)} data(agaricus.train, package='xgboost') data(agaricus.test, package='xgboost') diff --git a/R-package/man/xgbConfig.Rd b/R-package/man/xgbConfig.Rd index 94b220c7785b..13da423f6566 100644 --- a/R-package/man/xgbConfig.Rd +++ b/R-package/man/xgbConfig.Rd @@ -25,6 +25,18 @@ values of one or more global-scope parameters. Use \code{xgb.get.config} to fetc values of all global-scope parameters (listed in \url{https://xgboost.readthedocs.io/en/stable/parameter.html}). } +\details{ +Note that some functions might use a globally-configured number of threads, which is not +managed by this configuration. Typically, XGBoost methods accept an \code{nthreads} parameter +controlling the number of parallel threads that will be used, but some functionalities +might execute functions before this parameter can be supplied. + +In such cases, the global number of threads is taken from the configured number of OpenMP +(OMP) threads, which if not set, will default to the maximum number of available threads. +The number of OMP threads in turn can be configured through an environment variable +\code{OMP_NUM_THREADS} (which needs to be set before R is started), or alternatively, can be +changed in an R session through package \code{RhpcBLASctl}, by calling function \code{RhpcBLASctl::omp_set_num_threads}. +} \examples{ # Set verbosity level to silent (0) xgb.set.config(verbosity = 0) diff --git a/R-package/tests/testthat.R b/R-package/tests/testthat.R index 3bb229e705c9..7cf711292c48 100644 --- a/R-package/tests/testthat.R +++ b/R-package/tests/testthat.R @@ -2,3 +2,4 @@ library(testthat) library(xgboost) test_check("xgboost", reporter = ProgressReporter) +RhpcBLASctl::omp_set_num_threads(1) diff --git a/R-package/vignettes/xgboostPresentation.Rmd b/R-package/vignettes/xgboostPresentation.Rmd index efafc624d40f..0a6432d5f9cf 100644 --- a/R-package/vignettes/xgboostPresentation.Rmd +++ b/R-package/vignettes/xgboostPresentation.Rmd @@ -496,6 +496,9 @@ An interesting test to see how identical our saved model is to the original one ```{r loadModel, message=F, warning=F} # load binary model to R +# Note that the number of threads for 'xgb.load' is taken from global config, +# can be modified like this: +RhpcBLASctl::omp_set_num_threads(1) bst2 <- xgb.load(fname) xgb.parameters(bst2) <- list(nthread = 2) pred2 <- predict(bst2, test$data) diff --git a/src/gbm/gbtree_model.cc b/src/gbm/gbtree_model.cc index 14131865fe75..2edb456c95de 100644 --- a/src/gbm/gbtree_model.cc +++ b/src/gbm/gbtree_model.cc @@ -106,30 +106,13 @@ void GBTreeModel::Load(dmlc::Stream* fi) { Validate(*this); } -namespace { -std::int32_t IOThreads(Context const* ctx) { - CHECK(ctx); - std::int32_t n_threads = ctx->Threads(); - // CRAN checks for number of threads used by examples, but we might not have the right - // number of threads when serializing/unserializing models as nthread is a booster - // parameter, which is only effective after booster initialization. - // - // The threshold ratio of CPU time to user time for R is 2.5, we set the number of - // threads to 2. -#if defined(XGBOOST_STRICT_R_MODE) && XGBOOST_STRICT_R_MODE == 1 - n_threads = std::min(2, n_threads); -#endif - return n_threads; -} -} // namespace - void GBTreeModel::SaveModel(Json* p_out) const { auto& out = *p_out; CHECK_EQ(param.num_trees, static_cast(trees.size())); out["gbtree_model_param"] = ToJson(param); std::vector trees_json(trees.size()); - common::ParallelFor(trees.size(), IOThreads(ctx_), [&](auto t) { + common::ParallelFor(trees.size(), ctx_->Threads(), [&](auto t) { auto const& tree = trees[t]; Json jtree{Object{}}; tree->SaveModel(&jtree); @@ -167,7 +150,7 @@ void GBTreeModel::LoadModel(Json const& in) { CHECK_EQ(tree_info_json.size(), param.num_trees); tree_info.resize(param.num_trees); - common::ParallelFor(param.num_trees, IOThreads(ctx_), [&](auto t) { + common::ParallelFor(param.num_trees, ctx_->Threads(), [&](auto t) { auto tree_id = get(trees_json[t]["id"]); trees.at(tree_id).reset(new RegTree{}); trees[tree_id]->LoadModel(trees_json[t]); From 7f82ca4f6618d3650f7cb2b59bb6372694173f63 Mon Sep 17 00:00:00 2001 From: david-cortes Date: Mon, 19 Feb 2024 20:05:07 +0100 Subject: [PATCH 2/4] update docs --- R-package/R/xgb.config.R | 15 ++++++--------- R-package/man/xgbConfig.Rd | 15 ++++++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/R-package/R/xgb.config.R b/R-package/R/xgb.config.R index 06b294019be9..20b8aef90797 100644 --- a/R-package/R/xgb.config.R +++ b/R-package/R/xgb.config.R @@ -5,16 +5,13 @@ #' values of all global-scope parameters (listed in #' \url{https://xgboost.readthedocs.io/en/stable/parameter.html}). #' @details -#' Note that some functions might use a globally-configured number of threads, which is not -#' managed by this configuration. Typically, XGBoost methods accept an `nthreads` parameter -#' controlling the number of parallel threads that will be used, but some functionalities -#' might execute functions before this parameter can be supplied. +#' Note that serialization-related functions might use a globally-configured number of threads, +#' which is managed by the system's OpenMP (OMP) configuration instead. Typically, XGBoost methods +#' accept an `nthreads` parameter, but some methods like `readRDS` might get executed before such +#' parameter can be supplied. #' -#' In such cases, the global number of threads is taken from the configured number of OpenMP -#' (OMP) threads, which if not set, will default to the maximum number of available threads. -#' The number of OMP threads in turn can be configured through an environment variable -#' `OMP_NUM_THREADS` (which needs to be set before R is started), or alternatively, can be -#' changed in an R session through package `RhpcBLASctl`, by calling function `RhpcBLASctl::omp_set_num_threads`. +#' The number of OMP threads can in turn be configured for example through an environment variable +#' `OMP_NUM_THREADS` (needs to be set before R is started), or through `RhpcBLASctl::omp_set_num_threads`. #' @rdname xgbConfig #' @title Set and get global configuration #' @name xgb.set.config, xgb.get.config diff --git a/R-package/man/xgbConfig.Rd b/R-package/man/xgbConfig.Rd index 13da423f6566..164c62ef45d0 100644 --- a/R-package/man/xgbConfig.Rd +++ b/R-package/man/xgbConfig.Rd @@ -26,16 +26,13 @@ values of all global-scope parameters (listed in \url{https://xgboost.readthedocs.io/en/stable/parameter.html}). } \details{ -Note that some functions might use a globally-configured number of threads, which is not -managed by this configuration. Typically, XGBoost methods accept an \code{nthreads} parameter -controlling the number of parallel threads that will be used, but some functionalities -might execute functions before this parameter can be supplied. +Note that serialization-related functions might use a globally-configured number of threads, +which is managed by the system's OpenMP (OMP) configuration instead. Typically, XGBoost methods +accept an \code{nthreads} parameter, but some methods like \code{readRDS} might get executed before such +parameter can be supplied. -In such cases, the global number of threads is taken from the configured number of OpenMP -(OMP) threads, which if not set, will default to the maximum number of available threads. -The number of OMP threads in turn can be configured through an environment variable -\code{OMP_NUM_THREADS} (which needs to be set before R is started), or alternatively, can be -changed in an R session through package \code{RhpcBLASctl}, by calling function \code{RhpcBLASctl::omp_set_num_threads}. +The number of OMP threads can in turn be configured for example through an environment variable +\code{OMP_NUM_THREADS} (needs to be set before R is started), or through \code{RhpcBLASctl::omp_set_num_threads}. } \examples{ # Set verbosity level to silent (0) From 52c75652d501eba182b292f5126af9b20c900c6c Mon Sep 17 00:00:00 2001 From: david-cortes Date: Mon, 19 Feb 2024 20:18:45 +0100 Subject: [PATCH 3/4] add missing dep to CI --- R-package/tests/helper_scripts/install_deps.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R-package/tests/helper_scripts/install_deps.R b/R-package/tests/helper_scripts/install_deps.R index 3ae44f6b13f4..7a621798ab62 100644 --- a/R-package/tests/helper_scripts/install_deps.R +++ b/R-package/tests/helper_scripts/install_deps.R @@ -20,6 +20,7 @@ pkgs <- c( "igraph", "float", "titanic", + "RhpcBLASctl", ## imports "Matrix", "methods", From 80eceeb0105c03aebd0472cd575f4d2058e406ab Mon Sep 17 00:00:00 2001 From: david-cortes Date: Mon, 19 Feb 2024 20:36:34 +0100 Subject: [PATCH 4/4] fix typo --- R-package/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R-package/DESCRIPTION b/R-package/DESCRIPTION index 02f2d589449f..b4072aff0b41 100644 --- a/R-package/DESCRIPTION +++ b/R-package/DESCRIPTION @@ -57,7 +57,7 @@ Suggests: igraph (>= 1.0.1), float, titanic, - RhpcBLAStcl + RhpcBLASctl Depends: R (>= 4.3.0) Imports: