CMS 3D CMS Logo

ProcessCallGraph.cc
Go to the documentation of this file.
1 /*
2  *
3  */
4 
5 #include <cassert>
6 #include <iostream>
7 #include <string>
8 #include <type_traits>
9 #include <vector>
10 
11 // boost optional (used by boost graph) results in some false positives with -Wmaybe-uninitialized
12 #pragma GCC diagnostic push
13 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
14 #include <boost/graph/depth_first_search.hpp>
15 #pragma GCC diagnostic pop
16 
31 
32 
34 {
35 }
36 
37 
38 // adaptor to use range-based for loops with boost::graph edges(...) and vertices(...) functions
39 template <typename I>
40 struct iterator_pair_as_a_range : std::pair<I, I>
41 {
42 public:
43  using std::pair<I, I>::pair;
44 
45  I begin() { return this->first; }
46  I end() { return this->second; }
47 };
48 
49 template <typename I>
51 {
53 }
54 
55 
56 // FIXME
57 // - check that the Source has not already been added
58 void
60 {
61  // keep track of the Source module id
62  source_ = module.id();
63 
64  // create graph vertex for the source module
65  boost::add_vertex(graph_);
66  graph_.m_graph[module.id()] = { module, edm::EDMModuleType::kSource, true };
67 }
68 
69 
70 // FIXME
71 // - check that the Source has already been added
72 // - check that all module ids are valid (e.g. subprocesses are not being added in
73 // the wrong order)
74 void
76 {
77  unsigned int pid = registerProcess(context);
78 
79  // work on the full graph (for the main process) or a subgraph (for a subprocess)
80  GraphType & graph = context.isSubProcess() ? graph_.create_subgraph() : graph_.root();
81 
82  // set the graph name property to the process name
83  boost::get_property(graph, boost::graph_name) = context.processName();
84 
85  // create graph vertices associated to all modules in the process
86  auto size = pathsAndConsumes.allModules().size();
87  for (size_t i = 0; i < size; ++i)
88  boost::add_vertex(graph);
89 
90  // set the vertices properties (use the module id as the global index into the graph)
91  std::vector<unsigned int> modules;
92  modules.reserve(size);
93  for (edm::ModuleDescription const * module: pathsAndConsumes.allModules()) {
94  modules.push_back(module->id());
95  graph_.m_graph[module->id()] = { *module, edmModuleTypeEnum(*module), false };
96  }
97 
98  // add graph edges associated to module dependencies
99  for (edm::ModuleDescription const * consumer: pathsAndConsumes.allModules()) {
100  for (edm::ModuleDescription const * module: pathsAndConsumes.modulesWhoseProductsAreConsumedBy(consumer->id())) {
101  // module `consumer' depends on module `module'
102  boost::add_edge(consumer->id(), module->id(), graph_);
103  }
104  }
105 
106  // extract path names from the TriggerNamesService
108 
109  // extract the details of the paths and endpaths: name, modules on the path, and their dependencies
110  size = pathsAndConsumes.paths().size();
111  assert (tns.getTrigPaths().size() == size);
112  std::vector<PathType> paths;
113  paths.reserve(size);
114  for (unsigned int i = 0; i < size; ++i) {
115  std::vector<unsigned int> modules;
116  for (edm::ModuleDescription const * module: pathsAndConsumes.modulesOnPath(i)) {
117  modules.push_back(module->id());
118  // mark the modules in the Paths as scheduled
119  graph_.m_graph[module->id()].scheduled_ = true;
120  }
121  auto deps = dependencies(modules);
122  paths.emplace_back(tns.getTrigPath(i), modules, deps.first, deps.second);
123  }
124  size = pathsAndConsumes.endPaths().size();
125  std::vector<PathType> endPaths;
126  endPaths.reserve(size);
127  for (unsigned int i = 0; i < size; ++i) {
128  std::vector<unsigned int> modules;
129  for (edm::ModuleDescription const * module: pathsAndConsumes.modulesOnEndPath(i)) {
130  modules.push_back(module->id());
131  // mark the modules in the EndPaths as scheduled
132  graph_.m_graph[module->id()].scheduled_ = true;
133  }
134  auto deps = dependencies(modules);
135  endPaths.emplace_back(tns.getEndPath(i), modules, deps.first, deps.second);
136  }
137 
138  // store the description of process, modules and paths
139  process_description_.emplace_back(
140  context.processName(),
141  graph,
142  modules,
143  paths,
144  endPaths);
145  assert(process_description_.size() == pid+1);
146 
147  // attach a subprocess to its parent
148  if (context.isSubProcess()) {
149  unsigned int parent_pid = processId(context.parentProcessContext());
150  process_description_[parent_pid].subprocesses_.push_back(pid);
151  }
152 }
153 
154 
155 // number of modules stored in the call graph
156 unsigned int
158 {
159  return boost::num_vertices(graph_);
160 }
161 
162 
163 // retrieve the ModuleDescriptio associated to the given id and vertex
166 {
167  return graph_.m_graph[source_].module_;
168 }
169 
170 
171 // retrieve the ModuleDescription associated to the given id and vertex
173 ProcessCallGraph::module(unsigned int module) const
174 {
175  return graph_.m_graph[module].module_;
176 }
177 
178 
179 // retrieve the full information for a given module
182 {
183  return graph_.m_graph[module];
184 }
185 
186 
187 // find the dependencies of the given module
188 std::vector<unsigned int>
190 {
191  std::vector<unsigned int> colors(boost::num_vertices(graph_));
192  auto colormap = boost::make_container_vertex_map(colors);
193 
194  // depht-first visit all vertices starting from the given module
195  boost::default_dfs_visitor visitor;
196  boost::depth_first_visit(graph_, module, visitor, colormap);
197 
198  // count the visited vertices (the `black' ones) in order to properly size the
199  // output vector; then fill the dependencies with the list of visited nodes
200  unsigned int size = 0;
201  for (unsigned int i = 0; i < colors.size(); ++i)
202  if (boost::black_color == colors[i])
203  ++size;
204  std::vector<unsigned int> dependencies(size);
205  unsigned j = 0;
206  for (unsigned int i = 0; i < colors.size(); ++i)
207  if (boost::black_color == colors[i])
208  dependencies[j++] = i;
209  assert(size == j);
210 
211  return dependencies;
212 }
213 
214 // find the dependencies of all modules in the given path
215 //
216 // return two vector:
217 // - the first lists all the dependencies for the whole path
218 // - the second lists the one-after-the-last dependency index into the first vector for each module
219 std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
220 ProcessCallGraph::dependencies(std::vector<unsigned int> const & path)
221 {
222  std::vector<unsigned int> colors(boost::num_vertices(graph_));
223  auto colormap = boost::make_container_vertex_map(colors);
224 
225  // first, find and count all the path's modules' dependencies
226  boost::default_dfs_visitor visitor;
227  for (unsigned int module: path)
228  boost::depth_first_visit(graph_, module, visitor, colormap);
229 
230  unsigned int size = 0;
231  for (unsigned int i = 0; i < colors.size(); ++i)
232  if (colors[i] == 0)
233  ++size;
234 
235  // allocate the output vectors
236  std::vector<unsigned int> dependencies(size);
237  dependencies.resize(0);
238  std::vector<unsigned int> indices(path.size());
239  indices.resize(0);
240 
241  // reset the color map
242  for (unsigned int i = 0; i < colors.size(); ++i)
243  colors[i] = 0;
244 
245  // find again all the dependencies, and record those associated to each module
246  struct record_vertices : boost::default_dfs_visitor {
247  record_vertices(std::vector<unsigned int> & vertices) :
248  vertices_(vertices) { }
249 
250  void discover_vertex(unsigned int vertex, GraphType const& graph) {
251  vertices_.push_back(vertex);
252  }
253 
254  std::vector<unsigned int> & vertices_;
255  };
256  record_vertices recorder(dependencies);
257 
258  for (unsigned int module: path) {
259  // skip modules that have already been added as dependencies
260  if (colors[module] != boost::black_color)
261  boost::depth_first_visit(graph_, module, recorder, colormap);
262  indices.push_back(dependencies.size());
263  }
264 
265  return std::make_pair(dependencies, indices);
266 
267 }
268 
269 // register a (sub)process and assigns it a "process id"
270 // if called with a duplicate process name, returns the original process id
272 {
273  static unsigned int s_id = 0;
274 
275  // registerProcess (called by preBeginJob) must be called for the parent process before its subprocess(es)
276  if (context.isSubProcess() and process_id_.find(context.parentProcessContext().processName()) == process_id_.end()) {
278  << "ProcessCallGraph::preBeginJob(): called for subprocess \"" << context.processName() << "\""
279  << " before being called for its parent process \"" << context.parentProcessContext().processName() << "\"";
280  }
281 
282  // registerProcess (called by preBeginJob) should be called once or each (sub)process
283  auto id = process_id_.find(context.processName());
284  if (id != process_id_.end()) {
286  << "ProcessCallGraph::preBeginJob(): called twice for the same "
287  << (context.isSubProcess() ? "subprocess" : "process") << " " << context.processName();
288  }
289 
290  std::tie(id, std::ignore) = process_id_.insert(std::make_pair(context.processName(), s_id++));
291  return id->second;
292 }
293 
294 
295 // retrieve the "process id" of a process, given its ProcessContex
296 // throws an exception if the (sub)process was not registered
297 unsigned int ProcessCallGraph::processId(edm::ProcessContext const & context) const
298 {
299  auto id = process_id_.find(context.processName());
300  if (id == process_id_.end())
302  << "ProcessCallGraph::processId(): unexpected " << (context.isSubProcess() ? "subprocess" : "process") << " " << context.processName();
303  return id->second;
304 }
305 
306 
307 // retrieve the "process id" of a process, given its ProcessContex
308 // throws an exception if the (sub)process was not registered
310 {
311  auto id = process_id_.find(processName);
312  if (id == process_id_.end())
314  << "ProcessCallGraph::processId(): unexpected (sub)process " << processName;
315  return id->second;
316 }
317 
318 
319 // retrieve the number of processes
320 std::vector<ProcessCallGraph::ProcessType> const & ProcessCallGraph::processes() const
321 {
322  return process_description_;
323 }
324 
325 
326 // retrieve information about a process, given its "process id"
328 {
329  return process_description_.at(pid);
330 }
331 
332 
333 // retrieve information about a process, given its ProcessContex
335 {
336  unsigned int pid = processId(context);
337  return process_description_[pid];
338 }
339 
340 
341 // retrieve information about a process, given its ProcessContex
343 {
344  unsigned int pid = processId(processName);
345  return process_description_[pid];
346 }
unsigned int registerProcess(edm::ProcessContext const &)
std::string const & processName() const
unsigned int processId(edm::ProcessContext const &) const
std::unordered_map< std::string, unsigned int > process_id_
std::vector< ModuleDescription const * > const & modulesOnEndPath(unsigned int endPathIndex) const
std::vector< ModuleDescription const * > const & modulesOnPath(unsigned int pathIndex) const
edm::ModuleDescription const & source() const
ProcessContext const & parentProcessContext() const
std::vector< ModuleDescription const * > const & allModules() const
EDMModuleType edmModuleTypeEnum(edm::ModuleDescription const &module)
std::vector< std::string > const & endPaths() const
U second(std::pair< T, U > const &p)
vector< Color_t > colors
static const edm::ProductID s_id
Definition: EventBase.cc:27
void preBeginJob(edm::PathsAndConsumesOfModulesBase const &, edm::ProcessContext const &)
std::vector< ProcessType > const & processes() const
ProcessType const & processDescription(unsigned int) const
const std::complex< double > I
Definition: I.h:8
void preSourceConstruction(edm::ModuleDescription const &)
boost::subgraph< boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, NodeType, boost::property< boost::edge_index_t, int >, boost::property< boost::graph_name_t, std::string > >> GraphType
def ignore(seq)
std::vector< ProcessType > process_description_
std::vector< unsigned int > depends(unsigned int module) const
std::vector< ModuleDescription const * > const & modulesWhoseProductsAreConsumedBy(unsigned int moduleID) const
std::vector< std::string > const & paths() const
std::pair< std::vector< unsigned int >, std::vector< unsigned int > > dependencies(std::vector< unsigned int > const &path)
unsigned int size() const
edm::ModuleDescription const & module(unsigned int module) const
unsigned int source_
iterator_pair_as_a_range< I > make_range(std::pair< I, I > p)
bool isSubProcess() const
Definition: vlib.h:208
unsigned int id() const
NodeType const & operator[](unsigned int module) const