Optimize ByteString.grouped(size) (#25153)

* Add ByteString.grouped() benchmark & tests.

* Implement ByteString.grouped().

* PR comments.
This commit is contained in:
Doug Roper 2018-05-30 10:31:35 -04:00 committed by Konrad `ktoso` Malawski
parent a14347e18d
commit b6d6d543a8
3 changed files with 58 additions and 0 deletions

View file

@ -52,6 +52,15 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
} yield (xs, from, until)
}
case class ByteStringGrouped(bs: ByteString, size: Int)
implicit val arbitraryByteStringGrouped = Arbitrary {
for {
xs arbitraryByteString.arbitrary
size Gen.choose(1, 1 max xs.length)
} yield ByteStringGrouped(xs, size)
}
type ArraySlice[A] = (Array[A], Int, Int)
def arbSlice[A](arbArray: Arbitrary[Array[A]]): Arbitrary[ArraySlice[A]] = Arbitrary {
@ -730,6 +739,14 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
}
}
"calling grouped" in {
check { grouped: ByteStringGrouped
likeVector(grouped.bs) {
_.grouped(grouped.size).toIndexedSeq
}
}
}
"calling copyToArray" in {
check { slice: ByteStringSlice
slice match {

View file

@ -701,6 +701,16 @@ sealed abstract class ByteString extends IndexedSeq[Byte] with IndexedSeqOptimiz
// optimized in subclasses
override def indexOf[B >: Byte](elem: B): Int = indexOf(elem, 0)
override def grouped(size: Int): Iterator[ByteString] = {
if (size <= 0) {
throw new IllegalArgumentException(s"size=$size must be positive")
}
Iterator.iterate(this)(_.drop(size))
.takeWhile(_.nonEmpty)
.map(_.take(size))
}
override def toString(): String = {
val maxSize = 100
if (size > maxSize)

View file

@ -0,0 +1,31 @@
/**
* Copyright (C) 2014-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.util
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
class ByteString_grouped_Benchmark {
private val bsLarge = ByteString(Array.ofDim[Byte](1000 * 1000))
/*
> akka-bench-jmh/jmh:run -f1 .*ByteString_grouped_Benchmark
[info] Benchmark Mode Cnt Score Error Units
[info] ByteString_grouped_Benchmark.grouped avgt 10 59386.328 ± 1466.045 ns/op
*/
@Benchmark
def grouped(bh: Blackhole): Unit = {
bh.consume(bsLarge.grouped(1000).foreach(bh.consume))
}
}