Skip to content

Commit 1c4c733

Browse files
Merge branch 'release/1.33.2'
2 parents ac21d01 + 4996cc5 commit 1c4c733

18 files changed

+742
-504
lines changed

.github/workflows/R-CMD-check.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ jobs:
4343
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
4444
## Test in other locale (optional)
4545
LANGUAGE: ${{ matrix.config.language }}
46+
## R (>= 4.4.0) Note, no trailing underscore (sic!)
47+
_R_COMPARE_LANG_OBJECTS: eqonly
4648
## R CMD check
4749
_R_CHECK_CRAN_INCOMING_: false
4850
_R_CHECK_LENGTH_1_CONDITION_: true

DESCRIPTION

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: future
2-
Version: 1.33.1
2+
Version: 1.33.2
33
Title: Unified Parallel and Distributed Processing in R for Everyone
44
Imports:
55
digest,
@@ -39,5 +39,6 @@ LazyLoad: TRUE
3939
ByteCompile: TRUE
4040
URL: https://future.futureverse.org, https://github.com/HenrikBengtsson/future
4141
BugReports: https://github.com/HenrikBengtsson/future/issues
42-
RoxygenNote: 7.2.3
42+
Encoding: UTF-8
43+
RoxygenNote: 7.3.1
4344
Roxygen: list(markdown = TRUE)

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ S3method(getExpression,Future)
1717
S3method(getExpression,MulticoreFuture)
1818
S3method(getExpression,MultisessionFuture)
1919
S3method(getExpression,UniprocessFuture)
20+
S3method(globals,Future)
2021
S3method(journal,Future)
2122
S3method(journal,FutureJournal)
2223
S3method(journal,FutureJournalCondition)
@@ -35,6 +36,7 @@ S3method(nbrOfWorkers,cluster)
3536
S3method(nbrOfWorkers,future)
3637
S3method(nbrOfWorkers,multiprocess)
3738
S3method(nbrOfWorkers,uniprocess)
39+
S3method(packages,Future)
3840
S3method(plot,Mandelbrot)
3941
S3method(print,Future)
4042
S3method(print,FutureCondition)

NEWS.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
# Version 1.33.2 [2024-03-23]
2+
3+
## Performance
4+
5+
* Decreased the overhead of launching futures that occurred for future
6+
strategies that used a complex `workers` argument. For example,
7+
`plan(cluster, workers = cl)`, where `cl` is a `cluster` object,
8+
would come with an extra overhead, because the `workers` object was
9+
unnecessarily transferred to the cluster nodes.
10+
11+
## Miscellaneous
12+
13+
* Now `plan(multisession, workers = I(n))`, and same for `cluster`,
14+
preserves the "AsIs" class attribute on the `workers` argument so
15+
that it is propagated to `parallelly::makeClusterWorkers()`.
16+
17+
## Documentation
18+
19+
* Clarify that packages must not change any of the `future.*` options.
20+
21+
122
# Version 1.33.1 [2023-12-21]
223

324
## Bug Fixes
@@ -6,7 +27,7 @@
627
circumstances call `local()` on the global search path rather than
728
`base::local()` as intended. For example, if a package that
829
exports its own `local()` function was attached, then that would be
9-
called instead, often leading to a hard to troubleshoot error.
30+
called instead, often leading to a hard-to-troubleshoot error.
1031

1132

1233
# Version 1.33.0 [2023-07-01]

R/ClusterRegistry.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ ClusterRegistry <- local({
88

99
if (is.null(workers)) {
1010
} else if (is.numeric(workers)) {
11+
## Preserve class attributes, especially "AsIs"
12+
clazz <- class(workers)
1113
workers <- as.integer(workers)
14+
class(workers) <- clazz
1215
stop_if_not(length(workers) == 1, is.finite(workers))
1316
} else if (is.character(workers)) {
1417
stop_if_not(length(workers) >= 1, !anyNA(workers))

R/Future-class.R

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -736,9 +736,15 @@ getExpression.Future <- local({
736736
tmpl_enter_plan <- bquote_compile({
737737
## covr: skip=2
738738
.(enter)
739+
740+
## Record the original future strategy set on this worker
741+
...future.strategy.old <- future::plan("list")
742+
739743
## Prevent 'future.plan' / R_FUTURE_PLAN settings from being nested
740744
options(future.plan = NULL)
741745
Sys.unsetenv("R_FUTURE_PLAN")
746+
747+
## Use the next-level-down ("popped") future strategy
742748
future::plan(.(strategiesR), .cleanup = FALSE, .init = FALSE)
743749
})
744750

@@ -752,7 +758,8 @@ getExpression.Future <- local({
752758
Sys.unsetenv("R_FUTURE_PLAN")
753759
else
754760
Sys.setenv(R_FUTURE_PLAN = .(oenv))
755-
future::plan(.(strategies), .cleanup = FALSE, .init = FALSE)
761+
## Revert to the original future strategy
762+
future::plan(...future.strategy.old, .cleanup = FALSE, .init = FALSE)
756763
## FIXME: If we move .(exit) here, then 'R CMD check' on MS Windows
757764
## complain about leftover RscriptXXXXX temporary files. /2022-07-21
758765
## .(exit)
@@ -797,9 +804,12 @@ getExpression.Future <- local({
797804
## Pass down the default or the remain set of future strategies?
798805
strategiesR <- strategies[-1]
799806
## mdebugf("Number of remaining strategies: %d", length(strategiesR))
800-
801-
## Identify packages needed by the futures
802-
if (length(strategiesR) > 0L) {
807+
808+
## Use default future strategy + identify packages needed by the futures
809+
if (length(strategiesR) == 0L) {
810+
if (debug) mdebug("Packages needed by future strategies (n = 0): <none>")
811+
strategiesR <- "default"
812+
} else {
803813
## Identify package namespaces needed for strategies
804814
pkgsS <- lapply(strategiesR, FUN = environment)
805815
pkgsS <- lapply(pkgsS, FUN = environmentName)
@@ -808,8 +818,6 @@ getExpression.Future <- local({
808818
pkgsS <- intersect(pkgsS, loadedNamespaces())
809819
if (debug) mdebugf("Packages needed by future strategies (n = %d): %s", length(pkgsS), paste(sQuote(pkgsS), collapse = ", "))
810820
pkgs <- unique(c(pkgs, pkgsS))
811-
} else {
812-
if (debug) mdebug("Packages needed by future strategies (n = 0): <none>")
813821
}
814822

815823
## Make sure to load and attach all package needed
@@ -822,10 +830,6 @@ getExpression.Future <- local({
822830
enter <- bquote_apply(tmpl_enter_packages)
823831
}
824832

825-
## Make sure to set all nested future strategies needed
826-
## Use default future strategy?
827-
if (length(strategiesR) == 0L) strategiesR <- "default"
828-
829833
## Pass down future strategies
830834
enter <- bquote_apply(tmpl_enter_plan)
831835
exit <- bquote_apply(tmpl_exit_plan)
@@ -847,12 +851,14 @@ getExpression.Future <- local({
847851

848852
globals <- function(future, ...) UseMethod("globals")
849853

854+
#' @exportS3Method
850855
globals.Future <- function(future, ...) {
851856
future[["globals"]]
852857
}
853858

854859
packages <- function(future, ...) UseMethod("packages")
855860

861+
#' @exportS3Method
856862
packages.Future <- function(future, ...) {
857863
future[["packages"]]
858864
}

R/globals.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#'
1515
#' @param persistent If TRUE, non-existing globals (= identified in expression but not found in memory) are always silently ignored and assumed to be existing in the evaluation environment. If FALSE, non-existing globals are by default ignore, but may also trigger an informative error if option \option{future.globals.onMissing} in `"error"` (should only be used for troubleshooting).
1616
#'
17-
#' @param maxSize The maximum allowed total size (in bytes) of globals - for
17+
#' @param maxSize The maximum allowed total size (in bytes) of globals---for
1818
#' the purpose of preventing too large exports / transfers happening by
1919
#' mistake. If the total size of the global objects are greater than this
2020
#' limit, an informative error message is produced. If

R/journal.R

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,34 @@
1414
#' @return
1515
#' A data frame of class `FutureJournal` with columns:
1616
#'
17-
#' 1. `event` (character string) - type of event that took place
18-
#' 2. `category` (character string) - the category of the event
19-
#' 3. `parent` (character string) - (to be describe)
20-
#' 4. `start` (POSIXct) - the timestamp when the event started
21-
#' 5. `at` (difftime) - the time when the event started relative to
22-
#' first event
23-
#' 6. `duration` (difftime) - the duration of the event
17+
#' 1. `event` (factor) - type of event that took place
18+
#' 2. `category` (factor) - the category of the event
19+
#' 3. `parent` (character string) - (to be describe)
20+
#' 4. `start` (POSIXct) - the timestamp when the event started
21+
#' 5. `at` (difftime) - the time when the event started
22+
#' relative to first event
23+
#' 6. `duration` (difftime) - the duration of the event
2424
#' 7. `future_label` (character string) - the label of the future
25-
#' 8. `future_uuid` (character string) - the UUID of the future
26-
#' 9. `session_uuid` (character string) - the UUID of the R session
27-
#' where the event took place
25+
#' 8. `future_uuid` (factor) - the UUID of the future
26+
#' 9. `session_uuid` (factor) - the UUID of the R session where the
27+
#' event took place
2828
#'
2929
#' The common events are:
3030
#'
31-
#' * `create` - the future was created (an `overhead`)
32-
#' * `launch` - the future was launched (an `overhead`)
31+
#' * `create` - the future was created (an `overhead`)
32+
#' * `launch` - the future was launched (an `overhead`)
3333
#' * `evaluate` - the future was evaluated (an `evaluation`)
3434
#' * `resolved` - the future was queried (may be occur multiple times)
3535
#' (an `overhead`)
36-
#' * `gather` - the results was retrieved (an `overhead`)
36+
#' * `gather` - the results was retrieved (an `overhead`)
3737
#'
3838
#' but others may be added by other Future classes.
3939
#'
40-
#' Common event categorys are:
40+
#' Common event categories are:
4141
#'
4242
#' * `evaluation` - processing time is spent on evaluation
43-
#' * `overhead` - processing time is spent on orchestrating the future
44-
#' * `waiting` - processing time is spent on waiting to set up or
43+
#' * `overhead` - processing time is spent on orchestrating the future
44+
#' * `waiting` - processing time is spent on waiting to set up or
4545
#' querying the future
4646
#'
4747
#' but others may be added by other Future classes.
@@ -82,10 +82,10 @@ journal.Future <- function(x, ...) {
8282
if (!is.element("evaluate", data$event) && !is.null(x$result)) {
8383
stop_if_not(is.character(session_uuid))
8484
x <- appendToFutureJournal(x,
85-
event = "evaluate",
86-
category = "evaluation",
87-
start = x$result$started,
88-
stop = x$result$finished
85+
event = "evaluate",
86+
category = "evaluation",
87+
start = x$result$started,
88+
stop = x$result$finished
8989
)
9090
data <- x$.journal
9191
stop_if_not(length(x$result$session_uuid) == 1L, is.character(x$result$session_uuid))
@@ -117,6 +117,14 @@ journal.Future <- function(x, ...) {
117117
levels <- c(known_levels, other_levels)
118118
data$event <- factor(data$event, levels = levels)
119119

120+
## Coerce 'category' to a factor
121+
levels <- c("evaluation", "overhead", "waiting")
122+
data$category <- factor(data$category, levels = levels)
123+
124+
## Coerce 'category' to a factor
125+
levels <- c("evaluation", "overhead", "waiting")
126+
data$category <- factor(data$category, levels = levels)
127+
120128
## Sort by relative start time
121129
if (nrow(data) > 1L) data <- data[order(data$at), ]
122130

R/options.R

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@
77
#' change in future versions of the package. Please use with care until
88
#' further notice._
99
#'
10+
#' @section Packages must not change future options:
11+
#'
12+
#' Just like for other R options, as a package developer you must _not_ change
13+
#' any of the below `future.*` options. Only the end-user should set these.
14+
#' If you find yourself having to tweak one of the options, make sure to
15+
#' undo your changes immediately afterward. For example, if you want to
16+
#' bump up the `future.globals.maxSize` limit when creating a future,
17+
#' use something like the following inside your function:
18+
#'
19+
#' ```r
20+
#' oopts <- options(future.globals.maxSize = 1.0 * 1e9) ## 1.0 GB
21+
#' on.exit(options(oopts))
22+
#' f <- future({ expr }) ## Launch a future with large objects
23+
#' ```
24+
#'
1025
#' @section Settings moved to the 'parallelly' package:
1126
#' Several functions have been moved to the \pkg{parallelly} package:
1227
#'
@@ -33,7 +48,7 @@
3348
#'
3449
#' \item{\option{future.globals.maxSize}:}{(numeric) Maximum allowed total size (in bytes) of global variables identified. Used to prevent too large exports. If set of `+Inf`, then the check for large globals is skipped. (Default: `500 * 1024 ^ 2` = 500 MiB)}
3550
#'
36-
#' \item{\option{future.globals.onReference}: (_beta feature - may change_)}{(character string) Controls whether the identified globals should be scanned for so called _references_ (e.g. external pointers and connections) or not. It is unlikely that another \R process ("worker") can use a global that uses a internal reference of the master \R process - we call such objects _non-exportable globals_.
51+
#' \item{\option{future.globals.onReference}: (_beta feature - may change_)}{(character string) Controls whether the identified globals should be scanned for so called _references_ (e.g. external pointers and connections) or not. It is unlikely that another \R process ("worker") can use a global that uses a internal reference of the master \R process---we call such objects _non-exportable globals_.
3752
#' If this option is `"error"`, an informative error message is produced if a non-exportable global is detected.
3853
#' If `"warning"`, a warning is produced, but the processing will continue; it is likely that the future will be resolved with a run-time error unless processed in the master \R process (e.g. `plan(sequential)` and `plan(multicore)`).
3954
#' If `"ignore"`, no scan is performed.
@@ -67,7 +82,7 @@
6782
#'
6883
#' @section Options for controlling package startup:
6984
#' \describe{
70-
#' \item{\option{future.startup.script}:}{(character vector or a logical) Specifies zero of more future startup scripts to be sourced when the \pkg{future} package is _attached_. It is only the first existing script that is sourced. If none of the specified files exist, nothing is sourced - there will be neither a warning nor an error.
85+
#' \item{\option{future.startup.script}:}{(character vector or a logical) Specifies zero of more future startup scripts to be sourced when the \pkg{future} package is _attached_. It is only the first existing script that is sourced. If none of the specified files exist, nothing is sourced---there will be neither a warning nor an error.
7186
#' If this option is not specified, environment variable \env{R_FUTURE_STARTUP_SCRIPT} is considered, where multiple scripts may be separated by either a colon (`:`) or a semicolon (`;`). If neither is set, or either is set to `TRUE`, the default is to look for a \file{.future.R} script in the current directory and then in the user's home directory. To disable future startup scripts, set the option or the environment variable to `FALSE`. _Importantly_, this option is _always_ set to `FALSE` if the \pkg{future} package is loaded as part of a future expression being evaluated, e.g. in a background process. In order words, they are sourced in the main \R process but not in future processes. (Default: `TRUE` in main \R process and `FALSE` in future processes / during future evaluation)}
7287
#'
7388
#' \item{\option{future.cmdargs}:}{(character vector) Overrides \code{\link[base]{commandArgs}()} when the \pkg{future} package is _loaded_.}
@@ -113,9 +128,9 @@
113128
#' environment variable \env{R_FUTURE_*} _when the \pkg{future} package is
114129
#' loaded_. This means that those environment variables must be set before
115130
#' the \pkg{future} package is loaded in order to have an effect.
116-
#' For example, if `R_FUTURE_RNG_ONMISUSE = "ignore"`, then option
131+
#' For example, if `R_FUTURE_RNG_ONMISUSE="ignore"`, then option
117132
#' \option{future.rng.onMisuse} is set to `"ignore"` (character string).
118-
#' Similarly, if `R_FUTURE_GLOBALS_MAXSIZE = "50000000"`, then option
133+
#' Similarly, if `R_FUTURE_GLOBALS_MAXSIZE="50000000"`, then option
119134
#' \option{future.globals.maxSize} is set to `50000000` (numeric).
120135
#'
121136
#' @examples

R/zzz.plan.R

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
#' e.g. sequentially or in parallel.
66
#'
77
#' @param strategy The evaluation function (or name of it) to use
8-
#' for resolving a future. If NULL, then the current strategy is returned.
8+
#' for resolving a future. If `NULL`, then the current strategy is returned.
99
#'
1010
#' @param \dots Additional arguments overriding the default arguments
1111
#' of the evaluation function. Which additional arguments are supported
1212
#' depends on what evaluation function is used, e.g. several support
13-
#' argument `workers` but not all. For details, see the individual
13+
#' argument `workers` but not all. For details, see the individual
1414
#' functions of which some are linked to below.
1515
#"
16-
#' @param substitute If TRUE, the `strategy` expression is
16+
#' @param substitute If `TRUE`, the `strategy` expression is
1717
#' `substitute()`:d, otherwise not.
1818
#'
1919
#' @param .call (internal) Used for recording the call to this function.
2020
#'
2121
#' @param .skip (internal) If `TRUE`, then attempts to set a strategy
22-
#' that is the same as what is currently in use, will skipped.
22+
#' that is the same as what is currently in use, will be skipped.
2323
#'
2424
#' @param .cleanup (internal) Used to stop implicitly started clusters.
2525
#'
@@ -81,10 +81,10 @@
8181
#'
8282
#' @section For package developers:
8383
#' Please refrain from modifying the future strategy inside your packages /
84-
#' functions, i.e. do not call `plan()` in your code. Instead, leave
85-
#' the control on what backend to use to the end user. This idea is part of
86-
#' the core philosophy of the future framework - as a developer you can never
87-
#' know what future backends the user have access to. Moreover, by not making
84+
#' functions, i.e. do not call `plan()` in your code. Instead, leave
85+
#' the control on what backend to use to the end user. This idea is part of
86+
#' the core philosophy of the future framework---as a developer you can never
87+
#' know what future backends the user have access to. Moreover, by not making
8888
#' any assumptions about what backends are available, your code will also work
8989
#' automatically with any new backends developed after you wrote your code.
9090
#'
@@ -107,17 +107,17 @@
107107
#' others' toes._
108108
#'
109109
#' @section Using plan() in scripts and vignettes:
110-
#' When writing scripts or vignettes that uses futures, try to place any
111-
#' call to `plan()` as far up (as early on) in the code as possible.
110+
#' When writing scripts or vignettes that use futures, try to place any
111+
#' call to `plan()` as far up (i.e. as early on) in the code as possible.
112112
#' This will help users to quickly identify where the future plan is set up
113113
#' and allow them to modify it to their computational resources.
114114
#' Even better is to leave it to the user to set the `plan()` prior to
115115
#' `source()`:ing the script or running the vignette.
116116
#' If a \file{\link{.future.R}} exists in the current directory and / or in
117117
#' the user's home directory, it is sourced when the \pkg{future} package is
118-
#' _loaded_. Because of this, the \file{.future.R} file provides a
118+
#' _loaded_. Because of this, the \file{.future.R} file provides a
119119
#' convenient place for users to set the `plan()`.
120-
#' This behavior can be controlled via an \R option - see
120+
#' This behavior can be controlled via an \R option---see
121121
#' \link[future:future.options]{future options} for more details.
122122
#'
123123
#' @importFrom utils capture.output

0 commit comments

Comments
 (0)