topk_ties.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. #!/usr/bin/env python3
  2. """top_k_per_row_prefill keeps a run-dependent subset of pools tied at the k-th score.
  3. No checkpoint and no server: one CUDA device in a container that has vLLM
  4. importable.
  5. python topk_ties.py
  6. Each row gets random scores plus two extra copies of its k-th largest score,
  7. so the cut falls inside an exact tie. The same input then goes through the
  8. kernel 200 times, and the distinct outputs are counted (each row's ids sorted,
  9. so only the chosen set counts). One row alone comes back the same every time.
  10. A launch with as many rows as a real prefill chunk comes back different on
  11. almost every call.
  12. """
  13. import torch
  14. import vllm._custom_ops as ops
  15. K = 512
  16. CALLS = 200
  17. def distinct_outputs(rows, cols, extra_ties):
  18. torch.manual_seed(0)
  19. scores = torch.randn(rows, cols, device="cuda")
  20. kth = scores.topk(K, dim=1).values[:, -1:]
  21. # Put the copies on entries below the cut, chosen at random per row.
  22. below = torch.rand(rows, cols, device="cuda") + (scores >= kth).float() * 2
  23. scores.scatter_(1, below.argsort(dim=1)[:, :extra_ties], kth.expand(-1, extra_ties))
  24. starts = torch.zeros(rows, dtype=torch.int32, device="cuda")
  25. ends = torch.full((rows,), cols, dtype=torch.int32, device="cuda")
  26. seen = set()
  27. for _ in range(CALLS):
  28. out = torch.full((rows, K), -1, dtype=torch.int32, device="cuda")
  29. ops.top_k_per_row_prefill(scores, starts, ends, out, rows, scores.stride(0), scores.stride(1), K)
  30. seen.add(out.sort(dim=1).values.cpu().numpy().tobytes())
  31. return len(seen)
  32. for rows, cols in [(1, 1300), (1, 8000), (64, 1300), (16128, 1300), (16128, 8000)]:
  33. print(f"{rows:5d} rows x {cols} pools, 2 extra tied at the cut: "
  34. f"{distinct_outputs(rows, cols, 2)} distinct outputs in {CALLS} calls")