leideng/QCFuse / srt /eplb /eplb_manager.py
leideng's picture
download
raw
4.36 kB
import logging
import time
from typing import TYPE_CHECKING, List
import torch.cuda
from sglang.srt.eplb.expert_distribution import get_global_expert_distribution_recorder
from sglang.srt.eplb.expert_location import ExpertLocationMetadata
if TYPE_CHECKING:
from sglang.srt.model_executor.model_runner import ModelRunner
logger = logging.getLogger(__name__)
class EPLBManager:
def __init__(self, model_runner: "ModelRunner"):
super().__init__()
self._model_runner = model_runner
self._server_args = model_runner.server_args
self._rebalance_layers_per_chunk = (
self._server_args.eplb_rebalance_layers_per_chunk
)
self._rebalance_num_iterations = self._server_args.eplb_rebalance_num_iterations
# Otherwise, the circular buffer will contain stale data. If the case is needed, it can be implemented.
assert (
self._server_args.eplb_rebalance_num_iterations
>= self._server_args.expert_distribution_recorder_buffer_size
), "eplb_rebalance_num_iterations must be greater than expert_distribution_recorder_buffer_size"
if not get_global_expert_distribution_recorder().recording:
get_global_expert_distribution_recorder().start_record()
logger.info(
f"[EPLBManager] system started, will rebalance per {self._rebalance_num_iterations} iterations."
)
self._main_generator = self._entrypoint()
def on_forward_pass_end(self):
next(self._main_generator)
# can be more complex if needed
def _entrypoint(self):
while True:
for _ in range(self._rebalance_num_iterations):
yield
yield from self.rebalance()
def rebalance(self):
logger.info("[EPLBManager] rebalance start")
enable_timing = self._rebalance_layers_per_chunk is None
if enable_timing:
torch.get_device_module().synchronize()
time_start = time.time()
dump_record_output = get_global_expert_distribution_recorder().dump_record(
output_mode="object"
)
logical_count = dump_record_output["logical_count"]
average_utilization_rate_over_window = dump_record_output[
"average_utilization_rate_over_window"
]
# Check whether rebalancing is needed
if not self._check_rebalance_needed(average_utilization_rate_over_window):
return
expert_location_metadata = ExpertLocationMetadata.init_by_eplb(
self._server_args, self._model_runner.model_config, logical_count
)
update_layer_ids_chunks = self._compute_update_layer_ids_chunks()
for chunk_index, update_layer_ids in enumerate(update_layer_ids_chunks):
if len(update_layer_ids_chunks) > 1:
yield
self._model_runner.update_expert_location(
expert_location_metadata,
update_layer_ids=update_layer_ids,
)
msg = f"[EPLBManager] rebalance end"
if enable_timing:
torch.get_device_module().synchronize()
time_end = time.time()
msg += f" time={time_end - time_start:.3f}s"
logger.info(msg)
def _check_rebalance_needed(self, average_utilization_rate_over_window):
if average_utilization_rate_over_window is None:
return True
if (
average_utilization_rate_over_window
> self._server_args.eplb_min_rebalancing_utilization_threshold
):
logger.info(
f"[EPLBManager] Skipped ep rebalancing: current GPU utilization {average_utilization_rate_over_window:.2f} > minimum rebalance threshold {self._server_args.eplb_min_rebalancing_utilization_threshold:.2f}"
)
return False
return True
def _compute_update_layer_ids_chunks(self) -> List[List[int]]:
all_layer_ids = sorted(
list(self._model_runner.model.routed_experts_weights_of_layer.keys())
)
chunk_size = self._rebalance_layers_per_chunk or 1000000
return list(_chunk_list(all_layer_ids, chunk_size=chunk_size))
def _chunk_list(items: List, chunk_size):
for start_index in range(0, len(items), chunk_size):
yield items[start_index : start_index + chunk_size]

Xet Storage Details

Size:
4.36 kB
·
Xet hash:
304ec3ad94b0ec67da3835fa456dae988b3175ba103a157f8e3a60941c73e695

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.