Giter Site home page Giter Site logo

kotlinx-benchmark's Introduction

kotlinx-benchmark

Kotlin Alpha JetBrains incubator project GitHub license Build status Maven Central Gradle Plugin Portal

kotlinx-benchmark is a toolkit for running benchmarks for multiplatform code written in Kotlin. It 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.

Features

  • Low noise and reliable results
  • Statistical analysis
  • Detailed performance reports

Table of contents

Setting Up a Kotlin Multiplatform Project for Benchmarking

To configure a Kotlin Multiplatform project for benchmarking, follow the steps below. If you want to benchmark only Kotlin/JVM and Java code, you may refer to our comprehensive guide dedicated to setting up benchmarking in those specific project types.

Kotlin DSL
  1. Applying Benchmark Plugin: Apply the benchmark plugin.

    // build.gradle.kts
    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:

    // settings.gradle.kts
    pluginManagement {
        repositories {
            gradlePluginPortal()
        }
    }
  3. Adding Runtime Dependency: Next, add the kotlinx-benchmark-runtime dependency to the common source set:

    // build.gradle.kts
    kotlin {
        sourceSets {
            commonMain {
                dependencies {
                    implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10")
                }
            }
        }
    }
  4. Specifying Runtime Repository: Ensure you have mavenCentral() for dependencies lookup in the list of repositories:

    // build.gradle.kts
    repositories {
        mavenCentral()
    }
Groovy DSL
  1. Applying Benchmark Plugin: Apply the benchmark plugin.

    // 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:

    // settings.gradle
    pluginManagement {
        repositories {
            gradlePluginPortal()
        }
    }
  3. Adding Runtime Dependency: Next, add the kotlinx-benchmark-runtime dependency to the common source set:

    // build.gradle
    kotlin {
        sourceSets {
            commonMain {
                dependencies {
                    implementation 'org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10'
                }
            }
        }
    }
  4. Specifying Runtime Repository: Ensure you have mavenCentral() for dependencies lookup in the list of repositories:

    // build.gradle
    repositories {
        mavenCentral()
    }

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:

    // build.gradle.kts
    kotlin {
        jvm()
    }
  2. Register jvm as a benchmark target:

    // build.gradle.kts
    benchmark {
        targets { 
            register("jvm")
        }
    }
  3. Apply allopen plugin to ensure your benchmark classes and methods are open.

    // build.gradle.kts
    plugins {
        kotlin("plugin.allopen") version "1.9.20"
    }
    
    allOpen {
        annotation("org.openjdk.jmh.annotations.State")
    }
    Explanation

    Assume that you've annotated each of your benchmark classes with @State(Scope.Benchmark):

    // MyBenchmark.kt
    @State(Scope.Benchmark)
    class MyBenchmark {
        // Benchmarking-related methods and variables
        @Benchmark
        fun benchmarkMethod() {
            // benchmarking logic
        }
    }

    In Kotlin, classes are final by default, which means they can't be overridden. This conflicts with the Java Microbenchmark Harness (JMH) operation, 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:

    // 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:

    // build.gradle.kts
    kotlin {
        js { 
            nodejs() 
        }
    }
  2. Register js as a benchmark target:

    // build.gradle.kts
    benchmark {
        targets {
            register("js")
        }
    }

Kotlin/Native

To run benchmarks in Kotlin/Native:

  1. Create a Native target:

    // build.gradle.kts
    kotlin {
        linuxX64()
    }
  2. Register linuxX64 as a benchmark target:

    // build.gradle.kts
    benchmark {
        targets {
            register("linuxX64")
        }
    }

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.

Kotlin/Wasm

To run benchmarks in Kotlin/Wasm:

  1. Create a Wasm target with Node.js execution environment:

    // build.gradle.kts
    kotlin {
        wasm { 
            nodejs() 
        }
    }
  2. Register wasm as a benchmark target:

    // build.gradle.kts
    benchmark {
        targets {
            register("wasm")
        }
    }

Note: Kotlin/Wasm is an experimental compilation target for Kotlin. It may be dropped or changed at any time. Refer to Kotlin/Wasm documentation for up-to-date information about the target stability.

Writing Benchmarks

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:

  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).

    @State(Scope.Benchmark)
    class MyBenchmark {
    
    }
  2. Set Up Variables: Define variables needed for the benchmark.

    private val size = 10
    private val list = ArrayList<Int>()
  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.

    @Setup
    fun prepare() {
        for (i in 0..<size) {
            list.add(i)
        }
    }
    
    @TearDown
    fun cleanup() {
        list.clear()
    }
  4. Define Benchmark Methods: Next, create methods that you would like to be benchmarked within this class and annotate them with @Benchmark.

    @Benchmark
    fun benchmarkMethod(): Int {
        return list.sum()
    }

Your final benchmark class will look something like this:

import kotlinx.benchmark.*

@State(Scope.Benchmark)
class MyBenchmark {
    private val size = 10
    private val list = ArrayList<Int>()

    @Setup
    fun prepare() {
        for (i in 0..<size) {
            list.add(i)
        }
    }

    @TearDown
    fun cleanup() {
        list.clear()
    }

    @Benchmark
    fun benchmarkMethod(): Int {
        return list.sum()
    }
}

Note: Benchmark classes located in the common source set will be run in all platforms, while those located in a platform-specific source set will be run only in the corresponding platform.

See writing benchmarks for a complete guide for writing benchmarks.

Running Benchmarks

To run your benchmarks in all registered platforms, run benchmark Gradle task in your project. To run only on a specific platform, run <target-name>Benchmark, e.g., jvmBenchmark.

For more details about the tasks created by the kotlinx-benchmark plugin, refer to this guide.

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:

// build.gradle.kts
benchmark {
    configurations {
        named("main") {
            warmups = 20
            iterations = 10
            iterationTime = 3
            iterationTimeUnit = "s"
        }
        register("smoke") {
            include("<pattern of fully qualified name>")
            warmups = 5
            iterations = 3
            iterationTime = 500
            iterationTimeUnit = "ms"
        }
    }
}

Refer to our comprehensive guide to learn about configuration options and how they affect benchmark execution.

Separate source set 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 on configuring your project to set up a separate source set for benchmarks.

Examples

To help you better understand how to use the kotlinx-benchmark library, we've provided an examples subproject. These examples showcase various use cases and offer practical insights into the library's functionality.

Contributing

We welcome contributions to kotlinx-benchmark! If you want to contribute, please refer to our Contribution Guidelines.

kotlinx-benchmark's People

Contributors

4u7 avatar adam-enko avatar alikhachev avatar broadwaylamb avatar dumanskaia avatar erokhins avatar fabianishere avatar fzhinkin avatar goncalossilva avatar goooler avatar guohao avatar h0tk3y avatar igoriakovlev avatar ilgonmic avatar ilya-g avatar jisungbin avatar kyay10 avatar olme04 avatar orangy avatar pavelpunegov avatar qurbonzoda avatar qwwdfsad avatar skuzmich avatar stefma avatar tapchicoma avatar tbogdanova avatar whyoleg avatar wldeh avatar woainikk avatar yeicor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlinx-benchmark's Issues

Exception in linuxX64BenchmarkGenerate

In the attached sample project b.zip, which is as minimal as could possibly be,

$ ./gradlew bench

> Configure project :
Kotlin Multiplatform Projects are an Alpha feature. See: https://kotlinlang.org/docs/reference/evolution/components-stability.html. To hide this message, add 'kotlin.mpp.stability.nowarn=true' to the Gradle properties.


> Task :linuxX64BenchmarkGenerate FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':linuxX64BenchmarkGenerate'.
> There was a failure while executing work items
   > A failure occurred while executing kotlinx.benchmark.gradle.NativeSourceGeneratorWorker
      > 'org.jetbrains.kotlin.library.KotlinLibrary org.jetbrains.kotlin.library.SearchPathResolver$DefaultImpls.resolve$default(org.jetbrains.kotlin.library.SearchPathResolver, org.jetbrains.kotlin.library.UnresolvedLibrary, boolean, int, java.lang.Object)'

Using --info, --debug, --stacktrace doesn't provide any more useful information.

Include useful allOpen annotation example in readme

it would make more sense to explain that if for example you annotated each of your benchmark classes with

@BenchmarkMode(Mode.Throughput)
class Benchmark {}

and added to your build.gradle:

allOpen {
    annotation("org.openjdk.jmh.annotations.BenchmarkMode")
}

then you dont need to make things open. I think this would make it easier to follow

Gradle 6 compatibility

Currently using with gradle 6 gives:

Execution failed for task ':examples:benchmarksBenchmarkGenerate'.
> There was a failure while executing work items
   > A failure occurred while executing kotlinx.benchmark.gradle.JmhBytecodeGeneratorWorker
      > Generation of JMH bytecode failed with 1errors:
          - Group name should be the legal Java identifier.
           [org.openjdk.jmh.generators.reflection.RFMethodInfo@3a40cc4e]

Failed to run native benchmarks

plugin: 0.2.0-dev-2
kotlin: 1.3.40
ktor: 1.2.2

* What went wrong:
Execution failed for task ':ktor-client:ktor-client-benchmarks:linuxX64BenchmarkGenerate'.
> There was a failure while executing work items
   > A failure occurred while executing kotlinx.benchmark.gradle.NativeBytecodeGeneratorWorker
      > org.jetbrains.kotlin.konan.library.KonanLibraryUtilsKt.createKonanLibrary$default(Lorg/jetbrains/kotlin/konan/file/File;ILorg/jetbrains/kotlin/konan/target/KonanTarget;ZLorg/jetbrains/kotlin/konan/library/MetadataReader;ILjava/lang/Object;)Lorg/jetbrains/kotlin/konan/library/KonanLibrary;

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':ktor-client:ktor-client-benchmarks:linuxX64BenchmarkGenerate'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:95)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:91)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:119)
        at org.gradle.api.internal.tasks.execution.ResolvePreviousStateExecuter.execute(ResolvePreviousStateExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:93)
        at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:45)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:94)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:56)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:67)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:315)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:305)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:101)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.workers.WorkerExecutionException: There was a failure while executing work items
        at org.gradle.workers.internal.DefaultWorkerExecutor.workerExecutionException(DefaultWorkerExecutor.java:161)
        at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:155)
        at kotlinx.benchmark.gradle.NativeSourceGeneratorTask.generate(NativeSourceGeneratorTask.kt:56)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:48)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:41)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:704)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:671)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:284)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:273)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:258)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:67)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:145)
        at org.gradle.internal.execution.impl.steps.ExecuteStep.execute(ExecuteStep.java:49)
        at org.gradle.internal.execution.impl.steps.CancelExecutionStep.execute(CancelExecutionStep.java:34)
        at org.gradle.internal.execution.impl.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:69)
        at org.gradle.internal.execution.impl.steps.TimeoutStep.execute(TimeoutStep.java:49)
        at org.gradle.internal.execution.impl.steps.CatchExceptionStep.execute(CatchExceptionStep.java:33)
        at org.gradle.internal.execution.impl.steps.CreateOutputsStep.execute(CreateOutputsStep.java:50)
        at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:43)
        at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:29)
        at org.gradle.internal.execution.impl.steps.CacheStep.executeWithoutCache(CacheStep.java:134)
        at org.gradle.internal.execution.impl.steps.CacheStep.lambda$execute$3(CacheStep.java:83)
        at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:82)
        at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:36)
        at org.gradle.internal.execution.impl.steps.PrepareCachingStep.execute(PrepareCachingStep.java:33)
        at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:38)
        at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:23)
        at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96)
        at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89)
        at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:52)
        at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
        at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:34)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:91)
        ... 32 more
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing kotlinx.benchmark.gradle.NativeBytecodeGeneratorWorker
        at org.gradle.workers.internal.DefaultWorkerExecutor$WorkerExecution.waitForCompletion(DefaultWorkerExecutor.java:287)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:115)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:87)
        at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:153)
        ... 73 more
Caused by: java.lang.NoSuchMethodError: org.jetbrains.kotlin.konan.library.KonanLibraryUtilsKt.createKonanLibrary$default(Lorg/jetbrains/kotlin/konan/file/File;ILorg/jetbrains/kotlin/konan/target/KonanTarget;ZLorg/jetbrains/kotlin/konan/library/MetadataReader;ILjava/lang/Object;)Lorg/jetbrains/kotlin/konan/library/KonanLibrary;
        at kotlinx.benchmark.gradle.NativeBytecodeGeneratorWorker$ProvidedPathResolver.<init>(NativeSourceGeneratorTask.kt:138)
        at kotlinx.benchmark.gradle.NativeBytecodeGeneratorWorker.createModuleDescriptor(NativeSourceGeneratorTask.kt:99)
        at kotlinx.benchmark.gradle.NativeBytecodeGeneratorWorker.run(NativeSourceGeneratorTask.kt:76)
        at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:39)
        at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$WorkerCallable.call(IsolatedClassloaderWorkerFactory.java:164)
        at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory.executeInWorkerClassLoader(IsolatedClassloaderWorkerFactory.java:92)
        at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory.access$000(IsolatedClassloaderWorkerFactory.java:52)
        at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$1$1.execute(IsolatedClassloaderWorkerFactory.java:71)
        at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
        at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:315)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:305)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:101)
        at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
        at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
        at org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$1.execute(IsolatedClassloaderWorkerFactory.java:68)
        at org.gradle.workers.internal.DefaultWorkerExecutor$1.call(DefaultWorkerExecutor.java:108)
        at org.gradle.workers.internal.DefaultWorkerExecutor$1.call(DefaultWorkerExecutor.java:102)
        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215)
        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
        at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131)
        ... 3 more

Rename runtime artifact

Currently it's named kotlinx.benchmark.runtime which contradicts our artifact naming convention. Should be kotlinx-benchmark-runtime

IDE Support?

I found that this plugin does not support kotlinx-benchmark, so does kotlin have its own IDEA plugin?

Version 0.3 not working with kotlin 1.5.* MPP plugin

The gradle file I use (rewritten from examples/kotlin-mpp removing the infra stuff):

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version "1.5.10"
    id 'org.jetbrains.kotlin.plugin.allopen' version "1.5.0"
    id 'org.jetbrains.kotlinx.benchmark' version "0.3.1"
}

// how to apply plugin to a specific source set?
allOpen {
    annotation("org.openjdk.jmh.annotations.State")
}

repositories {
    mavenCentral()
}

kotlin {
    jvm {
        compilations.all {
            kotlinOptions {
                jvmTarget = '1.8'
            }
        }
    }

    js {
        nodejs {
            
        }
    }

    sourceSets.all {
        languageSettings {
            progressiveMode = true
            useExperimentalAnnotation("kotlin.Experimental")
            useExperimentalAnnotation('kotlin.ExperimentalUnsignedTypes')
        }
    }

    sourceSets {
        commonMain {
            dependencies {
                //classpath group: 'org.antlr', name: 'antlr4-runtime', version: '4.9.2'
                implementation "org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1"
            }
        }
        commonTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-common'
                implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
            }
        }
        jvmMain {}
        jvmTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test'
                implementation 'org.jetbrains.kotlin:kotlin-test-junit'
            }
        }
        jsMain {}
        jsTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-js'
            }
        }

        nativeMain {
            dependsOn commonMain
            dependencies {
            }
        }
    }
}

// Configure benchmark
benchmark {
    configurations {
        main { // --> jvmBenchmark/jsBenchmark + benchmark
            iterations = 5 // number of iterations
            iterationTime = 300
            iterationTimeUnit = "ms"
            advanced("forks", 3)
        }

        params {
            iterations = 5 // number of iterations
            iterationTime = 300
            iterationTimeUnit = "ms"
            include("ParamBenchmark")
            param("data", 5, 1, 8)
            param("unused", 6, 9)
        }

        fast { // --> jvmFastBenchmark
            include("Common")
            exclude("long")
            iterations = 1
            iterationTime = 300 // time in ms per iteration
            iterationTimeUnit = "ms" // time in ms per iteration
            advanced("forks", 1)
        }

        csv {
            include("Common")
            exclude("long")
            iterations = 1
            iterationTime = 300
            iterationTimeUnit = "ms"
            reportFormat = "csv" // csv report format
        }
    }

    // Setup configurations
    targets {
        // This one matches compilation base name, e.g. 'jvm', 'jvmTest', etc
        register("jvm") {
            jmhVersion = "1.21"
        }
        register("js")
        register("native")
        register("macosX64")
        register("linuxX64")
        register("mingwX64")
    }
}

This does not run, gradle benchmark resulting in a very cryptic error:

FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jsBenchmarkPackageJson'.
> org/antlr/v4/runtime/Lexer

This is, in fact, a j.l.ClassNotFoundException exception in plugin.
Adding the following buildscript header fixes the issue:

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath group: 'org.antlr', name: 'antlr4-runtime', version: '4.9.2'
    }
}

So this is, indeed, a missing dependency in plugin or something similar

Support for `suspend` setup and teardown in JS executor

On current moment, JS executor support returning Promise from benchmark function. But it doesn't support returning Promise from setup and teardown.
On JVM and Native we can call runBlocking in setup/teardown to call suspend code there. But on JS we can only call suspend code in benchmark functions.

Dependencies resolved at configuration time for fooBenchmarkJar tasks

This line resolves the dependencies at configuration time. If such a task is triggered to be configured and is not going to be executed, this is a performance issue. Moreover, resolving dependencies in the lazy configuration scope is dangerous, as it may involve resolving a project dependency on a project that has not yet been configured – then Gradle will configure it in-place, and its configuration logic (including plugins) may attempt to perform an action that can't be done in the lazy configuration scope, such as afterEvaluate { ... }.

These dependencies should not be resolved in the lazy configuration scope.

Support macosArm64 Kotlin target

To adopt kotlinx.benchmark in Kotlin/Native own performance infrastructure, we need the former to have macosArm64 support, because we are planning to run our benchmarks on Apple silicon.

Support for other Apple silicon targets (e.g. iosSimulatorArm64) would also be great.

Issue running benchmark with Java 16

Downloaded the repository, runs fine with when the Gradle JVM is set to Java 15 but not Java 16

Working with JDK 15.0.4
Can't compile with JDK 16.0.2

System
IntelliJ Ultimate 2021.2.1
macOS Big Sur version 11.3.1

Cause: class org.gradle.internal.compiler.java.ClassNameCollector (in unnamed module @0x7244f195) cannot access class com.sun.tools.javac.code.Symbol$TypeSymbol (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.code to unnamed module @0x7244f195

Implement easy publishing to a custom Maven repository

To adopt kotlinx.benchmark in Kotlin/Native own performance infrastructure, we need to be able to easily publish the library and the plugin to a custom Maven repository (probably somewhere on https://maven.pkg.jetbrains.space) from a branch.

Motivation: we'd like to have a fast feedback loop, because migrating to kotlinx.benchmark would probably require some adjustments to the latter. Same for the further maintenance.

Wrong treatment of dublication META-INF elements

The jvmBenchmarkJar task is failing with complex dependencies on Gradle 7 unless the following fix is applied:

    val jvmBenchmarkJar by tasks.getting(org.gradle.jvm.tasks.Jar::class) {
        duplicatesStrategy = org.gradle.api.file.DuplicatesStrategy.EXCLUDE
    }

Benchmark JAR cannot be built due to duplicate META-INF/versions/9/module-info.class even when jvmTarget=1.8

Sample project b.zip attached.

$ ./gradlew bench
> Task :mainBenchmarkJar FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mainBenchmarkJar'.
> Entry META-INF/versions/9/module-info.class is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.3/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.

Wrong configuration for 0.2.0-dev-2

Dependency configuration requires 0.2.0-dev-2, not 0.2.0. Also it requires maven ("https://dl.bintray.com/orangy/maven") because of dependency on non-published kotlinx-cli.

Long param values with spaces break code generator

Bumped into this while working on #61. If one uses a lenghy value for @Param that includes spaces, code generation in SuiteSourceGenerator.kt will incorrectly add newlines in-between.

For example, for:

@Param("""if(gte(now(), datetime("2021-10-22")), 1, 0)""")

The generated code looks like (notice the unexpected newline on line 33/34, breaking the string):

image

Consider unifying messagesg in kotlinx-benchmark executors with JMH's ones

  1. Messages printed by JMH are much more meaningful.
  2. Making messages formats similar improves the library user's experience.
Consider similar benchmark reports. The JMH one:
… space.kscience.kmath.benchmarks.ExpressionsInterpretersBenchmark.mstExpression

Warm-up 1: 2.464 ops/s
Warm-up 2: 2.530 ops/s
Iteration 1: 2.575 ops/s
Iteration 2: 2.561 ops/s
Iteration 3: 2.525 ops/s
Iteration 4: 2.509 ops/s
Iteration 5: 2.517 ops/s
Iteration 6: 2.493 ops/s
Iteration 7: 2.506 ops/s
Iteration 8: 2.477 ops/s
Iteration 9: 2.548 ops/s
Iteration 10: 2.549 ops/s

  Success: 2.526 ±(99.9%) 0.047 ops/s [Average]
  (min, avg, max) = (2.477, 2.526, 2.575), stdev = 0.031
  CI (99.9%): [2.479, 2.573] (assumes normal distribution)

The one by JsExecutor:

… space.kscience.kmath.benchmarks.ExpressionsInterpretersBenchmark.mstExpression
Iteration #0: 0.0569060 ops/sec
Iteration #1: 0.0558908 ops/sec
Iteration #2: 0.0571284 ops/sec
Iteration #3: 0.0568593 ops/sec
Iteration #4: 0.0571386 ops/sec
Iteration #5: 0.0551405 ops/sec
Iteration #6: 0.0564571 ops/sec
Iteration #7: 0.0558157 ops/sec
Iteration #8: 0.0585252 ops/sec
Iteration #9: 0.0602421 ops/sec
  Success:   ~ 0.0570104 ops/sec ±1.6%

It is obvious that JMH's format contains more information: warmup and measurement iterations are separated, and there are basic statistics of results.

Can't build when used with Kotlin experimental features

The library I am benchmarking uses Kotlin's ExperimentalUnsignedTypes. I have already added

@OptIn(ExperimentalUnsignedTypes::class)

to the benchmark class and

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

to build.gradle.kts but still got the following Gradle error:

Execution failed for task ':app:mainBenchmarkGenerate'.
> There was a failure while executing work items
   > A failure occurred while executing kotlinx.benchmark.gradle.JmhBytecodeGeneratorWorker
      > Generation of JMH bytecode failed with 1errors:
          - Group name should be the legal Java identifier.
           [org.openjdk.jmh.generators.reflection.RFMethodInfo@3212c77c]

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.


Separate warmup time from iteration time

Currently, both warmup and measurement iterations use iterationTime and iterationTimeUnit values defined in the configuration script. @Warmup annotation only defines iterations value leaning on @Measurement time and timeUnit values. When iterationTime and iterationTimeUnit are not defined in the configuration script, JVM run of the same benchmark uses different time and timeUnit for warmups than JS and Native.

Consider aligning behavior on all targets. For example, by introducing warmupTime/warmupTimeUnit and @Warmup(val time: Int, val timeUnit: BenchmarkTimeUnit).

Turn on JS IR

I'm having such an error because the runtime library doesn't support JS IR.

Caused by: org.gradle.internal.component.NoMatchingConfigurationSelectionException: No matching variant of org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20 was found. The consumer was configured to find a usage of 'kotlin-runtime' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js', attribute 'org.jetbrains.kotlin.js.compiler' with value 'ir' but:
  - Variant 'js-api' capability org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20 declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js':

Support CSV result format

It would be really good to add support of CSV for benchmarks result (JMH has it). CSV is easier for comparing different results and creating charts.

I can try to contribute it, if it's ok.

Pass command-line arguments to the actual `node` process used for benchmarks

It would be nice to have an option to pass command-line arguments to the actual node process used for benchmarks.

My personal use-case is that I need to pass --experimental-options --js.webassembly --polyglot to GraalVM's node.|

Workaround:

tasks.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec> {
    nodeArgs += arrayOf("--experimental-options", "--js.webassembly", "--polyglot")
}

Separating source sets for benchmarks in multiplatform projects

I've tried adding:

val commonBenchmark by creating {
    dependsOn(commonMain) // or commonTest

    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.1")
        implementation("com.goncalossilva:resources:0.2.1")
    }
}

And then configuring targets as documented:

benchmark {
    targets {
        register("js")
		// register("jvm")
        // ...
    }
}

But this is clearly not the right way, since nothing happens when running (“Test events were not received”) and there is this warning:

The Kotlin source set commonBenchmark was configured but not added to any Kotlin compilation. You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
BUILD SUCCESSFUL in 5s
1 actionable task: 1 executed
> Task :assembleBenchmarks UP-TO-DATE
> Task :kotlinNpmCachesSetup
> Task :jsPackageJson
> Task :kotlinNodeJsSetup UP-TO-DATE
> Task :kotlinYarnSetup UP-TO-DATE
> Task :jsBenchmarkPackageJson UP-TO-DATE
> Task :rootPackageJson
> Task :kotlinNpmInstall
> Task :jsGenerateExternalsIntegrated SKIPPED
> Task :compileKotlinJs UP-TO-DATE
> Task :jsProcessResources NO-SOURCE
> Task :jsMainClasses UP-TO-DATE
> Task :jsBenchmarkGenerate UP-TO-DATE
> Task :compileBenchmarkKotlinJs NO-SOURCE
> Task :jsBenchmark SKIPPED
> Task :benchmark UP-TO-DATE
BUILD SUCCESSFUL in 723ms
9 actionable tasks: 4 executed, 5 up-to-date
23:44:03: Execution finished 'benchmark'

Could this be documented?

Default `outputTimeUnit` is different for platforms

Consider next Gradle configuration:

benchmark {
    targets {
        register("jvm")
        register("js")
        register("native")
    }
    configurations {
        val main by getting {
            warmups = 20
            iterations = 10
            iterationTime = 500
            iterationTimeUnit = "ms"
        }
    }
}

EXPECTED: ops/ms or ops/s
ACTUAL: ops/ms for JS and Native, ops/s for JVM

Example of JS output

Iteration #0: 18.5661 ops/ms
Iteration #1: 83.6859 ops/ms
Iteration #2: 152.060 ops/ms
Iteration #3: 170.539 ops/ms
Iteration #4: 187.111 ops/ms
Iteration #5: 193.780 ops/ms
Iteration #6: 153.711 ops/ms

Example of JVM output

Warm-up 1: 807698.921 ops/s
Warm-up 2: 1630487.338 ops/s
Warm-up 3: 1297262.282 ops/s
Warm-up 4: 1552165.639 ops/s
Warm-up 5: 1572772.547 ops/s
Warm-up 6: 1632385.462 ops/s
Warm-up 7: 1317684.635 ops/s
Warm-up 8: 1161124.124 ops/s

Explore the possibility of making the plugin an ordinary module

On current moment, plugin is not in classpath of integration module, because plugin is included build (to use current version of plugin in examples).
We can add it to classpath in some way, but now kotlinx.benchmark.gradle.BenchmarkConfiguration depends on BenchmarksExtension, looks like we need to remove this dependency to reuse it.

I have a question, why is included build is used at all? Maybe it's possible to make it as an ordinary module, and after that integration module will be moved just to integrationTest source set under it (the same way as it's done in kotlinx-atomicfu and binary-compatibility-validator gradle plugins)

Originally posted by @whyoleg in #37 (comment)

js benchmark task failing with kotlin version > 1.4.10

For any kotlin version higher than 1.4.10 I'm getting the following gradle error with js benchmarking target:

Execution failed for task ':benchmarks:jsBenchmarkGenerate'.

Caused by: java.lang.NoSuchMethodError: kotlin.collections.CollectionsKt.maxOrNull(Ljava/lang/Iterable;)Ljava/lang/Comparable;
at org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer.computeExperimentalityModeForFunctions(MemberDeserializer.kt:233)

How to properly setup a separate benchmark sourceset in a multiplatform project

Hi,

I am struggeling to find the correct way on how to setup a separate sourceset for benchmarks in a multiplatform project using gradle kotlin dsl.

Before I had a normal jvm project which looked like this and this was working:

plugins {
    kotlin("jvm") version "1.6.10"
    id("org.jetbrains.kotlinx.benchmark") version "0.4.1"
}

group = "io.github.quillraven.fleks"
version = "1.0-RC3"
java.sourceCompatibility = JavaVersion.VERSION_1_8

val bmSourceSetName = "benchmarks"
sourceSets {
    create(bmSourceSetName) {
        compileClasspath += sourceSets["main"].output
        runtimeClasspath += sourceSets["main"].output
    }
}

configurations {
    getByName("${bmSourceSetName}Implementation") {
        extendsFrom(configurations["implementation"])
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    testImplementation("org.jetbrains.kotlin:kotlin-test:1.6.0")

    configurations["${bmSourceSetName}Implementation"]("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.1")
    configurations["${bmSourceSetName}Implementation"]("com.badlogicgames.ashley:ashley:1.7.3")
    configurations["${bmSourceSetName}Implementation"]("net.onedaybeard.artemis:artemis-odb:2.3.0")
}

benchmark {
    targets {
        register(bmSourceSetName)
    }
}

Now I converted it to multiplatform and according to IntelliJ it looks like the benchmarks sourceset is correctly created (since it is written in bold in the Project view) but I cannot run benchmarks with gradle. Running jvmBenchmark results in following error:

Error: Could not find or load main class kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt
Caused by: java.lang.ClassNotFoundException: kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt

Could not find or load main class kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt

Caused by: java.lang.ClassNotFoundException: kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt

The new gradle file looks like this:

plugins {
    kotlin("multiplatform") version "1.6.10"
    id("org.jetbrains.kotlinx.benchmark") version "0.4.2"
}

group = "io.github.quillraven.fleks"
version = "1.0-KMP-RC1"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}

kotlin {
    jvm {
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
        withJava()
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
    }

    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        val benchmarks by creating {
            dependsOn(commonMain)
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
                implementation("com.badlogicgames.ashley:ashley:1.7.4")
                implementation("net.onedaybeard.artemis:artemis-odb:2.3.0")
            }
        }
    }
}

benchmark {
    targets {
        register("jvm")
    }
}

I also tried to register the benchmarks sourceset like before instead of jvm but then I already get a warning by gradle that it cannot find a benchmark compilation 'benchmarks' and I don't get a gradle task to run the benchmarks:

benchmark {
    targets {
        register("benchmarks")
    }
}

With that setting I only have a benchmark gradle task and running that results in:

Kotlin Multiplatform Projects are an Alpha feature. See: https://kotlinlang.org/docs/reference/evolution/components-stability.html. To hide this message, add 'kotlin.mpp.stability.nowarn=true' to the Gradle properties.

Warning: Cannot find a benchmark compilation 'benchmarks', ignoring.

The Kotlin source set benchmarks was configured but not added to any Kotlin compilation. You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#connecting-source-sets

Any help is appreciated on how to properly set that up please. I found an issue via google where someone is adding the benchmark implementation dependency to the commonMain sourceset which solved his issue but I assume that this also means that benchmark is part of the final jar that gets build and I don't want that.

Benchmarks should be something separated that are not part of the "main" project.

edit: I tried adding the runtime dependency to commonMain now as well but still the same issues. So that doesn't seem to make a difference.

Could not find or load main class kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt

I am trying to use kotlinx-benchmark in an open source project. I followed all the instructions but I am getting the following error when I try to run the benchmarks from the Gradle panel in IntelliJ IDEA:

Could not find or load main class kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt

My changes to build.gradle.kts can be seen in the following pull-request:

https://github.com/tree-ware/tree-ware-kotlin-core/pull/13/files

I am interested only in JVM benchmarks. I tried adding a dependency to org.jetbrains.kotlinx:kotlinx-benchmark-runtime-jvm but that did not help either.

Unresolved reference: native (import kotlinx.benchmark.native.NativeExecutor)

In docs it is specified that it is possible to create benchmarks outside of main package.
However, it is not specified in terms of Kotlin Multiplatform.
Maybe it should be specified in documentation?

By the way, README.md is written for Gradle in Groovy.
What do you think about rewriting or additional tabs for Gradle KTS?

I was able to run those with next configuration:

My build.gradle:

plugins {
    kotlin("multiplatform") version "1.6.10"
    id("org.jetbrains.kotlinx.benchmark") version "0.4.2"
    id("org.jetbrains.kotlin.plugin.allopen") version "1.6.0"
}

repositories {
    mavenCentral()
}

kotlin {
    jvm {
        compilations.all {
            kotlinOptions.jvmTarget = "11"
        }
        withJava()
        testRuns["test"].executionTask.configure {
            useJUnitPlatform()
        }
    }
    js {
        nodejs()
        browser {
            commonWebpackConfig {
                cssSupport.enabled = true
            }
        }
    }
    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" -> macosX64("native")
        hostOs == "Linux" -> linuxX64("native")
        isMingwX64 -> mingwX64("native")
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }

    
    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        val jvmMain by getting
        val jvmTest by getting
        val jsMain by getting
        val jsTest by getting
        val nativeMain by getting
        val nativeTest by getting


        val benchmarks by creating {
            dependsOn(commonMain)
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
            }
        }
        val benchmarksJvm by creating {
            dependsOn(benchmarks)
            jvmMain.dependsOn(this) // important! dependsOn(jvmMain) does not work, as benchmark scope needs to register compilation targets
        }
        val benchmarksJs by creating {
            dependsOn(benchmarks)
            jsMain.dependsOn(this)
        }
        val benchmarksNative by creating {
            dependsOn(benchmarks)
            nativeMain.dependsOn(this)
        }
    }
}

// For JVM Benchmarks based on JMH
allOpen {
    annotation("org.openjdk.jmh.annotations.State")
}

benchmark {
    targets {
        register("jvm")
        register("js")
        register("native")
    }
}

Align default configurations of a benchmark method on all targets

Currently, if warmup (or measurement) iteration count is not defined in the configuration script and in the annotation for a benchmark, the default value in JVM is 5 while in JS and Native it is 3. Default warmup (or measurement) iteration time in JVM is 10 seconds while in JS and Native it is 1 second.

Find out differences in other default values and consider aligning them.

Weird result for warmup vs actual iterations for native benchmarks

This is the benchmark class:

@State(Scope.Benchmark)
@OutputTimeUnit(BenchmarkTimeUnit.MILLISECONDS)
@Measurement(time = 1, timeUnit = BenchmarkTimeUnit.SECONDS)
@BenchmarkMode(Mode.Throughput)
class SimpleOps {
  private var a = 0
  private var b = 0

  @Setup
  fun setup() {
    a = 10
    b = 20
  }

  @Benchmark
  fun add(): Int {
    return a + b
  }

  @Benchmark
  fun sub(): Int {
    return a - b
  }

  @Benchmark
  fun mul(): Int {
    return a * b
  }

  @Benchmark
  fun div(): Int {
    return a / b
  }
}

The results:

… benchmark.SimpleOps.add
Warm-up #0: 6,829.74 ops/ms
Warm-up #1: 7,094.42 ops/ms
Iteration #0: 89,446.7 ops/ms
Iteration #1: 92,966.6 ops/ms
Iteration #2: 83,1251.0 ops/ms
  Success:   ~ 88,513.1 ops/ms ±6.4%

… benchmark.SimpleOps.div
Warm-up #0: 6,988.84 ops/ms
Warm-up #1: 6,935.19 ops/ms
Iteration #0: 69,1761.0 ops/ms
Iteration #1: 69,718.8 ops/ms
Iteration #2: 82,222.2 ops/ms
  Success:   ~ 73,706.0 ops/ms ±11%

… benchmark.SimpleOps.mul
Warm-up #0: 5,537.26 ops/ms
Warm-up #1: 5,530.41 ops/ms
Iteration #0: 21,964.3 ops/ms
Iteration #1: 22,796.2 ops/ms
Iteration #2: 21,789.4 ops/ms
  Success:   ~ 22,183.3 ops/ms ±2.7%

… benchmark.SimpleOps.sub
Warm-up #0: 7,040.06 ops/ms
Warm-up #1: 7,041.18 ops/ms
Iteration #0: 91,877.2 ops/ms
Iteration #1: 78,932.6 ops/ms
Iteration #2: 90,539.2 ops/ms
  Success:   ~ 87,116.3 ops/ms ±9.2%

As you can see the actual iteration values is 10x of the warmup. It's probably due to this: https://github.com/Kotlin/kotlinx-benchmark/blob/master/runtime/nativeMain/src/kotlinx/benchmark/native/NativeExecutor.kt#L92 Before dividing should we change time to be the unit specified by the benchmark?

As a side note: JVM benchmarks is using JMH while the native one is using homemade. Would it be possible to provide a single implementation for all platforms?

Trouble with Kotlin/Native

I have a simple project in Idea for Kotlin/Native and next build.gradle file:

plugins {
    id "kotlinx.benchmark" version "0.2.0-dev-8"
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
    id 'java'
    id 'org.jetbrains.kotlin.plugin.allopen' version "1.3.61"
}
repositories {
    mavenCentral()
    maven { url 'https://dl.bintray.com/kotlin/kotlinx' }
}
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8"
}
kotlin {
    // 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
    mingwX64("mingw") {
        binaries {
            executable {
                // Change to specify fully qualified name of your application's entry point:
               entryPoint = 'sample.main'
                // Specify command-line arguments, if necessary:
                runTask?.args('')
            }
        }
    }
    sourceSets {
        // Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
        // in gradle.properties file and re-import your project in IDE.
        mingwMain {
        }
        mingwTest {
        }
    }
}

// Use the following Gradle tasks to run your application:
// :runReleaseExecutableMingw - without debug symbols
// :runDebugExecutableMingw - with debug symbols

benchmark {
    configurations {
        main {
            iterations = 10
            warmups = 1
        }
    }
    targets {
        register("mingw")
    }
}


allOpen {
    annotation("org.openjdk.jmh.annotations.State")
}

When I run benchmark task in Gradle, I got:

Testing started at 21:18 ...
> Configure project :
Kotlin Multiplatform Projects are an experimental feature.
Warning: Cannot find a benchmark compilation 'native', ignoring.
> Task :compileKotlinMingw UP-TO-DATE
> Task :mingwProcessResources NO-SOURCE
> Task :mingwMainKlibrary UP-TO-DATE
> Task :mingwBenchmarkGenerate FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':mingwBenchmarkGenerate'.
> There was a failure while executing work items
   > A failure occurred while executing kotlinx.benchmark.gradle.NativeSourceGeneratorWorker
      > e: Failed to resolve Kotlin library: stdlib
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 4s

What I am doing wrong?

Wha I am trying:

  1. Remove allopen
  2. Add additional stdlib
  3. Remve stdlib from External Libraries in Idea (it returns)

Jvm bench in multiplatform with separate SourceSet. No benchmarks to run; check the include/exclude regexps.

I'm trying to benchmark jvm on a multiplatform project with separate sourceSet

To reproduce just clone this branch

This is my setup so far:

kotlin {
    ...
    sourceSets {
        val jvmBench by creating {
            dependsOn(commonMain)
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.0")
                //                "benchCompile"(sourceSets.main.output + sourceSets.main.runtimeClasspath)
            }
        }
        ...
    }
    targets {
        jvm {
            compilations.create("bench")
        }
    }
}

benchmark {
    ...
    targets {
        register("jvm")
        //        register("js")
        //        register("native")
    }
}

val SourceSetContainer.main: SourceSet
    get() = named("main").get()
dependencies {
    kspMetadata(projects.processor)
    "jvmBenchImplementation"(sourceSets.main.output + sourceSets.main.runtimeClasspath)
    ...
}

When I try to execute jvmBenchmark I get:

Type-safe project accessors is an incubating feature.
Configure project :
Kotlin Multiplatform Projects are an Alpha feature. See: https://kotlinlang.org/docs/reference/evolution/components-stability.html. To hide this message, add 'kotlin.mpp.stability.nowarn=true' to the Gradle properties.
Task :processor:compileKotlinJvm UP-TO-DATE
Task :processor:jvmProcessResources UP-TO-DATE
Task :processor:jvmMainClasses UP-TO-DATE
Task :processor:jvmJar UP-TO-DATE
Task :kspKotlinMetadata UP-TO-DATE
Task :compileKotlinJvm UP-TO-DATE
Task :compileJava NO-SOURCE
Task :jvmProcessResources NO-SOURCE
Task :jvmMainClasses UP-TO-DATE
Task :jvmBenchmarkGenerate UP-TO-DATE
Task :jvmBenchmarkCompile NO-SOURCE
Task :jvmBenchmark FAILED
Running 'main' benchmarks for 'jvm'
Error: Could not find or load main class kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt
Caused by: java.lang.ClassNotFoundException: kotlinx.benchmark.jvm.JvmBenchmarkRunnerKt
FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':jvmBenchmark'.
    Process 'command '/usr/lib/jvm/java-11-openjdk-amd64/bin/java'' finished with non-zero exit value 1
  • Try:
    Run with --stacktrace option to get the stack trace.
    Run with --info or --debug option to get more log output.
    Run with --scan to get full insights.
  • Get more help at https://help.gradle.org
    Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
    You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
    See https://docs.gradle.org/7.3/userguide/command_line_interface.html#sec:command_line_warnings
    BUILD FAILED in 151ms
    7 actionable tasks: 1 executed, 6 up-to-date

I can't run a single test from Idea gui

As titled, if I try to run the following

Screenshot from 2021-12-16 18-13-57

it will automatically run this:

glm:jvmTest:cleanJvmTest glm:jvmTest:jvmTest --tests "glm.func.funcGeometric.test"

but it'll throw

Project 'glm' not found in root project 'glm'.

If I modify manually the line into

cleanJvmTest jvmTest --tests "glm.func.funcGeometric.test"

Then it's fine again

To reproduce clone this branch

Run individual benchmarks

The most frustrating thing about benchmarks is that I can't run individual benchmarks without running them all.

Error with compiling build.gradle.kts

Updated settings.gradle, added plugin import, dependencies and implementations but this error still being issued during compilation

A problem occurred configuring root project 'projectName'.

Could not resolve all artifacts for configuration ':classpath'.
Could not find org.jetbrains.kotlin:kotlin-native-library-reader:1.3.40-eap-105.
Searched in the following locations:
- https://dl.bintray.com/kotlin/kotlinx/org/jetbrains/kotlin/kotlin-native-library-reader/1.3.40-eap-105/kotlin-native-library-reader-1.3.40-eap-105.pom
- https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-native-library-reader/1.3.40-eap-105/kotlin-native-library-reader-1.3.40-eap-105.pom
Required by:
project : > kotlinx.benchmark:kotlinx.benchmark.gradle.plugin:0.2.0-dev-2 > org.jetbrains.kotlinx:kotlinx.benchmark.gradle:0.2.0-dev-2

Possible solution:

State annotation should not be mandatory for benchmark class

Good description of what jmh state is for.

It doesn't seem correct that for the js benchmark to run you must annotate the class with @State(Scope.Benchmark).

I believe the only requirement should be for the benchmark functions to be annotated with @Benchmark.

If it is an intentional requirement, then I think it should be mentioned in the README.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.