Make JS benchmarks of expressions #451

Merged
CommanderTvis merged 2 commits from commandertvis/js-benchmark into dev 2022-01-26 17:44:00 +03:00
5 changed files with 2183 additions and 121 deletions
Showing only changes of commit d10815020d - Show all commits

View File

@ -13,13 +13,22 @@ sourceSets.register("benchmarks")
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://repo.kotlin.link")
} }
kotlin { kotlin {
jvm() jvm()
js(IR) {
nodejs()
}
sourceSets { sourceSets {
all {
languageSettings {
progressiveMode = true
}
}
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation(project(":kmath-ast")) implementation(project(":kmath-ast"))
@ -29,9 +38,8 @@ kotlin {
implementation(project(":kmath-stat")) implementation(project(":kmath-stat"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
implementation(project(":kmath-jafama"))
implementation(project(":kmath-tensors")) implementation(project(":kmath-tensors"))
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1") implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
} }
} }
@ -42,6 +50,7 @@ kotlin {
implementation(project(":kmath-nd4j")) implementation(project(":kmath-nd4j"))
implementation(project(":kmath-kotlingrad")) implementation(project(":kmath-kotlingrad"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation(project(":kmath-jafama"))
implementation(project(":kmath-multik")) implementation(project(":kmath-multik"))
implementation("org.nd4j:nd4j-native:1.0.0-M1") implementation("org.nd4j:nd4j-native:1.0.0-M1")
// uncomment if your system supports AVX2 // uncomment if your system supports AVX2
@ -63,6 +72,7 @@ benchmark {
// Setup configurations // Setup configurations
targets { targets {
register("jvm") register("jvm")
register("js")
} }
fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() { fun kotlinx.benchmark.gradle.BenchmarkConfiguration.commonConfiguration() {
@ -88,7 +98,11 @@ benchmark {
} }
configurations.register("expressions") { configurations.register("expressions") {
commonConfiguration() // Some extra precision
warmups = 2
iterations = 10
iterationTime = 2000
iterationTimeUnit = "ms"
include("ExpressionsInterpretersBenchmark") include("ExpressionsInterpretersBenchmark")
} }
@ -125,7 +139,6 @@ afterEvaluate {
} }
} }
kotlin.sourceSets.all { kotlin.sourceSets.all {
with(languageSettings) { with(languageSettings) {
optIn("kotlin.contracts.ExperimentalContracts") optIn("kotlin.contracts.ExperimentalContracts")
@ -141,7 +154,6 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
} }
} }
readme { readme {
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
} }

View File

@ -0,0 +1,105 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.Algebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke
import kotlin.math.sin
import kotlin.random.Random
import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression
import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression
@State(Scope.Benchmark)
class ExpressionsInterpretersBenchmark {
/**
* Benchmark case for [Expression] created with [expressionInExtendedField].
*/
@Benchmark
fun functionalExpression(blackhole: Blackhole) = invokeAndSum(functional, blackhole)
/**
* Benchmark case for [Expression] created with [toExpression].
*/
@Benchmark
fun mstExpression(blackhole: Blackhole) = invokeAndSum(mst, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark
fun wasmExpression(blackhole: Blackhole) = invokeAndSum(wasm, blackhole)
/**
* Benchmark case for [Expression] created with [compileToExpression].
*/
@Benchmark
fun estreeExpression(blackhole: Blackhole) = invokeAndSum(estree, blackhole)
/**
* Benchmark case for [Expression] implemented manually with `kotlin.math` functions.
*/
@Benchmark
fun rawExpression(blackhole: Blackhole) = invokeAndSum(raw, blackhole)
/**
* Benchmark case for direct computation w/o [Expression].
*/
@Benchmark
fun justCalculate(blackhole: Blackhole) {
val random = Random(0)
var sum = 0.0
repeat(times) {
val x = random.nextDouble()
sum += x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
blackhole.consume(sum)
}
private fun invokeAndSum(expr: Expression<Double>, blackhole: Blackhole) {
val random = Random(0)
var sum = 0.0
val m = HashMap<Symbol, Double>()
repeat(times) {
m[x] = random.nextDouble()
sum += expr(m)
}
blackhole.consume(sum)
}
private companion object {
private val x by symbol
private const val times = 1_000_000
private val functional = DoubleField.expression {
val x = bindSymbol(Symbol.x)
x * number(2.0) + 2.0 / x - 16.0 / sin(x)
}
private val node = MstExtendedField {
x * 2.0 + number(2.0) / x - number(16.0) / sin(x)
}
private val mst = node.toExpression(DoubleField)
private val wasm = node.wasmCompileToExpression(DoubleField)
private val estree = node.estreeCompileToExpression(DoubleField)
private val raw = Expression<Double> { args ->
val x = args[x]!!
x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
}
}

View File

@ -14,7 +14,7 @@ repositories {
val toolsVersion: String by extra val toolsVersion: String by extra
val kotlinVersion = npmlibs.versions.kotlin.asProvider().get() val kotlinVersion = npmlibs.versions.kotlin.asProvider().get()
val benchmarksVersion = npmlibs.versions.kotlinx.benchmark.get() val benchmarksVersion = "0.4.2"
dependencies { dependencies {
api("ru.mipt.npm:gradle-tools:$toolsVersion") api("ru.mipt.npm:gradle-tools:$toolsVersion")

View File

@ -1,114 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.ast
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.bindSymbol
import space.kscience.kmath.operations.invoke
import kotlin.math.sin
import kotlin.random.Random
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.time.measureTime
import space.kscience.kmath.estree.compileToExpression as estreeCompileToExpression
import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression
// TODO move to benchmarks when https://github.com/Kotlin/kotlinx-benchmark/pull/38 or similar feature is merged
@Ignore
internal class TestExecutionTime {
private companion object {
private const val times = 1_000_000
private val x by symbol
private val algebra = DoubleField
private val functional = algebra.expressionInExtendedField {
bindSymbol(x) * const(2.0) + const(2.0) / bindSymbol(x) - const(16.0) / sin(bindSymbol(x))
}
private val node = MstExtendedField {
x * number(2.0) + number(2.0) / x - number(16.0) / sin(x)
}
private val mst = node.toExpression(algebra)
private val wasm = node.wasmCompileToExpression(algebra)
private val estree = node.estreeCompileToExpression(algebra)
// In JavaScript, the expression below is implemented like
// _no_name_provided__125.prototype.invoke_178 = function (args) {
// var tmp = getValue(args, raw$_get_x__3(this._$x$delegate_2)) * 2.0 + 2.0 / getValue(args, raw$_get_x__3(this._$x$delegate_2));
// var tmp0_sin_0_5 = getValue(args, raw$_get_x__3(this._$x$delegate_2));
// return tmp - 16.0 / Math.sin(tmp0_sin_0_5);
// };
private val raw = Expression<Double> { args ->
val x = args[x]!!
algebra { x * 2.0 + 2.0 / x - 16.0 / sin(x) }
}
private val justCalculate = { args: dynamic ->
val x = args[x].unsafeCast<Double>()
x * 2.0 + 2.0 / x - 16.0 / sin(x)
}
}
private fun invokeAndSum(name: String, expr: Expression<Double>) {
println(name)
val rng = Random(0)
var sum = 0.0
measureTime {
repeat(times) { sum += expr(x to rng.nextDouble()) }
}.also(::println)
}
/**
* [Expression] created with [expressionInExtendedField].
*/
@Test
fun functionalExpression() = invokeAndSum("functional", functional)
/**
* [Expression] created with [mstExpression].
*/
@Test
fun mstExpression() = invokeAndSum("mst", mst)
/**
* [Expression] created with [wasmCompileToExpression].
*/
@Test
fun wasmExpression() = invokeAndSum("wasm", wasm)
/**
* [Expression] created with [estreeCompileToExpression].
*/
@Test
fun estreeExpression() = invokeAndSum("estree", wasm)
/**
* [Expression] implemented manually with `kotlin.math`.
*/
@Test
fun rawExpression() = invokeAndSum("raw", raw)
/**
* Direct computation w/o [Expression].
*/
@Test
fun justCalculateExpression() {
println("justCalculate")
val rng = Random(0)
var sum = 0.0
measureTime {
repeat(times) {
val arg = rng.nextDouble()
val o = js("{}")
o["x"] = arg
sum += justCalculate(o)
}
}.also(::println)
}
}

2059
kotlin-js-store/yarn.lock Normal file

File diff suppressed because it is too large Load Diff