Fuse SamplingBatchInfo tensor construction into one pass

from_schedule_batch built temperature/top_p/top_k/min_p (+seed) with
4-5 separate list comprehensions and one synchronous H2D copy each,
plus 4 more passes for the is_all_greedy/need_* flags. Collect
everything in a single pass over reqs and upload the float params as
one pinned non-blocking H2D copy (disjoint device views of one
buffer; filter/merge only index and cat, producing fresh tensors, so
the shared buffer is safe), int32 top_k and optional int64 seeds as
their own pinned copies.

B300 (torch 2.11 cu130), scheduler-thread blocking time per call:
  bs=8: 38.8 -> 18.2 us (2.1x); bs=32: 1.9x; bs=200: 1.2x
CPU-only construction at bs=200: 2890 -> 1387 us (2.1x).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 05:04:39 +00:00
co-authored by Claude Fable 5
parent 0d065a8ab0
commit 87a22b17ce
2 changed files with 91 additions and 29 deletions
@@ -464,6 +464,47 @@ class TestFromScheduleBatch(CustomTestCase):
self.assertAlmostEqual(info.top_ps[0].item(), 0.9, places=5)
self.assertEqual(info.top_ks[0].item(), 50)
@patch("sglang.srt.sampling.sampling_batch_info.get_global_server_args")
def test_mixed_batch_tensor_values_dtypes_shapes(self, mock_server_args):
"""All per-request params land in the right slot with the right dtype,
and the fused float tensors are independent after filtering."""
mock_server_args.return_value.enable_deterministic_inference = False
mock_server_args.return_value.enable_custom_logit_processor = False
params = [
dict(temp=0.5, top_p=0.7, top_k=10, min_p=0.2),
dict(temp=1.0, top_p=1.0, top_k=1, min_p=0.0),
dict(temp=1.3, top_p=0.95, top_k=TOP_K_ALL, min_p=0.05),
]
batch = MagicMock()
batch.reqs = [self._make_req(**p) for p in params]
batch.device = DEVICE
info = SamplingBatchInfo.from_schedule_batch(batch, VOCAB_SIZE)
self.assertEqual(info.temperatures.shape, (3, 1))
self.assertEqual(info.temperatures.dtype, torch.float32)
self.assertEqual(info.top_ps.shape, (3,))
self.assertEqual(info.top_ps.dtype, torch.float32)
self.assertEqual(info.min_ps.shape, (3,))
self.assertEqual(info.top_ks.dtype, torch.int32)
for i, p in enumerate(params):
self.assertAlmostEqual(info.temperatures[i, 0].item(), p["temp"], places=5)
self.assertAlmostEqual(info.top_ps[i].item(), p["top_p"], places=5)
self.assertEqual(info.top_ks[i].item(), p["top_k"])
self.assertAlmostEqual(info.min_ps[i].item(), p["min_p"], places=5)
self.assertFalse(info.is_all_greedy)
self.assertTrue(info.need_top_p_sampling)
self.assertTrue(info.need_top_k_sampling)
self.assertTrue(info.need_min_p_sampling)
# Filtering must yield independent tensors with the right values even
# though construction shares one fused buffer.
keep = torch.tensor([2, 0], dtype=torch.int64)
info.filter_batch([2, 0], keep)
self.assertAlmostEqual(info.temperatures[0, 0].item(), 1.3, places=5)
self.assertAlmostEqual(info.top_ps[1].item(), 0.7, places=5)
self.assertEqual(info.top_ks[0].item(), TOP_K_ALL)
@patch("sglang.srt.sampling.sampling_batch_info.get_global_server_args")
def test_greedy_detection(self, mock_server_args):
"""Test that top_k=1 sets is_all_greedy=True."""