[Kotlin][FlexBuffers] JSON support for Flexbuffers (#6417)

* [Kotlin][FlexBuffers] Add JSON support for FlexBuffers

* [Kotlin][Flexbuffers] Re-implement JSON parser with a tokenizer.
This commit is contained in:
Paulo Pinheiro
2021-03-30 00:57:23 +02:00
committed by GitHub
parent 276b1bc342
commit 1c26d2a1a0
8 changed files with 1402 additions and 40 deletions

View File

@@ -5,6 +5,7 @@ plugins {
id("org.jetbrains.kotlin.plugin.allopen") version "1.4.20"
id("kotlinx.benchmark") version "0.2.0-dev-20"
id("io.morethan.jmhreport") version "0.9.0"
id("de.undercouch.download") version "4.1.1"
}
// allOpen plugin is needed for the benchmark annotations.
@@ -32,6 +33,8 @@ benchmark {
iterations = 5
iterationTime = 300
iterationTimeUnit = "ms"
// uncomment for benchmarking JSON op only
// include(".*JsonBenchmark.*")
}
}
targets {
@@ -76,6 +79,11 @@ kotlin {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.1")
//moshi
implementation("com.squareup.moshi:moshi-kotlin:1.11.0")
//gson
implementation("com.google.code.gson:gson:2.8.5")
}
}
@@ -88,3 +96,16 @@ kotlin {
}
}
}
// 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"))
}
project.tasks.named("compileKotlinJvm") {
dependsOn("downloadMultipleFiles")
}

View File

@@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 20, time = 1, timeUnit = TimeUnit.NANOSECONDS)
class KotlinBenchmark {
class FlexBuffersBenchmark {
var initialCapacity = 1024
var value: Double = 0.0

View File

@@ -0,0 +1,121 @@
/*
* 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)
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 twitterData = this.javaClass.classLoader.getResourceAsStream("twitter.json")!!.readBytes()
final val canadaData = this.javaClass.classLoader.getResourceAsStream("canada.json")!!.readBytes()
final val citmData = this.javaClass.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
fun readTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData))
@Benchmark
fun readTwitterMoshi(hole: Blackhole?) = hole?.consume(readMoshi(twitterData))
@Benchmark
fun readTwitterGson(hole: Blackhole?) = hole?.consume(readGson(twitterData))
@Benchmark
fun roundTripTwitterFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(twitterData).toJson())
@Benchmark
fun roundTripTwitterMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(twitterData)))
@Benchmark
fun roundTripTwitterGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(twitterData)))
// CITM
@Benchmark
fun readCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData))
@Benchmark
fun readCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
@Benchmark
fun readCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
@Benchmark
fun roundTripCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(citmData).toJson())
@Benchmark
fun roundTripCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(readMoshi(citmData)))
@Benchmark
fun roundTripCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(readGson(citmData)))
@Benchmark
fun writeCITMFlexBuffers(hole: Blackhole? = null) = hole?.consume(fbCitmRef.toJson())
@Benchmark
fun writeCITMMoshi(hole: Blackhole?) = hole?.consume(moshiAdapter.toJson(moshiCitmRef))
@Benchmark
fun writeCITMGson(hole: Blackhole?) = hole?.consume(gson.toJson(gsonCitmRef))
// CANADA
@Benchmark
fun readCanadaFlexBuffers(hole: Blackhole? = null) = hole?.consume(readFlexBuffers(canadaData))
@Benchmark
fun readCanadaMoshi(hole: Blackhole?) = hole?.consume(readMoshi(canadaData))
@Benchmark
fun readCanadaGson(hole: Blackhole?) = hole?.consume(readGson(canadaData))
}