Support getting checksums in weight checker (#24537)

This commit is contained in:
fzyzcjy
2026-05-06 22:59:28 +08:00
committed by GitHub
parent ae5ae840f6
commit c4c5541618
8 changed files with 331 additions and 18 deletions
@@ -138,6 +138,74 @@ class TestWeightCheckerE2E(CustomTestCase):
self.assertEqual(resp.status_code, 200)
self.assertTrue(resp.json()["success"])
def test_e_checksum_returns_ranks_with_hashes(self):
"""checksum action must yield a ranks list with hex hashes per rank."""
resp = self._post("checksum")
self.assertEqual(resp.status_code, 200)
body = resp.json()
self.assertTrue(body["success"])
self.assertIn("ranks", body)
ranks = body["ranks"]
self.assertIsInstance(ranks, list)
self.assertGreaterEqual(len(ranks), 1)
first = ranks[0]
self.assertIn("checksums", first)
self.assertIn("parallelism_info", first)
info = first["parallelism_info"]
for key in (
"tp_rank",
"tp_size",
"dp_rank",
"dp_size",
"pp_rank",
"pp_size",
"rank",
"size",
):
self.assertIn(key, info)
checksums = first["checksums"]
self.assertGreater(len(checksums), 0)
for name, h in checksums.items():
self.assertIsInstance(h, str)
self.assertEqual(len(h), 16, f"unexpected hash length for {name!r}: {h!r}")
int(h, 16)
def test_e_checksum_is_stable_across_calls(self):
"""Two consecutive checksum calls with no weight update must match."""
first = self._post("checksum").json()["ranks"]
second = self._post("checksum").json()["ranks"]
self.assertEqual(first, second)
def test_e_checksum_changes_after_weight_update(self):
"""Updating a tensor must change its corresponding hash."""
param_name = "model.layers.7.mlp.up_proj.weight"
fused_name = "model.layers.7.mlp.gate_up_proj.weight"
before = self._post("checksum").json()["ranks"][0]["checksums"]
before_hash = before.get(fused_name)
self.assertIsNotNone(before_hash, f"missing {fused_name!r} in checksum keys")
new_tensor = torch.full(_UP_PROJ_SHAPE, 0.5, device="cuda")
self.assertTrue(
self._update_weights([(param_name, new_tensor)]).json()["success"]
)
after = self._post("checksum").json()["ranks"][0]["checksums"]
self.assertNotEqual(after[fused_name], before_hash)
def test_e_checksum_skips_non_persistent_buffers(self):
"""No checksum entry should contain a non-persistent-buffer substring."""
ranks = self._post("checksum").json()["ranks"]
for rank in ranks:
for name in rank["checksums"]:
self.assertNotIn("cos_sin_cache", name)
self.assertNotIn("inv_freq", name)
self.assertNotIn("freqs_cis", name)
self.assertNotIn("_weight_fp32", name)
def test_z_snapshot_reset_compare_detects_diff(self):
"""Destructive: leaves weights randomized. Named test_z_* so it runs last."""
self.assertEqual(self._post("snapshot").status_code, 200)
@@ -26,8 +26,12 @@ from sglang.srt.layers.quantization.fp8_utils import (
transform_scale_ue8m0,
)
from sglang.srt.utils.weight_checker import (
ChecksumInfo,
ParallelismInfo,
WeightChecker,
_check_tensors,
_hash_tensor,
_is_non_persistent_buffer_name,
_postprocess_tensors,
_random_like,
)
@@ -99,11 +103,26 @@ class _TinyModel(nn.Module):
class _FakeModelRunner:
"""Minimal stand-in: WeightChecker only touches `.model.named_parameters()` and
`.model.named_buffers()`, nothing else."""
"""Minimal stand-in: WeightChecker touches `.model.named_parameters()`,
`.model.named_buffers()`, plus parallelism attributes for the checksum action."""
def __init__(self, model: nn.Module):
def __init__(
self,
model: nn.Module,
tp_rank: int = 0,
tp_size: int = 1,
dp_rank: int = 0,
dp_size: int = 1,
pp_rank: int = 0,
pp_size: int = 1,
):
self.model = model
self.tp_rank = tp_rank
self.tp_size = tp_size
self.dp_rank = dp_rank
self.dp_size = dp_size
self.pp_rank = pp_rank
self.pp_size = pp_size
# ---------------------------------------------------------------------------
@@ -449,13 +468,27 @@ class TestHandle(_WeightCheckerTestBase):
def test_routes_to_actions(self):
with patch.object(self.checker, "_snapshot") as m_snap, patch.object(
self.checker, "_reset_tensors"
) as m_reset, patch.object(self.checker, "_compare") as m_compare:
) as m_reset, patch.object(self.checker, "_compare") as m_compare, patch.object(
self.checker, "_compute_checksum", return_value={"checksums": {}}
) as m_checksum:
self.checker.handle("snapshot")
self.checker.handle("reset_tensors")
self.checker.handle("compare")
self.checker.handle("checksum")
m_snap.assert_called_once()
m_reset.assert_called_once()
m_compare.assert_called_once()
m_checksum.assert_called_once()
def test_returns_none_for_non_checksum_actions(self):
self.assertIsNone(self.checker.handle("snapshot"))
self.assertIsNone(self.checker.handle("compare"))
def test_returns_dict_for_checksum_action(self):
out = self.checker.handle("checksum")
self.assertIsInstance(out, dict)
self.assertIn("checksums", out)
self.assertIn("parallelism_info", out)
def test_unknown_action_raises(self):
with self.assertRaises(Exception) as ctx:
@@ -463,5 +496,138 @@ class TestHandle(_WeightCheckerTestBase):
self.assertIn("Unsupported", str(ctx.exception))
# ---------------------------------------------------------------------------
# _is_non_persistent_buffer_name
# ---------------------------------------------------------------------------
class TestIsNonPersistentBufferName(CustomTestCase):
def test_matches_cos_sin_cache_substring(self):
self.assertTrue(
_is_non_persistent_buffer_name("model.rotary_emb.cos_sin_cache")
)
def test_matches_inv_freq_substring(self):
self.assertTrue(_is_non_persistent_buffer_name("model.rotary_emb.inv_freq"))
def test_matches_freqs_cis_substring(self):
self.assertTrue(_is_non_persistent_buffer_name("model.rotary_emb.freqs_cis"))
def test_matches_weight_fp32_substring(self):
self.assertTrue(
_is_non_persistent_buffer_name("model.layers.0.mlp.gate._weight_fp32")
)
def test_does_not_match_normal_param_names(self):
self.assertFalse(_is_non_persistent_buffer_name("model.layers.0.mlp.weight"))
self.assertFalse(_is_non_persistent_buffer_name("model.embed_tokens.weight"))
# ---------------------------------------------------------------------------
# _hash_tensor
# ---------------------------------------------------------------------------
class TestHashTensor(CustomTestCase):
def test_stable_for_same_input(self):
t = torch.arange(64, dtype=torch.float32).cuda()
self.assertEqual(_hash_tensor(t), _hash_tensor(t.clone()))
def test_changes_with_data(self):
a = torch.zeros(64, dtype=torch.float32).cuda()
b = torch.ones(64, dtype=torch.float32).cuda()
self.assertNotEqual(_hash_tensor(a), _hash_tensor(b))
def test_returns_16_char_hex(self):
t = torch.zeros(64, dtype=torch.float32).cuda()
h = _hash_tensor(t)
self.assertEqual(len(h), 16)
int(h, 16) # raises if not hex
def test_does_not_mutate_input(self):
t = torch.arange(64, dtype=torch.float32).cuda()
before = t.clone()
_hash_tensor(t)
torch.testing.assert_close(t, before)
# ---------------------------------------------------------------------------
# _compute_checksum
# ---------------------------------------------------------------------------
class _ChecksumTestBase(CustomTestCase):
def setUp(self):
torch.manual_seed(0)
self.model = _TinyModel().cuda()
self.runner = _FakeModelRunner(
self.model,
tp_rank=2,
tp_size=4,
dp_rank=1,
dp_size=2,
pp_rank=0,
pp_size=1,
)
self.checker = WeightChecker(model_runner=self.runner)
class TestComputeChecksum(_ChecksumTestBase):
def test_returns_dict_with_expected_top_level_keys(self):
out = self.checker._compute_checksum()
self.assertEqual(set(out.keys()), {"checksums", "parallelism_info"})
def test_skips_non_persistent_buffers(self):
out = self.checker._compute_checksum()
names = set(out["checksums"].keys())
# Normal params and buffers are present.
self.assertIn("w", names)
self.assertIn("b", names)
self.assertIn("running_mean", names)
# Non-persistent buffer patterns are filtered out.
self.assertNotIn("rotary_emb_cos_sin_cache", names)
self.assertNotIn("rotary_emb_freqs_cis", names)
self.assertNotIn("gate_proj_weight_fp32_cache", names)
def test_hashes_are_hex_strings(self):
out = self.checker._compute_checksum()
for name, h in out["checksums"].items():
self.assertEqual(len(h), 16, f"unexpected hash length for {name!r}")
int(h, 16)
def test_parallelism_info_reflects_runner_state(self):
info = self.checker._compute_checksum()["parallelism_info"]
self.assertEqual(info["tp_rank"], 2)
self.assertEqual(info["tp_size"], 4)
self.assertEqual(info["dp_rank"], 1)
self.assertEqual(info["dp_size"], 2)
self.assertEqual(info["pp_rank"], 0)
self.assertEqual(info["pp_size"], 1)
# rank/size come from torch.distributed; default to 0/1 when uninitialized.
self.assertIn("rank", info)
self.assertIn("size", info)
def test_checksum_is_stable_for_unchanged_weights(self):
first = self.checker._compute_checksum()
second = self.checker._compute_checksum()
self.assertEqual(first, second)
def test_checksum_changes_after_param_mutation(self):
first = self.checker._compute_checksum()["checksums"]["w"]
with torch.no_grad():
self.model.w.data.fill_(99.0)
second = self.checker._compute_checksum()["checksums"]["w"]
self.assertNotEqual(first, second)
def test_validates_against_pydantic_schema(self):
out = self.checker._compute_checksum()
info = ChecksumInfo.model_validate(out)
self.assertIsInstance(info.parallelism_info, ParallelismInfo)
if __name__ == "__main__":
unittest.main()