<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.2 20190208//EN"
                  "JATS-archivearticle1.dtd">
<article xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" dtd-version="1.2" article-type="other">
<front>
<journal-meta>
<journal-id journal-id-type="publisher-id">jrepstat</journal-id>
<journal-title-group>
<journal-title>Journal of Reproducible Statistics</journal-title>
</journal-title-group>
<publisher>
<publisher-name>FAIR Press</publisher-name>
</publisher>
</journal-meta>
<article-meta>
<title-group>
<article-title>rush: Scalable Asynchronous Distributed Computing via Shared State in R</article-title>
</title-group>
<contrib-group>
<contrib contrib-type="author">
<contrib-id contrib-id-type="orcid">https://orcid.org/0000-0002-8115-0400</contrib-id>
<name>
<surname>Becker</surname>
<given-names>Marc</given-names>
</name>
<aff>Department of Statistics, Ludwig-Maximilians-Universität München, Munich; Munich Center for Machine Learning (MCML), Munich</aff>
</contrib>
<contrib contrib-type="author">
<contrib-id contrib-id-type="orcid">https://orcid.org/0000-0001-6002-6980</contrib-id>
<name>
<surname>Bischl</surname>
<given-names>Bernd</given-names>
</name>
<aff>Department of Statistics, Ludwig-Maximilians-Universität München, Munich; Munich Center for Machine Learning (MCML), Munich</aff>
</contrib>
</contrib-group>
<pub-date date-type="pub" publication-format="electronic">
<year>2026</year>
</pub-date>
<volume>1</volume>
<elocation-id>5</elocation-id>
<history>
<date date-type="received" iso-8601-date="2026-09-04"><day>04</day><month>09</month><year>2026</year></date>
<date date-type="accepted" iso-8601-date="2026-10-13"><day>13</day><month>10</month><year>2026</year></date>
</history>
<permissions>
<license license-type="open-access" xlink:href="https://creativecommons.org/licenses/by/4.0/">
<license-p>This is an open access article distributed under the terms of the Creative Commons Attribution 4.0 International License (CC-BY 4.0), which permits unrestricted use, distribution and reproduction in any medium provided the original work is properly cited.</license-p>
</license>
</permissions>
<self-uri content-type="html" xlink:href="https://jrepstat.fairpressjournals.com/articles/rush-scalable-asynchronous-distributed-computing-via-shared-state-in-r/"/>
<abstract>
<p>Many algorithms in statistics and machine learning can be parallelized in an asynchronous manner where workers need to communicate through shared state rather than execute independent tasks dispatched by a central controller. Especially in modern hyperparameter optimization and parallel black-box optimization with expensive objectives, this decentralized approach has become widespread, and several Python frameworks adopt it (e.g., Optuna, DeepHyper, and Hyperopt). However, all popular R packages for parallel computing follow a centralized controller-worker architecture that does not support this pattern. We present rush, an R package that provides a shared-state coordination layer for asynchronously parallelized iterative algorithms. rush uses a Redis database as a shared key-value store: workers read and write task data through the database and independently execute their own loops. The package provides a high-level API for managing tasks and their lifecycle, featuring sub-millisecond per-task overhead, robust error handling with automatic detection of lost workers, and efficient caching. rush optionally integrates with the mlr3 ecosystem, powering asynchronous optimization in the bbotk and mlr3tuning packages. We demonstrate the practical utility of rush by implementing asynchronous decentralized Bayesian optimization (ADBO) and benchmarking it on hyperparameter optimization of LightGBM across four datasets using 448 workers.</p>
</abstract>
<kwd-group kwd-group-type="author">
<kwd>asynchronous computing</kwd>
<kwd>distributed computing</kwd>
<kwd>R package</kwd>
<kwd>Redis</kwd>
<kwd>Bayesian optimization</kwd>
<kwd>hyperparameter optimization</kwd>
</kwd-group>
<custom-meta-group>
<custom-meta>
<meta-name>data-availability-route</meta-name>
<meta-value>open</meta-value>
</custom-meta>
<custom-meta>
<meta-name>founding-article</meta-name>
<meta-value>true</meta-value>
</custom-meta>
</custom-meta-group>
</article-meta>
</front>
<body>
<sec id="sec-introduction">
  <title>Introduction</title>
  <p>Many iterative algorithms in statistics and machine learning share
  a common computational structure: multiple workers operate
  concurrently, each maintaining local state and autonomously deciding
  what to compute next, while coordinating through a shared pool of
  (intermediate) results. This computational pattern has long-standing
  roots in the <italic>blackboard architecture</italic> (Nii, 1986) and
  the <italic>tuple space</italic> model (Gelernter, 1985), coordination
  paradigms in which autonomous processes communicate through a shared
  associative memory.</p>
  <p>Frameworks for parallel computation have become extremely popular
  in modern machine learning in the context of expensive black-box
  optimization and hyperparameter optimization (HPO, Bischl et al.,
  2023). Here, algorithmic approaches span a wide spectrum: from
  synchronous batch methods (Ginsbourger et al., 2010), through
  asynchronous but centralized schedulers such as ASHA (Li et al.,
  2020)<xref ref-type="fn" rid="fn1">1</xref> and Vizier (Golovin et
  al., 2017), to fully decentralized approaches such as asynchronous
  decentralized Bayesian optimization (ADBO, Egelé et al., 2023), where
  each worker autonomously decides what to compute next.</p>
  <p>In contrast to these decentralized approaches, existing R packages
  for parallel computing follow a fundamentally different design.
  Packages such as <monospace>parallel</monospace>,
  <monospace>future</monospace> (Bengtsson, 2021),
  <monospace>mirai</monospace> (Gao, 2025),
  <monospace>batchtools</monospace> (Lang et al., 2017), and
  <monospace>crew</monospace> (Landau, 2026) adopt a controller-worker
  architecture (Figure <xref alt="1" rid="fig-rush-network">1</xref>,
  left): a central process proposes tasks, dispatches them to workers,
  and collects results. These frameworks excel at parallelizing
  independent computations but do not support algorithms in which
  workers coordinate through shared state and autonomously decide what
  to compute next. Beyond expressiveness — some algorithms are simply
  more natural when each worker has the coordination logic locally — the
  decentralized design also has computational advantages. Planning and
  generating further downstream tasks can themselves incur
  non-negligible cost, and moving this work from a central node to the
  workers allows it to run in parallel and enables more efficient task
  spawning, especially when many workers are available.</p>
  <p>To fill this gap, we present <monospace>rush</monospace>, an R
  package that provides a shared-state coordination layer for iterative
  algorithms. <monospace>rush</monospace> uses a
  <monospace>Redis</monospace> database as its shared store: workers
  communicate by reading and writing task data—inputs, outputs, and
  computational states—through the database, each independently
  executing its own loop
  (Figure <xref alt="1" rid="fig-rush-network">1</xref>, right). The
  package provides a high-level API for managing tasks and their
  lifecycle, featuring sub-millisecond per-task overhead, robust error
  handling with automatic detection of lost workers, and an efficient
  caching mechanism that minimizes database operations.
  <monospace>rush</monospace> integrates with the
  <monospace>mlr3</monospace> (Lang et al., 2019) ecosystem, powering
  asynchronous optimization in the <monospace>bbotk</monospace> (Becker,
  Richter, et al., 2026) and <monospace>mlr3tuning</monospace> (Becker
  et al., 2025) packages.</p>
  <p>While <monospace>rush</monospace> is designed as a general
  coordination layer and can be used for a wide range of algorithms, we
  demonstrate its utility through a case study in black-box optimization
  — which was also our primary motivation for developing
  <monospace>rush</monospace>. We implement ADBO (Egelé et al., 2023) on
  <monospace>rush</monospace> and benchmark it across four datasets
  using 448 workers. Our experiments show that ADBO on
  <monospace>rush</monospace> achieves a CPU utilization above 93% in
  every run, compared to single-digit utilization for centralized
  approaches on short-running tasks.</p>
  <fig id="fig-rush-network">
    <caption><p>Centralized (left) and decentralized (right) networks.
    Octagons denote processes, including the main process
    <monospace>M</monospace> and workers <monospace>W</monospace>. In
    the centralized network, <monospace>M</monospace> proposes tasks to
    the workers and processes the returned computations. In the
    decentralized network, each worker <monospace>W</monospace>
    independently proposes tasks, evaluates them, and exchanges task
    information via a shared <monospace>Redis</monospace> database (red
    rectangle).</p></caption>
    <graphic mimetype="image" mime-subtype="png" xlink:href="figures/network_architectures.png" />
  </fig>
  <sec id="sec-related-work">
    <title>Related work</title>
    <p>The widespread adoption of multi-core processors in the 2000s
    created a growing need to utilize these resources effectively for
    computational tasks in R. The first packages to address this need
    were <monospace>snow</monospace> (Rossini et al., 2007) and
    <monospace>multicore</monospace>. With R version 2.14.0 (released in
    2011), parallel computing capabilities were integrated into the base
    R system through the <monospace>parallel</monospace> package. The
    <monospace>parallel</monospace> package provides
    <monospace>mclapply()</monospace> and
    <monospace>parLapply()</monospace> as parallel versions of
    <monospace>lapply()</monospace> for multicore and cluster computing,
    respectively. Although widely used, both functions block the R
    session until all tasks have completed, preventing retrieval of
    partial results. Moreover, their static task allocation can lead to
    poor load balancing when tasks have heterogeneous runtimes.</p>
    <p>The <monospace>future</monospace> package (Bengtsson, 2021)
    introduced a unified parallel computing interface in R, supporting
    various backends such as <monospace>multisession</monospace>,
    <monospace>multicore</monospace>, and <monospace>callr</monospace>,
    with the <monospace>future.apply</monospace> package providing
    parallel versions of the <monospace>*apply()</monospace> family of
    functions. However, the <monospace>multisession</monospace> and
    <monospace>cluster</monospace> backends are limited to 125 workers.
    Furthermore, per-task overhead — from process spawning
    (<monospace>multicore</monospace>, <monospace>callr</monospace>) or
    serialization across persistent workers
    (<monospace>multisession</monospace>,
    <monospace>cluster</monospace>) — can be significant for
    short-running tasks.</p>
    <p>The <monospace>mirai</monospace> package (Gao, 2025) evaluates an
    R expression asynchronously in a parallel process, either locally or
    distributed across the network. The package has a strong focus on
    throughput, low overhead, and modern networking, eliminating many
    drawbacks of the older approaches. <monospace>mirai</monospace>
    provides a comprehensive daemon management interface for launching
    and configuring persistent daemons across local and remote systems
    (including SSH-based deployment and HPC schedulers).
    <monospace>rush</monospace> leverages this mechanism to start
    workers on remote machines.</p>
    <p>The <monospace>batchtools</monospace> package (Lang et al., 2017)
    facilitates the execution of long-running tasks and large-scale
    computer experiments on HPC clusters. Because communication between
    the main process and workers relies entirely on the file system, the
    package incurs substantial per-task overhead from both job
    scheduling and file I/O, making it less suitable for high-throughput
    workloads with many short-running tasks. The more recent
    <monospace>crew</monospace> package (Landau, 2026) targets a similar
    use case, supporting long-running tasks in distributed systems
    ranging from traditional HPC clusters to cloud computing platforms,
    but likewise depends on a centralized controller for task
    coordination.</p>
    <p>The parallel and distributed computing ecosystem in R is
    complemented by database connectivity layers that enable shared
    state and coordination. For relational database management systems,
    <monospace>DBI</monospace> (R Special Interest Group on Databases
    (R-SIG-DB) et al., 2024) defines a standardized interface that
    separates a common frontend from database-specific backends. For
    <monospace>Redis</monospace>, <monospace>redux</monospace> provides
    R bindings to the <monospace>Redis</monospace> API and exposes the
    command set through a low-level and a high-level interface.</p>
    <p>The <monospace>rrq</monospace> (FitzJohn &amp; Ashton, 2026)
    package is a task queue system for R using
    <monospace>Redis</monospace> as a coordination backend. It addresses
    the limitations of other packages by providing a non-blocking
    interface to parallel computing and keeping the overhead per task
    low. The package supports independent task queues, priority levels
    within queues, and inter-task dependencies.
    <monospace>rrq</monospace> is implemented on top of
    <monospace>redux</monospace>, which provides the underlying
    <monospace>Redis</monospace> connectivity and command interface. Its
    error-handling mechanism, particularly its heartbeat approach,
    directly informed the design of the corresponding mechanism in
    <monospace>rush</monospace>.</p>
    <p>Python has a rich ecosystem for parallel and distributed
    computing. <monospace>Dask</monospace> (Rocklin, 2015) enables
    parallel and distributed execution via a dynamic task scheduler, and
    <monospace>Ray</monospace> (Moritz et al., 2018) is a
    general-purpose distributed computing framework. For HPO
    specifically, many frameworks adopt a centralized architecture in
    which a central scheduler dispatches configurations to workers:
    <monospace>Ray Tune</monospace> (Liaw et al., 2018),
    <monospace>Syne Tune</monospace> (Salinas et al., 2022),
    <monospace>Google Vizier</monospace> (Golovin et al., 2017),
    <monospace>Ax</monospace> (Olson et al., 2025),
    <monospace>Dragonfly</monospace> (Kandasamy et al., 2020), and
    <monospace>Keras Tuner</monospace> (O’Malley et al., 2019). In
    contrast, frameworks such as <monospace>Optuna</monospace> (Akiba et
    al., 2019), <monospace>DeepHyper</monospace> (Egelé et al., 2025),
    <monospace>Hyperopt</monospace> (Bergstra et al., 2013), and
    <monospace>Orion</monospace> (Bouthillier et al., 2023) support
    decentralized operation by coordinating workers through a shared
    database (SQL, <monospace>MongoDB</monospace>, or
    <monospace>Redis</monospace>), enabling each worker to independently
    propose and evaluate configurations.</p>
    <p>In summary, the existing R ecosystem for parallel and distributed
    computing is rich but uniformly follows a controller-worker
    architecture. Packages such as <monospace>parallel</monospace>,
    <monospace>future</monospace>, and <monospace>crew</monospace>
    centralize task proposal and result collection in a single process,
    which can become a bottleneck at scale. While
    <monospace>rrq</monospace> and <monospace>mirai</monospace> reduce
    per-task overhead, neither supports decentralized optimization loops
    in which workers independently propose and evaluate tasks. At least
    in the domains of expensive black-box optimization and HPO, Python
    offers frameworks such as <monospace>DeepHyper</monospace> and
    <monospace>Optuna</monospace> that support decentralized workflows,
    but no comparable solution exists in R. <monospace>rush</monospace>
    addresses this gap by combining a decentralized architecture with
    low-overhead <monospace>Redis</monospace>-based communication.</p>
  </sec>
</sec>
<sec id="sec-general-structure">
  <title>Architecture and API of rush</title>
  <p>This section introduces the architecture and core API of
  <monospace>rush</monospace>. A <monospace>rush</monospace> network
  consists of multiple workers that communicate via a shared
  <monospace>Redis</monospace> database. A worker can read shared state
  from the database, compute new (partial) results, and potentially
  write them back into the shared database, as illustrated in
  Figure <xref alt="2" rid="fig-worker-communication">2</xref>; see
  Figure <xref alt="1" rid="fig-rush-network">1</xref> (right) for the
  full network topology.</p>
  <fig id="fig-worker-communication">
    <caption><p>The communication flow between a worker and the
    <monospace>Redis</monospace> database in a
    <monospace>rush</monospace> network. The octagon represents a worker
    and the rectangle represents the <monospace>Redis</monospace>
    database. In each iteration, the worker reads shared state via
    <monospace>$fetch_tasks_with_state()</monospace>, registers a new
    task via <monospace>$push_running_tasks()</monospace>, computes the
    result, and writes it back via
    <monospace>$finish_tasks()</monospace>.</p></caption>
    <graphic mimetype="image" mime-subtype="png" xlink:href="figures/worker_communication.png" />
  </fig>
  <sec id="sec-manager">
    <title>Manager</title>
    <p>The <monospace>Rush</monospace> manager class is responsible for
    creating, monitoring, and stopping a network. It is initialized
    using the <monospace>rsh()</monospace> function, which requires a
    network identifier and a <monospace>config</monospace> argument that
    specifies how to connect to the <monospace>Redis</monospace>
    database via the <monospace>redux</monospace> package. A running
    <monospace>Redis</monospace> server must be started separately
    beforehand.<xref ref-type="fn" rid="fn2">2</xref></p>
    <code language="r script">library(rush)

config = redux::redis_config()

rush = rsh(network = &quot;demo-network&quot;, config = config)</code>
    <p>Workers are started using the
    <monospace>$start_workers()</monospace> method, which accepts the
    worker loop (see
    Section <xref alt="2.2" rid="sec-worker-loop">2.2</xref>) and the
    number of workers as arguments. Any additional named arguments are
    forwarded to the worker loop function. Workers run on
    <monospace>mirai</monospace> daemons (Gao, 2025), which are launched
    via <monospace>mirai::daemons()</monospace>. Calling
    <monospace>mirai::daemons(n)</monospace> opens a listener socket on
    the host, spawns a lightweight dispatcher implemented in C, and
    launches <monospace>n</monospace> persistent R processes that dial
    into the dispatcher and enter a receive–evaluate–return loop. Note
    that while <monospace>rush</monospace> still uses this mechanism to
    set up workers, most of the communication is actually facilitated
    via the <monospace>Redis</monospace> database, e.g., worker loop
    arguments, task inputs, outputs, and states. Daemons can be spawned
    on the local machine, on remote nodes via SSH, or on HPC clusters
    via job schedulers such as SLURM or SGE — in all cases, the daemon
    process dials back to the host and the same
    <monospace>rush</monospace> worker loop runs transparently.
    Alternatively, workers can be started locally via the
    <monospace>processx</monospace> package using
    <monospace>$start_local_workers()</monospace>, or a standalone
    script can be generated with <monospace>$worker_script()</monospace>
    for manual deployment; the only requirement is that the worker can
    connect to the <monospace>Redis</monospace> database.</p>
    <code language="r script">mirai::daemons(4)

rush$start_workers(
  worker_loop = worker_loop,
  n_workers = 4)
rush$wait_for_workers(4)</code>
    <p>Since <monospace>$start_workers()</monospace> returns
    immediately, <monospace>$wait_for_workers(n)</monospace> blocks
    until <monospace>n</monospace> workers have registered in
    <monospace>Redis</monospace>. Printing the
    <monospace>Rush</monospace> object reports the number of running
    workers and a summary of task states across the network (see
    Section <xref alt="2.3" rid="sec-tasks">2.3</xref>).</p>
    <code language="r script">rush</code>
    <preformat>
-- &lt;Rush&gt; --------------------------------------------------------------
* Running Workers: 4
* Queued Tasks: 0
* Running Tasks: 0
* Finished Tasks: 0
* Failed Tasks: 0</preformat>
    <p>The <monospace>$worker_info</monospace> active binding returns a
    <monospace>data.table</monospace> (Barrett et al., 2026) with one
    row per worker, containing the worker identifier, the process ID,
    the host on which it runs, whether a heartbeat (see
    Section <xref alt="2.5" rid="sec-error-handling">2.5</xref>) is
    active, and its current state.</p>
    <code language="r script">rush$worker_info</code>
    <preformat>          worker_id   pid         hostname profile heartbeat   state
             &lt;char&gt; &lt;int&gt;           &lt;char&gt;  &lt;char&gt;    &lt;lgcl&gt;  &lt;char&gt;
1: pseudopolitic... 37025 ThinkPad-T14-...    &lt;NA&gt;     FALSE running
2: idiocratic_ca... 37028 ThinkPad-T14-...    &lt;NA&gt;     FALSE running
3: papery_sloth_... 37023 ThinkPad-T14-...    &lt;NA&gt;     FALSE running
4: precarnival_b... 37021 ThinkPad-T14-...    &lt;NA&gt;     FALSE running</preformat>
    <p>The <monospace>profile</monospace> column identifies each
    worker’s compute profile, which can be used to group workers by
    available resources, e.g., CPUs or GPUs.</p>
    <p>The workers can be stopped individually via
    <monospace>$stop_workers(ids)</monospace> or all together using the
    <monospace>$reset()</monospace> method, which also resets the
    network and database.</p>
  </sec>
  <sec id="sec-worker-loop">
    <title>Worker loop</title>
    <p>A worker loop is a plain R function that receives a
    <monospace>RushWorker</monospace> object for communicating with
    <monospace>Redis</monospace>.</p>
    <code language="r script">worker_loop = function(rush, n_evals) {
  while (rush$n_finished_tasks &lt; n_evals) {
    archive = rush$fetch_tasks_with_state(states = c(&quot;running&quot;, &quot;finished&quot;))
    xs = compute_task_inputs(archive)
    keys = rush$push_running_tasks(xss = list(xs))
    ys = compute_task_results(xs)
    rush$finish_tasks(keys, yss = list(ys))
  }
}</code>
    <p>The above shows an abstract template for an exemplary situation
    where <monospace>rush</monospace> excels — and which is already
    fairly close to the asynchronous, decentralized HPO use case that we
    will cover in more detail in
    Section <xref alt="3" rid="sec-case-study">3</xref>. In the (common)
    example above, we assume the following:</p>
    <list list-type="bullet">
      <list-item>
        <p>The worker needs access to all available partial results to
        define and compute its next task.</p>
      </list-item>
      <list-item>
        <p>The inputs for the task also have to be computed and thus
        incur non-negligible costs.</p>
      </list-item>
      <list-item>
        <p>The computation of the task itself is time-consuming.</p>
      </list-item>
    </list>
    <p>Inside the loop, the worker communicates with the shared
    <monospace>Redis</monospace> database through the following core
    methods of the <monospace>RushWorker</monospace> object:</p>
    <list list-type="bullet">
      <list-item>
        <p><monospace>$push_running_tasks(xss)</monospace> - Creates one
        or more new tasks with inputs <monospace>xss</monospace> in the
        database, marks them as
        <monospace>&quot;running&quot;</monospace>, and returns their
        unique keys. Called before evaluation begins.</p>
      </list-item>
      <list-item>
        <p><monospace>$fetch_finished_tasks()</monospace> - Retrieves
        all finished tasks from the database as a
        <monospace>data.table</monospace> (keys, inputs, outputs).
        Supports caching: previously fetched tasks are served from a
        local cache, and only new results are read from
        <monospace>Redis</monospace>.</p>
      </list-item>
      <list-item>
        <p><monospace>$fetch_tasks_with_state(states)</monospace> - More
        general variant that retrieves tasks in specified states (e.g.,
        both <monospace>&quot;running&quot;</monospace> and
        <monospace>&quot;finished&quot;</monospace>), useful when the
        worker needs to account for evaluations still in progress.</p>
      </list-item>
      <list-item>
        <p><monospace>$finish_tasks(keys, yss)</monospace> - For tasks
        named by <monospace>keys</monospace>, writes corresponding
        results <monospace>yss</monospace> to the database and marks
        them as <monospace>&quot;finished&quot;</monospace>.</p>
      </list-item>
      <list-item>
        <p><monospace>$n_finished_tasks</monospace> - Active binding
        that returns the current global count of finished tasks across
        all workers, commonly used as a termination criterion.</p>
      </list-item>
    </list>
    <p>Both <monospace>$push_running_tasks()</monospace> and
    <monospace>$finish_tasks()</monospace> accept an
    <monospace>extra</monospace> argument for associating auxiliary
    information with tasks and results that does not naturally fit into
    the inputs or outputs.</p>
  </sec>
  <sec id="sec-tasks">
    <title>Tasks</title>
    <p>Tasks are the basic units through which workers exchange
    information. Each task consists of four components: a unique key
    used to reference the task in the <monospace>Redis</monospace>
    database, a computational state, an input
    (<monospace>xs</monospace>), and an output
    (<monospace>ys</monospace>). The input and output are lists that may
    contain arbitrary data.</p>
    <p>Tasks always have an associated computational state:
    <monospace>&quot;running&quot;</monospace>,
    <monospace>&quot;finished&quot;</monospace>,
    <monospace>&quot;failed&quot;</monospace>, or
    <monospace>&quot;queued&quot;</monospace>. The typical task
    lifecycle involves two transitions. The
    <monospace>$push_running_tasks()</monospace> method creates tasks
    marked as <monospace>&quot;running&quot;</monospace> and returns
    their keys. Upon completion, <monospace>$finish_tasks()</monospace>
    marks tasks as <monospace>&quot;finished&quot;</monospace> and
    stores the associated results. Tasks that encounter errors can be
    marked as <monospace>&quot;failed&quot;</monospace> using the
    <monospace>$fail_tasks()</monospace> method (see
    Section <xref alt="2.5" rid="sec-error-handling">2.5</xref>). The
    fourth state, <monospace>&quot;queued&quot;</monospace>, supports a
    queue mechanism described in
    Section <xref alt="2.4" rid="sec-queues">2.4</xref>.</p>
  </sec>
  <sec id="sec-queues">
    <title>Queues</title>
    <p>In addition to the autonomous loop pattern,
    <monospace>rush</monospace> supports a queue mechanism for cases in
    which tasks are created centrally and distributed to workers. The
    <monospace>$push_tasks()</monospace> method creates tasks in the
    <monospace>&quot;queued&quot;</monospace> state:</p>
    <code language="r script">xss = lapply(1:20, function(i) {
  list(x1 = runif(1, -5, 5), x2 = runif(1, -5, 5))
})

rush$push_tasks(xss = xss)</code>
    <p>Workers consume queued tasks using
    <monospace>$pop_task()</monospace>, which retrieves the next task,
    marks it as <monospace>&quot;running&quot;</monospace>, and returns
    it. When the queue is empty, the method returns
    <monospace>NULL</monospace>, which serves as the termination
    signal:</p>
    <code language="r script">worker_loop_queue = function(rush, fun) {
  repeat {
    task = rush$pop_task()
    if (is.null(task)) break
    ys = list(y = fun(task$xs$x1, task$xs$x2))
    rush$finish_tasks(task$key, yss = list(ys))
  }
}</code>
    <p>The two patterns can be combined: workers first drain a queue of
    centrally created tasks, then transition to an autonomous loop for
    the remainder of the computation.</p>
  </sec>
  <sec id="sec-error-handling">
    <title>Error handling</title>
    <p><monospace>rush</monospace> provides error-handling mechanisms
    for two failure modes: R errors during task evaluation and
    unexpected worker crashes. For errors that occur during task
    evaluation, users must catch them within the worker loop and mark
    the corresponding task as <monospace>&quot;failed&quot;</monospace>
    using <monospace>$fail_tasks()</monospace>. If the error is not
    caught, the worker will crash.</p>
    <code language="r script">tryCatch({
  ys = list(y = fun(xs$x1, xs$x2))
  rush$finish_tasks(key, yss = list(ys))
}, error = function(e) {
  condition = list(message = e$message)
  rush$fail_tasks(key, conditions = list(condition))
})</code>
    <p>When a worker fails unexpectedly due to a crash, segmentation
    fault, or lost network connection, its tasks may remain in the
    <monospace>&quot;running&quot;</monospace> state indefinitely. The
    <monospace>$detect_lost_workers()</monospace> method identifies such
    workers and updates their state to
    <monospace>&quot;terminated&quot;</monospace>. For workers started
    with <monospace>$start_workers()</monospace> or
    <monospace>$start_local_workers()</monospace>, lost worker detection
    works automatically by checking process status. Workers started via
    <monospace>$worker_script()</monospace> require an additional
    heartbeat mechanism: a background process periodically refreshes a
    <monospace>Redis</monospace> key with a set expiration timeout, and
    if the worker fails, the key expires, signaling that the worker is
    lost:</p>
    <code language="r script">script = rush$worker_script(
  worker_loop = worker_loop,
  heartbeat_period = 1,
  heartbeat_expire = 3)
cat(strwrap(script, width = 76, exdent = 2), sep = &quot;\n&quot;)</code>
    <preformat>Rscript -e 'rush::start_worker(network_id = &quot;demo-network&quot;, config =
  list(scheme = &quot;redis&quot;, host = &quot;127.0.0.1&quot;, port = &quot;6379&quot;),
  heartbeat_period = 1L, heartbeat_expire = 3L)'</preformat>
  </sec>
  <sec id="logging">
    <title>Logging</title>
    <p>Workers can write messages generated via the
    <monospace>lgr</monospace> (Fleck, 2026) package to the database.
    The <monospace>lgr_thresholds</monospace> argument of
    <monospace>$start_workers()</monospace> specifies the logging level
    for each logger by name, e.g.,
    <monospace>c(&quot;mlr3/rush&quot; = &quot;debug&quot;)</monospace>.
    The <monospace>mlr3/</monospace> prefix denotes the parent logger
    shared across the <monospace>mlr3</monospace> ecosystem, into which
    <monospace>rush</monospace> integrates. Logging introduces a minor
    performance overhead and is disabled by default.</p>
    <code language="r script">wl_log_message = function(rush) {
  lg = lgr::get_logger(&quot;mlr3/rush&quot;)
  lg$info(&quot;This is an info message from worker %s&quot;, rush$worker_id)
}

rush$start_workers(
  worker_loop = wl_log_message,
  n_workers = 2,
  lgr_thresholds = c(&quot;mlr3/rush&quot; = &quot;info&quot;))</code>
    <p>To retrieve all log entries, use the
    <monospace>$read_log()</monospace> method.</p>
    <code language="r script">Sys.sleep(1)

rush$read_log()[, list(worker_id, msg)]</code>
    <preformat>                         worker_id                             msg
                            &lt;char&gt;                          &lt;char&gt;
1: ungrammatical_beauceron_32fe... [rush] This is an info messa...
2:        accessible_bear_15e5052b [rush] This is an info messa...</preformat>
    <p>Outputs and messages can be written to files. Set a path to a
    folder via the <monospace>message_log</monospace> and
    <monospace>output_log</monospace> arguments of
    <monospace>$start_workers()</monospace>. Messages and outputs are
    written to files named
    <monospace>message_&lt;worker_id&gt;.log</monospace> and
    <monospace>output_&lt;worker_id&gt;.log</monospace> in that folder,
    respectively.</p>
    <code language="r script">rush$start_workers(
  worker_loop = wl_log_message,
  n_workers = 2,
  message_log = &quot;logs&quot;,
  output_log = &quot;logs&quot;)</code>
  </sec>
  <sec id="sec-data-storage">
    <title>Data storage</title>
    <p>By default, <monospace>rush</monospace> stores all data in a
    <monospace>Redis</monospace> database. <monospace>Redis</monospace>
    is an open-source, in-memory key-value store commonly used as a
    database, cache, and message broker. It offers extremely low latency
    and supports multiple data structures such as strings, hashes,
    lists, and sets. <monospace>rush</monospace> maps its data model
    onto three <monospace>Redis</monospace> data structures: hashes,
    sets, and lists.</p>
    <p><monospace>Redis</monospace> hashes are a data structure for
    storing associative field–value pairs under a single key. They are
    optimized for memory efficiency and fast access to structured data.
    This makes them suitable for representing the input, output, and
    metadata of tasks. The key of the hash identifies the task in
    <monospace>Redis</monospace> and <monospace>rush</monospace>. A task
    is stored as a <monospace>Redis</monospace> hash with the following
    structure:</p>
    <preformat>key : xs | ys | xs_extra</preformat>
    <p>The field–value pairs are written by different methods, e.g.,
    <monospace>$push_running_tasks()</monospace> writes
    <monospace>xs</monospace> and <monospace>$finish_tasks()</monospace>
    writes <monospace>ys</monospace>. The values of the fields are
    serialized lists or atomic values, e.g., deserializing the
    <monospace>xs</monospace> field gives
    <monospace>list(x1 = 1, x2 = 2)</monospace>. This structure allows a
    hash to be efficiently converted into a table row and multiple
    hashes to be joined into a table. When retrieved, multiple hashes
    are joined into a tabular format:</p>
    <preformat>| key | x1 | x2 | y | timestamp |
| 1.. |  3 |  4 | 7 |  12:04:11 |
| 2.. |  1 |  4 | 5 |  12:04:12 |
| 3.. |  1 |  1 | 2 |  12:04:13 |</preformat>
    <p><monospace>Redis</monospace> sets are unordered collections of
    unique elements that support efficient membership tests.
    <monospace>rush</monospace> uses sets to track task states; for
    example, the keys of all currently running tasks are stored in the
    <monospace>&quot;running_tasks&quot;</monospace> set.</p>
    <p>Lists in <monospace>Redis</monospace> are ordered collections
    stored as linked lists that support efficient insertion and removal
    at both ends. <monospace>rush</monospace> uses lists for two
    purposes: managing the task queue and recording finished tasks. For
    the queue, the list structure is a natural choice, as it supports
    atomic push and pop operations. Storing the finished tasks in a list
    gives them an order by time, enabling workers to cheaply retrieve
    only the most recent results.</p>
    <p><monospace>rush</monospace> uses three mechanisms to coordinate
    concurrent access to the database. First,
    <monospace>Redis</monospace> executes commands on a single thread,
    making individual operations such as adding a key to a set or
    appending a key to a list atomic. Second, operations involving
    several keys are grouped into
    <monospace>MULTI</monospace>/<monospace>EXEC</monospace>
    transactions, which <monospace>Redis</monospace> executes without
    interruption by other commands. For example,
    <monospace>$push_running_tasks()</monospace> adds new keys to the
    <monospace>&quot;running_tasks&quot;</monospace> set and the list of
    all tasks in a single transaction. Third, potentially competing
    state transitions are implemented as atomic
    <monospace>Lua</monospace> scripts. These include transitions by
    <monospace>$finish_tasks()</monospace> and
    <monospace>$fail_tasks()</monospace>, insuring that a task is not
    recorded in two states simultaneously.</p>
    <p>To minimize the number of <monospace>Redis</monospace>
    round-trips when workers repeatedly access many tasks (common in
    iterative algorithms), each <monospace>Rush</monospace> instance
    keeps a local <monospace>data.table</monospace> cache of the
    finished tasks it has already seen. On subsequent fetches, only the
    keys after the current cache length are requested; the corresponding
    hashes are read and appended to the cached table. This way, repeated
    queries scale with the number of newly finished tasks instead of the
    total result history.</p>
    <p>The <monospace>large_objects_path</monospace> option allows large
    objects to be stored on a shared file system accessible to all
    workers. This can reduce network transfer overhead by allowing
    workers on the same node to benefit from cached file reads.</p>
  </sec>
  <sec id="retrieving-results">
    <title>Retrieving results</title>
    <p>Once all computational tasks are completed, the results can be
    retrieved from the database. As already explained, the
    <monospace>$fetch_finished_tasks()</monospace> method returns a
    <monospace>data.table</monospace> containing the task key, input,
    and result.</p>
  </sec>
  <sec id="sec-testing">
    <title>Testing</title>
    <p><monospace>rush</monospace> includes 105 unit tests written with
    <monospace>testthat</monospace> (Wickham, 2011). GitHub Actions runs
    <monospace>R CMD check</monospace>, including these tests, on Ubuntu
    with the release and development versions of R. The tests cover the
    core functionality, error handling, lost-worker detection,
    concurrent access, and caching behavior of the package.</p>
  </sec>
</sec>
<sec id="sec-case-study">
  <title>Ask-and-tell and asynchronous decentralized Bayesian
  optimization</title>
  <sec id="sec-ask-tell">
    <title>The ask-and-tell pattern in optimization</title>
    <p>The worker loop we discussed in
    Section <xref alt="2.2" rid="sec-worker-loop">2.2</xref> directly
    mirrors the <italic>ask-and-tell</italic> interface pattern that has
    become the dominant API design for black-box optimization
    frameworks. In this pattern, the optimizer exposes two methods —
    <monospace>$ask()</monospace>, which proposes the next candidate
    point(s), and <monospace>$tell()</monospace>, which receives the
    corresponding evaluation result(s) — while the user retains full
    control over the evaluation loop. In the worker loop template, the
    trio of <monospace>$fetch_tasks_with_state()</monospace>,
    <monospace>compute_task_inputs()</monospace>, and
    <monospace>$push_running_tasks()</monospace> corresponds to the
    <italic>ask</italic> step, while
    <monospace>$finish_tasks()</monospace> corresponds to the
    <italic>tell</italic> step. This design decouples the optimizer from
    the objective function evaluation, allowing users to integrate
    optimization into arbitrary workflows. Prominent implementations
    include <monospace>Optuna</monospace>
    (<monospace>study.ask()</monospace>/<monospace>study.tell()</monospace>,
    Akiba et al., 2019), <monospace>Ax</monospace>
    (<monospace>get_next_trial()</monospace>/<monospace>complete_trial()</monospace>,
    Olson et al., 2025), <monospace>SMAC3</monospace> (Lindauer et al.,
    2022), and <monospace>scikit-optimize</monospace> (Head et al.,
    2018).</p>
    <p>The ask-and-tell interface enables two fundamentally different
    parallelization strategies for optimization, which differ in where
    the optimization logic runs and how workers coordinate.</p>
    <p><bold>Synchronous batch parallelism.</bold> In this scenario, a
    single central process runs the optimizer. In each iteration, the
    optimizer asks for a batch of <inline-formula><alternatives>
    <tex-math><![CDATA[q]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>q</mml:mi></mml:math></alternatives></inline-formula>
    candidate points, which are then evaluated in parallel across
    <inline-formula><alternatives>
    <tex-math><![CDATA[q]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>q</mml:mi></mml:math></alternatives></inline-formula>
    workers using a standard parallel backend (e.g.,
    <monospace>future</monospace>, <monospace>mirai</monospace>, or
    <monospace>parallel</monospace>). Once all
    <inline-formula><alternatives>
    <tex-math><![CDATA[q]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>q</mml:mi></mml:math></alternatives></inline-formula>
    evaluations complete, the results are told back to the optimizer,
    which updates its model and proposes the next batch. No shared state
    beyond the optimizer is needed, but depending on the complexity of
    the <monospace>$ask()</monospace> step, the central optimizer can
    become a bottleneck. Furthermore, synchronous batches waste
    resources when evaluation times are heterogeneous, as all workers
    must wait for the slowest evaluation in each batch to complete.</p>
    <p><bold>Asynchronous decentralized parallelism.</bold> In this
    scenario, each worker runs its own local optimization and
    ask-and-tell loop: it reads the shared history of completed (and
    possibly running) evaluations, asks its local optimizer for a
    proposal, evaluates it, and tells the result back to a shared store.
    Workers operate fully independently — they do not wait for each
    other and do not coordinate through a central controller. This
    eliminates both the controller bottleneck and the synchronization
    barrier. However, it requires infrastructure for shared-state
    coordination: workers must be able to read each other’s results and
    register their own tasks in a concurrent, conflict-free manner.</p>
  </sec>
  <sec id="sec-adbo">
    <title>Asynchronous decentralized Bayesian optimization</title>
    <p>Bayesian optimization (BO) is a sample-efficient strategy for
    optimizing expensive black-box functions. It maintains a
    probabilistic surrogate model (typically a Gaussian process or
    random forest) fitted to all completed evaluations, and selects the
    next evaluation point by optimizing an acquisition function that
    balances exploration and exploitation. In the standard sequential
    setting, a single process alternates between fitting the surrogate,
    proposing a point, and evaluating it. In ADBO (Egelé et al., 2023),
    this loop is replicated across multiple workers, each running
    independently. Every worker reads the shared history of evaluations
    from the database, fits its own local surrogate model, and proposes
    the next point autonomously — without waiting for other workers or
    coordinating through a central controller. For design points
    currently being evaluated on other workers (i.e., without a known
    objective value yet), a fake objective is imputed — usually a value
    between the minimum and maximum observed objective values in the
    archive. This penalizes the proposal of further design points near
    those currently under evaluation. Results are written back to the
    shared store as they complete, so all workers benefit from each
    other’s evaluations. To encourage diversity among proposals, each
    worker uses a stochastic acquisition function with different
    exploration–exploitation trade-offs (e.g., different
    <inline-formula><alternatives>
    <tex-math><![CDATA[\lambda]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>λ</mml:mi></mml:math></alternatives></inline-formula>
    values in a confidence bound criterion). This architecture
    eliminates the central bottleneck that arises in synchronous or
    centralized parallel BO, where a single process must fit the
    surrogate and propose points for all workers; see
    Section <xref alt="5" rid="sec-benchmarks-bo">5</xref>.</p>
    <p>We use the Branin function, defined over the domain
    <inline-formula><alternatives>
    <tex-math><![CDATA[x_1 \in [-5, 10]]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:msub><mml:mi>x</mml:mi><mml:mn>1</mml:mn></mml:msub><mml:mo>∈</mml:mo><mml:mo stretchy="false" form="prefix">[</mml:mo><mml:mi>−</mml:mi><mml:mn>5</mml:mn><mml:mo>,</mml:mo><mml:mn>10</mml:mn><mml:mo stretchy="false" form="postfix">]</mml:mo></mml:mrow></mml:math></alternatives></inline-formula>
    and <inline-formula><alternatives>
    <tex-math><![CDATA[x_2 \in [0, 15]]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:msub><mml:mi>x</mml:mi><mml:mn>2</mml:mn></mml:msub><mml:mo>∈</mml:mo><mml:mo stretchy="false" form="prefix">[</mml:mo><mml:mn>0</mml:mn><mml:mo>,</mml:mo><mml:mn>15</mml:mn><mml:mo stretchy="false" form="postfix">]</mml:mo></mml:mrow></mml:math></alternatives></inline-formula>,
    as a toy objective:</p>
    <code language="r script">branin = function(x1, x2) {
  (x2 - 5.1 / (4 * pi^2) * x1^2 + 5 / pi * x1 - 6)^2 +
    10 * (1 - 1 / (8 * pi)) * cos(x1) + 10
}</code>
    <p>Next, we define an optimizer that proposes new points for
    evaluation. It takes as input an <monospace>archive</monospace> of
    running and completed tasks and returns a new candidate point
    <monospace>xs</monospace> for evaluation. The archive is represented
    as a <monospace>data.table</monospace> object. If the
    <monospace>archive</monospace> is empty, the optimizer proposes a
    random point. Otherwise, it imputes missing objective values for
    running tasks using the mean objective value of completed tasks. It
    then fits a random forest surrogate model with the
    <monospace>ranger</monospace> (Wright &amp; Ziegler, 2017) R package
    and proposes a new point by minimizing a lower confidence bound
    acquisition function <inline-formula><alternatives>
    <tex-math><![CDATA[\hat\mu(x) - \lambda\,\hat\sigma(x)]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mover><mml:mi>μ</mml:mi><mml:mo accent="true">̂</mml:mo></mml:mover><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo><mml:mo>−</mml:mo><mml:mi>λ</mml:mi><mml:mspace width="0.167em"></mml:mspace><mml:mover><mml:mi>σ</mml:mi><mml:mo accent="true">̂</mml:mo></mml:mover><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo></mml:mrow></mml:math></alternatives></inline-formula>.
    The exploration parameter <inline-formula><alternatives>
    <tex-math><![CDATA[\lambda]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>λ</mml:mi></mml:math></alternatives></inline-formula>
    is passed as an argument; each worker draws its own value from
    <inline-formula><alternatives>
    <tex-math><![CDATA[\mathrm{Exp}(1)]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mrow><mml:mi mathvariant="normal">E</mml:mi><mml:mi mathvariant="normal">x</mml:mi><mml:mi mathvariant="normal">p</mml:mi></mml:mrow><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mn>1</mml:mn><mml:mo stretchy="false" form="postfix">)</mml:mo></mml:mrow></mml:math></alternatives></inline-formula>
    once at the start of the worker loop, following Egelé et al. (2023).
    Assigning each worker a different <inline-formula><alternatives>
    <tex-math><![CDATA[\lambda]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>λ</mml:mi></mml:math></alternatives></inline-formula>
    realizes the diversification mechanism of ADBO: independent workers
    explore different exploration–exploitation trade-offs without any
    coordination. Fitting the surrogate model becomes increasingly
    expensive as the number of tasks in the
    <monospace>archive</monospace> grows. A detailed understanding of
    this optimizer is not necessary to follow the subsequent examples.
    Two properties are relevant for what follows: it uses previously
    evaluated points from other workers to inform new proposals, and
    proposal generation incurs a non-negligible computational cost.</p>
    <code language="r script">optimizer = function(archive, lambda) {
  if (nrow(archive) == 0) {
    return(list(x1 = runif(1, -5, 10), x2 = runif(1, 0, 15)))
  }

  mean_y = mean(archive$y, na.rm = TRUE)
  archive[&quot;running&quot;, y := mean_y, on = &quot;state&quot;]

  surrogate = ranger::ranger(
    y ~ x1 + x2,
    data = archive,
    num.trees = 100L,
    keep.inbag = TRUE)

  xdt = data.table::data.table(
    x1 = runif(1000, -5, 10),
    x2 = runif(1000, 0, 15))
  p = predict(surrogate, xdt, type = &quot;se&quot;, se.method = &quot;jack&quot;)
  cb = p$predictions - lambda * p$se

  as.list(xdt[which.min(cb)])
}</code>
    <p>The BO worker loop combines the optimizer with the
    <monospace>rush</monospace> API. Note that it uses
    <monospace>$fetch_tasks_with_state()</monospace> to retrieve both
    running and finished tasks, allowing the optimizer to impute values
    for evaluations still in progress. This is the ADBO pattern: each
    worker fits its own surrogate model to all available data and
    independently proposes the next configuration.</p>
    <code language="r script">workerloop_adbo = function(rush, branin, optimizer) {
  # evaluate the initial design
  repeat {
    task = rush$pop_task()
    if (is.null(task)) break
    ys = list(y = branin(task$xs$x1, task$xs$x2))
    rush$finish_tasks(task$key, yss = list(ys))
  }

  # draw the worker-specific exploration parameter
  lambda = rexp(1)

  # ADBO loop
  while (rush$n_finished_tasks &lt; 100) {
    archive = rush$fetch_tasks_with_state(states = c(&quot;running&quot;, &quot;finished&quot;))
    xs = optimizer(archive, lambda)

    key = rush$push_running_tasks(xss = list(xs))

    ys = list(y = branin(xs$x1, xs$x2))
    rush$finish_tasks(key, yss = list(ys))
  }
}</code>
    <p>In practice, the optimization often starts with an initial design
    — a set of space-filling points that seed the surrogate model. The
    queue mechanism from
    Section <xref alt="2.4" rid="sec-queues">2.4</xref> provides a
    natural way to distribute such an initial design: a structured
    design (e.g., Latin hypercube sampling) is created once with the
    <monospace>lhs</monospace> (Carnell, 2026) R package and pushed to
    the queue, and workers drain it before transitioning to the
    model-based loop.</p>
    <code language="r script">lhs_points = lhs::maximinLHS(n = 25, k = 2)
x1_lower = -5
x1_range = 15
x2_lower = 0
x2_range = 15

xss = lapply(1:25, function(i) {
  # rescale to the domain
  list(
    x1 = lhs_points[i, 1] * x1_range + x1_lower,
    x2 = lhs_points[i, 2] * x2_range + x2_lower)
})

rush$push_tasks(xss = xss)</code>
  </sec>
</sec>
<sec id="benchmarking-of-core-functions">
  <title>Benchmarking of core functions</title>
  <sec id="experimental-setup">
    <title>Experimental setup</title>
    <p>The evaluation of short-running tasks is sensitive to the
    overhead introduced by <monospace>rush</monospace>. To quantify this
    overhead, we benchmark the core functions:
    <monospace>$push_running_tasks()</monospace>,
    <monospace>$finish_tasks()</monospace>, and
    <monospace>$fetch_finished_tasks()</monospace>. For
    <monospace>$push_running_tasks()</monospace> and
    <monospace>$finish_tasks()</monospace>, we vary the number of
    parameters (1, 10, 100) of the task and the payload size (1, 10,
    100, 1,000, 10,000) of each parameter. In the case of
    <monospace>$finish_tasks()</monospace>, the payload size refers to
    the result. The payload size is the number of elements in a double
    vector where each double takes 8 bytes, i.e., a payload size of
    10,000 corresponds to 80 kB. We benchmark
    <monospace>$fetch_finished_tasks()</monospace> both with and without
    caching to measure the effect of the caching mechanism. In addition
    to the number of parameters and payload size, we vary the number of
    tasks (1, 10, 100, 1,000, 10,000, 100,000). When caching is enabled,
    the cache holds all previously fetched tasks except the most recent
    one, so that each call retrieves only a single new result from the
    database. For both <monospace>$fetch_finished_tasks()</monospace>
    benchmarks, we cap the payload size at 100 to avoid creating overly
    large tables in memory. All benchmarks use
    <monospace>rush</monospace> version 1.3.0 with R 4.6.0 and
    <monospace>Redis</monospace> 8.10.1. All measurements are taken by a
    single worker against a local <monospace>Redis</monospace> instance
    communicating via a Unix
    socket.<xref ref-type="fn" rid="fn3">3</xref> The runtime is
    measured with the <monospace>microbenchmark</monospace> (Mersmann et
    al., 2024) R package. We report the median and the 95th percentile
    over the replicates. All figures are created with the
    <monospace>ggplot2</monospace> (Wickham, 2016),
    <monospace>cowplot</monospace> (Wilke, 2025), and
    <monospace>scales</monospace> (Wickham et al., 2025) R packages.</p>
  </sec>
  <sec id="results">
    <title>Results</title>
    <table-wrap>
      <caption>
        <p>Runtime of <monospace>$push_running_tasks()</monospace> and
        <monospace>$finish_tasks()</monospace> as a function of the
        number of fields and payload size. For
        <monospace>$push_running_tasks()</monospace>, the number of
        fields corresponds to the number of parameters and for
        <monospace>$finish_tasks()</monospace>, it corresponds to the
        number of results. The runtime is reported as the median over
        10,000 replicates with the 95th percentile in parentheses.</p>
      </caption>
      <table id="tbl-runtime-push-finish-tasks">
        <colgroup>
          <col width="25%" />
          <col width="19%" />
          <col width="26%" />
          <col width="29%" />
        </colgroup>
        <thead>
          <tr>
            <th align="right">Number of Fields</th>
            <th align="right">Payload Size</th>
            <th align="right">Runtime Push (ms)</th>
            <th align="right">Runtime Finish (ms)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="right">1</td>
            <td align="right">1</td>
            <td align="right">0.34 (0.75)</td>
            <td align="right">0.26 (0.39)</td>
          </tr>
          <tr>
            <td align="right">1</td>
            <td align="right">10</td>
            <td align="right">0.33 (0.62)</td>
            <td align="right">0.25 (0.34)</td>
          </tr>
          <tr>
            <td align="right">1</td>
            <td align="right">100</td>
            <td align="right">0.33 (0.73)</td>
            <td align="right">0.26 (0.42)</td>
          </tr>
          <tr>
            <td align="right">1</td>
            <td align="right">1,000</td>
            <td align="right">0.34 (0.61)</td>
            <td align="right">0.26 (0.32)</td>
          </tr>
          <tr>
            <td align="right">1</td>
            <td align="right">10,000</td>
            <td align="right">0.55 (1.27)</td>
            <td align="right">0.34 (0.49)</td>
          </tr>
          <tr>
            <td align="right">10</td>
            <td align="right">1</td>
            <td align="right">0.33 (0.39)</td>
            <td align="right">0.25 (0.32)</td>
          </tr>
          <tr>
            <td align="right">10</td>
            <td align="right">10</td>
            <td align="right">0.41 (1.15)</td>
            <td align="right">0.25 (0.33)</td>
          </tr>
          <tr>
            <td align="right">10</td>
            <td align="right">100</td>
            <td align="right">0.44 (1.15)</td>
            <td align="right">0.29 (0.38)</td>
          </tr>
          <tr>
            <td align="right">10</td>
            <td align="right">1,000</td>
            <td align="right">0.39 (1.16)</td>
            <td align="right">0.35 (0.51)</td>
          </tr>
          <tr>
            <td align="right">10</td>
            <td align="right">10,000</td>
            <td align="right">1.89 (2.58)</td>
            <td align="right">1.33 (2.03)</td>
          </tr>
          <tr>
            <td align="right">100</td>
            <td align="right">1</td>
            <td align="right">0.45 (1.11)</td>
            <td align="right">0.27 (0.46)</td>
          </tr>
          <tr>
            <td align="right">100</td>
            <td align="right">10</td>
            <td align="right">0.53 (1.32)</td>
            <td align="right">0.28 (0.36)</td>
          </tr>
          <tr>
            <td align="right">100</td>
            <td align="right">100</td>
            <td align="right">0.58 (1.30)</td>
            <td align="right">0.36 (0.48)</td>
          </tr>
          <tr>
            <td align="right">100</td>
            <td align="right">1,000</td>
            <td align="right">0.92 (1.45)</td>
            <td align="right">1.37 (2.07)</td>
          </tr>
          <tr>
            <td align="right">100</td>
            <td align="right">10,000</td>
            <td align="right">12.98 (25.41)</td>
            <td align="right">12.07 (20.80)</td>
          </tr>
        </tbody>
      </table>
    </table-wrap>
    <p>The runtime of the functions
    <monospace>$push_running_tasks()</monospace> and
    <monospace>$finish_tasks()</monospace> is summarized in
    Table <xref alt="1" rid="tbl-runtime-push-finish-tasks">1</xref>.
    For payload sizes up to 100, pushing a task to the database takes a
    median of 0.33–0.58 ms, and finishing a task takes slightly less,
    0.25–0.36 ms. For the largest configuration (payload size 10,000 and
    100 fields), the median push and finish times increase to 12.98 ms
    and 12.07 ms, respectively. The corresponding 95th percentiles are
    25.41 ms and 20.80 ms, approximately 2.0 and 1.7 times the medians,
    indicating greater upper-tail latency for large payloads. For
    realistic HPO workloads, which typically involve tens of
    hyperparameters with scalar values (payload size 1), the median
    per-task overhead remains well below 1 ms.</p>
    <p>For fetching, <monospace>rush</monospace> provides a built-in
    caching mechanism. The difference between fetching with and without
    caching is negligible for a small number of tasks. However, as the
    archive grows beyond 1,000 tasks, caching yields a substantial
    speedup, as shown in
    Figure <xref alt="3" rid="fig-fetch-finished-tasks">3</xref> and
    Table <xref alt="3" rid="tbl-runtime-finished-tasks">3</xref>. At
    10,000 tasks and 10 parameters, fetching without the cache takes
    approximately 556 ms, compared to 3 ms with caching.</p>
    <fig id="fig-fetch-finished-tasks">
      <caption><p>Median runtime in milliseconds of fetching finished
      tasks with (blue circles) and without (orange triangles) caching
      for different numbers of parameters as a function of the total
      number of tasks in the database. Both axes are log-scaled. The
      payload size is 1 in this benchmark. Without caching, all tasks
      are retrieved from the database on each call. With caching, only
      one new task is retrieved from the database; all previously
      fetched tasks are served from the worker’s cache.</p></caption>
      <graphic mimetype="image" mime-subtype="png" xlink:href="figures/fetch_finished_tasks.png" />
    </fig>
  </sec>
</sec>
<sec id="sec-benchmarks-bo">
  <title>Benchmarks of Bayesian optimization</title>
  <sec id="sec-benchmarks-bo-experimental-setup">
    <title>Experimental setup</title>
    <p>We compare the effective CPU utilization of three parallelization
    strategies for BO on HPO tasks, representing increasing degrees of
    decentralization. We optimize nine hyperparameters of
    <monospace>LightGBM</monospace> (Ke et al., 2017), using the
    implementation in <monospace>mlr3extralearners</monospace> (Fischer
    et al., 2025), on four datasets, as summarized in
    Table <xref alt="4" rid="tbl-lightgbm-hyperparameters">4</xref> and
    Table <xref alt="5" rid="tbl-datasets">5</xref>. The datasets are
    downloaded from OpenML with <monospace>mlr3oml</monospace> (Lang
    &amp; Fischer, 2026) and differ in their number of features and
    observations to induce a range of training runtimes. Each experiment
    runs for 10 minutes on 448 workers. The benchmarks are conducted on
    a Linux cluster at the Leibniz Supercomputing Centre (LRZ). Each
    node has 112 Intel Xeon Sapphire Rapids cores and 488 GiB of memory,
    running SUSE Linux Enterprise Server 15 SP6. We use four nodes for
    the benchmarks, resulting in 448 cores in total. All benchmarks use
    <monospace>rush</monospace> version 1.3.0.</p>
    <p><bold>Synchronous batch BO (CL).</bold> Evaluates
    <inline-formula><alternatives>
    <tex-math><![CDATA[q]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>q</mml:mi></mml:math></alternatives></inline-formula>
    hyperparameter configurations in parallel using the constant liar
    (CL) approach (Ginsbourger et al., 2010). The first point is
    obtained by maximizing the acquisition function. A fake value is
    then imputed, the surrogate model is updated, and the next point is
    proposed by maximizing the acquisition function again. Common
    imputation choices are the minimum, maximum, or predicted posterior
    mean of already evaluated points. In our setup, CL proposes 44
    configurations evaluated with 10-fold cross-validation, resulting in
    440 parallel evaluations. The remaining 8 cores cannot be used by
    design, since each configuration occupies 10 cores.
    <monospace>mlr3</monospace> uses <monospace>mirai</monospace> to
    parallelize over the resampling iterations. Batch parallelization
    requires all workers to finish before the next batch can be
    proposed. We use early stopping to determine the optimal number of
    boosting iterations, which naturally induces heterogeneous runtimes:
    some models train for only a few iterations while others run for up
    to 5,000. This heterogeneity exposes the synchronization overhead
    inherent in batch approaches, as fast workers remain idle while
    waiting for slower ones to complete.</p>
    <p><bold>Asynchronous centralized BO (ACBO).</bold> Eliminates
    synchronization overhead by evaluating configurations
    asynchronously. When a worker becomes idle, the central controller
    proposes a new configuration and sends it to the worker without
    waiting for other evaluations to complete. Objective values for
    configurations currently under evaluation are imputed, as in CL.
    However, surrogate fitting and acquisition function optimization
    remain centralized and are executed sequentially by the controller.
    With many workers, this sequential bottleneck limits scalability:
    new configurations cannot be proposed fast enough to keep all
    workers occupied.</p>
    <p><bold>Asynchronous decentralized BO (ADBO).</bold> Distributes
    both surrogate updates and acquisition function optimization across
    the workers. Each worker maintains its own surrogate model and
    independently proposes the next configuration. When a worker
    completes an evaluation, it asynchronously shares the result via the
    database; other workers incorporate this information into their
    local models. As in CL and ACBO, objective values for configurations
    currently being evaluated are imputed. ADBO additionally uses
    stochastic acquisition functions to promote varying
    exploration–exploitation trade-offs between workers, e.g., by
    initializing the <inline-formula><alternatives>
    <tex-math><![CDATA[\lambda]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>λ</mml:mi></mml:math></alternatives></inline-formula>
    parameter of the confidence bound acquisition function with
    different values for each
    worker.<xref ref-type="fn" rid="fn4">4</xref> ADBO requires
    infrastructure that allows workers to exchange information
    asynchronously without a central controller — the architecture
    provided by <monospace>rush</monospace>.</p>
    <p>All three strategies are implemented with
    <monospace>mlr3tuning</monospace> (Becker et al., 2025) and
    <monospace>mlr3mbo</monospace> (Becker, Schneider, et al., 2026) and
    use the <monospace>mlr3mbo</monospace> defaults: a random forest
    surrogate model (Wright &amp; Ziegler, 2017), a confidence bound
    acquisition function, and local search for acquisition function
    optimization. The strategies differ in the number of cores used per
    configuration. CL distributes the 10 resampling iterations across 10
    cores and evaluates 44 configurations in parallel. ACBO and ADBO
    execute the resampling iterations sequentially on a single core,
    allowing up to 448 configurations to be evaluated concurrently. For
    each task and replication, we randomly sample an initial design of
    100 configurations from the search space in
    Table <xref alt="4" rid="tbl-lightgbm-hyperparameters">4</xref> and
    evaluate them using 10-fold cross-validation. The evaluation counts,
    wall-clock times, and utilization in
    Table <xref alt="2" rid="tbl-benchmarks">2</xref> and the traces in
    Figure <xref alt="5" rid="fig-performance-algorithms">5</xref>
    exclude the initial design. Each task–algorithm combination is run
    five times. Benchmark jobs use fixed random seeds, with initial
    designs shared across strategies and fixed OpenML cross-validation
    splits. In asynchronous execution, result ordering depends on
    timing, so fixed seeds alone do not ensure exact
    reproducibility.</p>
    <p>We measure effective CPU utilization following Egelé et al.
    (2023). Let <inline-formula><alternatives>
    <tex-math><![CDATA[n_\text{workers}]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:msub><mml:mi>n</mml:mi><mml:mtext mathvariant="normal">workers</mml:mtext></mml:msub></mml:math></alternatives></inline-formula>
    denote the number of workers, where each worker corresponds to a
    single CPU core, and <inline-formula><alternatives>
    <tex-math><![CDATA[T_\text{walltime}]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">walltime</mml:mtext></mml:msub></mml:math></alternatives></inline-formula>
    be the elapsed wall-clock time. The total available CPU time is
    <inline-formula><alternatives>
    <tex-math><![CDATA[T_\text{CPU} = T_\text{walltime} \cdot n_\text{workers}]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">CPU</mml:mtext></mml:msub><mml:mo>=</mml:mo><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">walltime</mml:mtext></mml:msub><mml:mo>⋅</mml:mo><mml:msub><mml:mi>n</mml:mi><mml:mtext mathvariant="normal">workers</mml:mtext></mml:msub></mml:mrow></mml:math></alternatives></inline-formula>.
    Let <inline-formula><alternatives>
    <tex-math><![CDATA[T_\text{optimization}]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">optimization</mml:mtext></mml:msub></mml:math></alternatives></inline-formula>
    denote the cumulative time spent on surrogate fitting, acquisition
    optimization, and model training, summed across all workers. The
    effective CPU utilization is then <inline-formula><alternatives>
    <tex-math><![CDATA[U = \frac{T_\text{optimization}}{T_\text{CPU}}]]></tex-math>
    <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mi>U</mml:mi><mml:mo>=</mml:mo><mml:mfrac><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">optimization</mml:mtext></mml:msub><mml:msub><mml:mi>T</mml:mi><mml:mtext mathvariant="normal">CPU</mml:mtext></mml:msub></mml:mfrac></mml:mrow></mml:math></alternatives></inline-formula>.
    Utilization thus counts the time spent proposing configurations as
    well as evaluating them. All three strategies fit one surrogate and
    optimize the acquisition function once per proposed configuration.
    To separate the two components,
    Table <xref alt="6" rid="tbl-benchmarks-extra">6</xref> additionally
    reports the learner utilization, which counts only the time spent
    evaluating configurations.</p>
    <table-wrap>
      <caption>
        <p>Effective CPU utilization and the number of completed
        evaluations for CL, ACBO, and ADBO across four benchmark tasks.
        For each task–algorithm combination, the table reports the mean
        runtime of the 10-fold cross-validation (CV) in seconds, the
        number of completed evaluations, and the resulting effective CPU
        utilization in percent. All entries are the mean over five
        replications with the standard deviation in parentheses. Because
        each algorithm follows a different search trajectory, the
        configurations evaluated differ, leading to variation in mean CV
        runtimes across algorithms on the same dataset. This complicates
        direct comparison of raw evaluation counts: e.g., on airlines,
        ACBO’s mean evaluation (158 s) is shorter than CL’s (286 s), so
        part of ACBO’s higher evaluation count reflects faster
        individual evaluations rather than only better scheduling. The
        same effect explains the large standard deviation of the
        evaluation count of ADBO on airlines, whose mean evaluation
        ranges from 171 s to 403 s across replications.</p>
      </caption>
      <table id="tbl-benchmarks">
        <colgroup>
          <col width="24%" />
          <col width="13%" />
          <col width="26%" />
          <col width="17%" />
          <col width="21%" />
        </colgroup>
        <thead>
          <tr>
            <th align="left">Task</th>
            <th align="left">Algorithm</th>
            <th align="right">Mean Runtime CV (s)</th>
            <th align="right">Evaluations</th>
            <th align="right">Utilization (%)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">credit-g</td>
            <td align="left">CL</td>
            <td align="right">0.65 (0.06)</td>
            <td align="right">502 (24)</td>
            <td align="right">0.3 (0.0)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ACBO</td>
            <td align="right">0.53 (0.02)</td>
            <td align="right">545 (13)</td>
            <td align="right">0.3 (0.0)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ADBO</td>
            <td align="right">0.71 (0.02)</td>
            <td align="right">15,796 (231)</td>
            <td align="right">94.6 (0.1)</td>
          </tr>
          <tr>
            <td align="left">KDDCup09_appetency</td>
            <td align="left">CL</td>
            <td align="right">26.46 (2.71)</td>
            <td align="right">132 (0)</td>
            <td align="right">1.1 (0.1)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ACBO</td>
            <td align="right">23.21 (0.94)</td>
            <td align="right">526 (7)</td>
            <td align="right">4.9 (0.2)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ADBO</td>
            <td align="right">30.05 (0.80)</td>
            <td align="right">6,520 (106)</td>
            <td align="right">93.7 (0.1)</td>
          </tr>
          <tr>
            <td align="left">adult</td>
            <td align="left">CL</td>
            <td align="right">11.53 (0.74)</td>
            <td align="right">370 (24)</td>
            <td align="right">1.8 (0.1)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ACBO</td>
            <td align="right">11.43 (2.69)</td>
            <td align="right">500 (15)</td>
            <td align="right">2.4 (0.6)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ADBO</td>
            <td align="right">15.12 (0.33)</td>
            <td align="right">9,728 (108)</td>
            <td align="right">95.7 (0.0)</td>
          </tr>
          <tr>
            <td align="left">airlines</td>
            <td align="left">CL</td>
            <td align="right">285.90 (73.31)</td>
            <td align="right">97 (20)</td>
            <td align="right">9.2 (1.6)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ACBO</td>
            <td align="right">157.83 (29.29)</td>
            <td align="right">334 (31)</td>
            <td align="right">31.9 (7.6)</td>
          </tr>
          <tr>
            <td align="left"></td>
            <td align="left">ADBO</td>
            <td align="right">270.85 (95.37)</td>
            <td align="right">494 (365)</td>
            <td align="right">99.5 (0.2)</td>
          </tr>
        </tbody>
      </table>
    </table-wrap>
  </sec>
  <sec id="results-1">
    <title>Results</title>
    <p>The effective CPU utilization and the number of completed
    evaluations are summarized in
    Table <xref alt="2" rid="tbl-benchmarks">2</xref> as means and
    standard deviations over the five replications. Across all tasks and
    all replications, ADBO achieves substantially higher utilization
    than both CL and ACBO, and this improvement is consistent across
    datasets with both short and long training runtimes. The utilization
    of ADBO varies by less than one percentage point between
    replications of the same task, so the standard deviations in
    Table <xref alt="2" rid="tbl-benchmarks">2</xref> are small compared
    to the differences between the algorithms. ADBO also completes
    substantially more evaluations within the same wall-clock budget.
    Utilization includes the surrogate fitting needed to propose
    configurations: on credit-g, ADBO spends a mean cumulative 211,614 s
    fitting surrogates versus 11,164 s evaluating configurations
    (Table <xref alt="6" rid="tbl-benchmarks-extra">6</xref>). ADBO
    spends more time per proposal on surrogate fitting than CL and ACBO
    because the cost of fitting a random forest grows with the size of
    the archive, which reaches over 15,000 evaluations for ADBO but
    stays below 650 for CL and ACBO. Counting only the time spent
    evaluating configurations, ADBO reaches a mean learner utilization
    of 4.2% on credit-g, compared to 0.1% for CL and ACBO, and between
    56.2% and 99.2% on the other tasks, compared to at most 31.7% for CL
    and ACBO
    (Table <xref alt="6" rid="tbl-benchmarks-extra">6</xref>).</p>
    <p>All three methods reach similar final classification errors; ADBO
    achieves slightly lower mean errors, with differences of up to 0.8
    percentage points on credit-g and less than 0.1 percentage points on
    the other tasks. High utilization alone therefore does not imply
    better optimization performance. The additional evaluations mainly
    help reach a given error earlier
    (Figure <xref alt="5" rid="fig-performance-algorithms">5</xref>).</p>
    <p>On tasks with very short training runtimes, centralized methods
    leave most compute resources idle. This effect is most pronounced on
    the credit-g task, where CL and ACBO complete only a few hundred
    evaluations while ADBO completes over 15,000 in every replication.
    The utilization of ADBO is between 270 and 300 times that of CL and
    ACBO across the five replications.
    Figure <xref alt="4" rid="fig-cl-cpu-utilization">4</xref>
    illustrates the underlying mechanism. During the proposal phase,
    only a single core is busy, and for short-running objectives this
    phase dominates the wall-clock time.</p>
    <p>On medium-cost tasks such as adult and KDDCup09_appetency, the
    same pattern persists. ACBO improves utilization relative to CL by
    eliminating synchronization overhead, but the centralized surrogate
    update remains a limiting factor: on adult, for instance, ACBO
    achieves a mean utilization of 2.4% compared to 95.7% for ADBO.</p>
    <p>The airlines task has the longest training times, which reduces
    the relative overhead of surrogate updates and consequently
    increases utilization for all algorithms. Utilization also varies
    most between replications on this task, since a run evaluates only a
    few hundred configurations and their cost differs strongly: the
    utilization of ACBO ranges from 20% to 41% and its mean evaluation
    runtime from 132 s to 202 s across the five replications. Even in
    this favorable regime, ADBO maintains a substantial advantage,
    achieving between 2.4 and 4.9 times the utilization of ACBO in the
    individual replications. The decentralized architecture therefore
    scales robustly across workload regimes.</p>
    <p>Beyond utilization, the parallelization strategy also affects
    budget adherence. The wall-clock runtime of CL can overshoot the
    nominal 10-minute budget, sometimes substantially (e.g. on
    KDDCup09_appetency; see
    Table <xref alt="6" rid="tbl-benchmarks-extra">6</xref>), because CL
    checks the termination criterion only after an entire batch
    finishes. If a batch starts shortly before the time limit, the
    experiment continues until all evaluations complete, introducing
    significant budget overruns. In contrast, ACBO and ADBO check for
    termination after every completed evaluation, enforcing the
    wall-clock constraint more accurately. Asynchronous scheduling
    therefore improves both utilization and budget adherence.</p>
    <fig id="fig-cl-cpu-utilization">
      <caption><p>CPU utilization for short-running and long-running
      objective functions. Bars represent the proposal phase (light
      orange) and evaluation phase (dark blue) of the BO loop. Bar
      height indicates CPU utilization on a 10-core machine: during
      proposal, CL uses a single core, whereas during evaluation it uses
      all 10 cores. The proposal phase grows over iterations as
      surrogate fitting becomes more expensive with an increasing number
      of observations. For long-running objectives, evaluation dominates
      wall-clock time, yielding higher average CPU utilization than for
      short-running objectives. The effect is far more pronounced at the
      448-worker scale used in our benchmarks, where the proposal phase
      occupies only a single worker, about
      <inline-formula><alternatives>
      <tex-math><![CDATA[0.2\%]]></tex-math>
      <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mn>0.2</mml:mn><mml:mi>%</mml:mi></mml:mrow></mml:math></alternatives></inline-formula>
      of the available resources, leaving the remaining
      <inline-formula><alternatives>
      <tex-math><![CDATA[99.8\%]]></tex-math>
      <mml:math display="inline" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mn>99.8</mml:mn><mml:mi>%</mml:mi></mml:mrow></mml:math></alternatives></inline-formula>
      idle.</p></caption>
      <graphic mimetype="image" mime-subtype="png" xlink:href="figures/cpu_utilization.png" />
    </fig>
  </sec>
</sec>
<sec id="discussion-and-conclusion">
  <title>Discussion and conclusion</title>
  <p>We presented <monospace>rush</monospace>, a package for
  asynchronous and decentralized optimization in R. Its database-centric
  architecture eliminates the central controller bottleneck by allowing
  workers to communicate through a shared <monospace>Redis</monospace>
  database and independently execute their own optimization loops. The
  package combines sub-millisecond per-task overhead with an efficient
  caching mechanism and integrates with the <monospace>mlr3</monospace>
  ecosystem as the backend for asynchronous optimization in
  <monospace>bbotk</monospace> and <monospace>mlr3tuning</monospace>.
  Our benchmarks demonstrate that ADBO on <monospace>rush</monospace>
  achieves a CPU utilization above 93% on 448 workers in every run,
  compared to single-digit utilization for centralized approaches on
  short-running tasks. The sub-millisecond overhead measurements apply
  to a single worker using a local Unix socket; per-operation latency
  under concurrent TCP access was not measured separately.</p>
  <p>Despite these results, several limitations should be noted. The
  benchmarks are designed to measure utilization and not final
  optimization performance. All three algorithms reach similar final
  classification errors within the 10-minute budget
  (Figure <xref alt="5" rid="fig-performance-algorithms">5</xref> and
  Table <xref alt="6" rid="tbl-benchmarks-extra">6</xref>). ADBO reaches
  the lowest mean error on all four tasks, but the differences are
  small: 0.8 percentage points on credit-g and less than 0.1 percentage
  points on the other three tasks. The additional evaluations under ADBO
  mainly translate into reaching a given error earlier rather than into
  a lower final error. Whether the higher throughput yields better final
  solutions on more complex problems like hierarchical search spaces
  must be evaluated in future work. The caching mechanism reduces fetch
  overhead substantially, but the cost of binding new rows to the cached
  <monospace>data.table</monospace> grows with archive size, which may
  become relevant for optimization runs with very large numbers of
  evaluations. We do not provide a direct comparison with Python
  frameworks such as <monospace>DeepHyper</monospace> or
  <monospace>Optuna</monospace>, which would help position
  <monospace>rush</monospace> in the broader cross-language landscape.
  Worker startup can be delayed by transferring large datasets over the
  network, as observed for <monospace>kddcup09_appetency</monospace> on
  the LRZ cluster. Where a shared file system is available, the
  <monospace>large_objects_path</monospace> option can reduce this
  overhead through cached file reads
  (Section <xref alt="2.7" rid="sec-data-storage">2.7</xref>).</p>
  <p>Several directions for future work emerge from these limitations.
  Adaptive caching strategies, such as pre-allocation, could mitigate
  the growing cost of row binding for very large archives. The queue
  mechanism could be extended to support more complex task-dependency
  graphs, enabling workflows in which tasks have prerequisites or
  conditional branching.</p>
  <p>By providing a framework for decentralized optimization in R,
  <monospace>rush</monospace> enables a class of algorithms that was
  previously available only in the Python ecosystem. As computational
  budgets and model complexity continue to grow, the ability to scale
  optimization efficiently across large numbers of workers becomes
  increasingly important. We hope that <monospace>rush</monospace> will
  serve as a foundation for the development and application of new
  distributed optimization algorithms in R.</p>
</sec>
<sec id="acknowledgments">
  <title>Acknowledgments</title>
  <p>The authors gratefully acknowledge the computational and data
  resources provided by the Leibniz Supercomputing Centre (www.lrz.de).
  We thank Martin Binder and Sebastian Fischer for their valuable
  feedback.</p>
</sec>
<sec id="competing-interests">
  <title>Competing interests</title>
  <p>The authors develop and maintain the <monospace>rush</monospace>,
  <monospace>bbotk</monospace>, <monospace>mlr3tuning</monospace>, and
  <monospace>mlr3mbo</monospace> packages benchmarked in this paper.
  They declare no financial competing interests.</p>
</sec>
<sec id="data-and-code-availability" sec-type="data-availability">
  <title>Data and Code Availability</title>
  <p>The package is released on CRAN and its development version is
  hosted at
  <ext-link ext-link-type="uri" xlink:href="https://github.com/mlr-org/rush">https://github.com/mlr-org/rush</ext-link>.
  All code is available on zenodo at
  <ext-link ext-link-type="uri" xlink:href="https://zenodo.org/records/22899537">https://zenodo.org/records/22899537</ext-link>
  (Becker, 2026).</p>
</sec>
<sec id="appendix">
  <title>Appendix</title>
  <table-wrap>
    <caption>
      <p>Runtime of <monospace>$fetch_finished_tasks()</monospace> with
      and without caching as a function of the number of tasks, number
      of parameters or results, and payload size. When caching is
      enabled, the cache holds all previously fetched tasks except the
      most recent one, so that each call retrieves only a single new
      result from the database. The runtime is reported as the median
      with the 95th percentile in parentheses over 1,000 replicates for
      up to 1,000 tasks, 100 replicates for 10,000 tasks, and 10
      replicates for 100,000 tasks.</p>
    </caption>
    <table id="tbl-runtime-finished-tasks">
      <colgroup>
        <col width="16%" />
        <col width="31%" />
        <col width="13%" />
        <col width="22%" />
        <col width="19%" />
      </colgroup>
      <thead>
        <tr>
          <th align="right">Number of Tasks</th>
          <th align="right">Number of Parameters / Results</th>
          <th align="right">Payload Size</th>
          <th align="right">Runtime (ms)</th>
          <th align="right">Runtime Cache (ms)</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">1.08 (3.19)</td>
          <td align="right">1.11 (3.26)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">1.14 (3.27)</td>
          <td align="right">1.07 (3.28)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">1.13 (3.12)</td>
          <td align="right">1.07 (3.00)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">1.13 (3.32)</td>
          <td align="right">1.10 (3.29)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">1.09 (2.84)</td>
          <td align="right">1.19 (3.05)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">1.10 (3.37)</td>
          <td align="right">1.15 (2.65)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">1.19 (3.59)</td>
          <td align="right">1.28 (3.18)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">1.20 (3.52)</td>
          <td align="right">1.23 (3.63)</td>
        </tr>
        <tr>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">1.39 (3.78)</td>
          <td align="right">1.33 (3.86)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">1.41 (3.74)</td>
          <td align="right">1.09 (3.24)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">1.43 (3.81)</td>
          <td align="right">1.10 (3.33)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">1.44 (3.49)</td>
          <td align="right">1.12 (3.24)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">1.43 (3.86)</td>
          <td align="right">1.09 (3.30)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">1.53 (2.52)</td>
          <td align="right">1.10 (2.87)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">1.52 (1.95)</td>
          <td align="right">1.11 (3.39)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">1.82 (3.31)</td>
          <td align="right">1.23 (3.66)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">1.98 (3.69)</td>
          <td align="right">1.28 (1.50)</td>
        </tr>
        <tr>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">2.47 (7.73)</td>
          <td align="right">1.30 (2.52)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">4.11 (9.54)</td>
          <td align="right">1.15 (2.49)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">4.44 (11.13)</td>
          <td align="right">1.15 (3.22)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">4.40 (9.63)</td>
          <td align="right">1.16 (3.08)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">4.55 (9.44)</td>
          <td align="right">1.20 (2.52)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">4.90 (10.67)</td>
          <td align="right">1.19 (3.19)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">5.48 (11.28)</td>
          <td align="right">1.23 (3.33)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">8.26 (14.24)</td>
          <td align="right">1.59 (4.17)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">9.35 (17.08)</td>
          <td align="right">1.50 (4.03)</td>
        </tr>
        <tr>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">17.47 (137.19)</td>
          <td align="right">1.60 (4.20)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">38.93 (57.40)</td>
          <td align="right">1.26 (3.72)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">37.59 (49.12)</td>
          <td align="right">1.29 (3.22)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">38.57 (57.29)</td>
          <td align="right">1.29 (3.47)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">38.57 (50.38)</td>
          <td align="right">1.47 (3.21)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">43.13 (57.46)</td>
          <td align="right">1.43 (3.93)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">49.16 (184.47)</td>
          <td align="right">1.49 (3.88)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">82.07 (91.55)</td>
          <td align="right">3.24 (5.53)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">100.34 (148.00)</td>
          <td align="right">3.25 (6.13)</td>
        </tr>
        <tr>
          <td align="right">1,000</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">308.26 (360.19)</td>
          <td align="right">2.78 (6.33)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">305.06 (351.18)</td>
          <td align="right">1.60 (4.23)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">466.67 (524.38)</td>
          <td align="right">1.67 (4.42)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">331.99 (507.94)</td>
          <td align="right">1.74 (4.53)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">555.92 (581.49)</td>
          <td align="right">3.15 (6.68)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">436.40 (673.95)</td>
          <td align="right">3.80 (9.14)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">825.37 (1,015.53)</td>
          <td align="right">8.42 (16.69)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">1,413.02 (1,564.17)</td>
          <td align="right">24.88 (486.43)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">1,377.38 (1,870.65)</td>
          <td align="right">25.06 (96.38)</td>
        </tr>
        <tr>
          <td align="right">10,000</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">2,204.29 (2,528.53)</td>
          <td align="right">24.93 (748.25)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">1</td>
          <td align="right">1</td>
          <td align="right">3,667.42 (3,997.82)</td>
          <td align="right">12.70 (37.58)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">1</td>
          <td align="right">10</td>
          <td align="right">3,662.08 (4,210.95)</td>
          <td align="right">6.72 (27.55)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">1</td>
          <td align="right">100</td>
          <td align="right">4,120.23 (4,480.65)</td>
          <td align="right">20.10 (77.98)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">10</td>
          <td align="right">1</td>
          <td align="right">4,331.54 (4,750.86)</td>
          <td align="right">27.12 (62.05)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">10</td>
          <td align="right">10</td>
          <td align="right">5,497.61 (5,826.04)</td>
          <td align="right">40.10 (72.11)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">10</td>
          <td align="right">100</td>
          <td align="right">6,611.68 (7,147.28)</td>
          <td align="right">41.13 (64.47)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">100</td>
          <td align="right">1</td>
          <td align="right">18,637.08 (22,791.05)</td>
          <td align="right">575.03 (7,251.52)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">100</td>
          <td align="right">10</td>
          <td align="right">22,156.63 (26,338.94)</td>
          <td align="right">294.34 (2,407.24)</td>
        </tr>
        <tr>
          <td align="right">100,000</td>
          <td align="right">100</td>
          <td align="right">100</td>
          <td align="right">25,561.06 (29,777.61)</td>
          <td align="right">335.69 (422.81)</td>
        </tr>
      </tbody>
    </table>
  </table-wrap>
  <table-wrap>
    <caption>
      <p>Hyperparameters of <monospace>LightGBM</monospace> optimized in
      the benchmark experiments. The table shows the lower and upper
      bounds of the search space for each hyperparameter, along with any
      special properties such as logscale transformations.</p>
    </caption>
    <table id="tbl-lightgbm-hyperparameters">
      <thead>
        <tr>
          <th>Hyperparameter</th>
          <th>Lower</th>
          <th>Upper</th>
          <th>Properties</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><monospace>learning_rate</monospace></td>
          <td>1e-3</td>
          <td>1</td>
          <td>Logscale</td>
        </tr>
        <tr>
          <td><monospace>feature_fraction</monospace></td>
          <td>0.1</td>
          <td>1</td>
          <td></td>
        </tr>
        <tr>
          <td><monospace>min_data_in_leaf</monospace></td>
          <td>1</td>
          <td>200</td>
          <td></td>
        </tr>
        <tr>
          <td><monospace>num_leaves</monospace></td>
          <td>10</td>
          <td>255</td>
          <td></td>
        </tr>
        <tr>
          <td><monospace>extra_trees</monospace></td>
          <td>-</td>
          <td>-</td>
          <td>Logical value</td>
        </tr>
        <tr>
          <td><monospace>lambda_l1</monospace></td>
          <td>1e-3</td>
          <td>1e3</td>
          <td>Logscale</td>
        </tr>
        <tr>
          <td><monospace>lambda_l2</monospace></td>
          <td>1e-3</td>
          <td>1e3</td>
          <td>Logscale</td>
        </tr>
        <tr>
          <td><monospace>min_gain_to_split</monospace></td>
          <td>1e-3</td>
          <td>0.1</td>
          <td>Logscale</td>
        </tr>
        <tr>
          <td><monospace>num_iterations</monospace></td>
          <td>1</td>
          <td>5,000</td>
          <td>Internal tuning parameter</td>
        </tr>
      </tbody>
    </table>
  </table-wrap>
  <table-wrap>
    <caption>
      <p>Datasets used in the benchmark experiments. The table shows the
      OpenML (<named-content content-type="nocase">Bischl et
      al.</named-content>, 2025) task ID, name, number of instances,
      number of features, and number of classes for each dataset.</p>
    </caption>
    <table id="tbl-datasets">
      <thead>
        <tr>
          <th align="right">Task ID</th>
          <th align="left">Name</th>
          <th align="right">Instances</th>
          <th align="right">Features</th>
          <th align="right">Classes</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td align="right">31</td>
          <td align="left">credit-g</td>
          <td align="right">1,000</td>
          <td align="right">20</td>
          <td align="right">2</td>
        </tr>
        <tr>
          <td align="right">3945</td>
          <td align="left">KDDCup09_appetency</td>
          <td align="right">50,000</td>
          <td align="right">230</td>
          <td align="right">2</td>
        </tr>
        <tr>
          <td align="right">7592</td>
          <td align="left">adult</td>
          <td align="right">48,842</td>
          <td align="right">14</td>
          <td align="right">2</td>
        </tr>
        <tr>
          <td align="right">189354</td>
          <td align="left">airlines</td>
          <td align="right">539,383</td>
          <td align="right">7</td>
          <td align="right">2</td>
        </tr>
      </tbody>
    </table>
  </table-wrap>
  <table-wrap>
    <caption>
      <p>Runtime breakdown of the BO algorithms across the four
      benchmark tasks. For each task–algorithm combination, the table
      reports the cumulative runtime of the learners, the surrogate
      model, and the optimizer in seconds, the wall-clock time in
      seconds, the learner utilization in percent, and the final
      classification error. The learners, surrogate, and optimizer
      columns are cumulative runtimes summed across all workers. All
      entries are the mean over five replications with the standard
      deviation in parentheses.</p>
    </caption>
    <table id="tbl-benchmarks-extra">
      <colgroup>
        <col width="15%" />
        <col width="8%" />
        <col width="12%" />
        <col width="12%" />
        <col width="10%" />
        <col width="9%" />
        <col width="19%" />
        <col width="16%" />
      </colgroup>
      <thead>
        <tr>
          <th align="left">Task</th>
          <th align="left">Algorithm</th>
          <th align="right">Learners (s)</th>
          <th align="right">Surrogate (s)</th>
          <th align="right">Optimizer (s)</th>
          <th align="right">Wall-clock (s)</th>
          <th align="right">Learner Utilization (%)</th>
          <th align="right">Classification Error</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td align="left">credit-g</td>
          <td align="left">CL</td>
          <td align="right">326 (22)</td>
          <td align="right">201 (13)</td>
          <td align="right">331 (18)</td>
          <td align="right">575 (25)</td>
          <td align="right">0.1 (0.0)</td>
          <td align="right">0.1918 (0.0030)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ACBO</td>
          <td align="right">287 (14)</td>
          <td align="right">235 (4)</td>
          <td align="right">356 (5)</td>
          <td align="right">593 (2)</td>
          <td align="right">0.1 (0.0)</td>
          <td align="right">0.1910 (0.0028)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ADBO</td>
          <td align="right">11,164 (325)</td>
          <td align="right">211,614 (413)</td>
          <td align="right">29,172 (340)</td>
          <td align="right">594 (1)</td>
          <td align="right">4.2 (0.1)</td>
          <td align="right">0.1838 (0.0016)</td>
        </tr>
        <tr>
          <td align="left">KDDCup09 appetency</td>
          <td align="left">CL</td>
          <td align="right">3,493 (358)</td>
          <td align="right">23 (1)</td>
          <td align="right">74 (1)</td>
          <td align="right">725 (2)</td>
          <td align="right">1.1 (0.1)</td>
          <td align="right">0.0175 (0.0000)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ACBO</td>
          <td align="right">12,408 (521)</td>
          <td align="right">239 (5)</td>
          <td align="right">348 (5)</td>
          <td align="right">588 (2)</td>
          <td align="right">4.7 (0.2)</td>
          <td align="right">0.0175 (0.0000)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ADBO</td>
          <td align="right">201,426 (2,170)</td>
          <td align="right">37,155 (1,676)</td>
          <td align="right">9,165 (221)</td>
          <td align="right">590 (0)</td>
          <td align="right">76.2 (0.8)</td>
          <td align="right">0.0174 (0.0000)</td>
        </tr>
        <tr>
          <td align="left">adult</td>
          <td align="left">CL</td>
          <td align="right">4,271 (523)</td>
          <td align="right">123 (10)</td>
          <td align="right">242 (14)</td>
          <td align="right">580 (36)</td>
          <td align="right">1.6 (0.1)</td>
          <td align="right">0.1227 (0.0001)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ACBO</td>
          <td align="right">5,809 (1,486)</td>
          <td align="right">230 (7)</td>
          <td align="right">362 (7)</td>
          <td align="right">592 (2)</td>
          <td align="right">2.2 (0.6)</td>
          <td align="right">0.1226 (0.0003)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ADBO</td>
          <td align="right">149,507 (2,067)</td>
          <td align="right">91,394 (2,003)</td>
          <td align="right">13,774 (300)</td>
          <td align="right">594 (0)</td>
          <td align="right">56.2 (0.8)</td>
          <td align="right">0.1222 (0.0001)</td>
        </tr>
        <tr>
          <td align="left">airlines</td>
          <td align="left">CL</td>
          <td align="right">27,396 (7,143)</td>
          <td align="right">19 (4)</td>
          <td align="right">61 (12)</td>
          <td align="right">664 (103)</td>
          <td align="right">9.1 (1.6)</td>
          <td align="right">0.3236 (0.0004)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ACBO</td>
          <td align="right">83,108 (20,212)</td>
          <td align="right">220 (21)</td>
          <td align="right">364 (18)</td>
          <td align="right">585 (4)</td>
          <td align="right">31.7 (7.6)</td>
          <td align="right">0.3235 (0.0003)</td>
        </tr>
        <tr>
          <td align="left"></td>
          <td align="left">ADBO</td>
          <td align="right">260,517 (929)</td>
          <td align="right">293 (348)</td>
          <td align="right">567 (421)</td>
          <td align="right">586 (1)</td>
          <td align="right">99.2 (0.5)</td>
          <td align="right">0.3233 (0.0001)</td>
        </tr>
      </tbody>
    </table>
  </table-wrap>
  <fig id="fig-performance-algorithms">
    <caption><p>Incumbent classification error over wall-clock time of
    CL (dotted, orange), ACBO (dashed, blue), and ADBO (solid, green).
    Lines show the mean incumbent over the five replications. The shaded
    bands go from the best to the worst replication. All algorithms
    start from the same initial design of 100 configurations, evaluated
    before the timed runs
    (Section <xref alt="5.1" rid="sec-benchmarks-bo-experimental-setup">5.1</xref>).
    Each trace begins with the first completed evaluation proposed by
    the BO loop. For CL this is the completion of the first batch of 44
    configurations. ACBO and ADBO update the incumbent after every
    completed evaluation.</p></caption>
    <graphic mimetype="image" mime-subtype="png" xlink:href="figures/benchmark_competitors.png" />
  </fig>
</sec>
</body>
<back><ref-list><ref id="ref1"><element-citation publication-type="confproc"><person-group person-group-type="author"><name><surname>Akiba</surname><given-names>Takuya</given-names></name><name><surname>Sano</surname><given-names>Shotaro</given-names></name><name><surname>Yanase</surname><given-names>Toshihiko</given-names></name><name><surname>Ohta</surname><given-names>Takeru</given-names></name><name><surname>Koyama</surname><given-names>Masanori</given-names></name></person-group><article-title>Optuna: A Next-generation Hyperparameter Optimization Framework</article-title><source>Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery &amp; Data Mining</source><year>2019</year><fpage>2623</fpage><lpage>2631</lpage><publisher-name>ACM</publisher-name><pub-id pub-id-type="doi">10.1145/3292500.3330701</pub-id></element-citation></ref><ref id="ref2"><mixed-citation>Barrett, T., Dowle, M., Srinivasan, A., Gorecki, J., Chirico, M., Hocking, T., Schwendinger, B., &amp; Krylov, I. (2026). Data.table: Extension of data.frame [Manual]. https://doi.org/10.32614/CRAN.package.data.table</mixed-citation></ref><ref id="ref3"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Marc Becker</surname><given-names></given-names></name></person-group><article-title>slds-lmu/benchmark_2026_rush: benchmark 2026 rush 1.3.0</article-title><year>2026</year><publisher-name>Zenodo</publisher-name><pub-id pub-id-type="doi">10.5281/zenodo.22899537</pub-id></element-citation></ref><ref id="ref4"><mixed-citation>Becker, M., Lang, M., Bischl, B., &amp; Richter, J. (2025). mlr3tuning: Hyperparameter Optimization for ’mlr3’. Zenodo. https://doi.org/10.5281/zenodo.15852621</mixed-citation></ref><ref id="ref5"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Marc Becker</surname><given-names></given-names></name><name><surname>Jakob Richter</surname><given-names></given-names></name><name><surname>Michel Lang</surname><given-names></given-names></name><name><surname>Lennart Schneider</surname><given-names></given-names></name><name><surname>Bernd Bischl</surname><given-names></given-names></name><name><surname>Patrick Schratz</surname><given-names></given-names></name><name><surname>mb706</surname><given-names></given-names></name><name><surname>Sebastian Fischer</surname><given-names></given-names></name><name><surname>Michael Chirico</surname><given-names></given-names></name><name><surname>Manuel López-Ibáñez</surname><given-names></given-names></name><name><surname>Lionel Henry</surname><given-names></given-names></name><name><surname>Lukas Burk</surname><given-names></given-names></name></person-group><article-title>mlr-org/bbotk: bbotk 1.13.1</article-title><year>2026</year><publisher-name>Zenodo</publisher-name><pub-id pub-id-type="doi">10.5281/zenodo.20669808</pub-id></element-citation></ref><ref id="ref6"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Becker</surname><given-names>Marc</given-names></name><name><surname>Schneider</surname><given-names>Lennart</given-names></name><name><surname>Binder</surname><given-names>Martin</given-names></name><name><surname>Kotthoff</surname><given-names>Lars</given-names></name><name><surname>Bischl</surname><given-names>Bernd</given-names></name></person-group><article-title>mlr3mbo: Bayesian Optimization in R</article-title><year>2026</year><publisher-name>arXiv</publisher-name><pub-id pub-id-type="doi">10.48550/arXiv.2603.29730</pub-id></element-citation></ref><ref id="ref7"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Bengtsson</surname><given-names>Henrik</given-names></name></person-group><article-title>A Unifying Framework for Parallel and Distributed Processing in R using Futures</article-title><source>The R Journal</source><year>2021</year><volume>13</volume><issue>2</issue><fpage>208</fpage><publisher-name>The R Foundation</publisher-name><pub-id pub-id-type="doi">10.32614/RJ-2021-048</pub-id></element-citation></ref><ref id="ref8"><mixed-citation>Bergstra, J., Yamins, D., &amp; Cox, D. (2013). Making a Science of Model Search: Hyperparameter Optimization in Hundreds of Dimensions for Vision Architectures. Proceedings of the 30th International Conference on Machine Learning, 115–123. https://proceedings.mlr.press/v28/bergstra13.html</mixed-citation></ref><ref id="ref9"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Bischl</surname><given-names>Bernd</given-names></name><name><surname>Binder</surname><given-names>Martin</given-names></name><name><surname>Lang</surname><given-names>Michel</given-names></name><name><surname>Pielok</surname><given-names>Tobias</given-names></name><name><surname>Richter</surname><given-names>Jakob</given-names></name><name><surname>Coors</surname><given-names>Stefan</given-names></name><name><surname>Thomas</surname><given-names>Janek</given-names></name><name><surname>Ullmann</surname><given-names>Theresa</given-names></name><name><surname>Becker</surname><given-names>Marc</given-names></name><name><surname>Boulesteix</surname><given-names>Anne‐Laure</given-names></name><name><surname>Deng</surname><given-names>Difan</given-names></name><name><surname>Lindauer</surname><given-names>Marius</given-names></name></person-group><article-title>Hyperparameter optimization: Foundations, algorithms, best practices, and open challenges</article-title><source>WIREs Data Mining and Knowledge Discovery</source><year>2023</year><volume>13</volume><issue>2</issue><publisher-name>Wiley</publisher-name><pub-id pub-id-type="doi">10.1002/widm.1484</pub-id></element-citation></ref><ref id="ref10"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Bischl</surname><given-names>Bernd</given-names></name><name><surname>Casalicchio</surname><given-names>Giuseppe</given-names></name><name><surname>Das</surname><given-names>Taniya</given-names></name><name><surname>Feurer</surname><given-names>Matthias</given-names></name><name><surname>Fischer</surname><given-names>Sebastian</given-names></name><name><surname>Gijsbers</surname><given-names>Pieter</given-names></name><name><surname>Mukherjee</surname><given-names>Subhaditya</given-names></name><name><surname>Müller</surname><given-names>Andreas C.</given-names></name><name><surname>Németh</surname><given-names>László</given-names></name><name><surname>Oala</surname><given-names>Luis</given-names></name><name><surname>Purucker</surname><given-names>Lennart</given-names></name><name><surname>Ravi</surname><given-names>Sahithya</given-names></name><name><surname>van Rijn</surname><given-names>Jan N.</given-names></name><name><surname>Singh</surname><given-names>Prabhant</given-names></name><name><surname>Vanschoren</surname><given-names>Joaquin</given-names></name><name><surname>van der Velde</surname><given-names>Jos</given-names></name><name><surname>Wever</surname><given-names>Marcel</given-names></name></person-group><article-title>OpenML: Insights from 10 years and more than a thousand papers</article-title><source>Patterns</source><year>2025</year><volume>6</volume><issue>7</issue><fpage>101317</fpage><publisher-name>Elsevier BV</publisher-name><pub-id pub-id-type="doi">10.1016/j.patter.2025.101317</pub-id></element-citation></ref><ref id="ref11"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Bouthillier</surname><given-names>Xavier</given-names></name><name><surname>Corneau-Tremblay</surname><given-names>François</given-names></name><string-name>Notoraptor</string-name><name><surname>Tsirigotis</surname><given-names>Christos</given-names></name><string-name>Setepenre</string-name><name><surname>Dong</surname><given-names>Lin</given-names></name><name><surname>Bronzi</surname><given-names>Mirko</given-names></name><string-name>Reyhane Askari</string-name><string-name>Dendi Suhubdy</string-name><name><surname>Normandin</surname><given-names>Fabrice</given-names></name><string-name>Abergeron</string-name><string-name>Simnol22</string-name><name><surname>Breuleux</surname><given-names>Olivier</given-names></name><name><surname>Wagner</surname><given-names>Sean</given-names></name><string-name>Satyaog</string-name><name><surname>Alain</surname><given-names>Guillaume</given-names></name><name><surname>Bertrand</surname><given-names>Hadrien</given-names></name><string-name>, Michael</string-name><name><surname>Serdyuk</surname><given-names>Dmitriy</given-names></name><name><surname>Schweizer</surname><given-names>Thomas</given-names></name><name><surname>Henderson</surname><given-names>Peter</given-names></name><name><surname>Carrez</surname><given-names>Bruno</given-names></name><string-name>Olexa Bilaniuk</string-name><name><surname>Michalski</surname><given-names>Vincent</given-names></name><name><surname>Lamblin</surname><given-names>Pascal</given-names></name></person-group><article-title>Epistimio/orion: New Multi-Task Warm-starting and Dashboard Prototype</article-title><year>2023</year><publisher-name>Zenodo</publisher-name><pub-id pub-id-type="doi">10.5281/zenodo.3478592</pub-id></element-citation></ref><ref id="ref12"><mixed-citation>Carnell, R. (2026). Lhs: Latin hypercube samples [Manual]. https://doi.org/10.32614/CRAN.package.lhs</mixed-citation></ref><ref id="ref13"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Egele</surname><given-names>Romain</given-names></name><name><surname>Balaprakash</surname><given-names>Prasanna</given-names></name><name><surname>Wiggins</surname><given-names>Gavin M.</given-names></name><name><surname>Eiffert</surname><given-names>Brett</given-names></name></person-group><article-title>DeepHyper: A Python Package for Massively Parallel Hyperparameter Optimization in Machine Learning</article-title><source>Journal of Open Source Software</source><year>2025</year><volume>10</volume><issue>109</issue><fpage>7975</fpage><publisher-name>The Open Journal</publisher-name><pub-id pub-id-type="doi">10.21105/joss.07975</pub-id></element-citation></ref><ref id="ref14"><element-citation publication-type="confproc"><person-group person-group-type="author"><name><surname>Egelé</surname><given-names>Romain</given-names></name><name><surname>Guyon</surname><given-names>Isabelle</given-names></name><name><surname>Vishwanath</surname><given-names>Venkatram</given-names></name><name><surname>Balaprakash</surname><given-names>Prasanna</given-names></name></person-group><article-title>Asynchronous Decentralized Bayesian Optimization for Large Scale Hyperparameter Optimization</article-title><source>2023 IEEE 19th International Conference on e-Science (e-Science)</source><year>2023</year><fpage>1</fpage><lpage>10</lpage><publisher-name>IEEE</publisher-name><pub-id pub-id-type="doi">10.1109/e-Science58273.2023.10254839</pub-id></element-citation></ref><ref id="ref15"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Fischer</surname><given-names>Sebastian</given-names></name><name><surname>Zobolas</surname><given-names>John</given-names></name><name><surname>Sonabend</surname><given-names>Raphael</given-names></name><name><surname>Becker</surname><given-names>Marc</given-names></name><name><surname>Lang</surname><given-names>Michel</given-names></name><name><surname>Binder</surname><given-names>Martin</given-names></name><name><surname>Schneider</surname><given-names>Lennart</given-names></name><name><surname>Burk</surname><given-names>Lukas</given-names></name><name><surname>Schratz</surname><given-names>Patrick</given-names></name><name><surname>Jaeger</surname><given-names>Byron C.</given-names></name><name><surname>Lauer</surname><given-names>Stephen A</given-names></name><name><surname>Kapsner</surname><given-names>Lorenz A.</given-names></name><name><surname>Mücke</surname><given-names>Maximilian</given-names></name><name><surname>Wang</surname><given-names>Zezhi</given-names></name><name><surname>Pulatov</surname><given-names>Damir</given-names></name><name><surname>Ganz</surname><given-names>Keenan</given-names></name><name><surname>Funk</surname><given-names>Henri</given-names></name><name><surname>Harutyunyan</surname><given-names>Liana</given-names></name><name><surname>Camilleri</surname><given-names>Pierre</given-names></name><name><surname>Kopper</surname><given-names>Philipp</given-names></name><name><surname>Bender</surname><given-names>Andreas</given-names></name><name><surname>Zhou</surname><given-names>Baisu</given-names></name><name><surname>German</surname><given-names>Niko</given-names></name><name><surname>Koers</surname><given-names>Lona</given-names></name><name><surname>Nazarova</surname><given-names>Anna</given-names></name><name><surname>Bischl</surname><given-names>Bernd</given-names></name></person-group><article-title>mlr3extralearners: Expanding the mlr3 Ecosystem with Community-Driven Learner Integration</article-title><source>Journal of Open Source Software</source><year>2025</year><volume>10</volume><issue>115</issue><fpage>8331</fpage><publisher-name>The Open Journal</publisher-name><pub-id pub-id-type="doi">10.21105/joss.08331</pub-id></element-citation></ref><ref id="ref16"><mixed-citation>FitzJohn, R., &amp; Ashton, R. (2026). Rrq: Simple Redis queue [Manual]. https://github.com/mrc-ide/rrq</mixed-citation></ref><ref id="ref17"><mixed-citation>Fleck, S. (2026). Lgr: A fully featured logging framework [Manual]. https://doi.org/10.32614/CRAN.package.lgr</mixed-citation></ref><ref id="ref18"><mixed-citation>Gao, C. (2025). Mirai: Minimalist Async Evaluation Framework for R. Zenodo. https://doi.org/10.5281/zenodo.7912722</mixed-citation></ref><ref id="ref19"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Gelernter</surname><given-names>David</given-names></name></person-group><article-title>Generative communication in Linda</article-title><source>ACM Transactions on Programming Languages and Systems</source><year>1985</year><volume>7</volume><issue>1</issue><fpage>80</fpage><lpage>112</lpage><publisher-name>Association for Computing Machinery (ACM)</publisher-name><pub-id pub-id-type="doi">10.1145/2363.2433</pub-id></element-citation></ref><ref id="ref20"><element-citation publication-type="book"><person-group person-group-type="author"><name><surname>Ginsbourger</surname><given-names>David</given-names></name><name><surname>Le Riche</surname><given-names>Rodolphe</given-names></name><name><surname>Carraro</surname><given-names>Laurent</given-names></name></person-group><article-title>Kriging Is Well-Suited to Parallelize Optimization</article-title><source>Adaptation Learning and Optimization</source><year>2010</year><fpage>131</fpage><lpage>162</lpage><publisher-name>Springer Berlin Heidelberg</publisher-name><pub-id pub-id-type="doi">10.1007/978-3-642-10701-6_6</pub-id></element-citation></ref><ref id="ref21"><element-citation publication-type="confproc"><person-group person-group-type="author"><name><surname>Golovin</surname><given-names>Daniel</given-names></name><name><surname>Solnik</surname><given-names>Benjamin</given-names></name><name><surname>Moitra</surname><given-names>Subhodeep</given-names></name><name><surname>Kochanski</surname><given-names>Greg</given-names></name><name><surname>Karro</surname><given-names>John</given-names></name><name><surname>Sculley</surname><given-names>D.</given-names></name></person-group><article-title>Google Vizier: A Service for Black-Box Optimization</article-title><source>Proceedings of the 23rd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining</source><year>2017</year><fpage>1487</fpage><lpage>1495</lpage><publisher-name>ACM</publisher-name><pub-id pub-id-type="doi">10.1145/3097983.3098043</pub-id></element-citation></ref><ref id="ref22"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Head</surname><given-names>Tim</given-names></name><string-name>MechCoder</string-name><name><surname>Louppe</surname><given-names>Gilles</given-names></name><string-name>Iaroslav Shcherbatyi</string-name><string-name>Fcharras</string-name><string-name>Zé Vinícius</string-name><string-name>Cmmalone</string-name><name><surname>Schröder</surname><given-names>Christopher</given-names></name><string-name>Nel215</string-name><name><surname>Campos</surname><given-names>Nuno</given-names></name><name><surname>Young</surname><given-names>Todd</given-names></name><name><surname>Cereda</surname><given-names>Stefano</given-names></name><name><surname>Fan</surname><given-names>Thomas</given-names></name><string-name>Rene-Rex</string-name><string-name>Kejia (KJ) Shi</string-name><name><surname>Schwabedal</surname><given-names>Justus</given-names></name><string-name>Carlosdanielcsantos</string-name><string-name>Hvass-Labs</string-name><name><surname>Pak</surname><given-names>Mikhail</given-names></name><string-name>SoManyUsernamesTaken</string-name><name><surname>Callaway</surname><given-names>Fred</given-names></name><name><surname>Estève</surname><given-names>Loïc</given-names></name><name><surname>Besson</surname><given-names>Lilian</given-names></name><name><surname>Cherti</surname><given-names>Mehdi</given-names></name><string-name>Karlson Pfannschmidt</string-name><name><surname>Linzberger</surname><given-names>Fabian</given-names></name><name><surname>Cauet</surname><given-names>Christophe</given-names></name><name><surname>Gut</surname><given-names>Anna</given-names></name><name><surname>Mueller</surname><given-names>Andreas</given-names></name><name><surname>Fabisch</surname><given-names>Alexander</given-names></name></person-group><article-title>scikit-optimize/scikit-optimize: v0.5.2</article-title><year>2018</year><publisher-name>Zenodo</publisher-name><pub-id pub-id-type="doi">10.5281/zenodo.1207017</pub-id></element-citation></ref><ref id="ref23"><mixed-citation>Kandasamy, K., Vysyaraju, K. R., Neiswanger, W., Paria, B., Collins, C. R., Schneider, J., Poczos, B., &amp; Xing, E. P. (2020). Tuning Hyperparameters without Grad Students: Scalable and Robust Bayesian Optimisation with Dragonfly. Journal of Machine Learning Research, 21(81), 1–27. http://www.jmlr.org/papers/v21/18-223.html</mixed-citation></ref><ref id="ref24"><mixed-citation>Ke, G., Meng, Q., Finley, T., Wang, T., Chen, W., Ma, W., Ye, Q., &amp; Liu, T.-Y. (2017). LightGBM: A Highly Efficient Gradient Boosting Decision Tree. Advances in Neural Information Processing Systems, 30, 3146–3156. https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html</mixed-citation></ref><ref id="ref25"><mixed-citation>Landau, W. M. (2026). Crew: A distributed worker launcher framework [Manual]. https://wlandau.github.io/crew/</mixed-citation></ref><ref id="ref26"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Lang</surname><given-names>Michel</given-names></name><name><surname>Binder</surname><given-names>Martin</given-names></name><name><surname>Richter</surname><given-names>Jakob</given-names></name><name><surname>Schratz</surname><given-names>Patrick</given-names></name><name><surname>Pfisterer</surname><given-names>Florian</given-names></name><name><surname>Coors</surname><given-names>Stefan</given-names></name><name><surname>Au</surname><given-names>Quay</given-names></name><name><surname>Casalicchio</surname><given-names>Giuseppe</given-names></name><name><surname>Kotthoff</surname><given-names>Lars</given-names></name><name><surname>Bischl</surname><given-names>Bernd</given-names></name></person-group><article-title>mlr3: A modern object-oriented machine learning framework in R</article-title><source>Journal of Open Source Software</source><year>2019</year><volume>4</volume><issue>44</issue><fpage>1903</fpage><publisher-name>The Open Journal</publisher-name><pub-id pub-id-type="doi">10.21105/joss.01903</pub-id></element-citation></ref><ref id="ref27"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Lang</surname><given-names>Michel</given-names></name><name><surname>Bischl</surname><given-names>Bernd</given-names></name><name><surname>Surmann</surname><given-names>Dirk</given-names></name></person-group><article-title>batchtools: Tools for R to work on batch systems</article-title><source>The Journal of Open Source Software</source><year>2017</year><volume>2</volume><issue>10</issue><fpage>135</fpage><publisher-name>The Open Journal</publisher-name><pub-id pub-id-type="doi">10.21105/joss.00135</pub-id></element-citation></ref><ref id="ref28"><mixed-citation>Lang, M., &amp; Fischer, S. (2026). mlr3oml: Connector Between mlr3 and OpenML [Manual]. https://doi.org/10.32614/CRAN.package.mlr3oml</mixed-citation></ref><ref id="ref29"><mixed-citation>Li, L., Jamieson, K., Rostamizadeh, A., Gonina, E., Ben-tzur, J., Hardt, M., Recht, B., &amp; Talwalkar, A. (2020). A System for Massively Parallel Hyperparameter Tuning. Proceedings of Machine Learning and Systems, 2, 230–246. https://proceedings.mlsys.org/paper_files/paper/2020/hash/a06f20b349c6cf09a6b171c71b88bbfc-Abstract.html</mixed-citation></ref><ref id="ref30"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Liaw</surname><given-names>Richard</given-names></name><name><surname>Liang</surname><given-names>Eric</given-names></name><name><surname>Nishihara</surname><given-names>Robert</given-names></name><name><surname>Moritz</surname><given-names>Philipp</given-names></name><name><surname>Gonzalez</surname><given-names>Joseph E.</given-names></name><name><surname>Stoica</surname><given-names>Ion</given-names></name></person-group><article-title>Tune: A Research Platform for Distributed Model Selection and Training</article-title><year>2018</year><publisher-name>arXiv</publisher-name><pub-id pub-id-type="doi">10.48550/arXiv.1807.05118</pub-id></element-citation></ref><ref id="ref31"><mixed-citation>Lindauer, M., Eggensperger, K., Feurer, M., Biedenkapp, A., Deng, D., Benjamins, C., Ruhkopf, T., Sass, R., &amp; Hutter, F. (2022). SMAC3: A Versatile Bayesian Optimization Package for Hyperparameter Optimization. Journal of Machine Learning Research, 23(54), 1–9. http://jmlr.org/papers/v23/21-0888.html</mixed-citation></ref><ref id="ref32"><mixed-citation>Mersmann, O., Beleites, C., Hurling, R., Friedman, A., &amp; Ulrich, J. M. (2024). Microbenchmark: Accurate Timing Functions. https://doi.org/10.32614/CRAN.package.microbenchmark</mixed-citation></ref><ref id="ref33"><mixed-citation>Moritz, P., Nishihara, R., Wang, S., Tumanov, A., Liaw, R., Liang, E., Elibol, M., Yang, Z., Paul, W., Jordan, M. I., &amp; Stoica, I. (2018). Ray: A Distributed Framework for Emerging AI Applications. Proceedings of the 13th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’18), 561–577. https://www.usenix.org/conference/osdi18/presentation/moritz</mixed-citation></ref><ref id="ref34"><mixed-citation>Nii, H. P. (1986). The blackboard model of problem solving and the evolution of blackboard architectures. AI Magazine, 7(2), 38–53. https://doi.org/10.1609/aimag.v7i2.537</mixed-citation></ref><ref id="ref35"><mixed-citation>O’Malley, T., Bursztein, E., Long, J., Chollet, F., Jin, H., &amp; Invernizzi, L. (2019). Keras Tuner. https://github.com/keras-team/keras-tuner</mixed-citation></ref><ref id="ref36"><mixed-citation>Olson, M., Santorella, E., Tiao, L. C., Cakmak, S., Garrard, M., Daulton, S., Lin, Z. J., Ament, S., Beckerman, B., Onofrey, E., Igusti, P., Lara, C., Letham, B., Cardoso, C., Shen, S. S., Lin, A. C., Grange, M., Kashtelyan, E., Eriksson, D., … Bakshy, E. (2025). Ax: A Platform for Adaptive Experimentation. Proceedings of the Fourth International Conference on Automated Machine Learning, 21/1–25. https://proceedings.mlr.press/v293/olson25a.html</mixed-citation></ref><ref id="ref37"><mixed-citation>R Special Interest Group on Databases (R-SIG-DB), Wickham, H., &amp; Müller, K. (2024). DBI: R database interface [Manual]. https://dbi.r-dbi.org</mixed-citation></ref><ref id="ref38"><element-citation publication-type="confproc"><person-group person-group-type="author"><name><surname>Rocklin</surname><given-names>Matthew</given-names></name></person-group><article-title>Dask: Parallel Computation with Blocked algorithms and Task Scheduling</article-title><source>Proceedings of the Python in Science Conference</source><year>2015</year><fpage>126</fpage><lpage>132</lpage><publisher-name>SciPy</publisher-name><pub-id pub-id-type="doi">10.25080/Majora-7b98e3ed-013</pub-id></element-citation></ref><ref id="ref39"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Rossini</surname><given-names>A. J</given-names></name><name><surname>Tierney</surname><given-names>Luke</given-names></name><name><surname>Li</surname><given-names>Na</given-names></name></person-group><article-title>Simple Parallel Statistical Computing in R</article-title><source>Journal of Computational and Graphical Statistics</source><year>2007</year><volume>16</volume><issue>2</issue><fpage>399</fpage><lpage>420</lpage><publisher-name>Informa UK Limited</publisher-name><pub-id pub-id-type="doi">10.1198/106186007X178979</pub-id></element-citation></ref><ref id="ref40"><mixed-citation>Salinas, D., Seeger, M., Klein, A., Perrone, V., Wistuba, M., &amp; Archambeau, C. (2022). Syne Tune: A Library for Large Scale Hyperparameter Tuning and Reproducible Research. Proceedings of the First International Conference on Automated Machine Learning, 16/1–23. https://proceedings.mlr.press/v188/salinas22a.html</mixed-citation></ref><ref id="ref41"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Wickham</surname><given-names>Hadley</given-names></name></person-group><article-title>testthat: Get Started with Testing</article-title><source>The R Journal</source><year>2011</year><volume>3</volume><issue>1</issue><fpage>5</fpage><publisher-name>The R Foundation</publisher-name><pub-id pub-id-type="doi">10.32614/RJ-2011-002</pub-id></element-citation></ref><ref id="ref42"><element-citation publication-type="book"><person-group person-group-type="author"><name><surname>Wickham</surname><given-names>Hadley</given-names></name></person-group><article-title>ggplot2</article-title><source>Use R!</source><year>2016</year><publisher-name>Springer International Publishing</publisher-name><pub-id pub-id-type="doi">10.1007/978-3-319-24277-4</pub-id></element-citation></ref><ref id="ref43"><mixed-citation>Wickham, H., Pedersen, T. L., &amp; Seidel, D. (2025). Scales: Scale functions for visualization [Manual]. https://doi.org/10.32614/CRAN.package.scales</mixed-citation></ref><ref id="ref44"><mixed-citation>Wilke, C. O. (2025). Cowplot: Streamlined plot theme and plot annotations for ’ggplot2’ [Manual]. https://wilkelab.org/cowplot/</mixed-citation></ref><ref id="ref45"><element-citation publication-type="journal"><person-group person-group-type="author"><name><surname>Wright</surname><given-names>Marvin N.</given-names></name><name><surname>Ziegler</surname><given-names>Andreas</given-names></name></person-group><article-title>ranger
                    : A Fast Implementation of Random Forests for High Dimensional Data in
                    &lt;i&gt;C++&lt;/i&gt;
                    and
                    &lt;i&gt;R&lt;/i&gt;</article-title><source>Journal of Statistical Software</source><year>2017</year><volume>77</volume><issue>1</issue><publisher-name>Foundation for Open Access Statistic</publisher-name><pub-id pub-id-type="doi">10.18637/jss.v077.i01</pub-id></element-citation></ref></ref-list>
<ref-list>
  <title>References</title>
  <ref id="ref-akiba2019">
    <mixed-citation>Akiba, T., Sano, S., Yanase, T., Ohta, T., &amp;
    Koyama, M. (2019). Optuna: <named-content content-type="nocase">A
    Next-generation Hyperparameter Optimization
    Framework</named-content>. <italic>Proceedings of the 25th ACM
    SIGKDD International Conference on Knowledge Discovery &amp; Data
    Mining</italic>, 2623–2631.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1145/3292500.3330701">https://doi.org/10.1145/3292500.3330701</ext-link></mixed-citation>
  </ref>
  <ref id="ref-barrett2026">
    <mixed-citation>Barrett, T., Dowle, M., Srinivasan, A., Gorecki, J.,
    Chirico, M., Hocking, T., Schwendinger, B., &amp; Krylov, I. (2026).
    <italic>Data.table: Extension of data.frame</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.data.table">https://doi.org/10.32614/CRAN.package.data.table</ext-link></mixed-citation>
  </ref>
  <ref id="ref-becker2026f">
    <mixed-citation>Becker, M. (2026). <italic>Benchmark 2026 rush
    1.3.0</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.22899537">https://doi.org/10.5281/zenodo.22899537</ext-link></mixed-citation>
  </ref>
  <ref id="ref-becker2025a">
    <mixed-citation>Becker, M., Lang, M., Bischl, B., &amp; Richter, J.
    (2025). <italic>mlr3tuning: Hyperparameter Optimization for
    ’mlr3’</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.15852621">https://doi.org/10.5281/zenodo.15852621</ext-link></mixed-citation>
  </ref>
  <ref id="ref-becker2026b">
    <mixed-citation>Becker, M., Richter, J., Lang, M., Schneider, L.,
    Bischl, B., Schratz, P., Binder, M., Fischer, S., Chirico, M.,
    López-Ibáñez, M., Henry, L., &amp; Burk, L. (2026). <italic>Bbotk:
    Black-Box Optimization Toolkit</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.20669808">https://doi.org/10.5281/zenodo.20669808</ext-link></mixed-citation>
  </ref>
  <ref id="ref-becker2026c">
    <mixed-citation>Becker, M., Schneider, L., Binder, M., Kotthoff, L.,
    &amp; Bischl, B. (2026). <italic>mlr3mbo: Bayesian Optimization in
    R</italic> (arXiv:2603.29730). arXiv.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.48550/arXiv.2603.29730">https://doi.org/10.48550/arXiv.2603.29730</ext-link></mixed-citation>
  </ref>
  <ref id="ref-bengtsson2021">
    <mixed-citation>Bengtsson, H. (2021). A Unifying Framework for
    Parallel and Distributed Processing in R using Futures. <italic>The
    R Journal</italic>, <italic>13</italic>(2), 273–291.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/RJ-2021-048">https://doi.org/10.32614/RJ-2021-048</ext-link></mixed-citation>
  </ref>
  <ref id="ref-bergstra2013a">
    <mixed-citation>Bergstra, J., Yamins, D., &amp; Cox, D. (2013).
    Making a Science of Model Search: Hyperparameter Optimization in
    Hundreds of Dimensions for Vision Architectures. <italic>Proceedings
    of the 30th International Conference on Machine Learning</italic>,
    115–123.
    <ext-link ext-link-type="uri" xlink:href="https://proceedings.mlr.press/v28/bergstra13.html">https://proceedings.mlr.press/v28/bergstra13.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-bischl2023">
    <mixed-citation>Bischl, B., Binder, M., Lang, M., Pielok, T.,
    Richter, J., Coors, S., Thomas, J., Ullmann, T., Becker, M.,
    Boulesteix, A.-L., Deng, D., &amp; Lindauer, M. (2023).
    Hyperparameter optimization: Foundations, algorithms, best
    practices, and open challenges. <italic>WIREs Data Mining and
    Knowledge Discovery</italic>, <italic>13</italic>(2), e1484.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1002/widm.1484">https://doi.org/10.1002/widm.1484</ext-link></mixed-citation>
  </ref>
  <ref id="ref-bischl2025">
    <mixed-citation><named-content content-type="nocase">Bischl, B.,
    Casalicchio, G., Das, T., Feurer, M., Fischer, S., Gijsbers, P.,
    Mukherjee, S., Müller, A. C., Németh, L., Oala, L., Purucker, L.,
    Ravi, S., van Rijn, J. N., Singh, P., Vanschoren, J., van der Velde,
    J., &amp; Wever, M.</named-content> (2025). OpenML: Insights from 10
    years and more than a thousand papers. <italic>Patterns</italic>,
    <italic>6</italic>(7), 101317.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1016/j.patter.2025.101317">https://doi.org/10.1016/j.patter.2025.101317</ext-link></mixed-citation>
  </ref>
  <ref id="ref-bouthillier2023">
    <mixed-citation>Bouthillier, X., Tsirigotis, C., Corneau-Tremblay,
    F., Schweizer, T., Dong, L., Delaunay, P., Normandin, F., Bronzi,
    M., Suhubdy, D., Askari, R., Noukhovitch, M., Xue, C., Ortiz-Gagné,
    S., Breuleux, O., Bergeron, A., Bilaniuk, O., Bocco, S., Bertrand,
    H., Alain, G., … Beckham, C. (2023). <italic>Orion: Asynchronous
    distributed hyperparameter optimization</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.3478592">https://doi.org/10.5281/zenodo.3478592</ext-link></mixed-citation>
  </ref>
  <ref id="ref-carnell2026">
    <mixed-citation>Carnell, R. (2026). <italic>Lhs: Latin hypercube
    samples</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.lhs">https://doi.org/10.32614/CRAN.package.lhs</ext-link></mixed-citation>
  </ref>
  <ref id="ref-egele2025">
    <mixed-citation>Egelé, R., Balaprakash, P., Wiggins, G. M., &amp;
    Eiffert, B. (2025). DeepHyper: A Python Package for Massively
    Parallel Hyperparameter Optimization in Machine Learning.
    <italic>Journal of Open Source Software</italic>,
    <italic>10</italic>(109), 7975.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.21105/joss.07975">https://doi.org/10.21105/joss.07975</ext-link></mixed-citation>
  </ref>
  <ref id="ref-egele2023">
    <mixed-citation>Egelé, R., Guyon, I., Vishwanath, V., &amp;
    Balaprakash, P. (2023). Asynchronous Decentralized Bayesian
    Optimization for Large Scale Hyperparameter Optimization.
    <italic>2023 IEEE 19th International Conference on e-Science
    (e-Science)</italic>, 1–10.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1109/e-Science58273.2023.10254839">https://doi.org/10.1109/e-Science58273.2023.10254839</ext-link></mixed-citation>
  </ref>
  <ref id="ref-fischer2025">
    <mixed-citation>Fischer, S., Zobolas, J., Sonabend, R., Becker, M.,
    Lang, M., Binder, M., Schneider, L., Burk, L., Schratz, P., Jaeger,
    B. C., Lauer, S. A., Kapsner, L. A., Mücke, M., Wang, Z., Pulatov,
    D., Ganz, K., Funk, H., Harutyunyan, L., Camilleri, P., … Bischl, B.
    (2025). mlr3extralearners: Expanding the mlr3 Ecosystem with
    Community-Driven Learner Integration. <italic>Journal of Open Source
    Software</italic>, <italic>10</italic>(115), 8331.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.21105/joss.08331">https://doi.org/10.21105/joss.08331</ext-link></mixed-citation>
  </ref>
  <ref id="ref-fitzjohn2026">
    <mixed-citation>FitzJohn, R., &amp; Ashton, R. (2026). <italic>Rrq:
    Simple Redis queue</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://github.com/mrc-ide/rrq">https://github.com/mrc-ide/rrq</ext-link></mixed-citation>
  </ref>
  <ref id="ref-fleck2026">
    <mixed-citation>Fleck, S. (2026). <italic>Lgr: A fully featured
    logging framework</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.lgr">https://doi.org/10.32614/CRAN.package.lgr</ext-link></mixed-citation>
  </ref>
  <ref id="ref-gao2025">
    <mixed-citation>Gao, C. (2025). <italic>Mirai: Minimalist Async
    Evaluation Framework for R</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.7912722">https://doi.org/10.5281/zenodo.7912722</ext-link></mixed-citation>
  </ref>
  <ref id="ref-gelernter1985">
    <mixed-citation>Gelernter, D. (1985). Generative communication in
    Linda. <italic>ACM Transactions on Programming Languages and Systems
    (TOPLAS)</italic>, <italic>7</italic>(1), 80–112.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1145/2363.2433">https://doi.org/10.1145/2363.2433</ext-link></mixed-citation>
  </ref>
  <ref id="ref-ginsbourger2010">
    <mixed-citation>Ginsbourger, D., Le Riche, R., &amp; Carraro, L.
    (2010). Kriging Is Well-Suited to Parallelize Optimization. In Y.
    Tenne &amp; C.-K. Goh (Eds.), <italic>Computational Intelligence in
    Expensive Optimization Problems</italic> (pp. 131–162). Springer
    International Publishing.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1007/978-3-642-10701-6_6">https://doi.org/10.1007/978-3-642-10701-6_6</ext-link></mixed-citation>
  </ref>
  <ref id="ref-golovin2017">
    <mixed-citation>Golovin, D., Solnik, B., Moitra, S., Kochanski, G.,
    Karro, J., &amp; Sculley, D. (2017). Google Vizier: A Service for
    Black-Box Optimization. <italic>Proceedings of the 23rd ACM SIGKDD
    International Conference on Knowledge Discovery and Data Mining, KDD
    ’17</italic>, 1487–1495.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1145/3097983.3098043">https://doi.org/10.1145/3097983.3098043</ext-link></mixed-citation>
  </ref>
  <ref id="ref-head2018">
    <mixed-citation>Head, T., Shi, K., Vinícius, Z., Schröder, C.,
    Campos, N., Cereda, S., Fan, T., Shi, K., Schwabedal, J., Pak, M.,
    Callaway, F., Estève, L., Besson, L., Cherti, M., Pfannschmidt, K.,
    Linzberger, F., Cauet, C., Gut, A., Mueller, A., &amp; Fabisch, A.
    (2018). <italic>Scikit-optimize</italic>. Zenodo.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.5281/zenodo.1207017">https://doi.org/10.5281/zenodo.1207017</ext-link></mixed-citation>
  </ref>
  <ref id="ref-kandasamy2020">
    <mixed-citation>Kandasamy, K., Vysyaraju, K. R., Neiswanger, W.,
    Paria, B., Collins, C. R., Schneider, J., Poczos, B., &amp; Xing, E.
    P. (2020). Tuning Hyperparameters without Grad Students: Scalable
    and Robust Bayesian Optimisation with Dragonfly. <italic>Journal of
    Machine Learning Research</italic>, <italic>21</italic>(81), 1–27.
    <ext-link ext-link-type="uri" xlink:href="http://www.jmlr.org/papers/v21/18-223.html">http://www.jmlr.org/papers/v21/18-223.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-ke2017">
    <mixed-citation>Ke, G., Meng, Q., Finley, T., Wang, T., Chen, W.,
    Ma, W., Ye, Q., &amp; Liu, T.-Y. (2017). LightGBM: A Highly
    Efficient Gradient Boosting Decision Tree. <italic>Advances in
    Neural Information Processing Systems</italic>, <italic>30</italic>,
    3146–3156.
    <ext-link ext-link-type="uri" xlink:href="https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html">https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-landau2026">
    <mixed-citation>Landau, W. M. (2026). <italic>Crew: A distributed
    worker launcher framework</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://wlandau.github.io/crew/">https://wlandau.github.io/crew/</ext-link></mixed-citation>
  </ref>
  <ref id="ref-lang2019">
    <mixed-citation>Lang, M., Binder, M., Richter, J., Schratz, P.,
    Pfisterer, F., Coors, S., Au, Q., Casalicchio, G., Kotthoff, L.,
    &amp; Bischl, B. (2019). mlr3: A modern object-oriented machine
    learning framework in R. <italic>Journal of Open Source
    Software</italic>, <italic>4</italic>(44), 1903.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.21105/joss.01903">https://doi.org/10.21105/joss.01903</ext-link></mixed-citation>
  </ref>
  <ref id="ref-lang2017">
    <mixed-citation>Lang, M., Bischl, B., &amp; Surmann, D. (2017).
    Batchtools: Tools for R to work on batch systems. <italic>The
    Journal of Open Source Software</italic>, <italic>2</italic>(10),
    135.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.21105/joss.00135">https://doi.org/10.21105/joss.00135</ext-link></mixed-citation>
  </ref>
  <ref id="ref-lang2026">
    <mixed-citation>Lang, M., &amp; Fischer, S. (2026). <italic>mlr3oml:
    Connector Between mlr3 and OpenML</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.mlr3oml">https://doi.org/10.32614/CRAN.package.mlr3oml</ext-link></mixed-citation>
  </ref>
  <ref id="ref-li2020">
    <mixed-citation>Li, L., Jamieson, K., Rostamizadeh, A., Gonina, E.,
    Ben-tzur, J., Hardt, M., Recht, B., &amp; Talwalkar, A. (2020). A
    System for Massively Parallel Hyperparameter Tuning.
    <italic>Proceedings of Machine Learning and Systems</italic>,
    <italic>2</italic>, 230–246.
    <ext-link ext-link-type="uri" xlink:href="https://proceedings.mlsys.org/paper_files/paper/2020/hash/a06f20b349c6cf09a6b171c71b88bbfc-Abstract.html">https://proceedings.mlsys.org/paper_files/paper/2020/hash/a06f20b349c6cf09a6b171c71b88bbfc-Abstract.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-liaw2018">
    <mixed-citation>Liaw, R., Liang, E., Nishihara, R., Moritz, P.,
    Gonzalez, J. E., &amp; Stoica, I. (2018). <italic>Tune: A Research
    Platform for Distributed Model Selection and Training</italic>
    (arXiv:1807.05118). arXiv.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.48550/arXiv.1807.05118">https://doi.org/10.48550/arXiv.1807.05118</ext-link></mixed-citation>
  </ref>
  <ref id="ref-lindauer2022">
    <mixed-citation>Lindauer, M., Eggensperger, K., Feurer, M.,
    Biedenkapp, A., Deng, D., Benjamins, C., Ruhkopf, T., Sass, R.,
    &amp; Hutter, F. (2022). SMAC3: A Versatile Bayesian Optimization
    Package for Hyperparameter Optimization. <italic>Journal of Machine
    Learning Research</italic>, <italic>23</italic>(54), 1–9.
    <ext-link ext-link-type="uri" xlink:href="http://jmlr.org/papers/v23/21-0888.html">http://jmlr.org/papers/v23/21-0888.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-mersmann2024">
    <mixed-citation>Mersmann, O., Beleites, C., Hurling, R., Friedman,
    A., &amp; Ulrich, J. M. (2024). <italic>Microbenchmark: Accurate
    Timing Functions</italic>.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.microbenchmark">https://doi.org/10.32614/CRAN.package.microbenchmark</ext-link></mixed-citation>
  </ref>
  <ref id="ref-moritz2018">
    <mixed-citation>Moritz, P., Nishihara, R., Wang, S., Tumanov, A.,
    Liaw, R., Liang, E., Elibol, M., Yang, Z., Paul, W., Jordan, M. I.,
    &amp; Stoica, I. (2018). Ray: A Distributed Framework for Emerging
    AI Applications. <italic>Proceedings of the 13th USENIX Symposium on
    Operating Systems Design and Implementation (OSDI ’18)</italic>,
    561–577.
    <ext-link ext-link-type="uri" xlink:href="https://www.usenix.org/conference/osdi18/presentation/moritz">https://www.usenix.org/conference/osdi18/presentation/moritz</ext-link></mixed-citation>
  </ref>
  <ref id="ref-nii1986">
    <mixed-citation>Nii, H. P. (1986). The blackboard model of problem
    solving and the evolution of blackboard architectures. <italic>AI
    Magazine</italic>, <italic>7</italic>(2), 38–53.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1609/aimag.v7i2.537">https://doi.org/10.1609/aimag.v7i2.537</ext-link></mixed-citation>
  </ref>
  <ref id="ref-omalley2019">
    <mixed-citation>O’Malley, T., Bursztein, E., Long, J., Chollet, F.,
    Jin, H., &amp; Invernizzi, L. (2019). <italic>Keras Tuner</italic>.
    <ext-link ext-link-type="uri" xlink:href="https://github.com/keras-team/keras-tuner">https://github.com/keras-team/keras-tuner</ext-link></mixed-citation>
  </ref>
  <ref id="ref-olson2025">
    <mixed-citation>Olson, M., Santorella, E., Tiao, L. C., Cakmak, S.,
    Garrard, M., Daulton, S., Lin, Z. J., Ament, S., Beckerman, B.,
    Onofrey, E., Igusti, P., Lara, C., Letham, B., Cardoso, C., Shen, S.
    S., Lin, A. C., Grange, M., Kashtelyan, E., Eriksson, D., … Bakshy,
    E. (2025). Ax: A Platform for Adaptive Experimentation.
    <italic>Proceedings of the Fourth International Conference on
    Automated Machine Learning</italic>, 21/1–25.
    <ext-link ext-link-type="uri" xlink:href="https://proceedings.mlr.press/v293/olson25a.html">https://proceedings.mlr.press/v293/olson25a.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-rsigdb2024">
    <mixed-citation>R Special Interest Group on Databases (R-SIG-DB),
    Wickham, H., &amp; Müller, K. (2024). <italic>DBI: R database
    interface</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://dbi.r-dbi.org">https://dbi.r-dbi.org</ext-link></mixed-citation>
  </ref>
  <ref id="ref-rocklin2015">
    <mixed-citation>Rocklin, M. (2015). Dask: Parallel Computation with
    Blocked algorithms and Task Scheduling. In K. Huff &amp; J. Bergstra
    (Eds.), <italic>Proceedings of the 14th Python in Science
    Conference</italic> (pp. 126–132). SciPy.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.25080/Majora-7b98e3ed-013">https://doi.org/10.25080/Majora-7b98e3ed-013</ext-link></mixed-citation>
  </ref>
  <ref id="ref-rossini2007">
    <mixed-citation>Rossini, A. J., Tierney, L., &amp; Li, N. (2007).
    Simple Parallel Statistical Computing in R. <italic>Journal of
    Computational and Graphical Statistics</italic>,
    <italic>16</italic>(2), 399–420.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.1198/106186007X178979">https://doi.org/10.1198/106186007X178979</ext-link></mixed-citation>
  </ref>
  <ref id="ref-salinas2022">
    <mixed-citation>Salinas, D., Seeger, M., Klein, A., Perrone, V.,
    Wistuba, M., &amp; Archambeau, C. (2022). Syne Tune: A Library for
    Large Scale Hyperparameter Tuning and Reproducible Research.
    <italic>Proceedings of the First International Conference on
    Automated Machine Learning</italic>, 16/1–23.
    <ext-link ext-link-type="uri" xlink:href="https://proceedings.mlr.press/v188/salinas22a.html">https://proceedings.mlr.press/v188/salinas22a.html</ext-link></mixed-citation>
  </ref>
  <ref id="ref-wickham2011">
    <mixed-citation>Wickham, H. (2011). Testthat: Get Started with
    Testing. <italic>The R Journal</italic>, <italic>3</italic>(1),
    5–10.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/RJ-2011-002">https://doi.org/10.32614/RJ-2011-002</ext-link></mixed-citation>
  </ref>
  <ref id="ref-wickham2016">
    <mixed-citation>Wickham, H. (2016). <italic>ggplot2: Elegant
    graphics for data analysis</italic>. Springer-Verlag New York.
    <ext-link ext-link-type="uri" xlink:href="https://ggplot2.tidyverse.org">https://ggplot2.tidyverse.org</ext-link></mixed-citation>
  </ref>
  <ref id="ref-wickham2025">
    <mixed-citation>Wickham, H., Pedersen, T. L., &amp; Seidel, D.
    (2025). <italic>Scales: Scale functions for visualization</italic>
    [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.32614/CRAN.package.scales">https://doi.org/10.32614/CRAN.package.scales</ext-link></mixed-citation>
  </ref>
  <ref id="ref-wilke2025">
    <mixed-citation>Wilke, C. O. (2025). <italic>Cowplot: Streamlined
    plot theme and plot annotations for ’ggplot2’</italic> [Manual].
    <ext-link ext-link-type="uri" xlink:href="https://wilkelab.org/cowplot/">https://wilkelab.org/cowplot/</ext-link></mixed-citation>
  </ref>
  <ref id="ref-wright2017">
    <mixed-citation>Wright, M. N., &amp; Ziegler, A. (2017). Ranger: A
    Fast Implementation of Random Forests for High Dimensional Data in
    C++ and R. <italic>Journal of Statistical Software</italic>,
    <italic>77</italic>(1), 1–17.
    <ext-link ext-link-type="uri" xlink:href="https://doi.org/10.18637/jss.v077.i01">https://doi.org/10.18637/jss.v077.i01</ext-link></mixed-citation>
  </ref>
</ref-list>
<fn-group>
  <fn id="fn1">
    <label>1</label><p>ASHA is algorithmically decentralized, but its
    common implementation in Ray Tune uses a central scheduler.</p>
  </fn>
  <fn id="fn2">
    <label>2</label><p>Installation instructions for all platforms are
    available at
    <ext-link ext-link-type="uri" xlink:href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/">https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/</ext-link>.</p>
  </fn>
  <fn id="fn3">
    <label>3</label><p>The multi-node BO benchmarks in
    Section <xref alt="5" rid="sec-benchmarks-bo">5</xref> use TCP,
    which adds some overhead.</p>
  </fn>
  <fn id="fn4">
    <label>4</label><p>The workers therefore do not repeat each other’s
    computations: their archives usually differ in the configurations
    under evaluation and the imputed values, and even on identical
    archives, the randomness in the surrogate, the acquisition function,
    and its optimizer leads to different proposals.</p>
  </fn>
</fn-group>
</back>
</article>
