-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathParallelism.cpp
100 lines (79 loc) · 3.43 KB
/
Parallelism.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include "Parallelism.hpp"
#include <algorithm>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <thread>
namespace SetReplace::Parallelism {
namespace {
struct ParallelismBase {
explicit ParallelismBase(int numHardwareThreads) noexcept
: numHardwareThreads_(numHardwareThreads), threadsInUse_(0) {}
int numHardwareThreads_;
int threadsInUse_;
std::mutex reservationMutex_;
};
template <HardwareType>
class Parallelism;
template <>
class Parallelism<HardwareType::StdCpu> : private ParallelismBase {
public:
Parallelism() noexcept : ParallelismBase(static_cast<int>(std::thread::hardware_concurrency())) {}
[[nodiscard]] bool isAvailable() const { return numHardwareThreads_ >= 2; }
[[nodiscard]] int numThreadsAvailable() const { return isAvailable() ? numHardwareThreads_ - threadsInUse_ : 0; }
[[nodiscard]] int acquireThreads(const int& requestedNumThreads) {
std::lock_guard lock(reservationMutex_);
auto numThreadsToReserve = std::min(requestedNumThreads, numThreadsAvailable());
if (numThreadsToReserve <= 1) numThreadsToReserve = 0;
threadsInUse_ += numThreadsToReserve;
return numThreadsToReserve;
}
void releaseThreads(const int& numThreadsToReturn) {
std::lock_guard lock(reservationMutex_);
threadsInUse_ -= numThreadsToReturn;
}
void overrideNumHardwareThreads(const int& numThreads) { numHardwareThreads_ = numThreads; }
};
Parallelism<HardwareType::StdCpu> cpuParallelism;
/** @brief Reserves at most requestedNumThreads of the given hardware type and returns the number of threads
* successfully reserved.
*/
int acquireThreads(const HardwareType& type, const int& requestedNumThreads) {
if (requestedNumThreads <= 1) return 0;
if (type == HardwareType::StdCpu) return cpuParallelism.acquireThreads(requestedNumThreads);
throw std::runtime_error("Invalid Parallelism::HardwareType");
}
/** @brief Releases ownership of numThreadsToReturn of the given hardware type.
*/
void releaseThreads(const HardwareType& type, const int& numThreadsToReturn) {
if (type == HardwareType::StdCpu) return cpuParallelism.releaseThreads(numThreadsToReturn);
throw std::runtime_error("Invalid Parallelism::HardwareType");
}
} // namespace
class ThreadAcquisitionToken::Implementation {
public:
Implementation(const HardwareType& type, const int& requestedNumThreads)
: hardwareType_(type), threads_(acquireThreads(hardwareType_, requestedNumThreads)) {}
[[nodiscard]] constexpr const int& numThreads() const noexcept { return threads_; }
~Implementation() { releaseThreads(hardwareType_, threads_); }
private:
const HardwareType hardwareType_;
const int threads_;
};
ThreadAcquisitionToken::ThreadAcquisitionToken(const HardwareType& type, const int& requestedNumThreads)
: implementation_(std::make_shared<Implementation>(type, requestedNumThreads)) {}
int ThreadAcquisitionToken::numThreads() const noexcept { return implementation_->numThreads(); }
bool isAvailable(const HardwareType& type) {
if (type == HardwareType::StdCpu) return cpuParallelism.isAvailable();
throw std::runtime_error("Invalid Parallelism::HardwareType");
}
namespace Testing {
void overrideNumHardwareThreads(const HardwareType& type, const int& numThreads) {
if (type == HardwareType::StdCpu) {
cpuParallelism.overrideNumHardwareThreads(numThreads);
} else {
throw std::runtime_error("Invalid Parallelism::HardwareType");
}
}
} // namespace Testing
} // namespace SetReplace::Parallelism