From a486f7fe91915e07ddb3eac9eae51a1b768ff5c1 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 6 Nov 2026 09:55:19 -0800 Subject: [PATCH 01/24] blog: draft the rind launch post with interactive benchmark charts Co-Authored-By: Claude Opus 5.5 --- public/blog/rind/cover.svg | 4 + src/components/rind/build.tsx | 136 +++ src/components/rind/chart.css | 45 + src/components/rind/chart.tsx | 208 +++++ src/components/rind/coverage.tsx | 163 ++++ src/components/rind/data/build.json | 331 +++++++ src/components/rind/data/proofs.json | 1164 ++++++++++++++++++++++++ src/components/rind/data/quic.json | 236 +++++ src/components/rind/data/runtimes.json | 67 ++ src/components/rind/data/tally.json | 26 + src/components/rind/quic.tsx | 179 ++++ src/components/rind/runtimes.tsx | 103 +++ src/components/rind/speed.tsx | 151 +++ src/components/rind/tally.tsx | 130 +++ src/pages/blog/rind.mdx | 82 ++ tools/rind/README.md | 50 + tools/rind/quic.sh | 35 + tools/rind/refresh.ts | 305 +++++++ tools/rind/runtimes.mjs | 62 ++ tools/rind/runtimes.sh | 32 + 20 files changed, 3509 insertions(+) create mode 100644 public/blog/rind/cover.svg create mode 100644 src/components/rind/build.tsx create mode 100644 src/components/rind/chart.css create mode 100644 src/components/rind/chart.tsx create mode 100644 src/components/rind/coverage.tsx create mode 100644 src/components/rind/data/build.json create mode 100644 src/components/rind/data/proofs.json create mode 100644 src/components/rind/data/quic.json create mode 100644 src/components/rind/data/runtimes.json create mode 100644 src/components/rind/data/tally.json create mode 100644 src/components/rind/quic.tsx create mode 100644 src/components/rind/runtimes.tsx create mode 100644 src/components/rind/speed.tsx create mode 100644 src/components/rind/tally.tsx create mode 100644 src/pages/blog/rind.mdx create mode 100644 tools/rind/README.md create mode 100755 tools/rind/quic.sh create mode 100644 tools/rind/refresh.ts create mode 100644 tools/rind/runtimes.mjs create mode 100755 tools/rind/runtimes.sh diff --git a/public/blog/rind/cover.svg b/public/blog/rind/cover.svg new file mode 100644 index 0000000..2cd8cb9 --- /dev/null +++ b/public/blog/rind/cover.svg @@ -0,0 +1,4 @@ + + + TODO(kixelated): cover + diff --git a/src/components/rind/build.tsx b/src/components/rind/build.tsx new file mode 100644 index 0000000..7c0e936 --- /dev/null +++ b/src/components/rind/build.tsx @@ -0,0 +1,136 @@ +// What each crypto library costs to build from scratch: time, CPU, binary size, and the tools it needs. +import { createSignal, For } from "solid-js"; +import { Chart, commit, createTip, day, Toggle } from "./chart"; +import data from "./data/build.json"; + +const METRICS = { + seconds: { name: "Build time", unit: (v: number) => `${v.toFixed(1)} s` }, + cpu_seconds: { name: "CPU time", unit: (v: number) => `${v.toFixed(1)} s` }, + bytes: { name: "Binary size", unit: (v: number) => `${(v / 1024 / 1024).toFixed(2)} MiB` }, +} as const; +type Metric = keyof typeof METRICS; + +// The crate alone, or rustls on top of it, as a QUIC stack would use it. +const SCOPES = { + crate: ["rind", "aws-lc-rs", "ring", "rustcrypto", "graviola"], + rustls: ["rustls-rind", "rustls-aws-lc-rs", "rustls-ring"], +} as const; +type Scope = keyof typeof SCOPES; + +const NAMES: Record = { rustcrypto: "RustCrypto", graviola: "Graviola" }; + +const TOOLS: Record = { cc: "C compiler", make: "make", perl: "perl", "pkg-config": "pkg-config" }; + +const W = 640; +const LABEL = 132; +const RIGHT = 180; +const ROW = 26; +const BAR = 14; + +export default function Build() { + const [host, setHost] = createSignal(data.hosts[0].name); + const [metric, setMetric] = createSignal("seconds"); + const [scope, setScope] = createSignal("crate"); + const { tip, on } = createTip(); + + const current = () => data.hosts.find((h) => h.name === host()) ?? data.hosts[0]; + const builds = () => + SCOPES[scope()].flatMap((name) => + current().builds.filter((b) => b.name === name), + ) as (typeof data.hosts)[0]["builds"]; + const max = () => Math.max(...builds().map((b) => b[metric()])); + const width = (v: number) => (v / max()) * (W - LABEL - RIGHT); + const height = () => builds().length * ROW + 4; + const name = (b: { name: string }) => { + const lib = b.name.replace("rustls-", ""); + return `${b.name.startsWith("rustls-") ? "rustls + " : ""}${NAMES[lib] ?? lib}`; + }; + const needs = (tools: string[]) => + tools.length ? `needs ${tools.map((t) => TOOLS[t] ?? t).join(", ")}` : "rustc only"; + + return ( + + h.name)} value={host()} onChange={setHost} /> + [k, v.name]))} + value={metric()} + onChange={setMetric} + /> + + + } + width={W} + height={height()} + tip={tip} + label={`${METRICS[metric()].name} to build each library on ${host()}.`} + table={{ + head: ["Build", "Build time", "CPU time", "Binary size", "Packages", "Tools"], + rows: current().builds.map((b) => [ + name(b), + METRICS.seconds.unit(b.seconds), + METRICS.cpu_seconds.unit(b.cpu_seconds), + METRICS.bytes.unit(b.bytes), + b.packages, + needs(b.tools), + ]), + }} + source={ + <> + {current().source.cpu}, {current().source.jobs} jobs, {current().source.rustc}. rind's admin/build-cost at{" "} + {commit(current().source.rind)}: the median of {current().source.runs} cold builds, empty target directory and + warm download cache, {day(current().source.date)}. A tool counts when the build fails without it. + + } + > + + {(b, i) => { + const y = i() * ROW + 4; + const rind = b.name.endsWith("rind"); + return ( + ({ + x: LABEL + width(b[metric()]), + y: y + 2, + lines: [ + `${name(b)}: ${METRICS[metric()].unit(b[metric()])}`, + `${b.packages} packages, ${needs(b.tools)}`, + ], + }))} + > + + + {name(b)} + + + + {METRICS[metric()].unit(b[metric()])} + + + {needs(b.tools)} + + + ); + }} + + + ); +} diff --git a/src/components/rind/chart.css b/src/components/rind/chart.css new file mode 100644 index 0000000..e0033dc --- /dev/null +++ b/src/components/rind/chart.css @@ -0,0 +1,45 @@ +/* The rind post's charts. The site is dark, so dark is the default; an ancestor with + data-theme="light" swaps in the light steps, for screenshots and embeds. */ +.rind-chart { + --surface: #172033; + --ink: #f1f5f9; + --ink-2: #94a3b8; + --ink-3: #64748b; + --grid: rgb(148 163 184 / 18%); + --rind: #22c55e; + --aws-lc-rs: #94a3b8; + --ring: #475569; + --other: #64748b; + --e2e: #4ade80; + --e2e-ink: #052e16; + --routine: #15803d; + --none: #475569; + --button: rgb(51 65 85 / 60%); + --button-on: #22c55e; + --button-on-ink: #052e16; + + background: var(--surface); + color: var(--ink); +} + +[data-theme="light"] .rind-chart { + --surface: #ffffff; + --ink: #0f172a; + --ink-2: #475569; + --ink-3: #64748b; + --grid: rgb(100 116 139 / 20%); + --rind: #16a34a; + --aws-lc-rs: #64748b; + --ring: #94a3b8; + --other: #64748b; + --e2e: #15803d; + --e2e-ink: #ffffff; + --routine: #22c55e; + --none: #94a3b8; + --button: #e2e8f0; + --button-on: #16a34a; + --button-on-ink: #ffffff; + + border-color: #cbd5e1; + box-shadow: 5px 5px 0 0 rgb(15 23 42 / 12%); +} diff --git a/src/components/rind/chart.tsx b/src/components/rind/chart.tsx new file mode 100644 index 0000000..cb2e7e5 --- /dev/null +++ b/src/components/rind/chart.tsx @@ -0,0 +1,208 @@ +// The frame every chart in the rind post shares: title, toggles, a hover tip, the table view, and the source. +import { type Accessor, children, createSignal, For, type JSX, Show } from "solid-js"; +import "./chart.css"; + +export interface Tip { + x: number; // in the SVG's viewBox units + y: number; + lines: string[]; +} + +// A table cell: data, not JSX, so the frame builds each element once, in render order. +export type Cell = string | number | { text: string; href: string }; + +export interface Props { + title: string; + subtitle?: string; + controls?: JSX.Element; + stats?: JSX.Element; // headline numbers, above the plot + legend?: { color: string; label: string; pattern?: boolean }[]; + width: number; // the SVG's viewBox, so the tip can be placed over it + height: number; + tip: Accessor; + label: string; // what the chart shows, for screen readers + table: { head: string[]; rows: Cell[][] }; + source: JSX.Element; + children: JSX.Element; // the SVG's contents +} + +export function Chart(props: Props) { + // Resolved once: reading a JSX prop twice (in `when`, then as children) would build it twice. + const controls = children(() => props.controls); + const stats = children(() => props.stats); + + return ( +
+
+ {props.title} + + + {props.subtitle} + + +
+ + +
{controls()}
+
+ + {stats()} + + +
+ + {(item) => ( + + + {item.label} + + )} + +
+
+ +
+ + + + + + + {props.children} + + + {(tip) => ( +
+ + {(line, i) =>
{line}
} +
+
+ )} +
+
+ +
+ Table +
+ + + + {(h) => } + + + + + {(row) => ( + + + {(cell) => ( + + )} + + + )} + + +
{h}
+ {typeof cell === "object" ? ( + + {cell.text} + + ) : ( + cell + )} +
+
+
+ +
+ {props.source} +
+
+ ); +} + +// A row of mutually exclusive buttons, the toggle above every chart. +export function Toggle(props: { + label: string; + options: readonly T[]; + value: T; + onChange: (value: T) => void; + names?: Partial>; +}) { + return ( +
+ + {props.label} + + + {(option) => ( + + )} + +
+ ); +} + +// The hover state every chart keeps: one tip at a time. +export function createTip() { + const [tip, setTip] = createSignal(); + // Spread onto a mark so the pointer and keyboard both show its tip. + const on = (make: () => Tip) => ({ + tabIndex: 0, + onPointerEnter: () => setTip(make()), + onPointerLeave: () => setTip(undefined), + onFocus: () => setTip(make()), + onBlur: () => setTip(undefined), + }); + return { tip, on }; +} + +export function time(ns: number) { + if (ns < 1_000) return `${ns.toPrecision(3)} ns`; + if (ns < 1_000_000) return `${(ns / 1_000).toPrecision(3)} µs`; + return `${(ns / 1_000_000).toPrecision(3)} ms`; +} + +export const times = (x: number) => `${x.toFixed(2)}×`; + +export const day = (iso: string) => iso.slice(0, 10); + +export const short = (sha: string) => sha.slice(0, 8); + +export const commit = (sha: string) => ( + + {short(sha)} + +); diff --git a/src/components/rind/coverage.tsx b/src/components/rind/coverage.tsx new file mode 100644 index 0000000..6ee30bb --- /dev/null +++ b/src/components/rind/coverage.tsx @@ -0,0 +1,163 @@ +// Which of rind's primitives are proven, per CPU tier: end to end in Lean, only the routine underneath, or not at all. +import { For } from "solid-js"; +import { Chart, commit, createTip, day } from "./chart"; +import json from "./data/proofs.json"; + +export const STATUS = { + e2e: { label: "proven end to end", color: "var(--e2e)" }, + routine: { label: "proven routine, unproven glue", color: "var(--routine)" }, + none: { label: "unproven", color: "var(--none)" }, +} as const; +export type Status = keyof typeof STATUS; + +export interface Cell { + status: Status; + code: string; // README's Algorithms cell: whose code runs there + proof?: string; + note?: string; + link?: string; + speedup?: { geomean: number; min: number; max: number; n: number }; // aws-lc-rs's time over rind's +} + +export const data = json as { + source: { + rind: string; + nightly: { run: string; commit: string; date: string }; + cpus: Record; + generated: string; + }; + tiers: string[]; + rows: { name: string; cells: Cell[] }[]; +}; + +// Each tier's column group: the architecture it denies features on. +const GROUPS = [ + { name: "x86_64", from: 0, to: 5 }, + { name: "aarch64", from: 5, to: 7 }, + { name: "armv7", from: 7, to: 8 }, +]; + +// The first column of each group runs every feature, so it's named by what that means. +const FIRST: Record = { 0: "all", 5: "all", 7: "NEON" }; + +const W = 640; +const LABEL = 196; +const HEAD = 44; +const ROW = 24; +const GAP = 2; +const COL = (W - LABEL) / data.tiers.length; +const H = HEAD + data.rows.length * ROW; + +export default function Coverage() { + const { tip, on } = createTip(); + + return ( + [ + row.name, + ...row.cells.map((c) => (c.link ? { text: STATUS[c.status].label, href: c.link } : STATUS[c.status].label)), + ]), + }} + source={ + <> + ✓* on Intel CPUs only. rind {commit(data.source.rind)}: README's Algorithms tables and + benchmarks/verified.toml. Generated {day(data.source.generated)}. + + } + > + + {(g) => ( + <> + + {g.name} + + + + )} + + + {(tier, i) => ( + + {FIRST[i()] ?? tier} + + )} + + + {(row, r) => { + const y = HEAD + r() * ROW; + return ( + <> + + {row.name} + + + {(cell, c) => { + const status = STATUS[cell.status]; + const x = LABEL + c() * COL; + const lines = () => [ + `${row.name}, ${data.tiers[c()]}: ${status.label}`, + `code: ${cell.code.replaceAll(" ✓", "").replaceAll(" ⚡", "")}`, + ...(cell.proof ? [`proofs: ${cell.proof}`] : []), + ...(cell.note ? [cell.note] : []), + ]; + return ( + ({ x: x + COL / 2, y, lines: lines() }))} aria-label={lines().join(". ")}> + + {cell.status === "routine" && ( + + )} + {cell.status === "e2e" && ( + + ✓{cell.note ? "*" : ""} + + )} + + ); + }} + + + ); + }} + + + ); +} diff --git a/src/components/rind/data/build.json b/src/components/rind/data/build.json new file mode 100644 index 0000000..99ea0fe --- /dev/null +++ b/src/components/rind/data/build.json @@ -0,0 +1,331 @@ +{ + "source": { + "rind": "59f50f21c3f6cac4ca317c5490cafb831cbfe0d2", + "file": "benchmarks/data/.json", + "generated": "2026-11-06" + }, + "hosts": [ + { + "name": "Arrow Lake", + "source": { + "rind": "6024d1557147b1f5dae1da700ed7c7bee38cd086", + "cpu": "Intel(R) Core(TM) Ultra 7 270K Plus", + "jobs": 12, + "rustc": "rustc 1.98.1 (48a229cea 2026-09-01)", + "cc": "gcc (GCC) 16.2.0", + "date": "2026-11-05T01:28:33+00:00", + "runs": 3 + }, + "builds": [ + { + "name": "rind", + "seconds": 3.03, + "cpu_seconds": 6.87, + "bytes": 858032, + "tools": [], + "packages": 5 + }, + { + "name": "aws-lc-rs", + "seconds": 11.72, + "cpu_seconds": 36.59, + "bytes": 2991408, + "tools": ["cc"], + "packages": 14 + }, + { + "name": "ring", + "seconds": 4.94, + "cpu_seconds": 7.27, + "bytes": 744328, + "tools": ["cc"], + "packages": 9 + }, + { + "name": "rustcrypto", + "seconds": 2.82, + "cpu_seconds": 15.71, + "bytes": 540256, + "tools": [], + "packages": 57 + }, + { + "name": "graviola", + "seconds": 1.9, + "cpu_seconds": 4.14, + "bytes": 912208, + "tools": [], + "packages": 5 + }, + { + "name": "libcrux", + "seconds": 4.73, + "cpu_seconds": 11.58, + "bytes": 745648, + "tools": [], + "packages": 27 + }, + { + "name": "openssl", + "seconds": 2.81, + "cpu_seconds": 7.37, + "bytes": 377832, + "tools": ["cc", "pkg-config"], + "packages": 18 + }, + { + "name": "openssl-vendored", + "seconds": 17.74, + "cpu_seconds": 135.59, + "bytes": 5133408, + "tools": ["cc", "make", "perl"], + "packages": 19 + }, + { + "name": "rustls-rind", + "seconds": 3.5, + "cpu_seconds": 19.13, + "bytes": 2891640, + "tools": [], + "packages": 14 + }, + { + "name": "rustls-rind-lean", + "seconds": 3.51, + "cpu_seconds": 19.16, + "bytes": 1802000, + "tools": [], + "packages": 14 + }, + { + "name": "rustls-aws-lc-rs", + "seconds": 14.43, + "cpu_seconds": 48.99, + "bytes": 4114504, + "tools": ["cc"], + "packages": 20 + }, + { + "name": "rustls-ring", + "seconds": 7.51, + "cpu_seconds": 18.96, + "bytes": 1943680, + "tools": ["cc"], + "packages": 16 + } + ] + }, + { + "name": "Zen 3", + "source": { + "rind": "6024d1557147b1f5dae1da700ed7c7bee38cd086", + "cpu": "AMD Ryzen 9 5900XT 16-Core Processor", + "jobs": 16, + "rustc": "rustc 1.98.1 (48a229cea 2026-09-01)", + "cc": "gcc (GCC) 16.2.0", + "date": "2026-11-05T01:25:40+00:00", + "runs": 3 + }, + "builds": [ + { + "name": "rind", + "seconds": 4.16, + "cpu_seconds": 9.33, + "bytes": 858032, + "tools": [], + "packages": 5 + }, + { + "name": "aws-lc-rs", + "seconds": 16.28, + "cpu_seconds": 54.21, + "bytes": 2991408, + "tools": ["cc"], + "packages": 14 + }, + { + "name": "ring", + "seconds": 6.71, + "cpu_seconds": 9.49, + "bytes": 744328, + "tools": ["cc"], + "packages": 9 + }, + { + "name": "rustcrypto", + "seconds": 4.08, + "cpu_seconds": 20.74, + "bytes": 540256, + "tools": [], + "packages": 57 + }, + { + "name": "graviola", + "seconds": 2.58, + "cpu_seconds": 5.31, + "bytes": 912208, + "tools": [], + "packages": 5 + }, + { + "name": "libcrux", + "seconds": 6.79, + "cpu_seconds": 15.86, + "bytes": 745648, + "tools": [], + "packages": 27 + }, + { + "name": "openssl", + "seconds": 3.9, + "cpu_seconds": 9.67, + "bytes": 377832, + "tools": ["cc", "pkg-config"], + "packages": 18 + }, + { + "name": "openssl-vendored", + "seconds": 22.68, + "cpu_seconds": 200.25, + "bytes": 5133408, + "tools": ["cc", "make", "perl"], + "packages": 19 + }, + { + "name": "rustls-rind", + "seconds": 4.81, + "cpu_seconds": 24.65, + "bytes": 2891640, + "tools": [], + "packages": 14 + }, + { + "name": "rustls-rind-lean", + "seconds": 4.84, + "cpu_seconds": 24.78, + "bytes": 1802000, + "tools": [], + "packages": 14 + }, + { + "name": "rustls-aws-lc-rs", + "seconds": 19.75, + "cpu_seconds": 69.79, + "bytes": 4114504, + "tools": ["cc"], + "packages": 20 + }, + { + "name": "rustls-ring", + "seconds": 10.08, + "cpu_seconds": 23.95, + "bytes": 1943680, + "tools": ["cc"], + "packages": 16 + } + ] + }, + { + "name": "Coffee Lake", + "source": { + "rind": "a6d9bd7210fc2352f8dec4aa2078230f779398eb", + "cpu": "Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz", + "jobs": 2, + "rustc": "rustc 1.98.1 (48a229cea 2026-09-01)", + "cc": "gcc (GCC) 16.2.0", + "date": "2026-11-04T17:40:33+00:00", + "runs": 3 + }, + "builds": [ + { + "name": "rind", + "seconds": 7.57, + "cpu_seconds": 11.09, + "bytes": 830568, + "tools": [], + "packages": 5 + }, + { + "name": "aws-lc-rs", + "seconds": 32.97, + "cpu_seconds": 62.39, + "bytes": 2991408, + "tools": ["cc"], + "packages": 14 + }, + { + "name": "ring", + "seconds": 10.31, + "cpu_seconds": 12.53, + "bytes": 744328, + "tools": ["cc"], + "packages": 9 + }, + { + "name": "rustcrypto", + "seconds": 11.94, + "cpu_seconds": 23.43, + "bytes": 540320, + "tools": [], + "packages": 57 + }, + { + "name": "graviola", + "seconds": 4.45, + "cpu_seconds": 6.48, + "bytes": 912208, + "tools": [], + "packages": 5 + }, + { + "name": "libcrux", + "seconds": 10.66, + "cpu_seconds": 20.1, + "bytes": 745664, + "tools": [], + "packages": 27 + }, + { + "name": "openssl", + "seconds": 7.03, + "cpu_seconds": 11.51, + "bytes": 377864, + "tools": ["cc", "pkg-config"], + "packages": 18 + }, + { + "name": "openssl-vendored", + "seconds": 127.13, + "cpu_seconds": 241.7, + "bytes": 5133408, + "tools": ["cc", "make", "perl"], + "packages": 19 + }, + { + "name": "rustls-rind", + "seconds": 16.61, + "cpu_seconds": 32.52, + "bytes": 2869472, + "tools": [], + "packages": 14 + }, + { + "name": "rustls-aws-lc-rs", + "seconds": 63.41, + "cpu_seconds": 118.13, + "bytes": 4114504, + "tools": ["cc"], + "packages": 20 + }, + { + "name": "rustls-ring", + "seconds": 22.93, + "cpu_seconds": 35.32, + "bytes": 1943680, + "tools": ["cc"], + "packages": 16 + } + ] + } + ] +} diff --git a/src/components/rind/data/proofs.json b/src/components/rind/data/proofs.json new file mode 100644 index 0000000..7cd9845 --- /dev/null +++ b/src/components/rind/data/proofs.json @@ -0,0 +1,1164 @@ +{ + "source": { + "rind": "59f50f21c3f6cac4ca317c5490cafb831cbfe0d2", + "files": ["README.md#algorithms", "benchmarks/verified.toml", "benchmarks/data/tiers.json"], + "nightly": { + "run": "https://github.com/moq-dev/rind/actions/runs/41932862737", + "commit": "33bcf0e9fcbca76c991cee74ae6be0b31408902d", + "date": "2026-11-04T23:19:58Z" + }, + "cpus": { + "x86_64": "AMD EPYC 9V74 80-Core Processor", + "no AVX2": "AMD EPYC 7763 64-Core Processor", + "no AVX": "AMD EPYC 7763 64-Core Processor", + "no AES-NI": "AMD EPYC 9V74 80-Core Processor", + "SSE2": "AMD EPYC 9V74 80-Core Processor", + "aarch64": "Neoverse-N2", + "no crypto": "Neoverse-N2", + "armv7": "Neoverse-N2" + }, + "generated": "2026-11-06" + }, + "tiers": ["x86_64", "no AVX2", "no AVX", "no AES-NI", "SSE2", "aarch64", "no crypto", "armv7"], + "rows": [ + { + "name": "AES-GCM", + "cells": [ + { + "status": "none", + "code": "BoringSSL ⚡ + Graviola", + "speedup": { + "geomean": 1.3831424055000499, + "min": 1.0683039140445125, + "max": 2.1100182982616653, + "n": 18 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + Vale (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/AesGcm.lean", + "speedup": { + "geomean": 1.3390809658343648, + "min": 0.9755550012471939, + "max": 2.7141403865717195, + "n": 18 + } + }, + { + "status": "none", + "code": "Graviola", + "speedup": { + "geomean": 1.3920953808883119, + "min": 1.0954375137756227, + "max": 2.3655134939322586, + "n": 18 + } + }, + { + "status": "none", + "code": "aws-lc + rind", + "speedup": { + "geomean": 1.0160036541154513, + "min": 0.9798601061263869, + "max": 1.066806943713835, + "n": 18 + } + }, + { + "status": "none", + "code": "aws-lc + rind", + "speedup": { + "geomean": 1.2765270163474653, + "min": 1.2123961857889878, + "max": 1.3907991463125444, + "n": 18 + } + }, + { + "status": "none", + "code": "s2n-bignum + Graviola", + "speedup": { + "geomean": 1.1764263646417765, + "min": 1.059195547685302, + "max": 1.3065512978986402, + "n": 18 + } + }, + { + "status": "none", + "code": "aws-lc + rind", + "speedup": { + "geomean": 1.0032344776510722, + "min": 0.9825935596170583, + "max": 1.0258964143426295, + "n": 18 + } + }, + { + "status": "none", + "code": "aws-lc + rind", + "speedup": { + "geomean": 1.2041600052415053, + "min": 1.006114567689337, + "max": 1.8109640831758034, + "n": 18 + } + } + ] + }, + { + "name": "AES header protection", + "cells": [ + { + "status": "none", + "code": "Graviola + aws-lc", + "speedup": { + "geomean": 1.8314549967442513, + "min": 1.7589197224975226, + "max": 1.906981519507187, + "n": 2 + } + }, + { + "status": "none", + "code": "Graviola + aws-lc", + "speedup": { + "geomean": 1.4109984377464957, + "min": 1.3478818998716302, + "max": 1.4770704996577686, + "n": 2 + } + }, + { + "status": "none", + "code": "Graviola + aws-lc", + "speedup": { + "geomean": 1.5583455366535182, + "min": 1.4534817024205575, + "max": 1.6707749451291576, + "n": 2 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 0.9548527723781625, + "min": 0.9461611374407582, + "max": 0.9636242504996668, + "n": 2 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 1.2581759727888018, + "min": 1.245939675174014, + "max": 1.2705324423367106, + "n": 2 + } + }, + { + "status": "none", + "code": "Graviola + rind", + "speedup": { + "geomean": 1.0359516931489687, + "min": 0.9393624393624392, + "max": 1.1424726661059714, + "n": 2 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 0.9955302560775348, + "min": 0.9929947460595449, + "max": 0.9980722402597403, + "n": 2 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 0.9770296342835953, + "min": 0.970033296337403, + "max": 0.9840764331210191, + "n": 2 + } + } + ] + }, + { + "name": "ChaCha20 header protection", + "cells": [ + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.3458078445483987, + "min": 1.3458078445483987, + "max": 1.3458078445483987, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.2680456798103859, + "min": 1.2680456798103859, + "max": 1.2680456798103859, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.2920258620689655, + "min": 1.2920258620689655, + "max": 1.2920258620689655, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.320018365472911, + "min": 1.320018365472911, + "max": 1.320018365472911, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.3485974586430112, + "min": 1.3485974586430112, + "max": 1.3485974586430112, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.2139621087314663, + "min": 1.2139621087314663, + "max": 1.2139621087314663, + "n": 1 + } + }, + { + "status": "e2e", + "code": "HACL* ✓ + rind", + "proof": "rind (Lean, via Aeneas) + HACL* (F*)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/ChaCha20.lean", + "speedup": { + "geomean": 1.2151168777674801, + "min": 1.2151168777674801, + "max": 1.2151168777674801, + "n": 1 + } + }, + { + "status": "routine", + "code": "HACL* ✓ + rind", + "speedup": { + "geomean": 1.5205288796102991, + "min": 1.5205288796102991, + "max": 1.5205288796102991, + "n": 1 + } + } + ] + }, + { + "name": "ChaCha20-Poly1305", + "cells": [ + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0142104477687106, + "min": 1, + "max": 1.0559311616472036, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0122265806775135, + "min": 0.9935483870967742, + "max": 1.0628997867803838, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡" + }, + { + "status": "routine", + "code": "HACL* ✓ + aws-lc ⚡ + Graviola ⚡", + "speedup": { + "geomean": 0.974561382034103, + "min": 0.8517857142857143, + "max": 1.3613240418118466, + "n": 8 + } + }, + { + "status": "routine", + "code": "HACL* ✓ + Graviola ⚡", + "speedup": { + "geomean": 1.0377946271188476, + "min": 0.9550812360624402, + "max": 1.3786225065863758, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.007970891826104, + "min": 0.9964108227498619, + "max": 1.0515991471215351, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0057598904196727, + "min": 0.9852438857767455, + "max": 1.0508112724167378, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0665691323344613, + "min": 1.0086132644272179, + "max": 1.1904390243902438, + "n": 8 + } + } + ] + }, + { + "name": "SHA-256", + "cells": [ + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.1027870518341802, + "min": 1.0022031284423882, + "max": 1.2327252625760088, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0681160312368148, + "min": 0.9893162393162394, + "max": 1.2863260936066074, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0540121375868348, + "min": 0.9906266738082485, + "max": 1.1754478398314014, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡" + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0423937846278801, + "min": 0.9997638724911452, + "max": 1.1391526661796931, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.213259703448341, + "min": 1.004076265614727, + "max": 1.6660649819494584, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 1.0999141985864, + "min": 1.0005485463521668, + "max": 1.445978878960195, + "n": 8 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.241953395265951, + "min": 1.004797095812265, + "max": 1.9594078319006685, + "n": 8 + } + } + ] + }, + { + "name": "SHA-384, SHA-512", + "cells": [ + { + "status": "none", + "code": "OpenSSL ⚡", + "speedup": { + "geomean": 1.0207661347341526, + "min": 1.000735294117647, + "max": 1.1004098360655739, + "n": 12 + } + }, + { + "status": "e2e", + "code": "aws-lc ⚡", + "proof": "rind (Lean, via Aeneas) + aws-lc (SAW)", + "note": "Intel CPUs only", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/Sha512.lean", + "speedup": { + "geomean": 1.067200804887543, + "min": 1.005621693121693, + "max": 1.2187388671179196, + "n": 12 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0606072165533762, + "min": 0.9967845659163987, + "max": 1.2070187876639489, + "n": 12 + } + }, + { + "status": "none", + "code": "aws-lc ⚡" + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0207698932262075, + "min": 0.9992652461425422, + "max": 1.0877335499593828, + "n": 12 + } + }, + { + "status": "none", + "code": "aws-lc", + "speedup": { + "geomean": 1.0943010489630756, + "min": 1.0032076984763432, + "max": 1.2896825396825398, + "n": 12 + } + }, + { + "status": "e2e", + "code": "aws-lc ✓", + "proof": "rind (Lean, via Aeneas) + aws-lc (NSym)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/Sha512.lean", + "speedup": { + "geomean": 1.0630327332804372, + "min": 1.002169197396963, + "max": 1.1963249516441006, + "n": 12 + } + }, + { + "status": "none", + "code": "aws-lc ⚡", + "speedup": { + "geomean": 1.0214621665797168, + "min": 1.000163052339801, + "max": 1.0727571115973742, + "n": 12 + } + } + ] + }, + { + "name": "HMAC", + "cells": [ + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.2585915273448753, + "min": 1.0028776978417266, + "max": 2.1385041551246537, + "n": 36 + } + }, + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.1793581298183353, + "min": 0.9768185451638689, + "max": 1.860112359550562, + "n": 36 + } + }, + { + "status": "none", + "code": "Graviola ⚡" + }, + { + "status": "none", + "code": "Graviola ⚡" + }, + { + "status": "none", + "code": "Graviola ⚡" + }, + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.3045427462570673, + "min": 1.00524148259079, + "max": 2.314059500959693, + "n": 36 + } + }, + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.1252267433483598, + "min": 1.0035519125683061, + "max": 1.6063771302913687, + "n": 36 + } + }, + { + "status": "none", + "code": "Graviola ⚡", + "speedup": { + "geomean": 1.4648099842421218, + "min": 1.0045314775853698, + "max": 3.813383600377003, + "n": 36 + } + } + ] + }, + { + "name": "HKDF", + "cells": [ + { + "status": "none", + "code": "rind ⚡", + "speedup": { + "geomean": 1.5642697043129028, + "min": 1.1117710583153348, + "max": 2.1434528773978316, + "n": 12 + } + }, + { + "status": "none", + "code": "rind ⚡", + "speedup": { + "geomean": 1.6609281289039093, + "min": 1.1724301279014684, + "max": 2.15108293713682, + "n": 12 + } + }, + { + "status": "none", + "code": "rind ⚡" + }, + { + "status": "none", + "code": "rind ⚡" + }, + { + "status": "none", + "code": "rind ⚡" + }, + { + "status": "none", + "code": "rind ⚡", + "speedup": { + "geomean": 1.6810224312267028, + "min": 1.1914893617021276, + "max": 2.2027272727272726, + "n": 12 + } + }, + { + "status": "none", + "code": "rind ⚡", + "speedup": { + "geomean": 1.5259251491681158, + "min": 1.120099564405725, + "max": 2.0852554744525547, + "n": 12 + } + }, + { + "status": "none", + "code": "rind ⚡", + "speedup": { + "geomean": 1.7173489345806972, + "min": 1.0432999088422972, + "max": 2.6724137931034484, + "n": 12 + } + } + ] + }, + { + "name": "X25519", + "cells": [ + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean", + "speedup": { + "geomean": 0.9957629807570111, + "min": 0.9859890459814037, + "max": 1.0056338028169014, + "n": 2 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean", + "speedup": { + "geomean": 1.0079578702244927, + "min": 1.007166649843545, + "max": 1.0087497121805205, + "n": 2 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean" + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean" + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean" + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean", + "speedup": { + "geomean": 1.2749457689018222, + "min": 1.238788584740827, + "max": 1.3121582921114294, + "n": 2 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/X25519.lean", + "speedup": { + "geomean": 1.3138338367395348, + "min": 1.2524045467793645, + "max": 1.3782761768157517, + "n": 2 + } + }, + { + "status": "none", + "code": "BoringSSL ⚡ + curve25519-dalek ⚡", + "speedup": { + "geomean": 1.7055000140983245, + "min": 1.44136460554371, + "max": 2.0180392156862745, + "n": 2 + } + } + ] + }, + { + "name": "Ed25519", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola", + "speedup": { + "geomean": 1.0094620855835914, + "min": 0.9751420454545454, + "max": 1.0577982827117776, + "n": 3 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓ + Graviola", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light) + aws-lc (SAW)", + "note": "Intel CPUs only", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/Ed25519.lean", + "speedup": { + "geomean": 1.008118590737687, + "min": 0.9777571825764597, + "max": 1.045771144278607, + "n": 3 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola", + "speedup": { + "geomean": 1.3338480779856956, + "min": 1.2465769734492202, + "max": 1.3937916880451513, + "n": 3 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓ + Graviola", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light) + aws-lc (NSym)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/X86_64/Ed25519.lean", + "speedup": { + "geomean": 1.3529826013563702, + "min": 1.2945590994371483, + "max": 1.397175866495507, + "n": 3 + } + }, + { + "status": "none", + "code": "curve25519-dalek ⚡ + rind ⚡", + "speedup": { + "geomean": 1.4698527202834502, + "min": 1.4020172910662825, + "max": 1.5687811831789022, + "n": 3 + } + } + ] + }, + { + "name": "P-256", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.0668458742387028, + "min": 0.9801053247513165, + "max": 1.260940032414911, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.0128972528302154, + "min": 0.9395506946497192, + "max": 1.053125, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡" + }, + { + "status": "e2e", + "code": "s2n-bignum ✓ + Graviola", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/Aarch64/P256.lean", + "speedup": { + "geomean": 1.1029240514980767, + "min": 0.9767870302137067, + "max": 1.2960100647016535, + "n": 5 + } + }, + { + "status": "e2e", + "code": "s2n-bignum ✓ + Graviola", + "proof": "rind (Lean, via Aeneas) + s2n-bignum (HOL Light)", + "link": "https://github.com/moq-dev/rind/blob/main/proofs/Rind/Aarch64/P256.lean", + "speedup": { + "geomean": 1.083204581406689, + "min": 0.9777040722314354, + "max": 1.3092672413793103, + "n": 5 + } + }, + { + "status": "routine", + "code": "fiat-crypto ✓ + rind ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.87863892980044, + "min": 1.4324544713237293, + "max": 2.545962641564936, + "n": 5 + } + } + ] + }, + { + "name": "P-384", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.3497631633975444, + "min": 1.1005291005291005, + "max": 1.8312444046553267, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.320819830163345, + "min": 1.0883761427949499, + "max": 1.7459505541346974, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.5330779851499305, + "min": 1.2826310380267214, + "max": 2.032168726033234, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.5262509355680987, + "min": 1.2841726618705036, + "max": 1.9524780746481747, + "n": 5 + } + }, + { + "status": "routine", + "code": "fiat-crypto ✓ + rind ⚡", + "speedup": { + "geomean": 2.587050256839671, + "min": 2.2222222222222223, + "max": 3.145669291338583, + "n": 5 + } + } + ] + }, + { + "name": "P-521", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡", + "speedup": { + "geomean": 1.4188847903775397, + "min": 1.0760719225449515, + "max": 2.201933404940924, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡", + "speedup": { + "geomean": 1.3370351343964917, + "min": 1.0597156398104266, + "max": 2.0077220077220077, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡", + "speedup": { + "geomean": 1.8753316105743545, + "min": 1.5404201145767027, + "max": 2.6201039421979972, + "n": 5 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + rind ⚡", + "speedup": { + "geomean": 1.8767257745389143, + "min": 1.5469595670168736, + "max": 2.5558008339465292, + "n": 5 + } + }, + { + "status": "routine", + "code": "fiat-crypto ✓ + rind ⚡", + "speedup": { + "geomean": 1.2158772352691984, + "min": 0.9249836921069797, + "max": 1.5541306292872055, + "n": 5 + } + } + ] + }, + { + "name": "ML-KEM-768", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + mlkem-native ✓ + Graviola ⚡", + "speedup": { + "geomean": 1.5716245190372289, + "min": 1.3485369155472249, + "max": 2.072946733140908, + "n": 4 + } + }, + { + "status": "routine", + "code": "mlkem-native ⚡ + s2n-bignum ✓", + "speedup": { + "geomean": 1.29826077549949, + "min": 1.1129523165370308, + "max": 1.7425044091710757, + "n": 4 + } + }, + { + "status": "routine", + "code": "mlkem-native ⚡ + s2n-bignum ✓" + }, + { + "status": "routine", + "code": "mlkem-native ⚡ + s2n-bignum ✓" + }, + { + "status": "routine", + "code": "mlkem-native ⚡ + s2n-bignum ✓" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola ⚡", + "speedup": { + "geomean": 1.2536204782642164, + "min": 1.0112474437627812, + "max": 1.9242902208201893, + "n": 4 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + Graviola ⚡", + "speedup": { + "geomean": 1.386254205404602, + "min": 1.1463258785942492, + "max": 2.0344966713738146, + "n": 4 + } + }, + { + "status": "none", + "code": "mlkem-native ⚡ + OpenSSL ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.82268399213484, + "min": 1.4676296554124608, + "max": 2.79126213592233, + "n": 4 + } + } + ] + }, + { + "name": "RSA", + "cells": [ + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + rind ⚡", + "speedup": { + "geomean": 1.183663079347063, + "min": 0.9583333333333334, + "max": 2.498773507767784, + "n": 19 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡", + "speedup": { + "geomean": 1.152204085174512, + "min": 0.9730684326710817, + "max": 2.550426424800325, + "n": 19 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡" + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.682234849418117, + "min": 1.0123796423658873, + "max": 2.312819717873198, + "n": 19 + } + }, + { + "status": "routine", + "code": "s2n-bignum ✓ + aws-lc ⚡ + Graviola ⚡", + "speedup": { + "geomean": 1.682342768270677, + "min": 1.0286312849162011, + "max": 2.3265940902021773, + "n": 19 + } + }, + { + "status": "none", + "code": "aws-lc ⚡ + Graviola ⚡ + rind ⚡", + "speedup": { + "geomean": 1.1127030107927112, + "min": 0.9812332439678284, + "max": 1.6822549647661755, + "n": 19 + } + } + ] + } + ] +} diff --git a/src/components/rind/data/quic.json b/src/components/rind/data/quic.json new file mode 100644 index 0000000..cf7c34b --- /dev/null +++ b/src/components/rind/data/quic.json @@ -0,0 +1,236 @@ +{ + "hosts": [ + { + "name": "Arrow Lake", + "source": { + "rind": "59f50f21c3f6cac4ca317c5490cafb831cbfe0d2", + "aws-lc-rs": "1.19.0", + "ring": "0.17.14", + "rustls": "0.23.46", + "cpu": "Intel(R) Core(TM) Ultra 7 270K Plus", + "core": 2, + "date": "2026-11-06T17:41:16Z", + "load": [40.79, 49.02], + "logical_cpus": 24, + "note": "Taken on a busy host (load 41 to 49 on 24 CPUs); rerun on an idle one before posting.", + "rounds": 3 + }, + "rows": [ + { + "op": "seal", + "size": "quic-32B", + "ns": { + "rind": 23.247240327336268, + "aws-lc-rs": 39.026080046192796, + "ring": 43.544214008226795 + } + }, + { + "op": "open", + "size": "quic-32B", + "ns": { + "rind": 24.65895288712207, + "aws-lc-rs": 37.24157147972069, + "ring": 50.30580336189334 + } + }, + { + "op": "seal", + "size": "quic-64B", + "ns": { + "rind": 23.58486743707815, + "aws-lc-rs": 40.348890800274965, + "ring": 43.507056515017844 + } + }, + { + "op": "open", + "size": "quic-64B", + "ns": { + "rind": 24.00380318997295, + "aws-lc-rs": 38.40774247505769, + "ring": 50.915574989609425 + } + }, + { + "op": "seal", + "size": "1200B", + "ns": { + "rind": 74.39898659512589, + "aws-lc-rs": 131.67798819190904, + "ring": 95.75035156139273 + } + }, + { + "op": "open", + "size": "1200B", + "ns": { + "rind": 83.7983452600936, + "aws-lc-rs": 126.25587597002055, + "ring": 94.75548092467714 + } + }, + { + "op": "seal", + "size": "1350B", + "ns": { + "rind": 95.31657203685698, + "aws-lc-rs": 151.5938506530949, + "ring": 122.19264131836516 + } + }, + { + "op": "open", + "size": "1350B", + "ns": { + "rind": 102.01705611469238, + "aws-lc-rs": 147.8234004683782, + "ring": 114.49396125471486 + } + }, + { + "op": "header protection", + "size": "", + "ns": { + "rind": 2.0939268519146066, + "aws-lc-rs": 3.321690496035427, + "ring": 3.520426149140724 + } + }, + { + "op": "handshake", + "size": "X25519", + "ns": { + "rind": 155861.25663144674, + "aws-lc-rs": 210576.8056016945, + "ring": 230608.3891857544 + } + }, + { + "op": "handshake", + "size": "X25519MLKEM768", + "ns": { + "rind": 177379.02644229349, + "aws-lc-rs": 241996.73128220285 + } + } + ] + }, + { + "name": "Zen 3", + "source": { + "rind": "59f50f21c3f6cac4ca317c5490cafb831cbfe0d2", + "aws-lc-rs": "1.19.0", + "ring": "0.17.14", + "rustls": "0.23.46", + "cpu": "AMD Ryzen 9 5900XT 16-Core Processor", + "core": 7, + "date": "2026-11-06T17:49:21Z", + "load": [11.72, 13.67], + "logical_cpus": 32, + "note": "Taken on a busy host (load 12 to 14 on 32 CPUs); rerun on an idle one before posting.", + "rounds": 3 + }, + "rows": [ + { + "op": "seal", + "size": "quic-32B", + "ns": { + "rind": 42.80988414014218, + "aws-lc-rs": 85.15082873067641, + "ring": 64.49702400662423 + } + }, + { + "op": "open", + "size": "quic-32B", + "ns": { + "rind": 43.28194940712667, + "aws-lc-rs": 93.6920283425936, + "ring": 72.6114597597442 + } + }, + { + "op": "seal", + "size": "quic-64B", + "ns": { + "rind": 49.40791294545779, + "aws-lc-rs": 91.89312924345442, + "ring": 65.49030161182904 + } + }, + { + "op": "open", + "size": "quic-64B", + "ns": { + "rind": 53.65318317901977, + "aws-lc-rs": 97.0349889422959, + "ring": 72.10041026197045 + } + }, + { + "op": "seal", + "size": "1200B", + "ns": { + "rind": 150.97789410052476, + "aws-lc-rs": 294.9178358838434, + "ring": 168.89385168699383 + } + }, + { + "op": "open", + "size": "1200B", + "ns": { + "rind": 161.80385069500375, + "aws-lc-rs": 279.6363896970768, + "ring": 177.69737228603324 + } + }, + { + "op": "seal", + "size": "1350B", + "ns": { + "rind": 188.46480217942127, + "aws-lc-rs": 335.40566644514985, + "ring": 227.26328167188356 + } + }, + { + "op": "open", + "size": "1350B", + "ns": { + "rind": 209.62296591450774, + "aws-lc-rs": 335.81756470216914, + "ring": 202.81650593317494 + } + }, + { + "op": "header protection", + "size": "", + "ns": { + "rind": 4.681006246400369, + "aws-lc-rs": 7.133592364593619, + "ring": 7.280876897880118 + } + }, + { + "op": "handshake", + "size": "X25519", + "ns": { + "rind": 233177.11327126742, + "aws-lc-rs": 300801.71163390967, + "ring": 353416.10810255655 + } + }, + { + "op": "handshake", + "size": "X25519MLKEM768", + "ns": { + "rind": 253627.1641672824, + "aws-lc-rs": 328944.1675676075 + } + } + ] + } + ] +} diff --git a/src/components/rind/data/runtimes.json b/src/components/rind/data/runtimes.json new file mode 100644 index 0000000..9e53736 --- /dev/null +++ b/src/components/rind/data/runtimes.json @@ -0,0 +1,67 @@ +{ + "source": { + "host": "local smoke test, replace", + "cpu": "TODO", + "method": "taskset -c 7, three alternating rounds, each cell their median", + "note": "", + "generated": "2026-11-06" + }, + "runs": [ + { + "runtime": "Node", + "version": "node v24.21.0", + "us": { + "Ed25519 sign": 50.88, + "Ed25519 verify": 139.66, + "X25519 deriveBits": 57.29, + "P-256 sign": 81.06, + "P-256 verify": 127.36, + "P-384 sign": 335.68, + "P-384 verify": 548.6, + "P-384 importKey pkcs8": 245.56 + } + }, + { + "runtime": "Deno", + "version": "deno 2.9.7", + "us": { + "Ed25519 sign": 29.14, + "Ed25519 verify": 45.31, + "X25519 deriveBits": 79.6, + "P-256 sign": 674.99, + "P-256 verify": 817.66, + "P-384 sign": 1659.05, + "P-384 verify": 2126.06, + "P-384 importKey pkcs8": 13.17 + } + }, + { + "runtime": "Bun", + "version": "bun 1.4.2", + "us": { + "Ed25519 sign": 24.19, + "Ed25519 verify": 63.59, + "X25519 deriveBits": 44.27, + "P-256 sign": 40.52, + "P-256 verify": 65.41, + "P-384 sign": 512.19, + "P-384 verify": 484.79, + "P-384 importKey pkcs8": 715.14 + } + }, + { + "runtime": "Bun on rind", + "version": "TODO", + "us": { + "Ed25519 sign": 17, + "Ed25519 verify": 40, + "X25519 deriveBits": 44, + "P-256 sign": 40, + "P-256 verify": 65, + "P-384 sign": 47, + "P-384 verify": 172, + "P-384 importKey pkcs8": 700 + } + } + ] +} diff --git a/src/components/rind/data/tally.json b/src/components/rind/data/tally.json new file mode 100644 index 0000000..cc029d0 --- /dev/null +++ b/src/components/rind/data/tally.json @@ -0,0 +1,26 @@ +{ + "placeholder": true, + "todo": "TODO(kixelated): every number here is a placeholder. Replace them with the tally from rind's quest/m1/ai-usage (https://github.com/moq-dev/rind/pull/400) and set placeholder to false.", + "source": { + "quest": "https://github.com/moq-dev/rind/blob/main/quest/m1/ai-usage.md", + "span": { "from": "2026-10-27", "to": "TODO" }, + "method": "TODO: Claude Code and Codex logs by working directory, each message counted once, priced at API list prices as of TODO; maintainer hours from prompt timestamps, gaps over 15 minutes counted as away.", + "generated": "TODO" + }, + "lines": [ + { + "name": "rind", + "tokens": { "input": 1000000, "cache_write": 10000000, "cache_read": 1000000000, "output": 10000000 }, + "cost_usd": 10000, + "agent_hours": 1000, + "maintainer_hours": 100 + }, + { + "name": "migrations", + "tokens": { "input": 100000, "cache_write": 1000000, "cache_read": 100000000, "output": 1000000 }, + "cost_usd": 1000, + "agent_hours": 100, + "maintainer_hours": 10 + } + ] +} diff --git a/src/components/rind/quic.tsx b/src/components/rind/quic.tsx new file mode 100644 index 0000000..c60a24a --- /dev/null +++ b/src/components/rind/quic.tsx @@ -0,0 +1,179 @@ +// QUIC's per-packet crypto and handshakes: rind against aws-lc-rs (MoQ's old default) and ring, per CPU. +import { createSignal, For } from "solid-js"; +import { Chart, commit, createTip, day, Toggle, time, times } from "./chart"; +import json from "./data/quic.json"; + +interface Row { + op: string; + size: string; + ns: Partial> & { rind: number; "aws-lc-rs": number }; +} +interface Host { + name: string; + source: { + rind: string; + "aws-lc-rs": string; + ring: string; + rustls: string; + cpu: string; + core: number; + date: string; + load: number[]; // the 1-minute load average before and after + logical_cpus: number; + rounds: number; + note: string; + }; + rows: Row[]; +} +const data = json as { hosts: Host[] }; + +const LIBS = ["rind", "aws-lc-rs", "ring"] as const; +const OPS = ["seal", "open", "header protection", "handshake"] as const; +type Op = (typeof OPS)[number]; + +// QUIC's sizes are whole datagrams; the two smallest are ACK-sized packets under a 13 B short header. +const SIZES: Record = { + "quic-32B": "32 B", + "quic-64B": "64 B", + "1200B": "1200 B", + "1350B": "1350 B", + "": "AES-128", + X25519: "X25519", + X25519MLKEM768: "X25519MLKEM768", +}; + +const NOTES: Record = { + seal: "AES-128-GCM, encrypting one packet", + open: "AES-128-GCM, decrypting one packet", + "header protection": "masking one packet's header", + handshake: "a full TLS 1.3 handshake through rustls, both peers", +}; + +const W = 640; +const LABEL = 112; +const RIGHT = 120; +const BAR = 9; +const STEP = BAR + 3; +const GROUP = LIBS.length * STEP + 14; + +export default function Quic() { + const [host, setHost] = createSignal(data.hosts[0].name); + const [op, setOp] = createSignal("open"); + const { tip, on } = createTip(); + + const current = () => data.hosts.find((h) => h.name === host()) ?? data.hosts[0]; + const rows = () => current().rows.filter((r) => r.op === op()); + const max = () => Math.max(...rows().flatMap((r) => LIBS.map((l) => r.ns[l] ?? 0))); + const width = (ns: number) => (ns / max()) * (W - LABEL - RIGHT); + const height = () => rows().length * GROUP + 4; + + return ( + + h.name)} value={host()} onChange={setHost} /> + + + } + legend={LIBS.map((lib) => ({ + color: `var(--${lib})`, + label: lib === "aws-lc-rs" ? "aws-lc-rs (MoQ's old default)" : lib, + }))} + width={W} + height={height()} + tip={tip} + label={`Time per ${op()} on ${host()} for rind, aws-lc-rs, and ring.`} + table={{ + head: ["Operation", "Size", ...LIBS, "rind vs aws-lc-rs"], + rows: current().rows.map((r) => [ + r.op, + SIZES[r.size], + ...LIBS.map((l) => (r.ns[l] ? time(r.ns[l]) : "none")), + times(r.ns["aws-lc-rs"] / r.ns.rind), + ]), + }} + source={ + <> + {current().source.cpu}, rind {commit(current().source.rind)}, aws-lc-rs {current().source["aws-lc-rs"]}, ring{" "} + {current().source.ring}, rustls {current().source.rustls}. rind-bench on one pinned core, the median of{" "} + {current().source.rounds} criterion rounds, {day(current().source.date)}. {current().source.note} + + } + > + + {(row, i) => { + const y = i() * GROUP + 4; + const speedup = () => row.ns["aws-lc-rs"] / row.ns.rind; + return ( + <> + + {SIZES[row.size]} + + + {(lib, j) => { + const ns = row.ns[lib]; + if (ns === undefined) + return ( + + {lib} doesn't offer it + + ); + return ( + ({ + x: LABEL + width(ns), + y: y + j() * STEP, + lines: [ + `${lib}: ${time(ns)}`, + lib === "aws-lc-rs" + ? `${SIZES[row.size]} ${op()}` + : `${times(row.ns["aws-lc-rs"] / ns)} aws-lc-rs's speed`, + ], + }))} + > + + + {lib === "rind" && ( + + {time(ns)} + + )} + + ); + }} + + = 1 ? "var(--rind)" : "var(--ink-2)"} + font-weight="700" + font-size="14" + > + {times(speedup())} + + + ); + }} + + + vs aws-lc-rs + + + ); +} diff --git a/src/components/rind/runtimes.tsx b/src/components/rind/runtimes.tsx new file mode 100644 index 0000000..8f2a80e --- /dev/null +++ b/src/components/rind/runtimes.tsx @@ -0,0 +1,103 @@ +// WebCrypto's asymmetric operations in Node, Deno, and Bun, against a prototype Bun that runs them on rind. +import { createSignal, For } from "solid-js"; +import { Chart, createTip, day, Toggle } from "./chart"; +import data from "./data/runtimes.json"; + +const OPS = Object.keys(data.runs[0].us); +const RIND = "Bun on rind"; + +const W = 640; +const LABEL = 104; +const RIGHT = 70; +const ROW = 26; +const BAR = 14; + +const us = (v: number) => (v < 1000 ? `${v.toPrecision(3)} µs` : `${(v / 1000).toPrecision(3)} ms`); + +export default function Runtimes() { + const [op, setOp] = createSignal(OPS.includes("P-384 sign") ? "P-384 sign" : OPS[0]); + const { tip, on } = createTip(); + + const max = () => Math.max(...data.runs.map((r) => r.us[op() as keyof typeof r.us])); + const width = (v: number) => (v / max()) * (W - LABEL - RIGHT); + const value = (run: (typeof data.runs)[number]) => run.us[op() as keyof typeof run.us]; + // Bun on rind's speed over stock Bun's, the same Bun commit without rind. + const gain = (run: (typeof data.runs)[number]) => { + const bun = data.runs.find((r) => r.runtime === "Bun"); + return run.runtime === RIND && bun ? value(bun) / value(run) : undefined; + }; + const H = data.runs.length * ROW + 4; + + return ( + } + width={W} + height={H} + tip={tip} + label={`Time per WebCrypto ${op()} in Node, Deno, Bun, and Bun on rind.`} + table={{ + head: ["Operation", ...data.runs.map((r) => r.runtime)], + rows: OPS.map((o) => [o, ...data.runs.map((r) => us(r.us[o as keyof typeof r.us]))]), + }} + source={ + <> + {data.source.cpu}, one pinned core, the median of {data.source.rounds} alternating rounds,{" "} + {day(data.source.date)}. {data.runs.map((r) => `${r.runtime}: ${r.version}`).join("; ")}. Bun and Bun on rind + are release builds of Bun{" "} + + {data.source.bun.base.slice(0, 9)} + + , without and with{" "} + + the rind prototype + + , which moves Ed25519 and P-384 onto rind; X25519, P-256, and key import still run BoringSSL.{" "} + {data.source.note} + + } + > + + {(run, i) => { + const y = i() * ROW + 4; + const rind = run.runtime === RIND; + return ( + ({ + x: LABEL + width(value(run)), + y: y + 2, + lines: [ + `${run.runtime}: ${us(value(run))}`, + run.version, + ...(gain(run) ? [`${gain(run)?.toFixed(2)}× stock Bun's speed`] : []), + ], + }))} + > + + + {run.runtime} + + + + {us(value(run))} + + {gain(run) && ( + + {gain(run)?.toFixed(1)}× + + )} + + ); + }} + + + ); +} diff --git a/src/components/rind/speed.tsx b/src/components/rind/speed.tsx new file mode 100644 index 0000000..cbbc92d --- /dev/null +++ b/src/components/rind/speed.tsx @@ -0,0 +1,151 @@ +// rind's speed over aws-lc-rs's per primitive and CPU tier, colored by proof status: proofs didn't cost speed. +import { createSignal, For, Show } from "solid-js"; +import { Chart, commit, createTip, day, Toggle, times } from "./chart"; +import { data, STATUS, type Status } from "./coverage"; + +const W = 640; +const LABEL = 196; +const RIGHT = 52; +const HEAD = 22; +const ROW = 24; +const H = HEAD + data.rows.length * ROW + 18; + +// A log axis, so 2× faster and 2× slower sit the same distance from even. +const LO = 0.5; +const HI = 4; +const x = (v: number) => LABEL + ((Math.log(v) - Math.log(LO)) / (Math.log(HI) - Math.log(LO))) * (W - LABEL - RIGHT); +const TICKS = [0.5, 1, 2, 4]; + +const geomean = (xs: number[]) => Math.exp(xs.reduce((a, v) => a + Math.log(v), 0) / xs.length); + +// Every timed cell's speedup, by proof status, across every tier. +const SUMMARY = (Object.keys(STATUS) as Status[]).map((status) => { + const speedups = data.rows + .flatMap((r) => r.cells) + .flatMap((c) => (c.status === status && c.speedup ? [c.speedup.geomean] : [])); + return { status, speedup: geomean(speedups), n: speedups.length }; +}); + +export default function Speed() { + const [tier, setTier] = createSignal(data.tiers[0]); + const { tip, on } = createTip(); + const column = () => data.tiers.indexOf(tier()); + + return ( + } + legend={(Object.keys(STATUS) as Status[]).map((s) => ({ color: STATUS[s].color, label: STATUS[s].label }))} + width={W} + height={H} + tip={tip} + label={`rind's speedup over aws-lc-rs for each primitive on the ${tier()} tier, colored by how much of it is proven.`} + table={{ + head: ["Primitive", "Proof", "Speedup (geomean)", "Range", "Benchmarks"], + rows: data.rows.map((row) => { + const c = row.cells[column()]; + return [ + row.name, + STATUS[c.status].label, + c.speedup ? times(c.speedup.geomean) : "not timed", + c.speedup ? `${times(c.speedup.min)} to ${times(c.speedup.max)}` : "", + c.speedup?.n ?? "", + ]; + }), + }} + source={ + <> + Every tier: {SUMMARY.map((s) => `${STATUS[s.status].label} ${times(s.speedup)} (${s.n})`).join(", ")}. Each + dot is the geomean of the primitive's benchmarks, the whisker their range. GitHub's runners in the nightly{" "} + + bench run + {" "} + of {day(data.source.nightly.date)} at {commit(data.source.nightly.commit)}, on {data.source.cpus[tier()]} with + features denied. Proofs at {commit(data.source.rind)}. + + } + > + + {(t) => ( + <> + + + {t === 1 ? "even" : `${t}×`} + + + )} + + + ← aws-lc-rs faster + + + rind faster → + + + {(row, r) => { + const y = HEAD + r() * ROW + ROW / 2; + const cell = () => row.cells[column()]; + return ( + <> + + {row.name} + + + same code as the tier to the left, not timed + + } + > + {(s) => ( + ({ + x: x(s().geomean), + y: y - 6, + lines: [ + `${row.name}, ${tier()}: ${times(s().geomean)} aws-lc-rs's speed`, + `${s().n} benchmarks, ${times(s().min)} to ${times(s().max)}`, + STATUS[cell().status].label, + ], + }))} + > + + + + + {times(s().geomean)} + + + )} + + + ); + }} + + + ); +} diff --git a/src/components/rind/tally.tsx b/src/components/rind/tally.tsx new file mode 100644 index 0000000..4dedb25 --- /dev/null +++ b/src/components/rind/tally.tsx @@ -0,0 +1,130 @@ +// What building rind cost in AI: tokens, API-price dollars, and agent hours against the maintainer's. +import { createSignal, For, Show } from "solid-js"; +import { Chart, createTip, Toggle } from "./chart"; +import data from "./data/tally.json"; + +type Line = (typeof data.lines)[number]; + +const NAMES: Record = { rind: "rind", migrations: "migrating MoQ and Bun" }; +const TOKENS: Record = { + input: "input", + cache_write: "cache writes", + cache_read: "cache reads", + output: "output", +}; + +const count = (n: number) => + n >= 1e9 ? `${(n / 1e9).toPrecision(3)}B` : n >= 1e6 ? `${(n / 1e6).toPrecision(3)}M` : n.toLocaleString("en-US"); +const total = (line: Line) => Object.values(line.tokens).reduce((a, b) => a + b, 0); + +const W = 640; +const LABEL = 132; +const RIGHT = 80; +const ROW = 30; +const BAR = 16; +const H = 2 * ROW + 4; + +export default function Tally() { + const [name, setName] = createSignal(data.lines[0].name); + const { tip, on } = createTip(); + const line = () => data.lines.find((l) => l.name === name()) ?? data.lines[0]; + const hours = () => [ + { who: "agents", value: line().agent_hours, color: "var(--rind)" }, + { who: "maintainer", value: line().maintainer_hours, color: "var(--other)" }, + ]; + const width = (v: number) => (v / line().agent_hours) * (W - LABEL - RIGHT); + + const stats = () => [ + { + label: "tokens", + value: count(total(line())), + detail: (Object.keys(TOKENS) as (keyof Line["tokens"])[]) + .map((k) => `${count(line().tokens[k])} ${TOKENS[k]}`) + .join(", "), + }, + { label: "at API prices", value: `$${line().cost_usd.toLocaleString("en-US")}`, detail: "list price per model" }, + { label: "agent hours", value: line().agent_hours.toLocaleString("en-US"), detail: "summed wall-clock" }, + { + label: "maintainer hours", + value: line().maintainer_hours.toLocaleString("en-US"), + detail: "active at the keyboard", + }, + ]; + + return ( + + l.name)} + names={NAMES} + value={name()} + onChange={setName} + /> + + + placeholder data + + + + } + stats={ +
+ + {(s) => ( +
+
{s.value}
+
+ {s.label} +
+
+ )} +
+
+ } + width={W} + height={H} + tip={tip} + label={`Agent hours against maintainer hours for ${NAMES[name()] ?? name()}.`} + table={{ + head: ["Work", "Tokens", "API-price cost", "Agent hours", "Maintainer hours"], + rows: data.lines.map((l) => [ + NAMES[l.name] ?? l.name, + count(total(l)), + `$${l.cost_usd.toLocaleString("en-US")}`, + l.agent_hours, + l.maintainer_hours, + ]), + }} + source={ + <> + {data.placeholder ? `${data.todo} ` : ""}Method: {data.source.method} + + } + > + + {(h, i) => { + const y = i() * ROW + 4; + return ( + ({ x: LABEL + width(h.value), y, lines: [`${h.who}: ${h.value} hours`] }))}> + + + {h.who === "agents" ? "agent hours" : "maintainer hours"} + + + + {h.value.toLocaleString("en-US")} + + + ); + }} + + + {(line().agent_hours / line().maintainer_hours).toFixed(0)}× + +
+ ); +} diff --git a/src/pages/blog/rind.mdx b/src/pages/blog/rind.mdx new file mode 100644 index 0000000..16140ea --- /dev/null +++ b/src/pages/blog/rind.mdx @@ -0,0 +1,82 @@ +--- +layout: "@/layouts/global.astro" +title: "TODO(kixelated): rind" +author: kixelated +description: TODO(kixelated) a one-liner about rind. +cover: "/blog/rind/cover.svg" +date: 2026-12-01 +--- + +import Quic from "@/components/rind/quic"; +import Runtimes from "@/components/rind/runtimes"; +import Build from "@/components/rind/build"; +import Coverage from "@/components/rind/coverage"; +import Speed from "@/components/rind/speed"; +import Tally from "@/components/rind/tally"; +import Author from "@/components/author.astro"; + +# TODO(kixelated): rind +TODO(kixelated): the hook. + +TODO(kixelated): what [rind](https://github.com/moq-dev/rind) is, in one line. + +## TODO(kixelated): Why Write Our Own Crypto +TODO(kixelated): why not just use aws-lc-rs or ring. + +## TODO(kixelated): QUIC Go Brrr +TODO(kixelated): every packet MoQ sends is encrypted, so crypto is a big chunk of the CPU. + +
+ +
TODO(kixelated): caption
+
+ +TODO(kixelated): the takeaway for a relay. + +## TODO(kixelated): Moving the Needle +TODO(kixelated): we don't care about crypto in JavaScript, but rind makes a dent anyway. + +
+ +
TODO(kixelated): caption
+
+ +TODO(kixelated): what Bun on rind is, and that it's a prototype. + +## TODO(kixelated): No C Compiler Required +TODO(kixelated): build cost. + +
+ +
TODO(kixelated): caption
+
+ +## TODO(kixelated): Lean Into Lean +TODO(kixelated): the proofs, Aeneas to Lean, composed with s2n-bignum's HOL Light and Vale's F*. + +TODO(kixelated): link Anthropic's [LLMs are making formal software verification feasible at a much larger scale](https://www.anthropic.com/research/formal-verification). + +
+ +
TODO(kixelated): caption
+
+ +TODO(kixelated): proofs didn't cost speed. + +
+ +
TODO(kixelated): caption
+
+ +## TODO(kixelated): The Bill +TODO(kixelated): what it cost in tokens, hours, and dollars. + +
+ +
TODO(kixelated): caption
+
+ +## FIN +TODO(kixelated): sign-off. + + diff --git a/tools/rind/README.md b/tools/rind/README.md new file mode 100644 index 0000000..256ef09 --- /dev/null +++ b/tools/rind/README.md @@ -0,0 +1,50 @@ +# rind post data + +The charts in `src/pages/blog/rind.mdx` read `src/components/rind/data/*.json`. +Each file records its source: the rind commit, host, CPU, and date. +Refresh them all just before posting, after rind 0.2.0 and its QUIC quests land, then run `just fix`. + +## Proofs and build cost + +Both come from a rind checkout at the commit being announced, after its nightly +`bench` run and `admin/bench-report` collection: + +```bash +bun tools/rind/refresh.ts proofs # proofs.json: README's Algorithms, verified.toml, tiers.json +bun tools/rind/refresh.ts build # build.json: benchmarks/data/.json's build_cost +``` + +## QUIC + +rind's bench-report doesn't time QUIC's sizes, so `quic.sh` does, on one pinned core of an idle host. +On each host: + +```bash +tools/rind/quic.sh +``` + +Copy `` back, then: + +```bash +bun tools/rind/refresh.ts quic "Arrow Lake" /rounds/*/criterion +``` + +## Runtimes + +Release-build Bun twice from the same commit, without and with rind (the prototype branch), on an idle host. +Then, there, from this repository: + +```bash +tools/rind/runtimes.sh /build/release/bun /build/release/bun +``` + +Copy `` back, add the two Bun commits to its `source.json` under `bun`, then: + +```bash +bun tools/rind/refresh.ts runtimes /*.tsv +``` + +## AI tally + +`tally.json` is placeholder data until rind's `quest/m1/ai-usage` lands. +Copy its numbers in by hand and set `placeholder` to false. diff --git a/tools/rind/quic.sh b/tools/rind/quic.sh new file mode 100755 index 0000000..0f1adb2 --- /dev/null +++ b/tools/rind/quic.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Times the QUIC chart's rows for rind, aws-lc-rs, and ring on one pinned core, three rounds. +# Run on the measured host from a rind checkout: `tools/rind/quic.sh `. +# Then, here: `bun tools/rind/refresh.ts quic "" /rounds/*/criterion`. +set -euo pipefail +rind="$(cd "$1" && pwd)" core="$2" out="$(mkdir -p "$3" && cd "$3" && pwd)" +cd "$rind" +export CARGO_BUILD_JOBS="${JOBS:-6}" CARGO_TARGET_DIR="$rind/target" +benches=(cargo bench -p rind-bench --bench aes_gcm --bench cipher --bench rustls) +filter='^(aes128-gcm(-open)?/(rind|aws-lc-rs|ring)/(32B|1200B|1350B|quic-32B|quic-64B)|quic-header-protection/aes128/(rind|aws-lc-rs|ring)|rustls-full-handshake-group/(x25519|x25519mlkem768)/(rind|aws-lc-rs|ring))$' + +nix develop "path:$rind" --command "${benches[@]}" --no-run +load_before="$(cut -d' ' -f1 /proc/loadavg)" +for round in 1 2 3; do + rm -rf target/criterion + nix develop "path:$rind" --command taskset -c "$core" "${benches[@]}" -- \ + --noplot --warm-up-time 1 --measurement-time 3 "$filter" > "$out/bench-$round.log" 2>&1 + mkdir -p "$out/rounds/$round" && cp -r target/criterion "$out/rounds/$round/" +done + +version() { grep -A1 "^name = \"$1\"$" Cargo.lock | sed -n 's/^version = "\(.*\)"$/\1/p' | head -1; } +cat > "$out/source.json" < verified.toml + README + tiers.json +// bun tools/rind/refresh.ts build benchmarks/data/*.json build_cost +// bun tools/rind/refresh.ts quic ... quic.sh's rounds, one per criterion dir +// bun tools/rind/refresh.ts runtimes /*.tsv runtimes.sh's rounds +import { execFileSync } from "node:child_process"; +import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +const DATA = join(import.meta.dir, "../../src/components/rind/data"); + +const read = (path: string) => JSON.parse(readFileSync(path, "utf8")); +const write = (name: string, value: unknown) => { + writeFileSync(join(DATA, name), `${JSON.stringify(value, null, "\t")}\n`); + console.log(`wrote ${name}`); +}; +const today = () => new Date().toISOString().slice(0, 10); +const commit = (rind: string) => execFileSync("git", ["-C", rind, "rev-parse", "HEAD"], { encoding: "utf8" }).trim(); +const geomean = (xs: number[]) => Math.exp(xs.reduce((a, x) => a + Math.log(x), 0) / xs.length); + +// rind's CPU tiers, as bench.yml denies features and README's Algorithms tables name them. +// `features` is what rind can use on that tier, for matching verified.toml's `cpu` lists. +const X86 = ["aes", "pclmulqdq", "ssse3", "sse4.1", "avx", "movbe"]; +const TIERS = [ + { + name: "x86_64", + arch: "x86_64", + nightly: ["x86_64-unknown-linux-gnu", false, ""], + features: [...X86, "avx2", "bmi2", "adx", "sha", "vaes", "vpclmulqdq"], + }, + { name: "no AVX2", arch: "x86_64", nightly: ["x86_64-unknown-linux-gnu", true, ""], features: X86 }, + { + name: "no AVX", + arch: "x86_64", + nightly: ["x86_64-unknown-linux-gnu", true, "no-avx"], + features: X86.filter((f) => f !== "avx"), + }, + { name: "no AES-NI", arch: "x86_64", nightly: ["x86_64-unknown-linux-gnu", true, "no-aes"], features: ["ssse3"] }, + { name: "SSE2", arch: "x86_64", nightly: ["x86_64-unknown-linux-gnu", true, "no-ssse3"], features: [] }, + { + name: "aarch64", + arch: "aarch64", + nightly: ["aarch64-unknown-linux-gnu", false, ""], + features: ["aes", "pmull", "sha2", "sha3"], + }, + { name: "no crypto", arch: "aarch64", nightly: ["aarch64-unknown-linux-gnu", true, ""], features: [] }, + { name: "armv7", arch: "armv7", nightly: ["armv7-unknown-linux-gnueabihf", false, ""], features: [] }, +] as const; + +// The primitives charted, as README's Algorithms rows (by prefix) and nightly benchmark IDs +// (admin/bench-report's TIER_ROWS). +const ROWS = [ + { name: "AES-GCM", readme: "AES-GCM", pattern: /^aes(128|256)-gcm(-open)?\// }, + { name: "AES header protection", readme: "AES block", pattern: /^quic-header-protection\/aes(128|256)\// }, + { name: "ChaCha20 header protection", readme: "ChaCha20 (header", pattern: /^quic-header-protection\/chacha20\// }, + { name: "ChaCha20-Poly1305", readme: "ChaCha20-Poly1305", pattern: /^x?chacha20poly1305\// }, + { name: "SHA-256", readme: "SHA-256", pattern: /^sha256(-updates)?\// }, + { name: "SHA-384, SHA-512", readme: "SHA-384", pattern: /^sha(384|512|512-256)\// }, + { name: "HMAC", readme: "HMAC", pattern: /^hmac-/ }, + { name: "HKDF", readme: "HKDF", pattern: /^hkdf-/ }, + { name: "X25519", readme: "X25519", pattern: /^x25519-/ }, + { name: "Ed25519", readme: "Ed25519", pattern: /^ed25519-/ }, + { name: "P-256", readme: "P-256", pattern: /^p256-/ }, + { name: "P-384", readme: "P-384", pattern: /^p384-/ }, + { name: "P-521", readme: "P-521", pattern: /^p521-/ }, + { name: "ML-KEM-768", readme: "ML-KEM-768", pattern: /^mlkem768-/ }, + { name: "RSA", readme: "RSA", pattern: /^rsa\d+-(pkcs1|pss|load)/ }, +]; + +// Each tier's cell text in README's two Algorithms tables; an empty cell repeats the one to its left. +function algorithms(readme: string): Map { + const cells = new Map(); + for (const line of readme.split("\n")) { + if (!line.startsWith("| ") || line.startsWith("| Algorithm") || line.startsWith("|---")) continue; + const [name, ...rest] = line + .split("|") + .slice(1, -1) + .map((c) => c.trim()); + if (!rest.length) continue; + const filled = rest.reduce((acc, c) => { + acc.push(c || acc.at(-1) || ""); + return acc; + }, []); + cells.set(name, [...(cells.get(name) ?? []), ...filled]); + } + return cells; +} + +// Which proofs a verified.toml entry composes, in a few words. +function sources(proof: string): string { + const found = [ + [/HOL Light/, "s2n-bignum (HOL Light)"], + [/SAW/, "aws-lc (SAW)"], + [/NSym/, "aws-lc (NSym)"], + [/Vale's F\*/, "Vale (F*)"], + [/HACL\*'s rounds/, "HACL* (F*)"], + ] as const; + return ["rind (Lean, via Aeneas)", ...found.filter(([re]) => re.test(proof)).map(([, name]) => name)].join(" + "); +} + +function proofs(rind: string) { + const verified = Bun.TOML.parse(readFileSync(join(rind, "benchmarks/verified.toml"), "utf8")) as { + verified: { library: string; operations: string[]; arch?: string; cpu?: string[]; proof: string; link: string }[]; + }; + const readme = algorithms(readFileSync(join(rind, "README.md"), "utf8")); + const nightly = read(join(rind, "benchmarks/data/tiers.json")); + + const cells = ROWS.map((row) => { + const text = [...readme].find(([name]) => name.startsWith(row.readme))?.[1]; + if (!text || text.length !== TIERS.length) throw new Error(`README has no full Algorithms row for ${row.readme}`); + + return TIERS.map((tier, i) => { + const proven = verified.verified.find( + (v) => + v.library === "rind" && + v.arch === tier.arch && + v.operations.some((op) => row.pattern.test(`${op}/`)) && + (v.cpu ?? []).every((f) => + f === "intel" + ? true + : f.startsWith("!") + ? !tier.features.includes(f.slice(1) as never) + : tier.features.includes(f as never), + ), + ); + const run = nightly.tiers.find((t: { target: string; portable: boolean; tier: string }) => + [t.target, t.portable, t.tier].every((v, j) => v === tier.nightly[j]), + ); + const ratios = Object.entries(run?.benchmarks ?? {}) + .filter(([key]) => row.pattern.test(key)) + .flatMap(([, libs]) => { + const { rind: ours, "aws-lc-rs": theirs } = libs as Record; + return ours && theirs ? [theirs / ours] : []; + }); + + return { + status: proven ? "e2e" : text[i].includes("✓") ? "routine" : "none", + code: text[i], + proof: proven ? sources(proven.proof) : undefined, + note: proven?.cpu?.includes("intel") ? "Intel CPUs only" : undefined, + link: proven?.link, + // aws-lc-rs's time over rind's: above 1, rind is faster. + speedup: ratios.length + ? { geomean: geomean(ratios), min: Math.min(...ratios), max: Math.max(...ratios), n: ratios.length } + : undefined, + }; + }); + }); + + write("proofs.json", { + source: { + rind: commit(rind), + files: ["README.md#algorithms", "benchmarks/verified.toml", "benchmarks/data/tiers.json"], + nightly: { run: nightly.url, commit: nightly.commit, date: nightly.date }, + cpus: Object.fromEntries( + TIERS.map((tier) => [ + tier.name, + nightly.tiers.find((t: { target: string; portable: boolean; tier: string }) => + [t.target, t.portable, t.tier].every((v, j) => v === tier.nightly[j]), + )?.cpu, + ]), + ), + generated: today(), + }, + tiers: TIERS.map((t) => t.name), + rows: ROWS.map((r, i) => ({ name: r.name, cells: cells[i] })), + }); +} + +// rind's report hosts, named by CPU since the hostnames mean nothing outside. +const HOSTS: Record = { + desktop: "Arrow Lake", + nixos: "Zen 3", + nixos2: "Coffee Lake", +}; + +function build(rind: string) { + const hosts = Object.keys(HOSTS) + .filter((host) => existsSync(join(rind, `benchmarks/data/${host}.json`))) + .map((host) => { + const data = read(join(rind, `benchmarks/data/${host}.json`)); + const cost = data.build_cost; + if (cost.status !== "ok") throw new Error(`${host}'s build cost is ${cost.status}`); + return { + name: HOSTS[host], + source: { + rind: data.commit, + cpu: cost.host.cpu, + jobs: Number(cost.host.jobs), + rustc: cost.host.rustc, + cc: cost.host.cc, + date: cost.started, + runs: cost.runs, + }, + builds: cost.builds.map((b: Record) => ({ + name: b.name, + seconds: b.median_seconds, + cpu_seconds: b.median_cpu_seconds, + bytes: b.stripped_bytes, + tools: b.tools, + packages: (b.packages as string[]).length, + })), + }; + }); + write("build.json", { + source: { rind: commit(rind), file: "benchmarks/data/.json", generated: today() }, + hosts, + }); +} + +// criterion's estimate for each benchmark under `dir`, by its full ID. +function criterion(dir: string): Map { + const out = new Map(); + const walk = (d: string) => { + for (const entry of readdirSync(d, { withFileTypes: true })) { + const path = join(d, entry.name); + if (!entry.isDirectory()) continue; + if (entry.name === "new" && existsSync(join(path, "benchmark.json"))) { + const { full_id } = read(join(path, "benchmark.json")); + const est = read(join(path, "estimates.json")); + out.set(full_id, (est.slope ?? est.mean).point_estimate); + } else walk(path); + } + }; + walk(dir); + return out; +} + +// The QUIC chart's rows, as rind-bench IDs with `{lib}` in the library's place. +const QUIC = [ + ...["quic-32B", "quic-64B", "1200B", "1350B"].flatMap((size) => [ + { op: "seal", size, id: `aes128-gcm/{lib}/${size}` }, + { op: "open", size, id: `aes128-gcm-open/{lib}/${size}` }, + ]), + { op: "header protection", size: "", id: "quic-header-protection/aes128/{lib}" }, + { op: "handshake", size: "X25519", id: "rustls-full-handshake-group/x25519/{lib}" }, + { op: "handshake", size: "X25519MLKEM768", id: "rustls-full-handshake-group/x25519mlkem768/{lib}" }, +]; +const LIBS = ["rind", "aws-lc-rs", "ring"]; + +// `out` is quic.sh's output directory: its rounds/, the source it recorded, and its load. +function quic(host: string, out: string, rounds: string[]) { + const times = rounds.map(criterion); + const meta = read(join(out, "source.json")); + const rows = QUIC.map((row) => ({ + op: row.op, + size: row.size, + // The median of the rounds, in ns. ring has no X25519MLKEM768, so a library may be missing. + ns: Object.fromEntries( + LIBS.flatMap((lib) => { + const xs = times + .map((t) => t.get(row.id.replace("{lib}", lib))) + .filter((x) => x !== undefined) + .sort((a, b) => a - b); + if (xs.length && xs.length !== times.length) throw new Error(`${row.id} for ${lib} is missing a round`); + return xs.length ? [[lib, xs[xs.length >> 1]]] : []; + }), + ), + })); + const path = join(DATA, "quic.json"); + const existing = existsSync(path) ? read(path) : { hosts: [] }; + const hosts = existing.hosts.filter((h: { name: string }) => h.name !== host); + write("quic.json", { hosts: [...hosts, { name: host, source: { ...meta, rounds: rounds.length }, rows }] }); +} + +// runtimes.mjs prints `#