CMS 3D CMS Logo

PathsAndConsumesOfModules.cc
Go to the documentation of this file.
2 
6 
8 
9 #include <algorithm>
10 namespace edm {
11 
13 
14  void PathsAndConsumesOfModules::initialize(Schedule const* schedule, std::shared_ptr<ProductRegistry const> preg) {
16  preg_ = preg;
17 
18  paths_.clear();
19  schedule->triggerPaths(paths_);
20 
21  endPaths_.clear();
22  schedule->endPaths(endPaths_);
23 
24  modulesOnPaths_.resize(paths_.size());
25  unsigned int i = 0;
26  unsigned int hint = 0;
27  for (auto const& path : paths_) {
28  schedule->moduleDescriptionsInPath(path, modulesOnPaths_.at(i), hint);
29  if (!modulesOnPaths_.at(i).empty())
30  ++hint;
31  ++i;
32  }
33 
34  modulesOnEndPaths_.resize(endPaths_.size());
35  i = 0;
36  hint = 0;
37  for (auto const& endpath : endPaths_) {
38  schedule->moduleDescriptionsInEndPath(endpath, modulesOnEndPaths_.at(i), hint);
39  if (!modulesOnEndPaths_.at(i).empty())
40  ++hint;
41  ++i;
42  }
43 
44  schedule->fillModuleAndConsumesInfo(
46  }
47 
49  unsigned int dummy = 0;
50  auto target = std::make_pair(moduleID, dummy);
51  std::vector<std::pair<unsigned int, unsigned int>>::const_iterator iter =
52  std::lower_bound(moduleIDToIndex_.begin(), moduleIDToIndex_.end(), target);
53  if (iter == moduleIDToIndex_.end() || iter->first != moduleID) {
54  throw Exception(errors::LogicError) << "PathsAndConsumesOfModules::moduleDescription: Unknown moduleID\n";
55  }
56  return allModuleDescriptions_.at(iter->second);
57  }
58 
59  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesOnPath(unsigned int pathIndex) const {
60  return modulesOnPaths_.at(pathIndex);
61  }
62 
63  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesOnEndPath(
64  unsigned int endPathIndex) const {
65  return modulesOnEndPaths_.at(endPathIndex);
66  }
67 
68  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesWhoseProductsAreConsumedBy(
69  unsigned int moduleID) const {
71  }
72 
73  std::vector<ConsumesInfo> PathsAndConsumesOfModules::doConsumesInfo(unsigned int moduleID) const {
74  Worker const* worker = schedule_->allWorkers().at(moduleIndex(moduleID));
75  return worker->consumesInfo();
76  }
77 
78  unsigned int PathsAndConsumesOfModules::moduleIndex(unsigned int moduleID) const {
79  unsigned int dummy = 0;
80  auto target = std::make_pair(moduleID, dummy);
81  std::vector<std::pair<unsigned int, unsigned int>>::const_iterator iter =
82  std::lower_bound(moduleIDToIndex_.begin(), moduleIDToIndex_.end(), target);
83  if (iter == moduleIDToIndex_.end() || iter->first != moduleID) {
84  throw Exception(errors::LogicError) << "PathsAndConsumesOfModules::moduleIndex: Unknown moduleID\n";
85  }
86  return iter->second;
87  }
88 
89  //====================================
90  // checkForCorrectness algorithm
91  //
92  // The code creates a 'dependency' graph between all
93  // modules. A module depends on another module if
94  // 1) it 'consumes' data produced by that module
95  // 2) it appears directly after the module within a Path
96  //
97  // If there is a cycle in the 'dependency' graph then
98  // the schedule may be unrunnable. The schedule is still
99  // runnable if all cycles have at least two edges which
100  // connect modules only by Path dependencies (i.e. not
101  // linked by a data dependency).
102  //
103  // Example 1:
104  // C consumes data from B
105  // Path 1: A + B + C
106  // Path 2: B + C + A
107  //
108  // Cycle: A after C [p2], C consumes B, B after A [p1]
109  // Since this cycle has 2 path only edges it is OK since
110  // A and (B+C) are independent so their run order doesn't matter
111  //
112  // Example 2:
113  // B consumes A
114  // C consumes B
115  // Path: C + A
116  //
117  // Cycle: A after C [p], C consumes B, B consumes A
118  // Since this cycle has 1 path only edge it is unrunnable.
119  //
120  // Example 3:
121  // A consumes B
122  // B consumes C
123  // C consumes A
124  // (no Path since unscheduled execution)
125  //
126  // Cycle: A consumes B, B consumes C, C consumes A
127  // Since this cycle has 0 path only edges it is unrunnable.
128  //====================================
129 
130  void checkForModuleDependencyCorrectness(edm::PathsAndConsumesOfModulesBase const& iPnC, bool iPrintDependencies) {
131  using namespace edm::graph;
132  //Need to lookup ids to names quickly
133  std::unordered_map<unsigned int, std::string> moduleIndexToNames;
134 
135  std::unordered_map<std::string, unsigned int> pathStatusInserterModuleLabelToModuleID;
136 
137  //for testing, state that TriggerResults is at the end of all paths
138  const std::string kTriggerResults("TriggerResults");
139  const std::string kPathStatusInserter("PathStatusInserter");
140  const std::string kEndPathStatusInserter("EndPathStatusInserter");
141  unsigned int kTriggerResultsIndex = kInvalidIndex;
142  unsigned int largestIndex = 0;
143  unsigned int kPathToTriggerResultsDependencyLastIndex = kInvalidIndex;
144  for (auto const& description : iPnC.allModules()) {
145  moduleIndexToNames.insert(std::make_pair(description->id(), description->moduleLabel()));
146  if (kTriggerResults == description->moduleLabel()) {
147  kTriggerResultsIndex = description->id();
148  }
149  if (description->id() > largestIndex) {
150  largestIndex = description->id();
151  }
152  if (description->moduleName() == kPathStatusInserter || description->moduleName() == kEndPathStatusInserter) {
153  pathStatusInserterModuleLabelToModuleID[description->moduleLabel()] = description->id();
154  }
155  }
156  kPathToTriggerResultsDependencyLastIndex = largestIndex;
157 
158  /*
159  {
160  //We need to explicitly check that modules on Paths do not try to read data from
161  // Modules which are only on EndPaths. The circular dependency finder has been
162  // known to miss these.
163  std::unordered_set<unsigned int> modulesOnlyOnEndPaths;
164  auto const& endPaths = iPnC.endPaths();
165  for( unsigned int pathIndex = 0; pathIndex != endPaths.size(); ++pathIndex) {
166  auto const& moduleDescriptions = iPnC.modulesOnEndPath(pathIndex);
167  for(auto const& description: moduleDescriptions) {
168  modulesOnlyOnEndPaths.insert(description->id());
169  }
170  }
171 
172  std::unordered_set<unsigned int> modulesOnPaths;
173  auto const& paths = iPnC.paths();
174  for( unsigned int pathIndex = 0; pathIndex != paths.size(); ++pathIndex) {
175  auto const& moduleDescriptions = iPnC.modulesOnPath(pathIndex);
176  for(auto const& description: moduleDescriptions) {
177  auto itFind =modulesOnlyOnEndPaths.find(description->id());
178  if(modulesOnlyOnEndPaths.end() != itFind) {
179  modulesOnlyOnEndPaths.erase(itFind);
180  }
181  modulesOnPaths.insert(description->id());
182  }
183  }
184 
185  for(auto moduleIndex : modulesOnPaths) {
186  auto const& dependentModules = iPnC.modulesWhoseProductsAreConsumedBy(moduleIndex);
187  for(auto const& depDescription: dependentModules) {
188  auto itFind = modulesOnlyOnEndPaths.find(depDescription->id());
189  if(itFind != modulesOnlyOnEndPaths.end()) {
190  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
191  <<"The module "<<moduleIndexToNames[moduleIndex]<<" is on a Path and depends on data from module "
192  <<moduleIndexToNames[depDescription->id()]<<" which is on an EndPath.";
193  }
194  }
195  }
196 
197  }
198  */
199 
200  //If a module to module dependency comes from a path, remember which path
201  EdgeToPathMap edgeToPathMap;
202 
203  //Need to be able to quickly look up which paths a module appears on
204  std::unordered_map<unsigned int, std::vector<unsigned int>> moduleIndexToPathIndex;
205 
206  //determine the path dependencies
207  std::vector<std::string> pathNames = iPnC.paths();
208  const unsigned int kFirstEndPathIndex = pathNames.size();
209 
210  const std::string kPathEnded("@PathEnded");
211  const std::string kEndPathStart("@EndPathStart");
212 
213  //The finished processing depends on all paths and end paths
214  const std::string kFinishedProcessing("@FinishedProcessing");
215  const unsigned int kFinishedProcessingIndex{0};
216  moduleIndexToNames.insert(std::make_pair(kFinishedProcessingIndex, kFinishedProcessing));
217 
218  pathNames.insert(pathNames.end(), iPnC.endPaths().begin(), iPnC.endPaths().end());
219  std::vector<std::vector<unsigned int>> pathIndexToModuleIndexOrder(pathNames.size());
220  {
221  for (unsigned int pathIndex = 0; pathIndex != pathNames.size(); ++pathIndex) {
222  std::set<unsigned int> alreadySeenIndex;
223 
224  std::vector<ModuleDescription const*> const* moduleDescriptions;
225  if (pathIndex < kFirstEndPathIndex) {
226  moduleDescriptions = &(iPnC.modulesOnPath(pathIndex));
227  } else {
228  moduleDescriptions = &(iPnC.modulesOnEndPath(pathIndex - kFirstEndPathIndex));
229  }
230  unsigned int lastModuleIndex = kInvalidIndex;
231  auto& pathOrder = pathIndexToModuleIndexOrder[pathIndex];
232  pathOrder.reserve(moduleDescriptions->size() + 1);
233  for (auto const& description : *moduleDescriptions) {
234  auto found = alreadySeenIndex.insert(description->id());
235  if (found.second) {
236  //first time for this path
237  unsigned int const moduleIndex = description->id();
238  pathOrder.push_back(moduleIndex);
239  auto& paths = moduleIndexToPathIndex[moduleIndex];
240  paths.push_back(pathIndex);
241  if (lastModuleIndex != kInvalidIndex) {
242  edgeToPathMap[std::make_pair(moduleIndex, lastModuleIndex)].push_back(pathIndex);
243  }
244  lastModuleIndex = moduleIndex;
245  }
246  }
247  //Have TriggerResults depend on the end of all paths
248  // Have all EndPaths depend on TriggerResults
249  auto labelToID = pathStatusInserterModuleLabelToModuleID.find(pathNames[pathIndex]);
250  if (labelToID == pathStatusInserterModuleLabelToModuleID.end()) {
251  // should never happen
253  << "PathsAndConsumesOfModules::moduleDescription:checkForModuleDependencyCorrectness Could not find "
254  "PathStatusInserter\n";
255  }
256  unsigned int pathStatusInserterModuleID = labelToID->second;
257  if (pathIndex < kFirstEndPathIndex) {
258  if ((lastModuleIndex != kInvalidIndex)) {
259  edgeToPathMap[std::make_pair(pathStatusInserterModuleID, lastModuleIndex)].push_back(pathIndex);
260  moduleIndexToNames.insert(std::make_pair(pathStatusInserterModuleID, kPathEnded));
261  if (kTriggerResultsIndex != kInvalidIndex) {
262  edgeToPathMap[std::make_pair(kTriggerResultsIndex, pathStatusInserterModuleID)].push_back(
264  }
265  //Need to make dependency for finished process
266  edgeToPathMap[std::make_pair(kFinishedProcessingIndex, pathStatusInserterModuleID)].push_back(
268  pathOrder.push_back(pathStatusInserterModuleID);
269  }
270  } else {
271  if ((not moduleDescriptions->empty())) {
272  if (kTriggerResultsIndex != kInvalidIndex) {
273  ++kPathToTriggerResultsDependencyLastIndex;
274  edgeToPathMap[std::make_pair(moduleDescriptions->front()->id(), kPathToTriggerResultsDependencyLastIndex)]
275  .push_back(pathIndex);
276  moduleIndexToNames.insert(std::make_pair(kPathToTriggerResultsDependencyLastIndex, kEndPathStart));
277  edgeToPathMap[std::make_pair(kPathToTriggerResultsDependencyLastIndex, kTriggerResultsIndex)].push_back(
279  pathOrder.insert(pathOrder.begin(), kPathToTriggerResultsDependencyLastIndex);
280  }
281  //Need to make dependency for finished process
282  ++kPathToTriggerResultsDependencyLastIndex;
283  edgeToPathMap[std::make_pair(pathStatusInserterModuleID, lastModuleIndex)].push_back(pathIndex);
284  moduleIndexToNames.insert(std::make_pair(pathStatusInserterModuleID, kPathEnded));
285  edgeToPathMap[std::make_pair(kFinishedProcessingIndex, pathStatusInserterModuleID)].push_back(
287  pathOrder.push_back(pathStatusInserterModuleID);
288  }
289  }
290  }
291  }
292  {
293  //determine the data dependencies
294  for (auto const& description : iPnC.allModules()) {
295  unsigned int const moduleIndex = description->id();
296  auto const& dependentModules = iPnC.modulesWhoseProductsAreConsumedBy(moduleIndex);
297  for (auto const& depDescription : dependentModules) {
298  if (iPrintDependencies) {
299  edm::LogAbsolute("ModuleDependency")
300  << "ModuleDependency '" << description->moduleLabel() << "' depends on data products from module '"
301  << depDescription->moduleLabel() << "'";
302  }
303  //see if all paths containing this module also contain the dependent module earlier in the path
304  // if it does, then treat this only as a path dependency and not a data dependency as this
305  // simplifies the circular dependency checking logic
306  auto depID = depDescription->id();
307  auto itPathsFound = moduleIndexToPathIndex.find(moduleIndex);
308  bool keepDataDependency = true;
309  auto itDepsPathsFound = moduleIndexToPathIndex.find(depID);
310  if (itPathsFound != moduleIndexToPathIndex.end() and itDepsPathsFound != moduleIndexToPathIndex.end()) {
311  keepDataDependency = false;
312  for (auto const pathIndex : itPathsFound->second) {
313  for (auto idToCheck : pathIndexToModuleIndexOrder[pathIndex]) {
314  if (idToCheck == depID) {
315  //found dependent module first so check next path
316  break;
317  }
318  if (idToCheck == moduleIndex) {
319  //did not find dependent module earlier on path so
320  // must keep data dependency
321  keepDataDependency = true;
322  break;
323  }
324  }
325  if (keepDataDependency) {
326  break;
327  }
328  }
329  }
330  if (keepDataDependency) {
331  edgeToPathMap[std::make_pair(moduleIndex, depID)].push_back(kDataDependencyIndex);
332  }
333  }
334  }
335  }
336  // Don't bother if there are no modules in any paths (the
337  // dependence check crashes if the configuration has only Paths
338  // with Tasks with modules, but nothing to trigger any work to
339  // run)
340  if (not moduleIndexToPathIndex.empty()) {
341  graph::throwIfImproperDependencies(edgeToPathMap, pathIndexToModuleIndexOrder, pathNames, moduleIndexToNames);
342  }
343  }
344 } // namespace edm
virtual std::vector< ConsumesInfo > consumesInfo() const =0
AllWorkers const & allWorkers() const
returns the collection of pointers to workers
Definition: Schedule.cc:1251
std::vector< std::vector< ModuleDescription const * > > modulesWhoseProductsAreConsumedBy_
void throwIfImproperDependencies(EdgeToPathMap const &, std::vector< std::vector< unsigned int >> const &iPathIndexToModuleIndexOrder, std::vector< std::string > const &iPathNames, std::unordered_map< unsigned int, std::string > const &iModuleIndexToNames)
std::map< SimpleEdge, std::vector< unsigned int >> EdgeToPathMap
std::vector< ModuleDescription const * > const & doModulesOnPath(unsigned int pathIndex) const override
constexpr auto kDataDependencyIndex
std::vector< ModuleDescription const * > const & modulesOnEndPath(unsigned int endPathIndex) const
std::vector< ModuleDescription const * > const & modulesOnPath(unsigned int pathIndex) const
unsigned int moduleIndex(unsigned int moduleID) const
void moduleDescriptionsInEndPath(std::string const &iEndPathLabel, std::vector< ModuleDescription const * > &descriptions, unsigned int hint) const
Definition: Schedule.cc:1277
std::vector< std::pair< unsigned int, unsigned int > > moduleIDToIndex_
std::vector< ModuleDescription const * > const & doModulesWhoseProductsAreConsumedBy(unsigned int moduleID) const override
std::vector< ModuleDescription const * > allModuleDescriptions_
std::vector< ModuleDescription const * > const & allModules() const
std::vector< std::string > const & endPaths() const
std::vector< ModuleDescription const * > const & doModulesOnEndPath(unsigned int endPathIndex) const override
void initialize(Schedule const *, std::shared_ptr< ProductRegistry const >)
std::vector< std::vector< ModuleDescription const * > > modulesOnPaths_
void triggerPaths(std::vector< std::string > &oLabelsToFill) const
Definition: Schedule.cc:1263
std::vector< std::string > endPaths_
std::vector< ConsumesInfo > doConsumesInfo(unsigned int moduleID) const override
void fillModuleAndConsumesInfo(std::vector< ModuleDescription const * > &allModuleDescriptions, std::vector< std::pair< unsigned int, unsigned int >> &moduleIDToIndex, std::vector< std::vector< ModuleDescription const * >> &modulesWhoseProductsAreConsumedBy, ProductRegistry const &preg) const
Definition: Schedule.cc:1283
void checkForModuleDependencyCorrectness(edm::PathsAndConsumesOfModulesBase const &iPnC, bool iPrintDependencies)
std::shared_ptr< ProductRegistry const > preg_
std::vector< ModuleDescription const * > const & modulesWhoseProductsAreConsumedBy(unsigned int moduleID) const
HLT enums.
std::vector< std::string > const & paths() const
std::vector< std::vector< ModuleDescription const * > > modulesOnEndPaths_
ModuleDescription const * doModuleDescription(unsigned int moduleID) const override
constexpr auto kInvalidIndex
void endPaths(std::vector< std::string > &oLabelsToFill) const
adds to oLabelsToFill the labels for all end paths in the process
Definition: Schedule.cc:1265
void moduleDescriptionsInPath(std::string const &iPathLabel, std::vector< ModuleDescription const * > &descriptions, unsigned int hint) const
Definition: Schedule.cc:1271