diff options
author | Stephen Toub <stoub@microsoft.com> | 2017-11-29 20:57:47 +0300 |
---|---|---|
committer | Stephen Toub <stoub@microsoft.com> | 2017-12-01 21:32:00 +0300 |
commit | e0d999b6e85b325beada1f3cfec205d2cdf34684 (patch) | |
tree | a5272a9f3b7c76014d67fa6fd4e020d0509010fb | |
parent | 826a79eb6e55a8b3bf1beddc25ba1bbeb35d3cdf (diff) |
Avoid provider-related costs for default integer formatting (#15272)
One of the biggest costs when invoking Int32.ToString() and similar methods is in fetching the current culture, accessing its number information, etc. And if a culture is provided explicitly, there are still costs related to casts. But when the default format is used for non-negative integer values, the number format information isn't used at all, and we can skip the associated costs. This improves formatting perf in such cases by upwards of ~25%.
Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
10 files changed, 136 insertions, 111 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Byte.cs b/src/System.Private.CoreLib/shared/System/Byte.cs index a453a6f57..13ceb7573 100644 --- a/src/System.Private.CoreLib/shared/System/Byte.cs +++ b/src/System.Private.CoreLib/shared/System/Byte.cs @@ -174,22 +174,22 @@ namespace System public override String ToString() { - return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, null, null); } public String ToString(String format) { - return Number.FormatInt32(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, format, null); } public String ToString(IFormatProvider provider) { - return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/25337: Remove this overload once corefx is updated to target the new signatures @@ -198,7 +198,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } // diff --git a/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs index 7293642fb..a98023dc9 100644 --- a/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs +++ b/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs @@ -218,35 +218,25 @@ namespace System.Globalization public static NumberFormatInfo GetInstance(IFormatProvider formatProvider) { - if (formatProvider != null) + return formatProvider == null ? + CurrentInfo : // Fast path for a null provider + GetProviderNonNull(formatProvider); + + NumberFormatInfo GetProviderNonNull(IFormatProvider provider) { - // Fast case for a regular CultureInfo - NumberFormatInfo info; - CultureInfo cultureProvider = formatProvider as CultureInfo; - if (cultureProvider != null && !cultureProvider._isInherited) + // Fast path for a regular CultureInfo + if (provider is CultureInfo cultureProvider && !cultureProvider._isInherited) { return cultureProvider.numInfo ?? cultureProvider.NumberFormat; } - // Fast case for an NFI; - info = formatProvider as NumberFormatInfo; - if (info != null) - { - return info; - } - - info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo; - if (info != null) - { - return info; - } + return + provider as NumberFormatInfo ?? // Fast path for an NFI + provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo ?? + CurrentInfo; } - - return CurrentInfo; } - - public Object Clone() { NumberFormatInfo n = (NumberFormatInfo)MemberwiseClone(); diff --git a/src/System.Private.CoreLib/shared/System/Int16.cs b/src/System.Private.CoreLib/shared/System/Int16.cs index 2fadb1f9b..29cad9d66 100644 --- a/src/System.Private.CoreLib/shared/System/Int16.cs +++ b/src/System.Private.CoreLib/shared/System/Int16.cs @@ -69,22 +69,28 @@ namespace System public override String ToString() { - return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format) { - return ToString(format, NumberFormatInfo.CurrentInfo); + return ToString(format, null); } public String ToString(String format, IFormatProvider provider) { - return ToString(format, NumberFormatInfo.GetInstance(provider)); + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.FormatUInt32(temp, format, provider); + } + + return Number.FormatInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -93,24 +99,12 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) { uint temp = (uint)(m_value & 0x0000FFFF); - return Number.TryFormatUInt32(temp, format, info, destination, out charsWritten); - } - return Number.TryFormatInt32(m_value, format, info, destination, out charsWritten); - } - - private String ToString(String format, NumberFormatInfo info) - { - if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x0000FFFF); - return Number.FormatUInt32(temp, format, info); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); } - return Number.FormatInt32(m_value, format, info); + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } public static short Parse(String s) diff --git a/src/System.Private.CoreLib/shared/System/Int32.cs b/src/System.Private.CoreLib/shared/System/Int32.cs index 7a44a4a0e..52395d991 100644 --- a/src/System.Private.CoreLib/shared/System/Int32.cs +++ b/src/System.Private.CoreLib/shared/System/Int32.cs @@ -78,22 +78,22 @@ namespace System public override String ToString() { - return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, null, null); } public String ToString(String format) { - return Number.FormatInt32(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, format, null); } public String ToString(IFormatProvider provider) { - return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -102,7 +102,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } public static int Parse(String s) diff --git a/src/System.Private.CoreLib/shared/System/Int64.cs b/src/System.Private.CoreLib/shared/System/Int64.cs index c198f1800..8c2a43d7c 100644 --- a/src/System.Private.CoreLib/shared/System/Int64.cs +++ b/src/System.Private.CoreLib/shared/System/Int64.cs @@ -75,22 +75,22 @@ namespace System public override String ToString() { - return Number.FormatInt64(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatInt64(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatInt64(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt64(m_value, null, provider); } public String ToString(String format) { - return Number.FormatInt64(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatInt64(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatInt64(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt64(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -99,7 +99,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatInt64(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatInt64(m_value, format, provider, destination, out charsWritten); } public static long Parse(String s) diff --git a/src/System.Private.CoreLib/shared/System/Number.Formatting.cs b/src/System.Private.CoreLib/shared/System/Number.Formatting.cs index 17a4d87cb..ddf9ef1b2 100644 --- a/src/System.Private.CoreLib/shared/System/Number.Formatting.cs +++ b/src/System.Private.CoreLib/shared/System/Number.Formatting.cs @@ -573,10 +573,16 @@ namespace System return false; } - public static string FormatInt32(int value, ReadOnlySpan<char> format, NumberFormatInfo info) + public static string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt32ToDecStr((uint)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -613,10 +619,16 @@ namespace System } } - public static bool TryFormatInt32(int value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten) + public static bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -653,10 +665,16 @@ namespace System } } - public static string FormatUInt32(uint value, ReadOnlySpan<char> format, NumberFormatInfo info) + public static string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format + if (format.Length == 0) + { + return UInt32ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -691,10 +709,16 @@ namespace System } } - public static bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten) + public static bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format + if (format.Length == 0) + { + return TryUInt32ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -729,10 +753,16 @@ namespace System } } - public static string FormatInt64(long value, ReadOnlySpan<char> format, NumberFormatInfo info) + public static string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return UInt64ToDecStr((ulong)value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -770,10 +800,16 @@ namespace System } } - public static bool TryFormatInt64(long value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten) + public static bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format with a non-negative value + if (value >= 0 && format.Length == 0) + { + return TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -811,10 +847,16 @@ namespace System } } - public static string FormatUInt64(ulong value, ReadOnlySpan<char> format, NumberFormatInfo info) + public static string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format + if (format.Length == 0) + { + return UInt64ToDecStr(value, digits: -1); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') @@ -850,10 +892,16 @@ namespace System } } - public static bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten) + public static bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten) { - int digits; - char fmt = ParseFormatSpecifier(format, out digits); + // Fast path for default format + if (format.Length == 0) + { + return TryUInt64ToDecStr(value, digits: -1, destination, out charsWritten); + } + + char fmt = ParseFormatSpecifier(format, out int digits); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') diff --git a/src/System.Private.CoreLib/shared/System/SByte.cs b/src/System.Private.CoreLib/shared/System/SByte.cs index dc4f6adde..1242c9e46 100644 --- a/src/System.Private.CoreLib/shared/System/SByte.cs +++ b/src/System.Private.CoreLib/shared/System/SByte.cs @@ -73,22 +73,27 @@ namespace System // Provides a string representation of a byte. public override String ToString() { - return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatInt32(m_value, null, provider); } public String ToString(String format) { - return ToString(format, NumberFormatInfo.CurrentInfo); + return ToString(format, null); } public String ToString(String format, IFormatProvider provider) { - return ToString(format, NumberFormatInfo.GetInstance(provider)); + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x000000FF); + return Number.FormatUInt32(temp, format, provider); + } + return Number.FormatInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -97,24 +102,12 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) { uint temp = (uint)(m_value & 0x000000FF); - return Number.TryFormatUInt32(temp, format, info, destination, out charsWritten); - } - return Number.TryFormatInt32(m_value, format, info, destination, out charsWritten); - } - - private String ToString(String format, NumberFormatInfo info) - { - if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x000000FF); - return Number.FormatUInt32(temp, format, info); + return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); } - return Number.FormatInt32(m_value, format, info); + return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] diff --git a/src/System.Private.CoreLib/shared/System/UInt16.cs b/src/System.Private.CoreLib/shared/System/UInt16.cs index 5d21225b8..c79707270 100644 --- a/src/System.Private.CoreLib/shared/System/UInt16.cs +++ b/src/System.Private.CoreLib/shared/System/UInt16.cs @@ -69,23 +69,23 @@ namespace System // Converts the current value to a String in base-10 with no extra padding. public override String ToString() { - return Number.FormatUInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatUInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt32(m_value, null, provider); } public String ToString(String format) { - return Number.FormatUInt32(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt32(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -94,7 +94,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] diff --git a/src/System.Private.CoreLib/shared/System/UInt32.cs b/src/System.Private.CoreLib/shared/System/UInt32.cs index a007dc84e..752f67ff8 100644 --- a/src/System.Private.CoreLib/shared/System/UInt32.cs +++ b/src/System.Private.CoreLib/shared/System/UInt32.cs @@ -78,22 +78,22 @@ namespace System // The base 10 representation of the number with no extra padding. public override String ToString() { - return Number.FormatUInt32(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt32(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatUInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt32(m_value, null, provider); } public String ToString(String format) { - return Number.FormatUInt32(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt32(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt32(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -102,7 +102,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] diff --git a/src/System.Private.CoreLib/shared/System/UInt64.cs b/src/System.Private.CoreLib/shared/System/UInt64.cs index 87a3c5dae..34305b956 100644 --- a/src/System.Private.CoreLib/shared/System/UInt64.cs +++ b/src/System.Private.CoreLib/shared/System/UInt64.cs @@ -76,22 +76,22 @@ namespace System public override String ToString() { - return Number.FormatUInt64(m_value, null, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt64(m_value, null, null); } public String ToString(IFormatProvider provider) { - return Number.FormatUInt64(m_value, null, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt64(m_value, null, provider); } public String ToString(String format) { - return Number.FormatUInt64(m_value, format, NumberFormatInfo.CurrentInfo); + return Number.FormatUInt64(m_value, format, null); } public String ToString(String format, IFormatProvider provider) { - return Number.FormatUInt64(m_value, format, NumberFormatInfo.GetInstance(provider)); + return Number.FormatUInt64(m_value, format, provider); } // TODO https://github.com/dotnet/corefx/issues/23642: Remove once corefx has been updated with new overloads. @@ -100,7 +100,7 @@ namespace System public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider provider = null) { - return Number.TryFormatUInt64(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + return Number.TryFormatUInt64(m_value, format, provider, destination, out charsWritten); } [CLSCompliant(false)] |