tensorflow/compiler/jit/deadness_analysis.cc
// Compute a special topological order for the Graph, where nodes having the
// same root frame are placed adjacent to each other.  The traversal uses a
// variant of Kahn's algorithm.  num_ready_inputs is used to keep track of how
// many inputs of each node are ready; a node is ready to be scheduled if all
// of its inputs are ready.
// Ref. to https://en.wikipedia.org/wiki/Topological_sorting for details.
Compute a special topological order for the Graph, where nodes having the
same root frame are placed adjacent to each other.  The traversal uses a
variant of Kahn's algorithm.  num_ready_inputs is used to keep track of how
many inputs of each node are ready; a node is ready to be scheduled if all
of its inputs are ready.
Ref.
Compute a special topological order for the Graph where nodes having the same root frame are placed adjacent to each other . The traversal uses a variant of Kahn ' s algorithm . num_ready_inputs is used to keep track of how many inputs of each node are ready ; a node is ready to be scheduled if all of its inputs are ready . Ref .
Status DeadnessAnalysisImpl::GetFrameBasedTopologicalOrder(
    std::vector<Node*>* order) {
  absl::flat_hash_map<absl::string_view, size_t> num_enters_for_frame;
  absl::flat_hash_map<absl::string_view, size_t> num_exits_for_frame;
  std::vector<size_t> num_ready_inputs(graph_.num_node_ids(), 0);
  Node* src_node = graph_.source_node();
  for (const auto* node : graph_.op_nodes()) {
    const ControlFlowInfo& cf = control_flow_info_[node->id()];
    if (IsRootEnter(node)) {
      ++num_enters_for_frame[cf.frame_name];
    } else if (IsRootExit(node)) {
      ++num_exits_for_frame[cf.frame_name];
    }
    if (IsMerge(node)) {
      for (const Edge* e : node->in_edges()) {
        if (IsNextIteration(e->src())) {
          ++num_ready_inputs[node->id()];
        }
      }
    }
  }
  std::deque<Node*> ready;
  ready.push_back(src_node);
  absl::flat_hash_map<absl::string_view, std::vector<Node*>>
      ready_enters_per_frame;
  std::vector<Node*> ready_exits;
  while (!ready.empty()) {
    Node* curr_node = ready.front();
    ready.pop_front();
    VLOG(4) << "Visiting " << curr_node->name();
    order->push_back(curr_node);
    for (const Edge* out_edge : curr_node->out_edges()) {
      Node* out = out_edge->dst();
      int out_id = out->id();
      if (IsNextIteration(curr_node) && IsMerge(out)) {
        continue;
      }
      ++num_ready_inputs[out->id()];
      if (!out->IsOp()) continue;  
      if (num_ready_inputs[out->id()] != out->in_edges().size()) continue;
      absl::string_view frame_name = control_flow_info_[out_id].frame_name;
      if (IsRootEnter(out)) {
        ready_enters_per_frame[frame_name].push_back(out);
      } else if (IsRootExit(out)) {
        ready_exits.push_back(out);
      } else {
        ready.push_back(out);
      }
    }
    if (ready.empty()) {
      if (!ready_exits.empty()) {
        absl::string_view frame_name =
            control_flow_info_[ready_exits.front()->id()].frame_name;
        CHECK_EQ(ready_exits.size(), num_exits_for_frame[frame_name]);
        ready.insert(ready.end(), ready_exits.begin(), ready_exits.end());
        ready_exits.clear();
      } else {
        for (auto iter = ready_enters_per_frame.begin();
             iter != ready_enters_per_frame.end(); ++iter) {
          absl::string_view frame_name = iter->first;
          const std::vector<Node*>& ready_enters = iter->second;
          if (ready_enters.size() == num_enters_for_frame[frame_name]) {
            ready.insert(ready.end(), ready_enters.begin(), ready_enters.end());
            ready_enters_per_frame.erase(iter);
            break;
          }
        }
      }
    }
  }
  if (!ready_enters_per_frame.empty() || !ready_exits.empty()) {
    return errors::InvalidArgument(
        "Some enters/exits have never been visited in the traversal."
        " Most probably the input graph is malformed.");
  }
  return Status::OK();
}
Status DeadnessAnalysisImpl :: GetFrameBasedTopologicalOrder ( std :: vector < Node * > * order ) { absl :: flat_hash_map < absl :: string_view , size_t > num_enters_for_frame ; absl :: flat_hash_map < absl :: string_view , size_t > num_exits_for_frame ; std :: vector < size_t > num_ready_inputs ( graph_ . num_node_ids ( ) , 0 ) ; Node * src_node = graph_ . source_node ( ) ; for ( const auto * node : graph_ . op_nodes ( ) ) { const ControlFlowInfo & cf = control_flow_info_ [ node -> id ( ) ] ; if ( IsRootEnter ( node ) ) { ++ num_enters_for_frame [ cf . frame_name ] ; } else if ( IsRootExit ( node ) ) { ++ num_exits_for_frame [ cf . frame_name ] ; } if ( IsMerge ( node ) ) { for ( const Edge * e : node -> in_edges ( ) ) { if ( IsNextIteration ( e -> src ( ) ) ) { ++ num_ready_inputs [ node -> id ( ) ] ; } } } } std :: deque < Node * > ready ; ready . push_back ( src_node ) ; absl :: flat_hash_map < absl :: string_view , std :: vector < Node * > > ready_enters_per_frame ; std :: vector < Node * > ready_exits ; while ( ! ready . empty ( ) ) { Node * curr_node = ready . front ( ) ; ready . pop_front ( ) ; VLOG ( 4 ) << " " << curr_node -> name ( ) ; order -> push_back ( curr_node ) ; for ( const Edge * out_edge : curr_node -> out_edges ( ) ) { Node * out = out_edge -> dst ( ) ; int out_id = out -> id ( ) ; if ( IsNextIteration ( curr_node ) && IsMerge ( out ) ) { continue ; } ++ num_ready_inputs [ out -> id ( ) ] ; if ( ! out -> IsOp ( ) ) continue ; if ( num_ready_inputs [ out -> id ( ) ] != out -> in_edges ( ) . size ( ) ) continue ; absl :: string_view frame_name = control_flow_info_ [ out_id ] . frame_name ; if ( IsRootEnter ( out ) ) { ready_enters_per_frame [ frame_name ] . push_back ( out ) ; } else if ( IsRootExit ( out ) ) { ready_exits . push_back ( out ) ; } else { ready . push_back ( out ) ; } } if ( ready . empty ( ) ) { if ( ! ready_exits . empty ( ) ) { absl :: string_view frame_name = control_flow_info_ [ ready_exits . front ( ) -> id ( ) ] . frame_name ; CHECK_EQ ( ready_exits . size ( ) , num_exits_for_frame [ frame_name ] ) ; ready . insert ( ready . end ( ) , ready_exits . begin ( ) , ready_exits . end ( ) ) ; ready_exits . clear ( ) ; } else { for ( auto iter = ready_enters_per_frame . begin ( ) ; iter != ready_enters_per_frame . end ( ) ; ++ iter ) { absl :: string_view frame_name = iter -> first ; const std :: vector < Node * > & ready_enters = iter -> second ; if ( ready_enters . size ( ) == num_enters_for_frame [ frame_name ] ) { ready . insert ( ready . end ( ) , ready_enters . begin ( ) , ready_enters . end ( ) ) ; ready_enters_per_frame . erase ( iter ) ; break ; } } } } } if ( ! ready_enters_per_frame . empty ( ) || ! ready_exits . empty ( ) ) { return errors :: InvalidArgument ( " " " " ) ; } return Status :: OK ( ) ; }
Compute a special topological order for the Graph, where nodes having the
same root frame are placed adjacent to each other.
Compute a special topological order for the Graph where nodes having the same root frame are placed adjacent to each other .
// Since we care only the root-level frame, full frame names are the same // as frame names. // Edge NextIteration->Merge is counted before starting the traversal to // break the backedges. // dequeue is used to ensure that the nodes are first-in-first-out.  This // order guarantees that the exits in the ready queue are visited before // nodes that will become ready in the future. // ready_enters_per_frame and ready_exits serve as a staging area to buffer // the ready enters/exits before they are moved to the `ready` queue for // controlling the start and end of a processing frame. // Exit nodes shall all be from the same frame, as we process a frame at a // time. So, one vector is enough. // Edge NextIteration->Merge has been counted. // Skip Sink/Source nodes. // Try moving nodes from ready_enters_per_frame and ready_exits to // `ready`. // If there are nodes in ready_exits we must process them before // processing ready_enters_per_frame to make sure all nodes in the // currently processing frame are visited before starting processing // other frames. // Otherwise, try moving nodes from ready_enters to `ready`.