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  //for testing, state that TriggerResults is at the end of all paths
146  const std::string kTriggerResults("TriggerResults");
147  unsigned int kTriggerResultsIndex = kInvalidIndex;
148  unsigned int largestIndex = 0;
149  unsigned int kPathToTriggerResultsDependencyLastIndex = kInvalidIndex;
150  for(auto const& description: iPnC.allModules()) {
151  moduleIndexToNames.insert(std::make_pair(description->id(),
152  description->moduleLabel()));
153  if(kTriggerResults == description->moduleLabel()) {
154  kTriggerResultsIndex = description->id();
155  }
156  if(description->id() > largestIndex) {
157  largestIndex = description->id();
158  }
159  }
160  kPathToTriggerResultsDependencyLastIndex = largestIndex ;
161 
162 
163  /*
164  {
165  //We need to explicitly check that modules on Paths do not try to read data from
166  // Modules which are only on EndPaths. The circular dependency finder has been
167  // known to miss these.
168  std::unordered_set<unsigned int> modulesOnlyOnEndPaths;
169  auto const& endPaths = iPnC.endPaths();
170  for( unsigned int pathIndex = 0; pathIndex != endPaths.size(); ++pathIndex) {
171  auto const& moduleDescriptions = iPnC.modulesOnEndPath(pathIndex);
172  for(auto const& description: moduleDescriptions) {
173  modulesOnlyOnEndPaths.insert(description->id());
174  }
175  }
176 
177  std::unordered_set<unsigned int> modulesOnPaths;
178  auto const& paths = iPnC.paths();
179  for( unsigned int pathIndex = 0; pathIndex != paths.size(); ++pathIndex) {
180  auto const& moduleDescriptions = iPnC.modulesOnPath(pathIndex);
181  for(auto const& description: moduleDescriptions) {
182  auto itFind =modulesOnlyOnEndPaths.find(description->id());
183  if(modulesOnlyOnEndPaths.end() != itFind) {
184  modulesOnlyOnEndPaths.erase(itFind);
185  }
186  modulesOnPaths.insert(description->id());
187  }
188  }
189 
190  for(auto moduleIndex : modulesOnPaths) {
191  auto const& dependentModules = iPnC.modulesWhoseProductsAreConsumedBy(moduleIndex);
192  for(auto const& depDescription: dependentModules) {
193  auto itFind = modulesOnlyOnEndPaths.find(depDescription->id());
194  if(itFind != modulesOnlyOnEndPaths.end()) {
195  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
196  <<"The module "<<moduleIndexToNames[moduleIndex]<<" is on a Path and depends on data from module "
197  <<moduleIndexToNames[depDescription->id()]<<" which is on an EndPath.";
198  }
199  }
200  }
201 
202  }
203  */
204 
205  //If a module to module dependency comes from a path, remember which path
206  EdgeToPathMap edgeToPathMap;
207 
208  //Need to be able to quickly look up which paths a module appears on
209  std::unordered_map<unsigned int, std::vector<unsigned int>> moduleIndexToPathIndex;
210 
211  //determine the path dependencies
212  std::vector<std::string> pathNames = iPnC.paths();
213  const unsigned int kFirstEndPathIndex = pathNames.size();
214 
215  const std::string kPathEnded("@PathEnded");
216  const std::string kEndPathStart("@EndPathStart");
217 
218  //The finished processing depends on all paths and end paths
219  const std::string kFinishedProcessing("@FinishedProcessing");
220  const unsigned int kFinishedProcessingIndex{0};
221  moduleIndexToNames.insert(std::make_pair(kFinishedProcessingIndex,
222  kFinishedProcessing));
223 
224 
225  pathNames.insert(pathNames.end(), iPnC.endPaths().begin(), iPnC.endPaths().end());
226  std::vector<std::vector<unsigned int>> pathIndexToModuleIndexOrder(pathNames.size());
227  {
228 
229  for(unsigned int pathIndex = 0; pathIndex != pathNames.size(); ++pathIndex) {
230  std::set<unsigned int> alreadySeenIndex;
231 
232  std::vector<ModuleDescription const*> const* moduleDescriptions;
233  if(pathIndex < kFirstEndPathIndex) {
234  moduleDescriptions= &(iPnC.modulesOnPath(pathIndex));
235  } else {
236  moduleDescriptions= &(iPnC.modulesOnEndPath(pathIndex-kFirstEndPathIndex));
237  }
238  unsigned int lastModuleIndex = kInvalidIndex;
239  auto& pathOrder =pathIndexToModuleIndexOrder[pathIndex];
240  pathOrder.reserve(moduleDescriptions->size() + 1);
241  for(auto const& description: *moduleDescriptions) {
242  auto found = alreadySeenIndex.insert(description->id());
243  if(found.second) {
244  //first time for this path
245  unsigned int const moduleIndex = description->id();
246  pathOrder.push_back(moduleIndex);
247  auto& paths =moduleIndexToPathIndex[moduleIndex];
248  paths.push_back(pathIndex);
249  if(lastModuleIndex != kInvalidIndex ) {
250  edgeToPathMap[std::make_pair(moduleIndex,lastModuleIndex)].push_back(pathIndex);
251  }
252  lastModuleIndex = moduleIndex;
253  }
254  }
255  //Have TriggerResults depend on the end of all paths
256  // Have all EndPaths depend on TriggerResults
257  if( pathIndex < kFirstEndPathIndex) {
258  if( (lastModuleIndex != kInvalidIndex) ) {
259  ++kPathToTriggerResultsDependencyLastIndex;
260  edgeToPathMap[std::make_pair(kPathToTriggerResultsDependencyLastIndex,lastModuleIndex)].push_back(pathIndex);
261  moduleIndexToNames.insert(std::make_pair(kPathToTriggerResultsDependencyLastIndex,
262  kPathEnded));
263  if (kTriggerResultsIndex != kInvalidIndex) {
264  edgeToPathMap[std::make_pair(kTriggerResultsIndex, kPathToTriggerResultsDependencyLastIndex)].push_back(kDataDependencyIndex);
265  }
266  //Need to make dependency for finished process
267  edgeToPathMap[std::make_pair(kFinishedProcessingIndex, kPathToTriggerResultsDependencyLastIndex)].push_back(kDataDependencyIndex);
268  pathOrder.push_back(kPathToTriggerResultsDependencyLastIndex);
269  }
270  } else {
271  if( (not moduleDescriptions->empty()) ) {
272  if (kTriggerResultsIndex != kInvalidIndex) {
273  ++kPathToTriggerResultsDependencyLastIndex;
274  edgeToPathMap[std::make_pair(moduleDescriptions->front()->id(),kPathToTriggerResultsDependencyLastIndex)].push_back(pathIndex);
275  moduleIndexToNames.insert(std::make_pair(kPathToTriggerResultsDependencyLastIndex,
276  kEndPathStart));
277  edgeToPathMap[std::make_pair(kPathToTriggerResultsDependencyLastIndex,kTriggerResultsIndex)].push_back(kDataDependencyIndex);
278  pathOrder.insert(pathOrder.begin(),kPathToTriggerResultsDependencyLastIndex);
279  }
280  //Need to make dependency for finished process
281  ++kPathToTriggerResultsDependencyLastIndex;
282  edgeToPathMap[std::make_pair(kPathToTriggerResultsDependencyLastIndex,lastModuleIndex)].push_back(pathIndex);
283  moduleIndexToNames.insert(std::make_pair(kPathToTriggerResultsDependencyLastIndex,
284  kPathEnded));
285  edgeToPathMap[std::make_pair(kFinishedProcessingIndex, kPathToTriggerResultsDependencyLastIndex)].push_back(kDataDependencyIndex);
286  pathOrder.push_back(kPathToTriggerResultsDependencyLastIndex);
287  }
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") << "ModuleDependency '" << description->moduleLabel() <<
300  "' depends on data products from module '" << depDescription->moduleLabel()<<"'";
301  }
302  //see if all paths containing this module also contain the dependent module earlier in the path
303  // if it does, then treat this only as a path dependency and not a data dependency as this
304  // simplifies the circular dependency checking logic
305  auto depID =depDescription->id();
306  auto itPathsFound = moduleIndexToPathIndex.find(moduleIndex);
307  bool keepDataDependency = true;
308  auto itDepsPathsFound =moduleIndexToPathIndex.find(depID);
309  if(itPathsFound != moduleIndexToPathIndex.end() and itDepsPathsFound != moduleIndexToPathIndex.end()) {
310  keepDataDependency = false;
311  for(auto const pathIndex: itPathsFound->second) {
312  for(auto idToCheck: pathIndexToModuleIndexOrder[pathIndex]) {
313  if(idToCheck == depID) {
314  //found dependent module first so check next path
315  break;
316  }
317  if(idToCheck == moduleIndex) {
318  //did not find dependent module earlier on path so
319  // must keep data dependency
320  keepDataDependency = true;
321  break;
322  }
323  }
324  if(keepDataDependency) {
325  break;
326  }
327  }
328  }
329  if(keepDataDependency) {
330  edgeToPathMap[std::make_pair(moduleIndex, depID)].push_back(kDataDependencyIndex);
331  }
332  }
333  }
334  }
335  graph::throwIfImproperDependencies(edgeToPathMap,pathIndexToModuleIndexOrder,pathNames,moduleIndexToNames);
336  }
337 }
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:1060
AllWorkers const & allWorkers() const
returns the collection of pointers to workers
Definition: Schedule.cc:1020
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
virtual 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:1053
std::vector< std::pair< unsigned int, unsigned int > > moduleIDToIndex_
virtual 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
virtual 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:1030
std::vector< std::string > endPaths_
virtual 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_
virtual 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:1035
void moduleDescriptionsInPath(std::string const &iPathLabel, std::vector< ModuleDescription const * > &descriptions, unsigned int hint) const
Definition: Schedule.cc:1046