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

DedicatedThreadPool Performance Optimization #4537

Closed
Aaronontheweb opened this issue Aug 5, 2020 · 5 comments
Closed

DedicatedThreadPool Performance Optimization #4537

Aaronontheweb opened this issue Aug 5, 2020 · 5 comments

Comments

@Aaronontheweb
Copy link
Member

Version: 1.4.9

Prior to merging in #4511 , which moves all /system actors on a ForkJoinDispatcher, we need to go about improving the structure of the DedicatedThreadPool class (used by the ForkJoinDispatcher), which is sourced originally to this repository:

https://github.com/helios-io/DedicatedThreadPool

But that source code is referenced as a file directly into our project here:

https://github.com/akkadotnet/akka.net/blob/dev/src/core/Akka/Helios.Concurrency.DedicatedThreadPool.cs

The two code bases don't diverge much and that can easily be remedied. The last real change we made to the DedicatedThreadPool class was in 2017: #2714

Problems that #4511 May Introduce: Rise in Idle CPU Consumption

Running all of the /system actors on the ForkJoinDispatcher will help improve performance in really busy systems - it will allow workloads for Akka.Persistence, Akka.Cluster, etc independently from the primary .NET ThreadPool since the /system actors have to operate under real-time constraints. The only point of contention will be the OS thread scheduler, rather than the length of the work queue inside the shared ThreadPool. We've used this strategy successfully for years with Akka.Remote.

The problem is: what happens with ActorSytem instances that make virtually zero use of /system actors, such as the ActorSystems that are used inside stand-alone plugins like https://github.com/petabridge/Petabridge.Tracing.Zipkin ? The answer is we may start seeing increases in background CPU consumption as worker threads spin / wait / sleep for work, such as what we can observe with the HashedWheelTimerScheduler here: #4031

We need to avoid that to the extent that it's possible by freeing threads that don't have enough work do to and by adding new threads gradually as total demand increases. Spinning threads up and down is expensive, but having idle threads continuously sleeping and checking for work is also very expensive. We need to reach a happy medium.

Existing Issues with DedicatedThreadPool

Other issues that the DedicatedThreadPool has currently:

  1. End-to-end throughput - it's at least 20% slower than the ThreadPool and possibly moreso now since the DTP hasn't benefited from any of the performance improvements introduced during subsequent versions of .NET Core. Finding ways to improve this through the queuing mechanism, thread activation, and work distribution would be a huge plus.
  2. Thread pool scaling - I'm not confident that the current system is as effective as it should be when it comes to scaling thread pool usage relative to hardware. We should be able to scale the thread pool up and down relative to the number of virtual cores, unless explicitly overridden by configuration. Right now it seems that we only have the ability to explicitly configure the threadpool by computing what the desired number of threads should be externally. While we want to preserve the ability to explicitly limit the number of threads a DedicatedThreadPool can use, by default the DedicatedThreadPool should be able to dynamically adjust its thread count based on both demand and the number of virtual CPUs the machine has.

Constraints

  1. Need to stick with .NET Standard 2.0 for now - adding dual targeting to Akka.NET is a substantial project and we are not prepared to do that yet.
  2. All changes made to the DedicatedThreadPool need to have their own independent benchmarks, so it's probably best if we direct that work to the https://github.com/helios-io/DedicatedThreadPool repository (or fork it into this organization) and avoid running the giant Akka.NET test suite every time we make changes.
  3. Need to achieve both: very low IDLE CPU footprint and better throughput.
@Zetanova
Copy link
Contributor

I got already an issue with it #4636

@Aaronontheweb could u try my commit https://github.com/Zetanova/akka.net/tree/helios-idle-cpu
I am somehow not capable to run the MultiNodeRunner

I try https://github.com/helios-io/DedicatedThreadPool out

@Aaronontheweb
Copy link
Member Author

Made some massive progress towards replacing the DedicatedThreadPool here: #4882

This should get merged into v1.4.19 and deployed as an opt-in feature, with an area for collecting feedback from users here: #4983

@Aaronontheweb Aaronontheweb modified the milestones: 1.5.0, 1.5.1 Mar 2, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.1, 1.5.2 Mar 15, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.2, 1.5.3 Apr 6, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.3, 1.5.4, 1.5.5 Apr 20, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.5, 1.5.6, 1.5.7 May 4, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.7, 1.5.8 May 17, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.8, 1.5.9 Jun 15, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.9, 1.5.11 Jul 25, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.12, 1.5.13 Jul 27, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.13, 1.5.14 Sep 20, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.14, 1.5.15 Nov 29, 2023
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.15, 1.5.16 Jan 10, 2024
@Aaronontheweb
Copy link
Member Author

Workaround

If you're running into CPU consumption issues today, you can always disable the internal-dispatcher (which uses the DedicatedThreadPool) using the following HOCON:

akka.actor.internal-dispatcher{
      type = "Dispatcher"
      executor = "default-executor"
}

This will have it run on the built-in .NET ThreadPool instead of the DedicatedThreadPool.

@Zetanova
Copy link
Contributor

#7074 It is a PR to improve the default-executor and channel-executor

@Aaronontheweb Aaronontheweb modified the milestones: 1.5.16, 1.5.17 Jan 31, 2024
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.17, 1.5.18 Mar 5, 2024
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.18, 1.5.19 Mar 12, 2024
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.19, 1.5.20 Apr 15, 2024
@Aaronontheweb
Copy link
Member Author

Kind of solved this in v1.5.18 by just not using the DedicatedThreadPool.

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

No branches or pull requests

2 participants