@@ -641,9 +641,23 @@ def test_combined_q_and_kv(num_tokens: int, n_heads: int, block_size: int):
641641 k_cache_compare (k_cache , k_cache_ref , block_size , rtol = 1e-2 , atol = 1e-2 )
642642
643643
644- # ── Test 5: a backend override must not change results ───────────────────────
645- # A vendor override is a performance change. It runs only where one is
644+ # ── Test 5: a backend override must still satisfy the contract ───────────────
645+ # A vendor override is a performance change, so it is worth checking on shapes
646+ # the tests above do not cover -- in particular ones that are not multiples of
647+ # whatever tiling the override uses. It runs only where an override is
646648# registered, so nothing below executes on the generic path.
649+ #
650+ # It compares against the torch reference, not against the generic kernel. An
651+ # earlier version asserted the two Triton implementations agreed byte for byte,
652+ # which is stricter than anything the operator promises and is not even stable:
653+ # on Hygon BW1000 they are bit-identical under torch 2.10.0 / FlagTree
654+ # 0.6.1a1+hcu3.6, while under the combination backends.yaml pins for that card
655+ # (torch 2.9.0 / FlagTree 0.5.1+hcu3.1) q differs on 5 of 16744448 elements by up
656+ # to 2 bf16 ULP at 511 tokens and one FP8 cache byte differs at 1000 tokens. A
657+ # 2-D tile assigns lanes differently than a 1-D block, so `variance` rounds
658+ # differently and how that lowers depends on the compiler. The contract each
659+ # implementation owes is agreement with the reference, at the tolerances the
660+ # tests above use, and that is what this asserts.
647661_OVERRIDE_ACTIVE = (
648662 flag_gems .fused_deepseek_v4_qnorm_rope_kv_rope_quant_insert is not _generic_impl
649663)
@@ -661,7 +675,7 @@ def test_combined_q_and_kv(num_tokens: int, n_heads: int, block_size: int):
661675)
662676@pytest .mark .parametrize ("n_heads" , [64 , 128 ])
663677@pytest .mark .parametrize ("block_size" , [16 , 64 ])
664- def test_backend_override_matches_generic (
678+ def test_backend_override_matches_reference (
665679 num_tokens : int , n_heads : int , block_size : int
666680):
667681 torch .manual_seed (3 )
@@ -679,32 +693,19 @@ def test_backend_override_matches_generic(
679693 positions = torch .arange (num_tokens , dtype = torch .int64 , device = device )
680694 cos_sin_cache = make_cos_sin_cache (max_pos , ROPE_DIM , torch .float32 , device )
681695
682- q_gen , k_cache_gen = q .clone (), k_cache .clone ()
683- _generic_impl (
684- q_gen , kv , k_cache_gen , slot_mapping , positions , cos_sin_cache , eps , block_size
696+ q_ref , kv_ref = q .clone (), kv .clone ()
697+ k_cache_ref = k_cache .clone ()
698+ ref_impl_chunked (
699+ q_ref ,
700+ kv_ref ,
701+ k_cache_ref ,
702+ slot_mapping .clone (),
703+ positions .clone (),
704+ cos_sin_cache .clone (),
705+ eps ,
706+ block_size ,
685707 )
686708 fused_impl (q , kv , k_cache , slot_mapping , positions , cos_sin_cache , eps , block_size )
687709
688- # The cache is derived from kv through per-64-element reductions, identical
689- # in any sane decomposition, so it must match byte for byte -- an override
690- # that quantizes differently has changed behaviour.
691- assert torch .equal (k_cache , k_cache_gen ), (
692- f"override wrote a different FP8 cache at num_tokens={ num_tokens } , "
693- f"n_heads={ n_heads } , block_size={ block_size } : "
694- f"{ int ((k_cache != k_cache_gen ).sum ().item ())} of { k_cache .numel ()} "
695- "bytes differ"
696- )
697- # q goes through a 512-element RMSNorm reduction whose tree may legitimately
698- # differ between decompositions: a 2-D [TPP, 512] tile assigns lanes
699- # differently than a 1-D 512 block, so `variance` rounds differently, and
700- # that propagates through rsqrt into the stored bf16. Measured on a Hygon
701- # BW1000 at num_tokens=511: 5 of 16744448 elements differ, by up to 2 bf16
702- # ULP (7.8e-3 absolute, 7.8e-3 relative).
703- #
704- # The tolerance is the operator's own -- the same rtol/atol the accuracy
705- # tests above use against the torch reference. Asserting that an override
706- # tracks the generic kernel more closely than either tracks the reference
707- # would be asserting something the operator never promised. (It is in fact
708- # bit-identical on MetaX C550, but that is a property of one backend's lane
709- # assignment, not a contract.)
710- torch .testing .assert_close (q , q_gen , rtol = 1e-2 , atol = 1e-2 )
710+ k_cache_compare (k_cache , k_cache_ref , block_size , rtol = 1e-2 , atol = 1e-2 )
711+ assert_close_chunked (q , q_ref , rtol = 1e-2 , atol = 1e-2 )
0 commit comments