main
py 130 lines 4.55 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import pytest
5
6 from vllm.device_allocator import alloc_conf
7
8 pytestmark = pytest.mark.cpu_test
9
10 ES = alloc_conf.EXPANDABLE_SEGMENTS
11
12
13 @pytest.mark.parametrize(
14 "environ,expected",
15 [
16 ({}, False),
17 ({"PYTORCH_ALLOC_CONF": "expandable_segments:True"}, True),
18 ({"PYTORCH_ALLOC_CONF": "expandable_segments:true"}, True),
19 ({"PYTORCH_ALLOC_CONF": "expandable_segments:1"}, True),
20 ({"PYTORCH_ALLOC_CONF": "expandable_segments:False"}, False),
21 ({"PYTORCH_CUDA_ALLOC_CONF": "expandable_segments:True"}, True),
22 # ROCm users set this one; it was previously not looked at.
23 ({"PYTORCH_HIP_ALLOC_CONF": "expandable_segments:True"}, True),
24 (
25 {"PYTORCH_ALLOC_CONF": "max_split_size_mb:512,expandable_segments:True"},
26 True,
27 ),
28 ({"PYTORCH_ALLOC_CONF": "max_split_size_mb:512"}, False),
29 ({"PYTORCH_ALLOC_CONF": "not_expandable_segments_really:True"}, False),
30 ({"PYTORCH_ALLOC_CONF": ""}, False),
31 # torch's precedence: the accelerator-agnostic variable wins.
32 (
33 {
34 "PYTORCH_ALLOC_CONF": "expandable_segments:False",
35 "PYTORCH_CUDA_ALLOC_CONF": "expandable_segments:True",
36 },
37 False,
38 ),
39 ],
40 )
41 def test_expandable_segments_from_env(environ, expected):
42 assert alloc_conf.expandable_segments_enabled_from_env(environ) is expected
43
44
45 @pytest.mark.parametrize(
46 "conf,key,expected",
47 [
48 ("", ES, False),
49 ("expandable_segments:True", ES, True),
50 ("expandable_segments:TRUE", ES, True),
51 ("expandable_segments:1", ES, True),
52 ("expandable_segments:0", ES, False),
53 (" expandable_segments : True ", ES, True),
54 ("max_split_size_mb:512", ES, False),
55 ("max_split_size_mb:512", "max_split_size_mb", False),
56 ("garbage_collection_threshold:0.9", ES, False),
57 ],
58 )
59 def test_conf_flag_enabled(conf, key, expected):
60 assert alloc_conf.conf_flag_enabled(conf, key) is expected
61
62
63 @pytest.mark.parametrize(
64 "conf,enabled,expected",
65 [
66 ("", False, "expandable_segments:False"),
67 ("expandable_segments:True", False, "expandable_segments:False"),
68 ("expandable_segments:False", True, "expandable_segments:True"),
69 (
70 "max_split_size_mb:512,expandable_segments:True",
71 False,
72 "max_split_size_mb:512,expandable_segments:False",
73 ),
74 (
75 "garbage_collection_threshold:0.9,max_split_size_mb:512",
76 False,
77 "garbage_collection_threshold:0.9,max_split_size_mb:512,"
78 "expandable_segments:False",
79 ),
80 ],
81 )
82 def test_with_conf_flag_preserves_other_fields(conf, enabled, expected):
83 assert alloc_conf.with_conf_flag(conf, ES, enabled) == expected
84
85
86 def test_with_conf_flag_round_trips():
87 """The regression this guards: torch resets every option absent from the
88 string it is handed, so a one-field write drops the rest permanently."""
89 conf = (
90 "max_split_size_mb:512,expandable_segments:True,"
91 "garbage_collection_threshold:0.9"
92 )
93 off = alloc_conf.with_conf_flag(conf, ES, False)
94 assert "max_split_size_mb:512" in off
95 assert "garbage_collection_threshold:0.9" in off
96 assert alloc_conf.with_conf_flag(off, ES, True) == conf
97
98
99 def test_alloc_conf_from_env_precedence():
100 assert (
101 alloc_conf.alloc_conf_from_env(
102 {
103 "PYTORCH_CUDA_ALLOC_CONF": "max_split_size_mb:256",
104 "PYTORCH_HIP_ALLOC_CONF": "max_split_size_mb:128",
105 }
106 )
107 == "max_split_size_mb:256"
108 )
109 assert alloc_conf.alloc_conf_from_env({}) == ""
110
111
112 def test_current_alloc_conf_prefers_live_state(monkeypatch):
113 monkeypatch.setattr(alloc_conf, "live_alloc_conf", lambda: "max_split_size_mb:64")
114 monkeypatch.setattr(alloc_conf, "alloc_conf_from_env", lambda: "stale:True")
115 assert alloc_conf.current_alloc_conf() == "max_split_size_mb:64"
116
117 monkeypatch.setattr(alloc_conf, "live_alloc_conf", lambda: None)
118 assert alloc_conf.current_alloc_conf() == "stale:True"
119
120
121 def test_expandable_segments_enabled_returns_none_when_unreadable(monkeypatch):
122 """'cannot read' must stay distinct from 'disabled'."""
123
124 class _NoSnapshot:
125 @staticmethod
126 def _snapshot():
127 raise RuntimeError("no allocator here")
128
129 monkeypatch.setattr(alloc_conf.torch.cuda, "memory", _NoSnapshot)
130 assert alloc_conf.expandable_segments_enabled() is None