mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Chargement du modèle : --mlock seul ne coupe plus le mmap
UN VRAI BUG D'ABORD normalizeLoadFlags traduisait --mlock seul en « --load-mode mlock ». Or, pour llama.cpp (common/arg.cpp), « mlock » veut dire PAS de mmap + résident : le modèle entier montait en RAM au lieu d'être mappé. L'ancien --mlock, lui, gardait le mmap — son équivalent est « mmap+mlock ». Sur un modèle plus gros que la RAM (Qwen3.8-Flash-Next, 82 Go pour 64 Go), un preset qui avait coché « Garder en RAM » tournait donc à l'OOM dès qu'un moteur récent prenait le relais. Correspondance alignée sur AJEAN 0.16.0 : --mlock seul → mmap+mlock · --no-mmap → none · les deux → mlock. DANS LES DEUX SENS Un moteur ancien (ou un fork) ne connaît pas --load-mode et sort en erreur si on le lui passe : downgradeLoadMode le retraduit en anciens drapeaux (dio, inconnu de ces moteurs, est abandonné avec un avertissement). UN SÉLECTEUR À LA PLACE DE DEUX INTERRUPTEURS « Garder en RAM » et « Charger tout en mémoire » ne disaient pas leur combinaison. L'éditeur de preset propose les six modes de llama.cpp (auto, mmap, none, mlock, mmap+mlock, dio), avec ce que fait chacun en sous-titre, et écrit la forme moderne. Un preset aux anciens drapeaux s'affiche sur le bon mode sans modification. Au passage, q5_1 porte la mention « lent sur CUDA » dans la liste du cache KV (voir le commit du port occupé : non accéléré en Flash-Attention). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f75f3cf27d
commit
1db987844e
5 files changed
+193
-34
No files matched your search
@@ -141,22 +141,34 @@ func hasAnyFlag(args []string, flags ...string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// normalizeLoadFlags traduit les drapeaux de chargement dépréciés vers
|
||||
// --load-mode, sur les moteurs qui le connaissent :
|
||||
// normalizeLoadFlags choisit, au lancement, la forme des drapeaux de chargement
|
||||
// que CE binaire comprend. llama.cpp récent a remplacé --mlock / --no-mmap par
|
||||
// --load-mode (et refuse les anciens) ; un moteur ancien ou un fork ne connaît
|
||||
// que les anciens. L'éditeur écrit --load-mode, un preset d'avant peut porter
|
||||
// les anciens : on traduit dans le sens qu'il faut (repris d'AJEAN 0.16.0).
|
||||
//
|
||||
// --mlock → --load-mode mlock (garder le modèle en RAM)
|
||||
// --no-mmap → --load-mode none (tout charger, pas de mmap)
|
||||
// --mmap → --load-mode mmap
|
||||
// Vers --load-mode (moteur récent). Deux interrupteurs → une seule valeur, au
|
||||
// sens où llama.cpp l'entend (« mlock » = PAS de mmap + résident) :
|
||||
//
|
||||
// Les deux à la fois (--mlock --no-mmap) : mlock l'emporte, c'est l'intention la
|
||||
// plus forte (résident en RAM, jamais rendu au système). Un --load-mode déjà
|
||||
// écrit à la main dans EXTRA_ARGS gagne sur tout : on retire alors simplement
|
||||
// les vieux drapeaux. Sur un moteur ancien, rien n'est touché.
|
||||
// --mlock seul → mmap+mlock (l'ancien sens de --mlock : mmap conservé)
|
||||
// --no-mmap seul → none
|
||||
// --mlock + --no-mmap → mlock
|
||||
// --mmap → mmap
|
||||
//
|
||||
// ⚠️ Jusqu'ici --mlock seul devenait « mlock », qui COUPE le mmap : le modèle
|
||||
// entier montait en RAM au lieu d'être mappé — un OOM assuré sur un modèle plus
|
||||
// gros que la mémoire. Un --load-mode déjà écrit gagne sur tout : on retire
|
||||
// alors simplement les vieux drapeaux.
|
||||
//
|
||||
// Vers les anciens drapeaux (moteur qui ne connaît pas --load-mode) :
|
||||
//
|
||||
// none → --no-mmap · mlock → --mlock --no-mmap · mmap+mlock → --mlock
|
||||
// auto, mmap, dio → rien (défaut de l'ancien moteur)
|
||||
func normalizeLoadFlags(args []string, supportsLoadMode bool) []string {
|
||||
if !supportsLoadMode {
|
||||
return args
|
||||
return downgradeLoadMode(args)
|
||||
}
|
||||
explicit := hasAnyFlag(args, "--load-mode")
|
||||
explicit := hasAnyFlag(args, "--load-mode", "-lm")
|
||||
mlock, nommap, mmap := false, false, false
|
||||
out := make([]string, 0, len(args)+2)
|
||||
for _, a := range args {
|
||||
@@ -176,14 +188,62 @@ func normalizeLoadFlags(args []string, supportsLoadMode bool) []string {
|
||||
}
|
||||
mode := "mmap"
|
||||
switch {
|
||||
case mlock:
|
||||
case mlock && nommap:
|
||||
mode = "mlock"
|
||||
case mlock:
|
||||
mode = "mmap+mlock"
|
||||
case nommap:
|
||||
mode = "none"
|
||||
}
|
||||
return append(out, "--load-mode", mode)
|
||||
}
|
||||
|
||||
// downgradeLoadMode retraduit un --load-mode pour un moteur qui ne le connaît
|
||||
// pas : le lui passer tel quel le ferait sortir en erreur au démarrage.
|
||||
func downgradeLoadMode(args []string) []string {
|
||||
mode, found := "", false
|
||||
kept := make([]string, 0, len(args))
|
||||
for i := 0; i < len(args); i++ {
|
||||
a := args[i]
|
||||
if a == "--load-mode" || a == "-lm" {
|
||||
found = true
|
||||
if i+1 < len(args) {
|
||||
mode = strings.ToLower(args[i+1])
|
||||
i++
|
||||
}
|
||||
continue
|
||||
}
|
||||
if v, ok := strings.CutPrefix(a, "--load-mode="); ok {
|
||||
found, mode = true, strings.ToLower(v)
|
||||
continue
|
||||
}
|
||||
kept = append(kept, a)
|
||||
}
|
||||
if !found {
|
||||
return args
|
||||
}
|
||||
add := func(flag string) {
|
||||
for _, a := range kept {
|
||||
if a == flag {
|
||||
return
|
||||
}
|
||||
}
|
||||
kept = append(kept, flag)
|
||||
}
|
||||
switch mode {
|
||||
case "none":
|
||||
add("--no-mmap")
|
||||
case "mlock":
|
||||
add("--mlock")
|
||||
add("--no-mmap")
|
||||
case "mmap+mlock":
|
||||
add("--mlock")
|
||||
case "dio":
|
||||
fmt.Fprintf(os.Stderr, "[loki serve] ce moteur ne connaît pas --load-mode dio (DirectIO) : chargement par défaut\n")
|
||||
}
|
||||
return kept
|
||||
}
|
||||
|
||||
// cmdServe replaces the historic start.sh: read config.env, build the
|
||||
// llama-server invocation, and exec it (replacing this process so systemd
|
||||
// supervises llama-server directly).
|
||||
|
||||
@@ -33,16 +33,25 @@ func TestNormalizeLoadFlags(t *testing.T) {
|
||||
supports bool
|
||||
want []string
|
||||
}{
|
||||
{"moteur ancien : rien ne bouge", []string{"--mlock", "--no-mmap"}, false,
|
||||
{"moteur ancien : anciens drapeaux intacts", []string{"--mlock", "--no-mmap"}, false,
|
||||
[]string{"--mlock", "--no-mmap"}},
|
||||
{"mlock traduit", []string{"--mlock", "-fa"}, true,
|
||||
[]string{"-fa", "--load-mode", "mlock"}},
|
||||
// L'ancien --mlock gardait le mmap : « mlock » de llama.cpp le coupe.
|
||||
{"mlock seul → mmap+mlock", []string{"--mlock", "-fa"}, true,
|
||||
[]string{"-fa", "--load-mode", "mmap+mlock"}},
|
||||
{"no-mmap traduit", []string{"--no-mmap"}, true,
|
||||
[]string{"--load-mode", "none"}},
|
||||
{"mmap traduit", []string{"--mmap"}, true,
|
||||
[]string{"--load-mode", "mmap"}},
|
||||
{"mlock l'emporte sur no-mmap", []string{"--no-mmap", "--mlock"}, true,
|
||||
{"mlock + no-mmap → mlock", []string{"--no-mmap", "--mlock"}, true,
|
||||
[]string{"--load-mode", "mlock"}},
|
||||
{"moteur ancien : none retraduit", []string{"-fa", "--load-mode", "none"}, false,
|
||||
[]string{"-fa", "--no-mmap"}},
|
||||
{"moteur ancien : mlock retraduit", []string{"--load-mode", "mlock"}, false,
|
||||
[]string{"--mlock", "--no-mmap"}},
|
||||
{"moteur ancien : mmap+mlock retraduit", []string{"-lm", "mmap+mlock"}, false,
|
||||
[]string{"--mlock"}},
|
||||
{"moteur ancien : dio abandonné", []string{"--load-mode=dio", "-fa"}, false,
|
||||
[]string{"-fa"}},
|
||||
{"--load-mode explicite gagne, vieux drapeaux retirés",
|
||||
[]string{"--mlock", "--load-mode", "dio"}, true,
|
||||
[]string{"--load-mode", "dio"}},
|
||||
|
||||
@@ -3440,7 +3440,7 @@ html[data-files="1"] #files-btn{color:var(--accent)}
|
||||
<option value="">f16 (max qualité)</option>
|
||||
<option value="q8_0">q8_0 (léger)</option>
|
||||
<option value="q4_0">q4_0 (très léger)</option>
|
||||
<option value="q5_1">q5_1</option>
|
||||
<option value="q5_1">q5_1 (lent sur CUDA)</option>
|
||||
<option value="bf16">bf16</option>
|
||||
</select></span></span>
|
||||
</div>
|
||||
@@ -3493,13 +3493,19 @@ html[data-files="1"] #files-btn{color:var(--accent)}
|
||||
<span class="pe-row-l">Flash attention<span class="pe-sub">plus rapide, moins de VRAM</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-flash" onchange="eaSetValued('--flash-attn', this.checked?'on':'')"><span class="pe-sl"></span></label></span>
|
||||
</div>
|
||||
<!-- Chargement du modèle : --load-mode de llama.cpp (remplace --mlock et
|
||||
--no-mmap). Pour un moteur ancien, loki serve retraduit au lancement
|
||||
(normalizeLoadFlags). -->
|
||||
<div class="pe-row">
|
||||
<span class="pe-row-l">Garder en RAM<span class="pe-sub">évite le swap (mlock)</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-mlock" onchange="eaToggleFlag('--mlock', this.checked)"><span class="pe-sl"></span></label></span>
|
||||
</div>
|
||||
<div class="pe-row">
|
||||
<span class="pe-row-l">Charger tout en mémoire<span class="pe-sub">désactive le mmap</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-nommap" onchange="eaToggleFlag('--no-mmap', this.checked)"><span class="pe-sl"></span></label></span>
|
||||
<span class="pe-row-l">Chargement<span class="pe-sub" id="s-loadmode-sub"></span></span>
|
||||
<span class="pe-row-c"><span class="pe-selc"><select id="s-loadmode" onchange="eaSetLoadMode(this.value)">
|
||||
<option value="">auto</option>
|
||||
<option value="mmap">mmap</option>
|
||||
<option value="none">none</option>
|
||||
<option value="mlock">mlock</option>
|
||||
<option value="mmap+mlock">mmap+mlock</option>
|
||||
<option value="dio">dio</option>
|
||||
</select></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5789,8 +5795,47 @@ function populateSettings(){
|
||||
}
|
||||
}
|
||||
chk('s-flash', eaHasFlag('--flash-attn') && !/^off$/i.test(eaGetValued('--flash-attn')));
|
||||
chk('s-mlock', eaHasFlag('--mlock'));
|
||||
chk('s-nommap', eaHasFlag('--no-mmap'));
|
||||
const lm = eaLoadMode();
|
||||
set('s-loadmode', lm);
|
||||
syncLoadModeSub(lm);
|
||||
}
|
||||
// Chargement du modèle. llama.cpp récent a REMPLACÉ --mlock / --no-mmap par
|
||||
// --load-mode (auto, none, mmap, mlock, mmap+mlock, dio). L'éditeur écrit la
|
||||
// forme moderne ; au lancement, loki serve la retraduit pour un moteur ancien
|
||||
// (normalizeLoadFlags côté Go), et inversement.
|
||||
function eaLoadMode(){
|
||||
const lm = (eaGetValued('--load-mode') || eaGetValued('-lm')).toLowerCase();
|
||||
if(lm) return lm === 'auto' ? '' : lm;
|
||||
// Preset écrit avec les anciens drapeaux : même correspondance que le serveur.
|
||||
const mlock = eaHasFlag('--mlock'), nommap = eaHasFlag('--no-mmap');
|
||||
if(mlock && nommap) return 'mlock';
|
||||
if(mlock) return 'mmap+mlock';
|
||||
if(nommap) return 'none';
|
||||
if(eaHasFlag('--mmap')) return 'mmap';
|
||||
return '';
|
||||
}
|
||||
function eaSetLoadMode(mode){
|
||||
const t = eaTokens().filter(x=>x!=='--mlock' && x!=='--no-mmap' && x!=='--mmap' && !x.startsWith('--load-mode='));
|
||||
for(const f of ['--load-mode','-lm']){
|
||||
const i = t.indexOf(f);
|
||||
if(i>=0){ const hadVal = i+1<t.length && !t[i+1].startsWith('-'); t.splice(i, hadVal?2:1); }
|
||||
}
|
||||
if(mode) t.push('--load-mode', mode);
|
||||
eaSetTokens(t);
|
||||
syncLoadModeSub(mode);
|
||||
}
|
||||
// Sous-titre : ce que fait le mode choisi (les options ne portent que son nom).
|
||||
const LOAD_MODE_SUB = {
|
||||
'': 'mmap, sauf si un périphérique ne le permet pas',
|
||||
'mmap': 'fichier mappé, lu à la demande',
|
||||
'none': 'tout charger, sans mmap',
|
||||
'mlock': 'tout en RAM, verrouillé (sans mmap)',
|
||||
'mmap+mlock': 'mappé et verrouillé en RAM',
|
||||
'dio': 'lecture DirectIO si disponible',
|
||||
};
|
||||
function syncLoadModeSub(mode){
|
||||
const el = document.getElementById('s-loadmode-sub');
|
||||
if(el) el.textContent = LOAD_MODE_SUB[mode] ?? mode;
|
||||
}
|
||||
// --- Décodage spéculatif (--spec-type / --spec-draft-n-max) ----------------
|
||||
// Sélectionne le type courant. --spec-type accepte en réalité une LISTE séparée
|
||||
|
||||
@@ -1232,7 +1232,7 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
|
||||
<option value="">f16 (max qualité)</option>
|
||||
<option value="q8_0">q8_0 (léger)</option>
|
||||
<option value="q4_0">q4_0 (très léger)</option>
|
||||
<option value="q5_1">q5_1</option>
|
||||
<option value="q5_1">q5_1 (lent sur CUDA)</option>
|
||||
<option value="bf16">bf16</option>
|
||||
</select></span></span>
|
||||
</div>
|
||||
@@ -1285,13 +1285,19 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
|
||||
<span class="pe-row-l">Flash attention<span class="pe-sub">plus rapide, moins de VRAM</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-flash" onchange="eaSetValued('--flash-attn', this.checked?'on':'')"><span class="pe-sl"></span></label></span>
|
||||
</div>
|
||||
<!-- Chargement du modèle : --load-mode de llama.cpp (remplace --mlock et
|
||||
--no-mmap). Pour un moteur ancien, loki serve retraduit au lancement
|
||||
(normalizeLoadFlags). -->
|
||||
<div class="pe-row">
|
||||
<span class="pe-row-l">Garder en RAM<span class="pe-sub">évite le swap (mlock)</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-mlock" onchange="eaToggleFlag('--mlock', this.checked)"><span class="pe-sl"></span></label></span>
|
||||
</div>
|
||||
<div class="pe-row">
|
||||
<span class="pe-row-l">Charger tout en mémoire<span class="pe-sub">désactive le mmap</span></span>
|
||||
<span class="pe-row-c"><label class="pe-switch"><input type="checkbox" id="s-nommap" onchange="eaToggleFlag('--no-mmap', this.checked)"><span class="pe-sl"></span></label></span>
|
||||
<span class="pe-row-l">Chargement<span class="pe-sub" id="s-loadmode-sub"></span></span>
|
||||
<span class="pe-row-c"><span class="pe-selc"><select id="s-loadmode" onchange="eaSetLoadMode(this.value)">
|
||||
<option value="">auto</option>
|
||||
<option value="mmap">mmap</option>
|
||||
<option value="none">none</option>
|
||||
<option value="mlock">mlock</option>
|
||||
<option value="mmap+mlock">mmap+mlock</option>
|
||||
<option value="dio">dio</option>
|
||||
</select></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -926,8 +926,47 @@ function populateSettings(){
|
||||
}
|
||||
}
|
||||
chk('s-flash', eaHasFlag('--flash-attn') && !/^off$/i.test(eaGetValued('--flash-attn')));
|
||||
chk('s-mlock', eaHasFlag('--mlock'));
|
||||
chk('s-nommap', eaHasFlag('--no-mmap'));
|
||||
const lm = eaLoadMode();
|
||||
set('s-loadmode', lm);
|
||||
syncLoadModeSub(lm);
|
||||
}
|
||||
// Chargement du modèle. llama.cpp récent a REMPLACÉ --mlock / --no-mmap par
|
||||
// --load-mode (auto, none, mmap, mlock, mmap+mlock, dio). L'éditeur écrit la
|
||||
// forme moderne ; au lancement, loki serve la retraduit pour un moteur ancien
|
||||
// (normalizeLoadFlags côté Go), et inversement.
|
||||
function eaLoadMode(){
|
||||
const lm = (eaGetValued('--load-mode') || eaGetValued('-lm')).toLowerCase();
|
||||
if(lm) return lm === 'auto' ? '' : lm;
|
||||
// Preset écrit avec les anciens drapeaux : même correspondance que le serveur.
|
||||
const mlock = eaHasFlag('--mlock'), nommap = eaHasFlag('--no-mmap');
|
||||
if(mlock && nommap) return 'mlock';
|
||||
if(mlock) return 'mmap+mlock';
|
||||
if(nommap) return 'none';
|
||||
if(eaHasFlag('--mmap')) return 'mmap';
|
||||
return '';
|
||||
}
|
||||
function eaSetLoadMode(mode){
|
||||
const t = eaTokens().filter(x=>x!=='--mlock' && x!=='--no-mmap' && x!=='--mmap' && !x.startsWith('--load-mode='));
|
||||
for(const f of ['--load-mode','-lm']){
|
||||
const i = t.indexOf(f);
|
||||
if(i>=0){ const hadVal = i+1<t.length && !t[i+1].startsWith('-'); t.splice(i, hadVal?2:1); }
|
||||
}
|
||||
if(mode) t.push('--load-mode', mode);
|
||||
eaSetTokens(t);
|
||||
syncLoadModeSub(mode);
|
||||
}
|
||||
// Sous-titre : ce que fait le mode choisi (les options ne portent que son nom).
|
||||
const LOAD_MODE_SUB = {
|
||||
'': 'mmap, sauf si un périphérique ne le permet pas',
|
||||
'mmap': 'fichier mappé, lu à la demande',
|
||||
'none': 'tout charger, sans mmap',
|
||||
'mlock': 'tout en RAM, verrouillé (sans mmap)',
|
||||
'mmap+mlock': 'mappé et verrouillé en RAM',
|
||||
'dio': 'lecture DirectIO si disponible',
|
||||
};
|
||||
function syncLoadModeSub(mode){
|
||||
const el = document.getElementById('s-loadmode-sub');
|
||||
if(el) el.textContent = LOAD_MODE_SUB[mode] ?? mode;
|
||||
}
|
||||
// --- Décodage spéculatif (--spec-type / --spec-draft-n-max) ----------------
|
||||
// Sélectionne le type courant. --spec-type accepte en réalité une LISTE séparée
|
||||
|
||||
Reference in new issue
Block a user