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

Add new ChannelTaskScheduler Extension #5403

Merged
merged 6 commits into from
Dec 3, 2021

Conversation

Zetanova
Copy link
Contributor

As requested inlining https://github.com/Zetanova/Akka.Experimental.ChannelTaskScheduler into akka and using it as the default
channel-executor

usage like before, the channel-executor.priority config value is already set in the default config.

to activate use:

akka.actor.default-dispatcher = {
    executor = "channel-executor"
    #channel-executor.priority = "normal"
}

akka.actor.internal-dispatcher = {
    executor = "channel-executor"
    #channel-executor.priority = "high"
}

akka.remote.default-remote-dispatcher {
    executor = "channel-executor"
    #channel-executor.priority = "high"
}

akka.remote.backoff-remote-dispatcher {
    executor = "channel-executor"
    #channel-executor.priority = "low"
}

All akka components can thread-safe access the priority TaskSchedulers over:

ChannelTaskScheduler.Get(system).High
ChannelTaskScheduler.Get(system).Normal
ChannelTaskScheduler.Get(system).Low
ChannelTaskScheduler.Get(system).Idle

Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks fine - I think we also need to add:

  • Some configuration specs, validating that default values can be successfully changed via HOCON
  • Some documentation additions to https://getakka.net/articles/actors/dispatchers.html explaining how to configure this and when you might want to use it
  • Address some of my inline comments, which ask for more clarification on the internals so other contributors / maintainers can reason about the algorithm.

src/core/Akka/Akka.csproj Outdated Show resolved Hide resolved
src/core/Akka/Configuration/Pigeon.conf Show resolved Hide resolved
src/core/Akka/Configuration/Pigeon.conf Show resolved Hide resolved
src/core/Akka/Dispatch/ChannelSchedulerExtension.cs Outdated Show resolved Hide resolved
Copy link
Contributor Author

@Zetanova Zetanova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the comments and rollback dependency to 5.0.0

@Zetanova
Copy link
Contributor Author

Zetanova commented Dec 3, 2021

About the https://getakka.net/articles/actors/dispatchers.html where to edit it, can u do it?
The only thing that changed between the last channel-executor and this,
is the channel-executor.priority config instead of the max-concurrency calculated from fork-join-executor settings

@Aaronontheweb
Copy link
Member

Aaronontheweb commented Dec 3, 2021 via email

@Aaronontheweb
Copy link
Member

About the https://getakka.net/articles/actors/dispatchers.html where to edit it, can u do it?

Yep, I can do it.

@Aaronontheweb Aaronontheweb enabled auto-merge (squash) December 3, 2021 15:54
Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


sealed class PriorityTaskScheduler : TaskScheduler, IDisposable
{
readonly Channel<Task> _channel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be any benefit to just using a minimal abstraction over ConcurrentQueue<Task> for the common case of multiple readers (where an UnboundedChannel would be created)?

I mention this because UnboundedChannel.TryWrite involves a lock on a parent SyncObject, which is primarily used for waiting completions and async readers. This will likely impact max throughput. We do use the Async Completions in the control section but I wonder if there is a way we can work around that too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will take a look the the Channel source code too.

In the end the queue should lock-free queued and dequeued,
where adding side should have the heavier processing (splitting in priorities)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked up and yes there is a brief lock.

The lock will not have much perf. issue on the executing thread itself,
but of course on locking threads. Don't know why it is not implemented in a free-lock style.
Maybe the lock collation just don't happen that often to take it into effect?
I trust "stephentoub" in his decision to use a lock here.

The lock statement is on itself is not "bad" but misused it can be very fast.

The main benefit of the Channel class is following line, it is reducing the idle cpu to near zero:
https://github.com/dotnet/runtime/blob/386f87139c7fe48f6b733e3cae6d09128c827192/src/libraries/System.Threading.Channels/src/System/Threading/Channels/UnboundedChannel.cs#L295

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked now the UnboundedChannel.TryWrite code

  1. it would be absolutely hard not to use a sync lock
  2. Collisions are very unlikely, even under heavy queue load

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

Successfully merging this pull request may close these issues.

3 participants