From 1cf1ce73c1232d386c4108dbbc56928fd65869e2 Mon Sep 17 00:00:00 2001 From: Damian Maclennan Date: Wed, 6 May 2026 11:58:57 +1000 Subject: [PATCH 1/3] Benchmark app --- Nimbus.sln | 251 ++++++++++++++++-- src/Nimbus.Benchmark/BenchmarkCommand.cs | 9 + .../BenchmarkCommandHandler.cs | 12 + src/Nimbus.Benchmark/BenchmarkState.cs | 28 ++ src/Nimbus.Benchmark/Nimbus.Benchmark.csproj | 30 +++ src/Nimbus.Benchmark/Program.cs | 120 +++++++++ src/Nimbus.Benchmark/TransportFactory.cs | 51 ++++ 7 files changed, 485 insertions(+), 16 deletions(-) create mode 100644 src/Nimbus.Benchmark/BenchmarkCommand.cs create mode 100644 src/Nimbus.Benchmark/BenchmarkCommandHandler.cs create mode 100644 src/Nimbus.Benchmark/BenchmarkState.cs create mode 100644 src/Nimbus.Benchmark/Nimbus.Benchmark.csproj create mode 100644 src/Nimbus.Benchmark/Program.cs create mode 100644 src/Nimbus.Benchmark/TransportFactory.cs diff --git a/Nimbus.sln b/Nimbus.sln index f107f5bd..ebd6656c 100644 --- a/Nimbus.sln +++ b/Nimbus.sln @@ -77,142 +77,361 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nimbus.Transports.Nats", "src\Nimbus.Transports.Nats\Nimbus.Transports.Nats.csproj", "{014133B7-4470-4F8D-A886-75528F8C9374}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nimbus.Benchmark", "src\Nimbus.Benchmark\Nimbus.Benchmark.csproj", "{F0CB04E8-FF31-4026-8CBA-81F11526E25D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|x64.ActiveCfg = Debug|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|x64.Build.0 = Debug|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|x86.ActiveCfg = Debug|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Debug|x86.Build.0 = Debug|Any CPU {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|Any CPU.Build.0 = Release|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|x64.ActiveCfg = Release|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|x64.Build.0 = Release|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|x86.ActiveCfg = Release|Any CPU + {E847193E-0CDC-4526-B610-1D132BD110C1}.Release|x86.Build.0 = Release|Any CPU {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|x64.Build.0 = Debug|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Debug|x86.Build.0 = Debug|Any CPU {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|Any CPU.Build.0 = Release|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|x64.ActiveCfg = Release|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|x64.Build.0 = Release|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|x86.ActiveCfg = Release|Any CPU + {A5A54643-C0F6-4450-9A97-6CFA9EA068F4}.Release|x86.Build.0 = Release|Any CPU {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|x64.Build.0 = Debug|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Debug|x86.Build.0 = Debug|Any CPU {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|Any CPU.Build.0 = Release|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|x64.ActiveCfg = Release|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|x64.Build.0 = Release|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|x86.ActiveCfg = Release|Any CPU + {D021EEB0-8895-4896-B635-0C83AA8244A1}.Release|x86.Build.0 = Release|Any CPU {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|x64.Build.0 = Debug|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Debug|x86.Build.0 = Debug|Any CPU {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|Any CPU.Build.0 = Release|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|x64.ActiveCfg = Release|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|x64.Build.0 = Release|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|x86.ActiveCfg = Release|Any CPU + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3}.Release|x86.Build.0 = Release|Any CPU {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|x64.Build.0 = Debug|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Debug|x86.Build.0 = Debug|Any CPU {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|Any CPU.Build.0 = Release|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|x64.ActiveCfg = Release|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|x64.Build.0 = Release|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|x86.ActiveCfg = Release|Any CPU + {A6BEE995-F25E-47F4-B8A0-E7BC3FBABAC3}.Release|x86.Build.0 = Release|Any CPU {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|x64.ActiveCfg = Debug|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|x64.Build.0 = Debug|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|x86.ActiveCfg = Debug|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Debug|x86.Build.0 = Debug|Any CPU {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|Any CPU.Build.0 = Release|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|x64.ActiveCfg = Release|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|x64.Build.0 = Release|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|x86.ActiveCfg = Release|Any CPU + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B}.Release|x86.Build.0 = Release|Any CPU {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|x64.Build.0 = Debug|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Debug|x86.Build.0 = Debug|Any CPU {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|Any CPU.Build.0 = Release|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|x64.ActiveCfg = Release|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|x64.Build.0 = Release|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|x86.ActiveCfg = Release|Any CPU + {B9E53456-FC88-48D2-BC37-CB19247C5677}.Release|x86.Build.0 = Release|Any CPU {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|x64.Build.0 = Debug|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Debug|x86.Build.0 = Debug|Any CPU {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|Any CPU.Build.0 = Release|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|x64.ActiveCfg = Release|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|x64.Build.0 = Release|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|x86.ActiveCfg = Release|Any CPU + {5A4F14B8-D794-4B79-B550-DB06F2629C33}.Release|x86.Build.0 = Release|Any CPU {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|x64.ActiveCfg = Debug|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|x64.Build.0 = Debug|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Debug|x86.Build.0 = Debug|Any CPU {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|Any CPU.Build.0 = Release|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|x64.ActiveCfg = Release|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|x64.Build.0 = Release|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|x86.ActiveCfg = Release|Any CPU + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D}.Release|x86.Build.0 = Release|Any CPU {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|x64.Build.0 = Debug|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Debug|x86.Build.0 = Debug|Any CPU {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|Any CPU.Build.0 = Release|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|x64.ActiveCfg = Release|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|x64.Build.0 = Release|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|x86.ActiveCfg = Release|Any CPU + {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C}.Release|x86.Build.0 = Release|Any CPU {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|x64.Build.0 = Debug|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Debug|x86.Build.0 = Debug|Any CPU {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|Any CPU.Build.0 = Release|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|x64.ActiveCfg = Release|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|x64.Build.0 = Release|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|x86.ActiveCfg = Release|Any CPU + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B}.Release|x86.Build.0 = Release|Any CPU {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|x64.Build.0 = Debug|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Debug|x86.Build.0 = Debug|Any CPU {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|Any CPU.Build.0 = Release|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|x64.ActiveCfg = Release|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|x64.Build.0 = Release|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|x86.ActiveCfg = Release|Any CPU + {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3}.Release|x86.Build.0 = Release|Any CPU {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|x64.ActiveCfg = Debug|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|x64.Build.0 = Debug|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Debug|x86.Build.0 = Debug|Any CPU {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|Any CPU.Build.0 = Release|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|x64.ActiveCfg = Release|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|x64.Build.0 = Release|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|x86.ActiveCfg = Release|Any CPU + {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5}.Release|x86.Build.0 = Release|Any CPU {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|x64.Build.0 = Debug|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Debug|x86.Build.0 = Debug|Any CPU {A6D217AA-7866-4816-B60B-B884224E635D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6D217AA-7866-4816-B60B-B884224E635D}.Release|Any CPU.Build.0 = Release|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Release|x64.ActiveCfg = Release|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Release|x64.Build.0 = Release|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Release|x86.ActiveCfg = Release|Any CPU + {A6D217AA-7866-4816-B60B-B884224E635D}.Release|x86.Build.0 = Release|Any CPU {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|x64.ActiveCfg = Debug|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|x64.Build.0 = Debug|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|x86.ActiveCfg = Debug|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Debug|x86.Build.0 = Debug|Any CPU {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|Any CPU.ActiveCfg = Release|Any CPU {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|Any CPU.Build.0 = Release|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|x64.ActiveCfg = Release|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|x64.Build.0 = Release|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|x86.ActiveCfg = Release|Any CPU + {68ACF6B4-1400-4D21-AB35-32759B5DFE60}.Release|x86.Build.0 = Release|Any CPU {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|x64.ActiveCfg = Debug|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|x64.Build.0 = Debug|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|x86.ActiveCfg = Debug|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Debug|x86.Build.0 = Debug|Any CPU {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|Any CPU.ActiveCfg = Release|Any CPU {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|Any CPU.Build.0 = Release|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|x64.ActiveCfg = Release|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|x64.Build.0 = Release|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|x86.ActiveCfg = Release|Any CPU + {00CD641D-533C-4CDF-ADE4-CFA02703BAC4}.Release|x86.Build.0 = Release|Any CPU {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|x64.Build.0 = Debug|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Debug|x86.Build.0 = Debug|Any CPU {F8B2F785-E18F-425F-B009-E905215535D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8B2F785-E18F-425F-B009-E905215535D4}.Release|Any CPU.Build.0 = Release|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Release|x64.ActiveCfg = Release|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Release|x64.Build.0 = Release|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Release|x86.ActiveCfg = Release|Any CPU + {F8B2F785-E18F-425F-B009-E905215535D4}.Release|x86.Build.0 = Release|Any CPU {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|x64.Build.0 = Debug|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Debug|x86.Build.0 = Debug|Any CPU {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|Any CPU.Build.0 = Release|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|x64.ActiveCfg = Release|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|x64.Build.0 = Release|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|x86.ActiveCfg = Release|Any CPU + {E135964B-E995-45F0-B44B-BCE6F82566AF}.Release|x86.Build.0 = Release|Any CPU {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|x64.ActiveCfg = Debug|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|x64.Build.0 = Debug|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|x86.ActiveCfg = Debug|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Debug|x86.Build.0 = Debug|Any CPU {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|Any CPU.ActiveCfg = Release|Any CPU {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|Any CPU.Build.0 = Release|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|x64.ActiveCfg = Release|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|x64.Build.0 = Release|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|x86.ActiveCfg = Release|Any CPU + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07}.Release|x86.Build.0 = Release|Any CPU {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|x64.Build.0 = Debug|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Debug|x86.Build.0 = Debug|Any CPU {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|Any CPU.Build.0 = Release|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|x64.ActiveCfg = Release|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|x64.Build.0 = Release|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|x86.ActiveCfg = Release|Any CPU + {3D2433CC-40A7-4457-9305-23C45EE934B6}.Release|x86.Build.0 = Release|Any CPU {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|x64.Build.0 = Debug|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|x86.ActiveCfg = Debug|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Debug|x86.Build.0 = Debug|Any CPU {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|Any CPU.Build.0 = Release|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|x64.ActiveCfg = Release|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|x64.Build.0 = Release|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|x86.ActiveCfg = Release|Any CPU + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC}.Release|x86.Build.0 = Release|Any CPU {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|x64.Build.0 = Debug|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Debug|x86.Build.0 = Debug|Any CPU {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|Any CPU.Build.0 = Release|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|x64.ActiveCfg = Release|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|x64.Build.0 = Release|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|x86.ActiveCfg = Release|Any CPU + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3}.Release|x86.Build.0 = Release|Any CPU {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|x64.ActiveCfg = Debug|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|x64.Build.0 = Debug|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Debug|x86.Build.0 = Debug|Any CPU {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|Any CPU.Build.0 = Release|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|x64.ActiveCfg = Release|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|x64.Build.0 = Release|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|x86.ActiveCfg = Release|Any CPU + {658944B3-F287-48B8-B75E-6FC3D59FD6F0}.Release|x86.Build.0 = Release|Any CPU {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|x64.Build.0 = Debug|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|x86.ActiveCfg = Debug|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Debug|x86.Build.0 = Debug|Any CPU {A1B72845-33CE-4009-B801-6388C91ED400}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1B72845-33CE-4009-B801-6388C91ED400}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Release|x64.ActiveCfg = Release|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Release|x64.Build.0 = Release|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Release|x86.ActiveCfg = Release|Any CPU + {A1B72845-33CE-4009-B801-6388C91ED400}.Release|x86.Build.0 = Release|Any CPU {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|Any CPU.Build.0 = Debug|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|x64.ActiveCfg = Debug|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|x64.Build.0 = Debug|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|x86.ActiveCfg = Debug|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Debug|x86.Build.0 = Debug|Any CPU {014133B7-4470-4F8D-A886-75528F8C9374}.Release|Any CPU.ActiveCfg = Release|Any CPU {014133B7-4470-4F8D-A886-75528F8C9374}.Release|Any CPU.Build.0 = Release|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Release|x64.ActiveCfg = Release|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Release|x64.Build.0 = Release|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Release|x86.ActiveCfg = Release|Any CPU + {014133B7-4470-4F8D-A886-75528F8C9374}.Release|x86.Build.0 = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|x64.ActiveCfg = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|x64.Build.0 = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|x86.ActiveCfg = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Debug|x86.Build.0 = Debug|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|Any CPU.Build.0 = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|x64.ActiveCfg = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|x64.Build.0 = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|x86.ActiveCfg = Release|Any CPU + {F0CB04E8-FF31-4026-8CBA-81F11526E25D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {8AF93A0F-73E7-4D8F-8A57-841680777AC7} - EndGlobalSection GlobalSection(NestedProjects) = preSolution - {3D2433CC-40A7-4457-9305-23C45EE934B6} = {CCF90796-B7D3-4A78-8291-905EC1017D95} - {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC} = {CCF90796-B7D3-4A78-8291-905EC1017D95} - {BB6AB576-8B62-446B-8C3C-FD28CB2336B3} = {CCF90796-B7D3-4A78-8291-905EC1017D95} - {658944B3-F287-48B8-B75E-6FC3D59FD6F0} = {CCF90796-B7D3-4A78-8291-905EC1017D95} - {62AEF7AF-65E1-48ED-9072-086611B4ABBE} = {EC82EEE2-CD23-4147-9239-CB247F5FCB69} - {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} - {C5C7C5E4-8E11-4BBD-9771-B330303AEC07} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} + {D021EEB0-8895-4896-B635-0C83AA8244A1} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} + {205371C0-A19E-4BCE-927F-A4FF4C4E81B3} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} + {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} {B9E53456-FC88-48D2-BC37-CB19247C5677} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} - {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} {5A4F14B8-D794-4B79-B550-DB06F2629C33} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} + {7B8C9A12-3D4E-4F5A-8B6C-9E7D8A1B2C3D} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} {3F4A5B6C-7D8E-9F0A-1B2C-3D4E5F6A7B8C} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} + {4E5F6A7B-8C9D-0E1F-2A3B-4C5D6E7F8A9B} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} {4F7C8899-F3EB-4814-A0FE-2CDD875D20D3} = {11C15AC1-E240-45A3-BC79-753C97CEB085} - {E135964B-E995-45F0-B44B-BCE6F82566AF} = {11C15AC1-E240-45A3-BC79-753C97CEB085} {CCF5E938-F84E-4FBC-A63C-9ED961D0C9C5} = {11C15AC1-E240-45A3-BC79-753C97CEB085} {A6D217AA-7866-4816-B60B-B884224E635D} = {11C15AC1-E240-45A3-BC79-753C97CEB085} {68ACF6B4-1400-4D21-AB35-32759B5DFE60} = {11C15AC1-E240-45A3-BC79-753C97CEB085} {00CD641D-533C-4CDF-ADE4-CFA02703BAC4} = {11C15AC1-E240-45A3-BC79-753C97CEB085} - {9CEABC9B-2CFE-4850-8C95-5CEAC249E28B} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} - {205371C0-A19E-4BCE-927F-A4FF4C4E81B3} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} - {D021EEB0-8895-4896-B635-0C83AA8244A1} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} - {A1B72845-33CE-4009-B801-6388C91ED400} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} {F8B2F785-E18F-425F-B009-E905215535D4} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} + {E135964B-E995-45F0-B44B-BCE6F82566AF} = {11C15AC1-E240-45A3-BC79-753C97CEB085} + {C5C7C5E4-8E11-4BBD-9771-B330303AEC07} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} + {3D2433CC-40A7-4457-9305-23C45EE934B6} = {CCF90796-B7D3-4A78-8291-905EC1017D95} + {20E44CF5-B31C-437E-B0E8-A7AEA54E8CFC} = {CCF90796-B7D3-4A78-8291-905EC1017D95} + {BB6AB576-8B62-446B-8C3C-FD28CB2336B3} = {CCF90796-B7D3-4A78-8291-905EC1017D95} + {658944B3-F287-48B8-B75E-6FC3D59FD6F0} = {CCF90796-B7D3-4A78-8291-905EC1017D95} + {A1B72845-33CE-4009-B801-6388C91ED400} = {0BDBBB2A-E9D2-4315-80FA-B9E132AC258A} + {62AEF7AF-65E1-48ED-9072-086611B4ABBE} = {EC82EEE2-CD23-4147-9239-CB247F5FCB69} {014133B7-4470-4F8D-A886-75528F8C9374} = {BF624237-BE96-4FAA-BA8D-F21B16B887E1} + {F0CB04E8-FF31-4026-8CBA-81F11526E25D} = {11C15AC1-E240-45A3-BC79-753C97CEB085} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8AF93A0F-73E7-4D8F-8A57-841680777AC7} EndGlobalSection EndGlobal diff --git a/src/Nimbus.Benchmark/BenchmarkCommand.cs b/src/Nimbus.Benchmark/BenchmarkCommand.cs new file mode 100644 index 00000000..ddbac155 --- /dev/null +++ b/src/Nimbus.Benchmark/BenchmarkCommand.cs @@ -0,0 +1,9 @@ +using Nimbus.MessageContracts; + +namespace Nimbus.Benchmark; + +public class BenchmarkCommand : IBusCommand +{ + public long SentAtTicks { get; set; } + public int SequenceNumber { get; set; } +} diff --git a/src/Nimbus.Benchmark/BenchmarkCommandHandler.cs b/src/Nimbus.Benchmark/BenchmarkCommandHandler.cs new file mode 100644 index 00000000..1b1d5413 --- /dev/null +++ b/src/Nimbus.Benchmark/BenchmarkCommandHandler.cs @@ -0,0 +1,12 @@ +using Nimbus.InfrastructureContracts.Handlers; + +namespace Nimbus.Benchmark; + +public class BenchmarkCommandHandler(BenchmarkState state) : IHandleCommand +{ + public Task Handle(BenchmarkCommand busCommand) + { + state.RecordReceived(busCommand.SentAtTicks, busCommand.SequenceNumber); + return Task.CompletedTask; + } +} diff --git a/src/Nimbus.Benchmark/BenchmarkState.cs b/src/Nimbus.Benchmark/BenchmarkState.cs new file mode 100644 index 00000000..3d55a022 --- /dev/null +++ b/src/Nimbus.Benchmark/BenchmarkState.cs @@ -0,0 +1,28 @@ +using System.Diagnostics; + +namespace Nimbus.Benchmark; + +public class BenchmarkState +{ + private readonly long[] _latencyTicks; + private readonly CountdownEvent _countdown; + private long _lastReceiveTick; + + public BenchmarkState(int count) + { + _latencyTicks = new long[count]; + _countdown = new CountdownEvent(count); + } + + public void RecordReceived(long sentAtTicks, int sequenceNumber) + { + var now = Stopwatch.GetTimestamp(); + _latencyTicks[sequenceNumber] = now - sentAtTicks; + Interlocked.Exchange(ref _lastReceiveTick, now); + _countdown.Signal(); + } + + public bool WaitForCompletion(TimeSpan timeout) => _countdown.Wait(timeout); + + public (long lastReceiveTick, long[] latencyTicks) GetData() => (_lastReceiveTick, _latencyTicks); +} diff --git a/src/Nimbus.Benchmark/Nimbus.Benchmark.csproj b/src/Nimbus.Benchmark/Nimbus.Benchmark.csproj new file mode 100644 index 00000000..53ae3ff7 --- /dev/null +++ b/src/Nimbus.Benchmark/Nimbus.Benchmark.csproj @@ -0,0 +1,30 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Nimbus.Benchmark/Program.cs b/src/Nimbus.Benchmark/Program.cs new file mode 100644 index 00000000..63556db8 --- /dev/null +++ b/src/Nimbus.Benchmark/Program.cs @@ -0,0 +1,120 @@ +using System.Diagnostics; +using Autofac; +using Nimbus; +using Nimbus.Benchmark; +using Nimbus.Configuration; +using Nimbus.Configuration.Transport; +using Nimbus.Infrastructure; +using Nimbus.InfrastructureContracts; +using Nimbus.Logger.Serilog.Configuration; +using Nimbus.Serializers.Json.Configuration; +using Serilog; + +var count = 1000; +var transportName = "InProcess"; + +for (var i = 0; i < args.Length; i++) +{ + if (args[i] == "--count" && i + 1 < args.Length) count = int.Parse(args[++i]); + else if (args[i] == "--transport" && i + 1 < args.Length) transportName = args[++i]; +} + +if (args.Contains("--help") || args.Contains("-h")) +{ + Console.WriteLine("Usage: Nimbus.Benchmark [--transport ] [--count ]"); + Console.WriteLine($" Transports: {string.Join(", ", TransportFactory.ValidNames)}"); + Console.WriteLine($" Default : --transport InProcess --count 1000"); + return 0; +} + +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Warning() + .WriteTo.Console() + .CreateLogger(); + +Console.WriteLine("=== Nimbus Benchmark ==="); +Console.WriteLine($"Transport : {transportName}"); +Console.WriteLine($"Messages : {count:N0}"); +Console.WriteLine(); + +TransportConfiguration transportConfig; +try +{ + transportConfig = TransportFactory.Create(transportName); +} +catch (ArgumentException ex) +{ + Console.Error.WriteLine(ex.Message); + return 1; +} + +var benchmarkState = new BenchmarkState(count); + +var containerBuilder = new ContainerBuilder(); +var typeProvider = new AssemblyScanningTypeProvider(typeof(Program).Assembly); +containerBuilder.RegisterNimbus(typeProvider); +containerBuilder.RegisterInstance(benchmarkState).AsSelf().SingleInstance(); + +containerBuilder.Register(ctx => new BusBuilder() + .Configure() + .WithTransport(transportConfig) + .WithNames("NimbusBenchmark", Environment.MachineName) + .WithAutofacDefaults(ctx) + .WithSerilogLogger() + .WithJsonSerializer() + .Build()) + .As() + .SingleInstance(); + +using var container = containerBuilder.Build(); +var bus = (Bus)container.Resolve(); + +Console.Write("Starting bus..."); +var busReady = new TaskCompletionSource(); +bus.Started += (_, _) => busReady.TrySetResult(); +await bus.Start(); +await busReady.Task; +await Task.Delay(500); +Console.WriteLine(" ready."); +Console.WriteLine(); + +Console.Write($"Sending {count:N0} messages... "); +var sendStart = Stopwatch.GetTimestamp(); +for (var i = 0; i < count; i++) +{ + await bus.Send(new BenchmarkCommand + { + SentAtTicks = Stopwatch.GetTimestamp(), + SequenceNumber = i + }); +} +var sendEnd = Stopwatch.GetTimestamp(); +var sendElapsedSec = (sendEnd - sendStart) / (double)Stopwatch.Frequency; +Console.WriteLine($"done in {sendElapsedSec * 1000:F0}ms ({count / sendElapsedSec:F0} msg/s send rate)"); + +Console.Write("Waiting for all messages... "); +var completed = benchmarkState.WaitForCompletion(TimeSpan.FromMinutes(10)); +Console.WriteLine(completed ? "done." : "TIMED OUT!"); +Console.WriteLine(); + +if (completed) +{ + var (lastReceiveTick, latencyTicks) = benchmarkState.GetData(); + var totalElapsedSec = (lastReceiveTick - sendStart) / (double)Stopwatch.Frequency; + var throughput = count / totalElapsedSec; + + var sortedMs = latencyTicks + .Select(t => t / (double)Stopwatch.Frequency * 1000.0) + .OrderBy(x => x) + .ToArray(); + + double P(double pct) => sortedMs[(int)(sortedMs.Length * pct / 100.0)]; + + Console.WriteLine("--- Results ---"); + Console.WriteLine($" Total time : {totalElapsedSec * 1000:F0}ms"); + Console.WriteLine($" Throughput : {throughput:F0} msg/s"); + Console.WriteLine($" Latency (ms) : min={sortedMs[0]:F2} p50={P(50):F2} p95={P(95):F2} p99={P(99):F2} max={sortedMs[^1]:F2}"); +} + +await bus.Stop(); +return 0; diff --git a/src/Nimbus.Benchmark/TransportFactory.cs b/src/Nimbus.Benchmark/TransportFactory.cs new file mode 100644 index 00000000..3f225520 --- /dev/null +++ b/src/Nimbus.Benchmark/TransportFactory.cs @@ -0,0 +1,51 @@ +using Nimbus.Configuration.Transport; +using Nimbus.Transports.AMQP; +using Nimbus.Transports.InProcess; +using Nimbus.Transports.Nats; +using Nimbus.Transports.Postgres; +using Nimbus.Transports.Redis; +using Nimbus.Transports.SqlServer; + +namespace Nimbus.Benchmark; + +public static class TransportFactory +{ + public static readonly string[] ValidNames = + [ + "InProcess", "Redis", "Nats", "NatsJetStream", "Amqp", "SqlServer", "Postgres" + ]; + + public static TransportConfiguration Create(string name) => name.ToLowerInvariant() switch + { + "inprocess" => new InProcessTransportConfiguration(), + + "redis" => new RedisTransportConfiguration() + .WithConnectionString("localhost"), + + "nats" => new NatsTransportConfiguration() + .WithUrl("nats://localhost:4222") + .WithCredentials("admin", "password"), + + "natsjetstream" => new NatsTransportConfiguration() + .WithUrl("nats://localhost:4222") + .WithCredentials("admin", "password") + .WithJetStream(), + + "amqp" => new AMQPTransportConfiguration() + .WithBrokerUri("amqp://localhost:5672") + .WithCredentials("admin", "admin"), + + "sqlserver" => new SqlServerTransportConfiguration() + .WithConnectionString("Server=localhost,1433;Database=Nimbus;User Id=sa;Password=Nimbus_Dev_123!;TrustServerCertificate=true;") + .WithPollInterval(TimeSpan.FromMilliseconds(50)) + .WithAutoCreateSchema(), + + "postgres" => new PostgresTransportConfiguration() + .WithConnectionString("Host=localhost;Port=5432;Database=nimbus;Username=nimbus;Password=Nimbus_Dev_123!") + .WithPollInterval(TimeSpan.FromMilliseconds(50)) + .WithAutoCreateSchema(), + + _ => throw new ArgumentException( + $"Unknown transport '{name}'. Valid options: {string.Join(", ", ValidNames)}") + }; +} From e123499e341b70be057a6a2cc77157c6bf92fd4c Mon Sep 17 00:00:00 2001 From: Damian Maclennan Date: Wed, 6 May 2026 11:59:19 +1000 Subject: [PATCH 2/3] Optimise AMQP connections --- .../TransportSelector.cs | 2 +- src/Nimbus.Transports.Amqp/AMQPTransport.cs | 23 +++++++-- .../AMQPQueueSender.cs | 51 +++++++++++++++---- .../AMQPTopicSender.cs | 51 +++++++++++++++---- 4 files changed, 102 insertions(+), 25 deletions(-) diff --git a/src/Nimbus.Tests.Integration/TestScenarioGeneration/TransportSelector.cs b/src/Nimbus.Tests.Integration/TestScenarioGeneration/TransportSelector.cs index 179122a8..7359d89f 100644 --- a/src/Nimbus.Tests.Integration/TestScenarioGeneration/TransportSelector.cs +++ b/src/Nimbus.Tests.Integration/TestScenarioGeneration/TransportSelector.cs @@ -23,7 +23,7 @@ public static class TransportSelector private static TestTransport LoadFromEnvironment() { - var envVar = Environment.GetEnvironmentVariable("NIMBUS_TEST_TRANSPORT") ?? "InProcess"; + var envVar = Environment.GetEnvironmentVariable("NIMBUS_TEST_TRANSPORT") ?? "InMemory"; if (Enum.TryParse(envVar, ignoreCase: true, out var transport)) return transport; diff --git a/src/Nimbus.Transports.Amqp/AMQPTransport.cs b/src/Nimbus.Transports.Amqp/AMQPTransport.cs index 9607fbc5..de8adc9f 100644 --- a/src/Nimbus.Transports.Amqp/AMQPTransport.cs +++ b/src/Nimbus.Transports.Amqp/AMQPTransport.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Threading.Tasks; using Nimbus.Configuration.PoorMansIocContainer; using Nimbus.Infrastructure; @@ -16,6 +17,8 @@ internal class AMQPTransport : INimbusTransport, IDisposable private readonly PoorMansIoC _container; private readonly ILogger _logger; private readonly NmsConnectionManager _connectionManager; + private readonly ConcurrentDictionary _queueSenders = new(); + private readonly ConcurrentDictionary _topicSenders = new(); private bool _disposed; public AMQPTransport(PoorMansIoC container, ILogger logger, NmsConnectionManager connectionManager) @@ -34,8 +37,11 @@ public async Task TestConnection() public INimbusMessageSender GetQueueSender(string queuePath) { - _logger.Debug("Creating queue sender for {QueuePath}", queuePath); - return _container.ResolveWithOverrides(queuePath); + return _queueSenders.GetOrAdd(queuePath, path => + { + _logger.Debug("Creating queue sender for {QueuePath}", path); + return _container.ResolveWithOverrides(path); + }); } public INimbusMessageReceiver GetQueueReceiver(string queuePath) @@ -46,8 +52,11 @@ public INimbusMessageReceiver GetQueueReceiver(string queuePath) public INimbusMessageSender GetTopicSender(string topicPath) { - _logger.Debug("Creating topic sender for {TopicPath}", topicPath); - return _container.ResolveWithOverrides(topicPath); + return _topicSenders.GetOrAdd(topicPath, path => + { + _logger.Debug("Creating topic sender for {TopicPath}", path); + return _container.ResolveWithOverrides(path); + }); } public INimbusMessageReceiver GetTopicReceiver(string topicPath, string subscriptionName, IFilterCondition filter) @@ -65,6 +74,12 @@ public void Dispose() _logger.Info("Disposing AMQP transport"); + foreach (var sender in _queueSenders.Values) + try { sender.Dispose(); } catch { /* ignore */ } + + foreach (var sender in _topicSenders.Values) + try { sender.Dispose(); } catch { /* ignore */ } + try { _connectionManager?.Dispose(); diff --git a/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPQueueSender.cs b/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPQueueSender.cs index 7c6ff53d..280a661d 100644 --- a/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPQueueSender.cs +++ b/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPQueueSender.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Apache.NMS; using Nimbus.Extensions; @@ -9,17 +10,20 @@ namespace Nimbus.Transports.AMQP.MessageSendersAndReceivers { - internal class AMQPQueueSender : INimbusMessageSender + internal class AMQPQueueSender : INimbusMessageSender, IDisposable { private readonly string _queuePath; private readonly IQueueManager _queueManager; private readonly INmsMessageFactory _messageFactory; private readonly ILogger _logger; + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private ISession _session; + private IMessageProducer _producer; public AMQPQueueSender(string queuePath, - IQueueManager queueManager, - INmsMessageFactory messageFactory, - ILogger logger) + IQueueManager queueManager, + INmsMessageFactory messageFactory, + ILogger logger) { _queuePath = queuePath; _queueManager = queueManager; @@ -29,23 +33,50 @@ public AMQPQueueSender(string queuePath, public async Task Send(NimbusMessage message) { + await _lock.WaitAsync(); try { - using var session = await _queueManager.CreateSession(AcknowledgementMode.AutoAcknowledge); - var queue = await _queueManager.GetQueue(session, _queuePath); - - using var producer = session.CreateProducer(queue); - var nmsMessage = await _messageFactory.CreateNmsMessage(message, session); + await EnsureProducer(); + var nmsMessage = await _messageFactory.CreateNmsMessage(message, _session); _logger.Debug("Sending message {MessageId} to queue {QueuePath}", message.MessageId, _queuePath); - await producer.SendAsync(nmsMessage).ConfigureAwaitFalse(); + await _producer.SendAsync(nmsMessage).ConfigureAwaitFalse(); _logger.Debug("Message {MessageId} sent successfully to queue {QueuePath}", message.MessageId, _queuePath); } catch (Exception ex) { _logger.Error(ex, "Failed to send message {MessageId} to queue {QueuePath}", message.MessageId, _queuePath); + ResetProducer(); throw; } + finally + { + _lock.Release(); + } + } + + private async Task EnsureProducer() + { + if (_producer != null) return; + _session = await _queueManager.CreateSession(AcknowledgementMode.AutoAcknowledge); + var queue = await _queueManager.GetQueue(_session, _queuePath); + _producer = _session.CreateProducer(queue); + } + + private void ResetProducer() + { + try { _producer?.Close(); } catch { /* ignore */ } + try { _producer?.Dispose(); } catch { /* ignore */ } + try { _session?.Close(); } catch { /* ignore */ } + try { _session?.Dispose(); } catch { /* ignore */ } + _producer = null; + _session = null; + } + + public void Dispose() + { + ResetProducer(); + _lock.Dispose(); } } } diff --git a/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPTopicSender.cs b/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPTopicSender.cs index efdfbb57..8d5a1481 100644 --- a/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPTopicSender.cs +++ b/src/Nimbus.Transports.Amqp/MessageSendersAndReceivers/AMQPTopicSender.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Apache.NMS; using Nimbus.Extensions; @@ -9,17 +10,20 @@ namespace Nimbus.Transports.AMQP.MessageSendersAndReceivers { - internal class AMQPTopicSender : INimbusMessageSender + internal class AMQPTopicSender : INimbusMessageSender, IDisposable { private readonly string _topicPath; private readonly IQueueManager _queueManager; private readonly INmsMessageFactory _messageFactory; private readonly ILogger _logger; + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private ISession _session; + private IMessageProducer _producer; public AMQPTopicSender(string topicPath, - IQueueManager queueManager, - INmsMessageFactory messageFactory, - ILogger logger) + IQueueManager queueManager, + INmsMessageFactory messageFactory, + ILogger logger) { _topicPath = topicPath; _queueManager = queueManager; @@ -29,23 +33,50 @@ public AMQPTopicSender(string topicPath, public async Task Send(NimbusMessage message) { + await _lock.WaitAsync(); try { - using var session = await _queueManager.CreateSession(AcknowledgementMode.AutoAcknowledge); - var topic = await _queueManager.GetTopic(session, _topicPath); - - using var producer = session.CreateProducer(topic); - var nmsMessage = await _messageFactory.CreateNmsMessage(message, session); + await EnsureProducer(); + var nmsMessage = await _messageFactory.CreateNmsMessage(message, _session); _logger.Debug("Publishing message {MessageId} to topic {TopicPath}", message.MessageId, _topicPath); - await producer.SendAsync(nmsMessage).ConfigureAwaitFalse(); + await _producer.SendAsync(nmsMessage).ConfigureAwaitFalse(); _logger.Debug("Message {MessageId} published successfully to topic {TopicPath}", message.MessageId, _topicPath); } catch (Exception ex) { _logger.Error(ex, "Failed to publish message {MessageId} to topic {TopicPath}", message.MessageId, _topicPath); + ResetProducer(); throw; } + finally + { + _lock.Release(); + } + } + + private async Task EnsureProducer() + { + if (_producer != null) return; + _session = await _queueManager.CreateSession(AcknowledgementMode.AutoAcknowledge); + var topic = await _queueManager.GetTopic(_session, _topicPath); + _producer = _session.CreateProducer(topic); + } + + private void ResetProducer() + { + try { _producer?.Close(); } catch { /* ignore */ } + try { _producer?.Dispose(); } catch { /* ignore */ } + try { _session?.Close(); } catch { /* ignore */ } + try { _session?.Dispose(); } catch { /* ignore */ } + _producer = null; + _session = null; + } + + public void Dispose() + { + ResetProducer(); + _lock.Dispose(); } } } From 9a99833162451d6d65638c22cf251e1c000f57b0 Mon Sep 17 00:00:00 2001 From: Damian Maclennan Date: Thu, 7 May 2026 10:19:11 +1000 Subject: [PATCH 3/3] bump AMQP version --- src/Nimbus.Transports.Amqp/Nimbus.Transports.AMQP.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nimbus.Transports.Amqp/Nimbus.Transports.AMQP.csproj b/src/Nimbus.Transports.Amqp/Nimbus.Transports.AMQP.csproj index d2618bba..2842a4c5 100644 --- a/src/Nimbus.Transports.Amqp/Nimbus.Transports.AMQP.csproj +++ b/src/Nimbus.Transports.Amqp/Nimbus.Transports.AMQP.csproj @@ -1,7 +1,7 @@ - 1.0.0 + 1.1.0 true AMQP transport for the Nimbus messaging framework using Apache NMS AMQP (supports Artemis, ActiveMQ, and other AMQP brokers). nimbus;messaging;transport;amqp;artemis;activemq;nms