Benchmarks
What ruststream-gcp-pubsub costs on every message: throughput against the raw
google-cloud-pubsub client, and instructions and allocations of its code.
Against a raw client
The first chart runs each scenario three ways against the local Pub/Sub emulator. Raw client
drives google-cloud-pubsub directly. Broker crate drives this crate and nothing above it: its
subscription, the deliveries it yields and its publisher, pulled and settled by a loop with no
service around it. RustStream is the service a user writes: a #[subscriber] handler, the app
and the runtime. The first gap is what this crate costs over the client it wraps, and it is the
number this repository answers for. The second is what the runtime adds on top, over Pub/Sub in
particular.
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 second scenario publishes under ordering keys, the delivery shape a subscription with ordering enabled keeps in order per key. It also gives this crate one piece of work the first scenario does not have: the key is carried into the delivery's headers, where a service that names no broker reads it as a partition key. A loop on the client reads the field on the message and copies nothing.
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 the service a user
writes, built on PubSubBroker and started against the emulator of the same stand. Everything on
the service's thread is counted: the framework's dispatch, this crate's code and the
google-cloud-pubsub client, whose streaming pull, acknowledgements, lease extensions and publish
requests run there. Cold start is what starting the service and taking the first delivery cost
once.
Most of every row is the client. The reply row is the largest: the runtime waits for each reply to be confirmed before it takes the next delivery, so every reply leaves in a publish request of its own. The cold start is almost all the client too: it builds two administration clients, each of which reads and parses the machine's root certificates, so that column depends on the machine's certificate store.
Results
- Raw client
- Broker crate
- RustStream
- rounds, best to slowest
- best round
- median round
- spread of the rounds
- indistinguishable
ruststream-gcp-pubsub 0.7.0 on ruststream 0.7.0, AMD Ryzen 9 7900X 12-Core Processor, Linux 7.2.10-x64v3-xanmod1-1, Pub/Sub emulator, gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators, Google Cloud SDK 587.0.0, in Docker on localhost, measured 2026-10-18. How the numbers were taken
- indistinguishable
- indistinguishable
- indistinguishable
- indistinguishable (broker-bound)
Show the numbers as a table
| Scenario | Raw client | Broker crate | RustStream | Overhead |
|---|---|---|---|---|
| Streaming pull, 512 B JSON, ack each | 1,815 msg/s (1,807) | 1,805 msg/s (1,803) (indistinguishable over the client) | 1,819 msg/s (1,809) | indistinguishable |
| Streaming pull with ordering keys, 512 B JSON, ack each | 1,987 msg/s (1,986) | 1,987 msg/s (1,986) (indistinguishable over the client) | 1,988 msg/s (1,988) | indistinguishable (broker-bound) |
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-gcp-pubsub default (none), ruststream macros,json; google-cloud-pubsub 1.3.0. ruststream-gcp-pubsub 0.7.0, ruststream 0.7.0, measured 2026-10-18.
Show the numbers as a table
| Scenario | Instructions | Allocations | Cold start |
|---|---|---|---|
| consume, JSON decode into a small struct, ack each | 10,571 | 5.4 | 87,632,378 / 7,653 |
| reply to a declared topic, each confirmed before the next delivery | 106,926.5 | 88.3 | 87,639,206 / 7,680 |
| consume in batches of 64, assembled on the client | 10,809.5 | 5.5 | 87,640,196 / 7,657 |
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 | Pub/Sub emulator, gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators, Google Cloud SDK 587.0.0, in Docker on localhost |
| Round trip | 0.269 ms (20000 GetSubscription calls on one connection); a delivery is charged 1 of them |
| Compiler | 1.99.0 |
| Build profile | bench, inheriting release (opt-level = 3, lto = fat, codegen-units = 16, debug = line-tables-only) |
| Features | ruststream-gcp-pubsub default (none), ruststream macros,json; google-cloud-pubsub 1.3.0 |
| RUSTFLAGS | none (the recipe clears RUSTFLAGS, so the numbers are not tied to this CPU) |
| Valgrind | 3.25.1 |
The build flags are published with the numbers because they change them: a binary built with
-C target-cpu=native produces a figure no other machine can reproduce, so the recipe clears the
variable before it builds.
The numbers come from docs/benchmarks/results.json of this version, written by the benchmark
recipe. The framework's site reads the same document, published at
benchmarks/results.json,
to build its table across brokers. Methodology says how the numbers were taken,
and Numbers what they allow you to conclude.