Conversation
|
Azure Pipelines: Successfully started running 6 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
@dhartglassMSFT fyi @Garume how is this different than the prototype patch on #134538 (expand at bottom of initial comment). Seems like it handles fewer cases? |
|
Yes, this covers fewer cases than your prototype. I restricted it to the I've now compared the exact prototype and this PR on the same base (
Both replayed without misses or errors. The expanded regression tests also passed with Checked and Release JITs, including separate Checked runs with Here are the new BDN results. Each value is the average of three launch means, in ns/op; lower is faster.
Apple M4 Pro, macOS 26.5.1, BDN MADD was slower in all three prototype launches: 0.6709-0.6897 ns, versus 0.5055-0.5172 ns for the baseline. The timed method differs only by the removed self-move; the BDN loops are unchanged. I haven't identified the CPU-level cause. The UDIV difference is less conclusive: the A/A run also varied by 3.26%. AndImmediate produced identical code in the baseline and this PR, yet their measured means differed by about 4.29%. Background services were still running, and BDN could not elevate process priority. Small differences in this table should therefore be treated cautiously. These are tiny microbenchmarks, not application throughput results. Those measurements explain why I held back UDIV and MADD, but they don't justify leaving out all the other forms. Individual launch means, including calibrationA = baseline; B = this PR; C = issue prototype. Values are launch means in ns/op. The first two passes are A/A calibration and are excluded from the aggregate table.
MADD benchmark and actual timed assemblyThe measured method and its input fields, excerpted from internal uint left = 0xffff_ffffU;
internal uint right = 3U;
internal uint third = 5U;
[Benchmark]
public ulong MultiplyAdd()
{
uint result = unchecked(left * right + third);
return result;
}Baseline and this PR (32 bytes): stp fp, lr, [sp, #-0x10]!
mov fp, sp
ldp w1, w2, [x0, #0x10]
ldr w0, [x0, #0x18]
madd w0, w1, w2, w0
mov w0, w0
ldp fp, lr, [sp], #0x10
ret lrIssue prototype (28 bytes): stp fp, lr, [sp, #-0x10]!
mov fp, sp
ldp w1, w2, [x0, #0x10]
ldr w0, [x0, #0x18]
madd w0, w1, w2, w0
ldp fp, lr, [sp], #0x10
ret lrThe surrounding BDN workload loops were identical across all three variants. This excerpt omits the unchanged fixture setup; the complete source is preserved separately. Recorded benchmark commandThis is the baseline command from the first comparison pass, after A/A calibration. The other passes substitute the corresponding CoreRun host and a separate output directory. All three hosts use the same CoreRun and libraries; only the JIT differs. Paths below are local to the measurement machine. /Users/Shared/perf-lab/perfbuild/runtime-jit-134538/base/.dotnet/dotnet /Users/Shared/perf-lab/perfbuild/runtime-jit-134538/throughput-1/fixture/bin/Release/net11.0/Validate.dll --filter DirectBench.Add DirectBench.Divide DirectBench.Load32 ExpandedBench.AndRegister ExpandedBench.AndImmediate ExpandedBench.MultiplyAdd --coreRun /Users/Shared/perf-lab/perfbuild/runtime-jit-134538/pr-validation/hosts/base-release/corerun --cli /Users/Shared/perf-lab/perfbuild/runtime-jit-134538/base/.dotnet/dotnet --artifacts /Users/Shared/perf-lab/perfbuild/runtime-jit-134538/prototype-review/timing-2-A/bdn --warmupCount 5 --iterationCount 10 --iterationTime 200 --launchCount 1
|
Related to #134538.
A 32-bit ALU instruction already clears the upper half of its destination register, so a following
mov Wd, Wdcan be redundant. This extends the existing load check inemitter::IsRedundantMovto theIF_DR_3Aforms ofadd,mul,lsl,eor, andand.For example, the sequence for
(ulong)(left + right), withuintoperands, changes from:to:
The check requires a 32-bit producer writing the same general-purpose register, and uses the existing instruction-boundary guard. It excludes SP and checks the instruction format as well as the opcode. This is a partial implementation of the issue: other ALU opcodes and immediate forms are left for a separate change.
The tests cover unsigned results with bit 31 set, overflow, shift counts, and AND operands. They also check that signed widening still sign-extends and that 64-bit results retain their upper bits unless explicitly narrowed.
Validation
Tested on macOS ARM64 against
a5f96c5d7120969f9e22871a763c3d30567c7d84:SuperPMI asmdiffs with the Checked JITs and loop alignment enabled:
realworld.run.osx.arm64.checkedbenchmarks.run.osx.arm64.checkedBoth JITs replayed every context successfully, with no misses or errors. The collections can contain overlapping methods. The full runtime test suite has not been run locally.
These are code-size results. No whole-workload throughput improvement is claimed.
Developed with Codex assistance.