Halina
07/22/2025, 10:58 AMArjan van Wieringen
07/22/2025, 3:06 PMmarcinmoskala
07/22/2025, 3:43 PMKirill Grouchnikov
07/22/2025, 5:25 PM<http://Dispatchers.IO|Dispatchers.IO>.limitedParallelism(n)
is the best answer?marcinmoskala
07/22/2025, 7:25 PMArjan van Wieringen
07/23/2025, 5:39 AMmarcinmoskala
07/23/2025, 8:01 AMArjan van Wieringen
07/23/2025, 8:59 AMmarcinmoskala
07/23/2025, 9:44 AMWout Werkman
07/29/2025, 8:00 AMrunInterruptible {}
, and I've been waiting for a long time for someone to post an article about whether runInterruptible
is more efficient on virtual threads because I've been procrastinating investigating it myself :p
Naively I'd expect runInterruptible
to be orders of magnitude more efficient on virtual threads, and it should be a no-brainer to wrap all blocking IO calls in a virtual thread dispatcher + runInterruptible
. But my rudimentary benchmark doesn't show as much of a performance improvement as I expected.
(I've attached my benchmark result of cancelling a bunch of runInterruptible
coroutines using <http://Dispathers.IO|Dispathers.IO>
and a virtual thread dispatcher, but please take it with a bucket of salt! This is NOT a proper benchmark)marcinmoskala
08/08/2025, 5:30 AMmarcinmoskala
08/08/2025, 5:34 AMsuspend fun main() = coroutineScope {
repeat(1000) {
launch(Dispatchers.Default) {
Thread.sleep(1000)
val threadName = Thread.currentThread().name
println("Running on thread: $threadName")
}
}
}
vs
suspend fun main() = coroutineScope {
val dispatcher = Executors.newVirtualThreadPerTaskExecutor()
.asCoroutineDispatcher()
repeat(1000) {
launch(dispatcher) {
Thread.sleep(1000)
println("Running")
}
}
}
The first one should take around 16 seconds, the second one 1-2 seconds.marcinmoskala
08/08/2025, 5:35 AMrunInterruptible
is AFAIK primarly a JVM-specific withContext
with interruption mechanismWout Werkman
08/08/2025, 11:05 AMDispatchers.Default
vs that of the loom thread dispatcher.
If I use <http://Dispatchers.IO|Dispatchers.IO>.limitedParallelism(1000)
vs Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
, it results in 1.074489041s
vs 1.053347041s
, which is not a result anyone should take any conclusions from imho.
I'm not denying that virtual threads might be faster for IO, I was instead curious about cancellation performance.
runInterruptible
is an extremely useful utility that allows you to cancel IO calls on the JVM. Yet barely anyone uses it, it has literally 77 times less usages than <http://Dispatcher.IO|Dispatcher.IO>
(21k vs 271 public usages).
I assume that the reason is that people are worried about the cost of interrupting a thread, because they are expensive.
But this could in theory be negated with virtual threads from what I naively expect. But noone talks about it for some reason...marcinmoskala
08/08/2025, 11:49 AMrunInterruptible
to have faster cancellation together with Loom dispatcher to avoid costs of recreating threads. It might make sense in some kinds of system, like those with CPU-intensive non-suspending functions, however in most cases cancellation mechanism should be quite efficient, and I wouldn't expect any significant improvement from replacing it with thread interruption.Wout Werkman
08/08/2025, 12:57 PMrunInterruptible
!marcinmoskala
08/08/2025, 1:01 PM