From 4ca5fa1517cd85cb9f42c5b92bbd2f66fee69dcb Mon Sep 17 00:00:00 2001 From: Will Manning Date: Fri, 30 Oct 2026 11:02:38 -0400 Subject: [PATCH 1/3] Show hot random access on the home page's benchmark card The card's random-access panel now reads bench's hot (cached) runs instead of cold ones. The test fixture's random-access series are the hot times from the same two bench commits as before. Signed-off-by: Will Manning --- src/lib/bench-summary.ts | 6 +- tests/unit/bench-summary.test.ts | 28 +++--- tests/unit/benchmark-card.test.tsx | 12 +-- .../bench-group-random-access-nvme.json | 90 +++++++++---------- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/lib/bench-summary.ts b/src/lib/bench-summary.ts index c9870543..b656f696 100644 --- a/src/lib/bench-summary.ts +++ b/src/lib/bench-summary.ts @@ -135,19 +135,19 @@ const SOURCES: (Source | Panel)[] = [ }, GPU_PANEL, { - title: "Random access, NVMe, cold", + title: "Random access, NVMe, hot", unit: "ms", kind: "speed", better: "lower", group: "Random Access (NVMe)", - rankings: (s) => s.coldRankings, + rankings: (s) => s.hotRankings, name: (f) => f, score: "score", systems: WITH_ARROW, // Bench sums a dataset's charts ("taxi", "taxi/correlated", // "taxi/uniform") into one time per dataset. times: { - series: (f) => `${f}:cold`, + series: (f) => `${f}:hot`, bucket: (chart) => chart.split("/")[0] ?? chart } }, diff --git a/tests/unit/bench-summary.test.ts b/tests/unit/bench-summary.test.ts index 640f90e8..cf303484 100644 --- a/tests/unit/bench-summary.test.ts +++ b/tests/unit/bench-summary.test.ts @@ -30,7 +30,8 @@ const PAYLOAD = fixture("bench-groups.json") as { }; // bench.vortex.dev/api/group/{slug}?n=20 for the two time panels' groups, read // at the same time, cut down to the series the card reads (and one it does -// not) and their last five commits. +// not) and their last five commits. Random access's hot series were read on +// 30 Oct 2026, from the same two commits as the rest. const CHARTS = { Clickbench: fixture("bench-group-clickbench.json"), "Random Access (NVMe)": fixture("bench-group-random-access-nvme.json") @@ -60,18 +61,19 @@ const charts = () => >; // Each system's geomean time in ms, computed from the fixture apart from the -// site's code (Python, 29 Oct 2026): the latest time per ClickBench query, -// and per random-access dataset the sum of its charts' latest times. +// site's code (Python, 29 Oct 2026; random access, 30 Oct): the latest time +// per ClickBench query, and per random-access dataset the sum of its charts' +// latest hot times. const CLICKBENCH_MS = { Vortex: 42.24600623816063, Parquet: 65.621614465414, Lance: 578.6306006236966 }; const RANDOM_ACCESS_MS = { - Vortex: 2.222937250565255, - Parquet: 70.3627371272798, - Lance: 3.5349948744551765, - Arrow: 3.716574078241951 + Vortex: 0.7290522397517741, + Parquet: 70.61301694616571, + Lance: 2.1526584338038828, + Arrow: 3.7780608403173668 }; // The six panels from the fixtures. The ratio panels carry bench's published @@ -127,7 +129,7 @@ const PANELS: Panel[] = [ }, GPU_PANEL, { - title: "Random access, NVMe, cold", + title: "Random access, NVMe, hot", unit: "ms", kind: "speed", better: "lower", @@ -181,7 +183,7 @@ describe("panelsFrom", () => { }; for (const [i, g, key, name] of [ [2, "Clickbench", "rankings", (f: string) => `datafusion:${f}`], - [4, "Random Access (NVMe)", "coldRankings", (f: string) => f] + [4, "Random Access (NVMe)", "hotRankings", (f: string) => f] ] as const) { const rows = panels?.[i]?.rows ?? []; const ms = (s: string) => rows.find((r) => r.system === s)?.score ?? NaN; @@ -210,10 +212,8 @@ describe("panelsFrom", () => { it("drops a time that bench does not score", () => { const gs = groups(); - const cold = group(gs, "Random Access (NVMe)").summary; - cold.coldRankings = (cold.coldRankings ?? []).filter( - (r) => r.name !== "lance" - ); + const hot = group(gs, "Random Access (NVMe)").summary; + hot.hotRankings = (hot.hotRankings ?? []).filter((r) => r.name !== "lance"); assert.deepEqual(panelsFrom(gs, charts())?.[4]?.rows[2], { system: "Lance", score: null, @@ -341,7 +341,7 @@ describe("panelsFrom", () => { it("gives null when a group reports none of the systems it shows", () => { const gs = groups(); - group(gs, "Random Access (NVMe)").summary.coldRankings = []; + group(gs, "Random Access (NVMe)").summary.hotRankings = []; assert.equal(panelsFrom(gs, charts()), null); // Arrow alone is shown only where bench runs it, so a scan ranking that // listed only Arrow still counts as none. diff --git a/tests/unit/benchmark-card.test.tsx b/tests/unit/benchmark-card.test.tsx index c72aba72..2d3fae65 100644 --- a/tests/unit/benchmark-card.test.tsx +++ b/tests/unit/benchmark-card.test.tsx @@ -31,7 +31,7 @@ describe("BenchPanel", () => { it("matches renderPanel for a missing and a partial system", () => { const panel: Panel = { - title: "Random access, NVMe, cold", + title: "Random access, NVMe, hot", unit: "ms", kind: "speed", better: "lower", @@ -115,7 +115,7 @@ describe("BenchmarkCard", () => { }); // Note 34: the bars and the big figure read the same numbers, so NVMe's - // bars give the 32× above them. Note 63: time panels show geomean ms, each + // bars give the 97× above them. Note 63: time panels show geomean ms, each // value carries a short unit, and below 2× the figure is a percentage. it("ends each bar in its value and unit, keeps the full unit for screen readers and hover, and the bars agree with the figure", () => { assert.ok(PANELS); @@ -133,7 +133,7 @@ describe("BenchmarkCard", () => { ["1.58×", "1.00×", "0.53×"], ["42.25 ms", "65.62 ms", "578.63 ms"], ["17.00 ms", "65.88 ms", "N/A"], - ["2.22 ms", "3.53 ms", "3.72 ms", "70.36 ms"], + ["0.73 ms", "2.15 ms", "3.78 ms", "70.61 ms"], ["1×", "1.07×", "1.99×", "5.59×"] ].flat() ); @@ -150,11 +150,11 @@ describe("BenchmarkCard", () => { ["1.58× Parquet", "1.00× Parquet", "0.53× Parquet"], ["42.25 ms", "65.62 ms", "578.63 ms"], ["17.00 ms", "65.88 ms", "not available, no cuDF integration"], - ["2.22 ms", "3.53 ms", "3.72 ms", "70.36 ms"], + ["0.73 ms", "2.15 ms", "3.78 ms", "70.61 ms"], ["1×", "1.07×", "1.99×", "5.59×"] ].flat() ); - assert.match(html, /
/); + assert.match(html, /
/); const big = [ ...html.matchAll( /class="vx-bench-big">([^<]*)<\/span> ([^<]*) { "58% faster than Parquet", "55% faster than Parquet", "3.9× faster than Parquet", - "32× faster than Parquet", + "97× faster than Parquet", "7% larger than Parquet" ]); // Each panel's value column fits its longest value, so its bars share a scale. diff --git a/tests/unit/fixtures/bench-group-random-access-nvme.json b/tests/unit/fixtures/bench-group-random-access-nvme.json index a717acae..e1712abb 100644 --- a/tests/unit/fixtures/bench-group-random-access-nvme.json +++ b/tests/unit/fixtures/bench-group-random-access-nvme.json @@ -7,11 +7,11 @@ "display_name": "feature-vectors/correlated", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [1209291, 1236252, null, null, null], - "parquet:cold": [10781974, 10784105, null, null, null], - "lance:cold": [951081, 952246, null, null, null], - "arrow-ipc:cold": [2044002, 3730718, null, null, null], - "vortex-file-compressed:hot": [110418, 107654, null, null, null] + "vortex-file-compressed:hot": [110418, 107654, null, null, null], + "parquet:hot": [10354321, 10430477, null, null, null], + "lance:hot": [154771, 153570, null, null, null], + "arrow-ipc:hot": [2819676, 1335129, null, null, null], + "vortex-file-compressed:cold": [1209291, 1236252, null, null, null] } }, { @@ -20,11 +20,11 @@ "display_name": "feature-vectors/uniform", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [2356769, 2374317, null, null, null], - "parquet:cold": [137559887, 133802206, null, null, null], - "lance:cold": [1761123, 1810772, null, null, null], - "arrow-ipc:cold": [49861004, 35951190, null, null, null], - "vortex-file-compressed:hot": [984853, 1023791, null, null, null] + "vortex-file-compressed:hot": [984853, 1023791, null, null, null], + "parquet:hot": [137546685, 137992286, null, null, null], + "lance:hot": [982165, 998759, null, null, null], + "arrow-ipc:hot": [49583183, 52187750, null, null, null], + "vortex-file-compressed:cold": [2356769, 2374317, null, null, null] } }, { @@ -33,11 +33,11 @@ "display_name": "nested-lists/correlated", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [336783, 352557, null, null, null], - "parquet:cold": [4912625, 4893052, null, null, null], - "lance:cold": [429895, 436339, null, null, null], - "arrow-ipc:cold": [100073, 103886, null, null, null], - "vortex-file-compressed:hot": [98244, 96734, null, null, null] + "vortex-file-compressed:hot": [98244, 96734, null, null, null], + "parquet:hot": [4740464, 4832850, null, null, null], + "lance:hot": [113010, 115638, null, null, null], + "arrow-ipc:hot": [56383, 55985, null, null, null], + "vortex-file-compressed:cold": [336783, 352557, null, null, null] } }, { @@ -46,11 +46,11 @@ "display_name": "nested-lists/uniform", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [850357, 943825, null, null, null], - "parquet:cold": [38286166, 38424139, null, null, null], - "lance:cold": [1318625, 1346258, null, null, null], - "arrow-ipc:cold": [1121795, 1265763, null, null, null], - "vortex-file-compressed:hot": [423955, 421067, null, null, null] + "vortex-file-compressed:hot": [423955, 421067, null, null, null], + "parquet:hot": [39569156, 38667572, null, null, null], + "lance:hot": [841391, 848580, null, null, null], + "arrow-ipc:hot": [1081353, 1232824, null, null, null], + "vortex-file-compressed:cold": [850357, 943825, null, null, null] } }, { @@ -59,11 +59,11 @@ "display_name": "nested-structs/correlated", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [422279, 420708, null, null, null], - "parquet:cold": [9404889, 9615878, null, null, null], - "lance:cold": [490547, 484930, null, null, null], - "arrow-ipc:cold": [90364, 89430, null, null, null], - "vortex-file-compressed:hot": [120881, 120410, null, null, null] + "vortex-file-compressed:hot": [120881, 120410, null, null, null], + "parquet:hot": [9291171, 9459598, null, null, null], + "lance:hot": [216551, 214361, null, null, null], + "arrow-ipc:hot": [51535, 51409, null, null, null], + "vortex-file-compressed:cold": [422279, 420708, null, null, null] } }, { @@ -72,11 +72,11 @@ "display_name": "nested-structs/uniform", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [620198, 622078, null, null, null], - "parquet:cold": [37850813, 37834446, null, null, null], - "lance:cold": [2558267, 2439329, null, null, null], - "arrow-ipc:cold": [887290, 938270, null, null, null], - "vortex-file-compressed:hot": [265075, 250281, null, null, null] + "vortex-file-compressed:hot": [265075, 250281, null, null, null], + "parquet:hot": [37906744, 37750759, null, null, null], + "lance:hot": [2194352, 2180167, null, null, null], + "arrow-ipc:hot": [835952, 892694, null, null, null], + "vortex-file-compressed:cold": [620198, 622078, null, null, null] } }, { @@ -85,11 +85,11 @@ "display_name": "taxi", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [927198, 922392, null, null, null], - "parquet:cold": [17812885, 17913316, null, null, null], - "lance:cold": [1251132, 1249864, null, null, null], - "arrow-ipc:cold": [168412, 167889, null, null, null], - "vortex-file-compressed:hot": [222312, 218772, null, null, null] + "vortex-file-compressed:hot": [222312, 218772, null, null, null], + "parquet:hot": [17830822, 17437418, null, null, null], + "lance:hot": [432395, 439012, null, null, null], + "arrow-ipc:hot": [70759, 73165, null, null, null], + "vortex-file-compressed:cold": [927198, 922392, null, null, null] } }, { @@ -98,11 +98,11 @@ "display_name": "taxi/correlated", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [1092553, 1101477, null, null, null], - "parquet:cold": [24981353, 25099885, null, null, null], - "lance:cold": [1631034, 1631876, null, null, null], - "arrow-ipc:cold": [228782, 221890, null, null, null], - "vortex-file-compressed:hot": [257170, 252184, null, null, null] + "vortex-file-compressed:hot": [257170, 252184, null, null, null], + "parquet:hot": [25038867, 24779262, null, null, null], + "lance:hot": [659640, 632610, null, null, null], + "arrow-ipc:hot": [118273, 122058, null, null, null], + "vortex-file-compressed:cold": [1092553, 1101477, null, null, null] } }, { @@ -111,11 +111,11 @@ "display_name": "taxi/uniform", "unit_kind": "time_ns", "series": { - "vortex-file-compressed:cold": [2916577, 2978837, null, null, null], - "parquet:cold": [39590379, 39465964, null, null, null], - "lance:cold": [8003975, 7960086, null, null, null], - "arrow-ipc:cold": [3097507, 3026104, null, null, null], - "vortex-file-compressed:hot": [818962, 829891, null, null, null] + "vortex-file-compressed:hot": [818962, 829891, null, null, null], + "parquet:hot": [39484266, 39349064, null, null, null], + "lance:hot": [6889605, 6999406, null, null, null], + "arrow-ipc:hot": [2940796, 2933221, null, null, null], + "vortex-file-compressed:cold": [2916577, 2978837, null, null, null] } } ] From 8f553866253675d4ff0b47f4d0849b890ddf89d9 Mon Sep 17 00:00:00 2001 From: Will Manning Date: Fri, 30 Oct 2026 11:03:23 -0400 Subject: [PATCH 2/3] Tag Iceberg as In proposal and move LangChain after Dash0 The Ecosystem strip's Apache Iceberg tag now reads "In proposal" instead of "Coming soon". LangChain moves from after Spice AI to after Dash0. Signed-off-by: Will Manning --- src/components/logo-banner/index.tsx | 2 +- src/components/logo-banner/logos.ts | 18 +++++++++--------- tests/smoke.spec.ts | 4 ++-- tests/unit/logo-banner.test.tsx | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/logo-banner/index.tsx b/src/components/logo-banner/index.tsx index 3ce364d3..8dcf7899 100644 --- a/src/components/logo-banner/index.tsx +++ b/src/components/logo-banner/index.tsx @@ -89,7 +89,7 @@ export function LogoBanner({ className }: { className?: string | undefined }) { /> {soon && ( - Coming soon + In proposal )} diff --git a/src/components/logo-banner/logos.ts b/src/components/logo-banner/logos.ts index fe7fd82c..547e2ea2 100644 --- a/src/components/logo-banner/logos.ts +++ b/src/components/logo-banner/logos.ts @@ -24,7 +24,7 @@ export type Logo = { height: number; /** The owner's page about its Vortex use, else its homepage. */ href: string; - /** Tagged "Coming soon": its Vortex support has not shipped yet. */ + /** Tagged "In proposal": its Vortex support is proposed, not shipped. */ soon?: true; }; @@ -37,7 +37,7 @@ export type Logo = { // links to the owner's own page that mentions Vortex where there is one // (docs, a post or a list of users), else to the owner's homepage. Dash0 // links to the Vortex post by Polar Signals, which it acquired. Apache -// Iceberg is tagged "Coming soon" until its Vortex support ships. +// Iceberg is tagged "In proposal" until its Vortex support ships. export const LOGOS: readonly Logo[] = [ { name: "Microsoft", @@ -74,13 +74,6 @@ export const LOGOS: readonly Logo[] = [ height: 26, href: "https://spice.ai/blog/vortex-at-spice-ai-the-columnar-format-for-data-intensive-workloads" }, - { - name: "LangChain", - image: langchain, - width: 136, - height: 25, - href: "https://www.langchain.com/blog/introducing-smithdb" - }, { name: "DatologyAI", image: datologyai, @@ -95,6 +88,13 @@ export const LOGOS: readonly Logo[] = [ height: 23, href: "https://www.polarsignals.com/blog/posts/2025/11/25/interface-parquet-vortex" }, + { + name: "LangChain", + image: langchain, + width: 136, + height: 25, + href: "https://www.langchain.com/blog/introducing-smithdb" + }, { name: "Milvus", image: milvus, diff --git a/tests/smoke.spec.ts b/tests/smoke.spec.ts index df58c1ca..579d973d 100644 --- a/tests/smoke.spec.ts +++ b/tests/smoke.spec.ts @@ -260,7 +260,7 @@ test.describe("home bands", () => { expect(names.slice(0, 3)).toEqual(["Microsoft", "Palantir", "Spiral"]); expect(new Set(names).size).toBe(names.length); await expect( - banner.getByRole("link", { name: "Apache Iceberg Coming soon" }) + banner.getByRole("link", { name: "Apache Iceberg In proposal" }) ).toHaveClass("is-soon"); // Every mark loads. for (const img of await banner.getByRole("img").all()) @@ -1026,7 +1026,7 @@ test.describe("logo banner under reduced motion", () => { .locator('section[aria-label="Ecosystem"] .vx-logos-set') .first(); expect(await set.locator("img").count()).toBeGreaterThanOrEqual(16); - // A "Coming soon" tag hangs past its logo, so the tags are checked too. + // An "In proposal" tag hangs past its logo, so the tags are checked too. const marks = set.locator(":is(img, .vx-logos-soon)"); for (const box of await marks.evaluateAll((els) => els.map((e) => e.getBoundingClientRect().toJSON()) diff --git a/tests/unit/logo-banner.test.tsx b/tests/unit/logo-banner.test.tsx index 1ffcdbed..ca314752 100644 --- a/tests/unit/logo-banner.test.tsx +++ b/tests/unit/logo-banner.test.tsx @@ -8,16 +8,16 @@ import { LOGOS } from "@/components/logo-banner/logos"; // The logos Will cleared for public use, in the Home card's order: the // adopters and the projects that integrate Vortex. Spiral sits third, so it // is in view on load. Dash0 replaced Polar Signals, which it acquired. -// Apache Iceberg is tagged "Coming soon" until its Vortex support ships. +// Apache Iceberg is tagged "In proposal" until its Vortex support ships. const CLEARED = [ "Microsoft", "Palantir", "Spiral", "Firebolt", "Spice AI", - "LangChain", "DatologyAI", "Dash0", + "LangChain", "Milvus", "DuckDB", "Apache DataFusion", @@ -53,12 +53,12 @@ describe("LogoBanner", () => { ); }); - it("tags Apache Iceberg, and only Iceberg, as coming soon", () => { + it("tags Apache Iceberg, and only Iceberg, as in proposal", () => { const html = renderToStaticMarkup(); const first = html.split('
]*>]*alt="([^"]*)"[^>]*\/>Coming soon<\/span><\/a>/g + /]*>]*alt="([^"]*)"[^>]*\/>In proposal<\/span><\/a>/g ) ].map((m) => m[1]); assert.deepEqual(soon, ["Apache Iceberg"]); From 29cc041f0981190dd4dbe76e366c52fe71b9e52f Mon Sep 17 00:00:00 2001 From: Will Manning Date: Fri, 30 Oct 2026 11:04:13 -0400 Subject: [PATCH 3/3] bench: link the Vortex logo to vortex.dev The header's Vortex logo now links to vortex.dev instead of bench's own home page. Signed-off-by: Will Manning --- bench/web/components/Header.test.tsx | 6 ++++++ bench/web/components/Header.tsx | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bench/web/components/Header.test.tsx b/bench/web/components/Header.test.tsx index c2910e9f..4e5b7c69 100644 --- a/bench/web/components/Header.test.tsx +++ b/bench/web/components/Header.test.tsx @@ -36,6 +36,12 @@ describe('Header (server-rendered chrome)', () => { expect(matches).toHaveLength(2); }); + it('links the Vortex logo to vortex.dev', () => { + expect(html).toContain( + '', + ); + }); + it('renders the theme toggle with the v3 server-default label', () => { expect(html).toContain('data-role="theme-toggle"'); expect(html).toContain('data-next-theme="light"'); diff --git a/bench/web/components/Header.tsx b/bench/web/components/Header.tsx index 81804bce..24e17525 100644 --- a/bench/web/components/Header.tsx +++ b/bench/web/components/Header.tsx @@ -3,7 +3,6 @@ 'use client'; -import Link from 'next/link'; import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { FilterBar } from '@/components/FilterBar'; @@ -24,7 +23,8 @@ import { effectiveTheme } from '@/lib/theme'; * `server/src/html/render.rs::site_header` plus the section-18 header controls * of `chart-init.js`: the hamburger-toggled mobile nav panel (expand/collapse * all, the global filter dropdown, and the mobile-only GitHub link), the - * theme-aware logo, the desktop GitHub link, and the theme toggle. + * theme-aware logo (a link to vortex.dev), the desktop GitHub link, and the + * theme toggle. * * Expand/Collapse All writes `details.open` on every group disclosure * directly, then schedules one write of the open set into the URL fragment @@ -139,7 +139,7 @@ export function Header({ > - + - +

Vortex Benchmarks