Small optimization on "deserialization" and fix on benchmarks again (#7982)

* [Kotlin] Small optimizations and benchmark on deserialization

* [Kotlin] Remove redudant assign() method (use init() instead)

* [Kotlin] Fix benchmark run after change in flatbuffers-java deps

Commit 6e214c3a49 fixes Kotlin build,
but makes the kotlin-benchmark plugin misses the java classes at
runtime, causing NotClassFoundError. The alternative to solve the issue
is to read java's pom.xml to get the latest java version and use it
as dependency. With that we avoid compilation errors on a new version and
keep benchmark plugin happy.
This commit is contained in:
Paulo Pinheiro 2023-05-31 20:02:39 +02:00 committed by GitHub
parent 6e214c3a49
commit 85088a196d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 46 deletions

View File

@ -1,3 +1,5 @@
import groovy.xml.XmlParser
plugins {
kotlin("multiplatform")
id("org.jetbrains.kotlinx.benchmark")
@ -8,6 +10,18 @@ plugins {
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 {
@ -55,13 +69,13 @@ kotlin {
implementation(kotlin("stdlib-common"))
implementation(project(":flatbuffers-kotlin"))
implementation(libs.kotlinx.benchmark.runtime)
implementation("com.google.flatbuffers:flatbuffers-java:${readJavaFlatBufferVersion()}")
// 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")
}
}
}

View File

@ -5,8 +5,10 @@ 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
@ -14,6 +16,7 @@ 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)
@ -24,45 +27,103 @@ 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 monstersKotlin() {
fbKotlin.clear()
val monsterName = fbKotlin.createString("MonsterName");
val items = ubyteArrayOf(0u, 1u, 2u, 3u, 4u)
val inv = createInventoryVector(fbKotlin, items)
val monsterOffsets: MonsterOffsetArray = MonsterOffsetArray(repetition) {
Monster.startMonster(fbKotlin)
Monster.addName(fbKotlin, monsterName)
Monster.addPos(fbKotlin, Vec3.createVec3(fbKotlin, 1.0f, 2.0f, 3.0f))
Monster.addHp(fbKotlin, 80)
Monster.addMana(fbKotlin, 150)
Monster.addInventory(fbKotlin, inv)
Monster.endMonster(fbKotlin)
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))
}
val monsters = createMonstersVector(fbKotlin, monsterOffsets)
val allMonsters = createAllMonsters(fbKotlin, monsters)
fbKotlin.finish(allMonsters)
}
@Benchmark
fun monstersSerializationJava() {
populateMosterJava(fbJava)
}
@Benchmark
fun monstersjava() {
fbJava.clear()
val monsterName = fbJava.createString("MonsterName");
val inv = JMonster.createInventoryVector(fbJava, byteArrayOf(0, 1, 2, 3, 4).asUByteArray())
val monsters = JAllMonsters.createMonstersVector(fbJava, IntArray(repetition) {
JMonster.startJMonster(fbJava)
JMonster.addName(fbJava, monsterName)
JMonster.addPos(fbJava, JVec3.createJVec3(fbJava, 1.0f, 2.0f, 3.0f))
JMonster.addHp(fbJava, 80)
JMonster.addMana(fbJava, 150)
JMonster.addInventory(fbJava, inv)
JMonster.endJMonster(fbJava)
})
val allMonsters = JAllMonsters.createJAllMonsters(fbJava, monsters)
fbJava.finish(allMonsters)
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))
}
}
}

View File

@ -81,10 +81,10 @@ public open class Table {
/** Used to hold the vtable size. */
public var vtableSize: Int = 0
protected inline fun <reified T> Int.invalid(default: T, valid: (Int) -> T) : T =
protected inline fun <reified T> Int.invalid(default: T, crossinline valid: (Int) -> T) : T =
if (this != 0) valid(this) else default
protected inline fun <reified T> lookupField(i: Int, default: T, found: (Int) -> T) : T =
protected inline fun <reified T> lookupField(i: Int, default: T, crossinline found: (Int) -> T) : T =
offset(i).invalid(default) { found(it) }
/**

View File

@ -614,10 +614,6 @@ class KotlinKMPGenerator : public BaseGenerator {
// accessor object. This is to allow object reuse.
GenerateFunOneLine(writer, "init", "i: Int, buffer: ReadWriteBuffer",
esc_type, [&]() { writer += "reset(i, buffer)"; });
// Generate assign method
GenerateFunOneLine(writer, "assign", "i: Int, buffer: ReadWriteBuffer",
esc_type, [&]() { writer += "init(i, buffer)"; });
writer += ""; // line break
// Generate all getters
@ -740,7 +736,7 @@ class KotlinKMPGenerator : public BaseGenerator {
writer += "}"; // end comp < 0
writer += "else -> {";
writer.IncrementIdentLevel();
writer += "return (obj ?: {{struct_name}}()).assign(tableOffset, bb)";
writer += "return (obj ?: {{struct_name}}()).init(tableOffset, bb)";
writer.DecrementIdentLevel();
writer += "}"; // end else
writer.DecrementIdentLevel();
@ -1068,11 +1064,11 @@ class KotlinKMPGenerator : public BaseGenerator {
if (struct_def.fixed) {
// create getter with object reuse
// ex:
// fun pos(obj: Vec3) : Vec3? = obj.assign(bufferPos + 4, bb)
// fun pos(obj: Vec3) : Vec3? = obj.init(bufferPos + 4, bb)
// ? adds nullability annotation
GenerateFunOneLine(
writer, field_name, "obj: " + field_type, return_type, [&]() {
writer += "obj.assign(bufferPos + {{offset}}, bb)";
writer += "obj.init(bufferPos + {{offset}}, bb)";
});
} else {
// create getter with object reuse
@ -1080,7 +1076,7 @@ class KotlinKMPGenerator : public BaseGenerator {
// fun pos(obj: Vec3) : Vec3? {
// val o = offset(4)
// return if(o != 0) {
// obj.assign(o + bufferPos, bb)
// obj.init(o + bufferPos, bb)
// else {
// null
// }
@ -1092,7 +1088,7 @@ class KotlinKMPGenerator : public BaseGenerator {
writer.SetValue("seek", Indirect("it + bufferPos", fixed));
writer += LookupFieldOneLine(
offset_val, "obj.assign({{seek}}, bb)", "null");
offset_val, "obj.init({{seek}}, bb)", "null");
});
}
break;
@ -1142,7 +1138,7 @@ class KotlinKMPGenerator : public BaseGenerator {
case BASE_TYPE_STRUCT: {
bool fixed = vectortype.struct_def->fixed;
writer.SetValue("index", Indirect(index, fixed));
found = "obj.assign({{index}}, bb)";
found = "obj.init({{index}}, bb)";
break;
}
case BASE_TYPE_UNION:
@ -1247,7 +1243,7 @@ class KotlinKMPGenerator : public BaseGenerator {
writer, nested_method_name, "obj: " + nested_type_name,
nested_type_name + "?", [&]() {
writer += LookupFieldOneLine(
offset_val, "obj.assign(indirect(vector(it)), bb)", "null");
offset_val, "obj.init(indirect(vector(it)), bb)", "null");
});
}
@ -1348,7 +1344,7 @@ class KotlinKMPGenerator : public BaseGenerator {
writer, "asRoot", "buffer: ReadWriteBuffer, obj: {{gr_name}}",
struct_name, [&]() {
writer +=
"obj.assign(buffer.getInt(buffer.limit) + buffer.limit, buffer)";
"obj.init(buffer.getInt(buffer.limit) + buffer.limit, buffer)";
});
}