Fix device context during NIXL backend initialization (#38774)
Co-authored-by: Aurick Qiao <6137920+aurickq@users.noreply.github.com>
This commit is contained in:
co-authored by
Aurick Qiao
parent
a3bf25dc62
commit
7b7620774c
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
|
import torch
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -48,7 +49,7 @@ from sglang.srt.disaggregation.utils import (
|
|||||||
slice_dsa_tail_dst_ptrs_for_pp,
|
slice_dsa_tail_dst_ptrs_for_pp,
|
||||||
)
|
)
|
||||||
from sglang.srt.environ import envs
|
from sglang.srt.environ import envs
|
||||||
from sglang.srt.runtime_context import get_parallel, get_schedule
|
from sglang.srt.runtime_context import get_device, get_parallel, get_schedule
|
||||||
from sglang.srt.server_args import ServerArgs
|
from sglang.srt.server_args import ServerArgs
|
||||||
from sglang.srt.utils.common import run_with_deadline
|
from sglang.srt.utils.common import run_with_deadline
|
||||||
|
|
||||||
@@ -472,8 +473,13 @@ class NixlKVManager(StagingManagerMixin, CommonKVManager):
|
|||||||
backend_params.setdefault("thread_count", str(num_threads))
|
backend_params.setdefault("thread_count", str(num_threads))
|
||||||
elif backend == "UCCL":
|
elif backend == "UCCL":
|
||||||
backend_params.setdefault("num_cpus", str(num_threads))
|
backend_params.setdefault("num_cpus", str(num_threads))
|
||||||
|
|
||||||
|
def create_backend():
|
||||||
|
torch.get_device_module(get_device().device).set_device(self.kv_args.gpu_id)
|
||||||
|
return self.agent.create_backend(backend, backend_params)
|
||||||
|
|
||||||
run_with_deadline(
|
run_with_deadline(
|
||||||
lambda: self.agent.create_backend(backend, backend_params),
|
create_backend,
|
||||||
timeout_s=envs.SGLANG_DISAGGREGATION_ENGINE_INIT_TIMEOUT.get(),
|
timeout_s=envs.SGLANG_DISAGGREGATION_ENGINE_INIT_TIMEOUT.get(),
|
||||||
what=f"NIXL create_backend({backend!r}, {backend_params})",
|
what=f"NIXL create_backend({backend!r}, {backend_params})",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ from sglang.srt.disaggregation.nixl.conn import (
|
|||||||
TransferKVChunk,
|
TransferKVChunk,
|
||||||
TransferStatus,
|
TransferStatus,
|
||||||
)
|
)
|
||||||
|
from sglang.srt.disaggregation.utils import DisaggregationMode
|
||||||
|
from sglang.srt.runtime_context import get_context
|
||||||
from sglang.test.ci.ci_register import register_cpu_ci
|
from sglang.test.ci.ci_register import register_cpu_ci
|
||||||
from sglang.test.test_utils import CustomTestCase
|
from sglang.test.test_utils import CustomTestCase
|
||||||
|
|
||||||
@@ -106,6 +108,106 @@ def _fake_staging_buffer_module(mock_gather=None):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
class TestNixlBackendInitialization(CustomTestCase):
|
||||||
|
def _initialize_manager(self, agent, device_module, mode, gpu_id):
|
||||||
|
args = SimpleNamespace(
|
||||||
|
pp_rank=0,
|
||||||
|
engine_rank=0,
|
||||||
|
gpu_id=gpu_id,
|
||||||
|
kv_data_ptrs=[],
|
||||||
|
kv_item_lens=[],
|
||||||
|
)
|
||||||
|
mgr = object.__new__(NixlKVManager)
|
||||||
|
mgr.kv_args = args
|
||||||
|
mgr.disaggregation_mode = mode
|
||||||
|
mgr.enable_deferred_decode_kv_release = False
|
||||||
|
|
||||||
|
api = types.ModuleType("nixl._api")
|
||||||
|
api.nixl_agent = MagicMock(return_value=agent)
|
||||||
|
api.nixl_agent_config = MagicMock()
|
||||||
|
api.nixl_thread_sync_t = SimpleNamespace(NIXL_THREAD_SYNC_STRICT="strict")
|
||||||
|
nixl = types.ModuleType("nixl")
|
||||||
|
nixl._api = api
|
||||||
|
agent.get_plugin_list.return_value = ["UCX"]
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch.dict(sys.modules, {"nixl": nixl, "nixl._api": api}),
|
||||||
|
patch.dict(
|
||||||
|
"os.environ",
|
||||||
|
{
|
||||||
|
"SGLANG_DISAGGREGATION_NIXL_BACKEND": "UCX",
|
||||||
|
"SGLANG_DISAGGREGATION_NIXL_BACKEND_PARAMS": "{}",
|
||||||
|
"SGLANG_DISAGGREGATION_ENGINE_INIT_TIMEOUT": "5",
|
||||||
|
"SGLANG_DISAGGREGATION_QUEUE_SIZE": "0",
|
||||||
|
"SGLANG_DISAGG_STAGING_BUFFER": "false",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
get_context().override_server_args(device="cuda") as server_args,
|
||||||
|
patch.object(CommonKVManager, "__init__", return_value=None),
|
||||||
|
patch(
|
||||||
|
"sglang.srt.disaggregation.nixl.conn.get_parallel",
|
||||||
|
return_value=SimpleNamespace(tp_size=4),
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"sglang.srt.disaggregation.nixl.conn.torch.get_device_module",
|
||||||
|
return_value=device_module,
|
||||||
|
) as get_device_module,
|
||||||
|
patch.object(NixlKVManager, "register_buffer_to_engine"),
|
||||||
|
patch.object(NixlKVManager, "_start_bootstrap_thread"),
|
||||||
|
patch.object(NixlKVManager, "_start_decode_listener_thread"),
|
||||||
|
patch.object(NixlKVManager, "_start_heartbeat_checker_thread"),
|
||||||
|
):
|
||||||
|
self.assertIsNone(server_args.device)
|
||||||
|
NixlKVManager.__init__(mgr, args, mode, server_args)
|
||||||
|
get_device_module.assert_called_once_with("cuda")
|
||||||
|
|
||||||
|
def test_backend_initialization_selects_device_in_deadline_thread(self):
|
||||||
|
caller_thread = threading.get_ident()
|
||||||
|
for mode, gpu_id in (
|
||||||
|
(DisaggregationMode.PREFILL, 3),
|
||||||
|
(DisaggregationMode.DECODE, 1),
|
||||||
|
):
|
||||||
|
with self.subTest(mode=mode, gpu_id=gpu_id):
|
||||||
|
calls = []
|
||||||
|
device_module = MagicMock()
|
||||||
|
device_module.set_device.side_effect = lambda device: calls.append(
|
||||||
|
("set_device", device, threading.get_ident())
|
||||||
|
)
|
||||||
|
agent = MagicMock()
|
||||||
|
agent.create_backend.side_effect = lambda backend, params: calls.append(
|
||||||
|
("create_backend", backend, threading.get_ident())
|
||||||
|
)
|
||||||
|
|
||||||
|
self._initialize_manager(agent, device_module, mode, gpu_id)
|
||||||
|
|
||||||
|
self.assertEqual(len(calls), 2)
|
||||||
|
backend_thread = calls[1][2]
|
||||||
|
self.assertNotEqual(backend_thread, caller_thread)
|
||||||
|
self.assertEqual(
|
||||||
|
calls,
|
||||||
|
[
|
||||||
|
("set_device", gpu_id, backend_thread),
|
||||||
|
("create_backend", "UCX", backend_thread),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
agent.create_backend.assert_called_once_with(
|
||||||
|
"UCX",
|
||||||
|
{"num_threads": "8"} if mode == DisaggregationMode.PREFILL else {},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_backend_initialization_propagates_error(self):
|
||||||
|
error = RuntimeError("backend initialization failed")
|
||||||
|
agent = MagicMock()
|
||||||
|
agent.create_backend.side_effect = error
|
||||||
|
|
||||||
|
with self.assertRaises(RuntimeError) as raised:
|
||||||
|
self._initialize_manager(
|
||||||
|
agent, MagicMock(), DisaggregationMode.DECODE, gpu_id=3
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIs(raised.exception, error)
|
||||||
|
|
||||||
|
|
||||||
class TestNixlTransferInfo(CustomTestCase):
|
class TestNixlTransferInfo(CustomTestCase):
|
||||||
def test_from_zmq_parses_required_fields(self):
|
def test_from_zmq_parses_required_fields(self):
|
||||||
kv_indices = np.array([3, 5, 8], dtype=np.int32)
|
kv_indices = np.array([3, 5, 8], dtype=np.int32)
|
||||||
|
|||||||
Reference in New Issue
Block a user