fix(intern-decision-serve): load and score on one dedicated inference thread

torch keeps CUDA state per host thread (cuBLAS handles and workspaces), partly outside the
per-process VRAM cap. Scoring on anyio's threadpool let 40 threads each create it: measured on
fv-ml1 GPU 3, +252 MiB outside the cap and +326 MiB inside, which pushed the process past the
10,300 MiB GPU 1 budget. Load, warm-up and every call now run on the same single thread.
This commit is contained in:
vh
2026-09-30 09:18:46 -07:00
parent f7415db5c9
commit f21369e4ac
5 changed files with 87 additions and 11 deletions
@@ -432,3 +432,35 @@ def test_more_than_max_queue_requests_in_progress_get_429_busy_before_the_body_i
engine.release.set()
assert [f.result().status_code for f in held] == [200, 200]
assert make_client(FakeEngine(), max_queue=2).post("/decide", json=ROW, headers=AUTH).status_code == 200
class ThreadRecordingEngine(FakeEngine):
def __init__(self):
super().__init__()
self.threads = set()
def predict(self, request):
self.threads.add(threading.get_ident())
return super().predict(request)
def test_every_call_runs_on_one_dedicated_inference_thread():
"""torch keeps CUDA state per host thread (cuBLAS handles + workspaces), outside the VRAM cap;
measured 2026-09-30: 40 worker threads added 252 MiB outside the cap. So: one thread, always."""
engine = ThreadRecordingEngine()
with make_client(engine) as client, ThreadPoolExecutor(12) as pool:
futures = [pool.submit(client.post, "/decide/shared", json={"state": f"s{i}", "decisions": decisions(20)},
headers=AUTH) for i in range(24)]
assert all(f.result().status_code == 200 for f in futures)
assert len(engine.threads) == 1
def test_the_app_uses_the_executor_it_is_given():
from intern_decision_serve.app import create_app as build
engine = ThreadRecordingEngine()
executor = ThreadPoolExecutor(1)
home = executor.submit(threading.get_ident).result()
client = TestClient(build(Settings(api_token=TOKEN), engine, executor=executor))
assert client.post("/decide", json=ROW, headers=AUTH).status_code == 200
assert engine.threads == {home}
executor.shutdown()