Orisha Enters the Arena
Orisha Enters the Arena
Orisha’s entry to HttpArena — thirty benchmark profiles on dedicated 64-core hardware, same conditions for every framework, results published on a public leaderboard — is built, validated, and staged. It is also deliberately not submitted. This post is the state of things: what the entry is, what the audit found before we let it near the board, and what stands between staged and submitted.
One deliberate omission: this post carries no performance numbers. We are asking to be admitted to someone else’s benchmark — publishing our own measurements of their field beforehand is announcing results for a race we haven’t run yet. Every verdict below names a mechanism instead. The figures exist in the repo’s benchmark history; the board’s published number is the one that will count.
What the entry is
Orisha is an HTTP server written in Koru, a new language whose compiler is metacircular and whose backend emits Zig — so an Orisha deployment is a single native binary, no runtime, no VM. The part that matters for a benchmark board: the route table is resolved at compile time. GET /pipeline doesn’t reach a router at runtime; the compiler sees that the route is literal, sees the handler never touches the request, and emits a dispatch that compares two strings and writes a fully-baked response — status line, headers, body, all rodata — with a splice point for the Date header. The same specialization decides whether a connection needs a Request object at all.
The submission is two entries, same server, different I/O backends:
- orisha — an epoll pump: level-triggered readiness, shared epoll fd with work stealing, batched writes for pipelined requests.
- orisha-uring — an io_uring pump: per-worker rings with
SO_REUSEPORT, multishot accept and recv, a provided-buffer pool, adaptive CQE coalescing, and TLS connections whose OpenSSL instance hands the record layer to the kernel (kTLS) at handshake end — offloaded connections then run the same plain ring I/O as cleartext ones.
Both subscribe to seven profiles: baseline, latency-1m, latency-10k, pipelined, limited-conn, json-comp, json-tls. No response cache, no startup pre-serialization — json serializes every field per request, json-comp compresses per request.
Why the audit before the submission
The project’s rule is deficits first: anywhere another entry beats you is a defect report, and a defect report gets reproduced and decomposed until it has a mechanism’s name — then it’s either fixed or explained. A leaderboard position you can’t explain isn’t a result. Going in, the board showed us behind on three fronts. Each resolved differently.
Deficit one: json-tls — the court was lying
The first measurement had us behind on TLS JSON. Three contaminations were hiding in that result.
Host variance. Identical binaries on two different droplet pairs scored far apart — the difference was hypervisor steal, present on one pair and absent on the other. Part of every result was which physical machine we drew.
The load generator’s ceiling. At benchmark connection counts, every server — ours, the field’s, plaintext or TLS — converged to the same throughput ceiling. That is the load generator running out of threads, not a TLS tax: throughput differences above the instrument’s ceiling are invisible, and the separating variable is CPU per request, not requests per second.
The reference entry wasn’t answering. Zix validates Host against its certificate’s SAN list; on a private-IP link it answers 421 Misdirected Request to everything. Any comparison against those numbers measured error responses, not the server.
On a clean court — two droplets, private link, near-zero steal, the reference entry actually serving JSON — the separating variable became measurable for the first time: throughput pinned at the instrument’s ceiling, CPU per request the real comparison. And the audit produced a capability receipt, not just numbers: /proc/net/tls_stat showed every connection offloaded to kernel TLS — the first time the offload path was verified end to end on a real kernel.
Deficit two: the io_uring premium — the number was a ramp
The uring backend burned far more CPU than epoll on every TLS run — bad enough to ask whether the backend was simply broken. The decomposition said otherwise: the pump machinery per request was within a small factor of epoll’s. The excess was the handshake ramp — thousands of TLS handshakes inside a short measurement window is thousands of RSA signatures, and the old memory-BIO handshake path did that work in userspace, shuttling every flight through staged buffers, before kTLS took over. Given time to amortize, the steady state was sane. The window was lying about it.
The verdict on the number stands, but the machinery it measured no longer exists. Since that run, the io_uring TLS path was redesigned around the thing it should have been from the start: a socket-BIO connection — OpenSSL owns the fd for the handshake’s whole life, and the ring supplies readiness through poll_add instead of shuttling ciphertext through userspace buffers. SSL_OP_ENABLE_KTLS on the context lets OpenSSL install the kernel record layer itself at handshake end — no record-sequence bookkeeping on our side at all. Offloaded connections then drop the SSL entirely and become literally cleartext-shaped on the ring: plaintext in from the provided-buffer pool, plaintext out through ordinary send SQEs, the kernel doing the crypto either side. Connections the kernel can’t offload stay on SSL_read/SSL_write — the same cost model the epoll entry already pays. The redesign deleted the memory-BIO staging path entirely — 384 lines, including the sequence-number counting the manual handoff required. (The design, in full.)
Deficit three: pipelined — real, with a name
This one survived the audit. The board showed us behind on pipelined throughput, and the decomposition named real mechanisms:
Batching was never broken — one read, one write, responses per syscall matching the pipeline depth, verified by strace.
The epoll entry pays extra syscalls per batch — the rearm is the price of the shared-fd work-stealing design. Fixing it means per-worker epoll fds and level-triggering — real surgery, filed.
The uring entry lacked adaptive CQE coalescing. The field’s leader asks the kernel for a bounded batch of completions per io_uring_enter; ours parked on the first one. orisha@c01cbe7 ports the same mechanism — IORING_ENTER_EXT_ARG with a kernel-side bound, adaptive wait_nr, gated on IORING_FEAT_EXT_ARG — and completion waits per batch dropped accordingly.
The wire bytes. Our /pipeline response carries a Date: header that the field’s leader omits — a SHOULD, not a MUST. Matching wire bytes is a format decision, priced but not made.
Eliminated by measurement: the dispatch chain (skipping it wholesale scored within noise), header recording, request-buffer copies (the uring path already dispatches zero-copy inside its buffer pool).
What the board still has to measure
- Board scale. Every audited result came from small machines. Whether per-core efficiency holds across 64 cores and real connection storms is the actual question the submission answers — unmeasured by definition until they run it.
- The kTLS transition on the new path. Local Docker runs on a kernel without
CONFIG_TLS, so every local connection on the redesigned code exercised the userspace socket-BIO path — the fallback, not the offload. The transition is compiled and reviewed; the board will be its first flight. - Whether coalescing closes the pipelined gap at full connection counts. The mechanism favors high concurrency; the board will say.
- The
Date:call, and the epoll redesign. Both priced, neither done.
The state of the submission
The entry is staged — frameworks/orisha/ and frameworks/orisha-uring/, each a Dockerfile that shallow-clones main and compiles orisha/httparena/main.k with koruc, plus a meta.json declaring the seven profiles. The official validator passes 37/37 on both entries on the current code: baseline, randomized anti-cheat inputs, a TCP fragmentation sweep across a thousand split offsets, pipelined, per-request gzip, and the full TLS battery — TLS 1.3 + AEAD negotiation, weak-protocol rejection, per-request JSON over TLS — including everything the new socket-BIO path touches.
Worth saying plainly, because an honest entry is the only kind worth making: the arena-facing handlers exercise Orisha’s general machinery at full tilt — handlers may emit complete wire messages, with the send path splicing a live Date: line into the head — and that is a documented framework feature, not a benchmark backdoor. What remains genuinely board-shaped is thin and named: a response-item buffer sized to the fixture’s dataset (with a heap fallback for anything larger), and a Dockerfile sed that adds the TLS listener line for the uring build. Everything else — per-request serialization, per-request compression, live totals, no memoization, no canned bytes — is the same code path any Orisha app gets.
The board’s maintainer has invited a run on the arena itself — the real court: 64 cores, kTLS-capable kernel, connection storms, the first measurement of this stack that isn’t ours. A formal PR to upstream remains staged in the meantime (and, in the spirit of the rest of this post, one honest wrinkle: GitHub currently refuses to create any fork from our account — including a fork of octocat/Hello-World — so the pull-request mechanics are blocked on the platform side regardless).
So the entry sits exactly where it should: proven correct, its open questions named, held until the code — not the post — answers them. Whatever the board eventually says, it will be real — which is the entire reason to submit.
Ground truth: orisha/benchmarks/twobox/results/ha-2026-10-12T19-12-22Z, orisha@9530fed, koru-libs@ff5cb55, korulang/HttpArena@submission/orisha. Companion piece: Orisha io_uring TLS: Who Owns the Socket?. Earlier entries in the investigation: Getting on the Board, Compilation Is Configuration, Which Binary Is Running, The Codec Moved Home.*