chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from vllm.multimodal.gpu_ipc_memory import (
|
||||
MultiModalGPUMemoryPool,
|
||||
get_mm_gpu_ipc_pool,
|
||||
maybe_init_mm_gpu_ipc_pool,
|
||||
set_mm_gpu_ipc_pool,
|
||||
)
|
||||
from vllm.utils.mem_constants import GiB_bytes
|
||||
|
||||
|
||||
def test_acquire_release_accounting():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
lease = pool.acquire(40)
|
||||
assert pool.available_bytes == 60
|
||||
|
||||
lease.release()
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_acquire_too_large_raises():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
with pytest.raises(ValueError):
|
||||
pool.acquire(101)
|
||||
# Nothing should have been reserved.
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_negative_acquire_raises():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
with pytest.raises(ValueError):
|
||||
pool.acquire(-1)
|
||||
|
||||
|
||||
def test_double_release_is_noop():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
lease = pool.acquire(50)
|
||||
lease.release()
|
||||
assert pool.available_bytes == 100
|
||||
# Releasing again must not inflate the pool past its capacity.
|
||||
lease.release()
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_context_manager_releases_on_exception():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
with pytest.raises(RuntimeError), pool.acquire(50):
|
||||
assert pool.available_bytes == 50
|
||||
raise RuntimeError("boom")
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_acquire_blocks_until_release():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
first = pool.acquire(80)
|
||||
|
||||
acquired = threading.Event()
|
||||
|
||||
def waiter():
|
||||
# Needs 50 bytes but only 20 are free; must block until `first`
|
||||
# is released.
|
||||
with pool.acquire(50):
|
||||
acquired.set()
|
||||
|
||||
t = threading.Thread(target=waiter)
|
||||
t.start()
|
||||
|
||||
# The waiter cannot proceed yet.
|
||||
assert not acquired.wait(timeout=0.2)
|
||||
|
||||
# Releasing the first lease frees enough budget to unblock the waiter.
|
||||
first.release()
|
||||
assert acquired.wait(timeout=2.0)
|
||||
t.join(timeout=2.0)
|
||||
assert not t.is_alive()
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_concurrent_acquires_serialize():
|
||||
pool = MultiModalGPUMemoryPool(total_bytes=100)
|
||||
# Each task needs 60 bytes, so only one can hold the budget at a time.
|
||||
in_section = []
|
||||
max_concurrent = 0
|
||||
lock = threading.Lock()
|
||||
|
||||
def task():
|
||||
nonlocal max_concurrent
|
||||
with pool.acquire(60):
|
||||
with lock:
|
||||
in_section.append(1)
|
||||
max_concurrent = max(max_concurrent, len(in_section))
|
||||
time.sleep(0.05)
|
||||
with lock:
|
||||
in_section.pop()
|
||||
|
||||
threads = [threading.Thread(target=task) for _ in range(4)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join(timeout=5.0)
|
||||
assert not t.is_alive()
|
||||
|
||||
assert max_concurrent == 1
|
||||
assert pool.available_bytes == 100
|
||||
|
||||
|
||||
def test_zero_total_bytes_rejected():
|
||||
with pytest.raises(ValueError):
|
||||
MultiModalGPUMemoryPool(total_bytes=0)
|
||||
|
||||
|
||||
def test_global_pool_accessor():
|
||||
try:
|
||||
assert maybe_init_mm_gpu_ipc_pool(0) is None
|
||||
assert get_mm_gpu_ipc_pool() is None
|
||||
|
||||
pool = maybe_init_mm_gpu_ipc_pool(2)
|
||||
assert pool is not None
|
||||
assert get_mm_gpu_ipc_pool() is pool
|
||||
assert pool.total_bytes == 2 * GiB_bytes
|
||||
finally:
|
||||
set_mm_gpu_ipc_pool(None)
|
||||
|
||||
|
||||
def test_global_pool_splits_budget_across_api_processes():
|
||||
try:
|
||||
pool = maybe_init_mm_gpu_ipc_pool(2, api_process_count=4)
|
||||
assert pool is not None
|
||||
assert get_mm_gpu_ipc_pool() is pool
|
||||
assert pool.total_bytes == GiB_bytes // 2
|
||||
finally:
|
||||
set_mm_gpu_ipc_pool(None)
|
||||
|
||||
|
||||
def test_global_pool_rejects_invalid_api_process_count():
|
||||
with pytest.raises(ValueError):
|
||||
maybe_init_mm_gpu_ipc_pool(2, api_process_count=0)
|
||||
Reference in New Issue
Block a user