Skip to content

Commit a230fcf

Browse files
committed
Do not yield in CoroutineScheduler during transition to the idle state, introduce system properties to tune this behaviour.
Rationale: Thread.yield has a significant CPU cost (especially relatively to spins) and provides no significant benefits compared with exponential parking. Thus yields burn a lot of CPU due to JVM upcall + syscall, providing benefits neither for liveness property (as CPU is mostly busy with doing these calls) nor for latencies. Initial benchmarking shows significant CPU usage reduction (200-300%) in low-to-average load benchmarks with no degradation on target affinity benchmarks. Partially addresses #840
1 parent 4dc9469 commit a230fcf

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package benchmarks
6+
7+
import org.openjdk.jmh.annotations.*
8+
import org.openjdk.jmh.infra.*
9+
import java.util.concurrent.*
10+
11+
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
12+
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
13+
@Fork(value = 2)
14+
@BenchmarkMode(Mode.AverageTime)
15+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
16+
@State(Scope.Benchmark)
17+
open class YieldRelativeCostBenchmark {
18+
19+
@Param("1", "10", "100", "1000")
20+
var iterations: Int = 10
21+
22+
@Benchmark
23+
fun yields() {
24+
repeat(iterations) {
25+
Thread.yield()
26+
}
27+
}
28+
29+
@Benchmark
30+
fun spins(bh: Blackhole) {
31+
repeat(iterations) {
32+
bh.consume(it)
33+
}
34+
}
35+
}

core/kotlinx-coroutines-core/src/scheduling/CoroutineScheduler.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ internal class CoroutineScheduler(
251251
private val isTerminated: Boolean get() = _isTerminated.value != 0
252252

253253
companion object {
254-
private const val MAX_SPINS = 1000
255-
private const val MAX_YIELDS = MAX_SPINS + 500
254+
private val MAX_SPINS = systemProp("kotlinx.coroutines.scheduler.spins", 1000, minValue = 1)
255+
private val MAX_YIELDS = MAX_SPINS + systemProp("kotlinx.coroutines.scheduler.yields", 0, minValue = 0)
256256

257257
@JvmStatic // Note, that is fits into Int (it is equal to 10^9)
258258
private val MAX_PARK_TIME_NS = TimeUnit.SECONDS.toNanos(1).toInt()

0 commit comments

Comments
 (0)