Ring round-trip latency

Goblin Core's lowest-latency path is a shared-memory ring: no kernel on the request path, just a cache-line handoff between two cores. This page measures the round-trip latency of that path for the SBE binary wire and, for contrast, RESP over the same ring.

What is measured

benchmarks/sbe_vs_resp_ring.cpp starts one server that exposes two rings — RESP and SBE — with the client and server pinned to separate cores (where the OS allows). For each operation it times a synchronous round trip — send one command, read its reply — in a tight loop, and reports the median over a 3-second window (millions of samples) plus the p90/p99 tail.

Operations: PING (pure framing, nothing to parse), ZADD of 1 and 10 members (native double scores on SBE vs ASCII text on RESP), and HSET of 1 and 10 fields.

The median is the figure to read. On a scheduler-managed host the mean is dominated by rare preemptions of the busy-poll loop — it can sit several times above the median — so the median and the tight p90 are the true per-op cost.

AMD Ryzen Threadripper PRO 5995WX

64 cores / 128 threads, Zen 3, up to 4.575 GHz. Worth stating plainly: this is a many-core workstation part, tuned for aggregate throughput, not a single-thread speed champion — a consumer desktop chip clocks higher on one core. That makes these numbers a conservative floor: the ring turns in sub-microsecond round trips on a CPU that is not trying to win single-thread benchmarks.

Median round-trip, client and server pinned to separate cores:

operation SBE RESP
PING 0.13 µs 0.26 µs
ZADD, 1 member 0.21 µs 0.43 µs
ZADD, 10 members 0.93 µs 1.60 µs
HSET, 1 field 0.23 µs 0.41 µs
HSET, 10 fields 0.71 µs 1.24 µs

SBE runs ~1.7–2.0× faster than RESP over the same ring — the delta is pure protocol: native numbers instead of ASCII, and jump-table dispatch on the template id rather than tokenizing. The single-element ops hold p99 within ~0.1 µs of the median.

Apple M4 Max

12 performance cores, macOS. No taskset (and THREAD_AFFINITY_POLICY is a no-op on Apple Silicon), so client and server run unpinned. The busy-poll path is tuned for Darwin instead: 128-byte isolation of the four ring index words (Apple L1/L2 lines + 128-byte prefetch), USER_INTERACTIVE QoS on both processes, and cpu_relax that does not emit yield (a yield-spin is treated as low-value work and parked for ~10–15 µs).

Run the bench under caffeinate -i so App Nap / idle throttling does not sit on the spinners. Numbers below are SBE median round-trip µs, local M4 Max, Release build.

Single-element — clean:

operation p50 p99 min
PING 0.17 ~8 0.04
ZADD, 1 0.21 ~8 0.10
HSET, 1 0.17 ~1 0.10

A synchronous unpipelined round trip is reliably sub-250 ns at the median. The p99 still shows occasional ~8–15 µs scheduler holes (unpinned dual busy-poll has no isolcpus equivalent); they are rare enough that single-element medians stay honest.

Ten-element — partially open:

operation p50 note
ZADD, 10 ~1.0 usable; p90 still sees the ~15 µs cliff
HSET, 10 ~14–15 median sits on the cliff; min ≈ 0.6 µs proves the ring can do it

Do not publish the HSET-10 median as “ring latency” — it is a scheduler artifact (the min shows the path is fine when both threads stay hot). Two-sided sub-µs busy-poll without core isolation remains a Linux strength; on macOS the single-element path is solved, and batched ops are still under investigation (adaptive spin-then-timeout is in the tree as a tail bound, not a full cure).

Even the stalled ~15 µs numbers stay at Unix-domain-socket cost (~10 µs in BENCHMARKS), never worse. Single-element is ~50–75× under a socket.