This PR optimizes the delay delivery routing key algorithm to:
No longer use the BitArray which is a class that leads to allocations and internally copies the array passed
No longer use the string builder which allocates quite a bit of char array garbage
The routing key length is well-defined by the number of levels we allow plus the space needed for the address. This means we can use string.Create to get rid of the StringBuilder.
As a replacement for the BitArray the BitVector32 is a nice fit which is a struct.
To avoid having to loop through the bit vector multiple times or encoding some custom string parts into the string created to then splice the delayLevel out again I had to do a bit of more advanced trickery to be able to write the delayLevel which requires some unsafe declaration. That is fine though and even something the underlying RabbitMQ client uses.
## Benchmark
```csharp
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Text;
using BenchmarkDotNet.Attributes;
namespace MicroBenchmarks.RabbitMQ;
[SimpleJob]
[MemoryDiagnoser]
public class RoutingKey
{
[Benchmark(Baseline = true)]
[ArgumentsSource(nameof(Arguments))]
public string Before(int delay, string address) => CalculateRoutingKeyOld(10, "some-address", out _);
[Benchmark]
[ArgumentsSource(nameof(Arguments))]
public string After(int delay, string address) => CalculateRoutingKeyNew(10, "some-address", out _);
public IEnumerable
Based upon #1412
This PR optimizes the delay delivery routing key algorithm to:
The routing key length is well-defined by the number of levels we allow plus the space needed for the address. This means we can use
string.Create
to get rid of theStringBuilder
.As a replacement for the
BitArray
theBitVector32
is a nice fit which is a struct.To avoid having to loop through the bit vector multiple times or encoding some custom string parts into the string created to then splice the delayLevel out again I had to do a bit of more advanced trickery to be able to write the delayLevel which requires some unsafe declaration. That is fine though and even something the underlying RabbitMQ client uses.