diff --git a/README.md b/README.md
index 3ba5d201..b5264911 100644
--- a/README.md
+++ b/README.md
@@ -1,273 +1,438 @@
+# kotlinx-benchmark
+
[![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html)
[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)
-[![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxBenchmark_Build_All)
+[![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxBenchmark_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxBenchmark_Build_All)
[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-benchmark-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.jetbrains.kotlinx%22%20AND%20a:%22kotlinx-benchmark-runtime%22)
[![Gradle Plugin Portal](https://img.shields.io/maven-metadata/v?label=Gradle%20Plugin&metadataUrl=https://plugins.gradle.org/m2/org/jetbrains/kotlinx/kotlinx-benchmark-plugin/maven-metadata.xml)](https://plugins.gradle.org/plugin/org.jetbrains.kotlinx.benchmark)
-[![IR](https://img.shields.io/badge/Kotlin%2FJS-IR%20supported-yellow)](https://kotl.in/jsirsupported)
+kotlinx-benchmark is a toolkit for running benchmarks for multiplatform code written in Kotlin.
-> **_NOTE:_** Starting from version 0.3.0 of the library:
-> * The library runtime is published to Maven Central and no longer published to Bintray.
-> * The Gradle plugin is published to Gradle Plugin Portal
-> * The Gradle plugin id has changed to `org.jetbrains.kotlinx.benchmark`
-> * The library runtime artifact id has changed to `kotlinx-benchmark-runtime`
+## Features
+- Low noise and reliable results
+- Statistical analysis
+- Detailed performance reports
-**kotlinx.benchmark** is a toolkit for running benchmarks for multiplatform code written in Kotlin
-and running on the following supported targets: JVM, JavaScript and Native.
+## Table of contents
-Both Legacy and IR backends are supported for JS, however `kotlin.js.compiler=both` or `js(BOTH)` target declaration won't work.
-You should declare each targeted backend separately. See build script of the [kotlin-multiplatform example project](https://github.com/Kotlin/kotlinx-benchmark/tree/master/examples/kotlin-multiplatform).
+
-On JVM [JMH](https://openjdk.java.net/projects/code-tools/jmh/) is used under the hoods to run benchmarks.
-This library has a very similar way of defining benchmark methods. Thus, using this library you can run your JMH-based
-Kotlin/JVM benchmarks on other platforms with minimum modifications, if any at all.
+- [Using in Your Projects](#using-in-your-projects)
+ - [Project Setup](#project-setup)
+ - [Target-specific configurations](#target-specific-configurations)
+ - [Kotlin/JVM](#kotlinjvm)
+ - [Kotlin/JS](#kotlinjs)
+ - [Kotlin/Native](#kotlinnative)
+ - [Kotlin/Wasm](#kotlinwasm)
+ - [Writing Benchmarks](#writing-benchmarks)
+ - [Running Benchmarks](#running-benchmarks)
+ - [Benchmark Configuration Profiles](#benchmark-configuration-profiles)
+ - [Separate source sets for benchmarks](#separate-source-sets-for-benchmarks)
+- [Examples](#examples)
+- [Contributing](#contributing)
-# Requirements
+
-Gradle 7.0 or newer
+- **Useful guides**
+ - [Writing Benchmarks](docs/writing-benchmarks.md)
+ - [Configuring Benchmarks Execution](docs/configuration-options.md)
+ - [Setting Up Kotlin/JVM and Java Projects for Benchmarking](docs/kotlin-jvm-project-setup.md)
+ - [Setting Up a Separate Source Set for Benchmarks](docs/separate-benchmark-source-set.md)
+ - [Overview of Tasks Provided by kotlinx-benchmark Gradle Plugin](docs/tasks-overview.md)
-Kotlin 1.7.20 or newer
+## Using in Your Projects
-# Gradle plugin
+The `kotlinx-benchmark` library is designed to work with Kotlin/JVM, Kotlin/JS, Kotlin/Native, and Kotlin/Wasm (experimental) targets.
+To get started, ensure you're using Kotlin 1.9.20 or newer and Gradle 7.4 or newer.
-Use plugin in `build.gradle`:
+### Project Setup
-```groovy
-plugins {
- id 'org.jetbrains.kotlinx.benchmark' version '0.4.4'
-}
-```
+Follow the steps below to set up a Kotlin Multiplatform project for benchmarking.
-For Kotlin/JS specify building `nodejs` flavour:
+
+Kotlin DSL
-```groovy
-kotlin {
- js {
- nodejs()
- …
- }
-}
-```
+1. **Applying Benchmark Plugin**: Apply the benchmark plugin.
-For Kotlin/JVM code, add `allopen` plugin to make JMH happy. Alternatively, make all benchmark classes and methods `open`.
+ ```kotlin
+ // build.gradle.kts
+ plugins {
+ id("org.jetbrains.kotlinx.benchmark") version "0.4.10"
+ }
+ ```
-For example, if you annotated each of your benchmark classes with `@State(Scope.Benchmark)`:
-```kotlin
-@State(Scope.Benchmark)
-class Benchmark {
- …
-}
-```
-and added the following code to your `build.gradle`:
-```groovy
-plugins {
- id 'org.jetbrains.kotlin.plugin.allopen'
-}
+2. **Specifying Plugin Repository**: Ensure you have the Gradle Plugin Portal for plugin lookup in the list of repositories:
-allOpen {
- annotation("org.openjdk.jmh.annotations.State")
-}
-```
-then you don't have to make benchmark classes and methods `open`.
+ ```kotlin
+ // settings.gradle.kts
+ pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ }
+ }
+ ```
+
+3. **Adding Runtime Dependency**: Next, add the `kotlinx-benchmark-runtime` dependency to the common source set:
+
+ ```kotlin
+ // build.gradle.kts
+ kotlin {
+ sourceSets {
+ commonMain {
+ dependencies {
+ implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10")
+ }
+ }
+ }
+ }
+ ```
-# Runtime Library
+4. **Specifying Runtime Repository**: Ensure you have `mavenCentral()` for dependencies lookup in the list of repositories:
-You need a runtime library with annotations and code that will run benchmarks.
+ ```kotlin
+ // build.gradle.kts
+ repositories {
+ mavenCentral()
+ }
+ ```
-Enable Maven Central for dependencies lookup:
-```groovy
-repositories {
- mavenCentral()
-}
-```
+
-Add the runtime to dependencies of the platform source set, e.g.:
-```
-kotlin {
- sourceSets {
- commonMain {
- dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.4")
- }
+
+Groovy DSL
+
+1. **Applying Benchmark Plugin**: Apply the benchmark plugin.
+
+ ```groovy
+ // build.gradle
+ plugins {
+ id 'org.jetbrains.kotlinx.benchmark' version '0.4.10'
+ }
+ ```
+
+2. **Specifying Plugin Repository**: Ensure you have the Gradle Plugin Portal for plugin lookup in the list of repositories:
+
+ ```groovy
+ // settings.gradle
+ pluginManagement {
+ repositories {
+ gradlePluginPortal()
}
}
-}
-```
+ ```
+
+3. **Adding Runtime Dependency**: Next, add the `kotlinx-benchmark-runtime` dependency to the common source set:
+
+ ```groovy
+ // build.gradle
+ kotlin {
+ sourceSets {
+ commonMain {
+ dependencies {
+ implementation 'org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10'
+ }
+ }
+ }
+ }
+ ```
-# Configuration
+4. **Specifying Runtime Repository**: Ensure you have `mavenCentral()` for dependencies lookup in the list of repositories:
-In a `build.gradle` file create `benchmark` section, and inside it add a `targets` section.
-In this section register all compilations you want to run benchmarks for.
-`register` should either be called on the name of a target (e.g. `"jvm"`) which will register its `main` compilation
-(meaning that `register("jvm")` and `register("jvmMain")` register the same compilation)
-Or on the name of a source set (e.g. `"jvmTest"`, `"jsBenchmark"`) which will register the apt compilation
-(e.g. `register("jsFoo")` uses the `foo` compilation defined for the `js` target)
-Example for multiplatform project:
+ ```groovy
+ // build.gradle
+ repositories {
+ mavenCentral()
+ }
+ ```
-```groovy
-benchmark {
- targets {
- register("jvm")
- register("js")
- register("native")
- register("wasmJs") // Experimental
+
+
+### Target-specific configurations
+
+To run benchmarks on a platform ensure your Kotlin Multiplatform project targets that platform.
+For different platforms, there may be distinct requirements and settings that need to be configured.
+The guide below contains the steps needed to configure each supported platform for benchmarking.
+
+#### Kotlin/JVM
+
+To run benchmarks in Kotlin/JVM:
+1. Create a JVM target:
+
+ ```kotlin
+ // build.gradle.kts
+ kotlin {
+ jvm()
}
-}
-```
+ ```
-This package can also be used for Java and Kotlin/JVM projects. Register a Java sourceSet as a target:
+2. Register `jvm` as a benchmark target:
-```groovy
-benchmark {
- targets {
- register("main")
+ ```kotlin
+ // build.gradle.kts
+ benchmark {
+ targets {
+ register("jvm")
+ }
}
-}
-```
+ ```
+
+3. Apply [allopen plugin](https://kotlinlang.org/docs/all-open-plugin.html) to ensure your benchmark classes and methods are `open`.
-To configure benchmarks and create multiple profiles, create a `configurations` section in the `benchmark` block,
-and place options inside. Toolkit creates `main` configuration by default, and you can create as many additional
-configurations, as you need.
+ ```kotlin
+ // build.gradle.kts
+ plugins {
+ kotlin("plugin.allopen") version "1.9.20"
+ }
+ allOpen {
+ annotation("org.openjdk.jmh.annotations.State")
+ }
+ ```
-```groovy
-benchmark {
- configurations {
- main {
- // configure default configuration
+
+ Explanation
+
+ Assume that you've annotated each of your benchmark classes with `@State(Scope.Benchmark)`:
+
+ ```kotlin
+ // MyBenchmark.kt
+ @State(Scope.Benchmark)
+ class MyBenchmark {
+ // Benchmarking-related methods and variables
+ @Benchmark
+ fun benchmarkMethod() {
+ // benchmarking logic
}
- smoke {
- // create and configure "smoke" configuration, e.g. with several fast benchmarks to quickly check
- // if code changes result in something very wrong, or very right.
- }
}
-}
-```
+ ```
-Available configuration options:
-
-* `iterations` – number of measuring iterations
-* `warmups` – number of warm up iterations
-* `iterationTime` – time to run each iteration (measuring and warmup)
-* `iterationTimeUnit` – time unit for `iterationTime` (default is seconds)
-* `outputTimeUnit` – time unit for results output
-* `mode`
- - "thrpt" (default) – measures number of benchmark function invocations per time
- - "avgt" – measures time per benchmark function invocation
-* `include("…")` – regular expression to include benchmarks with fully qualified names matching it, as a substring
-* `exclude("…")` – regular expression to exclude benchmarks with fully qualified names matching it, as a substring
-* `param("name", "value1", "value2")` – specify a parameter for a public mutable property `name` annotated with `@Param`
-* `reportFormat` – format of report, can be `json`(default), `csv`, `scsv` or `text`
-* There are also some advanced platform-specific settings that can be configured using `advanced("…", …)` function,
- where the first argument is the name of the configuration parameter, and the second is its value. Valid options:
- * (Kotlin/Native) `nativeFork`
- - "perBenchmark" (default) – executes all iterations of a benchmark in the same process (one binary execution)
- - "perIteration" – executes each iteration of a benchmark in a separate process, measures in cold Kotlin/Native runtime environment
- * (Kotlin/Native) `nativeGCAfterIteration` – when set to `true`, additionally collects garbage after each measuring iteration (default is `false`).
- * (Kotlin/JVM) `jvmForks` – number of times harness should fork (default is `1`)
- - a non-negative integer value – the amount to use for all benchmarks included in this configuration, zero means "no fork"
- - "definedByJmh" – let the underlying JMH determine, which uses the amount specified in [`@Fork` annotation](https://javadoc.io/static/org.openjdk.jmh/jmh-core/1.21/org/openjdk/jmh/annotations/Fork.html) defined for the benchmark function or its enclosing class,
- or [Defaults.MEASUREMENT_FORKS (`5`)](https://javadoc.io/static/org.openjdk.jmh/jmh-core/1.21/org/openjdk/jmh/runner/Defaults.html#MEASUREMENT_FORKS) if it is not specified by `@Fork`.
- * (Kotlin/Js and Wasm) `jsUseBridge` – when `false` disables to generate special benchmark bridges to prevent inlining optimisations (only for `BuiltIn` benchmark executors).
-
-Time units can be NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, or their short variants such as "ms" or "ns".
-
-Example:
-
-```groovy
-benchmark {
- // Create configurations
- configurations {
- main { // main configuration is created automatically, but you can change its defaults
- warmups = 20 // number of warmup iterations
- iterations = 10 // number of iterations
- iterationTime = 3 // time in seconds per iteration
+ In Kotlin, classes are `final` by default, which means they can't be overridden.
+ This is incompatible with the operation of the Java Microbenchmark Harness (JMH), which kotlinx-benchmark uses under the hood for running benchmarks on JVM.
+ JMH requires benchmark classes and methods to be `open` to be able to generate subclasses and conduct the benchmark.
+
+ This is where the `allopen` plugin comes into play. With the plugin applied, any class annotated with `@State` is treated as `open`, which allows JMH to work as intended:
+
+ ```kotlin
+ // build.gradle.kts
+ plugins {
+ kotlin("plugin.allopen") version "1.9.20"
+ }
+
+ allOpen {
+ annotation("org.openjdk.jmh.annotations.State")
+ }
+ ```
+
+ This configuration ensures that your `MyBenchmark` class and its `benchmarkMethod` function are treated as `open`.
+
+
+
+ You can alternatively mark your benchmark classes and methods `open` manually, but using the `allopen` plugin enhances code maintainability.
+
+#### Kotlin/JS
+
+To run benchmarks in Kotlin/JS:
+1. Create a JS target with Node.js execution environment:
+
+ ```kotlin
+ // build.gradle.kts
+ kotlin {
+ js {
+ nodejs()
+ }
+ }
+ ```
+
+2. Register `js` as a benchmark target:
+
+ ```kotlin
+ // build.gradle.kts
+ benchmark {
+ targets {
+ register("js")
+ }
+ }
+ ```
+
+#### Kotlin/Native
+
+To run benchmarks in Kotlin/Native:
+1. Create a Native target:
+
+ ```kotlin
+ // build.gradle.kts
+ kotlin {
+ linuxX64()
+ }
+ ```
+
+2. Register `linuxX64` as a benchmark target:
+
+ ```kotlin
+ // build.gradle.kts
+ benchmark {
+ targets {
+ register("linuxX64")
}
- smoke {
- warmups = 5 // number of warmup iterations
- iterations = 3 // number of iterations
- iterationTime = 500 // time in seconds per iteration
- iterationTimeUnit = "ms" // time unit for iterationTime, default is seconds
- }
}
+ ```
- // Setup targets
- targets {
- // This one matches compilation base name, e.g. 'jvm', 'jvmTest', etc
- register("jvm") {
- jmhVersion = "1.21" // available only for JVM compilations & Java source sets
+It is possible to register multiple native targets. However, benchmarks can be executed only for the host target.
+This library supports all [targets supported by the Kotlin/Native compiler](https://kotlinlang.org/docs/native-target-support.html).
+
+#### Kotlin/Wasm
+
+To run benchmarks in Kotlin/Wasm:
+1. Create a Wasm target with Node.js execution environment:
+
+ ```kotlin
+ // build.gradle.kts
+ kotlin {
+ wasm {
+ nodejs()
}
- register("js") {
- // Note, that benchmarks.js uses a different approach of minTime & maxTime and run benchmarks
- // until results are stable. We estimate minTime as iterationTime and maxTime as iterationTime*iterations
- //
- // You can configure benchmark executor - benchmarkJs or buildIn (works only for JsIr backend) with the next line:
- // jsBenchmarksExecutor = JsBenchmarksExecutor.BuiltIn
+ }
+ ```
+
+2. Register `wasm` as a benchmark target:
+
+ ```kotlin
+ // build.gradle.kts
+ benchmark {
+ targets {
+ register("wasm")
}
- register("native")
- register("wasmJs") // Experimental
}
-}
-```
-
-# Separate source sets for benchmarks
+ ```
-Often you want to have benchmarks in the same project, but separated from main code, much like tests. Here is how:
-For a Kotlin/JVM project:
+Note: Kotlin/Wasm is an experimental compilation target for Kotlin. It may be dropped or changed at any time.
-Define source set:
-```groovy
-sourceSets {
- benchmarks
-}
-```
+### Writing Benchmarks
-Propagate dependencies and output from `main` sourceSet.
+After setting up your project and configuring targets, you can start writing benchmarks.
+As an example, let's write a simplified benchmark that tests how fast we can add up numbers in an `ArrayList`:
-```groovy
-dependencies {
- benchmarksCompile sourceSets.main.output + sourceSets.main.runtimeClasspath
-}
-```
+1. **Create Benchmark Class**: Create a class in your source set where you'd like to add the benchmark. Annotate this class with `@State(Scope.Benchmark)`.
-You can also add output and compileClasspath from `sourceSets.test` in the same way if you want
-to reuse some of the test infrastructure.
+ ```kotlin
+ @State(Scope.Benchmark)
+ class MyBenchmark {
+ }
+ ```
-Register `benchmarks` source set:
+2. **Set Up Variables**: Define variables needed for the benchmark.
-```groovy
-benchmark {
- targets {
- register("benchmarks")
+ ```kotlin
+ private val size = 10
+ private val list = ArrayList()
+ ```
+
+3. **Initialize Resources**: Within the class, you can define any setup or teardown methods using `@Setup` and `@TearDown` annotations respectively. These methods will be executed before and after the entire benchmark run.
+
+ ```kotlin
+ @Setup
+ fun prepare() {
+ for (i in 0..()
+
+ @Setup
+ fun prepare() {
+ for (i in 0..Benchmark`, e.g., `jvmBenchmark`.
+
+For more details about the tasks created by the kotlinx-benchmark plugin, refer to [this guide](docs/tasks-overview.md).
+
+### Benchmark Configuration Profiles
+
+The kotlinx-benchmark library provides the ability to create multiple configuration profiles. The `main` configuration is already created by the Toolkit.
+Additional profiles can be created as needed in the `configurations` section of the `benchmark` block:
+
+```kotlin
+// build.gradle.kts
benchmark {
- targets {
- register("jvmBenchmark")
+ configurations {
+ named("main") {
+ warmups = 20
+ iterations = 10
+ iterationTime = 3
+ iterationTimeUnit = "s"
+ }
+ register("smoke") {
+ include("")
+ warmups = 5
+ iterations = 3
+ iterationTime = 500
+ iterationTimeUnit = "ms"
+ }
}
}
-```
+```
+
+Refer to our [comprehensive guide](docs/configuration-options.md) to learn about configuration options and how they affect benchmark execution.
+
+### Separate source sets for benchmarks
+
+Often you want to have benchmarks in the same project, but separated from main code, much like tests.
+Refer to our [detailed documentation](docs/separate-benchmark-source-set.md) on configuring your project to add a separate source set for benchmarks.
+
+## Examples
+
+To help you better understand how to use the kotlinx-benchmark library, we've provided an [examples](examples) subproject.
+These examples showcase various use cases and offer practical insights into the library's functionality.
-# Examples
+## Contributing
-The project contains [examples](https://github.com/Kotlin/kotlinx-benchmark/tree/master/examples) subproject that demonstrates using the library.
-
+We welcome contributions to kotlinx-benchmark! If you want to contribute, please refer to our [Contribution Guidelines](CONTRIBUTING.md).
\ No newline at end of file