chantier F (CP HMX) : path sched livré, gain absent (overhead per-call)
cp_inference.h/.cpp : nouveau cp_load_with_backends + sched path dans
cp_forward_cached_step. KV cache aussi alloué sur backend buffer en mode sched
(no_alloc=true + ggml_backend_alloc_ctx_tensors_from_buft). Inputs (x, pos_ids,
mask) marqués ggml_set_input ; output ggml_set_output. Compute via sched_reset
+ alloc + tensor_set + sched_compute + tensor_get. Path legacy CPU pur (sans
sched) reste intact via détection s.sched.
tts_engine.cpp : KZTTS_CP_HTP=1 + KZTTS_CP_SCHED=1 active le path sched HMX.
KZTTS_CP_HTP=1 seul = poids HTP, compute CPU pur via opt_hostbuf=1 (baseline).
Mesures Pad3 (KZTTS_CP_CACHE=1, seed=42, 'Bonjour Kazeia') :
CPU baseline : CP 111 ms/frame, RTF 2.95, N=33
CP poids HTP, compute CPU : CP 108 ms/frame, RTF 2.93, N=33 (~baseline)
CP sched HMX (HTP-routé) : CP 181 ms/frame, RTF 3.87, N=64 REGRESSION
GGML_SCHED_DEBUG=2 confirme que ~95% des MUL_MAT CP tombent bien sur HTP0
(blk.0.attn_q.weight : 360 HTP / 21 CPU). Donc HMX est techniquement actif.
Cause de la régression :
- Overhead par sub-forward sched_reset + sched_alloc_graph + tensor_set/get
× 15 sub-forwards par frame. ~5 ms/appel × 15 = 75 ms ajoutés par frame
(cohérent avec 111 -> 181 ms).
- N=64 vs N=33 = trajectoire talker diverge à cause des codes CP qui
diffèrent (perte précision HMX f16 tile vs CPU NEON f32).
Pour livrer un gain CP HMX réel, il faudrait :
- (a) ctx persistant entre sub-forwards (~refactor architectural)
- (b) batcher les 15 sub-forwards en 1 graph autoregressif (~lourd)
Le path sched CP est laissé en opt-in (KZTTS_CP_SCHED=1, désactivé par défaut)
pour itérer dessus ultérieurement. KZTTS_CP_HTP=1 seul est neutre (~baseline).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bdd528ed25
commit
48c66c03e1
|
|
@ -1,6 +1,7 @@
|
|||
#include "cp_inference.h"
|
||||
#include <ggml.h>
|
||||
#include <gguf.h>
|
||||
#include <ggml-backend.h>
|
||||
#include <ggml-cpu.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
|
@ -108,33 +109,164 @@ bool cp_load(CPState& s, const char* gguf_path, const char* heads_path, const ch
|
|||
return true;
|
||||
}
|
||||
|
||||
// Variante backend-aware (Phase F : CP forward sur HMX V79). Charge les poids sur
|
||||
// le buft du 1er backend (HTP), crée un sched, et le forward sched-friendly est
|
||||
// activé via cp_forward_cached_step_sched.
|
||||
bool cp_load_with_backends(CPState& s, const char* gguf_path,
|
||||
const char* heads_path, const char* embs_path,
|
||||
const std::vector<ggml_backend_t>& devs,
|
||||
bool owns) {
|
||||
if (devs.empty()) { fprintf(stderr, "cp_load_with_backends: empty backends\n"); return false; }
|
||||
s.backends = devs;
|
||||
s.owns_backends = owns;
|
||||
ggml_backend_buffer_type_t buft_w = ggml_backend_get_default_buffer_type(s.backends[0]);
|
||||
|
||||
// 1) Lire metadata gguf (no_alloc=true)
|
||||
ggml_context* meta = nullptr;
|
||||
gguf_init_params p; p.no_alloc = true; p.ctx = &meta;
|
||||
gguf_context* g = gguf_init_from_file(gguf_path, p);
|
||||
if (!g) { fprintf(stderr, "cp_load_with_backends: gguf open fail %s\n", gguf_path); return false; }
|
||||
int64_t n = gguf_get_n_tensors(g);
|
||||
|
||||
// 2) Créer ctx weights no_alloc=true (juste metadata des tensors)
|
||||
size_t mem_meta = (size_t)n * ggml_tensor_overhead() + (1u << 20);
|
||||
ggml_init_params ip = { mem_meta, nullptr, /*no_alloc=*/true };
|
||||
s.weights_ctx = ggml_init(ip);
|
||||
|
||||
// 3) Pour chaque tenseur du gguf, créer le tensor avec son type final dans s.weights_ctx
|
||||
// Règle conservée : F16 source + nom != "_norm.weight" -> garde F16 (matmul-friendly).
|
||||
// sinon -> F32 (norms, etc.).
|
||||
size_t kept_f16 = 0, conv_f32 = 0;
|
||||
for (int64_t i = 0; i < n; i++) {
|
||||
const char* name = gguf_get_tensor_name(g, i);
|
||||
ggml_tensor* mt = ggml_get_tensor(meta, name);
|
||||
ggml_type dst_type = GGML_TYPE_F32;
|
||||
if (mt->type == GGML_TYPE_F16 && cp_keep_f16(name)) dst_type = GGML_TYPE_F16;
|
||||
ggml_tensor* t = ggml_new_tensor(s.weights_ctx, dst_type, ggml_n_dims(mt), mt->ne);
|
||||
ggml_set_name(t, name);
|
||||
s.tensors[name] = t;
|
||||
if (dst_type == GGML_TYPE_F16) kept_f16++; else conv_f32++;
|
||||
}
|
||||
|
||||
// 4) Allouer le buffer backend pour tous les tenseurs du ctx d'un coup
|
||||
s.weights_buf = ggml_backend_alloc_ctx_tensors_from_buft(s.weights_ctx, buft_w);
|
||||
if (!s.weights_buf) {
|
||||
fprintf(stderr, "cp_load_with_backends: weights buf alloc FAIL\n");
|
||||
gguf_free(g); ggml_free(meta); return false;
|
||||
}
|
||||
ggml_backend_buffer_set_usage(s.weights_buf, GGML_BACKEND_BUFFER_USAGE_WEIGHTS);
|
||||
|
||||
// 5) Lire les data depuis le gguf et upload via ggml_backend_tensor_set
|
||||
FILE* f = fopen(gguf_path, "rb");
|
||||
const size_t off = gguf_get_data_offset(g);
|
||||
std::vector<uint8_t> tmp;
|
||||
std::vector<float> tmp_f32;
|
||||
for (int64_t i = 0; i < n; i++) {
|
||||
const char* name = gguf_get_tensor_name(g, i);
|
||||
ggml_tensor* mt = ggml_get_tensor(meta, name);
|
||||
ggml_tensor* t = s.tensors[name];
|
||||
size_t nb_src = ggml_nbytes(mt);
|
||||
tmp.resize(nb_src);
|
||||
fseek(f, off + gguf_get_tensor_offset(g, i), SEEK_SET);
|
||||
if (fread(tmp.data(), 1, nb_src, f) != nb_src) {
|
||||
fprintf(stderr, "cp_load_with_backends: read fail %s\n", name); fclose(f); return false;
|
||||
}
|
||||
if (mt->type == t->type) {
|
||||
ggml_backend_tensor_set(t, tmp.data(), 0, nb_src);
|
||||
} else if (mt->type == GGML_TYPE_F16 && t->type == GGML_TYPE_F32) {
|
||||
// Convert F16 -> F32 host-side then upload.
|
||||
int64_t ne = ggml_nelements(mt);
|
||||
tmp_f32.resize(ne);
|
||||
ggml_fp16_to_fp32_row((const ggml_fp16_t*)tmp.data(), tmp_f32.data(), ne);
|
||||
ggml_backend_tensor_set(t, tmp_f32.data(), 0, (size_t)ne * sizeof(float));
|
||||
} else {
|
||||
fprintf(stderr, "cp_load_with_backends: bad cast %s -> %s for %s\n",
|
||||
ggml_type_name(mt->type), ggml_type_name(t->type), name);
|
||||
fclose(f); return false;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
gguf_free(g);
|
||||
ggml_free(meta);
|
||||
|
||||
// 6) Tables host-side (heads, codec_embs) : inchangées, utilisées en dot product CPU.
|
||||
const size_t TAB = (size_t)N_CB * N_VOCAB * N_EMBD;
|
||||
s.heads = read_floats(heads_path, TAB); if (s.heads.empty()) return false;
|
||||
s.codec_embs = read_floats(embs_path, TAB); if (s.codec_embs.empty()) return false;
|
||||
|
||||
// 7) Sched (gated par KZTTS_CP_SCHED=1 ; off par défaut tant que stage refactor non validé).
|
||||
// Sans sched, l'exec retombe sur compute_with_ctx CPU (poids HTP CPU-mappés via opt_hostbuf=1).
|
||||
if (getenv("KZTTS_CP_SCHED") && atoi(getenv("KZTTS_CP_SCHED")) != 0) {
|
||||
std::vector<ggml_backend_buffer_type_t> bufts;
|
||||
bufts.reserve(s.backends.size());
|
||||
for (auto b : s.backends) bufts.push_back(ggml_backend_get_default_buffer_type(b));
|
||||
s.sched = ggml_backend_sched_new(s.backends.data(), bufts.data(),
|
||||
(int)s.backends.size(), /*graph_size=*/8192,
|
||||
/*parallel=*/false, /*op_offload=*/true);
|
||||
if (!s.sched) { fprintf(stderr, "cp_load_with_backends: sched_new FAIL\n"); return false; }
|
||||
fprintf(stderr, "cp_load_with_backends: %lld tensors (f16=%zu f32=%zu) sur %s, sched ON\n",
|
||||
(long long)n, kept_f16, conv_f32, ggml_backend_name(s.backends[0]));
|
||||
} else {
|
||||
fprintf(stderr, "cp_load_with_backends: %lld tensors (f16=%zu f32=%zu) sur %s, sched OFF\n",
|
||||
(long long)n, kept_f16, conv_f32, ggml_backend_name(s.backends[0]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cp_free(CPState& s) {
|
||||
if (s.weights_ctx) ggml_free(s.weights_ctx);
|
||||
s.weights_ctx = nullptr;
|
||||
if (s.sched) { ggml_backend_sched_free(s.sched); s.sched = nullptr; }
|
||||
if (s.cache_buf) { ggml_backend_buffer_free(s.cache_buf); s.cache_buf = nullptr; }
|
||||
if (s.weights_buf) { ggml_backend_buffer_free(s.weights_buf); s.weights_buf = nullptr; }
|
||||
if (s.weights_ctx) { ggml_free(s.weights_ctx); s.weights_ctx = nullptr; }
|
||||
s.tensors.clear();
|
||||
s.heads.clear();
|
||||
s.codec_embs.clear();
|
||||
if (s.cache_ctx) ggml_free(s.cache_ctx);
|
||||
s.cache_ctx = nullptr;
|
||||
if (s.cache_ctx) { ggml_free(s.cache_ctx); s.cache_ctx = nullptr; }
|
||||
for (int i = 0; i < N_LAYER; i++) { s.K_cache[i] = nullptr; s.V_cache[i] = nullptr; }
|
||||
if (s.owns_backends) {
|
||||
for (auto b : s.backends) if (b) ggml_backend_free(b);
|
||||
}
|
||||
s.backends.clear();
|
||||
}
|
||||
|
||||
// Alloue le KV cache (paresseux, au 1er appel cp_predict_cached). Tensors persistants entre
|
||||
// étapes/frames (le data pointer reste stable), c'est ce qui permet à un graph par-étape de
|
||||
// lire/écrire dans le même buffer via ggml_view_3d + ggml_cpy.
|
||||
//
|
||||
// Path A (legacy CPU pur, s.sched=null) : ggml_init no_alloc=false, data sur ctx mem.
|
||||
// Path B (sched HTP, s.sched non-null) : ggml_init no_alloc=true, alloué sur backends[0]
|
||||
// buffer via ggml_backend_alloc_ctx_tensors_from_buft.
|
||||
static bool cp_cache_init(CPState& s) {
|
||||
if (s.cache_ctx) return true;
|
||||
const size_t per_tensor_bytes = (size_t)HEAD_DIM * N_KV * T_MAX * sizeof(float);
|
||||
const size_t total = (size_t)N_LAYER * 2 * (per_tensor_bytes + ggml_tensor_overhead()) + (1u << 16);
|
||||
ggml_init_params p = { total, nullptr, false };
|
||||
const bool use_sched = (s.sched != nullptr);
|
||||
if (!use_sched) {
|
||||
const size_t per_tensor_bytes = (size_t)HEAD_DIM * N_KV * T_MAX * sizeof(float);
|
||||
const size_t total = (size_t)N_LAYER * 2 * (per_tensor_bytes + ggml_tensor_overhead()) + (1u << 16);
|
||||
ggml_init_params p = { total, nullptr, /*no_alloc=*/false };
|
||||
s.cache_ctx = ggml_init(p);
|
||||
if (!s.cache_ctx) { fprintf(stderr, "cp_cache_init: ggml_init failed\n"); return false; }
|
||||
for (int L_i = 0; L_i < N_LAYER; L_i++) {
|
||||
s.K_cache[L_i] = ggml_new_tensor_3d(s.cache_ctx, GGML_TYPE_F32, HEAD_DIM, N_KV, T_MAX);
|
||||
s.V_cache[L_i] = ggml_new_tensor_3d(s.cache_ctx, GGML_TYPE_F32, HEAD_DIM, N_KV, T_MAX);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Path sched : ctx meta-only, buf backend séparé.
|
||||
size_t mem_meta = (size_t)N_LAYER * 2 * ggml_tensor_overhead() + (1u << 16);
|
||||
ggml_init_params p = { mem_meta, nullptr, /*no_alloc=*/true };
|
||||
s.cache_ctx = ggml_init(p);
|
||||
if (!s.cache_ctx) { fprintf(stderr, "cp_cache_init: ggml_init failed\n"); return false; }
|
||||
for (int L_i = 0; L_i < N_LAYER; L_i++) {
|
||||
s.K_cache[L_i] = ggml_new_tensor_3d(s.cache_ctx, GGML_TYPE_F32, HEAD_DIM, N_KV, T_MAX);
|
||||
s.V_cache[L_i] = ggml_new_tensor_3d(s.cache_ctx, GGML_TYPE_F32, HEAD_DIM, N_KV, T_MAX);
|
||||
// Pas besoin de zéro initial : les positions utilisées sont écrites avant lecture
|
||||
// (cpy ops topo-ordonnés avant les attention reads, cf cp_forward_cached_step).
|
||||
char nm[32];
|
||||
snprintf(nm, sizeof(nm), "K_cache_%d", L_i); ggml_set_name(s.K_cache[L_i], nm);
|
||||
snprintf(nm, sizeof(nm), "V_cache_%d", L_i); ggml_set_name(s.V_cache[L_i], nm);
|
||||
}
|
||||
ggml_backend_buffer_type_t buft = ggml_backend_get_default_buffer_type(s.backends[0]);
|
||||
s.cache_buf = ggml_backend_alloc_ctx_tensors_from_buft(s.cache_ctx, buft);
|
||||
if (!s.cache_buf) { fprintf(stderr, "cp_cache_init: backend alloc FAIL\n"); return false; }
|
||||
// KV cache n'est PAS un weight (il change entre frames), pas de set_usage WEIGHTS ici.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -234,25 +366,34 @@ static void cp_forward_lastpos(CPState& s, const std::vector<float>& embeds_flat
|
|||
static void cp_forward_cached_step(CPState& s, const float* embeds_new, int n_new,
|
||||
int pos_off, std::vector<float>& out_hn) {
|
||||
const int T_full = pos_off + n_new;
|
||||
// Buffer de calcul : on rebuild un graph par étape, mais chacun ne touche que n_new tokens
|
||||
// sur 5 couches. ~5-10 MB par graph en pratique ; on laisse de la marge.
|
||||
size_t mem = 32ULL * 1024 * 1024;
|
||||
ggml_init_params p = { mem, nullptr, false };
|
||||
const bool use_sched = (s.sched != nullptr);
|
||||
// mem : 32 MB pour le path legacy (no_alloc=false, alloue data activations),
|
||||
// 4 MB en mode sched (juste métadonnées des tensors, allocation backend séparée).
|
||||
size_t mem = use_sched ? (4ULL * 1024 * 1024) : (32ULL * 1024 * 1024);
|
||||
ggml_init_params p = { mem, nullptr, /*no_alloc=*/use_sched };
|
||||
ggml_context* ctx = ggml_init(p);
|
||||
|
||||
ggml_tensor* x = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, N_EMBD, n_new);
|
||||
memcpy(x->data, embeds_new, (size_t)N_EMBD * n_new * sizeof(float));
|
||||
ggml_tensor* pos_ids = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, n_new);
|
||||
for (int i = 0; i < n_new; i++) ((int32_t*)pos_ids->data)[i] = pos_off + i;
|
||||
// Pré-calcul host des données d'inputs : pos_ids et mask. Mode legacy on les memcpy
|
||||
// direct dans le tensor data. Mode sched on les uploade via tensor_set après alloc.
|
||||
std::vector<int32_t> pos_host(n_new);
|
||||
for (int i = 0; i < n_new; i++) pos_host[i] = pos_off + i;
|
||||
std::vector<float> mask_host((size_t)T_full * n_new);
|
||||
for (int q = 0; q < n_new; q++)
|
||||
for (int k = 0; k < T_full; k++)
|
||||
mask_host[k + T_full * q] = (k > pos_off + q) ? -INFINITY : 0.0f;
|
||||
|
||||
// Masque causal [T_full (n_k), n_new (n_q)] : la requête q (à position pos_off+q) voit
|
||||
// les clés 0..pos_off+q. KQ aura shape [T_full, n_new, N_HEAD] -> broadcast sur dim 2.
|
||||
ggml_tensor* x = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, N_EMBD, n_new);
|
||||
ggml_tensor* pos_ids = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, n_new);
|
||||
ggml_tensor* mask = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, T_full, n_new);
|
||||
{
|
||||
float* m = (float*)mask->data;
|
||||
for (int q = 0; q < n_new; q++)
|
||||
for (int k = 0; k < T_full; k++)
|
||||
m[k + T_full * q] = (k > pos_off + q) ? -INFINITY : 0.0f;
|
||||
if (use_sched) {
|
||||
ggml_set_name(x, "x"); ggml_set_input(x);
|
||||
ggml_set_name(pos_ids, "pos"); ggml_set_input(pos_ids);
|
||||
ggml_set_name(mask, "mask"); ggml_set_input(mask);
|
||||
} else {
|
||||
// Legacy : data est sur le ctx mem (no_alloc=false), memcpy direct OK.
|
||||
memcpy(x->data, embeds_new, (size_t)N_EMBD * n_new * sizeof(float));
|
||||
memcpy(pos_ids->data, pos_host.data(), pos_host.size() * sizeof(int32_t));
|
||||
memcpy(mask->data, mask_host.data(), mask_host.size() * sizeof(float));
|
||||
}
|
||||
const float scale = 1.0f / sqrtf((float)HEAD_DIM);
|
||||
|
||||
|
|
@ -335,6 +476,7 @@ static void cp_forward_cached_step(CPState& s, const float* embeds_new, int n_ne
|
|||
}
|
||||
x = ggml_rms_norm(ctx, x, RMS_EPS);
|
||||
x = ggml_mul(ctx, x, W("output_norm.weight"));
|
||||
if (use_sched) { ggml_set_name(x, "out_x"); ggml_set_output(x); }
|
||||
|
||||
ggml_cgraph* gf = ggml_new_graph_custom(ctx, 8192, false);
|
||||
// ORDRE CRITIQUE : pousser tous les cpy AVANT x. Comme K_cpy/V_cpy ne sont pas des
|
||||
|
|
@ -346,11 +488,30 @@ static void cp_forward_cached_step(CPState& s, const float* embeds_new, int n_ne
|
|||
for (auto* c : cpy_ops) ggml_build_forward_expand(gf, c);
|
||||
ggml_build_forward_expand(gf, x);
|
||||
|
||||
ggml_graph_compute_with_ctx(ctx, gf, s.n_threads);
|
||||
|
||||
out_hn.resize(N_EMBD);
|
||||
memcpy(out_hn.data(), (float*)x->data + (size_t)N_EMBD * (n_new - 1),
|
||||
N_EMBD * sizeof(float));
|
||||
if (use_sched) {
|
||||
ggml_backend_sched_reset(s.sched);
|
||||
if (!ggml_backend_sched_alloc_graph(s.sched, gf)) {
|
||||
fprintf(stderr, "cp_forward_cached_step: sched_alloc_graph FAIL\n");
|
||||
ggml_free(ctx); return;
|
||||
}
|
||||
// Inputs uploadés APRÈS sched_alloc_graph (les tensors ont maintenant leur backend buf).
|
||||
ggml_backend_tensor_set(x, embeds_new, 0, (size_t)N_EMBD * n_new * sizeof(float));
|
||||
ggml_backend_tensor_set(pos_ids, pos_host.data(), 0, pos_host.size() * sizeof(int32_t));
|
||||
ggml_backend_tensor_set(mask, mask_host.data(), 0, mask_host.size() * sizeof(float));
|
||||
if (ggml_backend_sched_graph_compute(s.sched, gf) != GGML_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "cp_forward_cached_step: sched_graph_compute FAIL\n");
|
||||
ggml_free(ctx); return;
|
||||
}
|
||||
// hn = colonne (n_new - 1) de x [N_EMBD, n_new]. tensor_get avec offset.
|
||||
ggml_backend_tensor_get(x, out_hn.data(),
|
||||
(size_t)N_EMBD * (n_new - 1) * sizeof(float),
|
||||
(size_t)N_EMBD * sizeof(float));
|
||||
} else {
|
||||
ggml_graph_compute_with_ctx(ctx, gf, s.n_threads);
|
||||
memcpy(out_hn.data(), (float*)x->data + (size_t)N_EMBD * (n_new - 1),
|
||||
N_EMBD * sizeof(float));
|
||||
}
|
||||
ggml_free(ctx);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@
|
|||
|
||||
struct ggml_context;
|
||||
struct ggml_tensor;
|
||||
struct ggml_backend;
|
||||
typedef struct ggml_backend * ggml_backend_t;
|
||||
struct ggml_backend_sched;
|
||||
struct ggml_backend_buffer;
|
||||
|
||||
// Nombre de couches du CP — fixe par l'archi. Déclaré ici car taille tableaux KV cache.
|
||||
#define CP_N_LAYER 5
|
||||
|
|
@ -29,6 +33,22 @@ struct CPState {
|
|||
std::vector<float> heads; // [15, 2048, 1024] f32
|
||||
std::vector<float> codec_embs; // [15, 2048, 1024] f32
|
||||
int n_threads = 4;
|
||||
|
||||
// --- Backend multi-device (Phase F : CP forward sur HMX V79) ---
|
||||
// Si backends est non vide après cp_load_with_backends(), les poids sont alloués
|
||||
// sur backends[0] (HTP) au lieu du weights_ctx CPU pur. Un sched est créé pour
|
||||
// splitter les ops auto entre HTP et CPU. cp_forward_cached_step détecte la
|
||||
// présence du sched et utilise la variante sched-friendly (no_alloc=true +
|
||||
// ggml_set_input/output + tensor_set/get).
|
||||
//
|
||||
// CP est un transformer 5L 1024-hidden, batches très petits (n_new=1 décode,
|
||||
// n_new=2 prefill), donc passe sans accroc la limite hexagon nrows(src1) ≤ 1024
|
||||
// qui bloquait BigVGAN.
|
||||
std::vector<ggml_backend_t> backends;
|
||||
ggml_backend_sched * sched = nullptr;
|
||||
ggml_backend_buffer * weights_buf = nullptr;
|
||||
ggml_backend_buffer * cache_buf = nullptr; // pour les K/V cache, sur le même backend
|
||||
bool owns_backends = false;
|
||||
// Sampler HF-style (rep_penalty + top_k + top_p + temperature). Par défaut = greedy
|
||||
// (top_k=1 -> argmax). Pour TTS prod, configurer en {temp=0.9, top_k=50, rep_penalty=1.05}
|
||||
// ce qui matche Python subtalker_*.
|
||||
|
|
@ -45,8 +65,17 @@ struct CPState {
|
|||
};
|
||||
|
||||
// Charge cp_f16.gguf + cp_heads.bin + cp_codec_embs.bin. Retourne false en cas d'échec.
|
||||
// Path CPU pur : poids dans weights_ctx alloué par ggml_init.
|
||||
bool cp_load(CPState& s, const char* gguf_path, const char* heads_path, const char* embs_path);
|
||||
|
||||
// Charge le même contenu mais alloue les poids sur le buft du 1er backend (HTP si
|
||||
// présent) et crée un sched pour le forward. devs[end] doit être CPU (assertion sched).
|
||||
// owns_backends=true -> cp_free libère aussi les backends.
|
||||
bool cp_load_with_backends(CPState& s, const char* gguf_path,
|
||||
const char* heads_path, const char* embs_path,
|
||||
const std::vector<ggml_backend_t>& devs,
|
||||
bool owns_backends);
|
||||
|
||||
// Libère les ressources.
|
||||
void cp_free(CPState& s);
|
||||
|
||||
|
|
|
|||
|
|
@ -218,13 +218,36 @@ TtsEngine * tts_engine_load(const TtsEngineLoadCfg & cfg) {
|
|||
eng->npe = (rt == LLAMA_ROPE_TYPE_MROPE || rt == LLAMA_ROPE_TYPE_IMROPE) ? 4 : 1;
|
||||
|
||||
// --- 5) CP ---
|
||||
// KZTTS_CP_HTP=1 + KZTTS_CP_SCHED=1 active le path sched HMX :
|
||||
// poids CP sur HTP buffer, forward via ggml_backend_sched (MUL_MAT -> HMX).
|
||||
// CP est un transformer 5L 1024-hidden, batches n_new in {1,2}, donc en-dessous de la
|
||||
// limite hexagon nrows(src1) ≤ 1024 qui bloquait BigVGAN. Pattern identique au talker
|
||||
// option C déjà validé.
|
||||
eng->cp_state.n_threads = cfg.n_threads;
|
||||
if (!cp_load(eng->cp_state,
|
||||
(D + "cp_f16.gguf").c_str(),
|
||||
(D + "cp_heads.bin").c_str(),
|
||||
(D + "cp_codec_embs.bin").c_str())) {
|
||||
fprintf(stderr, "CP load FAIL\n"); delete eng; return nullptr;
|
||||
const bool cp_htp = (getenv("KZTTS_CP_HTP") && atoi(getenv("KZTTS_CP_HTP")) != 0);
|
||||
bool cp_ok;
|
||||
if (cp_htp) {
|
||||
std::vector<ggml_backend_t> cp_backends;
|
||||
if (auto htp_dev = find_htp()) {
|
||||
ggml_backend_t htp = ggml_backend_dev_init(htp_dev, nullptr);
|
||||
if (htp) { cp_backends.push_back(htp); fprintf(stderr, "CP: HTP backend initialisé\n"); }
|
||||
else { fprintf(stderr, "CP: HTP init FAIL, fallback CPU only\n"); }
|
||||
}
|
||||
ggml_backend_t cpu = ggml_backend_init_by_type(GGML_BACKEND_DEVICE_TYPE_CPU, nullptr);
|
||||
if (!cpu) { fprintf(stderr, "CP: CPU backend init FAIL\n"); delete eng; return nullptr; }
|
||||
cp_backends.push_back(cpu); // CPU TOUJOURS en dernier (assertion sched)
|
||||
cp_ok = cp_load_with_backends(eng->cp_state,
|
||||
(D + "cp_f16.gguf").c_str(),
|
||||
(D + "cp_heads.bin").c_str(),
|
||||
(D + "cp_codec_embs.bin").c_str(),
|
||||
cp_backends, /*owns=*/true);
|
||||
} else {
|
||||
cp_ok = cp_load(eng->cp_state,
|
||||
(D + "cp_f16.gguf").c_str(),
|
||||
(D + "cp_heads.bin").c_str(),
|
||||
(D + "cp_codec_embs.bin").c_str());
|
||||
}
|
||||
if (!cp_ok) { fprintf(stderr, "CP load FAIL\n"); delete eng; return nullptr; }
|
||||
|
||||
// --- 6) Decoder ---
|
||||
// KZTTS_DECODER_HTP=1 -> charger via load_with_backends({HTP, CPU}) pour activer
|
||||
|
|
|
|||
Loading…
Reference in New Issue