@@ -463,49 +463,87 @@ int64_t EdgeWeightHeuristic(Edge *edge) {
463463
464464void Plan::ComputeCriticalPath () {
465465 METRIC_RECORD (" ComputeCriticalPath" );
466- // Remove duplicate targets
467- std::unordered_set<const Node*> unique_targets (targets_.begin (),
468- targets_.end ());
469-
470- // Use backflow algorithm to compute the critical path for all
471- // nodes, starting from the destination nodes.
472- // XXX: ignores pools
473- std::queue<Edge*> work_queue; // Queue, for breadth-first traversal
474- // The set of edges currently in work_queue, to avoid duplicates.
475- std::unordered_set<const Edge*> active_edges;
476-
477- for (const Node* target : unique_targets) {
478- if (Edge* in = target->in_edge ()) {
479- int64_t edge_weight = EdgeWeightHeuristic (in);
480- in->set_critical_path_weight (
481- std::max<int64_t >(edge_weight, in->critical_path_weight ()));
482- if (active_edges.insert (in).second ) {
483- work_queue.push (in);
466+
467+ // Convenience class to perform a topological sort of all edges
468+ // reachable from a set of unique targets. Usage is:
469+ //
470+ // 1) Create instance.
471+ //
472+ // 2) Call VisitTarget() as many times as necessary.
473+ // Note that duplicate targets are properly ignored.
474+ //
475+ // 3) Call result() to get a sorted list of edges,
476+ // where each edge appears _after_ its parents,
477+ // i.e. the edges producing its inputs, in the list.
478+ //
479+ struct TopoSort {
480+ void VisitTarget (const Node* target) {
481+ Edge* producer = target->in_edge ();
482+ if (producer)
483+ Visit (producer);
484+ }
485+
486+ const std::vector<Edge*>& result () const { return sorted_edges_; }
487+
488+ private:
489+ // Implementation note:
490+ //
491+ // This is the regular depth-first-search algorithm described
492+ // at https://en.wikipedia.org/wiki/Topological_sorting, except
493+ // that:
494+ //
495+ // - Edges are appended to the end of the list, for performance
496+ // reasons. Hence the order used in result().
497+ //
498+ // - Since the graph cannot have any cycles, temporary marks
499+ // are not necessary, and a simple set is used to record
500+ // which edges have already been visited.
501+ //
502+ void Visit (Edge* edge) {
503+ auto insertion = visited_set_.emplace (edge);
504+ if (!insertion.second )
505+ return ;
506+
507+ for (const Node* input : edge->inputs_ ) {
508+ Edge* producer = input->in_edge ();
509+ if (producer)
510+ Visit (producer);
484511 }
512+ sorted_edges_.push_back (edge);
485513 }
514+
515+ std::unordered_set<Edge*> visited_set_;
516+ std::vector<Edge*> sorted_edges_;
517+ };
518+
519+ TopoSort topo_sort;
520+ for (const Node* target : targets_) {
521+ topo_sort.VisitTarget (target);
486522 }
487523
488- while (!work_queue.empty ()) {
489- Edge* e = work_queue.front ();
490- work_queue.pop ();
491- // If the critical path of any dependent edges is updated, this
492- // edge may need to be processed again. So re-allow insertion.
493- active_edges.erase (e);
524+ const auto & sorted_edges = topo_sort.result ();
525+
526+ // First, reset all weights to 1.
527+ for (Edge* edge : sorted_edges)
528+ edge->set_critical_path_weight (EdgeWeightHeuristic (edge));
494529
495- for (const Node* input : e->inputs_ ) {
496- Edge* in = input->in_edge ();
497- if (!in) {
530+ // Second propagate / increment weidghts from
531+ // children to parents. Scan the list
532+ // in reverse order to do so.
533+ for (auto reverse_it = sorted_edges.rbegin ();
534+ reverse_it != sorted_edges.rend (); ++reverse_it) {
535+ Edge* edge = *reverse_it;
536+ int64_t edge_weight = edge->critical_path_weight ();
537+
538+ for (const Node* input : edge->inputs_ ) {
539+ Edge* producer = input->in_edge ();
540+ if (!producer)
498541 continue ;
499- }
500- // Only process edge if this node offers a higher weighted path
501- const int64_t edge_weight = EdgeWeightHeuristic (in);
502- const int64_t proposed_weight = e->critical_path_weight () + edge_weight;
503- if (proposed_weight > in->critical_path_weight ()) {
504- in->set_critical_path_weight (proposed_weight);
505- if (active_edges.insert (in).second ) {
506- work_queue.push (in);
507- }
508- }
542+
543+ int64_t producer_weight = producer->critical_path_weight ();
544+ int64_t candidate_weight = edge_weight + EdgeWeightHeuristic (producer);
545+ if (candidate_weight > producer_weight)
546+ producer->set_critical_path_weight (candidate_weight);
509547 }
510548 }
511549}
0 commit comments