Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Ask less expensive #3784

Open
Horusiath opened this issue May 14, 2019 · 2 comments
Open

Make Ask less expensive #3784

Horusiath opened this issue May 14, 2019 · 2 comments

Comments

@Horusiath
Copy link
Contributor

Horusiath commented May 14, 2019

Current state

Right now the way, how Ask is realized internally upon call, is that we're creating a temporary lightweight actor instance instance, that is supposed to asynchronously await for the response on our behalf.

This means, that we need to:

  1. Allocate actor - even if it's lightweight, it's still not alloc-free operation.
  2. Register/unregister temporary actor every time, so that it can be accessed by it's path (necessary to make Ask working with remote transparency mechanics). This also takes time.

All of that makes our Ask calls orders of magnitude slower and more expensive than standard Tell communication.

Idea

We could possibly change the mechanics behind Ask method to make use of pooling together with ValueTask<T>: it's an awaitable value type (convertible to Task<T> via AsTask method).

ValueTask<T> allows to use special constructor in form:

ValueTask<T>(IValueTaskSource<T> notifier, short correlationId)

This way it can be used together with pooling mechanisms, to make async await work without allocating TaskCompletionSource every time.

Example usage is new .NET socket I/O - SocketAsyncEventArgs are implementing this interface, making methods like Socket.SendAsync possible to work without any extra allocations.

How we could use this in akka? The idea is: instead of registering/deregistering Ask listener actors on every Ask call, we could keep a (dynamically resizable) pool of registered listeners, that could be reused across many Ask calls.

In order to correctly deadletter overdue responses - which may have arrived after ask has been timed out or cancelled, and actor was reused for another request - we have a second ValueTask parameter, which is correlationId.

This would mean, that we'd need to keep correlationId in Ask listener and pass it through back and forth. It's pretty easy in local communication (we already have such "magic number" in the Envelope itself), but probably would require something extra in terms of remote communication (maybe we could reuse ActorPath UIDs for that?).

@Aaronontheweb
Copy link
Member

I think it'd be possible to have a ThreadLocal pool of re-usable Ask actors that can be "reset" back to an empty set and re-used. I don't know about the IValueTaskSource<T> because I haven't worked with it at all hardly. Open to the idea.

@Bishnup1995
Copy link

Hey Horusiath,
Did you find any way to avoid creating of internal actor for every ask request?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants