docs / benchmarks

Benchmarks index
  1. Overview
  1. Exemptions
  2. Methodology

aot

  1. Overview
  2. Components

codec

  1. Overview
  2. Base32
  3. Base64
  4. Deflate (dev.cajeta.codec)

codec / csv

  1. CSV

codec / json

  1. JSON

collection

  1. Overview
  2. ArrayList
  3. BPlusTree
  4. Cache
  5. Collectors
  6. HashMap
  7. HashSet
  8. Heap
  9. ImmutableList
  10. ImmutableMap
  11. ImmutableSet
  12. LinkedList
  13. RedBlackTree
  14. Sort

collection / ltm

  1. LtmBPlusTree

concurrent

  1. Overview
  2. AtomicInt32
  3. AtomicInt64
  4. Channel
  5. Fiber
  6. FiberLocal
  7. Lock
  8. Mutex
  9. RwLock
  10. Semaphore
  11. Tasks

gfx

  1. Overview
  2. Texture2D

hash

  1. Overview
  2. DefaultHasher
  3. Hash
  4. SipHash
  5. XXHash3

http

  1. Overview
  2. Client
  3. Server
  4. Server-sent events
  5. Start-up and footprint
  6. TLS
  7. WebSocket

io

  1. Overview
  2. Buffer

io / file

  1. Directory walk
  2. File
  3. FileInfo
  4. FileReader
  5. FileWriter
  6. Path
  7. Watcher

io / net

  1. BufferPool
  2. ByteBuffer
  3. Server
  4. TcpListener
  5. TcpStream
  6. UdpSocket

io / net / uri

  1. Uri
  2. UriBuilder

lang

  1. Overview
  2. Allocation and ownership
  3. Calls and dispatch
  4. Exceptions
  5. Guid
  6. Loops
  7. Math and SIMD
  8. Optional
  9. Pair
  10. Slice
  11. String
  12. StringBuilder
  13. Whole programs: the Benchmarks Game

lang / stream

  1. ArrayStream
  2. Stream

llm

  1. Overview
  2. Decode
  3. Kernel parity
  4. Load and first token
  5. Long context
  6. Memory
  7. Prefill

math

  1. Overview
  2. Ray
  3. Rotation
  4. Tensor
  5. Transform

math / fft

  1. Fft

math / linalg

  1. LinAlg

math / npio

  1. Npy

math / poly

  1. Poly

math / random

  1. Generator

math / stats

  1. Stats

ml

  1. Overview
  2. Classical models
  3. Neural models
  4. XGBoost

nucleo

  1. Overview
  2. Column
  3. Fuse
  4. Table
  5. Tape
  6. Transforms

primavera

  1. Overview
  2. Build time
  3. Dependency injection at scale
  4. REST endpoint
  5. Start-up and footprint
  6. Templated page

process

  1. Overview
  2. Command
  3. Process

reflect

  1. Overview
  2. Class

search

  1. Overview

search / distance

  1. Distance

search / fuzzy

  1. Matcher

search / ngram

  1. Index

time

  1. Overview
  2. Clock
  3. DateTimeFormatter
  4. Duration
  5. Instant
  6. LocalDate
  7. LocalDateTime
  8. LocalTime
  9. ZonedDateTime

toolchain

  1. Overview
  2. Binary size
  3. Build tool
  4. Compile time
  5. Debugger
  6. Notebook kernel start-up
  7. Start-up

toolchain / kernel-startup

  1. Overview
  2. Where cold-start time goes

wire

  1. Overview
  2. CompressStream
  3. Encoder
  4. SchemaEncoder

xpu

  1. Overview
  2. Device
  3. KernelBuffer
  4. KernelStream
  5. KernelTimer

xpu / mesh

  1. MeshSimplifier
Machine
no runs yet

Methodology

The rules every number on this site obeys. A row that breaks one is not published; the fidelity gate (scripts/verify-fidelity.py) checks what can be checked mechanically, and the rest is the discipline of the run.

Builds

Cajeta is built --release --opt=O3 --cpu=native, native ahead-of-time, no JIT warm-up. Each competitor is built the way its community builds for speed: C++ at -O3 -march=native, Rust with target-cpu=native in release, Go with its default optimizer, Java 25 on the JIT with warm-up iterations before the measured trials, CPython 3.12 as shipped with NumPy where the idiomatic program uses it. The exact flags are in every row.

Measurement

Every benchmark declares its warm-up and trial counts. The row records the minimum, the median, the mean and the 95th percentile of the trials in nanoseconds, the throughput in the benchmark’s own unit when it has one, allocated bytes and allocation count during the measured window, peak resident memory, and the working-set delta. A per-iteration baseline (a fresh buffer, an unavoidable allocation) is subtracted when the benchmark declares one, and the row says so.

Same work

Every language performs the same logical work on the same input: the same element count, the same dataset by checksum, the same iteration count. The competitor uses the API the page names, not a faster primitive substituted by hand. Every result is consumed through the harness sink so no optimizer can delete or constant-fold the measured work; the smoke tests grep the release IR of the loop benchmarks to confirm the loop survived.

Correctness before speed

Every benchmark has a check: a sum, a checksum, a token count, an agreement rate, a residual. A row whose check fails is failed, with the reason, and is never a bar. For the language-model rows the check is the greedy agreement rate against llama.cpp on the same prompt ids, and a throughput is read only beside its printed tokens.

Skips are data

A benchmark that cannot run on a machine records a skipped row with its reason: a missing toolchain, a dataset not fetched, an unsupported variant, a memory budget. The chart shows the skip as an empty bar with the reason; nothing is left blank.

Devices

GPU clocks are bimodal, so a device row is measured in interleaved rounds: ours, the competitor, ours, the competitor, and the median is read. A single pair is never believed. Device time comes from the KernelTimer on its stream; the timer’s own resolution and its calibrated scale against the host clock are published under xpu/KernelTimer. Every device row carries the backend and the driver as reported at run time.

Machines

Two machines, both published, one shown at a time:

PhoenixProton
CPUAMD Ryzen 9 3950X, 16 coresAMD Ryzen AI MAX+ 395, 16 cores
Memory94 GB64 GB
GPUNVIDIA GeForce RTX 4090, 24 GBAMD Radeon 8060S (gfx1151)
OSUbuntu 24.04 under WSL2; Windows 11 native for some rowsUbuntu 26.04
BackendsCUDA, Vulkan, CPUHIP, Vulkan, CPU

CPU and language rows default to Proton (native Linux, no hypervisor between the clock and the hardware). CUDA rows are from Phoenix.

A quiet box

A published run starts only when the load average is under 1, no CI runner job is active, and the GPU shows zero utilization and no compute processes. That evidence is recorded in the run’s env.csv. A CPU-only run made with --no-gpu does not probe the GPU and says so in env.csv; the load-average check still applies. Every run is announced before it starts with its start time, its estimated end and the basis of the estimate, and reported against that estimate when it ends.

Provenance

Every row carries the compiler version and flags, the competitor language, library and version, the machine, the operating system, the driver, the model where there is one, the platform that produced the row, the dataset name, size and checksum, and the run id. A row that cannot record a version is a failed row, not an unlabeled number.

Smoke rows are never published

The smoke tests run every benchmark for one warm-up and three trials to prove it builds, checks and emits a row. Those rows are tagged smoke and the publish step drops them. No smoke number reaches a chart.

Where the numbers come from

Suites and sources the workloads mirror: nativejson-benchmark for JSON, Martin Ankerl’s hashmap suite, sort-research-rs, StringZilla and simdutf for strings and Base64, SMHasher for hashing, the crossbeam-channel harness for channels, the Computer Language Benchmarks Game for whole programs, BabelStream and mixbench for devices, the h2oai db-benchmark for dataframes, TechEmpower’s shapes for HTTP, and llama.cpp’s own llama-bench and llama-batched-bench for language models.

Source: bench/METHODOLOGY.md · 4 min read · 810 words