class_docstr: A wrapper over pytorch GradScaler made specifically for training hivemind.Optimizer with reuse_grad_buffers=True.
  
  :note: if not using reuse_grad_buffers=True, one can and *should* train normally without this class, e.g. using
    standard PyTorch AMP or Apex. This custom GradScaler is more memory-efficient, but requires custom training code.
  
  hivemind.GradScaler makes 3 modifications to the regular PyTorch AMP:
  
  - bypass .unscale_ and .update calls in order to accumulate gradients over several steps
  - limit increasing gradient scale to only immediately after global optimizer steps
  - allow training with some or master parameters in float16
  
  :note: The above modiffications will be enabled automatically. One can (and should) use hivemind.GradScaler exactly
    as regular ``torch.amp.GradScaler``.
code: def step(self, optimizer: TorchOptimizer, *args, **kwargs) -> bool:
      if self._is_running_global_step and not isinstance(
          optimizer, hivemind.Optimizer
      ):
          # ^-- invoked privately within hivemind optimizer
          inner_optimizer = optimizer
          with self._lock:
              if self._is_ready_to_update:
                  logger.warning(
                      "Please call grad_scaler.update() after each step"
                  )
  
              inner_optimizer_state = self._inner_optimizer_states.pop(
                  id(inner_optimizer), None
              )
              if inner_optimizer_state is not None:
                  self._per_optimizer_states[
                      id(inner_optimizer)
                  ] = inner_optimizer_state
              assert (
                  self._per_optimizer_states[id(inner_optimizer)][
                      "stage"
                  ]
                  == OptState.UNSCALED
              ), "InternalError: Optimizer should have called .unscale internally before invoking grad_scaler.step"
              if self.are_grads_finite(
                  inner_optimizer, use_cached=True
              ):
                  super().step(inner_optimizer, *args, **kwargs)
              else:
                  logger.warning(
                      "Skipping global step due to gradient over/underflow"
                  )
              self._is_ready_to_update = True
              return True
      else:
          super().step(optimizer)
          self._optimizer_states_to_reset.add(id(optimizer))
          return False
  
imports: import contextlib
  import threading
  from copy import deepcopy
  from typing import Dict, Optional
  import torch
  from torch.cuda.amp import GradScaler
  from torch.cuda.amp.grad_scaler import OptState, _refresh_per_optimizer_state
  from torch.optim import Optimizer
  import hivemind
  from hivemind.utils.logging import get_logger
label_desc: Use image node_id 4 for calling the GradScaler obj's underlying member method code with example usage: obj.step(optimizer) and returns: False, True