1 package benchmarks.debug 2 3 import kotlinx.coroutines.* 4 import kotlinx.coroutines.debug.* 5 import org.openjdk.jmh.annotations.* 6 import org.openjdk.jmh.annotations.State 7 import java.util.concurrent.* 8 import java.util.concurrent.atomic.AtomicInteger 9 10 /** 11 * The benchmark is supposed to show the DebugProbes overhead for a non-concurrent sequence builder. 12 * The code is actually part of the IDEA codebase, originally reported here: https://github.com/Kotlin/kotlinx.coroutines/issues/3527 13 */ 14 @Warmup(iterations = 5, time = 1) 15 @Measurement(iterations = 5, time = 1) 16 @Fork(value = 1) 17 @BenchmarkMode(Mode.AverageTime) 18 @OutputTimeUnit(TimeUnit.MICROSECONDS) 19 @State(Scope.Benchmark) 20 open class DebugSequenceOverheadBenchmark { 21 generateRecursiveSequencenull22 private fun <Node> generateRecursiveSequence( 23 initialSequence: Sequence<Node>, 24 children: (Node) -> Sequence<Node> 25 ): Sequence<Node> { 26 return sequence { 27 val initialIterator = initialSequence.iterator() 28 if (!initialIterator.hasNext()) { 29 return@sequence 30 } 31 val visited = HashSet<Node>() 32 val sequences = ArrayDeque<Sequence<Node>>() 33 sequences.addLast(initialIterator.asSequence()) 34 while (sequences.isNotEmpty()) { 35 val currentSequence = sequences.removeFirst() 36 for (node in currentSequence) { 37 if (visited.add(node)) { 38 yield(node) 39 sequences.addLast(children(node)) 40 } 41 } 42 } 43 } 44 } 45 46 @Param("true", "false") 47 var withDebugger = false 48 49 @Setup setupnull50 fun setup() { 51 DebugProbes.sanitizeStackTraces = false 52 DebugProbes.enableCreationStackTraces = false 53 if (withDebugger) { 54 DebugProbes.install() 55 } 56 } 57 58 @TearDown tearDownnull59 fun tearDown() { 60 if (withDebugger) { 61 DebugProbes.uninstall() 62 } 63 } 64 65 // Shows the overhead of sequence builder with debugger enabled 66 @Benchmark <lambda>null67 fun runSequenceSingleThread(): Int = runBlocking { 68 generateRecursiveSequence((1..100).asSequence()) { 69 (1..it).asSequence() 70 }.sum() 71 } 72 73 // Shows the overhead of sequence builder with debugger enabled and debugger is concurrently stressed out 74 @Benchmark <lambda>null75 fun runSequenceMultipleThreads(): Int = runBlocking { 76 val result = AtomicInteger(0) 77 repeat(Runtime.getRuntime().availableProcessors()) { 78 launch(Dispatchers.Default) { 79 result.addAndGet(generateRecursiveSequence((1..100).asSequence()) { 80 (1..it).asSequence() 81 }.sum()) 82 } 83 } 84 result.get() 85 } 86 } 87