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

Avoid allocations for Unicode data tries #15074

Merged
merged 1 commit into from
Mar 30, 2024

Conversation

MrJul
Copy link
Member

@MrJul MrJul commented Mar 21, 2024

What does the pull request do?

This PR completely avoids allocations for Unicode data tries on .NET 7+, removing 195 kB of always allocated managed memory.

How was the solution implemented (if it's not obvious)?

The trie's data is now a property returning ReadOnlySpan<uint> instead of ReadOnlySpan<byte>, that's used directly instead of copying the bytes to a runtime uint[].

Starting with .NET 7 and Roslyn 17.5, this syntax makes the compiler use RuntimeHelpers.CreateSpan, which references the data embedded inside the assembly directly on little-endian systems, without having to allocate or copy anything!
(For .NET Standard, the compiler falls back to an array.)

Performance

I expected better memory usage, but it turns out we also get some performance improvement. With these changes, the JIT is able to inline some constants from the Unicode data directly, generating better code.

Before:

Method CodepointValue Mean Error StdDev Allocated
GetGeneralCategory 65 0.4334 ns 0.0118 ns 0.0110 ns -
GetGeneralCategory 4660 0.4076 ns 0.0215 ns 0.0201 ns -

After:

Method CodepointValue Mean Error StdDev Allocated
GetGeneralCategory 65 0.2274 ns 0.0101 ns 0.0089 ns -
GetGeneralCategory 4660 0.2292 ns 0.0056 ns 0.0050 ns -

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0046432-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6
Copy link
Member

Starting with .NET 7 and Roslyn 17.5, this syntax makes the compiler use RuntimeHelpers.CreateSpan, which references the data embedded inside the assembly directly on little-endian systems, without having to allocate or copy anything!

I am pretty sure we expected old code to work exactly in this way, i.e. returning reference to the data inside of the assembly. I.e. for .NET 6 as well.
But I guess it was wrong.

@MrJul
Copy link
Member Author

MrJul commented Mar 22, 2024

I am pretty sure we expected old code to work exactly in this way, i.e. returning reference to the data inside of the assembly. I.e. for .NET 6 as well.
But I guess it was wrong.

You were right, it's been working for a while with ReadOnlySpan<byte>, but the old code then copied that to a runtime uint[], so we still had allocations.

Supporting other primitive types than byte with RuntimeHelpers.CreateSpan is much more recent: dotnet/roslyn#61414

@MrJul MrJul force-pushed the feature/unicode-trie-noalloc branch from d2b5720 to 6642296 Compare March 29, 2024 14:54
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0046814-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 added the backport-candidate-11.1.x Consider this PR for backporting to 11.1 branch label Mar 30, 2024
@maxkatz6 maxkatz6 merged commit 270f8c8 into AvaloniaUI:master Mar 30, 2024
10 checks passed
@MrJul MrJul deleted the feature/unicode-trie-noalloc branch March 30, 2024 13:16
@maxkatz6 maxkatz6 added backported-11.1.x and removed backport-candidate-11.1.x Consider this PR for backporting to 11.1 branch labels Apr 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants