dotnet / dotnet/runtime

JIT register allocation for cascading loops prioritizes outer loops over inner loops

Open
#122,026 2 comments 0 reactions 0 assignees View on GitHub
area-CodeGen-coreclr tenet-performance
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Description

Under register pressure, JIT holds registers for the outer loop and stack-saves registers in the inner loop. Save/restore would happen less often the other way around.

**r12**-**r15** and **rbx** are being used outside that outer main loop as well, feels like register allocation is "upside down". If JIT would save e.g. **r12** once around the outer loop, I get `8192 / 4 * 8192 * 8` or 134 million less save/restore operations.

Apologies for not providing a full working sample but there is a lot of simd extensions in the code.

On a side node, we've come a long way. The SIMD code generation today is highly impressive. Highly impressive. Kudos to the JIT team working on that in the last like 4-5 yrs.

### Code Fragment

``` C#
// enumerate pixels
ref var inputLine = ref input.Pin();
ref var outputLine = ref pixels.Span.Pin();

var batch = (Span) stackalloc float[8];
var count = width / 4 * height;

for (var pos = 0u ; pos < count ; pos++)
{
for (var nn = 0u ; nn < 8 ; nn++)
{
batch.At(nn) = (float) inputLine.At(nn);
}

var vi = Vector256.Create(batch) * 255; // 0…1 -> 0…255
var vo = vi.ToIntRound().ToInt16Sat().ToByteSat();

(vo.Permute(permute) | alpha).Store(ref outputLine);

inputLine = ref inputLine.At(8);
outputLine = ref outputLine.At(4);
}
```

### Disassembly

``` asm
G_M000_IG11: ;; offset=0x0127
xor edx, edx

G_M000_IG12: ;; offset=0x0129 -- inner loop (8x)
* mov qword ptr [rsp+0x50], rdx ; <-- save/restore register
mov ecx, edx
movzx rcx, word ptr [rdi+2*rcx]
call [System.Half:op_Explicit(System.Half):float]
* mov rcx, bword ptr [rsp+0x48] ; <-- and restore 2x
* mov rdx, qword ptr [rsp+0x50]
vmovss dword ptr [rcx+4*rdx], xmm0
inc edx
cmp edx, 8
jb SHORT G_M000_IG12

G_M000_IG13: ;; offset=0x0150 -- outer loop (16x)
mov rax, bword ptr [rsp+0x48] ; -- not needed, rcx always holds var batch
vmovups ymm0, ymmword ptr [rax]
vmulps ymm0, ymm0, dword ptr [reloc @RWD36] {1to8} ; -- constant 255 is not register "cached"
vcvtps2dq ymm0, ymm0
vmovaps ymm1, ymm0
vextracti128 xmm0, ymm0
vpackssdw xmm0, xmm1, xmm0
vpackuswb xmm0, xmm0, xmm0
vpshufb xmm0, xmm0, xmm6
vpor xmm0, xmm0, xmm7 ; -- var alpha is register "cached"
vmovups xmmword ptr [rbp], xmm0
add rdi, 16
add rbp, 16
inc esi
cmp esi, ebx
jb SHORT G_M000_IG11

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.