Skip to content

Commit

Permalink
nbrOfWorkers(free=TRUE) -> nbrOfFreeWorkers() [#264]
Browse files Browse the repository at this point in the history
  • Loading branch information
HenrikBengtsson committed Dec 3, 2020
1 parent e72e7ad commit ac534a6
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 83 deletions.
9 changes: 7 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ S3method(getExpression,MultisessionFuture)
S3method(getExpression,UniprocessFuture)
S3method(mandelbrot,matrix)
S3method(mandelbrot,numeric)
S3method(nbrOfFreeWorkers,"NULL")
S3method(nbrOfFreeWorkers,cluster)
S3method(nbrOfFreeWorkers,future)
S3method(nbrOfFreeWorkers,multicore)
S3method(nbrOfFreeWorkers,multiprocess)
S3method(nbrOfFreeWorkers,uniprocess)
S3method(nbrOfWorkers,"NULL")
S3method(nbrOfWorkers,cluster)
S3method(nbrOfWorkers,future)
S3method(nbrOfWorkers,logical)
S3method(nbrOfWorkers,multicore)
S3method(nbrOfWorkers,multiprocess)
S3method(nbrOfWorkers,uniprocess)
S3method(plot,Mandelbrot)
Expand Down Expand Up @@ -117,6 +121,7 @@ export(mandelbrot_tiles)
export(multicore)
export(multiprocess)
export(multisession)
export(nbrOfFreeWorkers)
export(nbrOfWorkers)
export(plan)
export(remote)
Expand Down
9 changes: 4 additions & 5 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: future
===============

Version: 1.20.1-9000 [2020-12-01]
Version: 1.20.1-9000 [2020-12-02]

NEW FEATURES:

Expand All @@ -18,10 +18,9 @@ NEW FEATURES:

BETA FEATURES:

* Add support nbrOfWorkers(free = TRUE) to query how many workers are free
to take on futures immediately. Until all third-party future backends have
implemented support for this, some backends might produce an error saying
'free = TRUE' is not yet supported.
* Add nbrOfFreeWorkers() to query how many workers are free to take on futures
immediately. Until all third-party future backends have implemented this,
some backends might produce an error saying it is not yet supported.

BUG FIXES:

Expand Down
145 changes: 81 additions & 64 deletions R/nbrOfWorkers.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,20 @@
#' If NULL (default), the current evaluator as returned
#' by [plan()] is used.
#'
#' @param free If FALSE (default), the total number of workers available
#' to resolve futures is returned. If TRUE, then the number of _free_
#' workers are returned. Note that `free = TRUE` will _not_ attempt to
#' collect results from active futures, which means that, in order to free up
#' workers, functions such as `value()` must be called on active futures.
#' Importantly, since `free = FALSE` was introduced in \pkg{future} 1.21.0, it
#' will take some time before all future backends support it. A backend that
#' supports it should ideally declare that it depends on `future (>= 1.21.0)`.
#'
#' @return A positive number in \eqn{{1, 2, 3, ...}}.
#' If `free = TRUE`, then it \eqn{0} (zero) may also be returned.
#' Note, it may also be `+Inf` for certain types of backends.
#' @return
#' `nbrOfWorkers()` returns a positive number in \eqn{{1, 2, 3, ...}}, which
#' for some future backends may also be `+Inf`.
#'
#' @example incl/nbrOfWorkers.R
#'
#' @export
nbrOfWorkers <- function(evaluator = NULL, free = FALSE) {
nbrOfWorkers <- function(evaluator = NULL) {
UseMethod("nbrOfWorkers")
}


#' @export
nbrOfWorkers.cluster <- function(evaluator, free = FALSE) {
nbrOfWorkers.cluster <- function(evaluator) {
assert_no_positional_args_but_first()

expr <- formals(evaluator)$workers
Expand All @@ -43,48 +34,21 @@ nbrOfWorkers.cluster <- function(evaluator, free = FALSE) {
}
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 1L, is.finite(workers))

if (isTRUE(free)) {
## Create a dummy, lazy future
f <- future(NULL, lazy = TRUE)
name <- attr(f$workers, "name", exact = TRUE)
stop_if_not(is.character(name), length(name) == 1L)
reg <- sprintf("workers-%s", name)
## Number of unresolved cluster futures
usedNodes <- length(FutureRegistry(reg, action = "list", earlySignal = FALSE))
workers <- workers - usedNodes
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 0L, is.finite(workers))
}

workers
}

#' @export
nbrOfWorkers.uniprocess <- function(evaluator, free = FALSE) {
nbrOfWorkers.uniprocess <- function(evaluator) {
assert_no_positional_args_but_first()

1L
}

#' @export
nbrOfWorkers.multicore <- function(evaluator, free = FALSE) {
assert_no_positional_args_but_first()

workers <- NextMethod(free = FALSE)
if (isTRUE(free)) {
workers <- workers - usedCores()
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 0L, is.finite(workers))
}
workers
}

#' @export
nbrOfWorkers.multiprocess <- function(evaluator, free = FALSE) {
nbrOfWorkers.multiprocess <- function(evaluator) {
assert_no_positional_args_but_first()

if (isTRUE(free)) {
stop("nbrOfWorkers(free = TRUE) is not implemented for this type of future backend (please contacts the maintainer of that backend): ", paste(sQuote(class(evaluator)), collapse = ", "))
}

expr <- formals(evaluator)$workers
workers <- eval(expr, enclos = baseenv())
if (is.function(workers)) workers <- workers()
Expand All @@ -98,13 +62,9 @@ nbrOfWorkers.multiprocess <- function(evaluator, free = FALSE) {
}

#' @export
nbrOfWorkers.future <- function(evaluator, free = FALSE) {
nbrOfWorkers.future <- function(evaluator) {
assert_no_positional_args_but_first()

if (isTRUE(free)) {
stop("nbrOfWorkers(free = TRUE) is not implemented for this type of future backend (please contacts the maintainer of that backend): ", paste(sQuote(class(evaluator)), collapse = ", "))
}

expr <- formals(evaluator)$workers
workers <- eval(expr, enclos = baseenv())
if (is.function(workers)) workers <- workers()
Expand All @@ -116,33 +76,90 @@ nbrOfWorkers.future <- function(evaluator, free = FALSE) {
}
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 1L)

if (isTRUE(free)) {
## If we reach this point, we have failed to infer how many free workers
## there are. Unless there is an infinite number of workers available,
## we have no option but producing an error here.
if (is.finite(workers)) {
stop("nbrOfWorkers(free = TRUE) is not implemented for this type of future backend (please contacts the maintainer of that backend): ", paste(sQuote(class(evaluator)), collapse = ", "))
}
}
workers
}

#' @export
nbrOfWorkers.NULL <- function(evaluator) {
assert_no_positional_args_but_first()

nbrOfWorkers(plan("next"))
}



#' @param \dots Not used; reserved for future use.
#'
#' @return
#' `nbrOfFreeWorkers()` returns a non-negative number in
#' \eqn{{0, 1, 2, 3, ...}} which is less than or equal to `nbrOfWorkers()`.
#'
#' @rdname nbrOfWorkers
#' @export
nbrOfFreeWorkers <- function(evaluator = NULL, ...) {
UseMethod("nbrOfFreeWorkers")
}


#' @export
nbrOfFreeWorkers.cluster <- function(evaluator, ...) {
assert_no_positional_args_but_first()

workers <- nbrOfWorkers(evaluator)

## Create a dummy, lazy future
f <- future(NULL, lazy = TRUE)
name <- attr(f$workers, "name", exact = TRUE)
stop_if_not(is.character(name), length(name) == 1L)
reg <- sprintf("workers-%s", name)
## Number of unresolved cluster futures
usedNodes <- length(FutureRegistry(reg, action = "list", earlySignal = FALSE))

workers <- workers - usedNodes
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 0L, is.finite(workers))

workers
}


#' @export
nbrOfWorkers.NULL <- function(evaluator, free = FALSE) {
nbrOfFreeWorkers.uniprocess <- function(evaluator, ...) {
assert_no_positional_args_but_first()

free <- force(free)
if (!isTRUE(free)) return(nbrOfWorkers(plan("next")))
1L
}

## This will produce an error on "unused argument (free = TRUE)"
## for backends that does not yet support a 'free' argument
nbrOfWorkers(plan("next"), free = TRUE)
#' @export
nbrOfFreeWorkers.multicore <- function(evaluator, ...) {
assert_no_positional_args_but_first()

workers <- nbrOfWorkers(evaluator)

workers <- workers - usedCores()
stop_if_not(length(workers) == 1L, !is.na(workers), workers >= 0L, is.finite(workers))

workers
}

#' @export
nbrOfFreeWorkers.multiprocess <- function(evaluator, ...) {
stop("nbrOfFreeWorkers() is not implemented for this type of future backend (please contacts the maintainer of that backend): ", paste(sQuote(class(evaluator)), collapse = ", "))
}

#' @export
nbrOfWorkers.logical <- function(evaluator, free = FALSE) {
nbrOfFreeWorkers.future <- function(evaluator, ...) {
assert_no_positional_args_but_first()
nbrOfWorkers(NULL, free = force(free))

workers <- nbrOfWorkers(evaluator)
if (is.infinite(workers)) return(workers)

stop("nbrOfFreeWorkers() is not implemented for this type of future backend (please contacts the maintainer of that backend): ", paste(sQuote(class(evaluator)), collapse = ", "))
}


#' @export
nbrOfFreeWorkers.NULL <- function(evaluator, ...) {
assert_no_positional_args_but_first()

nbrOfFreeWorkers(plan("next"))
}
22 changes: 10 additions & 12 deletions man/nbrOfWorkers.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ac534a6

Please sign in to comment.