From b85d94a39d24895bab78d2d8ee4d1e207ced9367 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:05:27 -0700 Subject: [PATCH] Use ValueArray in ParamsArray. --- .../src/System/IO/StreamWriter.cs | 18 ++-- .../src/System/ParamsArray.cs | 99 +++++++++---------- .../src/System/String.Manipulation.cs | 18 ++-- .../src/System/Text/StringBuilder.cs | 18 ++-- .../Text/ValueStringBuilder.AppendFormat.cs | 18 ++-- 5 files changed, 84 insertions(+), 87 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 7ba362604d01c7..ccb44424728a53 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -517,7 +517,7 @@ public override void WriteLine(ReadOnlySpan buffer) } } - private void WriteFormatHelper(string format, ParamsArray args, bool appendNewLine) + private void WriteFormatHelper(string format, ParamsArray args, bool appendNewLine) where TArr : IValueArray { StringBuilder sb = StringBuilderCache.Acquire((format?.Length ?? 0) + args.Length * 8) @@ -542,7 +542,7 @@ public override void Write(string format, object? arg0) { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: false); + WriteFormatHelper(format, ParamsArray.Create(arg0), appendNewLine: false); } else { @@ -554,7 +554,7 @@ public override void Write(string format, object? arg0, object? arg1) { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: false); + WriteFormatHelper(format, ParamsArray.Create(arg0, arg1), appendNewLine: false); } else { @@ -566,7 +566,7 @@ public override void Write(string format, object? arg0, object? arg1, object? ar { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: false); + WriteFormatHelper(format, ParamsArray.Create(arg0, arg1, arg2), appendNewLine: false); } else { @@ -582,7 +582,7 @@ public override void Write(string format, params object?[] arg) { throw new ArgumentNullException((format == null) ? nameof(format) : nameof(arg)); // same as base logic } - WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: false); + WriteFormatHelper(format, ParamsArray.Create(arg), appendNewLine: false); } else { @@ -594,7 +594,7 @@ public override void WriteLine(string format, object? arg0) { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0), appendNewLine: true); + WriteFormatHelper(format, ParamsArray.Create(arg0), appendNewLine: true); } else { @@ -606,7 +606,7 @@ public override void WriteLine(string format, object? arg0, object? arg1) { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0, arg1), appendNewLine: true); + WriteFormatHelper(format, ParamsArray.Create(arg0, arg1), appendNewLine: true); } else { @@ -618,7 +618,7 @@ public override void WriteLine(string format, object? arg0, object? arg1, object { if (GetType() == typeof(StreamWriter)) { - WriteFormatHelper(format, new ParamsArray(arg0, arg1, arg2), appendNewLine: true); + WriteFormatHelper(format, ParamsArray.Create(arg0, arg1, arg2), appendNewLine: true); } else { @@ -634,7 +634,7 @@ public override void WriteLine(string format, params object?[] arg) { throw new ArgumentNullException(nameof(arg)); } - WriteFormatHelper(format, new ParamsArray(arg), appendNewLine: true); + WriteFormatHelper(format, ParamsArray.Create(arg), appendNewLine: true); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/ParamsArray.cs b/src/libraries/System.Private.CoreLib/src/System/ParamsArray.cs index a995fe70be9af8..e88f243985591d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ParamsArray.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ParamsArray.cs @@ -3,73 +3,70 @@ namespace System { - internal readonly struct ParamsArray + internal readonly struct ParamsArray where TArr : IValueArray { - // Sentinel fixed-length arrays eliminate the need for a "count" field keeping this - // struct down to just 4 fields. These are only used for their "Length" property, - // that is, their elements are never set or referenced. - private static readonly object?[] s_oneArgArray = new object?[1]; - private static readonly object?[] s_twoArgArray = new object?[2]; - private static readonly object?[] s_threeArgArray = new object?[3]; + // NOTE: arguments in an array form are stored in element #0 of object?[MaxInlineArgs + 1]. + // alternatively we could use object?[2] with element #0 storing a sentinel object and #2 the actual array + // that will take less space, but sentinel check is an actual compare vs. length compare that can be done at JIT time. + // This is a size/cycles tradeoff, which I did not have time to measure, but either way would work. + private const int MaxInlineArgs = 3; - private readonly object? _arg0; - private readonly object? _arg1; - private readonly object? _arg2; + private readonly TArr _args; - // After construction, the first three elements of this array will never be accessed - // because the indexer will retrieve those values from arg0, arg1, and arg2. - private readonly object?[] _args; - - public ParamsArray(object? arg0) + internal ParamsArray(TArr args) { - _arg0 = arg0; - _arg1 = null; - _arg2 = null; - - // Always assign this.args to make use of its "Length" property - _args = s_oneArgArray; + _args = args; } - public ParamsArray(object? arg0, object? arg1) - { - _arg0 = arg0; - _arg1 = arg1; - _arg2 = null; + // NB: _args.Length is a JIT-time constant. + public int Length => _args.Length > MaxInlineArgs ? + ((object?[])_args[0]!).Length : + _args.Length; - // Always assign this.args to make use of its "Length" property - _args = s_twoArgArray; - } + public object? this[int index] => _args.Length > MaxInlineArgs ? + ((object?[])_args[0]!)[index] : + _args[index]; + } - public ParamsArray(object? arg0, object? arg1, object? arg2) - { - _arg0 = arg0; - _arg1 = arg1; - _arg2 = arg2; + internal static class ParamsArray + { + private static ParamsArray Create(TArr args) where TArr : IValueArray + => new ParamsArray(args); - // Always assign this.args to make use of its "Length" property - _args = s_threeArgArray; + public static ParamsArray> Create(object? arg0) + { + ValueArray args = default; + args[0] = arg0; + return Create(args); } - public ParamsArray(object?[] args) + public static ParamsArray> Create(object? arg0, object? arg1) { - int len = args.Length; - _arg0 = len > 0 ? args[0] : null; - _arg1 = len > 1 ? args[1] : null; - _arg2 = len > 2 ? args[2] : null; - _args = args; + ValueArray args = default; + args[0] = arg0; + args[1] = arg1; + return Create(args); } - public int Length => _args.Length; - - public object? this[int index] => index == 0 ? _arg0 : GetAtSlow(index); +#if NICE_SYNTAX + public static ParamsArray Create(object? arg0, object? arg1, object? arg2) + => Create(new object?[3] { arg0, arg1, arg2 }); +#else + public static ParamsArray> Create(object? arg0, object? arg1, object? arg2) + { + ValueArray args = default; + args[0] = arg0; + args[1] = arg1; + args[2] = arg2; + return Create(args); + } +#endif - private object? GetAtSlow(int index) + public static ParamsArray> Create(object?[] args) { - if (index == 1) - return _arg1; - if (index == 2) - return _arg2; - return _args[index]; + ValueArray argsArr = default; + argsArr[0] = args; + return Create(argsArr); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index 3e4ea6ab5ca8e5..c38b2314a666d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -439,17 +439,17 @@ public static string Concat(params string?[] values) public static string Format(string format, object? arg0) { - return FormatHelper(null, format, new ParamsArray(arg0)); + return FormatHelper(null, format, ParamsArray.Create(arg0)); } public static string Format(string format, object? arg0, object? arg1) { - return FormatHelper(null, format, new ParamsArray(arg0, arg1)); + return FormatHelper(null, format, ParamsArray.Create(arg0, arg1)); } public static string Format(string format, object? arg0, object? arg1, object? arg2) { - return FormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); + return FormatHelper(null, format, ParamsArray.Create(arg0, arg1, arg2)); } public static string Format(string format, params object?[] args) @@ -461,22 +461,22 @@ public static string Format(string format, params object?[] args) throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args)); } - return FormatHelper(null, format, new ParamsArray(args)); + return FormatHelper(null, format, ParamsArray.Create(args)); } public static string Format(IFormatProvider? provider, string format, object? arg0) { - return FormatHelper(provider, format, new ParamsArray(arg0)); + return FormatHelper(provider, format, ParamsArray.Create(arg0)); } public static string Format(IFormatProvider? provider, string format, object? arg0, object? arg1) { - return FormatHelper(provider, format, new ParamsArray(arg0, arg1)); + return FormatHelper(provider, format, ParamsArray.Create(arg0, arg1)); } public static string Format(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) { - return FormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); + return FormatHelper(provider, format, ParamsArray.Create(arg0, arg1, arg2)); } public static string Format(IFormatProvider? provider, string format, params object?[] args) @@ -488,10 +488,10 @@ public static string Format(IFormatProvider? provider, string format, params obj throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args)); } - return FormatHelper(provider, format, new ParamsArray(args)); + return FormatHelper(provider, format, ParamsArray.Create(args)); } - private static string FormatHelper(IFormatProvider? provider, string format, ParamsArray args) + private static string FormatHelper(IFormatProvider? provider, string format, ParamsArray args) where TArr : IValueArray { if (format == null) throw new ArgumentNullException(nameof(format)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 5957628a6ac846..9601ef5768e058 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -1499,11 +1499,11 @@ public StringBuilder Insert(int index, ReadOnlySpan value) return this; } - public StringBuilder AppendFormat(string format, object? arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); + public StringBuilder AppendFormat(string format, object? arg0) => AppendFormatHelper(null, format, ParamsArray.Create(arg0)); - public StringBuilder AppendFormat(string format, object? arg0, object? arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); + public StringBuilder AppendFormat(string format, object? arg0, object? arg1) => AppendFormatHelper(null, format, ParamsArray.Create(arg0, arg1)); - public StringBuilder AppendFormat(string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); + public StringBuilder AppendFormat(string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(null, format, ParamsArray.Create(arg0, arg1, arg2)); public StringBuilder AppendFormat(string format, params object?[] args) { @@ -1515,14 +1515,14 @@ public StringBuilder AppendFormat(string format, params object?[] args) throw new ArgumentNullException(paramName); } - return AppendFormatHelper(null, format, new ParamsArray(args)); + return AppendFormatHelper(null, format, ParamsArray.Create(args)); } - public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); + public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0)); - public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); + public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0, arg1)); - public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); + public StringBuilder AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0, arg1, arg2)); public StringBuilder AppendFormat(IFormatProvider? provider, string format, params object?[] args) { @@ -1534,7 +1534,7 @@ public StringBuilder AppendFormat(IFormatProvider? provider, string format, para throw new ArgumentNullException(paramName); } - return AppendFormatHelper(provider, format, new ParamsArray(args)); + return AppendFormatHelper(provider, format, ParamsArray.Create(args)); } private static void FormatError() @@ -1546,7 +1546,7 @@ private static void FormatError() private const int IndexLimit = 1000000; // Note: 0 <= ArgIndex < IndexLimit private const int WidthLimit = 1000000; // Note: -WidthLimit < ArgAlign < WidthLimit - internal StringBuilder AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args) + internal StringBuilder AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args) where TArr : IValueArray { if (format == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs index d11eae6ce47b9e..ed6b92e4321b5d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs @@ -5,11 +5,11 @@ namespace System.Text { internal ref partial struct ValueStringBuilder { - public void AppendFormat(string format, object? arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); + public void AppendFormat(string format, object? arg0) => AppendFormatHelper(null, format, ParamsArray.Create(arg0)); - public void AppendFormat(string format, object? arg0, object? arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); + public void AppendFormat(string format, object? arg0, object? arg1) => AppendFormatHelper(null, format, ParamsArray.Create(arg0, arg1)); - public void AppendFormat(string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2)); + public void AppendFormat(string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(null, format, ParamsArray.Create(arg0, arg1, arg2)); public void AppendFormat(string format, params object?[] args) { @@ -21,14 +21,14 @@ public void AppendFormat(string format, params object?[] args) throw new ArgumentNullException(paramName); } - AppendFormatHelper(null, format, new ParamsArray(args)); + AppendFormatHelper(null, format, ParamsArray.Create(args)); } - public void AppendFormat(IFormatProvider? provider, string format, object? arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0)); + public void AppendFormat(IFormatProvider? provider, string format, object? arg0) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0)); - public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1)); + public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0, arg1)); - public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2)); + public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(provider, format, ParamsArray.Create(arg0, arg1, arg2)); public void AppendFormat(IFormatProvider? provider, string format, params object?[] args) { @@ -40,7 +40,7 @@ public void AppendFormat(IFormatProvider? provider, string format, params object throw new ArgumentNullException(paramName); } - AppendFormatHelper(provider, format, new ParamsArray(args)); + AppendFormatHelper(provider, format, ParamsArray.Create(args)); } internal void AppendSpanFormattable(T value, string? format, IFormatProvider? provider) where T : ISpanFormattable @@ -57,7 +57,7 @@ internal void AppendSpanFormattable(T value, string? format, IFormatProvider? // Copied from StringBuilder, can't be done via generic extension // as ValueStringBuilder is a ref struct and cannot be used in a generic. - internal void AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args) + internal void AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args) where TArr : IValueArray { // Undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment. const int IndexLimit = 1000000; // Note: 0 <= ArgIndex < IndexLimit