Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Conversation

@benaadams
Copy link
Member

Noticed a change for Dictionary.KeyCollection and got a bit carried away...

Total bytes of diff: -1150 (-0.04% of base)
    diff is an improvement.

Top file improvements by size (bytes):
    -1150 : System.Private.CoreLib.dasm (-0.04% of base)

1 total files with size differences (1 improved, 0 regressed), 0 unchanged.

Top method improvements by size (bytes):
     -93 (-3.87% of base) : System.Private.CoreLib.dasm - KeyCollection:CopyTo(ref,int):this (13 methods)
     -60 (-2.38% of base) : System.Private.CoreLib.dasm - ValueCollection:CopyTo(ref,int):this (13 methods)
     -48 (-5.61% of base) : System.Private.CoreLib.dasm - DateTime:TryCreate(int,int,int,int,int,int,int,byref):bool
     -35 (-7.58% of base) : System.Private.CoreLib.dasm - JulianCalendar:ToDateTime(int,int,int,int,int,int,int,int):struct:this
     -28 (-5.10% of base) : System.Private.CoreLib.dasm - Utf8Parser:TryParseInt64D(struct,byref,byref):bool
     -20 (-5.60% of base) : System.Private.CoreLib.dasm - Vector2:CopyTo(ref,int):this
     -17 (-2.84% of base) : System.Private.CoreLib.dasm - String:IndexOf(ref,int,int,int):int:this
     -17 (-5.94% of base) : System.Private.CoreLib.dasm - Calendar:TimeToTicks(int,int,int,int):long
     -17 (-6.37% of base) : System.Private.CoreLib.dasm - GregorianCalendarHelper:TimeToTicks(int,int,int,int):long
     -16 (-8.42% of base) : System.Private.CoreLib.dasm - Char:IsSurrogatePair(ref,int):bool
     -16 (-2.69% of base) : System.Private.CoreLib.dasm - Char:ConvertToUtf32(ref,int):int
     -16 (-4.15% of base) : System.Private.CoreLib.dasm - Vector3:CopyTo(ref,int):this
     -16 (-3.83% of base) : System.Private.CoreLib.dasm - Vector4:CopyTo(ref,int):this
     -16 (-1.48% of base) : System.Private.CoreLib.dasm - EncodingNLS:GetBytes(ref,int,int,ref,int):int:this (2 methods)
     -16 (-1.49% of base) : System.Private.CoreLib.dasm - UnicodeEncoding:GetBytes(ref,int,int,ref,int):int:this (2 methods)
     -16 (-1.49% of base) : System.Private.CoreLib.dasm - UTF32Encoding:GetBytes(ref,int,int,ref,int):int:this (2 methods)
     -16 (-1.49% of base) : System.Private.CoreLib.dasm - UTF7Encoding:GetBytes(ref,int,int,ref,int):int:this (2 methods)
     -16 (-3.83% of base) : System.Private.CoreLib.dasm - IListWrapper:LastIndexOf(ref,int,int):int:this
     -14 (-10.00% of base) : System.Private.CoreLib.dasm - Char:IsHighSurrogate(ref,int):bool
     -14 (-10.00% of base) : System.Private.CoreLib.dasm - Char:IsLowSurrogate(ref,int):bool
     -14 (-0.44% of base) : System.Private.CoreLib.dasm - IdnMapping:PunycodeDecode(ref):ref
     -11 (-1.16% of base) : System.Private.CoreLib.dasm - ParseNumbers:StringToLong(struct,int,int,byref):long
     -11 (-1.11% of base) : System.Private.CoreLib.dasm - ParseNumbers:StringToInt(struct,int,int,byref):int
     -10 (-4.37% of base) : System.Private.CoreLib.dasm - String:IndexOf(ref,int,int):int:this (2 methods)
     -10 (-1.34% of base) : System.Private.CoreLib.dasm - DateTime:.ctor(int,int,int,int,int,int,int,ref,int):this
     -10 (-0.99% of base) : System.Private.CoreLib.dasm - StringSerializer:GetNextStringValue():ref:this
      -9 (-3.41% of base) : System.Private.CoreLib.dasm - Array:FindIndex(ref,int,int,ref):int
      -9 (-0.63% of base) : System.Private.CoreLib.dasm - DateTime:.ctor(int,int,int,int,int,int,int):this (2 methods)
      -9 (-1.41% of base) : System.Private.CoreLib.dasm - DateTime:.ctor(int,int,int,int,int,int,int,ref):this
      -9 (-1.70% of base) : System.Private.CoreLib.dasm - DecoderNLS:GetChars(ref,int,int,ref,int,bool):int:this
      -9 (-1.68% of base) : System.Private.CoreLib.dasm - EncoderNLS:GetBytes(ref,int,int,ref,int,bool):int:this
      -9 (-1.60% of base) : System.Private.CoreLib.dasm - EncodingNLS:GetChars(ref,int,int,ref,int):int:this
      -9 (-1.60% of base) : System.Private.CoreLib.dasm - UnicodeEncoding:GetChars(ref,int,int,ref,int):int:this
      -9 (-1.60% of base) : System.Private.CoreLib.dasm - UTF32Encoding:GetChars(ref,int,int,ref,int):int:this
      -9 (-1.60% of base) : System.Private.CoreLib.dasm - UTF7Encoding:GetChars(ref,int,int,ref,int):int:this
      -8 (-1.42% of base) : System.Private.CoreLib.dasm - Array:IndexOf(ref,ref,int,int):int (2 methods)
      -8 (-2.42% of base) : System.Private.CoreLib.dasm - String:ToCharArray(int,int):ref:this
      -8 (-2.16% of base) : System.Private.CoreLib.dasm - String:Insert(int,ref):ref:this
      -8 (-1.34% of base) : System.Private.CoreLib.dasm - String:LastIndexOf(ref,int,int,int):int:this
      -8 (-1.72% of base) : System.Private.CoreLib.dasm - BitConverter:ToString(ref,int,int):ref
      -8 (-11.11% of base) : System.Private.CoreLib.dasm - DateTimeParse:AdjustHour(byref,int):bool
      -8 (-1.85% of base) : System.Private.CoreLib.dasm - StringSerializer:SkipVersionNextDataFields(int):this
      -8 (-0.77% of base) : System.Private.CoreLib.dasm - StringSerializer:GetNextAdjustmentRuleValue():ref:this
      -8 (-0.70% of base) : System.Private.CoreLib.dasm - StringSerializer:GetNextTransitionTimeValue():struct:this
      -8 (-1.36% of base) : System.Private.CoreLib.dasm - RuntimeModule:ResolveSignature(int):ref:this
      -8 (-1.83% of base) : System.Private.CoreLib.dasm - BinaryReader:FillBuffer(int):this
      -8 (-1.66% of base) : System.Private.CoreLib.dasm - CompareInfo:IndexOf(ref,ushort,int,int,int):int:this
      -8 (-1.21% of base) : System.Private.CoreLib.dasm - CompareInfo:LastIndexOf(ref,ushort,int,int,int):int:this
      -8 (-1.23% of base) : System.Private.CoreLib.dasm - CompareInfo:LastIndexOf(ref,ref,int,int,int):int:this
      -8 (-3.03% of base) : System.Private.CoreLib.dasm - HebrewCalendar:GetLunarMonthDay(int,ref):int
      
132 total methods with size differences (132 improved, 0 regressed), 19003 unchanged.

/cc @jkotas

@hughbe
Copy link

hughbe commented Oct 26, 2019

Can the JIT still not optimise these sorts of range checks?!

@EgorBo
Copy link
Member

EgorBo commented Oct 26, 2019

@EgorBo
Copy link
Member

EgorBo commented Oct 26, 2019

However, I guess it's not always a good idea to optimize

if (startIndex < 0 || startIndex > array.Length)

into

if ((uint)startIndex > (uint)array.Length)

It makes sense to do only when the true-branch is unlikely to be taken (bb weight = 0)

image

{
ThrowInvalidOperationIfDefault();
if (index < 0 || index >= _count)
if ((uint)index >= (uint)_count)
Copy link
Member

Choose a reason for hiding this comment

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

The JIT would not be able to optimize cases like this. It does not know that _count is not negative.

Copy link
Member

Choose a reason for hiding this comment

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

@jkotas what about array.Length? Does JIT know it can't be negative? (e.g. mono/mono#16948)

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

@benaadams benaadams Oct 26, 2019

Choose a reason for hiding this comment

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

Clang's Isn't as optimized as the changed C#?

    cmp     edi, esi
    setg    al
    shr     edi, 31
    or      rdi, rax
    jne     .LBB0_1

vs sharplab.io

    cmp edx, r8d
    ja L001a

Copy link
Member

Choose a reason for hiding this comment

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

clang doesn't know our integer is actually a positive number :-)

@jkotas
Copy link
Member

jkotas commented Oct 26, 2019

I agree that it would be nice for the toolchain to optimize this for you. On the other hand, this became a CoreLib idiom used in hundreds of places, so one hundred more is not a big deal.

@mikedn
Copy link

mikedn commented Oct 26, 2019

So when we change the JIT to recognize this pattern what do we do? Go and cleanup all the FX that has now become riddled with this hack? I almost regret adding this to the JIT.

@benaadams
Copy link
Member Author

Go and cleanup all the FX that has now become riddled with this hack?

I'd be more than happy to do so.

@EgorBo
Copy link
Member

EgorBo commented Oct 26, 2019

@benaadams
Copy link
Member Author

Need to access the array and eliminate the bounds check for >=

if (i < 0 || i >= array.Length)
{
    throw new Exception();
}
i = array[i];

@benaadams
Copy link
Member Author

Also this pattern

if (i >= 0 && i < array.Length)
{
    i = array[i];
}

}

if (startIndex < 0 || startIndex > array.Length)
if ((uint)startIndex > (uint)array.Length)
Copy link
Member

@EgorBo EgorBo Oct 27, 2019

Choose a reason for hiding this comment

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

@benaadams um.. shouldn't it be >= here ?
It's currently incorrect (even before your PR)

Copy link
Member

Choose a reason for hiding this comment

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

No, >= fails for Fill(empty, default, 0, 0) (Or any Fill(arr, default, arr.Length, 0)

@EgorBo
Copy link
Member

EgorBo commented Oct 27, 2019

@benaadams I've updated #27462 (comment) (now the diff is -2569)

@sandreenko
Copy link

cc @dotnet/jit-contrib

@maryamariyan
Copy link

Thank you for your contribution. As announced in dotnet/coreclr#27549 this repository will be moving to dotnet/runtime on November 13. If you would like to continue working on this PR after this date, the easiest way to move the change to dotnet/runtime is:

  1. In your coreclr repository clone, create patch by running git format-patch origin
  2. In your runtime repository clone, apply the patch by running git apply --directory src/coreclr <path to the patch created in step 1>

Copy link
Member

@GrabYourPitchforks GrabYourPitchforks left a comment

Choose a reason for hiding this comment

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

Only one thing that needs to be fixed, the rest are just random comments.

public override int IndexOf(object? value, int startIndex, int count)
{
if (startIndex < 0 || startIndex > Count) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
if ((uint)startIndex > (uint)Count) throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
Copy link
Member

Choose a reason for hiding this comment

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

Technically a breaking change since the get_Count virtual method is now always called, even if startIndex is negative. Honestly I don't care, but wanted to point it out since it's an example of something the JIT wouldn't be able to optimize automatically since it'd likely pessimistically assume the get_Count virtual method might have side effects.

(This same comment applies to a few other places in this file.)


// Make sure n is legal
if (n < 0 || n > 0x10ffff || (n >= 0xD800 && n <= 0xDFFF))
if ((uint)n > 0x10ffff || (n >= 0xD800 && n <= 0xDFFF))
Copy link
Member

Choose a reason for hiding this comment

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

Hooray, a simplification! 😀
if (!Rune.IsValid(n))

Perhaps out of scope here, but there's likely some low-hanging fruit throughout the Framework that could be touched up in this manner.

throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);

if (count < 0 || startIndex > this.Length - count)
if ((uint)startIndex > (uint)(this.Length - count))
Copy link
Member

Choose a reason for hiding this comment

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

This optimization isn't valid. Still need to check count for a negative value.

@BruceForstall BruceForstall added the post-consolidation PRs which will be hand ported to dotnet/runtime label Nov 7, 2019
@benaadams
Copy link
Member Author

I'll follow up with this one post consolidation

@maryamariyan
Copy link

Thank you for your contribution. As announced in #27549 the dotnet/runtime repository will be used going forward for changes to this code base. Closing this PR as no more changes will be accepted into master for this repository. If you’d like to continue working on this change please move it to dotnet/runtime.

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

Labels

area-CodeGen optimization post-consolidation PRs which will be hand ported to dotnet/runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants