Skip to content

Commit

Permalink
Add benchmarking setup for geojson (#86)
Browse files Browse the repository at this point in the history
* Add benchmarking setup for geojson

* Update detekt configuration

Add more detail to BENCHMARK.md
  • Loading branch information
dellisd authored Apr 15, 2022
1 parent 38bc19c commit 827147c
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 40 deletions.
28 changes: 28 additions & 0 deletions BENCHMARK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Benchmarks

Benchmarks are set up for GeoJSON serialization and deserialization.

## Running Benchmarks

```shell
./gradlew :geojson:benchmark
```

This will run benchmarks on the JVM, NodeJS, and Kotlin/Native.

These benchmarks measure the time taken to serialize and deserialize a `FeatureCollection` containing 15,000 randomly
generated features.
See [GeoJsonBenchmark.kt](geojson/src/commonBench/kotlin/io/github/dellisd/spatialk/geojson/GeoJsonBenchmark.kt) for
details.

## Results

All measurements are in ms/op (milliseconds per operation). Lower score is better.

| Target | Serialization | Deserialization |
|---------------------|-----------------------|---------------------|
| JVM | `834.176 ± 55.719` | `77.700 ± 1.123` |
| JS | `3,287.40 ± 8.763` | `423.472 ± 17.805` |
| Native (`linuxX64`) | `5624.209 ± 1242.046` | `993.008 ± 243.719` |

_Run on Ubuntu 20.04 (WSL2). 32GB RAM, 3.6 GHz 8-core Intel Core i7_
13 changes: 9 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ allprojects {

detekt {
buildUponDefaultConfig = true
reports {
html.enabled = true
}

input = files(rootProject.projectDir)
source = files(
project(":geojson").projectDir.resolve("src"),
project(":turf").projectDir.resolve("src")
)
}


tasks.withType<Detekt> {
buildUponDefaultConfig = true
jvmTarget = "11"
reports {
html.required.set(true)
}
}

tasks.dokkaGfmMultiModule.configure {
Expand Down
107 changes: 73 additions & 34 deletions geojson/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,32 @@ plugins {
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.dokka)
alias(libs.plugins.publish)
alias(libs.plugins.kotlinx.benchmark)
}

kotlin {
jvm()
jvm {
compilations.create("bench")
}
js {
browser {
}
nodejs {
}

compilations.create("bench")
}
// For ARM, should be changed to iosArm32 or iosArm64
// For Linux, should be changed to e.g. linuxX64
// For MacOS, should be changed to e.g. macosX64
// For Windows, should be changed to e.g. mingwX64
linuxX64("native")
linuxX64("native") {
compilations.create("bench")
}
mingwX64("mingw")
macosX64("macos")
ios("ios")

sourceSets["commonMain"].dependencies {
api(libs.kotlinx.serialization)
}

sourceSets["commonTest"].dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-annotations-common"))
}

sourceSets["jvmMain"].dependencies {
}

sourceSets["jvmTest"].dependencies {
}

sourceSets["jsMain"].dependencies {
}

sourceSets["jsTest"].dependencies {
}

sourceSets["nativeMain"].dependencies {}
sourceSets["nativeTest"].dependencies {}

sourceSets {
all {
with(languageSettings) {
Expand All @@ -56,15 +39,71 @@ kotlin {
}
}

val nativeMain by getting {}
getByName("macosMain").dependsOn(nativeMain)
getByName("iosMain").dependsOn(nativeMain)
getByName("mingwMain").dependsOn(nativeMain)
val commonMain by getting {
dependencies {
api(libs.kotlinx.serialization)
}
}

val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-annotations-common"))
}
}

val jsMain by getting {}

val jvmMain by getting {}

val nativeMain by getting {
getByName("macosMain").dependsOn(this)
getByName("iosMain").dependsOn(this)
getByName("mingwMain").dependsOn(this)
}

val nativeTest by getting {
getByName("macosTest").dependsOn(this)
getByName("iosTest").dependsOn(this)
getByName("mingwTest").dependsOn(this)
}

val commonBench by creating {
dependsOn(commonMain)
dependencies {
implementation(libs.kotlinx.benchmark)
}
}

val jsBench by getting {
dependsOn(commonBench)
dependsOn(jsMain)
}

val jvmBench by getting {
dependsOn(commonBench)
dependsOn(jvmMain)
}

val nativeBench by getting {
dependsOn(commonBench)
dependsOn(nativeMain)
}
}
}

benchmark {
this.configurations {
getByName("main") {
iterations = 5
}
}

val nativeTest by getting {}
getByName("macosTest").dependsOn(nativeTest)
getByName("iosTest").dependsOn(nativeTest)
getByName("mingwTest").dependsOn(nativeTest)
targets {
register("jvmBench")
// Broken on 1.6.20???
// register("jsBench")
register("nativeBench")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@file:Suppress("MagicNumber")

package io.github.dellisd.spatialk.geojson

import io.github.dellisd.spatialk.geojson.FeatureCollection.Companion.toFeatureCollection
import io.github.dellisd.spatialk.geojson.dsl.feature
import io.github.dellisd.spatialk.geojson.dsl.featureCollection
import io.github.dellisd.spatialk.geojson.dsl.lineString
import io.github.dellisd.spatialk.geojson.dsl.point
import io.github.dellisd.spatialk.geojson.dsl.polygon
import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.BenchmarkMode
import kotlinx.benchmark.BenchmarkTimeUnit
import kotlinx.benchmark.Mode
import kotlinx.benchmark.OutputTimeUnit
import kotlinx.benchmark.Scope
import kotlinx.benchmark.Setup
import kotlinx.benchmark.State
import kotlin.random.Random

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(BenchmarkTimeUnit.MILLISECONDS)
open class GeoJsonBenchmark {
private lateinit var dataset: FeatureCollection
private lateinit var geojson: String

private fun generateDataset(): FeatureCollection {
val random = Random(0)
return featureCollection {
repeat(5000) {
+feature {
geometry = point(random.nextDouble(360.0) - 180, random.nextDouble(360.0) - 180)
}
}

repeat(5000) {
+feature {
geometry = lineString {
repeat(10) {
+Position(random.nextDouble(360.0) - 180, random.nextDouble(360.0) - 180)
}
}
}
}

repeat(5000) {
+feature {
geometry = polygon {
ring {
repeat(10) {
+Position(random.nextDouble(360.0) - 180, random.nextDouble(360.0) - 180)
}
}
}
}
}
}
}

@Setup
fun setup() {
dataset = generateDataset()
geojson = dataset.json
}

@Benchmark
fun serialization() {
dataset.json
}

@Benchmark
fun deserialization() {
geojson.toFeatureCollection()
}
}
5 changes: 4 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
[versions]
kotlin = "1.6.20"
resources = "0.2.1"
benchmark = "0.4.2"

[libraries]
resources = { module = "com.goncalossilva:resources", version.ref = "resources" }
kotlinx-serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
kotlinx-benchmark = { module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "benchmark" }

[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

kotlinx-benchmark = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "benchmark" }
publish = { id = "com.vanniktech.maven.publish", version = "0.19.0" }
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.17.0" }
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.20.0" }
dokka = { id = "org.jetbrains.dokka", version = "1.4.32" }
resources = { id = "com.goncalossilva.resources", version.ref = "resources" }
33 changes: 32 additions & 1 deletion kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ [email protected], base64id@~2.0.0:
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==

benchmark@*:
version "2.1.4"
resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629"
integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik=
dependencies:
lodash "^4.17.4"
platform "^1.3.3"

binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
Expand Down Expand Up @@ -582,6 +590,14 @@ dom-serialize@^2.2.1:
extend "^3.0.0"
void-elements "^2.0.0"

[email protected]:
version "0.5.8-rc.4"
resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21"
integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA==
dependencies:
google-protobuf "3.12.2"
typescript "3.9.5"

[email protected]:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
Expand Down Expand Up @@ -865,6 +881,11 @@ glob@^7.1.3, glob@^7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"

[email protected]:
version "3.12.2"
resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53"
integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA==

graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
Expand Down Expand Up @@ -1153,7 +1174,7 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"

lodash@^4.17.15, lodash@^4.17.21:
lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
Expand Down Expand Up @@ -1418,6 +1439,11 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"

platform@^1.3.3:
version "1.3.6"
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==

punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
Expand Down Expand Up @@ -1764,6 +1790,11 @@ type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"

[email protected]:
version "3.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==

ua-parser-js@^0.7.28:
version "0.7.31"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
Expand Down

0 comments on commit 827147c

Please sign in to comment.