Skip to content

Instantly share code, notes, and snippets.

@rossabaker
Last active June 29, 2020 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rossabaker/31ea688c2337c40a0a0a7070cc8b4e1d to your computer and use it in GitHub Desktop.
Save rossabaker/31ea688c2337c40a0a0a7070cc8b4e1d to your computer and use it in GitHub Desktop.
package org.http4s.bench
import java.nio.charset.StandardCharsets
import java.nio.ByteBuffer
import fs2.Chunk
import scodec.bits._
import org.openjdk.jmh.annotations._
import scala.annotation.tailrec
class EncodeBench {
import EncodeBench._
@Benchmark
def byteVectorConcat: ByteBuffer = {
val bb = get ++ sp ++ encodePath("/encode") ++ sp ++ http11 ++ rn ++
host ++ encodeHost("example.com") ++ rn ++
contentLength ++ encodeLong(1024) ++ rn ++
contentType ++ json ++ rn ++
time ++ encodeLong(System.currentTimeMillis) ++ rn ++
rn
Chunk.byteVector(bb).toByteBuffer
}
@Benchmark
def array: ByteBuffer = {
val buff = new Array[Byte](256)
var offset = 0
def append(bv: ByteVector) = {
bv.copyToArray(buff, offset)
offset += bv.length.toInt
}
append(get)
append(sp)
append(encodePath("/encode"))
append(sp)
append(http11)
append(rn)
append(host)
append(encodeHost("example.com"))
append(rn)
append(contentLength)
append(encodeLong(1024))
append(rn)
append(contentType)
append(json)
append(rn)
append(time)
append(encodeLong(System.currentTimeMillis))
append(rn)
Chunk.bytes(buff, 0, offset).toByteBuffer
}
@Benchmark
def byteBuffer: ByteBuffer = {
val buff = ByteBuffer.allocate(256)
get.copyToBuffer(buff)
sp.copyToBuffer(buff)
encodePath("/encode").copyToBuffer(buff)
sp.copyToBuffer(buff)
http11.copyToBuffer(buff)
rn.copyToBuffer(buff)
host.copyToBuffer(buff)
encodeHost("example.com").copyToBuffer(buff)
rn.copyToBuffer(buff)
contentLength.copyToBuffer(buff)
encodeLong(1024).copyToBuffer(buff)
rn.copyToBuffer(buff)
contentType.copyToBuffer(buff)
json.copyToBuffer(buff)
rn.copyToBuffer(buff)
time.copyToBuffer(buff)
encodeLong(System.currentTimeMillis).copyToBuffer(buff)
rn.copyToBuffer(buff)
Chunk.byteBuffer(buff).toByteBuffer
}
@Benchmark
def chars: ByteBuffer = Chunk.bytes(
new StringBuilder()
.append("GET")
.append(" ")
.append(new String("/encode"))
.append(" ")
.append("HTTP/1.1")
.append("\r\n")
.append("Host: ")
.append(new String("example.com"))
.append("\r\n")
.append("Content-Length: ")
.append(new String("1024"))
.append("\r\n")
.append("Content-Type: ")
.append("application/json")
.append("\r\n")
.append("Time: ")
.append(System.currentTimeMillis())
.append("\r\n")
.toString
.getBytes(StandardCharsets.US_ASCII))
.toByteBuffer
}
object EncodeBench {
val get = ByteVector.view("GET".getBytes(StandardCharsets.US_ASCII))
val sp = ByteVector(' '.toByte)
val rn = ByteVector.view("\r\n".getBytes(StandardCharsets.US_ASCII))
val http11 = ByteVector.view("HTTP/1.1".getBytes(StandardCharsets.US_ASCII))
val http = ByteVector.view("http".getBytes(StandardCharsets.US_ASCII))
val host = ByteVector.view("Host: ".getBytes(StandardCharsets.US_ASCII))
def encodeHost(host: String) = ByteVector.view(host.getBytes(StandardCharsets.US_ASCII))
val example = ByteVector.view("example.com".getBytes(StandardCharsets.US_ASCII))
val contentLength = ByteVector.view("Content-Length: ".getBytes(StandardCharsets.US_ASCII))
def encodeLong(len: Long) = ByteVector.view(signedDecimalBytes(len))
val oneThousandTwentyFour = ByteVector.view("1024".getBytes(StandardCharsets.US_ASCII))
val contentType = ByteVector.view("Content-Type: ".getBytes(StandardCharsets.US_ASCII))
def json = ByteVector.view("application/json".getBytes(StandardCharsets.US_ASCII))
def encodePath(path: String) = ByteVector.view(path.getBytes(StandardCharsets.US_ASCII))
val path = ByteVector.view("/encode".getBytes(StandardCharsets.US_ASCII))
val time = ByteVector.view("time".getBytes(StandardCharsets.US_ASCII))
def signedDecimalBytes(long: Long): Array[Byte] = {
val len = _numberOfDecimalDigits(long)
val buf = new Array[Byte](len)
getSignedDecimalBytes(long, len, buf)
buf
}
def _numberOfDecimalDigits(long: Long): Int = {
def mul10(l: Long) = (l << 3) + (l << 1)
@tailrec def len(test: Long, l: Long, result: Int): Int =
if (test > l || test < 0) result else len(mul10(test), l, result + 1)
if (long < 0) len(10, -long, 2) else len(10, long, 1)
}
val Zero = '0'.toByte
def getSignedDecimalBytes(long: Long, endIndex: Int, buf: Array[Byte]): Unit = {
def div10(i: Int) = {
var q = (i << 3) + (i << 2)
q += (q << 12) + (q << 8) + (q << 4) + i
q >>>= 19
q // 52429 * l / 524288 = l * 0.10000038146972656
}
def mul10(i: Int) = (i << 3) + (i << 1)
def mul100(l: Long) = (l << 6) + (l << 5) + (l << 2)
phase1(math.abs(long), endIndex)
// for large numbers we bite the bullet of performing one division every two digits
@tailrec def phase1(l: Long, ix: Int): Unit =
if (l > 65535L) {
val q = l / 100
val r = (l - mul100(q)).toInt
val rq = div10(r)
buf(ix - 2) = (Zero + rq).toByte
buf(ix - 1) = (Zero + r - mul10(rq)).toByte
phase1(q, ix - 2)
} else phase2(l.toInt, ix)
// for small numbers we can use the "fast-path"
@tailrec def phase2(i: Int, ix: Int): Unit = {
val q = div10(i)
val r = i - mul10(q)
buf(ix - 1) = (Zero + r).toByte
if (q != 0) phase2(q, ix - 1)
else if (long < 0) buf(ix - 2) = '-'
}
}
}
[info] Benchmark Mode Cnt Score Error Units
[info] EncodeBench.array thrpt 2 1768359.548 ops/s
[info] EncodeBench.array:·gc.alloc.rate thrpt 2 3520.462 MB/sec
[info] EncodeBench.array:·gc.alloc.rate.norm thrpt 2 2192.000 B/op
[info] EncodeBench.array:·gc.churn.PS_Eden_Space thrpt 2 3526.282 MB/sec
[info] EncodeBench.array:·gc.churn.PS_Eden_Space.norm thrpt 2 2195.565 B/op
[info] EncodeBench.array:·gc.churn.PS_Survivor_Space thrpt 2 0.116 MB/sec
[info] EncodeBench.array:·gc.churn.PS_Survivor_Space.norm thrpt 2 0.072 B/op
[info] EncodeBench.array:·gc.count thrpt 2 234.000 counts
[info] EncodeBench.array:·gc.time thrpt 2 186.000 ms
[info] EncodeBench.byteBuffer thrpt 2 1601312.530 ops/s
[info] EncodeBench.byteBuffer:·gc.alloc.rate thrpt 2 3374.084 MB/sec
[info] EncodeBench.byteBuffer:·gc.alloc.rate.norm thrpt 2 2320.000 B/op
[info] EncodeBench.byteBuffer:·gc.churn.PS_Eden_Space thrpt 2 3379.717 MB/sec
[info] EncodeBench.byteBuffer:·gc.churn.PS_Eden_Space.norm thrpt 2 2324.025 B/op
[info] EncodeBench.byteBuffer:·gc.churn.PS_Survivor_Space thrpt 2 0.097 MB/sec
[info] EncodeBench.byteBuffer:·gc.churn.PS_Survivor_Space.norm thrpt 2 0.066 B/op
[info] EncodeBench.byteBuffer:·gc.count thrpt 2 218.000 counts
[info] EncodeBench.byteBuffer:·gc.time thrpt 2 176.000 ms
[info] EncodeBench.byteVectorConcat thrpt 2 624651.771 ops/s
[info] EncodeBench.byteVectorConcat:·gc.alloc.rate thrpt 2 1987.882 MB/sec
[info] EncodeBench.byteVectorConcat:·gc.alloc.rate.norm thrpt 2 3504.000 B/op
[info] EncodeBench.byteVectorConcat:·gc.churn.PS_Eden_Space thrpt 2 1983.988 MB/sec
[info] EncodeBench.byteVectorConcat:·gc.churn.PS_Eden_Space.norm thrpt 2 3497.020 B/op
[info] EncodeBench.byteVectorConcat:·gc.churn.PS_Survivor_Space thrpt 2 0.134 MB/sec
[info] EncodeBench.byteVectorConcat:·gc.churn.PS_Survivor_Space.norm thrpt 2 0.237 B/op
[info] EncodeBench.byteVectorConcat:·gc.count thrpt 2 236.000 counts
[info] EncodeBench.byteVectorConcat:·gc.time thrpt 2 182.000 ms
[info] EncodeBench.chars thrpt 2 3004227.366 ops/s
[info] EncodeBench.chars:·gc.alloc.rate thrpt 2 3077.777 MB/sec
[info] EncodeBench.chars:·gc.alloc.rate.norm thrpt 2 1128.000 B/op
[info] EncodeBench.chars:·gc.churn.PS_Eden_Space thrpt 2 3076.120 MB/sec
[info] EncodeBench.chars:·gc.churn.PS_Eden_Space.norm thrpt 2 1127.396 B/op
[info] EncodeBench.chars:·gc.churn.PS_Survivor_Space thrpt 2 0.100 MB/sec
[info] EncodeBench.chars:·gc.churn.PS_Survivor_Space.norm thrpt 2 0.037 B/op
[info] EncodeBench.chars:·gc.count thrpt 2 228.000 counts
[info] EncodeBench.chars:·gc.time thrpt 2 179.000 ms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment