ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
weight_management.cpp
Go to the documentation of this file.
1#include "weight_management.h"
2
3#include <algorithm>
4#include <mutex>
5#include "ggml_types.h"
6#include "logger.h"
7#include "quantization.h"
8#include "utils.h"
9#ifdef HAS_CUDA
10#include "cuda_kernels.h"
11#endif
12
14 if (!this->embed_tokens_f32.empty()) return;
15
16 size_t total_elements_embed = static_cast<size_t>(config_.vocab_size) * config_.hidden_size;
17 if (!this->embed_tokens_q6k.empty()) {
18 dequantize_vector_q6k_to_f32(this->embed_tokens_q6k, this->embed_tokens_f32, total_elements_embed, 1);
19 } else if (!this->embed_tokens_q4k.empty()) {
20 dequantize_vector_q4k_to_f32(this->embed_tokens_q4k, this->embed_tokens_f32, total_elements_embed, 1);
21 } else if (!this->embed_tokens_q5k.empty()) {
22 dequantize_vector_q5k_to_f32(this->embed_tokens_q5k, this->embed_tokens_f32, total_elements_embed);
23 } else if (!this->embed_tokens_q8k.empty()) {
24 dequantize_q8_k(this->embed_tokens_q8k, this->embed_tokens_f32, total_elements_embed, true);
25 } else if (!this->embed_tokens_q8_0.empty()) {
26 dequantize_vector_q8_0_to_f32(this->embed_tokens_q8_0, this->embed_tokens_f32, total_elements_embed, 1);
27 } else if (!this->embed_tokens.empty()) {
29 }
30}
31
34 const size_t kv = hs / std::max(config_.num_attention_heads, 1) * config_.num_key_value_heads;
35 const size_t per_layer = hs * hs * 2 + hs * kv * 2 + hs * is * 3;
36 const size_t params = vs * hs * 2 + per_layer * config_.num_hidden_layers;
37 if (params > max_params) return false;
40 for (auto& lw : layers) { // MoE router (Q8_0 in paperkiln's Q8_0 exports)
41 if (lw.moe_router_f32.empty() && !lw.moe_router_q8_0.empty()) {
42 const size_t n = lw.moe_router_q8_0.size() * GGML_QK8_0;
43 dequantize_vector_q8_0_to_f32(lw.moe_router_q8_0, lw.moe_router_f32, n, 0);
44 lw.moe_router_q8_0.clear();
45 lw.moe_router_q8_0.shrink_to_fit();
46 }
47 }
48 for (int l = 0; l < static_cast<int>(layers.size()); ++l) {
56 }
57 // Free the quantised copies whose f32 copy now exists: nothing can then
58 // take a quantised kernel, and the model is not held twice (WASM memory).
59 auto drop = [](const std::vector<float>& f32, auto&... quant) {
60 if (f32.empty()) return;
61 ((quant.clear(), quant.shrink_to_fit()), ...);
62 };
65 for (auto& lw : layers) {
66 drop(lw.q_proj_f32, lw.q_proj_q4k, lw.q_proj_q5k, lw.q_proj_q6k, lw.q_proj_q8_0, lw.q_proj_q8k);
67 drop(lw.k_proj_f32, lw.k_proj_q4k, lw.k_proj_q5k, lw.k_proj_q6k, lw.k_proj_q8_0, lw.k_proj_q8k);
68 drop(lw.v_proj_f32, lw.v_proj_q4k, lw.v_proj_q5k, lw.v_proj_q6k, lw.v_proj_q8_0, lw.v_proj_q8k);
69 drop(lw.o_proj_f32, lw.o_proj_q4k, lw.o_proj_q5k, lw.o_proj_q6k, lw.o_proj_q8_0, lw.o_proj_q8k);
70 drop(lw.gate_proj_f32, lw.gate_proj_q4k, lw.gate_proj_q5k, lw.gate_proj_q6k, lw.gate_proj_q8_0, lw.gate_proj_q8k);
71 drop(lw.up_proj_f32, lw.up_proj_q4k, lw.up_proj_q5k, lw.up_proj_q6k, lw.up_proj_q8_0, lw.up_proj_q8k);
72 drop(lw.down_proj_f32, lw.down_proj_q4k, lw.down_proj_q5k, lw.down_proj_q6k, lw.down_proj_q8_0, lw.down_proj_q8k);
73 }
74 return true;
75}
76
78 if (!this->lm_head_f32.empty()) return;
79
80 size_t total_elements_lm_head = static_cast<size_t>(config_.vocab_size) * config_.hidden_size;
81 if (!this->lm_head_q6k.empty()) {
82 dequantize_vector_q6k_to_f32(this->lm_head_q6k, this->lm_head_f32, total_elements_lm_head, 1);
83 } else if (!this->lm_head_q4k.empty()) {
84 dequantize_vector_q4k_to_f32(this->lm_head_q4k, this->lm_head_f32, total_elements_lm_head, 1);
85 } else if (!this->lm_head_q5k.empty()) {
86 dequantize_vector_q5k_to_f32(this->lm_head_q5k, this->lm_head_f32, total_elements_lm_head);
87 } else if (!this->lm_head_q8k.empty()) {
88 dequantize_q8_k(this->lm_head_q8k, this->lm_head_f32, total_elements_lm_head, true);
89 } else if (!this->lm_head_q8_0.empty()) {
90 dequantize_vector_q8_0_to_f32(this->lm_head_q8_0, this->lm_head_f32, total_elements_lm_head, 1);
91 } else if (!this->lm_head.empty()) {
93 }
94}
95
97 if (layer_idx < 0 || layer_idx >= layers.size()) return;
98 auto& lw = layers[layer_idx];
99 if (!lw.q_proj_f32.empty()) return;
100
101 int hs = config_.hidden_size;
102 size_t q_proj_elements = static_cast<size_t>(hs) * hs;
103 Logger::info("[DEQUANT_MEM] Layer " + std::to_string(layer_idx) + ": Q-proj dequantization starting, elements=" + std::to_string(q_proj_elements));
104
105 if (!lw.q_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.q_proj_q6k, lw.q_proj_f32, q_proj_elements, 0);
106 else if (!lw.q_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.q_proj_q4k, lw.q_proj_f32, q_proj_elements, 0);
107 else if (!lw.q_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.q_proj_q5k, lw.q_proj_f32, q_proj_elements);
108 else if (!lw.q_proj_q8k.empty()) dequantize_q8_k(lw.q_proj_q8k, lw.q_proj_f32, q_proj_elements, true);
109 else if (!lw.q_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.q_proj_q8_0, lw.q_proj_f32, q_proj_elements, 0);
110 else if (!lw.q_proj.empty()) lw.q_proj_f32 = bf16vec_to_float_vec(lw.q_proj);
111
112 Logger::info("[DEQUANT_MEM] Layer " + std::to_string(layer_idx) + ": Q-proj dequantization completed, f32 size=" + std::to_string(lw.q_proj_f32.size()));
113}
114
116 if (layer_idx < 0 || layer_idx >= static_cast<int>(layers.size())) {
117 Logger::warning("clear_layer_dequantized_weights: Invalid layer index " + std::to_string(layer_idx));
118 return;
119 }
120
121 // Each f32 copy is cleared only when the weight has a quantised (or BF16) source to re-derive it
122 // from: for F32-native weights (transformer_cpp exports) the f32 vector IS the master copy, and
123 // clearing it is permanent destruction, not a memory optimisation (first hit 2026-07-20 on the
124 // 4-layer mid model: "No Q proj weights" on the second forward pass). Per weight, not per
125 // layer, so a layer whose weights mix sources keeps its f32-only ones.
126 auto& layer = layers[layer_idx];
127 auto clear_if_recoverable = [](std::vector<float>& f32, bool recoverable) {
128 if (!recoverable) return 0;
129 f32.clear();
130 f32.shrink_to_fit();
131 return 1;
132 };
133#define EMBER_RECOVERABLE(w) \
134 (!layer.w##_q4k.empty() || !layer.w##_q5k.empty() || !layer.w##_q6k.empty() || !layer.w##_q8k.empty() || \
135 !layer.w##_q8_0.empty() || !layer.w.empty())
136 int cleared = 0;
137 cleared += clear_if_recoverable(layer.q_proj_f32, EMBER_RECOVERABLE(q_proj));
138 cleared += clear_if_recoverable(layer.k_proj_f32, EMBER_RECOVERABLE(k_proj));
139 cleared += clear_if_recoverable(layer.v_proj_f32, EMBER_RECOVERABLE(v_proj));
140 cleared += clear_if_recoverable(layer.o_proj_f32, EMBER_RECOVERABLE(o_proj));
141 cleared += clear_if_recoverable(layer.gate_proj_f32, EMBER_RECOVERABLE(gate_proj));
142 cleared += clear_if_recoverable(layer.up_proj_f32, EMBER_RECOVERABLE(up_proj));
143 cleared += clear_if_recoverable(layer.down_proj_f32, EMBER_RECOVERABLE(down_proj));
144#undef EMBER_RECOVERABLE
145 if (cleared)
146 Logger::info("Cleared " + std::to_string(cleared) + " dequantized weights of layer " + std::to_string(layer_idx) +
147 " to save memory.");
148}
149
151 if (layer_idx < 0 || layer_idx >= layers.size()) return;
152 auto& lw = layers[layer_idx];
153 if (!lw.k_proj_f32.empty()) return;
154
155 int hs = config_.hidden_size;
156 size_t k_proj_elements = static_cast<size_t>(config_.num_key_value_heads * (hs / config_.num_attention_heads)) * hs;
157
158 if (!lw.k_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.k_proj_q6k, lw.k_proj_f32, k_proj_elements, 0);
159 else if (!lw.k_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.k_proj_q4k, lw.k_proj_f32, k_proj_elements, 0);
160 else if (!lw.k_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.k_proj_q5k, lw.k_proj_f32, k_proj_elements);
161 else if (!lw.k_proj_q8k.empty()) dequantize_q8_k(lw.k_proj_q8k, lw.k_proj_f32, k_proj_elements, false);
162 else if (!lw.k_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.k_proj_q8_0, lw.k_proj_f32, k_proj_elements, 0);
163 else if (!lw.k_proj.empty()) lw.k_proj_f32 = bf16vec_to_float_vec(lw.k_proj);
164}
165
167 if (layer_idx < 0 || layer_idx >= layers.size()) return;
168 auto& lw = layers[layer_idx];
169 if (!lw.v_proj_f32.empty()) return;
170
171 int hs = config_.hidden_size;
172 size_t v_proj_elements = static_cast<size_t>(config_.num_key_value_heads * (hs / config_.num_attention_heads)) * hs;
173
174 if (!lw.v_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.v_proj_q6k, lw.v_proj_f32, v_proj_elements, 0);
175 else if (!lw.v_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.v_proj_q4k, lw.v_proj_f32, v_proj_elements, 0);
176 else if (!lw.v_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.v_proj_q5k, lw.v_proj_f32, v_proj_elements);
177 else if (!lw.v_proj_q8k.empty()) dequantize_q8_k(lw.v_proj_q8k, lw.v_proj_f32, v_proj_elements, false);
178 else if (!lw.v_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.v_proj_q8_0, lw.v_proj_f32, v_proj_elements, 0);
179 else if (!lw.v_proj.empty()) lw.v_proj_f32 = bf16vec_to_float_vec(lw.v_proj);
180}
181
183 if (layer_idx < 0 || layer_idx >= layers.size()) return;
184 auto& lw = layers[layer_idx];
185 if (!lw.o_proj_f32.empty()) return;
186
187 int hs = config_.hidden_size;
188 size_t o_proj_elements = static_cast<size_t>(hs) * hs;
189
190 if (!lw.o_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.o_proj_q6k, lw.o_proj_f32, o_proj_elements, 0);
191 else if (!lw.o_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.o_proj_q4k, lw.o_proj_f32, o_proj_elements, 0);
192 else if (!lw.o_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.o_proj_q5k, lw.o_proj_f32, o_proj_elements);
193 else if (!lw.o_proj_q8k.empty()) dequantize_q8_k(lw.o_proj_q8k, lw.o_proj_f32, o_proj_elements, false);
194 else if (!lw.o_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.o_proj_q8_0, lw.o_proj_f32, o_proj_elements, 0);
195 else if (!lw.o_proj.empty()) lw.o_proj_f32 = bf16vec_to_float_vec(lw.o_proj);
196}
197
199 if (layer_idx < 0 || layer_idx >= layers.size()) return;
200 auto& lw = layers[layer_idx];
201 if (!lw.gate_proj_f32.empty()) return;
202
203 int hs = config_.hidden_size;
205 size_t gate_proj_elements = static_cast<size_t>(is) * hs;
206
207 if (!lw.gate_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.gate_proj_q6k, lw.gate_proj_f32, gate_proj_elements, 0);
208 else if (!lw.gate_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.gate_proj_q4k, lw.gate_proj_f32, gate_proj_elements, 0);
209 else if (!lw.gate_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.gate_proj_q5k, lw.gate_proj_f32, gate_proj_elements);
210 else if (!lw.gate_proj_q8k.empty()) dequantize_q8_k(lw.gate_proj_q8k, lw.gate_proj_f32, gate_proj_elements, false);
211 else if (!lw.gate_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.gate_proj_q8_0, lw.gate_proj_f32, gate_proj_elements, 0);
212 else if (!lw.gate_proj.empty()) lw.gate_proj_f32 = bf16vec_to_float_vec(lw.gate_proj);
213}
214
216 if (layer_idx < 0 || layer_idx >= layers.size()) return;
217 auto& lw = layers[layer_idx];
218 if (!lw.up_proj_f32.empty()) return;
219
220 int hs = config_.hidden_size;
222 size_t up_proj_elements = static_cast<size_t>(is) * hs;
223
224 if (!lw.up_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.up_proj_q6k, lw.up_proj_f32, up_proj_elements, 0);
225 else if (!lw.up_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.up_proj_q4k, lw.up_proj_f32, up_proj_elements, 0);
226 else if (!lw.up_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.up_proj_q5k, lw.up_proj_f32, up_proj_elements);
227 else if (!lw.up_proj_q8k.empty()) dequantize_q8_k(lw.up_proj_q8k, lw.up_proj_f32, up_proj_elements, false);
228 else if (!lw.up_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.up_proj_q8_0, lw.up_proj_f32, up_proj_elements, 0);
229 else if (!lw.up_proj.empty()) lw.up_proj_f32 = bf16vec_to_float_vec(lw.up_proj);
230}
231
233 if (layer_idx < 0 || layer_idx >= layers.size()) return;
234 auto& lw = layers[layer_idx];
235 if (!lw.down_proj_f32.empty()) return;
236
237 int hs = config_.hidden_size;
239 size_t down_proj_elements = static_cast<size_t>(hs) * is;
240
241 if (!lw.down_proj_q6k.empty()) dequantize_vector_q6k_to_f32(lw.down_proj_q6k, lw.down_proj_f32, down_proj_elements, 0);
242 else if (!lw.down_proj_q4k.empty()) dequantize_vector_q4k_to_f32(lw.down_proj_q4k, lw.down_proj_f32, down_proj_elements, 0);
243 else if (!lw.down_proj_q5k.empty()) dequantize_vector_q5k_to_f32(lw.down_proj_q5k, lw.down_proj_f32, down_proj_elements);
244 else if (!lw.down_proj_q8k.empty()) dequantize_q8_k(lw.down_proj_q8k, lw.down_proj_f32, down_proj_elements, false);
245 else if (!lw.down_proj_q8_0.empty()) dequantize_vector_q8_0_to_f32(lw.down_proj_q8_0, lw.down_proj_f32, down_proj_elements, 0);
246 else if (!lw.down_proj.empty()) lw.down_proj_f32 = bf16vec_to_float_vec(lw.down_proj);
247}
248
249#ifdef HAS_CUDA
251 // OPTIMIZED: Use concatenated weights for maximum GPU performance.
252 // Called once per layer on every decode step: the early return comes
253 // before any logging (a log line per call was ~0.5 ms per token).
256 return;
257 }
258 Logger::info("Loading concatenated F32 weights for optimal GPU performance");
259
260 Logger::info("Loading F32 concatenated weights on-demand for GPU inference");
261
262 // Concatenated FP32 / Dequantized Layer Weights for GPU layers (w_..._f32_dev_ pointers)
263 int hs = config_.hidden_size;
266
270
272 const std::function<const std::vector<float>&(const LayerWeights&)>& f32_accessor,
273 const std::function<const std::vector<uint16_t>&(const LayerWeights&)>& bf16_accessor,
274 const std::function<const std::vector<block_q8_0>&(const LayerWeights&)>& q8_accessor,
275 const std::function<const std::vector<block_q4_K>&(const LayerWeights&)>& q4k_accessor,
276 const std::function<const std::vector<block_q6_K>&(const LayerWeights&)>& q6k_accessor,
277 float*& dev_ptr, size_t single_layer_elem_size, const std::string& weight_name) {
278
279 std::vector<float> concatenated_f32;
281
284 // Individual weight dequantization will be handled by the lambda accessors below
286
287 const auto& f32_vec = f32_accessor(lw);
288 const auto& bf16_vec = bf16_accessor(lw);
289 const auto& q8_vec = q8_accessor(lw);
290 const auto& q4k_vec = q4k_accessor(lw);
291 const auto& q6k_vec = q6k_accessor(lw);
292
293 if (!f32_vec.empty()) {
294 concatenated_f32.insert(concatenated_f32.end(), f32_vec.begin(), f32_vec.end());
295 } else if (!bf16_vec.empty()) {
296 for (uint16_t bf16_val : bf16_vec) {
298 }
299 } else if (!q8_vec.empty()) {
300 std::vector<float> temp_f32(q8_vec.size() * GGML_QK8_0);
301 for (size_t i = 0; i < q8_vec.size(); ++i) {
303 }
304 concatenated_f32.insert(concatenated_f32.end(), temp_f32.begin(), temp_f32.end());
305 } else if (!q4k_vec.empty()) {
306 std::vector<float> temp_f32(q4k_vec.size() * GGML_QK_K);
307 for (size_t i = 0; i < q4k_vec.size(); ++i) {
309 }
310 concatenated_f32.insert(concatenated_f32.end(), temp_f32.begin(), temp_f32.end());
311 } else if (!q6k_vec.empty()) {
312 std::vector<float> temp_f32(q6k_vec.size() * GGML_QK_K);
313 for (size_t i = 0; i < q6k_vec.size(); ++i) {
315 }
316 concatenated_f32.insert(concatenated_f32.end(), temp_f32.begin(), temp_f32.end());
317 } else {
318 throw std::runtime_error("Layer " + std::to_string(l_model_idx) + ": No " + weight_name + " weights found for GPU processing");
319 }
320 }
321
322 if (!concatenated_f32.empty()) {
323 if (dev_ptr) { cudaFree(dev_ptr); dev_ptr = nullptr; }
324 gpuErrchk(cudaMalloc(&dev_ptr, concatenated_f32.size() * sizeof(float)));
326 Logger::info("Loaded concatenated " + weight_name + " (F32) to GPU for " + std::to_string(active_num_gpu_layers) + " layers");
327 }
328 };
329
331 [this](const LayerWeights& lw) -> const std::vector<float>& {
332 int layer_idx = &lw - &layers[0];
334 return lw.q_proj_f32;
335 },
336 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.q_proj; },
337 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.q_proj_q8_0; },
338 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.q_proj_q4k; },
339 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.q_proj_q6k; },
340 w_q_f32_dev_, layer_q_size_f32, "Q Proj");
341
342 // CRITICAL: Immediately clear Q-proj CPU memory after GPU upload to prevent OOM
347 layers[layer_idx].q_proj_f32.clear();
348 layers[layer_idx].q_proj_f32.shrink_to_fit();
349 }
350 Logger::info("Cleared Q-proj CPU memory immediately after GPU upload");
351 }
352
354 [this](const LayerWeights& lw) -> const std::vector<float>& {
355 int layer_idx = &lw - &layers[0];
357 return lw.k_proj_f32;
358 },
359 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.k_proj; },
360 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.k_proj_q8_0; },
361 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.k_proj_q4k; },
362 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.k_proj_q6k; },
363 w_k_f32_dev_, layer_k_size_f32, "K Proj");
364
369 layers[layer_idx].k_proj_f32.clear();
370 layers[layer_idx].k_proj_f32.shrink_to_fit();
371 }
372 Logger::info("Cleared K-proj CPU memory immediately after GPU upload");
373 }
374
376 [this](const LayerWeights& lw) -> const std::vector<float>& {
377 int layer_idx = &lw - &layers[0];
379 return lw.v_proj_f32;
380 },
381 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.v_proj; },
382 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.v_proj_q8_0; },
383 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.v_proj_q4k; },
384 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.v_proj_q6k; },
385 w_v_f32_dev_, layer_v_size_f32, "V Proj");
386
387 // Clear V-proj CPU memory immediately
392 layers[layer_idx].v_proj_f32.clear();
393 layers[layer_idx].v_proj_f32.shrink_to_fit();
394 }
395 }
396
398 [this](const LayerWeights& lw) -> const std::vector<float>& {
399 int layer_idx = &lw - &layers[0];
401 return lw.o_proj_f32;
402 },
403 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.o_proj; },
404 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.o_proj_q8_0; },
405 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.o_proj_q4k; },
406 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.o_proj_q6k; },
407 w_o_f32_dev_, layer_o_size_f32, "O Proj");
408
409 // Clear O-proj CPU memory immediately
414 layers[layer_idx].o_proj_f32.clear();
415 layers[layer_idx].o_proj_f32.shrink_to_fit();
416 }
417 }
418
419 if (config_.num_experts == 0) {
421 [this](const LayerWeights& lw) -> const std::vector<float>& {
422 int layer_idx = &lw - &layers[0];
424 return lw.gate_proj_f32;
425 },
426 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.gate_proj; },
427 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.gate_proj_q8_0; },
428 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.gate_proj_q4k; },
429 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.gate_proj_q6k; },
431
432 // Clear Gate-proj CPU memory immediately
437 layers[layer_idx].gate_proj_f32.clear();
438 layers[layer_idx].gate_proj_f32.shrink_to_fit();
439 }
440 }
441
443 [this](const LayerWeights& lw) -> const std::vector<float>& {
444 int layer_idx = &lw - &layers[0];
446 return lw.up_proj_f32;
447 },
448 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.up_proj; },
449 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.up_proj_q8_0; },
450 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.up_proj_q4k; },
451 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.up_proj_q6k; },
452 w_up_f32_dev_, layer_up_size_f32, "Up Proj");
453
454 // Clear Up-proj CPU memory immediately
459 layers[layer_idx].up_proj_f32.clear();
460 layers[layer_idx].up_proj_f32.shrink_to_fit();
461 }
462 }
463
465 [this](const LayerWeights& lw) -> const std::vector<float>& {
466 int layer_idx = &lw - &layers[0];
468 return lw.down_proj_f32;
469 },
470 [](const LayerWeights& lw) -> const std::vector<uint16_t>& { return lw.down_proj; },
471 [](const LayerWeights& lw) -> const std::vector<block_q8_0>& { return lw.down_proj_q8_0; },
472 [](const LayerWeights& lw) -> const std::vector<block_q4_K>& { return lw.down_proj_q4k; },
473 [](const LayerWeights& lw) -> const std::vector<block_q6_K>& { return lw.down_proj_q6k; },
475
476 // Clear Down-proj CPU memory immediately
481 layers[layer_idx].down_proj_f32.clear();
482 layers[layer_idx].down_proj_f32.shrink_to_fit();
483 }
484 }
485
486 } else {
487 // Mixture of experts: router and expert stacks per GPU layer, f32.
488 auto upload = [&](const std::function<const std::vector<float>&(LayerWeights&)>& get, float*& dev,
489 const char* what) {
490 std::vector<float> cat;
491 for (int g = 0; g < active_num_gpu_layers; ++g) {
493 if (lw.moe_router_f32.empty() && !lw.moe_router_q8_0.empty())
494 dequantize_vector_q8_0_to_f32(lw.moe_router_q8_0, lw.moe_router_f32, lw.moe_router_q8_0.size() * GGML_QK8_0, 0);
495 const std::vector<float>& v = get(lw);
496 if (v.empty())
497 throw std::runtime_error("MoE layer " + std::to_string(config_.num_cpu_offload_layers + g) + ": no " + what +
498 " weights for the GPU");
499 cat.insert(cat.end(), v.begin(), v.end());
500 }
501 if (dev) { cudaFree(dev); dev = nullptr; }
502 gpuErrchk(cudaMalloc(&dev, cat.size() * sizeof(float)));
503 gpuErrchk(cudaMemcpy(dev, cat.data(), cat.size() * sizeof(float), cudaMemcpyHostToDevice));
504 };
505 upload([](LayerWeights& lw) -> const std::vector<float>& { return lw.moe_router_f32; }, w_moe_router_dev_, "router");
506 upload([](LayerWeights& lw) -> const std::vector<float>& { return lw.moe_gate_exps_f32; }, w_moe_gate_dev_, "expert gate");
507 upload([](LayerWeights& lw) -> const std::vector<float>& { return lw.moe_up_exps_f32; }, w_moe_up_dev_, "expert up");
508 upload([](LayerWeights& lw) -> const std::vector<float>& { return lw.moe_down_exps_f32; }, w_moe_down_dev_, "expert down");
509 Logger::info("Loaded MoE router and expert stacks (F32) to GPU for " + std::to_string(active_num_gpu_layers) +
510 " layers (" + std::to_string(config_.num_experts) + " experts)");
511 }
512
514 Logger::info("Successfully loaded all concatenated F32 weights for GPU layers");
515}
516
517// BF16 Tensor Core weight management functions
519 Logger::info("Loading concatenated BF16 weights for Tensor Core acceleration");
520
523 return;
524 }
525
526 Logger::info("Converting F32 weights to BF16 for Tensor Core acceleration");
527
528 // Ensure F32 weights are loaded first
530
531 int hs = config_.hidden_size;
534
535 size_t layer_q_size = (size_t)hs*hs;
536 size_t layer_k_size = (size_t)kv_dim*hs;
537 size_t layer_v_size = (size_t)kv_dim*hs;
538 size_t layer_o_size = (size_t)hs*hs;
539 size_t layer_gate_size = (size_t)is*hs;
540 size_t layer_up_size = (size_t)is*hs;
541 size_t layer_down_size = (size_t)hs*is;
542
543 // Convert F32 weights to BF16 on GPU
544 auto convert_f32_to_bf16 = [&](float* f32_dev_ptr, uint16_t*& bf16_dev_ptr, size_t total_elements, const std::string& weight_name) {
545 if (f32_dev_ptr && total_elements > 0) {
546 if (bf16_dev_ptr) { cudaFree(bf16_dev_ptr); bf16_dev_ptr = nullptr; }
550 Logger::info("Converted " + weight_name + " from F32 to BF16 on GPU for Tensor Cores");
551 }
552 };
553
561
569
571 Logger::info("Successfully loaded all concatenated BF16 weights for Tensor Core acceleration");
572}
573
576
577 Logger::info("Freeing BF16 concatenated weights");
585
587 Logger::info("Successfully freed all BF16 concatenated weights");
588}
589
591#ifdef HAS_CUDA
592 if (layer_idx < 0 || layer_idx >= layers.size()) return;
593
594 // Check if this layer is supposed to be on GPU
598
600
601 // Check if weights are already loaded on GPU for this layer
602 if (lw.q_proj_f32_dev && lw.k_proj_f32_dev && lw.v_proj_f32_dev &&
603 lw.o_proj_f32_dev && lw.gate_proj_f32_dev && lw.up_proj_f32_dev && lw.down_proj_f32_dev) {
604 return; // Already loaded
605 }
606
607 // AGGRESSIVE MEMORY MANAGEMENT: Free previous layer weights to make room
608 // Keep only the current layer and maybe the next one
610 int prev_layer = layer_idx - 1;
611 if (prev_layer >= first_gpu_layer && prev_layer < layers.size()) {
613 }
614 }
615
616 // If still hitting memory limits, free ALL other GPU layers except current
617 if (layer_idx > first_gpu_layer + 1) {
618 for (int i = first_gpu_layer; i < layer_idx - 1; ++i) {
620 }
621 }
622
623 Logger::info("JIT loading layer " + std::to_string(layer_idx) + " weights to GPU (with aggressive eviction)");
624
625 // Dequantize and load each weight matrix individually
626 auto load_single_weight = [&](
627 const std::function<void()>& ensure_dequantized,
628 const std::function<const std::vector<float>&()>& get_f32_weights,
629 float*& dev_ptr,
630 const std::string& weight_name
631 ) {
633 const auto& f32_weights = get_f32_weights();
634 if (!f32_weights.empty()) {
635 if (dev_ptr) { cudaFree(dev_ptr); dev_ptr = nullptr; }
636
637 // Try allocation with error handling
638 cudaError_t malloc_result = cudaMalloc(&dev_ptr, f32_weights.size() * sizeof(float));
639 if (malloc_result != cudaSuccess) {
640 Logger::warning("GPU memory allocation failed for " + weight_name + " in layer " + std::to_string(layer_idx) +
641 ". Attempting emergency cleanup...");
642
643 // Emergency cleanup: free ALL other layer weights
645 if (emergency_idx != layer_idx) {
647 }
648 }
649
650 // Try allocation again after cleanup
651 malloc_result = cudaMalloc(&dev_ptr, f32_weights.size() * sizeof(float));
652 if (malloc_result != cudaSuccess) {
653 throw std::runtime_error("GPU OOM: Cannot allocate " + std::to_string(f32_weights.size() * sizeof(float)) +
654 " bytes for " + weight_name + " in layer " + std::to_string(layer_idx) +
655 " even after emergency cleanup. Try reducing --n-gpu-layers.");
656 }
657 Logger::info("Emergency cleanup successful, allocated " + weight_name);
658 }
659
661
662 // Immediately clear CPU memory to save RAM
664 const_cast<std::vector<float>&>(f32_weights).clear();
665 const_cast<std::vector<float>&>(f32_weights).shrink_to_fit();
666 }
667 }
668 };
669
670 // Load Q projection
673 [&lw]() -> const std::vector<float>& { return lw.q_proj_f32; },
674 lw.q_proj_f32_dev,
675 "Q Proj"
676 );
677
678 // Load K projection
681 [&lw]() -> const std::vector<float>& { return lw.k_proj_f32; },
682 lw.k_proj_f32_dev,
683 "K Proj"
684 );
685
686 // Load V projection
689 [&lw]() -> const std::vector<float>& { return lw.v_proj_f32; },
690 lw.v_proj_f32_dev,
691 "V Proj"
692 );
693
694 // Load O projection
697 [&lw]() -> const std::vector<float>& { return lw.o_proj_f32; },
698 lw.o_proj_f32_dev,
699 "O Proj"
700 );
701
702 // Load Gate projection
705 [&lw]() -> const std::vector<float>& { return lw.gate_proj_f32; },
706 lw.gate_proj_f32_dev,
707 "Gate Proj"
708 );
709
710 // Load Up projection
713 [&lw]() -> const std::vector<float>& { return lw.up_proj_f32; },
714 lw.up_proj_f32_dev,
715 "Up Proj"
716 );
717
718 // Load Down projection
721 [&lw]() -> const std::vector<float>& { return lw.down_proj_f32; },
722 lw.down_proj_f32_dev,
723 "Down Proj"
724 );
725#else
726 // CPU-only build - this function is a no-op
727 Logger::info("CPU-only build: ensure_layer_weights_on_gpu is a no-op for layer " + std::to_string(layer_idx));
728#endif
729}
730
732#ifdef HAS_CUDA
733 if (layer_idx < 0 || layer_idx >= layers.size()) return;
734
736
737 if (lw.q_proj_f32_dev) { cudaFree(lw.q_proj_f32_dev); lw.q_proj_f32_dev = nullptr; }
738 if (lw.k_proj_f32_dev) { cudaFree(lw.k_proj_f32_dev); lw.k_proj_f32_dev = nullptr; }
739 if (lw.v_proj_f32_dev) { cudaFree(lw.v_proj_f32_dev); lw.v_proj_f32_dev = nullptr; }
740 if (lw.o_proj_f32_dev) { cudaFree(lw.o_proj_f32_dev); lw.o_proj_f32_dev = nullptr; }
741 if (lw.gate_proj_f32_dev) { cudaFree(lw.gate_proj_f32_dev); lw.gate_proj_f32_dev = nullptr; }
742 if (lw.up_proj_f32_dev) { cudaFree(lw.up_proj_f32_dev); lw.up_proj_f32_dev = nullptr; }
743 if (lw.down_proj_f32_dev) { cudaFree(lw.down_proj_f32_dev); lw.down_proj_f32_dev = nullptr; }
744
745 Logger::info("Freed GPU weights for layer " + std::to_string(layer_idx) + " (~200MB freed)");
746#else
747 // CPU-only build - this function is a no-op
748 Logger::info("CPU-only build: free_layer_gpu_weights is a no-op for layer " + std::to_string(layer_idx));
749#endif
750}
751
752#endif // HAS_CUDA (GPU weight-management implementations)
753
754// NOTE: map_gguf_weights is required by BOTH CUDA and CPU builds (it maps
755// GGUF tensors into host-side model fields). It was previously trapped inside
756// the HAS_CUDA block opened far above, which broke CPU-only linking.
757// Load-time dequantisation of the formats ember has no kernels for: ggml's
758// legacy 32-wide blocks (Q4_0, Q4_1, Q5_0, Q5_1), F16 and BF16, written straight
759// into the f32 vector in the tensor's own element order. Block layouts as in
760// ggml-common.h: Q4_0 {d, qs[16]}, Q4_1 {d, m, qs[16]}, Q5_0 {d, qh[4], qs[16]},
761// Q5_1 {d, m, qh[4], qs[16]}; element j of a block uses the low nibble of qs[j],
762// element j+16 the high nibble, and the Q5 formats take bit j (resp. j+16) of qh
763// as the fifth bit. x = (q - 8) d, q d + m, (q - 16) d, q d + m respectively.
764// Also the K-quants Q4_K, Q5_K and Q6_K (256-wide super-blocks), for the
765// interpreter and any tensor the llama path keeps no blocks for.
766// Returns false for any other type.
767bool dequant_legacy_to_f32(const GGUFTensorInfo& info, const uint8_t* src, std::vector<float>& out) {
768 const GGMLType t = info.type;
769 const size_t n = info.num_elements;
771 out.resize(n);
772 dequant_span_to_f32(t, src, 0, n, out.data());
773 return true;
774 }
778 if (!(q4_0 || q4_1 || q5_0 || q5_1 || k_quant)) return false;
779 const size_t width = k_quant ? GGML_QK_K : 32;
780 if (n % width != 0)
781 throw std::runtime_error(std::string(ggml_type_name(t)) + " tensor with " + std::to_string(n) +
782 " elements (not a multiple of " + std::to_string(width) + ")");
783 const size_t bsz = ggml_type_size(t), nb = n / width;
784 if (info.size_in_bytes < nb * bsz) throw std::runtime_error("legacy-quantised tensor is shorter than its element count implies");
785 out.resize(n);
786 dequant_span_to_f32(t, src, 0, n, out.data());
787 return true;
788}
789
790// Every f16 value as f32, built once from fp16_to_fp32 itself (so a lookup equals the call bit for
791// bit): the interpreter dequantises an F16 weight row by row on every call, so a large F16 model
792// converts every weight once per decode step.
793static const float* fp16_table() {
794 static const std::vector<float> table = [] {
795 std::vector<float> t(65536);
797 return t;
798 }();
799 return table.data();
800}
801
802bool dequant_span_to_f32(GGMLType t, const uint8_t* src, size_t first, size_t n, float* out) {
803 if (t == GGMLType::GGML_TYPE_F16) {
804 const float* table = fp16_table();
805 const uint8_t* p = src + 2 * first;
806 for (size_t i = 0; i < n; ++i) {
807 uint16_t h;
808 std::memcpy(&h, p + 2 * i, 2);
809 out[i] = table[h];
810 }
811 return true;
812 }
813 if (t == GGMLType::GGML_TYPE_BF16) { // the top 16 bits of an f32: exact
814 for (size_t i = 0; i < n; ++i) {
815 uint16_t h;
816 std::memcpy(&h, src + 2 * (first + i), 2);
817 const uint32_t bits = static_cast<uint32_t>(h) << 16;
818 std::memcpy(out + i, &bits, 4);
819 }
820 return true;
821 }
823 const block_q8_0* blocks = reinterpret_cast<const block_q8_0*>(src) + first / GGML_QK8_0;
824 for (size_t b = 0; b < n / GGML_QK8_0; ++b) dequantize_q8_0_block(blocks + b, out + b * GGML_QK8_0);
825 return true;
826 }
827 // K-quants: 256-wide super-blocks (first and n multiples of GGML_QK_K), ember's block dequantisers.
829 const block_q4_K* blocks = reinterpret_cast<const block_q4_K*>(src) + first / GGML_QK_K;
830 for (size_t b = 0; b < n / GGML_QK_K; ++b) dequantize_q4_k_m(blocks + b, out + b * GGML_QK_K, GGML_QK_K);
831 return true;
832 }
834 const block_q5_K* blocks = reinterpret_cast<const block_q5_K*>(src) + first / GGML_QK_K;
835 for (size_t b = 0; b < n / GGML_QK_K; ++b) dequantize_q5_k(blocks + b, out + b * GGML_QK_K);
836 return true;
837 }
839 const block_q6_K* blocks = reinterpret_cast<const block_q6_K*>(src) + first / GGML_QK_K;
840 for (size_t b = 0; b < n / GGML_QK_K; ++b) dequantize_q6_k(blocks + b, out + b * GGML_QK_K, GGML_QK_K);
841 return true;
842 }
845 if (!(q4_0 || q4_1 || q5_0 || q5_1)) return false;
846 const bool has_min = q4_1 || q5_1, five_bit = q5_0 || q5_1;
847 const size_t bsz = ggml_type_size(t), b0 = first / 32, nb = n / 32;
848 for (size_t b = 0; b < nb; ++b) {
849 const uint8_t* p = src + (b0 + b) * bsz;
850 uint16_t dh, mh = 0;
851 std::memcpy(&dh, p, 2);
852 p += 2;
853 if (has_min) { std::memcpy(&mh, p, 2); p += 2; }
854 uint32_t qh = 0;
855 if (five_bit) { std::memcpy(&qh, p, 4); p += 4; }
856 const float d = fp16_to_fp32(dh, false), m = has_min ? fp16_to_fp32(mh, false) : 0.0f;
857 const int offset = has_min ? 0 : (five_bit ? 16 : 8);
858 float* y = out + b * 32;
859 for (int j = 0; j < 16; ++j) {
860 int lo = p[j] & 0x0F, hi = p[j] >> 4;
861 if (five_bit) {
862 lo |= ((qh >> j) & 1) << 4;
863 hi |= ((qh >> (j + 16)) & 1) << 4;
864 }
865 // ggml's dequantize_row_q4_0 / q5_0 write the product alone, q4_1 / q5_1 the product plus m (paperkiln
866 // spec/IR_SPEC.md 4.7): adding a +0 m to a -0 product would give +0, so the 0-offset forms add nothing.
867 if (has_min) {
868 y[j] = static_cast<float>(lo - offset) * d + m;
869 y[j + 16] = static_cast<float>(hi - offset) * d + m;
870 } else {
871 y[j] = static_cast<float>(lo - offset) * d;
872 y[j + 16] = static_cast<float>(hi - offset) * d;
873 }
874 }
875 }
876 return true;
877}
878
880 Logger::info("Mapping GGUF weights to model fields (ULTRA-OPTIMIZED VERSION)...");
881
882 const uint8_t* actual_data_block_start = nullptr;
883
884 // Determine which data source to use
885 if (gguf.mapped_tensor_data != nullptr && gguf.mapped_tensor_data_size > 0) {
886 const uint8_t* mmap_buffer_start = static_cast<const uint8_t*>(gguf.mapped_tensor_data);
887 actual_data_block_start = mmap_buffer_start + gguf.offset_diff_for_mmap;
888 Logger::info("map_gguf_weights: Using mmap mode (ZERO-COPY). Size: " +
889 std::to_string(gguf.mapped_tensor_data_size) + " bytes.");
890 } else if (!gguf.tensor_data.empty()) {
891 actual_data_block_start = gguf.tensor_data.data();
892 Logger::info("map_gguf_weights: Using non-mmap mode. Size: " +
893 std::to_string(gguf.tensor_data.size()) + " bytes.");
894 } else {
895 Logger::error("GGUF tensor data is not available. Cannot map weights.");
896 return;
897 }
898
899 const size_t num_tensors = gguf.tensor_infos_map.size();
900 Logger::info("Processing " + std::to_string(num_tensors) + " tensors with ultra-optimized parallel mapping...");
901
902 // Pre-allocate containers to avoid reallocations during parallel processing
903 std::vector<std::pair<std::string, GGUFTensorInfo>> tensor_pairs;
904 tensor_pairs.reserve(num_tensors);
905 for (const auto& pair : gguf.tensor_infos_map) {
906 tensor_pairs.emplace_back(pair.first, pair.second);
907 }
908
909 // Reserve capacity for major model containers to reduce allocations
910 const size_t typical_blocks = 4096;
911 if (model.lm_head_q8_0.capacity() == 0) model.lm_head_q8_0.reserve(32768);
912 if (model.embed_tokens_q8_0.capacity() == 0) model.embed_tokens_q8_0.reserve(32768);
913
914 for (auto& layer : model.layers) {
915 if (layer.q_proj_q8_0.capacity() == 0) layer.q_proj_q8_0.reserve(typical_blocks);
916 if (layer.k_proj_q8_0.capacity() == 0) layer.k_proj_q8_0.reserve(typical_blocks);
917 if (layer.v_proj_q8_0.capacity() == 0) layer.v_proj_q8_0.reserve(typical_blocks);
918 if (layer.o_proj_q8_0.capacity() == 0) layer.o_proj_q8_0.reserve(typical_blocks);
919 if (layer.gate_proj_q8_0.capacity() == 0) layer.gate_proj_q8_0.reserve(typical_blocks);
920 if (layer.up_proj_q8_0.capacity() == 0) layer.up_proj_q8_0.reserve(typical_blocks);
921 if (layer.down_proj_q8_0.capacity() == 0) layer.down_proj_q8_0.reserve(typical_blocks);
922 }
923
924 // BLAZING FAST: Sort tensors by type and process in bulk
925 std::vector<size_t> global_tensor_indices;
926 std::vector<std::vector<size_t>> layer_tensor_indices(model.layers.size());
927
928 global_tensor_indices.reserve(10); // output.weight, token_embd.weight, output_norm.weight, etc.
929 for (auto& layer_indices : layer_tensor_indices) {
930 layer_indices.reserve(9); // 7 weights + 2 norms per layer
931 }
932
933 // ULTRA-FAST categorization without string operations
934 for (size_t i = 0; i < tensor_pairs.size(); ++i) {
935 const std::string& name = tensor_pairs[i].first;
936 if (name[0] == 'o' || name[0] == 't') { // output.weight, token_embd.weight, output_norm.weight
937 global_tensor_indices.push_back(i);
938 } else if (name.size() > 4 && name[0] == 'b' && name[1] == 'l' && name[2] == 'k' && name[3] == '.') {
939 // Extract layer index without substr - MUCH faster
940 size_t layer_start = 4;
941 size_t layer_end = name.find('.', layer_start);
942 if (layer_end != std::string::npos) {
943 int layer_idx = 0;
944 for (size_t pos = layer_start; pos < layer_end; ++pos) {
945 layer_idx = layer_idx * 10 + (name[pos] - '0');
946 }
947 if (layer_idx >= 0 && static_cast<size_t>(layer_idx) < model.layers.size()) {
948 layer_tensor_indices[layer_idx].push_back(i);
949 }
950 }
951 }
952 }
953
954 std::atomic<int> processed_count{0};
955 std::atomic<int> error_count{0};
956 std::mutex first_error_mu;
957 std::string first_error; // reported if any tensor fails to map
958
959 // Process global tensors sequentially (small count, avoid overhead)
960 for (size_t idx : global_tensor_indices) {
961 try {
962 const std::string& target_field_key = tensor_pairs[idx].first;
963 const GGUFTensorInfo& info = tensor_pairs[idx].second;
965
966 // Global tensors with optimized type dispatch
967 if (target_field_key == "output.weight") {
968 switch (info.type) {
970 size_t num_blocks = info.size_in_bytes / sizeof(block_q6_K);
971 model.lm_head_q6k.resize(num_blocks);
972 std::memcpy(model.lm_head_q6k.data(), tensor_data_ptr, info.size_in_bytes);
973 break;
974 }
976 size_t num_blocks = info.size_in_bytes / sizeof(block_q4_K);
977 model.lm_head_q4k.resize(num_blocks);
978 std::memcpy(model.lm_head_q4k.data(), tensor_data_ptr, info.size_in_bytes);
979 break;
980 }
982 model.lm_head_q5k.resize(info.size_in_bytes / sizeof(block_q5_K));
983 std::memcpy(model.lm_head_q5k.data(), tensor_data_ptr, info.size_in_bytes);
984 break;
985 }
987 size_t num_blocks = info.size_in_bytes / sizeof(block_q8_0);
988 model.lm_head_q8_0.resize(num_blocks);
989 std::memcpy(model.lm_head_q8_0.data(), tensor_data_ptr, info.size_in_bytes);
990 break;
991 }
993 size_t num_blocks = info.size_in_bytes / sizeof(block_q8_K);
994 model.lm_head_q8k.resize(num_blocks);
995 std::memcpy(model.lm_head_q8k.data(), tensor_data_ptr, info.size_in_bytes);
996 break;
997 }
999 size_t num_elements = info.size_in_bytes / sizeof(float);
1000 model.lm_head_f32.resize(num_elements);
1001 std::memcpy(model.lm_head_f32.data(), tensor_data_ptr, info.size_in_bytes);
1002 break;
1003 }
1004 default:
1005 if (!dequant_legacy_to_f32(info, tensor_data_ptr, model.lm_head_f32)) throw std::runtime_error(target_field_key + ": unsupported tensor type " + ggml_type_name(info.type));
1006 break;
1007 }
1009 continue;
1010 }
1011
1012 if (target_field_key == "token_embd.weight") {
1013 switch (info.type) {
1015 size_t num_blocks = info.size_in_bytes / sizeof(block_q4_K);
1016 model.embed_tokens_q4k.resize(num_blocks);
1017 std::memcpy(model.embed_tokens_q4k.data(), tensor_data_ptr, info.size_in_bytes);
1018 break;
1019 }
1021 model.embed_tokens_q5k.resize(info.size_in_bytes / sizeof(block_q5_K));
1022 std::memcpy(model.embed_tokens_q5k.data(), tensor_data_ptr, info.size_in_bytes);
1023 break;
1024 }
1026 size_t num_blocks = info.size_in_bytes / sizeof(block_q8_0);
1027 model.embed_tokens_q8_0.resize(num_blocks);
1028 std::memcpy(model.embed_tokens_q8_0.data(), tensor_data_ptr, info.size_in_bytes);
1029 break;
1030 }
1032 size_t num_blocks = info.size_in_bytes / sizeof(block_q8_K);
1033 model.embed_tokens_q8k.resize(num_blocks);
1034 std::memcpy(model.embed_tokens_q8k.data(), tensor_data_ptr, info.size_in_bytes);
1035 break;
1036 }
1038 size_t num_blocks = info.size_in_bytes / sizeof(block_q6_K);
1039 model.embed_tokens_q6k.resize(num_blocks);
1040 std::memcpy(model.embed_tokens_q6k.data(), tensor_data_ptr, info.size_in_bytes);
1041 break;
1042 }
1044 size_t num_elements = info.size_in_bytes / sizeof(float);
1045 model.embed_tokens_f32.resize(num_elements);
1046 std::memcpy(model.embed_tokens_f32.data(), tensor_data_ptr, info.size_in_bytes);
1047 break;
1048 }
1049 default:
1050 if (!dequant_legacy_to_f32(info, tensor_data_ptr, model.embed_tokens_f32)) throw std::runtime_error(target_field_key + ": unsupported tensor type " + ggml_type_name(info.type));
1051 break;
1052 }
1054 continue;
1055 }
1056
1057 if (target_field_key == "output_norm.weight") {
1058 if (info.type == GGMLType::GGML_TYPE_F32) {
1059 size_t num_elements = info.size_in_bytes / sizeof(float);
1060 model.final_norm_f32.resize(num_elements);
1061 std::memcpy(model.final_norm_f32.data(), tensor_data_ptr, info.size_in_bytes);
1062 } else if (!dequant_legacy_to_f32(info, tensor_data_ptr, model.final_norm_f32)) throw std::runtime_error(target_field_key + ": unsupported tensor type " + ggml_type_name(info.type));
1064 continue;
1065 }
1066
1067 } catch (const std::exception& e) {
1068 error_count++;
1069 std::lock_guard<std::mutex> lk(first_error_mu);
1070 if (first_error.empty()) first_error = e.what();
1071 }
1072 }
1073
1074 // ULTRA-FAST: Process layers in parallel with pre-sorted tensors
1075 #pragma omp parallel for schedule(static) if(model.layers.size() > 4)
1076 for (int layer_idx = 0; layer_idx < (int)layer_tensor_indices.size(); ++layer_idx) {
1078 if (layer_indices.empty()) continue;
1079
1081
1082 try {
1083 for (size_t idx : layer_indices) {
1084 const std::string& name = tensor_pairs[idx].first;
1085 const GGUFTensorInfo& info = tensor_pairs[idx].second;
1087
1088 // BLAZING FAST: Direct character matching (faster than hashing)
1089 const size_t last_dot = name.find_last_of('.');
1090 if (last_dot == std::string::npos) continue;
1091
1092 const char* field = name.c_str() + name.find('.', 4) + 1;
1093
1094 // BLAZING FAST: Direct character-based dispatch without string operations
1095 #define LEGACY_OR_THROW(target_f32) \
1096 default: \
1097 if (!dequant_legacy_to_f32(info, tensor_data_ptr, target_f32)) \
1098 throw std::runtime_error(name + ": unsupported tensor type " + ggml_type_name(info.type)); \
1099 break;
1100 #define FAST_COPY_WEIGHT(target_vec, block_type) \
1101 target_vec.resize(info.size_in_bytes / sizeof(block_type)); \
1102 std::memcpy(target_vec.data(), tensor_data_ptr, info.size_in_bytes);
1103
1104 // IMPROVED: Pattern matching based on tensor name structure
1105 const char* name_cstr = name.c_str();
1106 const size_t name_len = name.length();
1107
1108 if (name_len > 10 && name.find("attn_") != std::string::npos) {
1109 if (name.find("attn_q.weight") != std::string::npos) {
1110 switch (info.type) {
1111 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.q_proj_q8_0, block_q8_0); break;
1117 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.q_proj_f32, float);
1118 Logger::info("[MAP_DEBUG] " + name + " F32 copied, elems=" +
1119 std::to_string(layer.q_proj_f32.size()) +
1120 " into layer_idx=" + std::to_string(layer_idx));
1121 break; // transformer_cpp fp32 exports
1122 LEGACY_OR_THROW(layer.q_proj_f32)
1123 }
1124 } else if (name.find("attn_k.weight") != std::string::npos) {
1125 switch (info.type) {
1126 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.k_proj_q8_0, block_q8_0); break;
1132 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.k_proj_f32, float); break; // transformer_cpp fp32 exports
1133 LEGACY_OR_THROW(layer.k_proj_f32)
1134 }
1135 } else if (name.find("attn_v.weight") != std::string::npos) {
1136 switch (info.type) {
1137 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.v_proj_q8_0, block_q8_0); break;
1143 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.v_proj_f32, float); break; // transformer_cpp fp32 exports
1144 LEGACY_OR_THROW(layer.v_proj_f32)
1145 }
1146 } else if (name.find("attn_output.weight") != std::string::npos) {
1147 switch (info.type) {
1148 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.o_proj_q8_0, block_q8_0); break;
1154 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.o_proj_f32, float); break; // transformer_cpp fp32 exports
1155 LEGACY_OR_THROW(layer.o_proj_f32)
1156 }
1157 } else if (name.find("attn_norm.weight") != std::string::npos) {
1158 if (info.type == GGMLType::GGML_TYPE_F32) { FAST_COPY_WEIGHT(layer.input_layernorm_f32, float); }
1159 else if (!dequant_legacy_to_f32(info, tensor_data_ptr, layer.input_layernorm_f32))
1160 throw std::runtime_error(name + ": unsupported tensor type " + ggml_type_name(info.type));
1161 }
1162 } else if (name_len > 10 && name.find("ffn_") != std::string::npos) {
1163 if (name.find("ffn_gate_inp.weight") != std::string::npos) { // MoE router
1164 if (info.type == GGMLType::GGML_TYPE_F32) { FAST_COPY_WEIGHT(layer.moe_router_f32, float); }
1165 else if (info.type == GGMLType::GGML_TYPE_Q8_0) { FAST_COPY_WEIGHT(layer.moe_router_q8_0, block_q8_0); }
1166 else if (!dequant_legacy_to_f32(info, tensor_data_ptr, layer.moe_router_f32))
1167 throw std::runtime_error(name + ": unsupported tensor type " + ggml_type_name(info.type));
1168 } else if (name.find("_exps.weight") != std::string::npos) { // MoE expert stacks
1169 std::vector<float>& dst = name.find("ffn_gate_exps") != std::string::npos ? layer.moe_gate_exps_f32
1170 : name.find("ffn_up_exps") != std::string::npos ? layer.moe_up_exps_f32
1171 : layer.moe_down_exps_f32;
1172 if (info.type == GGMLType::GGML_TYPE_F32) { FAST_COPY_WEIGHT(dst, float); }
1173 else if (!dequant_legacy_to_f32(info, tensor_data_ptr, dst))
1174 throw std::runtime_error(name + ": MoE expert stacks must be F32, F16 or Q4_0/Q4_1/Q5_0/Q5_1 (got " +
1175 std::string(ggml_type_name(info.type)) + ")");
1176 } else if (name.find("ffn_gate.weight") != std::string::npos) {
1177 switch (info.type) {
1178 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.gate_proj_q8_0, block_q8_0); break;
1179 case GGMLType::GGML_TYPE_Q4_K: FAST_COPY_WEIGHT(layer.gate_proj_q4k, block_q4_K); break;
1180 case GGMLType::GGML_TYPE_Q5_K: FAST_COPY_WEIGHT(layer.gate_proj_q5k, block_q5_K); break;
1181 case GGMLType::GGML_TYPE_Q6_K: FAST_COPY_WEIGHT(layer.gate_proj_q6k, block_q6_K); break;
1182 case GGMLType::GGML_TYPE_Q8_K: FAST_COPY_WEIGHT(layer.gate_proj_q8k, block_q8_K); break;
1184 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.gate_proj_f32, float); break; // transformer_cpp fp32 exports
1185 LEGACY_OR_THROW(layer.gate_proj_f32)
1186 }
1187 } else if (name.find("ffn_up.weight") != std::string::npos) {
1188 switch (info.type) {
1189 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.up_proj_q8_0, block_q8_0); break;
1190 case GGMLType::GGML_TYPE_Q4_K: FAST_COPY_WEIGHT(layer.up_proj_q4k, block_q4_K); break;
1191 case GGMLType::GGML_TYPE_Q5_K: FAST_COPY_WEIGHT(layer.up_proj_q5k, block_q5_K); break;
1192 case GGMLType::GGML_TYPE_Q6_K: FAST_COPY_WEIGHT(layer.up_proj_q6k, block_q6_K); break;
1193 case GGMLType::GGML_TYPE_Q8_K: FAST_COPY_WEIGHT(layer.up_proj_q8k, block_q8_K); break;
1195 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.up_proj_f32, float); break; // transformer_cpp fp32 exports
1196 LEGACY_OR_THROW(layer.up_proj_f32)
1197 }
1198 } else if (name.find("ffn_down.weight") != std::string::npos) {
1199 switch (info.type) {
1200 case GGMLType::GGML_TYPE_Q8_0: FAST_COPY_WEIGHT(layer.down_proj_q8_0, block_q8_0); break;
1201 case GGMLType::GGML_TYPE_Q4_K: FAST_COPY_WEIGHT(layer.down_proj_q4k, block_q4_K); break;
1202 case GGMLType::GGML_TYPE_Q5_K: FAST_COPY_WEIGHT(layer.down_proj_q5k, block_q5_K); break;
1203 case GGMLType::GGML_TYPE_Q6_K: FAST_COPY_WEIGHT(layer.down_proj_q6k, block_q6_K); break;
1204 case GGMLType::GGML_TYPE_Q8_K: FAST_COPY_WEIGHT(layer.down_proj_q8k, block_q8_K); break;
1206 case GGMLType::GGML_TYPE_F32: FAST_COPY_WEIGHT(layer.down_proj_f32, float); break; // transformer_cpp fp32 exports
1207 LEGACY_OR_THROW(layer.down_proj_f32)
1208 }
1209 } else if (name.find("ffn_norm.weight") != std::string::npos) {
1210 if (info.type == GGMLType::GGML_TYPE_F32) { FAST_COPY_WEIGHT(layer.post_attention_layernorm_f32, float); }
1211 else if (!dequant_legacy_to_f32(info, tensor_data_ptr, layer.post_attention_layernorm_f32))
1212 throw std::runtime_error(name + ": unsupported tensor type " + ggml_type_name(info.type));
1213 }
1214 }
1215
1216 #undef FAST_COPY_WEIGHT
1217 #undef LEGACY_OR_THROW
1218 }
1220 } catch (const std::exception& e) {
1221 error_count++;
1222 std::lock_guard<std::mutex> lk(first_error_mu);
1223 if (first_error.empty()) first_error = e.what();
1224 }
1225 }
1226
1227 Logger::info("Finished mapping GGUF weights: " + std::to_string(processed_count.load()) + "/" +
1228 std::to_string(num_tensors) + " tensors processed successfully (errors: " +
1229 std::to_string(error_count.load()) + ") with ultra-optimized parallel mapping");
1230 // A tensor that failed to map would leave the model silently incomplete.
1231 if (error_count.load() > 0)
1232 throw std::runtime_error("GGUF weight mapping failed for " + std::to_string(error_count.load()) +
1233 " layer(s); first error: " + first_error);
1234}
1235
1236#ifndef HAS_CUDA
1237
1239 Logger::info("CPU-only build: ensure_f32_concatenated_weights_loaded is a no-op");
1240}
1241
1243 Logger::info("CPU-only build: ensure_bf16_concatenated_weights_loaded is a no-op");
1244}
1245
1247 Logger::info("CPU-only build: free_bf16_concatenated_weights is a no-op");
1248}
1249
1250#endif // HAS_CUDA
1251
static void warning(const std::string &message)
Definition logger.cpp:139
static void info(const std::string &message)
Definition logger.cpp:135
static void error(const std::string &message)
Definition logger.cpp:143
Main transformer model class for TinyLlama.
Definition model.h:309
void free_layer_gpu_weights(int layer_idx)
bool f32_concatenated_weights_loaded_
Definition model.h:699
std::vector< block_q6_K > embed_tokens_q6k
Definition model.h:584
bool dequantize_all_if_small(size_t max_params)
void ensure_up_proj_dequantized(int layer_idx)
std::vector< float > final_norm_f32
Definition model.h:581
void free_bf16_concatenated_weights()
void ensure_v_proj_dequantized(int layer_idx)
std::vector< block_q4_K > lm_head_q4k
Definition model.h:582
std::vector< block_q6_K > lm_head_q6k
Definition model.h:584
void ensure_layer_weights_on_gpu(int layer_idx)
std::vector< block_q5_K > lm_head_q5k
Definition model.h:583
void ensure_embed_tokens_dequantized()
std::vector< LayerWeights > layers
Definition model.h:587
std::vector< block_q8_0 > embed_tokens_q8_0
Definition model.h:585
ModelConfig config_
Definition model.h:573
void ensure_o_proj_dequantized(int layer_idx)
void clear_layer_dequantized_weights(int layer_idx)
std::vector< block_q4_K > embed_tokens_q4k
Definition model.h:582
void ensure_k_proj_dequantized(int layer_idx)
std::vector< block_q8_0 > lm_head_q8_0
Definition model.h:585
std::vector< uint16_t > lm_head
Definition model.h:579
void ensure_f32_concatenated_weights_loaded()
std::vector< block_q5_K > embed_tokens_q5k
Definition model.h:583
std::vector< uint16_t > embed_tokens
Definition model.h:578
std::vector< block_q8_K > embed_tokens_q8k
Definition model.h:586
void ensure_bf16_concatenated_weights_loaded()
void ensure_q_proj_dequantized(int layer_idx)
void ensure_down_proj_dequantized(int layer_idx)
void ensure_gate_proj_dequantized(int layer_idx)
std::vector< float > embed_tokens_f32
Definition model.h:581
void ensure_lm_head_dequantized()
std::vector< float > lm_head_f32
Definition model.h:581
std::vector< block_q8_K > lm_head_q8k
Definition model.h:586
void convert_fp32_to_bf16_cuda(const float *fp32_in_dev, uint16_t *bf16_out_dev, size_t n_elements, cudaStream_t stream)
Type definitions for GGML (Georgi Gerganov Machine Learning) library.
GGMLType
Enumeration of GGML tensor data types.
Definition ggml_types.h:21
@ GGML_TYPE_F32
Definition ggml_types.h:22
@ GGML_TYPE_BF16
Definition ggml_types.h:40
@ GGML_TYPE_Q5_0
Definition ggml_types.h:27
@ GGML_TYPE_Q8_K
Definition ggml_types.h:36
@ GGML_TYPE_F16
Definition ggml_types.h:23
@ GGML_TYPE_Q6_K
Definition ggml_types.h:35
@ GGML_TYPE_Q4_1
Definition ggml_types.h:25
@ GGML_TYPE_Q8_0
Definition ggml_types.h:29
@ GGML_TYPE_Q5_K
Definition ggml_types.h:34
@ GGML_TYPE_Q5_1
Definition ggml_types.h:28
@ GGML_TYPE_Q4_K
Definition ggml_types.h:33
@ GGML_TYPE_Q4_0
Definition ggml_types.h:24
constexpr size_t GGML_QK8_0
Definition gguf_parser.h:43
constexpr size_t GGML_QK_K
Block size constants for different quantization formats.
Definition gguf_parser.h:42
const float * p[kIrDepthSourcesMax]
int n
std::vector< float > v
const char * name
node out
size_t j
Logging utilities for the TinyLlama implementation.
float bfloat16_to_float32(uint16_t b16)
Definition utils.cpp:145
Template get(const std::string &id)
void dequantize_q4_k_m(const block_q4_K *qblock, float *output, int num_weights_in_block, bool log_this_block)
void dequantize_vector_q5k_to_f32(const std::vector< block_q5_K > &q_weights, std::vector< float > &f32_weights, size_t total_num_elements)
Dequantizes a vector of Q5_K blocks to float32 (total_num_elements a multiple of GGML_QK_K)
size_t ggml_type_size(GGMLType type)
Gets the size in bytes of a GGML type.
const char * ggml_type_name(GGMLType type)
Gets the string name of a GGML type.
void dequantize_q5_k(const block_q5_K *qblock, float *output)
void dequantize_vector_q6k_to_f32(const std::vector< block_q6_K > &q_weights, std::vector< float > &f32_weights, size_t total_num_elements, int log_first_n_blocks)
Dequantizes a vector of Q6_K blocks to a vector of float32.
float fp16_to_fp32(uint16_t h, bool is_gguf_scale_field)
Converts a 16-bit floating point number to 32-bit float.
void dequantize_vector_q8_0_to_f32(const std::vector< block_q8_0 > &q_weights, std::vector< float > &f32_weights, size_t total_num_elements, int log_first_n_blocks)
Dequantizes a vector of Q8_0 blocks to a vector of float32.
void dequantize_vector_q4k_to_f32(const std::vector< block_q4_K > &q_weights, std::vector< float > &f32_weights, size_t total_num_elements, int log_first_n_blocks)
Dequantizes a vector of Q4_K blocks to a vector of float32.
void dequantize_q8_0_block(const block_q8_0 *qblock, float *output)
Dequantizes a Q8_0 block to float32.
void dequantize_q8_k(const std::vector< block_q8_K > &q_data, std::vector< float > &x, int n, bool log_this_block)
void dequantize_q6_k(const block_q6_K *qblock, float *output, int num_weights_in_block, bool log_this_block)
Weight quantization structures and functions for model compression.
Complete representation of a GGUF file's contents.
Information about a tensor stored in a GGUF file.
size_t size_in_bytes
size_t num_elements
uint64_t offset
Structure holding all weights for a single transformer layer.
Definition model.h:253
int hidden_size
Definition model.h:83
int vocab_size
Definition model.h:88
int num_attention_heads
Definition model.h:85
int intermediate_size
Definition model.h:84
int num_cpu_offload_layers
Definition model.h:106
bool enable_memory_efficient_layers
Definition model.h:111
int num_hidden_layers
Definition model.h:87
int num_key_value_heads
Definition model.h:86
int num_experts
Definition model.h:107
4-bit K-quantized block structure
5-bit K-quantized block structure (ggml's block_q5_K)
6-bit K-quantized block structure
Simple 8-bit quantized block structure.
8-bit K-quantized block structure with block sums
std::vector< float > bf16vec_to_float_vec(const std::vector< uint16_t > &v_bf16)
Definition utils.cpp:199
#define FAST_COPY_WEIGHT(target_vec, block_type)
void map_gguf_weights(const GGUFData &gguf, TinyLlamaModel &model)
#define EMBER_RECOVERABLE(w)
bool dequant_legacy_to_f32(const GGUFTensorInfo &info, const uint8_t *src, std::vector< float > &out)
static const float * fp16_table()
bool dequant_span_to_f32(GGMLType t, const uint8_t *src, size_t first, size_t n, float *out)
#define LEGACY_OR_THROW(target_f32)