Skip to content

chore(benchmark): head-to-head diffusion benchmark against Chmy.jl - #68

Merged
kylebeggs merged 3 commits into
mainfrom
chore/chmy-diffusion-comparison
Aug 14, 2026
Merged

chore(benchmark): head-to-head diffusion benchmark against Chmy.jl#68
kylebeggs merged 3 commits into
mainfrom
chore/chmy-diffusion-comparison

Conversation

@kylebeggs

@kylebeggs kylebeggs commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds benchmark/comparisons/chmy_diffusion_2d/ — a head-to-head CPU benchmark against Chmy.jl v0.1.26's published examples/diffusion_2d.jl, solving an identical problem so the only variable is how each package applies the discrete operator.
  • What differs: Chmy materializes a staggered flux field across two KernelAbstractions launches; MFO fuses the 5-point stencil into a single pass. For constant χ on a uniform grid the two forms are algebraically identical, so both compute the same update.
  • Single-threaded — MFO leads at every size: 16.9× at the published 126², settling to a ~2.6× asymptote once DRAM-resident (5 array passes per step against Chmy's 7).
  • Threaded — Chmy's KA CPU backend parallelizes while MFO's single-grid CPU path is serial, so Chmy takes a middle band (peaking ~1.5× ahead at 4094²) before its extra traffic saturates memory and the two pull level at 16382². Recorded as measured; a threaded MFO (perf: CPU apply! path is single-threaded — Julia threads give zero speedup #51) plus step fusion (perf: traffic-reduction and overlap designs surfaced by the Chmy.jl comparison #52) is the answer.
  • Congruency is asserted, not assumed: final fields after 100 steps agree to max relative difference ≈ 2–4 × 10⁻¹⁶ at every size, confirming the timing gap is a design difference rather than a setup error. Seven places where the two setups are not trivially identical are enumerated in the README, each either neutralized or measured deliberately.
  • It lives under benchmark/ alongside the AirspeedVelocity suite, in its own comparisons/ subtree — cross-package measurement, kept separate from the in-repo regression benchmarks that benchpkg runs. Its Project.toml is self-contained, so benchmarks.jl is untouched.
  • results/ is gitignored; field dumps reach 2.1 GB each at 16382² and go to a temp dir.
  • Also ignores an orphaned empty package-lock.json (no package.json in this Julia repo).

Nothing under src/ changes — this is measurement infrastructure only.

Head-to-head against Chmy.jl v0.1.26's published examples/diffusion_2d.jl on an
identical problem, isolating the one thing that differs: Chmy materializes a
staggered flux field across two kernel launches, MFO fuses the 5-point stencil
into a single pass.

Single-threaded, MFO leads at every size — 16.9× at the published 126², settling
to a ~2.6× asymptote once DRAM-resident (5 array passes per step against Chmy's
7). Threaded, Chmy's KernelAbstractions CPU backend scales while MFO's CPU path
is serial, so Chmy takes a middle band. Recorded as measured rather than tuned
around; a threaded MFO (#51) plus step fusion (#52) is the answer.

Final fields agree to machine epsilon at every size, which is what confirms the
two are the same discretization and that the timing gap is design, not setup.
An empty npm lockfile (zero packages) left in the repo root by tooling. There is
no package.json here and this is a Julia package, so it is detritus rather than
a dependency record — ignore it instead of letting it sit untracked in status.
The [sources] path and the .gitignore results/ rule follow the new depth;
everything else resolves through @__DIR__ and moved unchanged.
@kylebeggs kylebeggs changed the title chore(comparisons): head-to-head diffusion benchmark against Chmy.jl chore(benchmark): head-to-head diffusion benchmark against Chmy.jl Aug 14, 2026
@kylebeggs
kylebeggs merged commit 3422171 into main Aug 14, 2026
2 checks passed
@kylebeggs
kylebeggs deleted the chore/chmy-diffusion-comparison branch August 14, 2026 16:37
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-14 16:48 UTC

@github-actions

Copy link
Copy Markdown

Benchmark Results

Time

ba2626e... 53a6997... ba2626e... / 53a6997...
forest/2D 64×32²/halo_update! 20.2 ± 0.13 μs 20.5 ± 0.11 μs 0.988 ± 0.0083
forest/2D 64×32²/laplacian mul! 0.109 ± 0.00072 ms 0.11 ± 0.00069 ms 0.985 ± 0.009
forest/2D 64×32²/laplacian mul! (packed) 0.0987 ± 0.00067 ms 0.0987 ± 0.00067 ms 0.999 ± 0.0096
forest/2D 64×32²/prepare 0.0918 ± 0.024 ms 0.0931 ± 0.039 ms 0.985 ± 0.49
forest/2D refined/halo_update! 0.195 ± 0.0038 ms 0.197 ± 0.0032 ms 0.993 ± 0.025
forest/2D refined/halo_update_adjoint! 0.221 ± 0.0071 ms 0.224 ± 0.0072 ms 0.987 ± 0.045
forest/2D refined/laplacian apply_adjoint! 1.51 ± 0.012 ms 1.51 ± 0.012 ms 1 ± 0.011
grid/2D 256²/(∂x + ∂y)ᵀ adjoint 0.772 ± 0.0028 ms 0.773 ± 0.0027 ms 0.999 ± 0.0051
grid/2D 256²/2λ + κ·I mul! 0.11 ± 0.00087 ms 0.11 ± 0.00079 ms 0.996 ± 0.011
grid/2D 256²/adjoint(∂x + ∂y) mul! 0.784 ± 0.0037 ms 0.784 ± 0.0037 ms 1 ± 0.0067
grid/2D 256²/advection mul! 0.0671 ± 0.0024 ms 0.0676 ± 0.0016 ms 0.992 ± 0.043
grid/2D 256²/divergence mul! 0.105 ± 0.00039 ms 0.105 ± 0.00036 ms 0.998 ± 0.0051
grid/2D 256²/divergenceᵀ adjoint (β ≠ 0) 0.226 ± 0.0094 ms 0.226 ± 0.0034 ms 0.997 ± 0.044
grid/2D 256²/gradient mul! 0.176 ± 0.0035 ms 0.177 ± 0.0034 ms 0.995 ± 0.028
grid/2D 256²/gradientᵀ adjoint (β ≠ 0) 0.383 ± 0.011 ms 0.385 ± 0.01 ms 0.995 ± 0.039
grid/2D 256²/laplacian mul! 0.0574 ± 0.00051 ms 0.0578 ± 0.00051 ms 0.993 ± 0.012
grid/2D 256²/laplacian prepare 5.71 ± 5.6 μs 6.23 ± 6 μs 0.917 ± 1.3
grid/2D 256²/∂x adjoint (β = 0) 0.379 ± 0.01 ms 0.379 ± 0.0099 ms 0.998 ± 0.038
grid/2D 256²/∂x adjoint (β ≠ 0) 0.406 ± 0.011 ms 0.406 ± 0.01 ms 0.998 ± 0.036
grid/2D 256²/∇·(κ∇u) mul! 0.26 ± 0.01 ms 0.26 ± 0.01 ms 0.997 ± 0.055
grid/2D 256²/∇·(κ∇u) prepare 17.5 ± 13 μs 16.8 ± 13 μs 1.04 ± 1.1
grid/3D 64³/laplacian mul! 0.321 ± 0.01 ms 0.319 ± 0.011 ms 1.01 ± 0.047
grid/3D 64³/laplacian prepare 0.115 ± 0.019 ms 0.108 ± 0.043 ms 1.07 ± 0.47
time_to_load 0.292 ± 0.0044 s 0.29 ± 0.003 s 1.01 ± 0.018

Memory and allocations

ba2626e... 53a6997... ba2626e... / 53a6997...
forest/2D 64×32²/halo_update! 0 allocs: 0 B 0 allocs: 0 B
forest/2D 64×32²/laplacian mul! 0 allocs: 0 B 0 allocs: 0 B
forest/2D 64×32²/laplacian mul! (packed) 0 allocs: 0 B 0 allocs: 0 B
forest/2D 64×32²/prepare 0.582 k allocs: 1.72 MB 0.582 k allocs: 1.72 MB 1
forest/2D refined/halo_update! 0 allocs: 0 B 0 allocs: 0 B
forest/2D refined/halo_update_adjoint! 0 allocs: 0 B 0 allocs: 0 B
forest/2D refined/laplacian apply_adjoint! 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/(∂x + ∂y)ᵀ adjoint 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/2λ + κ·I mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/2D 256²/adjoint(∂x + ∂y) mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/2D 256²/advection mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/2D 256²/divergence mul! 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
grid/2D 256²/divergenceᵀ adjoint (β ≠ 0) 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/gradient mul! 1 allocs: 0.0469 kB 1 allocs: 0.0469 kB 1
grid/2D 256²/gradientᵀ adjoint (β ≠ 0) 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/laplacian mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/2D 256²/laplacian prepare 6 allocs: 1.02 MB 6 allocs: 1.02 MB 1
grid/2D 256²/∂x adjoint (β = 0) 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/∂x adjoint (β ≠ 0) 0 allocs: 0 B 0 allocs: 0 B
grid/2D 256²/∇·(κ∇u) mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/2D 256²/∇·(κ∇u) prepare 18 allocs: 5.08 MB 18 allocs: 5.08 MB 1
grid/3D 64³/laplacian mul! 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
grid/3D 64³/laplacian prepare 6 allocs: 4.39 MB 6 allocs: 4.39 MB 1
time_to_load 0.145 k allocs: 11 kB 0.145 k allocs: 11 kB 1

Benchmark Plots

A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR.
Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).

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