Skip to content

perf(api): an update reads and checks again only the parameters its new sources can change - #2069

Open
FBumann wants to merge 2 commits into
claude/specsolve-mathspec-update-1r2vw1from
perf/update-reads-what-changed
Open

FBumann wants to merge 2 commits into
claude/specsolve-mathspec-update-1r2vw1from
perf/update-reads-what-changed

Conversation

@FBumann

@FBumann FBumann commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Prompt: Let's work on those performance issues

Note

The following content was generated by AI.

Model.update re-read and re-checked every source through the door. It now keeps each parameter whose own source and dims' indices the update leaves alone. On top of #2065, an update of one parameter takes 0.80–0.88× the time, from the storage model at 1k columns to network at 1M.

What changed
  • tidy_sources takes kept: parameters it returned before, taken as they are, neither read nor checked again. Without it, nothing changes.
  • kept_parameters(program, tidied, changed) decides which. A parameter's checks read its own source and the index of each of its dims (a positional source reads its dim's index for its labels), so it is kept only where neither is among the names the update replaced.
  • Model.update passes the names it replaced to _fill, which asks kept_parameters of the last build's tidied frames. The first build passes nothing.
  • Dimensions, relations and the assumptions are still read and checked on every update: they are small, and an assumption may read any source.

What an update costs, measured before the change on storage at 1k columns: tidy_sources 9.6 ms, attach 1.3 ms and the build 24.3 ms of 36.9 ms. This change takes most of the first; the build is #2022's.

Probes and mutation table

tests/test_update.py:

  • test_an_update_checks_again_only_the_parameters_it_can_change, three cases on examples/dispatch.yaml: an update of load re-checks load alone; one of the snapshot index re-checks load, which is over it; one of the generator index re-checks p_max and cost.
  • test_an_update_that_drops_a_label_refuses_the_unchanged_parameter_still_over_it: snapshot loses label 3, and load, not in the update, is refused for still naming it.

The one condition in kept_parameters, by hand (sed on the line, __pycache__ dropped on both sides, git checkout -- to restore, tree clean after each), full suite:

mutation result
nothing kept (if False) caught, 3 failed
a parameter kept though one of its dims' indices changed (the dims clause dropped) caught, 3 failed
a parameter kept though its own source changed (name not in changed dropped) caught, 88 failed
A/B of the update, this container (4 cores, not the publishing machine)

Base #2065's head 792d441, head fe51b12. One fresh process per measurement, base and head alternated ABBA for three rounds. Each process builds the model once, then times 12 calls of model.update({'load': …}), each with new numbers on the same coordinates, and reports the median; the table gives the median over the three processes. Only the update is timed: no solve, no hand-off. Load average 0.90 before, 2.16 after.

model, what changes base ms head ms head ÷ base head faster in
storage 2xs (1k columns), load 38.2 33.7 0.88 2/3
storage xs (10k columns), load 40.4 33.5 0.83 3/3
network s (100k columns), load 96.2 77.4 0.80 3/3
network m (1M columns), load 385.1 319.5 0.83 3/3

Per-process medians, ms: storage 2xs base 38.2 / 34.4 / 41.8, head 42.9 / 33.7 / 31.1; storage xs base 40.6 / 38.7 / 40.4, head 34.6 / 33.5 / 33.3; network s base 93.7 / 106.3 / 96.2, head 77.4 / 77.0 / 79.3; network m base 395.2 / 385.1 / 359.8, head 303.5 / 319.5 / 319.9.

Gates at this head, on mathspec 0.4.0

No pixi in this container, so each gate ran in a uv environment synced from #2065's uv.lock (--group dev --group docs --all-extras, Python 3.12, polars 2.0.0):

  • ruff check ., ruff format --check . (0.16.9): clean.
  • pyrefly check on the package and on examples/ports/references/linopy (1.3.1): 0 errors.
  • pytest -q -n 4: 5887 passed, 458 skipped, 4 xfailed.
  • pytest tests/test_docs_math.py tests/test_docstring_links.py (docs-test): 116 passed.

Not run: docs-build, the floors environment, test-via-pyarrow.

Not done. An update that changes only numbers still runs the whole build, about two thirds of its time. Skipping that needs a path that recomputes only the bounds, right-hand sides and costs a parameter feeds; that is a design decision, not part of this PR. Stacked on #2065.


Generated by Claude Code

…ew sources can change

A parameter whose own source and whose dims' indices an update leaves alone
keeps the frame the door checked at the last build. Dimensions, relations
and the assumptions are still read and checked on every update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B3nA4oeSor7jjZ4vwux1G
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B3nA4oeSor7jjZ4vwux1G
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3cd47c18-2bdf-4856-b2d0-203bf1976545

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@codspeed

codspeed Bot commented Oct 28, 2026

Copy link
Copy Markdown

Merging this PR will regress 2 benchmarks

1 warning

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 23 improved benchmarks
❌ 2 regressed benchmarks
✅ 79 untouched benchmarks
⏩ 307 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_read[fleet-s-specsolve-parquet] 955 KB 1,121.7 KB -14.86%
❌ test_read[network-s-specsolve-parquet] 1.9 MB 2.1 MB -8.8%
⚡ test_read[storage-s-specsolve-parquet] 2 MB 1.4 MB +42.46%
⚡ test_window[milp-s-specsolve-highs-one] 22.3 MB 17.9 MB +24.64%
⚡ test_emit[fleet-s-specsolve-lp] 31.5 MB 25.6 MB +23.33%
⚡ test_emit[fleet-s-specsolve-in-memory-lp] 31.5 MB 25.6 MB +23.33%
⚡ test_emit[network-s-specsolve-lp] 34.5 MB 28.2 MB +22.37%
⚡ test_emit[network-s-specsolve-in-memory-lp] 34.5 MB 28.2 MB +22.37%
⚡ test_emit[commitment-s-specsolve-lp] 3.2 MB 2.6 MB +22.07%
⚡ test_emit[commitment-s-specsolve-in-memory-lp] 3.2 MB 2.6 MB +22.05%
⚡ test_emit[milp-s-specsolve-lp] 47.7 MB 39.8 MB +19.96%
⚡ test_emit[milp-s-specsolve-in-memory-lp] 47.7 MB 39.8 MB +19.96%
⚡ test_emit[nodal-s-specsolve-lp] 6.8 MB 5.7 MB +18.68%
⚡ test_emit[nodal-s-specsolve-in-memory-lp] 6.8 MB 5.8 MB +18.63%
⚡ test_emit[sector-s-specsolve-lp] 11.8 MB 9.9 MB +18.28%
⚡ test_emit[sector-s-specsolve-in-memory-lp] 11.8 MB 9.9 MB +18.27%
⚡ test_window[network-s-specsolve-highs-one] 13.9 MB 11.8 MB +18.22%
⚡ test_emit[storage-s-specsolve-lp] 24.5 MB 20.8 MB +17.86%
⚡ test_emit[storage-s-specsolve-in-memory-lp] 24.5 MB 20.8 MB +17.86%
⚡ test_window[sector-s-specsolve-highs-coefficient] 8.9 MB 7.7 MB +14.82%
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/update-reads-what-changed (fdeee15) with claude/specsolve-mathspec-update-1r2vw1 (792d441)

Open in CodSpeed

Footnotes

  1. 307 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants