feat(vlm): auto-select cuda vmm on multi-node mnnvl (#33936)
This commit is contained in:
@@ -524,6 +524,28 @@ def test_kimi_lazy_ipc_feature_acknowledges_all_tp_consumers():
|
||||
proxy.reconstruct_on_target_device.assert_called_once_with(0, consumer_count=8)
|
||||
|
||||
|
||||
def test_kimi_lazy_vmm_feature_uses_proxy_consumer_count():
|
||||
proxy = CudaIpcTensorTransportProxy.__new__(CudaIpcTensorTransportProxy)
|
||||
proxy.consumer_count = 2
|
||||
proxy.reconstruct_on_target_device = Mock(return_value=torch.randn(1, 2))
|
||||
item = MultimodalDataItem(modality=Modality.IMAGE, feature=proxy)
|
||||
|
||||
item.reconstruct(0, ipc_consumer_count=8)
|
||||
|
||||
proxy.reconstruct_on_target_device.assert_called_once_with(0, consumer_count=2)
|
||||
|
||||
|
||||
def test_kimi_lazy_vmm_cache_hit_uses_proxy_consumer_count():
|
||||
proxy = CudaIpcTensorTransportProxy.__new__(CudaIpcTensorTransportProxy)
|
||||
proxy.consumer_count = 2
|
||||
proxy.acknowledge_consumption = Mock()
|
||||
item = MultimodalDataItem(modality=Modality.IMAGE, feature=proxy)
|
||||
|
||||
item.acknowledge_deferred_cuda_ipc_feature(consumer_count=8)
|
||||
|
||||
proxy.acknowledge_consumption.assert_called_once_with(2)
|
||||
|
||||
|
||||
class _Tokenizer:
|
||||
def encode(self, text, allowed_special=None):
|
||||
tokens = {
|
||||
@@ -675,6 +697,7 @@ def test_kimi_k3_rejects_silently_dropped_images():
|
||||
|
||||
def test_kimi_k3_uses_token_ids_to_preserve_media_boundaries():
|
||||
processor = object.__new__(KimiK3ImageProcessor)
|
||||
processor.mm_feature_transport = "cpu"
|
||||
processor.mm_tokens = SimpleNamespace(image_token_id=99)
|
||||
processor.fast_load_mm_data = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
|
||||
@@ -181,6 +181,16 @@ class TestMultimodalFeatureTransport(CustomTestCase):
|
||||
|
||||
self.assertIn("deprecated", logs.output[0])
|
||||
|
||||
def test_legacy_keep_flag_rejects_explicit_cuda_vmm(self):
|
||||
server_args = ServerArgs(
|
||||
model_path="dummy",
|
||||
keep_mm_feature_on_device=True,
|
||||
mm_feature_transport="cuda_vmm",
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "conflicts.*cuda_vmm"):
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
def test_explicit_cpu_overrides_legacy_environment(self, _mock_is_cuda):
|
||||
server_args = ServerArgs(model_path="dummy", mm_feature_transport="cpu")
|
||||
@@ -231,6 +241,91 @@ class TestMultimodalFeatureTransport(CustomTestCase):
|
||||
|
||||
self.assertIn("auto-resolved to cuda_ipc", "\n".join(logs.output))
|
||||
|
||||
@patch("sglang.srt.server_args.os.path.exists", return_value=True)
|
||||
@patch("sglang.srt.server_args.is_mnnvl_fabric_device", return_value=True)
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
@patch(
|
||||
"sglang.srt.model_loader.utils.supports_cuda_vmm_feature_transport",
|
||||
return_value=True,
|
||||
)
|
||||
def test_default_transport_is_cuda_vmm_for_supported_multinode_mnnvl(
|
||||
self,
|
||||
_mock_supports_cuda_vmm,
|
||||
_mock_is_cuda,
|
||||
_mock_is_mnnvl,
|
||||
_mock_path_exists,
|
||||
):
|
||||
server_args = ServerArgs(model_path="dummy", nnodes=2)
|
||||
self._set_model_type(server_args, is_multimodal=True)
|
||||
|
||||
with patch.dict(os.environ, {}, clear=False):
|
||||
envs.SGLANG_USE_CUDA_IPC_TRANSPORT.clear()
|
||||
with self.assertLogs(server_args_module.logger, level="INFO") as logs:
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
self.assertEqual(server_args.mm_feature_transport, "cuda_vmm")
|
||||
self.assertFalse(envs.SGLANG_USE_CUDA_IPC_TRANSPORT.get())
|
||||
|
||||
output = "\n".join(logs.output)
|
||||
self.assertIn("auto-resolved to cuda_vmm", output)
|
||||
self.assertIn("CUDA FABRIC", output)
|
||||
|
||||
@patch("sglang.srt.server_args.os.path.exists", return_value=True)
|
||||
@patch("sglang.srt.server_args.is_mnnvl_fabric_device", return_value=True)
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
@patch(
|
||||
"sglang.srt.model_loader.utils.supports_cuda_vmm_feature_transport",
|
||||
return_value=False,
|
||||
)
|
||||
def test_default_transport_is_cpu_for_unsupported_multinode_model(
|
||||
self,
|
||||
_mock_supports_cuda_vmm,
|
||||
_mock_is_cuda,
|
||||
_mock_is_mnnvl,
|
||||
_mock_path_exists,
|
||||
):
|
||||
server_args = ServerArgs(model_path="dummy", nnodes=2)
|
||||
self._set_model_type(server_args, is_multimodal=True)
|
||||
|
||||
with self.assertLogs(server_args_module.logger, level="INFO") as logs:
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
self.assertEqual(server_args.mm_feature_transport, "cpu")
|
||||
self.assertIn("has not opted into CUDA VMM", "\n".join(logs.output))
|
||||
|
||||
@patch("sglang.srt.server_args.os.path.exists", return_value=False)
|
||||
@patch("sglang.srt.server_args.is_mnnvl_fabric_device", return_value=True)
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
def test_default_transport_is_cpu_without_imex_channel(
|
||||
self, _mock_is_cuda, _mock_is_mnnvl, _mock_path_exists
|
||||
):
|
||||
server_args = ServerArgs(model_path="dummy", nnodes=2)
|
||||
self._set_model_type(server_args, is_multimodal=True)
|
||||
|
||||
with patch.dict(os.environ, {}, clear=False):
|
||||
envs.SGLANG_USE_CUDA_IPC_TRANSPORT.clear()
|
||||
with self.assertLogs(server_args_module.logger, level="INFO") as logs:
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
self.assertEqual(server_args.mm_feature_transport, "cpu")
|
||||
|
||||
self.assertIn("no IMEX channel", "\n".join(logs.output))
|
||||
|
||||
@patch("sglang.srt.server_args.is_mnnvl_fabric_device", return_value=False)
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
def test_default_transport_is_cpu_for_multinode_non_mnnvl(
|
||||
self, _mock_is_cuda, _mock_is_mnnvl
|
||||
):
|
||||
server_args = ServerArgs(model_path="dummy", nnodes=2)
|
||||
self._set_model_type(server_args, is_multimodal=True)
|
||||
|
||||
with patch.dict(os.environ, {}, clear=False):
|
||||
envs.SGLANG_USE_CUDA_IPC_TRANSPORT.clear()
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
self.assertEqual(server_args.mm_feature_transport, "cpu")
|
||||
self.assertFalse(envs.SGLANG_USE_CUDA_IPC_TRANSPORT.get())
|
||||
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
def test_default_transport_is_cuda_ipc_for_language_only_model(self, _mock_is_cuda):
|
||||
server_args = ServerArgs(model_path="dummy", language_only=True)
|
||||
@@ -259,6 +354,31 @@ class TestMultimodalFeatureTransport(CustomTestCase):
|
||||
with self.assertRaisesRegex(ValueError, "single node"):
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=True)
|
||||
def test_cuda_vmm_is_explicit_and_uses_shared_budget(self, _mock_is_cuda):
|
||||
server_args = ServerArgs(
|
||||
model_path="dummy",
|
||||
mm_feature_transport="cuda_vmm",
|
||||
nnodes=2,
|
||||
tokenizer_worker_num=2,
|
||||
)
|
||||
|
||||
with (
|
||||
patch.dict(os.environ, {"SGLANG_USE_CUDA_IPC_TRANSPORT": "1"}),
|
||||
envs.SGLANG_MM_FEATURE_CACHE_MB.override(256),
|
||||
):
|
||||
with self.assertLogs(server_args_module.logger, level="INFO") as logs:
|
||||
server_args._handle_multimodal_feature_transport()
|
||||
|
||||
self.assertEqual(server_args.mm_feature_transport, "cuda_vmm")
|
||||
self.assertFalse(envs.SGLANG_USE_CUDA_IPC_TRANSPORT.get())
|
||||
|
||||
output = "\n".join(logs.output)
|
||||
self.assertIn("CUDA FABRIC", output)
|
||||
self.assertIn("256 MiB", output)
|
||||
self.assertIn("2 tokenizer worker", output)
|
||||
self.assertIn("falls back to inline CPU", output)
|
||||
|
||||
@patch("sglang.srt.server_args.is_cuda", return_value=False)
|
||||
def test_cuda_vmm_rejects_non_nvidia_platforms(self, _mock_is_cuda):
|
||||
server_args = ServerArgs(model_path="dummy", mm_feature_transport="cuda_vmm")
|
||||
|
||||
Reference in New Issue
Block a user