perf(int32): auto-widen labels to int64 on overflow instead of raising#822
perf(int32): auto-widen labels to int64 on overflow instead of raising#822FabianHofmann wants to merge 5 commits into
Conversation
|
@FabianHofmann I would say auto-widening should stay a convenience. Users should get a Warning. |
|
@FBumann good point, doing this now |
Replace the int32 overflow ValueError in add_variables/add_constraints with a monotonic, sticky widen of options["label_dtype"] to int64. Once a model crosses ~2.1 billion labels the process uses int64 labels everywhere (surviving options.reset()), so no cast site can silently narrow an int64 label back to int32. Mixed int32/int64 arrays upcast on concat, so previously-allocated int32 batches stay valid.
read_netcdf reconstructs label arrays directly and restores the counters without going through the add_variables/add_constraints widen path, so a model that had auto-widened to int64 would load with int64 label arrays while the process option stayed int32 -- the silent-downcast corner, reachable via read_netcdf and solve(remote=...). Widen the option on load whenever a restored counter exceeds the int32 maximum.
…odels Address review: keep auto-widening a convenience but emit a UserWarning at the int32->int64 transition, advising users to set label_dtype=int64 upfront (avoids the mid-build upcast, faster and lighter).
244e37c to
d338858
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
|
We need to rerun the codpseed after master finished with the original in32 PR |
do you know how to retrigger it? |
|
@FabianHofmann pushing an empty commit |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@FabianHofmann I think if we want to have an auto-widening that doesnt risk silent corruption. Therefore we need to store the labels dtype on the model, read from global options on creation, and optionally overwritable. Without it we will have issues with:
Also, Expressions actually always have a related Model (cant be detachted!) |
I argue that auto-adjustment of dtypes is the way to go and not over-complex in the implementation. this allows user to go wild with their problem sizes.
Note
The following content was generated by AI.
Stacked on #566 (
perf/int32). Answers the "auto-adjust the dtype once the boundary is hit" question from #566's review, replacing the hardValueErroroverflow guard with automatic widening.What changed
When
add_variables/add_constraintswould push the label counter past the int32 maximum (~2.1 billion), instead of raising,options["label_dtype"]is widened toint64:options.reset()(a newOptionSettings.widen_label_dtype()moves the default too), so once any model in the process mints a>2^31label,int64becomes the floor everywhere for the rest of the process. Revert explicitly withoptions["label_dtype"] = np.int32.Why sticky-global rather than per-model
The four float→int cast sites in
expressions.py/common.save_joinrun during expression building and have no reliable model reference (expressions can be detached). A per-model dtype would leave those sites able to silently narrow an already-int64label back toint32; guarding them with a per-array.max()would tax the exact build path #566 speeds up. Reading the process-global keeps them correct at zero hot-path cost, and the monotonic+sticky invariant guarantees no array is ever asked to narrow int64 values.Previously-allocated
int32batches stay valid (they only ever held small values) and upcast toint64on concat — verified that a widened label2147483648survives avariables.flat/constraints.flatround-trip without wrapping.Trade-off
A process that builds one
>2.1B-label model then many small ones keepsint64labels throughout (losing the int32 memory/speed win for the small ones). Documented in the release note and user-overridable.Tests
test/test_dtypes.py: the twotest_overflow_guard_*tests (which assertedValueError) are replaced withtest_auto_widen_variables/test_auto_widen_constraints(assert labels becomeint64and the option flips) andtest_auto_widen_is_sticky(widen survivesreset). Arestore_label_dtypefixture resets the sticky global so the widen tests don't leak into the rest of the suite.