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

Use unsigned casts for array/Span indexers #57970

Merged
merged 3 commits into from
Sep 16, 2021
Merged

Conversation

pentp
Copy link
Contributor

@pentp pentp commented Aug 23, 2021

Array and Span length is never negative and after bounds checking index is also guaranteed to be non-negative. Unsigned casts get rid of quite a few movsxd instructions and replace the rest with cheaper mov variants.

PMI diffs for System.Private.CoreLib show only 8 regressions, of which only a couple are because of CSE misses, most are from loop alignment changes.

Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 5517665
Total bytes of diff: 5513593
Total bytes of delta: -4072 (-0,07% of base)
Total relative delta: -10,86
    diff is an improvement.
    relative diff is an improvement.
Detailed diffs for win-x64
Top method regressions (bytes):
          20 ( 1,56% of base) : System.Private.CoreLib.dasm - PathHelper:TryExpandShortFileName(byref,String):String
           9 ( 1,29% of base) : System.Private.CoreLib.dasm - GregorianCalendarHelper:GetYearOffset(int,int,bool):int:this
           5 ( 0,90% of base) : System.Private.CoreLib.dasm - Encoder:GetBytes(long,int,long,int,bool):int:this
           4 ( 0,71% of base) : System.Private.CoreLib.dasm - Decoder:GetChars(long,int,long,int,bool):int:this
           3 ( 0,78% of base) : System.Private.CoreLib.dasm - String:<GetNonRandomizedHashCodeOrdinalIgnoreCase>g__GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow|47_0(String):int
           1 ( 0,27% of base) : System.Private.CoreLib.dasm - Decoder:GetCharCount(long,int,bool):int:this
           1 ( 0,26% of base) : System.Private.CoreLib.dasm - Encoder:GetByteCount(long,int,bool):int:this
           1 ( 0,16% of base) : System.Private.CoreLib.dasm - RuntimeModule:ResolveSignature(int):ref:this

Top method improvements (bytes):
        -112 (-1,08% of base) : System.Private.CoreLib.dasm - List`1:ConvertAll(Converter`2):List`1:this (64 methods)
         -70 (-0,64% of base) : System.Private.CoreLib.dasm - DefaultBinder:BindToMethod(int,ref,byref,ref,CultureInfo,ref,byref):MethodBase:this
         -60 (-0,66% of base) : System.Private.CoreLib.dasm - HashSet`1:SymmetricExceptWithEnumerable(IEnumerable`1):this (8 methods)
         -55 (-1,13% of base) : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (10 methods)
         -42 (-0,55% of base) : System.Private.CoreLib.dasm - Enumerator:MoveNext():bool:this (74 methods)
         -37 (-1,09% of base) : System.Private.CoreLib.dasm - HashSet`1:Resize(int,bool):this (8 methods)
         -35 (-3,43% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:SwapIfGreater(Span`1,Comparison`1,int,int) (9 methods)
         -32 (-1,47% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:DownHeap(Span`1,int,int,Comparison`1) (8 methods)
         -32 (-0,44% of base) : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(Object):this (8 methods)
         -31 (-0,41% of base) : System.Private.CoreLib.dasm - HashSet`1:OnDeserialization(Object):this (8 methods)
         -31 (-1,66% of base) : System.Private.CoreLib.dasm - List`1:RemoveAll(Predicate`1):int:this (8 methods)
         -30 (-0,82% of base) : System.Private.CoreLib.dasm - ArraySortHelper`2:DownHeap(Span`1,Span`1,int,int,IComparer`1) (8 methods)
         -30 (-0,50% of base) : System.Private.CoreLib.dasm - ArraySortHelper`2:PickPivotAndPartition(Span`1,Span`1,IComparer`1):int (8 methods)
         -30 (-0,77% of base) : System.Private.CoreLib.dasm - Vector128`1:ToString():String:this (6 methods)
         -30 (-0,77% of base) : System.Private.CoreLib.dasm - Vector256`1:ToString():String:this (6 methods)
         -29 (-0,35% of base) : System.Private.CoreLib.dasm - TlsOverPerCoreLockedStacksArrayPool`1:Rent(int):ref:this (10 methods)
         -28 (-0,46% of base) : System.Private.CoreLib.dasm - HashSet`1:IntersectWithEnumerable(IEnumerable`1):this (8 methods)
         -28 (-1,42% of base) : System.Private.CoreLib.dasm - PerCoreLockedStacks:TryPop():ref:this (8 methods)
         -28 (-0,76% of base) : System.Private.CoreLib.dasm - Vector64`1:ToString():String:this (6 methods)
         -24 (-0,54% of base) : System.Private.CoreLib.dasm - ConfigurableArrayPool`1:Rent(int):ref:this (8 methods)

Top method regressions (percentages):
          20 ( 1,56% of base) : System.Private.CoreLib.dasm - PathHelper:TryExpandShortFileName(byref,String):String
           9 ( 1,29% of base) : System.Private.CoreLib.dasm - GregorianCalendarHelper:GetYearOffset(int,int,bool):int:this
           5 ( 0,90% of base) : System.Private.CoreLib.dasm - Encoder:GetBytes(long,int,long,int,bool):int:this
           3 ( 0,78% of base) : System.Private.CoreLib.dasm - String:<GetNonRandomizedHashCodeOrdinalIgnoreCase>g__GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow|47_0(String):int
           4 ( 0,71% of base) : System.Private.CoreLib.dasm - Decoder:GetChars(long,int,long,int,bool):int:this
           1 ( 0,27% of base) : System.Private.CoreLib.dasm - Decoder:GetCharCount(long,int,bool):int:this
           1 ( 0,26% of base) : System.Private.CoreLib.dasm - Encoder:GetByteCount(long,int,bool):int:this
           1 ( 0,16% of base) : System.Private.CoreLib.dasm - RuntimeModule:ResolveSignature(int):ref:this

Top method improvements (percentages):
         -16 (-8,60% of base) : System.Private.CoreLib.dasm - String:TrimHelper(long,int,int):String:this
         -19 (-6,09% of base) : System.Private.CoreLib.dasm - Utf8Parser:TryParseByteX(ReadOnlySpan`1,byref,byref):bool
         -24 (-5,80% of base) : System.Private.CoreLib.dasm - Path:Populate83FileNameFromRandomBytes(long,int,Span`1)
         -10 (-5,03% of base) : System.Private.CoreLib.dasm - <GetEnumerator>d__17:MoveNext():bool:this
         -14 (-3,98% of base) : System.Private.CoreLib.dasm - Utf8Parser:TryParseUInt64X(ReadOnlySpan`1,byref,byref):bool
          -8 (-3,79% of base) : System.Private.CoreLib.dasm - TraceLoggingDataCollector:EndBufferedArray(int,int)
         -24 (-3,51% of base) : System.Private.CoreLib.dasm - TimeSpanTokenizer:GetNextToken():TimeSpanToken:this
          -1 (-3,45% of base) : System.Private.CoreLib.dasm - TimeSpanTokenizer:get_NextChar():ushort:this
         -35 (-3,43% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:SwapIfGreater(Span`1,Comparison`1,int,int) (9 methods)
          -6 (-3,41% of base) : System.Private.CoreLib.dasm - List`1:FindLast(Predicate`1):Vector`1:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - ParameterModifier:get_Item(int):bool:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - ParameterModifier:set_Item(int,bool):this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - StackFrameHelper:GetILOffset(int):int:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - StackFrameHelper:GetOffset(int):int:this
          -1 (-3,12% of base) : System.Private.CoreLib.dasm - ConcreteFormattableString:GetArgument(int):Object:this
          -1 (-3,12% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):int:this
         -16 (-3,08% of base) : System.Private.CoreLib.dasm - Enumerator:get_Current():byref:this (16 methods)
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - DTSubString:get_Item(int):ushort:this
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):long:this
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):Nullable`1:this

1334 total methods with Code Size differences (1326 improved, 8 regressed), 24492 unchanged.

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Aug 23, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Aug 23, 2021
@ghost
Copy link

ghost commented Aug 23, 2021

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

Issue Details

Array and Span length is never negative and after bounds checking index is also guaranteed to be non-negative. Unsigned casts get rid of quite a few movsxd instructions and replace the rest with cheaper mov variants.

PMI diffs for System.Private.CoreLib show only 8 regressions, of which only a couple are because of CSE misses, most are from loop alignment changes.

Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 5517665
Total bytes of diff: 5513593
Total bytes of delta: -4072 (-0,07% of base)
Total relative delta: -10,86
    diff is an improvement.
    relative diff is an improvement.
Detailed diffs for win-x64
Top method regressions (bytes):
          20 ( 1,56% of base) : System.Private.CoreLib.dasm - PathHelper:TryExpandShortFileName(byref,String):String
           9 ( 1,29% of base) : System.Private.CoreLib.dasm - GregorianCalendarHelper:GetYearOffset(int,int,bool):int:this
           5 ( 0,90% of base) : System.Private.CoreLib.dasm - Encoder:GetBytes(long,int,long,int,bool):int:this
           4 ( 0,71% of base) : System.Private.CoreLib.dasm - Decoder:GetChars(long,int,long,int,bool):int:this
           3 ( 0,78% of base) : System.Private.CoreLib.dasm - String:<GetNonRandomizedHashCodeOrdinalIgnoreCase>g__GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow|47_0(String):int
           1 ( 0,27% of base) : System.Private.CoreLib.dasm - Decoder:GetCharCount(long,int,bool):int:this
           1 ( 0,26% of base) : System.Private.CoreLib.dasm - Encoder:GetByteCount(long,int,bool):int:this
           1 ( 0,16% of base) : System.Private.CoreLib.dasm - RuntimeModule:ResolveSignature(int):ref:this

Top method improvements (bytes):
        -112 (-1,08% of base) : System.Private.CoreLib.dasm - List`1:ConvertAll(Converter`2):List`1:this (64 methods)
         -70 (-0,64% of base) : System.Private.CoreLib.dasm - DefaultBinder:BindToMethod(int,ref,byref,ref,CultureInfo,ref,byref):MethodBase:this
         -60 (-0,66% of base) : System.Private.CoreLib.dasm - HashSet`1:SymmetricExceptWithEnumerable(IEnumerable`1):this (8 methods)
         -55 (-1,13% of base) : System.Private.CoreLib.dasm - Dictionary`2:Resize(int,bool):this (10 methods)
         -42 (-0,55% of base) : System.Private.CoreLib.dasm - Enumerator:MoveNext():bool:this (74 methods)
         -37 (-1,09% of base) : System.Private.CoreLib.dasm - HashSet`1:Resize(int,bool):this (8 methods)
         -35 (-3,43% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:SwapIfGreater(Span`1,Comparison`1,int,int) (9 methods)
         -32 (-1,47% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:DownHeap(Span`1,int,int,Comparison`1) (8 methods)
         -32 (-0,44% of base) : System.Private.CoreLib.dasm - Dictionary`2:OnDeserialization(Object):this (8 methods)
         -31 (-0,41% of base) : System.Private.CoreLib.dasm - HashSet`1:OnDeserialization(Object):this (8 methods)
         -31 (-1,66% of base) : System.Private.CoreLib.dasm - List`1:RemoveAll(Predicate`1):int:this (8 methods)
         -30 (-0,82% of base) : System.Private.CoreLib.dasm - ArraySortHelper`2:DownHeap(Span`1,Span`1,int,int,IComparer`1) (8 methods)
         -30 (-0,50% of base) : System.Private.CoreLib.dasm - ArraySortHelper`2:PickPivotAndPartition(Span`1,Span`1,IComparer`1):int (8 methods)
         -30 (-0,77% of base) : System.Private.CoreLib.dasm - Vector128`1:ToString():String:this (6 methods)
         -30 (-0,77% of base) : System.Private.CoreLib.dasm - Vector256`1:ToString():String:this (6 methods)
         -29 (-0,35% of base) : System.Private.CoreLib.dasm - TlsOverPerCoreLockedStacksArrayPool`1:Rent(int):ref:this (10 methods)
         -28 (-0,46% of base) : System.Private.CoreLib.dasm - HashSet`1:IntersectWithEnumerable(IEnumerable`1):this (8 methods)
         -28 (-1,42% of base) : System.Private.CoreLib.dasm - PerCoreLockedStacks:TryPop():ref:this (8 methods)
         -28 (-0,76% of base) : System.Private.CoreLib.dasm - Vector64`1:ToString():String:this (6 methods)
         -24 (-0,54% of base) : System.Private.CoreLib.dasm - ConfigurableArrayPool`1:Rent(int):ref:this (8 methods)

Top method regressions (percentages):
          20 ( 1,56% of base) : System.Private.CoreLib.dasm - PathHelper:TryExpandShortFileName(byref,String):String
           9 ( 1,29% of base) : System.Private.CoreLib.dasm - GregorianCalendarHelper:GetYearOffset(int,int,bool):int:this
           5 ( 0,90% of base) : System.Private.CoreLib.dasm - Encoder:GetBytes(long,int,long,int,bool):int:this
           3 ( 0,78% of base) : System.Private.CoreLib.dasm - String:<GetNonRandomizedHashCodeOrdinalIgnoreCase>g__GetNonRandomizedHashCodeOrdinalIgnoreCaseSlow|47_0(String):int
           4 ( 0,71% of base) : System.Private.CoreLib.dasm - Decoder:GetChars(long,int,long,int,bool):int:this
           1 ( 0,27% of base) : System.Private.CoreLib.dasm - Decoder:GetCharCount(long,int,bool):int:this
           1 ( 0,26% of base) : System.Private.CoreLib.dasm - Encoder:GetByteCount(long,int,bool):int:this
           1 ( 0,16% of base) : System.Private.CoreLib.dasm - RuntimeModule:ResolveSignature(int):ref:this

Top method improvements (percentages):
         -16 (-8,60% of base) : System.Private.CoreLib.dasm - String:TrimHelper(long,int,int):String:this
         -19 (-6,09% of base) : System.Private.CoreLib.dasm - Utf8Parser:TryParseByteX(ReadOnlySpan`1,byref,byref):bool
         -24 (-5,80% of base) : System.Private.CoreLib.dasm - Path:Populate83FileNameFromRandomBytes(long,int,Span`1)
         -10 (-5,03% of base) : System.Private.CoreLib.dasm - <GetEnumerator>d__17:MoveNext():bool:this
         -14 (-3,98% of base) : System.Private.CoreLib.dasm - Utf8Parser:TryParseUInt64X(ReadOnlySpan`1,byref,byref):bool
          -8 (-3,79% of base) : System.Private.CoreLib.dasm - TraceLoggingDataCollector:EndBufferedArray(int,int)
         -24 (-3,51% of base) : System.Private.CoreLib.dasm - TimeSpanTokenizer:GetNextToken():TimeSpanToken:this
          -1 (-3,45% of base) : System.Private.CoreLib.dasm - TimeSpanTokenizer:get_NextChar():ushort:this
         -35 (-3,43% of base) : System.Private.CoreLib.dasm - ArraySortHelper`1:SwapIfGreater(Span`1,Comparison`1,int,int) (9 methods)
          -6 (-3,41% of base) : System.Private.CoreLib.dasm - List`1:FindLast(Predicate`1):Vector`1:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - ParameterModifier:get_Item(int):bool:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - ParameterModifier:set_Item(int,bool):this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - StackFrameHelper:GetILOffset(int):int:this
          -1 (-3,23% of base) : System.Private.CoreLib.dasm - StackFrameHelper:GetOffset(int):int:this
          -1 (-3,12% of base) : System.Private.CoreLib.dasm - ConcreteFormattableString:GetArgument(int):Object:this
          -1 (-3,12% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):int:this
         -16 (-3,08% of base) : System.Private.CoreLib.dasm - Enumerator:get_Current():byref:this (16 methods)
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - DTSubString:get_Item(int):ushort:this
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):long:this
          -1 (-3,03% of base) : System.Private.CoreLib.dasm - SZArrayHelper:get_Item(int):Nullable`1:this

1334 total methods with Code Size differences (1326 improved, 8 regressed), 24492 unchanged.
Author: pentp
Assignees: -
Labels:

area-CodeGen-coreclr, community-contribution

Milestone: -

@EgorBo
Copy link
Member

EgorBo commented Sep 13, 2021

I guess it does similar to what #52414 did, @BruceForstall had some objections about it.
Also if we could rely on assertprop and optimize things like this we could get this one for free including other patterns.

@pentp
Copy link
Contributor Author

pentp commented Sep 14, 2021

It's somewhat similar, but we don't have to wait for all of these magical features to get some nice wins right now without any hacks.
There's no downside to this - unlike my previous attempt a couple of years ago (and #52414 also maybe), this one has almost no regressions because it applies zero-extension to both arrays (length & indexer) and spans.

Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

This looks great to me, let me kick off outerloop and then I'll merge assuming it passes.
FWIW, it doesn't seem like there were any actual objections in #52414.

@jakobbotsch
Copy link
Member

/azp run runtime-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jakobbotsch
Copy link
Member

@pentp did you verify that the original test failures were unrelated? I saw one failing job but I couldn't see the test log, so I tried rerunning it, but looks like the build timed out. Can you rebase/merge this change so we can see if we can get a green CI run?

The outerloop failure looks unrelated, it's #58481.

@pentp
Copy link
Contributor Author

pentp commented Sep 16, 2021

I looked at the two failing test runs and I think they were some test infra failures (like sending to helix failed), don't remember exactly. Rebased the commits.

@jakobbotsch
Copy link
Member

The failure is #11063.

@kunalspathak
Copy link
Member

Improvements on Linux/x64 - dotnet/perf-autofiling-issues#1482

@kunalspathak
Copy link
Member

Improvements on windows/x64 - dotnet/perf-autofiling-issues#1490

@kunalspathak
Copy link
Member

Improvements on windows amd x64 - dotnet/perf-autofiling-issues#1512

@EgorBo
Copy link
Member

EgorBo commented Sep 23, 2021

Improvements on win-arm64: dotnet/perf-autofiling-issues#1538

@EgorBo
Copy link
Member

EgorBo commented Sep 23, 2021

Improvements on win-x64: dotnet/perf-autofiling-issues#1488

@EgorBo
Copy link
Member

EgorBo commented Sep 23, 2021

Improvements on alpine 3.12: dotnet/perf-autofiling-issues#1503

@kunalspathak
Copy link
Member

Noticed IniArray improved on win/arm64. @DrewScoggins - any idea why this was not caught by autofiler?

image

@ghost ghost locked as resolved and limited conversation to collaborators Nov 20, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants