Benchmarks
What ruststream-nats costs on every message: throughput against the raw async-nats client, and
instructions and allocations of its code.
Against a raw client
The first chart runs each scenario three ways on a real NATS server. Raw client drives
async-nats directly. Broker crate drives this crate's subscription with a loop of its own and
no runtime. RustStream is the service a user writes, a #[subscriber] handler in an app. The
first gap is what this crate costs over the client it wraps, and the second is what the runtime
adds on top.
Each loop is a bar from its best round to its slowest, with a dot at the best round and a notch at the median. The zero of the axis is the raw client's best round, and a dot right of zero is slower. A hollow dot is a loop whose difference from the raw client is smaller than the spread of the rounds. The table under the chart prints the best round with the median in parentheses.
The two scenarios are the two models. On a Core NATS subject the server pushes each match and settles nothing. On a JetStream pull consumer the client asks for messages in batches and acknowledges each one, as Path draws.
Cost of the code
The second chart counts what one message costs on the service's thread: instructions under
callgrind and allocations under DHAT, in the steady state. Each scenario is a service on
NatsBroker reading a JetStream pull consumer on the same
server. The service runs on one thread, and the async-nats connection task runs there too, so the
count holds the framework, this crate and the client's own work on the socket. The producer's
thread is not counted. Cold start is what connecting, finding the stream, creating the consumer
and taking the first message cost once.
Results
- Raw client
- Broker crate
- RustStream
- rounds, best to slowest
- best round
- median round
- spread of the rounds
- indistinguishable
ruststream-nats 0.7.0 on ruststream 0.7.0, AMD Ryzen 9 7900X 12-Core Processor, Linux 7.2.10-x64v3-xanmod1-1, nats:2-alpine in Docker on localhost, measured 2026-10-18. How the numbers were taken
- indistinguishable
- indistinguishable
- indistinguishable
- indistinguishable
Show the numbers as a table
| Scenario | Raw client | Broker crate | RustStream | Overhead |
|---|---|---|---|---|
| Core NATS, 512 B JSON | 1,569,899 msg/s (1,539,255) | 1,471,311 msg/s (1,394,551) (indistinguishable over the client) | 1,489,165 msg/s (1,414,937) | indistinguishable |
| JetStream pull consumer, 512 B JSON, ack each | 226,164 msg/s (218,628) | 220,256 msg/s (217,097) (indistinguishable over the client) | 220,367 msg/s (215,485) | indistinguishable |
AMD Ryzen 9 7900X 12-Core Processor, x86_64, 427-5912 MHz, 12 physical, 24 logical, 62.4 GiB, unknown, Linux 7.2.10-x64v3-xanmod1-1, 1.99.0, 3.25.1, bench, inheriting release (opt-level = 3, lto = fat, codegen-units = 16, debug = line-tables-only), ruststream-nats default (none), ruststream macros,json. ruststream-nats 0.7.0, ruststream 0.7.0, measured 2026-10-18.
Show the numbers as a table
| Scenario | Instructions | Allocations | Cold start |
|---|---|---|---|
| JetStream pull consumer, JSON decode into a small struct, ack each | 8,679.9 | 7.1 | 292,434 / 295 |
| JetStream pull consumer, reply published by NatsPublisher on Core NATS | 12,165.1 | 11.1 | 297,315 / 300 |
| JetStream fetches of 64, ack each | 8,469.6 | 6.4 | 316,037 / 322 |
The machine
| Processor | AMD Ryzen 9 7900X 12-Core Processor |
|---|---|
| Architecture | x86_64 |
| Frequency | 427-5912 MHz |
| Cores | 12 physical, 24 logical |
| Memory | 62.4 GiB |
| Memory speed | unknown |
| Operating system | Linux 7.2.10-x64v3-xanmod1-1 |
| Broker | nats:2-alpine in Docker on localhost |
| Round trip | 68.3 us (median of 30000 request/reply round trips on one connection) |
| Compiler | 1.99.0 |
| Build profile | bench, inheriting release (opt-level = 3, lto = fat, codegen-units = 16, debug = line-tables-only) |
| Features | ruststream-nats default (none), ruststream macros,json |
| RUSTFLAGS | none (the recipe clears RUSTFLAGS, so the numbers are not tied to this CPU) |
| Valgrind | 3.25.1 |
The numbers come from docs/benchmarks/results.json of this version, written by the benchmark
recipe. Methodology says how they were taken, and Numbers what
they allow you to conclude.