Tokio: join_all but for multiple Tasks


#1

futures has a function called join_all which is the most obvious choice in the situation: “I have a bunch of Futures and I want to collect and then do something with all their results once they’re all done.”

I ran into a curious situation building an experimental HTTP benchmarking tool using Tokio. Rather simple code here: https://github.com/fpgaminer/http-dyno/blob/45b4e59ac066709d2f8880195086ed30943bb1b5/src/main.rs

The gist of it is that I have a bunch of futures hammering a server with requests for some period of time. After they’re all done I want to collect up and analyze stats from them to report back to the user.

Again, join_all being the obvious choice here, it was my first thought. However using it would have shoved all those Futures into a single Task, killing performance. (IIUC a Task can only execute on a single thread at any given moment)

Luckily in my scenario there’s a simple, performant solution (Arc<Mutex<Vec<_>>>). But it got me wondering if Tokio has a more general solution. join_all but where all the sub-futures become their own Tasks.

Obviously this can be implemented manually, but just wondering if Tokio or Futures has something built-in?


#2

You could create a oneshot pair for each task, spawn off a future that runs the HTTP request and then sends the result through the channel, and then join_all on the oneshot receivers.


#3

You can use https://docs.rs/futures/0.1.22/futures/stream/fn.futures_unordered.html to turn them into a stream that you can then fold or iterate over.


#4

It’s not clear to me from the documentation if futures_unordered creates a single Task that contains all the futures? If so that’s what I’m trying to avoid (as that would have severe performance implications since the Task can only run on a single thread at any given moment. Unless I’m mistaken).


#5

futures_unordered is still going to be a single task, yeah.


#6

My understanding was that FuturesUnordered allows inner futures (and whatever tasks back them) to run independently. Whenever an inner future is notified, it’s placed on a separate queue that FuturesUnordered then polls and delivers the future’s result off. So there’s a bit of indirection happening with the queue, but it seems like the inner futures run independently. FuturesUnordered has its own task, but that shouldn’t prevent the underlying futures from making independent progress.

I might be wrong though.