meta(amr): forest performance — the coarse–fine exchange, not regridding, is the AMR tax
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Tái cấu trúc
- Độ rõ ràng
- Cần làm rõ
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- julia
- Lĩnh vực
- hpc, performance
Hướng nghiên cứu
Start with examples/niederer_benchmark.jl and run the documented full or NIEDERER_SMOKE benchmark to reproduce the phase table and inspect examples/niederer_cost.png. Read the linked candidate issues #83, #42, #84, #85, #78, and #72 before choosing a focused task; this parent tracker is complete when the selected optimization is measured without changing the nine-point correctness results.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Status (2026-09-30): #42, #84 and #85 have landed; the phase tables below predate them and need a re-run.
The new Niederer 2011 benchmark example (examples/niederer_benchmark.jl) is the first 3D BlockForest in this repo and the first workload that times the adaptive path against a like-for-like uniform baseline. It came out 2.34× faster overall — but the phase breakdown says the overhead is not where I expected, so this issue records the numbers and the optimization candidates they point at.
What the benchmark is
Monodomain cardiac EP on a 20 × 7 × 3 mm slab, ten Tusscher–Panfilov 2006 epicardial kinetics (18 gating/concentration states per cell, carried through regrid! as one SVector{18}-valued BlockField), anisotropic diffusion D_T ∇² + (D_L − D_T) ∂ₓₓ, Godunov splitting with explicit Euler on both halves, dt = 0.01 ms to t = 60 ms. Base 48×16×8, blocksize = (8,8,8), maxlevel = 2, so the finest spacing is ≈0.1 mm — Niederer's reference resolution — and the equal-resolution uniform grid is 192 × 64 × 32 = 393,216 cells.
Correctness first, because the perf numbers only matter if the answer is right. The scored metric is activation time at nine fixed points. Against an independent finite-difference code at dx = 0.1 mm the mean |Δ| is 0.40 ms (max 0.80). Against a uniform forest at the same finest spacing, all nine points agree to 0.00 ms. Every 3D coarse–fine path — Martin–Cartwright interpolation, flux-matching restriction, the balance cascade, the regrid! transfer of both a scalar and an 18-component field — was previously unexercised, and it is exact here.
The cost breakdown
5 threads, macOS/aarch64. The example now times both runs and splits the wall clock by phase.
| phase | adaptive | uniform | speedup |
|---|---|---|---|
| diffusion | 19.1 s | 8.5 s | 0.44× |
| reaction | 12.1 s | 68.9 s | 5.70× |
| regrid | 1.9 s | — | — |
| total | 33.2 s | 77.5 s | 2.34× |
The forest carries 18.8% of the uniform cell count on average, so the identity is 5.3× fewer cell-updates ÷ 2.28× dearer per update = 2.34× net (75 ns vs 33 ns per cell-update). Slightly more than half the cell saving is handed back.
Two things fall out:
- Reaction scales as hoped. It is pointwise, tracks the cell count, and at 5.70× marginally beats the 5.3× cell ratio because the smaller working set is kinder to cache.
- Regridding is cheap. 120 regrids — marking, topology edit, transfer of two fields, schedule rebuild — cost 1.9 s, under 6% of the run. This is the thing I assumed would dominate, and it does not.
Diffusion is the problem: 2.2× slower on 5.3× fewer cells, a ~12× per-cell penalty.
Where the diffusion time goes
Timing the pieces directly on a representative adapted forest (166 leaves, levels 0–2, refined in a slab-crossing band the way the wavefront refines it) against the uniform one:
| adapted, 166 leaves | uniform, 768 leaves | |
|---|---|---|
halo_update! |
0.94 ms | 0.22 ms |
apply_bc! |
~0 ms | 0.02 ms |
one laplacian apply |
1.01 ms | 0.53 ms |
| the anisotropic apply | 2.06 ms | 1.07 ms |
| exchange share of an apply | 92% | 45% |
| per cell-update | 24.2 ns | 2.7 ns |
On an adapted forest the halo exchange is the operator — 92% of an apply is ghost-filling, because every coarse–fine face runs quadratic interpolation and flux-matching restriction instead of a slab copy. Per leaf that is ~20× the cost of a same-level exchange.
Optimization candidates
This issue is the parent tracker for AMR forest performance. The numbers above that ranked these candidates are the pre-#84/#85 phase tables — the 92% exchange share and the 0.44× diffusion figure both predate those fusions — so re-run examples/niederer_benchmark.jl and post the updated phase table before prioritizing the open items.
- #42 — make
GhostFillisbits with a static term count (the prerequisite for #84). - #84 —
_run_fills!made one broadcast pass per term: 19 terms per interpolation fill in 3D, 16 fills per coarse–fine face, ~300 broadcasts per face. Fused into one gather per fill. - #85 —
Addedexchanged once per operand on a forest: two identical exchanges per step for a two-leafAdded, which with the exchange at 92% of an apply roughly doubled diffusion cost (~9 s of the 33 s run, estimated). - #94 — the adjoint half of #84:
halo_update_adjoint!still runs one scatter-add broadcast per CSR term, and fusing it bit-identically needs a source-centric transposed CSR built at schedule time. - #78 — batch the coarse–fine flux rewrite into one device launch for packed
Diffusion. - #72 — regrid cadence, buffer zone, and
preparereuse across timesteps (where the per-stepBlockFieldallocation inexamples/monodomain_amr.jllives). - Measure packed-on-CPU:
PackedBlockFieldfor a regridding workload. The packed path's reported 30–95× wins are GPU numbers. This workload regrids every 50 steps, and the documented contract is that a packed field cannot be regridded — you regrid the reference field and re-pack. Nobody has measured whether 50 packed steps amortize a re-pack on CPU. Given #83 threads the same reference path, worth deciding before optimizing it twice.
Cross-reference — threading, tracked under #51: #83. The reference forest path is single-threaded end to end: grep -rn "Threads\.\|@spawn\|@threads" src/ returns nothing; the leaf sweeps and the exchange descriptor loops are serial for loops. Likely the single biggest lever; it shares the executor decision with #51, which is why it is parented there rather than here.
Reproducing
julia -t auto --project=examples examples/niederer_benchmark.jl # both runs, ~2 min
NIEDERER_SMOKE=true ... # seconds, reduced levels/duration
NIEDERER_SKIP_UNIFORM=true ... # adaptive only
The example prints the phase table and writes examples/niederer_cost.png (cells over time, and wall time by phase). Full write-up in docs/pages/benchmark.qmd.
One caveat on all of the above: these are single-machine, 5-thread, CPU, BlockField numbers at one problem size. The relative weight of exchange versus stencil will move with block size, level count and thread count, and #83 in particular could reorder the rest.
—
🤖 Claude ticked the boxes here. The stopwatch still points at the exchange.
claude-opus-5-5[1m] (high) · claude-code 2.1.285 · unreviewed
- Ngôn ngữ chính
- Julia
- Star
- 3
- Fork
- 1
- Merge trung bình
- 4 ngày 19 giờ
- Pull request đã merge (30 ngày)
- 5
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của RallypointOne/MatrixFreeOperators.jl
-
Roadmap: open-issue priorityĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
-
enhancement
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
-
performance
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 45/100
-
perf(forest): thread the reference BlockForest path — leaf sweeps and exchange descriptor loopsĐang mởperformance
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
Tất cả issue của RallypointOne/MatrixFreeOperators.jl
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation invalid
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 88/100
JuliaPhysics/BeamletOptics.jl#190 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
mul! with a Symmetric or Hermitian factor throws for a matrix view whose columns are not contiguousCó thể đã có người làm @dkarrasch đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
JuliaLang/LinearAlgebra.jl#1829 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
`EnsembleSummary` `ci_type = :SEM` ribbon is `sqrt(N)` too wide for scalar statesCó thể đã có người làm @isaacsas đã nhận 1 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
SciML/SciMLBase.jl#1654 ·
Maintainer thường phản hồi trong vòng 3 ngày