Uh oh!
There was an error while loading. Please reload this page.
Use simd for small prolog zeroing (ia32/x64) - #32442
Conversation
At R2R changes prologue for G_M41002_IG01:subrsp,96movrsi,rcxleardi,[rsp+28H]movecx,12xorrax,raxrepstosdmovrcx,rsimov qword ptr [rsp+58H],rdxmovrbx,rcxmovrsi,rdxmovrdi,r8 ;; bbWeight=1 PerfScore 31.50to subrsp,96xorpsxmm4,xmm4movdqu xmmword ptr [rsp+28H],xmm4movdqu xmmword ptr [rsp+38H],xmm4movdqu xmmword ptr [rsp+48H],xmm4mov qword ptr [rsp+58H],rdxmovrbx,rcxmovrsi,rdxmovrdi,r8 ;; bbWeight=1 PerfScore 8.33 |
At Jit time (when it can use longer subrsp,96movrsi,rcxleardi,[rsp+20H]movecx,16xorrax,raxrepstosdmovrcx,rsimovrsi,rcx ;; bbWeight=1 PerfScore 32.00to subrsp,96vzeroupper vxorps ymm4,ymm4 vmovdqu ymmword ptr[rsp+20H],ymm4 vmovdqu ymmword ptr[rsp+40H],ymm4movrsi,rcx ;; bbWeight=1 PerfScore 8.83 |
benaadams
commented
Feb 17, 2020
|
subrsp,96movrsi,rcxleardi,[rsp+20H]movecx,16xorrax,raxrepstosdmovrcx,rsi ;; bbWeight=1 PerfScore 30.75To subrsp,96xorpsxmm4,xmm4movdqu xmmword ptr [rsp+20H],xmm4movdqu xmmword ptr [rsp+30H],xmm4movdqu xmmword ptr [rsp+40H],xmm4movdqu xmmword ptr [rsp+50H],xmm4movrsi,rdx ;; bbWeight=1 PerfScore 8.58Though at Tier1 that would convert to a smaller form of subrsp,96 vxorps ymm4,ymm4movdqu ymmword ptr [rsp+20H],ymm4movdqu ymmword ptr [rsp+40H],ymm4movrsi,rdx |
tannergooding
commented
Feb 17, 2020
The
and
Neither look to be listed for a specific micro-architecture. Since the current cache line size is 64-bytes and I believe we only guarantee 16-byte stack alignment there would be a decent chance of crossing the cache line boundary for 2x YMM writes. |
tannergooding
commented
Feb 17, 2020
benaadams
commented
Feb 17, 2020
Only pointer sized alignment? e.g 8 byte on x64
Still going to hit it with 4x XMM writes; its only removing 1 chance in a 32 byte segment (by having 2x 16 so a split in the middle).
Since the issue happens at page boundaries and the stack is 8 byte aligned, the chance is ever so slightly lower at 2 in 4096 rather than 3 in 4096?
Choose 4 x SIMD size due to the code gen size rather than the performance; only using |
tannergooding
commented
Feb 17, 2020
No, I believe the stack (for TYP_SIMD) has some special support and will be 16-byte aligned (maybe @CarolEidt could confirm while I try to find the relevant code).
Ah, I see; makes sense. |
Could we align to 32-byte for 64bit ? (And then not use Avx on 32-bit, not sure if its supported there?) That would also make |
tannergooding
commented
Feb 17, 2020
There is some information on the stack frame alignment here: https://github.com/dotnet/runtime/blob/master/src/coreclr/src/jit/lclvars.cpp#L4327 and here: https://github.com/dotnet/runtime/blob/master/src/coreclr/src/jit/lclvars.cpp#L6256 IIRC, we only properly align (on the stack and only for x64) |
5d056d2 to
89ae26bCompareAndyAyersMS
commented
Feb 17, 2020
ABI guarantees that the stack has 16 byte alignment for x64, both on Windows and SysV, outside of the prolog and epilog, for non-leaf methods. The local var area is likewise guaranteed by the jit to be 16 byte aligned, see eg |
benaadams
commented
Feb 17, 2020
So it should be possible to 32 byte align on x64 to avoid any page split penalties in the |
benaadams
commented
Feb 17, 2020
Change with a larger struct privatereadonlyMemory<byte>_expected=newbyte[5];publicboolReadOnlySequenceEqual(ReadOnlySequence<byte>ros){varspan=ros.FirstSpan;varexpected=_expected.Span;return(span.Length==expected.Length)?SequenceEqualFast(span,expected):SequenceEqualSlow(ros,expected);}[MethodImpl(MethodImplOptions.NoInlining)]publicstaticboolSequenceEqualFast(ReadOnlySpan<byte>input,ReadOnlySpan<byte>expected)=>true;[MethodImpl(MethodImplOptions.NoInlining)]publicstaticboolSequenceEqualSlow(ReadOnlySequence<byte>input,ReadOnlySpan<byte>expected)=>true;From subrsp,168vzerouppermovrsi,rcxleardi,[rsp+28H]movecx,32xorrax,raxrepstosdmovrcx,rsimovrdi,rcxmovrsi,rdx ;; bbWeight=1 PerfScore 36.25To subrsp,168vzeroupper vxorps ymm4,ymm4 vmovdqu ymmword ptr[rsp+28H],ymm4 vmovdqu ymmword ptr[rsp+48H],ymm4 vmovdqu ymmword ptr[rsp+68H],ymm4 vmovdqu ymmword ptr[rsp+88H],ymm4movrdi,rcxmovrsi,rdx ;; bbWeight=1 PerfScore 14.08 |
AndyAyersMS
commented
Feb 18, 2020
We currently don't have enough guarantees -- we'd need to implement "dynamic alignment" or similar, which comes with its own set of tradeoffs. |
So on the one hand crossing a page should be rareish; but on the other hand if you do cross a page and its in you hot-path range then will be continuously crossing a page as the stack unwinds and rewinds which would then make it very common. (i.e. once you are locked into crossing a page, it will happen frequently) Hmm... @tannergooding as you highlight that probably is quite a problem 😢 Some of the structs are pretty big (e.g. Don't want to add a branch to align... Could start stack sizes >= 64 unconditionally with a However that would over zero the stack by 16 bytes when unaligned; would that be a problem? (e.g. are there stack markers to detect overruns which would get upset if they were zeroed?) |
benaadams
commented
Feb 18, 2020
Talking of G_M25757_IG01:pushrbppushr15pushr14pushr13pushr12pushrdipushrsipushrbxsubrsp,216vzeroupperlearbp,[rsp+110H]movrsi,rcxleardi,[rbp-D8H]movecx,40xorrax,raxrepstosdmovrcx,rsimov qword ptr [rbp-E8H],rspmov gword ptr [rbp+10H],rcxmov bword ptr [rbp+18H],rdxmov bword ptr [rbp+28H],r9movesi,r8d ;; bbWeight=1 PerfScore 40.50G_M25757_IG02:movrcx, bword ptr [rbp+18H]movrdx, gword ptr [rcx]movrcx, bword ptr [rbp+18H]movrdi, gword ptr [rcx+8]movrcx, bword ptr [rbp+18H]movebx, dword ptr [rcx+16]andebx,0xD1FFAB1Emovrcx, bword ptr [rbp+18H]movr14d, dword ptr [rcx+20]andr14d,0xD1FFAB1Ecmprdx,rdije SHORT G_M25757_IG10 ;; bbWeight=1 PerfScore 13.75Bunch of busy work with With subrsp,216vzeroupperlearbp,[rsp+110H] vxorps ymm4,ymm4 vmovdqu ymmword ptr[rbp-D8H],ymm4 vmovdqu ymmword ptr[rbp-B8H],ymm4 vmovdqu ymmword ptr[rbp-98H],ymm4 vmovdqu ymmword ptr[rbp-78H],ymm4 vmovdqu ymmword ptr[rbp-58H],ymm4However only using subrsp,216vzeroupperlearbp,[rsp+110H] vxorps xmm4,xmm4 vmovdqu xmmword ptr [rbp-D8H],xmm4 vmovdqu xmmword ptr [rbp-C8H],xmm4 vmovdqu xmmword ptr [rbp-B8H],xmm4 vmovdqu xmmword ptr [rbp-A8H],xmm4 vmovdqu xmmword ptr [rbp-98H],xmm4 vmovdqu xmmword ptr [rbp-88H],xmm4 vmovdqu xmmword ptr [rbp-78H],xmm4 vmovdqu xmmword ptr [rbp-68H],xmm4 vmovdqu xmmword ptr [rbp-58H],xmm4 vmovdqu xmmword ptr [rbp-48H],xmm4 |
Now produces: subrsp,216vzeroupperlearbp,[rsp+110H] vxorps ymm4,ymm4 vmovdqu xmmword ptr [rbp-D8H],xmm4 ; Zero first 16 byteslearax,[rbp-D8H] ; Get block startandrax,31 ; Get offset for 32 byte alignment of block (is 16 byte aligned and first 16 are already zeroed)addrax,-216 ; Add back offsetaddrax,rbp ; Add back stack pointer vmovdqu ymmword ptr[rax],ymm4 vmovdqu ymmword ptr[rax+32],ymm4 vmovdqu ymmword ptr[rax+64],ymm4 vmovdqu ymmword ptr[rax+96],ymm4 vmovdqu ymmword ptr[rax+128],ymm4@tannergooding would that work? |
6856159 to
2f06da8Comparebenaadams
commented
Feb 18, 2020
Probably don't want to use non temporal? Not having visibility from other procs is fine (since its thread stack); however likely want it in the cache as its going to be immediately used? |
AndyAyersMS
commented
Feb 18, 2020
Maybe? I wonder if this might cause store buffer stalls. |
benaadams
commented
Feb 18, 2020
Also 32 bytes aligns leardi,[rbp-98H]movecx,26xorrax,raxrepstosdTo leardi,[rbp-98H]xorpsxmm4,xmm4movdqu xmmword ptr [rbp-98H],xmm4andrdi,31movecx,26subrcx,rdiaddrdi,-152addrdi,rbpxorrax,raxrepstosd |
benaadams
commented
Feb 18, 2020
Cross gen of corelib which is non-AVX is |
jkotas
commented
Feb 18, 2020
I would be ok to take this regression for this fix. |
benaadams
commented
Feb 18, 2020
Haven't got it quite right... |
Perhaps not everything is fully 16 byte aligned; not able to trigger this failure in simple program yet. @AndyAyersMS when you say:
Is there a way to detect its a leaf method? Then I can have more variable realignment but keep it to leaf methods (i.e. not assume its 16byte aligned) |
benaadams
commented
Feb 19, 2020
Think I can use? bool is16byteAligned = (compiler->compLclFrameSize % 16) == 0; |
AndyAyersMS
commented
Feb 19, 2020
For Windows x64, I think you can check if You could also add explicit tracking in |
5392cf1 to
4f5a326Comparebenaadams
commented
Feb 19, 2020
I've change it to assume no alignment and to always fully align with 2x xmm at start for It does make it regress further slightly |
benaadams
commented
Feb 19, 2020
Might wait to see if tests pass before breaking it down more 😅 |
benaadams
commented
Feb 19, 2020
Decided to do that 😄 #32538 |
benaadams
commented
Feb 19, 2020
Closing in preference of #32538 |
benaadams
commented
Feb 19, 2020
Ah... might be issue between bytes and dwords |
rep stosd has a low throughput if the memory is not 32byte aligned; however it is a compact code size. Use faster throughput SIMD instructions when the amount to zero in the prologue is low; moving back to rep stosd when it becomes too many instructions.
benaadams
commented
Feb 19, 2020
Will do it in other one |

rep stosd has a low throughput if the memory is not 32byte aligned; however it is a compact code size.
Use faster throughput SIMD instructions when the amount to zero in the prologue is low; moving back to rep stosd when it becomes too many instructions.
Also align
rep stosdto 32 bytes when it is used.Best checked with ignore whitespace (due to new
if/elseindenting)Contributes to #8890
/cc @erozenfeld not entirely sure what I'm doing :)