|
||||
File indexing completed on 2025-01-18 10:10:50
0001 // @(#)root/core/base:$Id$ 0002 // Author: Xavier Valls November 2020 0003 0004 /************************************************************************* 0005 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. * 0006 * All rights reserved. * 0007 * * 0008 * For the licensing terms see $ROOTSYS/LICENSE. * 0009 * For the list of contributors see $ROOTSYS/README/CREDITS. * 0010 *************************************************************************/ 0011 0012 #ifndef ROOT_TExecutorCRTP 0013 #define ROOT_TExecutorCRTP 0014 0015 #include "ROOT/TSeq.hxx" 0016 #include "ROOT/TypeTraits.hxx" // InvokeResult_t 0017 #include "TError.h" 0018 #include "TList.h" 0019 0020 #include <initializer_list> 0021 #include <type_traits> //std::enable_if 0022 #include <utility> //std::move 0023 #include <vector> 0024 0025 ////////////////////////////////////////////////////////////////////////// 0026 /// 0027 /// \class ROOT::TExecutorCRTP 0028 /// \brief This class defines an interface to execute the same task 0029 /// multiple times, possibly in parallel and with different arguments every 0030 /// time. 0031 /// 0032 /// ###ROOT::TExecutorCRTP<SubC>::Map 0033 /// The two possible usages of the Map method are:\n 0034 /// * `Map(F func, unsigned nTimes)`: func is executed nTimes with no arguments 0035 /// * `Map(F func, T& args)`: func is executed on each element of the collection of arguments args 0036 /// 0037 /// The Map function forwards the call to MapImpl, to be implemented by the child classes. 0038 /// 0039 /// For either signature, func is executed as many times as needed by a pool of 0040 /// n workers, where n typically defaults to the number of available cores.\n 0041 /// A collection containing the result of each execution is returned.\n 0042 /// **Note:** the user is responsible for the deletion of any object that might 0043 /// be created upon execution of func, returned objects included. ROOT::TExecutorCRTP derived classes 0044 /// never delete what they return, they simply forget it.\n 0045 /// 0046 /// \param func 0047 /// \parblock 0048 /// a callable object, such as a lambda expression, an std::function, a 0049 /// functor object or a function that takes zero arguments (for the first signature) 0050 /// or one (for the second signature). 0051 /// \endparblock 0052 /// \param args 0053 /// \parblock 0054 /// a standard vector, a ROOT::TSeq of integer type or an initializer list for the second signature. 0055 /// An integer only for the first.\n 0056 /// \endparblock 0057 /// 0058 /// **Note:** in cases where the function to be executed takes more than 0059 /// zero/one argument but all are fixed except zero/one, the function can be wrapped 0060 /// in a lambda or via std::bind to give it the right signature.\n 0061 /// 0062 /// #### Return value: 0063 /// An std::vector. The elements in the container 0064 /// will be the objects returned by func. The ordering of the elements corresponds to the ordering of 0065 /// the arguments. 0066 /// 0067 /// ### ROOT::TExecutorCRTP<SubC>::Reduce 0068 /// These set of methods combine all elements from a std::vector into a single value. 0069 /// \param redfunc 0070 /// \parblock 0071 /// a callable object, such as a lambda expression, an std::function, a 0072 /// functor object or a function that takes an std::vector and combines all its elements into a single result.\n 0073 /// \endparblock 0074 /// \param [args] 0075 /// \parblock 0076 /// a standard vector\n 0077 /// \endparblock 0078 /// 0079 /// ### ROOT::TExecutorCRTP<SubC>::MapReduce 0080 /// This set of methods behaves exactly like Map, but takes an additional 0081 /// function as a third argument. This function is applied to the set of 0082 /// objects returned by the corresponding Map execution to "squash" them 0083 /// into a single object. This function should be independent of the size of 0084 /// the vector returned by Map due to optimization of the number of chunks. 0085 /// 0086 /// #### Examples: 0087 /// ~~~{.cpp} 0088 /// Generate 1 ten times and sum those tens 0089 /// root[] ROOT::TProcessExecutor pool; auto ten = pool.MapReduce([]() { return 1; }, 10, [](const std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); }) 0090 /// root[] ROOT::TProcessExecutor pool; auto tenOnes = pool.Map([]() { return 1; }, 10); auto ten = Reduce([](const std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); }, tenOnes) 0091 /// 0092 /// Create 10 histograms and merge them into one 0093 /// root[] ROOT::TThreadExecutor pool; auto hist = pool.MapReduce(CreateAndFillHists, 10, PoolUtils::ReduceObjects); 0094 /// 0095 /// ~~~ 0096 /// 0097 ////////////////////////////////////////////////////////////////////////// 0098 0099 0100 namespace ROOT { 0101 0102 template<class SubC> 0103 class TExecutorCRTP { 0104 0105 protected: 0106 template <typename F, typename... Args> 0107 using InvokeResult_t = ROOT::TypeTraits::InvokeResult_t<F, Args...>; 0108 0109 /// type definition used in templated functions for not allowing mapping functions that return references or void. 0110 /// The resulting vector elements must be assignable, references aren't. 0111 template <class F, class... T> 0112 using validMapReturnCond = 0113 std::enable_if_t<!std::is_reference<InvokeResult_t<F, T...>>::value && !std::is_void<InvokeResult_t<F, T...>>::value>; 0114 0115 public: 0116 TExecutorCRTP() = default; 0117 TExecutorCRTP(const TExecutorCRTP &) = delete; 0118 TExecutorCRTP &operator=(const TExecutorCRTP &) = delete; 0119 0120 // Map 0121 // These trailing return types allow for a compile time check of compatibility between function signatures and args 0122 template <class F, class Cond = validMapReturnCond<F>> 0123 auto Map(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>>; 0124 template <class F, class INTEGER, class Cond = validMapReturnCond<F, INTEGER>> 0125 auto Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>>; 0126 template <class F, class T, class Cond = validMapReturnCond<F, T>> 0127 auto Map(F func, std::initializer_list<T> args) -> std::vector<InvokeResult_t<F, T>>; 0128 template <class F, class T, class Cond = validMapReturnCond<F, T>> 0129 auto Map(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>; 0130 template <class F, class T, class Cond = validMapReturnCond<F, T>> 0131 auto Map(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>; 0132 0133 // MapReduce 0134 // The trailing return types check at compile time that func is compatible with the type of the arguments. 0135 // A static_assert check in TExecutorCRTP<SubC>::Reduce is used to check that redfunc is compatible with the type returned by func 0136 template <class F, class R, class Cond = validMapReturnCond<F>> 0137 auto MapReduce(F func, unsigned nTimes, R redfunc) -> InvokeResult_t<F>; 0138 template <class F, class INTEGER, class R, class Cond = validMapReturnCond<F, INTEGER>> 0139 auto MapReduce(F func, ROOT::TSeq<INTEGER> args, R redfunc) -> InvokeResult_t<F, INTEGER>; 0140 template <class F, class T, class R, class Cond = validMapReturnCond<F, T>> 0141 auto MapReduce(F func, std::initializer_list<T> args, R redfunc) -> InvokeResult_t<F, T>; 0142 template <class F, class T, class R, class Cond = validMapReturnCond<F, T>> 0143 auto MapReduce(F func, const std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>; 0144 template <class F, class T, class R, class Cond = validMapReturnCond<F, T>> 0145 auto MapReduce(F func, std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>; 0146 template<class F, class T,class Cond = validMapReturnCond<F, T>> 0147 T* MapReduce(F func, std::vector<T*> &args); 0148 template<class F, class T,class Cond = validMapReturnCond<F, T>> 0149 T* MapReduce(F func, const std::vector<T*> &args); 0150 0151 template<class T> T* Reduce(const std::vector<T*> &mergeObjs); 0152 template<class T, class R> auto Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs)); 0153 0154 private: 0155 0156 SubC &Derived() 0157 { 0158 return *static_cast<SubC*>(this); 0159 } 0160 0161 /// Implementation of the Map method, left to the derived classes 0162 template <class F, class Cond = validMapReturnCond<F>> 0163 auto MapImpl(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>> = delete; 0164 /// Implementation of the Map method, left to the derived classes 0165 template <class F, class INTEGER, class Cond = validMapReturnCond<F, INTEGER>> 0166 auto MapImpl(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>> = delete; 0167 /// Implementation of the Map method, left to the derived classes 0168 template <class F, class T, class Cond = validMapReturnCond<F, T>> 0169 auto MapImpl(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> = delete; 0170 /// Implementation of the Map method, left to the derived classes 0171 template <class F, class T, class Cond = validMapReturnCond<F, T>> 0172 auto MapImpl(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> = delete; 0173 }; 0174 0175 ////////////////////////////////////////////////////////////////////////// 0176 /// \brief Execute a function without arguments several times. 0177 /// 0178 /// \param func Function to be executed. 0179 /// \param nTimes Number of times function should be called. 0180 /// \return A vector with the results of the function calls. 0181 /// Functions that take arguments can be executed (with 0182 /// fixed arguments) by wrapping them in a lambda or with std::bind. 0183 template <class SubC> 0184 template <class F, class Cond> 0185 auto TExecutorCRTP<SubC>::Map(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>> 0186 { 0187 return Derived().MapImpl(func, nTimes); 0188 } 0189 0190 ////////////////////////////////////////////////////////////////////////// 0191 /// \brief Execute a function over a sequence of indexes. 0192 /// 0193 /// \param func Function to be executed. Must take an element of the sequence passed assecond argument as a parameter. 0194 /// \param args Sequence of indexes to execute `func` on. 0195 /// \return A vector with the results of the function calls. 0196 template <class SubC> 0197 template <class F, class INTEGER, class Cond> 0198 auto TExecutorCRTP<SubC>::Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>> 0199 { 0200 return Derived().MapImpl(func, args); 0201 } 0202 0203 ////////////////////////////////////////////////////////////////////////// 0204 /// \brief Execute a function over the elements of an initializer_list. 0205 /// 0206 /// \param func Function to be executed on the elements of the initializer_list passed as second parameter. 0207 /// \param args initializer_list for a vector to apply `func` on. 0208 /// \return A vector with the results of the function calls. 0209 template <class SubC> 0210 template <class F, class T, class Cond> 0211 auto TExecutorCRTP<SubC>::Map(F func, std::initializer_list<T> args) -> std::vector<InvokeResult_t<F, T>> 0212 { 0213 std::vector<T> vargs(std::move(args)); 0214 const auto &reslist = Map(func, vargs); 0215 return reslist; 0216 } 0217 0218 ////////////////////////////////////////////////////////////////////////// 0219 /// \brief Execute a function over the elements of a vector. 0220 /// 0221 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0222 /// \param args Vector of elements passed as an argument to `func`. 0223 /// \return A vector with the results of the function calls. 0224 template <class SubC> 0225 template <class F, class T, class Cond> 0226 auto TExecutorCRTP<SubC>::Map(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> 0227 { 0228 return Derived().MapImpl(func, args); 0229 } 0230 0231 ////////////////////////////////////////////////////////////////////////// 0232 /// \brief Execute a function over the elements of an immutable vector 0233 0234 /// 0235 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0236 /// \param args Vector of elements passed as an argument to `func`. 0237 /// \return A vector with the results of the function calls. 0238 template <class SubC> 0239 template <class F, class T, class Cond> 0240 auto TExecutorCRTP<SubC>::Map(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> 0241 { 0242 return Derived().MapImpl(func, args); 0243 } 0244 0245 ////////////////////////////////////////////////////////////////////////// 0246 /// \brief Execute a function without arguments several times (Map) and accumulate the results into a single value (Reduce). 0247 /// 0248 /// \param func Function to be executed. 0249 /// \param nTimes Number of times function should be called. 0250 /// \return A vector with the results of the function calls. 0251 /// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`. 0252 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0253 template <class SubC> 0254 template <class F, class R, class Cond> 0255 auto TExecutorCRTP<SubC>::MapReduce(F func, unsigned nTimes, R redfunc) -> InvokeResult_t<F> 0256 { 0257 return Reduce(Map(func, nTimes), redfunc); 0258 } 0259 0260 ////////////////////////////////////////////////////////////////////////// 0261 /// \brief Execute a function over a sequence of indexes (Map) and accumulate the results into a single value (Reduce). 0262 /// 0263 /// \param func Function to be executed. Must take an element of the sequence passed assecond argument as a parameter. 0264 /// \param args Sequence of indexes to execute `func` on. 0265 /// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`. 0266 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0267 template <class SubC> 0268 template <class F, class INTEGER, class R, class Cond> 0269 auto TExecutorCRTP<SubC>::MapReduce(F func, ROOT::TSeq<INTEGER> args, R redfunc) -> InvokeResult_t<F, INTEGER> 0270 { 0271 return Reduce(Map(func, args), redfunc); 0272 } 0273 0274 ////////////////////////////////////////////////////////////////////////// 0275 /// \brief Execute a function over the elements of an initializer_list (Map) and accumulate the results into a single value (Reduce). 0276 /// 0277 /// \param func Function to be executed on the elements of the initializer_list passed as second parameter. 0278 /// \param args initializer_list for a vector to apply `func` on. 0279 /// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`. 0280 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0281 template <class SubC> 0282 template <class F, class T, class R, class Cond> 0283 auto TExecutorCRTP<SubC>::MapReduce(F func, std::initializer_list<T> args, R redfunc) -> InvokeResult_t<F, T> 0284 { 0285 std::vector<T> vargs(std::move(args)); 0286 return Reduce(Map(func, vargs), redfunc); 0287 } 0288 0289 ////////////////////////////////////////////////////////////////////////// 0290 /// \brief Execute a function over the elements of a vector (Map) and accumulate the results into a single value (Reduce). 0291 /// 0292 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0293 /// \param args Vector of elements passed as an argument to `func`. 0294 /// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`. 0295 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0296 template <class SubC> 0297 template <class F, class T, class R, class Cond> 0298 auto TExecutorCRTP<SubC>::MapReduce(F func, std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T> 0299 { 0300 return Reduce(Map(func, args), redfunc); 0301 } 0302 0303 ////////////////////////////////////////////////////////////////////////// 0304 /// \brief Execute a function over the elements of an immutable vector (Map) and accumulate the results into a single value (Reduce). 0305 /// 0306 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0307 /// \param args Immutable vector of elements passed as an argument to `func`. 0308 /// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`. 0309 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0310 template <class SubC> 0311 template <class F, class T, class R, class Cond> 0312 auto TExecutorCRTP<SubC>::MapReduce(F func, const std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T> 0313 { 0314 return Reduce(Map(func, args), redfunc); 0315 } 0316 0317 ////////////////////////////////////////////////////////////////////////// 0318 /// \brief Execute a function over the TObject-inheriting elements of a vector (Map) and merge the objects into a single one (Reduce). 0319 /// 0320 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0321 /// \param args Vector of elements passed as an argument to `func`. 0322 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0323 template<class SubC> template<class F, class T, class Cond> 0324 T* TExecutorCRTP<SubC>::MapReduce(F func, std::vector<T*> &args) 0325 { 0326 return Reduce(Map(func, args)); 0327 } 0328 0329 ////////////////////////////////////////////////////////////////////////// 0330 /// \brief Execute a function over the TObject-inheriting elements of an immutable vector (Map) and merge the objects into a single one (Reduce). 0331 /// 0332 /// \param func Function to be executed on the elements of the vector passed as second parameter. 0333 /// \param args Immutable vector of elements passed as an argument to `func`. 0334 /// \return A value result of "reducing" the vector returned by the Map operation into a single object. 0335 template<class SubC> template<class F, class T, class Cond> 0336 T* TExecutorCRTP<SubC>::MapReduce(F func, const std::vector<T*> &args) 0337 { 0338 return Reduce(Map(func, args)); 0339 } 0340 0341 ////////////////////////////////////////////////////////////////////////// 0342 /// \brief "Reduce" an std::vector into a single object by using the object's Merge method. 0343 /// 0344 /// \param mergeObjs A vector of ROOT objects implementing the Merge method 0345 /// \return An object result of merging the vector elements into one. 0346 template<class SubC> template<class T> 0347 T* TExecutorCRTP<SubC>::Reduce(const std::vector<T*> &mergeObjs) 0348 { 0349 ROOT::MergeFunc_t merge = mergeObjs.front()->IsA()->GetMerge(); 0350 if(!merge) { 0351 Error("TExecutorCRTP<SubC>::Reduce", "could not find merge method for the TObject\n. Aborting operation."); 0352 return nullptr; 0353 } 0354 0355 TList l; 0356 for(unsigned i =1; i<mergeObjs.size(); i++){ 0357 l.Add(mergeObjs[i]); 0358 } 0359 // use clone to return a new object 0360 auto retHist = dynamic_cast<T*>((mergeObjs.front())->Clone()); 0361 if (retHist) retHist->Merge(&l); 0362 return retHist; 0363 } 0364 0365 ////////////////////////////////////////////////////////////////////////// 0366 /// \brief "Reduce" an std::vector into a single object by passing a 0367 /// function as the second argument defining the reduction operation. 0368 /// 0369 /// \param objs A vector of elements to combine. 0370 /// \param redfunc Reduction function to combine the elements of the vector `objs` 0371 /// \return A value result of combining the vector elements into a single object of the same type. 0372 template<class SubC> template<class T, class R> 0373 auto TExecutorCRTP<SubC>::Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs)) 0374 { 0375 // check we can apply reduce to objs 0376 static_assert(std::is_same<decltype(redfunc(objs)), T>::value, "redfunc does not have the correct signature"); 0377 return redfunc(objs); 0378 } 0379 0380 } // end namespace ROOT 0381 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |