mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-07 14:21:06 -10:00
vram_fit: request slots ("Concurrent requests" 2 or more) take their sessions out of the expert cache's room. Each slot is a session the size of the main one on every backend (the engine's 0.23 GiB at 8K int8); the Flash-Next figures measured on the 8 GB A750 (base 4500 MiB, 2.22 MiB per cache slot) are used for these starts only. A start the engine would refuse gets a smaller context (32K, 16K, 8K, 4K, 2K) for that start, or runs one request at a time, with the fit note; the person's own --max-context is kept, and one-card-less, layer-split, DeepSeek and Metal starts are untouched (fit_start, called from serve/server.py engine_args). Tests for an 8 GB Arc at 8K with 2 requests, a 24 GB CUDA card, an explicit context. verify_refusal_test is registered with add_test
This commit is contained in:
1 parent
ab7baa5b17
commit
2a1042877d
4 files changed
+201
-15
No files matched your search
@@ -75,6 +75,7 @@ from strata_control import vision_reader # noqa: E402
|
||||
from serve.ds_pictures import DsPictures # noqa: E402
|
||||
from strata_control import draft_layer # noqa: E402
|
||||
from strata_control import drafting # noqa: E402
|
||||
from strata_control import vram_fit # noqa: E402
|
||||
from strata_control import user_args # noqa: E402
|
||||
from strata_control import gpu_roles # noqa: E402
|
||||
from serve import ready as ready_file # noqa: E402
|
||||
@@ -3427,6 +3428,17 @@ def engine_args(cfg: dict) -> list[str]:
|
||||
print(f"[strata] {drafting.line(decision.drafter, decision.note)}", flush=True)
|
||||
elif decision.flags:
|
||||
print(f"[strata] drafting: DFlash2 alone ({' '.join(decision.flags[:2])}, --draft-conf {args[args.index('--draft-conf') + 1] if '--draft-conf' in args else '-'})", flush=True)
|
||||
# "parallel" 2 or more: every request slot is a session of its own, taken out of the expert cache's room. A start the engine would refuse for lack of VRAM
|
||||
# ("no VRAM is left for the expert cache") gets a smaller context for this start, else runs one request at a time, and says so; the person's own
|
||||
# --max-context is kept (strata_control/vram_fit.py fit_start). Nothing changes without one card's memory, on a layer split, or for DeepSeek.
|
||||
try:
|
||||
args, fit_parallel, fit_note = vram_fit.fit_start(cfg, args, drafting_vram_mib(cfg, backend, archs), backend)
|
||||
except Exception: # noqa: BLE001 - an estimate that fails decides nothing; the engine still refuses what does not fit
|
||||
fit_parallel, fit_note = None, ""
|
||||
if fit_note:
|
||||
print(f"[strata] {fit_note}", flush=True)
|
||||
if fit_parallel is not None:
|
||||
cfg = {**cfg, "parallel": fit_parallel}
|
||||
# "mtp": false (the Models settings, `strata launch --mtp off`, or by hand): no draft layer, whatever the args say. A setup run
|
||||
# that writes --mtp back into the args cannot undo it, because this is applied at every start.
|
||||
if draft_layer.is_off(cfg):
|
||||
|
||||
+95
-15
@@ -19,6 +19,9 @@ The arithmetic (MiB), measured on the Arc A750 (8 GB, i915) with the SYCL engine
|
||||
* prompt buffers: 170 + 0.66 * chunk MiB, measured at chunks 256 .. 2048 (338 / 508 / 850 / 1525 MiB), independent of the context.
|
||||
`--prefill auto` resolved to 2048 on that card.
|
||||
* slot: 1.243 MiB (0.50 GiB / 412 slots).
|
||||
* request slots ("Concurrent requests" 2 or more, the engine's --batch N): every slot holds a session of its own, the size of the main one, on every backend
|
||||
(the engine's "--batch 2: 2 slot sessions (0.23 GiB each)" is session(8192) = 234 MiB), taken out of the expert cache's room. Only these starts also use the
|
||||
measured Qwen3.8-Flash-Next figures below (BASE_SLOTTED_MIB, SLOT_SLOTTED_MIB); the single-request estimate above is left as it is.
|
||||
|
||||
Why these numbers and not the engine's own: the engine only finds out after it has loaded 30 GB of weights; setup has to choose before.
|
||||
It is an estimate; the engine still says what is short if it is off (and a user who asked for a context or a reserve keeps it).
|
||||
@@ -48,6 +51,16 @@ MIN_CARD_GB = 5.0 # under this nothing here is claimed (th
|
||||
BASE_MIB = {"gyro-m": 5945, "gyro-s": 4720}
|
||||
BASE_GENERIC_MIB = 2448
|
||||
|
||||
# Starts with request slots. The generic base above is deliberately small (a default that fits today is never changed), so it cannot tell whether two more
|
||||
# sessions leave the expert cache room; for Qwen3.8-Flash-Next the engine's own figures can. Arc A750, IQ3_XXS with the MTP layer, 2 slots, 8192 tokens, the
|
||||
# engine log: 7721 MiB usable (8 GB less the 471 MiB gap), 2519 MiB free after everything but the expert cache, which held the weights (1466 + 1781 MiB), the
|
||||
# MTP layer (802), the Q5_K head (417), the main session (234) and the two slot sessions (468): base = 7721 - 2519 - 234 - 468 = 4500 MiB. The cache's
|
||||
# largest expert blob is 2.33 MB (2.22 MiB). Another quant or card holds somewhat different weights; MARGIN_MIB and the engine's own refusal cover the rest.
|
||||
BASE_SLOTTED_MIB = {"flash-next": 4500}
|
||||
SLOT_SLOTTED_MIB = 2.22
|
||||
CTX_STEPS_SLOTTED = CTX_STEPS + (4096, 2048) # below 8K only for a start with request slots: each slot's session is one more KV cache to pay for
|
||||
SLOTS_MAX = 8 # the engine's batch window (serve/server.py PARALLEL_MAX)
|
||||
|
||||
|
||||
def base_mib(model: str) -> int:
|
||||
low = str(model or "").lower().replace("_", "-")
|
||||
@@ -57,6 +70,15 @@ def base_mib(model: str) -> int:
|
||||
return BASE_GENERIC_MIB
|
||||
|
||||
|
||||
def slotted_base_mib(model: str) -> int:
|
||||
"""The base for a start with request slots: the measured Flash-Next figure, else the single-request one."""
|
||||
low = str(model or "").lower().replace("_", "-")
|
||||
for key, mib in BASE_SLOTTED_MIB.items():
|
||||
if key in low:
|
||||
return mib
|
||||
return base_mib(model)
|
||||
|
||||
|
||||
def known_model(model: str) -> bool:
|
||||
low = str(model or "").lower().replace("_", "-")
|
||||
return any(k in low for k in BASE_MIB)
|
||||
@@ -75,19 +97,27 @@ def chunk_of(prefill) -> int:
|
||||
|
||||
|
||||
def slack_mib(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: int = 300, prefill="auto",
|
||||
vendor: str = "intel") -> float:
|
||||
"""MiB the estimate leaves for the expert cache (negative: the engine would stop with "no VRAM is left for the expert cache")."""
|
||||
vendor: str = "intel", slots: int = 0) -> float:
|
||||
"""MiB the estimate leaves for the expert cache (negative: the engine would stop with "no VRAM is left for the expert cache").
|
||||
`slots` >= 2: a start with that many request slots, each one a session of the main one's size."""
|
||||
gap = GAP_MIB.get(str(vendor or "").lower(), GAP_OTHER_MIB)
|
||||
prompt = PROMPT_BASE_MIB + PROMPT_PER_TOKEN_MIB * chunk_of(prefill)
|
||||
return (vram_gb * 1024 - gap - base_mib(model) - session_mib(ctx, kv) - HEAD_MIB - max(int(reserve_mib), 0) - prompt)
|
||||
base = base_mib(model)
|
||||
extra = 0.0
|
||||
if slots >= 2:
|
||||
base = slotted_base_mib(model)
|
||||
extra = min(int(slots), SLOTS_MAX) * session_mib(ctx, kv)
|
||||
return (vram_gb * 1024 - gap - base - session_mib(ctx, kv) - extra - HEAD_MIB - max(int(reserve_mib), 0) - prompt)
|
||||
|
||||
|
||||
def needed_mib() -> float:
|
||||
def needed_mib(model: str = "", slots: int = 0) -> float:
|
||||
if slots >= 2 and slotted_base_mib(model) != base_mib(model):
|
||||
return MIN_SLOTS * SLOT_SLOTTED_MIB + MARGIN_MIB
|
||||
return MIN_SLOTS * SLOT_MIB + MARGIN_MIB
|
||||
|
||||
|
||||
def fits(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: int = 300, prefill="auto", vendor: str = "intel") -> bool:
|
||||
return slack_mib(model, vram_gb, ctx, kv, reserve_mib, prefill, vendor) >= needed_mib()
|
||||
def fits(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: int = 300, prefill="auto", vendor: str = "intel", slots: int = 0) -> bool:
|
||||
return slack_mib(model, vram_gb, ctx, kv, reserve_mib, prefill, vendor, slots) >= needed_mib(model, slots)
|
||||
|
||||
|
||||
def ctx_label(ctx: int) -> str:
|
||||
@@ -104,10 +134,11 @@ class Fit:
|
||||
slack_mib: float = 0.0
|
||||
line: str = "" # one plain sentence for the user, "" when nothing changed
|
||||
changes: list = field(default_factory=list)
|
||||
slots: int = 0 # the request slots this start keeps (plan(slots=N)): N, or 1 when not even the smallest context leaves the cache room
|
||||
|
||||
|
||||
def plan(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: int = 300, prefill="auto", vendor: str = "intel",
|
||||
title: str = "", ctx_fixed: bool = False, reserve_fixed: bool = False, prefill_fixed: bool = False) -> Fit:
|
||||
title: str = "", ctx_fixed: bool = False, reserve_fixed: bool = False, prefill_fixed: bool = False, slots: int = 0) -> Fit:
|
||||
"""The start that fits, as close to the one asked for as possible; the SAME values back when that one already fits (byte-identical).
|
||||
|
||||
Order of harm, mildest first: the context down the ladder 32K -> 16K -> 8K (the outer loop), at each step the prompt chunk (1024)
|
||||
@@ -115,25 +146,40 @@ def plan(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: in
|
||||
ladder fits, the smallest start is returned with fits=False and a line that says so."""
|
||||
ctx = int(ctx)
|
||||
reserve_mib = int(reserve_mib)
|
||||
keep = Fit(ctx, reserve_mib, None, False, True, 0.0)
|
||||
slots = int(slots) if slots and int(slots) >= 2 else 0
|
||||
keep = Fit(ctx, reserve_mib, None, False, True, 0.0, slots=slots)
|
||||
if not vram_gb or vram_gb < MIN_CARD_GB:
|
||||
return keep
|
||||
keep.slack_mib = slack_mib(model, vram_gb, ctx, kv, reserve_mib, prefill, vendor)
|
||||
if keep.slack_mib >= needed_mib():
|
||||
need = needed_mib(model, slots)
|
||||
keep.slack_mib = slack_mib(model, vram_gb, ctx, kv, reserve_mib, prefill, vendor, slots)
|
||||
if keep.slack_mib >= need:
|
||||
return keep
|
||||
ctxs = [ctx] if ctx_fixed else [ctx] + [c for c in CTX_STEPS if c < ctx]
|
||||
steps = CTX_STEPS_SLOTTED if slots else CTX_STEPS
|
||||
ctxs = [ctx] if ctx_fixed else [ctx] + [c for c in steps if c < ctx]
|
||||
chunks = [prefill] if prefill_fixed else [prefill] + [c for c in CHUNK_STEPS if c < chunk_of(prefill)]
|
||||
reserves = [reserve_mib] if reserve_fixed or reserve_mib <= RESERVE_FLOOR_MIB else [reserve_mib, RESERVE_FLOOR_MIB]
|
||||
best = None
|
||||
for c in ctxs:
|
||||
for p in chunks:
|
||||
for r in reserves:
|
||||
s = slack_mib(model, vram_gb, c, kv, r, p, vendor)
|
||||
s = slack_mib(model, vram_gb, c, kv, r, p, vendor, slots)
|
||||
best = (c, p, r, s)
|
||||
if s >= needed_mib():
|
||||
return _result(model, vram_gb, title, ctx, reserve_mib, prefill, c, p, r, s, True)
|
||||
if s >= need:
|
||||
out = _result(model, vram_gb, title, ctx, reserve_mib, prefill, c, p, r, s, True)
|
||||
out.slots = slots
|
||||
if slots and out.changed:
|
||||
out.line = out.line.replace(" so it fits", f" so {slots} requests at once fit")
|
||||
return out
|
||||
c, p, r, s = best
|
||||
return _result(model, vram_gb, title, ctx, reserve_mib, prefill, c, p, r, s, False)
|
||||
out = _result(model, vram_gb, title, ctx, reserve_mib, prefill, c, p, r, s, False)
|
||||
out.slots = slots
|
||||
if slots and not ctx_fixed: # not even the smallest context leaves the expert cache room for the request slots: one at a time, said plainly
|
||||
name = title or "This model"
|
||||
out.slots = 1
|
||||
out.changed = True
|
||||
out.line = (f"{name} on this {vram_gb:.0f} GB card: {slots} requests at once need more memory than is free here, even with the context at "
|
||||
f"{ctx_label(c)}, so this start answers one request at a time")
|
||||
return out
|
||||
|
||||
|
||||
def _result(model, vram_gb, title, ctx0, reserve0, prefill0, ctx, chunk, reserve, slack, ok) -> Fit:
|
||||
@@ -154,6 +200,40 @@ def _result(model, vram_gb, title, ctx0, reserve0, prefill0, ctx, chunk, reserve
|
||||
return Fit(ctx, reserve, str(chunk) if str(chunk) != str(prefill0) else None, bool(changes), ok, slack, line, changes)
|
||||
|
||||
|
||||
def fit_start(cfg: dict, args: list, vram_mib: float | None, backend: str | None) -> tuple[list, int | None, str]:
|
||||
"""The start the config asks for, when it has request slots ("parallel" 2 or more, the app's "Concurrent requests"): (args, parallel, note).
|
||||
The slot sessions are taken out of the expert cache's room (plan(slots=N)); when that leaves too little, the context for this start is lowered to the
|
||||
largest that fits (the same ladder, then 4K and 2K), and when not even that fits the start runs one request at a time. The person's own context
|
||||
(``--max-context`` in user_args) is kept: the engine's plain refusal answers it. `parallel` is the effective count (None: the config's, untouched).
|
||||
Nothing changes without a single card's memory (several cards, a shared-memory chip: None) or for a layer split / DeepSeek, which size their slots themselves."""
|
||||
n = cfg.get("parallel")
|
||||
if isinstance(n, bool) or not isinstance(n, int) or n < 2 or not vram_mib or backend in (None, "", "metal"):
|
||||
return args, None, ""
|
||||
if (cfg.get("arch") or "") == "deepseek41" or cfg.get("layer_split") or "--layer-split" in args or "--batch" in args or "--slots" in args:
|
||||
return args, None, ""
|
||||
|
||||
def flag(name):
|
||||
return args[args.index(name) + 1] if name in args and args.index(name) + 1 < len(args) else None
|
||||
|
||||
ctx_s = flag("--max-context")
|
||||
ctx = int(ctx_s) if ctx_s and str(ctx_s).isdigit() else 131072
|
||||
reserve_s = flag("--vram-reserve-mib")
|
||||
reserve = int(reserve_s) if reserve_s and str(reserve_s).isdigit() else 700 # the engine's default reserve
|
||||
user = [str(a) for a in (cfg.get("user_args") or [])]
|
||||
model = str(cfg.get("model_name") or flag("--native") or "")
|
||||
fr = plan(model, float(vram_mib) / 1024.0, ctx, flag("--kv") or "int8", reserve, flag("--prefill") or "auto", "intel" if backend == "sycl" else "other",
|
||||
title=str(cfg.get("model_name") or "This model"), ctx_fixed="--max-context" in user, reserve_fixed=True, prefill_fixed=True, slots=n)
|
||||
if not fr.changed:
|
||||
return args, None, ""
|
||||
out = list(args)
|
||||
if fr.ctx != ctx:
|
||||
if "--max-context" in out:
|
||||
out[out.index("--max-context") + 1] = str(fr.ctx)
|
||||
else:
|
||||
out += ["--max-context", str(fr.ctx)]
|
||||
return out, (fr.slots if fr.slots != n else None), fr.line
|
||||
|
||||
|
||||
def fit_context(model: str, vram_gb: float, ctx: int, kv: str = "int8", reserve_mib: int = 300, vendor: str = "intel") -> int:
|
||||
"""The context to recommend: `ctx` when it fits (unchanged), else the biggest step of the ladder that does (the reserve and
|
||||
chunk lowered the way plan() lowers them), else the smallest step."""
|
||||
|
||||
@@ -430,6 +430,7 @@ if(STRATA_SYCL_PARITY)
|
||||
target_link_libraries(dflash2_ring_test PRIVATE strata_engine strata_prefill strata_spec)
|
||||
add_executable(verify_refusal_test ${CMAKE_CURRENT_SOURCE_DIR}/src/core/verify_refusal_test.cpp) # a refused start (no VRAM expert tier) ends cleanly: the verifiers leave the default queue alone
|
||||
target_link_libraries(verify_refusal_test PRIVATE strata_engine strata_prefill strata_spec)
|
||||
add_test(NAME verify_refusal_test COMMAND verify_refusal_test) # needs one SYCL GPU
|
||||
add_executable(dflash2_bench ${CMAKE_CURRENT_SOURCE_DIR}/src/kernels/dflash2_bench.cpp) # DF2-SYCL: the DFlash2 Q4_0 drafter round on an Arc, kernel checks
|
||||
target_link_libraries(dflash2_bench PRIVATE strata_kernels)
|
||||
add_executable(dense_bench ${CMAKE_CURRENT_SOURCE_DIR}/src/kernels/dense_bench.cpp) # the decode window's dense GEMVs, graph, hash
|
||||
|
||||
@@ -148,5 +148,98 @@ class SetupIntel(unittest.TestCase):
|
||||
self.assertEqual(notes, [])
|
||||
|
||||
|
||||
FN = "qwen3.8-flash-next-iq3_xxs"
|
||||
|
||||
|
||||
class SlotSessions(unittest.TestCase):
|
||||
"""Request slots ("Concurrent requests" 2 or more, the engine's --batch N): each slot is a session of its own, out of the expert cache's room.
|
||||
The 8 GB Arc A750 figures are the engine's: --batch 2 at 8192 tokens with the MTP layer left 2519 MiB free and the cache needed 99 MiB more (0 slots, a
|
||||
refused start, SIGSEGV before ab7baa5b1)."""
|
||||
|
||||
def test_the_slot_session_is_the_main_sessions_size(self):
|
||||
self.assertAlmostEqual(V.session_mib(8192), 234, delta=8) # the engine: "2 slot sessions (0.23 GiB each)"
|
||||
|
||||
def test_without_slots_nothing_changes(self):
|
||||
a = V.slack_mib(FN, 8.0, 8192, "int8", 700, "auto", "intel")
|
||||
self.assertEqual(a, V.slack_mib(FN, 8.0, 8192, "int8", 700, "auto", "intel", 0))
|
||||
self.assertEqual(a, V.slack_mib(FN, 8.0, 8192, "int8", 700, "auto", "intel", 1))
|
||||
self.assertEqual(V.needed_mib(), V.needed_mib(FN, 1))
|
||||
|
||||
def test_an_8gb_arc_with_two_requests_at_8k_gets_a_context_that_fits(self):
|
||||
f = V.plan(FN, 8.0, 8192, "int8", 700, "auto", "intel", title=FN, reserve_fixed=True, prefill_fixed=True, slots=2)
|
||||
self.assertTrue(f.changed and f.fits)
|
||||
self.assertEqual((f.ctx, f.slots, f.reserve_mib), (4096, 2, 700))
|
||||
self.assertGreaterEqual(f.slack_mib, V.needed_mib(FN, 2))
|
||||
self.assertIn("context set to 4K", f.line)
|
||||
self.assertTrue(f.line.endswith("so 2 requests at once fit"), f.line)
|
||||
# the engine's refusal case, as the estimate sees it: 8192 with two slots does not leave the cache its 128 slots
|
||||
self.assertLess(V.slack_mib(FN, 8.0, 8192, "int8", 700, "auto", "intel", 2), V.needed_mib(FN, 2))
|
||||
|
||||
def test_not_even_the_smallest_context_fits_then_one_request_at_a_time(self):
|
||||
f = V.plan(FN, 8.0, 8192, "int8", 1024, "auto", "intel", title=FN, reserve_fixed=True, prefill_fixed=True, slots=2)
|
||||
self.assertEqual(f.slots, 1)
|
||||
self.assertTrue(f.changed)
|
||||
self.assertIn("one request at a time", f.line)
|
||||
|
||||
def test_a_24gb_cuda_card_is_unchanged(self):
|
||||
f = V.plan(FN, 24.0, 131072, "int8", 700, "auto", "other", title=FN, reserve_fixed=True, prefill_fixed=True, slots=4)
|
||||
self.assertFalse(f.changed)
|
||||
self.assertEqual((f.ctx, f.slots, f.line), (131072, 4, ""))
|
||||
|
||||
def test_a_chosen_context_is_kept(self):
|
||||
f = V.plan(FN, 8.0, 8192, "int8", 700, "auto", "intel", ctx_fixed=True, reserve_fixed=True, prefill_fixed=True, slots=2)
|
||||
self.assertEqual(f.ctx, 8192)
|
||||
self.assertFalse(f.changed) # the engine's plain refusal answers it
|
||||
|
||||
|
||||
class FitStart(unittest.TestCase):
|
||||
"""fit_start: the config's "parallel" and the engine arguments of one start (serve/server.py engine_args calls it)."""
|
||||
|
||||
ARGS = ["--native", "/m/Qwen3.8-Flash-Next-IQ3_XXS-00001-of-00002.gguf", "--expert-cache", "auto", "--prefill", "auto", "--max-context", "8192",
|
||||
"--kv", "int8", "--vram-reserve-mib", "700"]
|
||||
|
||||
def cfg(self, **kw):
|
||||
c = {"model_name": FN, "parallel": 2}
|
||||
c.update(kw)
|
||||
return c
|
||||
|
||||
def test_an_8gb_arc_gets_a_smaller_context_for_this_start(self):
|
||||
args, par, note = V.fit_start(self.cfg(), list(self.ARGS), 8192, "sycl")
|
||||
self.assertEqual(args[args.index("--max-context") + 1], "4096")
|
||||
self.assertIsNone(par) # still 2 requests at once
|
||||
self.assertIn("2 requests at once fit", note)
|
||||
self.assertEqual(self.ARGS[self.ARGS.index("--max-context") + 1], "8192") # the input is not touched
|
||||
|
||||
def test_the_persons_own_context_wins(self):
|
||||
args, par, note = V.fit_start(self.cfg(user_args=["--max-context", "8192"]), list(self.ARGS), 8192, "sycl")
|
||||
self.assertEqual((args, par, note), (self.ARGS, None, ""))
|
||||
|
||||
def test_a_24gb_cuda_card_and_one_request_are_unchanged(self):
|
||||
big = ["--native", "/m/q.gguf", "--max-context", "131072", "--kv", "int8", "--vram-reserve-mib", "700"]
|
||||
self.assertEqual(V.fit_start(self.cfg(parallel=4), list(big), 24576, "cuda"), (big, None, ""))
|
||||
self.assertEqual(V.fit_start(self.cfg(parallel=1), list(self.ARGS), 8192, "sycl"), (self.ARGS, None, ""))
|
||||
self.assertEqual(V.fit_start({"model_name": FN}, list(self.ARGS), 8192, "sycl"), (self.ARGS, None, ""))
|
||||
|
||||
def test_nothing_without_one_cards_memory_or_on_a_split_or_deepseek_or_metal(self):
|
||||
self.assertEqual(V.fit_start(self.cfg(), list(self.ARGS), None, "sycl"), (self.ARGS, None, ""))
|
||||
self.assertEqual(V.fit_start(self.cfg(), list(self.ARGS), 8192, "metal"), (self.ARGS, None, ""))
|
||||
self.assertEqual(V.fit_start(self.cfg(layer_split="auto"), list(self.ARGS), 8192, "sycl"), (self.ARGS, None, ""))
|
||||
self.assertEqual(V.fit_start(self.cfg(arch="deepseek41"), list(self.ARGS), 8192, "sycl"), (self.ARGS, None, ""))
|
||||
|
||||
def test_a_start_that_cannot_fit_runs_one_at_a_time(self):
|
||||
tight = [a if a != "700" else "1024" for a in self.ARGS]
|
||||
args, par, note = V.fit_start(self.cfg(), tight, 8192, "sycl")
|
||||
self.assertEqual(par, 1)
|
||||
self.assertIn("one request at a time", note)
|
||||
|
||||
def test_the_context_is_added_when_the_args_name_none(self):
|
||||
no_ctx = [a for a in self.ARGS]
|
||||
i = no_ctx.index("--max-context")
|
||||
del no_ctx[i:i + 2]
|
||||
args, par, note = V.fit_start(self.cfg(), no_ctx, 8192, "sycl")
|
||||
self.assertNotEqual(note, "")
|
||||
self.assertIn("--max-context", args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user