| 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.