Skip to content

About

Dynamic CUDA router for ExLlamaV2: auto-fallback from P40 (sm_61) to RTX 3050

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Router CUDA Dinamico ExLlamaV2 — P40 → RTX 3050

Panoramica

Script Python che intercetta i fallimenti CUDA su Tesla P40 (sm_6.1) e sposta automaticamente l'esecuzione dei kernel su NVIDIA RTX 3050 (sm_8.6), mantenendo i pesi del modello sulla P40 per sfruttare i 24 GB di VRAM.

Problema

Il sistema ha due GPU:

GPU CUDA Device Capability VRAM Ruolo
Tesla P40 CUDA 0 sm_6.1 24 GB Storage pesi + KV cache
RTX 3050 CUDA 1 sm_8.6 8 GB Esecuzione kernel

Problema: PyTorch 2.7.0+cu128 non supporta sm_6.1 (soglia minima: sm_7.5). Quando ExLlamaV2 v0.3.2 tenta di compilare JIT i kernel CUDA per la P40, fallisce con:

RuntimeError: CUDA error: no kernel image is available for execution on the device

Questo avviene perché i kernel CUDA sono compilati per sm_86 (RTX 3050), ma il dispositivo attivo è sm_61 (P40).

Architettura

Vincoli

  1. P40 (sm_6.1): PyTorch 2.7 non supporta esecuzione kernel. Solo lettura/scrittura tensori.
  2. RTX 3050 (sm_8.6): Supporto completo PyTorch 2.7. VRAM limitata (~7.4 GB disponibili).
  3. ExLlamaV2 v0.3.2: Compila kernel JIT per la GPU su cui viene inizializzato. Non supporta multi-device nativo.
  4. Modello 35B Q4_K_XL: ~22 GB VRAM — non entra sulla RTX 3050.

Strategia

┌─────────────────────────────────────────────────────────┐
│                   Router CUDA                           │
│                                                         │
│  1. Carica pesi modello su P40 (CUDA:0)                │
│  2. Quando serve eseguire kernel → sposta su RTX 3050  │
│  3. Esegui prefill/decode su RTX 3050                  │
│  4. Ritorna risultati su P40                            │
│  5. Ripeti per ogni layer                               │
└─────────────────────────────────────────────────────────┘
         │                          │
         ▼                          ▼
   ┌──────────┐              ┌────────────┐
   │  P40     │              │  RTX 3050  │
   │ 24 GB    │  ←→ Tensor   │ 7.4 GB     │
   │ Peseti   │  copy        │ Kernel     │
   │ KV cache │  (PCIe)      │ execution  │
   └──────────┘              └────────────┘

Componenti

1. config.py — Configurazione centralizzata

Dataclass gerarchica con supporto env vars e JSON:

  • DeviceConfig: weights_device (P40), compute_device (RTX 3050), fallback
  • MigrationConfig: prefetch_layers, prefetch_async, pinned memory, integrity check
  • KvCacheConfig: cache_device, max_seq_len, head_dim, shard_size
  • ErrorHandlerConfig: max_retries, catch_patterns, timeout, traceback
  • LoggingConfig: level, file, format
  • RouterConfig: aggrega tutti i config, from_env(), from_file(), summary()

2. error_handler.py — Gestione errori CUDA

  • CudaError: eccezione wrapper con device e error_type
  • DeviceMismatchError: errore specifico kernel non disponibile
  • catch_cuda_errors(): decoratore che intercetta errori CUDA e triggera failover
  • retry_on_device_error(): retry automatico su device di fallback con contatore
  • log_error_context(): registra contesto completo (VRAM, stack trace, CUDA version)
  • suppress_cuda_errors(): decoratore per operazioni non critiche

3. layer_migration.py — Gestione layer

  • LayerSnapshot: cattura e ripristino di tutti i tensori di un layer
  • LayerMigrationManager: manager principale con:
    • copy_layer_to_compute(): copia layer da P40 → RTX 3050 con cache
    • restore_layer_from_compute(): ripristina layer su P40 dopo esecuzione
    • prefetch_layer(): precaricamento asincrono in background (thread)
    • clear_compute_cache(): svuota cache layer su RTX 3050
    • _verify_integrity(): verifica correttezza copia tensori
    • get_memory_stats(): statistiche memoria GPU

4. kv_cache_manager.py — Gestione KV cache

  • KvCacheBlock: blocco KV per singolo layer (key_cache + value_cache)
    • update(): append incrementale con overflow protection
    • get(): lettura cache corrente
    • reset(): reset posizione
  • KvCacheManager: manager per tutti i layer
    • update(): aggiorna cache layer specifico
    • get(): recupera cache layer
    • get_all(): recupera cache tutti i layer
    • get_sequence_length(), get_remaining_slots(), is_full()
    • get_memory_usage(): statistiche memoria cache
    • Calcolo memoria stimata in fase di init

5. cuda_router.py — Core del router

  • LayerForwardHook: hook che intercetta forward di un layer
    • Copia layer su RTX 3050 → esegue forward → copia output su P40 → ripristina layer
  • CudaRouter: classe principale del router
    • _verify_devices(): verifica disponibilità e capability GPU
    • setup_kv_cache(): configura e alloca KV cache
    • attach_hooks(): registra hook su tutti i layer del modello
    • detach_hooks(): rimuove hook dal modello
    • forward(): forward pass layer-by-layer con routing automatico
    • generate(): wrapper per generazione testo con routing
    • Modalità layer_by_layer (default) e full_copy (tutti i layer su RTX 3050)

Flusso di esecuzione

┌─────────────────────────────────────────────────────────┐
│                   CudaRouter                            │
│                                                         │
│  ┌────────────────┐  ┌────────────────┐                │
│  │ LayerForward   │  │ LayerMigration │                │
│  │ Hook           │  │ Manager        │                │
│  │                │  │                │                │
│  │ 1. copy layer  │→│ copy_layer_to  │                │
│  │ 2. run forward │  │ compute()      │                │
│  │ 3. copy output │  │                │                │
│  │ 4. restore     │  │ prefetch_layer │                │
│  └────────────────┘  └────────────────┘                │
│                                                         │
│  ┌────────────────┐  ┌────────────────┐                │
│  │ KvCache        │  │ Error          │                │
│  │ Manager        │  │ Handler        │                │
│  │                │  │                │                │
│  │ allocate()     │  │ catch_errors   │                │
│  │ update()       │  │ retry_on_error │                │
│  │ get()          │  │ log_context    │                │
│  └────────────────┘  └────────────────┘                │
└─────────────────────────────────────────────────────────┘
         │                          │
         ▼                          ▼
   ┌──────────┐              ┌────────────┐
   │  P40     │              │  RTX 3050  │
   │ 24 GB    │  ←→ Tensor   │ 7.4 GB     │
   │ Peseti   │  copy        │ Kernel     │
   │ KV cache │  (PCIe)      │ execution  │
   └──────────┘              └────────────┘

Installazione

Prerequisiti

  • Python 3.12+
  • PyTorch 2.7.0+cu128
  • ExLlamaV2 v0.3.2+
  • Due GPU CUDA: P40 (cuda:0) e RTX 3050 (cuda:1)

Installazione dipendenze

python3 -m pip install --user --break-system-packages torch torchvision torchaudio
python3 -m pip install --user --break-system-packages exllamav2

Configurazione

La configurazione usa variabili d'ambiente o un file JSON:

# Variabili d'ambiente
export ROUTER_WEIGHTS_DEVICE=cuda:0
export ROUTER_COMPUTE_DEVICE=cuda:1
export ROUTER_PREFETCH_LAYERS=2
export ROUTER_MODE=layer_by_layer

Oppure crea un file router_config.json:

{
    "device": {
        "weights_device": "cuda:0",
        "compute_device": "cuda:1",
        "fallback_device": "cuda:1"
    },
    "migration": {
        "prefetch_layers": 2,
        "prefetch_async": true,
        "use_pinned_memory": true
    },
    "kv_cache": {
        "max_seq_len": 4096,
        "head_dim": 0
    },
    "error_handler": {
        "max_retries": 3
    },
    "mode": "layer_by_layer"
}

Uso

Integrazione con ExLlamaV2

import torch
from exllamav2 import ExLlamaV2, ExLlamaV2Config, ExLlamaV2Tokenizer
from config import RouterConfig
from cuda_router import CudaRouter

# 1. Carica modello sulla P40
config = ExLlamaV2Config()
config.model_dir = "/path/to/model"
model = ExLlamaV2(config)
model.load()

# 2. Configura router
router_config = RouterConfig()
router_config.kv_cache.max_seq_len = 4096

# 3. Crea e attiva router
router = CudaRouter(model, router_config)
router.attach_hooks()

# 4. Genera testo
output = router.generate("Il tuo prompt qui", max_new_tokens=256)
print(output)

# 5. Disattiva router quando finito
router.detach_hooks()

Uso standalone

from config import RouterConfig
from cuda_router import CudaRouter

# Caricamento da file JSON
config = RouterConfig.from_file("/path/to/router_config.json")

# Verifica configurazione
print(config.summary())

# Crea router
router = CudaRouter(model, config)

# Verifica dispositivi
router._verify_devices()

# Attiva routing
router.attach_hooks()

Esempi

Esempio 1: Configurazione base

from config import RouterConfig
from cuda_router import CudaRouter

config = RouterConfig()
config.kv_cache.max_seq_len = 8192
config.migration.prefetch_layers = 4

router = CudaRouter(model, config)
router.attach_hooks()

Esempio 2: Modalità full copy

config = RouterConfig()
config.mode = "full_copy"  # Copia tutti i layer su RTX 3050

router = CudaRouter(model, config)
router.attach_hooks()

Esempio 3: Debug con logging

from config import RouterConfig, LogLevel

config = RouterConfig()
config.logging.level = LogLevel.DEBUG
config.logging.log_file = "/tmp/router_debug.log"
config.migration.verify_copy_integrity = True

router = CudaRouter(model, config)

Esempio 4: Caricamento da variabili d'ambiente

from config import RouterConfig

config = RouterConfig.from_env()
router = CudaRouter(model, config)

Stato

COMPLETATO — 2026-06-06

Fase Stato Data
Fase 1 — Analisi e design ✅ Completata 2026-06-06
Fase 2 — Implementazione core ✅ Completata 2026-06-06
Fase 3 — Test e benchmark ⏳ In attesa —
Fase 4 — Documentazione vault ✅ Completata 2026-06-06

File del progetto

File Righe Descrizione
config.py ~150 Configurazione centralizzata (5 dataclass + from_env/from_file)
error_handler.py ~180 Gestione errori CUDA (decoratori, retry, logging)
layer_migration.py ~200 Migrazione layer tra GPU (snapshot, cache, prefetch async)
kv_cache_manager.py ~180 Gestione KV cache (block, manager, overflow protection)
cuda_router.py ~350 Core router (hook, forward layer-by-layer, ExLlamaV2 wrapper)
README.md — Questa documentazione
PROGRESS.md — Cronologia avanzamento

Scoperte chiave

  1. PyTorch 2.7 minimo sm_7.5 — la P40 (sm_6.1) è completamente incompatibile per esecuzione kernel
  2. Cross-device tensor copy funziona — i tensori possono essere spostati tra GPU
  3. llama-mtp 5d246a7 gira già sulla P40 a 44 tok/s — il router è utile solo per casi specifici ExLlamaV2
  4. VRAM RTX 3050 disponibile: ~7.4 GB (0.6 GB usato da Xorg)
  5. VRAM P40 disponibile: ~22 GB (modello 35B Q4_K_XL usa ~22 GB)

About

Dynamic CUDA router for ExLlamaV2: auto-fallback from P40 (sm_61) to RTX 3050

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages