dist: ROOT CAUSE crash dense-HTP = matmul HMX (fp16) ; fix = HVX pour le dense
Analyse complète (méthode SSM_CONV) du crash dense-HTP: - bench HTP synthétique (pp512, +tg8) = OK ; cli/engine texte réel crashe 0x2e > ~14 tokens. - OPFILTER bisect: SOFT_MAX/ROPE/RMS_NORM/GET_ROWS/CPY/CONT/ADD n'arrêtent PAS le crash ; MUL_MAT->CPU OUI. - GGML_HEXAGON_USE_HMX=0 (matmul HVX) supprime le crash, =1 le reproduit. => le matmul HMX (fp16 tile) faute sur les activations réelles du dense (massive activations Qwen3 > plage fp16 ; bench synthétique ne déclenche pas l'overflow). Analogue du SSM_CONV pour la 3.5, mais ici c'est le matmul HMX coeur. Câblage: lecture general.architecture dans le GGUF AVANT init backend -> hybride (qwen35/qwen3next): HMX on + option C (préservé) ; dense: USE_HMX=0 + HTP contexte-unique (HVX, ~40 prefill =3x CPU, decode ~11, stable). Repli CPU si pas de HTP. Validé test_jni_native: dense (HVX HTP) + 3.5 (option C) cohérents. TODO backend: matmul HMX robuste aux activations hors plage fp16. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c166391f49
commit
0407ac8b71
|
|
@ -3,18 +3,20 @@
|
|||
Point d'entrée unique. Remplace le LLM ExecuTorch/Genie par llama.cpp (fork ql) + Hexagon.
|
||||
GGUF, pas de `.pte`. STT reste ORT-QAIRT (inchangé).
|
||||
|
||||
## Générique : fait tourner N'IMPORTE QUEL LLM (détection de STRUCTURE au `load`)
|
||||
Le JNI détecte la structure du modèle via **`llama_model_is_hybrid` / `llama_model_is_recurrent`** (pas un
|
||||
match de string d'archi) et choisit le chemin :
|
||||
- **Hybride GDN (qwen3.5 / qwen3next)** → **option C** : prefill HTP → transfert KV → decode CPU (le decode
|
||||
GDN sur HTP est lent, on le garde CPU). Validé (3.5 : mono-tour + mémoire conversationnelle).
|
||||
- **Dense (qwen3, llama, …) ou pas de HTP** → **CPU pur** : universel, robuste, jamais de crash. Validé (dense
|
||||
Qwen3-4B cohérent).
|
||||
⚠ **Pourquoi pas le dense sur HTP** : le **prefill dense sur HTP crashe (`dspqueue_read 0x2e`)** sur prompt
|
||||
réaliste (reproduit aussi en `llama-cli -dev HTP0 -fa 1` ; un prompt minuscule sans fa passait par chance) —
|
||||
bug backend HTP de l'attention dense multi-token, non corrigé. Et le dense a un `.pte` rapide (prefill 451)
|
||||
de toute façon. Donc dense → CPU = le bon choix robuste.
|
||||
Les deux cibles (dense + 3.5) tournent, validées via `jni/test_jni_native.cpp` (détection + sortie cohérente).
|
||||
## Générique : fait tourner N'IMPORTE QUEL LLM (détecté à l'archi, au `load`, les deux sur HTP)
|
||||
Le JNI lit `general.architecture` dans l'en-tête GGUF **avant l'init backend** (pour régler `USE_HMX`), puis route :
|
||||
- **Hybride GDN (qwen3.5 / qwen3next)** → **option C** : prefill HTP (**HMX on**) → transfert KV → decode CPU
|
||||
(le decode GDN sur HTP est lent, on le garde CPU). Prefill ~110-180. Validé (mono-tour + mémoire).
|
||||
- **Dense (qwen3, …)** → **HTP contexte-unique, matmul HVX (`USE_HMX=0`)** : prefill+decode sur HTP, ~40 prefill
|
||||
(≈3× le CPU 14), decode ~11. Validé (qwen3-4B cohérent, plus de crash).
|
||||
- **pas de HTP / échec** → CPU pur (repli universel).
|
||||
|
||||
⚠ **Root cause du crash dense-HTP (élucidé)** : le **matmul HMX (fp16 tile)** faute (`dspqueue_read 0x2e`) sur les
|
||||
**activations réelles** du dense (« massive activations » de Qwen3 > plage fp16 ; invisible en bench synthétique,
|
||||
déclenché par du vrai texte > ~14 tokens). Confirmé : `GGML_HEXAGON_USE_HMX=0` (matmul HVX) supprime le crash,
|
||||
`=1` le reproduit ; et `OPFILTER=MUL_MAT` (matmuls→CPU) aussi. → dense en HVX sur HTP. La 3.5 garde HMX (elle ne
|
||||
faute pas). TODO backend possible : rendre le matmul HMX robuste aux activations hors plage fp16 (clamp/scale).
|
||||
Les deux cibles tournent sur HTP, validées via `jni/test_jni_native.cpp`.
|
||||
|
||||
## Décisions figées cette session
|
||||
- **Modèle Speaker = Qwen3.5-4B en variante `q35-lmq4.gguf`** (embeds en Q4 au lieu du Q6_K
|
||||
|
|
|
|||
|
|
@ -15,6 +15,18 @@
|
|||
#include <cstring>
|
||||
#include "llama.h"
|
||||
#include "ggml-backend.h"
|
||||
#include "gguf.h"
|
||||
|
||||
// Lit general.architecture dans l'en-tête GGUF SANS init backend (pour décider USE_HMX avant).
|
||||
static std::string read_arch(const char* path) {
|
||||
struct gguf_init_params gp = { /*no_alloc=*/true, /*ctx=*/nullptr };
|
||||
gguf_context* g = gguf_init_from_file(path, gp);
|
||||
if (!g) return "";
|
||||
int64_t kid = gguf_find_key(g, "general.architecture");
|
||||
std::string arch = (kid >= 0) ? gguf_get_val_str(g, kid) : "";
|
||||
gguf_free(g);
|
||||
return arch;
|
||||
}
|
||||
|
||||
struct KEngine {
|
||||
llama_model* m_h; llama_context* c_h; // prefill HTP (nullptr si pas de HTP)
|
||||
|
|
@ -80,33 +92,43 @@ Java_com_kazeia_llm_EngineJni_load(JNIEnv* e, jobject, jstring path, jint nctx)
|
|||
std::string p(path_c);
|
||||
e->ReleaseStringUTFChars(path, path_c);
|
||||
|
||||
// Détection d'archi AVANT init backend (USE_HMX est latché à l'init).
|
||||
std::string arch = read_arch(p.c_str());
|
||||
const bool hybrid = (arch.find("qwen3next") != std::string::npos) || (arch == "qwen35");
|
||||
|
||||
// Le matmul HMX faute (0x2e) sur les activations réelles du DENSE (massive activations > plage fp16).
|
||||
// -> HMX OFF pour le dense (matmul HVX, stable, ~3x CPU). L'hybride (3.5) garde HMX (prefill rapide, OK).
|
||||
if (!hybrid) setenv("GGML_HEXAGON_USE_HMX", "0", 1);
|
||||
setenv("GGML_HEXAGON_GDN_PREFILL", "1", 1); // sans effet sur les modèles sans GDN
|
||||
llama_backend_init();
|
||||
|
||||
llama_model* m_h = nullptr; llama_context* c_h = nullptr;
|
||||
llama_model* m_c = nullptr; llama_context* c_c = nullptr;
|
||||
|
||||
// Instance CPU — toujours chargée (universelle) et sert à détecter la structure.
|
||||
auto mp_c = llama_model_default_params(); mp_c.n_gpu_layers = 0;
|
||||
m_c = llama_model_load_from_file(p.c_str(), mp_c);
|
||||
if (!m_c) return 0;
|
||||
c_c = make_ctx(m_c, nctx, 4, LLAMA_FLASH_ATTN_TYPE_ENABLED); // decode/prefill CPU
|
||||
|
||||
const bool hybrid = llama_model_is_hybrid(m_c) || llama_model_is_recurrent(m_c);
|
||||
ggml_backend_dev_t htp = find_htp();
|
||||
|
||||
if (hybrid && htp) {
|
||||
// qwen3.5-like (GDN) -> OPTION C : ajoute une instance HTP pour le prefill, decode reste sur c_c (CPU).
|
||||
// (decode GDN sur HTP = lent, donc on le garde CPU.)
|
||||
if (htp) {
|
||||
ggml_backend_dev_t devs[2] = { htp, nullptr };
|
||||
auto mp_h = llama_model_default_params(); mp_h.n_gpu_layers = 99; mp_h.devices = devs;
|
||||
m_h = llama_model_load_from_file(p.c_str(), mp_h);
|
||||
if (m_h) c_h = make_ctx(m_h, nctx, 8, LLAMA_FLASH_ATTN_TYPE_ENABLED); // prefill HTP
|
||||
fprintf(stderr, "kazeia-engine: modèle HYBRIDE (GDN) -> option C (prefill HTP / decode CPU)\n");
|
||||
}
|
||||
if (m_h && hybrid) {
|
||||
// qwen3.5-like (GDN) -> OPTION C : prefill HTP (HMX) + instance CPU pour le decode (decode GDN HTP = lent).
|
||||
c_h = make_ctx(m_h, nctx, 8, LLAMA_FLASH_ATTN_TYPE_ENABLED);
|
||||
auto mp_c = llama_model_default_params(); mp_c.n_gpu_layers = 0;
|
||||
m_c = llama_model_load_from_file(p.c_str(), mp_c);
|
||||
if (m_c) c_c = make_ctx(m_c, nctx, 4, LLAMA_FLASH_ATTN_TYPE_ENABLED);
|
||||
fprintf(stderr, "kazeia-engine: HYBRIDE GDN (%s) -> option C, prefill HTP+HMX / decode CPU\n", arch.c_str());
|
||||
} else if (m_h) {
|
||||
// dense -> HTP contexte-unique, matmul HVX (HMX off) : prefill+decode HTP, stable, ~3x CPU.
|
||||
c_h = make_ctx(m_h, nctx, 4, LLAMA_FLASH_ATTN_TYPE_ENABLED);
|
||||
fprintf(stderr, "kazeia-engine: DENSE (%s) -> HTP contexte-unique, matmul HVX (HMX off)\n", arch.c_str());
|
||||
} else {
|
||||
// dense (qwen3, llama, ...) ou pas de HTP -> CPU pur. Le prefill dense sur HTP crashe (0x2e,
|
||||
// bug backend), et le dense a un .pte rapide ; le CPU est le chemin robuste et universel.
|
||||
fprintf(stderr, "kazeia-engine: modèle DENSE / autre -> CPU pur (prefill+decode CPU)\n");
|
||||
// pas de HTP (ou échec du load HTP) -> CPU pur universel.
|
||||
auto mp_c = llama_model_default_params(); mp_c.n_gpu_layers = 0;
|
||||
m_c = llama_model_load_from_file(p.c_str(), mp_c);
|
||||
if (!m_c) return 0;
|
||||
c_c = make_ctx(m_c, nctx, 4, LLAMA_FLASH_ATTN_TYPE_ENABLED);
|
||||
fprintf(stderr, "kazeia-engine: CPU pur (%s, pas de HTP)\n", arch.c_str());
|
||||
}
|
||||
|
||||
auto* k = new KEngine{ m_h, c_h, m_c, c_c,
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue