[feat] support minimum token load balance in dp attention (#7379)
This commit is contained in:
@@ -16,9 +16,13 @@
|
||||
import logging
|
||||
import multiprocessing as mp
|
||||
import signal
|
||||
import struct
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from enum import Enum, auto
|
||||
from multiprocessing import shared_memory
|
||||
from typing import Dict, List
|
||||
|
||||
import psutil
|
||||
import setproctitle
|
||||
@@ -32,6 +36,7 @@ from sglang.srt.managers.io_struct import (
|
||||
)
|
||||
from sglang.srt.managers.schedule_batch import Req
|
||||
from sglang.srt.managers.scheduler import run_scheduler_process
|
||||
from sglang.srt.managers.utils import DPBalanceMeta
|
||||
from sglang.srt.server_args import PortArgs, ServerArgs
|
||||
from sglang.srt.torch_memory_saver_adapter import TorchMemorySaverAdapter
|
||||
from sglang.srt.utils import bind_port, configure_logger, get_zmq_socket
|
||||
@@ -45,6 +50,7 @@ class LoadBalanceMethod(Enum):
|
||||
|
||||
ROUND_ROBIN = auto()
|
||||
SHORTEST_QUEUE = auto()
|
||||
MINIMUM_TOKENS = auto()
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, method: str):
|
||||
@@ -58,7 +64,16 @@ class LoadBalanceMethod(Enum):
|
||||
class DataParallelController:
|
||||
"""A controller that dispatches requests to multiple data parallel workers."""
|
||||
|
||||
def __init__(self, server_args: ServerArgs, port_args: PortArgs) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
server_args: ServerArgs,
|
||||
port_args: PortArgs,
|
||||
dp_balance_meta: DPBalanceMeta,
|
||||
) -> None:
|
||||
# for dp balance
|
||||
self.global_balance_id = 0
|
||||
self.balance_meta = dp_balance_meta
|
||||
|
||||
# Parse args
|
||||
self.max_total_num_tokens = None
|
||||
self.server_args = server_args
|
||||
@@ -79,6 +94,7 @@ class DataParallelController:
|
||||
dispatch_lookup = {
|
||||
LoadBalanceMethod.ROUND_ROBIN: self.round_robin_scheduler,
|
||||
LoadBalanceMethod.SHORTEST_QUEUE: self.shortest_queue_scheduler,
|
||||
LoadBalanceMethod.MINIMUM_TOKENS: self.minimum_tokens_scheduler,
|
||||
}
|
||||
self.dispatching = dispatch_lookup[self.load_balance_method]
|
||||
|
||||
@@ -234,6 +250,7 @@ class DataParallelController:
|
||||
pp_rank,
|
||||
dp_rank,
|
||||
writer,
|
||||
self.balance_meta,
|
||||
),
|
||||
)
|
||||
with memory_saver_adapter.configure_subprocess():
|
||||
@@ -269,6 +286,33 @@ class DataParallelController:
|
||||
def shortest_queue_scheduler(self, input_requests):
|
||||
raise NotImplementedError()
|
||||
|
||||
def minimum_tokens_scheduler(self, req):
|
||||
# This variable corresponds to the balance_id in TokenizedGenerateReqInput.
|
||||
# We use it to to control the number of onfly tokens (requests dispatched to workers but not yet received).
|
||||
def get_next_global_balance_id() -> int:
|
||||
INT32_MAX = 2147483647
|
||||
current_id = self.global_balance_id
|
||||
self.global_balance_id = (self.global_balance_id + 1) % INT32_MAX
|
||||
return current_id
|
||||
|
||||
req.dp_balance_id = get_next_global_balance_id()
|
||||
with self.balance_meta.mutex:
|
||||
# 1. local_tokens represents the tokens currently inferring on the worker,
|
||||
# while onfly refers to the requests dispatched by the dispatcher but not yet received by the scheduler.
|
||||
onfly_info = self.balance_meta.get_shared_onfly()
|
||||
local_tokens = self.balance_meta.get_shared_local_tokens()
|
||||
total_tokens = [
|
||||
local_token + sum(onfly_dict.values())
|
||||
for local_token, onfly_dict in zip(local_tokens, onfly_info)
|
||||
]
|
||||
target_worker = total_tokens.index(min(total_tokens))
|
||||
onfly_info[target_worker][req.dp_balance_id] = len(req.input_ids)
|
||||
# 2. write the new onfly info to the shm
|
||||
self.balance_meta.set_shared_onfly_info(onfly_info)
|
||||
|
||||
# logger.info(f"dp workers {local_tokens=}, {onfly_info=}, {target_worker=}")
|
||||
self.workers[target_worker].send_pyobj(req)
|
||||
|
||||
def event_loop(self):
|
||||
while True:
|
||||
while True:
|
||||
@@ -302,9 +346,12 @@ def run_data_parallel_controller_process(
|
||||
setproctitle.setproctitle("sglang::data_parallel_controller")
|
||||
configure_logger(server_args)
|
||||
parent_process = psutil.Process().parent()
|
||||
balance_meta = DPBalanceMeta(server_args.dp_size)
|
||||
|
||||
try:
|
||||
controller = DataParallelController(server_args, port_args)
|
||||
controller = DataParallelController(
|
||||
server_args, port_args, dp_balance_meta=balance_meta
|
||||
)
|
||||
pipe_writer.send(
|
||||
{
|
||||
"status": "ready",
|
||||
@@ -323,3 +370,6 @@ def run_data_parallel_controller_process(
|
||||
traceback = get_exception_traceback()
|
||||
logger.error(f"DataParallelController hit an exception: {traceback}")
|
||||
parent_process.send_signal(signal.SIGQUIT)
|
||||
finally:
|
||||
# we need to destruct mp.Manager() in balance_meta
|
||||
balance_meta.destructor()
|
||||
|
||||
Reference in New Issue
Block a user