Skip to content

Conversation

@kzrnm
Copy link
Contributor

@kzrnm kzrnm commented Sep 18, 2025

With the introduction of UnsafeAccessorTypeAttribute in .NET 10 and later, methods of the Number class in System.Private.CoreLib can now be called directly via UnsafeAccessorAttribute. This allows replacing the previous implementations that relied on Utf8Char, Utf16Char, and similar types.

Number.Unsafe.cs

Number.Unsafe.cs defines methods that are invoked from System.Runtime.Numerics. Since these methods are not used within System.Private.CoreLib, they must also be listed in ILLink.Descriptors.Shared.xml to prevent trimming.
Because the internal IUtfChar<TChar> interface cannot be accessed even with UnsafeAccessorAttribute, overloads for byte and char have been added. In addition, since the NumberBuffer ref struct is defined separately but identically in both System.Private.CoreLib and System.Runtime.Numerics, overloads using void* have been added to enable access.

The internal methods of NumberFormatInfo are also accessed via UnsafeAccessor.

benchmark


BenchmarkDotNet v0.15.2, Windows 11 (10.0.26100.4770/24H2/2024Update/HudsonValley)
13th Gen Intel Core i5-13500 2.50GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.100-rc.1.25420.111
  [Host]     : .NET 10.0.0 (10.0.25.42111), X64 RyuJIT AVX2
  Job-ZICBXN : .NET 10.0.0 (42.42.42.42424), X64 RyuJIT AVX2
  Job-QENHHP : .NET 10.0.0 (42.42.42.42424), X64 RyuJIT AVX2


Method Toolchain Mean Ratio Code Size Gen0 Allocated Alloc Ratio
UInt32ToString \main\corerun.exe 6.084 ns 1.00 160 B 0.0032 40 B 1.00
UInt32ToString \pr\corerun.exe 5.325 ns 0.88 160 B 0.0032 40 B 1.00
UInt32ToStringFormat \main\corerun.exe 34.040 ns 1.00 1,268 B 0.0038 48 B 1.00
UInt32ToStringFormat \pr\corerun.exe 31.780 ns 0.93 2,882 B 0.0038 48 B 1.00
BigIntegerToStringSmall \main\corerun.exe 66.705 ns 1.00 3,912 B 0.0082 104 B 1.00
BigIntegerToStringSmall \pr\corerun.exe 54.494 ns 0.82 3,834 B 0.0083 104 B 1.00
BigIntegerToStringSmallFormat \main\corerun.exe 93.504 ns 1.00 5,649 B 0.0038 48 B 1.00
BigIntegerToStringSmallFormat \pr\corerun.exe 84.198 ns 0.90 5,561 B 0.0038 48 B 1.00
BigIntegerParseSmall \main\corerun.exe 159.157 ns 1.00 1,384 B 0.0038 48 B 1.00
BigIntegerParseSmall \pr\corerun.exe 161.781 ns 1.02 1,369 B 0.0038 48 B 1.00
BigIntegerToStringLargeFormat \main\corerun.exe 6,472.271 ns 1.00 6,851 B - 48 B 1.00
BigIntegerToStringLargeFormat \pr\corerun.exe 6,094.135 ns 0.94 7,813 B - 48 B 1.00
BigIntegerParseLarge \main\corerun.exe 5,622.848 ns 1.00 3,091 B 0.0305 440 B 1.00
BigIntegerParseLarge \pr\corerun.exe 5,594.716 ns 1.00 3,076 B 0.0305 440 B 1.00
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Configs;
using System;
using System.Numerics;

[DisassemblyDiagnoser]
[MemoryDiagnoser(false)]
[HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByMethod)]
public class NumberClassTest
{
    BigInteger small = 10 * (BigInteger)UInt128.MaxValue;
    BigInteger large = BigInteger.Pow(11, 1000);
    string longString = new string('9', 1000);

    [Benchmark] public string UInt32ToString() => 123456789u.ToString();
    [Benchmark] public string UInt32ToStringFormat() => 123456789u.ToString("E5");

    [Benchmark] public string BigIntegerToStringSmall() => small.ToString();
    [Benchmark] public string BigIntegerToStringSmallFormat() => small.ToString("E5");
    [Benchmark] public BigInteger BigIntegerParseSmall() => BigInteger.Parse("12345678901234567890123456789012345678901234567890");

    [Benchmark] public string BigIntegerToStringLargeFormat() => large.ToString("E5");
    [Benchmark] public BigInteger BigIntegerParseLarge() => BigInteger.Parse(longString);
}

@dotnet-policy-service dotnet-policy-service bot added linkable-framework Issues associated with delivering a linker friendly framework community-contribution Indicates that the PR has been added by a community member labels Sep 18, 2025
@tannergooding
Copy link
Member

I'd rather we hold off on this and look at defining an API proposal to expose the needed APIs publicly as part of .NET 11 instead.

This is a core need for anyone trying to support both UTF-16 and UTF-8 for their own types and the workaround done was explicitly the way it was because other developers may end up copying it, so it does what is safe and well-defined. By using UnsafeAccessor it is introducing unnecessary coupling and risk due to relying on implementation details which are not guaranteed.

By exposing the necessary APIs and/or interfaces officially, we instead solve the issue more generally and avoid the need for the unsafe coupling.

@kzrnm
Copy link
Contributor Author

kzrnm commented Sep 18, 2025

I'd rather we hold off on this and look at defining an API proposal to expose the needed APIs publicly as part of .NET 11 instead.

That’s great!

@jkotas
Copy link
Member

jkotas commented Sep 18, 2025

I agree with @tannergooding .

We do not want to use UnsafeAccessor in the core libraries more than absolutely necessary.

@jkotas jkotas closed this Sep 18, 2025
@jkotas
Copy link
Member

jkotas commented Sep 18, 2025

(Alternatively, I think folding System.Runtime.Numerics into CoreLib would be acceptable as well and preferred over UnsafeAccessor.)

@kzrnm kzrnm deleted the feature/NumericsUnsafeAccessor branch September 19, 2025 01:13
@github-actions github-actions bot locked and limited conversation to collaborators Oct 20, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Numerics community-contribution Indicates that the PR has been added by a community member linkable-framework Issues associated with delivering a linker friendly framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants