Skip to content

Unify unroll limits in a single entry point - #83274

Merged
EgorBo merged 8 commits into
dotnet:mainfrom
EgorBo:unify-unroll-limits
Mar 13, 2023
Merged

Unify unroll limits in a single entry point#83274
EgorBo merged 8 commits into
dotnet:mainfrom
EgorBo:unify-unroll-limits

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 10, 2023

Copy link
Copy Markdown
Member

The current limits were a bit odd, e.g. hard limit of 128 bytes on x64 no matter if it supports AVX or not (2x less instructions).

Also, the new limits overall match whatever native compilers do for memset/memcpy unroll in -Os (size-aware): https://godbolt.org/z/dW1qqaP9a

Closes#82529

@ghostghost assigned EgorBoMar 10, 2023
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 10, 2023
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak
See info in area-owners.md if you want to be subscribed.

Issue Details

null

Author:EgorBo
Assignees:EgorBo
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
Member

Fixes #82529?

@EgorBo

EgorBo commented Mar 10, 2023

Copy link
Copy Markdown
MemberAuthor

Fixes #82529?

Ah, didn't see this one. Yeah, it does. It zeroes S2 struct via:

xoreax,eax vxorps ymm0,ymm0 vmovdqu ymmword ptr[rdx],ymm0 vmovdqu ymmword ptr[rdx+20H],ymm0 vmovdqu ymmword ptr[rdx+40H],ymm0 vmovdqu ymmword ptr[rdx+60H],ymm0 vmovdqu ymmword ptr[rdx+80H],ymm0mov qword ptr [rdx+A0H],rax

but only with AVX or on arm64

@EgorBo
EgorBo marked this pull request as ready for review March 10, 2023 22:09
@EgorBo

EgorBo commented Mar 11, 2023

Copy link
Copy Markdown
MemberAuthor

Bencmarks:

Memset

publicunsafeclassMemsetBenchmarks{privatestaticreadonlybyte[]Data1=newbyte[1024];[Benchmark]publicvoidMemset8()=>Unsafe.InitBlockUnaligned(refData1[0],0,8);[Benchmark]publicvoidMemset10()=>Unsafe.InitBlockUnaligned(refData1[0],0,10);[Benchmark]publicvoidMemset14()=>Unsafe.InitBlockUnaligned(refData1[0],0,14);[Benchmark]publicvoidMemset16()=>Unsafe.InitBlockUnaligned(refData1[0],0,16);[Benchmark]publicvoidMemset17()=>Unsafe.InitBlockUnaligned(refData1[0],0,17);[Benchmark]publicvoidMemset20()=>Unsafe.InitBlockUnaligned(refData1[0],0,20);[Benchmark]publicvoidMemset32()=>Unsafe.InitBlockUnaligned(refData1[0],0,32);[Benchmark]publicvoidMemset33()=>Unsafe.InitBlockUnaligned(refData1[0],0,33);[Benchmark]publicvoidMemset40()=>Unsafe.InitBlockUnaligned(refData1[0],0,40);[Benchmark]publicvoidMemset50()=>Unsafe.InitBlockUnaligned(refData1[0],0,50);[Benchmark]publicvoidMemset64()=>Unsafe.InitBlockUnaligned(refData1[0],0,64);[Benchmark]publicvoidMemset65()=>Unsafe.InitBlockUnaligned(refData1[0],0,65);[Benchmark]publicvoidMemset80()=>Unsafe.InitBlockUnaligned(refData1[0],0,80);[Benchmark]publicvoidMemset90()=>Unsafe.InitBlockUnaligned(refData1[0],0,90);[Benchmark]publicvoidMemset110()=>Unsafe.InitBlockUnaligned(refData1[0],0,110);[Benchmark]publicvoidMemset128()=>Unsafe.InitBlockUnaligned(refData1[0],0,128);[Benchmark]publicvoidMemset129()=>Unsafe.InitBlockUnaligned(refData1[0],0,129);[Benchmark]publicvoidMemset200()=>Unsafe.InitBlockUnaligned(refData1[0],0,200);[Benchmark]publicvoidMemset256()=>Unsafe.InitBlockUnaligned(refData1[0],0,256);[Benchmark]publicvoidMemset257()=>Unsafe.InitBlockUnaligned(refData1[0],0,257);[Benchmark]publicvoidMemset300()=>Unsafe.InitBlockUnaligned(refData1[0],0,300);[Benchmark]publicvoidMemset400()=>Unsafe.InitBlockUnaligned(refData1[0],0,400);[Benchmark]publicvoidMemset512()=>Unsafe.InitBlockUnaligned(refData1[0],0,512);}

image

Memcpy

publicunsafeclassMemcpyBenchmarks{privatestaticreadonlybyte[]Data1=newbyte[1024];privatestaticreadonlybyte[]Data2=newbyte[1024];[Benchmark]publicvoidMemcpy8()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],8);[Benchmark]publicvoidMemcpy10()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],10);[Benchmark]publicvoidMemcpy14()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],14);[Benchmark]publicvoidMemcpy16()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],16);[Benchmark]publicvoidMemcpy17()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],17);[Benchmark]publicvoidMemcpy20()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],20);[Benchmark]publicvoidMemcpy32()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],32);[Benchmark]publicvoidMemcpy33()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],33);[Benchmark]publicvoidMemcpy40()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],40);[Benchmark]publicvoidMemcpy50()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],50);[Benchmark]publicvoidMemcpy64()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],64);[Benchmark]publicvoidMemcpy65()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],65);[Benchmark]publicvoidMemcpy80()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],80);[Benchmark]publicvoidMemcpy90()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],90);[Benchmark]publicvoidMemcpy110()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],110);[Benchmark]publicvoidMemcpy128()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],128);[Benchmark]publicvoidMemcpy129()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],129);[Benchmark]publicvoidMemcpy200()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],200);[Benchmark]publicvoidMemcpy256()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],256);[Benchmark]publicvoidMemcpy257()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],257);[Benchmark]publicvoidMemcpy300()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],300);[Benchmark]publicvoidMemcpy400()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],400);[Benchmark]publicvoidMemcpy512()=>Unsafe.CopyBlockUnaligned(refData1[0],refData2[0],512);}

image

Verified on: Core i7 8700k, Core i9 9980HK, planning to test on Ryzen 7950X

@EgorBo
EgorBo marked this pull request as draft March 11, 2023 10:38
Comment threadsrc/coreclr/jit/targetx86.h Outdated
@EgorBo

EgorBo commented Mar 11, 2023

Copy link
Copy Markdown
MemberAuthor

@dotnet/jit-contrib PTAL
This PR unifies various unrolling strategies to a single entry point and fixes some oddities, e.g. on X64 we have a hard limit 128 bytes for memset and it doesn't matter whether we can use AVX or not or only GPR (in case of GC fields).

Visible things this PR fixes:

  • ARM32 used to have memset=32b and memcpy=64b - aparently whoever set those mixed them up, I made it memset=64b, memcpy=32 (large negative diffs)
  • Changed memset to 256b (with AVX available) and memcpy to 128b on AMD64 (used to be 128/64b) - see benchmarks above + this matches clang/LLVM with -Os (size) behavior for a generic cpu -- this is also needed for faster stackalloc zeroing, see https://user-images.githubusercontent.com/523221/224337267-efa1e0c9-5684-4c53-ab52-6154106d8d80.png
  • ARM64 had a weird limit if src/dst don't point to stack.

Diffs are not too big outside of coreclr_tests collection - around +2k-3k for libraries.pmi: diffs

To improve some of them I filed:

A typical size regression looks like this:

 mov qword ptr [rbp-C8H], rdx
mov rdx, bword ptr [rbp+18H]
; byrRegs +[rdx]
- lea rcx, bword ptr [rbp-B8H]- ; byrRegs +[rcx]- mov r8d, 80- call CORINFO_HELP_MEMCPY- ; byrRegs -[rcx rdx]+ vmovdqu ymm0, ymmword ptr[rdx]+ vmovdqu ymmword ptr[rbp-B8H], ymm0+ vmovdqu ymm0, ymmword ptr[rdx+20H]+ vmovdqu ymmword ptr[rbp-98H], ymm0+ vmovdqu xmm0, xmmword ptr [rdx+40H]+ vmovdqu xmmword ptr [rbp-78H], xmm0
mov rdx, qword ptr [rbp-C0H]
+ ; byrRegs -[rdx]
mov r8, qword ptr [rbp-C8H]
lea r9, [rbp-B8H]
lea rcx, [rbp-40H]

which is 2x faster on all machines I tested. There are several cases where unrolling produces more compact code than call memset/memcpy presumably, due to reg spills.

Diffs are mostly negative for ARM64, e.g.:

image

@EgorBo
EgorBo marked this pull request as ready for review March 11, 2023 21:20
Comment threadsrc/coreclr/jit/compiler.h Outdated
@EgorBo

Copy link
Copy Markdown
MemberAuthor

@tannergooding @dotnet/jit-contrib PTAL

@EgorBo
EgorBo merged commit c861106 into dotnet:mainMar 13, 2023
@EgorBo
EgorBo deleted the unify-unroll-limits branch March 13, 2023 17:14
@EgorBo

Copy link
Copy Markdown
MemberAuthor

image

Improved parsing of doubles

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate adjusting herustics for unrolled block copies/initialization

4 participants

@EgorBo@jakobbotsch@ANahr@tannergooding