Add ThreadPriority::Low, and use QoS class Utility on Mac

On Apple Silicon Macs, using a Darwin thread priority of PRIO_DARWIN_BG seems to
map directly to the QoS class Background. With this priority, the thread is
confined to efficiency cores only, which makes background indexing take forever.

Introduce a new ThreadPriority "Low" that sits in the middle between Background
and Default, and maps to QoS class "Utility" on Mac. Make this new priority the
default for indexing. This makes the thread run on all cores, but still lowers
priority enough to keep the machine responsive, and not interfere with
user-initiated actions.

I didn't change the implementations for Windows and Linux; on these systems,
both ThreadPriority::Background and ThreadPriority::Low map to the same thread
priority. This could be changed as a followup (e.g. by using SCHED_BATCH for Low
on Linux).

See also https://github.com/clangd/clangd/issues/1119.

Reviewed By: sammccall, dgoldman

Differential Revision: https://reviews.llvm.org/D124715
This commit is contained in:
stk 2022-05-16 10:01:09 +02:00 committed by Sam McCall
parent 5bc469fd96
commit 9902a0945d
5 changed files with 39 additions and 27 deletions

View File

@ -72,7 +72,7 @@ public:
explicit Task(std::function<void()> Run) : Run(std::move(Run)) {}
std::function<void()> Run;
llvm::ThreadPriority ThreadPri = llvm::ThreadPriority::Background;
llvm::ThreadPriority ThreadPri = llvm::ThreadPriority::Low;
unsigned QueuePri = 0; // Higher-priority tasks will run first.
std::string Tag; // Allows priority to be boosted later.
uint64_t Key = 0; // If the key matches a previous task, drop this one.

View File

@ -9025,7 +9025,9 @@ void clang::setThreadBackgroundPriority() {
return;
#if LLVM_ENABLE_THREADS
llvm::set_thread_priority(llvm::ThreadPriority::Background);
// The function name setThreadBackgroundPriority is for historical reasons;
// Low is more appropriate.
llvm::set_thread_priority(llvm::ThreadPriority::Low);
#endif
}

View File

@ -233,15 +233,20 @@ bool llvm_is_multithreaded();
unsigned get_cpus();
enum class ThreadPriority {
/// Lower the current thread's priority as much as possible. Can be used
/// for long-running tasks that are not time critical; more energy-
/// efficient than Low.
Background = 0,
Default = 1,
/// Lower the current thread's priority such that it does not affect
/// foreground tasks significantly. This is a good default for long-
/// running, latency-insensitive tasks to make sure cpu is not hogged
/// by this task.
Low = 1,
/// Restore the current thread's priority to default scheduling priority.
Default = 2,
};
/// If priority is Background tries to lower current threads priority such
/// that it does not affect foreground tasks significantly. Can be used for
/// long-running, latency-insensitive tasks to make sure cpu is not hogged by
/// this task.
/// If the priority is default tries to restore current threads priority to
/// default scheduling priority.
enum class SetThreadPriorityResult { FAILURE, SUCCESS };
SetThreadPriorityResult set_thread_priority(ThreadPriority Priority);
}

View File

@ -18,6 +18,7 @@
#if defined(__APPLE__)
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <pthread/qos.h>
#endif
#include <pthread.h>
@ -258,27 +259,29 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
// SCHED_OTHER the standard round-robin time-sharing policy;
return !pthread_setschedparam(
pthread_self(),
Priority == ThreadPriority::Background ? SCHED_IDLE : SCHED_OTHER,
// FIXME: consider SCHED_BATCH for Low
Priority == ThreadPriority::Default ? SCHED_OTHER : SCHED_IDLE,
&priority)
? SetThreadPriorityResult::SUCCESS
: SetThreadPriorityResult::FAILURE;
#elif defined(__APPLE__)
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getpriority.2.html
// When setting a thread into background state the scheduling priority is set
// to lowest value, disk and network IO are throttled. Network IO will be
// throttled for any sockets the thread opens after going into background
// state. Any previously opened sockets are not affected.
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getiopolicy_np.3.html
// I/Os with THROTTLE policy are called THROTTLE I/Os. If a THROTTLE I/O
// request occurs within a small time window (usually a fraction of a second)
// of another NORMAL I/O request, the thread that issues the THROTTLE I/O is
// forced to sleep for a certain interval. This slows down the thread that
// issues the THROTTLE I/O so that NORMAL I/Os can utilize most of the disk
// I/O bandwidth.
return !setpriority(PRIO_DARWIN_THREAD, 0,
Priority == ThreadPriority::Background ? PRIO_DARWIN_BG
: 0)
// https://developer.apple.com/documentation/apple-silicon/tuning-your-code-s-performance-for-apple-silicon
//
// Background - Applies to work that isnt visible to the user and may take significant
// time to complete. Examples include indexing, backing up, or synchronizing data. This
// class emphasizes energy efficiency.
//
// Utility - Applies to work that takes anywhere from a few seconds to a few minutes to
// complete. Examples include downloading a document or importing data. This class
// offers a balance between responsiveness, performance, and energy efficiency.
const auto qosClass = [&](){
switch (Priority) {
case ThreadPriority::Background: return QOS_CLASS_BACKGROUND;
case ThreadPriority::Low: return QOS_CLASS_UTILITY;
case ThreadPriority::Default: return QOS_CLASS_DEFAULT;
}
}();
return !pthread_set_qos_class_self_np(qosClass, 0)
? SetThreadPriorityResult::SUCCESS
: SetThreadPriorityResult::FAILURE;
#endif

View File

@ -120,8 +120,10 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
// End background processing mode. The system restores the resource scheduling
// priorities of the thread as they were before the thread entered background
// processing mode.
//
// FIXME: consider THREAD_PRIORITY_BELOW_NORMAL for Low
return SetThreadPriority(GetCurrentThread(),
Priority == ThreadPriority::Background
Priority != ThreadPriority::Default
? THREAD_MODE_BACKGROUND_BEGIN
: THREAD_MODE_BACKGROUND_END)
? SetThreadPriorityResult::SUCCESS