From e6e60200cc9c04b9ac26157774b73670a5a1a4a1 Mon Sep 17 00:00:00 2001 From: Matthias Vill Date: Wed, 16 Apr 2025 19:14:46 +0200 Subject: [PATCH 1/2] Convert to SDK-Style-Project for .NET 4.8 + 9 --- PerformanceStubs/PerformanceStubs.csproj | 94 ++----------------- .../Tests/ConvertByteArrayToHexString/Test.cs | 6 ++ .../GettingObjectPropertiesByName/Test.cs | 16 +++- PerformanceStubs/packages.config | 4 - 4 files changed, 28 insertions(+), 92 deletions(-) delete mode 100644 PerformanceStubs/packages.config diff --git a/PerformanceStubs/PerformanceStubs.csproj b/PerformanceStubs/PerformanceStubs.csproj index 06a7fd9..8151f10 100644 --- a/PerformanceStubs/PerformanceStubs.csproj +++ b/PerformanceStubs/PerformanceStubs.csproj @@ -1,84 +1,12 @@ - - + - Debug - x86 - 8.0.30703 - 2.0 - {33EC6BFE-BD71-4054-9B50-D047ADA0C5E5} Exe - Properties - PerformanceStubs - PerformanceStubs - v4.0 - - - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net48;net9.0 + false true - - - False - ..\packages\FastMember.1.0.0.9\lib\net40\FastMember.dll - - - - + - - - - - - - - - - - - - - - - - - - - Code - - - Code - - - - - - - - - - - @@ -88,12 +16,10 @@ Always - - + + + + + + \ No newline at end of file diff --git a/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs b/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs index de71678..29a3329 100644 --- a/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs +++ b/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs @@ -4,7 +4,9 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +#if NETFRAMEWORK using System.Runtime.Remoting.Metadata.W3cXsd2001; +#endif using System.Text; using PerformanceStubs.Core; @@ -31,7 +33,9 @@ protected override List> TestCandidates { ByteArrayToHexStringViaStringBuilderForEachAppendFormat, ByteArrayToHexViaByteManipulation, ByteArrayToHexViaByteManipulation2, +#if NETFRAMEWORK ByteArrayToHexViaSoapHexBinary, +#endif ByteArrayToHexViaLookupAndShift, ByteArrayToHexViaLookup, ByteArrayToHexViaLookupPerByte, @@ -92,10 +96,12 @@ static string ByteArrayToHexViaByteManipulation2(byte[] bytes) { } return new string(c); } +#if NETFRAMEWORK static string ByteArrayToHexViaSoapHexBinary(byte[] bytes) { SoapHexBinary soapHexBinary = new SoapHexBinary(bytes); return soapHexBinary.ToString(); } +#endif const string hexAlphabet = "0123456789ABCDEF"; static string ByteArrayToHexViaLookupAndShift(byte[] bytes) { diff --git a/PerformanceStubs/Tests/GettingObjectPropertiesByName/Test.cs b/PerformanceStubs/Tests/GettingObjectPropertiesByName/Test.cs index 9e972f5..8233f08 100644 --- a/PerformanceStubs/Tests/GettingObjectPropertiesByName/Test.cs +++ b/PerformanceStubs/Tests/GettingObjectPropertiesByName/Test.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using PerformanceStubs.Core; - using System.Web.Routing; using FastMember; using System.Dynamic; @@ -21,13 +20,22 @@ protected override string Caption { protected override List>> TestCandidates { get { return (new Func>[] { - IDictionaryRouteValueDictionaryLookup, +#if NETFRAMEWORK + IDictionarySystemWebRouteValueDictionaryLookup, +#endif + IDictionaryAspNetCoreRouteValueDictionaryLookup, ObjectAccessorLookup }).ToList(); } } - public static Tuple IDictionaryRouteValueDictionaryLookup(object source) { - IDictionary dictionarySource = source as IDictionary ?? new RouteValueDictionary(source); +#if NETFRAMEWORK + public static Tuple IDictionarySystemWebRouteValueDictionaryLookup(object source) { + IDictionary dictionarySource = source as IDictionary ?? new System.Web.Routing.RouteValueDictionary(source); + return new Tuple(dictionarySource["Id"] as string, dictionarySource["Value"] as int?); + } +#endif + public static Tuple IDictionaryAspNetCoreRouteValueDictionaryLookup(object source) { + IDictionary dictionarySource = source as IDictionary ?? new Microsoft.AspNetCore.Routing.RouteValueDictionary(source); return new Tuple(dictionarySource["Id"] as string, dictionarySource["Value"] as int?); } public static Tuple ObjectAccessorLookup(object source) { diff --git a/PerformanceStubs/packages.config b/PerformanceStubs/packages.config deleted file mode 100644 index 1cd7f06..0000000 --- a/PerformanceStubs/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From f8eb05ba1a5e018a09d15ec3088cc437e2c0baa8 Mon Sep 17 00:00:00 2001 From: Matthias Vill Date: Wed, 16 Apr 2025 19:57:19 +0200 Subject: [PATCH 2/2] Add some newer option to turn byte-arrays into hex-strings --- .../Core/PerformanceTestOneInOneOut.cs | 13 +++- .../Tests/ConvertByteArrayToHexString/Test.cs | 74 ++++++++++++++++++- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/PerformanceStubs/Core/PerformanceTestOneInOneOut.cs b/PerformanceStubs/Core/PerformanceTestOneInOneOut.cs index fe865fe..163ec58 100644 --- a/PerformanceStubs/Core/PerformanceTestOneInOneOut.cs +++ b/PerformanceStubs/Core/PerformanceTestOneInOneOut.cs @@ -12,9 +12,13 @@ public abstract class PerformanceTestOneInOneOut : IPerformanc protected abstract string Title { get; } protected abstract string Caption { get; } - private PerformanceTestCandidateResult Run(Func operation, TInput1 victim, TResult expectedOutput, long iterations) { + private PerformanceTestCandidateResult Run(Func operation, TInput1 victim, Func typicalCandidate, TResult expectedOutput, long iterations) { TResult testResult = operation(victim); // also primes anything that may be a one-time cost - Debug.Assert(this.OutputComparer(expectedOutput, testResult)); + if (!this.OutputComparer(expectedOutput, testResult)) + { + throw new InvalidOperationException( + $"{operation.Method.Name} did not return the same as {typicalCandidate.Method.Name}. {testResult} != {expectedOutput}."); + } List elapsedTicksCollection = new List(); Stopwatch timer = new Stopwatch(); @@ -35,9 +39,10 @@ public PerformanceTestSummary Run() { return testSummary; } - TResult typicalAnswer = this.TestCandidates.First()(this.Input1); + var typicalCandidate = this.TestCandidates.First(); + TResult typicalAnswer = typicalCandidate(this.Input1); foreach (Func testCandidate in this.TestCandidates) { - PerformanceTestCandidateResult candidateResult = this.Run(testCandidate, this.Input1, typicalAnswer, this.Iterations); + PerformanceTestCandidateResult candidateResult = this.Run(testCandidate, this.Input1, typicalCandidate, typicalAnswer, this.Iterations); candidateResults.Add(candidateResult); } return testSummary; diff --git a/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs b/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs index 29a3329..074044d 100644 --- a/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs +++ b/PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs @@ -24,10 +24,27 @@ protected override string Caption { protected override List> TestCandidates { get { return (new Func[] { +#if NET5_0_OR_GREATER + ByteArrayToHexStringViaConvertToHexString, +#endif +#if NET9_0_OR_GREATER + ByteArrayToHexStringViaStringCreateConvertTryToHexString, +#endif ByteArrayToHexStringViaStringJoinArrayConvertAll, ByteArrayToHexStringViaStringConcatArrayConvertAll, - ByteArrayToHexStringViaBitConverter, +#if NETCOREAPP2_1_OR_GREATER + ByteArrayToHexStringViaStringCreateByteTryFormat, +#endif +#if NET9_0_OR_GREATER + ByteArrayToHexStringViaStringCreateLoopConvertTryToHexString, +#endif + ByteArrayToHexStringViaBitConverterThenReplace, + +#if NETCOREAPP2_1_OR_GREATER + ByteArrayToHexStringViaBitConverterThenCopy, +#endif ByteArrayToHexStringViaStringBuilderAggregateByteToString, + ByteArrayToHexStringViaStringBuilderAggregateByteInterpolation, ByteArrayToHexStringViaStringBuilderAggregateAppendFormat, ByteArrayToHexStringViaStringBuilderForEachByteToString, ByteArrayToHexStringViaStringBuilderForEachAppendFormat, @@ -43,19 +60,72 @@ protected override List> TestCandidates { }).ToList(); } } +#if NET5_0_OR_GREATER + static string ByteArrayToHexStringViaConvertToHexString(byte[] bytes) { + return Convert.ToHexString(bytes); + } +#endif +#if NET9_0_OR_GREATER + static string ByteArrayToHexStringViaStringCreateConvertTryToHexString(byte[] bytes) { + return string.Create(bytes.Length * 2, bytes, static (span, bytes) => Convert.TryToHexString(bytes, span, out _)); + } +#endif static string ByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes) { return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2"))); } static string ByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes) { return string.Concat(Array.ConvertAll(bytes, b => b.ToString("X2"))); } - static string ByteArrayToHexStringViaBitConverter(byte[] bytes) { +#if NETCOREAPP2_1_OR_GREATER + static string ByteArrayToHexStringViaStringCreateByteTryFormat(byte[] bytes) { + return string.Create(bytes.Length * 2, bytes, static (span, bytes) => + { + for (var i = 0; i < bytes.Length; i++) + { + bytes[i].TryFormat(span.Slice(i * 2, 2), out _, "X2"); + } + }); + } +#endif +#if NET9_0_OR_GREATER + static string ByteArrayToHexStringViaStringCreateLoopConvertTryToHexString(byte[] bytes) { + return string.Create(bytes.Length * 2, bytes, static (span, bytes) => + { + for (var i = 0; i < bytes.Length; i++) + { + Convert.TryToHexString(bytes.AsSpan(i, 1), span.Slice(i * 2, 2), out _); + } + }); + } +#endif + static string ByteArrayToHexStringViaBitConverterThenReplace(byte[] bytes) { string hex = BitConverter.ToString(bytes); return hex.Replace("-", ""); } +#if NETCOREAPP2_1_OR_GREATER + static string ByteArrayToHexStringViaBitConverterThenCopy(byte[] bytes) { + if (bytes.Length < 2) + { + return BitConverter.ToString(bytes); + } + return string.Create(bytes.Length * 2, bytes, static (span, bytes) => + { + string hex = BitConverter.ToString(bytes); + for (int i = 0; i < bytes.Length; i++) + { + span[i * 2 + 0] = hex[i * 3 + 0]; + span[i * 2 + 1] = hex[i * 3 + 1]; + } + }); + } +#endif static string ByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes) { return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString(); } + static string ByteArrayToHexStringViaStringBuilderAggregateByteInterpolation(byte[] bytes) { + // Syntax-Compatible to .NET Framework, but uses Interpolation-Handler starting with .NET 6 + return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append($"{b:X2}")).ToString(); + } static string ByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes) { StringBuilder hex = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes)