Add naive benchmarks to compare MSTExpression, FunctionalExpressions and ASM Expressions API #108

Closed
CommanderTvis wants to merge 1 commits from adv-expr-benchmarks into adv-expr
5 changed files with 65 additions and 7 deletions

View File

@ -24,6 +24,7 @@ sourceSets {
} }
dependencies { dependencies {
implementation(project(":kmath-ast"))
implementation(project(":kmath-core")) implementation(project(":kmath-core"))
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))

View File

@ -0,0 +1,55 @@
package scientifik.kmath.ast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import scientifik.kmath.asm.compile
import scientifik.kmath.expressions.expressionInField
import scientifik.kmath.expressions.invoke
import scientifik.kmath.operations.RealField
import java.time.Duration
import java.time.Instant
import kotlin.random.Random
fun main() = runBlocking(Dispatchers.Default) {
val mstJob = async { runMst() }
val asmJob = async { runAsm() }
val feJob = async { runFE() }
println("ASM: ${asmJob.await()}")
println("FE: ${feJob.await()}")
println("MST: ${mstJob.await()}")
}
fun runFE(): Any {
val startTime = Instant.now()!!
val rand = Random(System.currentTimeMillis())
var sum = 0.0
val expr =
RealField.expressionInField { ((variable("x") * number(2.0) - 234) + 24.toByte()) * variable("x") * variable("x") }
repeat(10_000_000) { sum += expr("x" to rand.nextDouble()) }
println("asm-fe = $sum")
return Duration.between(startTime, Instant.now())
}
fun runAsm(): Any {
val startTime = Instant.now()!!
val rand = Random(System.currentTimeMillis())
var sum = 0.0
val expr = RealField.mstInField { ((symbol("x") * number(2.0) - 234) + 24.0) * symbol("x") * symbol("x") }.compile()
repeat(10_000_000) { sum += expr("x" to rand.nextDouble()) }
println("asm-sum = $sum")
return Duration.between(startTime, Instant.now())
}
fun runMst(): Any {
val startTime = Instant.now()!!
val rand = Random(System.currentTimeMillis())
var sum = 0.0
val expr = RealField.mstInField { ((symbol("x") * number(2.0) - 234) + 24.0) * symbol("x") * symbol("x") }
repeat(10_000_000) { sum += expr("x" to rand.nextDouble()) }
println("asm-mst = $sum")
return Duration.between(startTime, Instant.now())
}

View File

@ -25,7 +25,6 @@ kotlin.sourceSets {
implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3") implementation("com.github.h0tk3y.betterParse:better-parse-jvm:0.4.0-alpha-3")
implementation("org.ow2.asm:asm:8.0.1") implementation("org.ow2.asm:asm:8.0.1")
implementation("org.ow2.asm:asm-commons:8.0.1") implementation("org.ow2.asm:asm-commons:8.0.1")
implementation(kotlin("reflect"))
} }
} }

View File

@ -6,9 +6,10 @@ import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes import org.objectweb.asm.Opcodes
import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader import scientifik.kmath.asm.internal.AsmBuilder.ClassLoader
import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Algebra
import scientifik.kmath.ast.MST
/** /**
* ASM Builder is a structure that abstracts building a class that unwraps [AsmExpression] to plain Java expression. * ASM Builder is a structure that abstracts building a class that is used to unwrap [MST] to plain Java expression.
* This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class. * This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new class.
* *
* @param T the type of AsmExpression to unwrap. * @param T the type of AsmExpression to unwrap.
@ -205,7 +206,7 @@ internal class AsmBuilder<T> internal constructor(
private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar) private fun loadThis(): Unit = invokeMethodVisitor.visitLoadObjectVar(invokeThisVar)
internal fun loadNumberConstant(value: Number) { private fun loadNumberConstant(value: Number) {
val clazz = value.javaClass val clazz = value.javaClass
val c = clazz.name.replace('.', '/') val c = clazz.name.replace('.', '/')
val sigLetter = SIGNATURE_LETTERS[clazz] val sigLetter = SIGNATURE_LETTERS[clazz]
@ -280,7 +281,7 @@ internal class AsmBuilder<T> internal constructor(
internal companion object { internal companion object {
private val SIGNATURE_LETTERS: Map<Class<out Any>, String> by lazy { private val SIGNATURE_LETTERS: Map<Class<out Any>, String> by lazy {
mapOf( hashMapOf(
java.lang.Byte::class.java to "B", java.lang.Byte::class.java to "B",
java.lang.Short::class.java to "S", java.lang.Short::class.java to "S",
java.lang.Integer::class.java to "I", java.lang.Integer::class.java to "I",

View File

@ -3,12 +3,14 @@ package scientifik.kmath.asm.internal
import org.objectweb.asm.Opcodes import org.objectweb.asm.Opcodes
import scientifik.kmath.operations.Algebra import scientifik.kmath.operations.Algebra
private val methodNameAdapters: Map<String, String> = mapOf("+" to "add", "*" to "multiply", "/" to "divide") private val methodNameAdapters: Map<String, String> by lazy {
hashMapOf("+" to "add", "*" to "multiply", "/" to "divide")
}
internal fun <T> hasSpecific(context: Algebra<T>, name: String, arity: Int): Boolean { internal fun <T> hasSpecific(context: Algebra<T>, name: String, arity: Int): Boolean {
val aName = methodNameAdapters[name] ?: name val aName = methodNameAdapters[name] ?: name
context::class.java.methods.find { it.name == aName && it.parameters.size == arity } context.javaClass.methods.find { it.name == aName && it.parameters.size == arity }
?: return false ?: return false
return true return true
@ -17,7 +19,7 @@ internal fun <T> hasSpecific(context: Algebra<T>, name: String, arity: Int): Boo
internal fun <T> AsmBuilder<T>.tryInvokeSpecific(context: Algebra<T>, name: String, arity: Int): Boolean { internal fun <T> AsmBuilder<T>.tryInvokeSpecific(context: Algebra<T>, name: String, arity: Int): Boolean {
val aName = methodNameAdapters[name] ?: name val aName = methodNameAdapters[name] ?: name
context::class.java.methods.find { it.name == aName && it.parameters.size == arity } context.javaClass.methods.find { it.name == aName && it.parameters.size == arity }
?: return false ?: return false
val owner = context::class.java.name.replace('.', '/') val owner = context::class.java.name.replace('.', '/')