Back to home page

EIC code displayed by LXR

 
 

    


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