Skip to content

Commit

Permalink
=act improve ByteString#dropRight(...) (akka#21439)
Browse files Browse the repository at this point in the history
akka#21439: =act improve ByteString#dropRight(...)
  • Loading branch information
monkey-mas authored and drewhk committed Dec 6, 2016
1 parent 8d05592 commit c38d385
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 22 deletions.
86 changes: 76 additions & 10 deletions akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,23 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
ByteString1.fromString("0123456789").drop(1).take(2) should ===(ByteString("12"))
ByteString1.fromString("0123456789").drop(5).take(4).drop(1).take(2) should ===(ByteString("67"))
}
"dropRight" in {
ByteString1.empty.dropRight(-1) should ===(ByteString(""))
ByteString1.empty.dropRight(0) should ===(ByteString(""))
ByteString1.empty.dropRight(1) should ===(ByteString(""))
ByteString1.fromString("a").dropRight(-1) should ===(ByteString("a"))
ByteString1.fromString("a").dropRight(0) should ===(ByteString("a"))
ByteString1.fromString("a").dropRight(1) should ===(ByteString(""))
ByteString1.fromString("a").dropRight(2) should ===(ByteString(""))
ByteString1.fromString("abc").dropRight(-1) should ===(ByteString("abc"))
ByteString1.fromString("abc").dropRight(0) should ===(ByteString("abc"))
ByteString1.fromString("abc").dropRight(1) should ===(ByteString("ab"))
ByteString1.fromString("abc").dropRight(2) should ===(ByteString("a"))
ByteString1.fromString("abc").dropRight(3) should ===(ByteString(""))
ByteString1.fromString("abc").dropRight(4) should ===(ByteString(""))
ByteString1.fromString("0123456789").dropRight(1).take(2) should ===(ByteString("01"))
ByteString1.fromString("0123456789").dropRight(5).take(4).drop(1).take(2) should ===(ByteString("12"))
}
"take" in {
ByteString1.empty.take(-1) should ===(ByteString(""))
ByteString1.empty.take(0) should ===(ByteString(""))
Expand Down Expand Up @@ -341,6 +358,23 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
ByteString1C.fromString("0123456789").drop(1).take(2) should ===(ByteString("12"))
ByteString1C.fromString("0123456789").drop(5).take(4).drop(1).take(2) should ===(ByteString("67"))
}
"dropRight" in {
ByteString1C.fromString("").dropRight(-1) should ===(ByteString(""))
ByteString1C.fromString("").dropRight(0) should ===(ByteString(""))
ByteString1C.fromString("").dropRight(1) should ===(ByteString(""))
ByteString1C.fromString("a").dropRight(-1) should ===(ByteString("a"))
ByteString1C.fromString("a").dropRight(0) should ===(ByteString("a"))
ByteString1C.fromString("a").dropRight(1) should ===(ByteString(""))
ByteString1C.fromString("a").dropRight(2) should ===(ByteString(""))
ByteString1C.fromString("abc").dropRight(-1) should ===(ByteString("abc"))
ByteString1C.fromString("abc").dropRight(0) should ===(ByteString("abc"))
ByteString1C.fromString("abc").dropRight(1) should ===(ByteString("ab"))
ByteString1C.fromString("abc").dropRight(2) should ===(ByteString("a"))
ByteString1C.fromString("abc").dropRight(3) should ===(ByteString(""))
ByteString1C.fromString("abc").dropRight(4) should ===(ByteString(""))
ByteString1C.fromString("0123456789").dropRight(1).take(2) should ===(ByteString("01"))
ByteString1C.fromString("0123456789").dropRight(5).take(4).drop(1).take(2) should ===(ByteString("12"))
}
"take" in {
ByteString1.fromString("abcdefg").drop(1).take(0) should ===(ByteString(""))
ByteString1.fromString("abcdefg").drop(1).take(-1) should ===(ByteString(""))
Expand Down Expand Up @@ -392,6 +426,48 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
ByteString("0123456789").drop(5).drop(3).take(1) should ===(ByteString("8"))
(ByteString1C.fromString("a") ++ ByteString1.fromString("bc")).drop(2) should ===(ByteString("c"))
}
"dropRight" in {
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).dropRight(Int.MinValue) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).dropRight(-1) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).dropRight(0) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).dropRight(1) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("")).dropRight(Int.MaxValue) should ===(ByteString(""))

ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(Int.MinValue) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(-1) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(0) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(1) should ===(ByteString(""))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(2) should ===(ByteString(""))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(Int.MaxValue) should ===(ByteString(""))

ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(Int.MinValue) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(-1) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(0) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(1) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(2) should ===(ByteString(""))
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).dropRight(Int.MaxValue) should ===(ByteString(""))

val bss = ByteStrings(Vector(
ByteString1.fromString("a"),
ByteString1.fromString("bc"),
ByteString1.fromString("def")))

bss.dropRight(Int.MinValue) should ===(ByteString("abcdef"))
bss.dropRight(-1) should ===(ByteString("abcdef"))
bss.dropRight(0) should ===(ByteString("abcdef"))
bss.dropRight(1) should ===(ByteString("abcde"))
bss.dropRight(2) should ===(ByteString("abcd"))
bss.dropRight(3) should ===(ByteString("abc"))
bss.dropRight(4) should ===(ByteString("ab"))
bss.dropRight(5) should ===(ByteString("a"))
bss.dropRight(6) should ===(ByteString(""))
bss.dropRight(7) should ===(ByteString(""))
bss.dropRight(Int.MaxValue) should ===(ByteString(""))

ByteString("0123456789").dropRight(5).take(2) should ===(ByteString("01"))
ByteString("0123456789").dropRight(5).drop(3).take(1) should ===(ByteString("3"))
(ByteString1C.fromString("a") ++ ByteString1.fromString("bc")).dropRight(2) should ===(ByteString("a"))
}
"slice" in {
ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).slice(1, 1) should ===(ByteString(""))
// We explicitly test all edge cases to always test them, refs bug #21237
Expand Down Expand Up @@ -436,16 +512,6 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers {
// Get an empty if `from` is greater than `until`
ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(4, 0) should ===(ByteString(""))
}
"dropRight" in {
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(0) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(-1) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(Int.MinValue) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(1) should ===(ByteString(""))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(Int.MaxValue) should ===(ByteString(""))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("bc")).dropRight(1) should ===(ByteString("ab"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("bc")).dropRight(2) should ===(ByteString("a"))
ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("bc")).dropRight(3) should ===(ByteString(""))
}
"take" in {
ByteString.empty.take(-1) should ===(ByteString(""))
ByteString.empty.take(0) should ===(ByteString(""))
Expand Down
32 changes: 20 additions & 12 deletions akka-actor/src/main/scala/akka/util/ByteString.scala
Original file line number Diff line number Diff line change
Expand Up @@ -487,21 +487,29 @@ object ByteString {
}

override def dropRight(n: Int): ByteString =
if (n <= 0) this
else {
val last = bytestrings.last
if (n < last.length) new ByteStrings(bytestrings.init :+ last.dropRight1(n), length - n)
else {
val remaining = bytestrings.init
if (remaining.isEmpty) ByteString.empty
else {
val s = new ByteStrings(remaining, length - last.length)
val remainingToBeDropped = n - last.length
s.dropRight(remainingToBeDropped)
}
if (0 < n && n < length) dropRight0(n)
else if (n >= length) ByteString.empty
else this

private def dropRight0(n: Int): ByteString = {
val byteStringsSize = bytestrings.length
@tailrec def dropRightWithFullDropsAndRemainig(fullDrops: Int, remainingToDrop: Int): ByteString = {
val bs = bytestrings(byteStringsSize - fullDrops - 1)
if (bs.length > remainingToDrop) {
if (fullDrops == byteStringsSize - 1)
bytestrings(0).dropRight(remainingToDrop)
else if (remainingToDrop == 0)
new ByteStrings(bytestrings.dropRight(fullDrops), length - n)
else
new ByteStrings(bytestrings.dropRight(fullDrops + 1) :+ bytestrings(byteStringsSize - fullDrops - 1).dropRight1(remainingToDrop), length - n)
} else {
dropRightWithFullDropsAndRemainig(fullDrops + 1, remainingToDrop - bs.length)
}
}

dropRightWithFullDropsAndRemainig(0, n)
}

override def slice(from: Int, until: Int): ByteString =
if (from <= 0 && until >= length) this
else if (from > length || until <= from) ByteString.empty
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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_dropRight_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 -----------------------------------------------------------------------
commit 0f2da7b26b5c4af35be87d2bd4a1a2392365df15
[info] Benchmark Mode Cnt Score Error Units
[info] ByteString_dropRight_Benchmark.bss_avg thrpt 40 25626.311 ± 1395.662 ops/s
[info] ByteString_dropRight_Benchmark.bss_best thrpt 40 8667558.031 ± 200233.008 ops/s
[info] ByteString_dropRight_Benchmark.bss_greater_or_eq_to_len thrpt 40 12658.684 ± 376.730 ops/s
[info] ByteString_dropRight_Benchmark.bss_negative thrpt 40 1214680926.895 ± 10661843.507 ops/s
[info] ByteString_dropRight_Benchmark.bss_worst thrpt 40 13087.245 ± 246.911 ops/s
--------------------------------- AFTER --------------------------------------------------------------------------
------ TODAY –––––––
[info] Benchmark Mode Cnt Score Error Units
[info] ByteString_dropRight_Benchmark.bss_avg thrpt 40 528969.025 ± 6039.001 ops/s
[info] ByteString_dropRight_Benchmark.bss_best thrpt 40 7925951.396 ± 249279.950 ops/s
[info] ByteString_dropRight_Benchmark.bss_greater_or_eq_to_len thrpt 40 893475724.604 ± 9836471.105 ops/s
[info] ByteString_dropRight_Benchmark.bss_negative thrpt 40 1182275022.613 ± 9710755.955 ops/s
[info] ByteString_dropRight_Benchmark.bss_worst thrpt 40 244599.957 ± 3276.140 ops/s
*/

@Benchmark
def bss_negative(): ByteString =
bss.dropRight(n_neg)

@Benchmark
def bss_greater_or_eq_to_len(): ByteString =
bss.dropRight(n_greater_or_eq_to_len)

@Benchmark
def bss_avg(): ByteString =
bss.dropRight(n_avg)

@Benchmark
def bss_best(): ByteString =
bss.dropRight(n_best)

@Benchmark
def bss_worst(): ByteString =
bss.dropRight(n_worst)
}

0 comments on commit c38d385

Please sign in to comment.