Skip to content

Consolidating blockwise FP32 scale flag - #687

Open
asdfvg123 wants to merge 1 commit into
devfrom
yeonsoo/blockwise-tol
Open

Consolidating blockwise FP32 scale flag#687
asdfvg123 wants to merge 1 commit into
devfrom
yeonsoo/blockwise-tol

Conversation

@asdfvg123

@asdfvg123 asdfvg123 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

Please include a brief summary of the changes, relevant motivation and context.

The upstream already had a flag NVTE_FP8_BLOCK_SCALING_FP32_SCALES (default 0) which is used to limit and store the blockwise quantization scales in the power-of-2 in fp32 precision. This PR is removing the unnecessary additional flag and using the original flag and on gfx950, if this flag is set to 1, then the host blockwise gemm dispatcher selects the kernel with fp32 scale computation.

Fixes # (issue)

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • Change A
  • Change B

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

if IS_HIP_EXTENSION:
atol = 1e-5
rtol = 1.3e-6
fp32_scale_kernel = os.getenv("NVTE_FP8_BLOCK_SCALING_FP32_SCALES", "0") == "1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new kernel path is selected here, but the quantizers below still hardcode force_pow_2_scales=True (lines 141 and 149), so the FP32-scale kernel is only ever validated against scale values that happen to be powers of 2.

In production the flag is coupled: Float8BlockScaling sets power_2_scale=not use_f32_scales (transformer_engine/common/recipe/__init__.py:447), so NVTE_FP8_BLOCK_SCALING_FP32_SCALES=1 means the GEMM receives genuinely non-power-of-2 FP32 scales. That combination — which is exactly what this PR enables — is never exercised by this test.

Consider threading the flag through, e.g. force_pow_2_scales=not fp32_scale_kernel, so the loosened tolerances are actually measuring the configuration they were derived for.

rtol = 1.3e-6
fp32_scale_kernel = os.getenv("NVTE_FP8_BLOCK_SCALING_FP32_SCALES", "0") == "1"
large_magnitude = x_magnitude >= 1000
if not fp32_scale_kernel:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The tolerance branch keys off the env var alone, but the running-scale kernel only exists on gfx950. The CDNA3 backend (transformer_engine/common/gemm/kittens/cdna3/blockwise_fp8_gemm.cpp) has no getenv and no power-of-2 path at all — it always consumes FP32 scales directly, so this flag has zero effect on gfx942.

Net effect: running this suite on gfx942 with NVTE_FP8_BLOCK_SCALING_FP32_SCALES=1 relaxes rtol from 1.3e-6 to 2e-2 (~15000x) for a kernel whose behavior did not change, silently masking any regression there.

Suggest gating on arch as well, e.g. only take the loose branch when the device is gfx950.

Comment on lines +84 to +89
elif out_dtype == torch.float32:
rtol = 1e-3
atol = 1e-1 if large_magnitude else 2e-4
else:
rtol = 2e-2
atol = 3e1 if large_magnitude else 3e-2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Two notes on the loosened branches:

  1. These are six unexplained magic numbers in a file named ..._exact.py, and rtol = 2e-2 is roughly 2.5x a bf16 ulp — a large relaxation for a test whose purpose is bit-tight agreement with the reference. The PR description explains the cause (running-scale accumulation reorders the sum); a one-line comment here would keep that rationale attached to the numbers instead of only in the PR.

  2. large_magnitude is derived from x_magnitude only. Every call site in this file passes w_magnitude=1, so it is correct today, but the absolute error scales with the product of both operands — a future w_magnitude parametrization would silently get the tight atol.

Comment on lines +13 to +15
if IS_HIP_EXTENSION:
import os

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: os is stdlib and free to import; gating it behind IS_HIP_EXTENSION adds a conditional-import pattern that lint tools tend to flag and that readers have to double-check. A plain import os with the other top-level imports would be simpler.

Suggested change
if IS_HIP_EXTENSION:
import os
import os

@github-actions

Copy link
Copy Markdown

Review summary

Reviewed the full diff against the merge base (README.rst, test_float8_blockwise_gemm_exact.py, cdna4/blockwise_fp8_gemm.cpp).

Verdict: looks good overall, with test-side concerns worth addressing before merge.

The flag consolidation itself is correct. The inverted strcmp in the CDNA4 dispatcher preserves the previous default (power-of-2 scales when unset), the new semantics match the Python side's os.getenv(...) == "1" check in Float8BlockScaling, and there are no dangling references to NVTE_BLOCKWISE_FP8_POWER_OF_2_SCALE left anywhere in the tree. Coupling the GEMM kernel choice to the recipe's scale format also fixes the prior mismatch where FP32_SCALES=1 produced non-power-of-2 scales that the gfx950 kernel then rounded to E8M0.

The four inline comments are all on the test file:

  • The FP32-scale kernel is never exercised with actual non-power-of-2 scales, since the quantizers hardcode force_pow_2_scales=True — the loosened tolerances aren't measuring the configuration they were derived for.
  • Tolerance selection isn't arch-gated, so the ~15000x relaxation also applies on gfx942, where this flag has no effect on kernel behavior.
  • The new tolerance constants have no inline rationale, and large_magnitude keys off x_magnitude only.
  • Nit: the conditional import os.

Copyright headers: OK — all three modified files carry AMD lines ending in 2026, NVIDIA lines untouched.

if IS_HIP_EXTENSION:
atol = 1e-5
rtol = 1.3e-6
running_scale_kernel = (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Only the gfx950 + NVTE_FP8_BLOCK_SCALING_FP32_SCALES = 1 uses the continuous fp32 scale with running scale kernel.
By default NVTE_FP8_BLOCK_SCALING_FP32_SCALES = 0 and uses power-of-2 kernel.
gfx942 only has 1 kernel (continuous fp32 scale kernel) but this is not utilizing running scale method, and thus more precise.

@asdfvg123
asdfvg123 marked this pull request as ready for review July 31, 2026 17:22
@asdfvg123
asdfvg123 force-pushed the yeonsoo/blockwise-tol branch from c7b70d1 to 9881263 Compare July 31, 2026 19:13
@asdfvg123 asdfvg123 changed the title Consolidating blockwise FP32 scale flag and increasing the tolerance Consolidating blockwise FP32 scale flag Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-level 3 CI test level 3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants