perf(persistent): store tz identity in coordinate snapshots - #960
Open
olivier-lacroix wants to merge 2 commits into
Open
olivier-lacroix wants to merge 2 commits into
olivier-lacroix wants to merge 2 commits into
Conversation
_coord_snapshot used np.asarray, which on tz-aware DatetimeIndex coordinates materializes an object array of Timestamps - one Python object per coordinate per container, on every capture and again on every diff. Coordinates are only consumed by _coords_equal, so each coordinate is now stored as a (tz_key, UTC-ns datetime64) pair: the conversion is vectorized, and the tz key keeps equality semantics exact - naive never equals tz-aware (matching pandas), and a tz identity change triggers COORD_REINDEX (conservative rebuild). Measured on a 35,040-step, 8-container LP: solver build + snapshot 121.5 -> 34.1 ms; warm-start diffs shed their per-chunk Timestamp materialization. Regression tests cover tz-aware roundtrips, DST boundaries, naive-vs-aware and different-tz-same-instants.
Merging this PR will degrade performance by 19.49%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | test_to_lp[qp-n=1000] |
1.9 MB | 2.6 MB | -24.24% |
| ❌ | test_to_lp[nodal_balance_sparse-severity=50] |
2.8 MB | 3.7 MB | -24.11% |
| ❌ | test_to_lp[merge_balance-severity=0] |
2.7 MB | 3.3 MB | -18.26% |
| ❌ | test_to_lp[nodal_balance-severity=50] |
3.3 MB | 3.7 MB | -10.59% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing olivier-lacroix:perf/tz-aware-coord-snapshot (2dfc3e6) with master (718c0c1)
Footnotes
-
181 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. ↩
olivier-lacroix
marked this pull request as ready for review
September 20, 2026 02:36
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 PR improves speed when using tz-aware indexes and persistent solve machinery
Note
The following content was generated by AI.
Changes proposed in this Pull Request
_coord_snapshotsnapshotted coordinates withnp.asarray, which on tz-awareDatetimeIndexcoordinates triggers pandas' object-array fallback: oneTimestampPython object per coordinate per container — on every snapshot capture, and again on
every warm-start diff (the diff re-snapshots each container). On a 35,040-step,
8-container model that is ~280,000 Timestamps per solver build.
Snapshots are only consumed by
_coords_equal(array equality), so each coordinate isnow stored as a
(tz_key, UTC-ns datetime64 array)pair:idx.tz_convert(None).to_numpy()), andone (matching pandas), and a tz identity change now triggers
COORD_REINDEX: theconservative direction, since a rebuild is always safe while an in-place update
against re-labeled coordinates would not be.
Coordinate-snapshot equality semantics
DST needs no special handling: pandas stores tz-aware indexes as UTC internally and
tz_convert(None)drops the tz without re-localizing, so ambiguous/nonexistent walltimes — resolved at index construction — play no role at snapshot time.
Micro-benchmark
The script at the bottom of this description times the
snapshot path directly — no solver involved: 500,000 steps, 8 containers (4 variables
DatetimeIndex), tz-aware vs naive index, interleavedA/B (2 rounds per side). The naive pass is the control:
np.asarraywas alreadyvectorized there, so its timings should be flat across versions — and they are.
Repro script
Checklist
AGENTS.md).doc.doc/release_notes.rstof the upcoming release is included.