mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-25 14:07:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6050b467c0 | ||
|
|
2280f0fd16 | ||
|
|
1a0b56cec7 | ||
|
|
63412d8737 | ||
|
|
5c965fd8ca | ||
|
|
f1168ac417 | ||
|
|
fa3645feb5 | ||
|
|
7790a9231d | ||
|
|
c49edf0963 | ||
|
|
a55c16e4ee | ||
|
|
e321bdad7e | ||
|
|
459a1ba549 | ||
|
|
f0fb5984b0 | ||
|
|
9a4580b61c | ||
|
|
b083c384f7 | ||
|
|
344be0a600 | ||
|
|
b3fa69955e | ||
|
|
74bca39500 |
@@ -9,7 +9,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: [self-hosted, Linux, CPU]
|
||||
runs-on: [self-hosted, Linux, fast]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
- sanitizer: THREAD
|
||||
machine: ubuntu-24.04
|
||||
- sanitizer: UNDEFINED
|
||||
machine: [self-hosted, X64, Linux]
|
||||
machine: [self-hosted, X64, Linux, CPU]
|
||||
|
||||
runs-on: ${{ matrix.machine }}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ env:
|
||||
|
||||
jobs:
|
||||
cpu-x64-high-perf:
|
||||
runs-on: [self-hosted, Linux, X64]
|
||||
runs-on: [self-hosted, Linux, X64, CPU]
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
|
||||
@@ -460,10 +460,10 @@ function gg_run_qwen3_0_6b {
|
||||
# https://github.com/ggml-org/llama.cpp/actions/runs/39639985732
|
||||
if [ -z ${GG_BUILD_WEBGPU} ]; then
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa off ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
fi
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
|
||||
check_ppl "f16" "$(cat $OUT/${ci}-tg-f16.log | grep "^\[1\]")"
|
||||
if [ -z ${GG_BUILD_NO_BF16} ]; then
|
||||
|
||||
+89
-6
@@ -428,6 +428,41 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
|
||||
if (message.contains("tool_call_id")) {
|
||||
msg.tool_call_id = message.at("tool_call_id");
|
||||
}
|
||||
{
|
||||
json extra = json::object();
|
||||
for (const auto & [k, v] : message.items()) {
|
||||
if (k != "role" && k != "content" && k != "tool_calls" && k != "reasoning_content" &&
|
||||
k != "name" && k != "tool_call_id") {
|
||||
extra[k] = v;
|
||||
}
|
||||
}
|
||||
if (has_tool_calls) {
|
||||
// per tool call: keys besides type/function/id, and function keys besides name/arguments
|
||||
json tc_extra = json::array();
|
||||
bool any = false;
|
||||
for (const auto & tool_call : message.at("tool_calls")) {
|
||||
json e = json::object();
|
||||
for (const auto & [k, v] : tool_call.items()) {
|
||||
if (k != "type" && k != "function" && k != "id") {
|
||||
e[k] = v;
|
||||
}
|
||||
}
|
||||
for (const auto & [k, v] : tool_call.at("function").items()) {
|
||||
if (k != "name" && k != "arguments") {
|
||||
e["function"][k] = v;
|
||||
}
|
||||
}
|
||||
any = any || !e.empty();
|
||||
tc_extra.push_back(e);
|
||||
}
|
||||
if (any) {
|
||||
extra["__tool_call_extras"] = tc_extra;
|
||||
}
|
||||
}
|
||||
if (!extra.empty()) {
|
||||
msg.extra_json = extra.dump();
|
||||
}
|
||||
}
|
||||
|
||||
msgs.push_back(msg);
|
||||
}
|
||||
@@ -516,6 +551,50 @@ static json render_message_to_json(const std::vector<common_chat_msg> & msgs, co
|
||||
return messages_inp_normalizer(c).normalize(messages);
|
||||
}
|
||||
|
||||
// DeepSeek V4.1 reads non-OpenAI message keys and joins content blocks with "\n\n" around images,
|
||||
// so keep media markers as separate text parts and restore the extra keys
|
||||
static json render_message_to_json_deepseek_v4_1(const std::vector<common_chat_msg> & msgs, const jinja::caps & c) {
|
||||
json messages = json::array();
|
||||
for (const auto & msg : msgs) {
|
||||
json jmsg;
|
||||
if (msg.contains_media() && msg.content.empty()) {
|
||||
common_chat_msg typed = msg;
|
||||
for (auto & part : typed.content_parts) {
|
||||
part.type = "text";
|
||||
}
|
||||
jmsg = typed.to_json_oaicompat(/* concat_typed_text= */ false);
|
||||
} else {
|
||||
jmsg = msg.to_json_oaicompat(/* concat_typed_text= */ false);
|
||||
}
|
||||
if (!msg.extra_json.empty()) {
|
||||
const json extra = json::parse(msg.extra_json);
|
||||
for (const auto & [k, v] : extra.items()) {
|
||||
if (k == "__tool_call_extras") {
|
||||
if (!jmsg.contains("tool_calls")) {
|
||||
continue;
|
||||
}
|
||||
auto & tcs = jmsg.at("tool_calls");
|
||||
for (size_t i = 0; i < tcs.size() && i < v.size(); i++) {
|
||||
for (const auto & [tk, tv] : v[i].items()) {
|
||||
if (tk == "function") {
|
||||
for (const auto & [fk, fv] : tv.items()) {
|
||||
tcs[i]["function"][fk] = fv;
|
||||
}
|
||||
} else {
|
||||
tcs[i][tk] = tv;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!jmsg.contains(k)) {
|
||||
jmsg[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
messages.push_back(std::move(jmsg));
|
||||
}
|
||||
return messages_inp_normalizer(c).normalize(messages);
|
||||
}
|
||||
|
||||
// DEPRECATED: only used in tests
|
||||
json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msgs, bool concat_typed_text) {
|
||||
jinja::caps c;
|
||||
@@ -1208,11 +1287,13 @@ static common_chat_params_init_fn common_chat_template_detect_params_init(const
|
||||
// DeepSeek V3.2/V4 format detection: template defines dsml_token and uses it for tool calls.
|
||||
// The template source contains the token as a variable assignment, not as a literal in markup.
|
||||
// V3.2 names the tool call block "function_calls", V4 names it "tool_calls".
|
||||
if (src.find("dsml_token") != std::string::npos &&
|
||||
src.find("DSML") != std::string::npos &&
|
||||
(src.find("function_calls") != std::string::npos ||
|
||||
src.find("tool_calls") != std::string::npos)) {
|
||||
LOG_DBG("Using specialized template: DeepSeek V3.2/V4\n");
|
||||
// V4.1 tag names have a leading space and its template does not name the dsml_token variable.
|
||||
if ((src.find("dsml_token") != std::string::npos &&
|
||||
src.find("DSML") != std::string::npos &&
|
||||
(src.find("function_calls") != std::string::npos ||
|
||||
src.find("tool_calls") != std::string::npos)) ||
|
||||
src.find("|DSML| calls>") != std::string::npos) {
|
||||
LOG_DBG("Using specialized template: DeepSeek V3.2/V4/V4.1\n");
|
||||
return common_chat_params_init_deepseek_v3_2;
|
||||
}
|
||||
|
||||
@@ -1313,7 +1394,9 @@ static common_chat_params common_chat_templates_apply_jinja(const struct common_
|
||||
workaround::trim_all_content(trimmed_messages);
|
||||
messages_to_render = &trimmed_messages;
|
||||
}
|
||||
params.messages = render_message_to_json(*messages_to_render, tmpl.original_caps());
|
||||
params.messages = src.find("|DSML| calls>") != std::string::npos ?
|
||||
render_message_to_json_deepseek_v4_1(*messages_to_render, tmpl.original_caps()) :
|
||||
render_message_to_json(*messages_to_render, tmpl.original_caps());
|
||||
params.tool_choice = inputs.tool_choice;
|
||||
params.reasoning_format = inputs.reasoning_format;
|
||||
params.enable_thinking = inputs.enable_thinking;
|
||||
|
||||
@@ -94,6 +94,8 @@ struct common_chat_msg {
|
||||
std::string reasoning_content;
|
||||
std::string tool_name;
|
||||
std::string tool_call_id;
|
||||
// non-OpenAI message keys as a JSON object string, only for templates that read them (DeepSeek V4.1)
|
||||
std::string extra_json;
|
||||
|
||||
common_json to_json_oaicompat(bool concat_typed_text = false) const;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "json.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <cfloat>
|
||||
@@ -1439,7 +1440,10 @@ const func_builtins & value_undefined_t::get_builtins() const {
|
||||
//////////////////////////////////
|
||||
|
||||
|
||||
static value from_json(const common_json & j, bool mark_input) {
|
||||
// the runtime walks values with recursion, a deeper input overflows the stack
|
||||
static constexpr int FROM_JSON_MAX_DEPTH = 128;
|
||||
|
||||
static value from_json(const common_json & j, bool mark_input, int depth = 0) {
|
||||
if (j.is_null()) {
|
||||
return mk_val<value_none>();
|
||||
} else if (j.is_boolean()) {
|
||||
@@ -1454,16 +1458,18 @@ static value from_json(const common_json & j, bool mark_input) {
|
||||
str->mark_input();
|
||||
}
|
||||
return str;
|
||||
} else if ((j.is_array() || j.is_object()) && depth >= FROM_JSON_MAX_DEPTH) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(FROM_JSON_MAX_DEPTH) + " levels deep");
|
||||
} else if (j.is_array()) {
|
||||
auto arr = mk_val<value_array>();
|
||||
for (const auto & item : j) {
|
||||
arr->push_back(from_json(item, mark_input));
|
||||
arr->push_back(from_json(item, mark_input, depth + 1));
|
||||
}
|
||||
return arr;
|
||||
} else if (j.is_object()) {
|
||||
auto obj = mk_val<value_object>();
|
||||
for (auto it = j.begin(); it != j.end(); ++it) {
|
||||
obj->insert(it.key(), from_json(it.value(), mark_input));
|
||||
obj->insert(it.key(), from_json(it.value(), mark_input, depth + 1));
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
|
||||
@@ -224,7 +224,21 @@ class common_chat_schema_builder {
|
||||
return node;
|
||||
}
|
||||
|
||||
// the builder and the code that walks the built schema recurse once per level, a deeper schema overflows the stack
|
||||
static constexpr int max_depth = 128;
|
||||
int depth_ = 0;
|
||||
|
||||
common_chat_schema_ptr build_node(const common_json & schema, const std::string & path) {
|
||||
if (depth_ >= max_depth) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(max_depth) + " levels deep");
|
||||
}
|
||||
depth_++;
|
||||
auto node = build_node_impl(schema, path);
|
||||
depth_--;
|
||||
return node;
|
||||
}
|
||||
|
||||
common_chat_schema_ptr build_node_impl(const common_json & schema, const std::string & path) {
|
||||
if (!schema.is_object()) {
|
||||
fail(path, "schema must be an object");
|
||||
}
|
||||
|
||||
+53
-10
@@ -1,4 +1,5 @@
|
||||
#include "parsers.h"
|
||||
#include "log.h"
|
||||
|
||||
// The DeepSeek V4 reference implementation renders consecutive tool results into a single
|
||||
// user block, ordered by the tool call order of the preceding assistant message (matched
|
||||
@@ -75,13 +76,30 @@ common_chat_params common_chat_params_init_deepseek_v3_2(const common_chat_templ
|
||||
// non-thinking generation prompt ends with a bare </think> instead of an empty
|
||||
// <think></think> pair.
|
||||
const bool is_v4 = tmpl.source().find("function_calls") == std::string::npos;
|
||||
// V4.1: DSML tag names carry a leading space, e.g. <|DSML| calls>
|
||||
const bool is_v41 = is_v4 && tmpl.source().find("|DSML| calls>") != std::string::npos;
|
||||
|
||||
std::optional<json> adjusted_messages;
|
||||
if (is_v4) {
|
||||
adjusted_messages = deepseek_v4_sort_tool_results(inputs.messages);
|
||||
}
|
||||
|
||||
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
|
||||
// V4.1 can declare tools on a system message instead of top-level "tools": parse calls to those too.
|
||||
json tools = inputs.tools;
|
||||
if (is_v41 && !(tools.is_array() && !tools.empty()) && inputs.messages.is_array()) {
|
||||
for (const auto & msg : inputs.messages) {
|
||||
if (msg.value("role", "") == "system" && msg.contains("tools") && msg.at("tools").is_array()) {
|
||||
if (!tools.is_array()) {
|
||||
tools = json::array();
|
||||
}
|
||||
for (const auto & tool : msg.at("tools")) {
|
||||
tools.push_back(tool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto has_tools = tools.is_array() && !tools.empty();
|
||||
auto has_response_format = !inputs.json_schema.is_null() && inputs.json_schema.is_object();
|
||||
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
|
||||
auto include_grammar = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE);
|
||||
@@ -90,17 +108,39 @@ common_chat_params common_chat_params_init_deepseek_v3_2(const common_chat_templ
|
||||
if (is_v4 && has_response_format) {
|
||||
additional_context = json{ { "response_format", inputs.json_schema } };
|
||||
}
|
||||
// V4.1 raises on anything but low/high/max or 1-100: map minimal -> low, xhigh -> max, medium -> default
|
||||
if (is_v41 && inputs.extra_context.is_object() && inputs.extra_context.contains("reasoning_effort") &&
|
||||
inputs.extra_context.at("reasoning_effort").is_string()) {
|
||||
const std::string effort = inputs.extra_context.at("reasoning_effort").get<std::string>();
|
||||
json mapped;
|
||||
if (effort == "minimal") {
|
||||
mapped = "low";
|
||||
} else if (effort == "xhigh") {
|
||||
mapped = "max";
|
||||
} else if (effort == "medium") {
|
||||
mapped = nullptr;
|
||||
}
|
||||
if (effort == "minimal" || effort == "xhigh" || effort == "medium") {
|
||||
LOG_DBG("%s: DeepSeek V4.1 reasoning_effort '%s' -> %s\n", __func__, effort.c_str(),
|
||||
mapped.is_null() ? "unset (template default)" : mapped.get<std::string>().c_str());
|
||||
if (!additional_context.has_value()) {
|
||||
additional_context = json::object();
|
||||
}
|
||||
(*additional_context)["reasoning_effort"] = mapped;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string DSML = "|DSML|";
|
||||
const std::string THINK_START = "<think>";
|
||||
const std::string THINK_END = "</think>";
|
||||
const std::string TC_BLOCK = is_v4 ? "tool_calls" : "function_calls";
|
||||
const std::string FC_START = "<" + DSML + TC_BLOCK + ">";
|
||||
const std::string FC_END = "</" + DSML + TC_BLOCK + ">";
|
||||
const std::string INVOKE_START = "<" + DSML + "invoke";
|
||||
const std::string INVOKE_END = "</" + DSML + "invoke>";
|
||||
const std::string PARAM_START = "<" + DSML + "parameter";
|
||||
const std::string PARAM_END = "</" + DSML + "parameter>";
|
||||
const std::string TAG_SP = is_v41 ? " " : "";
|
||||
const std::string TC_BLOCK = is_v41 ? "calls" : is_v4 ? "tool_calls" : "function_calls";
|
||||
const std::string FC_START = "<" + DSML + TAG_SP + TC_BLOCK + ">";
|
||||
const std::string FC_END = "</" + DSML + TAG_SP + TC_BLOCK + ">";
|
||||
const std::string INVOKE_START = "<" + DSML + TAG_SP + "invoke";
|
||||
const std::string INVOKE_END = "</" + DSML + TAG_SP + "invoke>";
|
||||
const std::string PARAM_START = "<" + DSML + TAG_SP + "parameter";
|
||||
const std::string PARAM_END = "</" + DSML + TAG_SP + "parameter>";
|
||||
const std::string GEN_PROMPT = "<|Assistant|>";
|
||||
const std::string TC_SEPARATOR = "\n\n";
|
||||
|
||||
@@ -152,7 +192,7 @@ common_chat_params common_chat_params_init_deepseek_v3_2(const common_chat_templ
|
||||
// build tool call section first since we might need it in reasoning
|
||||
auto tool_choice = p.choice();
|
||||
if (has_tool_calls) {
|
||||
foreach_function(inputs.tools, [&](size_t tool_index, const json & tool) {
|
||||
foreach_function(tools, [&](size_t tool_index, const json & tool) {
|
||||
const auto & function = tool.at("function");
|
||||
std::string name = function.at("name");
|
||||
|
||||
@@ -223,7 +263,10 @@ common_chat_params common_chat_params_init_deepseek_v3_2(const common_chat_templ
|
||||
}
|
||||
|
||||
if (extract_reasoning && inputs.enable_thinking) {
|
||||
reasoning = p.optional(THINK_START + p.reasoning(p.until(THINK_END)) + THINK_END);
|
||||
// V4.1 renders a continuation without reasoning as "<|Assistant|></think>" even in thinking mode,
|
||||
// so consume the bare THINK_END too or it leaks into the content
|
||||
reasoning = is_v41 ? p.optional((THINK_START + p.reasoning(p.until(THINK_END)) + THINK_END) | p.literal(THINK_END))
|
||||
: p.optional(THINK_START + p.reasoning(p.until(THINK_END)) + THINK_END);
|
||||
reasoning_with_tc = THINK_START +
|
||||
p.reasoning(p.until_one_of({ TC_SEPARATOR + FC_START, FC_START, THINK_END })) +
|
||||
p.space() + obligatory_tool_calls;
|
||||
|
||||
@@ -70,6 +70,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"LingDSparkModel": "qwen",
|
||||
"DeepseekV4ForCausalLM": "deepseek",
|
||||
"DeepseekV4DSparkModel": "deepseek",
|
||||
"DeepseekV41ForCausalLM": "deepseek41",
|
||||
"DistilBertForMaskedLM": "bert",
|
||||
"DistilBertForSequenceClassification": "bert",
|
||||
"DistilBertModel": "bert",
|
||||
@@ -326,6 +327,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"VllmD3Model": "vllm_d3",
|
||||
"DeepseekOCR2ForCausalLM": "deepseek",
|
||||
"DeepseekOCRForCausalLM": "deepseek",
|
||||
"DeepseekV41ForCausalLM": "deepseek41",
|
||||
"DeepseekV4ForCausalLM": "deepseek",
|
||||
"Dots3NoteForCausalLM": "dots3",
|
||||
"Dots3NoteForConditionalGeneration": "dots3",
|
||||
|
||||
@@ -0,0 +1,467 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Iterable, TYPE_CHECKING, cast
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import LazyTorchTensor, ModelBase, TextModel, gguf
|
||||
from .deepseek import DeepseekV4FlashVisionModel, DeepseekV4Model
|
||||
|
||||
|
||||
# Engram hash constants from inference/engram.py, stored in the GGUF: the token map needs the HF tokenizers
|
||||
# normalizers and the multipliers numpy's PCG64 stream, neither of which the runtime has.
|
||||
|
||||
def _is_prime(n: int) -> bool:
|
||||
if n < 2 or n % 2 == 0:
|
||||
return n == 2
|
||||
return all(n % d for d in range(3, math.isqrt(n) + 1, 2))
|
||||
|
||||
|
||||
def engram_primes(n_layers: int, max_ngram_size: int, n_heads: int, vocab_size: int) -> list[list[list[int]]]:
|
||||
"""EngramLayout.from_args: [layer][ngram order 2..max][head] bucket moduli, never reused across layers."""
|
||||
seen: set[int] = set()
|
||||
out: list[list[list[int]]] = []
|
||||
for _ in range(n_layers):
|
||||
per_ngram = []
|
||||
for _ in range(max_ngram_size - 1):
|
||||
sizes, cur = [], vocab_size - 1
|
||||
for _ in range(n_heads):
|
||||
cand = cur + 1
|
||||
while not _is_prime(cand) or cand in seen:
|
||||
cand += 1
|
||||
cur = cand
|
||||
seen.add(cur)
|
||||
sizes.append(cur)
|
||||
per_ngram.append(sizes)
|
||||
out.append(per_ngram)
|
||||
return out
|
||||
|
||||
|
||||
def engram_multipliers(layer_ids: list[int], max_ngram_size: int, compressed_vocab_size: int) -> list[list[int]]:
|
||||
"""compute_hash_multipliers: odd, bounded so that token_id * multiplier cannot overflow int64."""
|
||||
bound = max(1, (np.iinfo(np.int64).max // compressed_vocab_size) // 2)
|
||||
rows = []
|
||||
for layer_id in layer_ids:
|
||||
rng = np.random.default_rng(10007 * layer_id)
|
||||
values = rng.integers(low=0, high=bound, size=(max_ngram_size,), dtype=np.int64)
|
||||
rows.append([int(v) * 2 + 1 for v in values])
|
||||
return rows
|
||||
|
||||
|
||||
def engram_compressed_token_map(tokenizer_dir: Path) -> tuple[list[int], int]:
|
||||
"""build_compressed_token_map: token id -> compressed id (tokens that normalize alike collapse)."""
|
||||
from tokenizers import Regex, normalizers
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(tokenizer_dir)
|
||||
sentinel = "\ue000"
|
||||
normalizer = normalizers.Sequence([
|
||||
normalizers.NFKC(),
|
||||
normalizers.NFD(),
|
||||
normalizers.StripAccents(),
|
||||
normalizers.Lowercase(),
|
||||
normalizers.Replace(Regex(r"[ \t\r\n]+"), " "),
|
||||
normalizers.Replace(Regex(r"^ $"), sentinel),
|
||||
normalizers.Strip(),
|
||||
normalizers.Replace(sentinel, " "),
|
||||
])
|
||||
backend = tokenizer.backend_tokenizer
|
||||
key_to_new: dict[str, int] = {}
|
||||
lookup = [0] * len(tokenizer)
|
||||
for token_id in range(len(tokenizer)):
|
||||
text = backend.decode([token_id], skip_special_tokens=False)
|
||||
if "\ufffd" in text:
|
||||
key = backend.id_to_token(token_id)
|
||||
else:
|
||||
normalized = normalizer.normalize_str(text)
|
||||
key = normalized if normalized else text
|
||||
lookup[token_id] = key_to_new.setdefault(key, len(key_to_new))
|
||||
return lookup, len(key_to_new)
|
||||
|
||||
|
||||
class _StreamedBytes:
|
||||
"""Raw tensor bytes produced chunk by chunk at write time (an Engram table is ~200 GB in BF16)."""
|
||||
def __init__(self, shape: tuple[int, ...], chunks: Callable[[], Iterable[np.ndarray]]):
|
||||
self.shape = shape
|
||||
self.dtype = np.dtype(np.uint8)
|
||||
self.nbytes = int(np.prod(shape))
|
||||
self._chunks = chunks
|
||||
|
||||
def tofile(self, fout) -> None:
|
||||
written = 0
|
||||
for chunk in self._chunks():
|
||||
chunk = np.ascontiguousarray(chunk, dtype=np.uint8)
|
||||
chunk.tofile(fout)
|
||||
written += chunk.nbytes
|
||||
if written != self.nbytes:
|
||||
raise ValueError(f"streamed {written} bytes, expected {self.nbytes}")
|
||||
|
||||
def byteswap(self, inplace: bool = False):
|
||||
raise NotImplementedError("streamed tensors are written little-endian only")
|
||||
|
||||
|
||||
def _e8m0_check(scale_u8: np.ndarray, name: str) -> None:
|
||||
# e4m3 (|x| in [2^-9, 448]) * 2^(e - 127) is exact in BF16 for e in [3, 246]
|
||||
if scale_u8.size and (int(scale_u8.min()) < 3 or int(scale_u8.max()) > 246):
|
||||
raise ValueError(f"{name}: E8M0 scale exponents outside [3, 246], the dequantized values would not be exact")
|
||||
|
||||
|
||||
@ModelBase.register("DeepseekV41ForCausalLM")
|
||||
class DeepseekV41Model(DeepseekV4Model):
|
||||
"""DeepSeek-V4.1 text model, lossless: FP8 (e4m3, 32x32 ue8m0 blocks) linears dequantized to BF16 (F32 with
|
||||
--outtype f32), routed experts copied bit-exact as MXFP4, Engram tables (e4m3 rows, one e8m0 scale per 32) as the
|
||||
exact BF16 lookup (F32 with --outtype f32), router and Engram gate F32. DSpark (mtp.*) is not converted here
|
||||
(--dspark, DeepseekV41DSparkModel); the vision tower goes to an mmproj (--mmproj, DeepseekV41VisionModel).
|
||||
The Engram tables of all layers are written as one per_layer_token_embd (rows concatenated in layer order) with
|
||||
the PLE keys of qwen4exp: absolute head offsets and bucket counts, so the runtime can read it lazily."""
|
||||
model_arch = gguf.MODEL_ARCH.DEEPSEEK41
|
||||
supports_mtp_export = False
|
||||
|
||||
_ENGRAM_EMBED_RE = re.compile(r"layers\.(\d+)\.engram\.embed\.(weight|scale)$")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.hparams.setdefault("num_hash_layers", 0) # no hash-routed layers in V4.1
|
||||
self._f32 = self.ftype == gguf.LlamaFileType.ALL_F32
|
||||
qc = self.hparams.get("quantization_config") or {}
|
||||
self._fp8_block = tuple(qc.get("weight_block_size") or (32, 32))
|
||||
if qc.get("scale_fmt", "ue8m0") != "ue8m0":
|
||||
raise ValueError(f"unsupported FP8 scale format {qc.get('scale_fmt')!r}")
|
||||
if self.hparams.get("gate_temp", 1.0) != 1.0:
|
||||
raise ValueError("gate_temp != 1 is not supported (the router logits would need dividing by it)")
|
||||
|
||||
# V4.1 does not use the DeepSeek-V4 template; a template shipped with the model is still added by set_vocab
|
||||
self.gguf_writer.remove_key(gguf.Keys.Tokenizer.CHAT_TEMPLATE)
|
||||
|
||||
def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]:
|
||||
# num_hidden_layers lives in text_config, which is merged only after indexing; MTP stages are skipped anyway
|
||||
return TextModel.index_tensors(self, remote_hf_model_id=remote_hf_model_id)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
if item[0].startswith("vision."):
|
||||
return None
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
hp = self.hparams
|
||||
w = self.gguf_writer
|
||||
w.add_attention_kv_source_layers(hp["kv_source_layer_ids"])
|
||||
w.add_indexer_source_layers(hp["index_source_layer_ids"])
|
||||
if hp.get("candidate_source_layer_id", -1) >= 0:
|
||||
w.add_indexer_candidate_source_layer(hp["candidate_source_layer_id"])
|
||||
w.add_indexer_candidate_top_k_blocks(hp["candidate_topk_blocks"])
|
||||
w.add_indexer_candidate_block_size(hp["candidate_block_size"])
|
||||
|
||||
# GGUF cannot hold empty arrays: the Engram keys are omitted when the model has no Engram layers
|
||||
layer_ids = list(hp.get("engram_layer_ids") or [])
|
||||
if layer_ids:
|
||||
max_ngram, n_heads = hp["engram_max_ngram_size"], hp["engram_n_heads"]
|
||||
primes = engram_primes(len(layer_ids), max_ngram, n_heads, hp["engram_vocab_size"])
|
||||
for li, layer_primes in enumerate(primes):
|
||||
if sum(p for per in layer_primes for p in per) != hp["engram_num_embeddings"][li]:
|
||||
raise ValueError(f"engram layer {layer_ids[li]}: prime sum != num_embeddings")
|
||||
token_map, cvocab = engram_compressed_token_map(self.dir_model)
|
||||
if cvocab != hp["engram_compressed_vocab_size"] or len(token_map) != hp["vocab_size"]:
|
||||
raise ValueError(f"compressed token map ({len(token_map)} -> {cvocab}) does not match the config")
|
||||
# absolute row offsets into the concatenated table
|
||||
sizes = [p for layer in primes for per in layer for p in per]
|
||||
offsets = [sum(sizes[:i]) for i in range(len(sizes))]
|
||||
w.add_key_value(gguf.Keys.PerLayerEmbedding.LAYERS.format(arch=w.arch), layer_ids,
|
||||
gguf.GGUFValueType.ARRAY, gguf.GGUFValueType.UINT32)
|
||||
w.add_ple_ngram_size(max_ngram)
|
||||
w.add_ple_heads_per_ngram(n_heads)
|
||||
w.add_embedding_length_per_layer_input(hp["engram_head_dim"])
|
||||
w.add_ple_layer_multipliers([m for row in engram_multipliers(layer_ids, max_ngram, cvocab) for m in row])
|
||||
w.add_ple_head_offsets(offsets)
|
||||
w.add_ple_head_vocab_sizes(sizes)
|
||||
w.add_engram_pad_token_id(hp["engram_pad_token_id"])
|
||||
w.add_engram_token_map(token_map)
|
||||
|
||||
def dequant_model(self):
|
||||
bo, bi = self._fp8_block
|
||||
|
||||
def dequant(weight: Tensor, scale: Tensor) -> Tensor:
|
||||
out_f, in_f = weight.shape
|
||||
s = self._e8m0_to_float(scale).repeat_interleave(bo, 0)[:out_f].repeat_interleave(bi, 1)[:, :in_f]
|
||||
return weight.float() * s
|
||||
|
||||
for name in [n for n in self.model_tensors if n.endswith(".scale") and not self._ENGRAM_EMBED_RE.match(n)]:
|
||||
weight_name = name.removesuffix(".scale") + ".weight"
|
||||
if weight_name not in self.model_tensors or self.model_tensors[weight_name]().dtype not in self._float8_dtypes():
|
||||
continue
|
||||
w, s = self.model_tensors[weight_name], self.model_tensors.pop(name)
|
||||
out_f, in_f = w().shape
|
||||
if tuple(s().shape) != (-(-out_f // bo), -(-in_f // bi)):
|
||||
raise ValueError(f"{weight_name}: scale shape {tuple(s().shape)} does not match {(bo, bi)} blocks")
|
||||
_e8m0_check(LazyTorchTensor.to_eager(s()).view(torch.uint8).numpy(), name)
|
||||
self.model_tensors[weight_name] = lambda w=w, s=s: dequant(w(), s())
|
||||
self._dsv4_fp8_dequantized.add(weight_name)
|
||||
|
||||
def _tensor_part(self, name: str) -> Path:
|
||||
index = self.dir_model / "model.safetensors.index.json"
|
||||
if not index.is_file():
|
||||
return self.dir_model / "model.safetensors"
|
||||
with open(index, "r", encoding="utf-8") as f:
|
||||
return self.dir_model / json.load(f)["weight_map"][name]
|
||||
|
||||
def _write_mxfp4_experts(self, bid: int, proj: str, key: gguf.MODEL_TENSOR, n_expert: int | None = None) -> None:
|
||||
n_expert = self.hparams["n_routed_experts"] if n_expert is None else n_expert
|
||||
names = [f"layers.{bid}.ffn.experts.{e}.{proj}" for e in range(n_expert)]
|
||||
pairs = [(self.model_tensors.pop(n + ".weight"), self.model_tensors.pop(n + ".scale")) for n in names]
|
||||
rows, packed_cols = pairs[0][0]().shape
|
||||
|
||||
def chunks() -> Iterable[np.ndarray]:
|
||||
for w, s in pairs:
|
||||
yield self.repack_mxfp4_blocks(LazyTorchTensor.to_eager(w()), LazyTorchTensor.to_eager(s()))
|
||||
data = cast(np.ndarray, _StreamedBytes((len(pairs), rows, packed_cols * 2 // 32 * 17), chunks))
|
||||
self.gguf_writer.add_tensor(self.format_tensor_name(key, bid), data, raw_dtype=gguf.GGMLQuantizationType.MXFP4)
|
||||
|
||||
def _write_engram_tables(self, layer_ids: list[int]) -> None:
|
||||
# official lookup e4m3 * 2^(e8m0 - 127), exact in BF16
|
||||
parts, dim = [], None
|
||||
for bid in layer_ids:
|
||||
wn, sn = f"layers.{bid}.engram.embed.weight", f"layers.{bid}.engram.embed.scale"
|
||||
rows, d = self.model_tensors.pop(wn)().shape
|
||||
if d % 32 != 0 or tuple(self.model_tensors.pop(sn)().shape) != (rows, d // 32) or d != (dim or d):
|
||||
raise ValueError(f"{wn}: expected one e8m0 scale per 32 elements and the same width in every layer")
|
||||
if rows != self.hparams["engram_num_embeddings"][len(parts)]:
|
||||
raise ValueError(f"{wn}: {rows} rows, config says {self.hparams['engram_num_embeddings'][len(parts)]}")
|
||||
dim = d
|
||||
parts.append((wn, sn, rows, self._tensor_part(wn), self._tensor_part(sn)))
|
||||
assert dim is not None
|
||||
name = self.format_tensor_name(gguf.MODEL_TENSOR.PER_LAYER_TOKEN_EMBD)
|
||||
n_rows = sum(p[2] for p in parts)
|
||||
|
||||
def chunks() -> Iterable[np.ndarray]:
|
||||
from safetensors import safe_open
|
||||
for wn, sn, rows, wpart, spart in parts:
|
||||
with safe_open(wpart, framework="pt") as fw, safe_open(spart, framework="pt") as fs:
|
||||
ws, ss = fw.get_slice(wn), fs.get_slice(sn)
|
||||
for r0 in range(0, rows, 1 << 18):
|
||||
q = ws[r0:r0 + (1 << 18)].view(torch.uint8).reshape(-1, 32)
|
||||
e = ss[r0:r0 + (1 << 18)].view(torch.uint8).reshape(-1, 1)
|
||||
_e8m0_check(e.numpy(), sn)
|
||||
v = (q.view(torch.float8_e4m3fn).float() * torch.exp2(e.float() - 127)).reshape(-1, dim)
|
||||
yield (v if self._f32 else v.bfloat16()).view(torch.uint8).numpy()
|
||||
qt = gguf.GGMLQuantizationType.F32 if self._f32 else gguf.GGMLQuantizationType.BF16
|
||||
data = cast(np.ndarray, _StreamedBytes((n_rows, dim * (4 if self._f32 else 2)), chunks))
|
||||
self.gguf_writer.add_tensor(name, data, raw_dtype=qt)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
if not self._dsv4_mxfp4_generated:
|
||||
for bid in range(self.block_count):
|
||||
self._write_mxfp4_experts(bid, "w1", gguf.MODEL_TENSOR.FFN_GATE_EXP)
|
||||
self._write_mxfp4_experts(bid, "w2", gguf.MODEL_TENSOR.FFN_DOWN_EXP)
|
||||
self._write_mxfp4_experts(bid, "w3", gguf.MODEL_TENSOR.FFN_UP_EXP)
|
||||
if self.hparams.get("engram_layer_ids"):
|
||||
self._write_engram_tables(list(self.hparams["engram_layer_ids"]))
|
||||
self._dsv4_mxfp4_generated = True
|
||||
return ()
|
||||
|
||||
_LAYER_MAP: dict[str, tuple[gguf.MODEL_TENSOR, str]] = {
|
||||
"attn.indexer.wk.weight": (gguf.MODEL_TENSOR.INDEXER_ATTN_K, ".weight"),
|
||||
"attn.indexer.k_norm.weight": (gguf.MODEL_TENSOR.INDEXER_K_NORM, ".weight"),
|
||||
"engram.wkv.weight": (gguf.MODEL_TENSOR.ENGRAM_KV, ".weight"),
|
||||
}
|
||||
|
||||
def _map_dsv4_tensor_name(self, name: str, bid: int | None) -> tuple[gguf.MODEL_TENSOR, str]:
|
||||
m = re.match(r"layers\.\d+\.(.+)$", name)
|
||||
if m is not None and m.group(1) in self._LAYER_MAP:
|
||||
return self._LAYER_MAP[m.group(1)]
|
||||
return super()._map_dsv4_tensor_name(name, bid)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.endswith(".engram.k_weight"):
|
||||
return [] # folded into engram_gate
|
||||
if name.endswith(".engram.q_weight"):
|
||||
# Engram gate weight = q_weight * k_weight (bf16 * bf16 is exact in f32)
|
||||
k = self.model_tensors[name.removesuffix("q_weight") + "k_weight"]
|
||||
return [(self.format_tensor_name(gguf.MODEL_TENSOR.ENGRAM_GATE, bid), data_torch.float() * k().float())]
|
||||
return super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool:
|
||||
if new_name.endswith((".engram_gate.weight", ".ffn_gate_inp.weight")) or name in self._dsv4_f32_tensors:
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
if n_dims >= 2 and (name in self._dsv4_fp8_dequantized or name in self._dsv4_bf16_tensors):
|
||||
return gguf.GGMLQuantizationType.F32 if self._f32 else gguf.GGMLQuantizationType.BF16
|
||||
return False
|
||||
|
||||
|
||||
class DeepseekV41DSparkModel(DeepseekV41Model):
|
||||
"""DeepSeek-V4.1 DSpark speculative draft (mtp.0..2 of the checkpoint) as a DFLASH GGUF (`--dspark`).
|
||||
|
||||
Layout = the V4 DSpark draft (conversion/deepseek.py DeepseekV4DSparkModel) with V4.1's differences flagged by
|
||||
`dflash.hyper_connection.single_pass`: stages blk.0..2 (MXFP4 experts bit-exact, dense BF16), mtp.0.main_proj /
|
||||
main_norm -> fc / enc.output_norm, mtp.2.norm -> output_norm, markov_head.embed / head -> markov_w1 / markov_w2,
|
||||
confidence_head.proj -> conf_proj (F32), no hc_head. Token embeddings and the lm head are the target's (shared at
|
||||
runtime through ctx_other). target_layers are the V4.1 ids as-is: the draft reads the attention input of those
|
||||
layers, which is what llama_set_embeddings_layer_inp extracts.
|
||||
"""
|
||||
model_arch = gguf.MODEL_ARCH.DFLASH
|
||||
mtp_only = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
hp = self.hparams
|
||||
self._n_main = hp["num_hidden_layers"]
|
||||
self._n_stage = hp["num_nextn_predict_layers"]
|
||||
if self._n_stage <= 0 or "dspark_block_size" not in hp:
|
||||
raise ValueError("DeepSeek-V4.1 DSpark export needs num_nextn_predict_layers > 0 and the dspark_* config")
|
||||
# stages keep main-model block ids (mtp.M -> layers.n_main+M); format_tensor_name() maps them back
|
||||
self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self._n_stage)
|
||||
|
||||
def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]:
|
||||
hp = {**self.hparams, **self.hparams.get("text_config", {})} # text_config is merged only after indexing
|
||||
type(self)._dsv4_main_layers = hp["num_hidden_layers"]
|
||||
type(self)._dsv4_nextn_layers = hp.get("num_nextn_predict_layers", 0)
|
||||
return TextModel.index_tensors(self, remote_hf_model_id=remote_hf_model_id)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
m = re.match(r"mtp\.(\d+)\.(.+)$", name)
|
||||
if m is None:
|
||||
return None # shared with the target at runtime
|
||||
stage, rest = int(m.group(1)), m.group(2)
|
||||
assert cls._dsv4_main_layers is not None
|
||||
if stage >= cls._dsv4_nextn_layers:
|
||||
raise ValueError(f"Unexpected DSpark stage {stage}")
|
||||
if rest.startswith(("markov_head.", "confidence_head.")):
|
||||
return f"dspark.{rest}", gen
|
||||
return f"layers.{cls._dsv4_main_layers + stage}.{rest}", gen
|
||||
|
||||
def format_tensor_name(self, key: gguf.MODEL_TENSOR, bid: int | None = None, suffix: str = ".weight") -> str:
|
||||
if bid is not None and bid >= self._n_main:
|
||||
bid -= self._n_main
|
||||
return super().format_tensor_name(key, bid, suffix)
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_mask_token_id(self.hparams["dspark_noise_token_id"])
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
hp = self.hparams
|
||||
n = self._n_stage
|
||||
w = self.gguf_writer
|
||||
w.add_block_count(n)
|
||||
w.add_context_length(hp["max_position_embeddings"])
|
||||
w.add_embedding_length(hp["hidden_size"])
|
||||
w.add_feed_forward_length(hp["moe_intermediate_size"])
|
||||
w.add_head_count(hp["num_attention_heads"])
|
||||
w.add_head_count_kv(1)
|
||||
w.add_key_length(hp["head_dim"])
|
||||
w.add_value_length(hp["head_dim"])
|
||||
w.add_rope_dimension_count(hp["qk_rope_head_dim"])
|
||||
# compress-ratio-0 layers: plain rope at rope_theta, no YaRN
|
||||
w.add_rope_freq_base(float(hp["rope_theta"]))
|
||||
w.add_layer_norm_rms_eps(hp["rms_norm_eps"])
|
||||
w.add_q_lora_rank(hp["q_lora_rank"])
|
||||
w.add_sliding_window(hp["sliding_window"])
|
||||
w.add_expert_count(hp["dspark_n_routed_experts"])
|
||||
w.add_expert_used_count(hp["dspark_num_experts_per_tok"])
|
||||
w.add_expert_feed_forward_length(hp["moe_intermediate_size"])
|
||||
w.add_expert_shared_count(hp["n_shared_experts"])
|
||||
w.add_expert_weights_scale(hp["routed_scaling_factor"])
|
||||
w.add_expert_weights_norm(hp["norm_topk_prob"])
|
||||
w.add_expert_gating_func(gguf.ExpertGatingFuncType.SQRTSOFTPLUS)
|
||||
w.add_swiglu_clamp_exp([hp["swiglu_limit"]] * n)
|
||||
w.add_swiglu_clamp_shexp([hp["swiglu_limit"]] * n)
|
||||
w.add_attention_output_group_count(hp["o_groups"])
|
||||
w.add_attention_output_lora_rank(hp["o_lora_rank"])
|
||||
w.add_attention_compress_ratios([0] * n)
|
||||
w.add_hyper_connection_count(hp["hc_mult"])
|
||||
w.add_hyper_connection_sinkhorn_iterations(hp["hc_sinkhorn_iters"])
|
||||
w.add_hyper_connection_epsilon(hp["hc_eps"])
|
||||
w.add_hyper_connection_single_pass(True)
|
||||
w.add_block_size(hp["dspark_block_size"])
|
||||
w.add_target_layers(hp["dspark_target_layer_ids"])
|
||||
w.add_sample_from_anchor(True)
|
||||
w.add_has_confidence_head(True)
|
||||
w.add_file_type(self.ftype)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
# no Engram tables in the draft
|
||||
if not self._dsv4_mxfp4_generated:
|
||||
n_expert = self.hparams["dspark_n_routed_experts"]
|
||||
for bid in range(self._n_main, self._n_main + self._n_stage):
|
||||
self._write_mxfp4_experts(bid, "w1", gguf.MODEL_TENSOR.FFN_GATE_EXP, n_expert)
|
||||
self._write_mxfp4_experts(bid, "w2", gguf.MODEL_TENSOR.FFN_DOWN_EXP, n_expert)
|
||||
self._write_mxfp4_experts(bid, "w3", gguf.MODEL_TENSOR.FFN_UP_EXP, n_expert)
|
||||
self._dsv4_mxfp4_generated = True
|
||||
return ()
|
||||
|
||||
_DSPARK_ROOT: dict[str, gguf.MODEL_TENSOR] = {
|
||||
"main_proj.weight": gguf.MODEL_TENSOR.FC,
|
||||
"main_norm.weight": gguf.MODEL_TENSOR.ENC_OUTPUT_NORM,
|
||||
"norm.weight": gguf.MODEL_TENSOR.OUTPUT_NORM,
|
||||
"dspark.markov_head.embed.weight": gguf.MODEL_TENSOR.DSPARK_MARKOV_W1,
|
||||
"dspark.markov_head.head.weight": gguf.MODEL_TENSOR.DSPARK_MARKOV_W2,
|
||||
"dspark.confidence_head.proj.weight": gguf.MODEL_TENSOR.DSPARK_CONF_PROJ,
|
||||
}
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.endswith(".ffn.gate.bias_vl"):
|
||||
return []
|
||||
if name in self._DSPARK_ROOT:
|
||||
return [(self.format_tensor_name(self._DSPARK_ROOT[name], None), data_torch)]
|
||||
m = re.match(r"layers\.\d+\.(main_proj\.weight|main_norm\.weight|norm\.weight)$", name)
|
||||
if m is not None:
|
||||
return [(self.format_tensor_name(self._DSPARK_ROOT[m.group(1)], None), data_torch)]
|
||||
return super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool:
|
||||
if new_name == "conf_proj.weight":
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
|
||||
@ModelBase.register("DeepseekV41ForCausalLM")
|
||||
@ModelBase.example("deepseek-ai/DeepSeek-V4.1-Flash-1017")
|
||||
class DeepseekV41VisionModel(DeepseekV4FlashVisionModel):
|
||||
"""DeepSeek-V4.1 vision tower (projector deepseek41v): the V4-Flash-Vision ViT + 3x3 aligner, with the V4.1 token
|
||||
block ([START] ([IMAGE]*w [NEWLINE])*h [END], no pads) and resize plan. ref: inference/vision.py,
|
||||
inference/image_processor.py of deepseek-ai/DeepSeek-V4.1-Flash-1017. The checkpoint config is HF-style
|
||||
(vision_config); image_start / image_end / image_newline are written like V4's sentinels, there is no image_pad."""
|
||||
|
||||
def get_vision_config(self) -> dict[str, Any] | None:
|
||||
vc = self.global_config.get("vision_config")
|
||||
if not vc or vc.get("num_hidden_layers", 0) == 0:
|
||||
raise ValueError("DeepseekV41VisionModel requires vision_config.num_hidden_layers > 0")
|
||||
return {
|
||||
"num_hidden_layers": vc["num_hidden_layers"],
|
||||
"hidden_size": vc["hidden_size"],
|
||||
"num_attention_heads": vc["num_attention_heads"],
|
||||
"intermediate_size": vc["intermediate_size"],
|
||||
"patch_size": vc["patch_size"],
|
||||
# dynamic resolution; only used for compat / warmup
|
||||
"image_size": vc["patch_size"] * vc["downsample_ratio"] * 16,
|
||||
"rope_theta": vc.get("rope_theta", 10000.0),
|
||||
"downsample_ratio": vc["downsample_ratio"],
|
||||
"min_pixels": vc["min_pixels"],
|
||||
"max_image_tokens": vc["max_image_tokens"],
|
||||
"max_wh_ratio": vc.get("max_wh_ratio"),
|
||||
}
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
assert self.hparams_vision is not None
|
||||
super(DeepseekV4FlashVisionModel, self).set_gguf_parameters()
|
||||
self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.DEEPSEEK41V)
|
||||
# pytorch default eps (vision.py), not the LLM rms_norm_eps (1e-20)
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(1e-6)
|
||||
self.gguf_writer.add_vision_use_silu(True)
|
||||
self.gguf_writer.add_vision_projector_scale_factor(self.hparams_vision["downsample_ratio"])
|
||||
self.gguf_writer.add_vision_min_pixels(self.hparams_vision["min_pixels"])
|
||||
# hardcoded in clip.cpp (PROJECTOR_TYPE_DEEPSEEK41V); add GGUF keys if a later model differs
|
||||
assert self.hparams_vision["max_image_tokens"] == 1024, self.hparams_vision["max_image_tokens"]
|
||||
assert self.hparams_vision["max_wh_ratio"] is None, self.hparams_vision["max_wh_ratio"]
|
||||
assert self.hparams_vision["rope_theta"] == 10000, self.hparams_vision["rope_theta"]
|
||||
+54
-15
@@ -41,6 +41,8 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
self._ple_shards: dict[int, str] = {}
|
||||
self._ple_row_dim: int | None = None
|
||||
self._mtp_fc: dict[str, Tensor] = {}
|
||||
# halves of the merged weights, keyed by (bid, merged tensor), see _merge_shared_input
|
||||
self._merge_parts: dict[tuple[int, gguf.MODEL_TENSOR], dict[int, Tensor]] = {}
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item):
|
||||
@@ -131,44 +133,79 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
# int64 hash constants must stay exact; 1-D tensors force F32, so use KV
|
||||
if name.endswith("ple_embedding.layer_multipliers"):
|
||||
self._ple_multipliers = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
if name.endswith("ple_embedding.ngram_heads_offsets"):
|
||||
self._ple_head_offsets = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
if name.endswith("ple_embedding.ngram_heads_vocab_sizes"):
|
||||
self._ple_head_vocab_sizes = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
|
||||
if ".ngram_embedding.shard_" in name:
|
||||
return self._place_ple_shard(data_torch, name)
|
||||
yield from self._place_ple_shard(data_torch, name)
|
||||
return
|
||||
|
||||
# eh_proj([e ; h_s]) = fc_embedding(e) + fc_hidden(h_s) for every hc stream s
|
||||
if name.endswith((".nextn_fc_embedding.weight", ".nextn_fc_hidden.weight")):
|
||||
self._mtp_fc[name.rsplit(".", 2)[1]] = data_torch
|
||||
if len(self._mtp_fc) < 2:
|
||||
return []
|
||||
return
|
||||
eh = torch.cat([self._mtp_fc.pop("nextn_fc_embedding"), self._mtp_fc.pop("nextn_fc_hidden")], dim=1)
|
||||
return [(self.format_tensor_name(gguf.MODEL_TENSOR.NEXTN_EH_PROJ, bid, ".weight"), eh)]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.NEXTN_EH_PROJ, bid, ".weight"), eh)
|
||||
return
|
||||
|
||||
# one projection feeds indexer q and k; split it, as minimax-m3 does
|
||||
if ".indexer.index_qk_proj.weight" in name:
|
||||
n_q = self.hparams["indexer_n_heads"] * self.hparams["indexer_head_dim"]
|
||||
q = data_torch[:n_q]
|
||||
k = data_torch[n_q:]
|
||||
return [
|
||||
(self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_Q_PROJ, bid, ".weight"), q),
|
||||
(self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_K_PROJ, bid, ".weight"), k),
|
||||
]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_Q_PROJ, bid, ".weight"), data_torch[:n_q])
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_K_PROJ, bid, ".weight"), data_torch[n_q:])
|
||||
return
|
||||
|
||||
# Gemma zero-centred gammas the inherited norm.weight rule misses
|
||||
if name.endswith((".ple.norm_key.weight", ".ple.norm_query.weight", ".ple.norm_conv.weight",
|
||||
".indexer.q_layernorm.weight", ".indexer.k_layernorm.weight")):
|
||||
return [(self.map_tensor_name(name), data_torch + 1)]
|
||||
yield (self.map_tensor_name(name), data_torch + 1)
|
||||
return
|
||||
|
||||
if name.endswith(".ple.conv1d.weight"):
|
||||
return [(self.map_tensor_name(name), data_torch.squeeze())]
|
||||
yield (self.map_tensor_name(name), data_torch.squeeze())
|
||||
return
|
||||
|
||||
return super().modify_tensors(data_torch, name, bid)
|
||||
for new_name, data in super().modify_tensors(data_torch, name, bid):
|
||||
yield from self._merge_shared_input(new_name, data, bid)
|
||||
|
||||
# matmuls that read the same input, written as one weight [a | b] so the runtime does one matmul:
|
||||
# (merged tensor, (a, b), linear-attention layers only)
|
||||
_SHARED_INPUT_MERGES = (
|
||||
(gguf.MODEL_TENSOR.HC_ATTN_DOWN_INJECT, (gguf.MODEL_TENSOR.HC_ATTN_DOWN, gguf.MODEL_TENSOR.HC_ATTN_INJECT), False),
|
||||
(gguf.MODEL_TENSOR.HC_FFN_DOWN_INJECT, (gguf.MODEL_TENSOR.HC_FFN_DOWN, gguf.MODEL_TENSOR.HC_FFN_INJECT), False),
|
||||
(gguf.MODEL_TENSOR.ATTN_QKV_GATE, (gguf.MODEL_TENSOR.ATTN_QKV, gguf.MODEL_TENSOR.ATTN_GATE), True),
|
||||
(gguf.MODEL_TENSOR.SSM_BETA_ALPHA_CAT, (gguf.MODEL_TENSOR.SSM_BETA, gguf.MODEL_TENSOR.SSM_ALPHA), True),
|
||||
)
|
||||
|
||||
# caches one half of a merged weight until the other half arrives, then yields [a | b]
|
||||
def _merge_shared_input(self, new_name: str, data: Tensor, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# a pre-quantized checkpoint carries per-tensor scales, which one merged weight cannot hold
|
||||
if bid is None or self.hparams.get("quantization_config"):
|
||||
yield (new_name, data)
|
||||
return
|
||||
|
||||
layer_types = self.hparams.get("layer_types") or []
|
||||
is_linear = bid < len(layer_types) and layer_types[bid] == "linear_attention"
|
||||
|
||||
match = next(((merged, parts.index(part)) for merged, parts, linear_only in self._SHARED_INPUT_MERGES
|
||||
for part in parts
|
||||
if (is_linear or not linear_only) and self.match_model_tensor_name(new_name, part, bid)), None)
|
||||
if match is None:
|
||||
yield (new_name, data)
|
||||
return
|
||||
|
||||
merged, idx = match
|
||||
halves = self._merge_parts.setdefault((bid, merged), {})
|
||||
halves[idx] = data
|
||||
if len(halves) == 2:
|
||||
del self._merge_parts[(bid, merged)]
|
||||
yield (self.format_tensor_name(merged, bid), torch.cat([halves[0], halves[1]], dim=0))
|
||||
|
||||
# the shards concatenate into a tensor of well over 100 GB
|
||||
# use LazyChunkedTensor here, a single shard resident at a time
|
||||
@@ -214,6 +251,8 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
super().prepare_tensors()
|
||||
if self._mtp_fc:
|
||||
raise ValueError(f"MTP projection missing its other half: {sorted(self._mtp_fc)}")
|
||||
if self._merge_parts:
|
||||
raise ValueError(f"merged weights missing a half: {sorted((bid, t.name) for bid, t in self._merge_parts)}")
|
||||
n_parts = self.hparams.get("split_ngram_parts", 0)
|
||||
if self._ple_shards and len(self._ple_shards) != n_parts:
|
||||
raise ValueError(
|
||||
|
||||
@@ -267,11 +267,15 @@ def main() -> None:
|
||||
sys.exit(1)
|
||||
|
||||
if args.dspark:
|
||||
if is_mistral_format or model_architecture != "DeepseekV4ForCausalLM":
|
||||
logger.error("--dspark is only supported for DeepseekV4ForCausalLM")
|
||||
if is_mistral_format or model_architecture not in ("DeepseekV4ForCausalLM", "DeepseekV41ForCausalLM"):
|
||||
logger.error("--dspark is only supported for DeepseekV4ForCausalLM and DeepseekV41ForCausalLM")
|
||||
sys.exit(1)
|
||||
from conversion.deepseek import DeepseekV4DSparkModel
|
||||
model_class = DeepseekV4DSparkModel
|
||||
if model_architecture == "DeepseekV41ForCausalLM":
|
||||
from conversion.deepseek41 import DeepseekV41DSparkModel
|
||||
model_class = DeepseekV41DSparkModel
|
||||
else:
|
||||
from conversion.deepseek import DeepseekV4DSparkModel
|
||||
model_class = DeepseekV4DSparkModel
|
||||
|
||||
if args.mtp or args.no_mtp:
|
||||
if not model_class.supports_mtp_export:
|
||||
|
||||
@@ -78,7 +78,7 @@ static inline float op_expm1(float x) {
|
||||
}
|
||||
|
||||
static inline float op_softplus(float x) {
|
||||
return (x > 20.0f) ? x : logf(1.0f + expf(x));
|
||||
return (x > 20.0f) ? x : log1pf(expf(x));
|
||||
}
|
||||
|
||||
static inline float op_floor(float x) {
|
||||
|
||||
@@ -1973,6 +1973,61 @@ static bool ggml_cuda_match_shared_expert(const ggml_cgraph * graph, int routed_
|
||||
return true;
|
||||
}
|
||||
|
||||
// Buffer- and batch-size independent, so graph_optimize produces the same graph topology for every ubatch size (see #29986).
|
||||
static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu, const int device) {
|
||||
const ggml_tensor * x_up = up->src[0];
|
||||
const ggml_tensor * y = up->src[1];
|
||||
|
||||
if (up->op != GGML_OP_MUL_MAT || !ggml_cuda_mmq_fusion_supported(x_up->type) || y->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The fused kernel is only compiled for weights whose rows fill whole tiles (no fallback).
|
||||
if (ggml_cuda_mmq_needs_fallback(x_up->ne[1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ggml_cuda_should_fuse_mul_mat(up, gate, glu)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The fused write-back applies SWIGLU_OAI with the default alpha and limit of ggml_cuda_op_swiglu_oai_single.
|
||||
if (ggml_get_glu_op(glu) == GGML_GLU_OP_SWIGLU_OAI && (ggml_get_op_params_f32(glu, 2) != 1.702f || ggml_get_op_params_f32(glu, 3) != 7.0f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NVIDIA only: the fused write-back relies on the MMA accumulator layout.
|
||||
return turing_mma_available(ggml_cuda_info().devices[device].cc);
|
||||
}
|
||||
|
||||
static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu) {
|
||||
// A hinted matmul can be fused, but its unfused path may be faster (e.g. FWHT for GGML_HINT_SRC0_IS_HADAMARD).
|
||||
if (ggml_get_op_params_i32(up, 1) != GGML_HINT_NONE || ggml_get_op_params_i32(gate, 1) != GGML_HINT_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The padding of a compute-buffer weight that is a view cannot be cleared, see ggml_cuda_mul_mat.
|
||||
const auto bad_padding_clear = [](const ggml_tensor * x) {
|
||||
return ggml_backend_buffer_get_usage(x->buffer) == GGML_BACKEND_BUFFER_USAGE_COMPUTE &&
|
||||
ggml_nbytes(x) != ggml_backend_buffer_get_alloc_size(x->buffer, x) && x->view_src;
|
||||
};
|
||||
const int device = ggml_cuda_get_device();
|
||||
if (!ggml_cuda_mul_mat_q_fusion_matches(up, gate, glu, device) || bad_padding_clear(up->src[0]) || bad_padding_clear(gate->src[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The batch size checks are done here, not in ggml_cuda_mul_mat_q_fusion_matches.
|
||||
const ggml_tensor * x_up = up->src[0];
|
||||
const ggml_tensor * y = up->src[1];
|
||||
const int cc = ggml_cuda_info().devices[device].cc;
|
||||
if (ggml_cuda_should_use_mmvq(x_up->type, cc, y->ne[1]) || !ggml_cuda_should_use_mmq(x_up->type, cc, y->ne[1], 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The fused kernel needs one block per tile (no stream-k split), so only fuse if the tiling is efficient.
|
||||
return ggml_cuda_mmq_fusion_is_efficient(x_up->type, x_up->ne[1], y->ne[1], y->ne[2]*y->ne[3], device);
|
||||
}
|
||||
|
||||
static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
@@ -2881,9 +2936,10 @@ static bool ggml_cuda_should_fuse_rope_set_rows(const ggml_tensor * rope,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only norm/neox shaders have the fusion code
|
||||
// Only norm/neox/multi shaders have the fusion code
|
||||
const int mode = ((const int32_t *) rope->op_params)[2];
|
||||
if (mode != GGML_ROPE_TYPE_NORMAL && mode != GGML_ROPE_TYPE_NEOX) {
|
||||
if (mode != GGML_ROPE_TYPE_NORMAL && mode != GGML_ROPE_TYPE_NEOX &&
|
||||
mode != GGML_ROPE_TYPE_MROPE && mode != GGML_ROPE_TYPE_IMROPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4377,6 +4433,18 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
|
||||
fused_node_count = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_fuse_mul_mat_q(up, gate, glu)) {
|
||||
ggml_cuda_mm_fusion_args_host fusion_data{};
|
||||
fusion_data.gate = gate->src[0];
|
||||
fusion_data.glu_op = ggml_get_glu_op(glu);
|
||||
fusion_data.glu_limit = ggml_get_op_params_f32(glu, 3);
|
||||
|
||||
ggml_cuda_mul_mat_q(*cuda_ctx, src0, src1, ids, glu, &fusion_data);
|
||||
fused_mul_mat_vec = true;
|
||||
fused_node_count = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4973,6 +5041,19 @@ static void ggml_backend_cuda_graph_optimize(ggml_backend_t backend, ggml_cgraph
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cgraph->n_nodes; ++i) {
|
||||
// Keep the inputs of the fused gate/up/GLU MMQ kernel alive until the GLU so that they cannot alias its output (under op offload the weights are buffer copies).
|
||||
if (i + 2 < cgraph->n_nodes && cgraph->nodes[i + 2]->op == GGML_OP_GLU) {
|
||||
ggml_tensor * glu = cgraph->nodes[i + 2];
|
||||
ggml_tensor * gate = glu->src[0];
|
||||
ggml_tensor * up = glu->src[1];
|
||||
const bool ok = (gate == cgraph->nodes[i] && up == cgraph->nodes[i + 1]) || (gate == cgraph->nodes[i + 1] && up == cgraph->nodes[i]);
|
||||
if (ok && ggml_cuda_mul_mat_q_fusion_matches(up, gate, glu, cuda_ctx->device)) {
|
||||
add_alloc_deps(i, i + 2);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ggml_cuda_moe_weighted_reduction_match match;
|
||||
if (ggml_cuda_match_moe_weighted_reduction(cgraph, i, match)) {
|
||||
params->add_alloc_dep(params->user_data, const_cast<ggml_tensor *>(match.experts), match.dst);
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "mmq.cuh"
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q1_0(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -116,7 +118,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q2_0(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -205,7 +209,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q4_0(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -268,7 +274,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q4_1(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -331,7 +339,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q5_0(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -411,7 +421,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q5_1(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -488,8 +500,8 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q8_0(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q8_0(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -519,7 +531,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q8_0 * bxi = (const block_q8_0 *) x + kbx0 + i*stride + kbx;
|
||||
const block_q8_0 * bxi = ggml_cuda_mmq_x_row<block_q8_0, has_fusion>(x, x_gate, kbx0, i, stride) + kbx;
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
x_qs[i*sram_stride + 0 + txi] = get_int_b2(bxi[0].qs, kqsx);
|
||||
@@ -542,7 +554,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q8_0 * bxi = (const block_q8_0 *) x + kbx0 + i*stride + kbxd;
|
||||
const block_q8_0 * bxi = ggml_cuda_mmq_x_row<block_q8_0, has_fusion>(x, x_gate, kbx0, i, stride) + kbxd;
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
x_df[i*sram_stride + kbxd] = bxi->d;
|
||||
@@ -555,7 +567,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q2_K(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -616,7 +630,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q3_K(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -728,8 +744,8 @@ static __device__ __forceinline__ int unpack_scales_q45_K(const int * scales, co
|
||||
((scales[ksc/2] >> (2 * (ksc % 2))) & 0x30303030); // upper 2 bits
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q4_K(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q4_K(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -757,7 +773,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q4_K * bxi = (const block_q4_K *) x + kbx0 + i*stride;
|
||||
const block_q4_K * bxi = ggml_cuda_mmq_x_row<block_q4_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
const int qs0 = get_int_b4(bxi->qs, txi);
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
@@ -786,7 +802,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q4_K * bxi = (const block_q4_K *) x + kbx0 + i*stride;
|
||||
const block_q4_K * bxi = ggml_cuda_mmq_x_row<block_q4_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const int * scales = (const int *) bxi->scales;
|
||||
const int ksc = threadIdx.x % 2;
|
||||
@@ -814,7 +830,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q4_K * bxi = (const block_q4_K *) x + kbx0 + i*stride;
|
||||
const block_q4_K * bxi = ggml_cuda_mmq_x_row<block_q4_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
x_dm[i] = bxi->dm;
|
||||
}
|
||||
@@ -827,7 +843,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q4_K * bxi = (const block_q4_K *) x + kbx0 + i*stride + (threadIdx.x % (MMQ_TILE_NE_K/8)) / (QI4_K/8);
|
||||
const block_q4_K * bxi = ggml_cuda_mmq_x_row<block_q4_K, has_fusion>(x, x_gate, kbx0, i, stride) + (threadIdx.x % (MMQ_TILE_NE_K/8)) / (QI4_K/8);
|
||||
|
||||
const int * scales = (const int *) bxi->scales;
|
||||
|
||||
@@ -839,8 +855,8 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
#endif // defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q5_K(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q5_K(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -868,7 +884,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q5_K * bxi = (const block_q5_K *) x + kbx0 + i*stride;
|
||||
const block_q5_K * bxi = ggml_cuda_mmq_x_row<block_q5_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
const int ky = QR5_K*txi;
|
||||
|
||||
const int ql = get_int_b4(bxi->qs, txi);
|
||||
@@ -909,7 +925,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q5_K * bxi = (const block_q5_K *) x + kbx0 + i*stride;
|
||||
const block_q5_K * bxi = ggml_cuda_mmq_x_row<block_q5_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const int * scales = (const int *) bxi->scales;
|
||||
const int ksc = threadIdx.x % 2;
|
||||
@@ -937,7 +953,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q5_K * bxi = (const block_q5_K *) x + kbx0 + i*stride;
|
||||
const block_q5_K * bxi = ggml_cuda_mmq_x_row<block_q5_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
x_dm[i] = bxi->dm;
|
||||
}
|
||||
@@ -951,7 +967,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q5_K * bxi = (const block_q5_K *) x + kbx0 + i*stride;
|
||||
const block_q5_K * bxi = ggml_cuda_mmq_x_row<block_q5_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const int * scales = (const int *) bxi->scales;
|
||||
|
||||
@@ -963,8 +979,8 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
#endif // defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q6_K(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_q6_K(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -993,7 +1009,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q6_K * bxi = (const block_q6_K *) x + kbx0 + i*stride;
|
||||
const block_q6_K * bxi = ggml_cuda_mmq_x_row<block_q6_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const int ql = get_int_b2(bxi->ql, txi);
|
||||
const int ql0 = (ql >> 0) & 0x0F0F0F0F;
|
||||
@@ -1023,7 +1039,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q6_K * bxi = (const block_q6_K *) x + kbx0 + i*stride;
|
||||
const block_q6_K * bxi = ggml_cuda_mmq_x_row<block_q6_K, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
x_df[i*sram_stride] = bxi->d;
|
||||
@@ -1041,7 +1057,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_q6_K * bxi = (const block_q6_K *) x + kbx0 + i*stride + (threadIdx.x % (MMQ_TILE_NE_K/8)) / 4;
|
||||
const block_q6_K * bxi = ggml_cuda_mmq_x_row<block_q6_K, has_fusion>(x, x_gate, kbx0, i, stride) + (threadIdx.x % (MMQ_TILE_NE_K/8)) / 4;
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
x_sc[i*sram_stride + threadIdx.x%4] = get_int_b2(bxi->scales, threadIdx.x % (MMQ_TILE_NE_K/8));
|
||||
@@ -1054,7 +1070,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq1_s(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1116,7 +1134,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq2_xxs(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1180,7 +1200,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq2_xs(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1245,7 +1267,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq2_s(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1313,7 +1337,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq3_xxs(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1377,7 +1403,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq3_s(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1445,8 +1473,8 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq4_xs(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq4_xs(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1473,7 +1501,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_iq4_xs * bxi = (const block_iq4_xs *) x + kbx0 + i*stride;
|
||||
const block_iq4_xs * bxi = ggml_cuda_mmq_x_row<block_iq4_xs, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const int aux_q4 = get_int_b4(bxi->qs, kqsx);
|
||||
const int2 v = get_int_from_table_16(aux_q4, kvalues_iq4nl);
|
||||
@@ -1497,7 +1525,7 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
i = min(i, i_max);
|
||||
}
|
||||
|
||||
const block_iq4_xs * bxi = (const block_iq4_xs *) x + kbx0 + i*stride;
|
||||
const block_iq4_xs * bxi = ggml_cuda_mmq_x_row<block_iq4_xs, has_fusion>(x, x_gate, kbx0, i, stride);
|
||||
|
||||
const float d = __half2float(bxi->d);
|
||||
|
||||
@@ -1513,7 +1541,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_iq4_nl(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1582,7 +1612,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_mxfp4(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q8) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q8);
|
||||
@@ -1649,7 +1681,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_mxfp4_fp4(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q4) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q4);
|
||||
@@ -1691,7 +1725,9 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kb0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kb0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
|
||||
@@ -1747,7 +1783,9 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __de
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4_nvfp4(
|
||||
const char * __restrict__ x, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
|
||||
GGML_UNUSED(x_gate);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q4) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q4);
|
||||
|
||||
+81
-31
@@ -131,12 +131,51 @@ static ggml_prec ggml_cuda_mmq_get_prec_src1(const ggml_tensor * src0, const ggm
|
||||
return GGML_PREC_Q4;
|
||||
}
|
||||
|
||||
// Tile width in ne11 (dense) / ne12 (MoE) direction with the fewest tiles that fits into shared memory, 0 if there is none.
|
||||
static int ggml_cuda_mmq_get_J_best(
|
||||
const ggml_type type, const int64_t ncols, const bool fallback, const int cc, const size_t smpbo, const ggml_prec prec_src1,
|
||||
int * nthreads_best = nullptr) {
|
||||
int J_best = 0;
|
||||
int ntiles_J_best = INT_MAX;
|
||||
|
||||
for (int J = 8; J <= 128 && ntiles_J_best > 1; J += 8) {
|
||||
const ggml_cuda_mmq_config config = ggml_cuda_mmq_get_config(type, J, fallback, cc, prec_src1);
|
||||
if (config.type == GGML_TYPE_COUNT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mmq_get_nbytes_shared(config, cc) > smpbo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int ntiles_x = (ncols + config.J - 1) / config.J;
|
||||
|
||||
if (ntiles_x < ntiles_J_best) {
|
||||
J_best = J;
|
||||
if (nthreads_best) {
|
||||
*nthreads_best = config.nthreads;
|
||||
}
|
||||
ntiles_J_best = ntiles_x;
|
||||
}
|
||||
}
|
||||
return J_best;
|
||||
}
|
||||
|
||||
void ggml_cuda_mul_mat_q(
|
||||
ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst) {
|
||||
ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst,
|
||||
const ggml_cuda_mm_fusion_args_host * fusion) {
|
||||
GGML_ASSERT( src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( dst->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(!ids || ids->type == GGML_TYPE_I32); // Optional, used for batched GGML_MUL_MAT_ID.
|
||||
|
||||
// Fused gate/up/GLU: src0 is the up weight, dst the GLU output.
|
||||
const ggml_tensor * gate = fusion ? fusion->gate : nullptr;
|
||||
if (fusion) {
|
||||
// TODO MoE support
|
||||
GGML_ASSERT(gate && !ids && !fusion->x_bias && !fusion->gate_bias && !fusion->x_scale && !fusion->gate_scale);
|
||||
GGML_ASSERT(gate->type == src0->type && ggml_are_same_shape(gate, src0) && ggml_are_same_stride(gate, src0));
|
||||
}
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS;
|
||||
|
||||
cudaStream_t stream = ctx.stream();
|
||||
@@ -158,14 +197,16 @@ void ggml_cuda_mul_mat_q(
|
||||
const float * src1_d = (const float *) src1->data;
|
||||
float * dst_d = (float *) dst->data;
|
||||
|
||||
// If src0 is a temporary compute buffer, clear any potential padding.
|
||||
if (ggml_backend_buffer_get_usage(src0->buffer) == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
|
||||
const size_t size_data = ggml_nbytes(src0);
|
||||
const size_t size_alloc = ggml_backend_buffer_get_alloc_size(src0->buffer, src0);
|
||||
if (size_alloc > size_data) {
|
||||
GGML_ASSERT(ggml_is_contiguously_allocated(src0));
|
||||
GGML_ASSERT(!src0->view_src);
|
||||
CUDA_CHECK(cudaMemsetAsync((char *) src0->data + size_data, 0, size_alloc - size_data, stream));
|
||||
// If src0 (or the fused gate weight) is a temporary compute buffer, clear any potential padding.
|
||||
for (const ggml_tensor * t : { src0, gate }) {
|
||||
if (t && ggml_backend_buffer_get_usage(t->buffer) == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
|
||||
const size_t size_data = ggml_nbytes(t);
|
||||
const size_t size_alloc = ggml_backend_buffer_get_alloc_size(t->buffer, t);
|
||||
if (size_alloc > size_data) {
|
||||
GGML_ASSERT(ggml_is_contiguously_allocated(t));
|
||||
GGML_ASSERT(!t->view_src);
|
||||
CUDA_CHECK(cudaMemsetAsync((char *) t->data + size_data, 0, size_alloc - size_data, stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +221,9 @@ void ggml_cuda_mul_mat_q(
|
||||
|
||||
const bool fallback = ggml_cuda_mmq_needs_fallback(ne01);
|
||||
|
||||
const ggml_prec prec_src1 = ggml_cuda_mmq_get_prec_src1(src0, dst, cc);
|
||||
// With fusion dst is the GLU output, not a MUL_MAT; the fused types always quantize src1 to Q8_1.
|
||||
// TODO support for Q4 precision if both tensors allow it
|
||||
const ggml_prec prec_src1 = gate ? GGML_PREC_Q8 : ggml_cuda_mmq_get_prec_src1(src0, dst, cc);
|
||||
|
||||
const bool use_native_fp4 = prec_src1 == GGML_PREC_Q4;
|
||||
const size_t y_block_size = use_native_fp4 ? sizeof(block_fp4_mmq) : sizeof(block_q8_1_mmq);
|
||||
@@ -201,26 +244,7 @@ void ggml_cuda_mul_mat_q(
|
||||
}
|
||||
}
|
||||
|
||||
int ntiles_J_best = INT_MAX;
|
||||
|
||||
for (int J = 8; J <= 128 && ntiles_J_best > 1; J += 8) {
|
||||
const ggml_cuda_mmq_config config = ggml_cuda_mmq_get_config(src0->type, J, fallback, cc, prec_src1);
|
||||
if (config.type == GGML_TYPE_COUNT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mmq_get_nbytes_shared(config, cc) > smpbo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int ntiles_x = (ncols_opt + config.J - 1) / config.J;
|
||||
|
||||
if (ntiles_x < ntiles_J_best) {
|
||||
J_best = J;
|
||||
nthreads_best = config.nthreads;
|
||||
ntiles_J_best = ntiles_x;
|
||||
}
|
||||
}
|
||||
J_best = ggml_cuda_mmq_get_J_best(src0->type, ncols_opt, fallback, cc, smpbo, prec_src1, &nthreads_best);
|
||||
}
|
||||
GGML_ASSERT(J_best > 0);
|
||||
|
||||
@@ -262,13 +286,20 @@ void ggml_cuda_mul_mat_q(
|
||||
ne11 * ne10_padded * sizeof(block_q8_1) / (QK8_1 * sizeof(int));
|
||||
const int64_t s13 = ne12*s12;
|
||||
|
||||
ggml_cuda_mm_fusion_args_device fusion_device = {};
|
||||
if (gate) {
|
||||
fusion_device.gate = gate->data;
|
||||
fusion_device.glu_op = fusion->glu_op;
|
||||
fusion_device.glu_limit = fusion->glu_limit;
|
||||
}
|
||||
|
||||
const mmq_args args = {
|
||||
src0_d, src0->type, (const int *) src1_q8_1.ptr, nullptr, nullptr, dst_d,
|
||||
src0->type == GGML_TYPE_NVFP4 && use_native_fp4 ? src1_scale.ptr : nullptr,
|
||||
ne00, ne01, ne1, s01, ne11, s1,
|
||||
ne02, ne12, s02, s12, s2,
|
||||
ne03, ne13, s03, s13, s3,
|
||||
ne1, J_best};
|
||||
ne1, J_best, fusion_device};
|
||||
ggml_cuda_mul_mat_q_switch_type(ctx, args, stream, prec_src1);
|
||||
return;
|
||||
}
|
||||
@@ -485,3 +516,22 @@ bool ggml_cuda_should_use_mmq(enum ggml_type type, int cc, int64_t ne11, int64_t
|
||||
|
||||
return (!GGML_CUDA_CC_IS_CDNA(cc)) || ne11 < MMQ_DP4A_MAX_BATCH_SIZE;
|
||||
}
|
||||
|
||||
bool ggml_cuda_mmq_fusion_is_efficient(const ggml_type type, const int64_t nrows_x, const int64_t ncols, const int64_t nchannels, const int device) {
|
||||
const int cc = ggml_cuda_info().devices[device].cc;
|
||||
const int nsm = ggml_cuda_info().devices[device].nsm;
|
||||
const size_t smpbo = ggml_cuda_info().devices[device].smpbo;
|
||||
|
||||
// Same tile width as ggml_cuda_mul_mat_q; the fused kernel has no fallback, quantizes src1 to Q8_1 and writes I/2 output rows per tile.
|
||||
const bool fallback = false;
|
||||
const int J_best = ggml_cuda_mmq_get_J_best(type, ncols, fallback, cc, smpbo, GGML_PREC_Q8);
|
||||
if (J_best == 0) {
|
||||
return false;
|
||||
}
|
||||
const int64_t nrows_tile = ggml_cuda_mmq_get_config(type, J_best, fallback, cc, GGML_PREC_Q8).I / 2;
|
||||
const int64_t ntiles = (ncols + J_best - 1)/J_best * ((nrows_x + nrows_tile - 1)/nrows_tile) * nchannels;
|
||||
// Same condition as launch_mul_mat_q uses to run the stream-k kernel with one block per tile.
|
||||
const int64_t tiles_nwaves = (ntiles + nsm - 1) / nsm;
|
||||
const int64_t tiles_efficiency_percent = 100 * ntiles / (nsm*tiles_nwaves);
|
||||
return tiles_efficiency_percent >= 90;
|
||||
}
|
||||
|
||||
+177
-106
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.cuh"
|
||||
#include "unary.cuh"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
@@ -10,10 +11,12 @@
|
||||
#define MMQ_ITER_K_FP4 512
|
||||
#define MMQ_NWARPS 8
|
||||
|
||||
typedef void (*ggml_cuda_mmq_load_tiles_t)(const char * __restrict__ x, int * x_tile, const int kbx0, const int i_max, const int stride);
|
||||
typedef void (*ggml_cuda_mmq_load_tiles_t)(const char * __restrict__ x, const char * __restrict__ x_gate, int * x_tile,
|
||||
const int kbx0, const int i_max, const int stride);
|
||||
typedef void (*ggml_cuda_mmq_vec_dot_t)(const int * __restrict__ x, const int * __restrict__ y, float * __restrict__ sum, const int k00);
|
||||
typedef void (*ggml_cuda_mmq_write_back_t)(const float * __restrict__ sum, const int32_t * __restrict__ get_rows_to_sorted,
|
||||
float * __restrict__ dst, const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max);
|
||||
float * __restrict__ dst, const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
|
||||
const ggml_glu_op glu_op, const float glu_limit);
|
||||
|
||||
enum mmq_q8_1_ds_layout {
|
||||
MMQ_Q8_1_DS_LAYOUT_D4,
|
||||
@@ -426,12 +429,37 @@ static __host__ int ggml_cuda_mmq_get_nbytes_shared_x(const ggml_cuda_mmq_config
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Weight types with a fused gate/up/GLU path (see ggml_cuda_mmq_get_util_funcs).
|
||||
static constexpr __host__ __device__ bool ggml_cuda_mmq_fusion_supported(const ggml_type type) {
|
||||
return type == GGML_TYPE_Q4_K || type == GGML_TYPE_Q5_K || type == GGML_TYPE_Q6_K || type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_XS;
|
||||
}
|
||||
|
||||
// Fused gate/up/GLU: the weight tile alternates chunks of this many up and gate rows, so rows r and r + 8 of an MMA tile (same thread) pair up.
|
||||
static constexpr int GGML_CUDA_MMQ_FUSION_ROWS = 8;
|
||||
|
||||
// Source of weight tile row i; with has_fusion the up or gate row that the interleaved tile holds there.
|
||||
template <typename block_t, bool has_fusion>
|
||||
static __device__ __forceinline__ const block_t * ggml_cuda_mmq_x_row(
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, const int kbx0, const int i, const int stride) {
|
||||
if constexpr (has_fusion) {
|
||||
constexpr int R = GGML_CUDA_MMQ_FUSION_ROWS;
|
||||
const char * x_src = (i / R) % 2 == 0 ? x : x_gate;
|
||||
return (const block_t *) x_src + kbx0 + ((i / (2*R))*R + i % R)*stride;
|
||||
} else {
|
||||
GGML_UNUSED(x_gate);
|
||||
return (const block_t *) x + kbx0 + i*stride;
|
||||
}
|
||||
}
|
||||
|
||||
#include "mmq-load-tiles.cuh"
|
||||
#include "mmq-vec-dot.cuh"
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __device__ __forceinline__ void ggml_cuda_mmq_write_back_dp4a(
|
||||
const float * __restrict__ sum, const int32_t * __restrict__ ids_dst, float * __restrict__ dst,
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max) {
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
|
||||
const ggml_glu_op glu_op, const float glu_limit) {
|
||||
GGML_UNUSED_VARS(glu_op, glu_limit);
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
|
||||
@@ -469,16 +497,19 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __de
|
||||
}
|
||||
}
|
||||
|
||||
template<ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
// has_fusion: accumulators l and l + ne/2 hold the up and gate values of one output element, the GLU of the two is written.
|
||||
template<ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
const float * __restrict__ sum, const int * __restrict__ ids_dst, float * __restrict__ dst,
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max) {
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
|
||||
const ggml_glu_op glu_op, const float glu_limit) {
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
typedef tile<16, 16, int, DATA_LAYOUT_J_MAJOR> tile_C;
|
||||
#else
|
||||
typedef tile<16, 8, int> tile_C;
|
||||
#endif // defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
static_assert(!has_fusion || tile_C::I == 2*GGML_CUDA_MMQ_FUSION_ROWS, "each C tile must hold one chunk of up rows and one of gate rows");
|
||||
|
||||
constexpr int rows_per_warp = ggml_cuda_mmq_get_rows_per_warp(type, J, fallback, prec_src1);
|
||||
constexpr int ntx = rows_per_warp/tile_C::I; // Number of x minitiles per warp.
|
||||
@@ -492,20 +523,23 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
#pragma unroll
|
||||
for (int n = 0; n < ntx; ++n) {
|
||||
#pragma unroll
|
||||
for (int l = 0; l < tile_C::ne; ++l) {
|
||||
for (int l = 0; l < (has_fusion ? tile_C::ne/2 : tile_C::ne); ++l) {
|
||||
const int j = j0 + (threadIdx.y % ntx) * tile_C::J + tile_C::get_j(l);
|
||||
|
||||
if (j > j_max) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int i = i0 + n*tile_C::I + tile_C::get_i(l);
|
||||
const int i = (i0 + n*tile_C::I)/(has_fusion ? 2 : 1) + tile_C::get_i(l);
|
||||
|
||||
if (fallback && i > i_max) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
if constexpr (has_fusion) {
|
||||
const int sum_index = (j0/tile_C::J + n)*tile_C::ne + l;
|
||||
dst[ids_dst[j]*stride + i] = ggml_cuda_op_glu_single(glu_op, sum[sum_index + tile_C::ne/2], sum[sum_index], glu_limit);
|
||||
} else if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
if (y_scale_used) {
|
||||
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/tile_C::J + n)*tile_C::ne + l];
|
||||
} else {
|
||||
@@ -518,6 +552,8 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GGML_UNUSED_VARS(y_scale_used, glu_op, glu_limit);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -534,8 +570,9 @@ struct ggml_cuda_mmq_util_funcs {
|
||||
vdr(vdr), load_tiles(load_tiles), vec_dot(vec_dot), write_back(write_back) {}
|
||||
};
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_funcs() {
|
||||
static_assert(!has_fusion || ggml_cuda_mmq_fusion_supported(type), "no fused loader for this type");
|
||||
if (!ggml_cuda_mmq_get_config(type, J, fallback, prec_src1).use_mma_data_layout()) {
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
@@ -577,7 +614,7 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
case GGML_TYPE_Q8_0:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
VDR_Q8_0_Q8_1_MMQ,
|
||||
ggml_cuda_mmq_load_tiles_q8_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q8_0<type, J, fallback, false>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_dp4a<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -596,19 +633,19 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
case GGML_TYPE_Q4_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
VDR_Q4_K_Q8_1_MMQ,
|
||||
ggml_cuda_mmq_load_tiles_q4_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q4_K<type, J, fallback, false>,
|
||||
ggml_cuda_mmq_vec_dot_q4_K_q8_1_dp4a<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
|
||||
case GGML_TYPE_Q5_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
VDR_Q5_K_Q8_1_MMQ,
|
||||
ggml_cuda_mmq_load_tiles_q5_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q5_K<type, J, fallback, false>,
|
||||
ggml_cuda_mmq_vec_dot_q5_K_q8_1_dp4a<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
|
||||
case GGML_TYPE_Q6_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
VDR_Q6_K_Q8_1_MMQ,
|
||||
ggml_cuda_mmq_load_tiles_q6_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q6_K<type, J, fallback, false>,
|
||||
ggml_cuda_mmq_vec_dot_q6_K_q8_1_dp4a<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -651,7 +688,7 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
case GGML_TYPE_IQ4_XS:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
VDR_IQ4_XS_Q8_1_MMQ,
|
||||
ggml_cuda_mmq_load_tiles_iq4_xs<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_iq4_xs<type, J, fallback, false>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_dp4a<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
@@ -688,7 +725,7 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_mxfp4_fp4<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_fp4_fp4_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
}
|
||||
break;
|
||||
case GGML_TYPE_NVFP4:
|
||||
@@ -697,7 +734,7 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_nvfp4_nvfp4<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_fp4_fp4_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -713,136 +750,136 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q1_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q2_0:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q2_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q4_0:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q4_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_DS4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q4_1:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q4_1<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_1_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q5_0:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q5_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q5_1:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q5_1<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_1_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q8_0:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q8_0<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q8_0<type, J, fallback, has_fusion>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
case GGML_TYPE_Q2_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q2_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q2_K_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q3_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q3_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_mma<type, J, fallback, prec_src1>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_Q4_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q4_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q4_K<type, J, fallback, has_fusion>,
|
||||
ggml_cuda_mmq_vec_dot_q8_1_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
|
||||
case GGML_TYPE_Q5_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q5_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q5_K<type, J, fallback, has_fusion>,
|
||||
ggml_cuda_mmq_vec_dot_q8_1_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
|
||||
case GGML_TYPE_Q6_K:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_q6_K<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_q6_K<type, J, fallback, has_fusion>,
|
||||
ggml_cuda_mmq_vec_dot_q6_K_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
case GGML_TYPE_IQ1_S:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq1_s<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_1_q8_1_mma<type, J, fallback>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ2_XXS:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq2_xxs<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ2_XS:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq2_xs<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_mma<type, J, fallback, prec_src1>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ2_S:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq2_s<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_mma<type, J, fallback, prec_src1>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ3_XXS:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq3_xxs<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ3_S:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq3_s<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_IQ4_XS:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq4_xs<type, J, fallback>,
|
||||
ggml_cuda_mmq_load_tiles_iq4_xs<type, J, fallback, has_fusion>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_iq4_nl<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
case GGML_TYPE_MXFP4:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_mxfp4<type, J, fallback>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_q8_1_mma<type, J, fallback, MMQ_Q8_1_DS_LAYOUT_D4>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
case GGML_TYPE_NVFP4:
|
||||
return ggml_cuda_mmq_util_funcs(
|
||||
-1,
|
||||
ggml_cuda_mmq_load_tiles_nvfp4<type, J, fallback, prec_src1>,
|
||||
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_mma<type, J, fallback, prec_src1>,
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1>);
|
||||
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
|
||||
default:
|
||||
return ggml_cuda_mmq_util_funcs(1, nullptr, nullptr, nullptr);
|
||||
}
|
||||
@@ -850,41 +887,44 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
static constexpr __device__ int ggml_cuda_mmq_get_vdr() {
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1>().vdr;
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1, false>().vdr;
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
static constexpr __device__ ggml_cuda_mmq_load_tiles_t ggml_cuda_mmq_get_load_tiles() {
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1>().load_tiles;
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1, has_fusion>().load_tiles;
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
static constexpr __device__ ggml_cuda_mmq_vec_dot_t ggml_cuda_mmq_get_vec_dot() {
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1>().vec_dot;
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1, false>().vec_dot;
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
static constexpr __device__ ggml_cuda_mmq_write_back_t ggml_cuda_mmq_get_write_back() {
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1>().write_back;
|
||||
return ggml_cuda_mmq_get_util_funcs<type, J, fallback, prec_src1, has_fusion>().write_back;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
template <ggml_type type, int J, bool fallback, bool fixup, ggml_prec prec_src1>
|
||||
// has_fusion: x and x_gate are the up and gate weights.
|
||||
template <ggml_type type, int J, bool fallback, bool fixup, ggml_prec prec_src1, bool has_fusion>
|
||||
static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
const char * __restrict__ x, const int offset_x, const int * __restrict__ y,
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, const int offset_x, const int * __restrict__ y,
|
||||
const int * __restrict__ ids_dst, float * __restrict__ dst, float * __restrict__ tmp_fixup,
|
||||
const float * __restrict__ y_scale,
|
||||
const float * __restrict__ y_scale, const ggml_glu_op glu_op, const float glu_limit,
|
||||
const int stride_row_x, const int ncols_y, const int stride_col_dst,
|
||||
const int tile_x_max_i, const int tile_y_max_j, const int kb0_start, const int kb0_stop) {
|
||||
static_assert(!(fixup && has_fusion), "the fused gate/up path does not split K");
|
||||
static_assert(!(fallback && has_fusion), "the fused gate/up path needs nrows_x % 128 == 0");
|
||||
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / warp_size;
|
||||
constexpr int qk = ggml_cuda_type_traits<type>::qk;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
|
||||
constexpr ggml_cuda_mmq_load_tiles_t load_tiles = ggml_cuda_mmq_get_load_tiles<type, J, fallback, prec_src1>();
|
||||
constexpr ggml_cuda_mmq_load_tiles_t load_tiles = ggml_cuda_mmq_get_load_tiles<type, J, fallback, prec_src1, has_fusion>();
|
||||
constexpr ggml_cuda_mmq_vec_dot_t vec_dot = ggml_cuda_mmq_get_vec_dot<type, J, fallback, prec_src1>();
|
||||
constexpr ggml_cuda_mmq_write_back_t write_back = ggml_cuda_mmq_get_write_back<type, J, fallback, prec_src1>();
|
||||
constexpr ggml_cuda_mmq_write_back_t write_back = ggml_cuda_mmq_get_write_back<type, J, fallback, prec_src1, has_fusion>();
|
||||
|
||||
extern __shared__ int data_mul_mat_q[];
|
||||
int * tile_y = data_mul_mat_q + J;
|
||||
@@ -907,7 +947,7 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
constexpr int sz = sizeof(block_q8_1_mmq) / sizeof(int);
|
||||
|
||||
for (int kb0 = kb0_start; kb0 < kb0_stop; kb0 += blocks_per_iter) {
|
||||
load_tiles(x, tile_x, offset_x + kb0, tile_x_max_i, stride_row_x);
|
||||
load_tiles(x, x_gate, tile_x, offset_x + kb0, tile_x_max_i, stride_row_x);
|
||||
{
|
||||
const int * by0 = y + ncols_y * (kb0 * qk / ne_block) * sz;
|
||||
#pragma unroll
|
||||
@@ -942,28 +982,34 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
}
|
||||
|
||||
if (fixup) {
|
||||
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), y_scale, I, I, J);
|
||||
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), y_scale, I, I, J, glu_op, glu_limit);
|
||||
} else {
|
||||
write_back(sum, ids_dst, dst, y_scale, stride_col_dst, tile_x_max_i, tile_y_max_j);
|
||||
write_back(sum, ids_dst, dst, y_scale, stride_col_dst, tile_x_max_i, tile_y_max_j, glu_op, glu_limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The mul_mat_q kernel implements "stream-k" work partitioning as described in https://arxiv.org/abs/2301.03598
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
__launch_bounds__(ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1), ggml_cuda_mmq_get_occupancy(type, J, fallback, prec_src1))
|
||||
static __global__ void mul_mat_q(
|
||||
const char * __restrict__ x, const int * __restrict__ y, const int32_t * __restrict__ ids_dst,
|
||||
const char * __restrict__ x, const char * __restrict__ x_gate, const int * __restrict__ y, const int32_t * __restrict__ ids_dst,
|
||||
const int32_t * __restrict__ expert_bounds, float * __restrict__ dst, float * __restrict__ tmp_fixup,
|
||||
const float * __restrict__ y_scale,
|
||||
const float * __restrict__ y_scale, const ggml_glu_op glu_op, const float glu_limit,
|
||||
const uint3 blocks_per_ne00, const int nrows_x, const int ncols_dst, const int stride_row_x, const int ncols_y, const int stride_col_dst,
|
||||
const uint3 channel_ratio, const uint3 nchannels_y, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
|
||||
const uint3 sample_ratio, const uint3 nsamples_y, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst,
|
||||
const uint3 ntx) {
|
||||
|
||||
#ifdef TURING_MMA_AVAILABLE
|
||||
constexpr bool fusion_available = true;
|
||||
#else
|
||||
constexpr bool fusion_available = false;
|
||||
#endif // TURING_MMA_AVAILABLE
|
||||
|
||||
// Skip unused template specializations for faster compilation:
|
||||
if (ggml_cuda_mmq_get_config(type, J, fallback, prec_src1).type == GGML_TYPE_COUNT) {
|
||||
if (ggml_cuda_mmq_get_config(type, J, fallback, prec_src1).type == GGML_TYPE_COUNT || (has_fusion && !fusion_available)) {
|
||||
NO_DEVICE_CODE;
|
||||
return;
|
||||
}
|
||||
@@ -991,12 +1037,16 @@ static __global__ void mul_mat_q(
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if constexpr (!ggml_cuda_mmq_get_stream_k(type, J, fallback, prec_src1)) {
|
||||
// The fused kernel needs the whole K range of a tile in one block, so it always uses tiling.
|
||||
if constexpr (has_fusion || !ggml_cuda_mmq_get_stream_k(type, J, fallback, prec_src1)) {
|
||||
// The fused kernel writes the GLU of I/2 up rows and I/2 gate rows per tile.
|
||||
constexpr int I_dst = has_fusion ? I/2 : I;
|
||||
|
||||
const uint2 tmp2 = fast_div_modulo(blockIdx.z, nchannels_y);
|
||||
const int wt = tmp2.x;
|
||||
const int zt = tmp2.y;
|
||||
const int jt = blockIdx.y;
|
||||
const int it = blockIdx.x;
|
||||
const int jt = blockIdx.x;
|
||||
const int it = blockIdx.y;
|
||||
|
||||
// Defaults for regular matrix multiplication:
|
||||
int col_low = 0;
|
||||
@@ -1041,22 +1091,22 @@ static __global__ void mul_mat_q(
|
||||
}
|
||||
|
||||
offset_y += (col_low + jt*J)*(sizeof(block_q8_1_mmq)/sizeof(int));
|
||||
offset_dst += it*I;
|
||||
offset_dst += it*I_dst;
|
||||
const float * y_scale_tile = nullptr;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale += col_low + jt*J;
|
||||
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
|
||||
}
|
||||
|
||||
const int tile_x_max_i = nrows_x - it*I - 1;
|
||||
const int tile_x_max_i = nrows_x - it*I_dst - 1;
|
||||
const int tile_y_max_j = col_diff - jt*J - 1;
|
||||
|
||||
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I*stride_row_x;
|
||||
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I_dst*stride_row_x;
|
||||
|
||||
constexpr bool fixup = false;
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, has_fusion>
|
||||
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, 0, blocks_per_ne00.z);
|
||||
return;
|
||||
}
|
||||
@@ -1148,9 +1198,9 @@ static __global__ void mul_mat_q(
|
||||
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I*stride_row_x;
|
||||
|
||||
constexpr bool fixup = false; // All but (potentially) the last iterations write their data to dst rather than the fixup buffer.
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, false>
|
||||
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
|
||||
|
||||
kbc += blocks_per_ne00.z;
|
||||
@@ -1232,9 +1282,9 @@ static __global__ void mul_mat_q(
|
||||
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I*stride_row_x;
|
||||
|
||||
constexpr bool fixup = true; // Last index writes its data to fixup buffer to avoid data races with other blocks.
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, false>
|
||||
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
|
||||
}
|
||||
|
||||
@@ -1384,6 +1434,7 @@ struct mmq_args {
|
||||
int64_t nsamples_x; int64_t nsamples_y; int64_t stride_sample_x; int64_t stride_sample_y; int64_t stride_sample_dst;
|
||||
int64_t ncols_max;
|
||||
int J_best; // Tile width in ne11(dense)/ne12(MoE) direction to use for optimal performance.
|
||||
ggml_cuda_mm_fusion_args_device fusion = {}; // gate != nullptr selects the fused gate/up/GLU path
|
||||
};
|
||||
|
||||
static size_t mmq_get_nbytes_shared(const ggml_cuda_mmq_config & config, const int cc) {
|
||||
@@ -1393,7 +1444,7 @@ static size_t mmq_get_nbytes_shared(const ggml_cuda_mmq_config & config, const i
|
||||
return nbs_ids + nbs_x + GGML_PAD(nbs_y, config.nthreads*sizeof(int));
|
||||
}
|
||||
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) {
|
||||
const int id = ggml_cuda_get_device();
|
||||
const int cc = ggml_cuda_info().devices[id].cc;
|
||||
@@ -1407,13 +1458,17 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
|
||||
const dim3 block_dims(warp_size, nwarps, 1);
|
||||
|
||||
CUDA_SET_SHARED_MEMORY_LIMIT((mul_mat_q<type, J, false, prec_src1>), nbytes_shared);
|
||||
CUDA_SET_SHARED_MEMORY_LIMIT((mul_mat_q<type, J, true, prec_src1>), nbytes_shared);
|
||||
CUDA_SET_SHARED_MEMORY_LIMIT((mul_mat_q<type, J, /*fallback =*/ false, prec_src1, has_fusion>), nbytes_shared);
|
||||
if constexpr (!has_fusion) {
|
||||
CUDA_SET_SHARED_MEMORY_LIMIT((mul_mat_q<type, J, /*fallback =*/ true, prec_src1, false>), nbytes_shared);
|
||||
}
|
||||
|
||||
const int nty = (args.nrows_x + config.I - 1) / config.I;
|
||||
const int I_dst = has_fusion ? config.I/2 : config.I; // Rows of dst per tile, see mul_mat_q.
|
||||
const int nty = (args.nrows_x + I_dst - 1) / I_dst;
|
||||
const int ntx = (args.ncols_max + config.J - 1) / config.J;
|
||||
const int ntzw = args.nchannels_y * args.nsamples_y;
|
||||
const dim3 block_nums_xy_tiling(nty, ntx, ntzw);
|
||||
// Column tiles innermost so that the concurrently running blocks share the same tile of x and read it from L2.
|
||||
const dim3 block_nums_xy_tiling(ntx, nty, ntzw);
|
||||
|
||||
GGML_ASSERT(args.nchannels_y % args.nchannels_x == 0);
|
||||
GGML_ASSERT(args.nsamples_y % args.nsamples_x == 0);
|
||||
@@ -1427,9 +1482,10 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
const uint3 channel_ratio_fd = init_fastdiv_values(channel_ratio);
|
||||
const uint3 sample_ratio_fd = init_fastdiv_values(sample_ratio);
|
||||
|
||||
if (!config.stream_k) {
|
||||
mul_mat_q<type, J, fallback, prec_src1><<<block_nums_xy_tiling, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, nullptr, args.y_scale,
|
||||
if (has_fusion || !config.stream_k) {
|
||||
GGML_ASSERT(nty <= 65535); // gridDim.y limit
|
||||
mul_mat_q<type, J, fallback, prec_src1, has_fusion><<<block_nums_xy_tiling, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.expert_bounds, args.dst, nullptr, args.y_scale, args.fusion.glu_op, args.fusion.glu_limit,
|
||||
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
|
||||
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
|
||||
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
|
||||
@@ -1459,8 +1515,8 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
const dim3 block_nums_fixup(block_nums_stream_k.x, config.I/warp_size, 1);
|
||||
const dim3 block_dims_fixup(block_dims.x, block_dims.y/2, block_dims.z);
|
||||
|
||||
mul_mat_q<type, J, fallback, prec_src1><<<block_nums_stream_k, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr, args.y_scale,
|
||||
mul_mat_q<type, J, fallback, prec_src1, has_fusion><<<block_nums_stream_k, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr, args.y_scale, args.fusion.glu_op, args.fusion.glu_limit,
|
||||
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
|
||||
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
|
||||
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
|
||||
@@ -1477,56 +1533,56 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
ntx_fd);
|
||||
}
|
||||
|
||||
template <ggml_type type, bool fallback, ggml_prec prec_src1>
|
||||
template <ggml_type type, bool fallback, ggml_prec prec_src1, bool has_fusion>
|
||||
void mul_mat_q_switch_J(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) {
|
||||
switch (args.J_best) {
|
||||
case 8:
|
||||
launch_mul_mat_q<type, 8, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 8, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 16:
|
||||
launch_mul_mat_q<type, 16, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 16, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 24:
|
||||
launch_mul_mat_q<type, 24, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 24, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 32:
|
||||
launch_mul_mat_q<type, 32, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 32, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 40:
|
||||
launch_mul_mat_q<type, 40, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 40, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 48:
|
||||
launch_mul_mat_q<type, 48, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 48, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 56:
|
||||
launch_mul_mat_q<type, 56, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 56, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 64:
|
||||
launch_mul_mat_q<type, 64, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 64, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 72:
|
||||
launch_mul_mat_q<type, 72, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 72, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 80:
|
||||
launch_mul_mat_q<type, 80, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 80, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 88:
|
||||
launch_mul_mat_q<type, 88, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 88, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 96:
|
||||
launch_mul_mat_q<type, 96, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 96, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 104:
|
||||
launch_mul_mat_q<type, 104, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 104, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 112:
|
||||
launch_mul_mat_q<type, 112, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 112, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 120:
|
||||
launch_mul_mat_q<type, 120, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 120, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
case 128:
|
||||
launch_mul_mat_q<type, 128, fallback, prec_src1>(ctx, args, stream);
|
||||
launch_mul_mat_q<type, 128, fallback, prec_src1, has_fusion>(ctx, args, stream);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "J_best=%d\n", args.J_best);
|
||||
@@ -1534,14 +1590,26 @@ void mul_mat_q_switch_J(ggml_backend_cuda_context & ctx, const mmq_args & args,
|
||||
}
|
||||
}
|
||||
|
||||
template <ggml_type type, bool fallback, ggml_prec prec_src1>
|
||||
static void mul_mat_q_switch_fusion(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) {
|
||||
if constexpr (!fallback && ggml_cuda_mmq_fusion_supported(type)) {
|
||||
if (args.fusion.gate) {
|
||||
mul_mat_q_switch_J<type, fallback, prec_src1, true>(ctx, args, stream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
GGML_ASSERT(!args.fusion.gate);
|
||||
mul_mat_q_switch_J<type, fallback, prec_src1, false>(ctx, args, stream);
|
||||
}
|
||||
|
||||
template <ggml_type type, ggml_prec prec_src1>
|
||||
void mul_mat_q_case(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) {
|
||||
if (ggml_cuda_mmq_needs_fallback(args.nrows_x)) {
|
||||
constexpr bool fallback = true;
|
||||
mul_mat_q_switch_J<type, fallback, prec_src1>(ctx, args, stream);
|
||||
mul_mat_q_switch_fusion<type, fallback, prec_src1>(ctx, args, stream);
|
||||
} else {
|
||||
constexpr bool fallback = false;
|
||||
mul_mat_q_switch_J<type, fallback, prec_src1>(ctx, args, stream);
|
||||
mul_mat_q_switch_fusion<type, fallback, prec_src1>(ctx, args, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1583,6 +1651,9 @@ extern DECL_MMQ_CASE_W4A4(GGML_TYPE_NVFP4);
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void ggml_cuda_mul_mat_q(
|
||||
ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst);
|
||||
ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * ids, ggml_tensor * dst,
|
||||
const ggml_cuda_mm_fusion_args_host * fusion = nullptr);
|
||||
|
||||
bool ggml_cuda_should_use_mmq(enum ggml_type type, int cc, int64_t ne11, int64_t n_experts);
|
||||
|
||||
bool ggml_cuda_mmq_fusion_is_efficient(ggml_type type, int64_t nrows_x, int64_t ncols, int64_t nchannels, int device);
|
||||
|
||||
+39
-20
@@ -196,9 +196,9 @@ static __global__ void rope_neox(const T * x,
|
||||
dst[idst + n_offs/2 + n_dims / 2] = ggml_cuda_cast<D>(x0 * sin_theta + x1 * cos_theta);
|
||||
}
|
||||
|
||||
template <bool forward, bool has_ff, typename T>
|
||||
template <bool forward, bool has_ff, typename T, typename D>
|
||||
static __global__ void rope_multi(const T * x,
|
||||
T * dst,
|
||||
D * dst,
|
||||
const int ne00,
|
||||
const int ne01,
|
||||
const int ne02,
|
||||
@@ -219,6 +219,8 @@ static __global__ void rope_multi(const T * x,
|
||||
const float * freq_factors,
|
||||
const mrope_sections sections,
|
||||
const bool is_imrope,
|
||||
const int64_t * row_indices,
|
||||
const int set_rows_stride,
|
||||
const bool inplace) {
|
||||
const int i0 = 2 * (blockDim.y * blockIdx.y + threadIdx.y);
|
||||
|
||||
@@ -236,12 +238,20 @@ static __global__ void rope_multi(const T * x,
|
||||
const int ix = i0 / 2 + i1 * s01 + i2 * s02 + i3 * s03;
|
||||
|
||||
ggml_cuda_pdl_sync();
|
||||
|
||||
// Fusion optimization: ROPE + VIEW + SET_ROWS.
|
||||
// The rope output is viewed as a 1D tensor and offset based on a row index in row_indices.
|
||||
if (set_rows_stride != 0) {
|
||||
idst = i1 * s1 + i0 / 2;
|
||||
idst += row_indices[i2] * set_rows_stride;
|
||||
}
|
||||
|
||||
if (i0 < n_offs || i0 >= n_offs + n_dims) {
|
||||
if (inplace) {
|
||||
return;
|
||||
}
|
||||
dst[idst + i0/2 + 0] = x[ix + i0/2 + 0];
|
||||
dst[idst + i0/2 + 1] = x[ix + i0/2 + 1];
|
||||
dst[idst + i0/2 + 0] = ggml_cuda_cast<D>(x[ix + i0/2 + 0]);
|
||||
dst[idst + i0/2 + 1] = ggml_cuda_cast<D>(x[ix + i0/2 + 1]);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -286,8 +296,8 @@ static __global__ void rope_multi(const T * x,
|
||||
const float x0 = x[ix + n_offs/2 + 0];
|
||||
const float x1 = x[ix + n_offs/2 + n_dims/2];
|
||||
|
||||
dst[idst + n_offs/2 + 0] = x0*cos_theta - x1*sin_theta;
|
||||
dst[idst + n_offs/2 + n_dims/2] = x0*sin_theta + x1*cos_theta;
|
||||
dst[idst + n_offs/2 + 0] = ggml_cuda_cast<D>(x0*cos_theta - x1*sin_theta);
|
||||
dst[idst + n_offs/2 + n_dims/2] = ggml_cuda_cast<D>(x0*sin_theta + x1*cos_theta);
|
||||
}
|
||||
|
||||
template <bool forward, bool has_ff, typename T>
|
||||
@@ -443,9 +453,9 @@ static void rope_neox_cuda(const T * x,
|
||||
}
|
||||
}
|
||||
|
||||
template <bool forward, typename T>
|
||||
template <bool forward, typename T, typename D>
|
||||
static void rope_multi_cuda(const T * x,
|
||||
T * dst,
|
||||
D * dst,
|
||||
const int ne00,
|
||||
const int ne01,
|
||||
const int ne02,
|
||||
@@ -467,6 +477,8 @@ static void rope_multi_cuda(const T * x,
|
||||
const float * freq_factors,
|
||||
const mrope_sections sections,
|
||||
const bool is_imrope,
|
||||
const int64_t * row_indices,
|
||||
const int set_rows_stride,
|
||||
const bool inplace,
|
||||
cudaStream_t stream) {
|
||||
GGML_ASSERT(ne00 % 2 == 0);
|
||||
@@ -478,14 +490,14 @@ static void rope_multi_cuda(const T * x,
|
||||
|
||||
if (freq_factors == nullptr) {
|
||||
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(block_nums, block_dims, 0, stream);
|
||||
ggml_cuda_kernel_launch(rope_multi<forward, false, T>, launch_params,
|
||||
ggml_cuda_kernel_launch(rope_multi<forward, false, T, D>, launch_params,
|
||||
x, dst, ne00, ne01, ne02, s01, s02, s03, s1, s2, s3, n_dims, n_offs, pos, freq_scale, ext_factor,
|
||||
attn_factor, corr_dims, theta_scale, freq_factors, sections, is_imrope, inplace);
|
||||
attn_factor, corr_dims, theta_scale, freq_factors, sections, is_imrope, row_indices, set_rows_stride, inplace);
|
||||
} else {
|
||||
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(block_nums, block_dims, 0, stream);
|
||||
ggml_cuda_kernel_launch(rope_multi<forward, true, T>, launch_params,
|
||||
ggml_cuda_kernel_launch(rope_multi<forward, true, T, D>, launch_params,
|
||||
x, dst, ne00, ne01, ne02, s01, s02, s03, s1, s2, s3, n_dims, n_offs, pos, freq_scale, ext_factor,
|
||||
attn_factor, corr_dims, theta_scale, freq_factors, sections, is_imrope, inplace);
|
||||
attn_factor, corr_dims, theta_scale, freq_factors, sections, is_imrope, row_indices, set_rows_stride, inplace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,14 +660,21 @@ void ggml_cuda_op_rope_impl(ggml_backend_cuda_context & ctx,
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
} else if (is_mrope && !is_vision) {
|
||||
if (src0->type == GGML_TYPE_F32) {
|
||||
rope_multi_cuda<forward>((const float *) src0_d, (float *) dst_d, ne00, ne01, ne02, s01, s02, s03, s1,
|
||||
s2, s3, n_dims, n_offs, nr, pos, freq_scale, freq_base, ext_factor, attn_factor,
|
||||
corr_dims, freq_factors, sections, is_imrope, inplace, stream);
|
||||
} else if (src0->type == GGML_TYPE_F16) {
|
||||
rope_multi_cuda<forward>((const half *) src0_d, (half *) dst_d, ne00, ne01, ne02, s01, s02, s03, s1,
|
||||
s2, s3, n_dims, n_offs, nr, pos, freq_scale, freq_base, ext_factor, attn_factor,
|
||||
corr_dims, freq_factors, sections, is_imrope, inplace, stream);
|
||||
if (src0->type == GGML_TYPE_F32 && dst_type == GGML_TYPE_F32) {
|
||||
rope_multi_cuda<forward, float, float>((const float *) src0_d, (float *) dst_d, ne00, ne01, ne02, s01, s02,
|
||||
s03, s1, s2, s3, n_dims, n_offs, nr, pos, freq_scale, freq_base,
|
||||
ext_factor, attn_factor, corr_dims, freq_factors, sections, is_imrope,
|
||||
row_indices, set_rows_stride, inplace, stream);
|
||||
} else if (src0->type == GGML_TYPE_F32 && dst_type == GGML_TYPE_F16) {
|
||||
rope_multi_cuda<forward, float, half>((const float *) src0_d, (half *) dst_d, ne00, ne01, ne02, s01, s02,
|
||||
s03, s1, s2, s3, n_dims, n_offs, nr, pos, freq_scale, freq_base,
|
||||
ext_factor, attn_factor, corr_dims, freq_factors, sections, is_imrope,
|
||||
row_indices, set_rows_stride, inplace, stream);
|
||||
} else if (src0->type == GGML_TYPE_F16 && dst_type == GGML_TYPE_F16) {
|
||||
rope_multi_cuda<forward, half, half>((const half *) src0_d, (half *) dst_d, ne00, ne01, ne02, s01, s02,
|
||||
s03, s1, s2, s3, n_dims, n_offs, nr, pos, freq_scale, freq_base,
|
||||
ext_factor, attn_factor, corr_dims, freq_factors, sections, is_imrope,
|
||||
row_indices, set_rows_stride, inplace, stream);
|
||||
} else {
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ __device__ void sqrt_softplus_warp_inplace(float (&vals)[experts_per_thread], co
|
||||
for (int i = 0; i < experts_per_thread; i++) {
|
||||
const int idx = lane + i * WARP_SIZE;
|
||||
const bool active = !use_limit || (idx < limit);
|
||||
vals[i] = active ? sqrtf(vals[i] > 20.0f ? vals[i] : logf(1.0f + expf(vals[i]))) : -INFINITY;
|
||||
vals[i] = active ? sqrtf(vals[i] > 20.0f ? vals[i] : log1pf(expf(vals[i]))) : -INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ static __device__ __forceinline__ float op_expm1(float x) {
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ float op_softplus(float x) {
|
||||
return (x > 20.0f) ? x : logf(1.0f + expf(x));
|
||||
return (x > 20.0f) ? x : log1pf(expf(x));
|
||||
}
|
||||
|
||||
static __device__ __forceinline__ float op_elu(float x) {
|
||||
|
||||
@@ -121,3 +121,18 @@ __device__ __forceinline__ float ggml_cuda_op_swiglu_clamp_single(float gate, fl
|
||||
|
||||
return ggml_cuda_op_silu_single(gate) * up;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ float ggml_cuda_op_glu_single(ggml_glu_op op, float gate, float up, float limit) {
|
||||
switch (op) {
|
||||
case GGML_GLU_OP_SWIGLU:
|
||||
return up * ggml_cuda_op_silu_single(gate);
|
||||
case GGML_GLU_OP_GEGLU:
|
||||
return up * ggml_cuda_op_gelu_single(gate);
|
||||
case GGML_GLU_OP_SWIGLU_OAI:
|
||||
return ggml_cuda_op_swiglu_oai_single(gate, up);
|
||||
case GGML_GLU_OP_SWIGLU_CLAMP:
|
||||
return ggml_cuda_op_swiglu_clamp_single(gate, up, limit);
|
||||
default:
|
||||
return up * gate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2397,7 +2397,7 @@ static ggml_status ggml_backend_hexagon_graph_compute(ggml_backend_t backend, gg
|
||||
}
|
||||
}
|
||||
|
||||
GGML_ASSERT(htp_kparams_solve(sess, node.node, node.kernel_params));
|
||||
GGML_ASSERT(htp_kparams_solve(sess, node));
|
||||
if (node.opcode == HTP_OP_CPY && node.kparams<htp_copy_kernel_params>()->total_elems == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,15 +192,20 @@ static bool fuse_mul_mat_add(const ggml_hexagon_session * sess, htp_opnode & pre
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ true, &src2)) return false;
|
||||
if (src2->type != GGML_TYPE_F32) return false;
|
||||
|
||||
const ggml_tensor * src0 = prev.src0();
|
||||
const ggml_tensor * src1 = prev.src1();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (!ggml_are_same_shape(prev.dst(), dst)) return false;
|
||||
const ggml_tensor * src0 = prev.node->src[0];
|
||||
const ggml_tensor * src1 = prev.node->src[1];
|
||||
const ggml_tensor * dst = cur.node;
|
||||
if (!ggml_are_same_shape(prev.node, dst)) return false;
|
||||
|
||||
const bool prev_collapsed = htp_matmul_can_collapse(src0, src1, prev.node);
|
||||
const bool cur_collapsed = htp_matmul_add_can_collapse(src0, src1, src2, dst);
|
||||
if (prev_collapsed != cur_collapsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto * orig_kparams = prev.kparams<htp_mm_kernel_params>();
|
||||
struct htp_mm_kernel_params kparams;
|
||||
if (!htp_kparams_solve_matmul_add(sess, src0, src1, src2, dst, &kparams) ||
|
||||
orig_kparams->collapse != kparams.collapse) {
|
||||
if (!htp_kparams_solve_matmul_add(sess, src0, src1, src2, dst, &kparams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -222,6 +227,7 @@ static bool fuse_mul_mat_add(const ggml_hexagon_session * sess, htp_opnode & pre
|
||||
prev.opcode = HTP_OP_MUL_MAT_ADD;
|
||||
prev.inputs = { src0, src1, src2 };
|
||||
prev.outputs = { dst };
|
||||
htp_opnode_collapse(sess, prev);
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
@@ -233,36 +239,31 @@ static bool fuse_mul_mat_nx(const ggml_hexagon_session * sess, htp_opnode & prev
|
||||
const uint32_t curr_n = (uint32_t) prev.outputs.size();
|
||||
if (curr_n >= HTP_OP_MAX_OUTPUTS || curr_n + 1 >= HTP_OP_MAX_INPUTS) return false;
|
||||
|
||||
const ggml_tensor * w0 = prev.inputs[0];
|
||||
const ggml_tensor * x = prev.inputs[curr_n];
|
||||
const ggml_tensor * w_in = cur.src0();
|
||||
const ggml_tensor * x_in = cur.src1();
|
||||
const ggml_tensor * d_in = cur.dst();
|
||||
const ggml_tensor * w0 = prev.inputs[0];
|
||||
const ggml_tensor * x_orig = prev.node->src[1];
|
||||
const ggml_tensor * w_in = cur.node->src[0];
|
||||
const ggml_tensor * x_in = cur.node->src[1];
|
||||
const ggml_tensor * d_in = cur.node;
|
||||
|
||||
auto * orig_kparams = prev.kparams<htp_mm_kernel_params>();
|
||||
const auto * cur_kparams = cur.kparams<htp_mm_kernel_params>();
|
||||
|
||||
if (x_in != x || w_in->type != w0->type || w_in->ne[0] != w0->ne[0] ||
|
||||
if (x_in != x_orig || w_in->type != w0->type || w_in->ne[0] != w0->ne[0] ||
|
||||
w0->ne[2] != 1 || w0->ne[3] != 1 || w_in->ne[2] != 1 || w_in->ne[3] != 1 ||
|
||||
htp_tensor_is_tiled(w_in) != htp_tensor_is_tiled(w0) ||
|
||||
orig_kparams->n_hmx != cur_kparams->n_hmx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool collapse = orig_kparams->collapse && htp_matmul_can_collapse(w_in, x, d_in);
|
||||
if ((x->ne[2] > 1 || x->ne[3] > 1) && !collapse) return false;
|
||||
const bool collapse = htp_matmul_can_collapse(w0, x_orig, prev.node) && htp_matmul_can_collapse(w_in, x_in, d_in);
|
||||
if ((x_orig->ne[2] > 1 || x_orig->ne[3] > 1) && !collapse) return false;
|
||||
|
||||
if (prev.opcode == HTP_OP_MUL_MAT_NX) {
|
||||
orig_kparams->n_weights = curr_n + 1;
|
||||
} else {
|
||||
struct htp_mm_kernel_params kparams;
|
||||
if (collapse) {
|
||||
const ggml_tensor x_collapsed = htp_tensor_collapse_rows(x);
|
||||
if (!htp_kparams_solve_mmnx(sess, w0, &x_collapsed, curr_n + 1, &kparams)) return false;
|
||||
kparams.collapse = 1;
|
||||
} else if (!htp_kparams_solve_mmnx(sess, w0, x, curr_n + 1, &kparams)) {
|
||||
return false;
|
||||
}
|
||||
const ggml_tensor x_collapsed = collapse ? htp_tensor_collapse_rows(x_orig) : *x_orig;
|
||||
if (!htp_kparams_solve_mmnx(sess, w0, &x_collapsed, curr_n + 1, &kparams)) return false;
|
||||
if (!is_supported_mul_mat_nx_kernel(w0, &kparams)) return false;
|
||||
|
||||
prev.set_kparams(kparams);
|
||||
@@ -270,7 +271,7 @@ static bool fuse_mul_mat_nx(const ggml_hexagon_session * sess, htp_opnode & prev
|
||||
}
|
||||
|
||||
prev.inputs.insert(prev.inputs.begin() + curr_n, w_in);
|
||||
prev.outputs.push_back(d_in);
|
||||
prev.outputs.push_back(collapse ? prev.add_dummy(htp_tensor_collapse_rows(d_in)) : d_in);
|
||||
prev.fused.push_back(cur.node);
|
||||
return true;
|
||||
}
|
||||
@@ -415,15 +416,13 @@ static bool fuse_unary_mul_glu(const ggml_hexagon_session * sess, htp_opnode & p
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (htp_inputs_overlap_unsafe(prev, dst, up)) return false;
|
||||
|
||||
struct htp_glu_kernel_params kparams;
|
||||
if (!htp_kparams_solve_unary_mul_glu(sess, glu_op, gate, up, dst, &kparams)) return false;
|
||||
|
||||
prev.opcode = glu_op;
|
||||
prev.inputs = { gate, up };
|
||||
prev.outputs = { dst };
|
||||
if (!htp_kparams_solve(sess, prev)) return false;
|
||||
|
||||
prev.fused.push_back(cur.node);
|
||||
memset(prev.params, 0, sizeof(prev.params));
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include "htp-kparams.h"
|
||||
#include "htp-opnode.h"
|
||||
#include "ggml-impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -1023,14 +1024,12 @@ static bool htp_kparams_solve_matmul(
|
||||
if (htp_matmul_can_collapse(src0, src1, dst)) {
|
||||
const ggml_tensor src1_collapsed = htp_tensor_collapse_rows(src1);
|
||||
const ggml_tensor dst_collapsed = htp_tensor_collapse_rows(dst);
|
||||
const bool ok = htp_kparams_solve_matmul_impl(sess, src0, &src1_collapsed, &dst_collapsed, 0, 0, kparams);
|
||||
kparams->collapse = 1;
|
||||
return ok;
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, &src1_collapsed, &dst_collapsed, 0, 0, kparams);
|
||||
}
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, src1, dst, 0, 0, kparams);
|
||||
}
|
||||
|
||||
static bool htp_matmul_add_can_collapse(
|
||||
bool htp_matmul_add_can_collapse(
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * src2,
|
||||
@@ -1068,9 +1067,7 @@ bool htp_kparams_solve_matmul_add(
|
||||
: (src2 ? *src2 : ggml_tensor{});
|
||||
const struct ggml_tensor * p_src2 = src2 ? &src2_collapsed : nullptr;
|
||||
const size_t src2_size = p_src2 ? hex_round_up(ggml_nbytes(p_src2), 128) : 0;
|
||||
const bool ok = htp_kparams_solve_matmul_impl(sess, src0, &src1_collapsed, &dst_collapsed, p_src2 ? p_src2->nb[1] : 0, src2_size, kparams);
|
||||
kparams->collapse = 1;
|
||||
return ok;
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, &src1_collapsed, &dst_collapsed, p_src2 ? p_src2->nb[1] : 0, src2_size, kparams);
|
||||
}
|
||||
const size_t src2_size = src2 ? hex_round_up(ggml_nbytes(src2), 128) : 0;
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, src1, dst, src2 ? src2->nb[1] : 0, src2_size, kparams);
|
||||
@@ -1405,47 +1402,103 @@ static bool htp_kparams_solve_binary(
|
||||
}
|
||||
|
||||
// collapse contiguous dims of elementwise ops so that rows fill at least one HVX vector
|
||||
static void htp_unary_collapse_dims(struct ggml_tensor * src, struct ggml_tensor * dst, size_t elem_size) {
|
||||
if (src->nb[0] != elem_size || dst->nb[0] != elem_size) {
|
||||
static void htp_collapse_dims(struct ggml_tensor ** tensors, size_t n_tensors, size_t elem_size, uint32_t n_threads) {
|
||||
if (n_tensors == 0 || !tensors[0]) {
|
||||
return;
|
||||
}
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
if (!tensors[t] || tensors[t]->nb[0] != elem_size) {
|
||||
return;
|
||||
}
|
||||
if (t > 0 && !ggml_are_same_shape(tensors[0], tensors[t])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (src->ne[1] > 1 || src->ne[2] > 1 || src->ne[3] > 1) {
|
||||
const size_t row_size = (size_t) src->ne[0] * elem_size;
|
||||
const bool rows_contig = src->nb[1] == row_size && dst->nb[1] == (size_t) dst->ne[0] * elem_size;
|
||||
const bool can_merge = (src->ne[1] == 1 && dst->ne[1] == 1) ||
|
||||
(rows_contig && (src->ne[0] == 1 || (row_size < 128 && row_size * src->ne[1] <= 2048)));
|
||||
struct ggml_tensor * t0 = tensors[0];
|
||||
|
||||
while (t0->ne[1] > 1 || t0->ne[2] > 1 || t0->ne[3] > 1) {
|
||||
const size_t row_size = (size_t) t0->ne[0] * elem_size;
|
||||
bool rows_contig = true;
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
if (tensors[t]->nb[1] != (size_t) tensors[t]->ne[0] * elem_size) {
|
||||
rows_contig = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool all_ne1_one = true;
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
if (tensors[t]->ne[1] != 1) {
|
||||
all_ne1_one = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool can_merge = all_ne1_one ||
|
||||
(rows_contig && (t0->ne[0] == 1 || (row_size < 128 && row_size * t0->ne[1] <= 2048)));
|
||||
if (!can_merge) {
|
||||
break;
|
||||
}
|
||||
src->ne[0] *= src->ne[1];
|
||||
src->ne[1] = src->ne[2];
|
||||
src->ne[2] = src->ne[3];
|
||||
src->ne[3] = 1;
|
||||
dst->ne[0] *= dst->ne[1];
|
||||
dst->ne[1] = dst->ne[2];
|
||||
dst->ne[2] = dst->ne[3];
|
||||
dst->ne[3] = 1;
|
||||
src->nb[1] = src->nb[2];
|
||||
src->nb[2] = src->nb[3];
|
||||
dst->nb[1] = dst->nb[2];
|
||||
dst->nb[2] = dst->nb[3];
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
tensors[t]->ne[0] *= tensors[t]->ne[1];
|
||||
tensors[t]->ne[1] = tensors[t]->ne[2];
|
||||
tensors[t]->ne[2] = tensors[t]->ne[3];
|
||||
tensors[t]->ne[3] = 1;
|
||||
tensors[t]->nb[1] = tensors[t]->nb[2];
|
||||
tensors[t]->nb[2] = tensors[t]->nb[3];
|
||||
tensors[t]->nb[3] = tensors[t]->nb[2] * tensors[t]->ne[2];
|
||||
}
|
||||
}
|
||||
|
||||
// ne1 too big to merge in full: merge it in chunks of k rows (k divides ne1)
|
||||
const size_t row_size = (size_t) src->ne[0] * elem_size;
|
||||
if (src->ne[1] > 1 && row_size < 128 && src->nb[1] == row_size && dst->nb[1] == row_size) {
|
||||
const size_t row_size = (size_t) t0->ne[0] * elem_size;
|
||||
bool rows_contig = true;
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
if (tensors[t]->nb[1] != row_size) {
|
||||
rows_contig = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t0->ne[1] > 1 && row_size < 128 && rows_contig) {
|
||||
int64_t k = 2048 / row_size;
|
||||
while (k > 1 && src->ne[1] % k != 0) {
|
||||
while (k > 1 && t0->ne[1] % k != 0) {
|
||||
k--;
|
||||
}
|
||||
if (k > 1) {
|
||||
src->ne[0] *= k;
|
||||
src->ne[1] /= k;
|
||||
src->nb[1] *= k;
|
||||
dst->ne[0] *= k;
|
||||
dst->ne[1] /= k;
|
||||
dst->nb[1] *= k;
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
tensors[t]->ne[0] *= k;
|
||||
tensors[t]->ne[1] /= k;
|
||||
tensors[t]->nb[1] *= k;
|
||||
tensors[t]->nb[2] = tensors[t]->nb[1] * tensors[t]->ne[1];
|
||||
tensors[t]->nb[3] = tensors[t]->nb[2] * tensors[t]->ne[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nrows too small to saturate threads: split ne0 into k rows (k divides ne0)
|
||||
const size_t row_size_curr = (size_t) t0->ne[0] * elem_size;
|
||||
const uint32_t nrows = (uint32_t) (t0->ne[1] * t0->ne[2] * t0->ne[3]);
|
||||
rows_contig = true;
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
if (tensors[t]->ne[1] != 1 && tensors[t]->nb[1] != row_size_curr) {
|
||||
rows_contig = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nrows < n_threads && rows_contig && row_size_curr >= 4096) {
|
||||
const size_t vec_elems = 128 / elem_size;
|
||||
int64_t k = std::max<int64_t>(row_size_curr / 16384, (n_threads * 4 + nrows - 1) / nrows);
|
||||
k = std::min<int64_t>(k, row_size_curr / 2048);
|
||||
while (k > 1 && t0->ne[0] % (k * vec_elems) != 0) {
|
||||
k--;
|
||||
}
|
||||
if (k > 1) {
|
||||
for (size_t t = 0; t < n_tensors; t++) {
|
||||
tensors[t]->ne[0] /= k;
|
||||
tensors[t]->ne[1] *= k;
|
||||
tensors[t]->nb[1] = tensors[t]->ne[0] * elem_size;
|
||||
tensors[t]->nb[2] = tensors[t]->nb[1] * tensors[t]->ne[1];
|
||||
tensors[t]->nb[3] = tensors[t]->nb[2] * tensors[t]->ne[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1507,19 +1560,6 @@ static bool htp_kparams_solve_unary_impl(
|
||||
|
||||
const size_t elem_size = ggml_type_size(src0->type);
|
||||
|
||||
struct ggml_tensor src0_c = *src0;
|
||||
struct ggml_tensor dst_c = *dst;
|
||||
if (htp_unary_is_elementwise(op)) {
|
||||
htp_unary_collapse_dims(&src0_c, &dst_c, elem_size);
|
||||
src0 = &src0_c;
|
||||
dst = &dst_c;
|
||||
for (int i = 0; i < HTP_OP_MAX_DIMS; i++) {
|
||||
kparams->ne[i] = (uint32_t) src0->ne[i];
|
||||
kparams->src0_nb[i] = (uint32_t) src0->nb[i];
|
||||
kparams->dst_nb[i] = (uint32_t) dst->nb[i];
|
||||
}
|
||||
}
|
||||
|
||||
const size_t dst_data_row_size = dst->ne[0] * ggml_type_size(dst->type);
|
||||
|
||||
const uint32_t src0_nrows = src0->ne[1] * src0->ne[2] * src0->ne[3];
|
||||
@@ -1533,9 +1573,14 @@ static bool htp_kparams_solve_unary_impl(
|
||||
const bool broadcast_weight = (op == HTP_OP_RMS_NORM_MUL) && (src1->ne[1] * src1->ne[2] * src1->ne[3] == 1);
|
||||
kparams->broadcast_weight = broadcast_weight;
|
||||
|
||||
const bool src0_contig = ggml_is_contiguous_1(src0);
|
||||
const bool src1_contig = (op == HTP_OP_RMS_NORM_MUL && !broadcast_weight) ? ggml_is_contiguous_1(src1) : true;
|
||||
const uint32_t max_block = (src0_contig && src1_contig) ? src0_nrows : (uint32_t) src0->ne[1];
|
||||
const bool src0_contig = ggml_is_contiguous_1(src0);
|
||||
const bool src1_contig = (op == HTP_OP_RMS_NORM_MUL && !broadcast_weight) ? ggml_is_contiguous_1(src1) : true;
|
||||
const uint32_t src0_nrows_per_thread = (src0_nrows + n_threads - 1) / n_threads;
|
||||
const uint32_t min_block_rows = (uint32_t) (std::max<size_t>(1, 2048 / dst_data_row_size));
|
||||
uint32_t max_block = (src0_contig && src1_contig) ? src0_nrows : (uint32_t) src0->ne[1];
|
||||
if (src0_nrows_per_thread >= 2 * min_block_rows) {
|
||||
max_block = (std::min)(max_block, (std::max)(min_block_rows, src0_nrows_per_thread / 4));
|
||||
}
|
||||
|
||||
kparams->src0_contig = src0_contig ? 1 : 0;
|
||||
kparams->src1_contig = src1_contig ? 1 : 0;
|
||||
@@ -2000,11 +2045,17 @@ bool htp_kparams_solve_unary_mul_glu(
|
||||
const size_t vtcm_size = htp_session_vtcm_size(sess);
|
||||
|
||||
const size_t row_size_aligned = hex_round_up(dst_row_size, 128);
|
||||
const uint32_t vtcm_row_per_thread = (uint32_t) (vtcm_size / (n_threads * 3 * row_size_aligned));
|
||||
uint32_t vtcm_row_per_thread = (uint32_t) (vtcm_size / (n_threads * 3 * row_size_aligned));
|
||||
if (vtcm_row_per_thread < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t src0_nrows_per_thread = (src0_nrows + n_threads - 1) / n_threads;
|
||||
if (src0_nrows_per_thread >= 2 && vtcm_row_per_thread > src0_nrows_per_thread) {
|
||||
uint32_t target_rpt = (src0_nrows_per_thread >= 4) ? (src0_nrows_per_thread / 2) & ~1u : 2;
|
||||
vtcm_row_per_thread = (std::max)(2u, (std::min)(vtcm_row_per_thread, target_rpt));
|
||||
}
|
||||
|
||||
struct htp_glu_vtcm_layout L;
|
||||
htp_glu_vtcm_layout_build(&L, dst_row_size, n_threads, vtcm_row_per_thread);
|
||||
|
||||
@@ -3246,12 +3297,131 @@ static inline bool htp_kparams_invoke(
|
||||
return SolveFn(sess, op, kparams ? (KParams *) kparams : &tmp);
|
||||
}
|
||||
|
||||
void htp_opnode_collapse(const struct ggml_hexagon_session * sess, htp_opnode & node) {
|
||||
const uint32_t n_threads = htp_session_n_threads(sess);
|
||||
|
||||
if (htp_op_is_unary(node.opcode) && htp_unary_is_elementwise(node.opcode)) {
|
||||
if (node.inputs.empty() || !node.inputs[0] || node.outputs.empty() || !node.outputs[0]) {
|
||||
return;
|
||||
}
|
||||
if (!ggml_are_same_shape(node.inputs[0], node.outputs[0])) {
|
||||
return;
|
||||
}
|
||||
const size_t elem_size = ggml_type_size(node.inputs[0]->type);
|
||||
struct ggml_tensor src0_c = *node.inputs[0];
|
||||
struct ggml_tensor dst_c = *node.outputs[0];
|
||||
struct ggml_tensor * tensors[2] = { &src0_c, &dst_c };
|
||||
htp_collapse_dims(tensors, 2, elem_size, n_threads);
|
||||
if (src0_c.ne[0] != node.inputs[0]->ne[0] || src0_c.ne[1] != node.inputs[0]->ne[1]) {
|
||||
node.inputs[0] = node.add_dummy(src0_c);
|
||||
node.outputs[0] = node.add_dummy(dst_c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (htp_op_is_glu(node.opcode)) {
|
||||
if (node.inputs.size() < 2 || !node.inputs[0] || !node.inputs[1] || node.outputs.empty() || !node.outputs[0]) {
|
||||
return;
|
||||
}
|
||||
if (!ggml_are_same_shape(node.inputs[0], node.outputs[0]) ||
|
||||
!ggml_are_same_shape(node.inputs[1], node.outputs[0])) {
|
||||
return;
|
||||
}
|
||||
const size_t elem_size = ggml_type_size(node.inputs[0]->type);
|
||||
struct ggml_tensor src0_c = *node.inputs[0];
|
||||
struct ggml_tensor src1_c = *node.inputs[1];
|
||||
struct ggml_tensor dst_c = *node.outputs[0];
|
||||
struct ggml_tensor * tensors[3] = { &src0_c, &src1_c, &dst_c };
|
||||
htp_collapse_dims(tensors, 3, elem_size, n_threads);
|
||||
if (src0_c.ne[0] != node.inputs[0]->ne[0] || src0_c.ne[1] != node.inputs[0]->ne[1]) {
|
||||
node.inputs[0] = node.add_dummy(src0_c);
|
||||
node.inputs[1] = node.add_dummy(src1_c);
|
||||
node.outputs[0] = node.add_dummy(dst_c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.opcode == HTP_OP_MUL_MAT) {
|
||||
if (node.inputs.size() < 2 || !node.inputs[0] || !node.inputs[1] || node.outputs.empty() || !node.outputs[0]) {
|
||||
return;
|
||||
}
|
||||
const struct ggml_tensor * src0 = node.inputs[0];
|
||||
const struct ggml_tensor * src1 = node.inputs[1];
|
||||
const struct ggml_tensor * dst = node.outputs[0];
|
||||
if (htp_matmul_can_collapse(src0, src1, dst)) {
|
||||
node.inputs[1] = node.add_dummy(htp_tensor_collapse_rows(src1));
|
||||
node.outputs[0] = node.add_dummy(htp_tensor_collapse_rows(dst));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.opcode == HTP_OP_MUL_MAT_ADD) {
|
||||
if (node.inputs.size() < 2 || !node.inputs[0] || !node.inputs[1] || node.outputs.empty() || !node.outputs[0]) {
|
||||
return;
|
||||
}
|
||||
const struct ggml_tensor * src0 = node.inputs[0];
|
||||
const struct ggml_tensor * src1 = node.inputs[1];
|
||||
const struct ggml_tensor * src2 = node.inputs.size() > 2 ? node.inputs[2] : nullptr;
|
||||
const struct ggml_tensor * dst = node.outputs[0];
|
||||
if (htp_matmul_add_can_collapse(src0, src1, src2, dst)) {
|
||||
node.inputs[1] = node.add_dummy(htp_tensor_collapse_rows(src1));
|
||||
node.outputs[0] = node.add_dummy(htp_tensor_collapse_rows(dst));
|
||||
if (src2 && (src2->ne[1] * src2->ne[2] * src2->ne[3] > 1)) {
|
||||
node.inputs[2] = node.add_dummy(htp_tensor_collapse_rows(src2));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
htp_opnode & node
|
||||
) {
|
||||
htp_opnode_collapse(sess, node);
|
||||
|
||||
const ggml_tensor * src0 = node.inputs.empty() ? nullptr : node.inputs[0];
|
||||
const ggml_tensor * src1 = node.inputs.size() > 1 ? node.inputs[1] : nullptr;
|
||||
const ggml_tensor * dst = node.dst();
|
||||
|
||||
if (htp_op_is_unary(node.opcode)) {
|
||||
return htp_kparams_solve_unary_impl(sess, node.opcode, src0, src1, dst, node.kparams<htp_unary_kernel_params>());
|
||||
}
|
||||
if (htp_op_is_glu(node.opcode)) {
|
||||
return htp_kparams_solve_unary_mul_glu(sess, node.opcode, src0, src1, dst, node.kparams<htp_glu_kernel_params>());
|
||||
}
|
||||
if (node.opcode == HTP_OP_MUL_MAT) {
|
||||
if (!htp_matmul_check_types(src0, src1, dst)) {
|
||||
return false;
|
||||
}
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, src1, dst, 0, 0, node.kparams<htp_mm_kernel_params>());
|
||||
}
|
||||
if (node.opcode == HTP_OP_MUL_MAT_ADD) {
|
||||
const ggml_tensor * src2 = node.inputs.size() > 2 ? node.inputs[2] : nullptr;
|
||||
const size_t src2_size = src2 ? hex_round_up(ggml_nbytes(src2), 128) : 0;
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, src1, dst, src2 ? src2->nb[1] : 0, src2_size, node.kparams<htp_mm_kernel_params>());
|
||||
}
|
||||
|
||||
return htp_kparams_solve(sess, node.node, node.kernel_params);
|
||||
}
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
void * kparams
|
||||
) {
|
||||
const htp_op_code opcode = htp_op_remap(op);
|
||||
if (htp_op_is_unary(opcode) || htp_op_is_glu(opcode)) {
|
||||
htp_opnode tmp(opcode, op);
|
||||
if (!htp_kparams_solve(sess, tmp)) {
|
||||
return false;
|
||||
}
|
||||
if (kparams) {
|
||||
memcpy(kparams, tmp.kernel_params, sizeof(tmp.kernel_params));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case HTP_OP_MUL_MAT:
|
||||
case HTP_OP_MUL_MAT_ID:
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
bool htp_rows_stride(const int64_t * ne, const size_t * nb, size_t * stride);
|
||||
bool htp_matmul_can_collapse(const struct ggml_tensor * src0, const struct ggml_tensor * src1, const struct ggml_tensor * dst);
|
||||
bool htp_matmul_add_can_collapse(const struct ggml_tensor * src0, const struct ggml_tensor * src1, const struct ggml_tensor * src2, const struct ggml_tensor * dst);
|
||||
ggml_tensor htp_tensor_collapse_rows(const struct ggml_tensor * t);
|
||||
|
||||
template <typename... Args>
|
||||
@@ -45,6 +46,18 @@ static inline bool htp_kparams_check(Args &&... args) {
|
||||
return SolveFn(std::forward<Args>(args)..., &kparams);
|
||||
}
|
||||
|
||||
struct htp_opnode;
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
struct htp_opnode & node
|
||||
);
|
||||
|
||||
void htp_opnode_collapse(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
struct htp_opnode & node
|
||||
);
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
|
||||
@@ -3775,16 +3775,6 @@ static int hmx_mm_op_matmul(struct htp_ops_context * octx, const struct htp_mm_k
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static inline void htp_mm_tensor_collapse_rows(struct htp_tensor * c, const struct htp_tensor * t, uint32_t stride) {
|
||||
*c = *t;
|
||||
c->ne[1] = t->ne[1] * t->ne[2] * t->ne[3];
|
||||
c->ne[2] = 1;
|
||||
c->ne[3] = 1;
|
||||
c->nb[1] = stride;
|
||||
c->nb[2] = c->nb[1] * c->ne[1];
|
||||
c->nb[3] = c->nb[2];
|
||||
}
|
||||
|
||||
static int op_matmul_impl(struct htp_ops_context * octx) {
|
||||
const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params;
|
||||
|
||||
@@ -3801,36 +3791,6 @@ static int op_matmul_impl(struct htp_ops_context * octx) {
|
||||
}
|
||||
|
||||
int op_matmul(struct htp_ops_context * octx) {
|
||||
const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params;
|
||||
|
||||
if (kparams->collapse) {
|
||||
const struct htp_tensor * act = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint32_t s_act = (act->ne[1] > 1) ? act->nb[1] : ((act->ne[2] > 1) ? act->nb[2] : act->nb[3]);
|
||||
const uint32_t sd = (dst->ne[1] > 1) ? dst->nb[1] : ((dst->ne[2] > 1) ? dst->nb[2] : dst->nb[3]);
|
||||
struct htp_tensor act_collapsed, dst_collapsed;
|
||||
htp_mm_tensor_collapse_rows(&act_collapsed, act, s_act);
|
||||
htp_mm_tensor_collapse_rows(&dst_collapsed, dst, sd);
|
||||
octx->src[1] = &act_collapsed;
|
||||
octx->dst = &dst_collapsed;
|
||||
|
||||
struct htp_tensor src2_collapsed;
|
||||
const struct htp_tensor * src2 = octx->src[2];
|
||||
if (src2 && (src2->ne[1] * src2->ne[2] * src2->ne[3] > 1)) {
|
||||
const uint32_t s2 = (src2->ne[1] > 1) ? src2->nb[1] : ((src2->ne[2] > 1) ? src2->nb[2] : src2->nb[3]);
|
||||
htp_mm_tensor_collapse_rows(&src2_collapsed, src2, s2);
|
||||
octx->src[2] = &src2_collapsed;
|
||||
}
|
||||
|
||||
const int status = op_matmul_impl(octx);
|
||||
octx->src[1] = act;
|
||||
octx->dst = dst;
|
||||
if (src2) {
|
||||
octx->src[2] = src2;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
return op_matmul_impl(octx);
|
||||
}
|
||||
|
||||
@@ -4562,35 +4522,5 @@ static int op_matmul_nx_impl(struct htp_ops_context * octx) {
|
||||
}
|
||||
|
||||
int op_matmul_nx(struct htp_ops_context * octx) {
|
||||
const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params;
|
||||
|
||||
if (kparams->collapse) {
|
||||
const uint32_t n_weights = kparams->n_weights;
|
||||
const struct htp_tensor * act = octx->src[n_weights];
|
||||
const uint32_t s1 = (act->ne[1] > 1) ? act->nb[1] : ((act->ne[2] > 1) ? act->nb[2] : act->nb[3]);
|
||||
struct htp_tensor act_collapsed;
|
||||
struct htp_tensor dsts_collapsed[HTP_OP_MAX_OUTPUTS];
|
||||
const struct htp_tensor * orig_dsts[HTP_OP_MAX_OUTPUTS];
|
||||
|
||||
htp_mm_tensor_collapse_rows(&act_collapsed, act, s1);
|
||||
octx->src[n_weights] = &act_collapsed;
|
||||
|
||||
for (uint32_t p = 0; p < n_weights; p++) {
|
||||
orig_dsts[p] = octx->dsts[p];
|
||||
const struct htp_tensor * d = octx->dsts[p];
|
||||
const uint32_t sd = (d->ne[1] > 1) ? d->nb[1] : ((d->ne[2] > 1) ? d->nb[2] : d->nb[3]);
|
||||
htp_mm_tensor_collapse_rows(&dsts_collapsed[p], d, sd);
|
||||
octx->dsts[p] = &dsts_collapsed[p];
|
||||
}
|
||||
|
||||
const int status = op_matmul_nx_impl(octx);
|
||||
|
||||
octx->src[n_weights] = act;
|
||||
for (uint32_t p = 0; p < n_weights; p++) {
|
||||
octx->dsts[p] = orig_dsts[p];
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
return op_matmul_nx_impl(octx);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ enum htp_mm_kernel_type {
|
||||
struct htp_mm_kernel_params {
|
||||
uint8_t kernel_type; // enum htp_mm_kernel_type
|
||||
uint8_t pipeline; // 1 = pipelined execution, 0 = standard
|
||||
uint8_t collapse; // 1 = collapse outer dims into 2D, 0 = standard
|
||||
uint8_t unused; // reserved
|
||||
uint8_t n_hmx; // 1 = use HMX, 0 = use HVX
|
||||
|
||||
uint8_t n_threads; // Number of threads to spawn
|
||||
|
||||
@@ -1984,29 +1984,8 @@ static int execute_op_unary(struct htp_ops_context * octx) {
|
||||
int op_unary(struct htp_ops_context * octx) {
|
||||
switch (octx->src[0]->type) {
|
||||
case HTP_TYPE_F32:
|
||||
case HTP_TYPE_F16: {
|
||||
if (!htp_unary_is_elementwise(octx->op)) {
|
||||
return execute_op_unary(octx);
|
||||
}
|
||||
|
||||
const struct htp_unary_kernel_params * kparams = (const struct htp_unary_kernel_params *) octx->kernel_params;
|
||||
|
||||
struct htp_tensor src0_c = *octx->src[0];
|
||||
struct htp_tensor dst_c = *octx->dst;
|
||||
memcpy(src0_c.ne, kparams->ne, sizeof(src0_c.ne));
|
||||
memcpy(dst_c.ne, kparams->ne, sizeof(dst_c.ne));
|
||||
memcpy(src0_c.nb, kparams->src0_nb, sizeof(src0_c.nb));
|
||||
memcpy(dst_c.nb, kparams->dst_nb, sizeof(dst_c.nb));
|
||||
|
||||
const struct htp_tensor * orig_src0 = octx->src[0];
|
||||
const struct htp_tensor * orig_dst = octx->dst;
|
||||
octx->src[0] = &src0_c;
|
||||
octx->dst = &dst_c;
|
||||
const int status = execute_op_unary(octx);
|
||||
octx->src[0] = orig_src0;
|
||||
octx->dst = orig_dst;
|
||||
return status;
|
||||
}
|
||||
case HTP_TYPE_F16:
|
||||
return execute_op_unary(octx);
|
||||
|
||||
default:
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
|
||||
@@ -24,11 +24,6 @@ struct htp_unary_kernel_params {
|
||||
struct fastdiv_values div_ne02;
|
||||
struct fastdiv_values div_ne012;
|
||||
struct fastdiv_values div_tpr;
|
||||
|
||||
// collapsed shape of elementwise ops
|
||||
uint32_t ne[HTP_OP_MAX_DIMS];
|
||||
uint32_t src0_nb[HTP_OP_MAX_DIMS];
|
||||
uint32_t dst_nb[HTP_OP_MAX_DIMS];
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@@ -72,6 +67,9 @@ static inline bool htp_op_is_unary(uint32_t opcode) {
|
||||
}
|
||||
|
||||
static inline bool htp_unary_is_elementwise(uint32_t opcode) {
|
||||
if (!htp_op_is_unary(opcode)) {
|
||||
return false;
|
||||
}
|
||||
switch (opcode) {
|
||||
case HTP_OP_NORM:
|
||||
case HTP_OP_RMS_NORM:
|
||||
|
||||
@@ -66,6 +66,8 @@ set(GGML_OPENCL_KERNELS
|
||||
moe_add_id_glu
|
||||
argmax
|
||||
argsort
|
||||
top_k
|
||||
lightning_indexer
|
||||
tri
|
||||
fill
|
||||
clamp
|
||||
@@ -99,6 +101,12 @@ set(GGML_OPENCL_KERNELS
|
||||
mul_mv_q4_0_f32_1d_16x_flat
|
||||
mul_mv_q4_1_f32
|
||||
mul_mv_q4_1_f32_flat
|
||||
gemm_noshuffle_q2_k_q8_1_dp4a
|
||||
gemv_noshuffle_q2_k_f32
|
||||
gemm_noshuffle_q2_k_f32
|
||||
mul_mv_q2_k_f32
|
||||
mul_mv_q2_k_f32_flat
|
||||
mul_mm_q2_k_f32_l4_lm
|
||||
mul_mv_q4_k_f32
|
||||
mul_mv_q4_k_f32_flat
|
||||
mul_mv_q5_0_f32
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2661,3 +2661,115 @@ kernel void kernel_moe_expand_scale_q5_K(
|
||||
dst_min[((long)e*ne01 + row)*nblk32 + sub] = (half)(dm * (float)mn);
|
||||
}
|
||||
}
|
||||
|
||||
struct block_q2_K {
|
||||
uint8_t scales[QK_K/16]; // low nibble scale, high nibble min, one per 16 weights
|
||||
uint8_t qs[QK_K/4]; // quants, 2 bits each
|
||||
half d; // super-block scale for the quantized scales
|
||||
half dmin; // super-block scale for the quantized mins
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_q2_K(
|
||||
global struct block_q2_K * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_sc,
|
||||
global half * dst_dm,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q2_K * b = src0 + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst_qs[64*i + j] = b->qs[j];
|
||||
}
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
dst_sc[16*i + j] = b->scales[j];
|
||||
}
|
||||
dst_dm[2*i + 0] = b->d;
|
||||
dst_dm[2*i + 1] = b->dmin;
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q2_K(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_sc,
|
||||
global half * src_dm,
|
||||
global struct block_q2_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q2_K * b = dst + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
b->qs[j] = src_qs[64*i + j];
|
||||
}
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
b->scales[j] = src_sc[16*i + j];
|
||||
}
|
||||
b->d = src_dm[2*i + 0];
|
||||
b->dmin = src_dm[2*i + 1];
|
||||
}
|
||||
|
||||
kernel void kernel_convert_block_q2_k_noshuffle(
|
||||
global struct block_q2_K * src0,
|
||||
global uchar * dst_qs, // QK_K/4 per block
|
||||
global uchar * dst_sc, // QK_K/16 per block
|
||||
global half * dst_dm, // 2 per block (d, dmin)
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q2_K * b = (global struct block_q2_K *) src0 + i;
|
||||
|
||||
dst_dm[2*i + 0] = b->d;
|
||||
dst_dm[2*i + 1] = b->dmin;
|
||||
|
||||
for (int j = 0; j < QK_K/16; ++j) { dst_sc[(QK_K/16) * i + j] = b->scales[j]; }
|
||||
|
||||
for (int g = 0; g < QK_K/4; ++g) {
|
||||
uint pk = 0;
|
||||
for (int t = 0; t < 4; ++t) {
|
||||
const int e = 4*g + t;
|
||||
const int sb = e >> 5;
|
||||
const int ee = e & 31;
|
||||
pk |= (uint)(((b->qs[32*(sb >> 2) + ee] >> (2*(sb & 3))) & 3) << (2*t));
|
||||
}
|
||||
dst_qs[(QK_K/4) * i + g] = (uchar)pk;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q2_k_noshuffle(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_sc,
|
||||
global half * src_dm,
|
||||
global struct block_q2_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q2_K * b = (global struct block_q2_K *) dst + i;
|
||||
|
||||
b->d = src_dm[2*i + 0];
|
||||
b->dmin = src_dm[2*i + 1];
|
||||
|
||||
for (int j = 0; j < QK_K/16; ++j) { b->scales[j] = src_sc[(QK_K/16) * i + j]; }
|
||||
|
||||
for (int n2 = 0; n2 < 2; ++n2) {
|
||||
for (int ee = 0; ee < 32; ++ee) {
|
||||
uint v = 0;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
const int sb = 4*n2 + j;
|
||||
const int e = 32*sb + ee;
|
||||
v |= ((uint)(src_qs[(QK_K/4) * i + (e >> 2)] >> (2*(e & 3))) & 3u) << (2*j);
|
||||
}
|
||||
b->qs[32*n2 + ee] = (uchar)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_q2_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_sc,
|
||||
global const half * src0_dm,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const half8 dm = vload8(0, src0_dm + 2 * (row + (i / 256) * m));
|
||||
const uchar4 sc = vload4(0, src0_sc + row + (i / 16) * m);
|
||||
const half4 scale = convert_half4(convert_float4(dm.s0246) * convert_float4(sc & (uchar4)(15)));
|
||||
const half4 mval = convert_half4(convert_float4(dm.s1357) * convert_float4(sc >> (uchar4)(4)));
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uchar4 bits = vload4(0, src0_qs + row + (ki / 4) * m);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const half4 w = convert_half4((bits >> (uchar4)(2 * j)) & (uchar4)(3)) * scale - mval;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline uint q2_k_pack(uint pk) {
|
||||
return ( (pk ) & 3u)
|
||||
| (((pk >> 2) & 3u) << 8)
|
||||
| (((pk >> 4) & 3u) << 16)
|
||||
| (((pk >> 6) & 3u) << 24);
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
#define KQ_STAGE(p) vload4(0, (p))
|
||||
|
||||
inline int dot4_q8a_v(uint4 qw, uint4 a) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q2_k_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uchar * src0_sc,
|
||||
__global const half * src0_dm, // half pairs: d, dmin
|
||||
__global const uint * src1_qa, // q8_1 activations int8 (as uint, 4/elem) [N, K]
|
||||
__global const half * src1_da, // q8_1 per-block scale [N, K/32]
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local float sh_s [TILESIZE_N][2]; // per-16 activation sums, in qa units
|
||||
__local half sh_d [TILESIZE_N];
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
const uint ib = sub >> 3;
|
||||
|
||||
const half2 dm = vload2(rrow + ib * (uint)m, src0_dm);
|
||||
const float dv = (float)dm.s0;
|
||||
const float dmv = (float)dm.s1;
|
||||
|
||||
const uint scb = rrow + (sub << 1) * (uint)m;
|
||||
const uint sc0 = (uint)src0_sc[scb + 0u * (uint)m];
|
||||
const uint sc1 = (uint)src0_sc[scb + 1u * (uint)m];
|
||||
const float dl0 = dv * (float)( sc0 & 0xFu);
|
||||
const float ml0 = dmv * (float)( sc0 >> 4);
|
||||
const float dl1 = dv * (float)( sc1 & 0xFu);
|
||||
const float ml1 = dmv * (float)( sc1 >> 4);
|
||||
|
||||
const uint qsb = rrow + (step >> 2) * (uint)m;
|
||||
uint4 qlo, qhi;
|
||||
qlo.s0 = q2_k_pack((uint)src0_qs[qsb + 0u * (uint)m]);
|
||||
qlo.s1 = q2_k_pack((uint)src0_qs[qsb + 1u * (uint)m]);
|
||||
qlo.s2 = q2_k_pack((uint)src0_qs[qsb + 2u * (uint)m]);
|
||||
qlo.s3 = q2_k_pack((uint)src0_qs[qsb + 3u * (uint)m]);
|
||||
qhi.s0 = q2_k_pack((uint)src0_qs[qsb + 4u * (uint)m]);
|
||||
qhi.s1 = q2_k_pack((uint)src0_qs[qsb + 5u * (uint)m]);
|
||||
qhi.s2 = q2_k_pack((uint)src0_qs[qsb + 6u * (uint)m]);
|
||||
qhi.s3 = q2_k_pack((uint)src0_qs[qsb + 7u * (uint)m]);
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2u; idx += 64u) {
|
||||
const uint t = idx >> 1;
|
||||
const uint h = idx & 1u;
|
||||
const uint c = col_base + t;
|
||||
const bool ok = c < (uint)n_no_padding;
|
||||
const uint4 w = ok ? KQ_STAGE(src1_qa + c * k_u + (step >> 2) + (h << 2))
|
||||
: (uint4)(0u);
|
||||
sh_qa4[t][h] = w;
|
||||
int s = 0;
|
||||
s = dot_acc_sat_4x8packed_ss_int(w.x, 0x01010101u, s);
|
||||
s = dot_acc_sat_4x8packed_ss_int(w.y, 0x01010101u, s);
|
||||
s = dot_acc_sat_4x8packed_ss_int(w.z, 0x01010101u, s);
|
||||
s = dot_acc_sat_4x8packed_ss_int(w.w, 0x01010101u, s);
|
||||
sh_s[t][h] = (float)s;
|
||||
if (h == 0u) {
|
||||
sh_d[t] = ok ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#define Q2_K_COL(b) (dl0 * (float)dot4_q8a_v(qlo, (uint4)(sh_qa4[b][0])) - ml0 * sh_s[b][0] + \
|
||||
dl1 * (float)dot4_q8a_v(qhi, (uint4)(sh_qa4[b][1])) - ml1 * sh_s[b][1])
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
rf.s0 = Q2_K_COL(b+0); rf.s1 = Q2_K_COL(b+1);
|
||||
rf.s2 = Q2_K_COL(b+2); rf.s3 = Q2_K_COL(b+3);
|
||||
acc[g] += LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef Q2_K_COL
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef Q2_K_MV_NSG
|
||||
#define Q2_K_MV_NSG 8
|
||||
#endif
|
||||
|
||||
#ifndef Q2_K_MV_R
|
||||
#define Q2_K_MV_R 4
|
||||
#endif
|
||||
|
||||
inline float4 q2_k_vals(uint pk) {
|
||||
return (float4)((float)( pk & 3u), (float)((pk >> 2) & 3u),
|
||||
(float)((pk >> 4) & 3u), (float)((pk >> 6) & 3u));
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_q2_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_sc,
|
||||
global const half * src0_dm,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00, // K
|
||||
int ne01, // M
|
||||
int ne10, // activation row stride, == K
|
||||
int ne0 // dst row stride
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
const uint mr = m / Q2_K_MV_R; // row groups
|
||||
const uint j = get_group_id(0) * 64u + lid; // row group this lane owns
|
||||
const uint row = j * Q2_K_MV_R;
|
||||
|
||||
#if Q2_K_MV_R == 4
|
||||
float sumf0 = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
float sumf2 = 0.f;
|
||||
float sumf3 = 0.f;
|
||||
|
||||
if (j < mr) {
|
||||
global const uint * qsu = (global const uint *)src0_qs;
|
||||
global const uint * scu = (global const uint *)src0_sc;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q2_K_MV_NSG) {
|
||||
const half4 dma = vload4(2u*(j + ib*mr) + 0u, src0_dm); // d0, dmin0, d1, dmin1
|
||||
const half4 dmb = vload4(2u*(j + ib*mr) + 1u, src0_dm); // d2, dmin2, d3, dmin3
|
||||
|
||||
float ad0 = 0.f, am0 = 0.f, ad1 = 0.f, am1 = 0.f;
|
||||
float ad2 = 0.f, am2 = 0.f, ad3 = 0.f, am3 = 0.f;
|
||||
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mr;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) {
|
||||
const uint scv = scu[j + (2u * (ib * 8u + sb) + h) * mr];
|
||||
|
||||
float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
|
||||
float4 as = (float4)(0.f);
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = qsu[qsb + gg * mr]; // four rows, one load
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
as += yv;
|
||||
a0 += dot(yv, q2_k_vals( qsv & 0xFFu));
|
||||
a1 += dot(yv, q2_k_vals((qsv >> 8) & 0xFFu));
|
||||
a2 += dot(yv, q2_k_vals((qsv >> 16) & 0xFFu));
|
||||
a3 += dot(yv, q2_k_vals((qsv >> 24) & 0xFFu));
|
||||
}
|
||||
const float asum = as.s0 + as.s1 + as.s2 + as.s3;
|
||||
|
||||
const uint s0 = scv & 0xFFu;
|
||||
const uint s1 = (scv >> 8) & 0xFFu;
|
||||
const uint s2 = (scv >> 16) & 0xFFu;
|
||||
const uint s3 = (scv >> 24) & 0xFFu;
|
||||
ad0 += (float)(s0 & 0xFu) * a0; am0 += (float)(s0 >> 4) * asum;
|
||||
ad1 += (float)(s1 & 0xFu) * a1; am1 += (float)(s1 >> 4) * asum;
|
||||
ad2 += (float)(s2 & 0xFu) * a2; am2 += (float)(s2 >> 4) * asum;
|
||||
ad3 += (float)(s3 & 0xFu) * a3; am3 += (float)(s3 >> 4) * asum;
|
||||
}
|
||||
}
|
||||
sumf0 += (float)dma.s0 * ad0 - (float)dma.s1 * am0;
|
||||
sumf1 += (float)dma.s2 * ad1 - (float)dma.s3 * am1;
|
||||
sumf2 += (float)dmb.s0 * ad2 - (float)dmb.s1 * am2;
|
||||
sumf3 += (float)dmb.s2 * ad3 - (float)dmb.s3 * am3;
|
||||
}
|
||||
}
|
||||
|
||||
#if Q2_K_MV_NSG > 1
|
||||
__local float4 part[Q2_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float4)(sumf0, sumf1, sumf2, sumf3);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q2_K_MV_NSG; ++s) {
|
||||
const float4 p = part[s][lid];
|
||||
sumf0 += p.s0;
|
||||
sumf1 += p.s1;
|
||||
sumf2 += p.s2;
|
||||
sumf3 += p.s3;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mr) {
|
||||
global float * o = dst + (ulong)col * (uint)ne0 + row;
|
||||
o[0] = sumf0; o[1] = sumf1; o[2] = sumf2; o[3] = sumf3;
|
||||
}
|
||||
|
||||
#elif Q2_K_MV_R == 2
|
||||
float sumf0 = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
|
||||
if (j < mr) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
global const ushort * scu = (global const ushort *)src0_sc;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q2_K_MV_NSG) {
|
||||
const half4 dm = vload4(j + ib * mr, src0_dm); // d0, dmin0, d1, dmin1
|
||||
|
||||
float ad0 = 0.f, am0 = 0.f, ad1 = 0.f, am1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mr;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) {
|
||||
const uint scv = (uint)scu[j + (2u * (ib * 8u + sb) + h) * mr];
|
||||
const uint s0 = scv & 0xFFu;
|
||||
const uint s1 = scv >> 8;
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
float4 as = (float4)(0.f);
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = (uint)qsu[qsb + gg * mr];
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
as += yv;
|
||||
a0 += dot(yv, q2_k_vals( qsv & 0xFFu));
|
||||
a1 += dot(yv, q2_k_vals((qsv >> 8) & 0xFFu));
|
||||
}
|
||||
const float asum = as.s0 + as.s1 + as.s2 + as.s3;
|
||||
ad0 += (float)(s0 & 0xFu) * a0; am0 += (float)(s0 >> 4) * asum;
|
||||
ad1 += (float)(s1 & 0xFu) * a1; am1 += (float)(s1 >> 4) * asum;
|
||||
}
|
||||
}
|
||||
sumf0 += (float)dm.s0 * ad0 - (float)dm.s1 * am0;
|
||||
sumf1 += (float)dm.s2 * ad1 - (float)dm.s3 * am1;
|
||||
}
|
||||
}
|
||||
|
||||
#if Q2_K_MV_NSG > 1
|
||||
__local float2 part[Q2_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf0, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q2_K_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf0 += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mr) {
|
||||
vstore2((float2)(sumf0, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
|
||||
#else
|
||||
float sumf0 = 0.f;
|
||||
|
||||
if (j < mr) {
|
||||
for (uint ib = sgi; ib < nsb; ib += Q2_K_MV_NSG) {
|
||||
const half2 dm = vload2(row + ib * m, src0_dm);
|
||||
|
||||
float ad = 0.f, am = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = row + grp * m;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) {
|
||||
const uint sc = (uint)src0_sc[row + (2u * (ib * 8u + sb) + h) * m];
|
||||
|
||||
float a = 0.f;
|
||||
float4 as = (float4)(0.f);
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
as += yv;
|
||||
const uint pkv = (uint)src0_qs[qsb + gg * m];
|
||||
a += dot(yv, q2_k_vals(pkv));
|
||||
}
|
||||
const float asum = as.s0 + as.s1 + as.s2 + as.s3;
|
||||
ad += (float)(sc & 0xFu) * a; am += (float)(sc >> 4) * asum;
|
||||
}
|
||||
}
|
||||
sumf0 += (float)dm.s0 * ad - (float)dm.s1 * am;
|
||||
}
|
||||
}
|
||||
|
||||
#if Q2_K_MV_NSG > 1
|
||||
__local float part[Q2_K_MV_NSG][64];
|
||||
part[sgi][lid] = sumf0;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q2_K_MV_NSG; ++s) {
|
||||
sumf0 += part[s][lid];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mr) {
|
||||
dst[(ulong)col * (uint)ne0 + row] = sumf0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#ifdef cl_khr_subgroup_shuffle
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
|
||||
#endif
|
||||
|
||||
kernel void kernel_lightning_indexer(
|
||||
global const char * q,
|
||||
ulong offset_q,
|
||||
global const char * k,
|
||||
ulong offset_k,
|
||||
global const char * w,
|
||||
ulong offset_w,
|
||||
global const char * mask,
|
||||
ulong offset_m,
|
||||
global float * dst,
|
||||
ulong offset_d,
|
||||
int head_size,
|
||||
int n_heads,
|
||||
int n_tokens,
|
||||
int n_keys,
|
||||
int n_masks,
|
||||
int type_k,
|
||||
ulong nbq1,
|
||||
ulong nbq2,
|
||||
ulong nbq3,
|
||||
ulong nbk2,
|
||||
ulong nbk3,
|
||||
ulong nbw1,
|
||||
ulong nbw3,
|
||||
ulong nbm1,
|
||||
ulong nbm3,
|
||||
int use_shuffle
|
||||
) {
|
||||
const int tid = get_local_id(0);
|
||||
const int lane = tid & (use_shuffle ? 3 : 15);
|
||||
const int key = use_shuffle ? get_group_id(0)*16 + tid/4 : get_group_id(0)*4 + tid/16;
|
||||
const int token = get_group_id(1);
|
||||
const int stream = get_group_id(2);
|
||||
|
||||
q += offset_q + token*nbq2 + stream*nbq3;
|
||||
k += offset_k + (ulong) key*nbk2 + stream*nbk3;
|
||||
w += offset_w + token*nbw1 + stream*nbw3;
|
||||
mask += offset_m + token*nbm1 + (stream % n_masks)*nbm3;
|
||||
dst = (global float *) ((global char *) dst + offset_d);
|
||||
|
||||
#ifdef cl_khr_subgroup_shuffle
|
||||
if (use_shuffle) {
|
||||
// Each group of four lanes keeps one key for all query heads.
|
||||
float4 cached[8];
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
const int i = 4*(lane + j*4);
|
||||
cached[j] = key < n_keys ? (type_k == 0 ? vload4(0, (global const float *) k + i) : convert_float4(vload4(0, (global const half *) k + i))) : (float4) (0.0f);
|
||||
}
|
||||
|
||||
float score = 0.0f;
|
||||
for (int h = 0; h < n_heads; ++h) {
|
||||
global const float * q_head = (global const float *) (q + h*nbq1);
|
||||
float4 sum = (float4) (0.0f);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
sum = mad(vload4(0, q_head + 4*(lane + j*4)), cached[j], sum);
|
||||
}
|
||||
float value = sum.s0 + sum.s1 + sum.s2 + sum.s3;
|
||||
value += sub_group_shuffle_xor(value, 2);
|
||||
value += sub_group_shuffle_xor(value, 1);
|
||||
if (lane == 0) {
|
||||
score = mad(fmax(value, 0.0f), ((global const float *) w)[h], score);
|
||||
}
|
||||
}
|
||||
if (lane == 0 && key < n_keys) {
|
||||
dst[((ulong) stream*n_tokens + token)*n_keys + key] = score + (float) ((global const half *) mask)[key];
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
local float sums[64];
|
||||
float score = 0.0f;
|
||||
for (int h = 0; h < n_heads; ++h) {
|
||||
global const float * q_head = (global const float *) (q + h*nbq1);
|
||||
float sum = 0.0f;
|
||||
if (key < n_keys) {
|
||||
for (int i = lane; i < head_size; i += 16) {
|
||||
float kv;
|
||||
if (type_k == 0) {
|
||||
kv = ((global const float *) k)[i];
|
||||
} else {
|
||||
kv = (float) ((global const half *) k)[i];
|
||||
}
|
||||
sum = mad(q_head[i], kv, sum);
|
||||
}
|
||||
}
|
||||
sums[tid] = sum;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
for (int stride = 8; stride > 0; stride /= 2) {
|
||||
if (lane < stride) {
|
||||
sums[tid] += sums[tid + stride];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
if (lane == 0) {
|
||||
score = mad(fmax(sums[tid], 0.0f), ((global const float *) w)[h], score);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (lane == 0 && key < n_keys) {
|
||||
dst[((ulong) stream*n_tokens + token)*n_keys + key] = score + (float) ((global const half *) mask)[key];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_q2_K {
|
||||
uchar scales[QK_K/16];
|
||||
uchar qs[QK_K/4];
|
||||
half d;
|
||||
half dmin;
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#ifndef BK
|
||||
#define BK 32
|
||||
#endif
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#else
|
||||
#define TM 4
|
||||
#endif
|
||||
#define TN 8
|
||||
|
||||
#ifndef LM_HALF
|
||||
#define LM_HALF 0
|
||||
#endif
|
||||
#if LM_HALF
|
||||
typedef half lm_st;
|
||||
#define LM_LD4(p, i) convert_float4(vload4((i), (p)))
|
||||
#else
|
||||
typedef float lm_st;
|
||||
#define LM_LD4(p, i) vload4((i), (p))
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_q2_k_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_dm,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local lm_st buf_a[BM * BK];
|
||||
local lm_st buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one q2_K super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float4 sums4[(TM/4) * TN];
|
||||
float4 cache_a4[TM/4];
|
||||
|
||||
for (int i = 0; i < (TM/4) * TN; i++) {
|
||||
sums4[i] = (float4)(0.0f);
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_q2_K);
|
||||
int e = idx % QK_K;
|
||||
|
||||
// dequantize_row_q2_K walks 2 halves of 128, each as 4 shifts of
|
||||
// 2 groups of 16. Recover that position from the element index.
|
||||
int n = e >> 7;
|
||||
int rem = e & 127;
|
||||
int j = rem >> 5;
|
||||
int s = (rem >> 4) & 1;
|
||||
int l16 = rem & 15;
|
||||
int isc = 8*n + 2*j + s;
|
||||
int shift = 2*j;
|
||||
|
||||
uchar sc = src0_sc[16*ib + isc];
|
||||
float d = (float)src0_dm[2*ib] * (float)(sc & 0xF);
|
||||
float m = -(float)src0_dm[2*ib + 1] * (float)(sc >> 4);
|
||||
|
||||
uchar4 q = vload4(0, src0_qs + 64*ib + 32*n + 16*s + l16);
|
||||
float4 v1 = convert_float4((uchar4)((q.s0 >> shift) & 3,
|
||||
(q.s1 >> shift) & 3,
|
||||
(q.s2 >> shift) & 3,
|
||||
(q.s3 >> shift) & 3)) * d + m;
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
cache_a4[a] = LM_LD4(buf_a + (i) * BM + th_r * TM, a);
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
const float cache_b = buf_b[(i) * BN + th_c * TN + cc];
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
const int sums_idx = cc*(TM/4) + a;
|
||||
sums4[sums_idx] = mad(cache_a4[a], (float4)cache_b, sums4[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
const float4 v = sums4[cc * (TM/4) + a];
|
||||
const float vs[4] = { v.s0, v.s1, v.s2, v.s3 };
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (dr + 4*a + k < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + 4*a + k] = vs[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// block_q2_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q + b
|
||||
typedef struct {
|
||||
uchar scales[QK_K/16]; // scales and mins, quantized with 4 bits
|
||||
uchar qs[QK_K/4]; // 2-bit quants
|
||||
half d; // super-block scale for quantized scales
|
||||
half dmin; // super-block scale for quantized mins
|
||||
} block_q2_K;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 4 // number of rows each SIMD group works on
|
||||
#define N_SIMDGROUP 1 // number of SIMD groups in a thread group
|
||||
#define N_SIMDWIDTH 16 // SIMD group size
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// number of (super) blocks each subgroup processes
|
||||
// 8 threads cover one super block, so a wave covers N_SIMDWIDTH/8 of them
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_q2_K_f32(
|
||||
global char * src0,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src0 = src0 + offset0;
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int ix = get_sub_group_local_id()/8; // super block index
|
||||
int it = get_sub_group_local_id()%8; // block index (inside super block)
|
||||
int iq = it/4; // 0 or 1 - first or second half of the super block
|
||||
int ir = it%4; // 0...3 - block index in the half super block
|
||||
int is = (8*ir)/16; // 0 or 1 - scale pair
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03;
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global block_q2_K * x = (global block_q2_K *) (src0 + offset_src0);
|
||||
global float * y = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
float sumf[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
global float * y4 = y + ix * QK_K + 128 * iq + 8 * ir;
|
||||
|
||||
for (int ib = ix; ib < nb; ib += BLOCK_STRIDE) {
|
||||
float4 sumy = {0.f, 0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
yl[i+ 0] = y4[i+ 0]; sumy.s0 += yl[i+ 0];
|
||||
yl[i+ 8] = y4[i+32]; sumy.s1 += yl[i+ 8];
|
||||
yl[i+16] = y4[i+64]; sumy.s2 += yl[i+16];
|
||||
yl[i+24] = y4[i+96]; sumy.s3 += yl[i+24];
|
||||
}
|
||||
|
||||
global uchar * sc = (global uchar *)x[ib].scales + 8*iq + is;
|
||||
global ushort * qs = (global ushort *)x[ib].qs + 16 * iq + 4 * ir;
|
||||
global half * dh = &x[ib].d;
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; row++) {
|
||||
float4 acc1 = {0.f, 0.f, 0.f, 0.f};
|
||||
float4 acc2 = {0.f, 0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
acc1.s0 += yl[i+ 0] * (qs[i/2] & 0x0003);
|
||||
acc2.s0 += yl[i+ 1] * (qs[i/2] & 0x0300);
|
||||
acc1.s1 += yl[i+ 8] * (qs[i/2] & 0x000c);
|
||||
acc2.s1 += yl[i+ 9] * (qs[i/2] & 0x0c00);
|
||||
acc1.s2 += yl[i+16] * (qs[i/2] & 0x0030);
|
||||
acc2.s2 += yl[i+17] * (qs[i/2] & 0x3000);
|
||||
acc1.s3 += yl[i+24] * (qs[i/2] & 0x00c0);
|
||||
acc2.s3 += yl[i+25] * (qs[i/2] & 0xc000);
|
||||
}
|
||||
|
||||
float dall = dh[0];
|
||||
float dmin = dh[1] * 1.f/16.f;
|
||||
sumf[row] += dall * ((acc1.s0 + 1.f/256.f * acc2.s0) * (sc[0] & 0xF) * 1.f/ 1.f +
|
||||
(acc1.s1 + 1.f/256.f * acc2.s1) * (sc[2] & 0xF) * 1.f/ 4.f +
|
||||
(acc1.s2 + 1.f/256.f * acc2.s2) * (sc[4] & 0xF) * 1.f/16.f +
|
||||
(acc1.s3 + 1.f/256.f * acc2.s3) * (sc[6] & 0xF) * 1.f/64.f) -
|
||||
dmin * (sumy.s0 * (sc[0] & 0xF0) + sumy.s1 * (sc[2] & 0xF0) +
|
||||
sumy.s2 * (sc[4] & 0xF0) + sumy.s3 * (sc[6] & 0xF0));
|
||||
|
||||
qs += nb01/2;
|
||||
sc += nb01;
|
||||
dh += nb01/2;
|
||||
}
|
||||
|
||||
y4 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add(sumf[row]);
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// struct block_q2_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q + b
|
||||
struct block_q2_K {
|
||||
uchar scales[QK_K/16]; // scales and mins, quantized with 4 bits
|
||||
uchar qs[QK_K/4]; // 2-bit quants
|
||||
half d; // super-block scale for quantized scales
|
||||
half dmin; // super-block scale for quantized mins
|
||||
};
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 4 // number of rows each SIMD group works on
|
||||
#define N_SIMDGROUP 1 // number of SIMD groups in a thread group
|
||||
#define N_SIMDWIDTH 16 // SIMD group size
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// number of (super) blocks each subgroup processes
|
||||
// 8 threads cover one super block, so a wave covers N_SIMDWIDTH/8 of them
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_q2_K_f32_flat(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_dm,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int ix = get_sub_group_local_id()/8; // super block index
|
||||
int it = get_sub_group_local_id()%8; // block index (inside super block)
|
||||
int iq = it/4; // 0 or 1 - first or second half of the super block
|
||||
int ir = it%4; // 0...3 - block index in the half super block
|
||||
int is = (8*ir)/16; // 0 or 1 - scale pair
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = (offset0 + first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03)/sizeof(struct block_q2_K);
|
||||
ulong row_stride = nb01/sizeof(struct block_q2_K);
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global float * y = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
float sumf[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
global float * y4 = y + ix * QK_K + 128 * iq + 8 * ir;
|
||||
|
||||
for (int ib = ix; ib < nb; ib += BLOCK_STRIDE) {
|
||||
float4 sumy = {0.f, 0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
yl[i+ 0] = y4[i+ 0]; sumy.s0 += yl[i+ 0];
|
||||
yl[i+ 8] = y4[i+32]; sumy.s1 += yl[i+ 8];
|
||||
yl[i+16] = y4[i+64]; sumy.s2 += yl[i+16];
|
||||
yl[i+24] = y4[i+96]; sumy.s3 += yl[i+24];
|
||||
}
|
||||
|
||||
global uchar * sc = (src0_sc + 16*(offset_src0 + ib)) + 8*iq + is;
|
||||
global ushort * qs = (global ushort *)(src0_qs + 64*(offset_src0 + ib)) + 16 * iq + 4 * ir;
|
||||
global half * dh = src0_dm + 2*(offset_src0 + ib);
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; row++) {
|
||||
float4 acc1 = {0.f, 0.f, 0.f, 0.f};
|
||||
float4 acc2 = {0.f, 0.f, 0.f, 0.f};
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
acc1.s0 += yl[i+ 0] * (qs[i/2] & 0x0003);
|
||||
acc2.s0 += yl[i+ 1] * (qs[i/2] & 0x0300);
|
||||
acc1.s1 += yl[i+ 8] * (qs[i/2] & 0x000c);
|
||||
acc2.s1 += yl[i+ 9] * (qs[i/2] & 0x0c00);
|
||||
acc1.s2 += yl[i+16] * (qs[i/2] & 0x0030);
|
||||
acc2.s2 += yl[i+17] * (qs[i/2] & 0x3000);
|
||||
acc1.s3 += yl[i+24] * (qs[i/2] & 0x00c0);
|
||||
acc2.s3 += yl[i+25] * (qs[i/2] & 0xc000);
|
||||
}
|
||||
|
||||
float dall = dh[0];
|
||||
float dmin = dh[1] * 1.f/16.f;
|
||||
sumf[row] += dall * ((acc1.s0 + 1.f/256.f * acc2.s0) * (sc[0] & 0xF) * 1.f/ 1.f +
|
||||
(acc1.s1 + 1.f/256.f * acc2.s1) * (sc[2] & 0xF) * 1.f/ 4.f +
|
||||
(acc1.s2 + 1.f/256.f * acc2.s2) * (sc[4] & 0xF) * 1.f/16.f +
|
||||
(acc1.s3 + 1.f/256.f * acc2.s3) * (sc[6] & 0xF) * 1.f/64.f) -
|
||||
dmin * (sumy.s0 * (sc[0] & 0xF0) + sumy.s1 * (sc[2] & 0xF0) +
|
||||
sumy.s2 * (sc[4] & 0xF0) + sumy.s3 * (sc[6] & 0xF0));
|
||||
|
||||
qs += row_stride*32;
|
||||
sc += row_stride*16;
|
||||
dh += row_stride*2;
|
||||
}
|
||||
|
||||
y4 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add(sumf[row]);
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#ifdef cl_khr_subgroups
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#endif
|
||||
|
||||
uint top_k_key(float value) {
|
||||
uint bits = as_uint(value);
|
||||
if ((bits & 0x7fffffffu) == 0) {
|
||||
bits = 0;
|
||||
}
|
||||
return bits & 0x80000000u ? ~bits : bits ^ 0x80000000u;
|
||||
}
|
||||
|
||||
uint4 top_k_keys(global const char * src, int i, int n, ulong nb0) {
|
||||
if (nb0 == sizeof(float) && i + 3 < n) {
|
||||
const float4 v = vload4(0, (global const float *) src + i);
|
||||
return (uint4) (top_k_key(v.s0), top_k_key(v.s1), top_k_key(v.s2), top_k_key(v.s3));
|
||||
}
|
||||
return (uint4) (
|
||||
i < n ? top_k_key(*(global const float *) (src + (ulong) i *nb0)) : 0,
|
||||
i + 1 < n ? top_k_key(*(global const float *) (src + (ulong) (i + 1)*nb0)) : 0,
|
||||
i + 2 < n ? top_k_key(*(global const float *) (src + (ulong) (i + 2)*nb0)) : 0,
|
||||
i + 3 < n ? top_k_key(*(global const float *) (src + (ulong) (i + 3)*nb0)) : 0);
|
||||
}
|
||||
|
||||
void top_k_store(global int * dst, global uint2 * candidates, global const char * src,
|
||||
ulong nb0, int indexed, int base, int i, uint pos) {
|
||||
const int index = indexed ? *(global const int *) (src + (ulong) i*nb0 + sizeof(float)) : base + i;
|
||||
if (candidates) {
|
||||
candidates[pos] = (uint2) (as_uint(*(global const float *) (src + (ulong) i*nb0)), index);
|
||||
} else {
|
||||
dst[pos] = index;
|
||||
}
|
||||
}
|
||||
|
||||
void top_k_select(
|
||||
global const char * src,
|
||||
global int * dst,
|
||||
global uint2 * candidates,
|
||||
ulong nb00,
|
||||
int ne00,
|
||||
int k,
|
||||
int base,
|
||||
int indexed,
|
||||
local uint * hist
|
||||
) {
|
||||
const int tid = get_local_id(0);
|
||||
const int nth = get_local_size(0);
|
||||
if (k == ne00) {
|
||||
for (int i = tid; i < ne00; i += nth) {
|
||||
top_k_store(dst, candidates, src, nb00, indexed, base, i, i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint prefix = 0;
|
||||
uint mask = 0;
|
||||
uint rank = k;
|
||||
for (int shift = 24; shift >= 0; shift -= 8) {
|
||||
for (int i = tid; i < 256; i += nth) {
|
||||
hist[i] = 0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
for (int i = 4*tid; i < ne00; i += 4*nth) {
|
||||
const uint4 v = top_k_keys(src, i, ne00, nb00);
|
||||
const uint keys[4] = {v.s0, v.s1, v.s2, v.s3};
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
const uint key = keys[j];
|
||||
if (i + j < ne00 && (key & mask) == prefix) {
|
||||
atomic_inc(hist + ((key >> shift) & 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid == 0) {
|
||||
uint above = 0;
|
||||
for (int b = 255; b >= 0; --b) {
|
||||
uint count = hist[b];
|
||||
if (above + count >= rank) {
|
||||
hist[256] = b;
|
||||
hist[257] = rank - above;
|
||||
hist[258] = count;
|
||||
break;
|
||||
}
|
||||
above += count;
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
prefix |= hist[256] << shift;
|
||||
rank = hist[257];
|
||||
const bool done = hist[258] == rank;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (done) {
|
||||
// The whole bucket is selected, so its low bits do not matter.
|
||||
break;
|
||||
}
|
||||
mask |= 255u << shift;
|
||||
}
|
||||
|
||||
uint greater = 0;
|
||||
uint equal = 0;
|
||||
for (int i = 4*tid; i < ne00; i += 4*nth) {
|
||||
const uint4 v = top_k_keys(src, i, ne00, nb00);
|
||||
const uint keys[4] = {v.s0, v.s1, v.s2, v.s3};
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
greater += i + j < ne00 && keys[j] > prefix;
|
||||
equal += i + j < ne00 && keys[j] == prefix;
|
||||
}
|
||||
}
|
||||
uint pos_greater;
|
||||
uint pos_equal;
|
||||
#ifdef cl_khr_subgroups
|
||||
const uint sg = get_sub_group_id();
|
||||
const uint nsg = get_num_sub_groups();
|
||||
const uint total_greater = sub_group_reduce_add(greater);
|
||||
const uint total_equal = sub_group_reduce_add(equal);
|
||||
pos_greater = sub_group_scan_exclusive_add(greater);
|
||||
pos_equal = sub_group_scan_exclusive_add(equal);
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
hist[sg] = total_greater;
|
||||
hist[nsg + sg] = total_equal;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
for (uint i = 0; i < nsg; ++i) {
|
||||
pos_equal += hist[i];
|
||||
if (i < sg) {
|
||||
pos_greater += hist[i];
|
||||
pos_equal += hist[nsg + i];
|
||||
}
|
||||
}
|
||||
#else
|
||||
hist[tid] = greater;
|
||||
hist[nth + tid] = equal;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
pos_greater = 0;
|
||||
pos_equal = 0;
|
||||
for (int i = 0; i < nth; ++i) {
|
||||
pos_equal += hist[i];
|
||||
if (i < tid) {
|
||||
pos_greater += hist[i];
|
||||
pos_equal += hist[nth + i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 4*tid; i < ne00; i += 4*nth) {
|
||||
const uint4 v = top_k_keys(src, i, ne00, nb00);
|
||||
const uint keys[4] = {v.s0, v.s1, v.s2, v.s3};
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (i + j < ne00) {
|
||||
if (keys[j] > prefix) {
|
||||
top_k_store(dst, candidates, src, nb00, indexed, base, i + j, pos_greater++);
|
||||
} else if (keys[j] == prefix && pos_equal < k) {
|
||||
top_k_store(dst, candidates, src, nb00, indexed, base, i + j, pos_equal++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_top_k_f32(
|
||||
global const char * src,
|
||||
ulong offset0,
|
||||
global int * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
ulong nb00,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int k_max,
|
||||
local uint * hist,
|
||||
global uint2 * candidates,
|
||||
int tile_width
|
||||
) {
|
||||
const ulong row = get_group_id(1);
|
||||
const int part = get_group_id(0);
|
||||
const int parts = get_num_groups(0);
|
||||
const int base = part*tile_width;
|
||||
const int n = min(ne00 - base, tile_width);
|
||||
const int k = min(k_max, n);
|
||||
const int out_width = (parts - 1)*k_max + min(k_max, ne00 - (parts - 1)*tile_width);
|
||||
src += offset0 + (row % ne01)*nb01 + (row / ne01 % ne02)*nb02 + (row / ne01 / ne02)*nb03 + (ulong) base*nb00;
|
||||
if (candidates) {
|
||||
candidates += row*out_width + part*k_max;
|
||||
} else {
|
||||
dst = (global int *) ((global char *) dst + offsetd) + row*out_width + part*k_max;
|
||||
}
|
||||
top_k_select(src, dst, candidates, nb00, n, k, base, 0, hist);
|
||||
}
|
||||
|
||||
kernel void kernel_top_k_merge_f32(
|
||||
global const uint2 * candidates,
|
||||
global int * dst,
|
||||
ulong offsetd,
|
||||
int n_candidates,
|
||||
int k,
|
||||
local uint * hist
|
||||
) {
|
||||
const ulong row = get_group_id(1);
|
||||
candidates += row*n_candidates;
|
||||
dst = (global int *) ((global char *) dst + offsetd) + row*k;
|
||||
top_k_select((global const char *) candidates, dst, 0, sizeof(uint2), n_candidates, k, 0, 1, hist);
|
||||
}
|
||||
@@ -382,6 +382,7 @@ enum vk_conv_shapes {
|
||||
CONV_SHAPE_64x32,
|
||||
CONV_SHAPE_32x256,
|
||||
CONV_SHAPE_64x128,
|
||||
CONV_SHAPE_256x128,
|
||||
CONV_SHAPE_COUNT,
|
||||
};
|
||||
|
||||
@@ -397,6 +398,7 @@ inline vk_conv_block_size vk_conv_block_sizes[CONV_SHAPE_COUNT] = {
|
||||
{ 64, 32, 32 }, // CONV_SHAPE_64x32
|
||||
{ 32, 256, 16 }, // CONV_SHAPE_32x256
|
||||
{ 64, 128, 16 }, // CONV_SHAPE_64x128
|
||||
{ 256, 128, 16 }, // CONV_SHAPE_256x128
|
||||
};
|
||||
|
||||
enum dmmv_wg_sizes {
|
||||
|
||||
@@ -1690,6 +1690,11 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
const uint32_t mul_mat_mm_warp_8 = std::min(mul_mat_subgroup_size_8, 64u);
|
||||
const uint32_t mul_mat_mm_warp_16 = std::min(mul_mat_subgroup_size_16, 64u);
|
||||
|
||||
// small warptiles split BM=32 between BLOCK_SIZE/WARP subgroups, so each subgroup needs its own warp tile
|
||||
const uint32_t s_warptile_wm_8 = 32 * subgroup_size_8 / subgroup_size_32;
|
||||
const uint32_t mul_mat_s_warptile_wm_8 = 32 * mul_mat_subgroup_size_8 / mul_mat_subgroup_size_32;
|
||||
const uint32_t mul_mat_s_warptile_wm_16 = 32 * mul_mat_subgroup_size_16 / mul_mat_subgroup_size_32;
|
||||
|
||||
const bool subgroup_min_size_16 = (!device->subgroup_size_control && device->subgroup_size >= 16) ||
|
||||
(device->subgroup_size_control && device->subgroup_max_size >= 16);
|
||||
|
||||
@@ -1784,20 +1789,18 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
const uint32_t itn = device->coopmat_int_n;
|
||||
const uint32_t itk = device->coopmat_int_k;
|
||||
|
||||
const uint32_t s_warptile_wm = device->subgroup_size == 8 ? 8 : 32;
|
||||
|
||||
l_warptile = { 128, 128, 128, 16, mm_warp_8 * 2, 64, 2, tm_l, tn_l, tk_l, mm_warp_8 };
|
||||
m_warptile = { 128, 64, 64, 16, mm_warp_8, 32, 2, tm_m, tn_m, tk_m, mm_warp_8 };
|
||||
s_warptile = { subgroup_size_32, 32, 32, 16, s_warptile_wm, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 };
|
||||
s_warptile = { subgroup_size_32, 32, 32, 16, s_warptile_wm_8, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 };
|
||||
|
||||
l_warptile_mmq = { 128, 128, 128, 32, mm_warp_8 * 2, 64, 2, tm_l, tn_l, tk_l, mm_warp_8 };
|
||||
m_warptile_mmq = { 128, 64, 64, 32, mm_warp_8, 32, 2, tm_m, tn_m, tk_m, mm_warp_8 };
|
||||
s_warptile_mmq = { subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 };
|
||||
s_warptile_mmq = { subgroup_size_32, 32, 32, 32, s_warptile_wm_8, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 };
|
||||
|
||||
// Integer MMQ has a smaller shared memory profile, but heavier register use
|
||||
l_warptile_mmq_int = { 128, 128, 128, 32, mm_warp_8 * 2, 64, 2, 4, 4, 1, mm_warp_8 };
|
||||
m_warptile_mmq_int = { 128, 64, 64, 32, mm_warp_8, 32, 2, 2, 2, 1, mm_warp_8 };
|
||||
s_warptile_mmq_int = { subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 2, 2, 1, 1, subgroup_size_8 };
|
||||
s_warptile_mmq_int = { subgroup_size_32, 32, 32, 32, s_warptile_wm_8, 32, 2, 2, 1, 1, subgroup_size_8 };
|
||||
|
||||
const auto cm1_bs = [cm1_sg](uint32_t bm, uint32_t bn) {
|
||||
return cm1_sg * (bm / std::min(cm1_sg, bm)) * (bn / 32);
|
||||
@@ -1818,23 +1821,23 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
// K-quants use even more registers, mitigate by setting WMITER to 1
|
||||
l_warptile_mmq_int_k = { 128, 128, 128, 32, mm_warp_8 * 2, 64, 1, 4, 4, 1, mm_warp_8 };
|
||||
m_warptile_mmq_int_k = { 128, 64, 64, 32, mm_warp_8, 32, 1, 2, 2, 1, mm_warp_8 };
|
||||
s_warptile_mmq_int_k = { subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 1, 2, 1, 1, subgroup_size_8 };
|
||||
s_warptile_mmq_int_k = { subgroup_size_32, 32, 32, 32, s_warptile_wm_8, 32, 1, 2, 1, 1, subgroup_size_8 };
|
||||
|
||||
l_warptile_id = { 128, 128, 128, 16, mul_mat_mm_warp_16 * 2, 64, 2, tm_l, tn_l, tk_l, mul_mat_mm_warp_16 };
|
||||
m_warptile_id = { 128, 64, 64, 16, mul_mat_mm_warp_16, 32, 2, tm_m, tn_m, tk_m, mul_mat_mm_warp_16 };
|
||||
s_warptile_id = { mul_mat_subgroup_size_16, 32, 32, 16, s_warptile_wm, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_16 };
|
||||
s_warptile_id = { mul_mat_subgroup_size_16, 32, 32, 16, 32, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_16 };
|
||||
|
||||
l_warptile_mmqid = { 128, 128, 128, 32, mul_mat_mm_warp_8 * 2, 64, 2, tm_l, tn_l, tk_l, mul_mat_mm_warp_8 };
|
||||
m_warptile_mmqid = { 128, 64, 64, 32, mul_mat_mm_warp_8, 32, 2, tm_m, tn_m, tk_m, mul_mat_mm_warp_8 };
|
||||
s_warptile_mmqid = { mul_mat_subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_8 };
|
||||
s_warptile_mmqid = { mul_mat_subgroup_size_32, 32, 32, 32, mul_mat_s_warptile_wm_8, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_8 };
|
||||
|
||||
l_warptile_mmqid_int = { 128, 128, 128, 32, mul_mat_mm_warp_8 * 2, 64, 2, 4, 4, 1, mul_mat_mm_warp_8 };
|
||||
m_warptile_mmqid_int = { 128, 64, 64, 32, mul_mat_mm_warp_8, 32, 2, 2, 2, 1, mul_mat_mm_warp_8 };
|
||||
s_warptile_mmqid_int = { mul_mat_subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 2, 2, 1, 1, mul_mat_subgroup_size_8 };
|
||||
s_warptile_mmqid_int = { mul_mat_subgroup_size_32, 32, 32, 32, mul_mat_s_warptile_wm_8, 32, 2, 2, 1, 1, mul_mat_subgroup_size_8 };
|
||||
|
||||
l_warptile_mmqid_int_k = { 128, 128, 128, 32, mul_mat_mm_warp_16 * 2, 64, 1, 4, 4, 1, mul_mat_mm_warp_16 };
|
||||
m_warptile_mmqid_int_k = { 128, 64, 64, 32, mul_mat_mm_warp_16, 32, 1, 2, 2, 1, mul_mat_mm_warp_16 };
|
||||
s_warptile_mmqid_int_k = { mul_mat_subgroup_size_32, 32, 32, 32, s_warptile_wm, 32, 1, 2, 1, 1, mul_mat_subgroup_size_16 };
|
||||
s_warptile_mmqid_int_k = { mul_mat_subgroup_size_32, 32, 32, 32, mul_mat_s_warptile_wm_16, 32, 1, 2, 1, 1, mul_mat_subgroup_size_16 };
|
||||
|
||||
// chip specific tuning
|
||||
if ((device->architecture == AMD_GCN) && (device->driver_id != vk::DriverId::eAmdProprietary)) {
|
||||
@@ -2793,9 +2796,8 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
&& !device->coopmat_bf16_support
|
||||
#endif
|
||||
) {
|
||||
const uint32_t s_warptile_wm_bf16 = device->subgroup_size == 8 ? 8 : 32;
|
||||
std::vector<vk_tile_config> tc_bf16_fb = {
|
||||
{{ subgroup_size_32, 32, 32, 16, s_warptile_wm_bf16, 32, 2, 2, 2, 1, subgroup_size_8 }, {32, 32, 1}, s_align},
|
||||
{{ subgroup_size_32, 32, 32, 16, s_warptile_wm_8, 32, 2, 2, 2, 1, subgroup_size_8 }, {32, 32, 1}, s_align},
|
||||
{{ 128, 64, 64, 16, mm_warp_8, 32, 2, 4, 2, 1, mm_warp_8 }, {64, 64, 1}, m_align},
|
||||
{{ 128, 128, 128, 16, mm_warp_8 * 2, 64, 2, 4, 4, 1, mm_warp_8 }, {128, 128, 1}, l_align},
|
||||
};
|
||||
@@ -3808,7 +3810,7 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
|
||||
auto shmem_req = [&](uint32_t pad, bool csh_store, bool fp16_shmem) {
|
||||
const uint32_t elem_size = fp16_shmem ? (uint32_t)sizeof(uint16_t) : (uint32_t)sizeof(float);
|
||||
const uint32_t csh_elems = csh_store ? conv2d_BS.K * conv2d_BS.NPQ : 0u;
|
||||
const uint32_t csh_elems = csh_store ? conv2d_BS.K * conv2d_BS.NPQ : (fp16_shmem ? 8u : 0u);
|
||||
return (conv2d_BS.K * (conv2d_BS.CRS + pad) + conv2d_BS.CRS * (conv2d_BS.NPQ + pad) + csh_elems) * elem_size;
|
||||
};
|
||||
|
||||
@@ -3836,9 +3838,9 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
}
|
||||
|
||||
// stage cm2 accumulator through shmem for coalesced global stores;
|
||||
// skipped on 128x128 where the extra Csh footprint hurts occupancy.
|
||||
// skipped on large tiles where the extra Csh footprint hurts occupancy.
|
||||
// cm1 always uses the staged path.
|
||||
uint32_t conv2d_csh_store = (device->coopmat2 && s != CONV_SHAPE_128x128) ? 1u : 0u;
|
||||
uint32_t conv2d_csh_store = (device->coopmat2 && s != CONV_SHAPE_128x128 && s != CONV_SHAPE_256x128) ? 1u : 0u;
|
||||
if (conv2d_use_cm1) {
|
||||
conv2d_csh_store = 1;
|
||||
}
|
||||
@@ -8718,6 +8720,26 @@ static vk_conv_shapes ggml_vk_conv_select_shape(ggml_backend_vk_context * ctx, u
|
||||
}
|
||||
}
|
||||
|
||||
static vk_conv_shapes ggml_vk_conv3d_select_shape(ggml_backend_vk_context * ctx, uint32_t K, uint32_t NPQ) {
|
||||
const auto & block = vk_conv_block_sizes[CONV_SHAPE_256x128];
|
||||
const uint32_t shader_core_count = ctx->device->shader_core_count;
|
||||
// Slowdowns were observed on DGX Spark with drivers older than R615.
|
||||
const bool driver_supported = ctx->device->driver_id != vk::DriverId::eNvidiaProprietary ||
|
||||
(ctx->device->properties.driverVersion >> 22) >= 615;
|
||||
if (ctx->device->coopmat2 && shader_core_count > 0 && K % block.K == 0 && driver_supported) {
|
||||
const uint64_t n_tiles = (uint64_t)(K / block.K) * CEIL_DIV((uint64_t)NPQ, block.NPQ);
|
||||
// Use the largest cm2 padding and include the dummy Csh[8].
|
||||
const uint32_t pad = 8, csh_elems = 8;
|
||||
const uint32_t shmem = (block.K * (block.CRS + pad) + block.CRS * (block.NPQ + pad) + csh_elems) * sizeof(uint16_t);
|
||||
// Reuse each loaded input across 256 output channels instead of 128.
|
||||
// Require full channel tiles and at least as many workgroups as GPU cores.
|
||||
if (n_tiles >= shader_core_count && shmem <= ctx->device->properties.limits.maxComputeSharedMemorySize) {
|
||||
return CONV_SHAPE_256x128;
|
||||
}
|
||||
}
|
||||
return ggml_vk_conv_select_shape(ctx, K, NPQ);
|
||||
}
|
||||
|
||||
static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * dst, ggml_op op) {
|
||||
switch (op) {
|
||||
case GGML_OP_GET_ROWS:
|
||||
@@ -9387,7 +9409,7 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
||||
const uint32_t IC = (uint32_t)ggml_get_op_params_i32(dst, 9);
|
||||
const uint32_t N = (uint32_t)ggml_get_op_params_i32(dst, 10);
|
||||
const uint32_t NPQ = N * dst->ne[2] * dst->ne[1] * dst->ne[0];
|
||||
const vk_conv_shapes shape = ggml_vk_conv_select_shape(ctx, OC, NPQ);
|
||||
const vk_conv_shapes shape = ggml_vk_conv3d_select_shape(ctx, OC, NPQ);
|
||||
|
||||
const uint32_t KW = (uint32_t)src0->ne[0];
|
||||
const uint32_t KH = (uint32_t)src0->ne[1];
|
||||
@@ -9700,7 +9722,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co
|
||||
case GGML_OP_CONV_3D:
|
||||
if constexpr (std::is_same_v<PC, vk_op_conv3d_push_constants>) {
|
||||
const uint32_t NPQ = pc.N * pc.OD * pc.OH * pc.OW;
|
||||
const vk_conv_shapes shape = ggml_vk_conv_select_shape(ctx, pc.OC, NPQ);
|
||||
const vk_conv_shapes shape = ggml_vk_conv3d_select_shape(ctx, pc.OC, NPQ);
|
||||
const uint32_t NPQ_blocks = CEIL_DIV(NPQ, vk_conv_block_sizes[shape].NPQ);
|
||||
|
||||
elements = { pc.OC, NPQ_blocks, 1 };
|
||||
|
||||
@@ -211,6 +211,7 @@ class Keys:
|
||||
VALUE_SCALE = "{arch}.attention.value_scale"
|
||||
COMPRESS_RATIOS = "{arch}.attention.compress_ratios"
|
||||
COMPRESS_ROPE_FREQ_BASE = "{arch}.attention.compress_rope_freq_base"
|
||||
KV_SOURCE_LAYERS = "{arch}.attention.kv_source_layers"
|
||||
TEMPERATURE_LENGTH = "{arch}.attention.temperature_length"
|
||||
KEY_LENGTH_MLA = "{arch}.attention.key_length_mla"
|
||||
VALUE_LENGTH_MLA = "{arch}.attention.value_length_mla"
|
||||
@@ -236,6 +237,10 @@ class Keys:
|
||||
TYPES = "{arch}.attention.indexer.types"
|
||||
KPOOL = "{arch}.attention.indexer.kpool" # GLM5-Next
|
||||
KPOOL_SELECT_TAIL = "{arch}.attention.indexer.kpool_select_tail" # GLM5-Next
|
||||
SOURCE_LAYERS = "{arch}.attention.indexer.source_layers"
|
||||
CANDIDATE_SOURCE_LAYER = "{arch}.attention.indexer.candidate_source_layer"
|
||||
CANDIDATE_TOP_K_BLOCKS = "{arch}.attention.indexer.candidate_top_k_blocks"
|
||||
CANDIDATE_BLOCK_SIZE = "{arch}.attention.indexer.candidate_block_size"
|
||||
|
||||
class HyperConnection:
|
||||
COUNT = "{arch}.hyper_connection.count"
|
||||
@@ -245,6 +250,12 @@ class Keys:
|
||||
MAGNITUDE = "{arch}.hyper_connection.magnitude"
|
||||
# absent means the mix projection is full rank (DeepSeek-V4 behaviour)
|
||||
LOW_RANK = "{arch}.hyper_connection.low_rank"
|
||||
# single-pass mHC: each sublayer collapses with the previous pre-mix (DeepSeek-V4.1)
|
||||
SINGLE_PASS = "{arch}.hyper_connection.single_pass"
|
||||
|
||||
class Engram: # deepseek41; the table uses the PerLayerEmbedding keys
|
||||
PAD_TOKEN_ID = "{arch}.engram.pad_token_id"
|
||||
TOKEN_MAP = "{arch}.engram.token_map"
|
||||
|
||||
class PerLayerEmbedding:
|
||||
LAYERS = "{arch}.ple.layers"
|
||||
@@ -636,6 +647,7 @@ class MODEL_ARCH(IntEnum):
|
||||
DEEPSEEK2OCR = auto()
|
||||
DEEPSEEK32 = auto()
|
||||
DEEPSEEK4 = auto()
|
||||
DEEPSEEK41 = auto()
|
||||
CHATGLM = auto()
|
||||
GLM4 = auto()
|
||||
GLM4_MOE = auto()
|
||||
@@ -908,6 +920,10 @@ class MODEL_TENSOR(IntEnum):
|
||||
HC_FFN_DOWN = auto() # qwen4exp
|
||||
HC_FFN_UP = auto() # qwen4exp
|
||||
HC_FFN_INJECT = auto() # qwen4exp
|
||||
HC_ATTN_DOWN_INJECT = auto() # qwen4exp, [hc_attn_down | hc_attn_inject]
|
||||
HC_FFN_DOWN_INJECT = auto() # qwen4exp, [hc_ffn_down | hc_ffn_inject]
|
||||
ATTN_QKV_GATE = auto() # qwen4exp, [attn_qkv | attn_gate]
|
||||
SSM_BETA_ALPHA_CAT = auto() # qwen4exp, [ssm_beta | ssm_alpha], unlike the grouped qwen3next ssm_ba
|
||||
PLE_KEY = auto() # qwen4exp
|
||||
PLE_VALUE = auto() # qwen4exp
|
||||
PLE_NORM_KEY = auto() # qwen4exp
|
||||
@@ -1317,6 +1333,8 @@ class MODEL_TENSOR(IntEnum):
|
||||
DSPARK_MARKOV_W1 = auto() # markov head: prev-token embed
|
||||
DSPARK_MARKOV_W2 = auto() # markov head: bias projection
|
||||
DSPARK_CONF_PROJ = auto() # confidence head
|
||||
ENGRAM_KV = auto()
|
||||
ENGRAM_GATE = auto()
|
||||
DFLASH_ATTN_CONV_BASE = auto()
|
||||
DFLASH_ATTN_CONV_PROJ = auto()
|
||||
DFLASH_FFN_CONV_BASE = auto()
|
||||
@@ -1460,6 +1478,7 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
||||
MODEL_ARCH.DEEPSEEK2OCR: "deepseek2-ocr",
|
||||
MODEL_ARCH.DEEPSEEK32: "deepseek32",
|
||||
MODEL_ARCH.DEEPSEEK4: "deepseek4",
|
||||
MODEL_ARCH.DEEPSEEK41: "deepseek41",
|
||||
MODEL_ARCH.CHATGLM: "chatglm",
|
||||
MODEL_ARCH.GLM4: "glm4",
|
||||
MODEL_ARCH.GLM4_MOE: "glm4moe",
|
||||
@@ -1731,6 +1750,10 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.HC_FFN_DOWN: "blk.{bid}.hc_ffn_down", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_UP: "blk.{bid}.hc_ffn_up", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_INJECT: "blk.{bid}.hc_ffn_inject", # qwen4exp
|
||||
MODEL_TENSOR.HC_ATTN_DOWN_INJECT: "blk.{bid}.hc_attn_down_inject", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_DOWN_INJECT: "blk.{bid}.hc_ffn_down_inject", # qwen4exp
|
||||
MODEL_TENSOR.ATTN_QKV_GATE: "blk.{bid}.attn_qkv_gate", # qwen4exp
|
||||
MODEL_TENSOR.SSM_BETA_ALPHA_CAT: "blk.{bid}.ssm_beta_alpha", # qwen4exp
|
||||
MODEL_TENSOR.PLE_KEY: "blk.{bid}.ple_key", # qwen4exp
|
||||
MODEL_TENSOR.PLE_VALUE: "blk.{bid}.ple_value", # qwen4exp
|
||||
MODEL_TENSOR.PLE_NORM_KEY: "blk.{bid}.ple_norm_key", # qwen4exp
|
||||
@@ -2164,6 +2187,8 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.DSPARK_MARKOV_W1: "markov_w1",
|
||||
MODEL_TENSOR.DSPARK_MARKOV_W2: "markov_w2",
|
||||
MODEL_TENSOR.DSPARK_CONF_PROJ: "conf_proj",
|
||||
MODEL_TENSOR.ENGRAM_KV: "blk.{bid}.engram_kv",
|
||||
MODEL_TENSOR.ENGRAM_GATE: "blk.{bid}.engram_gate",
|
||||
MODEL_TENSOR.DFLASH_ATTN_CONV_BASE: "blk.{bid}.attn_conv_base",
|
||||
MODEL_TENSOR.DFLASH_ATTN_CONV_PROJ: "blk.{bid}.attn_conv_proj",
|
||||
MODEL_TENSOR.DFLASH_FFN_CONV_BASE: "blk.{bid}.ffn_conv_base",
|
||||
@@ -3292,6 +3317,10 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.HC_FFN_DOWN,
|
||||
MODEL_TENSOR.HC_FFN_UP,
|
||||
MODEL_TENSOR.HC_FFN_INJECT,
|
||||
MODEL_TENSOR.HC_ATTN_DOWN_INJECT,
|
||||
MODEL_TENSOR.HC_FFN_DOWN_INJECT,
|
||||
MODEL_TENSOR.ATTN_QKV_GATE,
|
||||
MODEL_TENSOR.SSM_BETA_ALPHA_CAT,
|
||||
# full attention layers: ATTN_Q holds [q|gate] interleaved per head
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
MODEL_TENSOR.ATTN_Q_NORM,
|
||||
@@ -4358,6 +4387,46 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
|
||||
MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
|
||||
],
|
||||
MODEL_ARCH.DEEPSEEK41: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
MODEL_TENSOR.OUTPUT,
|
||||
MODEL_TENSOR.ATTN_NORM,
|
||||
MODEL_TENSOR.ATTN_SINKS,
|
||||
MODEL_TENSOR.ATTN_Q_A,
|
||||
MODEL_TENSOR.ATTN_Q_B,
|
||||
MODEL_TENSOR.ATTN_Q_A_NORM,
|
||||
MODEL_TENSOR.ATTN_KV,
|
||||
MODEL_TENSOR.ATTN_KV_NORM,
|
||||
MODEL_TENSOR.ATTN_OUT_A,
|
||||
MODEL_TENSOR.ATTN_OUT_B,
|
||||
MODEL_TENSOR.HC_ATTN_FN,
|
||||
MODEL_TENSOR.HC_ATTN_BASE,
|
||||
MODEL_TENSOR.HC_ATTN_SCALE,
|
||||
MODEL_TENSOR.HC_FFN_FN,
|
||||
MODEL_TENSOR.HC_FFN_BASE,
|
||||
MODEL_TENSOR.HC_FFN_SCALE,
|
||||
MODEL_TENSOR.ATTN_COMPRESSOR_WKV,
|
||||
MODEL_TENSOR.ATTN_COMPRESSOR_WGATE,
|
||||
MODEL_TENSOR.ATTN_COMPRESSOR_NORM,
|
||||
MODEL_TENSOR.INDEXER_ATTN_K,
|
||||
MODEL_TENSOR.INDEXER_K_NORM,
|
||||
MODEL_TENSOR.INDEXER_ATTN_Q_B,
|
||||
MODEL_TENSOR.INDEXER_PROJ,
|
||||
MODEL_TENSOR.PER_LAYER_TOKEN_EMBD,
|
||||
MODEL_TENSOR.ENGRAM_KV,
|
||||
MODEL_TENSOR.ENGRAM_GATE,
|
||||
MODEL_TENSOR.FFN_GATE_INP,
|
||||
MODEL_TENSOR.FFN_EXP_PROBS_B,
|
||||
MODEL_TENSOR.FFN_EXP_PROBS_B_VL,
|
||||
MODEL_TENSOR.FFN_NORM,
|
||||
MODEL_TENSOR.FFN_GATE_EXP,
|
||||
MODEL_TENSOR.FFN_DOWN_EXP,
|
||||
MODEL_TENSOR.FFN_UP_EXP,
|
||||
MODEL_TENSOR.FFN_GATE_SHEXP,
|
||||
MODEL_TENSOR.FFN_DOWN_SHEXP,
|
||||
MODEL_TENSOR.FFN_UP_SHEXP,
|
||||
],
|
||||
MODEL_ARCH.ERNIE4_5_MOE: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
@@ -6449,6 +6518,7 @@ class VisionProjectorType:
|
||||
DEEPSEEKOCR = "deepseekocr"
|
||||
DEEPSEEKOCR2 = "deepseekocr2"
|
||||
DEEPSEEK4V = "deepseek4v"
|
||||
DEEPSEEK41V = "deepseek41v"
|
||||
LFM2A = "lfm2a" # audio
|
||||
MUSIC_FLAMINGO = "musicflamingo" # audio
|
||||
GLM4V = "glm4v"
|
||||
|
||||
@@ -1077,6 +1077,28 @@ class GGUFWriter:
|
||||
def add_attention_compress_rope_freq_base(self, value: float) -> None:
|
||||
self.add_float32(Keys.Attention.COMPRESS_ROPE_FREQ_BASE.format(arch=self.arch), value)
|
||||
|
||||
# deepseek41
|
||||
def add_attention_kv_source_layers(self, layers: Sequence[int]) -> None:
|
||||
self.add_key_value(Keys.Attention.KV_SOURCE_LAYERS.format(arch=self.arch), list(layers), GGUFValueType.ARRAY, GGUFValueType.UINT32)
|
||||
|
||||
def add_indexer_source_layers(self, layers: Sequence[int]) -> None:
|
||||
self.add_key_value(Keys.Attention.Indexer.SOURCE_LAYERS.format(arch=self.arch), list(layers), GGUFValueType.ARRAY, GGUFValueType.UINT32)
|
||||
|
||||
def add_indexer_candidate_source_layer(self, layer: int) -> None:
|
||||
self.add_int32(Keys.Attention.Indexer.CANDIDATE_SOURCE_LAYER.format(arch=self.arch), layer)
|
||||
|
||||
def add_indexer_candidate_top_k_blocks(self, value: int) -> None:
|
||||
self.add_uint32(Keys.Attention.Indexer.CANDIDATE_TOP_K_BLOCKS.format(arch=self.arch), value)
|
||||
|
||||
def add_indexer_candidate_block_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.Attention.Indexer.CANDIDATE_BLOCK_SIZE.format(arch=self.arch), value)
|
||||
|
||||
def add_engram_pad_token_id(self, value: int) -> None:
|
||||
self.add_uint32(Keys.Engram.PAD_TOKEN_ID.format(arch=self.arch), value)
|
||||
|
||||
def add_engram_token_map(self, values: Sequence[int]) -> None:
|
||||
self.add_key_value(Keys.Engram.TOKEN_MAP.format(arch=self.arch), list(values), GGUFValueType.ARRAY, GGUFValueType.INT32)
|
||||
|
||||
def add_hyper_connection_count(self, count: int) -> None:
|
||||
self.add_uint32(Keys.HyperConnection.COUNT.format(arch=self.arch), count)
|
||||
|
||||
@@ -1092,6 +1114,9 @@ class GGUFWriter:
|
||||
def add_hyper_connection_low_rank(self, value: int) -> None:
|
||||
self.add_uint32(Keys.HyperConnection.LOW_RANK.format(arch=self.arch), value)
|
||||
|
||||
def add_hyper_connection_single_pass(self, value: bool) -> None:
|
||||
self.add_bool(Keys.HyperConnection.SINGLE_PASS.format(arch=self.arch), value)
|
||||
|
||||
def add_ple_layers(self, values: Sequence[int]) -> None:
|
||||
self.add_array(Keys.PerLayerEmbedding.LAYERS.format(arch=self.arch), values)
|
||||
|
||||
|
||||
@@ -1564,6 +1564,10 @@ extern "C" {
|
||||
float penalty_freq, // must be finite, 0.0 = disabled
|
||||
float penalty_present); // must be finite, 0.0 = disabled
|
||||
|
||||
/// maximum number of distinct DRY sequence breakers, duplicates not counted
|
||||
/// processing each breaker scans the entire vocabulary, so the init cost is O(num_breakers x n_vocab)
|
||||
#define LLAMA_DRY_MAX_SEQ_BREAKERS 64
|
||||
|
||||
/// @details DRY sampler, designed by p-e-w, as described in: https://github.com/oobabooga/text-generation-webui/pull/5677, porting Koboldcpp implementation authored by pi6am: https://github.com/LostRuins/koboldcpp/pull/982
|
||||
LLAMA_API struct llama_sampler * llama_sampler_init_dry(
|
||||
const struct llama_vocab * vocab,
|
||||
|
||||
@@ -215,6 +215,7 @@ def parse_log(file_path, pmu_index=None, limit=None, device_filter=None, op_filt
|
||||
'unwrapped_cycles_start': unwrapped_cycles_start,
|
||||
'pmu_val': pmu_val,
|
||||
'evt_val': evt_val,
|
||||
'mhz': float(op_match.group('mhz')) if op_match.group('mhz') else None,
|
||||
'abs_usec': abs_usec,
|
||||
'trace_events': [],
|
||||
'device': device
|
||||
@@ -630,6 +631,165 @@ def generate_report(ops, top_n, width_overrides, sort_col, pmu_name=None):
|
||||
logger.info("| " + " | ".join(row_vals) + " |")
|
||||
|
||||
|
||||
def has_perf_print(file_path):
|
||||
if file_path == "-":
|
||||
return True
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
return any("common_perf_print" in line for line in f)
|
||||
|
||||
|
||||
def pct_delta(a, b):
|
||||
return (b - a) * 100.0 / a if a else None
|
||||
|
||||
|
||||
def fmt_pct(p):
|
||||
return f"{p:+.2f}%" if p is not None else "n/a"
|
||||
|
||||
|
||||
def print_table(title, cols, rows, width_overrides):
|
||||
# cols: list of (cli name, header); rows: list of dicts keyed by cli name
|
||||
logger.info(f"\n# {title}\n")
|
||||
if not rows:
|
||||
logger.info("None.")
|
||||
return
|
||||
|
||||
headers, keys, widths = [], [], []
|
||||
for key, header in cols:
|
||||
natural_width = max([len(str(row[key])) for row in rows] + [len(header)])
|
||||
target_width = width_overrides.get(key, natural_width)
|
||||
if target_width == 0:
|
||||
continue
|
||||
headers.append(header)
|
||||
keys.append(key)
|
||||
widths.append(target_width)
|
||||
|
||||
logger.info("| " + " | ".join(f"{h:<{widths[i]}}" for i, h in enumerate(headers)) + " |")
|
||||
logger.info("| " + " | ".join("-" * widths[i] for i in range(len(headers))) + " |")
|
||||
for row in rows:
|
||||
row_vals = []
|
||||
for i, key in enumerate(keys):
|
||||
val = str(row[key])
|
||||
if len(val) > widths[i]:
|
||||
val = val[:widths[i] - 3] + "..."
|
||||
row_vals.append(f"{val:<{widths[i]}}")
|
||||
logger.info("| " + " | ".join(row_vals) + " |")
|
||||
|
||||
|
||||
def generate_compare_report(ops_a, ops_b, file_a, file_b, top_n, width_overrides, min_delta):
|
||||
def summarize(ops):
|
||||
batches = [o for o in ops if o['name'] == "OPBATCH"]
|
||||
others = [o for o in ops if o['name'] != "OPBATCH"]
|
||||
mhz = [o['mhz'] for o in others if o['mhz'] is not None]
|
||||
return {
|
||||
'n_ops': len(others),
|
||||
'tot_usec': sum(o['usec'] for o in others),
|
||||
'n_batch': len(batches),
|
||||
'batch_usec': sum(o['usec'] for o in batches),
|
||||
'avg_mhz': statistics.mean(mhz) if mhz else 0.0,
|
||||
}
|
||||
|
||||
sa, sb = summarize(ops_a), summarize(ops_b)
|
||||
|
||||
logger.info("\n# Compare Summary (OPBATCH excluded from op totals)\n")
|
||||
logger.info(f" A (baseline) : {file_a}")
|
||||
logger.info(f" B (new) : {file_b}")
|
||||
logger.info(" Deltas are B - A: negative means B is faster\n")
|
||||
logger.info(f" Ops : A {sa['n_ops']} B {sb['n_ops']}")
|
||||
logger.info(f" Tot op usec : A {sa['tot_usec']} B {sb['tot_usec']} delta {sb['tot_usec'] - sa['tot_usec']:+d} ({fmt_pct(pct_delta(sa['tot_usec'], sb['tot_usec']))})")
|
||||
logger.info(f" OPBATCH : A {sa['n_batch']} / {sa['batch_usec']} usec B {sb['n_batch']} / {sb['batch_usec']} usec")
|
||||
logger.info(f" Avg op mhz : A {sa['avg_mhz']:.1f} B {sb['avg_mhz']:.1f}")
|
||||
|
||||
if sa['n_batch'] != sb['n_batch']:
|
||||
logger.warning(f" WARNING: OPBATCH counts differ (A {sa['n_batch']}, B {sb['n_batch']}), one log may be incomplete")
|
||||
for label, path in (("A", file_a), ("B", file_b)):
|
||||
if not has_perf_print(path):
|
||||
logger.warning(f" WARNING: log {label} has no common_perf_print line, run may be incomplete")
|
||||
|
||||
# Per-op-name rollup
|
||||
by_name = defaultdict(lambda: [0, 0, 0, 0])
|
||||
for idx, ops in ((0, ops_a), (1, ops_b)):
|
||||
for op in ops:
|
||||
if op['name'] == "OPBATCH":
|
||||
continue
|
||||
by_name[op['name']][idx] += 1
|
||||
by_name[op['name']][idx + 2] += op['usec']
|
||||
|
||||
rows = []
|
||||
for name, (cnt_a, cnt_b, tot_a, tot_b) in by_name.items():
|
||||
p = pct_delta(tot_a, tot_b)
|
||||
if min_delta and p is not None and abs(p) < min_delta:
|
||||
continue
|
||||
rows.append({'op': name, 'count-a': cnt_a, 'count-b': cnt_b, 'tot-usec-a': tot_a, 'tot-usec-b': tot_b,
|
||||
'delta-usec': f"{tot_b - tot_a:+d}", 'delta-pct': fmt_pct(p), '_sort': abs(tot_b - tot_a)})
|
||||
rows.sort(key=lambda r: r['_sort'], reverse=True)
|
||||
|
||||
print_table("Per-Op Rollup (delta = B - A, sorted by abs delta usec)", [
|
||||
("op", "Op"), ("count-a", "Count A"), ("count-b", "Count B"), ("tot-usec-a", "Tot usec A"),
|
||||
("tot-usec-b", "Tot usec B"), ("delta-usec", "Delta usec"), ("delta-pct", "Delta %")], rows, width_overrides)
|
||||
|
||||
# Per-group compare keyed on (op, dims, dtypes)
|
||||
def group(ops):
|
||||
grouped = defaultdict(list)
|
||||
for op in ops:
|
||||
if op['name'] != "OPBATCH":
|
||||
grouped[(op['name'], op['dims'], op['types'])].append(op['usec'])
|
||||
return grouped
|
||||
|
||||
ga, gb = group(ops_a), group(ops_b)
|
||||
|
||||
rows = []
|
||||
for key in ga.keys() & gb.keys():
|
||||
ua, ub = ga[key], gb[key]
|
||||
tot_a, tot_b = sum(ua), sum(ub)
|
||||
avg_a, avg_b = tot_a / len(ua), tot_b / len(ub)
|
||||
p = pct_delta(avg_a, avg_b)
|
||||
if min_delta and p is not None and abs(p) < min_delta:
|
||||
continue
|
||||
rows.append({'op': key[0], 'dims': key[1], 'dtypes': key[2], 'count-a': len(ua), 'count-b': len(ub),
|
||||
'avg-usec-a': f"{avg_a:.2f}", 'avg-usec-b': f"{avg_b:.2f}", 'delta-pct': fmt_pct(p),
|
||||
'tot-usec-a': tot_a, 'tot-usec-b': tot_b, 'delta-usec': f"{tot_b - tot_a:+d}", '_sort': abs(tot_b - tot_a)})
|
||||
rows.sort(key=lambda r: r['_sort'], reverse=True)
|
||||
|
||||
print_table(f"Per-Group Compare (delta = B - A, top {top_n} sorted by abs delta usec)", [
|
||||
("op", "Op"), ("dims", "Dims"), ("dtypes", "DTypes"), ("count-a", "Count A"), ("count-b", "Count B"),
|
||||
("avg-usec-a", "Avg usec A"), ("avg-usec-b", "Avg usec B"), ("delta-pct", "Delta %"),
|
||||
("tot-usec-a", "Tot usec A"), ("tot-usec-b", "Tot usec B"), ("delta-usec", "Delta usec")], rows[:top_n], width_overrides)
|
||||
|
||||
# Groups present in only one log
|
||||
for label, g, other in (("A", ga, gb), ("B", gb, ga)):
|
||||
rows = [{'op': k[0], 'dims': k[1], 'dtypes': k[2], 'count': len(v), 'tot-usec': sum(v)} for k, v in g.items() if k not in other]
|
||||
rows.sort(key=lambda r: r['tot-usec'], reverse=True)
|
||||
print_table(f"Groups only in {label} (Top {top_n} sorted by tot usec)", [
|
||||
("op", "Op"), ("dims", "Dims"), ("dtypes", "DTypes"), ("count", "Count"), ("tot-usec", "Tot usec")], rows[:top_n], width_overrides)
|
||||
|
||||
|
||||
def filter_ops(ops, args):
|
||||
if args.filter:
|
||||
try:
|
||||
filter_re = re.compile(args.filter)
|
||||
except re.error as e:
|
||||
logger.error(f"Invalid regex filter: {e}")
|
||||
sys.exit(1)
|
||||
ops = [op for op in ops if filter_re.search(op['op_text'])]
|
||||
|
||||
if args.head is not None or args.tail is not None:
|
||||
ops_by_dev = defaultdict(list)
|
||||
for op in ops:
|
||||
ops_by_dev[op['device']].append(op)
|
||||
|
||||
filtered_ops = []
|
||||
for dev in sorted(ops_by_dev.keys()):
|
||||
dev_ops = ops_by_dev[dev]
|
||||
if args.head is not None:
|
||||
dev_ops = dev_ops[:args.head]
|
||||
elif args.tail is not None:
|
||||
dev_ops = dev_ops[-args.tail:]
|
||||
filtered_ops.extend(dev_ops)
|
||||
ops = filtered_ops
|
||||
|
||||
return ops
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Post-process Op profile info.")
|
||||
parser.add_argument("logfile")
|
||||
@@ -642,6 +802,8 @@ def main():
|
||||
help="Output ASCII art event summary or thread idle bubble analysis (default: summary)")
|
||||
parser.add_argument("--filter", type=str, help="Regex filter matching against the original profile-op line")
|
||||
parser.add_argument("--device", type=str, help="Device to filter by (e.g. HTP0, HTP0:0) or 'split' to generate separate reports per device")
|
||||
parser.add_argument("--compare", type=str, metavar="LOGFILE", help="Compare against another log (logfile is baseline A, this is B)")
|
||||
parser.add_argument("--min-delta", type=float, default=0, metavar="PCT", help="In compare mode hide rows with abs(Delta %%) below PCT")
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("--head", type=int, help="Limit to first N ops")
|
||||
@@ -655,6 +817,10 @@ def main():
|
||||
logger.error(f"Cannot sort by '{args.sort}' without --pmu-index.")
|
||||
sys.exit(1)
|
||||
|
||||
if args.compare and (args.timeline or args.device == "split"):
|
||||
logger.error("--compare cannot be used with --timeline or --device split.")
|
||||
sys.exit(1)
|
||||
|
||||
overrides = {}
|
||||
if args.width:
|
||||
for w in args.width:
|
||||
@@ -681,6 +847,13 @@ def main():
|
||||
if args.device and args.device != "split":
|
||||
ops = [op for op in ops if device_matches(op['device'], args.device)]
|
||||
|
||||
if args.compare:
|
||||
ops_b = parse_log(args.compare, pmu_index=args.pmu_index, limit=limit, device_filter=device_filter, op_filter_re=op_filter_re)
|
||||
if device_filter:
|
||||
ops_b = [op for op in ops_b if device_matches(op['device'], device_filter)]
|
||||
generate_compare_report(filter_ops(ops, args), filter_ops(ops_b, args), args.logfile, args.compare, args.top, overrides, args.min_delta)
|
||||
return
|
||||
|
||||
if args.device == "split":
|
||||
unique_devices = sorted(list(set(op['device'] for op in ops)))
|
||||
for dev in unique_devices:
|
||||
@@ -712,28 +885,7 @@ def main():
|
||||
else:
|
||||
generate_report(dev_ops, args.top, overrides, args.sort, pmu_name=final_pmu_name)
|
||||
else:
|
||||
if args.filter:
|
||||
try:
|
||||
filter_re = re.compile(args.filter)
|
||||
except re.error as e:
|
||||
logger.error(f"Invalid regex filter: {e}")
|
||||
sys.exit(1)
|
||||
ops = [op for op in ops if filter_re.search(op['op_text'])]
|
||||
|
||||
if args.head is not None or args.tail is not None:
|
||||
ops_by_dev = defaultdict(list)
|
||||
for op in ops:
|
||||
ops_by_dev[op['device']].append(op)
|
||||
|
||||
filtered_ops = []
|
||||
for dev in sorted(ops_by_dev.keys()):
|
||||
dev_ops = ops_by_dev[dev]
|
||||
if args.head is not None:
|
||||
dev_ops = dev_ops[:args.head]
|
||||
elif args.tail is not None:
|
||||
dev_ops = dev_ops[-args.tail:]
|
||||
filtered_ops.extend(dev_ops)
|
||||
ops = filtered_ops
|
||||
ops = filter_ops(ops, args)
|
||||
|
||||
if args.timeline:
|
||||
for op in ops:
|
||||
|
||||
@@ -87,6 +87,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
||||
{ LLM_ARCH_DEEPSEEK2OCR, "deepseek2-ocr" },
|
||||
{ LLM_ARCH_DEEPSEEK32, "deepseek32" },
|
||||
{ LLM_ARCH_DEEPSEEK4, "deepseek4" },
|
||||
{ LLM_ARCH_DEEPSEEK41, "deepseek41" },
|
||||
{ LLM_ARCH_CHATGLM, "chatglm" },
|
||||
{ LLM_ARCH_GLM4, "glm4" },
|
||||
{ LLM_ARCH_GLM4_MOE, "glm4moe" },
|
||||
@@ -311,6 +312,11 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_ATTENTION_INDEXER_TYPES, "%s.attention.indexer.types" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_KPOOL, "%s.attention.indexer.kpool" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_KPOOL_SELECT_TAIL, "%s.attention.indexer.kpool_select_tail" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_SOURCE_LAYERS, "%s.attention.indexer.source_layers" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_CANDIDATE_SOURCE_LAYER, "%s.attention.indexer.candidate_source_layer" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_CANDIDATE_TOP_K_BLOCKS, "%s.attention.indexer.candidate_top_k_blocks" },
|
||||
{ LLM_KV_ATTENTION_INDEXER_CANDIDATE_BLOCK_SIZE, "%s.attention.indexer.candidate_block_size" },
|
||||
{ LLM_KV_ATTENTION_KV_SOURCE_LAYERS, "%s.attention.kv_source_layers" },
|
||||
{ LLM_KV_ATTENTION_OUTPUT_GROUP_COUNT, "%s.attention.output_group_count" },
|
||||
{ LLM_KV_ATTENTION_OUTPUT_LORA_RANK, "%s.attention.output_lora_rank" },
|
||||
{ LLM_KV_ATTENTION_COMPRESS_ROPE_FREQ_BASE, "%s.attention.compress_rope_freq_base" },
|
||||
@@ -323,6 +329,10 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_HYPER_CONNECTION_EPSILON, "%s.hyper_connection.epsilon" },
|
||||
{ LLM_KV_HYPER_CONNECTION_MAGNITUDE, "%s.hyper_connection.magnitude" },
|
||||
{ LLM_KV_HYPER_CONNECTION_LOW_RANK, "%s.hyper_connection.low_rank" },
|
||||
{ LLM_KV_HYPER_CONNECTION_SINGLE_PASS, "%s.hyper_connection.single_pass" },
|
||||
|
||||
{ LLM_KV_ENGRAM_PAD_TOKEN_ID, "%s.engram.pad_token_id" },
|
||||
{ LLM_KV_ENGRAM_TOKEN_MAP, "%s.engram.token_map" },
|
||||
|
||||
{ LLM_KV_PLE_LAYERS, "%s.ple.layers" },
|
||||
{ LLM_KV_PLE_NGRAM_SIZE, "%s.ple.ngram_size" },
|
||||
@@ -625,6 +635,10 @@ static const std::map<llm_tensor, const char *> LLM_TENSOR_NAMES = {
|
||||
{ LLM_TENSOR_HC_FFN_DOWN, "blk.%d.hc_ffn_down" },
|
||||
{ LLM_TENSOR_HC_FFN_UP, "blk.%d.hc_ffn_up" },
|
||||
{ LLM_TENSOR_HC_FFN_INJECT, "blk.%d.hc_ffn_inject" },
|
||||
{ LLM_TENSOR_HC_ATTN_DOWN_INJECT, "blk.%d.hc_attn_down_inject" },
|
||||
{ LLM_TENSOR_HC_FFN_DOWN_INJECT, "blk.%d.hc_ffn_down_inject" },
|
||||
{ LLM_TENSOR_ATTN_QKV_GATE, "blk.%d.attn_qkv_gate" },
|
||||
{ LLM_TENSOR_SSM_BETA_ALPHA_CAT, "blk.%d.ssm_beta_alpha" },
|
||||
{ LLM_TENSOR_PLE_KEY, "blk.%d.ple_key" },
|
||||
{ LLM_TENSOR_PLE_VALUE, "blk.%d.ple_value" },
|
||||
{ LLM_TENSOR_PLE_NORM_KEY, "blk.%d.ple_norm_key" },
|
||||
@@ -782,6 +796,8 @@ static const std::map<llm_tensor, const char *> LLM_TENSOR_NAMES = {
|
||||
{ LLM_TENSOR_DSPARK_MARKOV_W1, "markov_w1" },
|
||||
{ LLM_TENSOR_DSPARK_MARKOV_W2, "markov_w2" },
|
||||
{ LLM_TENSOR_DSPARK_CONF_PROJ, "conf_proj" },
|
||||
{ LLM_TENSOR_ENGRAM_KV, "blk.%d.engram_kv" },
|
||||
{ LLM_TENSOR_ENGRAM_GATE, "blk.%d.engram_gate" },
|
||||
{ LLM_TENSOR_DFLASH_ATTN_CONV_BASE, "blk.%d.attn_conv_base" },
|
||||
{ LLM_TENSOR_DFLASH_ATTN_CONV_PROJ, "blk.%d.attn_conv_proj" },
|
||||
{ LLM_TENSOR_DFLASH_FFN_CONV_BASE, "blk.%d.ffn_conv_base" },
|
||||
@@ -895,6 +911,10 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
|
||||
{LLM_TENSOR_HC_FFN_DOWN, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_UP, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_ATTN_DOWN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_DOWN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_ATTN_QKV_GATE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_SSM_BETA_ALPHA_CAT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_KEY, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_VALUE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_NORM_KEY, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
|
||||
@@ -1120,6 +1140,8 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
|
||||
{LLM_TENSOR_DSPARK_MARKOV_W1, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_GET_ROWS}},
|
||||
{LLM_TENSOR_DSPARK_MARKOV_W2, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_DSPARK_CONF_PROJ, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_ENGRAM_KV, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_ENGRAM_GATE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
|
||||
{LLM_TENSOR_DFLASH_ATTN_CONV_BASE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
|
||||
{LLM_TENSOR_DFLASH_ATTN_CONV_PROJ, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_DFLASH_FFN_CONV_BASE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
|
||||
@@ -1239,6 +1261,7 @@ bool llm_arch_is_hybrid(const llm_arch & arch) {
|
||||
case LLM_ARCH_QWEN35MOE:
|
||||
case LLM_ARCH_QWEN4EXP:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_MINIMAX_01:
|
||||
return true;
|
||||
default:
|
||||
@@ -1265,6 +1288,7 @@ bool llm_arch_supports_rs_rollback(const llm_arch & arch) {
|
||||
case LLM_ARCH_QWEN35MOE:
|
||||
case LLM_ARCH_QWEN4EXP:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_GLM5_NEXT:
|
||||
case LLM_ARCH_NEMOTRON_H:
|
||||
case LLM_ARCH_NEMOTRON_H_MOE:
|
||||
@@ -1282,6 +1306,7 @@ bool llm_arch_supports_mixed_batch(const llm_arch & arch) {
|
||||
switch (arch) {
|
||||
case LLM_ARCH_COGVLM:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_GRANITE_SWITCH:
|
||||
case LLM_ARCH_EAGLE3:
|
||||
case LLM_ARCH_DFLASH:
|
||||
|
||||
@@ -92,6 +92,7 @@ enum llm_arch {
|
||||
LLM_ARCH_DEEPSEEK2OCR,
|
||||
LLM_ARCH_DEEPSEEK32,
|
||||
LLM_ARCH_DEEPSEEK4,
|
||||
LLM_ARCH_DEEPSEEK41,
|
||||
LLM_ARCH_CHATGLM,
|
||||
LLM_ARCH_GLM4,
|
||||
LLM_ARCH_GLM4_MOE,
|
||||
@@ -316,6 +317,11 @@ enum llm_kv {
|
||||
LLM_KV_ATTENTION_INDEXER_TYPES,
|
||||
LLM_KV_ATTENTION_INDEXER_KPOOL,
|
||||
LLM_KV_ATTENTION_INDEXER_KPOOL_SELECT_TAIL,
|
||||
LLM_KV_ATTENTION_INDEXER_SOURCE_LAYERS,
|
||||
LLM_KV_ATTENTION_INDEXER_CANDIDATE_SOURCE_LAYER,
|
||||
LLM_KV_ATTENTION_INDEXER_CANDIDATE_TOP_K_BLOCKS,
|
||||
LLM_KV_ATTENTION_INDEXER_CANDIDATE_BLOCK_SIZE,
|
||||
LLM_KV_ATTENTION_KV_SOURCE_LAYERS,
|
||||
LLM_KV_ATTENTION_OUTPUT_GROUP_COUNT,
|
||||
LLM_KV_ATTENTION_OUTPUT_LORA_RANK,
|
||||
LLM_KV_ATTENTION_COMPRESS_ROPE_FREQ_BASE,
|
||||
@@ -328,6 +334,10 @@ enum llm_kv {
|
||||
LLM_KV_HYPER_CONNECTION_EPSILON,
|
||||
LLM_KV_HYPER_CONNECTION_MAGNITUDE,
|
||||
LLM_KV_HYPER_CONNECTION_LOW_RANK,
|
||||
LLM_KV_HYPER_CONNECTION_SINGLE_PASS,
|
||||
|
||||
LLM_KV_ENGRAM_PAD_TOKEN_ID,
|
||||
LLM_KV_ENGRAM_TOKEN_MAP,
|
||||
|
||||
LLM_KV_PLE_LAYERS,
|
||||
LLM_KV_PLE_NGRAM_SIZE,
|
||||
@@ -669,6 +679,10 @@ enum llm_tensor {
|
||||
LLM_TENSOR_HC_FFN_DOWN, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_UP, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_HC_ATTN_DOWN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_DOWN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_ATTN_QKV_GATE, // qwen4exp
|
||||
LLM_TENSOR_SSM_BETA_ALPHA_CAT, // qwen4exp
|
||||
LLM_TENSOR_PLE_KEY, // qwen4exp
|
||||
LLM_TENSOR_PLE_VALUE, // qwen4exp
|
||||
LLM_TENSOR_PLE_NORM_KEY, // qwen4exp
|
||||
@@ -788,6 +802,8 @@ enum llm_tensor {
|
||||
LLM_TENSOR_DSPARK_MARKOV_W1,
|
||||
LLM_TENSOR_DSPARK_MARKOV_W2,
|
||||
LLM_TENSOR_DSPARK_CONF_PROJ,
|
||||
LLM_TENSOR_ENGRAM_KV,
|
||||
LLM_TENSOR_ENGRAM_GATE,
|
||||
LLM_TENSOR_DFLASH_ATTN_CONV_BASE,
|
||||
LLM_TENSOR_DFLASH_ATTN_CONV_PROJ,
|
||||
LLM_TENSOR_DFLASH_FFN_CONV_BASE,
|
||||
|
||||
@@ -1339,8 +1339,9 @@ void llama_context::set_abort_callback(bool (*abort_callback)(void * data), void
|
||||
void llama_context::set_embeddings(bool value) {
|
||||
LLAMA_LOG_DEBUG("%s: value = %d\n", __func__, value);
|
||||
|
||||
// a pooled context computes the logits only without embeddings, so the toggle changes the graph shape
|
||||
if (cparams.embeddings != value && cparams.pooling_type != LLAMA_POOLING_TYPE_NONE) {
|
||||
// the toggle changes the graph shape (pooled: logits only; pooling NONE: the reserve graph outputs all tokens,
|
||||
// which crashed ggml_gallocr on the first /completion), so re-reserve on every toggle
|
||||
if (cparams.embeddings != value) {
|
||||
sched_need_reserve = true;
|
||||
}
|
||||
|
||||
@@ -2536,6 +2537,7 @@ uint32_t llama_context::graph_max_nodes(uint32_t n_tokens) const {
|
||||
model.arch == LLM_ARCH_CLEF ||
|
||||
model.arch == LLM_ARCH_QWEN4EXP ||
|
||||
model.arch == LLM_ARCH_DEEPSEEK4 ||
|
||||
model.arch == LLM_ARCH_DEEPSEEK41 ||
|
||||
(model.arch == LLM_ARCH_DFLASH && model.hparams.dsv4_hc_mult > 0) ||
|
||||
model.arch == LLM_ARCH_NANBEIGE ||
|
||||
model.arch == LLM_ARCH_MINIMAX_01 ||
|
||||
@@ -4071,7 +4073,7 @@ llama_context * llama_init_from_model(
|
||||
}
|
||||
}
|
||||
|
||||
if ((model->hparams.is_mla() || model->arch == LLM_ARCH_DEEPSEEK4) && params.type_k != params.type_v) {
|
||||
if ((model->hparams.is_mla() || model->arch == LLM_ARCH_DEEPSEEK4 || model->arch == LLM_ARCH_DEEPSEEK41) && params.type_k != params.type_v) {
|
||||
LLAMA_LOG_ERROR("%s: model does not support different K (%s) and V (%s) cache types\n", __func__, ggml_type_name(params.type_k), ggml_type_name(params.type_v));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+11
-5
@@ -11,6 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#define MAX_REPETITION_THRESHOLD 2000
|
||||
#define MAX_GRAMMAR_RECURSION_DEPTH 256
|
||||
//
|
||||
// helpers
|
||||
//
|
||||
@@ -440,13 +441,14 @@ const char * llama_grammar_parser::parse_alternates(
|
||||
const char * src,
|
||||
const std::string & rule_name,
|
||||
uint32_t rule_id,
|
||||
bool is_nested) {
|
||||
bool is_nested,
|
||||
uint32_t depth) {
|
||||
llama_grammar_rule rule;
|
||||
const char * pos = parse_sequence(src, rule_name, rule, is_nested);
|
||||
const char * pos = parse_sequence(src, rule_name, rule, is_nested, depth);
|
||||
while (*pos == '|') {
|
||||
rule.push_back({LLAMA_GRETYPE_ALT, 0});
|
||||
pos = parse_space(pos + 1, true);
|
||||
pos = parse_sequence(pos, rule_name, rule, is_nested);
|
||||
pos = parse_sequence(pos, rule_name, rule, is_nested, depth);
|
||||
}
|
||||
rule.push_back({LLAMA_GRETYPE_END, 0});
|
||||
add_rule(rule_id, rule);
|
||||
@@ -457,7 +459,8 @@ const char * llama_grammar_parser::parse_sequence(
|
||||
const char * src,
|
||||
const std::string & rule_name,
|
||||
llama_grammar_rule & rule,
|
||||
bool is_nested) {
|
||||
bool is_nested,
|
||||
uint32_t depth) {
|
||||
size_t last_sym_start = rule.size();
|
||||
const char * pos = src;
|
||||
uint64_t n_prev_rules = 1;
|
||||
@@ -595,11 +598,14 @@ const char * llama_grammar_parser::parse_sequence(
|
||||
n_prev_rules = 1;
|
||||
rule.push_back({LLAMA_GRETYPE_RULE_REF, ref_rule_id});
|
||||
} else if (*pos == '(') { // grouping
|
||||
if (depth >= MAX_GRAMMAR_RECURSION_DEPTH) {
|
||||
throw std::runtime_error("grammar recursion depth limit exceeded (max " + std::to_string(MAX_GRAMMAR_RECURSION_DEPTH) + "), please reduce nesting depth");
|
||||
}
|
||||
// parse nested alternates into synthesized rule
|
||||
pos = parse_space(pos + 1, true);
|
||||
uint32_t n_rules_before = symbol_ids.size();
|
||||
uint32_t sub_rule_id = generate_symbol_id(rule_name);
|
||||
pos = parse_alternates(pos, rule_name, sub_rule_id, true);
|
||||
pos = parse_alternates(pos, rule_name, sub_rule_id, true, depth + 1);
|
||||
n_prev_rules = std::max(1u, (uint32_t)symbol_ids.size() - n_rules_before);
|
||||
last_sym_start = rule.size();
|
||||
// output reference to synthesized rule
|
||||
|
||||
+4
-2
@@ -102,13 +102,15 @@ struct llama_grammar_parser {
|
||||
const char * src,
|
||||
const std::string & rule_name,
|
||||
uint32_t rule_id,
|
||||
bool is_nested);
|
||||
bool is_nested,
|
||||
uint32_t depth = 0);
|
||||
|
||||
const char * parse_sequence(
|
||||
const char * src,
|
||||
const std::string & rule_name,
|
||||
llama_grammar_rule & rule,
|
||||
bool is_nested);
|
||||
bool is_nested,
|
||||
uint32_t depth = 0);
|
||||
|
||||
const char * parse_rule(const char * src);
|
||||
|
||||
|
||||
+9
-4
@@ -2010,7 +2010,7 @@ ggml_tensor * llm_graph_context::build_ffn(
|
||||
const float limit = hparams.swiglu_clamp_shexp[il];
|
||||
constexpr float eps = 1e-6f;
|
||||
if (limit > eps) {
|
||||
if (arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_GLM5_NEXT || (arch == LLM_ARCH_DFLASH && hparams.dsv4_hc_mult > 0)) {
|
||||
if (arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_DEEPSEEK41 || arch == LLM_ARCH_GLM5_NEXT || (arch == LLM_ARCH_DFLASH && hparams.dsv4_hc_mult > 0)) {
|
||||
cur = ggml_swiglu_clamp(ctx0, cur, tmp, limit);
|
||||
} else {
|
||||
tmp = ggml_clamp(ctx0, tmp, -limit, limit);
|
||||
@@ -2340,8 +2340,13 @@ ggml_tensor * llm_graph_context::build_moe_ffn(
|
||||
ggml_tensor * weights_sum = ggml_sum_rows(ctx0, weights); // [1, n_tokens]
|
||||
cb(weights_sum, "ffn_moe_weights_sum", il);
|
||||
|
||||
// Avoid division by zero, clamp to smallest number representable by F16
|
||||
weights_sum = ggml_clamp(ctx0, weights_sum, 6.103515625e-5, INFINITY);
|
||||
if (arch == LLM_ARCH_DEEPSEEK41) {
|
||||
// model.py Gate.forward: weights / (sum + 1e-20); the sum can fall below the F16 clamp below
|
||||
weights_sum = ggml_scale_bias(ctx0, weights_sum, 1.0f, 1e-20f);
|
||||
} else {
|
||||
// Avoid division by zero, clamp to smallest number representable by F16
|
||||
weights_sum = ggml_clamp(ctx0, weights_sum, 6.103515625e-5, INFINITY);
|
||||
}
|
||||
cb(weights_sum, "ffn_moe_weights_sum_clamped", il);
|
||||
|
||||
weights = ggml_div(ctx0, weights, weights_sum); // [n_expert_used, n_tokens]
|
||||
@@ -2431,7 +2436,7 @@ ggml_tensor * llm_graph_context::build_moe_ffn(
|
||||
const float limit = hparams.swiglu_clamp_exp[il];
|
||||
constexpr float eps = 1e-6f;
|
||||
if (limit > eps) {
|
||||
if (arch == LLM_ARCH_MAPLE || arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_GLM5_NEXT || (arch == LLM_ARCH_DFLASH && hparams.dsv4_hc_mult > 0) || arch == LLM_ARCH_HY_V4) {
|
||||
if (arch == LLM_ARCH_MAPLE || arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_DEEPSEEK41 || arch == LLM_ARCH_GLM5_NEXT || (arch == LLM_ARCH_DFLASH && hparams.dsv4_hc_mult > 0) || arch == LLM_ARCH_HY_V4) {
|
||||
cur = ggml_swiglu_clamp(ctx0, cur, up, limit);
|
||||
} else {
|
||||
up = ggml_clamp(ctx0, up, -limit, limit);
|
||||
|
||||
+15
-3
@@ -10,8 +10,8 @@
|
||||
// bump if necessary
|
||||
#define LLAMA_MAX_LAYERS 512
|
||||
#define LLAMA_MAX_EXPERTS 1024 // Kimi K3
|
||||
#define LLAMA_MAX_PLE_NGRAM 8 // qwen4exp
|
||||
#define LLAMA_MAX_PLE_HEADS 64 // qwen4exp
|
||||
#define LLAMA_MAX_PLE_NGRAM 8 // qwen4exp, deepseek41 (all hash modules)
|
||||
#define LLAMA_MAX_PLE_HEADS 64 // qwen4exp, deepseek41 (all hash modules)
|
||||
|
||||
enum llama_expert_gating_func_type {
|
||||
LLAMA_EXPERT_GATING_FUNC_TYPE_NONE = 0,
|
||||
@@ -61,7 +61,7 @@ struct llama_hparams {
|
||||
// for example: n_embd_out() vs n_embd_out_impl
|
||||
|
||||
bool vocab_only;
|
||||
bool no_alloc;
|
||||
bool no_alloc; // TODO: no_alloc is not a model hyperparameter; move it out of llama_hparams
|
||||
bool rope_finetuned;
|
||||
bool use_par_res;
|
||||
bool swin_norm;
|
||||
@@ -326,12 +326,20 @@ struct llama_hparams {
|
||||
uint32_t dsv4_o_group_count = 0;
|
||||
uint32_t dsv4_o_lora_rank = 0;
|
||||
uint32_t dsv4_hc_mult = 0;
|
||||
bool dsv4_hc_single_pass = false; // DeepSeek-V4.1 DSpark draft: lagged pre-mix, no hc_head
|
||||
uint32_t dsv4_hc_sinkhorn_iters = 0;
|
||||
uint32_t dsv4_hash_layer_count = 0;
|
||||
float dsv4_compress_rope_base = 0.0f;
|
||||
float dsv4_hc_eps = 0.0f;
|
||||
std::array<uint32_t, LLAMA_MAX_LAYERS> dsv4_compress_ratios;
|
||||
|
||||
// deepseek41
|
||||
std::array<int32_t, LLAMA_MAX_LAYERS> dsv41_kv_src; // layer whose compressed KV this layer reads, -1 = none
|
||||
std::array<int32_t, LLAMA_MAX_LAYERS> dsv41_topk_src; // layer whose top-k this layer uses, -1 = none
|
||||
int32_t dsv41_cand_src = -1; // -1 = no candidate block selection
|
||||
uint32_t dsv41_cand_topk_blocks = 0;
|
||||
uint32_t dsv41_cand_block_size = 0;
|
||||
|
||||
// 0 = full rank (DeepSeek-V4)
|
||||
uint32_t hc_low_rank = 0;
|
||||
|
||||
@@ -346,6 +354,10 @@ struct llama_hparams {
|
||||
uint32_t ple_eos_token_id = 0;
|
||||
// the id the PLE hash stands in at image positions; 0 makes the loader fall back to EOS
|
||||
uint32_t ple_image_token_id = 0;
|
||||
// the id the KV cells record at embedding (image) positions for the n-gram history; LLAMA_TOKEN_NULL = none
|
||||
llama_token ple_embd_token = LLAMA_TOKEN_NULL;
|
||||
// hash constant sets, stored back to back: 1 shared by all PLE layers (qwen4exp), or 1 per PLE layer in layer order (deepseek41)
|
||||
uint32_t ple_n_modules = 1;
|
||||
// the file lists PLE layer indices, so this is never a per-layer gguf array and can hold one bit per layer
|
||||
std::bitset<LLAMA_MAX_LAYERS> is_ple_impl;
|
||||
// the hash multipliers reach ~2e13 and have to stay 64-bit
|
||||
|
||||
+255
-47
@@ -333,12 +333,24 @@ static void dsv4_state_read_tensor_streams(
|
||||
}
|
||||
}
|
||||
|
||||
// serialized compressed-KV layers: all except per-device mirrors (kv_src[il] != il), refilled from their source on read
|
||||
static std::vector<uint32_t> dsv4_k_cache_state_layers(const llama_kv_cache * kv, const std::vector<int32_t> & kv_src) {
|
||||
std::vector<uint32_t> res;
|
||||
for (uint32_t il : kv->get_layer_ids()) {
|
||||
if (kv_src[il] == (int32_t) il) {
|
||||
res.push_back(il);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void dsv4_state_write_k_cache(
|
||||
llama_io_write_i & io,
|
||||
const llama_kv_cache * kv,
|
||||
llama_seq_id seq_id,
|
||||
llama_state_seq_flags flags,
|
||||
uint32_t n_rows) {
|
||||
uint32_t n_rows,
|
||||
const std::vector<int32_t> & kv_src) {
|
||||
GGML_UNUSED(flags);
|
||||
|
||||
uint32_t s0;
|
||||
@@ -347,7 +359,8 @@ static void dsv4_state_write_k_cache(
|
||||
|
||||
const uint32_t version = DSV4_K_CACHE_STATE_VER;
|
||||
const uint32_t kv_size = kv->get_size();
|
||||
const auto layer_ids = kv->get_layer_ids();
|
||||
// mirrors are never serialized, so the state format does not depend on the split
|
||||
const auto layer_ids = dsv4_k_cache_state_layers(kv, kv_src);
|
||||
const uint32_t n_layer = layer_ids.size();
|
||||
|
||||
if (n_rows > kv_size) {
|
||||
@@ -369,7 +382,8 @@ static void dsv4_state_read_k_cache(
|
||||
llama_io_read_i & io,
|
||||
llama_kv_cache * kv,
|
||||
llama_seq_id seq_id,
|
||||
llama_state_seq_flags flags) {
|
||||
llama_state_seq_flags flags,
|
||||
const std::vector<int32_t> & kv_src) {
|
||||
GGML_UNUSED(flags);
|
||||
|
||||
uint32_t version;
|
||||
@@ -399,7 +413,7 @@ static void dsv4_state_read_k_cache(
|
||||
uint32_t s0;
|
||||
dsv4_state_dst_stream_range(kv->get_n_stream(), seq_id, ns, s0);
|
||||
|
||||
const auto layer_ids = kv->get_layer_ids();
|
||||
const auto layer_ids = dsv4_k_cache_state_layers(kv, kv_src);
|
||||
if (n_layer_ref != layer_ids.size()) {
|
||||
throw std::runtime_error("DSV4 K-cache layer count mismatch");
|
||||
}
|
||||
@@ -413,6 +427,31 @@ static void dsv4_state_read_k_cache(
|
||||
|
||||
dsv4_state_read_tensor_streams(io, kv->get_k_storage(il), kv_size, n_rows_ref, s0, ns);
|
||||
}
|
||||
|
||||
// refill the mirrors from their source rows just read
|
||||
std::vector<uint8_t> buf;
|
||||
for (uint32_t il : kv->get_layer_ids()) {
|
||||
const int32_t src = kv_src[il];
|
||||
if (src == (int32_t) il) {
|
||||
continue;
|
||||
}
|
||||
ggml_tensor * t_src = kv->get_k_storage(src);
|
||||
ggml_tensor * t_dst = kv->get_k_storage(il);
|
||||
GGML_ASSERT(t_src->type == t_dst->type && t_src->ne[0] == t_dst->ne[0]);
|
||||
|
||||
const size_t row_size = ggml_row_size(t_src->type, t_src->ne[0]);
|
||||
const size_t stream_stride = (size_t) kv_size*row_size;
|
||||
const size_t size = (size_t) n_rows_ref*row_size;
|
||||
if (size == 0) {
|
||||
continue;
|
||||
}
|
||||
buf.resize(size);
|
||||
for (uint32_t s = 0; s < ns; ++s) {
|
||||
const size_t offset = (size_t) (s0 + s)*stream_stride;
|
||||
ggml_backend_tensor_get(t_src, buf.data(), offset, size);
|
||||
ggml_backend_tensor_set(t_dst, buf.data(), offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string dsv4_plan_positions(const std::vector<int32_t> & values) {
|
||||
@@ -432,6 +471,7 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
|
||||
const llama_ubatch & ubatch,
|
||||
uint32_t ratio,
|
||||
bool overlap,
|
||||
bool pad_blocks,
|
||||
uint32_t state_size,
|
||||
uint32_t kv_size,
|
||||
uint32_t n_stream,
|
||||
@@ -548,7 +588,7 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
|
||||
}
|
||||
}
|
||||
|
||||
if (ratio == DSV4_CSA_RATIO && !plan.state_pos.empty()) {
|
||||
if (pad_blocks && !plan.state_pos.empty()) {
|
||||
assert(kv_size > 0);
|
||||
|
||||
// Pad each stream to the reserve plan's block count.
|
||||
@@ -603,7 +643,7 @@ static llama_kv_cache_dsv4_context::comp_plan dsv4_build_comp_plan(
|
||||
}
|
||||
}
|
||||
|
||||
if (ratio == DSV4_HCA_RATIO && !plan.state_pos.empty() && plan.state_write_idxs.empty()) {
|
||||
if (!pad_blocks && !plan.state_pos.empty() && plan.state_write_idxs.empty()) {
|
||||
assert(kv_size > 0);
|
||||
// the last slot must not be live, or the dummy write would corrupt it;
|
||||
// a full stream implies a completed block, which implies real writes
|
||||
@@ -744,6 +784,7 @@ static std::vector<llama_kv_cache_dsv4_context::comp_plan> dsv4_build_comp_plans
|
||||
const std::vector<llama_ubatch> & ubatches,
|
||||
uint32_t ratio,
|
||||
bool overlap,
|
||||
bool pad_blocks,
|
||||
uint32_t state_size,
|
||||
uint32_t kv_size,
|
||||
uint32_t n_stream,
|
||||
@@ -755,7 +796,7 @@ static std::vector<llama_kv_cache_dsv4_context::comp_plan> dsv4_build_comp_plans
|
||||
// the first ubatch touching a seq consumes its rollback restore
|
||||
std::vector<uint32_t> rs(rs_idx);
|
||||
for (const llama_ubatch & ubatch : ubatches) {
|
||||
plans.push_back(dsv4_build_comp_plan(ubatch, ratio, overlap, state_size, kv_size, n_stream, n_rs_seq, rs));
|
||||
plans.push_back(dsv4_build_comp_plan(ubatch, ratio, overlap, pad_blocks, state_size, kv_size, n_stream, n_rs_seq, rs));
|
||||
|
||||
for (uint32_t s = 0; s < ubatch.n_seqs_unq; ++s) {
|
||||
const llama_seq_id seq_id = ubatch.seq_id_unq[s];
|
||||
@@ -905,13 +946,12 @@ llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
uint32_t n_rs_seq,
|
||||
const char * name,
|
||||
const llama_memory_i::layer_filter_cb & filter) :
|
||||
hparams(model.hparams),
|
||||
ratio(ratio),
|
||||
state_size(state_size),
|
||||
n_embd_state(n_embd_state),
|
||||
n_stream(unified ? 1 : n_seq_max),
|
||||
n_rs_seq(n_rs_seq) {
|
||||
const llama_hparams & hparams = model.hparams;
|
||||
|
||||
struct ggml_backend_buft_comparator {
|
||||
bool operator()(const ggml_backend_buffer_type_t & lhs, const ggml_backend_buffer_type_t & rhs) const {
|
||||
return strcmp(ggml_backend_buft_name(lhs), ggml_backend_buft_name(rhs)) < 0;
|
||||
@@ -986,7 +1026,15 @@ llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
}
|
||||
|
||||
for (auto & [buft, ctx] : ctx_map) {
|
||||
ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors_from_buft(ctx.get(), buft);
|
||||
ggml_backend_buffer_t buf;
|
||||
if (hparams.no_alloc) {
|
||||
buf = ggml_backend_buft_alloc_buffer(buft, /*size =*/ 0); // dummy buffer
|
||||
for (ggml_tensor * t = ggml_get_first_tensor(ctx.get()); t != nullptr; t = ggml_get_next_tensor(ctx.get(), t)) {
|
||||
t->buffer = buf; // set dummy buffer for DSV4 state so that the backend scheduler won't try to allocate it
|
||||
}
|
||||
} else {
|
||||
buf = ggml_backend_alloc_ctx_tensors_from_buft(ctx.get(), buft); // real buffer
|
||||
}
|
||||
if (!buf) {
|
||||
throw std::runtime_error("failed to allocate buffer for DSV4 compressor state");
|
||||
}
|
||||
@@ -1056,6 +1104,10 @@ uint32_t llama_dsv4_comp_state::get_ratio() const {
|
||||
return ratio;
|
||||
}
|
||||
|
||||
bool llama_dsv4_comp_state::is_overlap() const {
|
||||
return state_size == 2*ratio;
|
||||
}
|
||||
|
||||
uint32_t llama_dsv4_comp_state::get_state_size() const {
|
||||
return state_size;
|
||||
}
|
||||
@@ -1074,9 +1126,15 @@ uint32_t llama_dsv4_comp_state::get_n_rows() const {
|
||||
|
||||
std::map<ggml_backend_buffer_type_t, size_t> llama_dsv4_comp_state::memory_breakdown() const {
|
||||
std::map<ggml_backend_buffer_type_t, size_t> ret;
|
||||
for (const auto & [_, buf] : ctxs_bufs) {
|
||||
for (const auto & [ctx, buf] : ctxs_bufs) {
|
||||
ggml_backend_buffer_type_t buft = ggml_backend_buffer_get_type(buf.get());
|
||||
ret[buft] += ggml_backend_buffer_get_size(buf.get());
|
||||
|
||||
if (hparams.no_alloc) {
|
||||
GGML_ASSERT(ggml_backend_buffer_get_base(buf.get()) == nullptr);
|
||||
ret[buft] += ggml_backend_alloc_ctx_tensors_from_buft_size(ctx.get(), buft);
|
||||
} else {
|
||||
ret[buft] += ggml_backend_buffer_get_size(buf.get());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1261,6 +1319,23 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
v_trans, offload, swa_full, unified_raw, kv_size, n_seq_max, n_ubatch, n_pad,
|
||||
nullptr, filter_raw, reuse, nullptr);
|
||||
|
||||
// V4.1: CSA slot = ratio 2, HCA slot = ratio 1, no overlap and no indexer compressor; the index K is stored
|
||||
// next to the compressed KV ([kv | index K]) and only the KV source layers own rows
|
||||
const bool is_v41 = model.arch == LLM_ARCH_DEEPSEEK41;
|
||||
const uint32_t ratio_csa = is_v41 ? 2u : DSV4_CSA_RATIO;
|
||||
const uint32_t ratio_hca = is_v41 ? 1u : DSV4_HCA_RATIO;
|
||||
|
||||
if (is_v41) {
|
||||
const uint32_t n_embd_comp = model.hparams.n_embd_head_k() + model.hparams.indexer_head_size;
|
||||
for (llama_hparams * hp : { &hparams_csa, &hparams_hca }) {
|
||||
std::fill(hp->n_head_kv_arr.begin(), hp->n_head_kv_arr.end(), 1);
|
||||
hp->n_embd_head_k_full = n_embd_comp;
|
||||
hp->n_embd_head_v_full = n_embd_comp;
|
||||
hp->n_embd_head_k_swa = n_embd_comp;
|
||||
hp->n_embd_head_v_swa = n_embd_comp;
|
||||
}
|
||||
}
|
||||
|
||||
dsv4_make_k_only(hparams_csa);
|
||||
dsv4_make_k_only(hparams_hca);
|
||||
|
||||
@@ -1272,12 +1347,50 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
hparams_lid.rope_type = LLAMA_ROPE_TYPE_NEOX;
|
||||
dsv4_make_k_only(hparams_lid);
|
||||
|
||||
// V4.1 cross-layer KV sharing: a compressed layer reads the storage of its kv source or, when the source is on
|
||||
// another device (layer split), of a per-device mirror fed with the source's new rows, so the whole cache never
|
||||
// crosses devices. no mirrors without offload
|
||||
const uint32_t n_layer = model.hparams.n_layer();
|
||||
comp_read.assign(n_layer, -1);
|
||||
comp_kv_src.resize(n_layer);
|
||||
for (uint32_t il = 0; il < n_layer; ++il) {
|
||||
const int32_t src = is_v41 ? model.hparams.dsv41_kv_src[il] : (int32_t) il;
|
||||
comp_kv_src[il] = src >= 0 ? src : (int32_t) il;
|
||||
if (model.hparams.dsv4_compress_ratios[il] == 0) {
|
||||
continue;
|
||||
}
|
||||
comp_read[il] = src;
|
||||
if (src == (int32_t) il || !offload || model.dev_layer(src) == model.dev_layer(il)) {
|
||||
continue;
|
||||
}
|
||||
// the first layer of this source on this device holds the mirror
|
||||
comp_read[il] = il;
|
||||
for (int32_t j = src + 1; j < (int32_t) il; ++j) {
|
||||
if (comp_read[j] == j && comp_kv_src[j] == src && model.dev_layer(j) == model.dev_layer(il)) {
|
||||
comp_read[il] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compressed-KV storage: kv sources and mirrors; compressor state: kv sources only
|
||||
const auto is_comp_owner = [&](int32_t il, uint32_t ratio) {
|
||||
return model.hparams.dsv4_compress_ratios[il] == ratio && comp_read[il] == (int32_t) il;
|
||||
};
|
||||
|
||||
const auto is_state_owner = [&](int32_t il, uint32_t ratio) {
|
||||
if (model.hparams.dsv4_compress_ratios[il] != ratio) {
|
||||
return false;
|
||||
}
|
||||
return !is_v41 || model.hparams.dsv41_kv_src[il] == il;
|
||||
};
|
||||
|
||||
const layer_filter_cb filter_csa = [&](int32_t il) {
|
||||
if (filter && !filter(il)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return model.hparams.dsv4_compress_ratios[il] == DSV4_CSA_RATIO;
|
||||
return is_comp_owner(il, ratio_csa);
|
||||
};
|
||||
|
||||
const layer_filter_cb filter_hca = [&](int32_t il) {
|
||||
@@ -1285,52 +1398,67 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
return false;
|
||||
}
|
||||
|
||||
return model.hparams.dsv4_compress_ratios[il] == DSV4_HCA_RATIO;
|
||||
return is_comp_owner(il, ratio_hca);
|
||||
};
|
||||
|
||||
const layer_filter_cb filter_csa_state = [&](int32_t il) {
|
||||
return (!filter || filter(il)) && is_state_owner(il, ratio_csa);
|
||||
};
|
||||
|
||||
const layer_filter_cb filter_hca_state = [&](int32_t il) {
|
||||
return (!filter || filter(il)) && is_state_owner(il, ratio_hca);
|
||||
};
|
||||
|
||||
const layer_filter_cb filter_lid = [&](int32_t il) {
|
||||
return !is_v41 && filter_csa(il);
|
||||
};
|
||||
|
||||
const bool unified_compressed = false;
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 CSA compressed KV cache, size = %u cells\n",
|
||||
__func__, dsv4_comp_size(kv_size, DSV4_CSA_RATIO));
|
||||
__func__, dsv4_comp_size(kv_size, ratio_csa));
|
||||
|
||||
kv_csa = std::make_unique<llama_kv_cache>(
|
||||
model, hparams_csa, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, DSV4_CSA_RATIO), 256u), n_seq_max, n_pad,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_csa), 256u), n_seq_max, n_pad,
|
||||
0, LLAMA_SWA_TYPE_NONE, nullptr, filter_csa, nullptr, nullptr);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 HCA compressed KV cache, size = %u cells\n",
|
||||
__func__, dsv4_comp_size(kv_size, DSV4_HCA_RATIO));
|
||||
__func__, dsv4_comp_size(kv_size, ratio_hca));
|
||||
|
||||
kv_hca = std::make_unique<llama_kv_cache>(
|
||||
model, hparams_hca, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, DSV4_HCA_RATIO), 256u), n_seq_max, n_pad,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_hca), 256u), n_seq_max, n_pad,
|
||||
0, LLAMA_SWA_TYPE_NONE, nullptr, filter_hca, nullptr, nullptr);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 lightning-indexer KV cache, size = %u cells\n",
|
||||
__func__, dsv4_comp_size(kv_size, DSV4_CSA_RATIO));
|
||||
__func__, dsv4_comp_size(kv_size, ratio_csa));
|
||||
|
||||
kv_lid = std::make_unique<llama_kv_cache>(
|
||||
model, hparams_lid, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, DSV4_CSA_RATIO), 256u), n_seq_max, n_pad,
|
||||
0, LLAMA_SWA_TYPE_NONE, nullptr, filter_csa, nullptr, nullptr);
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_csa), 256u), n_seq_max, n_pad,
|
||||
0, LLAMA_SWA_TYPE_NONE, nullptr, filter_lid, nullptr, nullptr);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 CSA compressor state\n", __func__);
|
||||
|
||||
const uint32_t csa_state_size = is_v41 ? ratio_csa : 2*ratio_csa;
|
||||
const uint32_t csa_state_embd = is_v41 ? model.hparams.n_embd_head_k() : 2*model.hparams.n_embd_head_k();
|
||||
|
||||
csa_state = std::make_unique<llama_dsv4_comp_state>(
|
||||
model, offload, unified_compressed, n_seq_max, DSV4_CSA_RATIO, 2*DSV4_CSA_RATIO,
|
||||
2*model.hparams.n_embd_head_k(), n_rs_seq, "csa", filter_csa);
|
||||
model, offload, unified_compressed, n_seq_max, ratio_csa, csa_state_size,
|
||||
csa_state_embd, n_rs_seq, "csa", filter_csa_state);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 HCA compressor state\n", __func__);
|
||||
|
||||
hca_state = std::make_unique<llama_dsv4_comp_state>(
|
||||
model, offload, unified_compressed, n_seq_max, DSV4_HCA_RATIO, DSV4_HCA_RATIO,
|
||||
model.hparams.n_embd_head_k(), n_rs_seq, "hca", filter_hca);
|
||||
model, offload, unified_compressed, n_seq_max, ratio_hca, ratio_hca,
|
||||
model.hparams.n_embd_head_k(), n_rs_seq, "hca", filter_hca_state);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 lightning-indexer compressor state\n", __func__);
|
||||
|
||||
lid_state = std::make_unique<llama_dsv4_comp_state>(
|
||||
model, offload, unified_compressed, n_seq_max, DSV4_CSA_RATIO, 2*DSV4_CSA_RATIO,
|
||||
2*model.hparams.indexer_head_size, n_rs_seq, "lid", filter_csa);
|
||||
2*model.hparams.indexer_head_size, n_rs_seq, "lid", filter_lid);
|
||||
|
||||
// DSV4 attention reads compressed-K / compressor-state rows that the current
|
||||
// graph does not necessarily overwrite; uninitialized buffer contents would
|
||||
@@ -1339,12 +1467,76 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
clear_compressed(-1, true);
|
||||
}
|
||||
|
||||
// Engram hashes the n-gram ending at each token, so a token shared by several sequences (llama-batched prompt)
|
||||
// is well-defined only if all of them agree on its predecessors; returns false (and logs) otherwise
|
||||
static bool dsv41_shared_history_ok(const llama_batch & batch, const llama_kv_cache * kv, uint32_t n_hist) {
|
||||
if (n_hist == 0 || !batch.token || !batch.n_seq_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool any_shared = false;
|
||||
for (int32_t i = 0; i < batch.n_tokens && !any_shared; ++i) {
|
||||
any_shared = batch.n_seq_id[i] > 1;
|
||||
}
|
||||
if (!any_shared) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::pair<llama_seq_id, llama_pos>, llama_token> in_batch;
|
||||
for (int32_t i = 0; i < batch.n_tokens; ++i) {
|
||||
for (int32_t s = 0; s < batch.n_seq_id[i]; ++s) {
|
||||
in_batch[{ batch.seq_id[i][s], batch.pos[i] }] = batch.token[i];
|
||||
}
|
||||
}
|
||||
|
||||
const auto tok_at = [&](llama_seq_id seq_id, llama_pos p) -> llama_token {
|
||||
// batch first: the batch continues the cache
|
||||
auto it = in_batch.upper_bound({ seq_id, p });
|
||||
if (it != in_batch.begin()) {
|
||||
--it;
|
||||
if (it->first.first == seq_id) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return kv ? kv->get_cells(seq_id).seq_pos_tok_le(seq_id, p) : LLAMA_TOKEN_NULL;
|
||||
};
|
||||
|
||||
for (int32_t i = 0; i < batch.n_tokens; ++i) {
|
||||
if (batch.n_seq_id[i] <= 1) {
|
||||
continue;
|
||||
}
|
||||
for (uint32_t d = 1; d <= n_hist; ++d) {
|
||||
const llama_pos p = batch.pos[i] - (llama_pos) d;
|
||||
if (p < 0) {
|
||||
break;
|
||||
}
|
||||
const llama_token t0 = tok_at(batch.seq_id[i][0], p);
|
||||
for (int32_t s = 1; s < batch.n_seq_id[i]; ++s) {
|
||||
const llama_token ts = tok_at(batch.seq_id[i][s], p);
|
||||
if (ts != t0) {
|
||||
LLAMA_LOG_ERROR("%s: token %d at pos %d is shared by seq %d and seq %d, whose Engram n-gram histories "
|
||||
"differ at pos %d (%d vs %d); decode such tokens per sequence\n", __func__,
|
||||
i, batch.pos[i], batch.seq_id[i][0], batch.seq_id[i][s], p, t0, ts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
llama_memory_context_ptr llama_kv_cache_dsv4::init_batch(
|
||||
llama_batch_allocr & balloc,
|
||||
uint32_t n_ubatch,
|
||||
bool embd_all) {
|
||||
GGML_UNUSED(embd_all);
|
||||
|
||||
if (hparams_raw.ple_n_heads > 0 &&
|
||||
!dsv41_shared_history_ok(balloc.get_batch(), kv_raw->get_swa(), hparams_raw.ple_ngram_size - 1)) {
|
||||
return std::make_unique<llama_kv_cache_dsv4_context>(LLAMA_MEMORY_STATUS_FAILED_PREPARE);
|
||||
}
|
||||
|
||||
const bool raw_per_seq = kv_raw->get_base()->get_n_stream() != 1;
|
||||
const bool comp_per_seq = csa_state->get_n_stream() > 1;
|
||||
const bool has_coupled = dsv4_batch_has_coupled(balloc.get_batch());
|
||||
@@ -1475,9 +1667,9 @@ bool llama_kv_cache_dsv4::seq_rm(llama_seq_id seq_id, llama_pos p0, llama_pos p1
|
||||
bool res = true;
|
||||
|
||||
res = res & kv_raw->seq_rm(seq_id, p0, -1);
|
||||
res = res & kv_csa->seq_rm(seq_id, p0/DSV4_CSA_RATIO, -1);
|
||||
res = res & kv_hca->seq_rm(seq_id, p0/DSV4_HCA_RATIO, -1);
|
||||
res = res & kv_lid->seq_rm(seq_id, p0/DSV4_CSA_RATIO, -1);
|
||||
res = res & kv_csa->seq_rm(seq_id, p0/csa_state->get_ratio(), -1);
|
||||
res = res & kv_hca->seq_rm(seq_id, p0/hca_state->get_ratio(), -1);
|
||||
res = res & kv_lid->seq_rm(seq_id, p0/lid_state->get_ratio(), -1);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -1613,15 +1805,15 @@ void llama_kv_cache_dsv4::state_write(llama_io_write_i & io, llama_seq_id seq_id
|
||||
|
||||
//FIXME : note that we conflate token positions with rows, which is not true for multi-modal case.
|
||||
const uint32_t n_rows_csa = seq_id >= 0 ?
|
||||
dsv4_state_n_used_k_rows(pos_max, DSV4_CSA_RATIO, kv_csa->get_size()) : kv_csa->get_size();
|
||||
dsv4_state_n_used_k_rows(pos_max, csa_state->get_ratio(), kv_csa->get_size()) : kv_csa->get_size();
|
||||
const uint32_t n_rows_hca = seq_id >= 0 ?
|
||||
dsv4_state_n_used_k_rows(pos_max, DSV4_HCA_RATIO, kv_hca->get_size()) : kv_hca->get_size();
|
||||
dsv4_state_n_used_k_rows(pos_max, hca_state->get_ratio(), kv_hca->get_size()) : kv_hca->get_size();
|
||||
const uint32_t n_rows_lid = seq_id >= 0 ?
|
||||
dsv4_state_n_used_k_rows(pos_max, DSV4_CSA_RATIO, kv_lid->get_size()) : kv_lid->get_size();
|
||||
dsv4_state_n_used_k_rows(pos_max, lid_state->get_ratio(), kv_lid->get_size()) : kv_lid->get_size();
|
||||
|
||||
dsv4_state_write_k_cache(io, kv_csa.get(), seq_id, flags, n_rows_csa);
|
||||
dsv4_state_write_k_cache(io, kv_hca.get(), seq_id, flags, n_rows_hca);
|
||||
dsv4_state_write_k_cache(io, kv_lid.get(), seq_id, flags, n_rows_lid);
|
||||
dsv4_state_write_k_cache(io, kv_csa.get(), seq_id, flags, n_rows_csa, comp_kv_src);
|
||||
dsv4_state_write_k_cache(io, kv_hca.get(), seq_id, flags, n_rows_hca, comp_kv_src);
|
||||
dsv4_state_write_k_cache(io, kv_lid.get(), seq_id, flags, n_rows_lid, comp_kv_src);
|
||||
}
|
||||
|
||||
csa_state->state_write(io, seq_id, flags, rs_idx);
|
||||
@@ -1659,9 +1851,9 @@ void llama_kv_cache_dsv4::state_read(llama_io_read_i & io, llama_seq_id seq_id,
|
||||
if (!partial_only) {
|
||||
clear_compressed(seq_id, true);
|
||||
|
||||
dsv4_state_read_k_cache(io, kv_csa.get(), seq_id, flags);
|
||||
dsv4_state_read_k_cache(io, kv_hca.get(), seq_id, flags);
|
||||
dsv4_state_read_k_cache(io, kv_lid.get(), seq_id, flags);
|
||||
dsv4_state_read_k_cache(io, kv_csa.get(), seq_id, flags, comp_kv_src);
|
||||
dsv4_state_read_k_cache(io, kv_hca.get(), seq_id, flags, comp_kv_src);
|
||||
dsv4_state_read_k_cache(io, kv_lid.get(), seq_id, flags, comp_kv_src);
|
||||
}
|
||||
|
||||
csa_state->state_read(io, seq_id, flags);
|
||||
@@ -1704,6 +1896,10 @@ llama_dsv4_comp_state * llama_kv_cache_dsv4::get_lid_state() const {
|
||||
return lid_state.get();
|
||||
}
|
||||
|
||||
int32_t llama_kv_cache_dsv4::get_comp_read_layer(int32_t il) const {
|
||||
return comp_read[il];
|
||||
}
|
||||
|
||||
uint32_t llama_kv_cache_dsv4::get_n_rs_seq() const {
|
||||
return n_rs_seq;
|
||||
}
|
||||
@@ -1932,6 +2128,11 @@ void llama_kv_cache_dsv4_raw_context::set_input_kq_mask(ggml_tensor * dst, const
|
||||
kv_swa->set_input_kq_mask(dst, ubatch, causal_attn);
|
||||
}
|
||||
|
||||
void llama_kv_cache_dsv4_raw_context::get_prev_tokens(const llama_ubatch & ubatch, uint32_t n, std::vector<llama_token> & res) const {
|
||||
GGML_ASSERT(kv_swa != nullptr);
|
||||
kv_swa->get_prev_tokens(ubatch, n, res);
|
||||
}
|
||||
|
||||
void llama_kv_cache_dsv4_raw_context::set_input_k_rot(ggml_tensor * dst) const {
|
||||
kv_swa->set_input_k_rot(dst);
|
||||
}
|
||||
@@ -2016,7 +2217,8 @@ llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
reserve_plans(true),
|
||||
status(llama_memory_status_combine(
|
||||
llama_memory_status_combine(ctx_raw->get_status(), ctx_csa_mem->get_status()),
|
||||
llama_memory_status_combine(ctx_hca_mem->get_status(), ctx_lid_mem->get_status()))) {
|
||||
llama_memory_status_combine(ctx_hca_mem->get_status(), ctx_lid_mem->get_status()))),
|
||||
kv(kv) {
|
||||
}
|
||||
|
||||
llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
@@ -2041,7 +2243,8 @@ llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
llama_memory_status_combine(ctx_raw->get_status(), ctx_csa_mem->get_status()),
|
||||
llama_memory_status_combine(ctx_hca_mem->get_status(), ctx_lid_mem->get_status())),
|
||||
this->sc_info_csa.empty() && this->sc_info_hca.empty() && this->sc_info_lid.empty() ?
|
||||
LLAMA_MEMORY_STATUS_NO_UPDATE : LLAMA_MEMORY_STATUS_SUCCESS)) {
|
||||
LLAMA_MEMORY_STATUS_NO_UPDATE : LLAMA_MEMORY_STATUS_SUCCESS)),
|
||||
kv(kv) {
|
||||
}
|
||||
|
||||
llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
@@ -2052,13 +2255,13 @@ llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
std::vector<llama_ubatch> ubatches,
|
||||
std::vector<llama_ubatch> ubatches_raw) :
|
||||
ubatches(std::move(ubatches)),
|
||||
plans_csa(dsv4_build_comp_plans(this->ubatches, DSV4_CSA_RATIO, true,
|
||||
plans_csa(dsv4_build_comp_plans(this->ubatches, kv->get_csa_state()->get_ratio(), kv->get_csa_state()->is_overlap(), true,
|
||||
kv->get_csa_state()->get_state_size(), kv->get_csa()->get_size(), kv->get_csa_state()->get_n_stream(),
|
||||
kv->get_n_rs_seq(), kv->get_rs_idx())),
|
||||
plans_hca(dsv4_build_comp_plans(this->ubatches, DSV4_HCA_RATIO, false,
|
||||
plans_hca(dsv4_build_comp_plans(this->ubatches, kv->get_hca_state()->get_ratio(), kv->get_hca_state()->is_overlap(), false,
|
||||
kv->get_hca_state()->get_state_size(), kv->get_hca()->get_size(), kv->get_hca_state()->get_n_stream(),
|
||||
kv->get_n_rs_seq(), kv->get_rs_idx())),
|
||||
plans_lid(dsv4_build_comp_plans(this->ubatches, DSV4_CSA_RATIO, true,
|
||||
plans_lid(dsv4_build_comp_plans(this->ubatches, kv->get_lid_state()->get_ratio(), kv->get_lid_state()->is_overlap(), true,
|
||||
kv->get_lid_state()->get_state_size(), kv->get_lid()->get_size(), kv->get_lid_state()->get_n_stream(),
|
||||
kv->get_n_rs_seq(), kv->get_rs_idx())),
|
||||
ctx_raw(std::make_unique<llama_kv_cache_dsv4_raw_context>(
|
||||
@@ -2086,7 +2289,8 @@ llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
csa_state(kv->get_csa_state()),
|
||||
hca_state(kv->get_hca_state()),
|
||||
lid_state(kv->get_lid_state()),
|
||||
status(ctx_raw->get_status()) {
|
||||
status(ctx_raw->get_status()),
|
||||
kv(kv) {
|
||||
kv->reset_rs_idx_for_ubatches(this->ubatches);
|
||||
}
|
||||
|
||||
@@ -2181,6 +2385,10 @@ const llama_dsv4_comp_state * llama_kv_cache_dsv4_context::get_lid_state() const
|
||||
return lid_state;
|
||||
}
|
||||
|
||||
int32_t llama_kv_cache_dsv4_context::get_comp_read_layer(int32_t il) const {
|
||||
return kv->get_comp_read_layer(il);
|
||||
}
|
||||
|
||||
const llama_kv_cache_dsv4_context::comp_plan & llama_kv_cache_dsv4_context::get_csa_plan() const {
|
||||
assert(status == LLAMA_MEMORY_STATUS_SUCCESS);
|
||||
|
||||
@@ -2222,7 +2430,7 @@ const llama_kv_cache_dsv4_context::comp_plan & llama_kv_cache_dsv4_context::get_
|
||||
}
|
||||
|
||||
reserve_plan_csa = dsv4_build_reserve_comp_plan(
|
||||
ubatch, DSV4_CSA_RATIO, true,
|
||||
ubatch, csa_state->get_ratio(), csa_state->is_overlap(),
|
||||
csa_state->get_state_size(), get_csa()->get_n_kv(), csa_state->get_n_stream(), csa_state->get_n_rs_seq());
|
||||
|
||||
return reserve_plan_csa;
|
||||
@@ -2236,7 +2444,7 @@ const llama_kv_cache_dsv4_context::comp_plan & llama_kv_cache_dsv4_context::get_
|
||||
}
|
||||
|
||||
reserve_plan_hca = dsv4_build_reserve_comp_plan(
|
||||
ubatch, DSV4_HCA_RATIO, false,
|
||||
ubatch, hca_state->get_ratio(), hca_state->is_overlap(),
|
||||
hca_state->get_state_size(), get_hca()->get_n_kv(), hca_state->get_n_stream(), hca_state->get_n_rs_seq());
|
||||
|
||||
return reserve_plan_hca;
|
||||
@@ -2250,7 +2458,7 @@ const llama_kv_cache_dsv4_context::comp_plan & llama_kv_cache_dsv4_context::get_
|
||||
}
|
||||
|
||||
reserve_plan_lid = dsv4_build_reserve_comp_plan(
|
||||
ubatch, DSV4_CSA_RATIO, true,
|
||||
ubatch, lid_state->get_ratio(), lid_state->is_overlap(),
|
||||
lid_state->get_state_size(), get_lid()->get_n_kv(), lid_state->get_n_stream(), lid_state->get_n_rs_seq());
|
||||
|
||||
return reserve_plan_lid;
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
void apply_copies(const stream_copy_info & sc_info) const;
|
||||
|
||||
uint32_t get_ratio() const;
|
||||
bool is_overlap() const; // V4 CSA: state holds 2*ratio rows of 2x width
|
||||
uint32_t get_state_size() const;
|
||||
uint32_t get_n_stream() const;
|
||||
uint32_t get_n_rs_seq() const;
|
||||
@@ -60,6 +61,8 @@ private:
|
||||
std::vector<ggml_tensor *> score_stream;
|
||||
};
|
||||
|
||||
const llama_hparams & hparams;
|
||||
|
||||
const uint32_t ratio;
|
||||
const uint32_t state_size;
|
||||
const uint32_t n_embd_state;
|
||||
@@ -148,6 +151,9 @@ public:
|
||||
llama_dsv4_comp_state * get_hca_state() const;
|
||||
llama_dsv4_comp_state * get_lid_state() const;
|
||||
|
||||
// layer whose compressed-KV storage `il` reads, -1 = no compressed KV
|
||||
int32_t get_comp_read_layer(int32_t il) const;
|
||||
|
||||
uint32_t get_n_rs_seq() const;
|
||||
const std::vector<uint32_t> & get_rs_idx() const;
|
||||
void reset_rs_idx_for_ubatches(const std::vector<llama_ubatch> & ubatches);
|
||||
@@ -163,6 +169,12 @@ private:
|
||||
|
||||
std::vector<uint32_t> rs_idx;
|
||||
|
||||
// [n_layer] storage layer -> layer whose rows it holds (itself, or the source of a mirror)
|
||||
std::vector<int32_t> comp_kv_src;
|
||||
|
||||
// [n_layer] layer -> layer whose compressed-KV storage it reads (itself, its V4.1 kv source or a mirror), -1 = none
|
||||
std::vector<int32_t> comp_read;
|
||||
|
||||
std::unique_ptr<llama_kv_cache_iswa> kv_raw;
|
||||
std::unique_ptr<llama_kv_cache> kv_csa;
|
||||
std::unique_ptr<llama_kv_cache> kv_hca;
|
||||
@@ -214,6 +226,9 @@ public:
|
||||
void set_input_kq_mask(ggml_tensor * dst, const llama_ubatch * ubatch, bool causal_attn) const;
|
||||
void set_input_k_rot(ggml_tensor * dst) const;
|
||||
|
||||
// token history of the raw (window) cells, for host-side n-gram hashing (deepseek41 Engram)
|
||||
void get_prev_tokens(const llama_ubatch & ubatch, uint32_t n, std::vector<llama_token> & res) const;
|
||||
|
||||
private:
|
||||
size_t i_next = 0;
|
||||
|
||||
@@ -371,6 +386,8 @@ public:
|
||||
const comp_plan & get_hca_plan(const llama_ubatch & ubatch) const;
|
||||
const comp_plan & get_lid_plan(const llama_ubatch & ubatch) const;
|
||||
|
||||
int32_t get_comp_read_layer(int32_t il) const;
|
||||
|
||||
private:
|
||||
size_t i_next = 0;
|
||||
|
||||
@@ -403,4 +420,6 @@ private:
|
||||
mutable comp_plan reserve_plan_lid;
|
||||
|
||||
const llama_memory_status status;
|
||||
|
||||
const llama_kv_cache_dsv4 * kv = nullptr;
|
||||
};
|
||||
|
||||
@@ -1168,9 +1168,7 @@ void llama_kv_cache::apply_ubatch(const slot_info & sinfo, const llama_ubatch &
|
||||
} else if (hparams.ple_n_heads > 0) {
|
||||
// embd batch (multimodal input) has no token ids, need to pad it with the correct ID for PLE layers
|
||||
// TODO @ngxson : check if we can do the same as gemma 3n / gemma 4
|
||||
ext.tok = hparams.ple_image_token_id != 0
|
||||
? (llama_token) hparams.ple_image_token_id
|
||||
: (llama_token) hparams.ple_eos_token_id;
|
||||
ext.tok = hparams.ple_embd_token;
|
||||
}
|
||||
|
||||
cells.ext_set(idx, ext);
|
||||
@@ -1849,7 +1847,7 @@ void llama_kv_cache::set_input_v_rot(ggml_tensor * dst) const {
|
||||
}
|
||||
|
||||
bool llama_kv_cache::has_cell_ext() const {
|
||||
// M-RoPE needs the 2D position, the PLE n-gram hash needs the token id
|
||||
// M-RoPE needs the 2D position, the PLE / Engram n-gram hashes need the token id
|
||||
return hparams.n_pos_per_embd() > 1 || hparams.ple_n_heads > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,15 @@ llama_memory_recurrent::llama_memory_recurrent(
|
||||
|
||||
// allocate tensors and initialize the buffers to avoid NaNs in the padding
|
||||
for (auto & [buft, ctx] : ctx_map) {
|
||||
ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors_from_buft(ctx.get(), buft);
|
||||
ggml_backend_buffer_t buf;
|
||||
if (hparams.no_alloc) {
|
||||
buf = ggml_backend_buft_alloc_buffer(buft, /*size =*/ 0); // dummy buffer
|
||||
for (ggml_tensor * t = ggml_get_first_tensor(ctx.get()); t != nullptr; t = ggml_get_next_tensor(ctx.get(), t)) {
|
||||
t->buffer = buf; // set dummy buffer for RS cache so that the backend scheduler won't try to allocate it
|
||||
}
|
||||
} else {
|
||||
buf = ggml_backend_alloc_ctx_tensors_from_buft(ctx.get(), buft); // real buffer
|
||||
}
|
||||
if (!buf) {
|
||||
throw std::runtime_error("failed to allocate buffer for rs cache");
|
||||
}
|
||||
@@ -435,8 +443,15 @@ void llama_memory_recurrent::set_rs_idx(llama_seq_id seq_id, uint32_t idx) {
|
||||
|
||||
std::map<ggml_backend_buffer_type_t, size_t> llama_memory_recurrent::memory_breakdown() const {
|
||||
std::map<ggml_backend_buffer_type_t, size_t> ret;
|
||||
for (const auto & [_, buf] : ctxs_bufs) {
|
||||
ret[ggml_backend_buffer_get_type(buf.get())] += ggml_backend_buffer_get_size(buf.get());
|
||||
for (const auto & [ctx, buf] : ctxs_bufs) {
|
||||
ggml_backend_buffer_type_t buft = ggml_backend_buffer_get_type(buf.get());
|
||||
|
||||
if (hparams.no_alloc) {
|
||||
GGML_ASSERT(ggml_backend_buffer_get_base(buf.get()) == nullptr);
|
||||
ret[buft] += ggml_backend_alloc_ctx_tensors_from_buft_size(ctx.get(), buft);
|
||||
} else {
|
||||
ret[buft] += ggml_backend_buffer_get_size(buf.get());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -414,6 +414,7 @@ namespace GGUFMeta {
|
||||
template bool llama_model_loader::get_arr<std::vector<int32_t>>(enum llm_kv kid, std::vector<int32_t> & result, bool required);
|
||||
template bool llama_model_loader::get_arr<std::array<uint32_t, LLAMA_MAX_LAYERS>>(enum llm_kv kid, std::array<uint32_t, LLAMA_MAX_LAYERS> & result, bool required);
|
||||
template bool llama_model_loader::get_arr<std::vector<uint32_t>>(enum llm_kv kid, std::vector<uint32_t> & result, bool required);
|
||||
template bool llama_model_loader::get_arr<std::vector<uint64_t>>(enum llm_kv kid, std::vector<uint64_t> & result, bool required);
|
||||
template bool llama_model_loader::get_arr<std::array<uint64_t, LLAMA_MAX_PLE_NGRAM>>(enum llm_kv kid, std::array<uint64_t, LLAMA_MAX_PLE_NGRAM> & result, bool required);
|
||||
template bool llama_model_loader::get_arr<std::array<uint64_t, LLAMA_MAX_PLE_HEADS>>(enum llm_kv kid, std::array<uint64_t, LLAMA_MAX_PLE_HEADS> & result, bool required);
|
||||
|
||||
@@ -441,6 +442,7 @@ namespace GGUFMeta {
|
||||
template bool llama_model_loader::get_key<bool> (enum llm_kv kid, bool & result, bool required);
|
||||
template bool llama_model_loader::get_key<float> (enum llm_kv kid, float & result, bool required);
|
||||
template bool llama_model_loader::get_key<uint32_t> (enum llm_kv kid, uint32_t & result, bool required);
|
||||
template bool llama_model_loader::get_key<int32_t> (enum llm_kv kid, int32_t & result, bool required);
|
||||
template bool llama_model_loader::get_key<std::string>(enum llm_kv kid, std::string & result, bool required);
|
||||
|
||||
template<>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "llama-hparams.h"
|
||||
#include "llama-model.h"
|
||||
#include "llama-vocab.h"
|
||||
#include "models/models.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -122,6 +123,7 @@ void llama_model_saver::add_kv(const enum llm_kv key, const Container & value, c
|
||||
template void llama_model_saver::add_kv<std::vector<uint32_t>>(const enum llm_kv, const std::vector<uint32_t> &, const bool);
|
||||
template void llama_model_saver::add_kv<std::vector<float>>(const enum llm_kv, const std::vector<float> &, const bool);
|
||||
template void llama_model_saver::add_kv<std::vector<uint64_t>>(const enum llm_kv, const std::vector<uint64_t> &, const bool);
|
||||
template void llama_model_saver::add_kv<std::vector<int32_t>>(const enum llm_kv, const std::vector<int32_t> &, const bool);
|
||||
|
||||
void llama_model_saver::add_kv(const enum llm_kv key, const std::vector<std::string> & value) {
|
||||
std::vector<const char *> tmp(value.size());
|
||||
@@ -339,6 +341,32 @@ void llama_model_saver::add_kv_from_model() {
|
||||
add_kv(LLM_KV_HASH_LAYER_COUNT, hparams.dsv4_hash_layer_count);
|
||||
add_kv(LLM_KV_HYPER_CONNECTION_LOW_RANK, hparams.hc_low_rank);
|
||||
|
||||
if (model->arch == LLM_ARCH_DEEPSEEK41) {
|
||||
const auto * m41 = dynamic_cast<const llama_model_deepseek41 *>(model);
|
||||
GGML_ASSERT(m41 != nullptr);
|
||||
std::vector<uint32_t> kv_src;
|
||||
std::vector<uint32_t> idx_src;
|
||||
for (uint32_t il = 0; il < hparams.n_layer(); ++il) {
|
||||
if (hparams.dsv41_kv_src[il] == (int32_t) il) {
|
||||
kv_src.push_back(il);
|
||||
}
|
||||
if (hparams.dsv41_topk_src[il] == (int32_t) il) {
|
||||
idx_src.push_back(il);
|
||||
}
|
||||
}
|
||||
add_kv(LLM_KV_ATTENTION_KV_SOURCE_LAYERS, kv_src);
|
||||
add_kv(LLM_KV_ATTENTION_INDEXER_SOURCE_LAYERS, idx_src);
|
||||
if (hparams.dsv41_cand_src >= 0) {
|
||||
add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_SOURCE_LAYER, hparams.dsv41_cand_src);
|
||||
add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_TOP_K_BLOCKS, hparams.dsv41_cand_topk_blocks);
|
||||
add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_BLOCK_SIZE, hparams.dsv41_cand_block_size);
|
||||
}
|
||||
if (hparams.ple_n_heads > 0) {
|
||||
add_kv(LLM_KV_ENGRAM_PAD_TOKEN_ID, m41->engram_pad_id);
|
||||
add_kv(LLM_KV_ENGRAM_TOKEN_MAP, m41->engram_token_map);
|
||||
}
|
||||
}
|
||||
|
||||
// the PLE group only means anything whole: write all of it or none
|
||||
if (hparams.ple_n_heads > 0) {
|
||||
std::vector<uint32_t> ple_layers;
|
||||
@@ -355,13 +383,13 @@ void llama_model_saver::add_kv_from_model() {
|
||||
add_kv(LLM_KV_EMBEDDING_LENGTH_PER_LAYER, hparams.ple_head_dim);
|
||||
add_kv(LLM_KV_PLE_LAYER_MULTIPLIERS, std::vector<uint64_t>(
|
||||
hparams.ple_layer_multipliers.begin(),
|
||||
hparams.ple_layer_multipliers.begin() + hparams.ple_ngram_size));
|
||||
hparams.ple_layer_multipliers.begin() + hparams.ple_n_modules*hparams.ple_ngram_size));
|
||||
add_kv(LLM_KV_PLE_HEAD_OFFSETS, std::vector<uint64_t>(
|
||||
hparams.ple_head_offsets.begin(),
|
||||
hparams.ple_head_offsets.begin() + hparams.ple_n_heads));
|
||||
hparams.ple_head_offsets.begin() + hparams.ple_n_modules*hparams.ple_n_heads));
|
||||
add_kv(LLM_KV_PLE_HEAD_VOCAB_SIZES, std::vector<uint64_t>(
|
||||
hparams.ple_head_vocab_sizes.begin(),
|
||||
hparams.ple_head_vocab_sizes.begin() + hparams.ple_n_heads));
|
||||
hparams.ple_head_vocab_sizes.begin() + hparams.ple_n_modules*hparams.ple_n_heads));
|
||||
}
|
||||
|
||||
const float rope_scaling_factor = hparams.rope_freq_scale_train == 1.0f ? 0.0f : 1.0f/hparams.rope_freq_scale_train;
|
||||
|
||||
+6
-2
@@ -212,6 +212,8 @@ static llama_model * llama_model_mapping(llm_arch arch, const llama_model_params
|
||||
return new llama_model_dots3note(params);
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
return new llama_model_deepseek4(params);
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
return new llama_model_deepseek41(params);
|
||||
case LLM_ARCH_GLM_DSA:
|
||||
return new llama_model_glm_dsa(params);
|
||||
case LLM_ARCH_MISTRAL4:
|
||||
@@ -398,7 +400,7 @@ struct ggml_backend_meta_split_state llama_meta_device_get_split_state(const str
|
||||
const llama_meta_device_get_split_state_userdata * ud = (const llama_meta_device_get_split_state_userdata *) userdata;
|
||||
const llama_hparams & hparams = ud->model->hparams;
|
||||
const std::string tensor_name = tensor->name;
|
||||
const bool is_dsv4 = ud->model->arch == LLM_ARCH_DEEPSEEK4 ||
|
||||
const bool is_dsv4 = ud->model->arch == LLM_ARCH_DEEPSEEK4 || ud->model->arch == LLM_ARCH_DEEPSEEK41 ||
|
||||
(ud->model->arch == LLM_ARCH_DFLASH && hparams.dsv4_hc_mult > 0);
|
||||
|
||||
static const std::regex pattern_q_weight ("blk\\.\\d*\\.attn_q.weight");
|
||||
@@ -3032,6 +3034,7 @@ llama_memory_i * llama_model::create_memory(const llama_memory_params & params,
|
||||
nullptr);
|
||||
}
|
||||
} break;
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
{
|
||||
GGML_ASSERT(hparams.swa_type != LLAMA_SWA_TYPE_NONE);
|
||||
@@ -3441,7 +3444,7 @@ int32_t llama_model_n_head_kv(const llama_model * model) {
|
||||
int32_t llama_model_n_swa(const llama_model * model) {
|
||||
// dsv4 kv-cache has SWA but it cannot be used as a rollback because of
|
||||
// other compression ratios, so we return 0 here
|
||||
if (model->arch == LLM_ARCH_DEEPSEEK4) {
|
||||
if (model->arch == LLM_ARCH_DEEPSEEK4 || model->arch == LLM_ARCH_DEEPSEEK41) {
|
||||
return 0;
|
||||
}
|
||||
return model->hparams.n_swa;
|
||||
@@ -3536,6 +3539,7 @@ llama_rope_type llama_model_rope_type(const llama_model * model) {
|
||||
case LLM_ARCH_DEEPSEEK2OCR:
|
||||
case LLM_ARCH_DEEPSEEK32:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_MUSE_GLIMMER:
|
||||
case LLM_ARCH_MUSE_SPARK:
|
||||
case LLM_ARCH_PLM:
|
||||
|
||||
@@ -605,6 +605,10 @@ struct llama_layer {
|
||||
struct ggml_tensor * indexer_kpool_gate = nullptr;
|
||||
struct ggml_tensor * indexer_kpool_ape = nullptr;
|
||||
|
||||
// deepseek41
|
||||
struct ggml_tensor * engram_kv = nullptr;
|
||||
struct ggml_tensor * engram_gate = nullptr; // q_weight*k_weight folded
|
||||
|
||||
// MSA
|
||||
struct ggml_tensor * index_q_proj = nullptr;
|
||||
struct ggml_tensor * index_k_proj = nullptr;
|
||||
@@ -619,6 +623,10 @@ struct llama_layer {
|
||||
struct ggml_tensor * hc_ffn_down = nullptr;
|
||||
struct ggml_tensor * hc_ffn_up = nullptr;
|
||||
struct ggml_tensor * hc_ffn_inject = nullptr;
|
||||
struct ggml_tensor * hc_attn_down_inject = nullptr; // [hc_attn_down | hc_attn_inject] in one weight
|
||||
struct ggml_tensor * hc_ffn_down_inject = nullptr; // [hc_ffn_down | hc_ffn_inject] in one weight
|
||||
struct ggml_tensor * wqkv_z = nullptr; // [wqkv | wqkv_gate] in one weight
|
||||
struct ggml_tensor * ssm_beta_alpha_cat = nullptr; // [ssm_beta | ssm_alpha] in one weight, unlike the grouped ssm_beta_alpha
|
||||
|
||||
struct ggml_tensor * ple_key = nullptr;
|
||||
struct ggml_tensor * ple_value = nullptr;
|
||||
|
||||
+17
-12
@@ -125,7 +125,8 @@ static tensor_category tensor_get_category(const std::string & tensor_name) {
|
||||
if (tensor_name_match_token_embd(tensor_name.c_str())) {
|
||||
return tensor_category::TOKEN_EMBD;
|
||||
}
|
||||
if (tensor_name.find("attn_qkv.weight") != std::string::npos) {
|
||||
// attn_qkv_gate is [attn_qkv | attn_gate] in one weight, the qkv rows decide its type
|
||||
if (tensor_name.find("attn_qkv.weight") != std::string::npos || tensor_name.find("attn_qkv_gate.weight") != std::string::npos) {
|
||||
return tensor_category::ATTENTION_QKV;
|
||||
}
|
||||
if (tensor_name.find("attn_kv_b.weight") != std::string::npos) {
|
||||
@@ -161,6 +162,12 @@ static tensor_category tensor_get_category(const std::string & tensor_name) {
|
||||
return tensor_category::OTHER;
|
||||
}
|
||||
|
||||
// check if tensor is a MoE expert tensor (used by GGML_OP_MUL_MAT_ID)
|
||||
static bool tensor_is_expert(const std::string & tensor_name) {
|
||||
return tensor_name.find("_exps.") != std::string::npos ||
|
||||
tensor_name.find("_chexps.") != std::string::npos;
|
||||
}
|
||||
|
||||
// check if category is for attention-v-like tensors (more sensitive to quantization)
|
||||
static bool category_is_attn_v(tensor_category cat) {
|
||||
return cat == tensor_category::ATTENTION_V ||
|
||||
@@ -296,7 +303,7 @@ static bool tensor_allows_quantization(const llama_model_quantize_params * param
|
||||
// trivial checks first -- no string ops needed
|
||||
if (params->only_copy) return false;
|
||||
|
||||
// quantize only 2D and 3D tensors (experts)
|
||||
// quantize only 2D+ tensors (matrices, experts, conv kernels)
|
||||
if (ggml_n_dims(tensor) < 2) return false;
|
||||
|
||||
const std::string name = ggml_get_name(tensor);
|
||||
@@ -316,6 +323,9 @@ static bool tensor_allows_quantization(const llama_model_quantize_params * param
|
||||
// do not quantize the i32 token-id -> expert-id routing table (DeepSeek-V4)
|
||||
quantize &= name.find("ffn_gate_tid2eid.weight") == std::string::npos;
|
||||
|
||||
// Engram gate is an elementwise weight (ggml_mul), not a matmul operand
|
||||
quantize &= name.find("engram_gate.weight") == std::string::npos;
|
||||
|
||||
// these are very small (e.g. 4x4)
|
||||
quantize &= name.find("altup") == std::string::npos;
|
||||
quantize &= name.find("laurel") == std::string::npos;
|
||||
@@ -538,12 +548,7 @@ static ggml_type llama_tensor_get_type_impl(quantize_state_impl & qs, ggml_type
|
||||
} else if (ftype == LLAMA_FTYPE_MOSTLY_MXFP4_MOE) {
|
||||
// MoE tensors -> MXFP4
|
||||
// other tensors -> Q8_0
|
||||
// MLA projection tensors are also 3D, so match expert tensor roles explicitly.
|
||||
const bool is_bailingmoe3_expert = arch == LLM_ARCH_BAILINGMOE3 &&
|
||||
(category == tensor_category::FFN_UP ||
|
||||
category == tensor_category::FFN_GATE ||
|
||||
category == tensor_category::FFN_DOWN);
|
||||
if (tensor->ne[2] > 1 && (arch != LLM_ARCH_BAILINGMOE3 || is_bailingmoe3_expert)) {
|
||||
if (tensor_is_expert(name)) {
|
||||
new_type = GGML_TYPE_MXFP4;
|
||||
} else {
|
||||
new_type = GGML_TYPE_Q8_0;
|
||||
@@ -1304,11 +1309,11 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
if (it == imatrix_data->end()) {
|
||||
LLAMA_LOG_INFO("\n====== %s: did not find weights for %s\n", __func__, tensor->name);
|
||||
} else {
|
||||
if (it->second.size() == (size_t)tensor->ne[0]*tensor->ne[2]) {
|
||||
if (it->second.size() == (size_t)tensor->ne[0]*tensor->ne[2]*tensor->ne[3]) {
|
||||
imatrix = it->second.data();
|
||||
} else {
|
||||
LLAMA_LOG_INFO("\n====== %s: imatrix size %d is different from tensor size %d for %s\n", __func__,
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]), tensor->name);
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]*tensor->ne[3]), tensor->name);
|
||||
|
||||
// this can happen when quantizing an old mixtral model with split tensors with a new incompatible imatrix
|
||||
// this is a significant error and it may be good idea to abort the process if this happens,
|
||||
@@ -1316,7 +1321,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
// tok_embd should be ignored in this case, since it always causes this warning
|
||||
if (!tensor_name_match_token_embd(tensor->name)) {
|
||||
throw std::runtime_error(format("imatrix size %d is different from tensor size %d for %s",
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]), tensor->name));
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]*tensor->ne[3]), tensor->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1338,7 +1343,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
|
||||
const int64_t n_per_row = tensor->ne[0];
|
||||
const int64_t nrows_per_expert = tensor->ne[1];
|
||||
const int64_t nrows_total = tensor->ne[1] * tensor->ne[2];
|
||||
const int64_t nrows_total = ggml_nrows(tensor);
|
||||
|
||||
const size_t row_size_src = ggml_row_size(tensor->type, n_per_row);
|
||||
const size_t row_size_dst = ggml_row_size(new_type, n_per_row);
|
||||
|
||||
+36
-12
@@ -18,6 +18,7 @@
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <stdexcept>
|
||||
|
||||
// the ring buffer works similarly to std::deque, but with a fixed capacity
|
||||
@@ -3333,9 +3334,13 @@ struct llama_sampler_dry {
|
||||
};
|
||||
|
||||
// Ported from Koboldcpp, original PR: https://github.com/LostRuins/koboldcpp/pull/982 (Original author: pi6am)
|
||||
static void get_overlapping_token_sequences(const llama_vocab & vocab, const std::string& str, std::unordered_multimap<llama_token, std::vector<llama_token>>& token_sequences, int max_tail_len = -1) {
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab.n_tokens(); token_id++) {
|
||||
std::string word = vocab.detokenize({token_id}, true);
|
||||
static void get_overlapping_token_sequences(const llama_vocab & vocab, const std::vector<std::string> & vocab_words, const std::string& str, std::unordered_multimap<llama_token, std::vector<llama_token>>& token_sequences, int max_tail_len = -1) {
|
||||
// the tail tokenization only depends on how many characters of str were matched, so compute each one at most once
|
||||
std::vector<std::vector<llama_token>> tail_cache(str.size() + 1);
|
||||
std::vector<bool> tail_cached(str.size() + 1, false);
|
||||
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab_words.size(); token_id++) {
|
||||
const std::string & word = vocab_words[token_id];
|
||||
if (word.find(str) != std::string::npos) {
|
||||
token_sequences.emplace(token_id, std::vector<llama_token>());
|
||||
} else {
|
||||
@@ -3352,10 +3357,14 @@ static void get_overlapping_token_sequences(const llama_vocab & vocab, const std
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
std::vector<llama_token> tokenization = vocab.tokenize(str.substr(i), false, false);
|
||||
if (max_tail_len >= 0 && tokenization.size() > (size_t)max_tail_len) {
|
||||
tokenization.resize(max_tail_len);
|
||||
if (!tail_cached[i]) {
|
||||
tail_cache[i] = vocab.tokenize(str.substr(i), false, false);
|
||||
if (max_tail_len >= 0 && tail_cache[i].size() > (size_t)max_tail_len) {
|
||||
tail_cache[i].resize(max_tail_len);
|
||||
}
|
||||
tail_cached[i] = true;
|
||||
}
|
||||
const std::vector<llama_token> & tokenization = tail_cache[i];
|
||||
|
||||
// Ensure we don't already have a duplicate matching tokenization
|
||||
auto its = token_sequences.equal_range(token_id);
|
||||
@@ -3656,6 +3665,16 @@ struct llama_sampler * llama_sampler_init_dry(const struct llama_vocab * vocab,
|
||||
|
||||
if (dry_enabled && seq_breakers != nullptr && num_breakers > 0) {
|
||||
// Process sequence breakers
|
||||
// each distinct breaker costs a full vocabulary scan: drop duplicates and stop at the cap
|
||||
std::unordered_set<std::string> seen_breakers;
|
||||
|
||||
// detokenize the vocabulary once instead of rescanning it for every breaker
|
||||
std::vector<std::string> vocab_words;
|
||||
vocab_words.reserve(vocab->n_tokens());
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab->n_tokens(); ++token_id) {
|
||||
vocab_words.push_back(vocab->detokenize({token_id}, true));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_breakers; ++i) {
|
||||
if (seq_breakers[i] == nullptr || std::strlen(seq_breakers[i]) == 0) {
|
||||
LLAMA_LOG_WARN("skipping null or empty DRY sequence breaker at index %zu\n", i);
|
||||
@@ -3663,17 +3682,22 @@ struct llama_sampler * llama_sampler_init_dry(const struct llama_vocab * vocab,
|
||||
}
|
||||
|
||||
std::string sequence_break(seq_breakers[i]);
|
||||
if (sequence_break.empty()) {
|
||||
LLAMA_LOG_WARN("skipping empty DRY sequence breaker\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sequence_break.size() > MAX_CHAR_LEN) {
|
||||
LLAMA_LOG_WARN("truncating DRY sequence breaker to %d characters\n", MAX_CHAR_LEN);
|
||||
sequence_break.resize(MAX_CHAR_LEN);
|
||||
}
|
||||
|
||||
get_overlapping_token_sequences(*vocab, sequence_break, processed_breakers, MAX_SEQ_LEN);
|
||||
if (!seen_breakers.insert(sequence_break).second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen_breakers.size() > LLAMA_DRY_MAX_SEQ_BREAKERS) {
|
||||
LLAMA_LOG_WARN("ignoring DRY sequence breaker at index %zu: more than %d distinct breakers\n",
|
||||
i, LLAMA_DRY_MAX_SEQ_BREAKERS);
|
||||
break;
|
||||
}
|
||||
|
||||
get_overlapping_token_sequences(*vocab, vocab_words, sequence_break, processed_breakers, MAX_SEQ_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -8,14 +8,6 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
static float dsv4_rope_attn_factor(float freq_scale, float ext_factor) {
|
||||
if (ext_factor == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 1.0f / (1.0f + 0.1f*logf(1.0f/freq_scale));
|
||||
}
|
||||
|
||||
void llama_model_deepseek4::load_arch_hparams(llama_model_loader & ml) {
|
||||
if (hparams.n_layer_nextn > 0) {
|
||||
const uint32_t n_layer_main = hparams.n_layer_all - hparams.n_layer_nextn;
|
||||
@@ -358,6 +350,18 @@ ggml_tensor * llama_model_deepseek4::graph::build_hc_pre(
|
||||
ggml_tensor ** post,
|
||||
ggml_tensor ** comb,
|
||||
int il) const {
|
||||
return build_hc_pre(x, build_hc_mixes(x, hc_fn, hc_scale, hc_base, post, comb, false, il), il);
|
||||
}
|
||||
|
||||
ggml_tensor * llama_model_deepseek4::graph::build_hc_mixes(
|
||||
ggml_tensor * x,
|
||||
ggml_tensor * hc_fn,
|
||||
ggml_tensor * hc_scale,
|
||||
ggml_tensor * hc_base,
|
||||
ggml_tensor ** post,
|
||||
ggml_tensor ** comb,
|
||||
bool f32_src,
|
||||
int il) const {
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
const int64_t hc_dim = hc*n_embd;
|
||||
const int64_t hc_mix_dim = (2 + hc)*hc;
|
||||
@@ -369,6 +373,10 @@ ggml_tensor * llama_model_deepseek4::graph::build_hc_pre(
|
||||
ggml_tensor * flat = ggml_reshape_2d(ctx0, x, hc_dim, nt);
|
||||
ggml_tensor * flat_norm = ggml_rms_norm(ctx0, flat, norm_rms_eps);
|
||||
ggml_tensor * mixes = ggml_mul_mat(ctx0, hc_fn, flat_norm);
|
||||
if (f32_src && !ggml_is_quantized(hc_fn->type)) {
|
||||
// F32 weights (model.py hc_pre runs in fp32): request F32 inputs, the mixes are near-tie sensitive
|
||||
ggml_prec_set_src(mixes, GGML_PREC_F32, 1);
|
||||
}
|
||||
cb(mixes, "hc_mixes", il);
|
||||
|
||||
ggml_tensor * scale_pre = dsv4_view_1d(ctx0, hc_scale, 1, 0);
|
||||
@@ -404,8 +412,7 @@ ggml_tensor * llama_model_deepseek4::graph::build_hc_pre(
|
||||
}
|
||||
cb(*comb, "hc_comb", il);
|
||||
|
||||
ggml_tensor * result = build_hc_pre(x, pre, il);
|
||||
return result;
|
||||
return pre;
|
||||
}
|
||||
|
||||
ggml_tensor * llama_model_deepseek4::graph::build_hc_post(
|
||||
|
||||
@@ -0,0 +1,950 @@
|
||||
#include "models.h"
|
||||
#include "llama-impl.h"
|
||||
|
||||
#include "llama-kv-cache-dsv4.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// ref: deepseek-ai/DeepSeek-V4.1-Flash-1017 inference/model.py, engram.py
|
||||
// compressed layers attend over [window | all compressed rows] with top-k as a mask. with flash attention the default
|
||||
// is sparse: fused indexer, FA bounded to window + top-k, decode-sized ubatches gather only the selected rows (same
|
||||
// keys and mask, different key order). LLAMA_DSV41_SPARSE=0 restores the reference path
|
||||
|
||||
static constexpr int64_t DSV41_ENGRAM_DEAD = -1;
|
||||
|
||||
static bool dsv41_env_on(const char * name) {
|
||||
const char * v = getenv(name);
|
||||
return !v || atoi(v) != 0;
|
||||
}
|
||||
|
||||
void llama_model_deepseek41::load_arch_hparams(llama_model_loader & ml) {
|
||||
llama_model_deepseek4::load_arch_hparams(ml);
|
||||
|
||||
hparams.n_embd_out_impl = hparams.n_embd; // no hc_head
|
||||
if (hparams.indexer_top_k == 0) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 indexer top_k must be > 0");
|
||||
}
|
||||
|
||||
// each compressed layer reads the KV / top-k of the latest source layer at or before it
|
||||
std::vector<uint32_t> kv_src;
|
||||
std::vector<uint32_t> idx_src;
|
||||
ml.get_arr(LLM_KV_ATTENTION_KV_SOURCE_LAYERS, kv_src);
|
||||
ml.get_arr(LLM_KV_ATTENTION_INDEXER_SOURCE_LAYERS, idx_src);
|
||||
|
||||
const uint32_t n_layer = hparams.n_layer();
|
||||
const auto out_of_range = [&](const std::vector<uint32_t> & v) {
|
||||
return std::any_of(v.begin(), v.end(), [&](uint32_t il) { return il >= n_layer; });
|
||||
};
|
||||
if (out_of_range(kv_src) || out_of_range(idx_src)) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 kv/index source layer out of range");
|
||||
}
|
||||
hparams.dsv41_kv_src.fill(-1);
|
||||
hparams.dsv41_topk_src.fill(-1);
|
||||
int32_t last_kv = -1;
|
||||
int32_t last_idx = -1;
|
||||
for (uint32_t il = 0; il < n_layer; ++il) {
|
||||
const uint32_t ratio = hparams.dsv4_compress_ratios[il];
|
||||
const bool is_kv = std::find(kv_src.begin(), kv_src.end(), il) != kv_src.end();
|
||||
const bool is_idx = std::find(idx_src.begin(), idx_src.end(), il) != idx_src.end();
|
||||
if ((is_kv || is_idx) && ratio == 0) {
|
||||
throw std::runtime_error(format("DeepSeek-V4.1 source layer %u has compress ratio 0", il));
|
||||
}
|
||||
last_kv = is_kv ? (int32_t) il : last_kv;
|
||||
last_idx = is_idx ? (int32_t) il : last_idx;
|
||||
if (ratio == 0) {
|
||||
continue;
|
||||
}
|
||||
if (ratio > 2 || last_kv < 0 || last_idx < last_kv ||
|
||||
hparams.dsv4_compress_ratios[last_kv] != ratio || hparams.dsv4_compress_ratios[last_idx] != ratio) {
|
||||
throw std::runtime_error(format("DeepSeek-V4.1 layer %u: ratio %u without a matching kv/index source", il, ratio));
|
||||
}
|
||||
hparams.dsv41_kv_src[il] = last_kv;
|
||||
hparams.dsv41_topk_src[il] = last_idx;
|
||||
}
|
||||
|
||||
ml.get_key(LLM_KV_ATTENTION_INDEXER_CANDIDATE_SOURCE_LAYER, hparams.dsv41_cand_src, false);
|
||||
if (hparams.dsv41_cand_src >= 0) {
|
||||
ml.get_key(LLM_KV_ATTENTION_INDEXER_CANDIDATE_TOP_K_BLOCKS, hparams.dsv41_cand_topk_blocks);
|
||||
ml.get_key(LLM_KV_ATTENTION_INDEXER_CANDIDATE_BLOCK_SIZE, hparams.dsv41_cand_block_size);
|
||||
const int32_t cs = hparams.dsv41_cand_src;
|
||||
if ((uint32_t) cs >= n_layer || hparams.dsv41_topk_src[cs] != cs || hparams.dsv41_cand_block_size == 0 ||
|
||||
hparams.dsv41_cand_topk_blocks == 0) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 invalid candidate selection (source must be an index source, block size and top-k blocks > 0)");
|
||||
}
|
||||
for (uint32_t il = cs + 1; il < n_layer; ++il) {
|
||||
if (hparams.dsv41_topk_src[il] == (int32_t) il && hparams.dsv4_compress_ratios[il] != hparams.dsv4_compress_ratios[cs]) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 candidate consumers must share the source's compress ratio");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Engram: the PLE n-gram hash of qwen4exp with one set of hash constants per Engram layer, stored back to back
|
||||
std::vector<uint32_t> e_layers;
|
||||
ml.get_arr(LLM_KV_PLE_LAYERS, e_layers, false);
|
||||
if (!e_layers.empty()) {
|
||||
std::vector<uint64_t> mult;
|
||||
std::vector<uint64_t> offsets;
|
||||
std::vector<uint64_t> primes;
|
||||
ml.get_key(LLM_KV_PLE_NGRAM_SIZE, hparams.ple_ngram_size);
|
||||
ml.get_key(LLM_KV_PLE_HEADS_PER_NGRAM, hparams.ple_heads_per_ngram);
|
||||
ml.get_key(LLM_KV_EMBEDDING_LENGTH_PER_LAYER, hparams.ple_head_dim);
|
||||
ml.get_arr(LLM_KV_PLE_LAYER_MULTIPLIERS, mult);
|
||||
ml.get_arr(LLM_KV_PLE_HEAD_OFFSETS, offsets);
|
||||
ml.get_arr(LLM_KV_PLE_HEAD_VOCAB_SIZES, primes);
|
||||
ml.get_key(LLM_KV_ENGRAM_PAD_TOKEN_ID, engram_pad_id);
|
||||
ml.get_arr(LLM_KV_ENGRAM_TOKEN_MAP, engram_token_map);
|
||||
|
||||
const size_t n_e = e_layers.size();
|
||||
const size_t n_gram = hparams.ple_ngram_size;
|
||||
const size_t n_cols = n_gram >= 2 ? (n_gram - 1)*hparams.ple_heads_per_ngram : 0;
|
||||
if (n_cols == 0 || hparams.ple_head_dim == 0 ||
|
||||
mult.size() != n_e*n_gram || primes.size() != n_e*n_cols || offsets.size() != n_e*n_cols ||
|
||||
mult.size() > LLAMA_MAX_PLE_NGRAM || primes.size() > LLAMA_MAX_PLE_HEADS ||
|
||||
engram_pad_id >= engram_token_map.size()) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 engram metadata is inconsistent");
|
||||
}
|
||||
for (size_t e = 0; e < n_e; ++e) {
|
||||
// module e belongs to the e-th Engram layer
|
||||
if (e_layers[e] >= n_layer || (e > 0 && e_layers[e] <= e_layers[e - 1])) {
|
||||
throw std::runtime_error(format("DeepSeek-V4.1 engram layer %u is out of range or out of order", e_layers[e]));
|
||||
}
|
||||
hparams.is_ple_impl.set(e_layers[e]);
|
||||
}
|
||||
for (size_t c = 0; c < n_e*n_cols; ++c) {
|
||||
// a bucket count of 0 traps in the hash; row ids are int32
|
||||
if (primes[c] < 2 || offsets[c] + primes[c] > (uint64_t) INT32_MAX) {
|
||||
throw std::runtime_error(format("DeepSeek-V4.1 engram head %zu: bucket range out of range", c));
|
||||
}
|
||||
hparams.ple_head_offsets[c] = (uint32_t) offsets[c];
|
||||
hparams.ple_head_vocab_sizes[c] = (uint32_t) primes[c];
|
||||
}
|
||||
std::copy(mult.begin(), mult.end(), hparams.ple_layer_multipliers.begin());
|
||||
hparams.ple_n_heads = (uint32_t) n_cols;
|
||||
hparams.ple_n_modules = (uint32_t) n_e;
|
||||
}
|
||||
}
|
||||
|
||||
void llama_model_deepseek41::load_arch_tensors(llama_model_loader & ml) {
|
||||
LLAMA_LOAD_LOCALS;
|
||||
|
||||
const int64_t q_lora_rank = hparams.n_lora_q;
|
||||
const int64_t n_ff_exp = hparams.n_ff_exp();
|
||||
const int64_t n_expert_shared = hparams.n_expert_shared;
|
||||
const int64_t n_embd_head = hparams.n_embd_head_k();
|
||||
const int64_t o_groups = hparams.dsv4_o_group_count;
|
||||
const int64_t o_lora_rank = hparams.dsv4_o_lora_rank;
|
||||
const int64_t hc_mult = hparams.dsv4_hc_mult;
|
||||
const int64_t hc_dim = hc_mult * n_embd;
|
||||
const int64_t hc_mix_dim = (2 + hc_mult) * hc_mult;
|
||||
const int64_t n_idx_embd = hparams.indexer_head_size;
|
||||
const int64_t n_idx_head = hparams.indexer_n_head;
|
||||
|
||||
tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0);
|
||||
output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0);
|
||||
output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, 0);
|
||||
|
||||
// all Engram tables in one lazily read tensor, like the qwen4exp n-gram table
|
||||
if (hparams.ple_n_heads > 0) {
|
||||
int64_t n_rows = 0;
|
||||
for (size_t c = 0; c < (size_t) hparams.ple_n_modules*hparams.ple_n_heads; ++c) {
|
||||
n_rows = std::max(n_rows, (int64_t) hparams.ple_head_offsets[c] + hparams.ple_head_vocab_sizes[c]);
|
||||
}
|
||||
const std::string name = tn(LLM_TENSOR_PER_LAYER_TOKEN_EMBD, "weight").str();
|
||||
if (const auto * w = ml.get_weight(name.c_str())) {
|
||||
if (w->tensor->ne[1] < n_rows) {
|
||||
throw std::runtime_error(format("%s has %" PRId64 " rows, too few for the Engram heads (%" PRId64 ")",
|
||||
name.c_str(), w->tensor->ne[1], n_rows));
|
||||
}
|
||||
n_rows = w->tensor->ne[1];
|
||||
}
|
||||
per_layer_tok_embd = create_tensor(tn(LLM_TENSOR_PER_LAYER_TOKEN_EMBD, "weight"), {(int64_t) hparams.ple_head_dim, n_rows}, TENSOR_READ_LAZY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
auto & layer = layers[i];
|
||||
|
||||
layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0);
|
||||
layer.attn_sinks = create_tensor(tn(LLM_TENSOR_ATTN_SINKS, "weight", i), {n_head}, 0);
|
||||
layer.wq_a = create_tensor(tn(LLM_TENSOR_ATTN_Q_A, "weight", i), {n_embd, q_lora_rank}, 0);
|
||||
layer.attn_q_a_norm = create_tensor(tn(LLM_TENSOR_ATTN_Q_A_NORM, "weight", i), {q_lora_rank}, 0);
|
||||
layer.wq_b = create_tensor(tn(LLM_TENSOR_ATTN_Q_B, "weight", i), {q_lora_rank, n_head * n_embd_head}, 0);
|
||||
layer.wkv = create_tensor(tn(LLM_TENSOR_ATTN_KV, "weight", i), {n_embd, n_embd_head}, 0);
|
||||
layer.attn_kv_norm = create_tensor(tn(LLM_TENSOR_ATTN_KV_NORM, "weight", i), {n_embd_head}, 0);
|
||||
layer.wo_a = create_tensor(tn(LLM_TENSOR_ATTN_OUT_A, "weight", i), {n_head * n_embd_head / o_groups, o_lora_rank, o_groups}, TENSOR_ALLOW_RESHAPE);
|
||||
layer.wo_b = create_tensor(tn(LLM_TENSOR_ATTN_OUT_B, "weight", i), {o_groups * o_lora_rank, n_embd}, 0);
|
||||
|
||||
layer.hc_attn_fn = create_tensor(tn(LLM_TENSOR_HC_ATTN_FN, "weight", i), {hc_dim, hc_mix_dim}, 0);
|
||||
layer.hc_attn_base = create_tensor(tn(LLM_TENSOR_HC_ATTN_BASE, "weight", i), {hc_mix_dim}, 0);
|
||||
layer.hc_attn_scale = create_tensor(tn(LLM_TENSOR_HC_ATTN_SCALE, "weight", i), {3}, 0);
|
||||
layer.hc_ffn_fn = create_tensor(tn(LLM_TENSOR_HC_FFN_FN, "weight", i), {hc_dim, hc_mix_dim}, 0);
|
||||
layer.hc_ffn_base = create_tensor(tn(LLM_TENSOR_HC_FFN_BASE, "weight", i), {hc_mix_dim}, 0);
|
||||
layer.hc_ffn_scale = create_tensor(tn(LLM_TENSOR_HC_FFN_SCALE, "weight", i), {3}, 0);
|
||||
|
||||
if (hparams.dsv41_kv_src[i] == i) {
|
||||
layer.attn_comp_wkv = create_tensor(tn(LLM_TENSOR_ATTN_COMPRESSOR_WKV, "weight", i), {n_embd, n_embd_head}, 0);
|
||||
layer.attn_comp_norm = create_tensor(tn(LLM_TENSOR_ATTN_COMPRESSOR_NORM, "weight", i), {n_embd_head}, 0);
|
||||
if (hparams.dsv4_compress_ratios[i] > 1) {
|
||||
layer.attn_comp_wgate = create_tensor(tn(LLM_TENSOR_ATTN_COMPRESSOR_WGATE, "weight", i), {n_embd, n_embd_head}, 0);
|
||||
}
|
||||
layer.indexer_attn_k = create_tensor(tn(LLM_TENSOR_INDEXER_ATTN_K, "weight", i), {n_embd_head, n_idx_embd}, 0);
|
||||
layer.indexer_k_norm = create_tensor(tn(LLM_TENSOR_INDEXER_K_NORM, "weight", i), {n_idx_embd}, 0);
|
||||
}
|
||||
if (hparams.dsv41_topk_src[i] == i) {
|
||||
layer.indexer_attn_q_b = create_tensor(tn(LLM_TENSOR_INDEXER_ATTN_Q_B, "weight", i), {q_lora_rank, n_idx_head * n_idx_embd}, 0);
|
||||
layer.indexer_proj = create_tensor(tn(LLM_TENSOR_INDEXER_PROJ, "weight", i), {n_embd, n_idx_head}, 0);
|
||||
}
|
||||
|
||||
if (engram_index(i) >= 0) {
|
||||
layer.engram_kv = create_tensor(tn(LLM_TENSOR_ENGRAM_KV, "weight", i), {(int64_t) hparams.ple_n_heads * hparams.ple_head_dim, n_embd * (hc_mult + 1)}, 0);
|
||||
layer.engram_gate = create_tensor(tn(LLM_TENSOR_ENGRAM_GATE, "weight", i), {n_embd, hc_mult}, 0);
|
||||
}
|
||||
|
||||
layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), {n_embd, n_expert}, 0);
|
||||
layer.ffn_exp_probs_b = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B, "bias", i), {n_expert}, 0);
|
||||
layer.ffn_exp_probs_b_vl = create_tensor(tn(LLM_TENSOR_FFN_EXP_PROBS_B_VL, "bias", i), {n_expert}, TENSOR_NOT_REQUIRED);
|
||||
layer.ffn_norm = create_tensor(tn(LLM_TENSOR_FFN_NORM, "weight", i), {n_embd}, 0);
|
||||
|
||||
layer.ffn_gate_exps = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, 0);
|
||||
layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), {n_ff_exp, n_embd, n_expert}, 0);
|
||||
layer.ffn_up_exps = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "weight", i), {n_embd, n_ff_exp, n_expert}, 0);
|
||||
|
||||
layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), {n_embd, n_ff_exp * n_expert_shared}, 0);
|
||||
layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), {n_ff_exp * n_expert_shared, n_embd}, 0);
|
||||
layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), {n_embd, n_ff_exp * n_expert_shared}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<llm_graph_context> llama_model_deepseek41::build_arch_graph(const llm_graph_params & params) const {
|
||||
if (params.gtype == LLM_GRAPH_TYPE_DECODER_MTP) {
|
||||
throw std::runtime_error("DeepSeek-V4.1 has no MTP graph: its DSpark draft is a separate DFLASH GGUF "
|
||||
"(convert_hf_to_gguf.py --dspark), used with --spec-type draft-dspark");
|
||||
}
|
||||
return std::make_unique<graph>(*this, params);
|
||||
}
|
||||
|
||||
// Engram n-gram hash (engram.py NgramHashState.forward)
|
||||
int llama_model_deepseek41::engram_index(int il) const {
|
||||
if (hparams.ple_n_heads == 0 || !hparams.is_ple(il)) {
|
||||
return -1;
|
||||
}
|
||||
int e = 0;
|
||||
for (int j = 0; j < il; ++j) {
|
||||
e += hparams.is_ple(j);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
void llama_model_deepseek41::engram_hash(int e, const llama_ubatch & ubatch, const std::vector<llama_token> & prev, std::vector<int32_t> & out) const {
|
||||
const int64_t n_gram = hparams.ple_ngram_size;
|
||||
const int64_t n_prev = n_gram - 1;
|
||||
const int64_t n_head = hparams.ple_heads_per_ngram;
|
||||
const int64_t n_cols = hparams.ple_n_heads;
|
||||
|
||||
const uint64_t * mult = hparams.ple_layer_multipliers.data() + (size_t) e*n_gram;
|
||||
const uint32_t * primes = hparams.ple_head_vocab_sizes.data() + (size_t) e*n_cols;
|
||||
const int64_t pad = engram_token_map[engram_pad_id];
|
||||
const uint32_t * offs = hparams.ple_head_offsets.data() + (size_t) e*n_cols;
|
||||
|
||||
const auto cid = [&](llama_token t) -> int64_t {
|
||||
return t < 0 || t >= (llama_token) engram_token_map.size() ? DSV41_ENGRAM_DEAD : engram_token_map[t];
|
||||
};
|
||||
|
||||
out.resize(ubatch.n_tokens*n_cols);
|
||||
std::vector<uint64_t> toks(n_gram);
|
||||
for (int64_t i = 0; i < ubatch.n_tokens; ++i) {
|
||||
// shift 0 is the token itself; once a shift is blocked (sequence start or dead token) every longer one is too
|
||||
const bool is_embd = !ubatch.token || (ubatch.is_mixed() && ubatch.type[i]);
|
||||
int64_t c = is_embd ? DSV41_ENGRAM_DEAD : cid(ubatch.token[i]);
|
||||
bool blocked = c == DSV41_ENGRAM_DEAD;
|
||||
toks[0] = blocked ? pad : c;
|
||||
for (int64_t s = 1; s < n_gram; ++s) {
|
||||
c = ubatch.pos[i] - s < 0 ? DSV41_ENGRAM_DEAD : cid(prev[i*n_prev + (n_prev - s)]);
|
||||
blocked = blocked || c == DSV41_ENGRAM_DEAD;
|
||||
toks[s] = blocked ? pad : c;
|
||||
}
|
||||
|
||||
uint64_t rolling = toks[0]*mult[0];
|
||||
for (int64_t o = 1; o < n_gram; ++o) {
|
||||
rolling ^= toks[o]*mult[o];
|
||||
for (int64_t h = 0; h < n_head; ++h) {
|
||||
const int64_t col = (o - 1)*n_head + h;
|
||||
out[i*n_cols + col] = (int32_t) (rolling % primes[col] + offs[col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cand_pin: +1e30 (the reference uses +inf) on the block holding each query's newest compressed row, so a partly
|
||||
// filled block always survives; a finite pin keeps fully masked blocks at -inf instead of NaN
|
||||
class llm_graph_input_dsv41 : public llm_graph_input_i {
|
||||
public:
|
||||
llm_graph_input_dsv41(const llama_model_deepseek41 & model, const llama_kv_cache_dsv4_context * mctx) :
|
||||
model(model), mctx(mctx) {}
|
||||
|
||||
void set_input(const llama_ubatch * ubatch) override;
|
||||
bool can_reuse(const llm_graph_params & params) override;
|
||||
|
||||
std::vector<ggml_tensor *> engram_ids;
|
||||
ggml_tensor * engram_keep = nullptr; // 0 for embedding (image) tokens; only in mixed ubatches
|
||||
ggml_tensor * cand_pin = nullptr;
|
||||
bool cand_csa = false;
|
||||
|
||||
const llama_model_deepseek41 & model;
|
||||
const llama_kv_cache_dsv4_context * mctx;
|
||||
|
||||
private:
|
||||
const llama_kv_cache_dsv4_context::comp_plan & cand_plan(const llama_ubatch & ubatch) const {
|
||||
return cand_csa ? mctx->get_csa_plan(ubatch) : mctx->get_hca_plan(ubatch);
|
||||
}
|
||||
};
|
||||
|
||||
void llm_graph_input_dsv41::set_input(const llama_ubatch * ubatch) {
|
||||
if (!engram_ids.empty()) {
|
||||
// a token shared by several sequences has the same history in each, so its first sequence resolves it
|
||||
std::vector<llama_token> prev;
|
||||
mctx->get_raw()->get_prev_tokens(*ubatch, model.hparams.ple_ngram_size - 1, prev);
|
||||
// hash all Engram layers first so a lazily read table gets one batched readahead before the first gather
|
||||
std::vector<std::vector<int32_t>> ids(engram_ids.size());
|
||||
std::vector<int32_t> all;
|
||||
for (size_t e = 0; e < engram_ids.size(); ++e) {
|
||||
model.engram_hash((int) e, *ubatch, prev, ids[e]);
|
||||
all.insert(all.end(), ids[e].begin(), ids[e].end());
|
||||
}
|
||||
if (model.can_prefetch.count(model.per_layer_tok_embd)) {
|
||||
llama_prefetch_rows(model.per_layer_tok_embd, all.data(), all.size());
|
||||
}
|
||||
for (size_t e = 0; e < engram_ids.size(); ++e) {
|
||||
if (engram_ids[e]->buffer) {
|
||||
ggml_backend_tensor_set(engram_ids[e], ids[e].data(), 0, ids[e].size()*sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// embedding (image) tokens are hashed DEAD and get no Engram contribution (gate 0)
|
||||
if (engram_keep && engram_keep->buffer) {
|
||||
std::vector<float> keep(ubatch->n_tokens);
|
||||
for (uint32_t i = 0; i < ubatch->n_tokens; ++i) {
|
||||
const bool is_embd = !ubatch->token || (ubatch->is_mixed() && ubatch->type[i]);
|
||||
keep[i] = is_embd ? 0.0f : 1.0f;
|
||||
}
|
||||
ggml_backend_tensor_set(engram_keep, keep.data(), 0, keep.size()*sizeof(float));
|
||||
}
|
||||
|
||||
if (cand_pin && cand_pin->buffer) {
|
||||
const auto & plan = cand_plan(*ubatch);
|
||||
const int64_t n_blk = cand_pin->ne[0];
|
||||
const int64_t bs = model.hparams.dsv41_cand_block_size;
|
||||
GGML_ASSERT((int64_t) plan.n_visible.size() == (int64_t) ubatch->n_tokens);
|
||||
std::vector<float> pin(n_blk*ubatch->n_tokens, 0.0f);
|
||||
for (int64_t i = 0; i < (int64_t) ubatch->n_tokens; ++i) {
|
||||
const int64_t nv = plan.n_visible[i];
|
||||
if (nv > 0) {
|
||||
GGML_ASSERT((nv - 1)/bs < n_blk);
|
||||
pin[i*n_blk + (nv - 1)/bs] = 1e30f;
|
||||
}
|
||||
}
|
||||
ggml_backend_tensor_set(cand_pin, pin.data(), 0, pin.size()*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
bool llm_graph_input_dsv41::can_reuse(const llm_graph_params & params) {
|
||||
mctx = static_cast<const llama_kv_cache_dsv4_context *>(params.mctx);
|
||||
|
||||
bool res = true;
|
||||
for (auto * t : engram_ids) {
|
||||
res &= t->ne[0] == (int64_t) model.hparams.ple_n_heads*params.ubatch.n_tokens;
|
||||
}
|
||||
res &= (engram_keep != nullptr) == (!engram_ids.empty() && params.ubatch.embd != nullptr);
|
||||
if (engram_keep) {
|
||||
res &= engram_keep->ne[2] == (int64_t) params.ubatch.n_tokens;
|
||||
}
|
||||
if (cand_pin) {
|
||||
const auto & plan = cand_plan(params.ubatch);
|
||||
const int64_t bs = model.hparams.dsv41_cand_block_size;
|
||||
res &= plan.n_kv > 0 && cand_pin->ne[0] == (plan.n_kv + bs - 1)/bs;
|
||||
res &= cand_pin->ne[1]*cand_pin->ne[3] == (int64_t) params.ubatch.n_tokens;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_tensor * llama_model_deepseek41::graph::build_engram(
|
||||
const llama_model_deepseek41 & model,
|
||||
ggml_tensor * x,
|
||||
ggml_tensor * rows,
|
||||
ggml_tensor * keep,
|
||||
int il) const {
|
||||
const auto & layer = model.layers[il];
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
const int64_t nt = x->ne[2];
|
||||
|
||||
GGML_ASSERT(rows->ne[0] == (int64_t) (hparams.ple_head_dim*hparams.ple_n_heads) && rows->ne[1] == nt);
|
||||
|
||||
ggml_tensor * kv = build_lora_mm(layer.engram_kv, rows);
|
||||
cb(kv, "engram_kv", il);
|
||||
|
||||
ggml_tensor * key = ggml_cont(ctx0, ggml_view_3d(ctx0, kv, n_embd, hc, nt, ggml_row_size(kv->type, n_embd), kv->nb[1], 0));
|
||||
ggml_tensor * value = ggml_cont(ctx0, ggml_view_3d(ctx0, kv, n_embd, 1, nt, ggml_row_size(kv->type, n_embd), kv->nb[1],
|
||||
ggml_row_size(kv->type, hc*n_embd)));
|
||||
|
||||
// quantized copy has no binary-op kernel
|
||||
ggml_tensor * egate = ggml_is_quantized(layer.engram_gate->type) ? ggml_cast(ctx0, layer.engram_gate, GGML_TYPE_F32) : layer.engram_gate;
|
||||
ggml_tensor * dot = ggml_mul(ctx0, ggml_mul(ctx0, ggml_rms_norm(ctx0, x, norm_rms_eps), ggml_rms_norm(ctx0, key, norm_rms_eps)), egate);
|
||||
dot = ggml_scale(ctx0, ggml_sum_rows(ctx0, dot), 1.0f/sqrtf((float) n_embd));
|
||||
cb(dot, "engram_dot", il);
|
||||
|
||||
// sigmoid(copysign(sqrt(max(|dot|, 1e-6)), dot)); sign(0) = +1 as in copysign
|
||||
ggml_tensor * mag = ggml_sqrt(ctx0, ggml_clamp(ctx0, ggml_abs(ctx0, dot), 1e-6f, INFINITY));
|
||||
ggml_tensor * sign = ggml_scale_bias(ctx0, ggml_step(ctx0, ggml_neg(ctx0, dot)), -2.0f, 1.0f);
|
||||
ggml_tensor * gate = ggml_sigmoid(ctx0, ggml_mul(ctx0, mag, sign));
|
||||
if (keep) {
|
||||
gate = ggml_mul(ctx0, gate, keep);
|
||||
}
|
||||
cb(gate, "engram_gate", il);
|
||||
|
||||
return ggml_add(ctx0, x, ggml_mul(ctx0, ggml_repeat_4d(ctx0, value, n_embd, hc, nt, 1), gate));
|
||||
}
|
||||
|
||||
ggml_tensor * llama_model_deepseek41::graph::build_attention41(
|
||||
const llama_model_deepseek41 & model,
|
||||
llm_graph_input_dsv4 * inp_dsv4,
|
||||
ggml_tensor * cur,
|
||||
ggml_tensor * inp_pos,
|
||||
ggml_tensor * cand_pin,
|
||||
int il) {
|
||||
const auto & layer = model.layers[il];
|
||||
llm_graph_input_dsv4_raw * inp_attn = inp_dsv4->get_raw();
|
||||
|
||||
const int64_t n_embd_head = hparams.n_embd_head_k();
|
||||
const int64_t n_embd_head_rope = hparams.n_rot();
|
||||
const int64_t n_embd_head_nope = n_embd_head - n_embd_head_rope;
|
||||
const int64_t n_groups = hparams.dsv4_o_group_count;
|
||||
const int64_t o_lora_rank = hparams.dsv4_o_lora_rank;
|
||||
const int64_t n_idx_embd = hparams.indexer_head_size;
|
||||
const int64_t n_idx_head = hparams.indexer_n_head;
|
||||
const int64_t nt = cur->ne[1];
|
||||
const float kq_scale = 1.0f/sqrtf(float(n_embd_head));
|
||||
const int64_t ratio = hparams.dsv4_compress_ratios[il];
|
||||
|
||||
const bool comp_rope = ratio != 0;
|
||||
const float freq_base_l = comp_rope ? hparams.dsv4_compress_rope_base : freq_base;
|
||||
const float freq_scale_l = comp_rope ? freq_scale : 1.0f;
|
||||
const float ext_factor_l = comp_rope ? ext_factor : 0.0f;
|
||||
const float attn_factor_l = dsv4_rope_attn_factor(freq_scale_l, ext_factor_l);
|
||||
const float beta_fast_l = comp_rope ? beta_fast : 0.0f;
|
||||
const float beta_slow_l = comp_rope ? beta_slow : 0.0f;
|
||||
const int32_t n_ctx_orig_l = comp_rope ? n_ctx_orig : 0;
|
||||
|
||||
const auto rope = [&](ggml_tensor * t, ggml_tensor * pos, int64_t n_nope) {
|
||||
t = ggml_rope_ext(ctx0, t, pos, nullptr, n_embd_head_rope, rope_type, n_ctx_orig_l,
|
||||
freq_base_l, freq_scale_l, ext_factor_l, attn_factor_l, beta_fast_l, beta_slow_l);
|
||||
return ggml_rope_set_offset(t, n_nope);
|
||||
};
|
||||
|
||||
ggml_tensor * qr = build_norm(build_lora_mm(layer.wq_a, cur), layer.attn_q_a_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(qr, "qr_norm", il);
|
||||
|
||||
ggml_tensor * q = ggml_reshape_3d(ctx0, build_lora_mm(layer.wq_b, qr), n_embd_head, n_head, nt);
|
||||
q = rope(q, inp_pos, n_embd_head_nope);
|
||||
cb(q, "q", il);
|
||||
|
||||
ggml_tensor * kv = build_norm(build_lora_mm(layer.wkv, cur), layer.attn_kv_norm, nullptr, LLM_NORM_RMS, il);
|
||||
kv = rope(ggml_reshape_3d(ctx0, kv, n_embd_head, 1, nt), inp_pos, n_embd_head_nope);
|
||||
cb(kv, "kv", il);
|
||||
|
||||
const auto & ci = ratio == 2 ? inp_dsv4->get_csa() : inp_dsv4->get_hca();
|
||||
const auto * comp_ctx = ratio == 2 ? inp_dsv4->mctx->get_csa() : inp_dsv4->mctx->get_hca();
|
||||
const auto * comp_state = ratio == 2 ? inp_dsv4->mctx->get_csa_state() : inp_dsv4->mctx->get_hca_state();
|
||||
|
||||
const int32_t rd = inp_dsv4->mctx->get_comp_read_layer(il);
|
||||
|
||||
if (ratio != 0) {
|
||||
GGML_ASSERT(hparams.dsv41_kv_src[il] >= 0 && (int64_t) comp_state->get_ratio() == ratio);
|
||||
|
||||
if (hparams.dsv41_kv_src[il] == il && ci.state_pos) {
|
||||
ggml_tensor * state_kv = build_lora_mm(layer.attn_comp_wkv, cur);
|
||||
ggml_tensor * state_score = ratio > 1 ? build_lora_mm(layer.attn_comp_wgate, cur) : ggml_fill(ctx0, state_kv, 0.0f);
|
||||
|
||||
ggml_tensor * restored_kv = comp_state->get_kv_all(ctx0, il);
|
||||
ggml_tensor * restored_score = comp_state->get_score_all(ctx0, il);
|
||||
if (ci.state_restore_src_idxs && ci.state_restore_dst_idxs) {
|
||||
restored_kv = comp_state->cpy_kv (ctx0, ggml_get_rows(ctx0, restored_kv, ci.state_restore_src_idxs), ci.state_restore_dst_idxs, il);
|
||||
restored_score = comp_state->cpy_score(ctx0, ggml_get_rows(ctx0, restored_score, ci.state_restore_src_idxs), ci.state_restore_dst_idxs, il);
|
||||
}
|
||||
|
||||
if (ci.state_write_idxs) {
|
||||
const int64_t n_blocks = ci.state_write_pos->ne[0];
|
||||
const int64_t n_state = comp_state->get_n_rows();
|
||||
GGML_ASSERT(ci.state_read_idxs->ne[0] == ratio*n_blocks);
|
||||
|
||||
ggml_tensor * src_kv = ggml_concat(ctx0, ggml_view_2d(ctx0, restored_kv, restored_kv->ne[0], n_state, restored_kv->nb[1], 0), state_kv, 1);
|
||||
ggml_tensor * src_score = ggml_concat(ctx0, ggml_view_2d(ctx0, restored_score, restored_score->ne[0], n_state, restored_score->nb[1], 0), state_score, 1);
|
||||
|
||||
ggml_tensor * v = ggml_get_rows(ctx0, src_kv, ci.state_read_idxs);
|
||||
ggml_tensor * s = ggml_get_rows(ctx0, src_score, ci.state_read_idxs);
|
||||
v = ggml_cont(ctx0, ggml_permute(ctx0, ggml_reshape_3d(ctx0, v, n_embd_head, ratio, n_blocks), 1, 0, 2, 3));
|
||||
s = ggml_cont(ctx0, ggml_permute(ctx0, ggml_reshape_3d(ctx0, s, n_embd_head, ratio, n_blocks), 1, 0, 2, 3));
|
||||
|
||||
ggml_tensor * comp = ggml_sum_rows(ctx0, ggml_mul(ctx0, v, ggml_soft_max(ctx0, s)));
|
||||
comp = ggml_cont(ctx0, ggml_permute(ctx0, comp, 1, 0, 2, 3));
|
||||
comp = build_norm(comp, layer.attn_comp_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(comp, "comp_latent", il);
|
||||
|
||||
// index K from the RoPE-free latent; a latent stands for the first token of its group
|
||||
ggml_tensor * ik = build_norm(build_lora_mm(layer.indexer_attn_k, comp), layer.indexer_k_norm, nullptr, LLM_NORM_RMS, il);
|
||||
ik = rope(ik, ci.state_write_pos, n_idx_embd - n_embd_head_rope);
|
||||
comp = rope(comp, ci.state_write_pos, n_embd_head_nope);
|
||||
|
||||
ggml_tensor * row = ggml_concat(ctx0, comp, ik, 0);
|
||||
ggml_build_forward_expand(gf, comp_ctx->cpy_k(ctx0, row, ci.state_write_idxs, il));
|
||||
comp_row_of[il] = row;
|
||||
}
|
||||
|
||||
if (ci.state_snapshot_src_idxs && ci.state_snapshot_dst_idxs) {
|
||||
ggml_tensor * snap_kv = ggml_concat(ctx0, restored_kv, state_kv, 1);
|
||||
ggml_tensor * snap_score = ggml_concat(ctx0, restored_score, state_score, 1);
|
||||
ggml_build_forward_expand(gf, comp_state->cpy_kv (ctx0, ggml_get_rows(ctx0, snap_kv, ci.state_snapshot_src_idxs), ci.state_snapshot_dst_idxs, il));
|
||||
ggml_build_forward_expand(gf, comp_state->cpy_score(ctx0, ggml_get_rows(ctx0, snap_score, ci.state_snapshot_src_idxs), ci.state_snapshot_dst_idxs, il));
|
||||
}
|
||||
ggml_build_forward_expand(gf, comp_state->cpy_kv (ctx0, ggml_get_rows(ctx0, state_kv, ci.state_persist_src_idxs), ci.state_persist_dst_idxs, il));
|
||||
ggml_build_forward_expand(gf, comp_state->cpy_score(ctx0, ggml_get_rows(ctx0, state_score, ci.state_persist_src_idxs), ci.state_persist_dst_idxs, il));
|
||||
}
|
||||
|
||||
if (rd == il && hparams.dsv41_kv_src[il] != il && comp_row_of[hparams.dsv41_kv_src[il]]) {
|
||||
// first consumer on this device: append the source's new rows to the local mirror
|
||||
ggml_build_forward_expand(gf, comp_ctx->cpy_k(ctx0, comp_row_of[hparams.dsv41_kv_src[il]], ci.state_write_idxs, il));
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor * out = nullptr;
|
||||
if (ratio == 0 || !ci.kq_mask) {
|
||||
// no compressed row visible yet: window only
|
||||
out = build_raw_attention(inp_attn, q, kv, layer.attn_sinks, kq_scale, il);
|
||||
} else {
|
||||
const int64_t n_comp = ci.kq_mask->ne[0];
|
||||
|
||||
ggml_tensor * comp_all = comp_ctx->get_k(ctx0, rd);
|
||||
GGML_ASSERT(comp_all->ne[0] == n_embd_head + n_idx_embd && n_comp <= comp_all->ne[2]);
|
||||
const int64_t n_stream = comp_all->ne[3];
|
||||
|
||||
ggml_tensor * top_k = topk_of[hparams.dsv41_topk_src[il]];
|
||||
if (hparams.dsv41_topk_src[il] == il) {
|
||||
ggml_tensor * ik = ggml_view_4d(ctx0, comp_all, n_idx_embd, 1, n_comp, n_stream,
|
||||
comp_all->nb[1], comp_all->nb[2], comp_all->nb[3], ggml_row_size(comp_all->type, n_embd_head));
|
||||
// the fused indexer reads the strided view in place, the unfused path needs it contiguous; quantized
|
||||
// caches are dequantized straight from the strided view (CUDA has no same-type copy of quantized views)
|
||||
const bool fused_idx = sparse && cparams.fused_lid && ci.kq_mask->type == GGML_TYPE_F16;
|
||||
if (ggml_is_quantized(ik->type)) {
|
||||
ik = ggml_cast(ctx0, ik, GGML_TYPE_F32);
|
||||
} else if (!fused_idx) {
|
||||
ik = ggml_cont(ctx0, ik);
|
||||
}
|
||||
|
||||
ggml_tensor * iq = ggml_reshape_3d(ctx0, build_lora_mm(layer.indexer_attn_q_b, qr), n_idx_embd, n_idx_head, nt);
|
||||
iq = rope(iq, inp_pos, n_idx_embd - n_embd_head_rope);
|
||||
iq = ggml_view_4d(ctx0, iq, iq->ne[0], iq->ne[1], nt/n_stream, n_stream, iq->nb[1], iq->nb[2], iq->nb[2]*(nt/n_stream), 0);
|
||||
|
||||
ggml_tensor * iw = ggml_scale(ctx0, build_lora_mm(layer.indexer_proj, cur), 1.0f/sqrtf(float(n_idx_embd*n_idx_head)));
|
||||
iw = ggml_view_4d(ctx0, iw, iw->ne[0], nt/n_stream, 1, n_stream, iw->nb[1], iw->nb[1]*(nt/n_stream), iw->nb[1]*(nt/n_stream), 0);
|
||||
|
||||
// chunk queries so the [n_ih, tokens, n_comp] scores stay near 2^26 elements (at most 64 chunks); the fused
|
||||
// indexer has no such intermediate and runs as one chunk
|
||||
const int64_t n_tok = nt/n_stream;
|
||||
GGML_ASSERT(ci.kq_mask->ne[1] == n_tok);
|
||||
const int64_t chunk = fused_idx ? n_tok :
|
||||
std::max({ (n_tok + 63)/64, (INT64_C(1) << 26)/(n_idx_head*n_comp*n_stream), INT64_C(1) });
|
||||
|
||||
const int32_t cand_src = hparams.dsv41_cand_src;
|
||||
const bool pick_blk = cand_src == il && cand_pin; // level one: pick the candidate blocks
|
||||
const bool use_blk = cand_src >= 0 && cand_src < il && cand_sel; // level two: score inside them
|
||||
const int64_t bs = hparams.dsv41_cand_block_size;
|
||||
const int64_t n_blk = pick_blk || use_blk ? (n_comp + bs - 1)/bs : 0;
|
||||
GGML_ASSERT(!pick_blk || cand_pin->ne[0] == n_blk);
|
||||
GGML_ASSERT(!use_blk || cand_n_comp == n_comp);
|
||||
|
||||
ggml_tensor * ik_p = fused_idx ? nullptr : ggml_permute(ctx0, ik, 0, 2, 1, 3);
|
||||
std::vector<ggml_tensor *> parts_top_k;
|
||||
std::vector<ggml_tensor *> parts_sel;
|
||||
for (int64_t t0 = 0; t0 < n_tok; t0 += chunk) {
|
||||
const int64_t nc = std::min(chunk, n_tok - t0);
|
||||
const auto rows = [&](ggml_tensor * t) {
|
||||
return ggml_view_4d(ctx0, t, t->ne[0], nc, 1, n_stream, t->nb[1], t->nb[2], t->nb[3], t0*t->nb[1]);
|
||||
};
|
||||
ggml_tensor * score = nullptr;
|
||||
if (fused_idx) {
|
||||
score = ggml_lightning_indexer(ctx0, iq, ik, iw, ci.kq_mask);
|
||||
res->add_fused_node({LLM_FUSED_OP_LIGHTNING_INDEXER, score, il});
|
||||
} else {
|
||||
ggml_tensor * iqc = ggml_view_4d(ctx0, iq, iq->ne[0], iq->ne[1], nc, n_stream, iq->nb[1], iq->nb[2], iq->nb[3], t0*iq->nb[2]);
|
||||
|
||||
ggml_tensor * kq = ggml_mul_mat(ctx0, ik_p, ggml_permute(ctx0, iqc, 0, 2, 1, 3));
|
||||
if (!ggml_is_quantized(ik->type)) {
|
||||
ggml_prec_set_src(kq, GGML_PREC_F32, 1);
|
||||
}
|
||||
kq = ggml_cont(ctx0, ggml_permute(ctx0, kq, 2, 1, 0, 3));
|
||||
score = ggml_sum_rows(ctx0, ggml_mul(ctx0, ggml_relu(ctx0, kq), rows(iw)));
|
||||
ggml_tensor * mask = rows(ci.kq_mask);
|
||||
score = ggml_add(ctx0, ggml_cont(ctx0, ggml_permute(ctx0, score, 2, 1, 0, 3)),
|
||||
mask->type == GGML_TYPE_F32 ? mask : ggml_cast(ctx0, mask, GGML_TYPE_F32));
|
||||
}
|
||||
cb(score, "idx_score", il);
|
||||
|
||||
if (pick_blk) {
|
||||
// best position per block, pin the newest block, keep the top blocks
|
||||
ggml_tensor * s2 = ggml_reshape_2d(ctx0, score, n_comp, nc*n_stream);
|
||||
if (n_blk*bs != n_comp) {
|
||||
s2 = ggml_concat(ctx0, s2, ggml_fill(ctx0, ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_blk*bs - n_comp, nc*n_stream), -INFINITY), 0);
|
||||
}
|
||||
ggml_tensor * bmax = ggml_pool_1d(ctx0, s2, GGML_OP_POOL_MAX, bs, bs, 0);
|
||||
bmax = ggml_add(ctx0, ggml_reshape_4d(ctx0, bmax, n_blk, nc, 1, n_stream), rows(cand_pin));
|
||||
cb(bmax, "cand_block_score", il);
|
||||
parts_sel.push_back(ggml_top_k(ctx0, bmax, std::min<int64_t>(hparams.dsv41_cand_topk_blocks, n_blk)));
|
||||
} else if (use_blk) {
|
||||
// own scores, restricted to the source's candidate blocks: 0 inside them, -inf elsewhere
|
||||
ggml_tensor * bsel = ggml_cont(ctx0, rows(cand_sel));
|
||||
const int64_t k_b = bsel->ne[0];
|
||||
ggml_tensor * bm = ggml_fill(ctx0, ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, 1, n_blk, nc*n_stream, 1), -INFINITY);
|
||||
ggml_tensor * zeros = ggml_fill(ctx0, ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, 1, k_b, nc*n_stream, 1), 0.0f);
|
||||
bm = ggml_set_rows(ctx0, bm, zeros, ggml_reshape_3d(ctx0, bsel, k_b, nc*n_stream, 1));
|
||||
bm = ggml_reshape_2d(ctx0, ggml_repeat_4d(ctx0, bm, bs, n_blk, nc*n_stream, 1), bs*n_blk, nc*n_stream);
|
||||
bm = ggml_reshape_4d(ctx0, ggml_cont(ctx0, ggml_view_2d(ctx0, bm, n_comp, nc*n_stream, bm->nb[1], 0)), n_comp, nc, 1, n_stream);
|
||||
cb(bm, "cand_mask", il);
|
||||
score = ggml_add(ctx0, score, bm);
|
||||
}
|
||||
|
||||
parts_top_k.push_back(ggml_top_k(ctx0, score, std::min<int64_t>(n_comp, hparams.indexer_top_k)));
|
||||
}
|
||||
|
||||
const auto join = [&](std::vector<ggml_tensor *> v) {
|
||||
while (v.size() > 1) {
|
||||
std::vector<ggml_tensor *> w;
|
||||
for (size_t i = 0; i < v.size(); i += 2) {
|
||||
w.push_back(i + 1 < v.size() ? ggml_concat(ctx0, v[i], v[i + 1], 1) : v[i]);
|
||||
}
|
||||
v.swap(w);
|
||||
}
|
||||
return v[0];
|
||||
};
|
||||
if (pick_blk) {
|
||||
cand_sel = join(parts_sel);
|
||||
cand_n_comp = n_comp;
|
||||
}
|
||||
top_k = ggml_cont(ctx0, join(parts_top_k));
|
||||
cb(top_k, "idx_top_k", il);
|
||||
topk_of[il] = top_k;
|
||||
}
|
||||
GGML_ASSERT(top_k && "DeepSeek-V4.1: top-k source layer produced no top-k");
|
||||
|
||||
// a quantized K cache stores the window Hadamard-rotated (as build_raw_attention)
|
||||
ggml_tensor * k_rot = inp_attn->self_k_rot;
|
||||
if (k_rot) {
|
||||
q = llama_mul_mat_hadamard(ctx0, q, k_rot);
|
||||
kv = llama_mul_mat_hadamard(ctx0, kv, k_rot);
|
||||
}
|
||||
ggml_build_forward_expand(gf, q);
|
||||
ggml_build_forward_expand(gf, kv);
|
||||
ggml_build_forward_expand(gf, inp_attn->mctx->cpy_k(ctx0, kv, inp_attn->get_k_idxs(), il));
|
||||
|
||||
ggml_tensor * raw_k = inp_attn->mctx->get_k(ctx0, il);
|
||||
ggml_tensor * raw_mask = inp_attn->get_kq_mask();
|
||||
|
||||
const int32_t topk_src = hparams.dsv41_topk_src[il];
|
||||
const int64_t k_sel = top_k->ne[0];
|
||||
const int64_t n_raw = raw_k->ne[2];
|
||||
|
||||
// few queries per stream: gather each query's top-k rows and attend over [window | selected], one FA stream
|
||||
// per query, instead of a full n_comp mask per layer. same keys and mask as dense, different key order
|
||||
const bool gather = sparse && cparams.flash_attn && raw_mask->type == GGML_TYPE_F16 && ci.kq_mask->type == GGML_TYPE_F16 &&
|
||||
((n_stream == 1 && nt <= 32) || nt == n_stream);
|
||||
|
||||
if (gather) {
|
||||
// ggml_top_k returns the rows in no particular order (CUB DeviceTopK can vary between runs) and FA sums the
|
||||
// gathered keys in that order: sort the rows once per top-k source so the result is deterministic
|
||||
ggml_tensor *& sel = gather_sel_of[topk_src];
|
||||
if (!sel) {
|
||||
GGML_ASSERT(n_comp <= (INT64_C(1) << 24)); // row ids are exact in F32
|
||||
ggml_tensor * ids = ggml_reshape_2d(ctx0, top_k, k_sel, nt);
|
||||
ggml_tensor * ord = ggml_argsort(ctx0, ggml_cast(ctx0, ids, GGML_TYPE_F32), GGML_SORT_ORDER_ASC);
|
||||
sel = ggml_reshape_2d(ctx0, ggml_get_rows(ctx0, ggml_reshape_3d(ctx0, ids, 1, k_sel, nt), ord), k_sel, nt);
|
||||
cb(sel, "comp_top_k_sorted", il);
|
||||
}
|
||||
|
||||
// built once per (storage, top-k) pair
|
||||
ggml_tensor *& comp_k = gather_k_of[{rd, topk_src}];
|
||||
if (!comp_k) {
|
||||
if (n_stream == 1) {
|
||||
ggml_tensor * src_rows = ggml_view_3d(ctx0, comp_all, n_embd_head, n_comp, 1, comp_all->nb[2], comp_all->nb[3], 0);
|
||||
comp_k = ggml_get_rows(ctx0, src_rows, ggml_reshape_1d(ctx0, sel, k_sel*nt));
|
||||
} else {
|
||||
ggml_tensor * src_rows = ggml_view_3d(ctx0, comp_all, n_embd_head, n_comp, n_stream, comp_all->nb[2], comp_all->nb[3], 0);
|
||||
comp_k = ggml_get_rows(ctx0, src_rows, sel);
|
||||
}
|
||||
comp_k = ggml_reshape_4d(ctx0, comp_k, n_embd_head, 1, k_sel, nt); // F32 (get_rows dequantizes)
|
||||
if (k_rot) {
|
||||
// quantized K cache: the window is stored rotated, the compressed rows are not
|
||||
comp_k = llama_mul_mat_hadamard(ctx0, comp_k, k_rot);
|
||||
}
|
||||
if (comp_k->type != raw_k->type) {
|
||||
comp_k = ggml_cast(ctx0, comp_k, raw_k->type); // exact: rows are raw_k->type values
|
||||
}
|
||||
cb(comp_k, "comp_k_sel", il);
|
||||
}
|
||||
|
||||
if (n_stream == 1 && nt > 1) {
|
||||
if (ggml_is_quantized(raw_k->type)) {
|
||||
// no REPEAT kernel for quantized types: byte-exact copies by doubling concats
|
||||
ggml_tensor * rep = nullptr;
|
||||
ggml_tensor * pw = raw_k;
|
||||
for (int64_t n = nt; n > 0; n >>= 1) {
|
||||
if (n & 1) {
|
||||
rep = rep ? ggml_concat(ctx0, rep, pw, 3) : pw;
|
||||
}
|
||||
if (n > 1) {
|
||||
pw = ggml_concat(ctx0, pw, pw, 3);
|
||||
}
|
||||
}
|
||||
raw_k = rep;
|
||||
} else {
|
||||
raw_k = ggml_repeat_4d(ctx0, raw_k, n_embd_head, 1, n_raw, nt);
|
||||
}
|
||||
}
|
||||
ggml_tensor * k_all = ggml_concat(ctx0, raw_k, comp_k, 2);
|
||||
|
||||
// mask: window row, then comp mask at the selected rows (-inf where top-k picked an invisible row);
|
||||
// one per top-k source, shared by its layers
|
||||
ggml_tensor *& kq_mask = gather_mask_of[topk_src];
|
||||
if (!kq_mask) {
|
||||
ggml_tensor * sel_mask = ggml_get_rows(ctx0, ggml_reshape_3d(ctx0, ci.kq_mask, 1, n_comp, nt), sel);
|
||||
sel_mask = ggml_cast(ctx0, ggml_reshape_4d(ctx0, sel_mask, k_sel, 1, 1, nt), GGML_TYPE_F16);
|
||||
kq_mask = ggml_concat(ctx0, ggml_reshape_4d(ctx0, raw_mask, raw_mask->ne[0], 1, 1, nt), sel_mask, 0);
|
||||
cb(kq_mask, "comp_kq_mask_sel", il);
|
||||
}
|
||||
|
||||
out = build_attn_mha(q, k_all, k_all, nullptr, kq_mask, layer.attn_sinks, nullptr, 0, kq_scale, il);
|
||||
} else {
|
||||
// dense attention over [window | all compressed rows], selection applied as a mask
|
||||
ggml_tensor * comp_k = ggml_view_4d(ctx0, comp_all, n_embd_head, 1, n_comp, n_stream,
|
||||
comp_all->nb[1], comp_all->nb[2], comp_all->nb[3], 0);
|
||||
if (k_rot) {
|
||||
// compressed rows are stored unrotated: rotate in F32, then cast to the cache type (dequantized from
|
||||
// the strided view: CUDA has no same-type quantized copy)
|
||||
comp_k = comp_k->type != GGML_TYPE_F32 ? ggml_cast(ctx0, comp_k, GGML_TYPE_F32) : ggml_cont(ctx0, comp_k);
|
||||
comp_k = llama_mul_mat_hadamard(ctx0, comp_k, k_rot);
|
||||
if (comp_k->type != raw_k->type) {
|
||||
comp_k = ggml_cast(ctx0, comp_k, raw_k->type);
|
||||
}
|
||||
} else if (ggml_is_quantized(comp_k->type)) {
|
||||
// quantized cache without rotation (LLAMA_ATTN_ROT_DISABLE=1): via F32 for the same reason
|
||||
comp_k = ggml_cast(ctx0, ggml_cast(ctx0, comp_k, GGML_TYPE_F32), raw_k->type);
|
||||
} else {
|
||||
comp_k = comp_k->type != raw_k->type ? ggml_cast(ctx0, comp_k, raw_k->type) : ggml_cont(ctx0, comp_k);
|
||||
}
|
||||
cb(comp_k, "comp_k", il);
|
||||
ggml_tensor * k_all = ggml_concat(ctx0, raw_k, comp_k, 2);
|
||||
|
||||
ggml_tensor * sel_mask = build_top_k_mask(ci.kq_mask, top_k, "comp_top_k_mask", il);
|
||||
ggml_tensor * kq_mask = ggml_concat(ctx0, raw_mask, sel_mask, 0);
|
||||
cb(kq_mask, "comp_kq_mask", il);
|
||||
|
||||
// at most window + top-k visible columns per query: lets CUDA FA gather only those
|
||||
const int64_t n_kv_max = sparse ? std::min<int64_t>(raw_mask->ne[0], hparams.n_swa) + k_sel : 0;
|
||||
out = build_attn_mha(q, k_all, k_all, nullptr, kq_mask, layer.attn_sinks, nullptr, n_kv_max, kq_scale, il);
|
||||
}
|
||||
if (k_rot) {
|
||||
out = llama_mul_mat_hadamard(ctx0, out, k_rot);
|
||||
}
|
||||
cb(out, "attn_comp", il);
|
||||
}
|
||||
|
||||
out = ggml_reshape_3d(ctx0, out, n_embd_head, n_head, nt);
|
||||
out = ggml_rope_ext_back(ctx0, out, inp_pos, nullptr, n_embd_head_rope, rope_type, n_ctx_orig_l,
|
||||
freq_base_l, freq_scale_l, ext_factor_l, attn_factor_l, beta_fast_l, beta_slow_l);
|
||||
out = ggml_rope_set_offset(out, n_embd_head_nope);
|
||||
cb(out, "attn_derope", il);
|
||||
|
||||
out = ggml_permute(ctx0, ggml_reshape_3d(ctx0, out, n_head/n_groups*n_embd_head, n_groups, nt), 0, 2, 1, 3);
|
||||
ggml_tensor * oa = ggml_mul_mat(ctx0, layer.wo_a, out);
|
||||
cb(oa, "attn_wo_a", il);
|
||||
oa = ggml_cont_2d(ctx0, ggml_permute(ctx0, oa, 0, 2, 1, 3), o_lora_rank*n_groups, nt);
|
||||
|
||||
out = build_lora_mm(layer.wo_b, oa);
|
||||
cb(out, "attn_out", il);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
llama_model_deepseek41::graph::graph(const llama_model & model_base, const llm_graph_params & params) :
|
||||
llama_model_deepseek4::graph(params) {
|
||||
const auto & model = static_cast<const llama_model_deepseek41 &>(model_base);
|
||||
|
||||
static const bool sparse_env = dsv41_env_on("LLAMA_DSV41_SPARSE");
|
||||
sparse = sparse_env;
|
||||
|
||||
topk_of.assign(n_layer, nullptr);
|
||||
comp_row_of.assign(n_layer, nullptr);
|
||||
|
||||
ggml_tensor * cur;
|
||||
|
||||
ggml_tensor * inp = build_inp_embd(model.tok_embd);
|
||||
ggml_tensor * inp_pos = build_inp_pos();
|
||||
ggml_tensor * inp_out_ids = build_inp_out_ids();
|
||||
llm_graph_input_dsv4 * inp_dsv4 = build_inp_dsv4();
|
||||
ggml_build_forward_expand(gf, inp_dsv4->get_raw()->self_kq_mask);
|
||||
|
||||
auto inp41 = std::make_unique<llm_graph_input_dsv41>(model, inp_dsv4->mctx);
|
||||
// embedding input has no token ids: it skips Engram, like the reference's masked (image) positions
|
||||
// the gathers go right after the token embeddings, so they share the first graph split (the table stays on the host)
|
||||
std::vector<ggml_tensor *> engram_rows;
|
||||
for (int il = 0; ubatch.token && hparams.ple_n_heads > 0 && il < n_layer; ++il) {
|
||||
if (!hparams.is_ple(il)) {
|
||||
continue;
|
||||
}
|
||||
ggml_tensor * ids = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, (int64_t) hparams.ple_n_heads*n_tokens);
|
||||
ggml_set_name(ids, ("engram_ids-" + std::to_string(il)).c_str());
|
||||
ggml_set_input(ids);
|
||||
inp41->engram_ids.push_back(ids);
|
||||
|
||||
ggml_tensor * rows = ggml_get_rows(ctx0, model.per_layer_tok_embd, ids);
|
||||
rows = ggml_reshape_2d(ctx0, rows, hparams.ple_head_dim*hparams.ple_n_heads, n_tokens);
|
||||
cb(rows, "engram_rows", il);
|
||||
ggml_build_forward_expand(gf, rows);
|
||||
engram_rows.push_back(rows);
|
||||
}
|
||||
ggml_tensor * engram_keep = nullptr;
|
||||
if (hparams.ple_n_heads > 0 && ubatch.token && ubatch.embd) {
|
||||
engram_keep = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, 1, n_tokens);
|
||||
ggml_set_input(engram_keep);
|
||||
ggml_set_name(engram_keep, "engram_keep");
|
||||
inp41->engram_keep = engram_keep;
|
||||
}
|
||||
ggml_tensor * cand_pin = nullptr;
|
||||
if (hparams.dsv41_cand_src >= 0) {
|
||||
inp41->cand_csa = hparams.dsv4_compress_ratios[hparams.dsv41_cand_src] == 2;
|
||||
const auto & ci = inp41->cand_csa ? inp_dsv4->get_csa() : inp_dsv4->get_hca();
|
||||
if (ci.kq_mask) {
|
||||
const int64_t bs = hparams.dsv41_cand_block_size;
|
||||
cand_pin = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, (ci.kq_mask->ne[0] + bs - 1)/bs, ci.kq_mask->ne[1], 1, ci.kq_mask->ne[3]);
|
||||
ggml_set_input(cand_pin);
|
||||
ggml_set_name(cand_pin, "cand_pin");
|
||||
inp41->cand_pin = cand_pin;
|
||||
}
|
||||
}
|
||||
res->add_input(std::move(inp41));
|
||||
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
ggml_tensor * inpL = ggml_repeat_4d(ctx0, ggml_reshape_3d(ctx0, inp, n_embd, 1, n_tokens), n_embd, hc, n_tokens, 1);
|
||||
cb(inpL, "hc_init", -1);
|
||||
|
||||
// single-pass mHC: the pre-mix used to collapse the stream lags one sublayer
|
||||
ggml_tensor * pre_prev = nullptr;
|
||||
|
||||
for (int il = 0; il < n_layer; ++il) {
|
||||
const auto & layer = model.layers[il];
|
||||
|
||||
const int e = model.engram_index(il);
|
||||
if (e >= 0 && !engram_rows.empty()) {
|
||||
inpL = build_engram(model, inpL, engram_rows[e], engram_keep, il);
|
||||
cb(inpL, "engram_out", il);
|
||||
}
|
||||
|
||||
// DSpark target features: mean over the hc copies of the layer input
|
||||
if ((size_t) il < cparams.embeddings_layer_inp.size() && cparams.embeddings_layer_inp[il]) {
|
||||
res->t_layer_inp[il] = build_hc_mean(inpL);
|
||||
cb(res->t_layer_inp[il], "layer_inp", il);
|
||||
ggml_build_forward_expand(gf, res->t_layer_inp[il]);
|
||||
}
|
||||
|
||||
ggml_tensor * residual = inpL;
|
||||
ggml_tensor * post = nullptr;
|
||||
ggml_tensor * comb = nullptr;
|
||||
|
||||
ggml_tensor * attn_pre = build_hc_mixes(inpL, layer.hc_attn_fn, layer.hc_attn_scale, layer.hc_attn_base, &post, &comb, true, il);
|
||||
|
||||
// the first layer's attention collapses with the identity pre-mix: copy 0
|
||||
cur = pre_prev ? build_hc_pre(inpL, pre_prev, il) : ggml_cont(ctx0, ggml_view_2d(ctx0, inpL, n_embd, n_tokens, inpL->nb[2], 0));
|
||||
cb(cur, "hc_attn_pre", il);
|
||||
|
||||
cur = build_norm(cur, layer.attn_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(cur, "attn_norm", il);
|
||||
|
||||
cur = build_attention41(model, inp_dsv4, cur, inp_pos, cand_pin, il);
|
||||
|
||||
inpL = build_hc_post(cur, residual, post, comb, il);
|
||||
cb(inpL, "hc_attn_post", il);
|
||||
|
||||
residual = inpL;
|
||||
pre_prev = build_hc_mixes(inpL, layer.hc_ffn_fn, layer.hc_ffn_scale, layer.hc_ffn_base, &post, &comb, true, il);
|
||||
|
||||
cur = build_hc_pre(inpL, attn_pre, il);
|
||||
cb(cur, "hc_ffn_pre", il);
|
||||
|
||||
ggml_build_forward_expand(gf, residual);
|
||||
ggml_build_forward_expand(gf, post);
|
||||
ggml_build_forward_expand(gf, comb);
|
||||
|
||||
cur = build_norm(cur, layer.ffn_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(cur, "ffn_norm", il);
|
||||
|
||||
// F32 router inputs: expert selection is near-tie sensitive
|
||||
ggml_tensor * logits = build_lora_mm(layer.ffn_gate_inp, cur);
|
||||
ggml_prec_set_acc(logits, GGML_PREC_F32);
|
||||
if (!ggml_is_quantized(layer.ffn_gate_inp->type)) {
|
||||
ggml_prec_set_src(logits, GGML_PREC_F32, 1);
|
||||
}
|
||||
cb(logits, "ffn_moe_logits", il);
|
||||
|
||||
// image (embedding) tokens use the vision router bias, as DeepSeek-V4
|
||||
ggml_tensor * exp_probs_b = layer.ffn_exp_probs_b;
|
||||
if (ubatch.embd != nullptr && layer.ffn_exp_probs_b_vl) {
|
||||
exp_probs_b = layer.ffn_exp_probs_b_vl;
|
||||
}
|
||||
|
||||
// the reference normalizes only when it routes to more than one expert
|
||||
ggml_tensor * moe_out = build_moe_ffn(cur,
|
||||
layer.ffn_gate_inp,
|
||||
layer.ffn_up_exps,
|
||||
layer.ffn_gate_exps,
|
||||
layer.ffn_down_exps,
|
||||
exp_probs_b,
|
||||
n_expert, hparams.n_expert_used(),
|
||||
LLM_FFN_SILU, hparams.expert_weights_norm && hparams.n_expert_used() > 1,
|
||||
hparams.expert_weights_scale,
|
||||
(llama_expert_gating_func_type) hparams.expert_gating_func,
|
||||
il, logits);
|
||||
cb(moe_out, "ffn_moe_out", il);
|
||||
|
||||
ggml_tensor * ffn_shexp = build_ffn(cur,
|
||||
layer.ffn_up_shexp, nullptr, nullptr,
|
||||
layer.ffn_gate_shexp, nullptr, nullptr,
|
||||
layer.ffn_down_shexp, nullptr, nullptr,
|
||||
nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
||||
cb(ffn_shexp, "ffn_shexp", il);
|
||||
|
||||
cur = ggml_add(ctx0, moe_out, ffn_shexp);
|
||||
cb(cur, "ffn_out", il);
|
||||
|
||||
inpL = build_hc_post(cur, residual, post, comb, il);
|
||||
inpL = build_cvec(inpL, il);
|
||||
cb(inpL, "l_out", il);
|
||||
}
|
||||
|
||||
// no hc_head: collapse with the last FFN's pre-mix
|
||||
if (inp_out_ids) {
|
||||
inpL = ggml_reshape_3d(ctx0, ggml_get_rows(ctx0, ggml_reshape_2d(ctx0, inpL, n_embd*hc, n_tokens), inp_out_ids), n_embd, hc, inp_out_ids->ne[0]);
|
||||
pre_prev = ggml_get_rows(ctx0, pre_prev, inp_out_ids);
|
||||
}
|
||||
cur = build_hc_pre(inpL, pre_prev, -1);
|
||||
cb(cur, "hc_out", -1);
|
||||
|
||||
cur = build_norm(cur, model.output_norm, nullptr, LLM_NORM_RMS, -1);
|
||||
cb(cur, "result_norm", -1);
|
||||
res->t_embd = cur;
|
||||
|
||||
cur = ggml_mul_mat(ctx0, model.output, cur);
|
||||
cb(cur, "result_output", -1);
|
||||
res->t_logits = cur;
|
||||
}
|
||||
+260
-3
@@ -81,6 +81,7 @@ void llama_model_dflash::load_arch_hparams(llama_model_loader & ml) {
|
||||
ml.get_key(LLM_KV_HYPER_CONNECTION_SINKHORN_ITERATIONS, hparams.dsv4_hc_sinkhorn_iters);
|
||||
ml.get_key(LLM_KV_HYPER_CONNECTION_EPSILON, hparams.dsv4_hc_eps);
|
||||
ml.get_arr(LLM_KV_ATTENTION_COMPRESS_RATIOS, hparams.dsv4_compress_ratios, false);
|
||||
ml.get_key(LLM_KV_HYPER_CONNECTION_SINGLE_PASS, hparams.dsv4_hc_single_pass, false);
|
||||
|
||||
GGML_ASSERT(hparams.dsv4_o_group_count > 0); // avoid div by zero
|
||||
|
||||
@@ -94,6 +95,11 @@ void llama_model_dflash::load_arch_hparams(llama_model_loader & ml) {
|
||||
}
|
||||
|
||||
GGML_ASSERT(hparams.n_swa > 0);
|
||||
if (hparams.dsv4_hc_single_pass) {
|
||||
// DeepSeek-V4.1 DSpark: block queries see the main-KV ring plus the block via the mask row of the
|
||||
// first query, so that row's SWA window must reach window_size positions before the anchor
|
||||
hparams.n_swa += 1;
|
||||
}
|
||||
hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
|
||||
hparams.set_swa_pattern(0);
|
||||
for (uint32_t il = 0; il < hparams.n_layer_all; ++il) {
|
||||
@@ -257,9 +263,12 @@ void llama_model_dflash::load_arch_tensors(llama_model_loader &) {
|
||||
const int64_t hc_dim = hc_mult * n_embd;
|
||||
const int64_t hc_mix_dim = (2 + hc_mult) * hc_mult;
|
||||
|
||||
hc_head_fn = create_tensor(tn(LLM_TENSOR_HC_HEAD_FN, "weight"), {hc_dim, hc_mult}, 0);
|
||||
hc_head_base = create_tensor(tn(LLM_TENSOR_HC_HEAD_BASE, "weight"), {hc_mult}, 0);
|
||||
hc_head_scale = create_tensor(tn(LLM_TENSOR_HC_HEAD_SCALE, "weight"), {1}, 0);
|
||||
// single-pass mHC: collapse with the last FFN pre-mix, no hc_head
|
||||
if (!hparams.dsv4_hc_single_pass) {
|
||||
hc_head_fn = create_tensor(tn(LLM_TENSOR_HC_HEAD_FN, "weight"), {hc_dim, hc_mult}, 0);
|
||||
hc_head_base = create_tensor(tn(LLM_TENSOR_HC_HEAD_BASE, "weight"), {hc_mult}, 0);
|
||||
hc_head_scale = create_tensor(tn(LLM_TENSOR_HC_HEAD_SCALE, "weight"), {1}, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_layer; ++i) {
|
||||
auto & layer = layers[i];
|
||||
@@ -1280,12 +1289,260 @@ llama_model_dflash::graph_dsv4::graph_dsv4(const llama_model & model, const llm_
|
||||
}
|
||||
}
|
||||
|
||||
// DeepSeek-V4.1 DSpark decoder (mtp.0..2), dual-mode by batch type like graph_dsv4:
|
||||
// * embd batch -> writes every stage's kv_norm(wkv(main_proj(target features))) into its window ring
|
||||
// * token batch -> [anchor, noise...] block through the stages, single-pass mHC, no per-head q norm
|
||||
llama_model_dflash::graph_dsv41::graph_dsv41(const llama_model & model, const llm_graph_params & params) :
|
||||
llama_model_deepseek41::graph(params) {
|
||||
const int64_t n_embd_inp = hparams.n_embd_inp_enc();
|
||||
const int64_t n_embd_head = hparams.n_embd_head_k();
|
||||
const int64_t n_embd_head_rope = hparams.n_rot();
|
||||
const int64_t n_embd_head_nope = n_embd_head - n_embd_head_rope;
|
||||
|
||||
GGML_ASSERT(hparams.dsv4_hc_single_pass);
|
||||
|
||||
ggml_tensor * inp_pos = build_inp_pos();
|
||||
|
||||
llm_graph_input_attn_k_iswa * inp_attn = build_attn_inp_k_iswa();
|
||||
|
||||
// ratio-0 layers: plain rope at rope_theta, no YaRN (original_seq_len 0)
|
||||
const auto rope = [&](ggml_tensor * t) {
|
||||
t = ggml_rope_ext(ctx0, t, inp_pos, nullptr, n_embd_head_rope, rope_type, 0,
|
||||
freq_base, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
return ggml_rope_set_offset(t, n_embd_head_nope);
|
||||
};
|
||||
|
||||
ASSERT_EMBD_OR_TOKEN(ubatch);
|
||||
if (ubatch.embd) {
|
||||
auto inp = std::make_unique<llm_graph_input_embd>(n_embd_inp);
|
||||
|
||||
inp->embd = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_embd_inp, n_tokens);
|
||||
ggml_set_input(inp->embd);
|
||||
|
||||
ggml_tensor * inp_target = inp->embd;
|
||||
cb(inp_target, "inp_target_features", -1);
|
||||
|
||||
res->add_input(std::move(inp));
|
||||
|
||||
ggml_tensor * inp_g = build_lora_mm(model.fc, inp_target, model.fc_s);
|
||||
inp_g = build_norm(inp_g, model.output_norm_enc, nullptr, LLM_NORM_RMS, -1);
|
||||
cb(inp_g, "main_x", -1);
|
||||
|
||||
for (int il = 0; il < n_layer; ++il) {
|
||||
const auto & layer = model.layers[il];
|
||||
|
||||
ggml_tensor * kv = build_lora_mm(layer.wkv, inp_g);
|
||||
kv = build_norm(kv, layer.attn_kv_norm, nullptr, LLM_NORM_RMS, il);
|
||||
kv = ggml_reshape_3d(ctx0, kv, n_embd_head, 1, n_tokens);
|
||||
kv = rope(kv);
|
||||
cb(kv, "kv_injected", il);
|
||||
|
||||
if (inp_attn->self_k_rot_swa) {
|
||||
kv = llama_mul_mat_hadamard(ctx0, kv, inp_attn->self_k_rot_swa);
|
||||
}
|
||||
ggml_build_forward_expand(gf, inp_attn->mctx->get_swa()->cpy_k(ctx0, kv, inp_attn->get_k_idxs_swa(), il));
|
||||
}
|
||||
|
||||
res->t_embd = inp_g;
|
||||
|
||||
ggml_build_forward_expand(gf, inp_g);
|
||||
return;
|
||||
}
|
||||
|
||||
auto * tok_embd = model.tok_embd;
|
||||
if (tok_embd == nullptr) {
|
||||
GGML_ASSERT(cparams.ctx_other != nullptr);
|
||||
const auto * model_other = llama_get_model(cparams.ctx_other);
|
||||
GGML_ASSERT(model_other->tok_embd != nullptr && "DSpark decoder requires the target model's token embeddings");
|
||||
tok_embd = model_other->tok_embd;
|
||||
}
|
||||
|
||||
auto inp = std::make_unique<llm_graph_input_embd>(n_embd);
|
||||
|
||||
inp->tokens = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_tokens);
|
||||
ggml_set_input(inp->tokens);
|
||||
|
||||
ggml_tensor * inp_tokens = inp->tokens;
|
||||
|
||||
ggml_tensor * inpL = ggml_get_rows(ctx0, tok_embd, inp->tokens);
|
||||
cb(inpL, "inp_noise_embd", -1);
|
||||
|
||||
res->add_input(std::move(inp));
|
||||
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
const int64_t n_groups = hparams.dsv4_o_group_count;
|
||||
const int64_t n_heads_group = n_head / n_groups;
|
||||
const int64_t o_lora_rank = hparams.dsv4_o_lora_rank;
|
||||
const int64_t o_group_dim = n_heads_group*n_embd_head;
|
||||
const float kq_scale = 1.0f/sqrtf(float(n_embd_head));
|
||||
|
||||
GGML_ASSERT(n_embd_head == n_embd_head_v);
|
||||
GGML_ASSERT(n_head % n_groups == 0);
|
||||
|
||||
inpL = ggml_reshape_3d(ctx0, inpL, n_embd, 1, n_tokens);
|
||||
inpL = ggml_repeat_4d(ctx0, inpL, n_embd, hc, n_tokens, 1);
|
||||
cb(inpL, "hc_init", -1);
|
||||
|
||||
// window of a block = window of its first query, see n_swa
|
||||
{
|
||||
ggml_tensor * m = inp_attn->self_kq_mask_swa_cnv;
|
||||
const int64_t n_blocks = ubatch.n_seqs_unq;
|
||||
const int64_t bd = n_blocks > 0 ? n_tokens / n_blocks : 0;
|
||||
if (m && bd > 1 && n_tokens % n_blocks == 0 && m->ne[2] == 1 && m->ne[1] % bd == 0 && ggml_is_contiguous(m)) {
|
||||
ggml_tensor * m4 = ggml_reshape_4d(ctx0, m, m->ne[0], bd, m->ne[1]/bd, m->ne[3]);
|
||||
ggml_tensor * r0 = ggml_view_4d(ctx0, m4, m4->ne[0], 1, m4->ne[2], m4->ne[3], m4->nb[1], m4->nb[2], m4->nb[3], 0);
|
||||
m4 = ggml_repeat(ctx0, r0, m4);
|
||||
inp_attn->self_kq_mask_swa_cnv = ggml_reshape_4d(ctx0, m4, m->ne[0], m->ne[1], 1, m->ne[3]);
|
||||
cb(inp_attn->self_kq_mask_swa_cnv, "dspark_block_mask", -1);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor * pre_prev = nullptr;
|
||||
|
||||
for (int il = 0; il < n_layer; ++il) {
|
||||
const auto & layer = model.layers[il];
|
||||
|
||||
ggml_tensor * residual = inpL;
|
||||
ggml_tensor * post = nullptr;
|
||||
ggml_tensor * comb = nullptr;
|
||||
|
||||
ggml_tensor * attn_pre = build_hc_mixes(inpL, layer.hc_attn_fn, layer.hc_attn_scale, layer.hc_attn_base, &post, &comb, false, il);
|
||||
|
||||
ggml_tensor * cur;
|
||||
if (pre_prev == nullptr) {
|
||||
// identity pre-mix of the first stage: copy 0
|
||||
cur = ggml_cont(ctx0, ggml_view_2d(ctx0, inpL, n_embd, n_tokens, inpL->nb[2], 0));
|
||||
} else {
|
||||
cur = build_hc_pre(inpL, pre_prev, il);
|
||||
}
|
||||
cb(cur, "hc_attn_pre", il);
|
||||
|
||||
cur = build_norm(cur, layer.attn_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(cur, "attn_norm", il);
|
||||
|
||||
// non-causal draft context: block queries see the main-KV ring plus every block key
|
||||
{
|
||||
ggml_tensor * qr = build_lora_mm(layer.wq_a, cur);
|
||||
qr = build_norm(qr, layer.attn_q_a_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(qr, "qr_norm", il);
|
||||
|
||||
ggml_tensor * q = build_lora_mm(layer.wq_b, qr);
|
||||
q = ggml_reshape_3d(ctx0, q, n_embd_head, n_head, n_tokens);
|
||||
q = rope(q);
|
||||
cb(q, "q", il);
|
||||
|
||||
ggml_tensor * kv = build_lora_mm(layer.wkv, cur);
|
||||
kv = build_norm(kv, layer.attn_kv_norm, nullptr, LLM_NORM_RMS, il);
|
||||
kv = ggml_reshape_3d(ctx0, kv, n_embd_head, 1, n_tokens);
|
||||
kv = rope(kv);
|
||||
cb(kv, "kv", il);
|
||||
|
||||
ggml_tensor * out = build_attn(inp_attn,
|
||||
nullptr, nullptr, nullptr,
|
||||
q, kv, kv,
|
||||
nullptr, layer.attn_sinks, nullptr,
|
||||
kq_scale, il);
|
||||
cb(out, "attn_raw", il);
|
||||
|
||||
out = ggml_reshape_3d(ctx0, out, n_embd_head, n_head, n_tokens);
|
||||
out = ggml_rope_ext_back(ctx0, out, inp_pos, nullptr, n_embd_head_rope, rope_type, 0,
|
||||
freq_base, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f);
|
||||
out = ggml_rope_set_offset(out, n_embd_head_nope);
|
||||
cb(out, "attn_derope", il);
|
||||
|
||||
out = ggml_reshape_3d(ctx0, out, o_group_dim, n_groups, n_tokens);
|
||||
out = ggml_permute(ctx0, out, 0, 2, 1, 3);
|
||||
ggml_tensor * oa = ggml_mul_mat(ctx0, layer.wo_a, out);
|
||||
cb(oa, "attn_wo_a", il);
|
||||
oa = ggml_permute(ctx0, oa, 0, 2, 1, 3);
|
||||
oa = ggml_cont_2d(ctx0, oa, o_lora_rank*n_groups, n_tokens);
|
||||
|
||||
cur = build_lora_mm(layer.wo_b, oa);
|
||||
cb(cur, "attn_out", il);
|
||||
}
|
||||
|
||||
inpL = build_hc_post(cur, residual, post, comb, il);
|
||||
cb(inpL, "hc_attn_post", il);
|
||||
|
||||
residual = inpL;
|
||||
ggml_tensor * ffn_pre = build_hc_mixes(inpL, layer.hc_ffn_fn, layer.hc_ffn_scale, layer.hc_ffn_base, &post, &comb, false, il);
|
||||
|
||||
cur = build_hc_pre(inpL, attn_pre, il);
|
||||
cb(cur, "hc_ffn_pre", il);
|
||||
|
||||
cur = build_norm(cur, layer.ffn_norm, nullptr, LLM_NORM_RMS, il);
|
||||
cb(cur, "ffn_norm", il);
|
||||
|
||||
ggml_tensor * moe_out = build_moe_ffn(cur,
|
||||
layer.ffn_gate_inp,
|
||||
layer.ffn_up_exps,
|
||||
layer.ffn_gate_exps,
|
||||
layer.ffn_down_exps,
|
||||
layer.ffn_exp_probs_b,
|
||||
n_expert, hparams.n_expert_used(),
|
||||
LLM_FFN_SILU, hparams.expert_weights_norm,
|
||||
hparams.expert_weights_scale,
|
||||
(llama_expert_gating_func_type) hparams.expert_gating_func,
|
||||
il);
|
||||
cb(moe_out, "ffn_moe_out", il);
|
||||
|
||||
ggml_tensor * ffn_shexp = build_ffn(cur,
|
||||
layer.ffn_up_shexp, nullptr, nullptr,
|
||||
layer.ffn_gate_shexp, nullptr, nullptr,
|
||||
layer.ffn_down_shexp, nullptr, nullptr,
|
||||
nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
||||
cb(ffn_shexp, "ffn_shexp", il);
|
||||
|
||||
cur = ggml_add(ctx0, moe_out, ffn_shexp);
|
||||
cb(cur, "ffn_out", il);
|
||||
|
||||
inpL = build_hc_post(cur, residual, post, comb, il);
|
||||
|
||||
ggml_tensor * l_out = ggml_reshape_2d(ctx0, inpL, n_embd*hc, n_tokens);
|
||||
cb(l_out, "l_out", il);
|
||||
ggml_build_forward_expand(gf, l_out);
|
||||
|
||||
pre_prev = ffn_pre;
|
||||
}
|
||||
|
||||
// no hc_head: collapse with the last FFN's pre-mix
|
||||
ggml_tensor * cur = build_hc_pre(inpL, pre_prev, -1);
|
||||
cb(cur, "hc_out", -1);
|
||||
|
||||
// confidence head takes the un-normed collapsed state
|
||||
res->t_embd = cur;
|
||||
|
||||
cur = build_norm(cur, model.output_norm, nullptr, LLM_NORM_RMS, -1);
|
||||
cb(cur, "result_norm", -1);
|
||||
|
||||
auto * output = model.output;
|
||||
auto * output_s = model.output_s;
|
||||
if (output == nullptr) {
|
||||
GGML_ASSERT(cparams.ctx_other != nullptr);
|
||||
const auto * model_other = llama_get_model(cparams.ctx_other);
|
||||
GGML_ASSERT(model_other->output != nullptr && "DSpark decoder requires the target model's output projection");
|
||||
output = model_other->output;
|
||||
output_s = model_other->output_s;
|
||||
}
|
||||
|
||||
cur = build_lora_mm(output, cur, output_s);
|
||||
cb(cur, "result_output", -1);
|
||||
res->t_logits = cur;
|
||||
|
||||
if (model.dspark_markov_w1) {
|
||||
build_dspark_markov_head(*this, model, inp_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<llm_graph_context> llama_model_dflash::build_arch_graph(const llm_graph_params & params) const {
|
||||
switch (params.gtype) {
|
||||
case LLM_GRAPH_TYPE_ENCODER:
|
||||
return std::make_unique<graph<true>>(*this, params);
|
||||
case LLM_GRAPH_TYPE_DEFAULT:
|
||||
case LLM_GRAPH_TYPE_DECODER:
|
||||
if (hparams.dsv4_hc_mult > 0 && hparams.dsv4_hc_single_pass) {
|
||||
return std::make_unique<graph_dsv41>(*this, params);
|
||||
}
|
||||
if (hparams.dsv4_hc_mult > 0) {
|
||||
return std::make_unique<graph_dsv4>(*this, params);
|
||||
}
|
||||
|
||||
@@ -1331,6 +1331,15 @@ struct llama_model_dots3note : public llama_model_base {
|
||||
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
||||
};
|
||||
|
||||
// YaRN attention factor that cancels ggml's rope mscale (the DeepSeek-V4 reference applies none)
|
||||
static inline float dsv4_rope_attn_factor(float freq_scale, float ext_factor) {
|
||||
if (ext_factor == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 1.0f / (1.0f + 0.1f*logf(1.0f/freq_scale));
|
||||
}
|
||||
|
||||
struct llama_model_deepseek4 : public llama_model_base {
|
||||
llama_model_deepseek4(const struct llama_model_params & params) : llama_model_base(params) {}
|
||||
void load_arch_hparams(llama_model_loader & ml) override;
|
||||
@@ -1359,6 +1368,17 @@ struct llama_model_deepseek4 : public llama_model_base {
|
||||
ggml_tensor ** comb,
|
||||
int il) const;
|
||||
|
||||
// returns the pre weights and fills post / comb
|
||||
ggml_tensor * build_hc_mixes(
|
||||
ggml_tensor * x,
|
||||
ggml_tensor * hc_fn,
|
||||
ggml_tensor * hc_scale,
|
||||
ggml_tensor * hc_base,
|
||||
ggml_tensor ** post,
|
||||
ggml_tensor ** comb,
|
||||
bool f32_src,
|
||||
int il) const;
|
||||
|
||||
ggml_tensor * build_hc_post(
|
||||
ggml_tensor * x,
|
||||
ggml_tensor * residual,
|
||||
@@ -1473,6 +1493,57 @@ struct llama_model_deepseek4 : public llama_model_base {
|
||||
};
|
||||
|
||||
|
||||
struct llama_model_deepseek41 : public llama_model_deepseek4 {
|
||||
llama_model_deepseek41(const struct llama_model_params & params) : llama_model_deepseek4(params) {}
|
||||
void load_arch_hparams(llama_model_loader & ml) override;
|
||||
void load_arch_tensors(llama_model_loader & ml) override;
|
||||
|
||||
// Engram: the hash constants are hparams.ple_* (one module per Engram layer), hashed on the host (ggml has no
|
||||
// int64 mul/xor/mod); the token map and its pad id are V4.1 only
|
||||
uint32_t engram_pad_id = 0;
|
||||
std::vector<int32_t> engram_token_map;
|
||||
|
||||
int engram_index(int il) const;
|
||||
|
||||
void engram_hash(int e, const llama_ubatch & ubatch, const std::vector<llama_token> & prev, std::vector<int32_t> & out) const;
|
||||
|
||||
struct graph : public llama_model_deepseek4::graph {
|
||||
graph(const llama_model & model, const llm_graph_params & params);
|
||||
// helpers only (no graph built): base of graph_dsv41
|
||||
explicit graph(const llm_graph_params & params) : llama_model_deepseek4::graph(params) {}
|
||||
|
||||
ggml_tensor * build_engram(
|
||||
const llama_model_deepseek41 & model,
|
||||
ggml_tensor * x,
|
||||
ggml_tensor * rows,
|
||||
ggml_tensor * keep,
|
||||
int il) const;
|
||||
|
||||
ggml_tensor * build_attention41(
|
||||
const llama_model_deepseek41 & model,
|
||||
llm_graph_input_dsv4 * inp_dsv4,
|
||||
ggml_tensor * cur,
|
||||
ggml_tensor * inp_pos,
|
||||
ggml_tensor * cand_pin,
|
||||
int il);
|
||||
|
||||
std::vector<ggml_tensor *> topk_of; // top-k of each index source layer
|
||||
std::vector<ggml_tensor *> comp_row_of; // [n_layer] new compressed rows of each kv source (for mirrors)
|
||||
ggml_tensor * cand_sel = nullptr; // candidate blocks of the candidate source layer
|
||||
int64_t cand_n_comp = 0; // compressed rows the candidate source scored
|
||||
|
||||
// sparse path (LLAMA_DSV41_SPARSE=0 disables): fused indexer, FA bounded to window + top-k, decode gather
|
||||
bool sparse = true;
|
||||
// gather path, shared by layers with the same top-k: selected K rows per (storage layer, top-k source),
|
||||
// the sorted top-k and the mask per top-k source (one input copy per split)
|
||||
std::map<std::pair<int32_t, int32_t>, ggml_tensor *> gather_k_of;
|
||||
std::map<int32_t, ggml_tensor *> gather_sel_of;
|
||||
std::map<int32_t, ggml_tensor *> gather_mask_of;
|
||||
};
|
||||
|
||||
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
||||
};
|
||||
|
||||
struct llama_model_deepseek2ocr : public llama_model_base {
|
||||
llama_model_deepseek2ocr(const struct llama_model_params & params) : llama_model_base(params) {}
|
||||
void load_arch_hparams(llama_model_loader & ml) override;
|
||||
@@ -1532,6 +1603,11 @@ struct llama_model_dflash : public llama_model_base {
|
||||
graph_dsv4(const llama_model & model, const llm_graph_params & params);
|
||||
};
|
||||
|
||||
// DeepSeek-V4.1 DSpark: single-pass mHC (lagged pre-mix, no hc_head), no per-head q norm
|
||||
struct graph_dsv41 : public llama_model_deepseek41::graph {
|
||||
graph_dsv41(const llama_model & model, const llm_graph_params & params);
|
||||
};
|
||||
|
||||
std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
||||
};
|
||||
|
||||
@@ -2665,6 +2741,7 @@ struct llama_model_qwen4exp : public llama_model_base {
|
||||
ggml_tensor * w_down,
|
||||
ggml_tensor * w_up,
|
||||
ggml_tensor * w_inject,
|
||||
ggml_tensor * w_down_inject, // [w_down | w_inject] in one weight, can be null
|
||||
ggml_tensor ** inject,
|
||||
int il);
|
||||
|
||||
|
||||
+90
-24
@@ -115,6 +115,9 @@ void llama_model_qwen4exp::load_arch_hparams(llama_model_loader & ml) {
|
||||
|
||||
hparams.ple_n_heads = (hparams.ple_ngram_size - 1) * hparams.ple_heads_per_ngram;
|
||||
hparams.ple_head_dim = hparams.n_embd_per_layer;
|
||||
hparams.ple_embd_token = hparams.ple_image_token_id != 0
|
||||
? (llama_token) hparams.ple_image_token_id
|
||||
: (llama_token) hparams.ple_eos_token_id;
|
||||
if (hparams.ple_ngram_size < 2 || hparams.ple_ngram_size > LLAMA_MAX_PLE_NGRAM) {
|
||||
throw std::runtime_error(format("PLE n-gram size %u is out of range", hparams.ple_ngram_size));
|
||||
}
|
||||
@@ -228,15 +231,32 @@ void llama_model_qwen4exp::load_arch_tensors(llama_model_loader & ml) {
|
||||
const int64_t value_dim = head_v_dim * n_v_heads;
|
||||
const int64_t conv_dim = key_dim * 2 + value_dim;
|
||||
|
||||
// matmuls that read the same input: try the merged [a | b] weight the converter writes, else the separate ones
|
||||
auto create_merged = [&](llm_tensor t_merged, llm_tensor t_a, llm_tensor t_b, int64_t n_in, int64_t n_a, int64_t n_b,
|
||||
ggml_tensor *& merged, ggml_tensor *& a, ggml_tensor *& b) {
|
||||
if (flags & TENSOR_SKIP) {
|
||||
const int skip = TENSOR_NOT_REQUIRED | TENSOR_SKIP;
|
||||
create_tensor(tn(t_merged, "weight", il), { n_in, n_a + n_b }, skip | TENSOR_SKIP_IF_VIRTUAL);
|
||||
create_tensor(tn(t_a, "weight", il), { n_in, n_a }, skip);
|
||||
create_tensor(tn(t_b, "weight", il), { n_in, n_b }, skip);
|
||||
return;
|
||||
}
|
||||
merged = create_tensor(tn(t_merged, "weight", il), { n_in, n_a + n_b }, flags | TENSOR_NOT_REQUIRED | TENSOR_SKIP_IF_VIRTUAL);
|
||||
if (!merged) {
|
||||
a = create_tensor(tn(t_a, "weight", il), { n_in, n_a }, flags);
|
||||
b = create_tensor(tn(t_b, "weight", il), { n_in, n_b }, flags);
|
||||
}
|
||||
};
|
||||
|
||||
// two HC modules per layer: before the token mixer, before the MoE
|
||||
layer.hc_attn_norm = create_tensor(tn(LLM_TENSOR_HC_ATTN_NORM, "weight", il), { n_embd, hc }, flags | TENSOR_ALLOW_RESHAPE);
|
||||
layer.hc_attn_down = create_tensor(tn(LLM_TENSOR_HC_ATTN_DOWN, "weight", il), { hc_dim, hc_lr }, flags);
|
||||
layer.hc_attn_up = create_tensor(tn(LLM_TENSOR_HC_ATTN_UP, "weight", il), { hc_lr, hc_dim }, flags);
|
||||
layer.hc_attn_inject = create_tensor(tn(LLM_TENSOR_HC_ATTN_INJECT, "weight", il), { hc_dim, hc }, flags);
|
||||
create_merged(LLM_TENSOR_HC_ATTN_DOWN_INJECT, LLM_TENSOR_HC_ATTN_DOWN, LLM_TENSOR_HC_ATTN_INJECT, hc_dim, hc_lr, hc,
|
||||
layer.hc_attn_down_inject, layer.hc_attn_down, layer.hc_attn_inject);
|
||||
layer.hc_ffn_norm = create_tensor(tn(LLM_TENSOR_HC_FFN_NORM, "weight", il), { n_embd, hc }, flags | TENSOR_ALLOW_RESHAPE);
|
||||
layer.hc_ffn_down = create_tensor(tn(LLM_TENSOR_HC_FFN_DOWN, "weight", il), { hc_dim, hc_lr }, flags);
|
||||
layer.hc_ffn_up = create_tensor(tn(LLM_TENSOR_HC_FFN_UP, "weight", il), { hc_lr, hc_dim }, flags);
|
||||
layer.hc_ffn_inject = create_tensor(tn(LLM_TENSOR_HC_FFN_INJECT, "weight", il), { hc_dim, hc }, flags);
|
||||
create_merged(LLM_TENSOR_HC_FFN_DOWN_INJECT, LLM_TENSOR_HC_FFN_DOWN, LLM_TENSOR_HC_FFN_INJECT, hc_dim, hc_lr, hc,
|
||||
layer.hc_ffn_down_inject, layer.hc_ffn_down, layer.hc_ffn_inject);
|
||||
|
||||
if (!hparams.is_recr(il)) {
|
||||
// full attention: wq holds [q|gate] interleaved per head
|
||||
@@ -252,13 +272,13 @@ void llama_model_qwen4exp::load_arch_tensors(llama_model_loader & ml) {
|
||||
layer.index_q_norm = create_tensor(tn(LLM_TENSOR_INDEXER_Q_NORM, "weight", il), { idx_dim }, flags);
|
||||
layer.index_k_norm = create_tensor(tn(LLM_TENSOR_INDEXER_K_NORM, "weight", il), { idx_dim }, flags);
|
||||
} else {
|
||||
layer.wqkv = create_tensor(tn(LLM_TENSOR_ATTN_QKV, "weight", il), { n_embd, key_dim * 2 + value_dim }, flags);
|
||||
layer.wqkv_gate = create_tensor(tn(LLM_TENSOR_ATTN_GATE, "weight", il), { n_embd, value_dim }, flags);
|
||||
create_merged(LLM_TENSOR_ATTN_QKV_GATE, LLM_TENSOR_ATTN_QKV, LLM_TENSOR_ATTN_GATE, n_embd, key_dim * 2 + value_dim, value_dim,
|
||||
layer.wqkv_z, layer.wqkv, layer.wqkv_gate);
|
||||
layer.ssm_conv1d = create_tensor(tn(LLM_TENSOR_SSM_CONV1D, "weight", il), { hparams.ssm_d_conv, conv_dim }, flags);
|
||||
layer.ssm_dt = create_tensor(tn(LLM_TENSOR_SSM_DT, "bias", il), { hparams.ssm_dt_rank }, flags);
|
||||
layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A_NOSCAN, il), { hparams.ssm_dt_rank }, flags);
|
||||
layer.ssm_beta = create_tensor(tn(LLM_TENSOR_SSM_BETA, "weight", il), { n_embd, n_v_heads }, flags);
|
||||
layer.ssm_alpha = create_tensor(tn(LLM_TENSOR_SSM_ALPHA, "weight", il), { n_embd, n_v_heads }, flags);
|
||||
create_merged(LLM_TENSOR_SSM_BETA_ALPHA_CAT, LLM_TENSOR_SSM_BETA, LLM_TENSOR_SSM_ALPHA, n_embd, n_v_heads, n_v_heads,
|
||||
layer.ssm_beta_alpha_cat, layer.ssm_beta, layer.ssm_alpha);
|
||||
layer.ssm_norm = create_tensor(tn(LLM_TENSOR_SSM_NORM, "weight", il), { head_v_dim }, flags);
|
||||
layer.ssm_out = create_tensor(tn(LLM_TENSOR_SSM_OUT, "weight", il), { value_dim, n_embd }, flags);
|
||||
}
|
||||
@@ -330,6 +350,7 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
ggml_tensor * w_down,
|
||||
ggml_tensor * w_up,
|
||||
ggml_tensor * w_inject,
|
||||
ggml_tensor * w_down_inject,
|
||||
ggml_tensor ** inject,
|
||||
int il) {
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
@@ -342,8 +363,20 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
xn = ggml_reshape_2d(ctx0, xn, hc_dim, nt);
|
||||
cb(xn, "hc_norm", il);
|
||||
|
||||
ggml_tensor * lo = build_lora_mm(w_down, xn);
|
||||
lo = ggml_silu(ctx0, ggml_scale(ctx0, lo, 1.0f / (float) hc));
|
||||
ggml_tensor * lo = nullptr;
|
||||
if (inject && w_down_inject) {
|
||||
// one matmul for both: the hc_down rows, then the hc_inject rows
|
||||
const int64_t n_lo = w_down_inject->ne[1] - hc;
|
||||
ggml_tensor * lo_inject = build_lora_mm(w_down_inject, xn);
|
||||
// scale the combined rows before the split; both lo and inject use 1/hc
|
||||
lo_inject = ggml_scale(ctx0, lo_inject, 1.0f / (float) hc);
|
||||
lo = ggml_view_2d(ctx0, lo_inject, n_lo, nt, lo_inject->nb[1], 0);
|
||||
*inject = ggml_view_2d(ctx0, lo_inject, hc, nt, lo_inject->nb[1], ggml_row_size(lo_inject->type, n_lo));
|
||||
cb(*inject, "hc_inject", il);
|
||||
} else {
|
||||
lo = ggml_scale(ctx0, build_lora_mm(w_down, xn), 1.0f / (float) hc);
|
||||
}
|
||||
lo = ggml_silu(ctx0, lo);
|
||||
ggml_tensor * gate = build_lora_mm(w_up, lo);
|
||||
cb(gate, "hc_gate", il);
|
||||
|
||||
@@ -372,8 +405,8 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
}
|
||||
cb(mixed, "hc_mixed", il);
|
||||
|
||||
if (inject) {
|
||||
*inject = build_lora_mm(w_inject, xn);
|
||||
if (inject && !w_down_inject) {
|
||||
*inject = ggml_scale(ctx0, build_lora_mm(w_inject, xn), 1.0f / (float) hc);
|
||||
cb(*inject, "hc_inject", il);
|
||||
}
|
||||
|
||||
@@ -389,7 +422,7 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_combine(
|
||||
const int64_t nt = residual->ne[2];
|
||||
|
||||
// 2*sigmoid centres the scatter weights on 1, so a zero injection is a plain residual add
|
||||
ggml_tensor * w = ggml_sigmoid(ctx0, ggml_scale(ctx0, inject, 1.0f / (float) hc));
|
||||
ggml_tensor * w = ggml_sigmoid(ctx0, inject);
|
||||
w = ggml_scale(ctx0, w, 2.0f);
|
||||
|
||||
ggml_tensor * cur = nullptr;
|
||||
@@ -477,6 +510,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
model.layers[il].hc_attn_down,
|
||||
model.layers[il].hc_attn_up,
|
||||
model.layers[il].hc_attn_inject,
|
||||
model.layers[il].hc_attn_down_inject,
|
||||
&inject, il);
|
||||
|
||||
ggml_build_forward_expand(gf, cur);
|
||||
@@ -504,6 +538,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
model.layers[il].hc_ffn_down,
|
||||
model.layers[il].hc_ffn_up,
|
||||
model.layers[il].hc_ffn_inject,
|
||||
model.layers[il].hc_ffn_down_inject,
|
||||
&inject, il);
|
||||
|
||||
cur = build_layer_ffn(cur, il);
|
||||
@@ -529,7 +564,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
// the final mixer is the output norm: there is no separate one
|
||||
ggml_tensor * cur = build_hc_mix(res_hc,
|
||||
model.hc_head_norm, model.hc_head_down, model.hc_head_up,
|
||||
nullptr, nullptr, -1);
|
||||
nullptr, nullptr, nullptr, -1);
|
||||
|
||||
cb(cur, "result_norm", -1);
|
||||
res->t_embd = cur;
|
||||
@@ -607,11 +642,11 @@ llama_model_qwen4exp::graph_mtp::graph_mtp(const llama_model & model, const llm_
|
||||
cb(res_hc, "mtp_eh_proj", il);
|
||||
|
||||
ggml_tensor * inject = nullptr;
|
||||
ggml_tensor * cur = build_hc_mix(res_hc, layer.hc_attn_norm, layer.hc_attn_down, layer.hc_attn_up, layer.hc_attn_inject, &inject, il);
|
||||
ggml_tensor * cur = build_hc_mix(res_hc, layer.hc_attn_norm, layer.hc_attn_down, layer.hc_attn_up, layer.hc_attn_inject, layer.hc_attn_down_inject, &inject, il);
|
||||
cur = build_layer_attn(inp_hyb->get_attn(), mctx_hyb, inp_kpool, cur, inp_pos, sections, il);
|
||||
res_hc = build_hc_combine(res_hc, cur, inject, il);
|
||||
|
||||
cur = build_hc_mix(res_hc, layer.hc_ffn_norm, layer.hc_ffn_down, layer.hc_ffn_up, layer.hc_ffn_inject, &inject, il);
|
||||
cur = build_hc_mix(res_hc, layer.hc_ffn_norm, layer.hc_ffn_down, layer.hc_ffn_up, layer.hc_ffn_inject, layer.hc_ffn_down_inject, &inject, il);
|
||||
cur = build_layer_ffn(cur, il);
|
||||
res_hc = build_hc_combine(res_hc, cur, inject, il);
|
||||
|
||||
@@ -624,7 +659,7 @@ llama_model_qwen4exp::graph_mtp::graph_mtp(const llama_model & model, const llm_
|
||||
|
||||
cur = build_hc_mix(ggml_reshape_3d(ctx0, flat_out, n_embd, hc, flat_out->ne[1]),
|
||||
layer.nextn.hc_head_norm, layer.nextn.hc_head_down, layer.nextn.hc_head_up,
|
||||
nullptr, nullptr, il);
|
||||
nullptr, nullptr, nullptr, il);
|
||||
cb(cur, "result_norm", -1);
|
||||
res->t_embd = cur;
|
||||
|
||||
@@ -639,6 +674,23 @@ std::pair<ggml_tensor *, ggml_tensor *> llama_model_qwen4exp::graph::build_qkvz(
|
||||
const int64_t n_seqs = ubatch.n_seqs;
|
||||
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
|
||||
|
||||
if (model.layers[il].wqkv_z) {
|
||||
// qkv and z read the same input: one matmul, then the qkv rows and the z rows
|
||||
ggml_tensor * qkvz = build_lora_mm(model.layers[il].wqkv_z, input);
|
||||
cb(qkvz, "linear_attn_qkvz", il);
|
||||
|
||||
const int64_t n_z = hparams.ssm_d_state * hparams.ssm_dt_rank;
|
||||
const int64_t n_qkv = qkvz->ne[0] - n_z;
|
||||
|
||||
ggml_tensor * qkv_mixed = ggml_view_3d(ctx0, qkvz, n_qkv, n_seq_tokens, n_seqs, qkvz->nb[1], qkvz->nb[1] * n_seq_tokens, 0);
|
||||
cb(qkv_mixed, "linear_attn_qkv_mixed", il);
|
||||
|
||||
ggml_tensor * z = ggml_view_2d(ctx0, qkvz, n_z, n_seq_tokens * n_seqs, qkvz->nb[1], ggml_row_size(qkvz->type, n_qkv));
|
||||
cb(z, "z", il);
|
||||
|
||||
return { qkv_mixed, z };
|
||||
}
|
||||
|
||||
ggml_tensor * qkv_mixed = build_lora_mm(model.layers[il].wqkv, input, model.layers[il].wqkv_s);
|
||||
qkv_mixed = ggml_reshape_3d(ctx0, qkv_mixed, qkv_mixed->ne[0], n_seq_tokens, n_seqs);
|
||||
cb(qkv_mixed, "linear_attn_qkv_mixed", il);
|
||||
@@ -1069,17 +1121,30 @@ ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear(
|
||||
ggml_tensor * qkv_mixed = qkvz.first;
|
||||
ggml_tensor * z = qkvz.second;
|
||||
|
||||
ggml_tensor * beta = build_lora_mm(model.layers[il].ssm_beta, cur, model.layers[il].ssm_beta_s);
|
||||
beta = ggml_reshape_4d(ctx0, beta, 1, num_v_heads, n_seq_tokens, n_seqs);
|
||||
ggml_tensor * beta = nullptr;
|
||||
ggml_tensor * alpha = nullptr;
|
||||
if (model.layers[il].ssm_beta_alpha_cat) {
|
||||
// beta and alpha read the same input: one matmul, then the beta rows and the alpha rows
|
||||
ggml_tensor * ba = build_lora_mm(model.layers[il].ssm_beta_alpha_cat, cur);
|
||||
cb(ba, "beta_alpha", il);
|
||||
|
||||
beta = ggml_view_4d(ctx0, ba, 1, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ggml_element_size(ba), ba->nb[1], ba->nb[1] * n_seq_tokens, 0);
|
||||
alpha = ggml_view_3d(ctx0, ba, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ba->nb[1], ba->nb[1] * n_seq_tokens, ggml_row_size(ba->type, num_v_heads));
|
||||
} else {
|
||||
beta = build_lora_mm(model.layers[il].ssm_beta, cur, model.layers[il].ssm_beta_s);
|
||||
beta = ggml_reshape_4d(ctx0, beta, 1, num_v_heads, n_seq_tokens, n_seqs);
|
||||
|
||||
alpha = build_lora_mm(model.layers[il].ssm_alpha, cur, model.layers[il].ssm_alpha_s);
|
||||
alpha = ggml_reshape_3d(ctx0, alpha, num_v_heads, n_seq_tokens, n_seqs);
|
||||
}
|
||||
cb(beta, "beta", il);
|
||||
cb(alpha, "alpha", il);
|
||||
|
||||
beta = ggml_sigmoid(ctx0, beta);
|
||||
cb(beta, "beta_sigmoid", il);
|
||||
|
||||
ggml_tensor * alpha = build_lora_mm(model.layers[il].ssm_alpha, cur, model.layers[il].ssm_alpha_s);
|
||||
alpha = ggml_reshape_3d(ctx0, alpha, num_v_heads, n_seq_tokens, n_seqs);
|
||||
cb(alpha, "alpha", il);
|
||||
|
||||
ggml_tensor * alpha_biased = ggml_add(ctx0, alpha, model.layers[il].ssm_dt);
|
||||
ggml_tensor * alpha_softplus = ggml_softplus(ctx0, alpha_biased);
|
||||
cb(alpha_softplus, "a_softplus", il);
|
||||
@@ -1157,7 +1222,8 @@ ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear(
|
||||
|
||||
ggml_tensor * output = build_recurrent_attn(inp, ssm_states_all, q_conv, k_conv, v_conv, gate, beta, state, il);
|
||||
|
||||
ggml_tensor * z_2d = ggml_reshape_4d(ctx0, z, head_v_dim, num_v_heads, n_seq_tokens, n_seqs);
|
||||
ggml_tensor * z_2d = ggml_view_4d(ctx0, z, head_v_dim, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ggml_row_size(z->type, head_v_dim), z->nb[1], z->nb[1] * n_seq_tokens, 0);
|
||||
|
||||
// gated normalization, as self.norm(core_attn_out, z) in the reference
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
@@ -49,6 +49,8 @@ deepseek32 ,0 ,any ,NORM+MUL+ADD , 2
|
||||
deepseek32 ,0 ,any ,RMS_NORM+MUL , 9
|
||||
deepseek4 ,0 ,any ,MUL+ADD , 8
|
||||
deepseek4 ,0 ,any ,RMS_NORM+MUL , 20
|
||||
deepseek41 ,0 ,any ,MUL+ADD , 12
|
||||
deepseek41 ,0 ,any ,RMS_NORM+MUL , 29
|
||||
dots1 ,0 ,any ,ADD+ADD , 1
|
||||
dots1 ,0 ,any ,MUL+ADD , 2
|
||||
dots1 ,0 ,any ,RMS_NORM+MUL , 9
|
||||
|
||||
|
@@ -2927,430 +2927,552 @@ blk.60.attn_k_b.weight q4_0
|
||||
[MXFP4_MOE] mxfp4
|
||||
output.weight q8_0
|
||||
token_embd.weight q8_0
|
||||
blk.0.attn_k_b.weight q8_0
|
||||
blk.0.attn_kv_a_mqa.weight q8_0
|
||||
blk.0.attn_output.weight q8_0
|
||||
blk.0.attn_q_a.weight q8_0
|
||||
blk.0.attn_q_b.weight q8_0
|
||||
blk.0.attn_v_b.weight q8_0
|
||||
blk.0.ffn_down.weight q8_0
|
||||
blk.0.ffn_gate.weight q8_0
|
||||
blk.0.ffn_up.weight q8_0
|
||||
blk.1.attn_k_b.weight q8_0
|
||||
blk.1.attn_kv_a_mqa.weight q8_0
|
||||
blk.1.attn_output.weight q8_0
|
||||
blk.1.attn_q_a.weight q8_0
|
||||
blk.1.attn_q_b.weight q8_0
|
||||
blk.1.attn_v_b.weight q8_0
|
||||
blk.1.ffn_down.weight q8_0
|
||||
blk.1.ffn_gate.weight q8_0
|
||||
blk.1.ffn_up.weight q8_0
|
||||
blk.2.attn_k_b.weight q8_0
|
||||
blk.2.attn_kv_a_mqa.weight q8_0
|
||||
blk.2.attn_output.weight q8_0
|
||||
blk.2.attn_q_a.weight q8_0
|
||||
blk.2.attn_q_b.weight q8_0
|
||||
blk.2.attn_v_b.weight q8_0
|
||||
blk.2.ffn_down.weight q8_0
|
||||
blk.2.ffn_gate.weight q8_0
|
||||
blk.2.ffn_up.weight q8_0
|
||||
blk.3.attn_k_b.weight q8_0
|
||||
blk.3.attn_kv_a_mqa.weight q8_0
|
||||
blk.3.attn_output.weight q8_0
|
||||
blk.3.attn_q_a.weight q8_0
|
||||
blk.3.attn_q_b.weight q8_0
|
||||
blk.3.attn_v_b.weight q8_0
|
||||
blk.3.ffn_down_shexp.weight q8_0
|
||||
blk.3.ffn_gate_shexp.weight q8_0
|
||||
blk.3.ffn_up_shexp.weight q8_0
|
||||
blk.4.attn_k_b.weight q8_0
|
||||
blk.4.attn_kv_a_mqa.weight q8_0
|
||||
blk.4.attn_output.weight q8_0
|
||||
blk.4.attn_q_a.weight q8_0
|
||||
blk.4.attn_q_b.weight q8_0
|
||||
blk.4.attn_v_b.weight q8_0
|
||||
blk.4.ffn_down_shexp.weight q8_0
|
||||
blk.4.ffn_gate_shexp.weight q8_0
|
||||
blk.4.ffn_up_shexp.weight q8_0
|
||||
blk.5.attn_k_b.weight q8_0
|
||||
blk.5.attn_kv_a_mqa.weight q8_0
|
||||
blk.5.attn_output.weight q8_0
|
||||
blk.5.attn_q_a.weight q8_0
|
||||
blk.5.attn_q_b.weight q8_0
|
||||
blk.5.attn_v_b.weight q8_0
|
||||
blk.5.ffn_down_shexp.weight q8_0
|
||||
blk.5.ffn_gate_shexp.weight q8_0
|
||||
blk.5.ffn_up_shexp.weight q8_0
|
||||
blk.6.attn_k_b.weight q8_0
|
||||
blk.6.attn_kv_a_mqa.weight q8_0
|
||||
blk.6.attn_output.weight q8_0
|
||||
blk.6.attn_q_a.weight q8_0
|
||||
blk.6.attn_q_b.weight q8_0
|
||||
blk.6.attn_v_b.weight q8_0
|
||||
blk.6.ffn_down_shexp.weight q8_0
|
||||
blk.6.ffn_gate_shexp.weight q8_0
|
||||
blk.6.ffn_up_shexp.weight q8_0
|
||||
blk.7.attn_k_b.weight q8_0
|
||||
blk.7.attn_kv_a_mqa.weight q8_0
|
||||
blk.7.attn_output.weight q8_0
|
||||
blk.7.attn_q_a.weight q8_0
|
||||
blk.7.attn_q_b.weight q8_0
|
||||
blk.7.attn_v_b.weight q8_0
|
||||
blk.7.ffn_down_shexp.weight q8_0
|
||||
blk.7.ffn_gate_shexp.weight q8_0
|
||||
blk.7.ffn_up_shexp.weight q8_0
|
||||
blk.8.attn_k_b.weight q8_0
|
||||
blk.8.attn_kv_a_mqa.weight q8_0
|
||||
blk.8.attn_output.weight q8_0
|
||||
blk.8.attn_q_a.weight q8_0
|
||||
blk.8.attn_q_b.weight q8_0
|
||||
blk.8.attn_v_b.weight q8_0
|
||||
blk.8.ffn_down_shexp.weight q8_0
|
||||
blk.8.ffn_gate_shexp.weight q8_0
|
||||
blk.8.ffn_up_shexp.weight q8_0
|
||||
blk.9.attn_k_b.weight q8_0
|
||||
blk.9.attn_kv_a_mqa.weight q8_0
|
||||
blk.9.attn_output.weight q8_0
|
||||
blk.9.attn_q_a.weight q8_0
|
||||
blk.9.attn_q_b.weight q8_0
|
||||
blk.9.attn_v_b.weight q8_0
|
||||
blk.9.ffn_down_shexp.weight q8_0
|
||||
blk.9.ffn_gate_shexp.weight q8_0
|
||||
blk.9.ffn_up_shexp.weight q8_0
|
||||
blk.10.attn_k_b.weight q8_0
|
||||
blk.10.attn_kv_a_mqa.weight q8_0
|
||||
blk.10.attn_output.weight q8_0
|
||||
blk.10.attn_q_a.weight q8_0
|
||||
blk.10.attn_q_b.weight q8_0
|
||||
blk.10.attn_v_b.weight q8_0
|
||||
blk.10.ffn_down_shexp.weight q8_0
|
||||
blk.10.ffn_gate_shexp.weight q8_0
|
||||
blk.10.ffn_up_shexp.weight q8_0
|
||||
blk.11.attn_k_b.weight q8_0
|
||||
blk.11.attn_kv_a_mqa.weight q8_0
|
||||
blk.11.attn_output.weight q8_0
|
||||
blk.11.attn_q_a.weight q8_0
|
||||
blk.11.attn_q_b.weight q8_0
|
||||
blk.11.attn_v_b.weight q8_0
|
||||
blk.11.ffn_down_shexp.weight q8_0
|
||||
blk.11.ffn_gate_shexp.weight q8_0
|
||||
blk.11.ffn_up_shexp.weight q8_0
|
||||
blk.12.attn_k_b.weight q8_0
|
||||
blk.12.attn_kv_a_mqa.weight q8_0
|
||||
blk.12.attn_output.weight q8_0
|
||||
blk.12.attn_q_a.weight q8_0
|
||||
blk.12.attn_q_b.weight q8_0
|
||||
blk.12.attn_v_b.weight q8_0
|
||||
blk.12.ffn_down_shexp.weight q8_0
|
||||
blk.12.ffn_gate_shexp.weight q8_0
|
||||
blk.12.ffn_up_shexp.weight q8_0
|
||||
blk.13.attn_k_b.weight q8_0
|
||||
blk.13.attn_kv_a_mqa.weight q8_0
|
||||
blk.13.attn_output.weight q8_0
|
||||
blk.13.attn_q_a.weight q8_0
|
||||
blk.13.attn_q_b.weight q8_0
|
||||
blk.13.attn_v_b.weight q8_0
|
||||
blk.13.ffn_down_shexp.weight q8_0
|
||||
blk.13.ffn_gate_shexp.weight q8_0
|
||||
blk.13.ffn_up_shexp.weight q8_0
|
||||
blk.14.attn_k_b.weight q8_0
|
||||
blk.14.attn_kv_a_mqa.weight q8_0
|
||||
blk.14.attn_output.weight q8_0
|
||||
blk.14.attn_q_a.weight q8_0
|
||||
blk.14.attn_q_b.weight q8_0
|
||||
blk.14.attn_v_b.weight q8_0
|
||||
blk.14.ffn_down_shexp.weight q8_0
|
||||
blk.14.ffn_gate_shexp.weight q8_0
|
||||
blk.14.ffn_up_shexp.weight q8_0
|
||||
blk.15.attn_k_b.weight q8_0
|
||||
blk.15.attn_kv_a_mqa.weight q8_0
|
||||
blk.15.attn_output.weight q8_0
|
||||
blk.15.attn_q_a.weight q8_0
|
||||
blk.15.attn_q_b.weight q8_0
|
||||
blk.15.attn_v_b.weight q8_0
|
||||
blk.15.ffn_down_shexp.weight q8_0
|
||||
blk.15.ffn_gate_shexp.weight q8_0
|
||||
blk.15.ffn_up_shexp.weight q8_0
|
||||
blk.16.attn_k_b.weight q8_0
|
||||
blk.16.attn_kv_a_mqa.weight q8_0
|
||||
blk.16.attn_output.weight q8_0
|
||||
blk.16.attn_q_a.weight q8_0
|
||||
blk.16.attn_q_b.weight q8_0
|
||||
blk.16.attn_v_b.weight q8_0
|
||||
blk.16.ffn_down_shexp.weight q8_0
|
||||
blk.16.ffn_gate_shexp.weight q8_0
|
||||
blk.16.ffn_up_shexp.weight q8_0
|
||||
blk.17.attn_k_b.weight q8_0
|
||||
blk.17.attn_kv_a_mqa.weight q8_0
|
||||
blk.17.attn_output.weight q8_0
|
||||
blk.17.attn_q_a.weight q8_0
|
||||
blk.17.attn_q_b.weight q8_0
|
||||
blk.17.attn_v_b.weight q8_0
|
||||
blk.17.ffn_down_shexp.weight q8_0
|
||||
blk.17.ffn_gate_shexp.weight q8_0
|
||||
blk.17.ffn_up_shexp.weight q8_0
|
||||
blk.18.attn_k_b.weight q8_0
|
||||
blk.18.attn_kv_a_mqa.weight q8_0
|
||||
blk.18.attn_output.weight q8_0
|
||||
blk.18.attn_q_a.weight q8_0
|
||||
blk.18.attn_q_b.weight q8_0
|
||||
blk.18.attn_v_b.weight q8_0
|
||||
blk.18.ffn_down_shexp.weight q8_0
|
||||
blk.18.ffn_gate_shexp.weight q8_0
|
||||
blk.18.ffn_up_shexp.weight q8_0
|
||||
blk.19.attn_k_b.weight q8_0
|
||||
blk.19.attn_kv_a_mqa.weight q8_0
|
||||
blk.19.attn_output.weight q8_0
|
||||
blk.19.attn_q_a.weight q8_0
|
||||
blk.19.attn_q_b.weight q8_0
|
||||
blk.19.attn_v_b.weight q8_0
|
||||
blk.19.ffn_down_shexp.weight q8_0
|
||||
blk.19.ffn_gate_shexp.weight q8_0
|
||||
blk.19.ffn_up_shexp.weight q8_0
|
||||
blk.20.attn_k_b.weight q8_0
|
||||
blk.20.attn_kv_a_mqa.weight q8_0
|
||||
blk.20.attn_output.weight q8_0
|
||||
blk.20.attn_q_a.weight q8_0
|
||||
blk.20.attn_q_b.weight q8_0
|
||||
blk.20.attn_v_b.weight q8_0
|
||||
blk.20.ffn_down_shexp.weight q8_0
|
||||
blk.20.ffn_gate_shexp.weight q8_0
|
||||
blk.20.ffn_up_shexp.weight q8_0
|
||||
blk.21.attn_k_b.weight q8_0
|
||||
blk.21.attn_kv_a_mqa.weight q8_0
|
||||
blk.21.attn_output.weight q8_0
|
||||
blk.21.attn_q_a.weight q8_0
|
||||
blk.21.attn_q_b.weight q8_0
|
||||
blk.21.attn_v_b.weight q8_0
|
||||
blk.21.ffn_down_shexp.weight q8_0
|
||||
blk.21.ffn_gate_shexp.weight q8_0
|
||||
blk.21.ffn_up_shexp.weight q8_0
|
||||
blk.22.attn_k_b.weight q8_0
|
||||
blk.22.attn_kv_a_mqa.weight q8_0
|
||||
blk.22.attn_output.weight q8_0
|
||||
blk.22.attn_q_a.weight q8_0
|
||||
blk.22.attn_q_b.weight q8_0
|
||||
blk.22.attn_v_b.weight q8_0
|
||||
blk.22.ffn_down_shexp.weight q8_0
|
||||
blk.22.ffn_gate_shexp.weight q8_0
|
||||
blk.22.ffn_up_shexp.weight q8_0
|
||||
blk.23.attn_k_b.weight q8_0
|
||||
blk.23.attn_kv_a_mqa.weight q8_0
|
||||
blk.23.attn_output.weight q8_0
|
||||
blk.23.attn_q_a.weight q8_0
|
||||
blk.23.attn_q_b.weight q8_0
|
||||
blk.23.attn_v_b.weight q8_0
|
||||
blk.23.ffn_down_shexp.weight q8_0
|
||||
blk.23.ffn_gate_shexp.weight q8_0
|
||||
blk.23.ffn_up_shexp.weight q8_0
|
||||
blk.24.attn_k_b.weight q8_0
|
||||
blk.24.attn_kv_a_mqa.weight q8_0
|
||||
blk.24.attn_output.weight q8_0
|
||||
blk.24.attn_q_a.weight q8_0
|
||||
blk.24.attn_q_b.weight q8_0
|
||||
blk.24.attn_v_b.weight q8_0
|
||||
blk.24.ffn_down_shexp.weight q8_0
|
||||
blk.24.ffn_gate_shexp.weight q8_0
|
||||
blk.24.ffn_up_shexp.weight q8_0
|
||||
blk.25.attn_k_b.weight q8_0
|
||||
blk.25.attn_kv_a_mqa.weight q8_0
|
||||
blk.25.attn_output.weight q8_0
|
||||
blk.25.attn_q_a.weight q8_0
|
||||
blk.25.attn_q_b.weight q8_0
|
||||
blk.25.attn_v_b.weight q8_0
|
||||
blk.25.ffn_down_shexp.weight q8_0
|
||||
blk.25.ffn_gate_shexp.weight q8_0
|
||||
blk.25.ffn_up_shexp.weight q8_0
|
||||
blk.26.attn_k_b.weight q8_0
|
||||
blk.26.attn_kv_a_mqa.weight q8_0
|
||||
blk.26.attn_output.weight q8_0
|
||||
blk.26.attn_q_a.weight q8_0
|
||||
blk.26.attn_q_b.weight q8_0
|
||||
blk.26.attn_v_b.weight q8_0
|
||||
blk.26.ffn_down_shexp.weight q8_0
|
||||
blk.26.ffn_gate_shexp.weight q8_0
|
||||
blk.26.ffn_up_shexp.weight q8_0
|
||||
blk.27.attn_k_b.weight q8_0
|
||||
blk.27.attn_kv_a_mqa.weight q8_0
|
||||
blk.27.attn_output.weight q8_0
|
||||
blk.27.attn_q_a.weight q8_0
|
||||
blk.27.attn_q_b.weight q8_0
|
||||
blk.27.attn_v_b.weight q8_0
|
||||
blk.27.ffn_down_shexp.weight q8_0
|
||||
blk.27.ffn_gate_shexp.weight q8_0
|
||||
blk.27.ffn_up_shexp.weight q8_0
|
||||
blk.28.attn_k_b.weight q8_0
|
||||
blk.28.attn_kv_a_mqa.weight q8_0
|
||||
blk.28.attn_output.weight q8_0
|
||||
blk.28.attn_q_a.weight q8_0
|
||||
blk.28.attn_q_b.weight q8_0
|
||||
blk.28.attn_v_b.weight q8_0
|
||||
blk.28.ffn_down_shexp.weight q8_0
|
||||
blk.28.ffn_gate_shexp.weight q8_0
|
||||
blk.28.ffn_up_shexp.weight q8_0
|
||||
blk.29.attn_k_b.weight q8_0
|
||||
blk.29.attn_kv_a_mqa.weight q8_0
|
||||
blk.29.attn_output.weight q8_0
|
||||
blk.29.attn_q_a.weight q8_0
|
||||
blk.29.attn_q_b.weight q8_0
|
||||
blk.29.attn_v_b.weight q8_0
|
||||
blk.29.ffn_down_shexp.weight q8_0
|
||||
blk.29.ffn_gate_shexp.weight q8_0
|
||||
blk.29.ffn_up_shexp.weight q8_0
|
||||
blk.30.attn_k_b.weight q8_0
|
||||
blk.30.attn_kv_a_mqa.weight q8_0
|
||||
blk.30.attn_output.weight q8_0
|
||||
blk.30.attn_q_a.weight q8_0
|
||||
blk.30.attn_q_b.weight q8_0
|
||||
blk.30.attn_v_b.weight q8_0
|
||||
blk.30.ffn_down_shexp.weight q8_0
|
||||
blk.30.ffn_gate_shexp.weight q8_0
|
||||
blk.30.ffn_up_shexp.weight q8_0
|
||||
blk.31.attn_k_b.weight q8_0
|
||||
blk.31.attn_kv_a_mqa.weight q8_0
|
||||
blk.31.attn_output.weight q8_0
|
||||
blk.31.attn_q_a.weight q8_0
|
||||
blk.31.attn_q_b.weight q8_0
|
||||
blk.31.attn_v_b.weight q8_0
|
||||
blk.31.ffn_down_shexp.weight q8_0
|
||||
blk.31.ffn_gate_shexp.weight q8_0
|
||||
blk.31.ffn_up_shexp.weight q8_0
|
||||
blk.32.attn_k_b.weight q8_0
|
||||
blk.32.attn_kv_a_mqa.weight q8_0
|
||||
blk.32.attn_output.weight q8_0
|
||||
blk.32.attn_q_a.weight q8_0
|
||||
blk.32.attn_q_b.weight q8_0
|
||||
blk.32.attn_v_b.weight q8_0
|
||||
blk.32.ffn_down_shexp.weight q8_0
|
||||
blk.32.ffn_gate_shexp.weight q8_0
|
||||
blk.32.ffn_up_shexp.weight q8_0
|
||||
blk.33.attn_k_b.weight q8_0
|
||||
blk.33.attn_kv_a_mqa.weight q8_0
|
||||
blk.33.attn_output.weight q8_0
|
||||
blk.33.attn_q_a.weight q8_0
|
||||
blk.33.attn_q_b.weight q8_0
|
||||
blk.33.attn_v_b.weight q8_0
|
||||
blk.33.ffn_down_shexp.weight q8_0
|
||||
blk.33.ffn_gate_shexp.weight q8_0
|
||||
blk.33.ffn_up_shexp.weight q8_0
|
||||
blk.34.attn_k_b.weight q8_0
|
||||
blk.34.attn_kv_a_mqa.weight q8_0
|
||||
blk.34.attn_output.weight q8_0
|
||||
blk.34.attn_q_a.weight q8_0
|
||||
blk.34.attn_q_b.weight q8_0
|
||||
blk.34.attn_v_b.weight q8_0
|
||||
blk.34.ffn_down_shexp.weight q8_0
|
||||
blk.34.ffn_gate_shexp.weight q8_0
|
||||
blk.34.ffn_up_shexp.weight q8_0
|
||||
blk.35.attn_k_b.weight q8_0
|
||||
blk.35.attn_kv_a_mqa.weight q8_0
|
||||
blk.35.attn_output.weight q8_0
|
||||
blk.35.attn_q_a.weight q8_0
|
||||
blk.35.attn_q_b.weight q8_0
|
||||
blk.35.attn_v_b.weight q8_0
|
||||
blk.35.ffn_down_shexp.weight q8_0
|
||||
blk.35.ffn_gate_shexp.weight q8_0
|
||||
blk.35.ffn_up_shexp.weight q8_0
|
||||
blk.36.attn_k_b.weight q8_0
|
||||
blk.36.attn_kv_a_mqa.weight q8_0
|
||||
blk.36.attn_output.weight q8_0
|
||||
blk.36.attn_q_a.weight q8_0
|
||||
blk.36.attn_q_b.weight q8_0
|
||||
blk.36.attn_v_b.weight q8_0
|
||||
blk.36.ffn_down_shexp.weight q8_0
|
||||
blk.36.ffn_gate_shexp.weight q8_0
|
||||
blk.36.ffn_up_shexp.weight q8_0
|
||||
blk.37.attn_k_b.weight q8_0
|
||||
blk.37.attn_kv_a_mqa.weight q8_0
|
||||
blk.37.attn_output.weight q8_0
|
||||
blk.37.attn_q_a.weight q8_0
|
||||
blk.37.attn_q_b.weight q8_0
|
||||
blk.37.attn_v_b.weight q8_0
|
||||
blk.37.ffn_down_shexp.weight q8_0
|
||||
blk.37.ffn_gate_shexp.weight q8_0
|
||||
blk.37.ffn_up_shexp.weight q8_0
|
||||
blk.38.attn_k_b.weight q8_0
|
||||
blk.38.attn_kv_a_mqa.weight q8_0
|
||||
blk.38.attn_output.weight q8_0
|
||||
blk.38.attn_q_a.weight q8_0
|
||||
blk.38.attn_q_b.weight q8_0
|
||||
blk.38.attn_v_b.weight q8_0
|
||||
blk.38.ffn_down_shexp.weight q8_0
|
||||
blk.38.ffn_gate_shexp.weight q8_0
|
||||
blk.38.ffn_up_shexp.weight q8_0
|
||||
blk.39.attn_k_b.weight q8_0
|
||||
blk.39.attn_kv_a_mqa.weight q8_0
|
||||
blk.39.attn_output.weight q8_0
|
||||
blk.39.attn_q_a.weight q8_0
|
||||
blk.39.attn_q_b.weight q8_0
|
||||
blk.39.attn_v_b.weight q8_0
|
||||
blk.39.ffn_down_shexp.weight q8_0
|
||||
blk.39.ffn_gate_shexp.weight q8_0
|
||||
blk.39.ffn_up_shexp.weight q8_0
|
||||
blk.40.attn_k_b.weight q8_0
|
||||
blk.40.attn_kv_a_mqa.weight q8_0
|
||||
blk.40.attn_output.weight q8_0
|
||||
blk.40.attn_q_a.weight q8_0
|
||||
blk.40.attn_q_b.weight q8_0
|
||||
blk.40.attn_v_b.weight q8_0
|
||||
blk.40.ffn_down_shexp.weight q8_0
|
||||
blk.40.ffn_gate_shexp.weight q8_0
|
||||
blk.40.ffn_up_shexp.weight q8_0
|
||||
blk.41.attn_k_b.weight q8_0
|
||||
blk.41.attn_kv_a_mqa.weight q8_0
|
||||
blk.41.attn_output.weight q8_0
|
||||
blk.41.attn_q_a.weight q8_0
|
||||
blk.41.attn_q_b.weight q8_0
|
||||
blk.41.attn_v_b.weight q8_0
|
||||
blk.41.ffn_down_shexp.weight q8_0
|
||||
blk.41.ffn_gate_shexp.weight q8_0
|
||||
blk.41.ffn_up_shexp.weight q8_0
|
||||
blk.42.attn_k_b.weight q8_0
|
||||
blk.42.attn_kv_a_mqa.weight q8_0
|
||||
blk.42.attn_output.weight q8_0
|
||||
blk.42.attn_q_a.weight q8_0
|
||||
blk.42.attn_q_b.weight q8_0
|
||||
blk.42.attn_v_b.weight q8_0
|
||||
blk.42.ffn_down_shexp.weight q8_0
|
||||
blk.42.ffn_gate_shexp.weight q8_0
|
||||
blk.42.ffn_up_shexp.weight q8_0
|
||||
blk.43.attn_k_b.weight q8_0
|
||||
blk.43.attn_kv_a_mqa.weight q8_0
|
||||
blk.43.attn_output.weight q8_0
|
||||
blk.43.attn_q_a.weight q8_0
|
||||
blk.43.attn_q_b.weight q8_0
|
||||
blk.43.attn_v_b.weight q8_0
|
||||
blk.43.ffn_down_shexp.weight q8_0
|
||||
blk.43.ffn_gate_shexp.weight q8_0
|
||||
blk.43.ffn_up_shexp.weight q8_0
|
||||
blk.44.attn_k_b.weight q8_0
|
||||
blk.44.attn_kv_a_mqa.weight q8_0
|
||||
blk.44.attn_output.weight q8_0
|
||||
blk.44.attn_q_a.weight q8_0
|
||||
blk.44.attn_q_b.weight q8_0
|
||||
blk.44.attn_v_b.weight q8_0
|
||||
blk.44.ffn_down_shexp.weight q8_0
|
||||
blk.44.ffn_gate_shexp.weight q8_0
|
||||
blk.44.ffn_up_shexp.weight q8_0
|
||||
blk.45.attn_k_b.weight q8_0
|
||||
blk.45.attn_kv_a_mqa.weight q8_0
|
||||
blk.45.attn_output.weight q8_0
|
||||
blk.45.attn_q_a.weight q8_0
|
||||
blk.45.attn_q_b.weight q8_0
|
||||
blk.45.attn_v_b.weight q8_0
|
||||
blk.45.ffn_down_shexp.weight q8_0
|
||||
blk.45.ffn_gate_shexp.weight q8_0
|
||||
blk.45.ffn_up_shexp.weight q8_0
|
||||
blk.46.attn_k_b.weight q8_0
|
||||
blk.46.attn_kv_a_mqa.weight q8_0
|
||||
blk.46.attn_output.weight q8_0
|
||||
blk.46.attn_q_a.weight q8_0
|
||||
blk.46.attn_q_b.weight q8_0
|
||||
blk.46.attn_v_b.weight q8_0
|
||||
blk.46.ffn_down_shexp.weight q8_0
|
||||
blk.46.ffn_gate_shexp.weight q8_0
|
||||
blk.46.ffn_up_shexp.weight q8_0
|
||||
blk.47.attn_k_b.weight q8_0
|
||||
blk.47.attn_kv_a_mqa.weight q8_0
|
||||
blk.47.attn_output.weight q8_0
|
||||
blk.47.attn_q_a.weight q8_0
|
||||
blk.47.attn_q_b.weight q8_0
|
||||
blk.47.attn_v_b.weight q8_0
|
||||
blk.47.ffn_down_shexp.weight q8_0
|
||||
blk.47.ffn_gate_shexp.weight q8_0
|
||||
blk.47.ffn_up_shexp.weight q8_0
|
||||
blk.48.attn_k_b.weight q8_0
|
||||
blk.48.attn_kv_a_mqa.weight q8_0
|
||||
blk.48.attn_output.weight q8_0
|
||||
blk.48.attn_q_a.weight q8_0
|
||||
blk.48.attn_q_b.weight q8_0
|
||||
blk.48.attn_v_b.weight q8_0
|
||||
blk.48.ffn_down_shexp.weight q8_0
|
||||
blk.48.ffn_gate_shexp.weight q8_0
|
||||
blk.48.ffn_up_shexp.weight q8_0
|
||||
blk.49.attn_k_b.weight q8_0
|
||||
blk.49.attn_kv_a_mqa.weight q8_0
|
||||
blk.49.attn_output.weight q8_0
|
||||
blk.49.attn_q_a.weight q8_0
|
||||
blk.49.attn_q_b.weight q8_0
|
||||
blk.49.attn_v_b.weight q8_0
|
||||
blk.49.ffn_down_shexp.weight q8_0
|
||||
blk.49.ffn_gate_shexp.weight q8_0
|
||||
blk.49.ffn_up_shexp.weight q8_0
|
||||
blk.50.attn_k_b.weight q8_0
|
||||
blk.50.attn_kv_a_mqa.weight q8_0
|
||||
blk.50.attn_output.weight q8_0
|
||||
blk.50.attn_q_a.weight q8_0
|
||||
blk.50.attn_q_b.weight q8_0
|
||||
blk.50.attn_v_b.weight q8_0
|
||||
blk.50.ffn_down_shexp.weight q8_0
|
||||
blk.50.ffn_gate_shexp.weight q8_0
|
||||
blk.50.ffn_up_shexp.weight q8_0
|
||||
blk.51.attn_k_b.weight q8_0
|
||||
blk.51.attn_kv_a_mqa.weight q8_0
|
||||
blk.51.attn_output.weight q8_0
|
||||
blk.51.attn_q_a.weight q8_0
|
||||
blk.51.attn_q_b.weight q8_0
|
||||
blk.51.attn_v_b.weight q8_0
|
||||
blk.51.ffn_down_shexp.weight q8_0
|
||||
blk.51.ffn_gate_shexp.weight q8_0
|
||||
blk.51.ffn_up_shexp.weight q8_0
|
||||
blk.52.attn_k_b.weight q8_0
|
||||
blk.52.attn_kv_a_mqa.weight q8_0
|
||||
blk.52.attn_output.weight q8_0
|
||||
blk.52.attn_q_a.weight q8_0
|
||||
blk.52.attn_q_b.weight q8_0
|
||||
blk.52.attn_v_b.weight q8_0
|
||||
blk.52.ffn_down_shexp.weight q8_0
|
||||
blk.52.ffn_gate_shexp.weight q8_0
|
||||
blk.52.ffn_up_shexp.weight q8_0
|
||||
blk.53.attn_k_b.weight q8_0
|
||||
blk.53.attn_kv_a_mqa.weight q8_0
|
||||
blk.53.attn_output.weight q8_0
|
||||
blk.53.attn_q_a.weight q8_0
|
||||
blk.53.attn_q_b.weight q8_0
|
||||
blk.53.attn_v_b.weight q8_0
|
||||
blk.53.ffn_down_shexp.weight q8_0
|
||||
blk.53.ffn_gate_shexp.weight q8_0
|
||||
blk.53.ffn_up_shexp.weight q8_0
|
||||
blk.54.attn_k_b.weight q8_0
|
||||
blk.54.attn_kv_a_mqa.weight q8_0
|
||||
blk.54.attn_output.weight q8_0
|
||||
blk.54.attn_q_a.weight q8_0
|
||||
blk.54.attn_q_b.weight q8_0
|
||||
blk.54.attn_v_b.weight q8_0
|
||||
blk.54.ffn_down_shexp.weight q8_0
|
||||
blk.54.ffn_gate_shexp.weight q8_0
|
||||
blk.54.ffn_up_shexp.weight q8_0
|
||||
blk.55.attn_k_b.weight q8_0
|
||||
blk.55.attn_kv_a_mqa.weight q8_0
|
||||
blk.55.attn_output.weight q8_0
|
||||
blk.55.attn_q_a.weight q8_0
|
||||
blk.55.attn_q_b.weight q8_0
|
||||
blk.55.attn_v_b.weight q8_0
|
||||
blk.55.ffn_down_shexp.weight q8_0
|
||||
blk.55.ffn_gate_shexp.weight q8_0
|
||||
blk.55.ffn_up_shexp.weight q8_0
|
||||
blk.56.attn_k_b.weight q8_0
|
||||
blk.56.attn_kv_a_mqa.weight q8_0
|
||||
blk.56.attn_output.weight q8_0
|
||||
blk.56.attn_q_a.weight q8_0
|
||||
blk.56.attn_q_b.weight q8_0
|
||||
blk.56.attn_v_b.weight q8_0
|
||||
blk.56.ffn_down_shexp.weight q8_0
|
||||
blk.56.ffn_gate_shexp.weight q8_0
|
||||
blk.56.ffn_up_shexp.weight q8_0
|
||||
blk.57.attn_k_b.weight q8_0
|
||||
blk.57.attn_kv_a_mqa.weight q8_0
|
||||
blk.57.attn_output.weight q8_0
|
||||
blk.57.attn_q_a.weight q8_0
|
||||
blk.57.attn_q_b.weight q8_0
|
||||
blk.57.attn_v_b.weight q8_0
|
||||
blk.57.ffn_down_shexp.weight q8_0
|
||||
blk.57.ffn_gate_shexp.weight q8_0
|
||||
blk.57.ffn_up_shexp.weight q8_0
|
||||
blk.58.attn_k_b.weight q8_0
|
||||
blk.58.attn_kv_a_mqa.weight q8_0
|
||||
blk.58.attn_output.weight q8_0
|
||||
blk.58.attn_q_a.weight q8_0
|
||||
blk.58.attn_q_b.weight q8_0
|
||||
blk.58.attn_v_b.weight q8_0
|
||||
blk.58.ffn_down_shexp.weight q8_0
|
||||
blk.58.ffn_gate_shexp.weight q8_0
|
||||
blk.58.ffn_up_shexp.weight q8_0
|
||||
blk.59.attn_k_b.weight q8_0
|
||||
blk.59.attn_kv_a_mqa.weight q8_0
|
||||
blk.59.attn_output.weight q8_0
|
||||
blk.59.attn_q_a.weight q8_0
|
||||
blk.59.attn_q_b.weight q8_0
|
||||
blk.59.attn_v_b.weight q8_0
|
||||
blk.59.ffn_down_shexp.weight q8_0
|
||||
blk.59.ffn_gate_shexp.weight q8_0
|
||||
blk.59.ffn_up_shexp.weight q8_0
|
||||
blk.60.attn_k_b.weight q8_0
|
||||
blk.60.attn_kv_a_mqa.weight q8_0
|
||||
blk.60.attn_output.weight q8_0
|
||||
blk.60.attn_q_a.weight q8_0
|
||||
blk.60.attn_q_b.weight q8_0
|
||||
blk.60.attn_v_b.weight q8_0
|
||||
blk.60.ffn_down_shexp.weight q8_0
|
||||
blk.60.ffn_gate_shexp.weight q8_0
|
||||
blk.60.ffn_up_shexp.weight q8_0
|
||||
|
||||
@@ -2798,11 +2798,11 @@ struct test_rope_set_rows : public test_case {
|
||||
const ggml_type type_idx;
|
||||
const std::array<int64_t, 4> ne_a;
|
||||
int mode;
|
||||
const int n_dims;
|
||||
const int n_ctx{512};
|
||||
const int n_dims{128};
|
||||
|
||||
std::string vars() override {
|
||||
return VARS_TO_STR4(type, type_idx, ne_a, mode);
|
||||
return VARS_TO_STR5(type, type_idx, ne_a, mode, n_dims);
|
||||
}
|
||||
|
||||
std::string op_desc(ggml_tensor * t) override {
|
||||
@@ -2815,8 +2815,9 @@ struct test_rope_set_rows : public test_case {
|
||||
test_rope_set_rows(ggml_type type,
|
||||
ggml_type type_idx,
|
||||
std::array<int64_t, 4> ne_a,
|
||||
int mode)
|
||||
: type(type), type_idx(type_idx), ne_a(ne_a), mode(mode) {}
|
||||
int mode,
|
||||
int n_dims = 128)
|
||||
: type(type), type_idx(type_idx), ne_a(ne_a), mode(mode), n_dims(n_dims) {}
|
||||
|
||||
ggml_tensor * build_graph(ggml_context * ctx) override {
|
||||
ggml_tensor * a = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, ne_a[0], ne_a[1], ne_a[2], 1);
|
||||
@@ -2850,7 +2851,7 @@ struct test_rope_set_rows : public test_case {
|
||||
rope = ggml_rope_multi(ctx, a, pos, freq, n_dims, rope_sections, mode, 0, 10000.0f, fs, ef, af, 1.0f, 1.0f);
|
||||
}
|
||||
} else {
|
||||
rope = ggml_rope(ctx, a, pos, ne_a[0], mode);
|
||||
rope = ggml_rope(ctx, a, pos, n_dims, mode);
|
||||
}
|
||||
|
||||
ggml_tensor * view = ggml_view_2d(ctx, rope, ne_a[0] * ne_a[1], ne_a[2], rope->nb[2], 0);
|
||||
@@ -10233,6 +10234,12 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
}
|
||||
}
|
||||
test_cases.emplace_back(new test_rope_set_rows(GGML_TYPE_F32, GGML_TYPE_I32, { 128, 32, 8, 1 }, GGML_ROPE_TYPE_IMROPE));
|
||||
// partial rotation
|
||||
for (int mode : { GGML_ROPE_TYPE_NORMAL, GGML_ROPE_TYPE_NEOX, GGML_ROPE_TYPE_MROPE, GGML_ROPE_TYPE_IMROPE }) {
|
||||
for (ggml_type type : {GGML_TYPE_F16, GGML_TYPE_F32}) {
|
||||
test_cases.emplace_back(new test_rope_set_rows(type, GGML_TYPE_I64, { 256, 4, 8, 1 }, mode, 64));
|
||||
}
|
||||
}
|
||||
|
||||
for (ggml_type type_input : {GGML_TYPE_F32}) {
|
||||
for (ggml_op_pool pool_type : {GGML_OP_POOL_AVG, GGML_OP_POOL_MAX}) {
|
||||
@@ -10283,6 +10290,8 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
|
||||
// im2col 1D
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_F32, {3000, 128, 1, 1}, {3, 128, 1280, 1}, 1, 0, 1, 0, 1, 0, false));
|
||||
// deepseek4v / deepseek41v aligner: 3x3 stride-3 unfold (F.unfold == im2col)
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_F32, {48, 33, 1024, 1}, {3, 3, 1024, 1}, 3, 3, 0, 0, 1, 1, true));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_F32, {3000, 128, 1, 1}, {3, 128, 1280, 1}, 1, 0, 1, 0, 1, 0, false));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_F16, {3000, 128, 1, 1}, {3, 128, 1280, 1}, 1, 0, 1, 0, 1, 0, false));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_F16, {3000, 384, 1, 1}, {3, 384, 384, 1}, 1, 0, 1, 0, 1, 0, false));
|
||||
@@ -10491,6 +10500,8 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
};
|
||||
|
||||
for (ggml_type kernel_type : {GGML_TYPE_F32, GGML_TYPE_F16}) {
|
||||
test_cases.emplace_back(new test_conv_3d(1, 4, 6, 78, 54, 512, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1, kernel_type));
|
||||
test_cases.emplace_back(new test_conv_3d(2, 16, 4, 64, 64, 256, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, kernel_type));
|
||||
for (int N : {1, 2}) {
|
||||
for (int IC : {1, 3}) {
|
||||
for (int OC : {1, 4}) {
|
||||
@@ -12354,6 +12365,13 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
false, 16, 8, false, false, true, false, { 1, 1 }));
|
||||
}
|
||||
|
||||
// Fused gate/up + GLU on the MMQ path, only used if its tiling is efficient: ~1280 tiles per case, one case per type and GLU op.
|
||||
test_cases.emplace_back(new test_mul_mat_vec_fusion(GGML_TYPE_Q4_K, GGML_GLU_OP_SWIGLU, 17, 81920, 256, false, 1, 1, false, false, true, false, { 1, 1 }));
|
||||
test_cases.emplace_back(new test_mul_mat_vec_fusion(GGML_TYPE_Q5_K, GGML_GLU_OP_GEGLU, 150, 40960, 256, false, 1, 1, false, false, true, false, { 1, 1 }));
|
||||
test_cases.emplace_back(new test_mul_mat_vec_fusion(GGML_TYPE_Q6_K, GGML_GLU_OP_SWIGLU_OAI, 150, 40960, 256, false, 1, 1, false, false, true, false, { 1, 1 }));
|
||||
test_cases.emplace_back(new test_mul_mat_vec_fusion(GGML_TYPE_Q8_0, GGML_GLU_OP_SWIGLU_CLAMP, 150, 6912, 256, false, 1, 1, false, false, true, false, { 2, 3 }));
|
||||
test_cases.emplace_back(new test_mul_mat_vec_fusion(GGML_TYPE_IQ4_XS, GGML_GLU_OP_SWIGLU, 150, 40960, 512, false, 1, 1, false, false, true, false, { 1, 1 }));
|
||||
|
||||
for (auto gate : {GATING_FUNC_SOFTMAX, GATING_FUNC_SIGMOID, GATING_FUNC_SOFTMAX_WEIGHT, GATING_FUNC_SQRT_SOFTPLUS}) {
|
||||
for (bool with_norm : {false, true}) {
|
||||
for (bool bias_probs : {false, true}) {
|
||||
@@ -12591,6 +12609,15 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_perf() {
|
||||
{1, 320, 8, 76, 52, 1280, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{1, 1280, 8, 76, 52, 1280, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{1, 320, 8, 152, 104, 1280, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
// Wan VAE pads spatial borders and temporal history before conv3d; operator padding is zero.
|
||||
{1, 384, 3, 40, 28, 384, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 192, 4, 78, 54, 192, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 96, 6, 154, 106, 96, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 96, 6, 306, 210, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 1024, 3, 40, 28, 1024, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 256, 6, 154, 106, 256, 3, 3, 3, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 384, 3, 38, 26, 768, 3, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
{1, 192, 2, 76, 52, 96, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1},
|
||||
#if 0
|
||||
// too slow on some devices
|
||||
{1, 1280, 8, 152, 104, 1280, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "../tools/server/server-chat.h"
|
||||
#include "chat-auto-parser.h"
|
||||
#include "chat.h"
|
||||
#include "json-schema-to-grammar.h"
|
||||
#include "json-schema.h"
|
||||
#include "common.h"
|
||||
#include "ggml.h"
|
||||
#include "log.h"
|
||||
@@ -149,6 +151,21 @@ static common_chat_templates_ptr read_templates(const std::string & path) {
|
||||
return common_chat_templates_ptr(common_chat_templates_init(/* model= */ nullptr, read_file(path)));
|
||||
}
|
||||
|
||||
// DeepSeek V4.1 stand-in: the real template ships in the GGUF; this keeps only the turn markers,
|
||||
// the generation prompt, the V4.1 DSML block tag used for detection and the extra keys it reads
|
||||
static const char * DEEPSEEK_V4_1_MIN_TEMPLATE = R"({%- for m in messages -%}
|
||||
{%- if m.role == 'system' -%}<|System|>{{ m.content }}
|
||||
{%- elif m.role == 'user' -%}<|User|>
|
||||
{%- if m.content is string -%}{{ m.content }}{%- else -%}{%- for b in m.content -%}{%- if not loop.first -%}{{ '\n\n' }}{%- endif -%}{{ b.text }}{%- endfor -%}{%- endif -%}
|
||||
{%- if m.task -%}<|{{ m.task }}|>{%- endif -%}
|
||||
{%- elif m.role == 'assistant' -%}<|Assistant|></think>{{ m.content }}
|
||||
{%- for tc in m.tool_calls or [] -%}<|DSML| calls><|DSML| invoke name="{% if tc.namespace %}{{ tc.namespace }}::{% endif %}{{ tc.function.name }}"></|DSML| invoke></|DSML| calls>{%- endfor -%}
|
||||
<|end▁of▁sentence|>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if reasoning_effort is defined and reasoning_effort is not none -%}<|effort={{ reasoning_effort }}|>{%- endif -%}
|
||||
{%- if add_generation_prompt -%}<|Assistant|>{%- if enable_thinking -%}<think>{%- else -%}</think>{%- endif -%}{%- endif -%})";
|
||||
|
||||
static std::unique_ptr<llama_grammar> build_grammar(const std::string & grammar_str) {
|
||||
return std::unique_ptr<llama_grammar>(
|
||||
llama_grammar_init_impl(nullptr, grammar_str.c_str(), "root", false, nullptr, 0, nullptr, 0));
|
||||
@@ -961,6 +978,7 @@ const common_chat_msg message_user{
|
||||
/* .reasoning_content = */ "",
|
||||
/* .tool_name = */ "",
|
||||
/* .tool_call_id = */ "",
|
||||
/* .extra_json = */ "",
|
||||
};
|
||||
|
||||
const common_chat_msg message_user_parts{
|
||||
@@ -977,6 +995,7 @@ const common_chat_msg message_user_parts{
|
||||
"",
|
||||
/* .tool_name = */ "",
|
||||
/* .tool_call_id = */ "",
|
||||
/* .extra_json = */ "",
|
||||
};
|
||||
|
||||
static common_chat_msg simple_assist_msg(const std::string & content,
|
||||
@@ -1486,6 +1505,11 @@ class peg_tester {
|
||||
template_path_(template_path),
|
||||
detailed_debug_(detailed_debug) {}
|
||||
|
||||
peg_tester(const std::string & name, common_chat_templates_ptr tmpls, const bool detailed_debug = false) :
|
||||
tmpls_(std::move(tmpls)),
|
||||
template_path_(name),
|
||||
detailed_debug_(detailed_debug) {}
|
||||
|
||||
const std::string & template_path() const { return template_path_; }
|
||||
|
||||
peg_test_builder test(const std::string & input);
|
||||
@@ -4381,6 +4405,103 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
|
||||
.run();
|
||||
}
|
||||
|
||||
// DeepSeek V4.1: V4 behaviour, DSML tag names with a leading space
|
||||
{
|
||||
auto tst = peg_tester("deepseek-v4.1-min",
|
||||
common_chat_templates_ptr(common_chat_templates_init(nullptr, DEEPSEEK_V4_1_MIN_TEMPLATE)),
|
||||
detailed_debug);
|
||||
|
||||
tst.test("Hello, world!\nWhat's up?")
|
||||
.enable_thinking(false)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.expect(message_assist)
|
||||
.run();
|
||||
|
||||
tst.test("I'm\nthinking</think>Hello, world!\nWhat's up?")
|
||||
.enable_thinking(true)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.expect(message_assist_thoughts)
|
||||
.run();
|
||||
|
||||
tst.test(
|
||||
"Let me check the time</think>\n\n"
|
||||
"<|DSML| calls>\n"
|
||||
"<|DSML| invoke name=\"get_time\">\n"
|
||||
"<|DSML| parameter name=\"city\" string=\"true\">Tokyo</|DSML| parameter>\n"
|
||||
"</|DSML| invoke>\n"
|
||||
"</|DSML| calls>")
|
||||
.enable_thinking(true)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.tools({ get_time_tool })
|
||||
.expect(message_with_tool_calls_and_reasoning("get_time", R"({"city": "Tokyo"})", "Let me check the time"))
|
||||
.run();
|
||||
|
||||
tst.test(
|
||||
"<|DSML| calls>\n"
|
||||
"<|DSML| invoke name=\"special_function\">\n"
|
||||
"<|DSML| parameter name=\"arg1\" string=\"false\">1</|DSML| parameter>\n"
|
||||
"</|DSML| invoke>\n"
|
||||
"</|DSML| calls>")
|
||||
.enable_thinking(false)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.tools({ special_function_tool })
|
||||
.expect(message_assist_call)
|
||||
.run();
|
||||
|
||||
tst.test(
|
||||
"Calling both</think>\n\n"
|
||||
"<|DSML| calls>\n"
|
||||
"<|DSML| invoke name=\"get_time\">\n"
|
||||
"<|DSML| parameter name=\"city\" string=\"true\">Paris</|DSML| parameter>\n"
|
||||
"</|DSML| invoke>\n"
|
||||
"<|DSML| invoke name=\"get_weather\">\n"
|
||||
"<|DSML| parameter name=\"city\" string=\"true\">Paris</|DSML| parameter>\n"
|
||||
"</|DSML| invoke>\n"
|
||||
"</|DSML| calls>")
|
||||
.enable_thinking(true)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.parallel_tool_calls(true)
|
||||
.tools({ get_time_tool, get_weather_tool })
|
||||
.expect(message_with_reasoning_content_and_multiple_tool_calls(
|
||||
"Calling both", "",
|
||||
{ { "get_time", R"({"city": "Paris"})" }, { "get_weather", R"({"city": "Paris"})" } }))
|
||||
.run();
|
||||
|
||||
tst.test("world!\nWhat's up?")
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.enable_thinking(true)
|
||||
.messages({ message_user, message_assist_prefill_content })
|
||||
.add_generation_prompt(false)
|
||||
.continue_final_message(COMMON_CHAT_CONTINUATION_CONTENT)
|
||||
.expect_reasoning("I'm thinking")
|
||||
.expect_content("Hello, world!\nWhat's up?")
|
||||
.run();
|
||||
|
||||
tst.test(" thinking</think>Hello, world!\nWhat's up?")
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.enable_thinking(true)
|
||||
.messages({ message_user, message_assist_prefill_reasoning })
|
||||
.add_generation_prompt(false)
|
||||
.continue_final_message(COMMON_CHAT_CONTINUATION_REASONING)
|
||||
.expect_reasoning("I'm thinking")
|
||||
.expect_content("Hello, world!\nWhat's up?")
|
||||
.run();
|
||||
|
||||
tst.test(
|
||||
"Let me check the time\n\n"
|
||||
"<|DSML| calls>\n"
|
||||
"<|DSML| invoke name=\"get_time\">\n"
|
||||
"<|DSML| parameter name=\"city\" string=\"true\">Tokyo</|DSML| parameter>\n"
|
||||
"</|DSML| invoke>\n"
|
||||
"</|DSML| calls>") // no </think> after the TC close because the grammar will immediately constrain it to end
|
||||
.enable_thinking(true)
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
|
||||
.tools({ get_time_tool })
|
||||
.expect_reasoning("Let me check the time")
|
||||
.expect_tool_calls({ { "get_time", R"({"city": "Tokyo"})", {} } })
|
||||
.run();
|
||||
}
|
||||
|
||||
{
|
||||
// The DSML separator belongs to the tool call block, not assistant content.
|
||||
auto tst = peg_tester("models/templates/deepseek-ai-DeepSeek-V4-Flash-0731.jinja", detailed_debug);
|
||||
@@ -7825,6 +7946,62 @@ static void test_deepseek_v4_tool_result_ordering() {
|
||||
}
|
||||
}
|
||||
|
||||
// DeepSeek V4.1: extra message keys, "\n\n" around media markers, reasoning_effort mapping, system-message tools
|
||||
static void test_deepseek_v4_1_message_fields() {
|
||||
LOG_DBG("%s\n", __func__);
|
||||
|
||||
common_chat_templates_ptr tmpls(common_chat_templates_init(nullptr, DEEPSEEK_V4_1_MIN_TEMPLATE));
|
||||
auto render = [&](const char * messages_json, bool add_generation_prompt) {
|
||||
common_chat_templates_inputs inputs;
|
||||
inputs.messages = common_chat_msgs_parse_oaicompat(json::parse(messages_json));
|
||||
inputs.add_generation_prompt = add_generation_prompt;
|
||||
inputs.enable_thinking = false;
|
||||
return common_chat_templates_apply(tmpls.get(), inputs).prompt.str();
|
||||
};
|
||||
|
||||
assert_equals(std::string("<|User|>q<|action|>"),
|
||||
render(R"([{"role": "user", "content": "q", "task": "action"}])", false));
|
||||
|
||||
assert_contains(render(R"([
|
||||
{"role": "user", "content": "q"},
|
||||
{"role": "assistant", "content": "", "tool_calls": [{"type": "function", "namespace": "ns", "function": {"name": "f", "arguments": {"x": 1}}}]}
|
||||
])", false),
|
||||
"<|DSML| invoke name=\"ns::f\">");
|
||||
|
||||
assert_equals(std::string("<|User|>a\n\n<__media__>\n\nb<|Assistant|></think>"),
|
||||
render(R"([{"role": "user", "content": [{"type": "text", "text": "a"}, {"type": "media_marker", "text": "<__media__>"}, {"type": "text", "text": "b"}]}])", true));
|
||||
|
||||
for (const auto & [effort, expected] : std::vector<std::pair<std::string, std::string>>{
|
||||
{ "minimal", "<|effort=low|>" }, { "low", "<|effort=low|>" }, { "medium", "" },
|
||||
{ "high", "<|effort=high|>" }, { "xhigh", "<|effort=max|>" }, { "max", "<|effort=max|>" } }) {
|
||||
common_chat_templates_inputs inputs;
|
||||
inputs.messages = { message_user };
|
||||
inputs.add_generation_prompt = true;
|
||||
inputs.chat_template_kwargs["reasoning_effort"] = json(effort).dump();
|
||||
assert_equals("<|User|>Hey there!" + expected + "<|Assistant|><think>",
|
||||
common_chat_templates_apply(tmpls.get(), inputs).prompt.str());
|
||||
}
|
||||
|
||||
{
|
||||
common_chat_templates_inputs inputs;
|
||||
inputs.messages = common_chat_msgs_parse_oaicompat(json::parse(R"([
|
||||
{"role": "system", "content": "s", "tools": [{"type": "function", "function": {"name": "get_time",
|
||||
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]},
|
||||
{"role": "user", "content": "q"}
|
||||
])"));
|
||||
inputs.add_generation_prompt = true;
|
||||
inputs.enable_thinking = false;
|
||||
inputs.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
|
||||
common_chat_session session(tmpls.get(), nullptr, inputs, {});
|
||||
const auto & msg = session.finish(common_chat_input(std::string(
|
||||
"\n\n<|DSML| calls>\n<|DSML| invoke name=\"get_time\">\n"
|
||||
"<|DSML| parameter name=\"city\" string=\"true\">Tokyo</|DSML| parameter>\n</|DSML| invoke>\n</|DSML| calls>")));
|
||||
assert_equals(size_t(1), msg.tool_calls.size());
|
||||
assert_equals(std::string("get_time"), msg.tool_calls[0].name);
|
||||
assert_equals(std::string("{\"city\":\"Tokyo\"}"), msg.tool_calls[0].arguments);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_prompt_parts() {
|
||||
LOG_DBG("%s\n", __func__);
|
||||
const std::string tmpl_src =
|
||||
@@ -7931,6 +8108,58 @@ static void test_chat_session() {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_chat_json_depth() {
|
||||
LOG_DBG("%s\n", __func__);
|
||||
const auto arrays = [](size_t n) {
|
||||
return std::string(n, '[') + std::string(n, ']');
|
||||
};
|
||||
// a schema of n nested array types
|
||||
const auto schema = [](size_t n) {
|
||||
std::string s;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
s += R"({"type": "array", "items": )";
|
||||
}
|
||||
return s + R"({"type": "string"})" + std::string(n, '}');
|
||||
};
|
||||
const auto rejected = [](const std::function<void()> & fn) {
|
||||
try {
|
||||
fn();
|
||||
} catch (const std::invalid_argument & e) {
|
||||
return std::string(e.what()).find("nested more than 128 levels deep") != std::string::npos;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// the schema builder stops at 128 levels, the root schema is one of them
|
||||
assert_equals(false, rejected([&] { common_chat_schema_from_json(json::parse(schema(127))); }));
|
||||
assert_equals(true, rejected([&] { common_chat_schema_from_json(json::parse(schema(128))); }));
|
||||
assert_equals(true, rejected([&] { json_schema_to_grammar(json::parse(schema(1000))); }));
|
||||
|
||||
// the jinja engine and the schema builder reject the inputs before they recurse too deep
|
||||
auto tmpls = read_templates("models/templates/Qwen3.5-4B.jinja");
|
||||
const size_t deep = 1000;
|
||||
|
||||
common_chat_templates_inputs tool_schema;
|
||||
tool_schema.messages = { message_user };
|
||||
tool_schema.tools = { { "f", "", R"({"type": "object", "properties": {"x": )" + schema(deep) + "}}" } };
|
||||
assert_equals(true, rejected([&] { common_chat_templates_apply(tmpls.get(), tool_schema); }));
|
||||
|
||||
common_chat_templates_inputs tool_args;
|
||||
tool_args.messages = { message_user, simple_assist_msg("", "", "special_function", R"({"arg1": )" + arrays(deep) + "}") };
|
||||
tool_args.tools = { special_function_tool };
|
||||
assert_equals(true, rejected([&] { common_chat_templates_apply(tmpls.get(), tool_args); }));
|
||||
|
||||
common_chat_templates_inputs kwargs;
|
||||
kwargs.messages = { message_user };
|
||||
kwargs.chat_template_kwargs["deep"] = arrays(deep);
|
||||
assert_equals(true, rejected([&] { common_chat_templates_apply(tmpls.get(), kwargs); }));
|
||||
|
||||
common_chat_templates_inputs json_schema;
|
||||
json_schema.messages = { message_user };
|
||||
json_schema.json_schema = schema(deep);
|
||||
assert_equals(true, rejected([&] { common_chat_templates_apply(tmpls.get(), json_schema); }));
|
||||
}
|
||||
|
||||
static void test_reasoning_budget_tokens_per_request() {
|
||||
LOG_DBG("%s\n", __func__);
|
||||
// Use Qwen3 template which has <think>...</think> reasoning markers.
|
||||
@@ -8175,10 +8404,12 @@ int main(int argc, char ** argv) {
|
||||
test_translate_gemma();
|
||||
test_deepseek_v4_thinking_retention();
|
||||
test_deepseek_v4_tool_result_ordering();
|
||||
test_deepseek_v4_1_message_fields();
|
||||
test_template_generation_prompt();
|
||||
test_reasoning_effort_caps();
|
||||
test_prompt_parts();
|
||||
test_chat_session();
|
||||
test_chat_json_depth();
|
||||
test_reasoning_budget_tokens_per_request();
|
||||
test_reasoning_budget_message_per_request();
|
||||
test_template_output_peg_parsers(detailed_debug);
|
||||
|
||||
@@ -165,6 +165,20 @@ int main()
|
||||
root ::= "a"{5000,6000}
|
||||
)""");
|
||||
|
||||
// Recursion depth limit exceeded (Issue #29495)
|
||||
{
|
||||
std::string deep_grammar = "root ::= ";
|
||||
int n_depth = 300;
|
||||
for (int i = 0; i < n_depth; ++i) {
|
||||
deep_grammar += "(";
|
||||
}
|
||||
deep_grammar += "\"x\"";
|
||||
for (int i = 0; i < n_depth; ++i) {
|
||||
deep_grammar += ")";
|
||||
}
|
||||
verify_failure(deep_grammar.c_str());
|
||||
}
|
||||
|
||||
verify_parsing(R"""(
|
||||
root ::= "a"{0,5000}
|
||||
)""", {
|
||||
|
||||
@@ -195,6 +195,12 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
||||
n_head = 8;
|
||||
n_ff = 1024;
|
||||
n_layer = 4;
|
||||
} else if (arch == LLM_ARCH_DEEPSEEK41) {
|
||||
// window-only, Engram, ratio-2 and ratio-1 sources, top-k reuse and a candidate-masked index layer
|
||||
n_embd = 512;
|
||||
n_head = 8;
|
||||
n_ff = 1024;
|
||||
n_layer = 6;
|
||||
} else if (arch == LLM_ARCH_STEP35 || arch == LLM_ARCH_LAGUNA) {
|
||||
n_embd = 160; // exercise per-head tensor split granularity with head size 80
|
||||
} else if (arch == LLM_ARCH_QWEN3 || arch == LLM_ARCH_MUSE_GLIMMER || arch == LLM_ARCH_AFMOE) {
|
||||
@@ -285,11 +291,11 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
||||
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT_KV, n_head_per_layer);
|
||||
} else {
|
||||
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT, n_head);
|
||||
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT_KV, arch == LLM_ARCH_DEEPSEEK4 ? uint32_t(1) : n_head_kv);
|
||||
ms.add_kv(LLM_KV_ATTENTION_HEAD_COUNT_KV, (arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_DEEPSEEK41) ? uint32_t(1) : n_head_kv);
|
||||
}
|
||||
|
||||
ms.add_kv(LLM_KV_ATTENTION_MAX_ALIBI_BIAS, 8.0f);
|
||||
if (arch == LLM_ARCH_DEEPSEEK4) {
|
||||
if (arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_DEEPSEEK41) {
|
||||
ms.add_kv(LLM_KV_ATTENTION_KEY_LENGTH, n_embd_head);
|
||||
ms.add_kv(LLM_KV_ATTENTION_VALUE_LENGTH, n_embd_head);
|
||||
ms.add_kv(LLM_KV_ROPE_DIMENSION_COUNT, n_embd_head/2);
|
||||
@@ -337,7 +343,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
||||
ms.add_kv(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, 1e-5f);
|
||||
ms.add_kv(LLM_KV_ATTENTION_GROUPNORM_EPS, 1e-5f);
|
||||
ms.add_kv(LLM_KV_ATTENTION_GROUPNORM_GROUPS, uint32_t(8));
|
||||
ms.add_kv(LLM_KV_ATTENTION_Q_LORA_RANK, arch == LLM_ARCH_DEEPSEEK4 ? uint32_t(64) : uint32_t(512));
|
||||
ms.add_kv(LLM_KV_ATTENTION_Q_LORA_RANK, (arch == LLM_ARCH_DEEPSEEK4 || arch == LLM_ARCH_DEEPSEEK41) ? uint32_t(64) : uint32_t(512));
|
||||
ms.add_kv(LLM_KV_ATTENTION_KV_LORA_RANK, uint32_t(512));
|
||||
ms.add_kv(LLM_KV_ATTENTION_RELATIVE_BUCKETS_COUNT, uint32_t(8));
|
||||
ms.add_kv(LLM_KV_ATTENTION_SLIDING_WINDOW, n_ctx/8);
|
||||
@@ -476,6 +482,39 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
||||
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_NORM, true);
|
||||
}
|
||||
|
||||
if (arch == LLM_ARCH_DEEPSEEK41) {
|
||||
ms.add_kv(LLM_KV_ATTENTION_OUTPUT_GROUP_COUNT, uint32_t(8));
|
||||
ms.add_kv(LLM_KV_ATTENTION_OUTPUT_LORA_RANK, uint32_t(32));
|
||||
ms.add_kv(LLM_KV_ATTENTION_COMPRESS_RATIOS, std::vector<uint32_t>({0, 0, 2, 2, 1, 1}));
|
||||
ms.add_kv(LLM_KV_ATTENTION_COMPRESS_ROPE_FREQ_BASE, 160000.0f);
|
||||
ms.add_kv(LLM_KV_ATTENTION_KV_SOURCE_LAYERS, std::vector<uint32_t>({2, 4}));
|
||||
ms.add_kv(LLM_KV_ATTENTION_INDEXER_SOURCE_LAYERS, std::vector<uint32_t>({2, 4, 5}));
|
||||
ms.add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_SOURCE_LAYER, int32_t(4));
|
||||
ms.add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_TOP_K_BLOCKS, uint32_t(4096));
|
||||
ms.add_kv(LLM_KV_ATTENTION_INDEXER_CANDIDATE_BLOCK_SIZE, uint32_t(4));
|
||||
ms.add_kv(LLM_KV_HYPER_CONNECTION_COUNT, uint32_t(4));
|
||||
ms.add_kv(LLM_KV_HYPER_CONNECTION_SINKHORN_ITERATIONS, uint32_t(2));
|
||||
ms.add_kv(LLM_KV_HYPER_CONNECTION_EPSILON, 1.0e-6f);
|
||||
ms.add_kv(LLM_KV_HASH_LAYER_COUNT, uint32_t(0));
|
||||
ms.add_kv(LLM_KV_SWIGLU_CLAMP_EXP, 10.0f);
|
||||
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_SCALE, 1.0f);
|
||||
ms.add_kv(LLM_KV_EXPERT_WEIGHTS_NORM, true);
|
||||
std::vector<int32_t> token_map(n_vocab);
|
||||
for (uint32_t i = 0; i < n_vocab; ++i) {
|
||||
token_map[i] = (int32_t) (i % 61);
|
||||
}
|
||||
ms.add_kv(LLM_KV_PLE_LAYERS, std::vector<uint32_t>({1, 3}));
|
||||
ms.add_kv(LLM_KV_PLE_NGRAM_SIZE, uint32_t(3));
|
||||
ms.add_kv(LLM_KV_PLE_HEADS_PER_NGRAM, uint32_t(2));
|
||||
ms.add_kv(LLM_KV_EMBEDDING_LENGTH_PER_LAYER, uint32_t(64));
|
||||
ms.add_kv(LLM_KV_PLE_LAYER_MULTIPLIERS, std::vector<uint64_t>({ 76632096046245ull, 4839876093313ull, 35959672319349ull,
|
||||
67716810739261ull, 51510806800915ull, 30921347202721ull }));
|
||||
ms.add_kv(LLM_KV_PLE_HEAD_OFFSETS, std::vector<uint64_t>({0, 101, 204, 311, 420, 533, 660, 791}));
|
||||
ms.add_kv(LLM_KV_PLE_HEAD_VOCAB_SIZES, std::vector<uint64_t>({101, 103, 107, 109, 113, 127, 131, 137}));
|
||||
ms.add_kv(LLM_KV_ENGRAM_PAD_TOKEN_ID, uint32_t(2));
|
||||
ms.add_kv(LLM_KV_ENGRAM_TOKEN_MAP, token_map);
|
||||
}
|
||||
|
||||
if (arch == LLM_ARCH_HRM_TEXT) {
|
||||
// 6 cache slots alias 2 physical blocks: 1 low-stack layer + 1 high-stack layer
|
||||
ms.add_kv(LLM_KV_HRM_LAYERS_PER_STACK, uint32_t(1));
|
||||
@@ -519,6 +558,7 @@ static gguf_context_ptr get_gguf_ctx(const llm_arch arch, const bool moe) {
|
||||
gating_func = 1;
|
||||
break;
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
gating_func = 4;
|
||||
break;
|
||||
default:
|
||||
@@ -780,6 +820,7 @@ static bool moe_mandatory(const llm_arch arch) {
|
||||
case LLM_ARCH_DEEPSEEK32:
|
||||
case LLM_ARCH_DOTS3NOTE:
|
||||
case LLM_ARCH_DEEPSEEK4:
|
||||
case LLM_ARCH_DEEPSEEK41:
|
||||
case LLM_ARCH_GLM4_MOE:
|
||||
case LLM_ARCH_GLM_DSA:
|
||||
case LLM_ARCH_EXAONE_MOE:
|
||||
|
||||
@@ -220,7 +220,7 @@ int main(int argc, char ** argv) {
|
||||
bool list_cases = false;
|
||||
bool dump_logs_on_fail = true;
|
||||
|
||||
int parallel_workers = 1; // 0 = auto, same flag as in test-backend-ops
|
||||
int parallel_workers = 0; // 0 = auto, same flag as in test-backend-ops
|
||||
|
||||
auto parse_parallel_workers = [&](const char * flag, const char * value) {
|
||||
try {
|
||||
@@ -361,12 +361,9 @@ int main(int argc, char ** argv) {
|
||||
|
||||
llama_backend_init();
|
||||
|
||||
// the threads a context runs on, resolved by the common parser - in table mode every worker
|
||||
// builds a context of its own, so the run uses n_pool times this many
|
||||
const int32_t n_threads = params.cpuparams.n_threads;
|
||||
|
||||
// run the full suite for a single model
|
||||
if (models_dir.empty()) {
|
||||
const int32_t n_threads = params.cpuparams.n_threads;
|
||||
LOG("threads: %d\n", n_threads);
|
||||
|
||||
const model_result res = run_model(params.model.path, params, cases, logs_dir,
|
||||
@@ -404,14 +401,14 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// the dummy models are tiny, but the same flag is meant to run sweeps of real checkpoints,
|
||||
// where every worker holds the weights it loaded - so do not scale with the cores
|
||||
const unsigned n_hw = std::thread::hardware_concurrency();
|
||||
const size_t n_auto = n_hw > 0 ? std::min<size_t>(n_hw, 4) : 1;
|
||||
const unsigned n_hw_2 = std::thread::hardware_concurrency() / 2;
|
||||
const size_t n_auto = std::max<size_t>(1, std::min<size_t>(n_hw_2, 4));
|
||||
const size_t n_pool = parallel_workers > 0
|
||||
? std::min<size_t>((size_t) parallel_workers, models.size())
|
||||
: n_auto;
|
||||
|
||||
const int32_t n_threads = std::max<int32_t>(1, n_hw_2 / n_pool);
|
||||
|
||||
// a column is as wide as its case name, but never narrower than the widest verdict
|
||||
const size_t verdict_width = std::max({ test_status_width(test_status::PASS),
|
||||
test_status_width(test_status::FAIL),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../tools/server/server-common.h"
|
||||
#include "../tools/server/server-decision.h"
|
||||
#include "../tools/server/server-schema.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <regex>
|
||||
@@ -464,6 +465,68 @@ static void test_rerank_template(testing & t) {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// server_decision_context: the recursive functions stop at RECURSIVE_LIMIT
|
||||
//
|
||||
|
||||
static void test_decision_depth(testing & t) {
|
||||
const auto prepare = [](size_t depth) {
|
||||
server_decision_context ctx;
|
||||
ctx.type = COMMON_DECISION_TYPE_H2O_LIGHTNING; // the state goes through decision_py_dump
|
||||
std::string error;
|
||||
try {
|
||||
ctx.prepare_state(json::parse(std::string(depth, '[') + std::string(depth, ']')));
|
||||
} catch (const std::invalid_argument & e) {
|
||||
error = e.what();
|
||||
}
|
||||
return error;
|
||||
};
|
||||
t.test("state at the limit", [&](testing & t) {
|
||||
const std::string error = prepare(128);
|
||||
t.assert_true("accepted: " + error, error.empty());
|
||||
});
|
||||
t.test("deep state", [&](testing & t) {
|
||||
const std::string error = prepare(1000);
|
||||
t.assert_true("rejected as nested: " + error, error.find("nested more than 128 levels deep") != std::string::npos);
|
||||
});
|
||||
}
|
||||
|
||||
// DRY sequence breakers
|
||||
//
|
||||
|
||||
static void test_dry_sequence_breakers(testing & t) {
|
||||
common_params params_base;
|
||||
|
||||
const auto eval = [&](const json & data) {
|
||||
return server_schema::eval_llama_cmpl_schema(nullptr, params_base, {}, data);
|
||||
};
|
||||
|
||||
{
|
||||
json data;
|
||||
data["dry_sequence_breakers"] = json::array({"ab", "ab", "cd", "ab"});
|
||||
const auto params = eval(data);
|
||||
const std::vector<std::string> expected = {"ab", "cd"};
|
||||
t.assert_true("duplicates removed, order kept", params.sampling.dry_sequence_breakers == expected);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> many;
|
||||
for (int i = 0; i <= LLAMA_DRY_MAX_SEQ_BREAKERS; ++i) {
|
||||
many.push_back("b" + std::to_string(i));
|
||||
}
|
||||
json data;
|
||||
data["dry_sequence_breakers"] = many;
|
||||
|
||||
bool thrown = false;
|
||||
try {
|
||||
eval(data);
|
||||
} catch (const std::exception &) {
|
||||
thrown = true;
|
||||
}
|
||||
t.assert_true("more than the cap is rejected", thrown);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
testing t;
|
||||
|
||||
@@ -484,6 +547,8 @@ int main(int argc, char ** argv) {
|
||||
t.test("server_pipe", test_server_pipe);
|
||||
t.test("decision_escape", test_decision_escape);
|
||||
t.test("rerank_template", test_rerank_template);
|
||||
t.test("decision_depth", test_decision_depth);
|
||||
t.test("dry_sequence_breakers", test_dry_sequence_breakers);
|
||||
|
||||
return t.summary();
|
||||
}
|
||||
|
||||
@@ -553,6 +553,10 @@ static void gguf_merge(const split_params & split_params) {
|
||||
auto offset = gguf_get_data_offset(ctx_gguf) + gguf_get_tensor_offset(ctx_gguf, i_tensor);
|
||||
f_input.seekg(offset);
|
||||
f_input.read((char *)read_data.data(), n_bytes);
|
||||
if (!f_input) {
|
||||
fprintf(stderr, "%s: failed to read %zu bytes at offset %zu, input file may be truncated\n", __func__, n_bytes, offset);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!split_params.dry_run) {
|
||||
// write tensor data + padding
|
||||
fout.write((const char *)read_data.data(), n_bytes);
|
||||
|
||||
@@ -529,6 +529,7 @@ enum projector_type {
|
||||
PROJECTOR_TYPE_DEEPSEEKOCR,
|
||||
PROJECTOR_TYPE_DEEPSEEKOCR2,
|
||||
PROJECTOR_TYPE_DEEPSEEK4V,
|
||||
PROJECTOR_TYPE_DEEPSEEK41V,
|
||||
PROJECTOR_TYPE_LFM2A,
|
||||
PROJECTOR_TYPE_D1OMNI_A,
|
||||
PROJECTOR_TYPE_GLM4V,
|
||||
@@ -606,6 +607,7 @@ static std::map<projector_type, std::string> PROJECTOR_TYPE_NAMES = {
|
||||
{ PROJECTOR_TYPE_DEEPSEEKOCR, "deepseekocr"},
|
||||
{ PROJECTOR_TYPE_DEEPSEEKOCR2, "deepseekocr2"},
|
||||
{ PROJECTOR_TYPE_DEEPSEEK4V, "deepseek4v"},
|
||||
{ PROJECTOR_TYPE_DEEPSEEK41V, "deepseek41v"},
|
||||
{ PROJECTOR_TYPE_LFM2A, "lfm2a"},
|
||||
{ PROJECTOR_TYPE_D1OMNI_A, "d1omni_a"},
|
||||
{ PROJECTOR_TYPE_GLM4V, "glm4v"},
|
||||
@@ -879,6 +881,11 @@ static inline void clip_anyres_unpad(int cur_w, int cur_h, int orig_w, int orig_
|
||||
}
|
||||
}
|
||||
|
||||
// deepseek41v: [START] ([IMAGE]*n_llm_w [NEWLINE])*n_llm_h [END], ref: num_image_tokens() in image_processor.py
|
||||
static inline int dsv41_n_block_tokens(int n_llm_w, int n_llm_h) {
|
||||
return n_llm_h * (n_llm_w + 1) + 2;
|
||||
}
|
||||
|
||||
// deepseek4v: layout of the LLM token block built from the aligner grid
|
||||
struct dsv4_block_layout {
|
||||
int rows; // grid rows, padded to an even count
|
||||
|
||||
+73
-1
@@ -1111,6 +1111,7 @@ static std::unique_ptr<clip_graph> clip_get_graph_builder(clip_ctx * ctx, const
|
||||
builder = std::make_unique<clip_graph_kimik25>(ctx, img);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK4V:
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
builder = std::make_unique<clip_graph_deepseek4v>(ctx, img);
|
||||
} break;
|
||||
@@ -1773,6 +1774,30 @@ struct clip_model_loader {
|
||||
const int warmup_side = (int) std::sqrt((double) std::min(256, hparams.dsv4_max_n_token));
|
||||
hparams.set_warmup_n_tokens(warmup_side * warmup_side);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
// ref: load_image in inference/image_processor.py (V4.1), PIL ImageOps.pad on gray 127
|
||||
hparams.image_resize_algo = RESIZE_ALGO_BICUBIC;
|
||||
hparams.image_pad_color = {127, 127, 127};
|
||||
hparams.rope_theta = 10000.0f;
|
||||
get_u32(KEY_PROJ_SCALE_FACTOR, hparams.n_merge);
|
||||
get_u32(KEY_IMAGE_MIN_PIXELS, hparams.image_min_pixels);
|
||||
// vision_max_n_token = 1024, vision_max_wh_ratio = null (converter asserts both)
|
||||
hparams.dsv4_max_n_token = 1024;
|
||||
hparams.dsv4_max_wh_ratio = 0;
|
||||
const int patch_area = hparams.patch_size * hparams.patch_size * hparams.n_merge * hparams.n_merge;
|
||||
if (hparams.custom_image_min_tokens > 0) {
|
||||
hparams.image_min_pixels = hparams.custom_image_min_tokens * patch_area;
|
||||
}
|
||||
if (hparams.custom_image_max_tokens > 0) {
|
||||
// the cap is on the whole token block (newlines + start/end included)
|
||||
hparams.dsv4_max_n_token = std::max(hparams.custom_image_max_tokens, 16);
|
||||
}
|
||||
hparams.image_max_pixels = hparams.dsv4_max_n_token * patch_area;
|
||||
hparams.image_min_pixels = std::min(hparams.image_min_pixels, hparams.image_max_pixels);
|
||||
const int warmup_side = (int) std::sqrt((double) std::min(256, hparams.dsv4_max_n_token));
|
||||
hparams.set_warmup_n_tokens(warmup_side * warmup_side);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_GEMMA3:
|
||||
{
|
||||
// default value (used by all model sizes in gemma 3 family)
|
||||
@@ -3065,6 +3090,17 @@ struct clip_model_loader {
|
||||
model.token_embd_img_end = get_tensor(TN_TOK_IMG_END);
|
||||
model.token_embd_img_pad = get_tensor(TN_TOK_IMG_PAD);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
model.mm_1_w = get_tensor(string_format(TN_LLAVA_PROJ, 1, "weight"));
|
||||
model.mm_1_b = get_tensor(string_format(TN_LLAVA_PROJ, 1, "bias"));
|
||||
model.mm_2_w = get_tensor(string_format(TN_LLAVA_PROJ, 2, "weight"));
|
||||
model.mm_2_b = get_tensor(string_format(TN_LLAVA_PROJ, 2, "bias"));
|
||||
// image_start / image_end / image_newline, written into the output block
|
||||
model.image_newline = get_tensor(TN_IMAGE_NEWLINE);
|
||||
model.token_embd_img_start = get_tensor(TN_TOK_IMG_START);
|
||||
model.token_embd_img_end = get_tensor(TN_TOK_IMG_END);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_PIXTRAL:
|
||||
{
|
||||
model.mm_1_w = get_tensor(string_format(TN_LLAVA_PROJ, 1, "weight"));
|
||||
@@ -4377,7 +4413,7 @@ struct clip_cap clip_get_cap(const char * fname) {
|
||||
|
||||
int clip_get_image_max_tokens(const clip_ctx * ctx) {
|
||||
const auto & hparams = ctx->model.hparams;
|
||||
if (ctx->proj_type() == PROJECTOR_TYPE_DEEPSEEK4V) {
|
||||
if (ctx->proj_type() == PROJECTOR_TYPE_DEEPSEEK4V || ctx->proj_type() == PROJECTOR_TYPE_DEEPSEEK41V) {
|
||||
return hparams.dsv4_max_n_token;
|
||||
}
|
||||
if (hparams.image_max_pixels <= 0) {
|
||||
@@ -4585,6 +4621,13 @@ int clip_n_output_tokens(const clip_ctx * ctx, const clip_image_f32 * img) {
|
||||
const int n_llm_h = CLIP_ALIGN(img->ny(), out_patch_size) / out_patch_size;
|
||||
n_patches = dsv4_get_block_layout(n_llm_w, n_llm_h, img->lead_pad).n_out;
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
const int out_patch_size = params.patch_size * params.n_merge;
|
||||
const int n_llm_w = CLIP_ALIGN(img->nx(), out_patch_size) / out_patch_size;
|
||||
const int n_llm_h = CLIP_ALIGN(img->ny(), out_patch_size) / out_patch_size;
|
||||
n_patches = dsv41_n_block_tokens(n_llm_w, n_llm_h);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_PADDLEOCR:
|
||||
case PROJECTOR_TYPE_DOTS_OCR:
|
||||
case PROJECTOR_TYPE_DOTS3NOTE_V:
|
||||
@@ -5692,6 +5735,34 @@ bool clip_encode(struct clip_ctx * ctx, struct clip_encode_params * params) {
|
||||
idx.push_back(idx_end);
|
||||
set_input_i32("layout_idx", idx);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
// mrope layout, only the first 2 channels are used
|
||||
const int n_patches_per_row = image_size_width / patch_size;
|
||||
std::vector<int32_t> positions(n_pos * 4, 0);
|
||||
for (int i = 0; i < n_pos; i++) {
|
||||
positions[i] = i / n_patches_per_row;
|
||||
positions[n_pos + i] = i % n_patches_per_row;
|
||||
}
|
||||
set_input_i32("positions", positions);
|
||||
|
||||
// reading-order block: rows [0, n_grid) are the aligner output, then start / end / newline
|
||||
const int n_merge = hparams.n_merge;
|
||||
const int n_llm_w = CLIP_ALIGN(pos_w, n_merge) / n_merge;
|
||||
const int n_llm_h = CLIP_ALIGN(pos_h, n_merge) / n_merge;
|
||||
const int n_grid = n_llm_w * n_llm_h;
|
||||
std::vector<int32_t> idx;
|
||||
idx.reserve(dsv41_n_block_tokens(n_llm_w, n_llm_h));
|
||||
idx.push_back(n_grid); // IMAGE_START
|
||||
for (int r = 0; r < n_llm_h; r++) {
|
||||
for (int c = 0; c < n_llm_w; c++) {
|
||||
idx.push_back(r * n_llm_w + c);
|
||||
}
|
||||
idx.push_back(n_grid + 2); // IMAGE_NEW_LINE
|
||||
}
|
||||
idx.push_back(n_grid + 1); // IMAGE_END
|
||||
set_input_i32("layout_idx", idx);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_GLM_EDGE:
|
||||
{
|
||||
// llava and other models
|
||||
@@ -6583,6 +6654,7 @@ int clip_n_mmproj_embd(const struct clip_ctx * ctx) {
|
||||
case PROJECTOR_TYPE_KIMIK25:
|
||||
case PROJECTOR_TYPE_YASA2:
|
||||
case PROJECTOR_TYPE_DEEPSEEK4V:
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
case PROJECTOR_TYPE_COHERE2V:
|
||||
return ctx->model.mm_2_w->ne[1];
|
||||
case PROJECTOR_TYPE_HUNYUANVL:
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
// the mapping is precomputed on CPU as the "layout_idx" input (see set_input in clip.cpp)
|
||||
//
|
||||
// ref: inference/vision.py and inference/image_processor.py in the HF repo
|
||||
//
|
||||
// DeepSeek-V4.1-Flash (deepseek41v) shares the encoder and aligner; its block is plain reading order
|
||||
// [START] ([IMAGE]*n_llm_w [NEWLINE])*n_llm_h [END], no lead pads, no pad row, no IMAGE_PAD tensor
|
||||
|
||||
ggml_cgraph * clip_graph_deepseek4v::build() {
|
||||
const int n_merge = hparams.n_merge;
|
||||
@@ -52,7 +55,10 @@ ggml_cgraph * clip_graph_deepseek4v::build() {
|
||||
cur = ggml_pad(ctx0, cur, pad_x, pad_y, 0, 0);
|
||||
}
|
||||
|
||||
ggml_tensor * kernel = ggml_view_3d(ctx0, cur, n_merge, n_merge, cur->ne[2], 0, 0, 0);
|
||||
// contiguous kernel view: CUDA rejects a zero-stride one and im2col falls back to CPU
|
||||
const size_t esz = ggml_element_size(cur);
|
||||
ggml_tensor * kernel = ggml_view_3d(ctx0, cur, n_merge, n_merge, cur->ne[2],
|
||||
n_merge*esz, n_merge*n_merge*esz, 0);
|
||||
cur = ggml_im2col(ctx0, kernel, cur, n_merge, n_merge, 0, 0, 1, 1, true, inp->type);
|
||||
cur = ggml_reshape_2d(ctx0, cur, cur->ne[0], cur->ne[1] * cur->ne[2]);
|
||||
|
||||
@@ -73,19 +79,23 @@ ggml_cgraph * clip_graph_deepseek4v::build() {
|
||||
const int64_t n_grid = cur->ne[1]; // n_llm_w * n_llm_h
|
||||
|
||||
// rows n_grid + 0..3, keep in sync with the index computation in set_input
|
||||
const bool is_v41 = proj_type == PROJECTOR_TYPE_DEEPSEEK41V;
|
||||
ggml_tensor * sentinels[] = {
|
||||
model.token_embd_img_start,
|
||||
model.token_embd_img_end,
|
||||
model.image_newline,
|
||||
model.token_embd_img_pad,
|
||||
is_v41 ? nullptr : model.token_embd_img_pad,
|
||||
};
|
||||
for (ggml_tensor * tok : sentinels) {
|
||||
cur = ggml_concat(ctx0, cur, ggml_reshape_2d(ctx0, tok, n_embd_out, 1), 1);
|
||||
if (tok) {
|
||||
cur = ggml_concat(ctx0, cur, ggml_reshape_2d(ctx0, tok, n_embd_out, 1), 1);
|
||||
}
|
||||
}
|
||||
|
||||
const int n_llm_w = CLIP_ALIGN(n_patches_x, n_merge) / n_merge;
|
||||
const int n_llm_h = CLIP_ALIGN(n_patches_y, n_merge) / n_merge;
|
||||
const int n_out = dsv4_get_block_layout(n_llm_w, n_llm_h, img.lead_pad).n_out;
|
||||
const int n_out = is_v41 ? dsv41_n_block_tokens(n_llm_w, n_llm_h)
|
||||
: dsv4_get_block_layout(n_llm_w, n_llm_h, img.lead_pad).n_out;
|
||||
GGML_ASSERT(n_grid == n_llm_w * n_llm_h);
|
||||
|
||||
ggml_tensor * layout_idx = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, n_out);
|
||||
|
||||
@@ -1454,6 +1454,100 @@ mtmd_image_preproc_out mtmd_image_preprocessor_deepseek4v::preprocess(const clip
|
||||
return out;
|
||||
}
|
||||
|
||||
//
|
||||
// DeepSeek-V4.1-Flash (deepseek41v)
|
||||
//
|
||||
// port of plan_image_grid / load_image from inference/image_processor.py (V4.1)
|
||||
// differs from deepseek4v: block = h * (w + 1) + 2 tokens, no even-row rounding, a single solve, no stretch path
|
||||
//
|
||||
|
||||
void mtmd_image_preprocessor_deepseek41v::plan_grid(int width, int height, int p, int r, int max_n_token, int min_pixels,
|
||||
int & best_width, int & best_height) {
|
||||
auto n_tokens = [&](int bh, int bw) {
|
||||
const int n_llm_h = ((bh / p) + r - 1) / r;
|
||||
const int n_llm_w = ((bw / p) + r - 1) / r;
|
||||
return n_llm_h * (n_llm_w + 1) + 2;
|
||||
};
|
||||
if (min_pixels > 0 && (int64_t) width * height > 0 && (int64_t) width * height < min_pixels) {
|
||||
// python: ratio = (min_pixels / (w * h)) ** 0.5; int(w * ratio)
|
||||
const double ratio = std::sqrt((double) min_pixels / ((double) width * (double) height));
|
||||
width = (int) (width * ratio);
|
||||
height = (int) (height * ratio);
|
||||
}
|
||||
best_width = CLIP_ALIGN(width, p);
|
||||
best_height = CLIP_ALIGN(height, p);
|
||||
if (n_tokens(best_height, best_width) > max_n_token) {
|
||||
// solve_resize_ratio: largest aspect-preserving size whose token block fits
|
||||
const double ratio = (double) height / width;
|
||||
const double max_w_f = std::sqrt((max_n_token - 2) / ratio + 0.25) - 0.5;
|
||||
const double max_h_f = max_w_f * ratio;
|
||||
const int cell = p * r;
|
||||
if (max_w_f < 1.0) {
|
||||
best_height = (max_n_token - 2) / 2 * cell;
|
||||
best_width = cell;
|
||||
} else if (max_h_f < 1.0) {
|
||||
best_height = cell;
|
||||
best_width = (max_n_token - 3) * cell;
|
||||
} else {
|
||||
const double beta = std::min(std::floor(max_w_f) * cell / width, std::floor(max_h_f) * cell / height);
|
||||
best_height = (int) std::floor(height * beta / p) * p;
|
||||
best_width = (int) std::floor(width * beta / p) * p;
|
||||
}
|
||||
GGML_ASSERT(n_tokens(best_height, best_width) <= max_n_token);
|
||||
}
|
||||
}
|
||||
|
||||
mtmd_image_preproc_out mtmd_image_preprocessor_deepseek41v::preprocess(const clip_image_u8 & img) const {
|
||||
mtmd_image_preproc_out out;
|
||||
|
||||
const int p = hparams.patch_size;
|
||||
const clip_image_size orig = img.get_size();
|
||||
int best_width = 0, best_height = 0;
|
||||
plan_grid(orig.width, orig.height, p, hparams.n_merge, hparams.dsv4_max_n_token, hparams.image_min_pixels,
|
||||
best_width, best_height);
|
||||
|
||||
// PIL ImageOps.pad(color=127): ImageOps.contain then a centered paste; python round is half to even
|
||||
auto round_half_even = [](double x) { return (int) std::nearbyint(x); };
|
||||
int new_w = best_width;
|
||||
int new_h = best_height;
|
||||
const double im_ratio = (double) orig.width / orig.height;
|
||||
const double dest_ratio = (double) best_width / best_height;
|
||||
if (im_ratio != dest_ratio) {
|
||||
if (im_ratio > dest_ratio) {
|
||||
new_h = round_half_even((double) orig.height / orig.width * best_width);
|
||||
} else {
|
||||
new_w = round_half_even((double) orig.width / orig.height * best_height);
|
||||
}
|
||||
}
|
||||
clip_image_u8 resized;
|
||||
img_tool::resize(img, resized, {new_w, new_h}, hparams.image_resize_algo, PAD_NONE);
|
||||
|
||||
clip_image_u8 padded;
|
||||
if (new_w == best_width && new_h == best_height) {
|
||||
padded = std::move(resized);
|
||||
} else {
|
||||
padded.set_size({best_width, best_height}, img.is_placeholder());
|
||||
if (!img.is_placeholder()) {
|
||||
img_tool::fill(padded, hparams.image_pad_color);
|
||||
const int off_x = round_half_even((best_width - new_w) * 0.5);
|
||||
const int off_y = round_half_even((best_height - new_h) * 0.5);
|
||||
img_tool::composite(padded, resized, off_x, off_y);
|
||||
}
|
||||
}
|
||||
|
||||
out.append(hparams, padded);
|
||||
|
||||
// load_image rounds the normalized input to bf16 (round to nearest even)
|
||||
for (auto & e : out.entries) {
|
||||
std::vector<float> buf = e.get_ro_buf();
|
||||
for (float & v : buf) {
|
||||
v = ggml_bf16_to_fp32(ggml_fp32_to_bf16(v));
|
||||
}
|
||||
e.cpy_buf(buf);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
mtmd_image_preproc_out mtmd_image_preprocessor_deepseekocr::preprocess(const clip_image_u8 & img) const {
|
||||
mtmd_image_preproc_out output;
|
||||
int grid_w = 0;
|
||||
|
||||
@@ -166,6 +166,15 @@ private:
|
||||
static void safe_resize(int height, int width, int & best_height, int & best_width, int p, int r, int max_n_token);
|
||||
};
|
||||
|
||||
// ref: inference/image_processor.py in the HF repo (DeepSeek-V4.1-Flash): plan_image_grid / load_image
|
||||
struct mtmd_image_preprocessor_deepseek41v : mtmd_image_preprocessor {
|
||||
mtmd_image_preprocessor_deepseek41v(const clip_ctx * ctx) : mtmd_image_preprocessor(ctx) {}
|
||||
mtmd_image_preproc_out preprocess(const clip_image_u8 & img) const override;
|
||||
// target pixel size (multiples of patch_size) for an image of the given size, exposed for tests
|
||||
static void plan_grid(int width, int height, int p, int r, int max_n_token, int min_pixels,
|
||||
int & best_width, int & best_height);
|
||||
};
|
||||
|
||||
// custom llava-uhd slicing logic for MiniCPM-V
|
||||
struct mtmd_image_preprocessor_minicpmv : mtmd_image_preprocessor_llava_uhd {
|
||||
using mtmd_image_preprocessor_llava_uhd::mtmd_image_preprocessor_llava_uhd;
|
||||
|
||||
@@ -903,6 +903,11 @@ struct mtmd_context {
|
||||
// no vocab tokens are added; the start/end/newline markers are learned embeddings emitted by the encoder
|
||||
image_preproc = std::make_unique<mtmd_image_preprocessor_deepseek4v>(ctx_v);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DEEPSEEK41V:
|
||||
{
|
||||
// as deepseek4v: markers come from the encoder, the block is decoded causally
|
||||
image_preproc = std::make_unique<mtmd_image_preprocessor_deepseek41v>(ctx_v);
|
||||
} break;
|
||||
case PROJECTOR_TYPE_DOTS_OCR:
|
||||
case PROJECTOR_TYPE_DOTS3NOTE_V:
|
||||
{
|
||||
|
||||
@@ -563,7 +563,7 @@ These words will not be included in the completion, so make sure to add them to
|
||||
|
||||
`dry_penalty_last_n`: How many tokens to scan for repetitions. Default: `64`, where `0` is disabled.
|
||||
|
||||
`dry_sequence_breakers`: Specify an array of sequence breakers for DRY sampling. Only a JSON array of strings is accepted. Default: `['\n', ':', '"', '*']`
|
||||
`dry_sequence_breakers`: Specify an array of sequence breakers for DRY sampling. Only a JSON array of strings is accepted. At most 64 distinct breakers, duplicates are ignored. Default: `['\n', ':', '"', '*']`
|
||||
|
||||
`xtc_probability`: Set the chance for token removal via XTC sampler. Default: `0.0`, which is disabled.
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ static const size_t DECISION_LEV_N_RATINGS = 9;
|
||||
// a longer state keeps its head and its tail
|
||||
static const size_t DECISION_H2O_MAX_STATE_TOKENS = 32000;
|
||||
|
||||
// the recursive functions of this file walk request JSON, a deeper value would overflow the stack
|
||||
static const int RECURSIVE_LIMIT = 128;
|
||||
|
||||
static std::string decision_meta_str(const llama_model * model, const std::string & key) {
|
||||
char buf[256];
|
||||
const int32_t n = llama_model_meta_val_str(model, key.c_str(), buf, sizeof(buf));
|
||||
@@ -417,7 +420,10 @@ json server_decision_context::parse_state(const json & body, std::vector<raw_buf
|
||||
//
|
||||
|
||||
// replace text in all strings of a JSON value
|
||||
static json decision_replace_text(const json & val, const std::string & search, const std::string & replace) {
|
||||
static json decision_replace_text(const json & val, const std::string & search, const std::string & replace, int depth = 0) {
|
||||
if (depth >= RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (val.is_string()) {
|
||||
std::string str = val.get<std::string>();
|
||||
string_replace_all(str, search, replace);
|
||||
@@ -426,14 +432,14 @@ static json decision_replace_text(const json & val, const std::string & search,
|
||||
if (val.is_array()) {
|
||||
json out = json::array();
|
||||
for (const auto & item : val) {
|
||||
out.push_back(decision_replace_text(item, search, replace));
|
||||
out.push_back(decision_replace_text(item, search, replace, depth + 1));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (val.is_object()) {
|
||||
json out = json::object();
|
||||
for (const auto & [key, item] : val.items()) {
|
||||
out[key] = decision_replace_text(item, search, replace);
|
||||
out[key] = decision_replace_text(item, search, replace, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -441,18 +447,21 @@ static json decision_replace_text(const json & val, const std::string & search,
|
||||
}
|
||||
|
||||
// sort the keys of all objects of a JSON value
|
||||
static json decision_sort_keys(const json & val) {
|
||||
static json decision_sort_keys(const json & val, int depth = 0) {
|
||||
if (depth >= RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (val.is_array()) {
|
||||
json out = json::array();
|
||||
for (const auto & item : val) {
|
||||
out.push_back(decision_sort_keys(item));
|
||||
out.push_back(decision_sort_keys(item, depth + 1));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (val.is_object()) {
|
||||
std::map<std::string, json> sorted;
|
||||
for (const auto & [key, item] : val.items()) {
|
||||
sorted[key] = decision_sort_keys(item);
|
||||
sorted[key] = decision_sort_keys(item, depth + 1);
|
||||
}
|
||||
json out = json::object();
|
||||
for (const auto & [key, item] : sorted) {
|
||||
@@ -464,7 +473,10 @@ static json decision_sort_keys(const json & val) {
|
||||
}
|
||||
|
||||
// kev flattens a JSON value into text, the keys of an object are kept as labels (kev/api.py: render)
|
||||
static std::string decision_kev_render(const json & val, int indent = 0) {
|
||||
static std::string decision_kev_render(const json & val, int indent = 0, int depth = 0) {
|
||||
if (depth >= RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
const std::string pad(2 * indent, ' ');
|
||||
if (val.is_null()) {
|
||||
return "";
|
||||
@@ -478,7 +490,7 @@ static std::string decision_kev_render(const json & val, int indent = 0) {
|
||||
if (val.is_array()) {
|
||||
std::string out;
|
||||
for (const auto & item : val) {
|
||||
const std::string text = decision_kev_render(item, indent + 1);
|
||||
const std::string text = decision_kev_render(item, indent + 1, depth + 1);
|
||||
out += (out.empty() ? "" : "\n") + pad + "- " + text.substr(std::min(text.size(), text.find_first_not_of(" \t\n\r")));
|
||||
}
|
||||
return out;
|
||||
@@ -487,7 +499,7 @@ static std::string decision_kev_render(const json & val, int indent = 0) {
|
||||
std::string out;
|
||||
for (const auto & [key, item] : val.items()) {
|
||||
const bool is_nested = item.is_object() || item.is_array();
|
||||
out += (out.empty() ? "" : "\n") + pad + key + (is_nested ? ":\n" : ": ") + decision_kev_render(item, is_nested ? indent + 1 : 0);
|
||||
out += (out.empty() ? "" : "\n") + pad + key + (is_nested ? ":\n" : ": ") + decision_kev_render(item, is_nested ? indent + 1 : 0, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -528,21 +540,24 @@ static std::string decision_kev_text(const json & val) {
|
||||
}
|
||||
|
||||
// lfm2-d1-omni: special tokens written in the input must not be parsed as such, in keys too (d1-omni prompt.py: escape)
|
||||
static json decision_d1omni_escape(const json & val) {
|
||||
static json decision_d1omni_escape(const json & val, int depth = 0) {
|
||||
if (depth >= RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (val.is_string()) {
|
||||
return server_decision_escape_special_tokens(val.get<std::string>());
|
||||
}
|
||||
if (val.is_array()) {
|
||||
json out = json::array();
|
||||
for (const auto & item : val) {
|
||||
out.push_back(decision_d1omni_escape(item));
|
||||
out.push_back(decision_d1omni_escape(item, depth + 1));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (val.is_object()) {
|
||||
json out = json::object();
|
||||
for (const auto & [key, item] : val.items()) {
|
||||
out[server_decision_escape_special_tokens(key)] = decision_d1omni_escape(item);
|
||||
out[server_decision_escape_special_tokens(key)] = decision_d1omni_escape(item, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -669,6 +684,9 @@ static void decision_py_str(std::string & out, const std::string & s, bool ensur
|
||||
|
||||
// json.dumps() of Python: indent < 0 is no indent, keys stay in their order
|
||||
static void decision_py_dump(std::string & out, const json & val, int indent, int level, const std::string & item_sep, const std::string & key_sep, bool ensure_ascii) {
|
||||
if (level >= RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
const auto newline = [&](int lvl) {
|
||||
return indent < 0 ? std::string() : "\n" + std::string(indent * lvl, ' ');
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "json-schema-to-grammar.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace server_schema {
|
||||
|
||||
//
|
||||
@@ -246,6 +248,23 @@ std::vector<std::unique_ptr<field>> make_llama_cmpl_schema(const common_params &
|
||||
if (ctx.params.sampling.dry_sequence_breakers.empty()) {
|
||||
throw std::runtime_error("Error: dry_sequence_breakers must be a non-empty array of strings");
|
||||
}
|
||||
|
||||
// each distinct breaker costs a full vocabulary scan at sampler init, so dedupe before counting
|
||||
std::unordered_set<std::string> seen;
|
||||
std::vector<std::string> unique_breakers;
|
||||
|
||||
for (const auto & breaker : ctx.params.sampling.dry_sequence_breakers) {
|
||||
if (seen.insert(breaker).second) {
|
||||
unique_breakers.push_back(breaker);
|
||||
}
|
||||
}
|
||||
|
||||
if (unique_breakers.size() > LLAMA_DRY_MAX_SEQ_BREAKERS) {
|
||||
throw std::runtime_error(string_format(
|
||||
"Error: dry_sequence_breakers must contain at most %d distinct entries", LLAMA_DRY_MAX_SEQ_BREAKERS));
|
||||
}
|
||||
|
||||
ctx.params.sampling.dry_sequence_breakers = std::move(unique_breakers);
|
||||
}));
|
||||
|
||||
// handle both "json_schema" and "grammar"
|
||||
|
||||
Reference in New Issue
Block a user