diff --git a/dune/xt/common/parallel/threadstorage.hh b/dune/xt/common/parallel/threadstorage.hh index 00eb9ddb21acc1b46672ed7d41488043f2fbaa0f..f1e3c2c196df1c732931847085310666aa97b79b 100644 --- a/dune/xt/common/parallel/threadstorage.hh +++ b/dune/xt/common/parallel/threadstorage.hh @@ -256,6 +256,118 @@ private: }; // class PerThreadValue<ValueImp> +/** + * Previous implementation of PerThreadValue. This implementation suffers from the fact that it is not possible (or + * at least we did not find a way yet) to set a hard upper limit on the number of threads TBB uses. Setting max_threads + * via tbb::task_scheduler_init apparently only sets a soft limit on the number of threads. In addition, even if TBB + * uses only N threads at a time, it might be possible that a thread is destroyed and later in the program another + * thread with a different id replaces it, which will then get a number greater than or equal to N in our + * implementation (see ThreadManager::thread()). This occasionally leads to segfaults. + * We keep this implementation around as it is currently used by TimingData (see dune/xt/common/timings.hh) and the + * new implementation can't replace it in that context, as the new implementation based on + * tbb::enumerable_thread_specific lazily initalizes the values in each thread. + * \todo Either fix TimingData and remove this class or fix this class. + **/ +template <class ValueImp> +class UnsafePerThreadValue : public boost::noncopyable +{ +public: + typedef ValueImp ValueType; + typedef typename std::conditional<std::is_const<ValueImp>::value, ValueImp, const ValueImp>::type ConstValueType; + +private: + typedef UnsafePerThreadValue<ValueImp> ThisType; + typedef std::deque<std::unique_ptr<ValueType>> ContainerType; + +public: + //! Initialization by copy construction of ValueType + explicit UnsafePerThreadValue(ConstValueType& value) + : values_(threadManager().max_threads()) + { + std::generate(values_.begin(), values_.end(), [=]() { return Common::make_unique<ValueType>(value); }); + } + + //! Initialization by in-place construction ValueType with \param ctor_args + template <class... InitTypes> + explicit UnsafePerThreadValue(InitTypes&&... ctor_args) + : values_(threadManager().max_threads()) + { + for (auto&& val : values_) + val = Common::make_unique<ValueType>(ctor_args...); + } + + ThisType& operator=(ConstValueType&& value) + { + std::generate(values_.begin(), values_.end(), [=]() { return Common::make_unique<ValueType>(value); }); + return *this; + } + + operator ValueType() const + { + return this->operator*(); + } + + ValueType& operator*() + { + return *values_[threadManager().thread()]; + } + + ConstValueType& operator*() const + { + return *values_[threadManager().thread()]; + } + + ValueType* operator->() + { + return values_[threadManager().thread()].get(); + } + + ConstValueType* operator->() const + { + return values_[threadManager().thread()].get(); + } + + auto& get_pointer() + { + return values_[threadManager().thread()]; + } + + template <class BinaryOperation> + ValueType accumulate(ValueType init, BinaryOperation op) const + { + typedef const typename ContainerType::value_type ptr; + auto l = [&](ConstValueType& a, ptr& b) { return op(a, *b); }; + return std::accumulate(values_.begin(), values_.end(), init, l); + } + + ValueType sum() const + { + return accumulate(ValueType(0), std::plus<ValueType>()); + } + + typename ContainerType::iterator begin() + { + return values_.begin(); + } + typename ContainerType::iterator end() + { + return values_.end(); + } + + typename ContainerType::const_iterator begin() const + { + return values_.begin(); + } + typename ContainerType::const_iterator end() const + { + return values_.end(); + } + +private: + ContainerType values_; +}; // class UnsafePerThreadValue<...> + + template <class Imp, typename Result, class Reduction = std::plus<Result>> class ThreadResultPropagator { diff --git a/dune/xt/common/timings.cc b/dune/xt/common/timings.cc index 658ba3de9e260f130b1d6828ef4d57d2cfdb1599..ecde894b7a2e6e90337e059fabd8f3ff9c30fc9d 100644 --- a/dune/xt/common/timings.cc +++ b/dune/xt/common/timings.cc @@ -98,12 +98,11 @@ void Timings::start(std::string section_name) if (section != known_timers_map_.end()) { if (section->second.first) // timer currently running return; - section->second.first = true; // set active, start with new - section->second.second = PerThreadValue<TimingData>(section_name); + section->second.second = TimingData(section_name); } else { // init new section - known_timers_map_[section_name] = std::make_pair(true, PerThreadValue<TimingData>(section_name)); + known_timers_map_[section_name] = std::make_pair(true, TimingData(section_name)); } DXTC_LIKWID_BEGIN_SECTION(section_name) } // StartTiming diff --git a/dune/xt/common/timings.hh b/dune/xt/common/timings.hh index 2af4ad4ce3a930704cd75072516e78d6498589db..87220c07189303dcc2fd58a0c79847af8e2fdaf2 100644 --- a/dune/xt/common/timings.hh +++ b/dune/xt/common/timings.hh @@ -82,7 +82,7 @@ class Timings private: Timings(); - typedef std::map<std::string, std::pair<std::atomic<bool>, PerThreadValue<TimingData>>> KnownTimersMap; + typedef std::map<std::string, std::pair<std::atomic<bool>, UnsafePerThreadValue<TimingData>>> KnownTimersMap; //! section name -> seconds typedef std::map<std::string, TimingData::DeltaType> DeltaMap;