diff --git a/.github/workflows/publish-benchmarks-website.yml b/.github/workflows/publish-benchmarks-website.yml deleted file mode 100644 index 5555dcf841a..00000000000 --- a/.github/workflows/publish-benchmarks-website.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Publish Benchmarks Website - -on: - push: - branches: [develop] - paths: - - "benchmarks-website/**" - -jobs: - publish: - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - packages: write - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - - - name: Log in to GHCR - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 - - - name: Build and push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7 - with: - context: ./benchmarks-website - platforms: linux/arm64 - push: true - tags: ghcr.io/${{ github.repository }}/benchmarks-website:latest diff --git a/REUSE.toml b/REUSE.toml index fb3079a046d..6ce32411711 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -12,11 +12,6 @@ path = "vortex-btrblocks/decision-tree.*" SPDX-FileCopyrightText = "Copyright the Vortex contributors" SPDX-License-Identifier = "CC-BY-4.0" -[[annotations]] -path = "benchmarks-website/**" -SPDX-FileCopyrightText = "Copyright the Vortex contributors" -SPDX-License-Identifier = "CC-BY-4.0" - # Golden files are licensed under CC-BY-4.0. [[annotations]] path = "**/goldenfiles/**" diff --git a/_typos.toml b/_typos.toml index d5d2a8e4db3..359b89c80fb 100644 --- a/_typos.toml +++ b/_typos.toml @@ -11,7 +11,7 @@ extend-ignore-re = [ # scripts/measurement_id_golden.json is a frozen hash-pin artifact whose vectors include # deliberate Unicode edge cases (JSON-escaped, so "café" reads as the token "caf"); its bytes # must never change, so it is excluded rather than "fixed". -extend-exclude = ["/vortex-bench/**", "/docs/references.bib", "benchmarks/**", "vortex-sqllogictest/slt/**", "encodings/fsst/src/dfa/tests.rs", "encodings/fsst/src/dfa/flat_contains.rs", "benchmarks-website/server/static/**", "benchmarks-website/server/tests/snapshots/**", "scripts/measurement_id_golden.json"] +extend-exclude = ["/vortex-bench/**", "/docs/references.bib", "benchmarks/**", "vortex-sqllogictest/slt/**", "encodings/fsst/src/dfa/tests.rs", "encodings/fsst/src/dfa/flat_contains.rs", "scripts/measurement_id_golden.json"] [type.py] extend-ignore-identifiers-re = [ diff --git a/benchmarks-website/.dockerignore b/benchmarks-website/.dockerignore deleted file mode 100644 index a21f178d3ab..00000000000 --- a/benchmarks-website/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -dist -.git diff --git a/benchmarks-website/.gitignore b/benchmarks-website/.gitignore deleted file mode 100644 index 76add878f8d..00000000000 --- a/benchmarks-website/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist \ No newline at end of file diff --git a/benchmarks-website/Dockerfile b/benchmarks-website/Dockerfile deleted file mode 100644 index 1f87a7148b5..00000000000 --- a/benchmarks-website/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM node:24-alpine AS build -WORKDIR /app -COPY package.json package-lock.json ./ -RUN npm ci -COPY . . -RUN npm run build - -FROM node:24-alpine -WORKDIR /app -COPY package.json package-lock.json ./ -RUN npm ci --omit=dev -COPY --from=build /app/dist ./dist -COPY server.js . -COPY src/config.js ./src/config.js -EXPOSE 3000 -CMD ["node", "server.js"] diff --git a/benchmarks-website/README.md b/benchmarks-website/README.md deleted file mode 100644 index 01bbcc265fd..00000000000 --- a/benchmarks-website/README.md +++ /dev/null @@ -1,119 +0,0 @@ - - -# bench.vortex.dev - -The website behind `bench.vortex.dev`. The directory currently houses **two -implementations side by side**, run together until the v3 cutover lands: - -- **v2** (top-level files: `server.js`, `src/`, `index.html`, `vite.config.js`, - `package.json`, `Dockerfile`, `docker-compose.yml`, `public/`). The Node + - React stack that has shipped to production for the life of the site. Built - and published by - [`.github/workflows/publish-benchmarks-website.yml`](../.github/workflows/publish-benchmarks-website.yml). -- **v3** (`server/` + `migrate/` + `ops/`). A single Rust binary - - [`vortex-bench-server`](server/) - that owns a DuckDB file on local disk, - serves the API, and renders the HTML. Compiles all static assets - (`chart.umd.js`, `chart-init.js`, `style.css`) into the binary so deploys - are one file plus a database. Built directly on the EC2 host by - [`ops/deploy.sh`](ops/deploy.sh) - see [`ops/README.md`](ops/README.md). - [`migrate/`](migrate/) is a one-shot tool that loads v2's S3 dataset into a - v3 DuckDB; it is throwaway and goes away after cutover. - -Live results are produced by -[`.github/workflows/bench.yml`](../.github/workflows/bench.yml) and -[`.github/workflows/sql-benchmarks.yml`](../.github/workflows/sql-benchmarks.yml), -which CI runs after every push to `develop`. Until cutover the same payload is -emitted to both stacks (v2 via the legacy `--gh-json` path appended to a public -S3 bucket; v3 via `--gh-json-v3` POSTed to `/api/ingest`). - -## v3 architecture in one paragraph - -`axum` (HTTP) + `maud` (compile-time HTML) + embedded `duckdb-rs` over a single -local DB file. Five fact tables (`query_measurements`, `compression_times`, -`compression_sizes`, `random_access_times`, `vector_search_runs`) plus a -`commits` dim table - see [`server/src/schema.rs`](server/src/schema.rs) for -the column contracts. Three HTML routes (`/`, `/chart/{slug}`, -`/group/{slug}`) and four stable JSON routes (`GET /api/groups`, -`GET /api/chart/{slug}`, `GET /api/group/{slug}`, `GET /health`), plus -versioned group shard artifacts and bearer-gated `POST /api/ingest`. The hot -website path serves precomputed, precompressed latest-100 artifacts from an -in-memory read model; pages render chart shells and hydrate groups via shard -artifacts, while full history warms in the background. See -[`server/ARCHITECTURE.md`](server/ARCHITECTURE.md). - -For the per-module crate map and the request-flow walkthrough, see the -`//!` doc on [`server/src/lib.rs`](server/src/lib.rs). The producer side of -the ingest contract lives in -[`vortex-bench/src/v3.rs`](../vortex-bench/src/v3.rs); the historical-data -side in [`migrate/src/classifier.rs`](migrate/src/classifier.rs). - -## Local dev - -```bash -# v3 server (DuckDB lives at ./bench.duckdb by default). -INGEST_BEARER_TOKEN=dev cargo run -p vortex-bench-server -# server logs: "bench server listening addr=127.0.0.1:3000 db=bench.duckdb" - -# v3 historical migrator (writes a fully populated DuckDB the server can open). -cargo run -p vortex-bench-migrate -- run --output ./bench.duckdb -``` - -Ingest fixture data via the snapshot tests' envelopes (see -[`server/tests/common/mod.rs`](server/tests/common/mod.rs)) or by hand-rolling -a JSONL file and POSTing through `scripts/post-ingest.py`. - -```bash -cargo nextest run -p vortex-bench-server -p vortex-bench-migrate -INSTA_UPDATE=auto cargo nextest run -p vortex-bench-server # update snapshots -``` - -For the v2 stack: - -```bash -cd benchmarks-website -npm install -npm run dev -``` - -## Deployment - -v3 runs as a systemd service on a single EC2 host. The full operator -runbook (first-time install, day-to-day, failure modes) is in -[`ops/README.md`](ops/README.md). Summary: - -- A `vortex-bench-deploy.timer` polls `origin/develop` every 60s. If commits - in the range touch `benchmarks-website/server/`, `benchmarks-website/migrate/`, - `Cargo.toml`, or `Cargo.lock`, it builds and atomically swaps the binary, - then verifies `/health`. Otherwise it fast-forwards the working tree and - exits silently. -- A `vortex-bench-backup.timer` fires hourly: it asks the server to write a - per-table Vortex snapshot (`schema.sql` plus one `.vortex` file per - table) via the bearer-gated `/api/admin/snapshot` endpoint, `tar czf`s the - snapshot directory into `.tar.gz`, uploads it to - `s3://vortex-benchmark-results-database/v3-backups/`, and deletes the local - copies. -- For ad-hoc reads against the live DB, `ops/inspect.sh` calls a - bearer-gated `/api/admin/sql` endpoint - no server stop required. - -The v3 server is throwaway-friendly: every request runs against the local -DuckDB file, and a fresh boot reapplies the schema DDL idempotently. The -migrator deletes the target file (and its `.wal`) before populating it, so -re-running `vortex-bench-migrate run --output ...` is safe. - -## Cutover plan (in flight) - -The work to flip `bench.vortex.dev` from v2 to v3 is tracked outside this -repo. The relevant code-side bits: - -- v3 runs alongside v2 on the same EC2 host today and is fed by CI's - dual-write `--gh-json-v3` path. -- v2 keeps shipping unchanged until DNS flips. **Do not touch the top-level - v2 files unless you are doing the cleanup PR opened post-flip.** -- The v2 cleanup PR removes everything top-level under `benchmarks-website/` - that belongs to v2 (`server.js`, `src/`, `index.html`, `vite.config.js`, - `package.json`, `package-lock.json`, `public/`, the top-level `Dockerfile`, - `docker-compose.yml`, and the `publish-benchmarks-website.yml` workflow). - The v3 tree under `server/`, `migrate/`, and `ops/` is untouched. diff --git a/benchmarks-website/docker-compose.yml b/benchmarks-website/docker-compose.yml deleted file mode 100644 index b97482a230a..00000000000 --- a/benchmarks-website/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -services: - benchmarks-website: - image: ghcr.io/vortex-data/vortex/benchmarks-website:latest - ports: - - "80:3000" - restart: unless-stopped - - vortex-bench-server: - image: ghcr.io/vortex-data/vortex/vortex-bench-server:latest - ports: - - "3001:3000" - environment: - VORTEX_BENCH_DB: "/app/data/bench.duckdb" - VORTEX_BENCH_BIND: "0.0.0.0:3000" - VORTEX_BENCH_LOG: "info,vortex_bench_server=debug" - env_file: - - /etc/vortex-bench/secrets.env - volumes: - - /opt/benchmarks-website/data:/app/data - restart: unless-stopped - - watchtower: - image: containrrr/watchtower - volumes: - - /var/run/docker.sock:/var/run/docker.sock - environment: - - WATCHTOWER_POLL_INTERVAL=60 - - WATCHTOWER_CLEANUP=true - restart: unless-stopped diff --git a/benchmarks-website/index.html b/benchmarks-website/index.html deleted file mode 100644 index e475f3ad254..00000000000 --- a/benchmarks-website/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Vortex Benchmarks - - - - - - - - - - - - - - - - - -
- - - diff --git a/benchmarks-website/package-lock.json b/benchmarks-website/package-lock.json deleted file mode 100644 index 7eb09dbfa84..00000000000 --- a/benchmarks-website/package-lock.json +++ /dev/null @@ -1,1324 +0,0 @@ -{ - "name": "vortex-benchmarks-website", - "version": "2.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "vortex-benchmarks-website", - "version": "2.0.0", - "dependencies": { - "chart.js": "^4.5.1", - "chartjs-plugin-zoom": "^2.2.0", - "downsample": "^1.4.0", - "hammerjs": "^2.0.8", - "lucide-react": "^1.11.0", - "react": "^19.2.5", - "react-chartjs-2": "^5.3.1", - "react-dom": "^19.2.5" - }, - "devDependencies": { - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "concurrently": "^10.0.0", - "vite": "^8.0.10" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@kurkle/color": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", - "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", - "license": "MIT" - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.138.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", - "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", - "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", - "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", - "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", - "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", - "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", - "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", - "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", - "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", - "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", - "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", - "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", - "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", - "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", - "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", - "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/hammerjs": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", - "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", - "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.1" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chart.js": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", - "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", - "license": "MIT", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, - "node_modules/chartjs-plugin-zoom": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-2.2.0.tgz", - "integrity": "sha512-in6kcdiTlP6npIVLMd4zXZ08PDUXC52gZ4FAy5oyjk1zX3gKarXMAof7B9eFiisf9WOC3bh2saHg+J5WtLXZeA==", - "license": "MIT", - "dependencies": { - "@types/hammerjs": "^2.0.45", - "hammerjs": "^2.0.8" - }, - "peerDependencies": { - "chart.js": ">=3.2.0" - } - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/concurrently": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.3.tgz", - "integrity": "sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "5.6.2", - "rxjs": "7.8.2", - "shell-quote": "1.8.4", - "supports-color": "10.2.2", - "tree-kill": "1.2.2", - "yargs": "18.0.0" - }, - "bin": { - "conc": "dist/bin/index.js", - "concurrently": "dist/bin/index.js" - }, - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/downsample": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/downsample/-/downsample-1.4.0.tgz", - "integrity": "sha512-teYPhUPxqwtyICt47t1mP/LjhbRV/ghuKb/LmFDbcZ0CjqFD31tn6rVLZoeCEa1xr8+f2skW8UjRiLiGIKQE4w==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lucide-react": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.23.0.tgz", - "integrity": "sha512-38BpJcD0JhFosxHApP/BYsBetLpQFRoTRzEzstM/XCc3jsAG7wqaY1lgVwxiUe3xqYE+lNxo2PkCmYwXWrwwIw==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", - "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-chartjs-2": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.1.tgz", - "integrity": "sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==", - "license": "MIT", - "peerDependencies": { - "chart.js": "^4.1.1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.7" - } - }, - "node_modules/rolldown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", - "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.138.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.4", - "@rolldown/binding-darwin-arm64": "1.1.4", - "@rolldown/binding-darwin-x64": "1.1.4", - "@rolldown/binding-freebsd-x64": "1.1.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", - "@rolldown/binding-linux-arm64-gnu": "1.1.4", - "@rolldown/binding-linux-arm64-musl": "1.1.4", - "@rolldown/binding-linux-ppc64-gnu": "1.1.4", - "@rolldown/binding-linux-s390x-gnu": "1.1.4", - "@rolldown/binding-linux-x64-gnu": "1.1.4", - "@rolldown/binding-linux-x64-musl": "1.1.4", - "@rolldown/binding-openharmony-arm64": "1.1.4", - "@rolldown/binding-wasm32-wasi": "1.1.4", - "@rolldown/binding-win32-arm64-msvc": "1.1.4", - "@rolldown/binding-win32-x64-msvc": "1.1.4" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/vite": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", - "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.16", - "rolldown": "~1.1.3", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - } - } -} diff --git a/benchmarks-website/package.json b/benchmarks-website/package.json deleted file mode 100644 index a795e0fb22c..00000000000 --- a/benchmarks-website/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "vortex-benchmarks-website", - "version": "2.0.0", - "type": "module", - "scripts": { - "dev": "concurrently \"npm run server\" \"npm run vite\"", - "vite": "vite", - "server": "node server.js", - "build": "vite build", - "preview": "vite preview" - }, - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "chart.js": "^4.5.1", - "chartjs-plugin-zoom": "^2.2.0", - "downsample": "^1.4.0", - "hammerjs": "^2.0.8", - "lucide-react": "^1.11.0", - "react": "^19.2.5", - "react-chartjs-2": "^5.3.1", - "react-dom": "^19.2.5" - }, - "devDependencies": { - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "concurrently": "^10.0.0", - "vite": "^8.0.10" - } -} diff --git a/benchmarks-website/public/Vortex_Black_NoBG.png b/benchmarks-website/public/Vortex_Black_NoBG.png deleted file mode 100644 index b4caa0e0287..00000000000 Binary files a/benchmarks-website/public/Vortex_Black_NoBG.png and /dev/null differ diff --git a/benchmarks-website/public/Vortex_White_NoBG.png b/benchmarks-website/public/Vortex_White_NoBG.png deleted file mode 100644 index 702d378306b..00000000000 Binary files a/benchmarks-website/public/Vortex_White_NoBG.png and /dev/null differ diff --git a/benchmarks-website/public/android-chrome-192x192.png b/benchmarks-website/public/android-chrome-192x192.png deleted file mode 100644 index 1089ab6a060..00000000000 Binary files a/benchmarks-website/public/android-chrome-192x192.png and /dev/null differ diff --git a/benchmarks-website/public/android-chrome-512x512.png b/benchmarks-website/public/android-chrome-512x512.png deleted file mode 100644 index 3cfec717ee4..00000000000 Binary files a/benchmarks-website/public/android-chrome-512x512.png and /dev/null differ diff --git a/benchmarks-website/public/apple-touch-icon.png b/benchmarks-website/public/apple-touch-icon.png deleted file mode 100644 index 2b34da9ee0c..00000000000 Binary files a/benchmarks-website/public/apple-touch-icon.png and /dev/null differ diff --git a/benchmarks-website/public/favicon-16x16.png b/benchmarks-website/public/favicon-16x16.png deleted file mode 100644 index 81dd776858a..00000000000 Binary files a/benchmarks-website/public/favicon-16x16.png and /dev/null differ diff --git a/benchmarks-website/public/favicon-32x32.png b/benchmarks-website/public/favicon-32x32.png deleted file mode 100644 index 8d2cb9edabc..00000000000 Binary files a/benchmarks-website/public/favicon-32x32.png and /dev/null differ diff --git a/benchmarks-website/public/favicon.ico b/benchmarks-website/public/favicon.ico deleted file mode 100644 index 3d4e2ff9fe0..00000000000 Binary files a/benchmarks-website/public/favicon.ico and /dev/null differ diff --git a/benchmarks-website/public/site.webmanifest b/benchmarks-website/public/site.webmanifest deleted file mode 100644 index ba9480d90de..00000000000 --- a/benchmarks-website/public/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "Vortex Benchmarks", - "short_name": "Benchmarks", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/benchmarks-website/server.js b/benchmarks-website/server.js deleted file mode 100644 index d6d3f98eef9..00000000000 --- a/benchmarks-website/server.js +++ /dev/null @@ -1,775 +0,0 @@ -import http from "http"; -import fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; -import zlib from "zlib"; -import readline from "readline"; -import { Readable } from "stream"; -import { LTTB } from "downsample"; -import { QUERY_SUITES, FAN_OUT_GROUPS, ENGINE_RENAMES } from "./src/config.js"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// Configuration -const PORT = process.env.PORT || 3000; -const DATA_URL = - process.env.DATA_URL || - "https://vortex-ci-benchmark-results.s3.amazonaws.com/data.json.gz"; -const COMMITS_URL = - process.env.COMMITS_URL || - "https://vortex-ci-benchmark-results.s3.amazonaws.com/commits.json"; -const REFRESH_INTERVAL = process.env.REFRESH_INTERVAL || 5 * 60 * 1000; -const MAX_POINTS = 200; -const USE_LOCAL_DATA = process.env.USE_LOCAL_DATA === "true"; - -// Benchmark groups: non-query groups + simple suites + fan-out suites -const GROUPS = [ - "Compression", - "Compression Size", - ...QUERY_SUITES.filter((s) => !s.skip && !s.fanOut).map((s) => s.displayName), - "Random Access", - ...FAN_OUT_GROUPS, -]; - -const MIME = { - ".html": "text/html", - ".js": "application/javascript", - ".css": "text/css", - ".json": "application/json", - ".png": "image/png", - ".jpg": "image/jpeg", - ".svg": "image/svg+xml", - ".ico": "image/x-icon", - ".woff": "font/woff", - ".woff2": "font/woff2", - ".webmanifest": "application/manifest+json", -}; - -let store = { - commits: [], - groups: {}, - metadata: null, - downsampled: {}, - lastUpdated: null, -}; - -// Utilities -const rename = (s) => ENGINE_RENAMES[s.toLowerCase()] || ENGINE_RENAMES[s] || s; -const geoMean = (arr) => - arr.length - ? Math.pow( - arr.reduce((a, v) => a * v, 1), - 1 / arr.length, - ) - : null; - -// Categorize benchmarks based on name patterns and metadata -function getGroup(benchmark) { - const name = benchmark.name; - const lower = name.toLowerCase(); - - // Random Access: "random-access/..." or "random access/..." - if ( - lower.startsWith("random-access/") || - lower.startsWith("random access/") - ) { - return "Random Access"; - } - - // Compression Size: size measurements - if ( - lower.startsWith("vortex size/") || - lower.startsWith("vortex-file-compressed size/") || - lower.startsWith("parquet size/") || - lower.startsWith("lance size/") || - lower.includes(":raw size/") || - lower.includes(":parquet-zstd size/") || - lower.includes(":lance size/") - ) { - return "Compression Size"; - } - - // Compression: compress/decompress time and ratio measurements - if ( - lower.startsWith("compress time/") || - lower.startsWith("decompress time/") || - lower.startsWith("parquet_rs-zstd compress") || - lower.startsWith("parquet_rs-zstd decompress") || - lower.startsWith("lance compress") || - lower.startsWith("lance decompress") || - lower.startsWith("vortex:lance ratio") || - lower.startsWith("vortex:parquet-zstd ratio") || - lower.startsWith("vortex:raw ratio") - ) { - return "Compression"; - } - - // SQL query suites: match "{prefix}_q..." or "{prefix}/..." - for (const suite of QUERY_SUITES) { - if ( - !lower.startsWith(suite.prefix + "_q") && - !lower.startsWith(suite.prefix + "/") - ) - continue; - if (suite.skip) return null; - if (!suite.fanOut) return suite.displayName; - // Fan-out suites: expand by storage and scale factor - const storage = benchmark.storage?.toUpperCase() === "S3" ? "S3" : "NVMe"; - const rawSf = benchmark.dataset?.[suite.datasetKey]?.scale_factor; - const sf = rawSf ? Math.round(parseFloat(rawSf)) : 1; - return `${suite.displayName} (${storage}) (SF=${sf})`; - } - - return null; -} - -// Format query name for display: "{prefix}_q00" -> "{QUERY_PREFIX} Q0" -function formatQuery(q) { - const lower = q.toLowerCase(); - for (const suite of QUERY_SUITES) { - if (suite.skip) continue; - const m = lower.match(new RegExp(`^${suite.prefix}[_ ]?q(\\d+)`, "i")); - if (m) return `${suite.queryPrefix} Q${parseInt(m[1], 10)}`; - } - return q.toUpperCase().replace(/[_-]/g, " "); -} - -function normalizeChartName(group, chartName) { - if (group === "Compression Size" && chartName === "VORTEX FILE COMPRESSED SIZE") { - return "VORTEX SIZE"; - } - return chartName; -} - -// LTTB downsampling -function lttbIndices(seriesMap, target) { - const keys = [...seriesMap.keys()]; - if (!keys.length) return []; - const len = seriesMap.get(keys[0])?.length || 0; - if (len <= target) return [...Array(len).keys()]; - - const avg = Array(len); - for (let i = 0; i < len; i++) { - let sum = 0, - n = 0; - for (const arr of seriesMap.values()) { - const v = arr[i]?.value ?? arr[i]; - if (v != null && !isNaN(v)) { - sum += v; - n++; - } - } - avg[i] = [i, n ? sum / n : 0]; - } - - const idx = LTTB(avg, target).map((p) => Math.round(p[0])); - if (!idx.includes(0)) idx.unshift(0); - if (!idx.includes(len - 1)) idx.push(len - 1); - return idx.sort((a, b) => a - b); -} - -function downsample(data, factor) { - const target = Math.ceil(data.commits.length / factor); - if (target >= data.commits.length) return data; - - const idx = lttbIndices(data.series, target); - const series = new Map(); - for (const [k, v] of data.series) - series.set( - k, - idx.map((i) => v[i]), - ); - - return { - ...data, - commits: idx.map((i) => data.commits[i]), - series, - originalLength: data.commits.length, - }; -} - -// Data fetching — streams response body directly instead of buffering -async function fetchJsonl(url) { - const res = await fetch(url); - if (!res.ok) throw new Error(`Fetch failed: ${url} ${res.status}`); - return new Promise((resolve, reject) => { - const results = []; - const rl = readline.createInterface({ - input: Readable.fromWeb(res.body), - crlfDelay: Infinity, - }); - rl.on("line", (l) => { - if (l.trim()) - try { - results.push(JSON.parse(l)); - } catch {} - }); - rl.on("close", () => resolve(results)); - rl.on("error", reject); - }); -} - -function readLocalJsonl(fp) { - return new Promise((resolve, reject) => { - const results = []; - const rl = readline.createInterface({ - input: fs.createReadStream(fp), - crlfDelay: Infinity, - }); - rl.on("line", (l) => { - if (l.trim()) - try { - results.push(JSON.parse(l)); - } catch {} - }); - rl.on("close", () => resolve(results)); - rl.on("error", reject); - }); -} - -// Stream benchmark data record-by-record without buffering the entire dataset -async function forEachBenchmark(callback) { - let stream; - if (USE_LOCAL_DATA) { - stream = fs.createReadStream(path.join(__dirname, "sample/data.json")); - } else { - const res = await fetch(DATA_URL); - if (!res.ok) throw new Error(`Fetch failed: ${DATA_URL} ${res.status}`); - stream = Readable.fromWeb(res.body).pipe(zlib.createGunzip()); - } - return new Promise((resolve, reject) => { - const rl = readline.createInterface({ input: stream, crlfDelay: Infinity }); - rl.on("line", (l) => { - if (l.trim()) - try { - callback(JSON.parse(l)); - } catch {} - }); - rl.on("close", resolve); - rl.on("error", reject); - }); -} - -// Main data processing -async function refresh() { - console.log("Refreshing data..."); - const t0 = Date.now(); - - try { - // Load commits first (small dataset, must be fully in memory for indexing) - const commitsArr = USE_LOCAL_DATA - ? await readLocalJsonl(path.join(__dirname, "sample/commits.json")) - : await fetchJsonl(COMMITS_URL); - - // Build commit index (O(1) lookup) - const commitMap = new Map(commitsArr.map((c) => [c.id, c])); - const commits = commitsArr.sort( - (a, b) => new Date(a.timestamp) - new Date(b.timestamp), - ); - const commitIdx = new Map(commits.map((c, i) => [c.id, i])); - - const groups = Object.fromEntries(GROUPS.map((g) => [g, new Map()])); - let missing = 0; - let benchmarkCount = 0; - const uncategorized = new Set(); - - // Stream benchmarks one record at a time to avoid loading all into memory - await forEachBenchmark((b) => { - benchmarkCount++; - const commit = b.commit || commitMap.get(b.commit_id); - if (!commit) { - missing++; - return; - } - - const group = getGroup(b); - if (!group) { - uncategorized.add(b.name.split("/")[0]); - return; - } - if (!groups[group]) return; - - // Random access names have the form: random-access/{dataset}/{pattern}/{format} - // Historical random access names: random-access/{format} - // Other benchmarks use: {query}/{series} - let seriesName, chartName; - const parts = b.name.split("/"); - if (group === "Random Access" && parts.length === 4) { - chartName = `${parts[1]}/${parts[2]}`.toUpperCase().replace(/[_-]/g, " "); - seriesName = rename(parts[3] || "default"); - } else if (group === "Random Access" && parts.length === 2) { - chartName = "RANDOM ACCESS"; - seriesName = rename(parts[1] || "default"); - } else { - seriesName = rename(parts[1] || "default"); - chartName = formatQuery(parts[0]); - } - chartName = normalizeChartName(group, chartName); - if (chartName.includes("PARQUET-UNC")) return; - - // Skip throughput metrics (keep only time/size) - if (b.name.includes(" throughput")) return; - - let unit = b.unit; - if (!unit) { - if (b.name.toLowerCase().includes(" size/")) unit = "bytes"; - else if (b.name.toLowerCase().includes(" ratio ")) unit = "ratio"; - else unit = "ns"; - } - - const sortPos = parts[0].match(/q(\d+)$/i)?.[1] - ? parseInt(RegExp.$1, 10) - : 0; - const idx = commitIdx.get(commit.id); - if (idx === undefined) return; - - let chart = groups[group].get(chartName); - if (!chart) { - let displayUnit = unit; - if (unit === "ns") displayUnit = "ms/iter"; - else if (unit === "bytes") displayUnit = "MiB"; - chart = { - sort_position: sortPos, - commits, - unit: displayUnit, - series: new Map(), - }; - groups[group].set(chartName, chart); - } - - if (!chart.series.has(seriesName)) { - chart.series.set(seriesName, Array(commits.length).fill(null)); - } - - // Convert values: ns -> ms, bytes -> MiB - let val = b.value; - if (unit === "ns" && typeof val === "number") { - val = val / 1e6; // ns to ms - } else if (unit === "bytes" && typeof val === "number") { - val = val / (1024 * 1024); // bytes to MiB - } - - chart.series.get(seriesName)[idx] = { value: val }; - }); - - console.log( - `Processed ${benchmarkCount} benchmarks, ${commitsArr.length} commits`, - ); - - // Log uncategorized benchmarks for debugging - if (uncategorized.size > 0) { - console.log( - `Uncategorized benchmark prefixes (${uncategorized.size}):`, - [...uncategorized].slice(0, 20).join(", "), - ); - } - - // Trim leading empty commits - let firstIdx = commits.length; - for (const gc of Object.values(groups)) { - for (const cd of gc.values()) { - for (const sd of cd.series.values()) { - const i = sd.findIndex((d) => d !== null); - if (i !== -1 && i < firstIdx) firstIdx = i; - } - } - } - - if (firstIdx > 0 && firstIdx < commits.length) { - console.log(`Trimming ${firstIdx} empty commits`); - commits.splice(0, firstIdx); - for (const gc of Object.values(groups)) { - for (const cd of gc.values()) { - cd.commits = commits; - for (const [k, v] of cd.series) cd.series.set(k, v.slice(firstIdx)); - } - } - } - - // Sort charts within groups - for (const gc of Object.values(groups)) { - const sorted = [...gc.entries()].sort( - (a, b) => - a[1].sort_position - b[1].sort_position || a[0].localeCompare(b[0]), - ); - gc.clear(); - for (const [k, v] of sorted) gc.set(k, v); - } - - // Precompute downsampled versions - const downsampled = {}; - for (const [gn, gc] of Object.entries(groups)) { - downsampled[gn] = {}; - for (const [cn, cd] of gc) { - downsampled[gn][cn] = { - "1x": cd, - "2x": downsample(cd, 2), - "4x": downsample(cd, 4), - "8x": downsample(cd, 8), - }; - } - } - - // Count charts per group for logging - const groupCounts = Object.entries(groups) - .map(([n, g]) => `${n}: ${g.size}`) - .filter((s) => !s.endsWith(": 0")); - console.log("Charts per group:", groupCounts.join(", ")); - - store = { - commits, - groups, - metadata: buildMeta(groups, commits), - downsampled, - lastUpdated: new Date().toISOString(), - }; - console.log( - `Refresh done in ${Date.now() - t0}ms (${missing} missing commits)`, - ); - } catch (e) { - console.error("Refresh error:", e); - } -} - -// Summary calculations -function latestIdx(chart) { - for (let i = chart.commits.length - 1; i >= 0; i--) { - for (const s of chart.series.values()) if (s[i]?.value != null) return i; - } - return -1; -} - -function calcSummary(name, charts) { - if (name === "Random Access") { - for (const q of charts.values()) { - const i = latestIdx(q); - if (i === -1) continue; - const vals = new Map(); - for (const [n, d] of q.series) - if (d[i]?.value != null) vals.set(n, d[i].value); - if (!vals.size) continue; - const min = Math.min(...vals.values()); - return { - type: "randomAccess", - title: "Random Access Performance", - rankings: [...vals] - .map(([n, t]) => ({ name: n, time: t, ratio: t / min })) - .sort((a, b) => a.time - b.time), - explanation: "Random access time | Ratio to fastest (lower is better)", - }; - } - return null; - } - - if (name === "Compression") { - const cc = charts.get("VORTEX:PARQUET ZSTD RATIO COMPRESS TIME"); - const dc = charts.get("VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME"); - if (!cc && !dc) return null; - const i = latestIdx(cc || dc); - if (i === -1) return null; - const collect = (c) => - c - ? [...c.series] - .filter(([n]) => !n.toLowerCase().includes("wide table")) - .map(([, d]) => d[i]?.value) - .filter((v) => v > 0) - .map((v) => 1 / v) - : []; - return { - type: "compression", - title: "Compression Throughput vs Parquet", - compressRatio: geoMean(collect(cc)), - decompressRatio: geoMean(collect(dc)), - datasetCount: collect(cc).length, - explanation: - "Inverse geomean of Vortex/Parquet ratios (higher is better)", - }; - } - - if (name === "Compression Size") { - const c = charts.get("VORTEX:PARQUET ZSTD SIZE"); - if (!c) return null; - const i = latestIdx(c); - if (i === -1) return null; - const ratios = [...c.series] - .filter(([n]) => !n.toLowerCase().includes("wide table")) - .map(([, d]) => d[i]?.value) - .filter((v) => v > 0); - return ratios.length - ? { - type: "compressionSize", - title: "Compression Size Summary", - minRatio: Math.min(...ratios), - meanRatio: geoMean(ratios), - maxRatio: Math.max(...ratios), - datasetCount: ratios.length, - explanation: - "Geomean of Vortex/Parquet size ratios (lower is better)", - } - : null; - } - - if ( - QUERY_SUITES.some( - (s) => - !s.skip && - (name === s.displayName || name.startsWith(s.displayName + " ")), - ) - ) { - const all = new Map(); - for (const q of charts.values()) - for (const n of q.series.keys()) if (!all.has(n)) all.set(n, new Map()); - for (const [qn, qd] of charts) { - for (const [sn, sd] of qd.series) { - for (let i = sd.length - 1; i >= 0; i--) { - if (sd[i]?.value != null) { - all.get(sn).set(qn, sd[i].value); - break; - } - } - } - } - if (!all.size) return null; - - const scores = new Map(); - for (const [sn, qr] of all) { - let total = 0, - max = 0; - for (const v of qr.values()) { - total += v; - max = Math.max(max, v); - } - const penalty = Math.max(300000, max) * 2; - const ratios = []; - for (const qn of charts.keys()) { - let base = Infinity; - for (const m of all.values()) - if (m.has(qn)) base = Math.min(base, m.get(qn)); - if (base < Infinity) - ratios.push((10 + (qr.get(qn) ?? penalty)) / (10 + base)); - } - if (ratios.length) - scores.set(sn, { score: geoMean(ratios), totalRuntime: total }); - } - - return scores.size - ? { - type: "queryBenchmark", - title: "Performance Summary", - rankings: [...scores] - .map(([n, d]) => ({ name: n, ...d })) - .sort((a, b) => a.score - b.score), - explanation: - "Geomean of query time ratio to fastest (lower is better)", - } - : null; - } - return null; -} - -function buildMeta(groups, commits) { - const meta = {}; - for (const [gn, gc] of Object.entries(groups)) { - const charts = [...gc].map(([cn, cd]) => { - const latest = {}; - for (const [sn, sd] of cd.series) { - for (let i = sd.length - 1; i >= 0; i--) - if (sd[i]?.value != null) { - latest[sn] = sd[i].value; - break; - } - } - return { - name: cn, - unit: cd.unit, - series: [...cd.series.keys()], - sortPosition: cd.sort_position, - totalPoints: cd.commits.length, - latestValues: latest, - }; - }); - meta[gn] = { - charts, - totalCharts: charts.length, - hasData: charts.length > 0, - summary: calcSummary(gn, gc), - }; - } - return { - groups: meta, - totalCommits: commits.length, - commits: commits.map((c) => ({ - id: c.id, - message: c.message?.split("\n")[0] || "", - timestamp: c.timestamp, - author: c.author?.name || "Unknown", - })), - lastUpdated: new Date().toISOString(), - }; -} - -// HTTP handlers -const json = (res, code, data) => { - res.writeHead(code, { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }); - res.end(JSON.stringify(data)); -}; - -function serveFile(res, fp) { - fs.readFile(fp, (err, data) => { - if (err) { - res.writeHead(err.code === "ENOENT" ? 404 : 500); - return res.end(err.code === "ENOENT" ? "Not Found" : "Error"); - } - const ext = path.extname(fp).toLowerCase(); - const hdrs = { "Content-Type": MIME[ext] || "application/octet-stream" }; - if (ext === ".js") { - hdrs["Cache-Control"] = "no-cache"; - hdrs["Pragma"] = "no-cache"; - } - res.writeHead(200, hdrs); - res.end(data); - }); -} - -function handleData(res, group, chart, start, end, last, startIdx, endIdx) { - if (!store.downsampled) return json(res, 503, { error: "Loading" }); - const gd = store.downsampled[group]; - if (!gd) return json(res, 404, { error: "Group not found" }); - const cv = gd[chart]; - if (!cv) return json(res, 404, { error: "Chart not found" }); - - const full = cv["1x"]; - const ts = (c) => - typeof c?.timestamp === "number" - ? c.timestamp - : new Date(c?.timestamp).getTime(); - - let si = 0, - ei = full.commits.length - 1; - - // Support "last=N" parameter to get the last N commits - if (last && !start && !end && startIdx === null && endIdx === null) { - const n = parseInt(last, 10); - if (n > 0 && n < full.commits.length) { - si = full.commits.length - n; - } - } else if (startIdx !== null || endIdx !== null) { - // Support index-based range (startIdx, endIdx) - if (startIdx !== null) si = Math.max(0, parseInt(startIdx, 10)); - if (endIdx !== null) - ei = Math.min(full.commits.length - 1, parseInt(endIdx, 10)); - } else { - // Timestamp-based range - if (start) { - const t = +start, - i = full.commits.findIndex((c) => ts(c) >= t); - if (i !== -1) si = i; - } - if (end) { - const t = +end; - for (let i = ei; i >= 0; i--) - if (ts(full.commits[i]) <= t) { - ei = i; - break; - } - } - } - - const len = ei - si + 1; - const ver = - len <= MAX_POINTS - ? "1x" - : len <= MAX_POINTS * 2 - ? "2x" - : len <= MAX_POINTS * 4 - ? "4x" - : "8x"; - const cd = cv[ver]; - const val = (d) => d?.value ?? (typeof d === "number" ? d : null); - - let commits, series; - if (ver === "1x") { - commits = full.commits.slice(si, ei + 1); - series = Object.fromEntries( - [...full.series].map(([n, d]) => [n, d.slice(si, ei + 1).map(val)]), - ); - } else { - const s = +ver[0], - dsi = Math.floor(si / s), - dei = Math.min(Math.ceil(ei / s), cd.commits.length - 1); - commits = cd.commits.slice(dsi, dei + 1); - series = Object.fromEntries( - [...cd.series].map(([n, d]) => [n, d.slice(dsi, dei + 1).map(val)]), - ); - } - - json(res, 200, { - group, - chart, - unit: cd.unit, - downsampleLevel: ver, - originalLength: full.commits.length, - requestedRange: { startIndex: si, endIndex: ei, length: len }, - commits: commits.map((c) => ({ - id: c.id, - message: c.message?.split("\n")[0] || "", - timestamp: c.timestamp, - author: c.author?.name || "Unknown", - url: c.url, - })), - series, - }); -} - -const server = http.createServer((req, res) => { - const [path_, qs] = req.url.split("?"); - const params = new URLSearchParams(qs || ""); - - if (req.method === "OPTIONS") { - res.writeHead(204, { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": "Content-Type", - }); - return res.end(); - } - - if (path_ === "/api/metadata") - return store.metadata - ? json(res, 200, store.metadata) - : json(res, 503, { error: "Loading" }); - - if (path_.startsWith("/api/data/")) { - const p = path_.slice(10).split("/"); - return handleData( - res, - decodeURIComponent(p[0] || ""), - decodeURIComponent(p.slice(1).join("/") || ""), - params.get("start"), - params.get("end"), - params.get("last"), - params.has("startIdx") ? params.get("startIdx") : null, - params.has("endIdx") ? params.get("endIdx") : null, - ); - } - - const fp = path.join(__dirname, "dist", path_ === "/" ? "index.html" : path_); - if (!fp.startsWith(__dirname) || fp.includes("/sample/")) { - res.writeHead(403); - return res.end("Forbidden"); - } - serveFile(res, fp); -}); - -async function start() { - console.log("Starting server..."); - await refresh(); - setInterval(refresh, REFRESH_INTERVAL); - server.listen(PORT, () => console.log(`Server at http://localhost:${PORT}`)); -} - -start().catch(console.error); diff --git a/benchmarks-website/src/App.jsx b/benchmarks-website/src/App.jsx deleted file mode 100644 index 0df05bebf01..00000000000 --- a/benchmarks-website/src/App.jsx +++ /dev/null @@ -1,295 +0,0 @@ -import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; -import Header from './components/Header'; -import Sidebar from './components/Sidebar'; -import BenchmarkSection from './components/BenchmarkSection'; -import Modal from './components/Modal'; -import { fetchMetadata } from './api'; -import { BENCHMARK_CONFIGS, CATEGORY_TAGS } from './config'; - -export default function App() { - const [metadata, setMetadata] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [expandedGroups, setExpandedGroups] = useState(new Set()); - const [sidebarOpen, setSidebarOpen] = useState(false); - const [categoryFilter, setCategoryFilter] = useState('all'); - const [searchFilter, setSearchFilter] = useState(''); - const [viewMode, setViewMode] = useState('grid'); - const [modalChart, setModalChart] = useState(null); - const [showBackToTop, setShowBackToTop] = useState(false); - const metadataFetched = useRef(false); - - useEffect(() => { - if (metadataFetched.current) return; - metadataFetched.current = true; - - async function loadMetadata() { - try { - const data = await fetchMetadata(); - setMetadata(data); - const params = new URLSearchParams(window.location.search); - if (params.get('expanded') === 'true' && data?.groups) { - setExpandedGroups(new Set(Object.keys(data.groups))); - } - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } - } - loadMetadata(); - }, []); - - useEffect(() => { - const handleScroll = () => { - setShowBackToTop(window.scrollY > 400); - }; - window.addEventListener('scroll', handleScroll, { passive: true }); - return () => window.removeEventListener('scroll', handleScroll); - }, []); - - // Handle hash-based navigation on page load - useEffect(() => { - if (!metadata || loading) return; - - const hash = window.location.hash; - if (hash && hash.startsWith('#group-')) { - const groupId = hash.slice(1); // Remove the '#' - const groupName = groupId.replace('group-', '').replace(/-/g, ' '); - - // Find the matching group (case-insensitive, handle hyphenated names) - const matchingGroup = Object.keys(metadata.groups).find(name => - name.replace(/\s+/g, '-') === groupId.replace('group-', '') - ); - - if (matchingGroup) { - // Expand the group - setExpandedGroups(prev => new Set([...prev, matchingGroup])); - - // Scroll to the element after a short delay to allow rendering - setTimeout(() => { - const element = document.getElementById(groupId); - if (element) { - const headerHeight = 72; - const y = element.getBoundingClientRect().top + window.scrollY - headerHeight - 16; - window.scrollTo({ top: y, behavior: 'smooth' }); - } - }, 100); - } - } - }, [metadata, loading]); - - // Get benchmark config by group name - const getBenchmarkConfig = useCallback((groupName) => { - return BENCHMARK_CONFIGS.find(c => c.name === groupName) || {}; - }, []); - - // Filter groups based on category and search - const filteredGroups = useMemo(() => { - if (!metadata?.groups) return []; - - return Object.keys(metadata.groups).filter(groupName => { - // Category filter - if (categoryFilter !== 'all') { - const tags = CATEGORY_TAGS[groupName] || []; - if (!tags.includes(categoryFilter)) return false; - } - - // Search filter - if (searchFilter) { - const searchLower = searchFilter.toLowerCase(); - const matchesGroup = groupName.toLowerCase().includes(searchLower); - const groupData = metadata.groups[groupName]; - const charts = groupData?.charts || []; - const matchesChart = charts.some(c => - c.name.toLowerCase().includes(searchLower) - ); - if (!matchesGroup && !matchesChart) return false; - } - - return true; - }); - }, [metadata, categoryFilter, searchFilter]); - - // Toggle group expansion - const toggleGroup = useCallback((groupName) => { - setExpandedGroups(prev => { - const next = new Set(prev); - if (next.has(groupName)) { - next.delete(groupName); - } else { - next.add(groupName); - } - return next; - }); - }, []); - - // Expand all groups - const expandAll = useCallback(() => { - if (metadata?.groups) { - setExpandedGroups(new Set(Object.keys(metadata.groups))); - const url = new URL(window.location); - url.searchParams.set('expanded', 'true'); - window.history.replaceState(null, '', url); - } - }, [metadata]); - - // Collapse all groups - const collapseAll = useCallback(() => { - setExpandedGroups(new Set()); - const url = new URL(window.location); - url.searchParams.delete('expanded'); - window.history.replaceState(null, '', url); - }, []); - - // Scroll to group - const scrollToGroup = useCallback((groupName) => { - const element = document.getElementById(`group-${groupName.replace(/\s+/g, '-')}`); - if (element) { - const headerHeight = 72; - const y = element.getBoundingClientRect().top + window.scrollY - headerHeight - 16; - window.scrollTo({ top: y, behavior: 'smooth' }); - } - setSidebarOpen(false); - }, []); - - // Back to top - const scrollToTop = useCallback(() => { - window.scrollTo({ top: 0, behavior: 'smooth' }); - }, []); - - // Clear search - const clearFilter = useCallback(() => { - setSearchFilter(''); - setCategoryFilter('all'); - }, []); - - if (loading) { - return ( -
-
setSidebarOpen(!sidebarOpen)} - categoryFilter={categoryFilter} - onCategoryChange={setCategoryFilter} - searchFilter={searchFilter} - onSearchChange={setSearchFilter} - viewMode={viewMode} - onViewModeChange={setViewMode} - onExpandAll={expandAll} - onCollapseAll={collapseAll} - /> -
-
-
-
-

Loading benchmarks...

-
-
-
-
- ); - } - - if (error) { - return ( -
-
setSidebarOpen(!sidebarOpen)} - categoryFilter={categoryFilter} - onCategoryChange={setCategoryFilter} - searchFilter={searchFilter} - onSearchChange={setSearchFilter} - viewMode={viewMode} - onViewModeChange={setViewMode} - onExpandAll={expandAll} - onCollapseAll={collapseAll} - /> -
-
-
-

Error loading benchmarks: {error}

-
-
-
-
- ); - } - - return ( -
-
setSidebarOpen(!sidebarOpen)} - categoryFilter={categoryFilter} - onCategoryChange={setCategoryFilter} - searchFilter={searchFilter} - onSearchChange={setSearchFilter} - viewMode={viewMode} - onViewModeChange={setViewMode} - onExpandAll={expandAll} - onCollapseAll={collapseAll} - /> - -
- setSidebarOpen(false)} - onGroupClick={scrollToGroup} - onClearFilter={clearFilter} - showClearFilter={categoryFilter !== 'all' || searchFilter !== ''} - /> - -
setSidebarOpen(false)} - /> - -
- {filteredGroups.map(groupName => { - const groupData = metadata.groups[groupName] || {}; - const charts = groupData.charts || []; - const config = getBenchmarkConfig(groupName); - const isExpanded = expandedGroups.has(groupName); - - if (config.hidden) return null; - - return ( - toggleGroup(groupName)} - viewMode={viewMode} - onFullscreen={(chartData) => setModalChart(chartData)} - commitRange={metadata.totalCommits} - summary={groupData.summary} - /> - ); - })} -
-
- - {showBackToTop && ( - - )} - - {modalChart && ( - setModalChart(null)} - /> - )} -
- ); -} diff --git a/benchmarks-website/src/api.js b/benchmarks-website/src/api.js deleted file mode 100644 index 042ea7a6f0b..00000000000 --- a/benchmarks-website/src/api.js +++ /dev/null @@ -1,41 +0,0 @@ -const API_BASE = ''; - -export async function fetchMetadata() { - const response = await fetch(`${API_BASE}/api/metadata`); - if (!response.ok) throw new Error(`Failed to fetch metadata: ${response.status}`); - return response.json(); -} - -export async function fetchChartData(groupName, chartName, options = {}) { - const { startTimestamp, endTimestamp, last, startIdx, endIdx } = options; - let url = `${API_BASE}/api/data/${encodeURIComponent(groupName)}/${encodeURIComponent(chartName)}`; - const params = new URLSearchParams(); - - if (last) { - params.set('last', last); - } else if (startIdx !== undefined || endIdx !== undefined) { - // Index-based range - if (startIdx !== undefined) params.set('startIdx', startIdx); - if (endIdx !== undefined) params.set('endIdx', endIdx); - } else { - // Timestamp-based range - if (startTimestamp) { - const ts = typeof startTimestamp === 'number' - ? startTimestamp - : new Date(startTimestamp).getTime(); - params.set('start', ts); - } - if (endTimestamp) { - const ts = typeof endTimestamp === 'number' - ? endTimestamp - : new Date(endTimestamp).getTime(); - params.set('end', ts); - } - } - - if (params.toString()) url += '?' + params.toString(); - - const response = await fetch(url); - if (!response.ok) throw new Error(`Failed to fetch chart data: ${response.status}`); - return response.json(); -} diff --git a/benchmarks-website/src/components/BenchmarkSection.jsx b/benchmarks-website/src/components/BenchmarkSection.jsx deleted file mode 100644 index 706a9c27fe6..00000000000 --- a/benchmarks-website/src/components/BenchmarkSection.jsx +++ /dev/null @@ -1,147 +0,0 @@ -import React, { useState, useCallback, useMemo } from 'react'; -import { Info, Link2 } from 'lucide-react'; -import ChartContainer from './ChartContainer'; -import BenchmarkSummary from './BenchmarkSummary'; -import { getBenchmarkDescription, remapChartName } from '../utils'; - -export default function BenchmarkSection({ - groupName, - charts, - config, - isExpanded, - onToggle, - viewMode, - onFullscreen, - commitRange, - summary, -}) { - const [engineFilter, setEngineFilter] = useState('all'); - const [copiedLink, setCopiedLink] = useState(false); - - // Get unique engines from chart series - const engines = useMemo(() => { - const engineSet = new Set(); - charts.forEach(chart => { - chart.series?.forEach(seriesName => { - if (seriesName.includes(':')) { - const engine = seriesName.split(':')[0].toLowerCase(); - engineSet.add(engine); - } - }); - }); - return Array.from(engineSet).sort(); - }, [charts]); - - // Filter and sort charts based on config - const filteredCharts = useMemo(() => { - if (!charts) return []; - - let result = charts.filter(chart => { - // Apply keptCharts filter - if (config.keptCharts) { - const upperName = chart.name.toUpperCase(); - return config.keptCharts.some(kept => upperName === kept.toUpperCase()); - } - return true; - }); - - // Sort by keptCharts order if specified - if (config.keptCharts) { - const orderMap = new Map(config.keptCharts.map((name, idx) => [name.toUpperCase(), idx])); - result.sort((a, b) => { - const aIdx = orderMap.get(a.name.toUpperCase()) ?? 999; - const bIdx = orderMap.get(b.name.toUpperCase()) ?? 999; - return aIdx - bIdx; - }); - } - - return result; - }, [charts, config]); - - // Copy link to clipboard - const handleCopyLink = useCallback((e) => { - e.stopPropagation(); - const url = `${window.location.origin}${window.location.pathname}#group-${groupName.replace(/\s+/g, '-')}`; - navigator.clipboard.writeText(url); - setCopiedLink(true); - setTimeout(() => setCopiedLink(false), 2000); - }, [groupName]); - - const description = getBenchmarkDescription(groupName); - const hasData = filteredCharts.length > 0; - const chartCount = filteredCharts.length; - - return ( -
-
-
- {isExpanded ? '▼' : '▶'} -
-

- {groupName} - -

- {description && ( - - - - )} -
- {chartCount} {chartCount === 1 ? 'CHART' : 'CHARTS'} -
-
-
- - {isExpanded && engines.length > 0 && ( -
- Filter by engine: - - {engines.map(engine => ( - - ))} -
- )} -
- - - - {isExpanded && ( -
- {filteredCharts.map(chart => ( - - ))} -
- )} -
- ); -} diff --git a/benchmarks-website/src/components/BenchmarkSummary.jsx b/benchmarks-website/src/components/BenchmarkSummary.jsx deleted file mode 100644 index 6bc2d4f1790..00000000000 --- a/benchmarks-website/src/components/BenchmarkSummary.jsx +++ /dev/null @@ -1,129 +0,0 @@ -import React from 'react'; -import { formatTime } from '../utils'; - -// BenchmarkSummary now uses pre-computed summary from metadata (passed via props) -// instead of fetching all chart data -export default function BenchmarkSummary({ groupName, charts, summary }) { - // Use pre-computed summary from metadata - const summaryData = summary; - - if (!summaryData) return null; - - // Query benchmarks (Clickbench, TPC-H, TPC-DS, etc.) - if (summaryData.type === 'queryBenchmark' && summaryData.rankings?.length > 0) { - return ( -
-

{summaryData.title || 'Performance Summary'}

-
- {summaryData.rankings.map((item, idx) => ( -
- #{idx + 1} - {item.name} - - {item.score.toFixed(2)}x - {formatTime(item.totalRuntime)} - -
- ))} -
-
- {summaryData.explanation || 'Score: geometric mean of query time ratio to fastest (lower is better)'} -
-
- ); - } - - if (summaryData.type === 'randomAccess' && summaryData.rankings?.length > 0) { - return ( -
-

{summaryData.title || 'Random Access Performance'}

-
- {summaryData.rankings.map((item, idx) => ( -
- #{idx + 1} - {item.name} - - {formatTime(item.time)} - {item.ratio.toFixed(2)}x - -
- ))} -
-
- {summaryData.explanation || 'Random access time | Ratio to fastest (lower is better)'} -
-
- ); - } - - if (summaryData.type === 'compression') { - return ( -
-

{summaryData.title || 'Compression Throughput vs Parquet'}

-
- {summaryData.compressRatio && ( -
- - Write Speed (Compression) - - {summaryData.compressRatio.toFixed(2)}x - -
- )} - {summaryData.decompressRatio && ( -
- 📤 - Scan Speed (Decompression) - - {summaryData.decompressRatio.toFixed(2)}x - -
- )} -
-
- {summaryData.explanation || `Inverse geometric mean of Vortex/Parquet ratios across ${summaryData.datasetCount || 'multiple'} datasets (higher is better)`} -
-
- ); - } - - if (summaryData.type === 'compressionSize' && summaryData.meanRatio) { - return ( -
-

{summaryData.title || 'Compression Size Summary'}

-
- {summaryData.minRatio && ( -
- ⬇️ - Min Size Ratio - - {summaryData.minRatio.toFixed(2)}x - -
- )} -
- 📊 - Mean Size Ratio - - {summaryData.meanRatio.toFixed(2)}x - -
- {summaryData.maxRatio && ( -
- ⬆️ - Max Size Ratio - - {summaryData.maxRatio.toFixed(2)}x - -
- )} -
-
- {summaryData.explanation || `Geometric mean of Vortex/Parquet size ratios across ${summaryData.datasetCount || 'multiple'} datasets (lower is better)`} -
-
- ); - } - - return null; -} diff --git a/benchmarks-website/src/components/ChartContainer.jsx b/benchmarks-website/src/components/ChartContainer.jsx deleted file mode 100644 index 1dfb193e836..00000000000 --- a/benchmarks-website/src/components/ChartContainer.jsx +++ /dev/null @@ -1,664 +0,0 @@ -import { - CategoryScale, - Chart as ChartJS, - Legend, - LinearScale, - LineElement, - PointElement, - Title, - Tooltip, -} from 'chart.js'; -import zoomPlugin from 'chartjs-plugin-zoom'; -import { - ChevronLeft, - ChevronRight, - Expand, - MoveHorizontal, - SkipBack, - SkipForward, - ZoomIn, - ZoomOut, -} from 'lucide-react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Line } from 'react-chartjs-2'; -import { fetchChartData } from '../api'; -import { formatDate, stringToColor } from '../utils'; - -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - Title, - Tooltip, - Legend, - zoomPlugin -); - -// Custom tooltip positioner - 50px from cursor, 50px above nearest point -Tooltip.positioners.topCorner = function(elements, eventPosition) { - const chart = this.chart; - const chartCenter = (chart.chartArea.left + chart.chartArea.right) / 2; - const chartVerticalCenter = (chart.chartArea.top + chart.chartArea.bottom) / 2; - const isOnRightSide = eventPosition.x > chartCenter; - const isOnTopSide = eventPosition.y < chartVerticalCenter; - - let x = isOnRightSide ? eventPosition.x - 150 : eventPosition.x + 150; - let y = isOnTopSide ? chart.chartArea.top + 100 : chart.chartArea.bottom - 100; - return { - x, - y, - xAlign: isOnRightSide ? 'right' : 'left', - yAlign: isOnTopSide ? 'top' : 'bottom', - }; -}; - -const DEFAULT_RANGE_SIZE = 100; - -export default function ChartContainer({ - groupName, - chartName, - displayName, - unit, - config, - engineFilter, - onFullscreen, -}) { - const [totalCommits, setTotalCommits] = useState(null); - const [chartData, setChartData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - // viewRange stores the requested range: either { last: N } or { startIdx, endIdx } - const [viewRange, setViewRange] = useState({ last: DEFAULT_RANGE_SIZE }); - const chartRef = useRef(null); - const isResettingZoom = useRef(false); - - // Fetch data for the current view range - useEffect(() => { - let cancelled = false; - - async function loadData() { - setLoading(true); - setError(null); - - try { - let options = {}; - if (viewRange.last) { - // Initial load: get last N commits - options = { last: viewRange.last }; - } else if (viewRange.startIdx !== undefined && viewRange.endIdx !== undefined) { - // Navigation: use index-based range - options = { startIdx: viewRange.startIdx, endIdx: viewRange.endIdx }; - } - - const data = await fetchChartData(groupName, chartName, options); - if (!cancelled && data) { - setChartData(data); - if (data.originalLength) { - setTotalCommits(data.originalLength); - } else if (data.commits) { - setTotalCommits(data.commits.length); - } - } - } catch (err) { - if (!cancelled) { - setError(err.message); - } - } finally { - if (!cancelled) { - setLoading(false); - } - } - } - - loadData(); - - return () => { - cancelled = true; - }; - }, [groupName, chartName, viewRange]); - - // Compute display range info from chartData (for rendering only) - const displayRangeInfo = useMemo(() => { - if (!chartData) return { startIdx: 0, endIdx: 0, total: 0, rangeSize: 0 }; - const total = chartData.originalLength || chartData.commits?.length || 0; - const rangeSize = chartData.commits?.length || 0; - const req = chartData.requestedRange || {}; - const startIdx = req.startIndex ?? (total - rangeSize); - const endIdx = req.endIndex ?? (total - 1); - return { startIdx, endIdx, total, rangeSize }; - }, [chartData]); - - // Compute current range from viewRange state (for navigation calculations) - const getCurrentRange = useCallback(() => { - const total = totalCommits || 0; - if (viewRange.last) { - const rangeSize = Math.min(viewRange.last, total); - return { - startIdx: Math.max(0, total - rangeSize), - endIdx: total - 1, - total, - rangeSize, - }; - } - const startIdx = viewRange.startIdx ?? 0; - const endIdx = viewRange.endIdx ?? (total - 1); - return { - startIdx, - endIdx, - total, - rangeSize: endIdx - startIdx + 1, - }; - }, [viewRange, totalCommits]); - - const isAtStart = displayRangeInfo.startIdx === 0; - const isAtEnd = displayRangeInfo.endIdx >= displayRangeInfo.total - 1; - const currentRangeSize = displayRangeInfo.rangeSize; - - // Navigation handlers - use functional updates to get latest state - const handleGoToStart = useCallback(() => { - const range = getCurrentRange(); - if (range.startIdx === 0 || !range.total) return; - setViewRange({ - startIdx: 0, - endIdx: Math.min(range.rangeSize - 1, range.total - 1), - }); - }, [getCurrentRange]); - - const handleGoToEnd = useCallback(() => { - const range = getCurrentRange(); - if (range.endIdx >= range.total - 1 || !range.total) return; - setViewRange({ last: range.rangeSize }); - }, [getCurrentRange]); - - const handleMoveBackward = useCallback(() => { - const range = getCurrentRange(); - if (range.startIdx === 0 || !range.total) return; - const moveAmount = Math.max(1, Math.floor(range.rangeSize / 2)); - const newStartIdx = Math.max(0, range.startIdx - moveAmount); - const newEndIdx = newStartIdx + range.rangeSize - 1; - setViewRange({ - startIdx: newStartIdx, - endIdx: Math.min(newEndIdx, range.total - 1), - }); - }, [getCurrentRange]); - - const handleMoveForward = useCallback(() => { - const range = getCurrentRange(); - if (range.endIdx >= range.total - 1 || !range.total) return; - const moveAmount = Math.max(1, Math.floor(range.rangeSize / 2)); - const newEndIdx = Math.min(range.total - 1, range.endIdx + moveAmount); - const newStartIdx = Math.max(0, newEndIdx - range.rangeSize + 1); - setViewRange({ startIdx: newStartIdx, endIdx: newEndIdx }); - }, [getCurrentRange]); - - const handleZoomIn = useCallback(() => { - const range = getCurrentRange(); - if (!range.total || range.rangeSize <= 10) return; - const center = Math.floor((range.startIdx + range.endIdx) / 2); - const newRangeSize = Math.max(10, Math.floor(range.rangeSize / 2)); - const halfRange = Math.floor(newRangeSize / 2); - let newStartIdx = center - halfRange; - let newEndIdx = newStartIdx + newRangeSize - 1; - - // Clamp to bounds - if (newStartIdx < 0) { - newStartIdx = 0; - newEndIdx = newRangeSize - 1; - } - if (newEndIdx >= range.total) { - newEndIdx = range.total - 1; - newStartIdx = Math.max(0, newEndIdx - newRangeSize + 1); - } - - setViewRange({ startIdx: newStartIdx, endIdx: newEndIdx }); - }, [getCurrentRange]); - - const handleZoomOut = useCallback(() => { - const range = getCurrentRange(); - if (!range.total) return; - const center = Math.floor((range.startIdx + range.endIdx) / 2); - const newRangeSize = Math.min(range.total, range.rangeSize * 2); - const halfRange = Math.floor(newRangeSize / 2); - let newStartIdx = center - halfRange; - let newEndIdx = newStartIdx + newRangeSize - 1; - - // Clamp to bounds - if (newStartIdx < 0) { - newStartIdx = 0; - newEndIdx = Math.min(newRangeSize - 1, range.total - 1); - } - if (newEndIdx >= range.total) { - newEndIdx = range.total - 1; - newStartIdx = Math.max(0, newEndIdx - newRangeSize + 1); - } - - setViewRange({ startIdx: newStartIdx, endIdx: newEndIdx }); - }, [getCurrentRange]); - - const handleShowFullRange = useCallback(() => { - const range = getCurrentRange(); - if (!range.total) return; - setViewRange({ startIdx: 0, endIdx: range.total - 1 }); - }, [getCurrentRange]); - - const isFullRange = isAtStart && isAtEnd; - - // Handle drag selection zoom - const handleDragZoom = useCallback((startDataIdx, endDataIdx) => { - if (!chartData?.commits || !chartData.requestedRange) return; - - const numCommits = chartData.commits.length; - if (numCommits < 2) return; - - const rangeStart = chartData.requestedRange.startIndex; - const rangeEnd = chartData.requestedRange.endIndex; - const total = chartData.originalLength || rangeEnd + 1; - - // Map chart indices to original dataset indices using linear interpolation - // This correctly handles downsampled data where numCommits < (rangeEnd - rangeStart + 1) - const minIdx = Math.min(startDataIdx, endDataIdx); - const maxIdx = Math.max(startDataIdx, endDataIdx); - const globalStartIdx = rangeStart + Math.round(minIdx / (numCommits - 1) * (rangeEnd - rangeStart)); - const globalEndIdx = rangeStart + Math.round(maxIdx / (numCommits - 1) * (rangeEnd - rangeStart)); - - // Ensure minimum range - if (globalEndIdx - globalStartIdx < 5) return; - - setViewRange({ - startIdx: Math.max(0, globalStartIdx), - endIdx: Math.min(total - 1, globalEndIdx), - }); - }, [chartData]); - - // Process series data with filters and renaming - const processedData = useMemo(() => { - if (!chartData?.series || !chartData?.commits) return null; - - const { series, commits } = chartData; - const datasets = []; - const labels = commits.map(c => formatDate(c.timestamp)); - - Object.entries(series).forEach(([seriesName, points]) => { - // Apply removed datasets filter - if (config.removedDatasets?.has(seriesName)) return; - - // Apply engine filter - if (engineFilter !== 'all') { - const engine = seriesName.split(':')[0].toLowerCase(); - if (engine !== engineFilter && !seriesName.toLowerCase().includes(engineFilter)) { - return; - } - } - - // Rename series if needed - let displaySeriesName = seriesName; - if (config.renamedDatasets) { - const caseInsensitive = {}; - Object.entries(config.renamedDatasets).forEach(([k, v]) => { - caseInsensitive[k.toLowerCase()] = v; - }); - displaySeriesName = caseInsensitive[seriesName.toLowerCase()] || seriesName; - } - - // Check if hidden by default - const hidden = config.hiddenDatasets?.has(seriesName) || - config.hiddenDatasets?.has(displaySeriesName); - - datasets.push({ - label: displaySeriesName, - data: points, - borderColor: stringToColor(displaySeriesName), - backgroundColor: stringToColor(displaySeriesName) + '20', - pointRadius: 2, - pointHoverRadius: 5, - pointStyle: 'cross', - borderWidth: 1.5, - tension: 0, - spanGaps: true, - hidden, - }); - }); - - return { labels, datasets, commits }; - }, [chartData, config, engineFilter]); - - // Handle click on chart point to open commit on GitHub - const handleChartClick = useCallback((event, elements) => { - if (!elements.length || !processedData?.commits) return; - const dataIndex = elements[0].index; - const commit = processedData.commits[dataIndex]; - if (commit?.id) { - window.open(`https://github.com/vortex-data/vortex/commit/${commit.id}`, '_blank'); - } - }, [processedData]); - - // Chart.js options with drag zoom - const options = useMemo(() => ({ - responsive: true, - maintainAspectRatio: false, - animation: false, - onClick: handleChartClick, - onHover: (event, elements) => { - event.native.target.style.cursor = elements.length ? 'pointer' : 'default'; - }, - interaction: { - mode: 'index', - intersect: true, - }, - plugins: { - legend: { - position: 'top', - align: 'start', - labels: { - boxWidth: 12, - padding: 8, - font: { - size: 12, - family: 'Geist, sans-serif', - }, - usePointStyle: true, - pointStyle: 'rectRounded', - }, - }, - tooltip: { - backgroundColor: 'rgba(16, 16, 16, 0.9)', - titleFont: { family: 'Geist, sans-serif', size: 13 }, - bodyFont: { family: 'Geist Mono, monospace', size: 12 }, - padding: 12, - cornerRadius: 4, - position: 'topCorner', - caretSize: 0, - itemSort: (a, b) => b.parsed.y - a.parsed.y, - // Limit to top 10 items by value to prevent tooltip from getting too large - filter: (item, _index, items) => { - if (items.length <= 10) return item.parsed.y != null; - const validItems = items.filter(i => i.parsed.y != null); - if (validItems.length <= 10) return item.parsed.y != null; - const sorted = [...validItems].sort((a, b) => (b.parsed.y ?? 0) - (a.parsed.y ?? 0)); - const top10 = sorted.slice(0, 10); - return top10.some(i => i.datasetIndex === item.datasetIndex); - }, - callbacks: { - title: (items) => { - if (!items.length || !processedData?.commits) return ''; - const commit = processedData.commits[items[0].dataIndex]; - if (!commit) return items[0].label; - const author = commit.author || 'Unknown'; - return `${formatDate(commit.timestamp)} — ${author}\n(${commit.id?.slice(0, 7) || ''}) ${commit.message || ''}`; - }, - label: (item) => { - const value = item.parsed.y; - if (value == null) return null; - const formattedValue = value < 1 ? value.toFixed(4) : value.toFixed(2); - return `${item.dataset.label}: ${formattedValue} ${unit || ''}`; - }, - labelTextColor: (tooltipItem) => { - const chart = tooltipItem.chart; - const activeElements = chart._active || []; - if (activeElements.length === 0) return '#ffffff'; - const lastEvent = chart._lastEvent; - if (!lastEvent) return '#ffffff'; - // Find which dataset point is closest to the cursor - let hoveredDatasetIndex = activeElements[0].datasetIndex; - let closestDist = Infinity; - for (const el of activeElements) { - const point = chart.getDatasetMeta(el.datasetIndex).data[el.index]; - if (point) { - const dist = Math.hypot(point.x - lastEvent.x, point.y - lastEvent.y); - if (dist < closestDist) { - closestDist = dist; - hoveredDatasetIndex = el.datasetIndex; - } - } - } - if (tooltipItem.datasetIndex === hoveredDatasetIndex) { - return '#ffffff'; - } - return 'rgba(255, 255, 255, 0.45)'; - }, - }, - }, - zoom: { - zoom: { - drag: { - enabled: true, - backgroundColor: 'rgba(99, 102, 241, 0.2)', - borderColor: 'rgba(99, 102, 241, 0.8)', - borderWidth: 1, - }, - mode: 'x', - onZoomComplete: ({ chart }) => { - // Prevent infinite loop from resetZoom triggering onZoomComplete - if (isResettingZoom.current) { - isResettingZoom.current = false; - return; - } - - const { min, max } = chart.scales.x; - const startIdx = Math.floor(min); - const endIdx = Math.ceil(max); - if (startIdx >= 0 && endIdx > startIdx) { - handleDragZoom(startIdx, endIdx); - } - // Reset chart zoom state - isResettingZoom.current = true; - chart.resetZoom(); - }, - }, - }, - }, - scales: { - x: { - display: true, - grid: { - display: true, - color: 'rgba(166, 166, 166, 0.12)', - }, - ticks: { - maxRotation: 45, - minRotation: 45, - font: { - size: 11, - family: 'Geist, sans-serif', - }, - maxTicksLimit: 10, - callback: function(value, index, ticks) { - // Always show first and last tick - if (index === 0 || index === ticks.length - 1) { - return this.getLabelForValue(value); - } - // Show intermediate ticks based on maxTicksLimit - const step = Math.ceil(ticks.length / 10); - if (index % step === 0) { - return this.getLabelForValue(value); - } - return null; - }, - }, - }, - y: { - display: true, - beginAtZero: true, - grid: { - color: 'rgba(166, 166, 166, 0.12)', - }, - ticks: { - font: { - size: 12, - family: 'Geist Mono, monospace', - }, - }, - title: { - display: !!unit, - text: unit || '', - font: { - size: 12, - family: 'Geist, sans-serif', - }, - }, - }, - }, - }), [unit, processedData, handleDragZoom, handleChartClick]); - - // Fullscreen handler - const handleFullscreen = useCallback(() => { - if (processedData) { - onFullscreen({ - title: displayName, - groupName, - chartName, - unit, - config, - initialData: processedData, - totalCommits, - currentRange: getCurrentRange(), - }); - } - }, [processedData, displayName, groupName, chartName, unit, config, totalCommits, getCurrentRange, onFullscreen]); - - // Show placeholder only on initial load (no data yet) - const showPlaceholder = !processedData && (loading || error); - const showOverlay = loading && processedData; - - if (showPlaceholder) { - return ( -
-
- {displayName} -
-
- {error ? ( -

Error loading chart

- ) : ( -
- )} -
-
- ); - } - - if (!loading && error) { - return ( -
-
- {displayName} -
-
-

Error loading chart

-
-
- ); - } - - if (!processedData || processedData.datasets.length === 0) { - return ( -
-
- {displayName} -
-
-

No data available

-
-
- ); - } - - return ( -
-
- - {displayName} - {chartData?.downsampleLevel && chartData.downsampleLevel !== '1x' && ( - - {chartData.downsampleLevel} downsampled - - )} - -
-
- - - - - - - -
- -
-
-
- - {showOverlay && ( -
-
-
- )} -
-
- ); -} diff --git a/benchmarks-website/src/components/Header.jsx b/benchmarks-website/src/components/Header.jsx deleted file mode 100644 index b9624794119..00000000000 --- a/benchmarks-website/src/components/Header.jsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; - -export default function Header({ - sidebarOpen, - onMenuToggle, - categoryFilter, - onCategoryChange, - searchFilter, - onSearchChange, - viewMode, - onViewModeChange, - onExpandAll, - onCollapseAll, -}) { - return ( -
-
-
- - - - - - Vortex - - -
- -

Vortex Benchmarks

- -
-
- - - - onSearchChange(e.target.value)} - /> -
-
- -
-
- - -
- - - - - GitHub - -
-
-
- ); -} diff --git a/benchmarks-website/src/components/Modal.jsx b/benchmarks-website/src/components/Modal.jsx deleted file mode 100644 index b9bb7d419b1..00000000000 --- a/benchmarks-website/src/components/Modal.jsx +++ /dev/null @@ -1,476 +0,0 @@ -import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; -import { Line } from 'react-chartjs-2'; -import { - SkipBack, - ChevronLeft, - ChevronRight, - SkipForward, - ZoomIn, - ZoomOut, - MoveHorizontal, - X, -} from 'lucide-react'; -import { fetchChartData } from '../api'; -import { stringToColor, formatDate } from '../utils'; - -export default function Modal({ chartData, onClose }) { - const [loading, setLoading] = useState(false); - const [viewRange, setViewRange] = useState(null); - const [currentData, setCurrentData] = useState(null); - const [totalCommits, setTotalCommits] = useState(chartData?.totalCommits || 0); - const chartRef = useRef(null); - const isResettingZoom = useRef(false); - - // Initialize with the data passed from parent - useEffect(() => { - if (chartData?.initialData) { - setCurrentData(chartData.initialData); - setTotalCommits(chartData.totalCommits || chartData.initialData.commits?.length || 0); - if (chartData.currentRange) { - setViewRange({ - startIdx: chartData.currentRange.startIdx, - endIdx: chartData.currentRange.endIdx, - }); - } - } - }, [chartData]); - - // Close on escape key - useEffect(() => { - const handleKeyDown = (e) => { - if (e.key === 'Escape') { - onClose(); - } - }; - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [onClose]); - - // Prevent body scroll when modal is open - useEffect(() => { - document.body.style.overflow = 'hidden'; - return () => { - document.body.style.overflow = ''; - }; - }, []); - - const handleBackdropClick = useCallback((e) => { - if (e.target === e.currentTarget) { - onClose(); - } - }, [onClose]); - - // Fetch data for a new range - const fetchRange = useCallback(async (range) => { - if (!chartData?.groupName || !chartData?.chartName) return; - - setLoading(true); - try { - let options = {}; - if (range.last) { - options = { last: range.last }; - } else if (range.startIdx !== undefined && range.endIdx !== undefined) { - options = { startIdx: range.startIdx, endIdx: range.endIdx }; - } - - const data = await fetchChartData(chartData.groupName, chartData.chartName, options); - if (data) { - // Process the data similar to ChartContainer - const processedData = processChartData(data, chartData.config); - setCurrentData(processedData); - if (data.originalLength) { - setTotalCommits(data.originalLength); - } - } - } catch (err) { - console.error('Error fetching range:', err); - } finally { - setLoading(false); - } - }, [chartData]); - - // Process chart data (similar to ChartContainer) - const processChartData = useCallback((data, config) => { - if (!data?.series || !data?.commits) return null; - - const { series, commits } = data; - const datasets = []; - const labels = commits.map(c => formatDate(c.timestamp)); - - Object.entries(series).forEach(([seriesName, points]) => { - if (config?.removedDatasets?.has(seriesName)) return; - - let displaySeriesName = seriesName; - if (config?.renamedDatasets) { - const caseInsensitive = {}; - Object.entries(config.renamedDatasets).forEach(([k, v]) => { - caseInsensitive[k.toLowerCase()] = v; - }); - displaySeriesName = caseInsensitive[seriesName.toLowerCase()] || seriesName; - } - - const dataPoints = points.map(p => p); - const hidden = config?.hiddenDatasets?.has(seriesName) || - config?.hiddenDatasets?.has(displaySeriesName); - - datasets.push({ - label: displaySeriesName, - data: dataPoints, - borderColor: stringToColor(displaySeriesName), - backgroundColor: stringToColor(displaySeriesName) + '20', - pointRadius: 2, - pointHoverRadius: 5, - pointStyle: 'cross', - borderWidth: 1.5, - tension: 0, - spanGaps: true, - hidden, - }); - }); - - return { labels, datasets, commits }; - }, []); - - // Get current range info - const getCurrentRange = useCallback(() => { - if (!viewRange) { - return { startIdx: 0, endIdx: totalCommits - 1, total: totalCommits, rangeSize: totalCommits }; - } - const startIdx = viewRange.startIdx ?? 0; - const endIdx = viewRange.endIdx ?? (totalCommits - 1); - return { - startIdx, - endIdx, - total: totalCommits, - rangeSize: endIdx - startIdx + 1, - }; - }, [viewRange, totalCommits]); - - const range = getCurrentRange(); - const isAtStart = range.startIdx === 0; - const isAtEnd = range.endIdx >= range.total - 1; - const currentRangeSize = range.rangeSize; - const isFullRange = isAtStart && isAtEnd; - - // Navigation handlers - const handleGoToStart = useCallback(() => { - if (isAtStart || !range.total) return; - const newRange = { - startIdx: 0, - endIdx: Math.min(currentRangeSize - 1, range.total - 1), - }; - setViewRange(newRange); - fetchRange(newRange); - }, [isAtStart, currentRangeSize, range.total, fetchRange]); - - const handleGoToEnd = useCallback(() => { - if (isAtEnd || !range.total) return; - const newRange = { last: currentRangeSize }; - setViewRange({ - startIdx: range.total - currentRangeSize, - endIdx: range.total - 1, - }); - fetchRange(newRange); - }, [isAtEnd, currentRangeSize, range.total, fetchRange]); - - const handleMoveBackward = useCallback(() => { - if (isAtStart || !range.total) return; - const moveAmount = Math.max(1, Math.floor(currentRangeSize / 2)); - const newStartIdx = Math.max(0, range.startIdx - moveAmount); - const newEndIdx = newStartIdx + currentRangeSize - 1; - const newRange = { - startIdx: newStartIdx, - endIdx: Math.min(newEndIdx, range.total - 1), - }; - setViewRange(newRange); - fetchRange(newRange); - }, [isAtStart, range, currentRangeSize, fetchRange]); - - const handleMoveForward = useCallback(() => { - if (isAtEnd || !range.total) return; - const moveAmount = Math.max(1, Math.floor(currentRangeSize / 2)); - const newEndIdx = Math.min(range.total - 1, range.endIdx + moveAmount); - const newStartIdx = Math.max(0, newEndIdx - currentRangeSize + 1); - const newRange = { startIdx: newStartIdx, endIdx: newEndIdx }; - setViewRange(newRange); - fetchRange(newRange); - }, [isAtEnd, range, currentRangeSize, fetchRange]); - - const handleZoomIn = useCallback(() => { - if (!range.total || currentRangeSize <= 10) return; - const center = Math.floor((range.startIdx + range.endIdx) / 2); - const newRangeSize = Math.max(10, Math.floor(currentRangeSize / 2)); - const halfRange = Math.floor(newRangeSize / 2); - let newStartIdx = center - halfRange; - let newEndIdx = newStartIdx + newRangeSize - 1; - - if (newStartIdx < 0) { - newStartIdx = 0; - newEndIdx = newRangeSize - 1; - } - if (newEndIdx >= range.total) { - newEndIdx = range.total - 1; - newStartIdx = Math.max(0, newEndIdx - newRangeSize + 1); - } - - const newRange = { startIdx: newStartIdx, endIdx: newEndIdx }; - setViewRange(newRange); - fetchRange(newRange); - }, [range, currentRangeSize, fetchRange]); - - const handleZoomOut = useCallback(() => { - if (!range.total) return; - const center = Math.floor((range.startIdx + range.endIdx) / 2); - const newRangeSize = Math.min(range.total, currentRangeSize * 2); - const halfRange = Math.floor(newRangeSize / 2); - let newStartIdx = center - halfRange; - let newEndIdx = newStartIdx + newRangeSize - 1; - - if (newStartIdx < 0) { - newStartIdx = 0; - newEndIdx = Math.min(newRangeSize - 1, range.total - 1); - } - if (newEndIdx >= range.total) { - newEndIdx = range.total - 1; - newStartIdx = Math.max(0, newEndIdx - newRangeSize + 1); - } - - const newRange = { startIdx: newStartIdx, endIdx: newEndIdx }; - setViewRange(newRange); - fetchRange(newRange); - }, [range, currentRangeSize, fetchRange]); - - const handleShowFullRange = useCallback(() => { - if (!range.total) return; - const newRange = { startIdx: 0, endIdx: range.total - 1 }; - setViewRange(newRange); - fetchRange(newRange); - }, [range.total, fetchRange]); - - // Handle drag selection zoom - const handleDragZoom = useCallback((startDataIdx, endDataIdx) => { - if (!currentData?.commits) return; - - const numCommits = currentData.commits.length; - if (numCommits < 2) return; - - const rangeStart = range.startIdx; - const rangeEnd = range.endIdx; - const total = range.total; - - const minIdx = Math.min(startDataIdx, endDataIdx); - const maxIdx = Math.max(startDataIdx, endDataIdx); - const globalStartIdx = rangeStart + Math.round(minIdx / (numCommits - 1) * (rangeEnd - rangeStart)); - const globalEndIdx = rangeStart + Math.round(maxIdx / (numCommits - 1) * (rangeEnd - rangeStart)); - - if (globalEndIdx - globalStartIdx < 5) return; - - const newRange = { - startIdx: Math.max(0, globalStartIdx), - endIdx: Math.min(total - 1, globalEndIdx), - }; - setViewRange(newRange); - fetchRange(newRange); - }, [currentData, range, fetchRange]); - - // Chart options - const options = useMemo(() => ({ - responsive: true, - maintainAspectRatio: false, - animation: false, - interaction: { - mode: 'index', - intersect: false, - }, - plugins: { - legend: { - position: 'top', - align: 'start', - labels: { - boxWidth: 12, - padding: 8, - font: { size: 11, family: 'Geist, sans-serif' }, - usePointStyle: true, - pointStyle: 'rectRounded', - }, - }, - tooltip: { - backgroundColor: 'rgba(16, 16, 16, 0.9)', - titleFont: { family: 'Geist, sans-serif', size: 12 }, - bodyFont: { family: 'Geist Mono, monospace', size: 11 }, - padding: 12, - cornerRadius: 4, - caretSize: 0, - position: 'topCorner', - itemSort: (a, b) => b.parsed.y - a.parsed.y, - callbacks: { - title: (items) => { - if (!items.length || !currentData?.commits) return ''; - const commit = currentData.commits[items[0].dataIndex]; - if (!commit) return items[0].label; - const author = commit.author || 'Unknown'; - return `${formatDate(commit.timestamp)} — ${author}\n(${commit.id?.slice(0, 7) || ''}) ${commit.message || ''}`; - }, - label: (item) => { - const value = item.parsed.y; - if (value == null) return null; - const formattedValue = value < 1 ? value.toFixed(4) : value.toFixed(2); - return `${item.dataset.label}: ${formattedValue} ${chartData?.unit || ''}`; - }, - }, - }, - zoom: { - zoom: { - drag: { - enabled: true, - backgroundColor: 'rgba(99, 102, 241, 0.2)', - borderColor: 'rgba(99, 102, 241, 0.8)', - borderWidth: 1, - }, - mode: 'x', - onZoomComplete: ({ chart }) => { - if (isResettingZoom.current) { - isResettingZoom.current = false; - return; - } - const { min, max } = chart.scales.x; - const startIdx = Math.floor(min); - const endIdx = Math.ceil(max); - if (startIdx >= 0 && endIdx > startIdx) { - handleDragZoom(startIdx, endIdx); - } - isResettingZoom.current = true; - chart.resetZoom(); - }, - }, - }, - }, - scales: { - x: { - display: true, - grid: { display: true, color: 'rgba(0, 0, 0, 0.12)' }, - ticks: { - maxRotation: 45, - minRotation: 45, - font: { size: 10, family: 'Geist, sans-serif' }, - maxTicksLimit: 15, - callback: function(value, index, ticks) { - // Always show first and last tick - if (index === 0 || index === ticks.length - 1) { - return this.getLabelForValue(value); - } - // Show intermediate ticks based on maxTicksLimit - const step = Math.ceil(ticks.length / 15); - if (index % step === 0) { - return this.getLabelForValue(value); - } - return null; - }, - }, - }, - y: { - display: true, - beginAtZero: true, - grid: { color: 'rgba(0, 0, 0, 0.12)' }, - ticks: { font: { size: 11, family: 'Geist Mono, monospace' } }, - title: { - display: !!chartData?.unit, - text: chartData?.unit || '', - font: { size: 11, family: 'Geist, sans-serif' }, - }, - }, - }, - }), [currentData, chartData, handleDragZoom]); - - if (!chartData) return null; - - return ( -
-
-
-

{chartData.title}

-
-
- - - - - - - -
- -
-
-
- {currentData && ( - - )} - {loading && ( -
-
-
- )} -
-
-
- ); -} diff --git a/benchmarks-website/src/components/Sidebar.jsx b/benchmarks-website/src/components/Sidebar.jsx deleted file mode 100644 index 3a53b801f8e..00000000000 --- a/benchmarks-website/src/components/Sidebar.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; - -export default function Sidebar({ - isOpen, - groups, - onClose, - onGroupClick, - onClearFilter, - showClearFilter, -}) { - return ( - - ); -} diff --git a/benchmarks-website/src/config.js b/benchmarks-website/src/config.js deleted file mode 100644 index dfe5555019d..00000000000 --- a/benchmarks-website/src/config.js +++ /dev/null @@ -1,285 +0,0 @@ -// ============================================================================= -// SQL query benchmark suites — single source of truth. -// To add a new SQL query benchmark, add one entry to QUERY_SUITES. -// The server (routing/formatting) and frontend (UI config) both derive from this. -// ============================================================================= - -export const QUERY_SUITES = [ - { - prefix: "clickbench", - displayName: "Clickbench", - queryPrefix: "CLICKBENCH", - description: - "ClickHouse's analytical benchmark suite testing real-world query patterns on web analytics data", - tags: ["Queries (NVMe)"], - hiddenDatasets: ["datafusion:lance"], - }, - { - prefix: "clickbench-sorted", - displayName: "Clickbench Sorted", - queryPrefix: "CLICKBENCH SORTED", - description: - "ClickBench queries over data globally sorted by event date and event time", - tags: ["Queries (NVMe)"], - hiddenDatasets: ["datafusion:lance"], - }, - { - prefix: "statpopgen", - displayName: "Statistical and Population Genetics", - queryPrefix: "STATPOPGEN", - description: - "A suite of Statistical and Population genetics queries using the gnomAD dataset", - tags: ["Queries (NVMe)", "StatPopGen"], - }, - { - prefix: "polarsignals", - displayName: "PolarSignals Profiling", - queryPrefix: "POLARSIGNALS", - description: - "Profiling data benchmark modeled on PolarSignals/Parca, exercising scan-layer performance with projection and filter pushdown on deeply nested schemas", - tags: ["Queries (NVMe)", "PolarSignals"], - }, - { - prefix: "tpch", - displayName: "TPC-H", - queryPrefix: "TPC-H", - datasetKey: "tpch", - fanOut: true, - hiddenDatasets: ["datafusion:lance"], - }, - { - prefix: "tpcds", - displayName: "TPC-DS", - queryPrefix: "TPC-DS", - datasetKey: "tpcds", - fanOut: true, - }, - { prefix: "fineweb", skip: true }, -]; - -// Pre-registered fan-out groups (storage x scale factor). -export const FAN_OUT_GROUPS = [ - "TPC-H (NVMe) (SF=1)", - "TPC-H (S3) (SF=1)", - "TPC-H (NVMe) (SF=10)", - "TPC-H (S3) (SF=10)", - "TPC-H (NVMe) (SF=100)", - "TPC-H (S3) (SF=100)", - "TPC-H (NVMe) (SF=1000)", - "TPC-H (S3) (SF=1000)", - "TPC-DS (NVMe) (SF=1)", - "TPC-DS (NVMe) (SF=10)", -]; - -// Canonical engine:format renaming used by all query suites. -export const ENGINE_RENAMES = { - "datafusion:vortex-file-compressed": "datafusion:vortex", - "datafusion:parquet": "datafusion:parquet", - "datafusion:arrow": "datafusion:in-memory-arrow", - "datafusion:lance": "datafusion:lance", - "datafusion:vortex-compact": "datafusion:vortex-compact", - "duckdb:vortex-file-compressed": "duckdb:vortex", - "duckdb:parquet": "duckdb:parquet", - "duckdb:duckdb": "duckdb:duckdb", - "duckdb:vortex-compact": "duckdb:vortex-compact", - "vortex-tokio-local-disk": "vortex-nvme", - "vortex-compact-tokio-local-disk": "vortex-compact-nvme", - "lance-tokio-local-disk": "lance-nvme", - "parquet-tokio-local-disk": "parquet-nvme", - lance: "lance", -}; - -// ============================================================================= -// Below: frontend UI config, derived from QUERY_SUITES where possible. -// ============================================================================= - -// Build BENCHMARK_CONFIGS: bespoke non-query groups + generated query group entries. -const BESPOKE_CONFIGS = [ - { - name: "Random Access", - renamedDatasets: { - "vortex-tokio-local-disk": "vortex-nvme", - "vortex-compact-tokio-local-disk": "vortex-compact-nvme", - "lance-tokio-local-disk": "lance-nvme", - "parquet-tokio-local-disk": "parquet-nvme", - }, - }, - { - name: "Compression", - keptCharts: [ - "COMPRESS TIME", - "DECOMPRESS TIME", - "PARQUET RS ZSTD COMPRESS TIME", - "PARQUET RS ZSTD DECOMPRESS TIME", - "LANCE COMPRESS TIME", - "LANCE DECOMPRESS TIME", - "VORTEX:PARQUET ZSTD RATIO COMPRESS TIME", - "VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME", - "VORTEX:LANCE RATIO COMPRESS TIME", - "VORTEX:LANCE RATIO DECOMPRESS TIME", - ], - hiddenDatasets: new Set([ - "wide table cols=1000 chunks=1 rows=1000", - "wide table cols=1000 chunks=50 rows=1000", - ]), - removedDatasets: new Set([ - "TPC-H l_comment canonical", - "TPC-H l_comment chunked without fsst", - "wide table cols=10 chunks=1 rows=1000", - "wide table cols=100 chunks=1 rows=1000", - "wide table cols=10 chunks=50 rows=1000", - "wide table cols=100 chunks=50 rows=1000", - ]), - renamedDatasets: { lance: "lance", Lance: "lance", LANCE: "lance" }, - }, - { - name: "Compression Size", - keptCharts: [ - "VORTEX SIZE", - "PARQUET SIZE", - "LANCE SIZE", - "VORTEX:PARQUET ZSTD SIZE", - "VORTEX:LANCE SIZE", - ], - hiddenDatasets: new Set(["wide table cols=1000"]), - removedDatasets: new Set([ - "wide table cols=10 chunks=1 rows=1000", - "wide table cols=100 chunks=1 rows=1000", - "wide table cols=10 chunks=50 rows=1000", - "wide table cols=100 chunks=50 rows=1000", - ]), - renamedDatasets: { lance: "lance", Lance: "lance", LANCE: "lance" }, - }, -]; - -function querySuiteConfig(name, suite) { - const cfg = { name, renamedDatasets: { ...ENGINE_RENAMES } }; - if (suite?.hiddenDatasets?.length) - cfg.hiddenDatasets = new Set(suite.hiddenDatasets); - return cfg; -} - -function buildQueryConfigs() { - const configs = []; - for (const s of QUERY_SUITES) { - if (s.skip) continue; - if (!s.fanOut) { - configs.push(querySuiteConfig(s.displayName, s)); - } - } - for (const g of FAN_OUT_GROUPS) { - const suite = QUERY_SUITES.find( - (s) => s.fanOut && g.startsWith(s.displayName), - ); - const cfg = querySuiteConfig(g, suite); - if (g.includes("SF=1000") || (g.includes("TPC-DS") && g.includes("SF=10)"))) - cfg.hidden = true; - configs.push(cfg); - } - return configs; -} - -export const BENCHMARK_CONFIGS = [...BESPOKE_CONFIGS, ...buildQueryConfigs()]; - -// Chart name remapping (compression benchmarks only) -export const CHART_NAME_MAP = { - "COMPRESS TIME": "VORTEX WRITE TIME (COMPRESSION)", - "DECOMPRESS TIME": "VORTEX SCAN TIME (DECOMPRESSION)", - "PARQUET RS ZSTD COMPRESS TIME": "PARQUET WRITE TIME (COMPRESSION)", - "PARQUET RS ZSTD DECOMPRESS TIME": "PARQUET SCAN TIME (DECOMPRESSION)", - "LANCE COMPRESS TIME": "LANCE WRITE TIME (COMPRESSION)", - "LANCE DECOMPRESS TIME": "LANCE SCAN TIME (DECOMPRESSION)", - "VORTEX SIZE": "VORTEX SIZE", - "PARQUET ZSTD SIZE": "PARQUET SIZE", - "LANCE SIZE": "LANCE SIZE", - "VORTEX:RAW SIZE": "VORTEX vs RAW SIZE RATIO", - "VORTEX:PARQUET ZSTD SIZE": "VORTEX vs PARQUET SIZE RATIO", - "VORTEX:LANCE SIZE": "VORTEX vs LANCE SIZE RATIO", - "VORTEX:PARQUET ZSTD RATIO COMPRESS TIME": - "VORTEX vs PARQUET WRITE TIME RATIO", - "VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME": - "VORTEX vs PARQUET SCAN TIME RATIO", - "VORTEX:LANCE RATIO COMPRESS TIME": "VORTEX vs LANCE WRITE TIME RATIO", - "VORTEX:LANCE RATIO DECOMPRESS TIME": "VORTEX vs LANCE SCAN TIME RATIO", -}; - -// Category tags for sidebar filtering -export const CATEGORY_TAGS = { - "Random Access": ["Read/Write"], - Compression: ["Read/Write"], - "Compression Size": ["Read/Write"], -}; -for (const s of QUERY_SUITES) { - if (!s.skip && !s.fanOut && s.tags) CATEGORY_TAGS[s.displayName] = s.tags; -} -for (const g of FAN_OUT_GROUPS) { - const m = g.match(/^(.+?) \((NVMe|S3)\) \((SF=\d+)\)$/); - CATEGORY_TAGS[g] = [ - m[2] === "S3" ? "Queries (S3)" : "Queries (NVMe)", - `${m[1]} (${m[3]})`, - ]; -} - -// Benchmark descriptions -export const BENCHMARK_DESCRIPTIONS = { - "Random Access": - "Tests performance of selecting arbitrary row indices from a file on NVMe storage", - Compression: - "Measures encoding and decoding throughput (MB/s) for Vortex files and Parquet files (with zstd page compression)", - "Compression Size": - "Compares compressed file sizes and compression ratios across different encoding strategies", -}; -for (const s of QUERY_SUITES) { - if (s.description) BENCHMARK_DESCRIPTIONS[s.displayName] = s.description; -} - -// Scale factor descriptions -export const SCALE_FACTOR_DESCRIPTIONS = { - 1: "SF=1 (~1GB of data)", - 10: "SF=10 (~10GB of data)", - 100: "SF=100 (~100GB of data)", - 1000: "SF=1000 (~1TB of data)", -}; - -// Engine filter labels -export const ENGINE_LABELS = { - all: "All", - duckdb: "DuckDB", - datafusion: "DataFusion", - vortex: "Vortex", - parquet: "Parquet", -}; - -// Series color map -export const SERIES_COLOR_MAP = { - "vortex-nvme": "#19a508", - "vortex-compact-nvme": "#15850a", - "parquet-nvme": "#ef7f1d", - "lance-nvme": "#3B82F6", - "datafusion:arrow": "#7a27b1", - "datafusion:in-memory-arrow": "#7a27b1", - "datafusion:parquet": "#ef7f1d", - "datafusion:vortex": "#19a508", - "datafusion:vortex-compact": "#15850a", - "datafusion:lance": "#2D936C", - "duckdb:parquet": "#985113", - "duckdb:vortex": "#0e5e04", - "duckdb:vortex-compact": "#0b4a03", - "duckdb:duckdb": "#87752e", - "vortex:lance": "#FF8787", -}; - -// Fallback color palette -export const FALLBACK_PALETTE = [ - "#5971FD", - "#CEE562", - "#EEB3E1", - "#FF8C42", - "#B8336A", - "#726DA8", - "#2D936C", - "#E9B44C", -]; - -// Default visible commits -export const DEFAULT_COMMIT_RANGE = 100; diff --git a/benchmarks-website/src/main.jsx b/benchmarks-website/src/main.jsx deleted file mode 100644 index 32fcc3f979f..00000000000 --- a/benchmarks-website/src/main.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App'; -import './styles/index.css'; - -ReactDOM.createRoot(document.getElementById('root')).render( - - - -); diff --git a/benchmarks-website/src/styles/index.css b/benchmarks-website/src/styles/index.css deleted file mode 100644 index 910670ec28f..00000000000 --- a/benchmarks-website/src/styles/index.css +++ /dev/null @@ -1,1319 +0,0 @@ -/* CSS Variables for consistent theming */ -:root { - /* Vortex Brand Colors */ - --vortex-black: #101010; - --vortex-gray: #ECECEC; - --vortex-green: #CEE562; - --vortex-blue: #5971FD; - --vortex-pink: #EEB3E1; - - /* Theme Colors */ - --primary-color: var(--vortex-blue); - --primary-hover: #4A5FE5; - --accent-color: var(--vortex-green); - --bg-color: #ffffff; - --bg-secondary: #FAFAFA; - --text-color: var(--vortex-black); - --text-secondary: #666666; - --border-color: var(--vortex-gray); - - /* Layout */ - --header-height: 72px; - --sidebar-width: 280px; - --chart-spacing: 24px; - --mobile-breakpoint: 768px; - --tablet-breakpoint: 1024px; - - /* Shadows */ - --shadow-sm: 0 1px 3px rgba(16,16,16,0.08); - --shadow-md: 0 4px 8px rgba(16,16,16,0.08); - --shadow-lg: 0 12px 24px rgba(16,16,16,0.12); - - /* Border Radius */ - --radius-sm: 4px; - --radius-md: 8px; - --radius-lg: 12px; -} - -@media (prefers-color-scheme: dark) { :root { - --primary-color: var(--vortex-blue); - --primary-hover: #8a9cff; - --accent-color: var(--vortex-green); - --bg-color: #050507; - --bg-secondary: #121219; - --text-color: #F5F5F7; - --text-secondary: #A0A0B0; - --border-color: #30303a; - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.6); - --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.7); - --shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.8); -}} - -/* Reset and base styles */ -* { - box-sizing: border-box; -} - -html { - font-family: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", "SF Pro Display", Roboto, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background-color: var(--bg-color); - font-size: 16px; - scroll-behavior: smooth; - overflow-x: hidden; -} - -body { - color: var(--text-color); - margin: 0; - padding: 0; - font-size: 1em; - font-weight: 400; - padding-top: var(--header-height); - line-height: 1.6; - letter-spacing: -0.01em; - overflow-x: hidden; - position: relative; -} - -/* Typography */ -h1, h2, h3, h4, h5, h6 { - font-family: "Funnel Display", sans-serif; - font-weight: 600; - letter-spacing: -0.02em; -} - -code, pre { - font-family: "Geist Mono", monospace; - font-size: 0.9em; -} - -/* Sticky Header */ -.sticky-header { - position: fixed; - top: 0; - left: 0; - right: 0; - height: var(--header-height); - background: rgba(255, 255, 255, 0.95); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - border-bottom: 1px solid var(--border-color); - box-shadow: var(--shadow-sm); - z-index: 1000; - display: flex; - align-items: center; -} - -.header-content { - display: flex; - align-items: center; - justify-content: space-between; - height: 100%; - padding: 0 32px; - width: 100%; - gap: 24px; - background: var(--bg-secondary); -} - -.header-left { - display: flex; - align-items: center; - gap: 12px; -} - -.menu-toggle { - display: block; - background: none; - border: none; - font-size: 20px; - cursor: pointer; - padding: 8px; - border-radius: var(--radius-sm); - transition: all 0.2s; - color: var(--text-color); -} - -.menu-toggle:hover { - background-color: var(--bg-secondary); -} - -.logo-link { - display: flex; - align-items: center; - text-decoration: none; - transition: opacity 0.2s; -} - -.logo-link:hover { - opacity: 0.8; -} - -.site-logo { - height: 24px; - width: auto; - display: block; -} - -.site-title { - font-family: "Funnel Display", sans-serif; - font-size: 1.5rem; - font-weight: 600; - margin: 0; - margin-left: calc(var(--sidebar-width) - 156px); - color: var(--text-color); - display: none; - white-space: nowrap; -} - -@media (min-width: 1400px) { - .site-title { - display: block; - } -} - -.header-center { - flex: 1; - display: flex; - justify-content: center; - padding: 0 20px; -} - -.filter-controls { - display: flex; - align-items: center; - gap: 16px; - max-width: 600px; -} - -.control-btn, .view-btn { - font-family: "Geist", sans-serif; - padding: 8px 20px; - border: 1px solid var(--border-color); - background: var(--bg-color); - color: var(--text-color); - border-radius: var(--radius-md); - cursor: pointer; - font-size: 14px; - font-weight: 500; - transition: all 0.2s; - white-space: nowrap; -} - -.control-btn:hover, .view-btn:hover { - background-color: var(--bg-secondary); - border-color: var(--primary-color); - transform: translateY(-1px); - box-shadow: var(--shadow-sm); -} - -.view-btn.active { - background-color: var(--primary-color); - color: white; - border-color: var(--primary-color); -} - -.category-filter, .search-filter { - font-family: "Geist", sans-serif; - padding: 8px 12px; - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - font-size: 14px; - font-weight: 500; - background: var(--bg-color); - color: var(--text-color); - transition: border-color 0.2s; -} - -.category-filter:focus, .search-filter:focus { - outline: none; - border-color: var(--primary-color); -} - -.search-filter { - width: 200px; -} - -.header-right { - display: flex; - align-items: center; - gap: 16px; -} - -.view-controls { - display: flex; - gap: 4px; -} - -.repo-link { - display: flex; - align-items: center; - gap: 8px; - color: var(--text-color); - text-decoration: none; - font-weight: 600; - font-size: 14px; - padding: 8px 16px; - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - transition: all 0.2s; -} - -.github-logo { - flex-shrink: 0; -} - -.repo-link:hover { - background-color: var(--bg-secondary); - border-color: var(--primary-color); - color: var(--primary-color); -} - -/* Main Container */ -.main-container { - display: flex; - min-height: calc(100vh - var(--header-height)); - overflow-x: hidden; - width: 100%; -} - -/* Sidebar Navigation */ -.sidebar { - width: var(--sidebar-width); - background: var(--bg-secondary); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - border-right: 1px solid var(--border-color); - position: fixed; - top: var(--header-height); - left: 0; - height: calc(100vh - var(--header-height)); - overflow-y: auto; - transition: transform 0.3s ease; - z-index: 998; - transform: translateX(-100%); - box-shadow: 2px 0 12px rgba(0, 0, 0, 0.15); -} - -.sidebar.active, -.sidebar.open { - transform: translateX(0); -} - -.sidebar-nav { - display: flex; - flex-direction: column; - height: 100%; -} - -.sidebar-header { - padding: 20px; - border-bottom: 1px solid var(--border-color); - display: flex; - justify-content: space-between; - align-items: center; -} - -.sidebar-header h2 { - font-family: "Funnel Display", sans-serif; - margin: 0; - font-size: 1.2rem; - color: var(--text-color); - font-weight: 600; -} - -.sidebar-close { - background: none; - border: none; - font-size: 24px; - cursor: pointer; - padding: 4px; - line-height: 1; -} - -.clear-filter-btn { - font-family: "Geist", sans-serif; - width: calc(100% - 40px); - margin: 16px 20px; - padding: 10px 16px; - background-color: var(--primary-color); - color: white; - border: 1px solid var(--primary-color); - border-radius: var(--radius-md); - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; -} - -.clear-filter-btn:hover { - background-color: var(--primary-hover); - border-color: var(--primary-hover); -} - -.toc-list { - list-style: none; - padding: 0; - margin: 0; - flex: 1; - overflow-y: auto; -} - -.toc-list li { - border-bottom: 1px solid rgba(0,0,0,0.05); -} - -.toc-list a { - display: block; - padding: 12px 20px; - color: var(--text-color); - text-decoration: none; - transition: all 0.2s; - position: relative; -} - -.toc-list a:hover { - background-color: rgba(89, 113, 253, 0.08); - color: var(--primary-color); - padding-left: 24px; -} - -.sidebar-footer { - padding: 20px; - border-top: 1px solid var(--border-color); -} - -.download-btn { - display: block; - width: 100%; - padding: 10px 16px; - background-color: transparent; - color: var(--text-secondary); - text-align: center; - text-decoration: none; - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - font-size: 13px; - font-weight: 500; - transition: all 0.2s; -} - -.download-btn:hover { - background-color: var(--bg-secondary); - color: var(--text-color); - border-color: var(--text-secondary); -} - -/* Sidebar overlay */ -.sidebar-overlay { - display: none; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.3); - z-index: 997; -} - -.sidebar-overlay.active { - display: block; -} - -/* Main Content */ -.main-content { - flex: 1; - padding: 32px; - width: 100%; - position: relative; -} - -@media (min-width: 1600px) { - .main-content { - padding: 40px 60px; - } - .header-content { - padding: 0 60px; - } -} - -@media (min-width: 1920px) { - .main-content { - padding: 48px 80px; - } - .header-content { - padding: 0 80px; - } -} - -/* Loading Indicator */ -.loading-indicator, -.error-indicator { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: 400px; - gap: 16px; -} - -.loading-indicator p, -.error-indicator p { - font-family: "Geist", sans-serif; - color: var(--text-secondary); - font-size: 14px; - font-weight: 500; -} - -.spinner { - width: 48px; - height: 48px; - border: 3px solid var(--border-color); - border-top: 3px solid var(--primary-color); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -/* Benchmark Sections */ -.benchmark-set { - margin-bottom: 48px; - background: var(--bg-color); - border-radius: var(--radius-lg); - border: 1px solid var(--border-color); - overflow: visible; - transition: opacity 0.3s ease, border-color 0.3s ease; - position: relative; - z-index: 1; -} - -.benchmark-set:first-child { - margin-top: 0; -} - -.benchmark-set.no-data { - opacity: 0.5; -} - -.benchmark-set.no-data .benchmark-header { - cursor: not-allowed; -} - -.benchmark-set.no-data .collapse-icon { - visibility: hidden; -} - -.sticky-header-container { - position: relative; - z-index: 50; - background: var(--bg-secondary); - border-radius: var(--radius-lg) var(--radius-lg) 0 0; - margin: 0; - overflow: visible; - padding: 0; -} - -.benchmark-header { - display: flex; - align-items: center; - gap: 12px; - padding: 16px 32px; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border-color); - border-radius: var(--radius-lg); - cursor: pointer; - -webkit-user-select: none; - user-select: none; - transition: background-color 0.2s; - overflow: visible; -} - -.benchmark-header:hover { - background: var(--bg-color); -} - -.title-wrapper { - display: flex; - align-items: center; - gap: 12px; - flex: 1; - min-width: 0; - overflow: visible; -} - -.benchmark-title { - font-family: "Funnel Display", sans-serif; - font-size: 1.5rem; - font-weight: 600; - margin: 0; - color: var(--text-color); - display: flex; - align-items: center; - gap: 12px; - letter-spacing: -0.02em; - line-height: 1.2; -} - -.group-link-btn { - font-size: 18px; - background: none; - border: none; - cursor: pointer; - opacity: 0.5; - padding: 4px 8px; - border-radius: var(--radius-sm); - transition: opacity 0.2s, background-color 0.2s, color 0.2s; - color: var(--text-secondary); -} - -.benchmark-header:hover .group-link-btn { - opacity: 1; -} - -.group-link-btn:hover { - background-color: var(--bg-secondary); - color: var(--primary-color); -} - -.group-link-btn.copied { - color: var(--accent-color); - opacity: 1; -} - -.collapse-icon { - font-size: 1rem; - flex-shrink: 0; - width: 1rem; - text-align: center; - display: inline-block; -} - -.benchmark-secondary-info { - display: flex; - align-items: center; - gap: 12px; - flex-shrink: 0; -} - -.benchmark-meta { - font-family: "Geist Mono", monospace; - display: flex; - gap: 16px; - font-size: 11px; - font-weight: 500; - color: var(--text-secondary); - letter-spacing: 0.02em; - text-transform: uppercase; - flex-shrink: 0; - line-height: 1; -} - -.info-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - border-radius: 50%; - background: var(--bg-secondary); - color: var(--text-secondary); - font-size: 16px; - cursor: help; - position: relative; - transition: background-color 0.2s, color 0.2s; - flex-shrink: 0; -} - -.info-icon:hover { - background: var(--primary-color); - color: white; -} - -.info-icon::after { - content: attr(data-tooltip); - position: absolute; - bottom: calc(100% + 8px); - left: 50%; - transform: translateX(-50%); - padding: 8px 12px; - background: rgba(16, 16, 16, 0.95); - color: white; - font-size: 13px; - line-height: 1.4; - border-radius: var(--radius-sm); - white-space: nowrap; - opacity: 0; - visibility: hidden; - transition: opacity 0.2s, visibility 0.2s; - pointer-events: none; - z-index: 10000; - font-weight: normal; -} - -.info-icon:hover::after { - opacity: 1; - visibility: visible; -} - -/* Generic Tooltip */ -[data-tooltip] { - position: relative; -} - -[data-tooltip]::after { - content: attr(data-tooltip); - position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); - margin-bottom: 6px; - padding: 6px 10px; - background: rgba(16, 16, 16, 0.95); - color: white; - font-family: "Geist", sans-serif; - font-size: 12px; - font-weight: 500; - line-height: 1.3; - border-radius: var(--radius-sm); - white-space: nowrap; - opacity: 0; - visibility: hidden; - transition: all 0.15s; - pointer-events: none; - z-index: 1000; -} - -[data-tooltip]:hover::after { - opacity: 1; - visibility: visible; -} - -[data-tooltip]:disabled::after, -[data-tooltip][disabled]::after { - display: none; -} - -/* Engine Filter Controls */ -.engine-filter-container { - padding: 12px 32px; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border-color); - display: flex; - align-items: center; - gap: 12px; - flex-wrap: wrap; -} - -.engine-filter-label { - font-family: "Geist", sans-serif; - font-size: 14px; - font-weight: 500; - color: var(--text-secondary); -} - -.engine-filter-btn { - font-family: "Geist", sans-serif; - padding: 6px 14px; - border: 1px solid var(--border-color); - background: var(--bg-color); - border-radius: var(--radius-sm); - font-size: 13px; - font-weight: 500; - color: var(--text-color); - cursor: pointer; - transition: all 0.2s; -} - -.engine-filter-btn:hover { - background-color: var(--bg-secondary); - border-color: var(--primary-color); -} - -.engine-filter-btn.active { - background-color: var(--primary-color); - color: white; - border-color: var(--primary-color); -} - -/* Chart Grid */ -.benchmark-graphs { - display: grid; - grid-template-columns: 1fr; - gap: 20px; - padding: 20px; - border-radius: 0 0 var(--radius-lg) var(--radius-lg); - background: var(--bg-color); -} - -@media (min-width: 1200px) { - .benchmark-graphs { - grid-template-columns: repeat(2, 1fr); - gap: 24px; - padding: 24px; - } - - .benchmark-graphs.single-chart { - grid-template-columns: 1fr; - max-width: 1400px; - margin: 0 auto; - } -} - -@media (min-width: 1600px) { - .benchmark-graphs { - padding: 28px 32px; - } -} - -.benchmark-graphs.list-view { - grid-template-columns: 1fr; - max-width: 1200px; - margin: 0 auto; -} - -.chart-container { - background: var(--bg-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - padding: 20px; - position: relative; - transition: all 0.3s ease; - display: flex; - flex-direction: column; -} - -.chart-container:hover { - box-shadow: var(--shadow-md); -} - -.chart-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; -} - -.chart-title { - font-family: "Funnel Display", sans-serif; - font-size: 16px; - font-weight: 500; - color: var(--text-color); - letter-spacing: -0.01em; - display: flex; - align-items: center; - gap: 8px; -} - -.downsample-indicator { - font-family: "Geist Mono", monospace; - font-size: 10px; - font-weight: 600; - min-width: 100px; - padding: 2px 6px; - background-color: var(--vortex-pink); - color: var(--vortex-black); - border-radius: var(--radius-sm); - text-transform: uppercase; - letter-spacing: 0.02em; -} - -.chart-actions { - display: flex; - gap: 8px; -} - -.chart-action-btn { - background: var(--bg-color); - border: 1px solid var(--border-color); - padding: 6px 12px; - border-radius: var(--radius-sm); - cursor: pointer; - font-size: 12px; - font-weight: 500; - transition: all 0.2s; - display: flex; - align-items: center; - gap: 6px; -} - -.chart-action-btn:hover { - background-color: #f5f5f5; - border-color: var(--primary-color); -} - -.chart-zoom-controls { - display: flex; - gap: 2px; - margin-right: 8px; -} - -.chart-zoom-btn { - background: var(--bg-color); - border: 1px solid var(--border-color); - padding: 4px 8px; - border-radius: var(--radius-sm); - cursor: pointer; - font-size: 12px; - font-weight: 600; - min-width: 28px; - transition: all 0.15s; - color: var(--text-secondary); - display: flex; - align-items: center; - justify-content: center; -} - -.chart-zoom-btn:hover:not(:disabled) { - background-color: var(--primary-color); - border-color: var(--primary-color); - color: white; -} - -.chart-zoom-btn:disabled { - opacity: 0.4; - cursor: not-allowed; - background: var(--bg-secondary); -} - -/* Chart Canvas */ -.chart-container canvas { - max-height: 450px; - min-height: 320px; - width: 100% !important; - height: auto !important; - display: block; -} - -.benchmark-graphs.list-view .chart-container canvas { - max-height: 600px; - min-height: 400px; -} - -.chart-canvas-wrapper { - position: relative; - height: 100%; - min-height: 320px; -} - -.chart-canvas-wrapper.loading canvas { - filter: blur(2px); - pointer-events: none; - transition: filter 0.2s; -} - -.chart-canvas-placeholder { - display: flex; - align-items: center; - justify-content: center; - min-height: 320px; - max-height: 450px; - background: var(--bg-secondary); - border-radius: var(--radius-sm); - margin-top: 8px; - position: relative; - overflow: hidden; -} - -.chart-loading-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - z-index: 10; -} - -.chart-loading-spinner { - width: 32px; - height: 32px; - border: 3px solid var(--border-color); - border-top-color: var(--primary-color); - border-radius: 50%; - animation: spin 0.8s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -/* Back to Top Button */ -.back-to-top { - position: fixed; - bottom: 32px; - right: 32px; - width: 48px; - height: 48px; - background-color: var(--primary-color); - color: white; - border: none; - border-radius: 50%; - font-size: 20px; - cursor: pointer; - box-shadow: 0 4px 12px rgba(89, 113, 253, 0.3); - opacity: 0; - visibility: hidden; - transition: all 0.3s ease; - z-index: 999; -} - -.back-to-top.visible { - opacity: 1; - visibility: visible; -} - -.back-to-top:hover { - background-color: var(--primary-hover); - transform: translateY(-4px); - box-shadow: 0 8px 20px rgba(89, 113, 253, 0.4); -} - -/* Chart Modal */ -.chart-modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.8); - z-index: 2000; -} - -.chart-modal.active { - display: flex; - align-items: center; - justify-content: center; -} - -.modal-content { - background: var(--bg-color); - padding: 32px; - border-radius: var(--radius-lg); - width: 95%; - max-width: 1800px; - height: 90vh; - position: relative; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.modal-header h2 { - font-family: 'Funnel Display', sans-serif; - font-size: 20px; - font-weight: 600; - margin: 0; -} - -.modal-controls { - display: flex; - align-items: center; - gap: 12px; -} - -.modal-close-btn { - background: var(--bg-color); - border: 1px solid var(--border-color); - padding: 6px; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text-secondary); - display: flex; - align-items: center; - justify-content: center; - transition: all 0.15s; -} - -.modal-close-btn:hover { - background-color: var(--primary-color); - border-color: var(--primary-color); - color: white; -} - -.modal-chart-container { - width: 100%; - height: calc(100% - 60px); - position: relative; -} - -.modal-chart-container.loading canvas { - filter: blur(2px); - pointer-events: none; - transition: filter 0.2s; -} - -/* Benchmark Scores Summary */ -.benchmark-scores-summary { - background: var(--bg-secondary); - border-bottom: 1px solid var(--border-color); - padding: 12px 32px; - margin: 0; - margin-top: 0 !important; -} - -.scores-title { - font-size: 12px; - font-weight: 600; - margin: 0 0 8px 0; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.scores-list { - column-count: 2; - column-gap: 20px; -} - -.scores-list:has(.score-item:only-child) { - column-count: 1; -} - -@media (max-width: 780px) { - .scores-list { - column-count: 1; - } -} - -.score-item { - display: flex; - align-items: center; - background: transparent; - padding: 6px 0; - margin-bottom: 4px; - border-radius: 0; - border: none; - transition: none; - font-size: 14px; - break-inside: avoid; -} - -.score-rank { - font-weight: 500; - color: var(--primary-color); - min-width: 24px; - font-size: 14px; -} - -.score-series { - flex: 1; - font-weight: 500; - color: var(--text-color); - margin: 0 8px; - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.score-metrics { - display: flex; - gap: 6px; - align-items: center; -} - -.score-value { - font-family: 'Geist Mono', monospace; - font-weight: 600; - color: var(--primary-color); - font-size: 13px; -} - -.score-runtime { - font-family: 'Geist Mono', monospace; - font-weight: 600; - color: var(--text-secondary); - background: transparent; - padding: 0 4px; - border-radius: 0; - font-size: 13px; -} - -.scores-explanation { - margin-top: 8px; - font-size: 11px; - color: var(--text-secondary); - font-style: italic; - text-align: center; -} - -/* Utility Classes */ -.hidden { - display: none !important; -} - -/* Mobile Styles */ -@media (max-width: 768px) { - :root { - --header-height: 56px; - } - - .header-content { - padding: 0 8px; - gap: 8px; - } - - .header-left { - gap: 8px; - } - - .site-logo { - height: 16px; - } - - .site-title { - display: none; - } - - .header-center { - display: none; - } - - .view-controls { - display: none; - } - - .repo-link span { - display: none; - } - - .main-content { - padding: 16px; - max-width: 100vw; - } - - .benchmark-set { - margin-bottom: 24px; - border-radius: var(--radius-md); - overflow: hidden; - } - - .benchmark-header { - padding: 12px 16px; - flex-wrap: wrap; - gap: 8px; - overflow: hidden; - } - - .title-wrapper { - flex-wrap: wrap; - gap: 8px; - overflow: hidden; - } - - .benchmark-title { - font-size: 1.1rem; - flex: 1 1 auto; - min-width: 0; - word-break: break-word; - } - - .benchmark-meta { - flex-shrink: 1; - font-size: 10px; - } - - .benchmark-secondary-info { - flex-shrink: 1; - min-width: 0; - } - - .benchmark-graphs { - grid-template-columns: 1fr; - gap: 16px; - padding: 12px; - } - - .chart-container { - padding: 12px; - overflow: hidden; - } - - .chart-header { - flex-wrap: wrap; - gap: 8px; - } - - .chart-title { - flex: 1 1 100%; - min-width: 0; - font-size: 14px; - } - - .chart-actions { - width: 100%; - justify-content: flex-start; - } - - .chart-zoom-controls { - flex-wrap: wrap; - gap: 4px; - } - - .chart-zoom-btn { - padding: 6px 8px; - min-width: 32px; - } - - .chart-container canvas { - max-height: 350px; - min-height: 250px; - } - - .chart-container:hover { - transform: none; - box-shadow: none; - } - - .chart-action-btn { - display: none; - } - - .engine-filter-container { - padding: 12px 16px; - flex-wrap: wrap; - } - - .back-to-top { - bottom: 16px; - right: 16px; - width: 40px; - height: 40px; - font-size: 16px; - } - - .modal-content { - padding: 16px; - height: 90vh; - } - - .modal-header { - flex-wrap: wrap; - gap: 8px; - } - - .modal-header h2 { - font-size: 16px; - min-width: 0; - word-break: break-word; - } - - .benchmark-scores-summary { - padding: 8px 16px; - } -} diff --git a/benchmarks-website/src/utils.js b/benchmarks-website/src/utils.js deleted file mode 100644 index 140ebebeb6c..00000000000 --- a/benchmarks-website/src/utils.js +++ /dev/null @@ -1,104 +0,0 @@ -import { SERIES_COLOR_MAP, FALLBACK_PALETTE, CHART_NAME_MAP } from './config'; - -// Simple hash function for color selection -function simpleHash(str) { - let hash = 0; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; - } - return Math.abs(hash); -} - -export function stringToColor(str) { - if (SERIES_COLOR_MAP[str]) { - return SERIES_COLOR_MAP[str]; - } - - const lowerStr = str - .replace(/^DataFusion:/i, 'datafusion:') - .replace(/^DuckDB:/i, 'duckdb:') - .replace(/^Vortex:/i, 'vortex:') - .replace(/^Arrow:/i, 'arrow:'); - - if (lowerStr !== str && SERIES_COLOR_MAP[lowerStr]) { - return SERIES_COLOR_MAP[lowerStr]; - } - - const index = simpleHash(str) % FALLBACK_PALETTE.length; - return FALLBACK_PALETTE[index]; -} - -export function remapChartName(name) { - if (CHART_NAME_MAP[name]) { - return CHART_NAME_MAP[name]; - } - // Convert dashes to spaces for readability - return name.replace(/-/g, ' '); -} - -export function formatDate(timestamp) { - if (!timestamp) return ''; - const date = new Date(timestamp); - const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`; -} - -export function formatTime(ms) { - if (ms < 1) return `${(ms * 1000).toFixed(0)}μs`; - if (ms < 1000) return `${ms.toFixed(1)}ms`; - if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; - return `${(ms / 60000).toFixed(1)}m`; -} - -export function debounce(func, wait) { - let timeout; - return function (...args) { - clearTimeout(timeout); - timeout = setTimeout(() => func.apply(this, args), wait); - }; -} - -export function throttle(func, limit) { - let inThrottle; - return function (...args) { - if (!inThrottle) { - func.apply(this, args); - inThrottle = true; - setTimeout(() => (inThrottle = false), limit); - } - }; -} - -export function isMobile() { - return window.innerWidth <= 768; -} - -export function getBenchmarkDescription(categoryName) { - if (categoryName.startsWith('TPC-H')) { - const match = categoryName.match(/SF=(\d+)/); - const sf = match ? match[1] : null; - const sfDesc = sf ? `at SF=${sf} (~${sf === '1' ? '1GB' : sf === '10' ? '10GB' : sf === '100' ? '100GB' : '1TB'} of data)` : ''; - if (categoryName.includes('NVMe')) { - return `TPC-H benchmark queries on local NVMe storage ${sfDesc}`; - } else if (categoryName.includes('S3')) { - return `TPC-H benchmark queries against S3 storage ${sfDesc}`; - } - } - if (categoryName.startsWith('TPC-DS')) { - const match = categoryName.match(/SF=(\d+)/); - const sf = match ? match[1] : null; - const sfDesc = sf ? `at SF=${sf}` : ''; - return `TPC-DS benchmark queries on local NVMe storage ${sfDesc}`; - } - const descriptions = { - 'Random Access': 'Tests performance of selecting arbitrary row indices from a file on NVMe storage', - 'Compression': 'Measures encoding and decoding throughput (MB/s) for Vortex and Parquet files', - 'Compression Size': 'Compares compressed file sizes across different encoding strategies', - 'Clickbench': "ClickHouse's analytical benchmark suite on web analytics data", - 'Clickbench Sorted': 'ClickBench queries over data globally sorted by event date and event time', - 'Statistical and Population Genetics': 'Statistical and population genetics queries on gnomAD dataset', - }; - return descriptions[categoryName] || ''; -} diff --git a/benchmarks-website/vite.config.js b/benchmarks-website/vite.config.js deleted file mode 100644 index ad0cc7446f6..00000000000 --- a/benchmarks-website/vite.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; - -export default defineConfig({ - plugins: [react()], - publicDir: 'public', - server: { - port: 5173, - proxy: { - '/api': { - target: 'http://localhost:3000', - changeOrigin: true, - }, - }, - }, - build: { - outDir: 'dist', - }, -});