profiling: phase-2 instrumentation and the measured result (#507) - #190
Merged
Conversation
…(#507)
Step 0 of PyAutoArray#507: instrument before optimizing, so the phase-2 levers
are chosen from measurements rather than the plan's estimates.
Two additions to both numba CPU breakdown harnesses:
1. The `MGE operated mapping matrix` row is split into the three pieces of
`LightProfileLinearObjFuncList.operated_mapping_matrix_override` -- 60x
`image_2d_from(grid)`, 60x `image_2d_from(blurring_grid)`, one batched
real-space PSF convolution -- with the pre-existing row becoming the
residual, exactly the scheme the #505 F sub-blocks use. The override is a
`cached_property` and the three pieces are recomputed uncached, so timing
them does not prime the step that follows, and the four rows sum to the raw
un-split cost (`mge_operated_mapping_matrix_total_s` in the JSON).
`mge_split_reproduces_step` records a one-off bit-identical check that the
three pieces really do reconstruct the dict the step returns, so a dataset
that took a different branch of the override would be flagged rather than
silently mis-attributed. It is True on all three cells.
2. Model-independent geometry constants are recorded once into the JSON:
the PSF shape, the sparse operator's stored-pair count and per-row
lengths (sum/mean/max), and per mapper its `params`, `data_to_pix_unique`
shape and `pix_lengths` (mean/max/sum). From those, the exact innermost
accumulation count of `curvature_matrix_via_sparse_operator_from`,
sum over stored (data_0, data_1) pairs of u0(data_0) * u1(data_1),
plus the same geometry costed for the phase-2 two-stage reformulation.
This turns the plan's "~2e8 ops" estimate into a measurement.
Measured (OMP_NUM_THREADS=1, AUTOARRAY_NUMBA_OPERATED_MEMO=0, n_repeats 10):
hst rectangular (bilinear, 784) mapper x mapper 0.2551 s MGE 0.2086 s
MGE = 0.1160 (grid eval) + 0.0460 (blurring eval) + 0.0541 (convolution)
- 0.0075 (residual); profile evaluation is 78 % of the MGE row.
mapper x mapper inner ops 1.773e8 (plan estimated ~2e8).
euclid rectangular (bilinear, 784) mapper x mapper 0.0491 s MGE 0.0704 s
hst delaunay (Hilbert, 1250) mapper x mapper 0.1304 s MGE 0.2229 s
The `pixelization_numba` euclid and hst cells and the `delaunay_numba` hst cell
are re-run and their artifacts regenerated; `build_readme.py` re-run for the
generated headline tables (only this task's three rows move).
Refs PyAutoLabs/PyAutoArray#507
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fr6iJ5T1RDARWfxttCuGkK
…ture kernel (#507)
Artifacts only -- no harness change. Re-run on PyAutoArray 88e14bc6 (#507 step 2,
the two-stage mapper x mapper source-space accumulator), same environment as the
step-0 baseline: OMP_NUM_THREADS=1, AUTOARRAY_NUMBA_OPERATED_MEMO=0, n_repeats
10, worktree PyAutoArray on PYTHONPATH, first post-rebuild run discarded.
step-by-step total before after
hst rectangular 0.6334 s 0.3869 s
euclid rectangular 0.1978 s 0.1652 s
hst delaunay 0.7344 s 0.5403 s
F: mapper x mapper block
hst rectangular 0.2551 s 0.0719 s
euclid rectangular 0.0491 s 0.0168 s
hst delaunay 0.1304 s 0.0607 s
All three pinned log likelihoods pass at rtol=1e-6 (euclid has no pin and prints
its value, unchanged at 6213.306873885871).
These harness numbers are *unpaired* across sessions, so they carry this host's
20-30 % run-to-run variance -- the MGE rows move here too, and nothing in this
task touched them. The paired same-session before/after that the speed-up claim
rests on is in the PyAutoArray commit message and on the issue.
Auto-tables regenerated (`build_readme.py`); only this task's three rows move.
Refs PyAutoLabs/PyAutoArray#507
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fr6iJ5T1RDARWfxttCuGkK
…ess fix (#507)
Appends a "Phase 2 (#507)" section to
`results/notes/numba_curvature_matrix_f_split.md` and re-runs the four numba
CPU cells on the final branch.
The headline, paired B/A/B/A in one session across three arms (phase-2 base =
PyAutoArray main @1b89404b + PyAutoGalaxy main @0fbe863d; after steps 1-2;
after step 3 in both libraries), three rounds per cell in rotating arm order
with the first discarded:
step-by-step total base step 2 final
hst rectangular 0.6214 s 0.4324 s 0.3334 s (1.86x)
euclid rectangular 0.2226 s 0.1870 s 0.1417 s (1.57x)
hst delaunay-1250 0.6331 s 0.6076 s 0.4884 s (1.30x)
F: mapper x mapper (hst) 0.2581 s 0.0819 s 0.0825 s (3.13x)
MGE total (hst) 0.1970 s 0.1937 s 0.0844 s (2.33x)
MGE 60x image_2d_from 0.1042 s 0.0995 s 0.0233 s (4.48x)
The issue's goal -- 0.60 -> ~0.35 s at HST rectangular -- is met (0.333 s
step total, 0.301 s directly timed). Rows the change does not touch hold still:
`Blurred image` 0.98-1.02x, the batched MGE PSF convolution 0.96-1.00x.
Correctness recorded in the note: all three pinned log likelihoods pass at the
explicit `rtol=1e-6` on every arm and are unchanged to every recorded digit
(euclid, unpinned, likewise); the MGE operated mapping matrix is bit-identical
base -> final; `curvature_matrix` is bit-identical for step 3 alone and agrees
to 1.24e-14 relative across the whole phase (`allclose(rtol=1e-12)` True).
Pool run (8 cores, `PYAUTO_TEST_MODE=1`, `PYAUTO_SMALL_DATASETS` unset, 2828
masked pixels, 60-Gaussian linear MGE bulge): per evaluation 0.2000 -> 0.1697 s
in a pool of 8 and 0.4099 -> 0.3634 s serial, so the parallel speed-up ratio is
2.05x -> 2.14x. Flat to slightly up is the pass condition -- it is what would
fall if a lever had introduced hidden threads.
**Harness fix (the one behaviour change here).** The
`MGE: image_2d_from(blurring_grid) x60` row hand-rolled the per-profile loop
that `operated_mapping_matrix_override` used to contain. PyAutoArray#507 step 3b
moved that stack behind `LightProfileLinearObjFuncList._image_slim_list_from`,
so the hand-rolled loop would have kept timing the *old* code on the new library
and mis-attributed the entire win to the residual row. `_mge_blurring_stack` now
calls the same path the override calls, dispatching on the attribute so both
libraries stay measurable, and records which branch it took as
`mge_blurring_stack_path` in the result JSON (`per_profile_loop` on the base
arms, `shared_geometry` on the final artifacts). `mge_split_reproduces_step`
stays True on all four cells.
Artifacts re-run on the final branch: `pixelization_numba` hst + euclid,
`delaunay_numba` hst, and `pixelization_numba` hst `--rect-mesh rtu` once for
currency (unpaired; it is a 7.1 s cell dominated by its 6.73 s mapper-sparse-
triplets step and is GPU-only by the 2026-08-28 decision). Auto-tables
regenerated with `build_readme.py`, `--check` clean; only this task's three rows
move.
Refs PyAutoLabs/PyAutoArray#507
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fr6iJ5T1RDARWfxttCuGkK
`ruff format --check .` is a lint-CI gate in this repo and the step-0 commit (`3f69b34`) left both harnesses failing it -- the branch had not been PR'd yet, so nothing had run the gate. `origin/main` is clean; only these two files regressed, and only in the blocks step 0 and step 4 added. Formatting only (line-length 100 collapses of list comprehensions and call chains); no statement, timing or artifact changes. `ruff check .` and `build_readme.py --check` were already clean and stay clean; both harnesses still pass the `AUTOLENS_PROFILING_SMOKE=1` import path the lint job runs. Refs PyAutoLabs/PyAutoArray#507 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fr6iJ5T1RDARWfxttCuGkK
This was referenced Aug 28, 2026
perf(numba): the mapper x mapper block and the MGE operated matrix (#507)
PyAutoLabs/PyAutoArray#508
Merged
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.
Summary
The measurement half of PyAutoLabs/PyAutoArray#507 phase 2: instrument first,
then record the result.
Step 0 split the
MGE operated mapping matrixrow into its three pieces plus aresidual (the same residual scheme the phase-1 F rows use, so the four rows sum
to the un-split step) and recorded the sparse operator's model-independent
geometry into every result JSON. That checkpoint is what chose the phase's two
levers: profile evaluation is 78 % of the MGE row, not the PSF convolution,
and the mapper x mapper kernel does exactly 1.773e8 inner accumulations at
the HST fiducial (the plan had estimated "~2e8").
Step 4 appends the phase-2 section to
results/notes/numba_curvature_matrix_f_split.md— the step-0 split, theper-step paired tables, the measured two-stage crossover sweep, the pins, the
pool ratio, and what was deliberately not paired — and re-runs the four cells on
the final branch.
1b89404b)Three arms, paired B/A/B/A in one session, three rounds per cell in rotating arm
order with the first discarded (numba
cache: truerecompiles on an armswitch), mean of the last two.
Scripts Changed
scripts/imaging/likelihood_breakdown/pixelization_numba.py— step 0: splitthe MGE row into
MGE: image_2d_from(grid) x60,MGE: image_2d_from(blurring_grid) x60andMGE: batched PSF convolution, with thepre-existing row becoming the residual;
mge_split_reproduces_steprecords aone-off bit-identical check that the three pieces reconstruct the step's
output;
_geometry_constantswrites the PSF shape, the sparse operator'sstored-pair count and row lengths, and per mapper its
params,data_to_pix_uniqueshape andpix_lengthsinto the JSON, plus the exactinner-operation count of both kernel forms. Step 4:
_mge_blurring_stacknow calls the same path
operated_mapping_matrix_overridecalls instead ofhand-rolling the per-profile loop — after perf(linear): share one eccentric-radius grid across an MGE basis (PyAutoArray#507) PyAutoGalaxy#590 the
hand-rolled version would have kept timing the old code on the new library
and mis-attributed the win to the residual row. It dispatches on the attribute
so both libraries stay measurable and records which branch it took as
mge_blurring_stack_path.scripts/imaging/likelihood_breakdown/delaunay_numba.py— the same twochanges, kept in sync as
STEP_ACCESSORShas been throughout.results/notes/numba_curvature_matrix_f_split.md— new "Phase 2 (#507)"section.
results/breakdown/imaging/{pixelization_numba_breakdown_hst, pixelization_numba_breakdown_hst_rtu, pixelization_numba_breakdown_euclid, delaunay_numba_breakdown_hst}_v2026.8.17.1.{json,png}— re-run on the finalbranch.
README.md,scripts/misc/likelihood_breakdown/README.md— auto-tablesregenerated by
build_readme.py; only this task's three rows move.ruff formaton the two harnesses: the step-0 commit hadleft them failing this repo's
ruff format --checklint gate (the branch hadnot been PR'd, so nothing had run it).
origin/mainwas and is clean.Upstream PR
OverSamplerdivisor cache.
basis.
Merge order: PyAutoArray#508 -> PyAutoGalaxy#590 -> this PR. The note and the
artifacts here describe library behaviour that must already be on
main.Test Plan
ruff check .— All checks passed.ruff format --check .— 181 files already formatted (was 2 failingbefore the format commit).
python scripts/misc/tooling/build_readme.py --check— clean, i.e.idempotent after the regeneration.
AUTOLENS_PROFILING_SMOKE=1on both changed harnesses — imports + modulesetup OK, which is what the lint job runs.
artifacts come from);
mge_split_reproduces_stepis True on every one, andmge_blurring_stack_pathreadsshared_geometryon the final artifactsand
per_profile_loopon the base arms.rtol=1e-6PASSED on allthree pinned cells and are unchanged to every recorded digit.
Heart
Heart is RED at ship time for a pre-existing reason unrelated to this task.
Verbatim from
pyauto-heart readiness --json(2026-08-28T21:34:42Z, score 45):red_reasons:"release validation FAILED (stage integrate)"yellow_reasons:"workspace validation not passing (2 failed, cloud#33179766004: autolens_test scripts/imaging/rectangular_mge.py, autolens_test scripts/imaging/rectangular_mge_rtu.py)","manifest drift: session-start hooks (generated) — 32 mismatch(es) vs PyAutoMind/repos.yaml"Human authorisation to ship over it, given 2026-08-28, verbatim:
Generated by the PyAutoLabs agent workflow.