From 890fe4053426cd2533534d589a754974e8907b6e Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Fri, 25 Jul 2025 10:07:20 -0400 Subject: [PATCH 1/4] Tests: Add benchmark suite for easily measuring improvements --- StackExchange.Redis.sln | 9 +++++- .../StackExchange.Redis.csproj | 1 + .../CustomConfig.cs | 24 ++++++++++++++ .../FormatBenchmarks.cs | 32 +++++++++++++++++++ .../StackExchange.Redis.Benchmarks/Program.cs | 10 ++++++ .../SlowConfig.cs | 12 +++++++ .../StackExchange.Redis.Benchmarks.csproj | 15 +++++++++ tests/StackExchange.Redis.Benchmarks/run.cmd | 1 + tests/StackExchange.Redis.Benchmarks/run.sh | 2 ++ 9 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 tests/StackExchange.Redis.Benchmarks/CustomConfig.cs create mode 100644 tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs create mode 100644 tests/StackExchange.Redis.Benchmarks/Program.cs create mode 100644 tests/StackExchange.Redis.Benchmarks/SlowConfig.cs create mode 100644 tests/StackExchange.Redis.Benchmarks/StackExchange.Redis.Benchmarks.csproj create mode 100644 tests/StackExchange.Redis.Benchmarks/run.cmd create mode 100644 tests/StackExchange.Redis.Benchmarks/run.sh diff --git a/StackExchange.Redis.sln b/StackExchange.Redis.sln index 20b5e2f01..8f772ae42 100644 --- a/StackExchange.Redis.sln +++ b/StackExchange.Redis.sln @@ -13,13 +13,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props + tests\RedisConfigs\docker-compose.yml = tests\RedisConfigs\docker-compose.yml global.json = global.json NuGet.Config = NuGet.Config README.md = README.md docs\ReleaseNotes.md = docs\ReleaseNotes.md Shared.ruleset = Shared.ruleset version.json = version.json - tests\RedisConfigs\docker-compose.yml = tests\RedisConfigs\docker-compose.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RedisConfigs", "RedisConfigs", "{96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}" @@ -120,6 +120,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestBaseline", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docs", "docs\docs.csproj", "{1DC43E76-5372-4C7F-A433-0602273E87FC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StackExchange.Redis.Benchmarks", "tests\StackExchange.Redis.Benchmarks\StackExchange.Redis.Benchmarks.csproj", "{59889284-FFEE-82E7-94CB-3B43E87DA6CF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -174,6 +176,10 @@ Global {1DC43E76-5372-4C7F-A433-0602273E87FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DC43E76-5372-4C7F-A433-0602273E87FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DC43E76-5372-4C7F-A433-0602273E87FC}.Release|Any CPU.Build.0 = Release|Any CPU + {59889284-FFEE-82E7-94CB-3B43E87DA6CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59889284-FFEE-82E7-94CB-3B43E87DA6CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59889284-FFEE-82E7-94CB-3B43E87DA6CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59889284-FFEE-82E7-94CB-3B43E87DA6CF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -195,6 +201,7 @@ Global {A9F81DA3-DA82-423E-A5DD-B11C37548E06} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05} {A0F89B8B-32A3-4C28-8F1B-ADE343F16137} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A} {69A0ACF2-DF1F-4F49-B554-F732DCA938A3} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A} + {59889284-FFEE-82E7-94CB-3B43E87DA6CF} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {193AA352-6748-47C1-A5FC-C9AA6B5F000B} diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index e1b428a36..44efe09be 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -43,6 +43,7 @@ + diff --git a/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs new file mode 100644 index 000000000..85080b9a4 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs @@ -0,0 +1,24 @@ +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Validators; + +namespace StackExchange.Redis.Benchmarks +{ + internal class CustomConfig : ManualConfig + { + protected virtual Job Configure(Job j) => j; + + public CustomConfig() + { + AddDiagnoser(MemoryDiagnoser.Default); + AddColumn(StatisticColumn.OperationsPerSecond); + AddValidator(JitOptimizationsValidator.FailOnError); + + AddJob(Configure(Job.Default.WithRuntime(ClrRuntime.Net481))); + AddJob(Configure(Job.Default.WithRuntime(CoreRuntime.Core80))); + } + } +} diff --git a/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs new file mode 100644 index 000000000..cbfa30fd7 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs @@ -0,0 +1,32 @@ +using System.Net; +using BenchmarkDotNet.Attributes; + +namespace StackExchange.Redis.Benchmarks +{ + [Config(typeof(CustomConfig))] + public class FormatBenchmarks + { + [GlobalSetup] + public void Setup() { } + + [Benchmark] + [Arguments("64")] + [Arguments("-1")] + [Arguments("0")] + [Arguments("123442")] + public long ParseInt64(string s) => Format.ParseInt64(s); + + [Benchmark] + [Arguments("64")] + [Arguments("-1")] + [Arguments("0")] + [Arguments("123442")] + public long ParseInt32(string s) => Format.ParseInt32(s); + + [Benchmark] + [Arguments("host.com", -1)] + [Arguments("host.com", 0)] + [Arguments("host.com", 65345)] + public EndPoint ParseEndPoint(string host, int port) => Format.ParseEndPoint(host, port); + } +} diff --git a/tests/StackExchange.Redis.Benchmarks/Program.cs b/tests/StackExchange.Redis.Benchmarks/Program.cs new file mode 100644 index 000000000..622d7d593 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/Program.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using BenchmarkDotNet.Running; + +namespace StackExchange.Redis.Benchmarks +{ + internal static class Program + { + private static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly).Run(args); + } +} diff --git a/tests/StackExchange.Redis.Benchmarks/SlowConfig.cs b/tests/StackExchange.Redis.Benchmarks/SlowConfig.cs new file mode 100644 index 000000000..0c3546006 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/SlowConfig.cs @@ -0,0 +1,12 @@ +using BenchmarkDotNet.Jobs; + +namespace StackExchange.Redis.Benchmarks +{ + internal class SlowConfig : CustomConfig + { + protected override Job Configure(Job j) + => j.WithLaunchCount(1) + .WithWarmupCount(1) + .WithIterationCount(5); + } +} diff --git a/tests/StackExchange.Redis.Benchmarks/StackExchange.Redis.Benchmarks.csproj b/tests/StackExchange.Redis.Benchmarks/StackExchange.Redis.Benchmarks.csproj new file mode 100644 index 000000000..be9a3081b --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/StackExchange.Redis.Benchmarks.csproj @@ -0,0 +1,15 @@ + + + StackExchange.Redis MicroBenchmark Suite + net481;net8.0 + Release + Exe + true + + + + + + + + diff --git a/tests/StackExchange.Redis.Benchmarks/run.cmd b/tests/StackExchange.Redis.Benchmarks/run.cmd new file mode 100644 index 000000000..2b8844c56 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/run.cmd @@ -0,0 +1 @@ +dotnet run --framework net8.0 -c Release %* \ No newline at end of file diff --git a/tests/StackExchange.Redis.Benchmarks/run.sh b/tests/StackExchange.Redis.Benchmarks/run.sh new file mode 100644 index 000000000..1824c7161 --- /dev/null +++ b/tests/StackExchange.Redis.Benchmarks/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet run --framework net8.0 -c Release "$@" \ No newline at end of file From 24bed1229dc3218fce54d51eccda32d673e7b6d2 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 25 Jul 2025 15:36:35 +0100 Subject: [PATCH 2/4] - fix mono - add double parse - add double format --- .../CustomConfig.cs | 12 ++++++++-- .../FormatBenchmarks.cs | 23 ++++++++++++++++++- tests/StackExchange.Redis.Benchmarks/run.sh | 0 3 files changed, 32 insertions(+), 3 deletions(-) mode change 100644 => 100755 tests/StackExchange.Redis.Benchmarks/run.sh diff --git a/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs index 85080b9a4..8e87c66b8 100644 --- a/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs +++ b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs @@ -1,4 +1,5 @@ -using BenchmarkDotNet.Columns; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Environments; @@ -17,7 +18,14 @@ public CustomConfig() AddColumn(StatisticColumn.OperationsPerSecond); AddValidator(JitOptimizationsValidator.FailOnError); - AddJob(Configure(Job.Default.WithRuntime(ClrRuntime.Net481))); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + AddJob(Configure(Job.Default.WithRuntime(ClrRuntime.Net481))); + } + else + { + AddJob(Configure(Job.Default.WithRuntime(MonoRuntime.Mono90))); + } AddJob(Configure(Job.Default.WithRuntime(CoreRuntime.Core80))); } } diff --git a/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs index cbfa30fd7..2af850eba 100644 --- a/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs +++ b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using BenchmarkDotNet.Attributes; namespace StackExchange.Redis.Benchmarks @@ -23,6 +24,26 @@ public void Setup() { } [Arguments("123442")] public long ParseInt32(string s) => Format.ParseInt32(s); + [Benchmark] + [Arguments("64")] + [Arguments("-1")] + [Arguments("0")] + [Arguments("123442")] + [Arguments("-inf")] + [Arguments("nan")] + public double ParseDouble(string s) => Format.TryParseDouble(s, out var val) ? val : double.NaN; + + private byte[] buffer = new byte[128]; + + [Benchmark] + [Arguments(64D)] + [Arguments(-1D)] + [Arguments(0D)] + [Arguments(123442D)] + [Arguments(double.NegativeInfinity)] + [Arguments(double.NaN)] + public double FormatDouble(double value) => Format.FormatDouble(value, buffer.AsSpan()); + [Benchmark] [Arguments("host.com", -1)] [Arguments("host.com", 0)] diff --git a/tests/StackExchange.Redis.Benchmarks/run.sh b/tests/StackExchange.Redis.Benchmarks/run.sh old mode 100644 new mode 100755 From 7fe99b0dea96cd2eb7efa21a2339b9bc232a39b0 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 25 Jul 2025 15:40:21 +0100 Subject: [PATCH 3/4] limit netfx to windows --- tests/StackExchange.Redis.Benchmarks/CustomConfig.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs index 8e87c66b8..09f44cc31 100644 --- a/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs +++ b/tests/StackExchange.Redis.Benchmarks/CustomConfig.cs @@ -22,10 +22,6 @@ public CustomConfig() { AddJob(Configure(Job.Default.WithRuntime(ClrRuntime.Net481))); } - else - { - AddJob(Configure(Job.Default.WithRuntime(MonoRuntime.Mono90))); - } AddJob(Configure(Job.Default.WithRuntime(CoreRuntime.Core80))); } } From c7e13a7abeade8e2ae639cbf95214be29c03b730 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 25 Jul 2025 15:44:19 +0100 Subject: [PATCH 4/4] tyop --- tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs index 2af850eba..77548b254 100644 --- a/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs +++ b/tests/StackExchange.Redis.Benchmarks/FormatBenchmarks.cs @@ -42,7 +42,7 @@ public void Setup() { } [Arguments(123442D)] [Arguments(double.NegativeInfinity)] [Arguments(double.NaN)] - public double FormatDouble(double value) => Format.FormatDouble(value, buffer.AsSpan()); + public int FormatDouble(double value) => Format.FormatDouble(value, buffer.AsSpan()); [Benchmark] [Arguments("host.com", -1)]