Quicx

Never asks the OSfor another byte

A deterministic-memory task queue, written in C. You give it a memory budget at startup — it uses exactly that, tells you precisely what it is using, and never grows.

74,300tasks/secend to end, 1 thread
51 µsp50submit → done
0 KBgrowthover 443,912 tasks
54 KBbinaryzero dependencies

Measured on one machine with bench/run.sh. Nothing here is estimated.

§ 02/Measured

Every number herecame off a run you can repeat.

One machine, one build, one afternoon — reproducible with bench/run.sh. Nothing below is estimated, extrapolated, or taken from documentation.

machine
Apple M4 Pro (12 cores), 24 GB
os
macOS 26.5.1, arm64
build
Apple clang 17, quicxd built -O2
allocator
Default 8 MB PMAD pool
transport
Loopback TCP, no network hop
load
8 worker processes, 8 producer connections, C clients
Throughput
74,300tasks/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 task is a full lifecycle — submit, ack, dispatch, parse, done, notify. Five protocol messages, not one enqueue.

Latency · submit → done
51µs p50
  • p5051 µs
  • p9067 µs
  • p99111 µs
  • p99.9174 µs

Open loop at 40k/s offered, timed from the scheduled send — stalls are charged in full, not hidden.

Memory
0 KBgrowth
idle RSS
9,584 KB
after 443,912 tasks
9,584 KB
peak of 8 MB pool
10.7%

Not low growth — no growth. The pool is mapped once at startup and every allocation comes out of it.

Latency vs offered loadFlat 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

How it was measured

The methodology is the difference between a benchmark and a marketing claim, so it belongs here and not in a footnote.

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.
Full benchmark write-up
§ 03/Principles

Six opinions.So you don't have to make them.

What makes Quicx, Quicx — six deliberate choices, baked into every frame, every slab, every byte.

§ 04/Under the hood

One daemon.Every piece in its place.

Producers submit tasks over a compact binary protocol. The Quicx daemon routes, queues and dispatches work to a fixed pool of workers — all backed by PMAD, our deterministic slab allocator.

PMAD SLAB ALLOCATORO(1) alloc / freePRODUCERSStats MonitorProducer BProducer AQUICX DAEMONConnection RouterPROCESSINGTask QueueDispatcherWorker PoolWORKERSWorker 1Worker 2Worker NMSG_STATSMSG_SUBMITMSG_OKMSG_TASKMSG_DONEMSG_SUBMIT (producer → daemon)MSG_OK (acknowledgment)MSG_TASK (task dispatch)MSG_DONE / memory allocMSG_STATS / MSG_WAIT
QuicxOne daemon · zero moving parts
§ 05/Honest limits

What Quicx does not do.Because some of the speed is work it is skipping.

Quicx is a fast in-memory broker, not a durable one. If you need at-least-once delivery, use beanstalkd or Redis Streams — and if you need an exact memory ceiling, keep reading.

  • Persistence

    A restart loses everything queued.

  • RedeliveryIn progress

    If a worker dies mid-task, the task is lost and the producer is not notified.

  • Retries

    No automatic retry, no dead-lettering.

  • Priorities, delays, named queues

    One global FIFO.

  • Client libraries beyond Java

    dev.quicx:quicx-client is the only one. Every other language talks the wire protocol directly — it is documented and small.

  • Payloads above 1 KB

    PROTOCOL_MAX_PAYLOAD is 1024 bytes, matching the largest PMAD size class. Oversized frames get a clean MSG_ERROR.

  • Multiple coresIn progress

    Single-threaded today. Scale out by running several daemons behind a TCP load balancer.

Against other queues

Only the Quicx row was measured here. Every other figure comes from vendor documentation or third-party benchmarks on unknown hardware, and is 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
The 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.
§ 06/Get started

One command away.

Install the daemon, pull the Java client from Maven Central, and submit your first task — no managed service, no account, no surprises.

$curl -fsSL https://quicx.dev/install.sh | sh