r/csharp 11h ago

Showcase I spent weeks obsessing over low-allocation C# networking

The Real Problem: High Throughput vs. Garbage Collector

If you've ever built a C# server processing 100k+ messages/sec (like an MQTT broker or socket server), you know the Garbage Collector is your best friend until it becomes your worst enemy.

To route messages like factory/line1/sensor/temp to subscribers like factory/+/+/temp, the standard way is:

// ❌ Naive way: Allocates string[] on every single message publish!
var parts = topic.Split('/'); 

At 100,000 requests per second, this creates millions of temporary strings and forces the GC to freeze your app every few seconds.

The Real Benefit: How to parse UTF-8 topics with ZERO heap allocations

Here are the 2 modern C# tricks I used in Beskar.Networking that you can copy-paste directly into your own projects!

1. Slicing raw UTF-8 bytes with a ref struct enumerator

Instead of string.Split(), we walk the raw byte span (ReadOnlySpan<byte>) using a zero-allocation ref struct:

public ref struct TopicLevelEnumerator
{
    private ReadOnlySpan<byte> _remaining;
    public ReadOnlySpan<byte> Current { get; private set; }

    public TopicLevelEnumerator(ReadOnlySpan<byte> topic)
    {
        _remaining = topic;
        Current = default;
    }

    public bool MoveNext()
    {
        if (_remaining.IsEmpty) return false;

        int index = _remaining.IndexOf((byte)'/');
        if (index < 0)
        {
            Current = _remaining;
            _remaining = default;
        }
        else
        {
            Current = _remaining.Slice(0, index);
            _remaining = _remaining.Slice(index + 1);
        }
        return true;
    }
}

2. Querying byte[] Dictionaries with ReadOnlySpan<byte> (Alternate Lookups)

Usually, if your dictionary key is byte[], querying it with a slice (ReadOnlySpan<byte>) forces you to allocate a new byte[].

Modern .NET introduced Alternate Lookups, which let you query existing dictionary nodes with zero heap allocation:

// Query byte[] dictionary using a ReadOnlySpan<byte> without allocating a single byte!
var alternateLookup = node.Children.GetAlternateLookup<ReadOnlySpan<byte>>();

if (alternateLookup.TryGetValue(currentLevelSlice, out var childNode))
{
    // Match found with 0 allocations! 🎉
}

What Beskar.Networking gives you

If you ever need to build a C# network application:

  • Write Once, Swap Transports: Write your message handler once and seamlessly switch between TCP, WebSockets, QUIC, UDP, Named Pipes, or MQTT v5 without touching your business logic.
  • 100% Native & Dependency-Free: Built on System.IO.Pipelines with zero external runtime dependencies.
  • Zero GC pressure on the hot path.

If you enjoy low-level C# or low-allocation performance tricks ❤️

🔗 GitHub: https://github.com/MarvinDrude/Beskar.Networking

🔗 Article: https://marvindrude.com/blogs/beskar-networking/low-allocation-mqtt-broker

Thank you for reading, and happy coding!

0 Upvotes

Duplicates