├── .azure
└── build_release.yaml
├── .config
└── dotnet-tools.json
├── .gitattributes
├── .github
├── dependabot.yaml
└── workflows
│ └── pr_validation.yaml
├── .gitignore
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE.md
├── README.md
├── RELEASE_NOTES.md
├── TurboMqtt.sln
├── TurboMqtt.sln.DotSettings
├── benchmarks
└── TurboMqtt.Benchmarks
│ ├── MicroBenchmarkConfig.cs
│ ├── Mqtt311
│ ├── Mqtt311ConnectCodecBenchmarks.cs
│ ├── Mqtt311End2EndTcpBenchmarks.cs
│ ├── Mqtt311PacketSizeEstimatorBenchmark.cs
│ └── Mqtt311PublishCodecBenchmarks.cs
│ ├── Program.cs
│ ├── README.md
│ ├── TurboMqtt.Benchmarks.csproj
│ ├── start-emqx.ps1
│ └── stop-eqmx.ps1
├── build.ps1
├── docs
├── Performance.md
├── Telemetry.md
├── img
│ └── emqx-mqtt3111.png
└── logo.png
├── global.json
├── nuget.config
├── samples
├── TurboMqtt.Samples.BackpressureProducer
│ ├── MqttConfig.cs
│ ├── MqttProducerService.cs
│ ├── Program.cs
│ ├── TurboMqtt.Samples.BackpressureProducer.csproj
│ ├── appsettings.json
│ └── run5.ps1
└── TurboMqtt.Samples.DevNullConsumer
│ ├── MqttConfig.cs
│ ├── MqttConsumerService.cs
│ ├── Program.cs
│ ├── TurboMqtt.Samples.DevNullConsumer.csproj
│ └── appsettings.json
├── scripts
├── bumpVersion.ps1
├── getReleaseNotes.ps1
├── signPackages.ps1
└── signsettings.json
├── src
└── TurboMqtt
│ ├── Client
│ ├── ClientManagerActor.cs
│ ├── ClientStreamInstance.cs
│ ├── ClientStreamOwner.cs
│ ├── IMqttClient.cs
│ ├── IMqttClientFactory.cs
│ ├── LoggingHelpers.cs
│ ├── MqttClientConnectOptions.cs
│ └── MqttClientTcpOptions.cs
│ ├── ControlPacketHeaders.cs
│ ├── IO
│ ├── DisconnectToBinary.cs
│ ├── FakeServerHandle.cs
│ ├── IFakeServerHandleFactory.cs
│ ├── InMem
│ │ ├── FakeServerAckingFlow.cs
│ │ └── InMemoryMqttTransport.cs
│ ├── MqttTransport.cs
│ ├── Tcp
│ │ ├── FakeMqttTcpServer.cs
│ │ ├── ITransportManager.cs
│ │ ├── TcpConnectionManager.cs
│ │ ├── TcpTransport.cs
│ │ └── TcpTransportActor.cs
│ └── UnsharedMemoryOwner.cs
│ ├── MqttClientIdValidator.cs
│ ├── MqttMessage.cs
│ ├── MqttTopicValidator.cs
│ ├── NonZeroUInt16.cs
│ ├── PacketTypes
│ ├── AuthPacket.cs
│ ├── ConnAckPacket.cs
│ ├── ConnectPacket.cs
│ ├── DisconnectPacket.cs
│ ├── MqttPacket.cs
│ ├── PingReqPacket.cs
│ ├── PingRespPacket.cs
│ ├── PubCompPacket.cs
│ ├── PubRecPacket.cs
│ ├── PubRelPacket.cs
│ ├── PublishAckPacket.cs
│ ├── PublishPacket.cs
│ ├── SubscribeAckPacket.cs
│ ├── SubscribePacket.cs
│ ├── UnsubAckPacket.cs
│ └── UnsubscribePacket.cs
│ ├── Properties
│ └── Friends.cs
│ ├── Protocol
│ ├── AckProtocol.cs
│ ├── ClientAcksActor.cs
│ ├── HeartBeatActor.cs
│ ├── Mqtt311Decoder.cs
│ ├── Mqtt311Encoder.cs
│ ├── MqttDecoderException.cs
│ ├── MqttPacketSizeEstimator.cs
│ ├── MqttProtocolVersion.cs
│ ├── PacketSize.cs
│ └── Pub
│ │ ├── AtLeastOncePublishRetryActor.cs
│ │ ├── ExactlyOncePublishRetryActor.cs
│ │ ├── PublishProtocolDefaults.cs
│ │ └── PublishingProtocol.cs
│ ├── QualityOfService.cs
│ ├── Streams
│ ├── ClientAckingFlow.cs
│ ├── MqttClientStreams.cs
│ ├── MqttDecodingFlows.cs
│ ├── MqttEncodingFlows.cs
│ ├── MqttReceiverFlows.cs
│ ├── MqttRequiredActors.cs
│ ├── OpenTelemetryFlows.cs
│ └── PacketSizeFilter.cs
│ ├── Telemetry
│ ├── OpenTelemetryConfig.cs
│ └── OpenTelemetrySupport.cs
│ ├── TurboMqtt.csproj
│ ├── TurbotMqttHostingExtensions.cs
│ └── Utility
│ ├── Deadline.cs
│ ├── SimpleLruCache.cs
│ ├── TopicCacheManager.cs
│ └── UShortCounter.cs
└── tests
├── TestContainers.TurboMqtt
├── EMQX
│ ├── EmqxBuilder.cs
│ ├── EmqxConfiguration.cs
│ └── EmqxContainer.cs
├── NanoMq
│ ├── NanoMqBuilder.cs
│ ├── NanoMqConfiguration.cs
│ └── NanoMqContainer.cs
├── TestContainers.TurboMqtt.csproj
└── Usings.cs
├── TurboMqtt.Container.Tests
├── EmqxFixture.cs
├── End2End
│ └── EmqxMqtt311End2EndSpecs.cs
└── TurboMqtt.Container.Tests.csproj
└── TurboMqtt.Tests
├── End2End
├── InMemoryMqtt311End2EndSpecs.cs
├── TcpMqtt311End2EndSpecs.cs
├── TcpMqtt311HeartbeatFailureEnd2EndSpecs.cs
└── TransportSpecBase.cs
├── GlobalUsings.cs
├── IO
└── FinalDisconnectPacketSpecs.cs
├── MqttClientIdValidatorTests.cs
├── MqttTopicValidatorSpecs.cs
├── NonWindowsTheoryAttribute.cs
├── NonZeroUintTests.cs
├── PacketGenerators.cs
├── Packets
├── ConnAck
│ ├── ConnAckPacketMqtt311EndToEndCodecSpecs.cs
│ └── ConnAckPacketSpecs.cs
├── Connect
│ ├── ConnectFlagsSpecs.cs
│ ├── ConnectPacketMqtt311EndToEndCodecSpecs.cs
│ ├── ConnectPacketMqtt311Specs.cs
│ └── ConnectPacketMqtt5Specs.cs
├── Disconnect
│ └── DisconnectPacketMqtt311End2EndCodecSpecs.cs
├── PacketEncodingTestHelper.cs
├── PingPackets
│ ├── PingReqPacketMqtt311End2EndCodecSpecs.cs
│ └── PingRespMqtt311End2EndCodecSpecs.cs
├── PubPackets
│ ├── PubAckPacketMqtt311EndToEndCodecSpecs.cs
│ ├── PubCompPacketMqtt311EndToEndCodecSpecs.cs
│ ├── PubRecPacketMqtt311EndToEndCodecSpecs.cs
│ ├── PubRelPacketMqtt311EndToEndCodecSpecs.cs
│ └── PublishPacketMqtt311EndToEndCodecSpecs.cs
├── SubscribePackets
│ ├── SubAckPacketMqtt311EndToEndCodecSpecs.cs
│ └── SubscribePacketMqtt311EndToEndCodecSpecs.cs
└── UnsubscribePackets
│ ├── UnsubAckPacketMqt311End2EndCodecSpecs.cs
│ └── UnsubscribePacketMqtt3111End2EndCodecSpecs.cs
├── Protocol
├── AtLeastOncePublishRetryActorSpecs.cs
├── ClientAcksActorSpecs.cs
├── ExactlyOncePublishRetryActorSpecs.cs
├── Mqtt311DecoderSpecs.cs
└── Mqtt311EncoderSpecs.cs
├── Streams
├── MqttDecodingFlowSpecs.cs
└── MqttEncodingFlowSpecs.cs
├── TurboMqtt.Tests.csproj
└── Utility
├── SimpleLruCacheSpecs.cs
└── UShortCounterSpecs.cs
/.azure/build_release.yaml:
--------------------------------------------------------------------------------
1 | trigger:
2 | branches:
3 | include:
4 | - refs/tags/*
5 | pr: none
6 |
7 | pool:
8 | vmImage: 'windows-latest'
9 |
10 | variables:
11 | - group: signingSecrets #create this group with SECRET variables `signingUsername` and `signingPassword`
12 | - group: nugetKeys #create this group with SECRET variables `nugetKey`
13 | - group: sdkbinNuget
14 | - name: githubConnectionName
15 | value: TurboMqttReleases
16 | - name: projectName
17 | value: TurboMqtt
18 | - name: githubRepositoryName
19 | value: https://github.com/petabridge/TurboMqtt
20 | - name: buildConfiguration
21 | value: 'Release'
22 | - name: productUrl
23 | value: 'https://turbomqtt.org'
24 |
25 | stages:
26 | - stage: BuildAndSign
27 | displayName: 'Build and Sign'
28 | jobs:
29 | - job: Sign
30 | displayName: 'Sign and Push Packages'
31 | steps:
32 | - checkout: self
33 | - task: UseDotNet@2
34 | displayName: 'Install .NET SDK'
35 | inputs:
36 | packageType: 'sdk'
37 | useGlobalJson: true
38 |
39 | - powershell: ./build.ps1
40 | displayName: 'Update Release Notes'
41 |
42 | - script: 'dotnet pack --configuration $(buildConfiguration) -o ./bin/nuget'
43 | displayName: 'Build Package'
44 |
45 | - script: 'dotnet tool restore'
46 | displayName: 'Restore .NET Tools'
47 |
48 | - powershell: |
49 | echo "Starting the signing process..."
50 | dotnet tool run SignClient sign ./scripts/signsettings.json `
51 | -UserName "$(signingUsername)" `
52 | -Password "$(signingPassword)" `
53 | -ProductName "TurboMqtt" `
54 | -ProductDescription "TurboMqtt tools and drivers by Petabridge." `
55 | -ProductUrl "$(productUrl)" `
56 | -DirectoryPath "./bin/nuget"
57 | displayName: 'Sign Artifacts'
58 |
59 | # PowerShell script to push all NuGet packages to SdkBin
60 | - powershell: |
61 | $ErrorActionPreference = "Stop" # Makes the script stop on errors
62 | Get-ChildItem "bin\nuget\*.nupkg" -Recurse | ForEach-Object {
63 | dotnet nuget push $_.FullName --api-key $(sdkbinKey) --source $(sdkbinUri)
64 | }
65 | displayName: 'Push to SdkBin'
66 |
67 | # PowerShell script to push all NuGet packages to NuGet.org
68 | - powershell: |
69 | $ErrorActionPreference = "Stop" # Makes the script stop on errors
70 | Get-ChildItem "bin\nuget\*.nupkg" -Recurse | ForEach-Object {
71 | dotnet nuget push $_.FullName --api-key $(nugetKey) --source https://api.nuget.org/v3/index.json
72 | }
73 | displayName: 'Push to NuGet.org'
74 |
75 | - task: GitHubRelease@0
76 | displayName: 'GitHub release (create)'
77 | inputs:
78 | gitHubConnection: $(githubConnectionName)
79 | repositoryName: $(githubRepositoryName)
80 | title: '$(projectName) v$(Build.SourceBranchName)'
81 | releaseNotesFile: 'RELEASE_NOTES.md'
82 | assets: |
83 | bin\nuget\*.nupkg
84 |
--------------------------------------------------------------------------------
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "signclient": {
6 | "version": "1.2.109",
7 | "commands": [
8 | "SignClient"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 |
5 | # Custom for Visual Studio
6 | *.cs diff=csharp
7 | *.sln merge=union
8 | *.csproj merge=union
9 | *.vbproj merge=union
10 | *.fsproj merge=union
11 | *.dbproj merge=union
12 |
13 | # Standard to msysgit
14 | *.doc diff=astextplain
15 | *.DOC diff=astextplain
16 | *.docx diff=astextplain
17 | *.DOCX diff=astextplain
18 | *.dot diff=astextplain
19 | *.DOT diff=astextplain
20 | *.pdf diff=astextplain
21 | *.PDF diff=astextplain
22 | *.rtf diff=astextplain
23 | *.RTF diff=astextplain
24 |
25 | # Needed for Mono build shell script
26 | *.sh -text eol=lf
27 |
28 | # Needed for API Approvals
29 | *.txt text eol=crlf
30 |
31 | build.sh eol=lf
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | updates:
4 | - package-ecosystem: github-actions
5 | directory: "/"
6 | schedule:
7 | interval: daily
8 | time: "11:00"
9 |
10 | - package-ecosystem: nuget
11 | directory: "/"
12 | schedule:
13 | interval: daily
14 | time: "11:00"
15 |
--------------------------------------------------------------------------------
/.github/workflows/pr_validation.yaml:
--------------------------------------------------------------------------------
1 | name: pr_validation
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - dev
8 | - main
9 | pull_request:
10 | branches:
11 | - master
12 | - dev
13 | - main
14 |
15 | permissions:
16 | checks: write
17 | pull-requests: write
18 | issues: write
19 | contents: read
20 |
21 | jobs:
22 | test:
23 | timeout-minutes: 20 # Increase this timeout value as needed
24 | # Permissions this GitHub Action needs for other things in GitHub
25 | name: Test-${{matrix.os}}
26 | runs-on: ${{matrix.os}}
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | os: [ubuntu-latest, windows-latest]
32 |
33 | steps:
34 | - name: "Checkout"
35 | uses: actions/checkout@v4.2.2
36 | with:
37 | lfs: true
38 | fetch-depth: 0
39 |
40 | - name: "Install .NET SDK"
41 | uses: actions/setup-dotnet@v4.3.0
42 | with:
43 | global-json-file: "./global.json"
44 |
45 | - name: "Update release notes"
46 | shell: pwsh
47 | run: |
48 | ./build.ps1
49 |
50 | - name: "dotnet build"
51 | run: dotnet build -c Release
52 |
53 | - name: "dotnet pack"
54 | run: dotnet pack -c Release
55 |
56 | - name: "dotnet test (Linux)"
57 | if: runner.os == 'Linux'
58 | run: dotnet test --configuration Release --verbosity normal --logger trx --collect:"XPlat Code Coverage"
59 |
60 | - name: "dotnet test (Windows)"
61 | if: runner.os == 'Windows'
62 | run: dotnet test --configuration Release --verbosity normal --logger trx --collect:"XPlat Code Coverage" ./tests/TurboMqtt.Tests/TurboMqtt.Tests.csproj
63 |
64 | - name: Combine Coverage Reports
65 | if: runner.os == 'Linux'
66 | uses: danielpalme/ReportGenerator-GitHub-Action@5.4.5
67 | with:
68 | reports: "**/*.cobertura.xml"
69 | targetdir: "${{ github.workspace }}"
70 | reporttypes: "Cobertura"
71 | verbosity: "Info"
72 | title: "Code Coverage"
73 | tag: "${{ github.run_number }}_${{ github.run_id }}"
74 | customSettings: ""
75 | toolpath: "reportgeneratortool"
76 |
77 | - name: Publish Code Coverage Report
78 | if: runner.os == 'Linux'
79 | uses: irongut/CodeCoverageSummary@v1.3.0
80 | with:
81 | filename: "Cobertura.xml"
82 | badge: true
83 | fail_below_min: false
84 | format: markdown
85 | hide_branch_rate: false
86 | hide_complexity: false
87 | indicators: true
88 | output: both
89 | thresholds: "10 30"
90 |
91 | - name: Add Coverage PR Comment
92 | if: github.event_name == 'pull_request_target' && runner.os == 'Linux'
93 | uses: marocchino/sticky-pull-request-comment@v2
94 | with:
95 | recreate: true
96 | path: code-coverage-results.md
97 |
98 | - name: Upload Test Result Files
99 | if: runner.os == 'Linux'
100 | uses: actions/upload-artifact@v4
101 | with:
102 | name: test-results
103 | path: ${{ github.workspace }}/**/TestResults/**/*
104 | retention-days: 5
105 |
106 | - name: Publish Test Results
107 | if: always() && runner.os == 'Linux'
108 | uses: EnricoMi/publish-unit-test-result-action@v2.19.0
109 | with:
110 | trx_files: "${{ github.workspace }}/**/*.trx"
111 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright © 2024 Petabridge, LLC
4 | $(NoWarn);CS1591
5 | true
6 | 0.1.1
7 | Petabridge
8 | TurboMqtt v0.1.1 includes critical bug fixes and massive performance improvements over v0.1.0.
9 |
10 | **Bug Fixes and Improvements**
11 |
12 | * [Fixed QoS=1 packet handling - was previously treating it like QoS=2](https://github.com/petabridge/TurboMqtt/pull/103).
13 | * [Improved flow control inside `ClientAckHandler`](https://github.com/petabridge/TurboMqtt/pull/105) - result is a massive performance improvement when operating at QoS 1 and 2.
14 | * [Fix OpenTelemetry `TagList` for clientId and MQTT version](https://github.com/petabridge/TurboMqtt/pull/104) - now we can accurately track metrics per clientId via OpenTelemetry.
15 |
16 | **Performance**
17 |
18 | ```
19 |
20 | BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3447/23H2/2023Update/SunValley3)
21 | 12th Gen Intel Core i7-1260P, 1 CPU, 16 logical and 12 physical cores
22 | .NET SDK 8.0.101
23 | [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2
24 | Job-FBXRHG : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2
25 |
26 | InvocationCount=1 LaunchCount=10 RunStrategy=Monitoring
27 | UnrollFactor=1 WarmupCount=10
28 |
29 | ```
30 | | Method | QoSLevel | PayloadSizeBytes | ProtocolVersion | Mean | Error | StdDev | Median | Req/sec |
31 | |-------------------------- |------------ |----------------- |---------------- |----------:|----------:|---------:|----------:|-----------:|
32 | | **PublishAndReceiveMessages** | **AtMostOnce** | **10** | **V3_1_1** | **5.175 μs** | **0.6794 μs** | **2.003 μs** | **4.345 μs** | **193,230.35** |
33 | | **PublishAndReceiveMessages** | **AtLeastOnce** | **10** | **V3_1_1** | **26.309 μs** | **1.4071 μs** | **4.149 μs** | **25.906 μs** | **38,010.35** |
34 | | **PublishAndReceiveMessages** | **ExactlyOnce** | **10** | **V3_1_1** | **44.501 μs** | **2.2778 μs** | **6.716 μs** | **42.175 μs** | **22,471.53** |
35 |
36 |
37 | [Learn more about TurboMqtt's performance figures here](https://github.com/petabridge/TurboMqtt/blob/dev/docs/Performance.md).
38 |
39 |
40 | latest
41 | enable
42 | enable
43 |
44 |
45 | https://github.com/petabridge/TurboMqtt
46 | Apache-2.0
47 | README.md
48 | logo.png
49 |
50 | https://raw.githubusercontent.com/petabridge/TurboMqtt/dev/docs/logo.png
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | https://github.com/petabridge/TurboMqtt
64 |
65 | true
66 |
67 | true
68 | snupkg
69 |
70 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | 1.5.37
7 | 1.5.31.1
8 | 1.10.0
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | #### 0.2.0 June 14th 2024 ####
2 |
3 | * License has been migrated to Apache 2.0
4 | * Upgraded to [Akka.NET v1.5.25](https://github.com/akkadotnet/akka.net/releases/tag/1.5.25).
5 |
6 | #### 0.1.1 May 2nd 2024 ####
7 |
8 | TurboMqtt v0.1.1 includes critical bug fixes and massive performance improvements over v0.1.0.
9 |
10 | **Bug Fixes and Improvements**
11 |
12 | * [Fixed QoS=1 packet handling - was previously treating it like QoS=2](https://github.com/petabridge/TurboMqtt/pull/103).
13 | * [Improved flow control inside `ClientAckHandler`](https://github.com/petabridge/TurboMqtt/pull/105) - result is a massive performance improvement when operating at QoS 1 and 2.
14 | * [Fix OpenTelemetry `TagList` for clientId and MQTT version](https://github.com/petabridge/TurboMqtt/pull/104) - now we can accurately track metrics per clientId via OpenTelemetry.
15 |
16 | **Performance**
17 |
18 | ```
19 |
20 | BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3447/23H2/2023Update/SunValley3)
21 | 12th Gen Intel Core i7-1260P, 1 CPU, 16 logical and 12 physical cores
22 | .NET SDK 8.0.101
23 | [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2
24 | Job-FBXRHG : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2
25 |
26 | InvocationCount=1 LaunchCount=10 RunStrategy=Monitoring
27 | UnrollFactor=1 WarmupCount=10
28 |
29 | ```
30 | | Method | QoSLevel | PayloadSizeBytes | ProtocolVersion | Mean | Error | StdDev | Median | Req/sec |
31 | |-------------------------- |------------ |----------------- |---------------- |----------:|----------:|---------:|----------:|-----------:|
32 | | **PublishAndReceiveMessages** | **AtMostOnce** | **10** | **V3_1_1** | **5.175 μs** | **0.6794 μs** | **2.003 μs** | **4.345 μs** | **193,230.35** |
33 | | **PublishAndReceiveMessages** | **AtLeastOnce** | **10** | **V3_1_1** | **26.309 μs** | **1.4071 μs** | **4.149 μs** | **25.906 μs** | **38,010.35** |
34 | | **PublishAndReceiveMessages** | **ExactlyOnce** | **10** | **V3_1_1** | **44.501 μs** | **2.2778 μs** | **6.716 μs** | **42.175 μs** | **22,471.53** |
35 |
36 |
37 | [Learn more about TurboMqtt's performance figures here](https://github.com/petabridge/TurboMqtt/blob/dev/docs/Performance.md).
38 |
--------------------------------------------------------------------------------
/TurboMqtt.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | -----------------------------------------------------------------------
3 | <copyright file="${File.FileName}" company="Petabridge, LLC">
4 | Copyright (C) ${File.CreatedYear} - ${CurrentDate.Year} Petabridge, LLC <https://petabridge.com>
5 | </copyright>
6 | -----------------------------------------------------------------------
7 | True
--------------------------------------------------------------------------------
/benchmarks/TurboMqtt.Benchmarks/MicroBenchmarkConfig.cs:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2024 - 2024 Petabridge, LLC
4 | //
5 | // -----------------------------------------------------------------------
6 |
7 | using System.Reflection;
8 | using BenchmarkDotNet.Attributes;
9 | using BenchmarkDotNet.Columns;
10 | using BenchmarkDotNet.Configs;
11 | using BenchmarkDotNet.Diagnosers;
12 | using BenchmarkDotNet.Exporters;
13 | using BenchmarkDotNet.Reports;
14 | using BenchmarkDotNet.Running;
15 |
16 | namespace TurboMqtt.Benchmarks;
17 |
18 | public class RequestsPerSecondColumn : IColumn
19 | {
20 | public string Id => nameof(RequestsPerSecondColumn);
21 | public string ColumnName => "Req/sec";
22 |
23 | public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false;
24 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase) => GetValue(summary, benchmarkCase, SummaryStyle.Default);
25 | public bool IsAvailable(Summary summary) => true;
26 | public bool AlwaysShow => true;
27 | public ColumnCategory Category => ColumnCategory.Custom;
28 | public int PriorityInCategory => -1;
29 | public bool IsNumeric => true;
30 | public UnitType UnitType => UnitType.Dimensionless;
31 | public string Legend => "Requests per Second";
32 |
33 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style)
34 | {
35 | var benchmarkAttribute = benchmarkCase.Descriptor.WorkloadMethod.GetCustomAttribute();
36 | var totalOperations = benchmarkAttribute?.OperationsPerInvoke ?? 1;
37 |
38 | if (!summary.HasReport(benchmarkCase))
39 | return "";
40 |
41 | var report = summary[benchmarkCase];
42 | var statistics = report?.ResultStatistics;
43 | if(statistics is null)
44 | return "";
45 |
46 | var nsPerOperation = statistics.Mean;
47 | var operationsPerSecond = 1 / (nsPerOperation / 1e9);
48 |
49 | return operationsPerSecond.ToString("N2"); // or format as you like
50 |
51 | }
52 | }
53 |
54 | ///
55 | /// Basic BenchmarkDotNet configuration used for microbenchmarks.
56 | ///
57 | public class MicroBenchmarkConfig : ManualConfig
58 | {
59 | public MicroBenchmarkConfig()
60 | {
61 | AddDiagnoser(MemoryDiagnoser.Default);
62 | AddExporter(MarkdownExporter.GitHub);
63 | AddColumn(new RequestsPerSecondColumn());
64 | }
65 | }
66 |
67 | ///
68 | /// BenchmarkDotNet configuration used for monitored jobs (not for microbenchmarks).
69 | ///
70 | public class MonitoringConfig : ManualConfig
71 | {
72 | public MonitoringConfig()
73 | {
74 | AddExporter(MarkdownExporter.GitHub);
75 | AddColumn(new RequestsPerSecondColumn());
76 | }
77 | }
--------------------------------------------------------------------------------
/benchmarks/TurboMqtt.Benchmarks/Mqtt311/Mqtt311ConnectCodecBenchmarks.cs:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2024 - 2024 Petabridge, LLC
4 | //
5 | // -----------------------------------------------------------------------
6 |
7 | using System.Collections.Immutable;
8 | using BenchmarkDotNet.Attributes;
9 | using TurboMqtt.PacketTypes;
10 | using TurboMqtt.Protocol;
11 |
12 | namespace TurboMqtt.Benchmarks.Mqtt311;
13 |
14 | [Config(typeof(MicroBenchmarkConfig))]
15 | public class Mqtt311ConnectCodecBenchmarks
16 | {
17 | private readonly Mqtt311Decoder _decoder = new();
18 |
19 | private readonly ConnectPacket _connectPacket = new ConnectPacket(MqttProtocolVersion.V3_1_1)
20 | {
21 | ClientId = "benchmark-client",
22 | UserName = "benchmark-user",
23 | Password = "benchmark-password",
24 | ProtocolName = "MQTT",
25 | KeepAliveSeconds = 2,
26 | ConnectFlags = new ConnectFlags
27 | {
28 | CleanSession = true,
29 | WillFlag = false,
30 | WillQoS = QualityOfService.AtMostOnce,
31 | WillRetain = false
32 | },
33 | Will = new MqttLastWill("benchmark-topic", new ReadOnlyMemory([0x1, 0x2, 0x3, 0x4]))
34 | {
35 | ResponseTopic = null,
36 | WillCorrelationData = null,
37 | ContentType = null,
38 | PayloadFormatIndicator = PayloadFormatIndicator.Unspecified,
39 | DelayInterval = default,
40 | MessageExpiryInterval = 0,
41 | WillProperties = null
42 | }
43 | };
44 |
45 | private byte[] _writeableBytes = Array.Empty();
46 | private ReadOnlyMemory _encodedConnectPacket;
47 | private PacketSize _estimatedConnectPacketSize;
48 |
49 | [GlobalSetup]
50 | public void Setup()
51 | {
52 | var estimate = MqttPacketSizeEstimator.EstimateMqtt3PacketSize(_connectPacket);
53 | _writeableBytes = new byte[estimate.TotalSize];
54 | var memory = new Memory(new byte[estimate.TotalSize]);
55 | _encodedConnectPacket = memory;
56 | Mqtt311Encoder.EncodePacket(_connectPacket, ref memory, estimate);
57 | _estimatedConnectPacketSize = estimate;
58 | }
59 |
60 | private Memory _writeableBuffer;
61 |
62 | [IterationSetup]
63 | public void IterationSetup()
64 | {
65 | _writeableBuffer = new Memory(_writeableBytes);
66 | }
67 |
68 | [Benchmark]
69 | public ImmutableList DecodeConnectPacket()
70 | {
71 | _decoder.TryDecode(_encodedConnectPacket, out var packets);
72 | return packets;
73 | }
74 |
75 | [Benchmark]
76 | public int EncodeConnectPacket()
77 | {
78 | return Mqtt311Encoder.EncodePacket(_connectPacket, ref _writeableBuffer, _estimatedConnectPacketSize);
79 | }
80 | }
--------------------------------------------------------------------------------
/benchmarks/TurboMqtt.Benchmarks/Mqtt311/Mqtt311PacketSizeEstimatorBenchmark.cs:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2024 - 2024 Petabridge, LLC
4 | //
5 | // -----------------------------------------------------------------------
6 |
7 | using BenchmarkDotNet.Attributes;
8 | using TurboMqtt.PacketTypes;
9 | using TurboMqtt.Protocol;
10 |
11 | namespace TurboMqtt.Benchmarks.Mqtt311;
12 |
13 | [Config(typeof(MicroBenchmarkConfig))]
14 | public class Mqtt311PacketSizeEstimatorBenchmark
15 | {
16 | public static IEnumerable