ErisApps / CatCore

A shared high-performance chat client library written in .NET Standard 2.0
GNU General Public License v3.0
29 stars 3 forks source link

Span-based multi-message deconstruction #28

Closed ErisApps closed 2 years ago

ErisApps commented 2 years ago

This PR reworks the incoming IRC message handler by reducing GC pressure caused by unnecessary allocations. The message handler could sometimes receive multiple IRC messages in a single raw message and was relying string-based splitting in order to be able to deconstruct singular IRC messages. This at the cost of short-lived string allocations and array allocations.

The new approach is span-based. This means that we create new ReadOnlySpan<char> wrappers that still point to the same memory block under the hood, given that those wrappers are readonly ref struct's, they're much lighter on the GC than the old approach. It also makes sense to use the new approach as the IrcExtensions.ParseIrcMessage(...) method was already using those under the hood.

Other than having reduced GC pressure, it became overall faster, as can be seen in the resultset below.

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1526 (21H2) AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores [Host] : .NET Framework 4.8 (4.8.4470.0), X64 RyuJIT Job-CIRMVA : .NET 5.0.14 (5.0.1422.5710), X64 RyuJIT Job-NVAYXH : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT Job-ZSFKTD : .NET Framework 4.8 (4.8.4470.0), X64 RyuJIT Job-XTWDPN : Mono 5.11.0 (Visual Studio), X86 Job-UGEEKR : Mono 6.13.0 (Visual Studio), X86 Job-LJOXDK : Mono 6.13.0 (Visual Studio), X86

Method Job Runtime RawIrcMultiMessage Mean Error StdDev Ratio Gen 0 Gen 1 Allocated
InlineSpanBasedSplitBenchmark Job-CIRMVA .NET 5.0 :tmi(...)TE [528] 1.301 μs 0.00094 μs 0.0084 μs 0.79 0.2728 - 2,288 B
IntermediateStringSplitBenchmark Job-CIRMVA .NET 5.0 :tmi(...)TE [528] 1.642 μs 0.0123 μs 0.0115 μs 1.00 0.4501 0.0019 3,768 B
IntermediateStringSplitBenchmark Job-NVAYXH .NET 6.0 :tmi(...)TE [528] 1.247 μs 0.0057 μs 0.0051 μs 1.00 0.4501 0.0019 3,768 B
InlineSpanBasedSplitBenchmark Job-NVAYXH .NET 6.0 :tmi(...)TE [528] 1.274 μs 0.0070 μs 0.0065 μs 1.02 0.2728 - 2,288 B
InlineSpanBasedSplitBenchmark Job-ZSFKTD .NET Framework 4.7.2 :tmi(...)TE [528] 2.253 μs 0.0094 μs 0.0088 μs 0.93 0.3929 - 2,503 B
IntermediateStringSplitBenchmark Job-ZSFKTD .NET Framework 4.7.2 :tmi(...)TE [528] 2.431 μs 0.0121 μs 0.0113 μs 1.00 0.9727 0.0038 6,146 B
InlineSpanBasedSplitBenchmark Job-XTWDPN Mono Unity 2019.4.28f1 :tmi(...)TE [528] 5.192 μs 0.0258 μs 0.0241 μs 0.79 0.4883 - -
IntermediateStringSplitBenchmark Job-XTWDPN Mono Unity 2019.4.28f1 :tmi(...)TE [528] 6.596 μs 0.0275 μs 0.0257 μs 1.00 1.3199 - -
InlineSpanBasedSplitBenchmark Job-UGEEKR Mono Unity 2021.2.0b4 :tmi(...)TE [528] 3.805 μs 0.0124 μs 0.0116 μs 0.92 0.4349 - -
IntermediateStringSplitBenchmark Job-UGEEKR Mono Unity 2021.2.0b4 :tmi(...)TE [528] 4.149 μs 0.0164 μs 0.0154 μs 1.00 0.7553 - -
InlineSpanBasedSplitBenchmark Job-LJOXDK Mono Unity 2022.1.0a12 :tmi(...)TE [528] 3.894 μs 0.0156 μs 0.0146 μs 0.93 0.4349 - -
IntermediateStringSplitBenchmark Job-LJOXDK Mono Unity 2022.1.0a12 :tmi(...)TE [528] 4.179 μs 0.0142 μs 0.0133 μs 1.00 0.7553 - -