patch_dspark_woa.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/env python3
  2. """Patch dspark.py forward_dspark so the drafter wo_a feeds DeepGEMM fp8_einsum
  3. with reconstructed, DeepGEMM-compatible scale tensors (BUG-2026-08-03T204037).
  4. Probe (runtime shapes on production-3.8):
  5. out_fp8=(40,4,4096) fp8 out_scale=(40,4,8) weight=(4096,4096)
  6. scale_inv=(32,32) ue8m0 projected=(40,4,1024) groups=4 o_lora=1024
  7. recipe=(1,1,128)
  8. Root cause: DeepGEMM's fp8_bmm ("bhr,hdr->bhd") requires (1) the weight rank-3
  9. (H,D,R), (2) the b_scale per-row 128-block (H,D,R/128). The drafter instead
  10. passes:
  11. - weight 2D (H*D,R) -> reshape to (H,D,R)
  12. - a_scale: DeepGEMM permutes the A-side itself; the native UE8M0 int scale
  13. (B,H,R/512) is already correct — do NOT expand.
  14. - b_scale tiled (32,32) UE8M0 (128x128) -> broadcast each tile's scale to its
  15. 128 output rows => per-row (H,D,R/128) fp32
  16. Verified via a GPU harness that these shapes/dtypes pass DeepGEMM's shape checks.
  17. """
  18. import pathlib
  19. P = pathlib.Path(
  20. "/opt/env/lib/python3.12/site-packages/vllm/models/deepseek_v4/nvidia/dspark.py"
  21. )
  22. src = P.read_text()
  23. OLD = """ torch.ops.vllm.deepseek_v4_fp8_einsum(
  24. out_fp8,
  25. out_scale,
  26. self.wo_a.weight,
  27. self.wo_a.weight_scale_inv,
  28. projected,
  29. "bhr,hdr->bhd",
  30. list(self._einsum_recipe),
  31. )"""
  32. NEW = """ # --- sm121-dspark38: reconstruct wo_a tensors for DeepGEMM fp8_einsum
  33. # wo_a.weight is 2D (n_local_groups*o_lora_rank, hidden); fp8_bmm needs
  34. # rank-3 (H, D, R) where H must equal the a-side group count so the
  35. # einsum "bhr,hdr->bhd" contracts correctly (einsum.hpp:164 checks
  36. # batch H from a == H from b). Use n_local_groups, NOT n_groups.
  37. _grp = self.n_local_groups
  38. _olar = self.o_lora_rank
  39. _w3 = self.wo_a.weight.view(_grp, _olar, -1)
  40. # a_scale (out_scale): DeepGEMM permutes/owns the A-side itself
  41. # (a_scale.permute(1,0,2) -> (H,B,blocks)); the native UE8M0 int scale
  42. # (B,H,R/512) already matches the int branch (ceil(R/512)=size(-1)). Do
  43. # NOT expand it.
  44. _a_sc = out_scale
  45. # b_scale (weight_scale_inv) is a tiled (32,32) UE8M0 128x128 scale;
  46. # broadcast each tile's scale to its 128 output rows -> per-row fp32.
  47. _bs = self.wo_a.weight_scale_inv
  48. _nm = _grp * _olar
  49. _ntiles = _bs.shape[-2]
  50. _btile = _nm // _ntiles
  51. _bs_f = (_bs.to(torch.int32) << 23).view(torch.float32)
  52. _b_sc = (
  53. _bs_f.unsqueeze(1).expand(_ntiles, _btile, -1)
  54. .reshape(_nm, -1)
  55. .view(_grp, _olar, -1)
  56. )
  57. torch.ops.vllm.deepseek_v4_fp8_einsum(
  58. out_fp8,
  59. _a_sc,
  60. _w3,
  61. _b_sc,
  62. projected,
  63. "bhr,hdr->bhd",
  64. list(self._einsum_recipe),
  65. )"""
  66. assert src.count(OLD) == 1, f"expected 1 einsum call, got {src.count(OLD)}"
  67. P.write_text(src.replace(OLD, NEW))
  68. print("patched dspark.py: drafter wo_a w3/a_sc/b_sc reconstruction")