diff --git a/benchmarks/README.md b/benchmarks/README.md
index cd8fbafd3..1535d55e6 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -1,4 +1,119 @@
-# Module benchmarks
+# BenchmarksResult
+
+## Report for benchmark configuration main
+
+* Run on Java HotSpot(TM) 64-Bit Server VM (build 21.0.4+8-LTS-jvmci-23.1-b41) with Java process:
+
+```
+C:\Users\altavir\scoop\apps\graalvm-oracle-21jdk\current\bin\java.exe -XX:ThreadPriorityPolicy=1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCIProduct -XX:-UnlockExperimentalVMOptions -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant
+```
+* JMH 1.21 was used in `thrpt` mode with 5 warmup iterations by 10 s and 5 measurement iterations by 10 s.
+### [ArrayBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ArrayBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`benchmarkArrayRead`|1.9E+07 ± 2.3E+05 ops/s|
+|`benchmarkBufferRead`|1.4E+07 ± 8.7E+05 ops/s|
+|`nativeBufferRead`|1.4E+07 ± 1.3E+06 ops/s|
+### [BigIntBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`jvmAdd`|5.1E+07 ± 1.3E+06 ops/s|
+|`jvmAddLarge`|5.1E+04 ± 8.2E+02 ops/s|
+|`jvmMultiply`|8.5E+07 ± 9.7E+06 ops/s|
+|`jvmMultiplyLarge`|2.5E+02 ± 15 ops/s|
+|`jvmParsing10`|8.7E+06 ± 5.1E+05 ops/s|
+|`jvmParsing16`|6.4E+06 ± 1.8E+05 ops/s|
+|`jvmPower`|28 ± 0.79 ops/s|
+|`jvmSmallAdd`|7.0E+07 ± 4.3E+06 ops/s|
+|`kmAdd`|4.8E+07 ± 2.2E+06 ops/s|
+|`kmAddLarge`|3.5E+04 ± 3.7E+03 ops/s|
+|`kmMultiply`|6.7E+07 ± 1.5E+07 ops/s|
+|`kmMultiplyLarge`|54 ± 4.2 ops/s|
+|`kmParsing10`|4.5E+06 ± 8.3E+04 ops/s|
+|`kmParsing16`|4.9E+06 ± 1.1E+05 ops/s|
+|`kmPower`|10 ± 0.96 ops/s|
+|`kmSmallAdd`|4.1E+07 ± 5.9E+05 ops/s|
+### [BufferBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`bufferViewReadWrite`|5.8E+06 ± 1.6E+05 ops/s|
+|`bufferViewReadWriteSpecialized`|5.6E+06 ± 2.6E+05 ops/s|
+|`complexBufferReadWrite`|6.6E+06 ± 2.7E+05 ops/s|
+|`doubleArrayReadWrite`|7.5E+06 ± 1.0E+06 ops/s|
+|`doubleBufferReadWrite`|8.0E+06 ± 6.7E+05 ops/s|
+### [DotBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`bufferedDot`|1.3 ± 0.020 ops/s|
+|`cmDot`|0.47 ± 0.42 ops/s|
+|`cmDotWithConversion`|0.76 ± 0.13 ops/s|
+|`ejmlDot`|6.7 ± 0.091 ops/s|
+|`ejmlDotWithConversion`|6.4 ± 0.82 ops/s|
+|`multikDot`|40 ± 6.7 ops/s|
+|`parallelDot`|12 ± 1.8 ops/s|
+|`tensorDot`|1.2 ± 0.041 ops/s|
+|`tfDot`|5.9 ± 0.49 ops/s|
+### [ExpressionsInterpretersBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`asmGenericExpression`|29 ± 1.2 ops/s|
+|`asmPrimitiveExpression`|43 ± 1.3 ops/s|
+|`asmPrimitiveExpressionArray`|71 ± 0.38 ops/s|
+|`functionalExpression`|5.6 ± 0.11 ops/s|
+|`justCalculate`|69 ± 9.0 ops/s|
+|`mstExpression`|7.1 ± 0.020 ops/s|
+|`rawExpression`|41 ± 1.5 ops/s|
+### [IntegrationBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/IntegrationBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`complexIntegration`|3.6E+03 ± 1.9E+02 ops/s|
+|`doubleIntegration`|3.7E+03 ± 12 ops/s|
+### [JafamaBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`core`|38 ± 0.64 ops/s|
+|`jafama`|52 ± 0.36 ops/s|
+|`strictJafama`|52 ± 4.0 ops/s|
+### [MatrixInverseBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`cmLUPInversion`|2.2E+03 ± 76 ops/s|
+|`ejmlInverse`|1.3E+03 ± 5.7 ops/s|
+|`kmathLupInversion`|9.5E+02 ± 1.8E+02 ops/s|
+|`kmathParallelLupInversion`|9.1E+02 ± 1.4E+02 ops/s|
+### [NDFieldBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`boxingFieldAdd`|7.7 ± 0.79 ops/s|
+|`multikAdd`|6.5 ± 0.33 ops/s|
+|`multikInPlaceAdd`|64 ± 0.79 ops/s|
+|`specializedFieldAdd`|8.0 ± 0.090 ops/s|
+|`tensorAdd`|9.2 ± 0.053 ops/s|
+|`tensorInPlaceAdd`|17 ± 10 ops/s|
+|`viktorAdd`|7.6 ± 1.2 ops/s|
+### [ViktorBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`doubleFieldAddition`|7.7 ± 0.34 ops/s|
+|`rawViktor`|5.9 ± 1.1 ops/s|
+|`viktorFieldAddition`|7.3 ± 1.1 ops/s|
+### [ViktorLogBenchmark](src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt)
+
+| Benchmark | Score |
+|:---------:|:-----:|
+|`rawViktorLog`|1.4 ± 0.076 ops/s|
+|`realFieldLog`|1.3 ± 0.069 ops/s|
+|`viktorFieldLog`|1.3 ± 0.032 ops/s|
diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts
index ccbb99927..2bdab89fb 100644
--- a/benchmarks/build.gradle.kts
+++ b/benchmarks/build.gradle.kts
@@ -3,10 +3,6 @@ import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.benchmark.gradle.BenchmarksExtension
import java.time.LocalDateTime
import java.time.ZoneId
-import java.time.format.DateTimeFormatter
-import java.time.format.DateTimeFormatterBuilder
-import java.time.format.SignStyle
-import java.time.temporal.ChronoField.*
import java.util.*
plugins {
@@ -220,41 +216,20 @@ readme {
val jsonMapper = jacksonObjectMapper()
-
- val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run {
- parseCaseInsensitive()
- appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
- appendLiteral('-')
- appendValue(MONTH_OF_YEAR, 2)
- appendLiteral('-')
- appendValue(DAY_OF_MONTH, 2)
- appendLiteral('T')
- appendValue(HOUR_OF_DAY, 2)
- appendLiteral('.')
- appendValue(MINUTE_OF_HOUR, 2)
- optionalStart()
- appendLiteral('.')
- appendValue(SECOND_OF_MINUTE, 2)
- optionalStart()
- appendFraction(NANO_OF_SECOND, 0, 9, true)
- optionalStart()
- appendOffsetId()
- optionalStart()
- appendLiteral('[')
- parseCaseSensitive()
- appendZoneRegionId()
- appendLiteral(']')
- toFormatter()
- }
-
fun noun(number: Number, singular: String, plural: String) = if (number.toLong() == 1L) singular else plural
extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg ->
- property("benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}") {
- val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get()
+ val propertyName =
+ "benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}"
- val resDirectory = launches.files().maxByOrNull {
- LocalDateTime.parse(it.name, ISO_DATE_TIME).atZone(ZoneId.systemDefault()).toInstant()
+ logger.info("Processing benchmark data from benchmark ${cfg.name} into readme property $propertyName")
+
+ val launches = layout.buildDirectory.dir("reports/benchmarks/${cfg.name}").get().asFile
+ if (!launches.exists()) return@forEach
+
+ property(propertyName) {
+ val resDirectory = launches.listFiles()?.maxByOrNull {
+ LocalDateTime.parse(it.name).atZone(ZoneId.systemDefault()).toInstant()
}
if (resDirectory == null || !(resDirectory.resolve("jvm.json")).exists()) {
@@ -264,10 +239,7 @@ readme {
jsonMapper.readValue>(resDirectory.resolve("jvm.json"))
buildString {
- appendLine("")
- appendLine("")
- appendLine("Report for benchmark configuration ${cfg.name}
")
- appendLine("
")
+ appendLine("## Report for benchmark configuration ${cfg.name}
")
appendLine()
val first = reports.first()
@@ -289,15 +261,19 @@ readme {
} by ${first.measurementTime}."
)
- appendLine()
- appendLine("| Benchmark | Score |")
- appendLine("|:---------:|:-----:|")
+ reports.groupBy { it.benchmark.substringBeforeLast(".") }.forEach { (cl, compare) ->
+ appendLine("### [${cl.substringAfterLast(".")}](src/jvmMain/kotlin/${cl.replace(".","/")}.kt)")
+ appendLine()
+ appendLine("| Benchmark | Score |")
+ appendLine("|:---------:|:-----:|")
+ compare.forEach { report ->
+ val benchmarkName = report.benchmark.substringAfterLast(".")
+ val score = String.format("%.2G", report.primaryMetric.score)
+ val error = String.format("%.2G", report.primaryMetric.scoreError)
- reports.forEach { report ->
- appendLine("|`${report.benchmark}`|${report.primaryMetric.score} ± ${report.primaryMetric.scoreError} ${report.primaryMetric.scoreUnit}|")
+ appendLine("|`$benchmarkName`|$score ± $error ${report.primaryMetric.scoreUnit}|")
+ }
}
-
- appendLine(" ")
}
}
}
diff --git a/benchmarks/docs/README-TEMPLATE.md b/benchmarks/docs/README-TEMPLATE.md
new file mode 100644
index 000000000..eaac18cd8
--- /dev/null
+++ b/benchmarks/docs/README-TEMPLATE.md
@@ -0,0 +1,5 @@
+# BenchmarksResult
+
+${benchmarkMain}
+
+
diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt
index 89a1cdc28..062057ae7 100644
--- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt
+++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt
@@ -36,6 +36,26 @@ private suspend fun runKMathChained(): Duration {
return Duration.between(startTime, Instant.now())
}
+private fun runKMathBlocking(): Duration {
+ val generator = RandomGenerator.fromSource(RandomSource.MT, 123L)
+ val normal = GaussianSampler(7.0, 2.0)
+ val chain = normal.sample(generator)
+ val startTime = Instant.now()
+ var sum = 0.0
+
+ repeat(10000001) { counter ->
+ sum += chain.nextBlocking()
+
+ if (counter % 100000 == 0) {
+ val duration = Duration.between(startTime, Instant.now())
+ val meanValue = sum / counter
+ println("Chain sampler completed $counter elements in $duration: $meanValue")
+ }
+ }
+
+ return Duration.between(startTime, Instant.now())
+}
+
private fun runCMDirect(): Duration {
val rng = RandomSource.MT.create(123L)
@@ -67,6 +87,8 @@ private fun runCMDirect(): Duration {
fun main(): Unit = runBlocking(Dispatchers.Default) {
val directJob = async { runCMDirect() }
val chainJob = async { runKMathChained() }
+ val blockingJob = async { runKMathBlocking() }
println("KMath Chained: ${chainJob.await()}")
+ println("KMath Blocking: ${blockingJob.await()}")
println("Apache Direct: ${directJob.await()}")
}
diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt
index 18e1edb56..0b3ac56fd 100644
--- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt
+++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt
@@ -67,13 +67,14 @@ public fun > MatrixBuilder.symmetric(
): Matrix {
require(columns == rows) { "In order to build symmetric matrix, number of rows $rows should be equal to number of columns $columns" }
return with(BufferAccessor2D(rows, rows, MutableBufferFactory(type))) {
- val cache = factory(rows * rows) { null }
+ val cache = HashMap()
linearSpace.buildMatrix(rows, rows) { i, j ->
- val cached = cache[i, j]
+ val index = intArrayOf(i, j)
+ val cached = cache[index]
if (cached == null) {
val value = if (i <= j) builder(i, j) else builder(j, i)
- cache[i, j] = value
- cache[j, i] = value
+ cache[index] = value
+ cache[index] = value
value
} else {
cached
diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingSampler.kt
similarity index 100%
rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingEvent.kt
rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/RandomForkingSampler.kt