From 0ee54157e5b2746aea39d9a5d5d8a1b50aa7413e Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 8 Jun 2026 17:43:12 +0200 Subject: [PATCH] =?UTF-8?q?fix(p1g):=20garde-fou=20VRAM=20adapt=C3=A9=20?= =?UTF-8?q?=C3=A0=20la=20m=C3=A9moire=20unifi=C3=A9e=20(DGX=20GB10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolve_device('auto') renvoyait 'cpu' sur le GB10 : le plafond max_total_gb=6 (pensé pour la RTX 12 Go dédiés) voyait used≈99 Go car la mémoire UNIFIÉE compte la RAM système. Au-dessus de DEFAULT_LARGE_VRAM_GB=24 (grosse carte / mémoire unifiée), le plafond n'est plus appliqué ; seul free >= min_free_gb décide. RTX (<=24 Go) inchangée. Détecté au bench GB10 2026-06-08 (auto->cpu, OCR 10x plus lent). +2 tests (17/17). Co-Authored-By: Claude Opus 4.8 (1M context) --- core/gpu/device_policy.py | 21 ++++++++++++++++++--- tests/unit/test_device_policy.py | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/gpu/device_policy.py b/core/gpu/device_policy.py index b356c1d83..67c03f8a5 100644 --- a/core/gpu/device_policy.py +++ b/core/gpu/device_policy.py @@ -36,6 +36,12 @@ _VALID = {"cpu", "cuda", "auto"} # Garde-fous par défaut (Go). DEFAULT_MIN_FREE_GB = 2.0 # VRAM libre minimale pour autoriser cuda DEFAULT_MAX_TOTAL_GB = 6.0 # plafond d'usage VRAM total après bascule +# Au-delà de ce total VRAM, on considère une grosse carte (data-center) ou une +# mémoire UNIFIÉE (DGX GB10 : ~121 Go partagés CPU+GPU). Dans ce cas `used` +# (= total - free) inclut la RAM système → le plafond fixe `max_total_gb` (pensé +# pour la RTX 12 Go dédiés) devient un faux positif qui force CPU à tort. On ne +# l'applique donc QUE sous ce seuil ; au-dessus, seul `free ≥ min_free_gb` décide. +DEFAULT_LARGE_VRAM_GB = 24.0 def _env_override() -> Optional[str]: @@ -135,13 +141,22 @@ def resolve_device( ) return "cpu" - if used_gb > max_total_gb: + # Plafond d'usage : seulement sur carte dédiée "petite" (type RTX). Sur grosse + # mémoire / mémoire unifiée (GB10), `used` inclut la RAM système → non pertinent. + if total_gb <= DEFAULT_LARGE_VRAM_GB and used_gb > max_total_gb: logger.info( - "auto: usage VRAM %.1f Go > plafond %.1f Go — CPU", - used_gb, max_total_gb, + "auto: usage VRAM %.1f Go > plafond %.1f Go (carte %.1f Go) — CPU", + used_gb, max_total_gb, total_gb, ) return "cpu" + if total_gb > DEFAULT_LARGE_VRAM_GB: + logger.info( + "auto: grosse mémoire/unifiée %.1f Go, libre %.1f Go — CUDA (plafond ignoré)", + total_gb, free_gb, + ) + return "cuda" + logger.info( "auto: VRAM libre %.1f Go (usage %.1f/%.1f Go) — CUDA", free_gb, used_gb, total_gb, diff --git a/tests/unit/test_device_policy.py b/tests/unit/test_device_policy.py index f3e58282d..026a770ba 100644 --- a/tests/unit/test_device_policy.py +++ b/tests/unit/test_device_policy.py @@ -105,6 +105,26 @@ def test_resolve_auto_cuda_when_under_total_cap(monkeypatch): max_total_gb=6.0) == "cuda" +# ── mémoire unifiée / grosse carte (DGX GB10) : plafond inapplicable ───────── + +def test_resolve_auto_cuda_on_unified_memory_ignores_total_cap(monkeypatch): + """Mémoire unifiée GB10 : total=121, free=22 → used=99 > cap 6, MAIS total + > seuil grosse mémoire (24) → plafond ignoré, free 22 ≥ min 2 → CUDA. + Sans ce comportement, le DGX tomberait à tort sur CPU (régression observée + au bench GB10 2026-06-08).""" + monkeypatch.delenv("RPA_VISION_DEVICE", raising=False) + with _mock_cuda(available=True, free_gb=22.0, total_gb=121.0): + assert device_policy.resolve_device("auto", min_free_gb=2.0, + max_total_gb=6.0) == "cuda" + + +def test_resolve_auto_cpu_on_large_memory_when_free_too_low(monkeypatch): + """Grosse mémoire mais free < min → CPU (free reste le garde-fou réel).""" + monkeypatch.delenv("RPA_VISION_DEVICE", raising=False) + with _mock_cuda(available=True, free_gb=1.0, total_gb=121.0): + assert device_policy.resolve_device("auto", min_free_gb=2.0) == "cpu" + + # ── override env RPA_VISION_DEVICE ────────────────────────────────────────── def test_env_override_cpu_forces_cpu_even_in_auto(monkeypatch):