Program Listing for File ThreadVec.hpp
↰ Return to documentation for file (src/utils/ThreadVec.hpp)
#ifndef THREADVEC_HPP
#define THREADVEC_HPP
#ifdef _OPENMP
# include <algorithm>
# include <omp.h>
# include <thread>
#endif
#include <cassert>
#include <cstddef>
#include <vector>
namespace utils {
#ifdef _OPENMP
// Multiplier applied to std::thread::hardware_concurrency() when
// clamping ThreadVec's own construction-time size -- see
// ThreadVec::ThreadVec()'s own comment for why this exists at
// all. Generous on purpose: every T ThreadVec is instantiated
// with in this codebase is a small handle (pointer-sized or
// smaller), so erring on the large side here costs negligible
// memory, while erring small risks the out-of-bounds access this
// whole mechanism exists to prevent.
inline constexpr int threadVecCapOversubscription = 8;
#endif
template <class T> class ThreadVec {
public:
ThreadVec() {
#ifdef _OPENMP
const int limit = omp_get_thread_limit();
const int cap = static_cast<int>(std::max<unsigned>(1, std::thread::hardware_concurrency()))
* threadVecCapOversubscription;
obj_.resize(static_cast<std::size_t>(limit > 0 && limit < cap ? limit : cap));
#else
obj_.resize(1);
#endif
}
~ThreadVec() = default;
// Disable copy and move constructors and assignment operators
ThreadVec(const ThreadVec&) = delete;
ThreadVec(ThreadVec&&) = delete;
auto operator=(const ThreadVec&) -> ThreadVec& = delete;
auto operator=(ThreadVec&&) -> ThreadVec& = delete;
[[nodiscard]] auto size() const { return obj_.size(); }
auto operator()() -> auto& {
#ifdef _OPENMP
const int ithread = omp_get_thread_num();
#else
const int ithread = 0;
#endif
assert(ithread >= 0 && static_cast<std::size_t>(ithread) < obj_.size());
return obj_[ithread]; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- hot path, must stay unchecked for performance; bounds asserted just above
}
auto operator()() const -> const auto& {
#ifdef _OPENMP
const int ithread = omp_get_thread_num();
#else
const int ithread = 0;
#endif
assert(ithread >= 0 && static_cast<std::size_t>(ithread) < obj_.size());
return obj_[ithread]; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- hot path, must stay unchecked for performance; bounds asserted just above
}
auto operator[] (const int i) -> auto& {
assert(i >= 0 && static_cast<std::size_t>(i) < obj_.size());
return obj_[i]; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- hot path, must stay unchecked for performance; bounds asserted just above
}
auto operator[] (const int i) const -> const auto& {
assert(i >= 0 && static_cast<std::size_t>(i) < obj_.size());
return obj_[i]; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- hot path, must stay unchecked for performance; bounds asserted just above
}
auto begin() { return obj_.begin(); }
auto end() { return obj_.end(); }
private:
std::vector<T> obj_;
};
} // namespace utils
#endif // THREADVEC_HPP