Skip to content

Windows: use EcoQoS for ThreadPriority::Background #148797

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

Merged
merged 6 commits into from
Jul 29, 2025

Conversation

timblechmann
Copy link
Contributor

The SetThreadInformation API allows threads to be scheduled on the most efficient cores on the most efficient frequency.
Using this API for ThreadPriority::Background should make clangd-based IDEs a little less CPU hungry.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2025

@llvm/pr-subscribers-platform-windows

@llvm/pr-subscribers-llvm-support

Author: Tim Blechmann (timblechmann)

Changes

The SetThreadInformation API allows threads to be scheduled on the most efficient cores on the most efficient frequency.
Using this API for ThreadPriority::Background should make clangd-based IDEs a little less CPU hungry.


Full diff: https://github.com/llvm/llvm-project/pull/148797.diff

1 Files Affected:

  • (modified) llvm/lib/Support/Windows/Threading.inc (+25)
diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index d862dbd7f71c9..b52c6dff4e5f4 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -107,6 +107,31 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
 }
 
 SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
+
+  typedef BOOL(WINAPI * SetThreadInformation_t)(
+      HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
+      _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
+      ULONG ThreadInformationSize);
+  static const auto pfnSetThreadInformation =
+      (SetThreadInformation_t)GetProcAddress(
+          GetModuleHandle(TEXT("kernel32.dll")), "SetThreadInformation");
+
+  if (pfnSetThreadInformation) {
+    if (Priority == ThreadPriority::Background) {
+      // Use EcoQoS for ThreadPriority::Background available (running on most
+      // efficent cores at the most efficient cpu frequency):
+      // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
+      // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
+      THREAD_POWER_THROTTLING_STATE state;
+      memset(&state, 0, sizeof(state));
+      state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
+      state.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
+      state.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
+      pfnSetThreadInformation(GetCurrentThread(), ThreadPowerThrottling, &state,
+                              sizeof(state));
+    }
+  }
+
   // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority
   // Begin background processing mode. The system lowers the resource scheduling
   // priorities of the thread so that it can perform background work without

Copy link
Member

@aganea aganea left a comment

Choose a reason for hiding this comment

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

Thanks for the changes. Just some more minor nits:

The SetThreadInformation API allows threads to be scheduled on the most
efficient cores on the most efficient frequency.
Using this API for ThreadPriority::Background should make clangd-based
IDEs a little less CPU hungry.
Copy link
Member

@aganea aganea left a comment

Choose a reason for hiding this comment

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

Looks good, thanks for all the changes!

Copy link

github-actions bot commented Jul 23, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@aganea aganea left a comment

Choose a reason for hiding this comment

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

Second thought, I think I'll change this to load the DLL directly from the system32 folder, otherwise this poses a security risk.

@aganea
Copy link
Member

aganea commented Jul 23, 2025

@mstorsjo (or anyone else with Windows knowledge) - would you be able please to review this?

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

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

Looks reasonable overall, but a fix is needed to make it compile properly everywhere.

@aganea
Copy link
Member

aganea commented Jul 27, 2025

I made the change to not use TEXT() as you suggested; and also moved the function to WindowsSupport.h since I'd like to use in it other places that retrive system modules directly without their full path qualification.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

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

Looks ok to me, and seems to compile fine for mingw - thanks!

@aganea aganea merged commit 860b1e6 into llvm:main Jul 29, 2025
8 of 9 checks passed
Copy link

@timblechmann Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@Sharjeel-Khan
Copy link
Contributor

On Android buildbots, it seems this PR started causing failures:

FAILED: lib/Support/CMakeFiles/LLVMSupport.dir/Threading.cpp.obj 
/b/f/w/src/git/out/stage2-install/bin/clang++ --sysroot=/b/f/w/src/git/out/sysroots/x86_64-w64-mingw32 -DEXPERIMENTAL_KEY_INSTRUCTIONS -DGTEST_HAS_RTTI=0 -D_FILE_OFFSET_BITS=64 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__SHORT_FILE__=\"Threading.cpp\" -I/b/f/w/src/git/out/lib/libsimpleperf_readelf-windows/lib/Support -I/b/f/w/src/git/out/llvm-project/llvm/lib/Support -I/b/f/w/src/git/out/lib/libsimpleperf_readelf-windows/include -I/b/f/w/src/git/out/llvm-project/llvm/include -I/b/f/w/src/git/out/llvm-project/llvm/../third-party/siphash/include -ffile-prefix-map=/b/f/w/src/git/= -B/b/f/w/src/git/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/bin --target=x86_64-pc-windows-gnu -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_WIN32_WINNT=0x0600 -DWINVER=0x0600 -D__MSVCRT_VERSION__=0x1400 -D_LIBCPP_AVAILABILITY_HAS_NO_VERBOSE_ABORT=1 -Wno-unused-command-line-argument --sysroot=/b/f/w/src/git/out/sysroots/x86_64-w64-mingw32 -stdlib=libc++ -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -ffunction-sections -fdata-sections  -O3 -DNDEBUG -std=c++17  -fno-exceptions -funwind-tables -fno-rtti -MD -MT lib/Support/CMakeFiles/LLVMSupport.dir/Threading.cpp.obj -MF lib/Support/CMakeFiles/LLVMSupport.dir/Threading.cpp.obj.d -o lib/Support/CMakeFiles/LLVMSupport.dir/Threading.cpp.obj -c /b/f/w/src/git/out/llvm-project/llvm/lib/Support/Threading.cpp
In file included from /[b/f/w/src/git/out/llvm-project/llvm/lib/Support/Threading.cpp:70](https://cs.corp.google.com/piper///depot/google3/b/f/w/src/git/out/llvm-project/llvm/lib/Support/Threading.cpp?l=70):
/[b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc:152](https://cs.corp.google.com/piper///depot/google3/b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc?l=152):9: error: unknown type name 'THREAD_POWER_THROTTLING_STATE'
  152 |         THREAD_POWER_THROTTLING_STATE state{};
      |         ^
/[b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc:153](https://cs.corp.google.com/piper///depot/google3/b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc?l=153):25: error: use of undeclared identifier 'THREAD_POWER_THROTTLING_CURRENT_VERSION'
  153 |         state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/[b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc:157](https://cs.corp.google.com/piper///depot/google3/b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc?l=157):35: error: use of undeclared identifier 'ThreadPowerThrottling'
  157 |             ::GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
      |                                   ^~~~~~~~~~~~~~~~~~~~~
/[b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc:165](https://cs.corp.google.com/piper///depot/google3/b/f/w/src/git/out/llvm-project/llvm/lib/Support/Windows/Threading.inc?l=165):34: error: use of undeclared identifier 'THREAD_POWER_THROTTLING_EXECUTION_SPEED'
  165 |                                ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 errors generated.

Where are these things types and identifiers supposed to be pulled from?

@timblechmann
Copy link
Contributor Author

hmm, they should come from processthreadsapi.h (via windows.h). makes me wonder if it requires a minimum requirement needs to be defined explicitly?

@mstorsjo
Copy link
Member

hmm, they should come from processthreadsapi.h (via windows.h). makes me wonder if it requires a minimum requirement needs to be defined explicitly?

For the case of mingw headers, these defines should be available since mingw-w64/mingw-w64@3a137bd, available in mingw-w64 v12 and v13.

I'm not sure if we strictly want to require any specific minimum SDK to be used here - perhaps this can go within an #ifdef THREAD_POWER_THROTTLING_EXECUTION_SPEED which indicates that a new enough SDK is available? (I checked that this constant is provided as a #define, as opposed to an enum, in WinSDK too.)

@timblechmann
Copy link
Contributor Author

i guess we could also just do the declarations ourselves if they aren't defined ... thoughts?

@mstorsjo
Copy link
Member

i guess we could also just do the declarations ourselves if they aren't defined ... thoughts?

That's also certainly possible (and there is some precedent for it - that we recently removed in 163871c), but it ends up kinda ugly (you either still need to detect - guess - whether the real declarations are available or not, or consistently use a different name to avoid conflicts if the real thing is available). It mostly depends on how complex the missing types are I guess.

@aganea
Copy link
Member

aganea commented Jul 30, 2025

This is because SetThreadInformation is Windows 8:

(in C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\processthreadsapi.h)
...
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
...
WINBASEAPI
BOOL
WINAPI
SetThreadInformation(
    _In_ HANDLE hThread,
    _In_ THREAD_INFORMATION_CLASS ThreadInformationClass,
    _In_reads_bytes_(ThreadInformationSize) LPVOID ThreadInformation,
    _In_ DW

However THREAD_INFORMATION_CLASS and THREAD_POWER_THROTTLING_CURRENT_VERSION are Windows 10:

(in C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\processthreadsapi.h)
...
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS3)

#define THREAD_POWER_THROTTLING_CURRENT_VERSION 1

#define THREAD_POWER_THROTTLING_EXECUTION_SPEED 0x1

#define THREAD_POWER_THROTTLING_VALID_FLAGS (THREAD_POWER_THROTTLING_EXECUTION_SPEED)

typedef struct _THREAD_POWER_THROTTLING_STATE {
    ULONG Version;
    ULONG ControlMask;
    ULONG StateMask;
} THREAD_POWER_THROTTLING_STATE;

#endif // (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS3)

MinGW seems to have these unguarded by the Win10 define, but from the errors above it looks like we should somehow #include "processthreadsapi.h" explicity in llvm/lib/Support/Windows/Threading.inc for MinGW to build correctly. @mstorsjo I don't have a MinGW setup, are you able to confirm this?

@aganea
Copy link
Member

aganea commented Jul 30, 2025

@Sharjeel-Khan Are you able to check if applying aganea@606253e on ToT fixes the issue you're seeing?

@Sharjeel-Khan
Copy link
Contributor

Our buildbot pulls the new LLVM commits and rebuilds every 6 hours so I'll just wait for the next run which is at 12 pm

@mstorsjo
Copy link
Member

This is because SetThreadInformation is Windows 8:

However THREAD_INFORMATION_CLASS and THREAD_POWER_THROTTLING_CURRENT_VERSION are Windows 10:

MinGW seems to have these unguarded by the Win10 define, but from the errors above it looks like we should somehow #include "processthreadsapi.h" explicity in llvm/lib/Support/Windows/Threading.inc for MinGW to build correctly. @mstorsjo I don't have a MinGW setup, are you able to confirm this?

No, you're drawing the wrong conclusions here.

Yes, in the MS WinSDK, THREAD_INFORMATION_CLASS and THREAD_POWER_THROTTLING_CURRENT_VERSION require targeting Windows 10. If you'd build with MS WinSDK targeting a lower version of Windows, you'd run into the same error messages there.

In mingw headers, these declarations don't have the Windows target version checks - so as long as processthreadsapi.h gets included, they should be available. And these files do build fine for me, with mingw-w64 - processthreadsapi.h does get implicitly included by windows.h somewhere. It's just that if you have an older version of mingw-w64 headers, like I presume @Sharjeel-Khan does, then you run into this error.

So in order to fix building with both WinSDK targeting older versions of Windows, and older mingw-w64, we'd need to add an ifdef like what I'm suggesting. (Or manually duplicate the definition of these types.)

@mstorsjo
Copy link
Member

Our buildbot pulls the new LLVM commits and rebuilds every 6 hours so I'll just wait for the next run which is at 12 pm

He pointed to his private fork - his suggested change isn't included in the latest git main, so it won't be automatically tested by your buildbot.

Can you check the version of __MINGW64_VERSION_MAJOR in your _mingw_mac.h header?

@Sharjeel-Khan
Copy link
Contributor

Sharjeel-Khan commented Jul 30, 2025

He pointed to his private fork - his suggested change isn't included in the latest git main, so it won't be automatically tested by your buildbot.

I noticed after my message it was a private fork change. I began testing his change locally.

Can you check the version of __MINGW64_VERSION_MAJOR in your _mingw_mac.h header?

It is 7

@mstorsjo
Copy link
Member

He pointed to his private fork - his suggested change isn't included in the latest git main, so it won't be automatically tested by your buildbot.

I noticed after my message it was a private fork change. I began testing his change locally.

Can you check the version of __MINGW64_VERSION_MAJOR in your _mingw_mac.h header?

It is 7

Right, then there's no doubt - your headers are way too old to have the declarations for this API. I've set up fix in #151388 - I tried compiling this with older mingw-w64 headers, and it seems to fix the issue.

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

Successfully merging this pull request may close these issues.

5 participants