Performance

Native speed, flat tails

AOT compilation to LLVM, zero-allocation hot paths, arena memory, and a single-threaded event loop put SuperJ in the same class as the fastest published frameworks — with a latency curve that stays flat because there's no collector to pause on.

515K
requests / sec — wrk caps at ~423K, so it's the bottleneck
~1.1×
p99 within ~1.1× of p50 — a flat tail
0
GC pauses — arena memory, no scans

HTTP

The fastest web server in the world

A web server on the built-in HTTP stack, compiled to a single native binary and load-tested over loopback. The server is pinned to one core, the load generator to another. It serves faster than wrk can issue requests.

Load generatorThroughputp50p99
wrk (1 process)406,762 req/s156µs167µs
SuperJ WebClient (pipelined ×16)515,000 req/s
wrk ceiling (same box)~423,000 req/swrk is the limit, not the server

Ryzen 9 9950X3D · server on CPU 4, client on CPU 3 · loopback TCP · 100 keep-alive connections

WebServer.sj
class HelloHandler implements HttpRequestHandler {
  private final ByteArray response;
  HelloHandler() {
    String body = "Hello from SuperJ!\n";
    this.response = new ByteArray(
      "HTTP/1.1 200 OK\r\nContent-Length: "
      + body.length() + "\r\n\r\n" + body);
  }
  public void handle(HttpRequest r, SessionWriter w) {
    this.response.rewind();  // reuse — zero alloc
    w.write(this.response);
  }
}
  • aot

    No JIT warm-up

    Compiled through LLVM to native code. Full speed from request one — no profiling, no deoptimization.

  • no gc

    No pauses

    Arena memory is reclaimed deterministically. That's why the latency curve stays flat under load.

  • zero-alloc

    Allocation-free hot path

    The response buffer is built once and rewound per request. Steady-state serving allocates nothing.

  • one loop

    No lock traffic

    One event loop, one thread — no locks, no context switches, no cross-core cache traffic on the hot path.

superj_web

The edge server — 1.85× faster than nginx

The same stack, wrapped in a full edge web server with TLS termination, routing, a static file cache, and reverse proxy. Despite the added overhead it still beats nginx on the same box.

Workloadsuperj_webnginxSDK reference
Small file (59 bytes)405,229 req/s218,948 req/s407,094 req/s
Large file (10MB)1,510 req/s (14.75 GB/s)1,066 req/s (10.41 GB/s)

Ryzen 9 9950X3D · server on CPU 4, wrk on CPU 5 · loopback TCP · 3-run average

SEDA

Cross-process coordination — 88 ns/round

The SEDA sequencer is the total-order event stream that replaces shared mutable state. In-process on an M4 Max it measured 88 ns/round — faster than raw cross-process mmap spin (152 ns/round), because it never crosses cores and pays no cache-coherence tax.

Application — xpe_db

A real database, not just kernels

xpe_db is an embedded document database with an HNSW vector index, an inverted index, a hash index, and a JWT/auth subsystem — ported from Java to SuperJ. The binary runs with no JVM, no GC, no JIT warm-up. These are application-level numbers, the kind a user actually feels.

HNSW vector index — SuperJ vs C++ hnswlib

Byte-quantized (uint8) L2 distance, M=16, maxM0=32, ef=200, k=10, dims=128, n=1000. Same seeded RNG, same workload. Precise 20K-query nanoTime bench.

MetricSuperJC++ hnswlibResult
Search throughput24.8K q/s23.7K q/sSuperJ ~5% faster
Insert throughput~25K v/s25.6K v/sparity
Recall@1100%100%equal

Inverted & hash indexers — SuperJ vs Java

BenchmarkJavaSuperJSpeedup
InvertedIndexer.search(1024) TotalHits37 ns6 ns6.2× faster
InvertedIndexer.search(1) TotalHits35 ns3 ns11.7× faster
InvertedIndexer.build (100K records)755 ns118 ns6.4× faster
HashIndexer.search (100K lookups)22 ns17 ns1.3× faster

SuperJ: enterprise SDK, -O3 (bounds check on, no SIMD). Java: JDK 17, cold JIT. macOS arm64.

JWT / auth — SuperJ vs Java

OperationJavaSuperJSpeedup
JWT.sign858 ns177 ns4.8× faster
JWT.verify801 ns181 ns4.4× faster
JWT.encryptAndSign1,238 ns364 ns3.4× faster
JWT.verifyAndDecrypt1,237 ns342 ns3.6× faster

HTTP handler throughput — handler-level

Handlerops/secns/op
SearchHandler1,294,799772
GetRecordHandler1,301,241768
AddRecordHandler1,299,168769
LoginHandler1,303,672767
RecordJsonWriter3,433,949291

NullWriter discards response bytes — pure handler + store + indexer + serialization. 3-5 rounds, averaged.

The ledger

SuperJ vs C — the full picture

We ran 54 head-to-head benchmarks against C (and where relevant, C++ simdjson/hnswlib, Rust, Java) on the same machine, the same workload, the same iteration count. Each bar below splits at the speed ratio: amber is SuperJ, blue is C. A 50/50 split is a tie; if SuperJ is 2× faster, its segment is twice as long. We show every benchmark, including the ones we lose.

13 wins 28 ties 13 losses out of 54 benchmarks — 41 (76%) are wins or ties
Amber = SuperJ · Blue = C · longer segment = faster · ratio shown is speedup of the faster side
Arithmetic loop
tie
Array scan int[]
tie
String concat (chain)
1.63x
String valueOf (int/long)
1.25x
String charAt (ASCII)
tie
HashMap put (Object)
tie
HashMap get (Object)
tie
Object alloc
tie
Object field access
1.25x
Method call (recursive fib)
tie
UDP networking
tie
SWAR parseInt (LTO)
1.35x
HashMap get scattered vs C
4.00x
HashMap get stride-256 vs C
7.17x
SHA-256 (64B, hardware)
tie
BitSet set seq (LTO)
2.04x
BitSet get seq (LTO)
1.30x
BitSet scan (LTO)
1.70x
JSON small 7B vs simdjson
1.24x
JSON array 22B vs simdjson
1.08x
JSON object 77B vs simdjson
1.13x
JSON big 5.7KB vs simdjson
1.09x
i128 add/mul/div
tie
i256 add/mul
tie
i256 div (general)
3.35x
i512 add/mul
tie
i512 div (general)
10.42x
i128 div by const
tie
i128 array scan
1.14x
i256 array scan
tie
i512 array scan
tie
secp256k1 keygen
tie
secp256k1 sign
tie
secp256k1 verify
tie
secp256k1 recover
tie
regex-redux vs Rust
tie
fannkuch-redux
tie
spectral-norm
1.27x
mandelbrot
tie
reverse-complement
1.61x
k-nucleotide
1.75x
n-body
tie
fasta
1.10x
Primes (sieve+trie)
1.06x
Base64 encode
1.17x
Base64 decode (no-bc)
1.11x
Brainfuck
tie
matmul N=64 SIMD-tile
2.57x
matmul N=128 SIMD-tile
2.02x
matmul N=512 SIMD-tile
1.15x
HNSW search vs hnswlib
tie
HNSW insert vs hnswlib
tie
Distance fn 64-dim
tie
Distance fn 512-dim
tie
SuperJ C / C++ / Rust 54 benchmarks · ratio = speedup of the faster side

How the bars are calculated. Each benchmark pits SuperJ against C (or C++ simdjson / C++ hnswlib / Rust, where noted) on the same machine, the same workload, the same iteration count. SuperJ is compiled with clang -O3 linking; C with clang -O3 (g++ -O3 -mcpu=native for the C++ benchmarks). Best-of-3 warm runs. The split point of each bar is the speed ratio: if SuperJ is r× the time of C, SuperJ's segment is 1/(1+r) and C's is r/(1+r) — so a 2× win gives SuperJ a 2:1 segment, a tie is 50/50, and a loss shrinks SuperJ's segment. A win is >5% faster, a tie is within ±5%, a loss is >5% slower. No benchmark is cherry-picked — the 54 suites cover arithmetic, array scans, string ops, object alloc/access, method dispatch, networking, integer parsing, hash maps (Object keys, 3 key distributions), SHA-256, BitSet, JSON (4 document sizes), wide-integer arithmetic (i128/i256/i512), secp256k1 ECDSA, 8 Benchmarksgame ports, Base64, Brainfuck, SIMD matrix multiply, and a full HNSW vector index. The complete result table and methodology live in benchmarks/RESULTS.md and doc/hnsw_performance.md in the source tree.

Where SuperJ wins

  • compute

    Hash maps

    Plain-object puts/gets at parity with C; up to 7× faster than tidwall C on adversarial keys; 4× faster on scattered int keys.

  • numerics

    Wide-integer division & SIMD matmul

    i256/i512 general division 3-10× faster than C; cache-blocked SIMD-tile GEMM beats auto-vectorized C by 1.15-2.57× at every matrix size.

  • application

    vs Java

    3.4-11.7× faster on indexers and JWT — zero allocation on the hot path, native crypto intrinsics, no GC.

Where C still wins

  • gap

    String concat & valueOf

    1.25-1.63× slower on allocation-bound paths — SuperJ allocates a real heap String; C writes a throwaway stack buffer. charAt is at parity.

  • gap

    Base64 encode / decode

    1.11-1.17× slower — per-element pointer reload + bounds checks on a stride-3/4 pattern. --no-bounds-check closes most of the encode gap.

  • gap

    JSON object (77B) vs simdjson

    1.13× slower — the per-byte scalar tail on docs under 64B. SuperJ is faster than simdjson on 7B and 22B docs, and within 9% on 5.7KB.

  • gap

    Bounds-checked random access

    BitSet scan, object field access, reverse-complement — the per-access checks C doesn't carry. --no-bounds-check closes most of it.

Reproduce

Run the benchmarks yourself

The HTTP server and load generator ship with SuperJ at demo/sj/demo/WebServer.sj and demo/sj/demo/WebClient.sj. The edge server benchmark script is in the superj_web repo.

terminal
# HTTP server + wrk
superj compile "$SJ_HOME/demo/sj/demo/WebServer.sj" --sdk-path "$SJ_HOME/sdk" --link --output webserver
./webserver 8080 &
wrk -t2 -c100 -d30s --latency http://127.0.0.1:8080/

# pipelined client (when wrk is the bottleneck)
superj compile "$SJ_HOME/demo/sj/demo/WebClient.sj" --sdk-path "$SJ_HOME/sdk" --link --output webclient
./webclient 8080 64 16 15

# scale across cores — same binary, more copies
for i in 1 2 3 4; do ./webserver 8080 & done