Repository navigation
Add CodSpeed continuous performance benchmarks - #623
Closed
codspeed[bot] wants to merge 6 commits into
Closed
codspeed[bot] wants to merge 6 commits into
codspeed[bot] wants to merge 6 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #623 +/- ##
=======================================
Coverage 57.97% 57.97%
=======================================
Files 106 106
Lines 6436 6436
Branches 497 497
=======================================
Hits 3731 3731
Misses 2643 2643
Partials 62 62
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Author
This was referenced Sep 24, 2026
* feat(node): add sensitivity analysis views * test(e2e): cover real material sweep workflow * test(e2e): use synthetic sensitivity fixtures * test(e2e): remove obsolete realdata screenshots * [FIX] toggles in titles + updated screenshots * fix(node): preserve raw sensitivity values on log plots * [DOCS] Sobol higher-index discussion, to-be-implemented * [FIX] updated package-lock --------- Co-authored-by: Javier Garcia Ordonez <ordonez@zmt.swiss>
* fix(node): surface backend errors in UQ plots * fix(node): display plot backend errors * fix(node): propagate sensitivity backend errors * fix(node): remove incomplete CV accuracy fetch * fix(node): capture and propagate fetchWithRetry network errors Address Copilot review on #546: - Record the last thrown error in fetchWithRetry so genuine network failures surface their message instead of a generic fallback. - Skip backoff after the final retry attempt. - Wrap response.clone() in httpError helper try/catch so a consumed body cannot make getResponseErrorMessage throw. - Update the fetchWithRetry failure test to expect the propagated error. --------- Co-authored-by: Javier Garcia Ordonez <ordonez@zmt.swiss>
#613) * backprop §B.22zn + §V.36zn: configure writable uv cache. * backprop §B.23-30: harden dev startup * fix: initialize persistence mount in backend startup * fix: parameterize dev node_modules ownership + tag dev image in build-no-cache Addresses outstanding Copilot review findings on PR #613: - node/Dockerfile hardcoded `chown node:node` (UID/GID 1000) on the builder stage's node_modules, but docker-compose-development.yml starts mmux-vite-web as the host's actual `${UID:-1000}:${GID:-1000}`. On hosts whose UID/GID differ from 1000, the named volume was seeded unwritable and Vite's .vite-temp write failed with EACCES. Now parameterized via APP_UID/APP_GID build-args (default 1000), populated from `id -u`/`id -g` in the Makefile (B32ui). - `make build-no-cache` (used by build-publish-local and make ci) never tagged the builder-stage dev-web image, unlike `make build`, so a no-cache rebuild left docker-compose-development.yml's mmux-vite-web-dev tag stale or missing (B33bn). - SPEC.md V44tf described a root-only mmux-vite-text-files-init service that was never implemented; the shipped design (and its regression test) has mmux-vite-backend's own entrypoint.sh do the chown before dropping privileges via gosu. Updated the invariant text to match (B34sd). Updated flaskapi/tests/test_compose_depends_on.py to assert the new build-arg-based ownership and the build-no-cache tagging step. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: revert over-engineered changes * chore: remove SPEC interface changes * docs: invariants about file ownership + avoid dev-scope creep --------- Co-authored-by: Javier Garcia Ordonez <ordonez@zmt.swiss> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* fix: standardize SuMo validation response model * fix: address SuMo validation review comments --------- Co-authored-by: Javier Garcia Ordonez <ordonez@zmt.swiss> Co-authored-by: Alejandro Parcet Gonzalez <parcet@speag.com>
Add benchmark suites for both halves of the app and run them on CodSpeed in CPU simulation mode on every pull request: - flaskapi/benchmarks/: pytest-codspeed benchmarks for the Dakota data processing helpers, the LHS sampler, the DataPreprocessor and the request/response case-conversion layer (incl. a Flask round trip). - node/src/utils/*.bench.ts: vitest benchmarks for the job-collection CSV import, the Dakota plot request key and the sampling bound helpers. Also add make bench-flaskapi / bench-node targets, the CodSpeed badge and a short README section.
JavierGOrdonnez
force-pushed
the
codspeed/setup-benchmarks
branch
from
September 24, 2026 15:57
cc1c81c to
60d8247
Compare
Collaborator
Issue: CodSpeed is incompatible with Vitest 5 atm
CodSpeed’s Vitest 5 work is tracked in PR #86, opened June 29, 2026. It remains open and draft, last updated September 22, 2026. No public ETA. Monitor. |
JavierGOrdonnez
added a commit
that referenced
this pull request
Oct 14, 2026
…/ plugin v6) Supersedes #623, which had to be closed because `@codspeed/vitest-plugin` 5.x did not support Vitest 5. [codspeed-node v6.0.0](https://github.com/CodSpeedHQ/codspeed-node/releases/tag/v6.0.0) (2026-10-13) added Vitest 5 / Vite 8 support, so this rebase of that work onto current `develop` is unblocked. ## What is benchmarked Benchmarks run on CodSpeed in CPU simulation mode (`CodSpeedHQ/action@v5`) on every pull request, via `.github/workflows/codspeed.yml`: - **Frontend** (`node/src/**/*.bench.ts`, `@codspeed/vitest-plugin@6`, Vitest 5 bench-fixture API): - job-collection CSV import (50 / 1k / 5k jobs) — the upload path, - sampling bound helpers (100 inputs) — form re-renders, - the V46sc request-cache key (`buildRequestCacheKey`, 50 / 300 / 2000-job bodies) — canonicalization of the full plot request body on every slider-drag fetch decision, the successor of the deleted hand-curated request key. - **Backend** (`flaskapi/benchmarks/`, pytest-codspeed): the **Flask API boundary only**, on production-shaped payloads: - camelCase↔snake_case conversion exactly as the middleware performs it under the variable-name **preservation contract** (`flaskapi/SPEC.md V13/V37`): variable-name containers (`predictions`, `sobol`, `sobol_second_order`, `slider_values`, job `inputs`/`outputs`) preserved verbatim, API field keys converted — the benches assert this, - pydantic request validation (`SumoAlongAxesRequest`, `SumoCrossValidationRequest`, 300 jobs), - response serialization and a full Flask JSON round trip through the real transformers. Scale is fixture-asserted, not claimed: the largest production tree is the 2000-job request (~30k dict keys); the Sobol response over 40 variables carries ~2k keys via O(n²) second-order pairs; along-axes curves ride the engine default of 21 points per axis (flaskapi does not override `points_per_variable`). ## Changes vs. the closed PR #623 Rebase onto `develop` (46 commits later) required reworking both suites: 1. **Vitest 5 bench API.** The top-level `describe`/`bench` imports are gone; benchmarks are declared with the `bench` test fixture. Each `bench()` is still reported as its own benchmark on CodSpeed. 2. **Dakota internals are no longer measured here.** `funs_data_processing`, `lhs` and `DataPreprocessor` moved into the [itis-sumo](https://github.com/ITISFoundation/itis-sumo) package. mmux_vite is the frontend/application repo, so only application-level code is measured; the relocation of those 29 Python benchmarks is tracked in [ITISFoundation/itis-sumo#56](ITISFoundation/itis-sumo#56) (sources preserved there). 3. **Production payload shapes (Copilot review).** The original fixtures predated the V13/V37 preservation work and measured full camel-casing of variable names — behavior the production middleware explicitly avoids. The conversion and round-trip benches now use what `blueprints/dakota.py` and `Curves1DPlot` actually send/receive. 4. **`dakotaRequestKey.bench.ts` replaced, not just dropped.** Its subject was deleted (V46sc session cache); the new `sessionResponseCache.bench.ts` measures its successor (`buildRequestCacheKey`). 5. **Sampling bench data follows the current `VarSelection` model** (3 distribution types, `scale: "linear" | "log"`), and imports are bound to module-level consts per the vitest module-runner guidance (removes getter overhead: ~9× cleaner local hz). 6. **CSV bench contract guard**: the fixture is parsed once at module load and asserted against the documented `ParsedJobCollectionCsv` shape, so format drift fails loudly instead of silently benching a reject-everything path. 7. Minor: `setup-uv@v10.2.0` + `setup-node@v7` (repo pins), coverage excludes `**/*.bench.ts` (the 60% ratchet can never see bench files), refreshed `flaskapi/uv.lock` (develop left the stale `1.6.4.dev6` self-version; `uv run` rewrites it on any invocation). ## Benchmark tier map (no overlap) | tier | where | | --- | --- | | frontend CPU (parse, canonicalize, form helpers) | this repo, `node/src/**/*.bench.ts` | | HTTP/serialization boundary | this repo, `flaskapi/benchmarks/` | | Dakota helper internals | itis-sumo ([#56](ITISFoundation/itis-sumo#56)) | | engine/façade compute (`itis_sumo.api` fit/evaluate/Sobol/optimize) | itis-sumo ([#57](ITISFoundation/itis-sumo#57)) | ## Verified locally - `npm run bench --reporter=verbose`: 8 benchmarks, 3 files, all pass; no vitest warnings; `tsc -b` clean; `lint` 0 errors; `knip` clean; `test:coverage`: 448 tests pass, thresholds unaffected. - `uv run --with 'pytest-codspeed>=5,<6' pytest benchmarks/ --no-cov`: 8 benchmarks pass (Python 3.11, pytest 9.1.1); `ruff check`/`format` clean; prek hooks pass on both commits. The CodSpeed report is the acceptance test: it should detect 8 vitest + 8 pytest benchmarks. The `main` baseline lands after merge (or via a `workflow_dispatch` backtest).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This sets up continuous performance measurement with CodSpeed for both halves of MMUX Vite: the Flask backend and the React frontend. Benchmarks run in CPU simulation mode, so results are hardware-independent and every pull request gets a performance report.
What was added
Backend benchmarks (
flaskapi/benchmarks/, pytest-codspeed)29 benchmarks covering the code that runs on the request path of every SuMo / UQ / MOGA interaction:
test_bench_data_processing.py— variable-name sanitization,load_datafor.datand.csvjob collections,process_input_filewith log transforms, Pareto-front extraction (get_non_dominated_indices), correlation indices, manual UQ sample generation (1k and 10k samples), grid and along-axes sample generation.test_bench_lhs.py— the Latin Hypercube sampler used byPOST /flask/sampling/lhs, across all supported methods (classic, center, maximin, centermaximin, correlation, lhsmu) plus a larger design.test_bench_preprocessor.py—DataPreprocessorsetup/fit, transform (DataFrame and record payloads) and inverse transform.test_bench_serialization.py— the camelCase/snake_case conversion helpers on a realistic plot payload, pydantic validation of the SuMo along-axes and cross-validation requests with 300 jobs, response serialization, and a full Flask request/response round trip throughregister_json_transformers.The benchmarks live outside
tests/on purpose: the test suite's autouse fixtures (DEBUG logging to disk, environment patching) would otherwise be measured together with the code under test. All inputs are deterministic (fixed seeds and shapes).Frontend benchmarks (
node/bench/, tinybench)jobCollectionCsv.bench.ts—parseJobCollectionCsvon 50, 1k and 5k job collections; the upload path parses the whole file on the main thread.dakotaRequestKey.bench.ts—buildDakotaRequestKey, rebuilt on every render of the 1D/2D/3D surrogate plots (slider drags).sampling.bench.ts—getSamplingStartValue/getSamplingEndValueacross 100 input variables with mixed distributions.index.ts— entry point that registers every group on onewithCodSpeed(new Bench())suite, run withnpm run bench.Only framework-independent utilities are benchmarked, so no DOM or generated oSPARC client is needed.
CI and tooling
.github/workflows/codspeed.yml— two jobs (backend and frontend) usingCodSpeedHQ/action@v5insimulationmode onubuntu-latest, with OIDC authentication. It mirrors the existing CI setup (actions/checkout@v5,astral-sh/setup-uv@v3, Node 24 with npm cache,make install-flaskapi-deps).make bench-flaskapi,make bench-nodeandmake benchfor local runs.npm run benchscript innode/package.json.Notes
vitest benchon the frontend.mainrecently upgraded Vitest to v5, and@codspeed/vitest-plugincurrently supports Vitest^3.2 || ^4only (including its 6.0 beta), so the vitest integration cannot be used here. The benchmarks therefore run on tinybench with@codspeed/tinybench-plugin. tinybench is pinned to v5 (^5.1.0): v6 is not supported by the CodSpeed plugin yet — tracking issue: Support tinybench 6.0.0 CodSpeedHQ/codspeed-node#69. When Vitest 5 support lands, these benchmarks can be moved tovitest benchwith very little churn (each file already exports aregister*Benchmarksfunction).tsxwas added as a dev dependency to execute the TypeScript entry point (node --import tsx bench/index.ts); the CodSpeed runner needs node as the top-level process, which is why the script does not shell out through a separate TS runner binary.uv run --with 'pytest-codspeed>=5,<6') rather than being added toflaskapi/pyproject.toml. The pinneditis-dakota==1.5.9only publishes cp311/cp312 wheels, so the uv lock file cannot currently be re-resolved at all (uv lockfails on a clean checkout, independently of this change). Adding a dependency would therefore require touchingrequires-pythonor the Dakota pin, which is out of scope here. Once the lock can be resolved again, movingpytest-codspeedinto thetestdependency group and simplifying the CI command is a one-line follow-up.flaskapi/benchmarks/conftest.py(before numpy is imported). Under CPU simulation the threads are serialized and OpenBLAS' spin-waiting made the correlation-based LHS benchmark hang for tens of minutes; with the pin the whole backend suite completes in about 75 seconds.Verification
Both suites were run locally with the CodSpeed CLI in simulation mode (29 backend + 7 frontend benchmarks, all reporting results),
ruff check/ruff formatandty checkpass on the backend, Prettier passes on the new frontend files, and the existing vitest tests still pass.node/package-lock.jsonwas regenerated with npm (not hand-merged) after mergingmain.Next steps
mainso CodSpeed records a baseline; subsequent pull requests will then show relative changes.