Skip to content

Commit 7a078e8

Browse files
Liziqi-77NSDie
authored andcommitted
[Feat](Mooncake) Supports multiple input suffixes for global_segment_size (vllm-project#3690)
### What this PR does / why we need it? - global_segment_size and local_buffer_size use constants for unified management. - Newly added support for input formats ending with GB, MB, KB, and B, while being compatible with existing input methods. ### Does this PR introduce _any_ user-facing change? - Users can use new input methods - The documentation has also been modified ### How was this patch tested? - vLLM version: v0.11.0 - vLLM main: vllm-project/vllm@83f478b --------- Signed-off-by: 李子琦 <[email protected]> Signed-off-by: nsdie <[email protected]>
1 parent d214b82 commit 7a078e8

File tree

3 files changed

+158
-6
lines changed

3 files changed

+158
-6
lines changed

docs/source/user_guide/feature_guide/kv_pool_mooncake.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Software:
66
* Python >= 3.9, < 3.12
77
* CANN >= 8.3.rc1
8-
* PyTorch == 2.7.1, torch-npu == 2.7.1
8+
* PyTorch >= 2.7.1, torch-npu >= 2.7.1.dev20250724
99
* vLLM:main branch
1010
* vLLM-Ascend:main branch
1111
* Mooncake:main branch
@@ -41,7 +41,7 @@ The environment variable **MOONCAKE_CONFIG_PATH** is configured to the full path
4141
"use_ascend_direct": true,
4242
"alloc_in_same_node": true,
4343
"master_server_address": "xx.xx.xx.xx:50088",
44-
"global_segment_size": 30000000000
44+
"global_segment_size": "1GB" (1024MB/1048576KB/1073741824B/1073741824)
4545
}
4646
```
4747

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import unittest
2+
3+
from vllm_ascend.distributed.mooncake.config_data import (
4+
_convert_to_bytes, _parse_global_segment_size)
5+
6+
7+
class TestParseGlobalSegmentSize(unittest.TestCase):
8+
9+
def test_int_input(self):
10+
self.assertEqual(_parse_global_segment_size(1024), 1024)
11+
self.assertEqual(_parse_global_segment_size(0), 0)
12+
13+
def test_gb_unit(self):
14+
self.assertEqual(_parse_global_segment_size("2GB"), 2 * 1024**3)
15+
self.assertEqual(_parse_global_segment_size("1.5GB"),
16+
int(1.5 * 1024**3))
17+
self.assertEqual(_parse_global_segment_size(" 2 GB "), 2 * 1024**3)
18+
19+
def test_gb_unit_edge_cases(self):
20+
with self.assertRaises(ValueError):
21+
_parse_global_segment_size("GB")
22+
with self.assertRaises(ValueError):
23+
_parse_global_segment_size("abcGB")
24+
25+
def test_mb_unit(self):
26+
self.assertEqual(_parse_global_segment_size("512MB"), 512 * 1024**2)
27+
self.assertEqual(_parse_global_segment_size("0.5MB"),
28+
int(0.5 * 1024**2))
29+
self.assertEqual(_parse_global_segment_size("1024MB"), 1024 * 1024**2)
30+
31+
def test_kb_unit(self):
32+
self.assertEqual(_parse_global_segment_size("256KB"), 256 * 1024)
33+
self.assertEqual(_parse_global_segment_size("1.25KB"),
34+
int(1.25 * 1024))
35+
36+
def test_b_unit(self):
37+
self.assertEqual(_parse_global_segment_size("4096B"), 4096)
38+
self.assertEqual(_parse_global_segment_size("1024b"), 1024)
39+
40+
def test_no_unit(self):
41+
self.assertEqual(_parse_global_segment_size("2048"), 2048)
42+
self.assertEqual(_parse_global_segment_size("0"), 0)
43+
44+
def test_non_string_non_int_input(self):
45+
self.assertEqual(_parse_global_segment_size(2048.0), 2048)
46+
self.assertEqual(_parse_global_segment_size(True), 1)
47+
48+
with self.assertRaises(TypeError):
49+
_parse_global_segment_size(None)
50+
51+
with self.assertRaises(TypeError):
52+
_parse_global_segment_size({"size": 1024})
53+
54+
55+
class TestConvertToBytes(unittest.TestCase):
56+
57+
def test_valid_conversion(self):
58+
self.assertEqual(_convert_to_bytes("10", 1, "10"), 10)
59+
self.assertEqual(_convert_to_bytes("1.5", 1024, "1.5KB"),
60+
int(1.5 * 1024))
61+
self.assertEqual(_convert_to_bytes("0", 1024**3, "0GB"), 0)
62+
63+
def test_invalid_numbers(self):
64+
with self.assertRaises(ValueError):
65+
_convert_to_bytes("abc", 1, "abc")
66+
67+
with self.assertRaises(ValueError):
68+
_convert_to_bytes("1.2.3", 1024, "1.2.3KB")

vllm_ascend/distributed/mooncake/config_data.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import hashlib
33
import json
44
import os
5+
import re
56
from dataclasses import dataclass
67
from typing import Iterable, List, Optional, Tuple, Union
78

@@ -11,6 +12,9 @@
1112
from vllm.utils import cdiv, logger
1213
from vllm.v1.core.sched.output import NewRequestData
1314

15+
DEFAULT_GLOBAL_SEGMENT_SIZE = 3355443200 # 3.125 GiB
16+
DEFAULT_LOCAL_BUFFER_SIZE = 1073741824 # 1.0 GiB
17+
1418

1519
@dataclass
1620
class MooncakeEngineMetadata:
@@ -419,7 +423,7 @@ class LasyerMultiBlockReqMeta:
419423
class MooncakeStoreConfig:
420424
local_hostname: str
421425
metadata_server: str
422-
global_segment_size: int
426+
global_segment_size: Union[int, str]
423427
local_buffer_size: int
424428
protocol: str
425429
device_name: str
@@ -433,8 +437,11 @@ def from_file(file_path: str) -> "MooncakeStoreConfig":
433437
return MooncakeStoreConfig(
434438
local_hostname=config.get("local_hostname"),
435439
metadata_server=config.get("metadata_server"),
436-
global_segment_size=config.get("global_segment_size", 3355443200),
437-
local_buffer_size=config.get("local_buffer_size", 1073741824),
440+
global_segment_size=_parse_global_segment_size(
441+
config.get("global_segment_size",
442+
DEFAULT_GLOBAL_SEGMENT_SIZE)),
443+
local_buffer_size=(config.get("local_buffer_size",
444+
DEFAULT_LOCAL_BUFFER_SIZE)),
438445
protocol=config.get("protocol", "tcp"),
439446
device_name=config.get("device_name", ""),
440447
master_server_address=config.get("master_server_address"),
@@ -446,4 +453,81 @@ def load_from_env() -> "MooncakeStoreConfig":
446453
if not config_path:
447454
raise ValueError(
448455
"The environment variable 'MOONCAKE_CONFIG_PATH' is not set.")
449-
return MooncakeStoreConfig.from_file(config_path)
456+
return MooncakeStoreConfig.from_file(config_path)
457+
458+
459+
def _parse_global_segment_size(value) -> int:
460+
"""
461+
Parse storage size strings with support for units: GB, MB, KB, B
462+
463+
Args:
464+
value: Input value (int, str, or other convertible types)
465+
466+
Returns:
467+
int: Size in bytes
468+
469+
Raises:
470+
ValueError: For invalid format, missing number, or negative values
471+
TypeError: For unsupported input types
472+
"""
473+
474+
if isinstance(value, int):
475+
return value
476+
elif not isinstance(value, str):
477+
try:
478+
return int(value)
479+
except (TypeError, ValueError) as e:
480+
raise TypeError(
481+
f"Unsupported type for global_segment_size: {type(value)}"
482+
) from e
483+
484+
cleaned_input = value.strip().lower()
485+
if not cleaned_input:
486+
raise ValueError("global segment size cannot be empty.")
487+
488+
UNIT_MULTIPLIERS = {
489+
'gb': 1024**3, # 1 GB = 1024^3 bytes
490+
'mb': 1024**2, # 1 MB = 1024^2 bytes
491+
'kb': 1024, # 1 KB = 1024 bytes
492+
'b': 1 # 1 B = 1 byte
493+
}
494+
pattern = r'^\s*([\d.]+)\s*(gb|mb|kb|b)?\s*$'
495+
match = re.match(pattern, cleaned_input)
496+
497+
if not match:
498+
raise ValueError(f"Invalid format: '{value}'")
499+
500+
number_str = match.group(1)
501+
unit = match.group(2) or 'b'
502+
503+
multiplier = UNIT_MULTIPLIERS[unit]
504+
return _convert_to_bytes(number_str, multiplier, value)
505+
506+
507+
def _convert_to_bytes(number_str: str, multiplier: int,
508+
original_input: str) -> int:
509+
"""
510+
Convert numeric string to byte count
511+
512+
Args:
513+
number_str: Numeric portion of input
514+
multiplier: Unit conversion factor
515+
original_input: Original input string (for error messages)
516+
517+
Returns:
518+
int: Byte count
519+
520+
Raises:
521+
ValueError: For invalid numbers or negative results
522+
"""
523+
try:
524+
numeric_value = float(number_str)
525+
except ValueError:
526+
raise ValueError(
527+
f"Invalid numeric value '{number_str}' in: '{original_input}'")
528+
# Calculate byte count
529+
try:
530+
byte_count = int(numeric_value * multiplier)
531+
except OverflowError:
532+
raise ValueError(f"Storage size too large: '{original_input}'")
533+
return byte_count

0 commit comments

Comments
 (0)