@@ -266,7 +266,23 @@ internal static partial class Number
266
266
private const int CharStackBufferSize = 32 ;
267
267
private const string PosNumberFormat = "#" ;
268
268
269
- private static readonly string [ ] s_singleDigitStringCache = { "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" } ;
269
+ /// <summary>The non-inclusive upper bound of <see cref="s_smallNumberCache"/>.</summary>
270
+ /// <remarks>
271
+ /// This is a semi-arbitrary bound. For mono, which is often used for more size-constrained workloads,
272
+ /// we keep the size really small, supporting only single digit values. For coreclr, we use a larger
273
+ /// value, still relatively small but large enough to accomodate common sources of numbers to strings, e.g. HTTP success status codes.
274
+ /// By being >= 255, it also accomodates all byte.ToString()s. If no small numbers are ever formatted, we incur
275
+ /// the ~2400 bytes on 64-bit for the array itself. If all small numbers are formatted, we incur ~11,500 bytes
276
+ /// on 64-bit for the array and all the strings.
277
+ /// </remarks>
278
+ private const int SmallNumberCacheLength =
279
+ #if MONO
280
+ 10 ;
281
+ #else
282
+ 300 ;
283
+ #endif
284
+ /// <summary>Lazily-populated cache of strings for uint values in the range [0, <see cref="SmallNumberCacheLength"/>).</summary>
285
+ private static readonly string [ ] s_smallNumberCache = new string [ SmallNumberCacheLength ] ;
270
286
271
287
private static readonly string [ ] s_posCurrencyFormats =
272
288
{
@@ -1683,14 +1699,31 @@ internal static unsafe void WriteTwoDigits(byte* ptr, uint value)
1683
1699
return bufferEnd ;
1684
1700
}
1685
1701
1702
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1686
1703
internal static unsafe string UInt32ToDecStr ( uint value )
1687
1704
{
1688
- // For single-digit values that are very common, especially 0 and 1, just return cached strings .
1689
- if ( value < 10 )
1705
+ // For small numbers, consult a lazily-populated cache .
1706
+ if ( value < SmallNumberCacheLength )
1690
1707
{
1691
- return s_singleDigitStringCache [ value ] ;
1708
+ return UInt32ToDecStrForKnownSmallNumber ( value ) ;
1692
1709
}
1693
1710
1711
+ return UInt32ToDecStr_NoSmallNumberCheck ( value ) ;
1712
+ }
1713
+
1714
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1715
+ internal static string UInt32ToDecStrForKnownSmallNumber ( uint value )
1716
+ {
1717
+ Debug . Assert ( value < SmallNumberCacheLength ) ;
1718
+ return s_smallNumberCache [ value ] ?? CreateAndCacheString ( value ) ;
1719
+
1720
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ] // keep rare usage out of fast path
1721
+ static string CreateAndCacheString ( uint value ) =>
1722
+ s_smallNumberCache [ value ] = UInt32ToDecStr_NoSmallNumberCheck ( value ) ;
1723
+ }
1724
+
1725
+ private static unsafe string UInt32ToDecStr_NoSmallNumberCheck ( uint value )
1726
+ {
1694
1727
int bufferLength = FormattingHelpers . CountDigits ( value ) ;
1695
1728
1696
1729
string result = string . FastAllocateString ( bufferLength ) ;
@@ -2086,10 +2119,10 @@ private static uint Int64DivMod1E9(ref ulong value)
2086
2119
2087
2120
internal static unsafe string UInt64ToDecStr ( ulong value )
2088
2121
{
2089
- // For single-digit values that are very common, especially 0 and 1, just return cached strings .
2090
- if ( value < 10 )
2122
+ // For small numbers, consult a lazily-populated cache .
2123
+ if ( value < SmallNumberCacheLength )
2091
2124
{
2092
- return s_singleDigitStringCache [ value ] ;
2125
+ return UInt32ToDecStrForKnownSmallNumber ( ( uint ) value ) ;
2093
2126
}
2094
2127
2095
2128
int bufferLength = FormattingHelpers . CountDigits ( value ) ;
@@ -2374,15 +2407,13 @@ private static ulong Int128DivMod1E19(ref UInt128 value)
2374
2407
2375
2408
internal static unsafe string UInt128ToDecStr ( UInt128 value )
2376
2409
{
2377
- // Intrinsified in mono interpreter
2378
- int bufferLength = FormattingHelpers . CountDigits ( value ) ;
2379
-
2380
- // For single-digit values that are very common, especially 0 and 1, just return cached strings.
2381
- if ( bufferLength == 1 )
2410
+ if ( value . Upper == 0 )
2382
2411
{
2383
- return s_singleDigitStringCache [ value . Lower ] ;
2412
+ return UInt64ToDecStr ( value . Lower ) ;
2384
2413
}
2385
2414
2415
+ int bufferLength = FormattingHelpers . CountDigits ( value ) ;
2416
+
2386
2417
string result = string . FastAllocateString ( bufferLength ) ;
2387
2418
fixed ( char * buffer = result )
2388
2419
{
0 commit comments