Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7.0 Proposal: BasicPublish with ROM<byte> for exchange and routing key #972

Closed
bollhals opened this issue Nov 7, 2020 · 4 comments · Fixed by #990
Closed

7.0 Proposal: BasicPublish with ROM<byte> for exchange and routing key #972

bollhals opened this issue Nov 7, 2020 · 4 comments · Fixed by #990

Comments

@bollhals
Copy link
Contributor

bollhals commented Nov 7, 2020

The basic publish is one of the "hottest" method we have, so any improvements there are surely very valuable.

Extracted from #970, I propose to extend the IModel interface with a new method for 7.0:
void BasicPublish(ReadOnlyMemory<byte> exchange, ReadOnlyMemory<byte> routingKey, bool mandatory, IBasicProperties basicProperties, ReadOnlyMemory<byte> body);

This replaces the expensive string -> byte encoding for the often times same string.

@stebet
Copy link
Contributor

stebet commented Nov 8, 2020

I'm all for performance, but I don't really see this as something that will be widely used, but for some very niche cases. IO will always be the bottleneck, not CPU usage.

@stebet
Copy link
Contributor

stebet commented Nov 10, 2020

I've just had a second thought about this, and I've gone 180 on my original comment. I think this might be a clever idea for high-perf scenarios. I also remembered that there exists an API that does similar things: https://github.com/StackExchange/StackExchange.Redis/blob/main/src/StackExchange.Redis/Interfaces/IDatabase.cs#L241

The StackExchange.Redis API uses RedisKey structs as key parameters which have implicit conversion to both byte[] and string: https://github.com/StackExchange/StackExchange.Redis/blob/main/src/StackExchange.Redis/RedisKey.cs#L178-L191

That means that clients that use hardcoded strings for exchanges/queues etc. can define a ready-made ReadOnlyMemory<byte> instance that can reused without the repeated string -> byte[] cost.

@bollhals
Copy link
Contributor Author

bollhals commented Nov 10, 2020

To indicate the caching intent for this, we could also define the signature as
void BasicPublish(RabbitMqString exchange, RabbitMqString routingKey, bool mandatory, IBasicProperties basicProperties, ReadOnlyMemory<byte> body);
where RabbitMqString is defined as

public readonly struct RabbitMqString 
{ 
    public readonly string Value;
    internal readonly ReadOnlyMemory<byte> CachedBytes;
    public RabbitMqString(string value) { /* implement */ };
}
/**************** OR *****************/
public sealed class RabbitMqString 
{ 
    private ReadOnlyMemory<byte> _cache;
    public string Value { get;}
    internal ReadOnlyMemory<byte> CachedBytes => /* populate _cache if not empty or return _cache */;
    public RabbitMqString(string value) { /* implement */ };
}

where the struct would build the cache within the ctor, while the class could defer it to it's first use if we want to.

@bollhals
Copy link
Contributor Author

Update from a local running version on this:

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated Code Size
BasicPublishWrite 41.3951 ns 0.8661 ns 0.0475 ns - - - - 386 B
BasicPublishSize 18.5197 ns 0.1749 ns 0.0096 ns - - - - 178 B
BasicPublishMemoryWrite 14.3104 ns 0.6103 ns 0.0334 ns - - - - 775 B
BasicPublishMemorySize 0.8075 ns 0.0310 ns 0.0017 ns - - - - 39 B

Obviously the gain increases the larger the string (Exchange / RoutingKey) is. (Benchmark is with "Exchange_OR_RoutingKey").

Another benchmark to serialize into the 3 frames of a message:

Method Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated Code Size
BasicPublish 223.9 ns 13.50 ns 0.74 ns 1.00 - - - - 2504 B
BasicPublishMemory 182.1 ns 7.83 ns 0.43 ns 0.81 - - - - 2518 B

It my does not seem huge, but it increases the throughput on the most relevant hotpath.
I'll setup a PR for 7.0 in the next days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants