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