Repository navigation
fix(callgrind): model REP copies with calibrated bulk costs - #60
not-matthias wants to merge 2 commits into
Conversation
Replace per-element REP MOVS/STOS cycle estimates with measured hot-copy curves on amd64. Charge the calibrated marginal through existing Ct/Cl counters and report only REP data misses, retaining cache simulation, instruction counters, and all miss penalties. Enable the model with cache and cycle estimation by default; --rep-bulk-cost=no preserves legacy accounting. Keep the output format unchanged and document mixed-counter, overlap, cache-residency, and vector-comparison limitations. Add default and legacy runtime regressions for zero-length copies, hot/cold counters, backward overlap, stores, and self-copy.
Describe the measured offset-eight class precisely rather than implying every matching unaligned pointer pair selects that curve.
Merging this PR will improve performance by 10.56%
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
[Medium impact] The PR changes profiling results for a bounded set of amd64 Callgrind runs. Fix the wrong-curve state and zero-cost small copies before merging.
|
| dir = ((Long)dflag < 0) ? CLG_REP_DIR_BACKWARD : CLG_REP_DIR_FORWARD; | ||
| align = rep_align_class(op, src, dst); | ||
| es->rep_tail_carry = 0; /* new curve run: restart tail remainder */ |
There was a problem hiding this comment.
log_rep_bulk computes dir and align for the first element but never saves them into es->rep_dir and es->rep_align. The next element reads those fields, which remain zero, and switches to the forward/aligned curve. Every multi-element backward or unaligned copy therefore mixes curves and reports the wrong Ct/Cl total.
Save both fields when starting an operation, and add an exact-cost regression for backward and unaligned copies.
| dir = ((Long)dflag < 0) ? CLG_REP_DIR_BACKWARD : CLG_REP_DIR_FORWARD; | |
| align = rep_align_class(op, src, dst); | |
| es->rep_tail_carry = 0; /* new curve run: restart tail remainder */ | |
| dir = ((Long)dflag < 0) ? CLG_REP_DIR_BACKWARD : CLG_REP_DIR_FORWARD; | |
| align = rep_align_class(op, src, dst); | |
| es->rep_dir = dir; | |
| es->rep_align = align; | |
| es->rep_tail_carry = 0; /* new curve run: restart tail remainder */ |
Prompt To Fix With AI
This is a comment left during a code review.
Path: callgrind/sim.c
Line: 1347-1349
Comment:
**Copies use the wrong curve**
`log_rep_bulk` computes `dir` and `align` for the first element but never saves them into `es->rep_dir` and `es->rep_align`. The next element reads those fields, which remain zero, and switches to the forward/aligned curve. Every multi-element backward or unaligned copy therefore mixes curves and reports the wrong `Ct`/`Cl` total.
Save both fields when starting an operation, and add an exact-cost regression for backward and unaligned copies.
```suggestion
dir = ((Long)dflag < 0) ? CLG_REP_DIR_BACKWARD : CLG_REP_DIR_FORWARD;
align = rep_align_class(op, src, dst);
es->rep_dir = dir;
es->rep_align = align;
es->rep_tail_carry = 0; /* new curve run: restart tail remainder */
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| { 112u, 0ull }, | ||
| { 126u, 0ull }, | ||
| { 128u, 0ull }, | ||
| { 130u, 0ull }, | ||
| { 160u, 0ull }, | ||
| { 192u, 0ull }, | ||
| { 224u, 0ull }, | ||
| { 254u, 0ull }, |
There was a problem hiding this comment.
The forward, aligned rep movsw curve assigns zero total cost to every copy from 2 through 254 bytes. A hot 128-byte copy gets zero Ct/Cl: the static instruction cost is removed, every bulk difference is zero, and the data L1-hit charges are suppressed. Real copying work disappears from the cycle estimate.
Recalibrate these entries or keep a nonzero fallback where the measured total collapses to zero.
Prompt To Fix With AI
This is a comment left during a code review.
Path: callgrind/rep_bulk_table.inc
Line: 524-531
Comment:
**Small copies cost nothing**
The forward, aligned `rep movsw` curve assigns zero total cost to every copy from 2 through 254 bytes. A hot 128-byte copy gets zero `Ct`/`Cl`: the static instruction cost is removed, every bulk difference is zero, and the data L1-hit charges are suppressed. Real copying work disappears from the cycle estimate.
Recalibrate these entries or keep a nonzero fallback where the measured total collapses to zero.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
--rep-bulk-cost=norestores legacy accounting. No new output events or downstream format changes.Verification
Modeled cycles per copy, including the same loop overhead:
Modeling limits requiring review
This is deliberately a draft, not a claim of calibrated end-to-end speedups. The native 10 KiB comparison was 34.97 ns REP versus 41.46 ns AVX2, whereas the new score favors REP by about 22x. At 1 KiB native AVX2 is faster, but the model favors REP. Existing scalar/vector pricing is unchanged.
The table was measured on Zen 5; the existing static instruction LUT is Zen 4. Calibration targets disjoint L1-resident buffers, up to 16 KiB MOVS / 32 KiB STOS. Larger sizes extrapolate the final slope and retain existing miss penalties. Overlap performance is outside the calibration domain, although copy semantics are preserved. Held-out L1 samples at least 1 KiB have 0.203% median absolute relative error but 65.1% p95 and 154.8% maximum error; smaller sizes also have substantial relative-error outliers. Mixed REP/non-REP Ct/Cl aggregation retains the existing geometric-mean cross terms.
Dr/Dw become modeled miss-only references for supported REP instructions, not raw access counts. Results across this model boundary should not be interpreted as application performance changes.