Download cpp/sdq_buft.cpp from HelloSun/sddqwen35a3b: direct link, hf CLI and curl.
- Browser
- Download file 7.98 kB
-
https://huggingface.co/HelloSun/sddqwen35a3b/resolve/main/cpp/sdq_buft.cpp
- Command line
-
hf download hf://HelloSun/sddqwen35a3b/cpp/sdq_buft.cpp
-
curl -L -o sdq_buft.cpp https://huggingface.co/HelloSun/sddqwen35a3b/resolve/main/cpp/sdq_buft.cpp
7.98 kB
| // SDQ expert buffer type | |
| // | |
| // 這是一個「假」buffer type:它宣稱接受所有張量,但實際上 | |
| // * 不配置任何 RAM(alloc_buffer 回傳 0 大小的 buffer) | |
| // * set_tensor 忽略傳入的資料(資料來自 llama.cpp 的 mmap,我們不碰) | |
| // * 真正的資料由 sdq::pager 從檔案 pread 到自己的 arena | |
| // | |
| // 用途:讓 llama.cpp 把 18.2 GiB 的 MoE 專家權重「掛在模型上」卻不放進 RAM, | |
| // 改由分頁器管理 SSD → RAM → CPU 的三層資料流。 | |
| namespace sdq { | |
| // 給 tensor->data 的假位址。每個 expert 權重張量分到一小塊(預設 4 KB), | |
| // 指標必須有效(ggml 會做指標運算),但內容永遠不會被讀。 | |
| struct expert_buffer { | |
| std::vector<uint8_t> scratch; | |
| }; | |
| static bool g_enabled = false; | |
| void sdq_set_enabled(bool on) { g_enabled = on; } | |
| bool sdq_enabled() { return g_enabled; } | |
| static config g_cfg; | |
| void sdq_set_config(const config & c) { g_cfg = c; } | |
| const config & sdq_get_config() { return g_cfg; } | |
| // ---------------------------------------------------------------- buffer iface | |
| // ggml_backend_buffer_i 的欄位依賴下面的實作,先給宣告 | |
| struct ggml_backend_buffer_i sdq_buffer_iface(); | |
| static const char * sdq_buft_get_name(ggml_backend_buffer_type_t buft) { | |
| (void) buft; | |
| return "SDQ_ExpertSSD"; | |
| } | |
| static size_t sdq_buft_get_alignment(ggml_backend_buffer_type_t buft) { | |
| (void) buft; | |
| return 4096; | |
| } | |
| static size_t sdq_buft_get_max_size(ggml_backend_buffer_type_t buft) { | |
| (void) buft; | |
| return SIZE_MAX; | |
| } | |
| // 每個 expert 權重張量只回報 4 KB:18.2 GiB 的權重「不會」被載入 RAM 的關鍵。 | |
| static size_t sdq_buft_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) { | |
| (void) buft; | |
| (void) tensor; | |
| return 4096; | |
| } | |
| static bool sdq_buft_is_host(ggml_backend_buffer_type_t buft) { | |
| (void) buft; | |
| return false; | |
| } | |
| static ggml_backend_buffer_t sdq_buft_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) { | |
| // llama.cpp 會用 get_alloc_size() 計算總大小後呼叫本函式; | |
| // 我們只保留真正需要的假位址空間(每個張量 4 KB)。 | |
| const size_t bytes = size < 4096 ? 4096 : size; | |
| auto * buf = new expert_buffer(); | |
| buf->scratch.assign(bytes, 0); | |
| auto * b = new ggml_backend_buffer{ | |
| /* .iface = */ sdq_buffer_iface(), | |
| /* .buft = */ buft, | |
| /* .context = */ buf, | |
| /* .size = */ bytes, | |
| /* .usage = */ GGML_BACKEND_BUFFER_USAGE_WEIGHTS, | |
| }; | |
| return b; | |
| } | |
| // 一次配置整個 context 的張量:每個張量 4 KB 假位址,實際權重 0 RAM。 | |
| static ggml_backend_buffer_t sdq_buft_alloc_buffer_n(ggml_backend_buffer_type_t buft, | |
| ggml_tensor ** tensors, int n_tensors) { | |
| const size_t slot = 4096; | |
| const size_t bytes = slot * (size_t) std::max(n_tensors, 1); | |
| ggml_backend_buffer_t buffer = sdq_buft_alloc_buffer(buft, bytes); | |
| if (!buffer) { | |
| return nullptr; | |
| } | |
| uint8_t * base = (uint8_t *) buffer->iface.get_base(buffer); | |
| size_t off = 0; | |
| for (int i = 0; i < n_tensors; i++) { | |
| ggml_tensor * t = tensors[i]; | |
| t->data = base + off; | |
| t->buffer = buffer; | |
| off += slot; | |
| buffer->iface.init_tensor(buffer, t); | |
| } | |
| return buffer; | |
| } | |
| static void sdq_buft_free_buffer(ggml_backend_buffer_t buffer) { | |
| // 只釋放自己的資料;ggml_backend_buffer_free() 會負責 delete buffer 本體, | |
| // 這裡再 delete 一次會造成 double free。 | |
| delete (expert_buffer *) buffer->context; | |
| buffer->context = nullptr; | |
| } | |
| static void * sdq_buft_get_base(ggml_backend_buffer_t buffer) { | |
| return ((expert_buffer *) buffer->context)->scratch.data(); | |
| } | |
| static ggml_status sdq_buft_init_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor) { | |
| (void) buffer; | |
| (void) tensor; | |
| return GGML_STATUS_SUCCESS; | |
| } | |
| static void sdq_buft_memset_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, | |
| uint8_t value, size_t offset, size_t size) { | |
| (void) buffer; (void) tensor; (void) value; (void) offset; (void) size; | |
| } | |
| static void sdq_buft_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, | |
| const void * data, size_t offset, size_t size) { | |
| // 關鍵:忽略。llama.cpp 會把 mmap 的資料交給我們,這裡一個位元組都不落地, | |
| // 專家權利永遠不會佔用 RAM。 | |
| (void) buffer; (void) tensor; (void) data; (void) offset; (void) size; | |
| } | |
| static void sdq_buft_get_tensor(ggml_backend_buffer_t buffer, const ggml_tensor * tensor, | |
| void * data, size_t offset, size_t size) { | |
| (void) buffer; (void) tensor; (void) offset; | |
| memset(data, 0, size); | |
| } | |
| static bool sdq_buft_cpy_tensor(ggml_backend_buffer_t buffer, const ggml_tensor * src, ggml_tensor * dst) { | |
| (void) buffer; (void) src; (void) dst; | |
| return true; | |
| } | |
| static void sdq_buft_clear(ggml_backend_buffer_t buffer, uint8_t value) { | |
| (void) buffer; (void) value; | |
| } | |
| struct ggml_backend_buffer_i sdq_buffer_iface() { | |
| static ggml_backend_buffer_i i = [] { | |
| ggml_backend_buffer_i x {}; | |
| x.free_buffer = sdq_buft_free_buffer; | |
| x.get_base = sdq_buft_get_base; | |
| x.init_tensor = sdq_buft_init_tensor; | |
| x.memset_tensor = sdq_buft_memset_tensor; | |
| x.set_tensor = sdq_buft_set_tensor; | |
| x.get_tensor = sdq_buft_get_tensor; | |
| x.cpy_tensor = sdq_buft_cpy_tensor; | |
| x.clear = sdq_buft_clear; | |
| return x; | |
| }(); | |
| return i; | |
| } | |
| ggml_backend_buffer_type_t sdq_expert_buft() { | |
| static ggml_backend_buffer_type_t buft = [] { | |
| return new ggml_backend_buffer_type{ | |
| /* .iface = */ { | |
| /* .get_name = */ sdq_buft_get_name, | |
| /* .alloc_buffer = */ sdq_buft_alloc_buffer, | |
| /* .alloc_buffer_n = */ sdq_buft_alloc_buffer_n, | |
| /* .get_alignment = */ sdq_buft_get_alignment, | |
| /* .get_max_size = */ sdq_buft_get_max_size, | |
| /* .get_alloc_size = */ sdq_buft_get_alloc_size, | |
| /* .get_alloc_size_n = */ nullptr, | |
| /* .is_host = */ sdq_buft_is_host, | |
| }, | |
| /* .device = */ nullptr, | |
| /* .context = */ nullptr, | |
| }; | |
| }(); | |
| return buft; | |
| } | |
| // llama.cpp 端需要的介面宣告 | |
| void sdq_install_buft_overrides(llama_model_params * params); | |
| static const llama_model_tensor_buft_override * sdq_overrides() { | |
| static llama_model_tensor_buft_override ov[] = { | |
| { "blk\\.[0-9]+\\.ffn_(gate|up|down|gate_up)_exps\\.weight", sdq_expert_buft() }, | |
| { nullptr, nullptr }, | |
| }; | |
| return ov; | |
| } | |
| void sdq_install_buft_overrides(llama_model_params * params) { | |
| if (!params) { | |
| return; | |
| } | |
| if (params->tensor_buft_overrides == nullptr) { | |
| params->tensor_buft_overrides = sdq_overrides(); | |
| } else { | |
| // 使用者已有 override:把手動指定的順序原樣保留,附加在尾端 | |
| size_t n = 0; | |
| while (params->tensor_buft_overrides[n].pattern != nullptr) { | |
| n++; | |
| } | |
| static std::vector<llama_model_tensor_buft_override> merged; | |
| merged.clear(); | |
| for (size_t i = 0; i < n; i++) { | |
| merged.push_back(params->tensor_buft_overrides[i]); | |
| } | |
| for (size_t i = 0; sdq_overrides()[i].pattern != nullptr; i++) { | |
| merged.push_back(sdq_overrides()[i]); | |
| } | |
| merged.push_back({ nullptr, nullptr }); | |
| params->tensor_buft_overrides = merged.data(); | |
| } | |
| } | |
| } // namespace sdq |