.. _program_listing_file_src_utils_ThreadVec.hpp: Program Listing for File ThreadVec.hpp ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/utils/ThreadVec.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #ifndef THREADVEC_HPP #define THREADVEC_HPP #ifdef _OPENMP # include # include # include #endif #include #include #include 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 ThreadVec { public: ThreadVec() { #ifdef _OPENMP const int limit = omp_get_thread_limit(); const int cap = static_cast(std::max(1, std::thread::hardware_concurrency())) * threadVecCapOversubscription; obj_.resize(static_cast(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(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(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(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(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 obj_; }; } // namespace utils #endif // THREADVEC_HPP