Common: Set a min size on threadpool work chunks.

This should avoid slicing loops, etc. into too many chunks.  Generalize
the memcpy a bit.
This commit is contained in:
Unknown W. Brackets
2021-04-15 23:59:02 -07:00
parent 639dd67130
commit b2f3f06768
5 changed files with 48 additions and 45 deletions
+30 -29
View File
@@ -1,3 +1,5 @@
#include <algorithm>
#include <cstring>
#include "Common/Thread/ThreadPool.h"
#include "Common/Thread/ThreadUtil.h"
@@ -54,34 +56,18 @@ void WorkerThread::WorkFunc() {
}
}
void LoopWorkerThread::Process(std::function<void(int, int)> work, int start, int end) {
void LoopWorkerThread::ProcessLoop(std::function<void(int, int)> work, int start, int end) {
std::lock_guard<std::mutex> guard(mutex);
work_ = std::move(work);
loopWork_ = std::move(work);
work_ = [this]() {
loopWork_(start_, end_);
};
start_ = start;
end_ = end;
jobsTarget = jobsDone + 1;
signal.notify_one();
}
void LoopWorkerThread::WorkFunc() {
setCurrentThreadName("LoopWorker");
std::unique_lock<std::mutex> guard(mutex);
while (active) {
// 'active == false' is one of the conditions for signaling,
// do not "optimize" it
while (active && jobsTarget <= jobsDone) {
signal.wait(guard);
}
if (active) {
work_(start_, end_);
std::lock_guard<std::mutex> doneGuard(doneMutex);
jobsDone++;
done.notify_one();
}
}
}
///////////////////////////// ThreadPool
ThreadPool::ThreadPool(int numThreads) {
@@ -108,23 +94,32 @@ void ThreadPool::StartWorkers() {
}
}
void ThreadPool::ParallelLoop(const std::function<void(int,int)> &loop, int lower, int upper) {
void ThreadPool::ParallelLoop(const std::function<void(int,int)> &loop, int lower, int upper, int minSize) {
// Don't parallelize tiny loops.
if (minSize == -1)
minSize = 4;
int range = upper - lower;
if (range >= numThreads_ * 2) { // don't parallelize tiny loops (this could be better, maybe add optional parameter that estimates work per iteration)
if (range >= minSize) {
std::lock_guard<std::mutex> guard(mutex);
StartWorkers();
// could do slightly better load balancing for the generic case,
// but doesn't matter since all our loops are power of 2
int chunk = range / numThreads_;
int chunk = std::max(minSize, range / numThreads_);
int s = lower;
for (auto& worker : workers) {
worker->Process(loop, s, s+chunk);
s+=chunk;
for (auto &worker : workers) {
// We'll do the last chunk on the current thread.
if (s + chunk >= upper) {
break;
}
worker->ProcessLoop(loop, s, s + chunk);
s += chunk;
}
// This is the final chunk.
loop(s, upper);
for (auto& worker : workers) {
if (s < upper)
loop(s, upper);
for (auto &worker : workers) {
worker->WaitForCompletion();
}
} else {
@@ -132,3 +127,9 @@ void ThreadPool::ParallelLoop(const std::function<void(int,int)> &loop, int lowe
}
}
void ThreadPool::ParallelMemcpy(void *dest, const void *src, int size) {
static const int MIN_SIZE = 128 * 1024;
ParallelLoop([&](int l, int h) {
memmove((uint8_t *)dest + l, (const uint8_t *)src + l, h - l);
}, 0, size, MIN_SIZE);
}