A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 04:45 UTC
[Core] Do not copy array during hashing (#19484)
Signed-off-by: Lukas Geiger <lukas.geiger94@gmail.com>
Lukas Geiger committed
Jun 18, 2025 at 23:36 UTC
3b523e38d96ab5a29e9d157f10891ca953881964
3 files changed
+25
-11
tests/multimodal/test_hasher.py
+12
@@ -60,3 +60,15 @@ def test_hash_collision_array_shape():
60
61
hasher = MultiModalHasher
62
assert hasher.hash_kwargs(data=arr1) != hasher.hash_kwargs(data=arr2)
63
+
64
+
65
+def test_hash_non_contiguous_array():
66
+ arr = np.arange(24).reshape(4, 6).T
67
+ assert not arr.flags.c_contiguous
68
+
69
+ arr_c = np.ascontiguousarray(arr)
70
+ assert arr_c.flags.c_contiguous
71
+
72
+ hasher = MultiModalHasher
73
+ # Both should be hashable and produce the same hashes
74
+ assert hasher.hash_kwargs(data=arr) == hasher.hash_kwargs(data=arr_c)
vllm/multimodal/hasher.py
+12
-10
@@ -3,6 +3,7 @@
3
4
import pickle
5
from collections.abc import Iterable, Mapping
6
+from typing import Union
7
8
import numpy as np
9
import torch
@@ -23,11 +24,11 @@ A dictionary containing hashes for items in each modality.
24
class MultiModalHasher:
25
26
@classmethod
26
- def serialize_item(cls, obj: object) -> bytes:
27
+ def serialize_item(cls, obj: object) -> Union[bytes, memoryview]:
28
# Simple cases
29
if isinstance(obj, str):
30
return obj.encode("utf-8")
30
- if isinstance(obj, bytes):
31
+ if isinstance(obj, (bytes, memoryview)):
32
return obj
33
if isinstance(obj, (int, float)):
34
return np.array(obj).tobytes()
@@ -38,12 +39,13 @@ class MultiModalHasher:
39
if isinstance(obj, torch.Tensor):
40
return cls.item_to_bytes("tensor", obj.numpy())
41
if isinstance(obj, np.ndarray):
41
- return cls.item_to_bytes(
42
- "ndarray", {
43
- "dtype": obj.dtype.str,
44
- "shape": obj.shape,
45
- "data": obj.tobytes(),
46
- })
42
+ # If the array is non-contiguous, we need to copy it first
43
+ arr_data = obj.data if obj.flags.c_contiguous else obj.tobytes()
44
+ return cls.item_to_bytes("ndarray", {
45
+ "dtype": obj.dtype.str,
46
+ "shape": obj.shape,
47
+ "data": arr_data,
48
+ })
49
50
logger.warning(
51
"No serialization method found for %s. "
@@ -64,7 +66,7 @@ class MultiModalHasher:
66
cls,
67
key: str,
68
obj: object,
67
- ) -> Iterable[tuple[bytes, bytes]]:
69
+ ) -> Iterable[tuple[bytes, Union[bytes, memoryview]]]:
70
# Recursive cases
71
if isinstance(obj, (list, tuple)):
72
for i, elem in enumerate(obj):
@@ -73,7 +75,7 @@ class MultiModalHasher:
75
for k, v in obj.items():
76
yield from cls.iter_item_to_bytes(f"{key}.{k}", v)
77
else:
76
- key_bytes = cls.serialize_item(key)
78
+ key_bytes = key.encode("utf-8")
79
value_bytes = cls.serialize_item(obj)
80
yield key_bytes, value_bytes
81
vllm/v1/serial_utils.py
+1
-1
@@ -140,7 +140,7 @@ class MsgpackEncoder:
140
) -> tuple[str, tuple[int, ...], Union[int, memoryview]]:
141
assert self.aux_buffers is not None
142
# If the array is non-contiguous, we need to copy it first
143
- arr_data = obj.data if obj.data.c_contiguous else obj.tobytes()
143
+ arr_data = obj.data if obj.flags.c_contiguous else obj.tobytes()
144
if not obj.shape or obj.nbytes < self.size_threshold:
145
# Encode small arrays and scalars inline. Using this extension type
146
# ensures we can avoid copying when decoding.