Merge commit '0f6aab9da6fe982218a01f4a5b896e65fcced437' as 'third_party/flatbuffers'
This commit is contained in:
@@ -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;
|
||||
+129
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+199
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
+122
@@ -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))
|
||||
}
|
||||
+235
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user