From d48bb1a01015e325c9a6e6e38273a318599ace33 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 24 Sep 2020 20:25:45 +0300 Subject: [PATCH 1/2] allocation optimizations --- src/main/scala/scorex/util/encode/Base16.scala | 9 +++++---- .../scorex/util/serialization/VLQByteBufferWriter.scala | 5 +++++ src/main/scala/scorex/util/serialization/VLQWriter.scala | 2 +- src/main/scala/scorex/util/serialization/Writer.scala | 8 ++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/scala/scorex/util/encode/Base16.scala b/src/main/scala/scorex/util/encode/Base16.scala index 8ce97ec..6ce1c74 100644 --- a/src/main/scala/scorex/util/encode/Base16.scala +++ b/src/main/scala/scorex/util/encode/Base16.scala @@ -33,10 +33,11 @@ object Base16 extends BytesEncoder { } def decode(input: String): Try[Array[Byte]] = { - var (isError, errorMsg) = if (input.length % 2 == 0) { - (false, "") - } else { - (true, s"invalid length ${input.length} of Hex data") + var isError = false + var errorMsg = "" + if (input.length % 2 != 0) { + isError = true + errorMsg = s"invalid length ${input.length} of Hex data" } val out = Array.ofDim[Byte](input.length / 2) diff --git a/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala b/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala index afb097d..9e9f8d3 100644 --- a/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala @@ -33,6 +33,11 @@ class VLQByteBufferWriter(b: ByteArrayBuilder) extends Writer with VLQWriter { this } + @inline override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = { + b.append(xs, offset, length) + this + } + @inline override def length(): Int = b.length() @inline override def result(): ByteArrayBuilder = b diff --git a/src/main/scala/scorex/util/serialization/VLQWriter.scala b/src/main/scala/scorex/util/serialization/VLQWriter.scala index 2f891f4..85e947e 100644 --- a/src/main/scala/scorex/util/serialization/VLQWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQWriter.scala @@ -104,7 +104,7 @@ trait VLQWriter extends Writer { if ((value & ~0x7FL) == 0) { buffer(position) = value.asInstanceOf[Byte] position += 1 - putBytes(util.Arrays.copyOf(buffer, position)) + putBytes(buffer, 0, position) return this } else { buffer(position) = ((value.asInstanceOf[Int] & 0x7F) | 0x80).toByte diff --git a/src/main/scala/scorex/util/serialization/Writer.scala b/src/main/scala/scorex/util/serialization/Writer.scala index b98f5fe..84496a5 100644 --- a/src/main/scala/scorex/util/serialization/Writer.scala +++ b/src/main/scala/scorex/util/serialization/Writer.scala @@ -117,6 +117,14 @@ trait Writer { */ def putBytes(xs: Array[Byte]): this.type + /** + * Encode a slice of array of bytes. + * @param xs an array to take bytes from + * @param offset first byte of the slice + * @param length of the slice (number of bytes) + */ + def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type + /** * Encode an array of boolean values as a bit array * From 46d58b52e5fdfe8863475f0bdfa4ff1987d30e54 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 9 Oct 2020 13:20:35 +0300 Subject: [PATCH 2/2] some optimisation in putBits --- .../scala/scorex/util/serialization/VLQWriter.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/scala/scorex/util/serialization/VLQWriter.scala b/src/main/scala/scorex/util/serialization/VLQWriter.scala index 85e947e..e2c2e9d 100644 --- a/src/main/scala/scorex/util/serialization/VLQWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQWriter.scala @@ -116,10 +116,19 @@ trait VLQWriter extends Writer { // see https://rosettacode.org/wiki/Variable-length_quantity for implementations in other languages } + // TODO optimize: it is possible to further significantly optimize this method + // by directly packing bits into bytes and putting bytes into the writer. + // this can be done without any additional memory garbage (BitSet, toByteArray, copyOf). @inline override def putBits(xs: Array[Boolean]): this.type = { if (xs.isEmpty) return this - val bitSet = new util.BitSet(xs.length) - xs.zipWithIndex.foreach { case (bool, i) => bitSet.set(i, bool)} + val len = xs.length + val bitSet = new util.BitSet(len) + var i = 0 + while (i < len) { + val bool = xs(i) + bitSet.set(i, bool) + i += 1 + } // pad the byte array to fix the "no bit was set" behaviour // see https://stackoverflow.com/questions/11209600/how-do-i-convert-a-bitset-initialized-with-false-in-a-byte-containing-0-in-java val bytes = util.Arrays.copyOf(bitSet.toByteArray, (xs.length + 7) / 8)