-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Eliminate a bounds check in NumberBuffer
#81039
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
Conversation
|
Tagging subscribers to this area: @dotnet/area-system-numerics Issue DetailsWe check
|
| public byte* GetDigitsPointer() | ||
| { | ||
| // This is safe to do since we are a ref struct | ||
| return (byte*)(Unsafe.AsPointer(ref Digits[0])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should ensure this doesn't impact inlining.
The IL is going to be larger as a result of the call and not every lowlevel API is currently Intrinsic and so may not "boost inlining" to account for the increase in IL size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe MemoryMarshal.GetReference was recently updated to be Intrinsic and so should, but it would be good to double check that codegen isn't negatively impacted anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both old and new are under "always inline" limit so it's fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added AggressiveInlining just to be sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tannergooding / @EgorBo, do we want the extra attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe
MemoryMarshal.GetReferencewas recently updated to beIntrinsicand so should, but it would be good to double check that codegen isn't negatively impacted anywhere.
There is no IntrinsicAttribute on MemoryMarshal.GetReference.
runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs
Lines 78 to 82 in 007df05
| /// <summary> | |
| /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element | |
| /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced. | |
| /// </summary> | |
| public static ref T GetReference<T>(Span<T> span) => ref span._reference; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want the extra attribute?
@stephentoub, RyuJIT currently defines ALWAYS_INLINE_SIZE = 16 and so any method with a number of IL bytes less than this should implicitly be treated this way. Other runtimes may have their own differing limits and, with MethodImplOptions being one of the metadata special attributes, there is no real harm in providing it since it doesn't impact IL metadata size. It effectively just ensures the JIT gives it the same multiplier.
That being said, SharpLab shows that GetDigitsPointer is 17 bytes of IL:
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.Span`1<uint8> C::Digits
IL_0006: call !!0& [System.Memory]System.Runtime.InteropServices.MemoryMarshal::GetReference<uint8>(valuetype [System.Runtime]System.Span`1<!!0>)
IL_000b: call void* [System.Runtime]System.Runtime.CompilerServices.Unsafe::AsPointer<uint8>(!!0&)
IL_0010: retBecause of this, it is not guaranteed to be inlined even though what we're doing compiles down to a field read, so it would be directly beneficial to annotate it here.
We check
Debug.Assert(!digits.IsEmpty)in the ctor.