class_docstr: Tracks saved skip tensors through portals. The skip tensors will be
  hidden in portals so that the autograd engine does not need to track them.
  
  This tracker is only used when the training or evaluating module is wrapped
  with :class:`torchpipe.Pipe`.
class_docstr_tok: ["Tracks","saved","skip","tensors","through","portals",".","The","skip","tensors","will","be","hidden","in","portals","so","that","the","autograd","engine","does","not","need","to","track","them",".","This","tracker","is","only","used","when","the","training","or","evaluating","module","is","wrapped","with",":","class",":","`","torchpipe.Pipe","`","."]
docstr: Saves the stashed skip tensor in a portal. The portal is then
  connected to the given micro-batch with :class:`Join`.
docstr_tok: ["Saves","the","stashed","skip","tensor","in","a","portal",".","The","portal","is","then","connected","to","the","given","micro-batch","with",":","class",":","`","Join","`","."]
code: def save(
      self,
      batch: Batch,
      ns: Namespace,
      name: str,
      tensor: Optional[Tensor],
  ) -> None:
      """Saves the stashed skip tensor in a portal. The portal is then
      connected to the given micro-batch with :class:`Join`.
      """
      if not self.skip_layout.requires_copy(ns, name):
          super().save(batch, ns, name, tensor)
          return
  
      # See [Tensor Life of Portal] at Portal.put_tensor() to understand the
      # below tensor_life values. Here are the selected events which retrieve
      # the tensor in portal:
      #
      #  1. [x] blue()
      #     ...
      #  6. [x]   PortalOrange.forward
      #     ...
      #  8. [x]   PortalOrange.forward (recomputed)
      #     ...
      # 11. [x] blue() (recomputed)
      #
      if (ns, name) not in self.portals:
          if is_checkpointing():
              # Under checkpointing, the tensor used by the first
              # PortalOrange should be alive in the portal. This tensor will
              # be used again by the second PortalOrange during the
              # recomputation.
              tensor_life = (
                  3  # Delete at [8. PortalOrange.forward (recomputed)]
              )
          else:
              tensor_life = 2  # Delete at [6. PortalOrange.forward]
  
          portal = Portal(tensor, tensor_life)
          self.portals[(ns, name)] = portal
  
      else:
          # Under recomputation, the portal already exists.
          portal = self.portals[(ns, name)]
  
          # The existing tensor life already became 0. It should be reset as
          # 1 to delete the tensor after the second PortalBlue immediately.
          tensor_life = 1  # Delete at [11. blue() (recomputed)]
  
          portal.put_tensor(tensor, tensor_life)
  
      phony = portal.blue()
      tensor_idx = batch.find_tensor_idx()
      batch[tensor_idx] = join(batch[tensor_idx], phony)
  
imports: from contextlib import contextmanager
  import threading
  from typing import Dict, Generator, List, Optional, Tuple
  from torch import Tensor
  from ..checkpoint import is_checkpointing
  from ..dependency import fork, join
  from ..microbatch import Batch
  from ..stream import AbstractStream
  from .layout import SkipLayout
  from .namespace import Namespace
  from .portal import Portal
label_desc: Use image node_id 2 for calling the SkipTrackerThroughPotals obj's underlying member method code with example usage: obj.save(batch, ns, name, tensor) without return types