Windows: use EcoQoS for ThreadPriority::Background (#148797)

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.

---------

Co-authored-by: Alexandre Ganea <aganea@havenstudios.com>
This commit is contained in:
Tim Blechmann 2025-07-29 23:24:06 +08:00 committed by GitHub
parent 3b66d4a987
commit 860b1e68ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 0 deletions

View File

@ -245,6 +245,10 @@ LLVM_ABI std::error_code widenPath(const Twine &Path8,
SmallVectorImpl<wchar_t> &Path16, SmallVectorImpl<wchar_t> &Path16,
size_t MaxPathLen = MAX_PATH); size_t MaxPathLen = MAX_PATH);
/// Retrieves the handle to a in-memory system module such as ntdll.dll, while
/// ensuring we're not retrieving a malicious injected module but a module
/// loaded from the system path.
LLVM_ABI HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName);
} // end namespace windows } // end namespace windows
} // end namespace sys } // end namespace sys
} // end namespace llvm. } // end namespace llvm.

View File

@ -106,7 +106,67 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
Name.clear(); Name.clear();
} }
namespace llvm::sys::windows {
HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName) {
// Ensure we load indeed a module from system32 path.
// As per GetModuleHandle documentation:
// "If lpModuleName does not include a path and there is more than one loaded
// module with the same base name and extension, you cannot predict which
// module handle will be returned.". This mitigates
// https://learn.microsoft.com/en-us/security-updates/securityadvisories/2010/2269637
SmallVector<wchar_t, MAX_PATH> Buf;
size_t Size = MAX_PATH;
do {
Buf.resize_for_overwrite(Size);
SetLastError(NO_ERROR);
Size = ::GetSystemDirectoryW(Buf.data(), Buf.size());
if (Size == 0)
return NULL;
// Try again with larger buffer.
} while (Size > Buf.size());
Buf.truncate(Size);
Buf.push_back(L'\\');
Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName));
Buf.push_back(0);
return ::GetModuleHandleW(Buf.data());
}
} // namespace llvm::sys::windows
SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
if (kernelM) {
// SetThreadInformation is only available on Windows 8 and later. Since we
// still support compilation on Windows 7, we load the function dynamically.
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(kernelM,
"SetThreadInformation");
if (pfnSetThreadInformation) {
auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
THREAD_POWER_THROTTLING_STATE state{};
state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
state.ControlMask = ControlMaskAndStateMask;
state.StateMask = ControlMaskAndStateMask;
return pfnSetThreadInformation(
::GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
};
// 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
setThreadInformation(Priority == ThreadPriority::Background
? THREAD_POWER_THROTTLING_EXECUTION_SPEED
: 0);
}
}
// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority
// Begin background processing mode. The system lowers the resource scheduling // Begin background processing mode. The system lowers the resource scheduling
// priorities of the thread so that it can perform background work without // priorities of the thread so that it can perform background work without