diff --git a/docs/docs/references/environment_variables.mdx b/docs/docs/references/environment_variables.mdx index 567211913..574d3541d 100644 --- a/docs/docs/references/environment_variables.mdx +++ b/docs/docs/references/environment_variables.mdx @@ -2158,13 +2158,13 @@ SGLang supports various environment variables that can be used to configure its SGLANG_NUMA_BIND_V2 - Use the v2 NUMA binding implementation. + Select the NUMA binding implementation. true uses a pre-launch numactl wrapper; false binds in the worker process. This variable does not enable or disable NUMA binding. true SGLANG_AUTO_NUMA_BIND - Automatically bind processes to NUMA nodes. - false + Automatically detect each NVIDIA GPU's local NUMA node and bind its worker when --numa-node is not specified. Set to false to leave workers unbound; an explicit --numa-node takes precedence. + true SGLANG_CRASH_ON_NUMA_BIND_FAILURE diff --git a/python/sglang/srt/environ.py b/python/sglang/srt/environ.py index 5390e8583..ac474a024 100644 --- a/python/sglang/srt/environ.py +++ b/python/sglang/srt/environ.py @@ -1053,7 +1053,7 @@ class Envs: # Numa SGLANG_NUMA_BIND_V2 = EnvBool(True) - SGLANG_AUTO_NUMA_BIND = EnvBool(False) + SGLANG_AUTO_NUMA_BIND = EnvBool(True) SGLANG_CRASH_ON_NUMA_BIND_FAILURE = EnvBool(False) # Metrics diff --git a/python/sglang/srt/utils/numa_utils.py b/python/sglang/srt/utils/numa_utils.py index 45959b746..d9f765c69 100644 --- a/python/sglang/srt/utils/numa_utils.py +++ b/python/sglang/srt/utils/numa_utils.py @@ -129,6 +129,8 @@ def get_numa_node_if_available(server_args: ServerArgs, gpu_id: int) -> Optional """ if server_args.numa_node is not None: return server_args.numa_node[gpu_id] + if not envs.SGLANG_AUTO_NUMA_BIND.get(): + return None if _is_numa_available(): queried_numa_node = _query_numa_node_for_gpu(gpu_id) if len(queried_numa_node) == 0: diff --git a/test/registered/utils/test_numa_utils.py b/test/registered/utils/test_numa_utils.py index c59c34a44..d9f8261f8 100644 --- a/test/registered/utils/test_numa_utils.py +++ b/test/registered/utils/test_numa_utils.py @@ -4,6 +4,7 @@ import unittest from contextlib import ExitStack from unittest.mock import MagicMock, patch +from sglang.srt.environ import envs from sglang.srt.utils.numa_utils import ( _handle_numa_bind_failure, _is_numa_available, @@ -178,13 +179,34 @@ class TestGetNumaNodeIfAvailable(unittest.TestCase): args.numa_node = numa_node return args - def test_returns_explicit_numa_node_from_server_args(self): + def test_auto_numa_bind_enabled_by_default(self): + with patch.dict(os.environ, {}, clear=True): + self.assertTrue(envs.SGLANG_AUTO_NUMA_BIND.get()) + + @patch.dict(os.environ, {"SGLANG_AUTO_NUMA_BIND": "0"}) + def test_returns_explicit_numa_node_when_auto_bind_disabled(self): args = self._make_server_args(numa_node=[2, 3, 0, 1]) self.assertEqual(get_numa_node_if_available(args, 0), 2) self.assertEqual(get_numa_node_if_available(args, 1), 3) self.assertEqual(get_numa_node_if_available(args, 2), 0) self.assertEqual(get_numa_node_if_available(args, 3), 1) + @patch("sglang.srt.utils.numa_utils._query_numa_node_for_gpu") + @patch("sglang.srt.utils.numa_utils._is_numa_available") + def test_auto_bind_disabled_skips_numa_detection(self, mock_avail, mock_query): + args = self._make_server_args(numa_node=None) + for bind_v2 in ("0", "1"): + with self.subTest(bind_v2=bind_v2), patch.dict( + os.environ, + { + "SGLANG_AUTO_NUMA_BIND": "0", + "SGLANG_NUMA_BIND_V2": bind_v2, + }, + ): + self.assertIsNone(get_numa_node_if_available(args, 0)) + mock_avail.assert_not_called() + mock_query.assert_not_called() + @patch("sglang.srt.utils.numa_utils._is_numa_available", return_value=False) def test_returns_none_when_numa_not_available(self, _mock_avail): args = self._make_server_args(numa_node=None) @@ -196,6 +218,7 @@ class TestGetNumaNodeIfAvailable(unittest.TestCase): args = self._make_server_args(numa_node=None) self.assertIsNone(get_numa_node_if_available(args, 0)) + @patch.dict(os.environ, {"SGLANG_AUTO_NUMA_BIND": "1"}) @patch("sglang.srt.utils.numa_utils._query_numa_node_for_gpu", return_value=[1]) @patch("sglang.srt.utils.numa_utils._is_numa_available", return_value=True) def test_returns_queried_single_node(self, _mock_avail, _mock_gpu):