Closed wravery closed 1 week ago
Here's a link to the resolver-visitor
branch, I'll keep it around for now. But I'm going to shelve this issue unless someone else wants to pick it up.
I decided I was over complicating it by trying to turn the visitor into another awaitable and defer execution till it was done. Instead, I'm accumulating a list of "tokens" and then playing them back against the visitor when the resolvers have all finished. The list of tokens can be built in arbitrary orders or in parallel and spliced together without additional copies.
Here's a link to the simplified approach in visitor-queue
. I was able to eek out a little more performance from client_benchmark
(about 3%) with a prototype state machine visitor building the client Response
from the ValueTokenStream
. Now I'm investigating what it will take to auto-generate the visitor object complete with a state machine that bypasses conversion to a rersponse::Value
.
Not sure this is going to work, at least not with better performance. The problem is the entries in a map or list need to be added in the correct order, which means the visitor methods need to be called in that order. I can get it to do that, but it ends up serializing all of the resolvers, whereas without that, resolvers can run in parallel, and the results can be gathered from nested fields and merged together in order as they complete.
With the
client_benchmark
performing 100000 iterations on my laptop in high performance mode, I'm currently getting a max throughput of about 33k req/s on thenext
branch, and theresolver-visitor
branch is only getting about 31k req/s. Theresolver-visitor
branch is also not passing unit tests right now because theBlockingAsyncExpensive
case actually tests that all of the resolvers are allowed to run in parallel, and because it needs to serialize access to the visitor, theresolver-visitor
branch cannot do that, at least not without adding another thread. I haven't been able to come up with an elegant enough awaitable type for the visitor itself to be able to make the later resolvers wait and resume the first one without blocking.