diff --git a/benchmarks/src/jmh/kotlin/benchmarks/debug/DebugProbesConcurrentBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/debug/DebugSequenceOverheadBenchmark.kt similarity index 50% rename from benchmarks/src/jmh/kotlin/benchmarks/debug/DebugProbesConcurrentBenchmark.kt rename to benchmarks/src/jmh/kotlin/benchmarks/debug/DebugSequenceOverheadBenchmark.kt index 4c1a67a4d0..16e93e1f1b 100644 --- a/benchmarks/src/jmh/kotlin/benchmarks/debug/DebugProbesConcurrentBenchmark.kt +++ b/benchmarks/src/jmh/kotlin/benchmarks/debug/DebugSequenceOverheadBenchmark.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package benchmarks.debug @@ -9,46 +9,24 @@ import kotlinx.coroutines.debug.* import org.openjdk.jmh.annotations.* import org.openjdk.jmh.annotations.State import java.util.concurrent.* +import java.util.concurrent.atomic.AtomicInteger +/** + * The benchmark is supposed to show the DebugProbes overhead for a non-concurrent sequence builder. + * The code is actually part of the IDEA codebase, originally reported here: https://github.com/Kotlin/kotlinx.coroutines/issues/3527 + */ @Warmup(iterations = 5, time = 1) @Measurement(iterations = 5, time = 1) @Fork(value = 1) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Benchmark) -open class DebugProbesConcurrentBenchmark { - - @Setup - fun setup() { - DebugProbes.sanitizeStackTraces = false - DebugProbes.enableCreationStackTraces = false - DebugProbes.install() - } - - @TearDown - fun tearDown() { - DebugProbes.uninstall() - } - +open class DebugSequenceOverheadBenchmark { - @Benchmark - fun run() = runBlocking { - var sum = 0L - repeat(8) { - launch(Dispatchers.Default) { - val seq = stressSequenceBuilder((1..100).asSequence()) { - (1..it).asSequence() - } - - for (i in seq) { - sum += i.toLong() - } - } - } - sum - } - - private fun stressSequenceBuilder(initialSequence: Sequence, children: (Node) -> Sequence): Sequence { + private fun generateRecursiveSequence( + initialSequence: Sequence, + children: (Node) -> Sequence + ): Sequence { return sequence { val initialIterator = initialSequence.iterator() if (!initialIterator.hasNext()) { @@ -68,4 +46,45 @@ open class DebugProbesConcurrentBenchmark { } } } + + @Param("true", "false") + var withDebugger = false + + @Setup + fun setup() { + DebugProbes.sanitizeStackTraces = false + DebugProbes.enableCreationStackTraces = false + if (withDebugger) { + DebugProbes.install() + } + } + + @TearDown + fun tearDown() { + if (withDebugger) { + DebugProbes.uninstall() + } + } + + // Shows the overhead of sequence builder with debugger enabled + @Benchmark + fun runSequenceSingleThread(): Int = runBlocking { + generateRecursiveSequence((1..100).asSequence()) { + (1..it).asSequence() + }.sum() + } + + // Shows the overhead of sequence builder with debugger enabled and debugger is concurrently stressed out + @Benchmark + fun runSequenceMultipleThreads(): Int = runBlocking { + val result = AtomicInteger(0) + repeat(Runtime.getRuntime().availableProcessors()) { + launch(Dispatchers.Default) { + result.addAndGet(generateRecursiveSequence((1..100).asSequence()) { + (1..it).asSequence() + }.sum()) + } + } + result.get() + } }