I just finished cleaning up the codebase, and I’m currently busy with a few performance optimizations for the most frequent tasks, namely:
- Fetching preview images for feed entries
- Importing feeds from OPML files
- Fetching new and updated feed entries in standalone mode
The initial prototype processed HTTP requests one by one in a single thread, but I quickly switched to batch processing in order to speed things up. Each batch spawned a separate thread for every request, enabling the app to process a few requests in parallel. That helped, but the app still had to iterate through those batches sequentially, so every batch was always blocked by a previous batch, which in itself was blocked by its slowest request. That might be less of an issue if all your requests take the same time to execute, but it’s certainly not the case for a feed reader app.
It’s possible to avoid batch processing and just spawn an independent thread for every request, but that approach won’t scale. Spawning thousands of threads is wasteful, and it can lead to unpleasant side effects. Most smartphones can handle insane amounts of concurrent threads but those threads still need to compete for a single network connection, which might be pretty slow. In other words, having too much concurrent requests can add per-request latency.
A better approach is to create a fixed number of concurrent and fully independent workers. A few slow queries can block some of those workers, but they’re unlikely to bring the whole pool to a halt, which boosts total bandwidth without sacrificing per-request latency. There are many ways to implement such a feature, but I used this task as an excuse to try Kotlin Channels. Here is the quote from the doc:
Multiple coroutines may receive from the same channel, distributing work between themselves
This approach is called fan-out and this term seems to be borrowed from digital electronics. I tried it, and it helped me to speed things up by an order of magnitude.