Quicx
§ 02.04Core Concepts

Benchmarks

74,300 tasks/sec end to end at 51 µs median, with zero memory growth over 443,912 tasks. Every figure here was measured on one machine with the harness in bench/, and anything that was not measured is labelled with its provenance.

NOTEWhat “measured” means on this page
Every figure below came off one machine, one build, one afternoon, and is reproducible with bench/run.sh. Numbers that were not measured here appear only in the comparison table at the bottom, and are labelled with their provenance every time they appear.

Reference run

Configuration
machineApple M4 Pro (12 cores), 24 GB
osmacOS 26.5.1, arm64
buildApple clang 17, quicxd built -O2
allocatorDefault 8 MB PMAD pool
transportLoopback TCP, no network hop
load8 worker processes, 8 producer connections, C clients
HEADS UPEarlier figures were withdrawn
Anything measured before the send_frame_v fix is unverifiable and has been removed from this site. protocol.c wrote one struct iovec past the end of a stack array on every task dispatch that carried a payload — undefined behaviour. One build aborted on the first task; another silently corrupted adjacent stack slots and kept running. A measurement taken before the fix may be fine or may be garbage, and there is no way to tell retroactively, so everything was retaken on a fixed build. The fix itself is free: 46.8k/s fixed versus 46.4k/s unfixed at identical settings.

Throughput

74,300 tasks/sec — end to end, single thread. Closed loop, 8 connections × 32 in flight, 8 workers. 443,912 tasks in the measured window, 0 errors, 0 dropped connections. Repeat runs land between 74k and 75k.

One Quicx task is a full lifecycle: producer submits → daemon acks → daemon dispatches → worker parses the payload → worker reports done → producer is notified. Five protocol messages. This is not a single enqueue operation, and it should not be compared against one.

Latency

Open loop at 40,000 tasks/sec offered. Tasks are due on a fixed schedule and latency is measured from the scheduled time, so any stall is charged in full rather than hidden by a client that slows down along with the server.

Percentilesubmit → done
p5051 µs
p9067 µs
p99111 µs
p99.9174 µs

Latency stays flat from 10k/s to 40k/s — the daemon is not straining anywhere in that range.

offeredp50p99p99.9
10k/s52 µs116 µs157 µs
20k/s52 µs99 µs163 µs
40k/s51 µs111 µs174 µs
HEADS UPsubmit → ack is not the number that matters
A submit → ack measurement times how long the daemon takes to say “received”. It never touches a worker. Every latency figure on this page is submit → done: the producer is notified only after a worker has actually parsed the payload and reported completion.

Memory

Idle RSS 9,584 KB. After 443,912 tasks: 9,584 KB. Delta: 0 KB.

Not “low growth” — no growth. The pool is mapped once at startup and every allocation comes out of it. quicx status reports live usage per size class, so you can see exactly which classes your workload consumes. At the default 8 MB pool under this load, peak usage was 10.7% of the pool.

Footprint

Value
binary54.4 KB (-O2, stripped of nothing)
runtime dependenciesnone
source~2,480 lines (of C)
threads1

Methodology

The harness lives in bench/ and you can run it yourself. The choices below are the difference between a benchmark and a marketing claim.

C clients, not Python
A Python client tops out around 50–100k socket ops/sec and contends on the GIL across threads, so it saturates before the daemon does and ends up measuring itself.
Latency is submit → done
Not submit → ack. An ack only means the daemon received the frame; it never touches a worker. For a task queue, the number that matters is when the work is finished.
Open loop for latency, closed loop for throughput
Latency runs put tasks on a fixed schedule and charge stalls against the scheduled send time — the standard correction for coordinated omission. Closed-loop latency is not quoted, because a client that backs off when the server slows cannot measure the server slowing.
Real payloads, really parsed
nginx combined-format access-log lines, 120–260 bytes. Workers parse them — IP, method, path hash, status, bytes — and report per-status totals, so a run can be verified rather than trusted.
One machine, loopback TCP
No network hop. Real deployments will be slower.
The harness checks itself
256 outstanding requests ÷ 74.3k/s = 3.4 ms, and measured closed-loop p50 was 3.6 ms. Little's Law agrees, so the instrument is measuring queueing and not itself.

Comparison with other queues

HEADS UPOnly the Quicx row was measured here
Every other figure comes from vendor documentation or third-party benchmarks on unknown hardware. They are included for rough orientation, not as a head-to-head result.
throughputlatencyprovenance
Quicx74.3k tasks/s (1 thread)51 µs p50 end-to-endmeasured here
Redis (LPUSH)~161k commands/s0.30 ms p50, one commandreported
RabbitMQ~40k msg/sreported
Kafka>1M msg/s, batched~msreported
beanstalkdno comparable figure found
PERFORMANCEThe unit trap
A Quicx task is five protocol messages across two connections. A Redis LPUSH is one command — and a real Redis job queue needs LPUSH + BRPOP+ an ack. Normalised per completed job, Redis’s ~161k commands/sec is on the order of 50k jobs/sec. Putting 74.3k tasks/s next to 161k LPUSH/s directly would flatter Redis by roughly 3×.

Part of Quicx’s latency advantage is work it is not doing. Before you take these numbers as a reason to switch, read Limitations — it lists exactly which work that is.