Skip to content

Make bounded scalar folds data-first - #8

Merged
PraneethMerugu merged 1 commit into
mainfrom
codex/data-first-fold
Sep 7, 2026
Merged

Make bounded scalar folds data-first#8
PraneethMerugu merged 1 commit into
mainfrom
codex/data-first-fold

Conversation

@PraneethMerugu

Copy link
Copy Markdown
Owner

Summary

  • add the single data-first LocalMath.fold(values; ...) authoring operation
  • retain checked BoundedFold(T, ...) and evaluate_bounded as compiler contracts
  • close map, accumulator, and result types for compiler construction
  • preserve Candidate and OrderedFold validation barriers for rejecting folds
  • delete the author-facing bounded_fold spelling without an alias

Validation

  • focused authoring, ordered-fold, and API tests pass
  • full LocalMath package test is running
  • coordinated CorePotts and Potts consumer branches will be qualified before the breaking train merges

@PraneethMerugu
PraneethMerugu changed the base branch from codex/empty-compacted-storage to main September 7, 2026 00:08
@PraneethMerugu
PraneethMerugu merged commit 687b16c into main Sep 7, 2026
6 checks passed
@PraneethMerugu

Copy link
Copy Markdown
Owner Author

Post-merge compiler measurement follow-up

These measurements close the requested engineering notes; they are not timing gates. Julia 1.12.6, Apple M1, one Julia thread, KernelAbstractions 0.9.42, Statistics 1.11.5. Each column used a fresh Julia process with existing package caches and an isolated temporary Pkg environment. No repository Project/Manifest or production code was changed.

The small witness has two source items, four Float32 input values [1,2,3,4], a fixed two-lane relation with endpoints reshape(Int32[1,2,3,4],2,2), and one Float32 output Field. Every run checks [3,7] independently before taking nine warm samples of wait(execute!(prepared)).

Measurement Pre-train callable fold (7b0cdb8) Data-first fold (b699002a) Familiar sum (b699002a)
Cold construction, s 0.03119 0.02006 0.02090
Cold binding, s 0.30239 0.28124 0.29308
Cold planning, s 3.05607 3.00710 2.93716
Cold preparation, s 1.44957 1.30180 0.66376
First execution, s 0.10826 0.09599 0.09617
First execution Julia compilation, s 0.10790 0.09562 0.09585
Warm median, μs 4.584 4.458 4.000
Warm host allocated bytes 2624 2608 1536
Warm compilation / recompilation 0 / 0 0 / 0 0 / 0
Complete provider launches 5 5 2
Workspace bytes 92 92 24
Optimized typed-IR statements in selected stage evaluator 252 250 234

All three evaluators are concrete/isbits and infer UniqueValue{Float32}. The rejecting positive-domain custom fold retains Candidate reset → evaluation → validation → finalization/publication (plus program reset). sum has different empty/domain semantics and is infallible here, so it qualifies for the existing direct Unique phase plus program reset. The sum column is an API/physical-path witness, not an assertion that these operations have identical invalid-input semantics. CPU compilation is measured separately from warmed execution; these timings make no Metal latency claim. Real-Metal correctness is covered by the package suite and final ecosystem qualification.

Reproduction: develop the selected LocalMath checkout temporarily, add KernelAbstractions and Statistics to that temporary environment, and use the public authoring fixture below. Run the three spellings in separate processes; the legacy spelling is used only when checking out the historical baseline.

using LocalMath, KernelAbstractions, Statistics, InteractiveUtils

function make_law(source, values, output, neighbors)
    @localmath i  source begin
        gathered = samples(values[neighbors(i)])
        output[i] = LocalMath.fold(gathered;
            combine=+, init=0.0f0, domain = >(0.0f0))
    end
end

source = Space(2)
values_space = Space(4)
values = Field(values_space, Float32)
output = Field(source, Float32)
neighbors = FixedRelation(source => values_space; degree=2)
law = make_law(source, values, output, neighbors)
bound = LocalMath.bind(law,
    values => Float32[1,2,3,4], output => zeros(Float32,2),
    neighbors => reshape(Int32[1,2,3,4],2,2))
planned = LocalMath.plan(bound; backend=CPU())
prepared = prepare(planned)
wait(execute!(prepared))
@assert LocalMath.storage(prepared, output) == Float32[3,7]

Time construction, binding, planning, preparation, and first execution separately with @timed, with GC.gc() before each cold phase. Take warm samples inside a specialized Julia function after warming that same function. Read launch/workspace facts from LocalMath.inspect(prepared). For the typed-IR figure, within the LocalMath-owned checkout use code_typed on the stage evaluator and its selected callback signature reported by inspection; this measures that method, not transitive compiler IR.

For the historical case, construct LocalMath.bounded_fold(identity, +, 0f0, (total,count)->total; domain=LocalMath.Where(>(0f0))) outside the authored evaluator and call it on gathered. For familiar reduction, substitute sum(gathered).

Interpretation: no extra fold launch family, no warm compilation/recompilation, no warm-host allocation regression in this witness, and concrete evaluator results. Single-run cold differences are descriptive, not a general speedup claim.

@PraneethMerugu

Copy link
Copy Markdown
Owner Author

Existing four-stage compiler benchmark: baseline/current follow-up

Ran the repository-owned workload, unchanged, in fresh Julia 1.12.6 processes with one thread, KernelAbstractions 0.9.42, on Apple M1:

julia --project=<temporary-environment> --startup-file=no \
  <checkout>/benchmark/compiler_scaling/stage_scaling.jl \
  --stages=4 --items=16 --warm-samples=9

Temporary environments used Pkg.develop(path=<checkout>); no source/project/manifest change was committed. Baseline: 7b0cdb8da04190526f6d301a4bfd8d19c4b0a667. Current: b699002a05f84e240e34162d509d6b952bf7d437.

Measurement Baseline, first / repeated Current, first / repeated
Cold planning, s 6.82073 / 7.06206 7.25458 / 6.91116
Cold preparation, s 6.01101 / 6.23040 6.32360 / 6.22393
First execution, s 0.10251 / 0.10240 0.10742 / 0.10125
Warm median, μs 37.167 / 35.916 36.667 / 35.541
Warm host bytes, both runs 10400 10400
Warm Julia compilation/recompilation, both runs 0 / 0 0 / 0
Complete provider launches, both runs 20 20

The repeated pair ran sequentially with the other compiler measurement process stopped. The initially observed ~6% cold difference did not persist: repeated preparation is effectively the same, and planning varied in the opposite direction. These few runs are descriptive compiler-health evidence, not a speedup claim or timing acceptance threshold. Physical phases, warm allocation, and warm compilation remain unchanged for the existing Unique/Reduce/Resolve/Collect workload. No benchmark-specific optimization or production change was made.

The focused fold/reduction evaluator measurements (including selected optimized typed IR) are in #8 (comment) .

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