7 ms·
Kotlin Coroutines vs. Threads Performance Benchmark
- jqpabc123 3y agoConclusion: A thread pool is the preferred solution and the go to that I have been using for years. The tricky part is optimizing the size of the pool.
- sgammon 3y agoI'm not sure a cached pool of OS threads is comparable in this way to coroutines. The author also isn't doing any I/O bound work... or... any work at all? You can schedule coroutines over a threadpool. From the author's code, I'm not even sure this was accounted for in the benchmark. If they are using a threadpool and vanilla coroutines, without specifying where they are scheduled, yes, of course it won't start up a pool of threads for you and be as fast as a pool of threads. It would be terrible if it did that by default.
- tadfisher 3y agoIt does do this by default; Dispatchers.Default wraps a ThreadPoolExecutor with n threads, where n is the number of logical CPU cores. This is used for computation-bound work. The other built-in Dispatcher instances are Dispatchers.IO, with an unbounded thread pool for I/O-bound work; and Dispatchers.Main, which schedules on your platform's UI thread.
- jillesvangurp 3y agoThe IO dispatcher is a fixed size threadpool of 64 threads or the number of cpu cores (whichever is larger). You can use the kotlinx.coroutines.io.parallelism system property to override the default of 64.
- sgammon 3y agoThat would require `withContext(Dispatchers.IO)`...
- sgammon 3y agoIs this the default on all platforms, Android, or JVM?
- hota_mazi 3y agoThat's really not the conclusion, and not something you should seriously do. The rule of thumb should be: - Is your task CPU bound? Put it in a thread pool. - Is your task spending most of its time waiting (e.g. network call)? Use a coroutine
- manmal 3y agoCoroutines also run on a thread pool, and usually the pool size is optimized for the device‘s CPU.
- jillesvangurp 3y agoYou can schedule co-routines on a thread pool. Any Threadpool can be trivially turned into a co-routines dispatcher. Including project loom backed ones (not available on Android of course). The comparison is nonsensical. It's not an either or kind of thing. If your co-routines are doing any blocking IO, you'd use the built in IO dispatcher, which is backed by a thread pool or you'd create your own dispatcher. Each co-routine gets its own thread if you use that; which can then do blocking IO and block the thread. Very common on the jvm if you are interacting with blocking IO things. I was working on some code that needed this yesterday. The examples in this "benchmark" don't make any sense. It's not clear what the work is or why threads/co-routines are even needed. Handling UI events with threads is not a thing. But co-routines are very useful for that. Because they have very low overhead.
- sgammon 3y agoWell that was a frustrating read, largely because of the comment section
- sgammon 3y agoPlease don't downvote me? I'm referring to the comment section, mostly
- koito17 3y agoDeeply flawed methodology. This comment in the blog post summarizes it best > Under the hood, [Kotlin] coroutines are runnables with an additional parameter representing the continuation, running on an executor. If you are not going to make any use of the continuation, you do not need the coroutines, you are just adding overhead over runnables and fixedthreadpools (which is what you measure).
- rawrawrawrr 3y agoDoesn't seem like there's anything wrong with the methodology in the quote. It's perfectly fine to compare two things, even if one is not meant for the task.
- refulgentis 3y agoI don't understand: the methodology is flawed because it was empirical, and it could have just been argued from theory? Sure, but, 15 years in a software career...I think the best parts have always come from checking what things did in theory versus practice
- yafetn 3y agoLest someone new to Kotlin only reads the title and decides to spawn bare threads all over the place, don’t. At first pass, it's not clear to me what the actual work the author is doing in his benchmark. Instead, I’d point to the excellent work of Marcin Moskala’s Kt.Academy[0] and other resources on the topic. [0] https://kt.academy/article/cc-best-practices https://kt.academy/article/cc-best-practices
- zekrioca 3y agoThere aren’t numbers in your reference, just a set of recommendations. The author of the post tried to compare the performance overheads of using Coroutines over threads, that’s all.
- asdfaoeu 3y agoThe issue with OPs is that it's really a case where coroutines would have any advantage so it's kind of pointless.
- zekrioca 3y agoI agree OP could have been more elaborate, but the main takeaway is that goroutines still cost at least 50% that of normal threads (though this needs to be verified in normal servers as well, not just smartphones). With goroutines, though usually supported by runtime systems (e.g., in Go), applications are responsible to manage tasks (e.g., HTTP requests) in order to harvest goroutines benefits, whereas this is easier with threads. Edited for clarification.
- otabdeveloper4 3y agoWhat, you mean using threads doesn't automatically make your machine slow to a crawl because of le ""context switches""?? Heresy! But my async!
- taeric 3y agoI went in expecting an odd explanation about how threads are inherently evil. Was surprised to see a speed comparison that largely confirmed that creating threads is the slowest option. And mostly delighted to see the admission that even thread creation is not hugely problematic. The language affordances of coroutines are not to be ignored. But performance is a siren song.
- otabdeveloper4 3y agoCreating threads is about the fastest of all system calls. (Still a system call, though, and predicated on all the other runtime crap to support the obscure pthreads API features.)
- lawgimenez 3y agoFrom my limited understanding is that Coroutines can suspend themselves (unless you use some dispatcher I guess), while Threads you have to invoke suspend(). Right? Thus comparing the two of them is insane.
- Jtsummers 3y agoSince Kotlin threads appear to be Java threads (NB: Just following their documentation, I've never used Kotlin and have only started learning it because we may use it at work for some things in the near-ish future), you oughtn't use Thread::suspend. It's been deprecated for a while. Java threads are preemptible, though. You don't need to explicitly suspend them (even if that method weren't deprecated) since the scheduler will happily do it for you.
- tadfisher 3y agoCoroutines are always scheduled on a Dispatcher; if you don't provide one in launch() it will use Dispatchers.Default. But under the hood, Dispatchers schedule on Executors, usually ThreadPoolExecutor. So the more apt technology to compare to is VirtualThread with structured concurrency, and the takeaway should be that coroutines are an ergonomic way to manage concurrent work. Secondarily, they _can_ improve performance by reducing the number of OS threads, if the alternative were to naively spawn them and wait on I/O or something.
- avianlyric 3y agoIt’s a little more complicated than that, and Kotlin co-routine docs aren’t great at describing the internals of the coroutines infrastructure. Coroutines by default are launched without an dispatcher, and simply create a single threaded event loop that schedules a single co-routine at a time to use the thread that created the coroutine scope. In this world the is no multithreading and coroutines can’t be moved between system threads, and no thread pools or equivalent are created. However in most uses of coroutines (especially on Android), you’ll have a framework that already created a coroutine scope (using something like runBlocking) and provided the default dispatcher as the coroutine scopes dispatcher, which is then automatically inherited by any child coroutines or coroutines scope. Personally I wished they had called Dispatchers.Default something else, it’s a confusing and slightly misleading name, given Dispatchers aren’t a hard requirement to use coroutines. But I agree the big think to take away from Kotlin coroutines is structured concurrency, and the ergonomics to provides to manage concurrent operations in simple clean manner that avoids nasty resource leakages. For everything else in coroutines, it’s pretty easy to reconfigure the coroutine runtime to use also most any underlying threading model, including ThreadPools, Singleton Threads, virtual threads, and my favourite, no additional threads whatsoever. The above means that measuring coroutine startup and creation time and comparing it to threads kinda misses the point of using coroutines in the first place. And it’s possible to make coroutine creation cheaper or more expensive, depending on how exactly you’ve setup your coroutine scope, and which dispatchers (if any) you’re using.
- DotaFan 3y agoTo get anyone from outside of Android ecosystem up to speed, blog author has always had controversial opinions. He's been trying to prove to the community that new Compose library is for what worse than decade old imperative UI system on Android for the past couple of years.
- pjmlp 3y agoI agree with several of his opinions, many in the Android community cargo cult opinions from influencers in the Android team, sorry Developer Advocates, without any thoughts of how real they happen to be.
- carstenhag 3y ago(I am aware of the author). The opinion not mentioned is not flawed. Some basic things were definitely missing when Compose was made 1.0. Now 2 years later, there are still features open that some consider basic. My opinion as an android dev: he's definitely very knowledgeable, but you should also hear other opinions. It's not a writer you should listen to blindly.
- csydas 3y agowill take advantage of your knowledge for a question and also a comment to your post. No writer should be trusted blindly but i don’t thjnk you meant that literally ;) just for others to see. all information should have some verification even from trusted sources. on the author though, can you share a bit about the subject and the author’s participation in android dev? i’ve never touched android anything dev wise so i’m not even sure if the topic being discussed is a new and unexplored territory, explored but still evolving, or if it’s something the community has settled on with plenty of data to back their conclusion. similarly im not sure if the author is really skilled enough to have controversial opinions or if it’s more they’re alright but excitable. how would you position the two points just from your point of view? i get the impression the author of the article is controversial and also opinionated i. “unusual” ways — for me, such persons are interesting to hear from even if a lot of their ideas are non-sense or fanciful. “out-there” ideas often are just out there for no reason, but sometimes there’s gold in such ideas, or good thoughts and starting points for actual golden ones. wiuod you personally at least listen to this author just in case there’s some gold or are they very much so just in their own world?
- wiseowise 3y agoShowing results without any code is silly.
- simon_void 3y agohere is the talk by Roman Elizarov, the Kotlin project lead, at KotlinConf 2023 about Coroutines vs VThreads: https://www.youtube.com/watch?v=zluKcazgkV4 https://www.youtube.com/watch?v=zluKcazgkV4 short version: both were optimized for different things, so there is a place for both of them.
- MrBuddyCasino 3y agoDidn’t find it too convincing. He says Coroutines are for fine-granular concurrency, and Virtual Threads for coarse thread-per-request apps. In my experience fine granular concurrency is rare, and thread-per-request is 99% of the work. This might be different for Android development, but its true for server side code. The cost you pay for this gain in fine grained control (which you rarely need) is unfortunately the colored function problem, and in my experience it is rather annoying. Also the complexity of having a second unit-of-computation abstraction besides threads, and Coroutines aren’t a small API surface (goes along with Flows). tl;dr: the inferior developer experience (added complexity, colored functions) must be offset by a gain in usefulness, and I just don’t see it
- yawboakye 3y agoone of the (main) benefits of coroutines is the luxury of offloading thread management and semantics to a lower layer so that we can deal in functions/methods (aka units of execution) as units of concurrency. obviously that comes at a cost, like any general purpose convenience.
- wiseowise 3y agoFunction color is a feature: https://elizarov.medium.com/how-do-you-color-your-functions-a6bb423d936d https://elizarov.medium.com/how-do-you-color-your-functions-...