Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Request fewer threads in the thread pool - #57885

Merged
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads
Sep 9, 2021
Merged

Request fewer threads in the thread pool#57885
kouvel merged 2 commits into
dotnet:mainfrom
kouvel:RequestFewerThreads

Conversation

@kouvel

Copy link
Copy Markdown
Contributor
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes#39559

@kouvelkouvel added this to the 7.0.0 milestone Aug 21, 2021
@kouvel
kouvel requested review from janvorli and mangod9August 21, 2021 18:18
@kouvelkouvel self-assigned this Aug 21, 2021
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Issue Details
  • Currently up to proc count thread requests are made, one when each work item is enqueued and one when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see Thread pool - Fix a couple of things that were reverted #8951 (comment)).
  • When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
  • Fixed by using a similar solution to this. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
  • After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see Maybe spin-waits in the thread pool can be tuned better for arm/arm64 #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
  • The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting. On x64 at full load there's not much change in perf, maybe a small improvement in FortunesPlatform and Fortunes. On arm64 at full load there seem to be some improvements in Json/Fortunes-like benchmarks.

Fixes #39559

Author:kouvel
Assignees:kouvel
Labels:

area-System.Threading

Milestone:7.0.0

@kouvel

Copy link
Copy Markdown
ContributorAuthor

The first commit is a non-functional change if it would be easier to review the commits separately.

@kouvel

kouvel commented Aug 21, 2021

Copy link
Copy Markdown
ContributorAuthor

Perf results

14-core 28-thread x64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform102411778884.911812301.70.3%
.1289345579.89602478.22.7%
.645967326.66351665.86.4%
.323370511.53699054.99.7%
.161876116.32026980.78.0%
JsonPlatform5121282081.71282930.90.1%
.128904762.4935978.13.5%
.64553516.6597037.87.9%
.32312666.4345400.710.5%
.16176371.1202064.614.6%
FortunesPlatform512360573.2362949.50.7%
.128293084.4300313.42.5%
.64189093.8196991.24.2%
.32111167.8118190.16.3%
.1666368.968662.43.5%
Plaintext2564831994.34823856.7-0.2%
.1284368565.44408030.30.9%
.643444864.33460070.80.4%
.322251174.72265688.40.6%
.161155317.81262074.09.2%
Json2561004373.51009589.50.5%
.128851413.4871906.02.4%
.64509635.9564147.110.7%
.32302007.1331372.89.7%
.16171973.3186460.98.4%
Fortunes256360573.2362949.50.7%
.128279174.8286798.82.7%
.64180147.9184358.22.3%
.32107639.5113557.65.5%
.1663590.066166.34.1%

32-core arm64 processor

TestConnectionsBeforeAfterDiff
PlaintextPlatform10246952199.36954765.00.0%
.644692982.74905584.74.5%
.321911023.93179312.066.4%
.16736905.61635691.6122.0%
.8467633.3846071.180.9%
JsonPlatform512644628.6671099.24.1%
.64418614.3437252.84.5%
.32290946.9297934.92.4%
.1672023.7164891.6128.9%
.844577.377018.272.8%
FortunesPlatform51295295.3106695.512.0%
.6429753.158789.197.6%
.3219522.543028.3120.4%
.1613108.529179.6122.6%
.88834.018195.0106.0%
Plaintext2562625118.82644431.00.7%
.641923785.41963492.32.1%
.321340738.31361157.31.5%
.16285234.3788014.0176.3%
.8177535.4415107.9133.8%
Json256429996.6437539.61.8%
.64346248.3351532.41.5%
.32240169.7243038.31.2%
.1650730.1132630.4161.4%
.834659.162971.081.7%
Fortunes25671565.583441.616.6%
.6423091.142949.786.0%
.3215635.931614.4102.2%
.1611090.822191.8100.1%
.87481.514996.7100.4%

Koundinya Veluri added 2 commits August 26, 2021 15:48
- Currently up to proc count yet-to-be-serviced thread requests are made when each work item is enqueued and when each work item is dequeued. Some of the conditions for making a thread request are speculative, making it difficult to reduce the cap without running into issues (see #8951 (comment)).
- When the thread pool is not fully loaded, more threads are requested than necessary, causing more threads to wake up and compete for work items, and this shows a perf degradation at some point as load is decreased
- Fixed by using a similar solution to https://github.com/dotnet/runtime/blob/50576e326d1015906608e3c06670344e335c3225/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs#L209. With this change, at most one thread is requested at a time. A thread pool thread requests another thread for parallelization of work after dequeuing the first work item and does not request any more threads, leaving it up to whichever thread services the new thread request to parallelize further.
- After this, there was a regression in ASP.NET benchmarks on arm64 with default number of connections, see #39559 for more info. Increased the spin-waiting duration on thread pool worker threads to compensate. Spin-waiting longer is preferrable to busy-waiting and competing with other threads for work items, and hitting a full wait and waking up frequently.
- The change seems to solve the perf cliff seen at some point as load is decreased, especially on arm64. A portion of the perf improvements seen on arm64 at lower load is due to the increase in spin-waiting.
Fixes#39559
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Rebased

@kouvelkouvel closed this Sep 1, 2021
@kouvelkouvel reopened this Sep 1, 2021
@kouvel

Copy link
Copy Markdown
ContributorAuthor

Failure is known and unrelated

@kouvel
kouvel merged commit 32fed09 into dotnet:mainSep 9, 2021
@kouvel
kouvel deleted the RequestFewerThreads branch September 9, 2021 17:03
@kunalspathak

kunalspathak commented Sep 14, 2021

Copy link
Copy Markdown
Contributor

Nice improvements on windows-x64: dotnet/perf-autofiling-issues#1366 and dotnet/perf-autofiling-issues#1339

@kunalspathak

Copy link
Copy Markdown
Contributor

Ubuntu/x64 improvements: dotnet/perf-autofiling-issues#1330

@kunalspathak

Copy link
Copy Markdown
Contributor

Arm64 improvements: dotnet/perf-autofiling-issues#1448

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1543

@EgorBo

Copy link
Copy Markdown
Member

arm64 improvements: dotnet/perf-autofiling-issues#1542

@ghostghost locked as resolved and limited conversation to collaborators Nov 3, 2021
@sebastienros

Copy link
Copy Markdown
Member

Can these changes explain a perf improvement on Windows for Fortunes?
We are going from 236K to 280K on Windows with a range that contains this PR: ef85762...c21a701

@kouvel

kouvel commented Nov 9, 2021

Copy link
Copy Markdown
ContributorAuthor

Can these changes explain a perf improvement on Windows for Fortunes?

Possibly, it seems like the likely change out of the set. Fortunes queues fewer work items to the worker pool, so it would probably benefit more from this change compared to the faster platform benchmarks, but typically thread pool changes (at least on Linux) have not changed perf much on Fortunes, so not sure. Things work a bit differently on Windows though, as the IO pool threads may be competing for time with the worker pool threads.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Maybe spin-waits in the thread pool can be tuned better for arm/arm64

5 participants

@kouvel@kunalspathak@EgorBo@sebastienros@mangod9