Skip to content

Commit 2db6ede

Browse files
committed
MB-36956: FollyExecutorPool: Implement thread priorities
Use folly::PriorityThreadFactory when creating CPUPool (Reader/Writer/AuxIO/NonIO) threads. Use the same priorties as CB3ExecutorPool - Writers have lowest priority, all others have default priority. Change-Id: Id101962c781989edecd2cbc2a3a3c9075b3bd629 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/133974 Tested-by: Build Bot <[email protected]> Reviewed-by: Trond Norbye <[email protected]>
1 parent 74fd5e3 commit 2db6ede

File tree

5 files changed

+98
-34
lines changed

5 files changed

+98
-34
lines changed

engines/ep/src/cb3_executorthread.cc

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,36 +85,22 @@ void CB3ExecutorThread::run() {
8585
// BucketAllocationGuard to account task deletion to the bucket.
8686
NonBucketAllocationGuard guard;
8787

88-
// Decrease the priority of Writer threads to lessen their impact on
89-
// other threads (esp front-end workers which should be prioritized ahead
90-
// of non-critical path Writer tasks (both Flusher and Compaction).
91-
// TODO: Investigate if it is worth increasing the priority of Flusher tasks
92-
// which _are_ on the critical path for front-end operations - i.e.
93-
// SyncWrites at level=persistMajority / persistActive.
94-
// This could be done by having two different priority thread pools (say
95-
// Low and High IO threads and putting critical path Reader (BGFetch) and
96-
// Writer (SyncWrite flushes) on the High IO thread pool; keeping
97-
// non-persist SyncWrites / normal mutations & compaction on the Low IO
98-
// pool.
88+
// Set priority based on this thread's task type.
9989
#if defined(__linux__)
10090
// Only doing this for Linux at present:
10191
// - On Windows folly's getpriority() compatability function changes the
10292
// priority of the entire process.
10393
// - On macOS setpriority(PRIO_PROCESS) affects the entire process (unlike
10494
// Linux where it's only the current thread), hence calling setpriority()
10595
// would be pointless.
106-
if (taskType == WRITER_TASK_IDX) {
107-
// Note Linux uses the range -20..19 (highest..lowest).
108-
const int lowestPriority = 19;
109-
if (setpriority(PRIO_PROCESS,
110-
/*Current thread*/ 0,
111-
lowestPriority)) {
112-
EP_LOG_WARN("Failed to set thread {} to background priority - {}",
113-
getName(),
114-
strerror(errno));
115-
} else {
116-
EP_LOG_INFO("Set thread {} to background priority", getName());
117-
}
96+
if (setpriority(PRIO_PROCESS,
97+
/*Current thread*/ 0,
98+
ExecutorPool::getThreadPriority(taskType))) {
99+
EP_LOG_WARN("Failed to set thread {} to background priority - {}",
100+
getName(),
101+
strerror(errno));
102+
} else {
103+
EP_LOG_INFO("Set thread {} to background priority", getName());
118104
}
119105
#endif
120106

engines/ep/src/executorpool.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,39 @@ size_t ExecutorPool::calcNumNonIO(size_t threadCount) const {
183183
}
184184
return count;
185185
}
186+
187+
int ExecutorPool::getThreadPriority(task_type_t taskType) {
188+
// Decrease the priority of Writer threads to lessen their impact on
189+
// other threads (esp front-end workers which should be prioritized ahead
190+
// of non-critical path Writer tasks (both Flusher and Compaction).
191+
// TODO: Investigate if it is worth increasing the priority of Flusher tasks
192+
// which _are_ on the critical path for front-end operations - i.e.
193+
// SyncWrites at level=persistMajority / persistActive.
194+
// This could be done by having two different priority thread pools (say
195+
// Low and High IO threads and putting critical path Reader (BGFetch) and
196+
// Writer (SyncWrite flushes) on the High IO thread pool; keeping
197+
// non-persist SyncWrites / normal mutations & compaction on the Low IO
198+
// pool.
199+
#if defined(__linux__)
200+
// Only doing this for Linux at present:
201+
// - On Windows folly's getpriority() compatability function changes the
202+
// priority of the entire process.
203+
// - On macOS setpriority(PRIO_PROCESS) affects the entire process (unlike
204+
// Linux where it's only the current thread), hence calling setpriority()
205+
// would be pointless.
206+
switch (taskType) {
207+
case WRITER_TASK_IDX:
208+
// Linux uses the range -20..19 (highest..lowest).
209+
return 19;
210+
case READER_TASK_IDX:
211+
case AUXIO_TASK_IDX:
212+
case NONIO_TASK_IDX:
213+
return 0;
214+
case NO_TASK_TYPE:
215+
case NUM_TASK_GROUPS:
216+
// These are both invalid taskTypes.
217+
folly::assume_unreachable();
218+
}
219+
#endif
220+
return 0;
221+
}

engines/ep/src/executorpool.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <memcached/engine_common.h>
1919
#include <memcached/thread_pool_config.h>
2020

21+
#include "task_type.h"
2122
#include <atomic>
2223
#include <memory>
2324
#include <mutex>
@@ -165,6 +166,13 @@ class ExecutorPool {
165166

166167
virtual ~ExecutorPool() = default;
167168

169+
/**
170+
* Return the thread priority to use for threads of the given task type.
171+
* @param taskType
172+
* @return priority to use, as passed to setpriority().
173+
*/
174+
static int getThreadPriority(task_type_t taskType);
175+
168176
protected:
169177
ExecutorPool(size_t maxThreads);
170178

engines/ep/src/folly_executorpool.cc

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@
2828

2929
#include <folly/executors/CPUThreadPoolExecutor.h>
3030
#include <folly/executors/IOThreadPoolExecutor.h>
31+
#include <folly/executors/thread_factory/PriorityThreadFactory.h>
32+
33+
/**
34+
* Thread factory for CPU pool threads.
35+
* - Gives each thread name based on the given prefix.
36+
* - Sets each thread to the given priority.
37+
*/
38+
class CBPriorityThreadFactory : public folly::ThreadFactory {
39+
public:
40+
CBPriorityThreadFactory(std::string prefix, int priority)
41+
: priorityThreadFactory(
42+
std::make_shared<folly::NamedThreadFactory>(prefix),
43+
priority) {
44+
}
45+
46+
std::thread newThread(folly::Func&& func) override {
47+
return priorityThreadFactory.newThread(std::move(func));
48+
}
49+
50+
folly::PriorityThreadFactory priorityThreadFactory;
51+
};
3152

3253
/**
3354
* Proxy object recorded for each registered (scheduled) Task. Inherits from
@@ -433,19 +454,37 @@ FollyExecutorPool::FollyExecutorPool(size_t maxThreads,
433454
maxWriters(calcNumWriters(maxWriters_)),
434455
maxAuxIO(calcNumAuxIO(maxAuxIO_)),
435456
maxNonIO(calcNumNonIO(maxNonIO_)) {
457+
/*
458+
* Define a function to create thread factory with a given prefix,
459+
* and priority where supported.
460+
*
461+
* Only setting priority for Linux at present:
462+
* - On Windows folly's getpriority() compatibility function changes the
463+
* priority of the entire process.
464+
* - On macOS setpriority(PRIO_PROCESS) affects the entire process (unlike
465+
* Linux where it's only the current thread), hence calling
466+
* setpriority() would be pointless.
467+
*/
468+
auto makeThreadFactory = [](std::string prefix, task_type_t taskType) {
469+
#if defined(__linux__)
470+
return std::make_shared<CBPriorityThreadFactory>(
471+
prefix, ExecutorPool::getThreadPriority(taskType));
472+
#else
473+
return std::make_shared<folly::NamedThreadFactory>(prefix);
474+
#endif
475+
};
476+
436477
futurePool = std::make_unique<folly::IOThreadPoolExecutor>(
437478
1, std::make_shared<folly::NamedThreadFactory>("SchedulerPool"));
438479

439480
readerPool = std::make_unique<folly::CPUThreadPoolExecutor>(
440-
maxReaders,
441-
std::make_shared<folly::NamedThreadFactory>("ReaderPool")),
481+
maxReaders, makeThreadFactory("ReaderPool", READER_TASK_IDX));
442482
writerPool = std::make_unique<folly::CPUThreadPoolExecutor>(
443-
maxWriters,
444-
std::make_shared<folly::NamedThreadFactory>("WriterPool"));
483+
maxWriters, makeThreadFactory("WriterPool", WRITER_TASK_IDX));
445484
auxPool = std::make_unique<folly::CPUThreadPoolExecutor>(
446-
maxAuxIO, std::make_shared<folly::NamedThreadFactory>("AuxIoPool"));
485+
maxAuxIO, makeThreadFactory("AuxIoPool", AUXIO_TASK_IDX));
447486
nonIoPool = std::make_unique<folly::CPUThreadPoolExecutor>(
448-
maxNonIO, std::make_shared<folly::NamedThreadFactory>("NonIoPool"));
487+
maxNonIO, makeThreadFactory("NonIoPool", NONIO_TASK_IDX));
449488
}
450489

451490
FollyExecutorPool::~FollyExecutorPool() {

engines/ep/tests/module_tests/executorpool_test.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,6 @@ TYPED_TEST(ExecutorPoolTest, increase_workers) {
440440
// Verifies the priority of the different thread types. On Windows and Linux
441441
// the Writer threads should be low priority.
442442
TYPED_TEST(ExecutorPoolTest, ThreadPriorities) {
443-
if (typeid(TypeParam) == typeid(FollyExecutorPool)) {
444-
// Not yet implemented for FollyExecutorPool.
445-
GTEST_SKIP();
446-
}
447-
448443
// Create test pool and register a (mock) taskable to start all threads.
449444
this->makePool(10);
450445

0 commit comments

Comments
 (0)