Skip to content

Commit

Permalink
Optimize BigInteger formatting (dotnet#100181)
Browse files Browse the repository at this point in the history
* Cleanup DecStr formatting

* Cleanup ToNumber portion

* Clean and pool base1E9 buffer

* Split leaf span and array path

* Post cherry-pick update

* Remove unworthy positive/negative span/string split

* Use stackalloc for buffers

* Update constant usage

* Use UInt32ToDecChars

* Fix type assert

* Fix strLength

---------

Co-authored-by: Tanner Gooding <[email protected]>
  • Loading branch information
2 people authored and matouskozak committed Apr 30, 2024
1 parent 14e3f1d commit 2e49497
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 168 deletions.
2 changes: 2 additions & 0 deletions src/libraries/Common/src/System/Number.Formatting.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace System
{
internal static partial class Number
{
private const int CharStackBufferSize = 32;

private const int DefaultPrecisionExponentialFormat = 6;

private const int MaxUInt32DecDigits = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Base64Decoder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Base64Validator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\FormattingHelpers.CountDigits.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\FormattingHelpers.CountDigits.Int128.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Constants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\FormattingHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Boolean.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Buffers.Text
{
internal static partial class FormattingHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountDigits(UInt128 value)
{
ulong upper = value.Upper;

// 1e19 is 8AC7_2304_89E8_0000
// 1e20 is 5_6BC7_5E2D_6310_0000
// 1e21 is 36_35C9_ADC5_DEA0_0000

if (upper == 0)
{
// We have less than 64-bits, so just return the lower count
return CountDigits(value.Lower);
}

// We have more than 1e19, so we have at least 20 digits
int digits = 20;

if (upper > 5)
{
// ((2^128) - 1) / 1e20 < 34_02_823_669_209_384_635 which
// is 18.5318 digits, meaning the result definitely fits
// into 64-bits and we only need to add the lower digit count

value /= new UInt128(0x5, 0x6BC7_5E2D_6310_0000); // value /= 1e20
Debug.Assert(value.Upper == 0);

digits += CountDigits(value.Lower);
}
else if ((upper == 5) && (value.Lower >= 0x6BC75E2D63100000))
{
// We're greater than 1e20, but definitely less than 1e21
// so we have exactly 21 digits

digits++;
Debug.Assert(digits == 21);
}

return digits;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountHexDigits(UInt128 value)
{
// The number of hex digits is log16(value) + 1, or log2(value) / 4 + 1
return ((int)UInt128.Log2(value) >> 2) + 1;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
Expand All @@ -11,47 +10,6 @@ namespace System.Buffers.Text
{
internal static partial class FormattingHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountDigits(UInt128 value)
{
ulong upper = value.Upper;

// 1e19 is 8AC7_2304_89E8_0000
// 1e20 is 5_6BC7_5E2D_6310_0000
// 1e21 is 36_35C9_ADC5_DEA0_0000

if (upper == 0)
{
// We have less than 64-bits, so just return the lower count
return CountDigits(value.Lower);
}

// We have more than 1e19, so we have at least 20 digits
int digits = 20;

if (upper > 5)
{
// ((2^128) - 1) / 1e20 < 34_02_823_669_209_384_635 which
// is 18.5318 digits, meaning the result definitely fits
// into 64-bits and we only need to add the lower digit count

value /= new UInt128(0x5, 0x6BC7_5E2D_6310_0000); // value /= 1e20
Debug.Assert(value.Upper == 0);

digits += CountDigits(value.Lower);
}
else if ((upper == 5) && (value.Lower >= 0x6BC75E2D63100000))
{
// We're greater than 1e20, but definitely less than 1e21
// so we have exactly 21 digits

digits++;
Debug.Assert(digits == 21);
}

return digits;
}

// Based on do_count_digits from https://github.com/fmtlib/fmt/blob/662adf4f33346ba9aba8b072194e319869ede54a/include/fmt/format.h#L1124
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountDigits(ulong value)
Expand Down Expand Up @@ -149,13 +107,6 @@ public static int CountDigits(uint value)
return (int)((value + tableValue) >> 32);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountHexDigits(UInt128 value)
{
// The number of hex digits is log16(value) + 1, or log2(value) / 4 + 1
return ((int)UInt128.Log2(value) >> 2) + 1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CountHexDigits(ulong value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,6 @@ internal static partial class Number
private const int SinglePrecisionCustomFormat = 7;
private const int DoublePrecisionCustomFormat = 15;

private const int CharStackBufferSize = 32;

/// <summary>The non-inclusive upper bound of <see cref="s_smallNumberCache"/>.</summary>
/// <remarks>
/// This is a semi-arbitrary bound. For mono, which is often used for more size-constrained workloads,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
</ItemGroup>

<ItemGroup>
<Compile Include="$(CoreLibSharedDir)System\Buffers\Text\FormattingHelpers.CountDigits.cs"
Link="CoreLib\System\Buffers\Text\FormattingHelpers.CountDigits.cs" />
<Compile Include="$(CoreLibSharedDir)System\Collections\Generic\ValueListBuilder.cs"
Link="CoreLib\System\Collections\Generic\ValueListBuilder.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs"
Expand Down
Loading

0 comments on commit 2e49497

Please sign in to comment.