Merge commit '0f6aab9da6fe982218a01f4a5b896e65fcced437' as 'third_party/flatbuffers'

This commit is contained in:
Siarhei Fedartsou
2024-06-22 13:33:34 +02:00
1814 changed files with 326902 additions and 0 deletions
@@ -0,0 +1,152 @@
import groovy.xml.XmlParser
plugins {
kotlin("multiplatform")
id("org.jetbrains.kotlinx.benchmark")
id("io.morethan.jmhreport")
id("de.undercouch.download")
}
group = "com.google.flatbuffers.jmh"
version = "2.0.0-SNAPSHOT"
// Reads latest version from Java's runtime pom.xml,
// so we can use it for benchmarking against Kotlin's
// runtime
fun readJavaFlatBufferVersion(): String {
val pom = XmlParser().parse(File("../java/pom.xml"))
val versionTag = pom.children().find {
val node = it as groovy.util.Node
node.name().toString().contains("version")
} as groovy.util.Node
return versionTag.value().toString()
}
// This plugin generates a static html page with the aggregation
// of all benchmarks ran. very useful visualization tool.
jmhReport {
val baseFolder = project.file("build/reports/benchmarks/main").absolutePath
val lastFolder = project.file(baseFolder).list()?.sortedArray()?.lastOrNull() ?: ""
jmhResultPath = "$baseFolder/$lastFolder/jvm.json"
jmhReportOutput = "$baseFolder/$lastFolder"
}
// For now we benchmark on JVM only
benchmark {
configurations {
this.getByName("main") {
iterations = 5
iterationTime = 300
iterationTimeUnit = "ms"
// uncomment for benchmarking JSON op only
include(".*FlatbufferBenchmark.*")
}
}
targets {
register("jvm")
}
}
kotlin {
jvm {
compilations {
val main by getting { }
// custom benchmark compilation
val benchmarks by compilations.creating {
defaultSourceSet {
dependencies {
// Compile against the main compilation's compile classpath and outputs:
implementation(main.compileDependencyFiles + main.output.classesDirs)
}
}
}
}
}
sourceSets {
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
implementation(project(":flatbuffers-kotlin"))
implementation(libs.kotlinx.benchmark.runtime)
// json serializers
implementation(libs.moshi.kotlin)
implementation(libs.gson)
}
kotlin.srcDir("src/jvmMain/generated/kotlin/")
kotlin.srcDir("src/jvmMain/generated/java/")
kotlin.srcDir("../../java/src/main/java")
}
}
}
// This task download all JSON files used for benchmarking
tasks.register<de.undercouch.gradle.tasks.download.Download>("downloadMultipleFiles") {
// We are downloading json benchmark samples from serdes-rs project.
// see: https://github.com/serde-rs/json-benchmark/blob/master/data
val baseUrl = "https://github.com/serde-rs/json-benchmark/raw/master/data/"
src(listOf("$baseUrl/canada.json", "$baseUrl/twitter.json", "$baseUrl/citm_catalog.json"))
dest(File("${project.projectDir.absolutePath}/src/jvmMain/resources"))
overwrite(false)
}
abstract class GenerateFBTestClasses : DefaultTask() {
@get:InputFiles
abstract val inputFiles: ConfigurableFileCollection
@get:Input
abstract val includeFolder: Property<String>
@get:Input
abstract val outputFolder: Property<String>
@get:Input
abstract val variants: ListProperty<String>
@Inject
protected open fun getExecActionFactory(): org.gradle.process.internal.ExecActionFactory? {
throw UnsupportedOperationException()
}
init {
includeFolder.set("")
}
@TaskAction
fun compile() {
val execAction = getExecActionFactory()!!.newExecAction()
val sources = inputFiles.asPath.split(":")
val langs = variants.get().map { "--$it" }
val args = mutableListOf("flatc","-o", outputFolder.get(), *langs.toTypedArray())
if (includeFolder.get().isNotEmpty()) {
args.add("-I")
args.add(includeFolder.get())
}
args.addAll(sources)
println(args)
execAction.commandLine = args
print(execAction.execute())
}
}
// Use the default greeting
tasks.register<GenerateFBTestClasses>("generateFBTestClassesKt") {
inputFiles.setFrom("$projectDir/monster_test_kotlin.fbs")
includeFolder.set("$rootDir/../tests/include_test")
outputFolder.set("${projectDir}/src/jvmMain/generated/kotlin/")
variants.addAll("kotlin-kmp")
}
tasks.register<GenerateFBTestClasses>("generateFBTestClassesJava") {
inputFiles.setFrom("$projectDir/monster_test_java.fbs")
includeFolder.set("$rootDir/../tests/include_test")
outputFolder.set("${projectDir}/src/jvmMain/generated/java/")
variants.addAll("kotlin")
}
project.tasks.forEach {
if (it.name.contains("compileKotlin")) {
it.dependsOn("generateFBTestClassesKt")
it.dependsOn("generateFBTestClassesJava")
}
}
@@ -0,0 +1,37 @@
// Example IDL file for our monster's schema.
namespace jmonster;
enum JColor:byte { Red = 0, Green, Blue = 2 }
union JEquipment { JWeapon } // Optionally add more tables.
struct JVec3 {
x:float;
y:float;
z:float;
}
table JMonster {
pos:JVec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte];
color:JColor = Blue;
weapons:[JWeapon];
equipped:JEquipment;
path:[JVec3];
}
table JWeapon {
name:string;
damage:short;
}
table JAllMonsters {
monsters: [JMonster];
}
root_type JAllMonsters;
@@ -0,0 +1,37 @@
// Example IDL file for our monster's schema.
namespace monster;
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon } // Optionally add more tables.
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte];
color:Color = Blue;
weapons:[Weapon];
equipped:Equipment;
path:[Vec3];
}
table Weapon {
name:string;
damage:short;
}
table AllMonsters {
monsters: [Monster];
}
root_type AllMonsters;
@@ -0,0 +1,129 @@
@file:OptIn(ExperimentalUnsignedTypes::class)
package com.google.flatbuffers.kotlin.benchmark
import com.google.flatbuffers.kotlin.FlatBufferBuilder
import jmonster.JAllMonsters
import jmonster.JColor
import jmonster.JMonster
import jmonster.JVec3
import monster.AllMonsters
import monster.AllMonsters.Companion.createAllMonsters
import monster.AllMonsters.Companion.createMonstersVector
import monster.Monster
import monster.Monster.Companion.createInventoryVector
import monster.MonsterOffsetArray
import monster.Vec3
import org.openjdk.jmh.annotations.*
import org.openjdk.jmh.infra.Blackhole
import java.util.concurrent.TimeUnit
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.NANOSECONDS)
open class FlatbufferBenchmark {
val repetition = 1000000
val fbKotlin = FlatBufferBuilder(1024 * repetition)
val fbDeserializationKotlin = FlatBufferBuilder(1024 * repetition)
val fbJava = com.google.flatbuffers.FlatBufferBuilder(1024 * repetition)
val fbDeserializationJava = com.google.flatbuffers.FlatBufferBuilder(1024 * repetition)
init {
populateMosterKotlin(fbDeserializationKotlin)
populateMosterJava(fbDeserializationJava)
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun populateMosterKotlin(fb: FlatBufferBuilder) {
fb.clear()
val monsterName = fb.createString("MonsterName");
val items = ubyteArrayOf(0u, 1u, 2u, 3u, 4u)
val inv = createInventoryVector(fb, items)
val monsterOffsets: MonsterOffsetArray = MonsterOffsetArray(repetition) {
Monster.startMonster(fb)
Monster.addName(fb, monsterName)
Monster.addPos(fb, Vec3.createVec3(fb, 1.0f, 2.0f, 3.0f))
Monster.addHp(fb, 80)
Monster.addMana(fb, 150)
Monster.addInventory(fb, inv)
Monster.addColor(fb, monster.Color.Red)
Monster.endMonster(fb)
}
val monsters = createMonstersVector(fb, monsterOffsets)
val allMonsters = createAllMonsters(fb, monsters)
fb.finish(allMonsters)
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun populateMosterJava(fb: com.google.flatbuffers.FlatBufferBuilder){
fb.clear()
val monsterName = fb.createString("MonsterName");
val inv = JMonster.createInventoryVector(fb, ubyteArrayOf(0u, 1u, 2u, 3u, 4u))
val monsters = JAllMonsters.createMonstersVector(fb, IntArray(repetition) {
JMonster.startJMonster(fb)
JMonster.addName(fb, monsterName)
JMonster.addPos(fb, JVec3.createJVec3(fb, 1.0f, 2.0f, 3.0f))
JMonster.addHp(fb, 80)
JMonster.addMana(fb, 150)
JMonster.addInventory(fb, inv)
JMonster.addColor(fb, JColor.Red)
JMonster.endJMonster(fb)
})
val allMonsters = JAllMonsters.createJAllMonsters(fb, monsters)
fb.finish(allMonsters)
}
@Benchmark
fun monstersSerializationKotlin() {
populateMosterKotlin(fbKotlin)
}
@OptIn(ExperimentalUnsignedTypes::class)
@Benchmark
fun monstersDeserializationKotlin(hole: Blackhole) {
val monstersRef = AllMonsters.asRoot(fbDeserializationKotlin.dataBuffer())
for (i in 0 until monstersRef.monstersLength) {
val monster = monstersRef.monsters(i)!!
val pos = monster.pos!!
hole.consume(monster.name)
hole.consume(pos.x)
hole.consume(pos.y)
hole.consume(pos.z)
hole.consume(monster.hp)
hole.consume(monster.mana)
hole.consume(monster.color)
hole.consume(monster.inventory(0).toByte())
hole.consume(monster.inventory(1))
hole.consume(monster.inventory(2))
hole.consume(monster.inventory(3))
}
}
@Benchmark
fun monstersSerializationJava() {
populateMosterJava(fbJava)
}
@Benchmark
fun monstersDeserializationJava(hole: Blackhole) {
val monstersRef = JAllMonsters.getRootAsJAllMonsters(fbDeserializationJava.dataBuffer())
for (i in 0 until monstersRef.monstersLength) {
val monster = monstersRef.monsters(i)!!
val pos = monster.pos!!
hole.consume(monster.name)
hole.consume(pos.x)
hole.consume(pos.y)
hole.consume(pos.z)
hole.consume(monster.hp)
hole.consume(monster.mana)
hole.consume(monster.color)
hole.consume(monster.inventory(0))
hole.consume(monster.inventory(1))
hole.consume(monster.inventory(2))
hole.consume(monster.inventory(3))
}
}
}
@@ -0,0 +1,199 @@
/*
* Copyright 2021 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalUnsignedTypes::class)
package com.google.flatbuffers.kotlin.benchmark
import com.google.flatbuffers.ArrayReadWriteBuf
import com.google.flatbuffers.FlexBuffers
import com.google.flatbuffers.FlexBuffersBuilder.BUILDER_FLAG_SHARE_ALL
import com.google.flatbuffers.kotlin.FlexBuffersBuilder
import com.google.flatbuffers.kotlin.getRoot
import kotlinx.benchmark.Blackhole
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Measurement
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.annotations.OutputTimeUnit
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.Setup
import org.openjdk.jmh.annotations.State
import java.util.concurrent.TimeUnit
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.NANOSECONDS)
open class FlexBuffersBenchmark {
var initialCapacity = 1024
var value: Double = 0.0
val stringKey = Array(500) { "Ḧ̵̘́ȩ̵̐myFairlyBigKey$it" }
val stringValue = Array(500) { "Ḧ̵̘́ȩ̵̐myFairlyBigValue$it" }
val bigIntArray = IntArray(5000) { it }
@Setup
fun setUp() {
value = 3.0
}
@Benchmark
open fun mapKotlin(blackhole: Blackhole) {
val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
kBuilder.putMap {
this["hello"] = "world"
this["int"] = 10
this["float"] = 12.3
this["intarray"] = bigIntArray
this.putMap("myMap") {
this["cool"] = "beans"
}
}
val ref = getRoot(kBuilder.finish())
val map = ref.toMap()
blackhole.consume(map.size)
blackhole.consume(map["hello"].toString())
blackhole.consume(map["int"].toInt())
blackhole.consume(map["float"].toDouble())
blackhole.consume(map["intarray"].toIntArray())
blackhole.consume(ref["myMap"]["cool"].toString())
blackhole.consume(ref["invalid_key"].isNull)
}
@Benchmark
open fun mapJava(blackhole: Blackhole) {
val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
val startMap = jBuilder.startMap()
jBuilder.putString("hello", "world")
jBuilder.putInt("int", 10)
jBuilder.putFloat("float", 12.3)
val startVec = jBuilder.startVector()
bigIntArray.forEach { jBuilder.putInt(it) }
jBuilder.endVector("intarray", startVec, true, false)
val startInnerMap = jBuilder.startMap()
jBuilder.putString("cool", "beans")
jBuilder.endMap("myMap", startInnerMap)
jBuilder.endMap(null, startMap)
val ref = FlexBuffers.getRoot(jBuilder.finish())
val map = ref.asMap()
blackhole.consume(map.size())
blackhole.consume(map.get("hello").toString())
blackhole.consume(map.get("int").asInt())
blackhole.consume(map.get("float").asFloat())
val vec = map.get("intarray").asVector()
blackhole.consume(IntArray(vec.size()) { vec.get(it).asInt() })
blackhole.consume(ref.asMap()["myMap"].asMap()["cool"].toString())
blackhole.consume(ref.asMap()["invalid_key"].isNull)
}
@Benchmark
open fun intArrayKotlin(blackhole: Blackhole) {
val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
kBuilder.put(bigIntArray)
val root = getRoot(kBuilder.finish())
blackhole.consume(root.toIntArray())
}
@Benchmark
open fun intArrayJava(blackhole: Blackhole) {
val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
val v = jBuilder.startVector()
bigIntArray.forEach { jBuilder.putInt(it) }
jBuilder.endVector(null, v, true, false)
jBuilder.finish()
val root = FlexBuffers.getRoot(jBuilder.buffer)
val vec = root.asVector()
blackhole.consume(
IntArray(vec.size()) {
vec[it].asInt()
}
)
}
@Benchmark
open fun stringArrayKotlin(blackhole: Blackhole) {
val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
kBuilder.putVector { stringValue.forEach { kBuilder.put(it) } }
kBuilder.finish()
val root = getRoot(kBuilder.buffer)
val vec = root.toVector()
blackhole.consume(Array(vec.size) { vec[it].toString() })
}
@Benchmark
open fun stringArrayJava(blackhole: Blackhole) {
val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
val v = jBuilder.startVector()
stringValue.forEach { jBuilder.putString(it) }
jBuilder.endVector(null, v, false, false)
jBuilder.finish()
val root = FlexBuffers.getRoot(jBuilder.buffer)
val vec = root.asVector()
blackhole.consume(Array(vec.size()) { vec[it].toString() })
}
@Benchmark
open fun stringMapKotlin(blackhole: Blackhole) {
val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
val pos = kBuilder.startMap()
for (i in stringKey.indices) {
kBuilder[stringKey[i]] = stringValue[i]
}
kBuilder.endMap(pos)
val ref = getRoot(kBuilder.finish())
val map = ref.toMap()
val keys = map.keys
for (key in keys) {
blackhole.consume(map[key.toString()].toString())
}
}
@Benchmark
open fun stringMapBytIndexKotlin(blackhole: Blackhole) {
val kBuilder = FlexBuffersBuilder(initialCapacity, FlexBuffersBuilder.SHARE_KEYS_AND_STRINGS)
val pos = kBuilder.startMap()
for (i in stringKey.indices) {
kBuilder[stringKey[i]] = stringValue[i]
}
kBuilder.endMap(pos)
val ref = getRoot(kBuilder.finish())
val map = ref.toMap()
for (index in 0 until map.size) {
blackhole.consume(map[index].toString())
}
}
@Benchmark
open fun stringMapJava(blackhole: Blackhole) {
val jBuilder = com.google.flatbuffers.FlexBuffersBuilder(ArrayReadWriteBuf(initialCapacity), BUILDER_FLAG_SHARE_ALL)
val v = jBuilder.startMap()
for (i in stringKey.indices) {
jBuilder.putString(stringKey[i], stringValue[i])
}
jBuilder.endMap(null, v)
val ref = FlexBuffers.getRoot(jBuilder.finish())
val map = ref.asMap()
val keyVec = map.keys()
for (i in 0 until keyVec.size()) {
val s = keyVec[i].toString()
blackhole.consume(map[s].toString())
}
}
}
@@ -0,0 +1,122 @@
/*
* Copyright 2021 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.flatbuffers.kotlin.benchmark
import com.google.flatbuffers.kotlin.ArrayReadBuffer
import com.google.flatbuffers.kotlin.JSONParser
import com.google.flatbuffers.kotlin.Reference
import com.google.flatbuffers.kotlin.toJson
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.benchmark.Blackhole
import okio.Buffer
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Measurement
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.annotations.OutputTimeUnit
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State
import java.io.ByteArrayInputStream
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 100, time = 1, timeUnit = TimeUnit.MICROSECONDS)
open class JsonBenchmark {
final val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
final val moshiAdapter = moshi.adapter(Map::class.java)
final val gson = Gson()
final val gsonParser = JsonParser()
val fbParser = JSONParser()
final val classLoader = this.javaClass.classLoader
final val twitterData = classLoader.getResourceAsStream("twitter.json")!!.readBytes()
final val canadaData = classLoader.getResourceAsStream("canada.json")!!.readBytes()
final val citmData = classLoader.getResourceAsStream("citm_catalog.json")!!.readBytes()
val fbCitmRef = JSONParser().parse(ArrayReadBuffer(citmData))
val moshiCitmRef = moshi.adapter(Map::class.java).fromJson(citmData.decodeToString())
val gsonCitmRef = gsonParser.parse(citmData.decodeToString())
fun readFlexBuffers(data: ByteArray): Reference = fbParser.parse(ArrayReadBuffer(data))
fun readMoshi(data: ByteArray): Map<*, *>? {
val buffer = Buffer().write(data)
return moshiAdapter.fromJson(buffer)
}
fun readGson(data: ByteArray): JsonObject {
val parser = JsonParser()
val jsonReader = InputStreamReader(ByteArrayInputStream(data))
return parser.parse(jsonReader).asJsonObject
}
// TWITTER
@Benchmark
open fun readTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData))
@Benchmark
open fun readTwitterMoshi(hole: Blackhole?) = hole?.consume(readMoshi(twitterData))
@Benchmark
open fun readTwitterGson(hole: Blackhole?) = hole?.consume(readGson(twitterData))
@Benchmark
open fun roundTripTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData).toJson())
@Benchmark
open fun roundTripTwitterMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(twitterData)))
@Benchmark
open fun roundTripTwitterGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(twitterData)))
// CITM
@Benchmark
open fun readCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData))
@Benchmark
open fun readCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
@Benchmark
open fun readCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
@Benchmark
open fun roundTripCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData).toJson())
@Benchmark
open fun roundTripCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
@Benchmark
open fun roundTripCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
@Benchmark
open fun writeCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(fbCitmRef.toJson())
@Benchmark
open fun writeCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(moshiCitmRef))
@Benchmark
open fun writeCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(gsonCitmRef))
// CANADA
@Benchmark
open fun readCanadaFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(canadaData))
@Benchmark
open fun readCanadaMoshi(hole: Blackhole?) = hole?.consume(readMoshi(canadaData))
@Benchmark
open fun readCanadaGson(hole: Blackhole?) = hole?.consume(readGson(canadaData))
}
@@ -0,0 +1,235 @@
/*
* Copyright 2021 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.flatbuffers.kotlin.benchmark
import com.google.flatbuffers.kotlin.ArrayReadWriteBuffer
import com.google.flatbuffers.kotlin.Key
import com.google.flatbuffers.kotlin.Utf8
import kotlinx.benchmark.Blackhole
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Measurement
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.annotations.OutputTimeUnit
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.Setup
import org.openjdk.jmh.annotations.State
import java.nio.ByteBuffer
import java.util.concurrent.TimeUnit
import kotlin.random.Random
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 100, time = 1, timeUnit = TimeUnit.MICROSECONDS)
open class UTF8Benchmark {
private val sampleSize = 5000
private val stringSize = 25
private var sampleSmallUtf8 = (0..sampleSize).map { populateUTF8(stringSize) }.toList()
private var sampleSmallUtf8Decoded = sampleSmallUtf8.map { it.encodeToByteArray() }.toList()
private var sampleSmallAscii = (0..sampleSize).map { populateAscii(stringSize) }.toList()
private var sampleSmallAsciiDecoded = sampleSmallAscii.map { it.encodeToByteArray() }.toList()
@Setup
fun setUp() {
}
@Benchmark
fun encodeUtf8KotlinStandard(blackhole: Blackhole) {
for (i in sampleSmallUtf8) {
blackhole.consume(i.encodeToByteArray())
}
}
@Benchmark
fun encodeUtf8KotlinFlatbuffers(blackhole: Blackhole) {
for (i in sampleSmallUtf8) {
val byteArray = ByteArray((i.length * 4))
blackhole.consume(Utf8.encodeUtf8Array(i, byteArray, 0, byteArray.size))
}
}
@Benchmark
fun encodeUtf8JavaFlatbuffers(blackhole: Blackhole) {
val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
for (i in sampleSmallUtf8) {
val byteBuffer = ByteBuffer.wrap(ByteArray(i.length * 4))
blackhole.consume(javaUtf8.encodeUtf8(i, byteBuffer))
}
}
@Benchmark
fun decodeUtf8KotlinStandard(blackhole: Blackhole) {
for (ary in sampleSmallUtf8Decoded) {
blackhole.consume(ary.decodeToString())
}
}
@Benchmark
fun decodeUtf8KotlinFlatbuffers(blackhole: Blackhole) {
for (ary in sampleSmallUtf8Decoded) {
blackhole.consume(Utf8.decodeUtf8Array(ary, 0, ary.size))
}
}
@Benchmark
fun decodeUtf8JavaFlatbuffers(blackhole: Blackhole) {
val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
for (ary in sampleSmallUtf8Decoded) {
val byteBuffer = ByteBuffer.wrap(ary)
blackhole.consume(javaUtf8.decodeUtf8(byteBuffer, 0, ary.size))
}
}
// ASCII TESTS
@Benchmark
fun encodeAsciiKotlinStandard(blackhole: Blackhole) {
for (i in sampleSmallAscii) {
blackhole.consume(i.encodeToByteArray())
}
}
@Benchmark
fun encodeAsciiKotlinFlatbuffers(blackhole: Blackhole) {
for (i in sampleSmallAscii) {
val byteArray = ByteArray(i.length) // Utf8.encodedLength(i))
blackhole.consume(Utf8.encodeUtf8Array(i, byteArray, 0, byteArray.size))
}
}
@Benchmark
fun encodeAsciiJavaFlatbuffers(blackhole: Blackhole) {
val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
for (i in sampleSmallAscii) {
val byteBuffer = ByteBuffer.wrap(ByteArray(i.length))
blackhole.consume(javaUtf8.encodeUtf8(i, byteBuffer))
}
}
@Benchmark
fun decodeAsciiKotlinStandard(blackhole: Blackhole) {
for (ary in sampleSmallAsciiDecoded) {
String(ary)
blackhole.consume(ary.decodeToString())
}
}
@Benchmark
fun decodeAsciiKotlinFlatbuffers(blackhole: Blackhole) {
for (ary in sampleSmallAsciiDecoded) {
blackhole.consume(Utf8.decodeUtf8Array(ary, 0, ary.size))
}
}
@Benchmark
fun decodeAsciiJavaFlatbuffers(blackhole: Blackhole) {
val javaUtf8 = com.google.flatbuffers.Utf8.getDefault()
for (ary in sampleSmallAsciiDecoded) {
val byteBuffer = ByteBuffer.wrap(ary)
blackhole.consume(javaUtf8.decodeUtf8(byteBuffer, 0, ary.size))
}
}
@Benchmark
fun readAllCharsString(blackhole: Blackhole) {
for (ary in sampleSmallAsciiDecoded) {
val key = Utf8.decodeUtf8Array(ary, 0, ary.size)
for (i in key.indices) {
blackhole.consume(key[i])
}
}
}
@Benchmark
fun readAllCharsCharSequence(blackhole: Blackhole) {
for (ary in sampleSmallAsciiDecoded) {
val key = Key(ArrayReadWriteBuffer(ary), 0, ary.size)
for (i in 0 until key.sizeInChars) {
blackhole.consume(key[i])
}
}
}
fun populateAscii(size: Int): String {
val data = ByteArray(size)
for (i in data.indices) {
data[i] = Random.nextInt(0, 127).toByte()
}
return String(data, 0, data.size)
}
// generate a string having at least length N
// can exceed by up to 3 chars, returns the actual length
fun populateUTF8(size: Int): String {
val data = ByteArray(size + 3)
var i = 0
while (i < size) {
val w = Random.nextInt() and 0xFF
when {
w < 0x80 -> data[i++] = 0x20; // w;
w < 0xE0 -> {
data[i++] = (0xC2 + Random.nextInt() % (0xDF - 0xC2 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w == 0xE0 -> {
data[i++] = w.toByte()
data[i++] = (0xA0 + Random.nextInt() % (0xBF - 0xA0 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w <= 0xEC -> {
data[i++] = w.toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w == 0xED -> {
data[i++] = w.toByte()
data[i++] = (0x80 + Random.nextInt() % (0x9F - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w <= 0xEF -> {
data[i++] = w.toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w < 0xF0 -> {
data[i++] = (0xF1 + Random.nextInt() % (0xF3 - 0xF1 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w == 0xF0 -> {
data[i++] = w.toByte()
data[i++] = (0x90 + Random.nextInt() % (0xBF - 0x90 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w <= 0xF3 -> {
data[i++] = (0xF1 + Random.nextInt() % (0xF3 - 0xF1 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
w == 0xF4 -> {
data[i++] = w.toByte()
data[i++] = (0x80 + Random.nextInt() % (0x8F - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
data[i++] = (0x80 + Random.nextInt() % (0xBF - 0x80 + 1)).toByte()
}
}
}
return String(data, 0, i)
}
}