-
Notifications
You must be signed in to change notification settings - Fork 341
Increase ThreadPool.MinThreads limit #3845
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| using System.Globalization; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Threading; | ||
|
|
||
| using Microsoft.VisualStudio.TestPlatform.CommandLine.Internal; | ||
| using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors; | ||
|
|
@@ -60,6 +61,20 @@ internal class Executor | |
| /// </summary> | ||
| public Executor(IOutput output) : this(output, TestPlatformEventSource.Instance, new ProcessHelper(), new PlatformEnvironment()) | ||
| { | ||
| // TODO: Get rid of this by making vstest.console code properly async. | ||
| // The current implementation of vstest.console is blocking many threads that just wait | ||
| // for completion in non-async way. Because threadpool is setting the limit based on processor count, | ||
| // we exhaust the threadpool threads quickly when we set maxCpuCount to use as many workers as we have threads. | ||
| // | ||
| // This setting allow the threadpool to start start more threads than it normally would without any delay. | ||
| // This won't pre-start the threads, it just pushes the limit of how many are allowed to start without waiting, | ||
| // and in effect makes callbacks processed earlier, because we don't have to wait that much to receive the callback. | ||
| // The correct fix would be to re-visit all code that offloads work to threadpool and avoid blocking any thread, | ||
| // and also use async await when we need to await a completion of an action. But that is a far away goal, so this | ||
| // is a "temporary" measure to remove the threadpool contention. | ||
| var additionalThreadsCount = Environment.ProcessorCount * 4; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand the concept of pushing the thread pool limit higher, but I'm not sure I get why we decided to raise the limit to exactly 4 times the processor count. Is "4" a constant determined empirically that yielded the best throughput overall or is there a more formal reason for choosing it ?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used 2*, then 5* then 10*. I know that the path blocks 2-3 threads, and there are double the threads blocked when you have a datacollector. So it is part empirical, part guessing.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I ended up with 1* (workerThreads) + 4*, to land on 5*
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would write that down as comment in code for later.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you tried to change only the worker one? I don't know the I/O one makes difference here, we're not async at all so I suppose we're not using it. And let's hope to not generate too much contention/memory consuption(if I recall well on .NET 1mb per stack is commited) on huge test servers 🤞 const heuristics is...meh... Maybe if it's only for VS responsiveness we could recognize it(design mode) and increase only in that case, but not mandatory.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did try to change, but if the IO pool is not exhausted then this option has no impact, it simply won't get past the limit. In both cases we are just telling the threadpool thread to start more threads without delay when it needs to. We will eventually stop blocking them, and thread pool can re-balance itself, we are only paying a small price say 1MB per thread, but we know we will cap out on the amount of threads that is x times the processor count. So if there is a server that has 100 cores, it probably will also have 1GB of ram to spare. If it ever gets to the point where it allocates that many threads.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the comment. |
||
| ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads); | ||
| ThreadPool.SetMinThreads(workerThreads + additionalThreadsCount, completionPortThreads + additionalThreadsCount); | ||
| } | ||
|
|
||
| internal Executor(IOutput output, ITestPlatformEventSource testPlatformEventSource, IProcessHelper processHelper, IEnvironment environment) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.