mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-24 15:38:12 +02:00
Placement : liaison PCIe des cartes et guide d'ordre dans l'éditeur, en conseil
Deux cartes inégales (5060 Ti + 3060) se placent par deux règles du moteur qui peuvent s'opposer : --fit remplit d'abord la DERNIÈRE carte, qui porte la couche de sortie, et les experts MoE en RAM sont recopiés au prefill vers la PREMIÈRE. Pour choisir l'ordre il faut voir la liaison de chaque carte ; l'éditeur n'en montrait rien. - detectGPUs lit aussi la liaison PCIe (génération et largeur, actuelles et maximales), le bus et l'horloge mémoire max, dans la même invocation de nvidia-smi. « [N/A] » vaut zéro ; un pilote qui refuse un champ fait retomber sur la requête historique (jamais de GPU perdu), un délai dépassé n'est pas relancé. Borné à 10 s. La largeur du bus mémoire n'existe pas dans nvidia-smi : absente. - /api/backends/devices : une seule lecture nvidia-smi par énumération complète mémoire manquante ET liaison (annotateDevices, pure), par nom de carte, CUDA seulement, rien pour deux cartes homonymes. La lecture des jauges (gpuStatsCached) n'est pas touchée. - Éditeur : liaison max par carte (l'actuelle et l'horloge en info-bulle), guide de placement dans l'ordre du moteur (--device compris), marges FIT_TARGET carte par carte, signalement d'un --tensor-split ou d'un CUDA_VISIBLE_DEVICES propre au preset, lien « inverser l'ordre » ; l'ordre choisi survit aux cases cochées. - Pas de bouton de mesure : comparer deux ordres demande deux rechargements et un ordre inversé peut manquer de VRAM — le guide explique la marche à suivre (copie du preset, bench complet sur chacun). - loki gpu affiche la liaison. Rien ne change dans la ligne de commande du moteur. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
889a85888d
commit
986a636c20
8 files changed
+481
-67
No files matched your search
@@ -115,50 +115,56 @@ func devPersistPut(key string, devs []map[string]any) {
|
||||
}
|
||||
}
|
||||
|
||||
// fillMissingMemory complète les mémoires que le moteur n'a pas su lire. Quand
|
||||
// une carte est déjà saturée par le modèle en cours, llama.cpp annonce 0 Mio —
|
||||
// l'UI n'avait alors rien à afficher pour elle, ce qui donnait une liste
|
||||
// incohérente (une carte avec sa taille, l'autre sans).
|
||||
// annotateDevices complète les cartes listées par le moteur avec ce que
|
||||
// nvidia-smi sait d'elles (UNE lecture, detectGPUs). Fonction pure.
|
||||
//
|
||||
// On complète depuis nvidia-smi PAR CORRESPONDANCE DE NOM, et uniquement pour
|
||||
// ça : la mémoire totale est une donnée matérielle fixe, indépendante du
|
||||
// backend. L'ordre et les identifiants des devices, eux, appartiennent au
|
||||
// moteur (CUDA0 et Vulkan0 ne désignent pas la même carte) et ne doivent jamais
|
||||
// venir de nvidia-smi. Un nom en double (deux cartes identiques) rend la
|
||||
// correspondance ambiguë : on préfère alors ne rien dire.
|
||||
func fillMissingMemory(devs []map[string]any) {
|
||||
missing := false
|
||||
for _, d := range devs {
|
||||
if n, _ := d["total_mib"].(int); n <= 0 {
|
||||
missing = true
|
||||
}
|
||||
}
|
||||
if !missing {
|
||||
return
|
||||
}
|
||||
gpus, err := detectGPUs()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totals := map[string]int{}
|
||||
// Mémoire totale manquante : quand une carte est déjà saturée par le modèle en
|
||||
// cours, llama.cpp annonce 0 Mio — l'UI n'avait alors rien à afficher pour
|
||||
// elle, ce qui donnait une liste incohérente (une carte avec sa taille, l'autre
|
||||
// sans). Liaison PCIe et horloge mémoire (clé « link ») : de quoi choisir
|
||||
// l'ordre des cartes en connaissance de cause (voir le guide de placement de
|
||||
// l'éditeur), jamais appliqué d'office.
|
||||
//
|
||||
// Tout passe PAR CORRESPONDANCE DE NOM : ce sont des données matérielles fixes,
|
||||
// indépendantes du backend. L'ordre et les identifiants des devices, eux,
|
||||
// appartiennent au moteur (CUDA0 et Vulkan0 ne désignent pas la même carte) et
|
||||
// ne doivent jamais venir de nvidia-smi. Un nom en double (deux cartes
|
||||
// identiques) rend la correspondance ambiguë : on préfère alors ne rien dire.
|
||||
// La liaison ne s'attache qu'aux cartes CUDA : un moteur Vulkan énumère dans
|
||||
// son propre ordre, et le conseil qui l'accompagne parle de CUDA.
|
||||
func annotateDevices(devs []map[string]any, gpus []gpuInfo) {
|
||||
byName := map[string]gpuInfo{}
|
||||
dup := map[string]bool{}
|
||||
for _, g := range gpus {
|
||||
name := strings.TrimSpace(g.Name)
|
||||
if _, seen := totals[name]; seen {
|
||||
if _, seen := byName[name]; seen {
|
||||
dup[name] = true
|
||||
continue
|
||||
}
|
||||
if mb, err := strconv.Atoi(strings.TrimSpace(g.MemTotal)); err == nil {
|
||||
totals[name] = mb
|
||||
}
|
||||
byName[name] = g
|
||||
}
|
||||
for _, d := range devs {
|
||||
if n, _ := d["total_mib"].(int); n > 0 {
|
||||
name, _ := d["name"].(string)
|
||||
name = strings.TrimSpace(name)
|
||||
g, ok := byName[name]
|
||||
if !ok || dup[name] {
|
||||
continue
|
||||
}
|
||||
name, _ := d["name"].(string)
|
||||
if mb, ok := totals[strings.TrimSpace(name)]; ok && !dup[strings.TrimSpace(name)] {
|
||||
d["total_mib"] = mb
|
||||
if n, _ := d["total_mib"].(int); n <= 0 {
|
||||
if mb, err := strconv.Atoi(strings.TrimSpace(g.MemTotal)); err == nil {
|
||||
d["total_mib"] = mb
|
||||
}
|
||||
}
|
||||
if id, _ := d["id"].(string); !strings.HasPrefix(id, "CUDA") {
|
||||
continue
|
||||
}
|
||||
if g.LinkGenMax == 0 && g.LinkWidthMax == 0 && g.MemClockMax == 0 {
|
||||
continue
|
||||
}
|
||||
d["link"] = map[string]any{
|
||||
"gen": g.LinkGen, "gen_max": g.LinkGenMax,
|
||||
"width": g.LinkWidth, "width_max": g.LinkWidthMax,
|
||||
"mem_clock_mhz": g.MemClockMax,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +244,12 @@ func handleBackendDevices(w http.ResponseWriter, r *http.Request) {
|
||||
sendJSON(w, 200, map[string]any{"ok": true, "devices": devs, "stale": true})
|
||||
return
|
||||
}
|
||||
fillMissingMemory(devs)
|
||||
// Une seule lecture nvidia-smi par énumération (et l'énumération est en
|
||||
// cache dix minutes) : mémoire manquante et liaison PCIe ensemble. Sans
|
||||
// nvidia-smi (Mac, AMD), rien n'est ajouté.
|
||||
if gpus, gerr := detectGPUs(); gerr == nil {
|
||||
annotateDevices(devs, gpus)
|
||||
}
|
||||
// Quand une carte est déjà saturée par le modèle en cours, le moteur peut
|
||||
// annoncer 0 Mio de mémoire : c'est une lecture transitoire, on ne la fige
|
||||
// pas dans le cache (sinon l'UI affiche « 0 Go » pendant dix minutes).
|
||||
|
||||
Reference in new issue
Block a user