def opt_step(
    fp_optimizer, tracker, full_batch_size, update_freq, math_mode, world_size
):
    """Performs one optimizer step.

    Args:
        fp_optimizer (:obj:`FP16Optimizer` | :obj:`FP32Optimizer`): The FP Optimizer
        tracker (:obj:`mlbench_core.utils.Tracker`, optional) The current tracker
        full_batch_size (int): The total batch size (over all batches since last update)
        update_freq (int): The update frequency between batches
        math_mode (str): The used math mode
        world_size (int): Distributed world size

    Returns:
        (bool): Whether the weights were updated or not (i.e. if no overflow detected)
    """
    if math_mode == "fp32":
        updated = fp_optimizer.step(tracker=tracker, denom=full_batch_size)
    elif math_mode == "fp16":
        # This results in reducing tensor and dividing by `loss_scale * full_batch_size`
        # but we divide by world size before reduction to avoid overflow, and
        # re-multiply after reduction to rescale
        multiplier = full_batch_size / (world_size * update_freq)
        denom = world_size * update_freq
        updated = fp_optimizer.step(tracker=tracker, denom=denom, multiplier=multiplier)
    else:
        raise NotImplementedError("Unknown math mode {}".format(math_mode))
    return updated