Skip to content

fix: add zero-guards for divisor/scale edge cases (#7838) - #8133

Open
liuyun7345 wants to merge 2 commits into
deepspeedai:masterfrom
liuyun7345:fix/zero-guards-7838
Open

fix: add zero-guards for divisor/scale edge cases (#7838)#8133
liuyun7345 wants to merge 2 commits into
deepspeedai:masterfrom
liuyun7345:fix/zero-guards-7838

Conversation

@liuyun7345

Copy link
Copy Markdown

Summary

Fixes #7838 by adding explicit zero/non-finite guards at the four reported sites so DeepSpeed fails early with a clear error instead of raising raw ZeroDivisionError or silently propagating inf/nan.

This picks up the inactive draft #7855 (no updates since 2026-02-17).

Changes

  1. deepspeed/utils/groups.py_ensure_divisibility asserts denominator != 0 before modulo.
  2. deepspeed/utils/timer.pyThroughputTimer rejects non-positive steps_per_output in __init__, and _is_report_boundary re-checks if the attribute is later mutated.
  3. deepspeed/inference/v2/inference_utils.pyceil_div raises ValueError when b == 0.
  4. op_builder/hpu/fp_quantizer.pydequantize rejects zero / non-finite scales before computing 1.0 / scale.
  5. tests/unit/utils/test_zero_guards.py — regression tests for each failure mode (HPU builder tests skip if that module is unavailable).

Test plan

  • _ensure_divisibility(8, 0) raises clear AssertionError
  • valid _ensure_divisibility inputs still pass
  • ceil_div(10, 0) raises ValueError
  • ceil_div matches math.ceil for valid inputs
  • ThroughputTimer(..., steps_per_output=0/-1) raises ValueError
  • mutated steps_per_output=0 is caught in _is_report_boundary
  • HPU dequantize rejects zero / NaN / zero-scalar scales before HPU op

Fixes #7838

Prevent ZeroDivisionError and silent non-finite propagation at four
reported sites:

1. groups._ensure_divisibility: reject denominator == 0 before modulo
2. ThroughputTimer: reject non-positive steps_per_output at init and
   report-boundary time
3. inference_utils.ceil_div: reject divisor == 0
4. HPU FPQuantizer.dequantize: reject zero/non-finite scales before
   1/scale

Adds focused unit tests covering each failure mode.

Picks up the inactive draft deepspeedai#7855.

Fixes deepspeedai#7838

Signed-off-by: liuyun7345 <liuyun7345@sina.com>
Comment thread tests/unit/utils/test_zero_guards.py Outdated
pytest.skip("HPU FPQuantizer builder is not available")


def test_hpu_fp_quantizer_dequantize_rejects_zero_scale():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests seems to be hpu-specific. Can they not be generalized?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — generalizing is possible, and I'll do it. The 1.0 / scale inversion currently only exists in the HPU FPQuantizer.dequantize, but the same failure mode applies to all backends: quantize computes scale = q_range / max_vals, so inf/nan in the input drives max_vals to inf and silently produces scale = 0.

I'll move the zero/non-finite scale validation up into the shared front-end FP_Quantize.dequantize (deepspeed/ops/fp_quantizer/quantize.py) so it covers CUDA and HPU alike, and relocate the test into tests/unit/ops/fp_quantizer/test_fp_quant.py using the existing accelerator-agnostic pattern (deepspeed.ops.op_builder.FPQuantizerBuilder + module-level skip via __compatible_ops__). The direct op_builder.hpu import goes away.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — pushed as 1098c0a. The zero/non-finite scale validation now lives in the shared front-end FP_Quantize.dequantize / selective_dequantize (deepspeed/ops/fp_quantizer/quantize.py), and the regression tests moved to tests/unit/ops/fp_quantizer/test_fp_quant.py using the accelerator-agnostic pattern (deepspeed.ops.op_builder.FPQuantizerBuilder + module-level skip via __compatible_ops__), exercised through the public FP_Quantize.dequantize API. The HPU-only guard in op_builder/hpu/fp_quantizer.py and the direct op_builder.hpu import in the tests are both gone.

"""
Return ceil(a / b).
"""
if b == 0:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please share the usage case that found this issue?

@liuyun7345 liuyun7345 Jul 31, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honest answer: there is no production usage case on my side. All four guards in this PR come from issue #7838, which was reported from code inspection with synthetic repros (ceil_div(10, 0) raising a raw ZeroDivisionError), not from a real workload failure.

The case for keeping this guard is narrow: ceil_div is a small shared utility, and the guard turns a cryptic ZeroDivisionError into a clear ValueError at the API boundary, with zero behavior change for valid inputs. That said, if you'd rather not add guards without a demonstrated trigger, I'm happy to drop this hunk.

…ai#8133)

Move the zero/non-finite scale validation out of the HPU-only
`op_builder/hpu/fp_quantizer.py` and into the shared front-end
`FP_Quantize.dequantize` / `selective_dequantize` in
`deepspeed/ops/fp_quantizer/quantize.py`. The same `1.0 / scale`
failure mode applies to every backend (CUDA included): `quantize`
computes `scale = q_range / max_vals`, so inf/nan in the input
silently yields `scale = 0` and corrupts dequantized output. One
guard at the API boundary covers all backends.

Relocate the regression tests from `tests/unit/utils/test_zero_guards.py`
into `tests/unit/ops/fp_quantizer/test_fp_quant.py` using the existing
accelerator-agnostic pattern (`deepspeed.ops.op_builder.FPQuantizerBuilder`
+ module-level skip via `__compatible_ops__`), exercising the guard
through the public `FP_Quantize.dequantize` API. Drop the direct
`op_builder.hpu` import and the now-unused `torch` import from
`test_zero_guards.py`.

Signed-off-by: liuyun7345 <liuyun7345@sina.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Multiple missing zero-guards cause ZeroDivisionError / non-finite values across DeepSpeed (4 locations)

2 participants