Skip to content

DatumThe streaming layer that outruns the JVM

Akka Streams' API shape. FS2 and ZIO's concurrency vocabulary. Every claim measured against all three — in safe Rust on Tokio.

Datum — a blue jellyfish

The scoreboard

Faster than the best JVM competitor on every benchmarked row

Wall-clock and whole-process CPU, same host, warmed JVMs. Each figure below is the headline row of a checked-in result table — Datum versus the best of Akka, FS2, and ZIO for that scenario.

9.9×Remote StreamRefsvs Akka Artery-TCP, forced-remote · 14.8× less CPU
83×Signal propagation, 1,024 subscribersvs FS2 SignallingRef · 145× less CPU
23×Topic fan-out, 1,024 subscribersvs ZIO Hub · 158× less CPU
7.5×Channel MPSC, 1,024 producersvs Akka MergeHub · 20.5× less CPU
13–46×Fused graph chainsvs warmed Akka · typed executor, near-zero allocation
0Unsafe blocks#![forbid(unsafe_code)] — enforced by the compiler, not policy

Measured at v0.9.0 (2026-07-03) · full result tables

By area

Where the time goes

AreaHeadline scenarioWallCPURecord
Concurrency primitivesTopic fan-out ×1024 vs ZIO Hub23× faster158× lesstable
Remote StreamRefs1,024 elements, fold-to-sum vs Akka Artery-TCP9.9× faster14.8× lesstable
Graph & junctionsFused identity/map chains vs Akka13–46× fastertracks walltable
QueuesSource-queue backpressure vs Akka985× faster112× less alloctable
Network carriersTLS echo round-trip vs Akka · RSS 8 MB vs 799 MB3.4× faster8.3× lesstable
Substreamsprefix_and_tail / flat_map_concat vs Akka10–12× faster29–43× less alloctable
Source / FlowSync operator chains vs Akka2.3–5.6× faster4–36× less alloctable

Not every row is a blowout — bounded-queue offer sits at ~parity (0.96×) and JSON framing at 1.00×, and the tables say so. The bar is parity-or-better on wall and CPU, everywhere.

Why you can trust the numbers

Benchmark discipline is part of the library

The CPU column, always

A path can win wall-clock by busy-spinning while the JVM parks. Every table reports whole-process CPU so that cost can't hide.

Correctness counters

Every scenario asserts the work actually happened — element counts, per-subscriber totals, final values — on both sides of the comparison.

Frozen baselines first

JVM target numbers are captured and committed before the Datum implementation exists, then never touched.

Root-cause every miss

A losing row is never shipped as "inherent cost". It gets a named lever or a fix — the record keeps both the misses and the levers.

Scope

What Datum is — and deliberately isn't

It is

  • An Akka/Pekko Streams mirror — API shape and behavior, so Scala teams feel at home in a week, not a quarter.
  • A concurrency toolkitSignal, Subscription, Channel, Topic for state, handoff, and pub-sub between streams, actors, and plain code.
  • A foundation layerdatum-core plus satellites for networking, jobs, and now cluster membership & sharding (datum-net, datum-agent, datum-cluster); built to carry higher-level frameworks on top.

It is not

  • A Reactive Streams bridge — no Publisher/Subscriber interop; the integration surfaces are Tokio futures and Ractor actors.
  • A line-by-line port — where Rust conventions differ, Rust wins: filter_map over collect, Result over exceptions, explicit overflow policies over defaults.
  • Benchmarketing — the harness measures both sides fairly or the number doesn't ship.