torch/csrc/jit/interpreter.cpp
// for each input, should we move rather than copy the inputs
for each input, should we move rather than copy the inputs
for each input should we move rather than copy the inputs
std::unordered_map<Node*, std::vector<uint8_t>> findLastUses(Graph & g) {
  struct FindLastUses {
    Graph & graph;
    std::unordered_set<Value*> seen;
    std::unordered_map<Node*, std::vector<uint8_t>> move_flags;
    std::unordered_map<Node*, Node*> drop_for_node;
    FindLastUses(Graph & g)
    : graph(g) {
      scanBlock(graph.block());
    }
    void scanBlock(Block * b) {
      scanNode(b->return_node());
      for(auto n : b->nodes().reverse()) {
        scanNode(n);
      }
    }
    void scanNode(Node * n) {
      for(auto b : n->blocks()) {
        scanBlock(b);
      }
      move_flags[n].resize(n->inputs().size());
      for(size_t i = n->inputs().size(); i > 0; --i) {
        scanUse(n, i-1);
      }
    }
    void scanUse(Node * n, size_t i) {
      auto & move_flags_n = move_flags[n];
      auto v = n->inputs()[i];
      auto inserted = seen.insert(v).second;
      if(!inserted) {
        move_flags_n[i] = false;
        return;
      }
      Node * same_depth_node = findOwnerInBlock(n, v->node()->owningBlock());
      JIT_ASSERT(same_depth_node); 
      if(same_depth_node == n) {
        move_flags_n[i] = true;
        return;
      }
      move_flags_n[i] = false;
      addToDropIfNotExists(findOrCreateDropInstructionForNode(same_depth_node), v);
    }
    Node * findOwnerInBlock(Node * n, Block * block) {
      while(n != nullptr && block != n->owningBlock()) {
        n = n->owningBlock()->owningNode();
      }
      return n;
    }
    Node * findOrCreateDropInstructionForNode(Node * n) {
      auto it = drop_for_node.find(n);
      if(it == drop_for_node.end()) {
        auto drop_node = graph.create(prim::Drop, 0);
        drop_node->insertAfter(n);
        it = drop_for_node.emplace(n, drop_node).first;
      }
      return it->second;
    }
    void addToDropIfNotExists(Node * drop, Value * v) {
      for(auto i : drop->inputs()) {
        if(i == v)
          return;
      }
      drop->addInput(v);
      move_flags[drop].push_back(true);
    }
  };
  return FindLastUses(g).move_flags;
}
std :: unordered_map < Node * , std :: vector < uint8_t > > findLastUses ( Graph & g ) { struct FindLastUses { Graph & graph ; std :: unordered_set < Value * > seen ; std :: unordered_map < Node * , std :: vector < uint8_t > > move_flags ; std :: unordered_map < Node * , Node * > drop_for_node ; FindLastUses ( Graph & g ) : graph ( g ) { scanBlock ( graph . block ( ) ) ; } void scanBlock ( Block * b ) { scanNode ( b -> return_node ( ) ) ; for ( auto n : b -> nodes ( ) . reverse ( ) ) { scanNode ( n ) ; } } void scanNode ( Node * n ) { for ( auto b : n -> blocks ( ) ) { scanBlock ( b ) ; } move_flags [ n ] . resize ( n -> inputs ( ) . size ( ) ) ; for ( size_t i = n -> inputs ( ) . size ( ) ; i > 0 ; -- i ) { scanUse ( n , i - 1 ) ; } } void scanUse ( Node * n , size_t i ) { auto & move_flags_n = move_flags [ n ] ; auto v = n -> inputs ( ) [ i ] ; auto inserted = seen . insert ( v ) . second ; if ( ! inserted ) { move_flags_n [ i ] = false ; return ; } Node * same_depth_node = findOwnerInBlock ( n , v -> node ( ) -> owningBlock ( ) ) ; JIT_ASSERT ( same_depth_node ) ; if ( same_depth_node == n ) { move_flags_n [ i ] = true ; return ; } move_flags_n [ i ] = false ; addToDropIfNotExists ( findOrCreateDropInstructionForNode ( same_depth_node ) , v ) ; } Node * findOwnerInBlock ( Node * n , Block * block ) { while ( n != nullptr && block != n -> owningBlock ( ) ) { n = n -> owningBlock ( ) -> owningNode ( ) ; } return n ; } Node * findOrCreateDropInstructionForNode ( Node * n ) { auto it = drop_for_node . find ( n ) ; if ( it == drop_for_node . end ( ) ) { auto drop_node = graph . create ( prim :: Drop , 0 ) ; drop_node -> insertAfter ( n ) ; it = drop_for_node . emplace ( n , drop_node ) . first ; } return it -> second ; } void addToDropIfNotExists ( Node * drop , Value * v ) { for ( auto i : drop -> inputs ( ) ) { if ( i == v ) return ; } drop -> addInput ( v ) ; move_flags [ drop ] . push_back ( true ) ; } } ; return FindLastUses ( g ) . move_flags ; }
for each input, should we move rather than copy the inputs
for each input should we move rather than copy the inputs
// struct to share common data structures // have we seen this value, yet, if not, it is the last use of the value // A map from an If or Loop node to the optional Drop block that // occurs directly after it to release any tensors that go out of scope // when the If/Loop exits. These are created and inserted on demand. // scan backwards so if a value is used twice in the list then it is a move // the last use of v may be in a nested block of an If or Loop statement // find the node 'same_depth_node' at the same depth as the definition of v, // and consider that node to be the last use of v. // This ensures we do not delete nodes in nested scopes // that may be executed multiple times // and that nodes used on one side of an if // but not the other get deleted regardless of the branch // e.g. // a = 4 // while <...>: //   y = a + a // drop(a) // In other words, we find the first program point for v that // _reverse_ dominates the definition of v, and add a drop point there. // failure means v is not in scope for n, use lint! // In the case where v and n are in the same block, just mark // its move_flags to be true // in the case where the use is nested in a block // add a Drop node after that block which will drop 'v'. // finds the node in block 'block' that contains in 'n' // or nullptr if no such node exists, e.g.: // n0: a = 4 // n1: if <cond>: // n2:    b = a + a // findOwnerInBlock(n2, n0.block()) == n1 // we already accounted for this use