We often take for granted the idea that Kotlin Coroutines are better than Threads in terms of performance. I decided to put this hypothesis to test by implementing a real-world benchmark and see the actual metrics. In this post, I’ll summarize my findings, which, I guess, might surprise some of the readers.
Coroutines vs Threads Benchmark
To compare the performance of Kotlin Coroutines and standard Threads I implemented a new benchmark in my open-sourced TechYourChance application.
The basic idea behind this benchmark is to launch many background tasks sequentially and, for each task, measure the time between a call that starts the task and the first instruction inside it. This duration, which I call “startup duration”, is basically the latency associated with transferring some work to the background.
The benchmark exercises three different background work mechanisms:
- A new bare Thread is created and started for each task.
- A new Coroutine is launched for each task.
- Tasks are submitted to a single thread pool for execution.
Obviously, the results of this benchmark will vary between different devices. If you’d like to run the benchmark yourself, then just install the TechYourChance app on your own device (see the installation instructions on GitHub).
Coroutines vs Threads Startup Results
I ran the benchmark on two devices:
- Samsung Galaxy S7, which is the oldest device in my possession which is capable of running the TechYourChance app.
- Samsung Galaxy S20 FE, which is my current daily driver.
Galaxy S7 is ancient and weak device, so it should provide a good approximation of the worst case performance. Galaxy S20 FE is also relatively old and relatively weak device by today’s standard, so its performance will reflect the average experience of the bottom percentiles of your users.
These are the results (please note that the chart in TechYourChance app is zoom-able):
It shouldn’t come as a surprise that instantiating and starting a new Thread to handle each background task yields the worst performance. This approach also demonstrates the highest variance. Relatively many data points fall at 200% of the average startup duration or more.
The surprising result is that using Coroutines incurs ~50% startup time overhead compared to a standard thread pool. So, the generally accepted idea that Coroutines are faster than Threads turns out to be incorrect.
The most important observation, however, is that all these performance considerations are pretty much irrelevant in the context of Android development. Note that even on Galaxy S7, which is a dinosaur device, instantiating and starting a new Thread takes less than 1 millisecond on average, and even the slowest startups take just several milliseconds. On newer devices, everything is much faster. This overhead is negligible for absolute majority of Android applications out there. Unless your app starts hundreds of background tasks, you can happily use bare Threads for background work.
Benchmark’s Source Code Video Review
This section contains a premium content for TechYourChance members exclusively.
This benchmark demonstrated that Kotlin Coroutines aren’t necessarily faster than Threads. Sure, I can construct a benchmark where Coroutines will show better results, but the scenario that I tested here is actually the most common use case for Coroutines in Android applications. Therefore, in the context of Android development, I expect the average performance impact of Coroutines to be in the ballpark of the results described in this article.
In the next post, I’ll benchmark and compare the memory consumption of Coroutines and Threads.
As always, thank you for reading. If you liked this article, consider subscribing to my email list to be notified about new posts.