docstr: A context manager for timing a code block, with optional cuda synchronization and verbose timing.
  
  Args:
      action_name: the name under which to store the timing of what is enclosed in the context manager.
docstr_tok: ["A","context","manager","for","timing","a","code","block",",","with","optional","cuda","synchronization","and","verbose","timing",".","Args",":","action_name",":","the","name","under","which","to","store","the","timing","of","what","is","enclosed","in","the","context","manager","."]
code: def time(
      self,
      action_name: str,
  ) -> Generator[None, None, None]:
      """
      A context manager for timing a code block, with optional cuda synchronization and verbose timing.
  
      Args:
          action_name: the name under which to store the timing of what is enclosed in the context manager.
      """
      start_time: float = perf_counter()
      try:
          if self.cuda_sync:
              torch.cuda.synchronize()
          yield
      finally:
          if self.cuda_sync:
              torch.cuda.synchronize()
          interval_time: float = perf_counter() - start_time
          if self.verbose:
              logger.info(
                  f"{action_name} took {interval_time} seconds."
              )
      self.recorded_durations[action_name].append(interval_time)
  
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 2 for calling the Timer obj's underlying member method code with example usage: obj.time(action_name) without return types