Skip to content

perf(numba/sizeshape): share the foreground pass across primitives#79

Merged
timtreis merged 1 commit into
feat/numba-sizeshapefrom
perf/numba-sizeshape-shared-prep
Jun 6, 2026
Merged

perf(numba/sizeshape): share the foreground pass across primitives#79
timtreis merged 1 commit into
feat/numba-sizeshapefrom
perf/numba-sizeshape-shared-prep

Conversation

@timtreis

@timtreis timtreis commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #78. Internal optimization of the numba sizeshape lane — no behavior change.

Problem

The four primitives in core/numba/_sizeshape.py each recomputed labels_to_offsets(labels) over the full raster independently, so it ran per get_sizeshape call (moments, convex, perimeter, crofton/euler).

Fix

  • Add a _Prep NamedTuple wrapping the labels_to_offsets result (lut, n, offsets) — the only prep every primitive shares — plus _foreground_prep(labels).
  • The four primitives take an optional prep arg; _sizeshape_2d computes one prep and threads it into all four.
  • Each primitive still computes its own prep when called standalone (prep=None), so the public functions and their tests are unchanged.
  • The full foreground pixel list (rows/cols/obj nonzero) is needed only by the moment kernel, so it stays inside spatial_moments_2d — standalone perimeter_2d/crofton_euler_2d/convex_area_2d no longer pay for a full-raster nonzero they never read. convex_area_2d does its own nonzero over the boundary mask (sparse — not redundant).

Impact

Removes 3 redundant labels_to_offsets passes: ~9 ms / ~5% on the 1080² / 132-object tile.

Verification

  • ruff check + ruff format --check: clean.
  • test/test_sizeshape_numba.py: 24 passed — bit-exactness, dispatch, and edge cases unchanged.

🤖 Generated with Claude Code

…ives

The four sizeshape primitives (spatial_moments_2d, convex_area_2d,
perimeter_2d, crofton_euler_2d) each recomputed labels_to_offsets over
the full raster, so it ran 4x per get_sizeshape call. Add a _Prep
NamedTuple wrapping the labels_to_offsets result (lut, n, offsets) — the
only prep every primitive shares — plus a _foreground_prep() helper; the
primitives now take an optional `prep` arg and _sizeshape_2d threads one
prep into all four. Each primitive still computes its own prep when
called standalone (prep=None), so the public functions and their tests
are unchanged.

The full foreground pixel list (rows/cols/obj nonzero) is needed only by
the moment kernel, so it stays inside spatial_moments_2d rather than the
shared prep — standalone perimeter_2d/crofton_euler_2d/convex_area_2d no
longer pay for a full-raster nonzero they never read. convex_area_2d
does its own nonzero over the boundary mask (sparse, not redundant).

Removes 3 redundant labels_to_offsets passes: ~9 ms / ~5% on the
1080^2 / 132-object tile. 24 tests unchanged (bit-exactness, dispatch,
edge cases); ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@timtreis timtreis force-pushed the perf/numba-sizeshape-shared-prep branch from 75d10e6 to a4974d0 Compare June 6, 2026 21:25
@timtreis timtreis merged commit a4974d0 into feat/numba-sizeshape Jun 6, 2026
@timtreis timtreis deleted the perf/numba-sizeshape-shared-prep branch June 6, 2026 21:31
@timtreis

timtreis commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator Author

Folded into #78. The shared-prep optimization only touches core/numba/_sizeshape.py (a file introduced by #78), so it can't merge to main independently. Commit a4974d0 is now part of #78's branch feat/numba-sizeshape; GitHub auto-closed this PR as merged once that commit landed on its base branch. Branch deleted.

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.

1 participant