class_docstr: A Timer class which implements TimerProtocol and stores timings in a dictionary `recorded_durations`.
  
  Same behavior as timer, but with the addition of size_bounds = (lower, upper)
  
  Args:
      ...
      size_bounds: defines the range of samples that should be kept in the timer. The lower bound should be smaller than
          the upper bound. When the number of samples reaches the upper bound, the oldest (upper-lower) bound samples will
          be removed. This range is applied per action.
code: def __init__(
      self, lower_bound: int, upper_bound: int, **kwargs: Any
  ) -> None:
      super().__init__(**kwargs)
      assert lower_bound > 0
      assert lower_bound < upper_bound
      self.lower_bound = lower_bound
      self.upper_bound = upper_bound
  
imports: import datetime
  import logging
  import os
  from collections import defaultdict
  from contextlib import contextmanager
  from time import perf_counter
  from typing import Any, Dict, Generator, List, Optional, Protocol, runtime_checkable, Sequence, Tuple, TypeVar
  import numpy
  import torch
  import torch.distributed
  from tabulate import tabulate
  from torchtnt.utils.distributed import PGWrapper
label_desc: Use image node_id 1 to create a new BoundedTimer object from inherited base classes: Timer with example: obj = BoundedTimer(lower_bound, upper_bound)