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:
@@ -10,6 +10,7 @@ import sglang.srt.sampling.penaltylib as penaltylib
|
||||
from sglang.srt.sampling.custom_logit_processor import CustomLogitProcessor
|
||||
from sglang.srt.sampling.sampling_params import TOP_K_ALL
|
||||
from sglang.srt.server_args import get_global_server_args
|
||||
from sglang.srt.utils.common import is_pin_memory_available
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sglang.srt.managers.schedule_batch import ScheduleBatch
|
||||
@@ -73,39 +74,59 @@ class SamplingBatchInfo:
|
||||
|
||||
reqs = batch.reqs
|
||||
device = batch.device
|
||||
temperatures = torch.tensor(
|
||||
[r.sampling_params.temperature for r in reqs],
|
||||
dtype=torch.float,
|
||||
device=device,
|
||||
).view(-1, 1)
|
||||
top_ps = torch.tensor(
|
||||
[r.sampling_params.top_p for r in reqs], dtype=torch.float, device=device
|
||||
)
|
||||
top_ks = torch.tensor(
|
||||
[r.sampling_params.top_k for r in reqs], dtype=torch.int32, device=device
|
||||
)
|
||||
min_ps = torch.tensor(
|
||||
[r.sampling_params.min_p for r in reqs], dtype=torch.float, device=device
|
||||
bs = len(reqs)
|
||||
pin = is_pin_memory_available(device)
|
||||
|
||||
# Collect every per-request sampling param and the aggregate flags in a
|
||||
# single pass, then upload the float params with one pinned H2D copy
|
||||
# instead of one synchronous copy per parameter. The device tensors are
|
||||
# disjoint slices of one buffer; filter/merge only index and cat them,
|
||||
# which always produces fresh tensors, so sharing a buffer is safe.
|
||||
fused_float = [0.0] * (3 * bs)
|
||||
top_ks_cpu = [0] * bs
|
||||
seeds_cpu = [0] * bs if enable_deterministic else None
|
||||
is_all_greedy = True
|
||||
need_top_p_sampling = False
|
||||
need_top_k_sampling = False
|
||||
need_min_p_sampling = False
|
||||
has_logit_bias = False
|
||||
for i, r in enumerate(reqs):
|
||||
sp = r.sampling_params
|
||||
top_k = sp.top_k
|
||||
min_p = sp.min_p
|
||||
fused_float[i] = sp.temperature
|
||||
fused_float[bs + i] = sp.top_p
|
||||
fused_float[2 * bs + i] = min_p
|
||||
top_ks_cpu[i] = top_k
|
||||
if enable_deterministic:
|
||||
seeds_cpu[i] = (
|
||||
sp.sampling_seed if sp.sampling_seed is not None else 42
|
||||
)
|
||||
is_all_greedy &= top_k <= 1
|
||||
need_top_p_sampling |= sp.top_p != 1.0
|
||||
need_top_k_sampling |= top_k != TOP_K_ALL
|
||||
need_min_p_sampling |= min_p > 0
|
||||
has_logit_bias |= sp.logit_bias is not None
|
||||
|
||||
fused_float_device = torch.tensor(
|
||||
fused_float, dtype=torch.float, pin_memory=pin
|
||||
).to(device, non_blocking=True)
|
||||
temperatures = fused_float_device[:bs].view(-1, 1)
|
||||
top_ps = fused_float_device[bs : 2 * bs]
|
||||
min_ps = fused_float_device[2 * bs :]
|
||||
top_ks = torch.tensor(top_ks_cpu, dtype=torch.int32, pin_memory=pin).to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
sampling_seed = (
|
||||
torch.tensor(
|
||||
[
|
||||
(
|
||||
r.sampling_params.sampling_seed
|
||||
if r.sampling_params.sampling_seed is not None
|
||||
else 42
|
||||
)
|
||||
for r in reqs
|
||||
],
|
||||
dtype=torch.int64,
|
||||
device=device,
|
||||
torch.tensor(seeds_cpu, dtype=torch.int64, pin_memory=pin).to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
if enable_deterministic
|
||||
else None
|
||||
)
|
||||
|
||||
logit_bias = None
|
||||
if any(r.sampling_params.logit_bias is not None for r in reqs):
|
||||
if has_logit_bias:
|
||||
logit_bias = torch.zeros(len(reqs), vocab_size, device=device)
|
||||
for i, r in enumerate(reqs):
|
||||
if r.sampling_params.logit_bias is not None:
|
||||
@@ -168,10 +189,10 @@ class SamplingBatchInfo:
|
||||
top_ks=top_ks,
|
||||
min_ps=min_ps,
|
||||
sampling_seed=sampling_seed,
|
||||
is_all_greedy=all(r.sampling_params.top_k <= 1 for r in reqs),
|
||||
need_top_p_sampling=any(r.sampling_params.top_p != 1.0 for r in reqs),
|
||||
need_top_k_sampling=any(r.sampling_params.top_k != TOP_K_ALL for r in reqs),
|
||||
need_min_p_sampling=any(r.sampling_params.min_p > 0 for r in reqs),
|
||||
is_all_greedy=is_all_greedy,
|
||||
need_top_p_sampling=need_top_p_sampling,
|
||||
need_top_k_sampling=need_top_k_sampling,
|
||||
need_min_p_sampling=need_min_p_sampling,
|
||||
vocab_size=vocab_size,
|
||||
penalizer_orchestrator=penalizer_orchestrator,
|
||||
has_custom_logit_processor=has_custom_logit_processor,
|
||||
|
||||
Reference in New Issue
Block a user