Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RAction.hxx
Go to the documentation of this file.
1 // Author: Enrico Guiraud, Danilo Piparo CERN 09/2018
2 
3 /*************************************************************************
4  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
5  * All rights reserved. *
6  * *
7  * For the licensing terms see $ROOTSYS/LICENSE. *
8  * For the list of contributors see $ROOTSYS/README/CREDITS. *
9  *************************************************************************/
10 
11 #ifndef ROOT_RACTION
12 #define ROOT_RACTION
13 
14 #include "ROOT/RDF/GraphNode.hxx"
15 #include "ROOT/RDF/RActionBase.hxx"
16 #include "ROOT/RDF/NodesUtils.hxx" // InitRDFValues
17 #include "ROOT/RDF/Utils.hxx" // ColumnNames_t
20 
21 #include <cstddef> // std::size_t
22 #include <memory>
23 #include <string>
24 #include <vector>
25 
26 namespace ROOT {
27 namespace Internal {
28 namespace RDF {
29 
30 namespace RDFDetail = ROOT::Detail::RDF;
31 namespace RDFGraphDrawing = ROOT::Internal::RDF::GraphDrawing;
32 
33 // fwd declarations for RActionCRTP
34 namespace GraphDrawing {
35 std::shared_ptr<GraphNode> CreateDefineNode(const std::string &colName, const RDFDetail::RCustomColumnBase *columnPtr);
36 bool CheckIfDefaultOrDSColumn(const std::string &name, const std::shared_ptr<RDFDetail::RCustomColumnBase> &column);
37 } // ns GraphDrawing
38 
39 /// Unused, not instantiatable. Only the partial specialization RActionCRTP<RAction<...>> can be used.
40 template <typename Dummy>
41 class RActionCRTP {
42  static_assert(sizeof(Dummy) < 0, "The unspecialized version of RActionCRTP should never be instantiated");
43 };
44 
45 /// A type-erasing wrapper around RColumnValue.
46 /// Used to reduce compile time by avoiding instantiation of very large tuples and/or (std::get<N>...) fold expressions.
47 class RTypeErasedColumnValue {
48  std::shared_ptr<void> fPtr; // shared_ptr correctly deletes the type-erased object
49 
50 public:
51  template <typename T>
52  RTypeErasedColumnValue(std::unique_ptr<RColumnValue<T>> v) : fPtr(std::move(v))
53  {
54  }
55 
56  template <typename T>
57  T &Get(ULong64_t e)
58  {
59  return std::static_pointer_cast<RColumnValue<T>>(fPtr)->Get(e);
60  }
61 
62  template <typename T>
63  RColumnValue<T> *Cast()
64  {
65  return static_cast<RColumnValue<T> *>(fPtr.get());
66  }
67 };
68 
69 /// This overload is specialized to act on RTypeErasedColumnValues instead of RColumnValues.
70 template <std::size_t... S, typename... ColTypes>
71 void InitRDFValues(unsigned int slot, std::vector<RTypeErasedColumnValue> &values, TTreeReader *r,
72  const ColumnNames_t &bn, const RBookedCustomColumns &customCols, std::index_sequence<S...>,
73  ROOT::TypeTraits::TypeList<ColTypes...>, const std::array<bool, sizeof...(S)> &isTmpColumn)
74 {
75  using expander = int[];
76  (void)slot; // avoid bogus 'unused parameter' warning
77  (void)r; // avoid bogus 'unused parameter' warning
78  (void)expander{(values.emplace_back(std::make_unique<RColumnValue<ColTypes>>()), 0)..., 0};
79  (void)expander{(isTmpColumn[S]
80  ? values[S].Cast<ColTypes>()->SetTmpColumn(slot, customCols.GetColumns().at(bn.at(S)).get())
81  : values[S].Cast<ColTypes>()->MakeProxy(r, bn.at(S)),
82  0)...,
83  0};
84 }
85 
86 /// This overload is specialized to act on RTypeErasedColumnValues instead of RColumnValues.
87 template <std::size_t... S, typename... ColTypes>
88 void ResetRDFValueTuple(std::vector<RTypeErasedColumnValue> &values, std::index_sequence<S...>,
89  ROOT::TypeTraits::TypeList<ColTypes...>)
90 {
91  using expander = int[];
92  (void)expander{(values[S].Cast<ColTypes>()->Reset(), 0)...};
93 }
94 
95 // fwd decl for RActionCRTP
96 template <typename Helper, typename PrevDataFrame, typename ColumnTypes_t>
97 class RAction;
98 
99 /// A common template base class for all RActions. Avoids code repetition for specializations of RActions
100 /// for different helpers, implementing all of the common logic.
101 template <typename Helper, typename PrevDataFrame, typename ColumnTypes_t>
102 class RActionCRTP<RAction<Helper, PrevDataFrame, ColumnTypes_t>> : public RActionBase {
103  using Action_t = RAction<Helper, PrevDataFrame, ColumnTypes_t>;
104 
105  Helper fHelper;
106  const std::shared_ptr<PrevDataFrame> fPrevDataPtr;
107  PrevDataFrame &fPrevData;
108 
109 protected:
110  /// The nth flag signals whether the nth input column is a custom column or not.
111  std::array<bool, ColumnTypes_t::list_size> fIsCustomColumn;
112 
113 public:
114  using TypeInd_t = std::make_index_sequence<ColumnTypes_t::list_size>;
115 
116  RActionCRTP(Helper &&h, const ColumnNames_t &columns, std::shared_ptr<PrevDataFrame> pd,
117  RBookedCustomColumns &&customColumns)
118  : RActionBase(pd->GetLoopManagerUnchecked(), columns, std::move(customColumns)), fHelper(std::forward<Helper>(h)),
119  fPrevDataPtr(std::move(pd)), fPrevData(*fPrevDataPtr), fIsCustomColumn()
120  {
121  const auto nColumns = columns.size();
122  const auto &customCols = GetCustomColumns();
123  for (auto i = 0u; i < nColumns; ++i)
124  fIsCustomColumn[i] = customCols.HasName(columns[i]);
125  }
126 
127  RActionCRTP(const RActionCRTP &) = delete;
128  RActionCRTP &operator=(const RActionCRTP &) = delete;
129  // must call Deregister here, before fPrevDataFrame is destroyed,
130  // otherwise if fPrevDataFrame is fLoopManager we get a use after delete
131  ~RActionCRTP() { fLoopManager->Deregister(this); }
132 
133  Helper &GetHelper() { return fHelper; }
134 
135  void Initialize() final { fHelper.Initialize(); }
136 
137  void InitSlot(TTreeReader *r, unsigned int slot) final
138  {
139  for (auto &bookedBranch : GetCustomColumns().GetColumns())
140  bookedBranch.second->InitSlot(r, slot);
141  static_cast<Action_t *>(this)->InitColumnValues(r, slot);
142  fHelper.InitTask(r, slot);
143  }
144 
145  void Run(unsigned int slot, Long64_t entry) final
146  {
147  // check if entry passes all filters
148  if (fPrevData.CheckFilters(slot, entry))
149  static_cast<Action_t *>(this)->Exec(slot, entry, TypeInd_t());
150  }
151 
152  void TriggerChildrenCount() final { fPrevData.IncrChildrenCount(); }
153 
154  void FinalizeSlot(unsigned int slot) final
155  {
156  ClearValueReaders(slot);
157  for (auto &column : GetCustomColumns().GetColumns()) {
158  column.second->ClearValueReaders(slot);
159  }
160  fHelper.CallFinalizeTask(slot);
161  }
162 
163  void ClearValueReaders(unsigned int slot) { static_cast<Action_t *>(this)->ResetColumnValues(slot, TypeInd_t()); }
164 
165  void Finalize() final
166  {
167  fHelper.Finalize();
168  SetHasRun();
169  }
170 
171  std::shared_ptr<RDFGraphDrawing::GraphNode> GetGraph()
172  {
173  auto prevNode = fPrevData.GetGraph();
174  auto prevColumns = prevNode->GetDefinedColumns();
175 
176  // Action nodes do not need to ask an helper to create the graph nodes. They are never common nodes between
177  // multiple branches
178  auto thisNode = std::make_shared<RDFGraphDrawing::GraphNode>(fHelper.GetActionName());
179  auto evaluatedNode = thisNode;
180  for (auto &column : GetCustomColumns().GetColumns()) {
181  /* Each column that this node has but the previous hadn't has been defined in between,
182  * so it has to be built and appended. */
183  if (RDFGraphDrawing::CheckIfDefaultOrDSColumn(column.first, column.second))
184  continue;
185  if (std::find(prevColumns.begin(), prevColumns.end(), column.first) == prevColumns.end()) {
186  auto defineNode = RDFGraphDrawing::CreateDefineNode(column.first, column.second.get());
187  evaluatedNode->SetPrevNode(defineNode);
188  evaluatedNode = defineNode;
189  }
190  }
191 
192  thisNode->AddDefinedColumns(GetCustomColumns().GetNames());
193  thisNode->SetAction(HasRun());
194  evaluatedNode->SetPrevNode(prevNode);
195  return thisNode;
196  }
197 
198  /// This method is invoked to update a partial result during the event loop, right before passing the result to a
199  /// user-defined callback registered via RResultPtr::RegisterCallback
200  void *PartialUpdate(unsigned int slot) final { return PartialUpdateImpl(slot); }
201 
202 private:
203  // this overload is SFINAE'd out if Helper does not implement `PartialUpdate`
204  // the template parameter is required to defer instantiation of the method to SFINAE time
205  template <typename H = Helper>
206  auto PartialUpdateImpl(unsigned int slot) -> decltype(std::declval<H>().PartialUpdate(slot), (void *)(nullptr))
207  {
208  return &fHelper.PartialUpdate(slot);
209  }
210 
211  // this one is always available but has lower precedence thanks to `...`
212  void *PartialUpdateImpl(...) { throw std::runtime_error("This action does not support callbacks!"); }
213 };
214 
215 /// An action node in a RDF computation graph.
216 template <typename Helper, typename PrevDataFrame, typename ColumnTypes_t = typename Helper::ColumnTypes_t>
217 class RAction final : public RActionCRTP<RAction<Helper, PrevDataFrame, ColumnTypes_t>> {
218  std::vector<RDFValueTuple_t<ColumnTypes_t>> fValues;
219 
220 public:
221  using ActionCRTP_t = RActionCRTP<RAction<Helper, PrevDataFrame, ColumnTypes_t>>;
222 
223  RAction(Helper &&h, const ColumnNames_t &bl, std::shared_ptr<PrevDataFrame> pd,
224  RBookedCustomColumns &&customColumns)
225  : ActionCRTP_t(std::forward<Helper>(h), bl, std::move(pd), std::move(customColumns)), fValues(GetNSlots()) { }
226 
227  void InitColumnValues(TTreeReader *r, unsigned int slot)
228  {
229  InitRDFValues(slot, fValues[slot], r, RActionBase::GetColumnNames(), RActionBase::GetCustomColumns(),
230  typename ActionCRTP_t::TypeInd_t{}, ActionCRTP_t::fIsCustomColumn);
231  }
232 
233  template <std::size_t... S>
234  void Exec(unsigned int slot, Long64_t entry, std::index_sequence<S...>)
235  {
236  (void)entry; // avoid bogus 'unused parameter' warning in gcc4.9
237  ActionCRTP_t::GetHelper().Exec(slot, std::get<S>(fValues[slot]).Get(entry)...);
238  }
239 
240  template <std::size_t... S>
241  void ResetColumnValues(unsigned int slot, std::index_sequence<S...> s)
242  {
243  ResetRDFValueTuple(fValues[slot], s);
244  }
245 };
246 
247 // These specializations let RAction<SnapshotHelper[MT]> type-erase their column values, for (presumably) a small hit in
248 // performance (which hopefully be completely swallowed by the cost of I/O during the event loop) and a large,
249 // measurable gain in compile time and therefore jitting time.
250 // Snapshot is the action that most suffers from long compilation times because it happens to be called with dozens
251 // if not with a few hundred template parameters, which pretty much never happens for other actions.
252 
253 // fwd decl
254 template <typename... BranchTypes>
255 class SnapshotHelper;
256 
257 template <typename... BranchTypes>
258 class SnapshotHelperMT;
259 
260 template <typename PrevDataFrame, typename... ColTypes>
261 class RAction<SnapshotHelper<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>> final
262  : public RActionCRTP<RAction<SnapshotHelper<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>>> {
263 
264  using ActionCRTP_t =
265  RActionCRTP<RAction<SnapshotHelper<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>>>;
266  using ColumnTypes_t = typename SnapshotHelper<ColTypes...>::ColumnTypes_t;
267 
268  std::vector<std::vector<RTypeErasedColumnValue>> fValues;
269 
270 public:
271  RAction(SnapshotHelper<ColTypes...> &&h, const ColumnNames_t &bl, std::shared_ptr<PrevDataFrame> pd,
272  RBookedCustomColumns &&customColumns)
273  : ActionCRTP_t(std::forward<SnapshotHelper<ColTypes...>>(h), bl, std::move(pd), std::move(customColumns)),
274  fValues(GetNSlots())
275  {
276  }
277 
278  void InitColumnValues(TTreeReader *r, unsigned int slot)
279  {
280  InitRDFValues(slot, fValues[slot], r, RActionBase::GetColumnNames(), RActionBase::GetCustomColumns(),
281  typename ActionCRTP_t::TypeInd_t{}, ColumnTypes_t{}, ActionCRTP_t::fIsCustomColumn);
282  }
283 
284  template <std::size_t... S>
285  void Exec(unsigned int slot, Long64_t entry, std::index_sequence<S...>)
286  {
287  (void)entry; // avoid bogus 'unused parameter' warning in gcc4.9
288  ActionCRTP_t::GetHelper().Exec(slot, fValues[slot][S].template Get<ColTypes>(entry)...);
289  }
290 
291  template <std::size_t... S>
292  void ResetColumnValues(unsigned int slot, std::index_sequence<S...> s)
293  {
294  ResetRDFValueTuple(fValues[slot], s, ColumnTypes_t{});
295  }
296 };
297 
298 // Same exact code as above, but for SnapshotHelperMT. I don't know how to avoid repeating this code
299 template <typename PrevDataFrame, typename... ColTypes>
300 class RAction<SnapshotHelperMT<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>> final
301  : public RActionCRTP<
302  RAction<SnapshotHelperMT<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>>> {
303 
304  using ActionCRTP_t =
305  RActionCRTP<RAction<SnapshotHelperMT<ColTypes...>, PrevDataFrame, ROOT::TypeTraits::TypeList<ColTypes...>>>;
306  using ColumnTypes_t = typename SnapshotHelperMT<ColTypes...>::ColumnTypes_t;
307 
308  std::vector<std::vector<RTypeErasedColumnValue>> fValues;
309 
310 public:
311  RAction(SnapshotHelperMT<ColTypes...> &&h, const ColumnNames_t &bl, std::shared_ptr<PrevDataFrame> pd,
312  RBookedCustomColumns &&customColumns)
313  : ActionCRTP_t(std::forward<SnapshotHelperMT<ColTypes...>>(h), bl, std::move(pd), std::move(customColumns)),
314  fValues(GetNSlots())
315  {
316  }
317 
318  void InitColumnValues(TTreeReader *r, unsigned int slot)
319  {
320  InitRDFValues(slot, fValues[slot], r, RActionBase::GetColumnNames(), RActionBase::GetCustomColumns(),
321  typename ActionCRTP_t::TypeInd_t{}, ColumnTypes_t{}, ActionCRTP_t::fIsCustomColumn);
322  }
323 
324  template <std::size_t... S>
325  void Exec(unsigned int slot, Long64_t entry, std::index_sequence<S...>)
326  {
327  (void)entry; // avoid bogus 'unused parameter' warning in gcc4.9
328  ActionCRTP_t::GetHelper().Exec(slot, fValues[slot][S].template Get<ColTypes>(entry)...);
329  }
330 
331  template <std::size_t... S>
332  void ResetColumnValues(unsigned int slot, std::index_sequence<S...> s)
333  {
334  ResetRDFValueTuple(fValues[slot], s, ColumnTypes_t{});
335  }
336 };
337 
338 } // ns RDF
339 } // ns Internal
340 } // ns ROOT
341 
342 #endif // ROOT_RACTION