Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

121 lines
4.6 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Unit tests for the EPD vision-embedding cache.
These exercise pure Python logic (no GPU, no model, no torch import): the
bytes-bounded LRU and the two-tier (L1 VRAM + L2 host DRAM) cache that let
duplicate images skip the vision tower.
"""
from __future__ import annotations
from tokenspeed.runtime.cache.embedding_cache import (
EmbeddingCache,
TieredEmbeddingCache,
)
# --------------------------------------------------------------------------- #
# EmbeddingCache
# --------------------------------------------------------------------------- #
def test_cache_hit_miss_and_counters():
c = EmbeddingCache(capacity_bytes=1000)
assert c.get("a") is None
assert c.misses == 1
c.put("a", "embA", nbytes=100)
assert c.get("a") == "embA"
assert c.hits == 1
assert len(c) == 1
assert c.current_bytes == 100
def test_cache_lru_eviction_by_bytes():
c = EmbeddingCache(capacity_bytes=250)
c.put("a", "A", 100)
c.put("b", "B", 100)
# Touch "a" so "b" becomes the LRU victim.
assert c.get("a") == "A"
c.put("c", "C", 100) # 300 > 250 -> evict LRU ("b")
assert "a" in c
assert "c" in c
assert "b" not in c
assert c.current_bytes == 200
def test_cache_skips_item_larger_than_capacity():
c = EmbeddingCache(capacity_bytes=50)
c.put("a", "A", 10)
c.put("big", "B", 100) # too big to ever retain; must not evict "a"
assert "big" not in c
assert "a" in c
assert c.current_bytes == 10
# --------------------------------------------------------------------------- #
# TieredEmbeddingCache (L1 VRAM + L2 host DRAM)
#
# Inject identity / tagging copy functions so the tiering logic is exercised
# without a GPU (the production defaults call .cpu()/.to(device) on real
# tensors). Sizes are tiny so eviction is easy to drive.
# --------------------------------------------------------------------------- #
def _tiered(l1, l2, *, to_host=lambda v: v, to_device=lambda v: v):
return TieredEmbeddingCache(l1, l2, to_host=to_host, to_device=to_device)
def test_tiered_demote_on_l1_eviction():
# L1 holds one 100B entry; the second put evicts the first down to L2.
tc = _tiered(150, 1000)
tc.put("a", "A", 100)
tc.put("b", "B", 100) # 200 > 150 -> "a" demoted to L2
assert "a" not in tc.l1 and "a" in tc.l2 # exclusive
assert "b" in tc.l1 and "b" not in tc.l2
assert tc.demotions == 1
# Aggregate views span both tiers.
assert "a" in tc and "b" in tc # TieredEmbeddingCache.__contains__
assert len(tc) == 2 # __len__ = len(l1) + len(l2)
def test_tiered_promote_on_l2_hit_is_exclusive():
tc = _tiered(150, 1000)
tc.put("a", "A", 100)
tc.put("b", "B", 100) # "a" -> L2
# Hit on "a": promoted back to L1, removed from L2; "b" (now LRU) demotes.
assert tc.get("a") == "A"
assert (tc.l1_hits, tc.l2_hits, tc.misses) == (0, 1, 0)
assert tc.promotions == 1
assert "a" in tc.l1 and "a" not in tc.l2
assert "b" in tc.l2 and "b" not in tc.l1
assert tc.demotions == 2 # "a" out, then "b" out
# Aggregate `hits` includes L2 hits (here l1_hits=0, l2_hits=1).
assert tc.hits == 1
def test_tiered_l2_disabled_behaves_single_tier():
# L2 capacity 0: demote is a no-op, nothing is ever promoted.
tc = _tiered(150, 0)
tc.put("a", "A", 100)
tc.put("b", "B", 100) # "a" evicted; L2 can't hold it -> truly dropped
assert "a" not in tc.l1 and "a" not in tc.l2
assert tc.demotions == 0
assert tc.get("a") is None
assert tc.misses == 1
assert "b" in tc.l1