-
Notifications
You must be signed in to change notification settings - Fork 5k
Optimize '[Guid]' parsing on Native AOT #116324
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 '[Guid]' parsing on Native AOT #116324
Conversation
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.
Pull Request Overview
This PR aims to optimize the parsing of [Guid] in Native AOT by improving the underlying UTF8 string decoding and Guid parsing logic. Key changes include:
- Adding a new DecodeStringUtf8 method in NativeFormatReader.String.cs to efficiently decode UTF8 strings.
- Introducing a GetRawStringDataUtf8 helper in ConstantStringValue to leverage the new decoding method.
- Refactoring Guid parsing in NativeFormatRuntimeNamedTypeInfo.cs to use a static local method that processes UTF8 data for performance gains.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
File | Description |
---|---|
src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs | Added method for decoding UTF8 strings with boundary checks and copying to a destination span |
src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs | Added a helper that uses the new decode method to extract UTF8 encoded string data |
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs | Replaced direct Guid creation with a static local method that decodes and parses UTF8 data for Guid construction |
Comments suppressed due to low confidence (1)
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs:88
- [nitpick] Consider using a parameter name in the exception call that more clearly reflects the null context of the handle value rather than 'utf8Text', which might be misleading.
ArgumentNullException.Throw("utf8Text");
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas |
...Lib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs
Outdated
Show resolved
Hide resolved
Any perf numbers? |
This comment was marked as outdated.
This comment was marked as outdated.
@@ -63,6 +64,32 @@ public unsafe uint DecodeString(uint offset, out string value) | |||
return endOffset; | |||
} | |||
|
|||
public unsafe Guid ParseGuid(uint offset) | |||
{ | |||
#if NETFX_45 |
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.
This is dead ifdef. You can delete it
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.
There's another one in the method above, should I remove that one too?
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.
Mmh if I remove it, building fails with this at the Guid.Parse
call:
D:\git\runtime\src\coreclr\tools\Common\Internal\NativeFormat\NativeFormatReader.String.cs(75,31): error CS1503: Argume
nt 1: cannot convert from 'System.ReadOnlySpan<byte>' to 'System.ReadOnlySpan<char>' [D:\git\runtime\src\coreclr\tools\
aot\ILCompiler.MetadataTransform\ILCompiler.MetadataTransform.csproj]
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.
Hmm, that's because we have not picked up #105654 in the LKG SDK yet.
It should be fixed by https://github.com/dotnet/runtime/pull/116328/files#diff-8df3cd354bc584349d04ad5675b33c042d8b99b741b8b95af394c55e0f5001bfR3
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.
It seems like it's fine now after doing the changes Michal suggested. I removed that ifdef 👍
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.
Could you please delete the ifdef (and the define in .csproj) while you are on in, even though it is not strictly required for the rest?
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.
Yup, of course, but just to clarify, I need to wait for that PR you linked to be merged first, and then to update this branch, right?
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 do not think you need to wait for that PR. The earlier build break was caused by the new code that you have introduced. The existing code should compile just fine with the ifdef deleted.
{ | ||
// We don't really have a parameter, so just match the name of the 'Guid.ctor' parameter | ||
#if SYSTEM_PRIVATE_CORELIB | ||
ArgumentNullException.Throw("input"); |
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.
Where was this exception thrown before?
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.
By the Guid
constructor I think. The code was getting a ConstantStringValue
value, which immediatley returns in the constructor if the input handle is null. That leaves the string field set to default, and that's then directly passed to the Guid
constructor. I added this check here to try to match the semantics in case of failures as close as possible.
src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs
Outdated
Show resolved
Hide resolved
...Lib/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeNamedTypeInfo.cs
Outdated
Show resolved
Hide resolved
/azp run runtime-nativeaot-outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
Could you please measure the perf improvement for Type.GUID? |
Yup yup, will set some time aside to whip together a small benchmark. Was chatting with @EgorBo who said he might look into adding NAOT support to the bot, but I might just do a simple thing locally for now to unblock this sooner 🙂 |
I'm struggling to figure out how to run BenchmarkDotNet with two local Native AOT builds, are there any docs? 😅 What I did is:
And then I have: using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<Benchmark>();
[MemoryDiagnoser]
public class Benchmark
{
private readonly Type _type = typeof(IFoo);
[Benchmark]
public Guid GetGUID() => _type.GUID;
}
[Guid("1DB7A2FC-9CB3-4A95-BEED-758C77F7715B")]
public interface IFoo; I would imagine I'd need a way to setup two jobs, each with |
It is the same as what's documented in |
Thank you! Made some progress but things are still not working. I have this code: using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.NativeAot;
var config = DefaultConfig.Instance
.AddJob(Job.ShortRun
.WithToolchain(NativeAotToolchain.CreateBuilder()
.UseLocalBuild(new DirectoryInfo(@"D:\diffs\nativeaot\guid_optimize\main\Shipping\"))
.DisplayName("NativeAOT main")
.TargetFrameworkMoniker("net10.0")
.ToToolchain()))
.AddJob(Job.ShortRun
.WithToolchain(NativeAotToolchain.CreateBuilder()
.UseLocalBuild(new DirectoryInfo(@"D:\diffs\nativeaot\guid_optimize\pr\Shipping\"))
.DisplayName("NativeAOT PR")
.TargetFrameworkMoniker("net10.0")
.ToToolchain()));
BenchmarkRunner.Run<Benchmark>(config);
[MemoryDiagnoser]
public class Benchmark
{
private readonly Type _type = typeof(IFoo);
[Benchmark]
public Guid GetGUID() => _type.GUID;
}
[Guid("1DB7A2FC-9CB3-4A95-BEED-758C77F7715B")]
public interface IFoo; I built the two packages in those two directories. When BDN tries building the two benchmarks, it fails with this error:
@EgorBo mentioned it's likely because SSE was removed, but BDN tries to use it here. Any possible workaround I could use? 😅 |
Why is it not taking the |
When I was trying to follow the steps, I ended up with this error: error LNK2001: unresolved external symbol CompressionNative_InflateReset2_ 🙁 |
I am fine if you just build a trivial manual test as a console app and run it before/after. Personally, I tend to do that for quick measurements - it is faster, guaranteed to work, and you can tell exactly what it is measuring.
|
028c367
to
04c482c
Compare
Results aren't making any sense to me given the changes:
I'll re-test. I don't see how this could possibly be the case. |
04c482c
to
41870df
Compare
Double checked after rebasing. Same exact results. I did not accidentally swap them, the numbers are correct.
I genuinely don't understand how the changes here could possibly regress perf. |
I see a very small (~1.5%) improvement (win-x64, default PublishAot, AMD EPYC 7763 - standard MS DevBox). It is not unexpected since this is not improving where the time is actually spent in Type.GUID. |
What are the machine specs that you run this on? Just want to double check - do you run this on release builds of the runtime ( |
That's in line with what I was expecting too: small perf improvement, and less memory use (which we can't see here though).
Local desktop, i9-12900K.
Yeah I just did |
src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeMetadataReader.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: kasperk81 <83082615+kasperk81@users.noreply.github.com>
Sounds good, making the property zero allocation (when called repeatedly) is a reasonable justification. |
We're going to need to use
[Guid]
in some scenarios in CsWinRT 3.0 (see #114179), and while looking at theType.GUID
logic in Native AOT I saw some potential small perf improvements we could do. Figured I'd give it a shot and see if it's well received 😄