4

In the task documentation, there's a section that talks about calling blocking code in async, and how that should be avoided as to not block the async thread too much (https://docs.rs/tokio/1.21.2/tokio/task/index.html#blocking-and-yielding).

It also talks about using tokio::task::spawn_blocking for these tasks, but I'm wondering at which point sending the work to a different thread is recommended? I'm currently writing a program that recovers a large amount of ecdsa signatures, which takes about 100 microseconds per message, all while doing a large amount of network IO. As a concrete example, is this enough to use things like spawn_blocking?

boston
  • 95
  • 1
  • 7
  • Does this answer your question? [Why does Future::select choose the future with a longer sleep period first?](https://stackoverflow.com/questions/48735952/why-does-futureselect-choose-the-future-with-a-longer-sleep-period-first) – Ömer Erden Nov 23 '22 at 13:46
  • This might explain better: [What is the best approach to encapsulate blocking I/O in future-rs?](https://stackoverflow.com/questions/41932137/what-is-the-best-approach-to-encapsulate-blocking-i-o-in-future-rs) – Ömer Erden Nov 23 '22 at 13:49
  • 2
    I'm not sure that I agree this is a duplicate of those questions - those questions are about how to handle blocking, but this question is more about "what *is* blocking (and how much is too much)". – Joe Clay Nov 23 '22 at 13:55
  • Thanks, these are both very helpful posts but not exactly what I was looking for. – boston Nov 23 '22 at 14:11

1 Answers1

9

Alice Rhyl (one of the Tokio devs) has a good blog post on blocking in async code.

One of the key parts for your question:

To give a sense of scale of how much time is too much, a good rule of thumb is no more than 10 to 100 microseconds between each .await. That said, this depends on the kind of application you are writing.

Given that you're spending 100 microseconds per message, I think you're likely to be getting to the point where moving to a different thread is the right call.

The post also gives a rule of thumb for how to approach moving the work onto different threads:

  • For synchronous IO, use spawn_blocking.
  • For CPU-bound computation, use a separate fork-join thread pool like rayon.
  • For synchronous work that runs forever (e.g. listening to a database connection), spawn your own dedicated thread to avoid taking one away from the Tokio/Rayon pool.
Joe Clay
  • 33,401
  • 4
  • 85
  • 85
  • Thank you! I remember reading that article a while back but didn't get a full grasp, will revisit. And in this case, since recovering signatures is CPU bound, isn't something like Rayon the best solution? (I've never used it but have heard about it) – boston Nov 23 '22 at 14:09
  • 1
    @boston: Ah, I thought the network IO you mentioned was the big factor, rather than the signature bit. In that case, Rayon could be worth looking into - that tends to be most effective when the work can be broken down into smaller chunks that can be distributed across CPU cores. – Joe Clay Nov 23 '22 at 14:16