@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 3b523e38d9
Fork 0

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.