Optimization suggestion for Akka ByteString.compact method

Hi Akka Team,

I was recently doing performance optimization for our project which is using Akka Http and was looking into ByteString.compact method for specifically ByteStrings class:

final class ByteStrings private (private[akka] val bytestrings: Vector[ByteString1], val length: Int)
      extends ByteString {
....
 def compact: CompactByteString = {
      if (isCompact) bytestrings.head.compact
      else {
        val ar = new Array[Byte](length)
        var pos = 0
        bytestrings.foreach { b =>
          b.copyToArray(ar, pos, b.length)
          pos += b.length
        }
        ByteString1C(ar)
      }
    }

uses copyToArray which delegates to IterableOnce which is coyping byte by byte in the loop.

I`m wondering have you considered using ByteBuffer instead of Array like this:

def compact: CompactByteString = {
      if (isCompact) bytestrings.head.compact
      else {
          val bb = ByteBuffer.alocate(length)
          bytestrings.foreach { b =>
            bb.put(b.asByteBuffer)
         }
         ByteString1C(bb.array())
      }
    }

which would be using ByteBuffer.put() which is optimised if both ByteBuffers (source and destination) are instance of HeapByteBuffer to use bulk copy?

 public ByteBuffer put(ByteBuffer src) {

        if (src instanceof HeapByteBuffer) {
            if (src == this)
                throw createSameBufferException();
            HeapByteBuffer sb = (HeapByteBuffer)src;
            int n = sb.remaining();
            if (n > remaining())
                throw new BufferOverflowException();
            System.arraycopy(sb.hb, sb.ix(sb.position()),
                             hb, ix(position()), n);
            sb.position(sb.position() + n);
            position(position() + n);
        } else if (src.isDirect()) {
            int n = src.remaining();
            if (n > remaining())
                throw new BufferOverflowException();
            src.get(hb, ix(position()), n);
            position(position() + n);
        } else {
            super.put(src);
        }
        return this;
    }

In Akka HTTP I think it would give a lot of improvements when parsing incoming http message (as those bytestring already backed with HeapByteBuffer) when dealing with TLS and in Framing where ByteString.compact is used a lot.

Regards,
Kyrylo

Potentially relevant recent fix https://github.com/akka/akka/pull/28116

1 Like