@@ -266,7 +266,23 @@ internal static partial class Number
266266 private const int CharStackBufferSize = 32 ;
267267 private const string PosNumberFormat = "#" ;
268268
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 ] ;
270286
271287 private static readonly string [ ] s_posCurrencyFormats =
272288 {
@@ -1683,14 +1699,31 @@ internal static unsafe void WriteTwoDigits(byte* ptr, uint value)
16831699 return bufferEnd ;
16841700 }
16851701
1702+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
16861703 internal static unsafe string UInt32ToDecStr ( uint value )
16871704 {
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 )
16901707 {
1691- return s_singleDigitStringCache [ value ] ;
1708+ return UInt32ToDecStrForKnownSmallNumber ( value ) ;
16921709 }
16931710
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+ {
16941727 int bufferLength = FormattingHelpers . CountDigits ( value ) ;
16951728
16961729 string result = string . FastAllocateString ( bufferLength ) ;
@@ -2086,10 +2119,10 @@ private static uint Int64DivMod1E9(ref ulong value)
20862119
20872120 internal static unsafe string UInt64ToDecStr ( ulong value )
20882121 {
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 )
20912124 {
2092- return s_singleDigitStringCache [ value ] ;
2125+ return UInt32ToDecStrForKnownSmallNumber ( ( uint ) value ) ;
20932126 }
20942127
20952128 int bufferLength = FormattingHelpers . CountDigits ( value ) ;
@@ -2374,15 +2407,13 @@ private static ulong Int128DivMod1E19(ref UInt128 value)
23742407
23752408 internal static unsafe string UInt128ToDecStr ( UInt128 value )
23762409 {
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 )
23822411 {
2383- return s_singleDigitStringCache [ value . Lower ] ;
2412+ return UInt64ToDecStr ( value . Lower ) ;
23842413 }
23852414
2415+ int bufferLength = FormattingHelpers . CountDigits ( value ) ;
2416+
23862417 string result = string . FastAllocateString ( bufferLength ) ;
23872418 fixed ( char * buffer = result )
23882419 {
0 commit comments