=act clean up ByteString#drop(...) (#21440)
* =act clean up ByteString#drop(...) Current implementation has a good algorithm but seems a little bit complicated. Clening-up does not suffer the performance (actually seems to have the better performance when dropping(N-1)) where N is the length, and is easy to understand almost the same algorithm now. * Change private[akka] to priavte * Rename go(...) and some variables They should be easy for us to understand what they are. * Add benchmark of ByteString#drop(...)
This commit is contained in:
parent
cc845c84b0
commit
19dbe9a487
3 changed files with 150 additions and 70 deletions
|
|
@ -292,22 +292,23 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
|
|||
}
|
||||
|
||||
"ByteString1" must {
|
||||
"drop(0)" in {
|
||||
ByteString1.fromString("").drop(0) should ===(ByteString.empty)
|
||||
"drop" in {
|
||||
ByteString1.empty.drop(-1) should ===(ByteString(""))
|
||||
ByteString1.empty.drop(0) should ===(ByteString(""))
|
||||
ByteString1.empty.drop(1) should ===(ByteString(""))
|
||||
ByteString1.fromString("a").drop(-1) should ===(ByteString("a"))
|
||||
ByteString1.fromString("a").drop(0) should ===(ByteString("a"))
|
||||
}
|
||||
"drop(1)" in {
|
||||
ByteString1.fromString("").drop(1) should ===(ByteString(""))
|
||||
ByteString1.fromString("a").drop(1) should ===(ByteString(""))
|
||||
ByteString1.fromString("ab").drop(1) should ===(ByteString("b"))
|
||||
ByteString1.fromString("xaaa").drop(1) should ===(ByteString("aaa"))
|
||||
ByteString1.fromString("xaab").drop(1).take(2) should ===(ByteString("aa"))
|
||||
ByteString1.fromString("a").drop(2) should ===(ByteString(""))
|
||||
ByteString1.fromString("abc").drop(-1) should ===(ByteString("abc"))
|
||||
ByteString1.fromString("abc").drop(0) should ===(ByteString("abc"))
|
||||
ByteString1.fromString("abc").drop(1) should ===(ByteString("bc"))
|
||||
ByteString1.fromString("abc").drop(2) should ===(ByteString("c"))
|
||||
ByteString1.fromString("abc").drop(3) should ===(ByteString(""))
|
||||
ByteString1.fromString("abc").drop(4) should ===(ByteString(""))
|
||||
ByteString1.fromString("0123456789").drop(1).take(2) should ===(ByteString("12"))
|
||||
ByteString1.fromString("0123456789").drop(5).take(4).drop(1).take(2) should ===(ByteString("67"))
|
||||
}
|
||||
"drop(n)" in {
|
||||
ByteString1.fromString("ab").drop(2) should ===(ByteString(""))
|
||||
ByteString1.fromString("ab").drop(3) should ===(ByteString(""))
|
||||
}
|
||||
"take" in {
|
||||
ByteString1.empty.take(-1) should ===(ByteString(""))
|
||||
ByteString1.empty.take(0) should ===(ByteString(""))
|
||||
|
|
@ -323,18 +324,22 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
|
|||
}
|
||||
}
|
||||
"ByteString1C" must {
|
||||
"drop(0)" in {
|
||||
ByteString1C.fromString("").drop(0) should ===(ByteString.empty)
|
||||
ByteString1C.fromString("a").drop(0) should ===(ByteString("a"))
|
||||
}
|
||||
"drop(1)" in {
|
||||
"drop" in {
|
||||
ByteString1C.fromString("").drop(-1) should ===(ByteString(""))
|
||||
ByteString1C.fromString("").drop(0) should ===(ByteString(""))
|
||||
ByteString1C.fromString("").drop(1) should ===(ByteString(""))
|
||||
ByteString1C.fromString("a").drop(-1) should ===(ByteString("a"))
|
||||
ByteString1C.fromString("a").drop(0) should ===(ByteString("a"))
|
||||
ByteString1C.fromString("a").drop(1) should ===(ByteString(""))
|
||||
ByteString1C.fromString("ab").drop(1) should ===(ByteString("b"))
|
||||
}
|
||||
"drop(n)" in {
|
||||
ByteString1C.fromString("ab").drop(2) should ===(ByteString(""))
|
||||
ByteString1C.fromString("ab").drop(3) should ===(ByteString(""))
|
||||
ByteString1C.fromString("a").drop(2) should ===(ByteString(""))
|
||||
ByteString1C.fromString("abc").drop(-1) should ===(ByteString("abc"))
|
||||
ByteString1C.fromString("abc").drop(0) should ===(ByteString("abc"))
|
||||
ByteString1C.fromString("abc").drop(1) should ===(ByteString("bc"))
|
||||
ByteString1C.fromString("abc").drop(2) should ===(ByteString("c"))
|
||||
ByteString1C.fromString("abc").drop(3) should ===(ByteString(""))
|
||||
ByteString1C.fromString("abc").drop(4) should ===(ByteString(""))
|
||||
ByteString1C.fromString("0123456789").drop(1).take(2) should ===(ByteString("12"))
|
||||
ByteString1C.fromString("0123456789").drop(5).take(4).drop(1).take(2) should ===(ByteString("67"))
|
||||
}
|
||||
"take" in {
|
||||
ByteString1.fromString("abcdefg").drop(1).take(0) should ===(ByteString(""))
|
||||
|
|
@ -345,33 +350,47 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
|
|||
}
|
||||
}
|
||||
"ByteStrings" must {
|
||||
"drop(0)" in {
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(0) should ===(ByteString.empty)
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(0) should ===(ByteString("a"))
|
||||
(ByteString1C.fromString("") ++ ByteString1.fromString("a")).drop(0) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(0) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("a")).drop(0) should ===(ByteString("aa"))
|
||||
"drop" in {
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(Int.MinValue) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(-1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(0) should ===(ByteString(""))
|
||||
}
|
||||
"drop(1)" in {
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(1) should ===(ByteString("bcd"))
|
||||
ByteStrings(Vector(ByteString1.fromString("xaaa"))).drop(1) should ===(ByteString("aaa"))
|
||||
}
|
||||
"drop(n)" in {
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(3) should ===(ByteString("d"))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(4) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(5) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(10) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(Int.MaxValue) should ===(ByteString(""))
|
||||
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).drop(-2) should ===(ByteString("abcd"))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).drop(-2) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("")).drop(Int.MinValue) should ===(ByteString("ab"))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("ab")).dropRight(Int.MinValue) should ===(ByteString("ab"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(Int.MinValue) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(-1) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(0) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(2) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).drop(Int.MaxValue) should ===(ByteString(""))
|
||||
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(Int.MinValue) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(-1) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(0) should ===(ByteString("a"))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(1) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(2) should ===(ByteString(""))
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).drop(Int.MaxValue) should ===(ByteString(""))
|
||||
|
||||
val bss = ByteStrings(Vector(
|
||||
ByteString1.fromString("a"),
|
||||
ByteString1.fromString("bc"),
|
||||
ByteString1.fromString("def")))
|
||||
|
||||
bss.drop(Int.MinValue) should ===(ByteString("abcdef"))
|
||||
bss.drop(-1) should ===(ByteString("abcdef"))
|
||||
bss.drop(0) should ===(ByteString("abcdef"))
|
||||
bss.drop(1) should ===(ByteString("bcdef"))
|
||||
bss.drop(2) should ===(ByteString("cdef"))
|
||||
bss.drop(3) should ===(ByteString("def"))
|
||||
bss.drop(4) should ===(ByteString("ef"))
|
||||
bss.drop(5) should ===(ByteString("f"))
|
||||
bss.drop(6) should ===(ByteString(""))
|
||||
bss.drop(7) should ===(ByteString(""))
|
||||
bss.drop(Int.MaxValue) should ===(ByteString(""))
|
||||
|
||||
ByteString("0123456789").drop(5).take(2) should ===(ByteString("56"))
|
||||
ByteString("0123456789").drop(5).drop(3).take(1) should ===(ByteString("8"))
|
||||
(ByteString1C.fromString("a") ++ ByteString1.fromString("bc")).drop(2) should ===(ByteString("c"))
|
||||
}
|
||||
"slice" in {
|
||||
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).slice(1, 1) should ===(ByteString(""))
|
||||
|
|
|
|||
|
|
@ -485,32 +485,24 @@ object ByteString {
|
|||
else drop0(n)
|
||||
|
||||
private def drop0(n: Int): ByteString = {
|
||||
var continue = true
|
||||
var fullDrops = 0
|
||||
var remainingToDrop = n
|
||||
do {
|
||||
// impl note: could be optimised a bit by using VectorIterator instead,
|
||||
// however then we're forced to call .toVector which halfs performance
|
||||
// We can work around that, as there's a Scala private method "remainingVector" which is fast,
|
||||
// but let's not go into calling private APIs here just yet.
|
||||
val currentLength = bytestrings(fullDrops).length
|
||||
if (remainingToDrop >= currentLength) {
|
||||
fullDrops += 1
|
||||
remainingToDrop -= currentLength
|
||||
} else continue = false
|
||||
} while (remainingToDrop > 0 && continue)
|
||||
// impl note: could be optimised a bit by using VectorIterator instead,
|
||||
// however then we're forced to call .toVector which halfs performance
|
||||
// We can work around that, as there's a Scala private method "remainingVector" which is fast,
|
||||
// but let's not go into calling private APIs here just yet.
|
||||
@tailrec def findSplit(fullDrops: Int, remainingToDrop: Int): (Int, Int) = {
|
||||
val bs = bytestrings(fullDrops)
|
||||
if (bs.length > remainingToDrop) (fullDrops, remainingToDrop)
|
||||
else findSplit(fullDrops + 1, remainingToDrop - bs.length)
|
||||
}
|
||||
|
||||
val remainingByteStrings = bytestrings.drop(fullDrops)
|
||||
if (remainingByteStrings.isEmpty) ByteString.empty
|
||||
else if (remainingToDrop > 0) {
|
||||
val h: ByteString1 = remainingByteStrings.head.drop1(remainingToDrop)
|
||||
val bs = remainingByteStrings.tail
|
||||
val (fullDrops, remainingToDrop) = findSplit(0, n)
|
||||
|
||||
if (h.isEmpty)
|
||||
if (bs.isEmpty) ByteString.empty
|
||||
else new ByteStrings(bs, length - n)
|
||||
else new ByteStrings(h +: bs, length - n)
|
||||
} else ByteStrings(remainingByteStrings, length - n)
|
||||
if (remainingToDrop == 0)
|
||||
new ByteStrings(bytestrings.drop(fullDrops), length - n)
|
||||
else if (fullDrops == bytestrings.length - 1)
|
||||
bytestrings(fullDrops).drop(remainingToDrop)
|
||||
else
|
||||
new ByteStrings(bytestrings(fullDrops).drop1(remainingToDrop) +: bytestrings.drop(fullDrops + 1), length - n)
|
||||
}
|
||||
|
||||
protected def writeReplace(): AnyRef = new SerializationProxy(this)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Copyright (C) 2014-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package akka.util
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.util.ByteString.{ ByteString1, ByteStrings }
|
||||
import org.openjdk.jmh.annotations._
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@Measurement(timeUnit = TimeUnit.MILLISECONDS)
|
||||
class ByteString_drop_Benchmark {
|
||||
|
||||
val str = List.fill[Byte](4)(0).mkString
|
||||
val numVec = 1024
|
||||
val bss = ByteStrings(Vector.fill(numVec)(ByteString1.fromString(str)))
|
||||
|
||||
val rand = new Random()
|
||||
val len = str.size * numVec
|
||||
val n_greater_or_eq_to_len = len + rand.nextInt(Int.MaxValue - len)
|
||||
val n_neg = rand.nextInt(Int.MaxValue) * -1
|
||||
val n_avg = len / 2
|
||||
val n_best = 1
|
||||
val n_worst = len - 1
|
||||
|
||||
/*
|
||||
--------------------------------- BASELINE ------------------------------------------------------------------
|
||||
[info] Benchmark Mode Cnt Score Error Units
|
||||
[info] ByteString_drop_Benchmark.bss_avg thrpt 40 544841.222 ± 12917.565 ops/s
|
||||
[info] ByteString_drop_Benchmark.bss_best thrpt 40 10141204.609 ± 415441.925 ops/s
|
||||
[info] ByteString_drop_Benchmark.bss_greater_or_eq_to_len thrpt 40 902173327.723 ± 9921650.983 ops/s
|
||||
[info] ByteString_drop_Benchmark.bss_negative thrpt 40 1179430602.793 ± 12193702.247 ops/s
|
||||
[info] ByteString_drop_Benchmark.bss_worst thrpt 40 297489.038 ± 5534.801 ops/s
|
||||
|
||||
*/
|
||||
|
||||
@Benchmark
|
||||
def bss_negative(): Unit = {
|
||||
@volatile var m: ByteString = null
|
||||
m = bss.drop(n_neg)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
def bss_greater_or_eq_to_len(): Unit = {
|
||||
@volatile var m: ByteString = null
|
||||
m = bss.drop(n_greater_or_eq_to_len)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
def bss_avg(): Unit = {
|
||||
@volatile var m: ByteString = null
|
||||
m = bss.drop(n_avg)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
def bss_best(): Unit = {
|
||||
@volatile var m: ByteString = null
|
||||
m = bss.drop(n_best)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
def bss_worst(): Unit = {
|
||||
@volatile var m: ByteString = null
|
||||
m = bss.drop(n_worst)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue