Yesterday I posted some metrics about my upcoming .NET client for NATS. In those metrics I had only optimised the read side. The producing side was slow which also affected the consumer side as there was no data to consume :-) Today, I thought I should fix the final piece on the send side.
Update
More tweaks has been done to get event better performance. Read more here: MyNatsClient - It flushes, but so can you
Old code
You can find the change in this commit. But what the previous code where doing was using itterators and yield constructs to combine three sets of bytes:
internal static IEnumerable<T> CombineWith<T>(
this IEnumerable<T> src1,
IEnumerable<T> src2)
{
foreach (var i in src1)
yield return i;
foreach (var i in src2)
yield return i;
}
This was consumed like:
internal static class PubCmd
{
internal static byte[] Generate(string subject, string body, string replyTo = null)
=> Generate(subject, NatsEncoder.GetBytes(body), replyTo);
internal static byte[] Generate(string subject, byte[] body, string replyTo = null)
{
return GeneratePreBody(subject, body.Length, replyTo)
.CombineWith(body)
.CombineWith(GenerateAfterBody())
.ToArray();
}
private static byte[] GeneratePreBody(
string subject,
int bodyLength,
string replyTo = null)
{
var s = replyTo != null ? " " : string.Empty;
return NatsEncoder.GetBytes($"PUB {subject}{s}{replyTo} {bodyLength}{NatsEncoder.Crlf}");
}
private static byte[] GenerateAfterBody() => NatsEncoder.CrlfBytes;
}
New code
The new code skipped the iterators and yield statements and instead used pre allocated bytes array that got filled:
internal static class PubCmd
{
internal static byte[] Generate(string subject, string body, string replyTo = null)
=> Generate(subject, NatsEncoder.GetBytes(body), replyTo);
internal static byte[] Generate(string subject, byte[] body, string replyTo = null)
{
var preBody = GeneratePreBody(subject, body.Length, replyTo);
var crlfLen = NatsEncoder.CrlfBytes.Length;
var buff = new byte[
preBody.Length +
crlfLen +
body.Length +
crlfLen];
Array.Copy(preBody, buff, preBody.Length);
Array.Copy(NatsEncoder.CrlfBytes, 0, buff, preBody.Length, crlfLen);
Array.Copy(body, 0, buff, preBody.Length + crlfLen, body.Length);
Array.Copy(NatsEncoder.CrlfBytes, 0, buff, preBody.Length + crlfLen + body.Length, crlfLen);
return buff;
}
private static byte[] GeneratePreBody(string subject, int bodyLength, string replyTo = null)
{
var s = replyTo != null ? " " : string.Empty;
return NatsEncoder.GetBytes($"PUB {subject}{s}{replyTo} {bodyLength}");
}
}
Metrics
Hope you will enjoy the upcoming v0.1.0
release.
//Daniel