danielwertheim

danielwertheim


notes from a passionate developer

Developer that lives by the mantra "code is meant to be shared".

Share


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

MyNatsClient optimised writes - It's now flying

Daniel WertheimDaniel Wertheim

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

Developer that lives by the mantra "code is meant to be shared".

Comments