Table of Contents
Fetching ...

A Unified Approach to Concurrent, Parallel Map-Reduce in R using Futures

Henrik Bengtsson

TL;DR

The paper introduces futurize, a unified transpiler that converts sequential map-reduce operations into future-based parallel executions in R, enabling minimal code changes and backend-agnostic execution. By translating calls from base R, purrr, foreach, plyr, and BiocParallel (and various domain-specific packages) into their future counterparts, futurize decouples what to parallelize from how it runs, while preserving stdout, conditions, and error behavior. Its design emphasizes a minimal API surface, a pipe-friendly workflow, and unified future options that abstract away backend-specific configurations. This approach reduces developer and user lock-in, supports a wide range of backends, and demonstrates practical parallelization across numerous packages and domain workflows, potentially accelerating scalable analyses in R. The work highlights future directions for validation, observability, and standardized transpiler interfaces to broaden adoption and reliability in the R parallel computing ecosystem.

Abstract

The R ecosystem offers a rich variety of map-reduce application programming interfaces (APIs) for iterative computations, yet parallelizing code across these diverse frameworks requires learning multiple, often incompatible, parallel APIs. The futurize package addresses this challenge by providing a single function, futurize(), which transpiles sequential map-reduce expressions into their parallel equivalents in the future ecosystem, which performs all the heavy lifting. By leveraging R's native pipe operator, users can parallelize existing code with minimal refactoring -- often by simply appending `|> futurize()' to an expression. The package supports classical map-reduce functions from base R, purrr, crossmap, foreach, plyr, BiocParallel, e.g., lapply(xs, fcn) |> futurize() and map(xs, fcn) |> futurize(), as well as a growing set of domain-specific packages, e.g., boot, caret, glmnet, lme4, mgcv, and tm. By abstracting away the underlying parallel machinery, and unifying handling of future options, the package enables developers to declare what to parallelize via futurize(), and end-users to choose how via plan(). This article describes the philosophy, design, and implementation of futurize, demonstrates its usage across various map-reduce paradigms, and discusses its role in simplifying parallel computing in R.

A Unified Approach to Concurrent, Parallel Map-Reduce in R using Futures

TL;DR

The paper introduces futurize, a unified transpiler that converts sequential map-reduce operations into future-based parallel executions in R, enabling minimal code changes and backend-agnostic execution. By translating calls from base R, purrr, foreach, plyr, and BiocParallel (and various domain-specific packages) into their future counterparts, futurize decouples what to parallelize from how it runs, while preserving stdout, conditions, and error behavior. Its design emphasizes a minimal API surface, a pipe-friendly workflow, and unified future options that abstract away backend-specific configurations. This approach reduces developer and user lock-in, supports a wide range of backends, and demonstrates practical parallelization across numerous packages and domain workflows, potentially accelerating scalable analyses in R. The work highlights future directions for validation, observability, and standardized transpiler interfaces to broaden adoption and reliability in the R parallel computing ecosystem.

Abstract

The R ecosystem offers a rich variety of map-reduce application programming interfaces (APIs) for iterative computations, yet parallelizing code across these diverse frameworks requires learning multiple, often incompatible, parallel APIs. The futurize package addresses this challenge by providing a single function, futurize(), which transpiles sequential map-reduce expressions into their parallel equivalents in the future ecosystem, which performs all the heavy lifting. By leveraging R's native pipe operator, users can parallelize existing code with minimal refactoring -- often by simply appending `|> futurize()' to an expression. The package supports classical map-reduce functions from base R, purrr, crossmap, foreach, plyr, BiocParallel, e.g., lapply(xs, fcn) |> futurize() and map(xs, fcn) |> futurize(), as well as a growing set of domain-specific packages, e.g., boot, caret, glmnet, lme4, mgcv, and tm. By abstracting away the underlying parallel machinery, and unifying handling of future options, the package enables developers to declare what to parallelize via futurize(), and end-users to choose how via plan(). This article describes the philosophy, design, and implementation of futurize, demonstrates its usage across various map-reduce paradigms, and discusses its role in simplifying parallel computing in R.
Paper Structure (33 sections, 1 figure)

This paper contains 33 sections, 1 figure.

Figures (1)

  • Figure 1: An illustration of how a map-reduce lapply() call is evaluated without (left) and with futurize() (right). There are in total eight fcn() calls - one per element in input vector xs. By default, these function calls, or "tasks", are evaluated sequentially one after the other. With futurize(), the evaluation is handled by the future ecosystem. For example, with three parallel workers, the tasks are distributed to workers and processed concurrently, after which their results are collected and reduced to its final form. Depending on future plan() set, parallel workers may run locally on the current machine or remotely on external machines. When futurizing purrr::map() and foreach::foreach() %do% { }, the parallel orchestration works the same way.