Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Math.Round for arm64 and AwayFromZero for x64 #64016

Merged
merged 20 commits into from
Feb 13, 2022

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Jan 19, 2022

I noticed that people quite often explicitly set AwayFromZero rounding for Math.Round (btw in C++ round(double) uses AwayFromZero by default while we use "accountant-friendly" ToEven) to remind you the difference:

1.5 2.5
ToEven 2.0 2.0
AwayFromZero 2.0 3.0

so I decided to optimize it.

double Test(double x) => Math.Round(x, MidpointRounding.AwayFromZero);

Current codegen:

; Method Program:Test(double):double:this
            stp     fp, lr, [sp,#-16]!
            mov     fp, sp

            mov     w0, #0
            mov     w1, #1
            ldp     fp, lr, [sp],#16
            b       System.Math:Round(double,int,int):double ;; call, much slower
; Total bytes of code: 24

New codegen:

; Method Program:Test(double):double:this
            stp     fp, lr, [sp,#-16]!
            mov     fp, sp

            frinta  d0, d0

            ldp     fp, lr, [sp],#16
            ret     lr
; Total bytes of code: 20

cc @tannergooding

PS: I can guard my change with RuntimeHelpers.IsConstArgument but I think it worth inlining as is.

@ghost
Copy link

ghost commented Jan 19, 2022

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Issue Details

I noticed that people quite often explicitly set AwayFromZero rounding for Math.Round (btw in C++ round(double) uses AwayFromZero by default while we use "accountant-friendly" ToEven) so I decided to optimize it.

double Test(double x) => Math.Round(x, MidpointRounding.AwayFromZero);

Current codegen:

       vzeroupper 
       vmovaps  xmm0, xmm1
       xor      edx, edx
       mov      r8d, 1
       jmp      System.Math:Round(double,int,int):double ;; not inlined, is not hw accelerated for AwayFromZero
; Total bytes of code: 20

New codegen:

       vzeroupper 
       vandpd   xmm0, xmm1, xmmword ptr [reloc @RWD00]
       vorpd    xmm0, xmm0, xmmword ptr [reloc @RWD16]
       vaddpd   xmm0, xmm0, xmm1
       vroundsd xmm0, xmm0, xmm0, 3
       ret      
RWD00  	dq	8000000000000000h, 8000000000000000h
RWD16  	dq	3FDFFFFFFFFFFFFFh, 3FDFFFFFFFFFFFFFh
; Total bytes of code: 30

On ARM64 it's a single instruction for both modes.
cc @tannergooding

Author: EgorBo
Assignees: EgorBo
Labels:

area-System.Numerics

Milestone: -

@stephentoub
Copy link
Member

I noticed that people quite often explicitly set AwayFromZero rounding for Math.Round

Can you share your data source?

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Src looks good to me. Please ensure there are tests that will exercise the new code paths. Thanks.

@EgorBo
Copy link
Member Author

EgorBo commented Jan 27, 2022

Failed job is unrelated.

@tannergooding are the tests I added sufficient? Also, it seems like Math.Round(var = 0.49999999999999994) returns 1 on ARM32 😐

@EgorBo
Copy link
Member Author

EgorBo commented Feb 9, 2022

@tannergooding ping 🙂

@EgorBo EgorBo merged commit 9e847a5 into dotnet:main Feb 13, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Mar 17, 2022
@EgorBo EgorBo changed the title Optimize Math.Round(x, MidpointRounding.AwayFromZero/ToEven) Optimize Math.Round for arm64 and AwayFromZero for x64 Aug 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants