-
-
Save rossabaker/31ea688c2337c40a0a0a7070cc8b4e1d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) = '-' | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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