mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-27 23:17:30 -05:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ca4060b7e | ||
|
|
d05706b4f3 | ||
|
|
1b4554eff1 | ||
|
|
a919179942 | ||
|
|
338fce6511 | ||
|
|
19ff817fb2 | ||
|
|
eb7818c0b8 | ||
|
|
6c44591049 | ||
|
|
19cce69199 | ||
|
|
e86f5fd1e9 | ||
|
|
27b7a8a009 | ||
|
|
7ecb57f7ad | ||
|
|
8542e94350 | ||
|
|
922f53ed33 | ||
|
|
0c8c59ef3d | ||
|
|
3ab37aedfe | ||
|
|
85cc432e2c | ||
|
|
74a1fd3d03 | ||
|
|
7eed6b7685 | ||
|
|
276378515c | ||
|
|
d78f40d9d3 | ||
|
|
fae9ac561f | ||
|
|
a8276b70cc | ||
|
|
de5122d879 | ||
|
|
6666a7330c | ||
|
|
7a2e5aba90 | ||
|
|
0d5ca60098 | ||
|
|
d930fe3c26 | ||
|
|
a2b3e121ce | ||
|
|
823df7d0fb | ||
|
|
1a97e387d3 | ||
|
|
bc262f33cc | ||
|
|
f5892ca5ad | ||
|
|
7aca0d2ea8 | ||
|
|
961c376a2d | ||
|
|
4244ad7acd | ||
|
|
63b4b303bd | ||
|
|
caa8191df1 | ||
|
|
a4699400e3 | ||
|
|
4b851fbee8 | ||
|
|
4d71b42fd8 | ||
|
|
70c5f26324 | ||
|
|
5cd62ffee9 | ||
|
|
38645c2ad9 | ||
|
|
02269b4c00 | ||
|
|
5320c87ff0 | ||
|
|
74b104c9ba | ||
|
|
c6fb3b5be2 | ||
|
|
8b28c23943 | ||
|
|
fd8f8451a2 | ||
|
|
9125a2ebe8 | ||
|
|
9b8446b914 | ||
|
|
dc9e3bed42 | ||
|
|
69b3cc406e | ||
|
|
224d94c02b | ||
|
|
48c36110ab | ||
|
|
a074215352 | ||
|
|
d854d98bae | ||
|
|
48c9f08b7b | ||
|
|
6050b467c0 | ||
|
|
2280f0fd16 | ||
|
|
1a0b56cec7 |
@@ -107,3 +107,52 @@ jobs:
|
||||
source .venv/bin/activate
|
||||
cd build
|
||||
ctest -L behavior -LE 'model|backend' --verbose --timeout 900
|
||||
|
||||
windows:
|
||||
runs-on: windows-2025
|
||||
|
||||
cache-mode: write
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
key: behavior-check-windows-2025
|
||||
evict-old-files: 1d
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Python Dependencies
|
||||
id: python_depends
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -m venv .venv
|
||||
source .venv/Scripts/activate
|
||||
pip install jinja2==3.1.6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cmake -B build -G "Ninja Multi-Config" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/x64-windows-llvm.cmake ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DGGML_NATIVE=OFF ^
|
||||
-DGGML_BACKEND_DL=OFF
|
||||
set /A NINJA_JOBS=%NUMBER_OF_PROCESSORS%-1
|
||||
cmake --build build --config Release -j %NINJA_JOBS%
|
||||
|
||||
- name: Test
|
||||
id: cmake_test
|
||||
shell: bash
|
||||
run: |
|
||||
source .venv/Scripts/activate
|
||||
cd build
|
||||
ctest -C Release -L behavior -LE 'model|backend' --verbose --timeout 900
|
||||
|
||||
@@ -107,62 +107,6 @@ jobs:
|
||||
# ref: https://github.com/ggml-org/llama.cpp/pull/19802#issuecomment-4013704023
|
||||
ctest -L main -E "test-llama-archs|test-llama-context|test-speculative-rejection-sampling" --verbose --timeout 900
|
||||
|
||||
macos-latest-x64:
|
||||
runs-on: macos-15-intel
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
restore: false
|
||||
save: false
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: apple-x64
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
# Metal is disabled due to intermittent failures with Github runners not having a GPU:
|
||||
# https://github.com/ggml-org/llama.cpp/actions/runs/8635935781/job/23674807267#step:5:2313
|
||||
cmake -B build \
|
||||
-DCMAKE_BUILD_RPATH="@loader_path" \
|
||||
-DLLAMA_FATAL_WARNINGS=ON \
|
||||
-DLLAMA_BUILD_BORINGSSL=ON \
|
||||
-DGGML_METAL=OFF \
|
||||
-DGGML_RPC=ON \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=13.3
|
||||
time cmake --build build --config Release -j $(sysctl -n hw.logicalcpu)
|
||||
|
||||
- name: ccache-buckets-save
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CACHE_OUTPUT }}
|
||||
with:
|
||||
key: apple-x64
|
||||
folder: llama.cpp
|
||||
evict-old-files: 1d
|
||||
hf_bucket: ggml-org/cache
|
||||
save: true
|
||||
|
||||
- name: Test
|
||||
id: cmake_test
|
||||
run: |
|
||||
cd build
|
||||
ctest -L main --verbose --timeout 900
|
||||
|
||||
macos-latest-ios-xcode:
|
||||
runs-on: macos-latest
|
||||
|
||||
|
||||
@@ -58,7 +58,12 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libvulkan1 mesa-vulkan-drivers libglvnd0 libgl1 libglx0 libegl1 libgles2 libssl-dev time unzip wget python3 python3-venv python3-pip
|
||||
sudo apt install -y software-properties-common
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libvulkan1 mesa-vulkan-drivers libglvnd0 libgl1 libglx0 libegl1 libgles2 libssl-dev time unzip wget python3.12 python3.12-venv
|
||||
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1
|
||||
sudo update-alternatives --config python3
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
|
||||
@@ -968,6 +968,11 @@ jobs:
|
||||
evict-old-files: 1d
|
||||
max-size: "1G"
|
||||
|
||||
- name: Tune ccache for ROCm
|
||||
run: |
|
||||
# since ROCm 10.1, clang -E output has no GPU code, so the preprocessor fallback can reuse objects with old GPU code
|
||||
ccache --set-config=depend_mode=true
|
||||
|
||||
# - name: Cache ROCm Installation
|
||||
# id: cache-rocm
|
||||
# uses: actions/cache@v5
|
||||
@@ -1545,6 +1550,8 @@ jobs:
|
||||
# builds hit.
|
||||
ccache --set-config=compiler_check=content
|
||||
ccache --set-config=sloppiness=time_macros,include_file_mtime,include_file_ctime
|
||||
# since ROCm 10.1, clang -E output has no GPU code, so the preprocessor fallback can reuse objects with old GPU code
|
||||
ccache --set-config=depend_mode=true
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ endif()
|
||||
|
||||
# subprocess spawning isn't a supported/sandbox-friendly operation on mobile OSes or in WASM
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android" OR ANDROID
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN)
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN OR CMAKE_SYSTEM_NAME STREQUAL "WASI")
|
||||
set(LLAMA_SUBPROCESS_DEFAULT OFF)
|
||||
else()
|
||||
set(LLAMA_SUBPROCESS_DEFAULT ON)
|
||||
|
||||
@@ -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
|
||||
|
||||
+16
-1
@@ -924,7 +924,7 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context
|
||||
// TODO @ngxson : maybe show a list of available models in CLI in this case
|
||||
bool can_skip_model = params.usage || params.completion || !params.server_base.empty();
|
||||
if (!can_skip_model && params.model.path.empty()) {
|
||||
throw std::invalid_argument("error: --model is required\n");
|
||||
throw std::invalid_argument("error: --model or -hf is required\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3708,6 +3708,13 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.models_max = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_MODELS_MAX"));
|
||||
add_opt(common_arg(
|
||||
{"--models-memory-margin"}, "N",
|
||||
string_format("for router server, MiB of device memory to keep free per device, unload LRU models until a new one fits (default: %d, 0 = disabled)", params.models_memory_margin),
|
||||
[](common_params & params, int value) {
|
||||
params.models_memory_margin = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_MODELS_MEMORY_MARGIN"));
|
||||
add_opt(common_arg(
|
||||
{"--models-autoload"},
|
||||
{"--no-models-autoload"},
|
||||
@@ -4505,6 +4512,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.tts_lang = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TTS}));
|
||||
add_opt(common_arg(
|
||||
{"--tts-voice"}, "VOICE",
|
||||
"voice for audio generation (model-dependent)\n"
|
||||
"see tts/README.md for per-model usage notes",
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.tts_voice = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TTS}));
|
||||
add_opt(common_arg(
|
||||
{"--tts-speaker-file"}, "FNAME",
|
||||
"speaker file path for audio generation",
|
||||
|
||||
+110
-23
@@ -190,10 +190,17 @@ json common_chat_msg::to_json_oaicompat(bool concat_typed_text) const {
|
||||
} else {
|
||||
auto & parts = jmsg["content"] = json::array();
|
||||
for (const auto & part : content_parts) {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"text", part.text},
|
||||
});
|
||||
if (part.type == "tool_reference") {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"name", part.name},
|
||||
});
|
||||
} else {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"text", part.text},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -309,6 +316,7 @@ struct common_chat_templates {
|
||||
bool has_explicit_template; // Model had builtin template or template overridden was specified.
|
||||
std::unique_ptr<common_chat_template> template_default; // always set (defaults to chatml)
|
||||
std::unique_ptr<common_chat_template> template_tool_use;
|
||||
std::unique_ptr<common_chat_template> template_tts; // the prompt of a text to speak
|
||||
};
|
||||
|
||||
common_chat_tool_choice common_chat_tool_choice_parse_oaicompat(const std::string & tool_choice) {
|
||||
@@ -370,12 +378,16 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
|
||||
throw std::invalid_argument("Missing content part type: " + part.dump());
|
||||
}
|
||||
const auto & type = part.at("type");
|
||||
if (type != "text" && type != "media_marker") {
|
||||
throw std::invalid_argument("Unsupported content part type: " + type.dump());
|
||||
}
|
||||
common_chat_msg_content_part msg_part;
|
||||
msg_part.type = type;
|
||||
msg_part.text = part.at("text");
|
||||
if (type == "text" || type == "media_marker") {
|
||||
msg_part.text = part.at("text");
|
||||
} else if (type == "tool_reference") {
|
||||
// names a request tool, the template renders its definition
|
||||
msg_part.name = part.at("name");
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported content part type: " + type.dump());
|
||||
}
|
||||
msg.content_parts.push_back(msg_part);
|
||||
}
|
||||
} else if (!content.is_null()) {
|
||||
@@ -610,13 +622,17 @@ json common_chat_tools_to_json_oaicompat(const std::vector<common_chat_tool> & t
|
||||
|
||||
auto result = json::array();
|
||||
for (const auto & tool : tools) {
|
||||
json function {
|
||||
{ "name", tool.name },
|
||||
{ "description", tool.description },
|
||||
{ "parameters", json::parse(tool.parameters) },
|
||||
};
|
||||
if (tool.defer_loading) {
|
||||
function["defer_loading"] = true;
|
||||
}
|
||||
result.push_back({
|
||||
{ "type", "function" },
|
||||
{ "function", {
|
||||
{ "name", tool.name },
|
||||
{ "description", tool.description },
|
||||
{ "parameters", json::parse(tool.parameters) },
|
||||
}},
|
||||
{ "function", function },
|
||||
});
|
||||
}
|
||||
return result;
|
||||
@@ -653,10 +669,19 @@ std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const json & too
|
||||
}
|
||||
|
||||
const auto & function = tool.at("function");
|
||||
|
||||
// "defer_loading" sits on the function, or on the tool next to "type"; the function wins
|
||||
const json & defer = function.contains("defer_loading") ? function.at("defer_loading")
|
||||
: tool.value("defer_loading", json(false));
|
||||
if (!defer.is_boolean()) {
|
||||
throw std::invalid_argument("Expected 'defer_loading' to be a boolean, got " + defer.dump());
|
||||
}
|
||||
|
||||
result.push_back({
|
||||
/* .name = */ function.at("name"),
|
||||
/* .description = */ function.value("description", ""),
|
||||
/* .parameters = */ function.value("parameters", json::object()).dump(),
|
||||
/* .defer_loading = */ defer.get<bool>(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -779,19 +804,70 @@ bool common_chat_templates_was_explicit(const struct common_chat_templates * tmp
|
||||
return tmpls->has_explicit_template;
|
||||
}
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates) {
|
||||
// renders a task template (e.g. "tts") with the given variables
|
||||
static std::string common_chat_template_render(const common_chat_template & tmpl, const json & vars) {
|
||||
jinja::context ctx(tmpl.source());
|
||||
jinja::global_from_json(ctx, vars, false);
|
||||
ctx.set_val("bos_token", jinja::mk_val<jinja::value_string>(tmpl.bos_token()));
|
||||
ctx.set_val("eos_token", jinja::mk_val<jinja::value_string>(tmpl.eos_token()));
|
||||
jinja::runtime runtime(ctx);
|
||||
return jinja::runtime::gather_string_parts(runtime.execute(tmpl.prog))->as_string().str();
|
||||
}
|
||||
|
||||
// LFM3 format detection: template renders deferred tools inside <additional_tools>...</additional_tools>
|
||||
static bool is_lfm3_template(const std::string & src) {
|
||||
return src.find("<additional_tools>") != std::string::npos;
|
||||
}
|
||||
|
||||
// LFM3-Audio: a known language code maps to its name, no language lets the model detect it
|
||||
static std::string lfm3_asr_system_prompt(const std::string & language) {
|
||||
static const std::map<std::string, std::string> names = {
|
||||
{ "ar", "Arabic" }, { "da", "Danish" }, { "de", "German" }, { "el", "Greek" }, { "en", "English" },
|
||||
{ "es", "Spanish" }, { "fi", "Finnish" }, { "fr", "French" }, { "he", "Hebrew" }, { "it", "Italian" },
|
||||
{ "ja", "Japanese" }, { "ko", "Korean" }, { "nl", "Dutch" }, { "pl", "Polish" }, { "pt", "Portuguese" },
|
||||
{ "sv", "Swedish" }, { "tr", "Turkish" }, { "zh", "Chinese" },
|
||||
};
|
||||
if (language.empty()) {
|
||||
return "Perform ASR.";
|
||||
}
|
||||
std::string code = language;
|
||||
std::transform(code.begin(), code.end(), code.begin(), [](unsigned char c) { return (char) std::tolower(c); });
|
||||
const auto it = names.find(code);
|
||||
return "Perform ASR in " + (it != names.end() ? it->second : language) + ".";
|
||||
}
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates, const std::string & language) {
|
||||
common_chat_prompt_preset asr_preset;
|
||||
asr_preset.system = "";
|
||||
asr_preset.user = "Transcribe audio to text";
|
||||
|
||||
if (chat_templates && chat_templates->template_default && is_lfm2_template(chat_templates->template_default->source())) {
|
||||
asr_preset.system = "Perform ASR.";
|
||||
asr_preset.user = "";
|
||||
if (chat_templates && chat_templates->template_default) {
|
||||
const std::string src = chat_templates->template_default->source();
|
||||
if (is_lfm2_template(src)) {
|
||||
asr_preset.system = "Perform ASR.";
|
||||
asr_preset.user = "";
|
||||
} else if (is_lfm3_template(src)) {
|
||||
asr_preset.system = lfm3_asr_system_prompt(language);
|
||||
asr_preset.user = "";
|
||||
asr_preset.has_language = true;
|
||||
}
|
||||
}
|
||||
|
||||
return asr_preset;
|
||||
}
|
||||
|
||||
std::string common_chat_get_tts_prompt(const common_chat_templates * chat_templates, const std::string & text,
|
||||
const std::string & language, const std::string & voice) {
|
||||
if (!chat_templates || !chat_templates->template_tts) {
|
||||
return "";
|
||||
}
|
||||
return common_chat_template_render(*chat_templates->template_tts, {
|
||||
{"messages", json::array({{{"role", "user"}, {"content", text}}})},
|
||||
{"language", language},
|
||||
{"voice", voice},
|
||||
});
|
||||
}
|
||||
|
||||
std::string common_chat_templates_source(const struct common_chat_templates * tmpls, const std::string & variant) {
|
||||
if (!variant.empty()) {
|
||||
if (variant == "tool_use") {
|
||||
@@ -902,6 +978,16 @@ common_chat_templates_ptr common_chat_templates_init(const struct llama_model *
|
||||
LOG_ERR("%s: failed to parse tool use chat template (ignoring it): %s\n", __func__, e.what());
|
||||
}
|
||||
}
|
||||
// task templates of the model, kept with a chat template override
|
||||
if (model) {
|
||||
if (const char * src = llama_model_chat_template(model, "tts")) {
|
||||
try {
|
||||
tmpls->template_tts = std::make_unique<common_chat_template>(src, token_bos, token_eos);
|
||||
} catch (const std::exception & e) {
|
||||
LOG_ERR("%s: failed to parse the tts chat template (ignoring it): %s\n", __func__, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmpls;
|
||||
}
|
||||
|
||||
@@ -1854,12 +1940,13 @@ const common_chat_msg & common_chat_session::finish(const common_chat_input & ch
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<std::string, bool> common_chat_templates_get_caps(const common_chat_templates * chat_templates) {
|
||||
common_json common_chat_templates_get_caps(const common_chat_templates * chat_templates) {
|
||||
GGML_ASSERT(chat_templates != nullptr);
|
||||
GGML_ASSERT(chat_templates->template_default != nullptr);
|
||||
if (chat_templates->template_tool_use != nullptr) {
|
||||
// take the more expressive template when available
|
||||
return chat_templates->template_tool_use->caps.to_map();
|
||||
}
|
||||
return chat_templates->template_default->caps.to_map();
|
||||
// take the more expressive template when available
|
||||
const auto & caps = chat_templates->template_tool_use != nullptr ? chat_templates->template_tool_use->caps
|
||||
: chat_templates->template_default->caps;
|
||||
common_json result = caps.to_map();
|
||||
result["reasoning_effort_levels"] = caps.reasoning_effort_levels;
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-3
@@ -45,6 +45,7 @@ struct common_chat_tool_call {
|
||||
struct common_chat_msg_content_part {
|
||||
std::string type;
|
||||
std::string text;
|
||||
std::string name; // tool_reference: name of a tool from the request tools
|
||||
|
||||
// TODO @ngxson : no known chat templates support reasoning_content in content parts yet
|
||||
// this can be useful for models with interleaved thinking (like Kimi-K2)
|
||||
@@ -52,7 +53,7 @@ struct common_chat_msg_content_part {
|
||||
// std::string reasoning_content;
|
||||
|
||||
bool operator==(const common_chat_msg_content_part & other) const {
|
||||
return type == other.type && text == other.text;
|
||||
return type == other.type && text == other.text && name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -226,6 +227,9 @@ struct common_chat_tool {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string parameters;
|
||||
|
||||
// withheld from the system prompt, a tool_reference reveals it later in the conversation
|
||||
bool defer_loading = false;
|
||||
};
|
||||
|
||||
enum common_chat_tool_choice {
|
||||
@@ -468,7 +472,7 @@ common_json common_chat_tools_to_json_oaicompat(const std::vector<common_chat_to
|
||||
common_json common_chat_tool_parameters(const common_json & function);
|
||||
|
||||
// get template caps, useful for reporting to server /props endpoint
|
||||
std::map<std::string, bool> common_chat_templates_get_caps(const common_chat_templates * chat_templates);
|
||||
common_json common_chat_templates_get_caps(const common_chat_templates * chat_templates);
|
||||
|
||||
common_chat_prompt common_chat_template_direct_apply(
|
||||
const common_chat_template & tmpl,
|
||||
@@ -487,6 +491,12 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
|
||||
struct common_chat_prompt_preset {
|
||||
std::string system;
|
||||
std::string user;
|
||||
bool has_language = false; // the preset already names the requested language
|
||||
};
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates);
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates, const std::string & language = "");
|
||||
|
||||
// prompt of the model's "tts" chat template for the text to speak, empty if the model has none
|
||||
// language and voice are optional, the template defines their defaults
|
||||
std::string common_chat_get_tts_prompt(const common_chat_templates * chat_templates, const std::string & text,
|
||||
const std::string & language = "", const std::string & voice = "");
|
||||
|
||||
+94
-75
@@ -742,50 +742,56 @@ static inline bool glob_class_match(const char c, const char * pattern, const ch
|
||||
}
|
||||
|
||||
// simple glob: * matches non-/ chars, ** matches anything including /, [] matches character class
|
||||
static inline bool glob_match(const char * pattern, const char * str) {
|
||||
if (*pattern == '\0') {
|
||||
return *str == '\0';
|
||||
}
|
||||
if (pattern[0] == '*' && pattern[1] == '*') {
|
||||
const char * p = pattern + 2;
|
||||
if (glob_match(p, str)) return true;
|
||||
if (*str != '\0') return glob_match(pattern, str + 1);
|
||||
// only wildcards recurse, too many wildcards: no match
|
||||
static bool glob_match(const char * pattern, const char * str, int depth = 0) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
if (*pattern == '*') {
|
||||
const char * p = pattern + 1;
|
||||
for (; *str != '\0' && *str != '/'; str++) {
|
||||
if (glob_match(p, str)) return true;
|
||||
}
|
||||
return glob_match(p, str);
|
||||
}
|
||||
if (*pattern == '?' && *str != '\0' && *str != '/') {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
}
|
||||
if (*pattern == '[') {
|
||||
const char * class_end = pattern + 1;
|
||||
// If first character after '[' is ']' or '-', treat it as literal
|
||||
if (*class_end == ']' || *class_end == '-') {
|
||||
class_end++;
|
||||
}
|
||||
while (*class_end != '\0' && *class_end != ']') {
|
||||
class_end++;
|
||||
}
|
||||
if (*class_end == ']') {
|
||||
if (*str == '\0') return false;
|
||||
bool matched = glob_class_match(*str, pattern + 1, class_end);
|
||||
return matched && glob_match(class_end + 1, str + 1);
|
||||
} else {
|
||||
if (*str == '[') {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
while (*pattern != '\0') {
|
||||
if (pattern[0] == '*' && pattern[1] == '*') {
|
||||
const char * p = pattern + 2;
|
||||
for (;; str++) {
|
||||
if (glob_match(p, str, depth + 1)) return true;
|
||||
if (*str == '\0') return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (*pattern == '*') {
|
||||
const char * p = pattern + 1;
|
||||
for (; *str != '\0' && *str != '/'; str++) {
|
||||
if (glob_match(p, str, depth + 1)) return true;
|
||||
}
|
||||
return glob_match(p, str, depth + 1);
|
||||
}
|
||||
if (*pattern == '?' && *str != '\0' && *str != '/') {
|
||||
pattern++;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*pattern == '[') {
|
||||
const char * class_end = pattern + 1;
|
||||
// If first character after '[' is ']' or '-', treat it as literal
|
||||
if (*class_end == ']' || *class_end == '-') {
|
||||
class_end++;
|
||||
}
|
||||
while (*class_end != '\0' && *class_end != ']') {
|
||||
class_end++;
|
||||
}
|
||||
if (*class_end == ']') {
|
||||
if (*str == '\0' || !glob_class_match(*str, pattern + 1, class_end)) return false;
|
||||
pattern = class_end + 1;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*str != '[') return false;
|
||||
pattern++;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*pattern != *str) return false;
|
||||
pattern++;
|
||||
str++;
|
||||
}
|
||||
if (*pattern == *str) {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
}
|
||||
return false;
|
||||
return *str == '\0';
|
||||
}
|
||||
|
||||
bool glob_match(const std::string & pattern, const std::string & str) {
|
||||
@@ -1181,6 +1187,9 @@ static const std::map<common_decision_type, std::string> COMMON_DECISION_TYPE_NA
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1_OMNI, "lfm2-d1-omni" },
|
||||
{ COMMON_DECISION_TYPE_GRANITE_SWITCH, "granite-switch"},
|
||||
{ COMMON_DECISION_TYPE_H2O_LIGHTNING, "h2o-lightning" },
|
||||
{ COMMON_DECISION_TYPE_MINICPM_OCR_JEV, "openbmb-minicpm-ocr-jev" },
|
||||
{ COMMON_DECISION_TYPE_LFM3_D1, "lfm3-d1" },
|
||||
{ COMMON_DECISION_TYPE_LFM3_D1_OMNI, "lfm3-d1-omni" },
|
||||
// for future models, ALWAYS add org name prefix to avoid name collisions, e.g. "orgname-modelname"
|
||||
};
|
||||
|
||||
@@ -1246,44 +1255,50 @@ common_gguf_info common_get_gguf_info(const std::string & fname) {
|
||||
return info;
|
||||
}
|
||||
|
||||
void common_fit_apply(common_params & params, llama_model_params & mparams, llama_context_params & cparams) {
|
||||
if (!params.fit_params) {
|
||||
return;
|
||||
}
|
||||
|
||||
COM_TRC("%s", "fitting params to device memory ...\n");
|
||||
COM_TRC("%s", "(for bugs during this step try to reproduce them with -fit off, or provide --verbose logs if the bug only occurs with -fit on)\n");
|
||||
|
||||
// the draft context is created from the same base params and follows the main context, fit both together
|
||||
const bool has_draft = params.speculative.has_dft();
|
||||
const bool spec_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(),
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
|
||||
|
||||
common_params params_dft = common_base_params_to_speculative(params);
|
||||
|
||||
auto mparams_dft = common_model_params_to_llama(params_dft);
|
||||
auto cparams_dft = common_context_params_to_llama(params_dft);
|
||||
if (spec_mtp) {
|
||||
cparams_dft.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
|
||||
}
|
||||
cparams_dft.n_rs_seq = 0;
|
||||
|
||||
const common_fit_extra_model extra = {
|
||||
/*.path_model =*/ params_dft.model.path.c_str(),
|
||||
/*.mparams =*/ &mparams_dft,
|
||||
/*.cparams =*/ &cparams_dft,
|
||||
/*.shares_model =*/ !has_draft, // an MTP context runs on the weights of the main model
|
||||
};
|
||||
|
||||
common_fit_params(params.model.path.c_str(), &mparams, &cparams,
|
||||
params.tensor_split,
|
||||
params.tensor_buft_overrides.data(),
|
||||
params.fit_params_target.data(),
|
||||
params.fit_params_min_ctx,
|
||||
has_draft || spec_mtp ? &extra : nullptr,
|
||||
params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR);
|
||||
}
|
||||
|
||||
common_init_result::common_init_result(common_params & params, bool model_only) :
|
||||
pimpl(new impl{}) {
|
||||
auto mparams = common_model_params_to_llama(params);
|
||||
auto cparams = common_context_params_to_llama(params);
|
||||
|
||||
if (params.fit_params) {
|
||||
COM_TRC("%s", "fitting params to device memory ...\n");
|
||||
COM_TRC("%s", "(for bugs during this step try to reproduce them with -fit off, or provide --verbose logs if the bug only occurs with -fit on)\n");
|
||||
|
||||
// the draft context is created from the same base params and follows the main context, fit both together
|
||||
const bool has_draft = params.speculative.has_dft();
|
||||
const bool spec_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(),
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
|
||||
|
||||
common_params params_dft = common_base_params_to_speculative(params);
|
||||
|
||||
auto mparams_dft = common_model_params_to_llama(params_dft);
|
||||
auto cparams_dft = common_context_params_to_llama(params_dft);
|
||||
if (spec_mtp) {
|
||||
cparams_dft.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
|
||||
}
|
||||
cparams_dft.n_rs_seq = 0;
|
||||
|
||||
const common_fit_extra_model extra = {
|
||||
/*.path_model =*/ params_dft.model.path.c_str(),
|
||||
/*.mparams =*/ &mparams_dft,
|
||||
/*.cparams =*/ &cparams_dft,
|
||||
/*.shares_model =*/ !has_draft, // an MTP context runs on the weights of the main model
|
||||
};
|
||||
|
||||
common_fit_params(params.model.path.c_str(), &mparams, &cparams,
|
||||
params.tensor_split,
|
||||
params.tensor_buft_overrides.data(),
|
||||
params.fit_params_target.data(),
|
||||
params.fit_params_min_ctx,
|
||||
has_draft || spec_mtp ? &extra : nullptr,
|
||||
params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR);
|
||||
}
|
||||
common_fit_apply(params, mparams, cparams);
|
||||
|
||||
llama_model * model = llama_model_load_from_file(params.model.path.c_str(), mparams);
|
||||
if (model == NULL) {
|
||||
@@ -1301,13 +1316,17 @@ common_init_result::common_init_result(common_params & params, bool model_only)
|
||||
// these decision models return a score for each token via the embeddings output
|
||||
// TODO: maybe improve this in the future
|
||||
const auto decision_type = common_get_decision_type(model);
|
||||
// minicpm-ocr-jev reads its class scores from the pooled output, the pooling type is the one of the GGUF
|
||||
if (decision_type == COMMON_DECISION_TYPE_LAYA || decision_type == COMMON_DECISION_TYPE_KEV || decision_type == COMMON_DECISION_TYPE_CLEF ||
|
||||
decision_type == COMMON_DECISION_TYPE_LFM2_D1_OMNI || decision_type == COMMON_DECISION_TYPE_VLLM_DECISION2) {
|
||||
decision_type == COMMON_DECISION_TYPE_LFM2_D1_OMNI || decision_type == COMMON_DECISION_TYPE_VLLM_DECISION2 ||
|
||||
decision_type == COMMON_DECISION_TYPE_MINICPM_OCR_JEV) {
|
||||
const auto pooling_type = decision_type == COMMON_DECISION_TYPE_MINICPM_OCR_JEV ? LLAMA_POOLING_TYPE_UNSPECIFIED : LLAMA_POOLING_TYPE_NONE;
|
||||
|
||||
params.embedding = true;
|
||||
params.pooling_type = LLAMA_POOLING_TYPE_NONE;
|
||||
params.pooling_type = pooling_type;
|
||||
|
||||
cparams.embeddings = true;
|
||||
cparams.pooling_type = LLAMA_POOLING_TYPE_NONE;
|
||||
cparams.pooling_type = pooling_type;
|
||||
cparams.n_outputs_max = cparams.n_batch;
|
||||
cparams.n_outputs_max_per_seq = 1;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#define DIRECTORY_SEPARATOR '/'
|
||||
#endif // _WIN32
|
||||
|
||||
#define COMMON_RECURSIVE_LIMIT 128
|
||||
|
||||
#define COM_DBG(fmt, ...) LOG_DBG("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
#define COM_TRC(fmt, ...) LOG_TRC("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
#define COM_INF(fmt, ...) LOG_INF("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
@@ -699,6 +701,7 @@ struct common_params {
|
||||
std::string models_dir = ""; // directory containing models for the router server
|
||||
std::string models_preset = ""; // directory containing model presets for the router server
|
||||
int models_max = 4; // maximum number of models to load simultaneously
|
||||
int models_memory_margin = 0; // MiB of device memory to keep free when loading models (0 = no memory accounting)
|
||||
bool models_autoload = true; // automatically load models when requested via the router server
|
||||
std::string models_preset_hf = ""; // show a warning about remote presets on router loaded (if not empty)
|
||||
|
||||
@@ -769,6 +772,7 @@ struct common_params {
|
||||
|
||||
// TTS params
|
||||
std::string tts_lang = "";
|
||||
std::string tts_voice = "";
|
||||
std::string tts_speaker_file = "";
|
||||
|
||||
bool is_gen_docs = false; // whether we are running inside llama-gen-docs
|
||||
@@ -976,6 +980,9 @@ enum common_decision_type {
|
||||
COMMON_DECISION_TYPE_LFM2_D1_OMNI, // same as laya, other prompt layout
|
||||
COMMON_DECISION_TYPE_GRANITE_SWITCH, // same as openjev, run by the decision aLoRA of granite switch
|
||||
COMMON_DECISION_TYPE_H2O_LIGHTNING, // same as openjev, the labels are read after an answer prefill
|
||||
COMMON_DECISION_TYPE_MINICPM_OCR_JEV, // one score per class, the pooled embeddings output of the last token
|
||||
COMMON_DECISION_TYPE_LFM3_D1, // same as lfm2-d1, non-causal, label codes A..Z then AA, AB..
|
||||
COMMON_DECISION_TYPE_LFM3_D1_OMNI, // same as lfm3-d1
|
||||
COMMON_DECISION_TYPE_UNKNOWN, // a decision model of a type that is not supported
|
||||
};
|
||||
|
||||
@@ -1015,6 +1022,9 @@ common_init_result_ptr common_init_from_params(common_params & params, bool mode
|
||||
struct llama_model_params common_model_params_to_llama ( common_params & params);
|
||||
struct llama_context_params common_context_params_to_llama(const common_params & params);
|
||||
|
||||
// apply --fit to mparams and cparams, no-op if params.fit_params is false
|
||||
void common_fit_apply(common_params & params, struct llama_model_params & mparams, struct llama_context_params & cparams);
|
||||
|
||||
// clear LoRA adapters from context, then apply new list of adapters
|
||||
void common_set_adapter_lora(struct llama_context * ctx, std::vector<common_adapter_lora_info> & lora);
|
||||
|
||||
|
||||
+197
-103
@@ -1,6 +1,8 @@
|
||||
#include "fit.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "json.h"
|
||||
#include "llama-cpp.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "../src/llama-ext.h"
|
||||
@@ -27,6 +29,32 @@ class common_params_fit_exception : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
// RAII: send llama logs below min_level to the debug log
|
||||
struct common_fit_log_scope {
|
||||
common_fit_log_scope(ggml_log_level min_level) : min_level(min_level) {
|
||||
llama_log_get(&original_logger.callback, &original_logger.user_data);
|
||||
llama_log_set([](ggml_log_level level, const char * text, void * user_data) {
|
||||
const common_fit_log_scope * self = (const common_fit_log_scope *) user_data;
|
||||
const ggml_log_level level_eff = level >= self->min_level ? level : GGML_LOG_LEVEL_DEBUG;
|
||||
self->original_logger.callback(level_eff, text, self->original_logger.user_data);
|
||||
}, this);
|
||||
}
|
||||
|
||||
~common_fit_log_scope() {
|
||||
llama_log_set(original_logger.callback, original_logger.user_data);
|
||||
}
|
||||
|
||||
common_fit_log_scope(const common_fit_log_scope &) = delete;
|
||||
common_fit_log_scope & operator=(const common_fit_log_scope &) = delete;
|
||||
|
||||
private:
|
||||
struct {
|
||||
ggml_log_callback callback;
|
||||
void * user_data;
|
||||
} original_logger;
|
||||
ggml_log_level min_level;
|
||||
};
|
||||
|
||||
static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
@@ -36,22 +64,7 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
uint32_t & hp_n_ctx_train,
|
||||
uint32_t & hp_n_expert,
|
||||
ggml_log_level log_level) {
|
||||
struct user_data_t {
|
||||
struct {
|
||||
ggml_log_callback callback;
|
||||
void * user_data;
|
||||
} original_logger;
|
||||
ggml_log_level min_level; // prints below this log level go to debug log
|
||||
};
|
||||
user_data_t ud;
|
||||
llama_log_get(&ud.original_logger.callback, &ud.original_logger.user_data);
|
||||
ud.min_level = log_level;
|
||||
|
||||
llama_log_set([](ggml_log_level level, const char * text, void * user_data) {
|
||||
const user_data_t * ud = (const user_data_t *) user_data;
|
||||
const ggml_log_level level_eff = level >= ud->min_level ? level : GGML_LOG_LEVEL_DEBUG;
|
||||
ud->original_logger.callback(level_eff, text, ud->original_logger.user_data);
|
||||
}, &ud);
|
||||
common_fit_log_scope log_scope(log_level);
|
||||
|
||||
llama_model_params mparams_copy = *mparams;
|
||||
mparams_copy.no_alloc = true;
|
||||
@@ -59,14 +72,12 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
|
||||
llama_model * model = llama_model_load_from_file(path_model, mparams_copy);
|
||||
if (model == nullptr) {
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
throw std::runtime_error("failed to load model");
|
||||
}
|
||||
|
||||
llama_context * ctx = llama_init_from_model(model, *cparams);
|
||||
if (ctx == nullptr) {
|
||||
llama_model_free(model);
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
throw std::runtime_error("failed to create llama_context from model");
|
||||
}
|
||||
|
||||
@@ -148,11 +159,81 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
|
||||
llama_free(ctx);
|
||||
llama_model_free(model);
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<common_memory_breakdown_row> common_fit_measure(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
const llama_context_params * cparams,
|
||||
uint32_t * n_ctx_train,
|
||||
uint32_t * n_ctx,
|
||||
ggml_log_level log_level) {
|
||||
common_fit_log_scope log_scope(log_level);
|
||||
|
||||
llama_model_params mparams_copy = *mparams;
|
||||
mparams_copy.no_alloc = true;
|
||||
mparams_copy.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
|
||||
llama_model_ptr model(llama_model_load_from_file(path_model, mparams_copy));
|
||||
if (!model) {
|
||||
throw std::runtime_error("failed to load model");
|
||||
}
|
||||
|
||||
llama_context_ptr ctx(llama_init_from_model(model.get(), *cparams));
|
||||
if (!ctx) {
|
||||
throw std::runtime_error("failed to create llama_context from model");
|
||||
}
|
||||
|
||||
if (n_ctx_train) {
|
||||
*n_ctx_train = llama_model_n_ctx_train(model.get());
|
||||
}
|
||||
if (n_ctx) {
|
||||
*n_ctx = llama_n_ctx(ctx.get());
|
||||
}
|
||||
|
||||
return common_memory_breakdown_rows(ctx.get());
|
||||
}
|
||||
|
||||
common_memory_reports common_measure_memory(common_params & params) {
|
||||
const ggml_log_level log_level = params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR;
|
||||
const char * path_model = params.model.path.c_str();
|
||||
|
||||
common_memory_reports res;
|
||||
|
||||
auto mparams = common_model_params_to_llama(params);
|
||||
auto cparams = common_context_params_to_llama(params);
|
||||
|
||||
// max: what --fit picks now, no-op with -fit off
|
||||
{
|
||||
auto mparams_fit = mparams;
|
||||
auto cparams_fit = cparams;
|
||||
common_fit_apply(params, mparams_fit, cparams_fit);
|
||||
res.info_max = common_fit_measure(path_model, &mparams_fit, &cparams_fit, &res.n_ctx_train, &res.n_ctx_max, log_level);
|
||||
}
|
||||
|
||||
// min: the smallest context --fit may pick (--fit only changes n_ctx when it is 0)
|
||||
uint32_t n_ctx_min = cparams.n_ctx;
|
||||
if (n_ctx_min == 0 && params.fit_params) {
|
||||
const uint32_t n_streams = cparams.kv_unified ? 1 : std::max<uint32_t>(1, cparams.n_seq_max);
|
||||
// -c 0 sets fit_params_min_ctx to UINT32_MAX, cap at n_ctx_train like --fit
|
||||
n_ctx_min = (uint32_t) std::min<uint64_t>(
|
||||
uint64_t(params.fit_params_min_ctx) * n_streams,
|
||||
uint64_t(res.n_ctx_train) * n_streams);
|
||||
}
|
||||
if (n_ctx_min == 0 || n_ctx_min >= res.n_ctx_max) {
|
||||
// nothing to reduce, or -fit off with the trained context
|
||||
res.n_ctx_min = res.n_ctx_max;
|
||||
res.info_min = res.info_max;
|
||||
} else {
|
||||
cparams.n_ctx = n_ctx_min;
|
||||
res.info_min = common_fit_measure(path_model, &mparams, &cparams, nullptr, &res.n_ctx_min, log_level);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
common_device_memory_data_vec common_get_device_memory_data(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
@@ -903,8 +984,7 @@ enum common_params_fit_status common_fit_params(
|
||||
return status;
|
||||
}
|
||||
|
||||
void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
//const auto & devices = ctx->get_model().devices;
|
||||
std::vector<common_memory_breakdown_row> common_memory_breakdown_rows(const struct llama_context * ctx) {
|
||||
const auto * model = llama_get_model(ctx);
|
||||
|
||||
std::vector<ggml_backend_dev_t> devices;
|
||||
@@ -914,18 +994,6 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
|
||||
llama_memory_breakdown memory_breakdown = llama_get_memory_breakdown(ctx);
|
||||
|
||||
std::vector<std::array<std::string, 9>> table_data;
|
||||
table_data.reserve(devices.size());
|
||||
|
||||
// same data as the table below, for --log-jsonl consumers
|
||||
common_json rows = common_json::array();
|
||||
const std::string template_header = "%s: | %s | %s %s %s %s %s %s %s |\n";
|
||||
const std::string template_gpu = "%s: | %s | %s = %s + (%s = %s + %s + %s) + %s |\n";
|
||||
const std::string template_other = "%s: | %s | %s %s %s = %s + %s + %s %s |\n";
|
||||
|
||||
table_data.push_back({template_header, "memory breakdown [MiB]", "total", "free", "self", "model", "context", "compute", "unaccounted"});
|
||||
|
||||
constexpr size_t MiB = 1024 * 1024;
|
||||
const std::vector<std::string> desc_prefixes_strip = {"NVIDIA ", "GeForce ", "Tesla ", "AMD ", "Radeon ", "Instinct "};
|
||||
|
||||
// track seen buffer types to avoid double counting:
|
||||
@@ -964,104 +1032,130 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// print memory breakdown for each device:
|
||||
std::vector<common_memory_breakdown_row> rows;
|
||||
rows.reserve(devices.size() + 1 + memory_breakdown.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); i++) {
|
||||
ggml_backend_dev_t dev = devices[i];
|
||||
llama_memory_breakdown_data mb = mb_dev[i];
|
||||
|
||||
const std::string name = ggml_backend_dev_name(dev);
|
||||
std::string desc = ggml_backend_dev_description(dev);
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::DEVICE;
|
||||
row.name = ggml_backend_dev_name(dev);
|
||||
row.description = ggml_backend_dev_description(dev);
|
||||
for (const std::string & prefix : desc_prefixes_strip) {
|
||||
if (desc.length() >= prefix.length() && desc.substr(0, prefix.length()) == prefix) {
|
||||
desc = desc.substr(prefix.length());
|
||||
if (row.description.length() >= prefix.length() && row.description.substr(0, prefix.length()) == prefix) {
|
||||
row.description = row.description.substr(prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
size_t free, total;
|
||||
ggml_backend_dev_memory(dev, &free, &total);
|
||||
|
||||
const size_t self = mb.model + mb.context + mb.compute;
|
||||
const int64_t unaccounted = static_cast<int64_t>(total) - static_cast<int64_t>(free) - static_cast<int64_t>(self);
|
||||
|
||||
table_data.push_back({
|
||||
template_gpu,
|
||||
" - " + name + " (" + desc + ")",
|
||||
std::to_string(total / MiB),
|
||||
std::to_string(free / MiB),
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb.model / MiB),
|
||||
std::to_string(mb.context / MiB),
|
||||
std::to_string(mb.compute / MiB),
|
||||
std::to_string(unaccounted / static_cast<int64_t>(MiB))});
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "device"},
|
||||
{"name", name},
|
||||
{"description", desc},
|
||||
{"total", total / MiB},
|
||||
{"free", free / MiB},
|
||||
{"self", self / MiB},
|
||||
{"model", mb.model / MiB},
|
||||
{"context", mb.context / MiB},
|
||||
{"compute", mb.compute / MiB},
|
||||
{"unaccounted", unaccounted / static_cast<int64_t>(MiB)},
|
||||
});
|
||||
row.total = total;
|
||||
row.free = free;
|
||||
row.model = mb_dev[i].model;
|
||||
row.context = mb_dev[i].context;
|
||||
row.compute = mb_dev[i].compute;
|
||||
rows.push_back(std::move(row));
|
||||
}
|
||||
|
||||
// print memory breakdown for host:
|
||||
{
|
||||
const size_t self = mb_host.model + mb_host.context + mb_host.compute;
|
||||
table_data.push_back({
|
||||
template_other,
|
||||
" - Host",
|
||||
"", // total
|
||||
"", // free
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb_host.model / MiB),
|
||||
std::to_string(mb_host.context / MiB),
|
||||
std::to_string(mb_host.compute / MiB),
|
||||
""}); // unaccounted
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "host"},
|
||||
{"name", "Host"},
|
||||
{"self", self / MiB},
|
||||
{"model", mb_host.model / MiB},
|
||||
{"context", mb_host.context / MiB},
|
||||
{"compute", mb_host.compute / MiB},
|
||||
});
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::HOST;
|
||||
row.name = "Host";
|
||||
row.model = mb_host.model;
|
||||
row.context = mb_host.context;
|
||||
row.compute = mb_host.compute;
|
||||
rows.push_back(std::move(row));
|
||||
}
|
||||
|
||||
// print memory breakdown for all remaining buffer types:
|
||||
// all remaining buffer types:
|
||||
for (const auto & buft_mb : memory_breakdown) {
|
||||
ggml_backend_buffer_type_t buft = buft_mb.first;
|
||||
const llama_memory_breakdown_data & mb = buft_mb.second;
|
||||
if (seen_buffer_types.count(buft) == 1) {
|
||||
continue;
|
||||
}
|
||||
const std::string name = ggml_backend_buft_name(buft);
|
||||
const size_t self = mb.model + mb.context + mb.compute;
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::BUFFER_TYPE;
|
||||
row.name = ggml_backend_buft_name(buft);
|
||||
row.model = mb.model;
|
||||
row.context = mb.context;
|
||||
row.compute = mb.compute;
|
||||
rows.push_back(std::move(row));
|
||||
|
||||
seen_buffer_types.insert(buft);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
const std::vector<common_memory_breakdown_row> rows = common_memory_breakdown_rows(ctx);
|
||||
|
||||
std::vector<std::array<std::string, 9>> table_data;
|
||||
table_data.reserve(rows.size() + 1);
|
||||
|
||||
// same data as the table below, for --log-jsonl consumers
|
||||
common_json json_rows = common_json::array();
|
||||
const std::string template_header = "%s: | %s | %s %s %s %s %s %s %s |\n";
|
||||
const std::string template_gpu = "%s: | %s | %s = %s + (%s = %s + %s + %s) + %s |\n";
|
||||
const std::string template_other = "%s: | %s | %s %s %s = %s + %s + %s %s |\n";
|
||||
|
||||
table_data.push_back({template_header, "memory breakdown [MiB]", "total", "free", "self", "model", "context", "compute", "unaccounted"});
|
||||
|
||||
constexpr size_t MiB = 1024 * 1024;
|
||||
|
||||
for (const auto & row : rows) {
|
||||
const size_t self = row.model + row.context + row.compute;
|
||||
|
||||
if (row.kind == common_memory_breakdown_row::DEVICE) {
|
||||
const int64_t unaccounted = row.total - row.free - static_cast<int64_t>(self);
|
||||
|
||||
table_data.push_back({
|
||||
template_gpu,
|
||||
" - " + row.name + " (" + row.description + ")",
|
||||
std::to_string(row.total / MiB),
|
||||
std::to_string(row.free / MiB),
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(row.model / MiB),
|
||||
std::to_string(row.context / MiB),
|
||||
std::to_string(row.compute / MiB),
|
||||
std::to_string(unaccounted / static_cast<int64_t>(MiB))});
|
||||
|
||||
json_rows.push_back({
|
||||
{"kind", "device"},
|
||||
{"name", row.name},
|
||||
{"description", row.description},
|
||||
{"total", row.total / MiB},
|
||||
{"free", row.free / MiB},
|
||||
{"self", self / MiB},
|
||||
{"model", row.model / MiB},
|
||||
{"context", row.context / MiB},
|
||||
{"compute", row.compute / MiB},
|
||||
{"unaccounted", unaccounted / static_cast<int64_t>(MiB)},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
table_data.push_back({
|
||||
template_other,
|
||||
" - " + name,
|
||||
" - " + row.name,
|
||||
"", // total
|
||||
"", // free
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb.model / MiB),
|
||||
std::to_string(mb.context / MiB),
|
||||
std::to_string(mb.compute / MiB),
|
||||
std::to_string(row.model / MiB),
|
||||
std::to_string(row.context / MiB),
|
||||
std::to_string(row.compute / MiB),
|
||||
""}); // unaccounted
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "buffer_type"},
|
||||
{"name", name},
|
||||
json_rows.push_back({
|
||||
{"kind", row.kind == common_memory_breakdown_row::HOST ? "host" : "buffer_type"},
|
||||
{"name", row.name},
|
||||
{"self", self / MiB},
|
||||
{"model", mb.model / MiB},
|
||||
{"context", mb.context / MiB},
|
||||
{"compute", mb.compute / MiB},
|
||||
{"model", row.model / MiB},
|
||||
{"context", row.context / MiB},
|
||||
{"compute", row.compute / MiB},
|
||||
});
|
||||
|
||||
seen_buffer_types.insert(buft);
|
||||
}
|
||||
|
||||
for (size_t j = 1; j < table_data[0].size(); j++) {
|
||||
@@ -1081,7 +1175,7 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
|
||||
LOG_JSON("fit_memory_breakdown", common_json({
|
||||
{"unit", "MiB"},
|
||||
{"rows", rows},
|
||||
{"rows", json_rows},
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ggml.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum common_params_fit_status {
|
||||
@@ -45,6 +46,47 @@ void common_fit_print(
|
||||
|
||||
void common_memory_breakdown_print(const llama_context * ctx);
|
||||
|
||||
// one row of the memory breakdown: a device, the host or another buffer type
|
||||
struct common_memory_breakdown_row {
|
||||
enum kind_t { DEVICE, HOST, BUFFER_TYPE };
|
||||
|
||||
kind_t kind = DEVICE;
|
||||
std::string name; // device or buffer type name, "Host" for the host row
|
||||
std::string description; // device description, empty otherwise
|
||||
int64_t total = 0; // device memory in bytes, 0 unless kind == DEVICE
|
||||
int64_t free = 0; // free device memory in bytes, 0 unless kind == DEVICE
|
||||
size_t model = 0; // bytes held by this context, by kind of buffer
|
||||
size_t context = 0;
|
||||
size_t compute = 0;
|
||||
};
|
||||
|
||||
// memory a model needs at the min and max context --fit may pick
|
||||
struct common_memory_reports {
|
||||
uint32_t n_ctx_train = 0;
|
||||
uint32_t n_ctx_max = 0; // the largest context that fits the free device memory right now
|
||||
uint32_t n_ctx_min = 0; // --fit-ctx (the smallest context --fit may pick), with the placement set in params
|
||||
std::vector<common_memory_breakdown_row> info_max; // breakdown at n_ctx_max
|
||||
std::vector<common_memory_breakdown_row> info_min; // breakdown at n_ctx_min
|
||||
};
|
||||
|
||||
// rows of common_memory_breakdown_print: model devices first, then host
|
||||
std::vector<common_memory_breakdown_row> common_memory_breakdown_rows(const llama_context * ctx);
|
||||
|
||||
// load model + context with no_alloc and return the memory breakdown, params are used as-is
|
||||
// n_ctx_train and n_ctx are optional outputs; throws on failure
|
||||
std::vector<common_memory_breakdown_row> common_fit_measure(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
const llama_context_params * cparams,
|
||||
uint32_t * n_ctx_train,
|
||||
uint32_t * n_ctx,
|
||||
ggml_log_level log_level);
|
||||
|
||||
struct common_params;
|
||||
|
||||
// measure min and max without allocating, params may be modified as with --fit; throws on failure
|
||||
common_memory_reports common_measure_memory(common_params & params);
|
||||
|
||||
struct common_device_memory_data {
|
||||
int64_t total;
|
||||
int64_t free;
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// we can remove it in the future when we figure out a better way to define inputs using jinja::value
|
||||
#include "json.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#define FILENAME "jinja-caps"
|
||||
@@ -184,6 +186,10 @@ caps caps_get(jinja::program & prog) {
|
||||
{
|
||||
{"role", "user"},
|
||||
{"content", json::array({
|
||||
{
|
||||
{"type", "text"},
|
||||
{"text", content_marker}
|
||||
}
|
||||
})}
|
||||
}
|
||||
});
|
||||
@@ -584,6 +590,58 @@ caps caps_get(jinja::program & prog) {
|
||||
}
|
||||
);
|
||||
|
||||
if (result.supports_reasoning_effort) {
|
||||
JJ_DEBUG("%s\n", ">>> Running capability check: reasoning effort levels");
|
||||
|
||||
static const std::vector<std::string> candidates = { "minimal", "low", "medium", "high", "xhigh", "max" };
|
||||
|
||||
// renders a single user turn with the given effort, nothing when the template raises
|
||||
const auto render = [&](const std::string & effort) {
|
||||
std::optional<std::string> rendered;
|
||||
caps_try_execute(
|
||||
prog,
|
||||
[&]() {
|
||||
// messages
|
||||
return json::array({
|
||||
{
|
||||
{"role", "user"},
|
||||
{"content", "User message"}
|
||||
},
|
||||
});
|
||||
},
|
||||
[&](context & ctx) {
|
||||
ctx.set_val("enable_thinking", mk_val<value_bool>(true));
|
||||
caps_apply_reasoning_effort(ctx, effort);
|
||||
},
|
||||
nullptr, // tools_fn
|
||||
[&](context &, bool success, value &, value &, const std::string & output) {
|
||||
if (success) {
|
||||
rendered = output;
|
||||
}
|
||||
}
|
||||
);
|
||||
return rendered;
|
||||
};
|
||||
|
||||
// an unknown level shows the render of the template fallback, a level that renders the same is not honored
|
||||
// levels sharing a render are aliases and the highest one is kept
|
||||
const auto unknown = render("REASONING_EFFORT_UNKNOWN");
|
||||
std::vector<std::string> renders;
|
||||
for (const auto & level : candidates) {
|
||||
const auto rendered = render(level);
|
||||
if (!rendered || rendered == unknown) {
|
||||
continue;
|
||||
}
|
||||
const auto it = std::find(renders.begin(), renders.end(), *rendered);
|
||||
if (it != renders.end()) {
|
||||
result.reasoning_effort_levels[it - renders.begin()] = level;
|
||||
} else {
|
||||
renders.push_back(*rendered);
|
||||
result.reasoning_effort_levels.push_back(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JJ_DEBUG("%s\n", result.to_string().c_str());
|
||||
|
||||
return result;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace jinja {
|
||||
|
||||
@@ -19,6 +20,9 @@ struct caps {
|
||||
// supports reasoning effort levels
|
||||
bool supports_reasoning_effort = false;
|
||||
|
||||
// reasoning effort levels the template renders distinctly, from lowest to highest
|
||||
std::vector<std::string> reasoning_effort_levels;
|
||||
|
||||
// one of the 2 content capabilities must be true
|
||||
bool supports_string_content = true;
|
||||
bool supports_typed_content = false;
|
||||
|
||||
+21
-3
@@ -21,6 +21,17 @@ static bool is_type(const statement_ptr & ptr) {
|
||||
class parser {
|
||||
const std::vector<token> & tokens;
|
||||
size_t current = 0;
|
||||
int depth = 0;
|
||||
|
||||
struct depth_guard {
|
||||
parser & p;
|
||||
depth_guard(parser & p) : p(p) {
|
||||
if (++p.depth > JINJA_RECURSIVE_LIMIT) {
|
||||
throw parser_exception("Parser Error: template is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep", p.source, p.peek().pos);
|
||||
}
|
||||
}
|
||||
~depth_guard() { p.depth--; }
|
||||
};
|
||||
|
||||
std::string source; // for error reporting
|
||||
|
||||
@@ -94,6 +105,7 @@ private:
|
||||
}
|
||||
|
||||
statement_ptr parse_any() {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
switch (peek().t) {
|
||||
case token::comment:
|
||||
@@ -254,6 +266,7 @@ private:
|
||||
size_t pos0 = current;
|
||||
++current; // consume {%
|
||||
++current; // consume 'elif'
|
||||
depth_guard guard(*this);
|
||||
alternate.push_back(parse_if_statement(pos0)); // nested If
|
||||
} else if (is_statement({"else"})) {
|
||||
++current; // consume {%
|
||||
@@ -330,6 +343,7 @@ private:
|
||||
}
|
||||
|
||||
statement_ptr parse_if_expression() {
|
||||
depth_guard guard(*this);
|
||||
auto a = parse_logical_or_expression();
|
||||
if (is_identifier("if")) {
|
||||
// Ternary expression
|
||||
@@ -373,6 +387,7 @@ private:
|
||||
statement_ptr parse_logical_negation_expression() {
|
||||
// Try parse unary operators
|
||||
if (is_identifier("not")) {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
auto op = next();
|
||||
return mk_stmt<unary_expression>(start_pos, op, parse_logical_negation_expression());
|
||||
@@ -458,6 +473,7 @@ private:
|
||||
|
||||
statement_ptr parse_unary_expression() {
|
||||
if (is(token::unary_operator)) {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
auto op = next();
|
||||
return mk_stmt<unary_expression>(start_pos, op, parse_unary_expression());
|
||||
@@ -477,9 +493,11 @@ private:
|
||||
size_t start_pos = current;
|
||||
auto expr = mk_stmt<call_expression>(start_pos, std::move(callee), parse_args());
|
||||
auto member = parse_member_expression(std::move(expr)); // foo.x().y
|
||||
return is(token::open_paren)
|
||||
? parse_call_expression(std::move(member)) // foo.x()()
|
||||
: std::move(member);
|
||||
if (!is(token::open_paren)) {
|
||||
return member;
|
||||
}
|
||||
depth_guard guard(*this);
|
||||
return parse_call_expression(std::move(member)); // foo.x()()
|
||||
}
|
||||
|
||||
statements parse_args() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "value.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@@ -52,32 +53,39 @@ static void ensure_key_type_allowed(const value & val) {
|
||||
|
||||
// execute with error handling
|
||||
value statement::execute(context & ctx) const {
|
||||
std::exception_ptr pending;
|
||||
std::string message;
|
||||
try {
|
||||
return execute_impl(ctx);
|
||||
} catch (const continue_statement::signal & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const break_statement::signal & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const rethrown_exception & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const not_implemented_exception & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const std::exception & e) {
|
||||
const std::string & source = *ctx.src;
|
||||
if (source.empty()) {
|
||||
std::ostringstream oss;
|
||||
oss << "\nError executing " << type() << " at position " << pos << ": " << e.what();
|
||||
throw rethrown_exception(oss.str());
|
||||
message = oss.str();
|
||||
} else {
|
||||
std::ostringstream oss;
|
||||
oss << "\n------------\n";
|
||||
oss << "While executing " << type() << " at " << get_line_col(source, pos) << " in source:\n";
|
||||
oss << peak_source(source, pos) << "\n";
|
||||
oss << "Error: " << e.what();
|
||||
// throw as another exception to avoid repeated formatting
|
||||
throw rethrown_exception(oss.str());
|
||||
message = oss.str();
|
||||
}
|
||||
}
|
||||
// throw outside the catch: MSVC keeps the unwound frames on the stack while a catch block runs
|
||||
if (pending) {
|
||||
std::rethrow_exception(pending);
|
||||
}
|
||||
// throw as another exception to avoid repeated formatting
|
||||
throw rethrown_exception(message);
|
||||
}
|
||||
|
||||
value identifier::execute_impl(context & ctx) const {
|
||||
@@ -747,7 +755,11 @@ value macro_statement::execute_impl(context & ctx) const {
|
||||
std::string name = cast_stmt<identifier>(this->name)->val;
|
||||
|
||||
const func_handler func = [this, name](const func_args & args) -> value {
|
||||
if (args.ctx.macro_depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("macro calls are nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
context macro_ctx(args.ctx); // new scope for macro execution
|
||||
macro_ctx.macro_depth = args.ctx.macro_depth + 1;
|
||||
|
||||
bind_parameters(name, this->args, args, macro_ctx);
|
||||
|
||||
@@ -778,7 +790,11 @@ value call_statement::execute_impl(context & ctx) const {
|
||||
context caller_ctx(ctx); // new scope for caller execution
|
||||
|
||||
const func_handler func = [this, caller_ctx = std::move(caller_ctx)](const func_args & args) -> value {
|
||||
if (args.ctx.macro_depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("macro calls are nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
context block_ctx(caller_ctx); // new scope for block execution
|
||||
block_ctx.macro_depth = args.ctx.macro_depth + 1;
|
||||
|
||||
bind_parameters("caller", this->caller_args, args, block_ctx);
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ struct context {
|
||||
|
||||
bool is_get_stats = false; // whether to collect stats
|
||||
|
||||
int macro_depth = 0;
|
||||
|
||||
visitor_fn visitor;
|
||||
|
||||
// src is optional, used for error reporting
|
||||
@@ -78,6 +80,7 @@ struct context {
|
||||
src(parent.src),
|
||||
current_time(parent.current_time),
|
||||
is_get_stats(parent.is_get_stats),
|
||||
macro_depth(parent.macro_depth),
|
||||
// inherit variables (for example, when entering a new scope)
|
||||
env(mk_val<value_object>(*parent.env)) {}
|
||||
|
||||
@@ -710,7 +713,7 @@ struct rethrown_exception : public std::exception {
|
||||
|
||||
//////////////////////
|
||||
|
||||
static void gather_string_parts_recursive(const value & val, value_string & parts) {
|
||||
static void gather_string_parts_recursive(const value & val, value_string & parts, int depth = 0) {
|
||||
// TODO: probably allow print value_none as "None" string? currently this breaks some templates
|
||||
if (is_val<value_string>(val)) {
|
||||
const auto & str_val = cast_val<value_string>(val)->val_str;
|
||||
@@ -718,10 +721,12 @@ static void gather_string_parts_recursive(const value & val, value_string & part
|
||||
} else if (is_val<value_int>(val) || is_val<value_float>(val) || is_val<value_bool>(val)) {
|
||||
std::string str_val = val->as_string().str();
|
||||
parts->val_str.append(str_val);
|
||||
} else if (is_val<value_array>(val) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (is_val<value_array>(val)) {
|
||||
auto items = cast_val<value_array>(val)->as_array();
|
||||
for (const auto & item : items) {
|
||||
gather_string_parts_recursive(item, parts);
|
||||
gather_string_parts_recursive(item, parts, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#define JINJA_RECURSIVE_LIMIT 128
|
||||
|
||||
namespace jinja {
|
||||
|
||||
static void string_replace_all(std::string & s, const std::string & search, const std::string & replace) {
|
||||
|
||||
+11
-8
@@ -1441,8 +1441,6 @@ const func_builtins & value_undefined_t::get_builtins() const {
|
||||
|
||||
|
||||
// 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>();
|
||||
@@ -1458,8 +1456,8 @@ static value from_json(const common_json & j, bool mark_input, int depth = 0) {
|
||||
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() || j.is_object()) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (j.is_array()) {
|
||||
auto arr = mk_val<value_array>();
|
||||
for (const auto & item : j) {
|
||||
@@ -1664,6 +1662,8 @@ static void value_to_json_internal(std::ostringstream & oss, const value & val,
|
||||
}
|
||||
}
|
||||
oss << "\"";
|
||||
} else if ((is_val<value_array>(val) || is_val<value_object>(val)) && curr_lvl >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (is_val<value_array>(val)) {
|
||||
const auto & arr = val->as_array();
|
||||
oss << "[";
|
||||
@@ -1729,17 +1729,20 @@ std::string value_to_string_repr(const value & val) {
|
||||
}
|
||||
|
||||
// stats utility
|
||||
void value_t::stats_t::mark_used(value & val, bool deep) {
|
||||
void value_t::stats_t::mark_used(value & val, bool deep, int depth) {
|
||||
val->stats.used = true;
|
||||
if (deep && (is_val<value_array>(val) || is_val<value_object>(val)) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (deep) {
|
||||
if (is_val<value_array>(val)) {
|
||||
for (auto & item : val->val_arr) {
|
||||
mark_used(item, deep);
|
||||
mark_used(item, deep, depth + 1);
|
||||
}
|
||||
} else if (is_val<value_object>(val)) {
|
||||
for (auto & pair : val->val_obj) {
|
||||
mark_used(pair.first, deep);
|
||||
mark_used(pair.second, deep);
|
||||
mark_used(pair.first, deep, depth + 1);
|
||||
mark_used(pair.second, deep, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ struct value_t {
|
||||
// ops can be builtin calls or operators: "array_access", "object_access"
|
||||
std::set<std::string> ops;
|
||||
// utility to recursively mark value and its children as used
|
||||
static void mark_used(value & val, bool deep = false);
|
||||
static void mark_used(value & val, bool deep = false, int depth = 0);
|
||||
} stats;
|
||||
|
||||
value_t() = default;
|
||||
|
||||
@@ -272,15 +272,12 @@ static bool is_reserved_name(const std::string & name) {
|
||||
return RESERVED_NAMES.find(name) != RESERVED_NAMES.end();
|
||||
}
|
||||
|
||||
static std::regex INVALID_RULE_CHARS_RE("[^a-zA-Z0-9-]+");
|
||||
static std::regex GRAMMAR_LITERAL_ESCAPE_RE("[\r\n\"\\\\]");
|
||||
static std::regex GRAMMAR_RANGE_LITERAL_ESCAPE_RE("[\r\n\"\\]\\-\\\\]");
|
||||
static std::unordered_map<char, std::string> GRAMMAR_LITERAL_ESCAPES = {
|
||||
{'\r', "\\r"}, {'\n', "\\n"}, {'"', "\\\""}, {'-', "\\-"}, {']', "\\]"}, {'\\', "\\\\"}
|
||||
};
|
||||
|
||||
static const int MAX_PATTERN_DEPTH = 100;
|
||||
|
||||
static std::unordered_set<char> NON_LITERAL_SET = {'|', '.', '(', ')', '[', ']', '{', '}', '*', '+', '?', '^', '$'};
|
||||
static std::unordered_set<char> ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS = {'^', '$', '.', '[', ']', '(', ')', '|', '{', '}', '*', '+', '?'};
|
||||
|
||||
@@ -312,6 +309,22 @@ static std::string format_literal(const std::string & literal) {
|
||||
|
||||
std::string gbnf_format_literal(const std::string & literal) { return format_literal(literal); }
|
||||
|
||||
// same as std::regex_replace with [^a-zA-Z0-9-]+ -> "-", no std::regex: libstdc++ recurses per char
|
||||
std::string gbnf_format_rule_name(const std::string & name) {
|
||||
std::string out;
|
||||
bool in_invalid = false;
|
||||
for (char c : name) {
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-') {
|
||||
out += c;
|
||||
in_invalid = false;
|
||||
} else if (!in_invalid) {
|
||||
out += '-';
|
||||
in_invalid = true;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static size_t gbnf_escape_length(const std::string & pattern, size_t pos) {
|
||||
if (pos + 1 >= pattern.length() || pattern[pos] != '\\') {
|
||||
return 0;
|
||||
@@ -347,6 +360,17 @@ private:
|
||||
std::unordered_set<std::string> _refs_being_resolved;
|
||||
std::vector<std::string> _errors;
|
||||
std::vector<std::string> _warnings;
|
||||
int _depth = 0;
|
||||
|
||||
struct depth_guard {
|
||||
int & depth;
|
||||
depth_guard(int & depth) : depth(depth) {
|
||||
if (++depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
}
|
||||
~depth_guard() { depth--; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static const T & as(const common_chat_schema & node) {
|
||||
@@ -354,7 +378,7 @@ private:
|
||||
}
|
||||
|
||||
std::string _add_rule(const std::string & name, const std::string & rule) {
|
||||
std::string esc_name = regex_replace(name, INVALID_RULE_CHARS_RE, "-");
|
||||
std::string esc_name = gbnf_format_rule_name(name);
|
||||
if (_rules.find(esc_name) == _rules.end() || _rules[esc_name] == rule) {
|
||||
_rules[esc_name] = rule;
|
||||
return esc_name;
|
||||
@@ -482,7 +506,7 @@ private:
|
||||
}
|
||||
}
|
||||
paren_depth++;
|
||||
if (paren_depth > MAX_PATTERN_DEPTH) {
|
||||
if (paren_depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw unsupported_pattern("pattern nesting too deep");
|
||||
}
|
||||
seq.emplace_back("(" + to_rule(transform()) + ")", false);
|
||||
@@ -644,7 +668,11 @@ private:
|
||||
std::string char_rule = _add_primitive("char", PRIMITIVE_RULES.at("char"));
|
||||
std::ostringstream out;
|
||||
out << "[\"] ( ";
|
||||
std::function<void(size_t)> visit = [&](size_t idx) {
|
||||
// one level per char of the longest name
|
||||
std::function<void(size_t, int)> visit = [&](size_t idx, int depth) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema property name is longer than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " chars");
|
||||
}
|
||||
const auto & node = trie.nodes[idx];
|
||||
std::string rejects;
|
||||
auto first = true;
|
||||
@@ -659,7 +687,7 @@ private:
|
||||
out << "[" << c << "]";
|
||||
if (!trie.nodes[child].children.empty()) {
|
||||
out << " (";
|
||||
visit(child);
|
||||
visit(child, depth + 1);
|
||||
out << ")";
|
||||
} else {
|
||||
out << " " << char_rule << "+";
|
||||
@@ -669,7 +697,7 @@ private:
|
||||
out << " | [^\"" << rejects << "] " << char_rule << "*";
|
||||
}
|
||||
};
|
||||
visit(0);
|
||||
visit(0, 0);
|
||||
|
||||
out << " )";
|
||||
if (trie.nodes[0].pattern < 0) {
|
||||
@@ -682,8 +710,7 @@ private:
|
||||
std::string _resolve_ref(const common_chat_schema_ref & schema) {
|
||||
auto it = schema.ref.find('#');
|
||||
std::string ref_fragment = it != std::string::npos ? schema.ref.substr(it + 1) : schema.ref;
|
||||
static const std::regex nonalphanumeric_regex(R"([^a-zA-Z0-9-]+)");
|
||||
std::string ref_name = "ref" + std::regex_replace(ref_fragment, nonalphanumeric_regex, "-");
|
||||
std::string ref_name = "ref" + gbnf_format_rule_name(ref_fragment);
|
||||
if (_rules.find(ref_name) == _rules.end() && _refs_being_resolved.find(schema.ref) == _refs_being_resolved.end()) {
|
||||
if (!schema.target) {
|
||||
_errors.push_back("Unresolved $ref " + schema.ref);
|
||||
@@ -833,10 +860,13 @@ public:
|
||||
std::unordered_set<std::string> required;
|
||||
std::vector<std::pair<std::string, const common_chat_schema *>> properties;
|
||||
std::map<std::string, size_t> enum_values;
|
||||
std::function<void(const common_chat_schema &, bool)> add_component = [&](const common_chat_schema & comp, bool is_required) {
|
||||
std::function<void(const common_chat_schema &, bool, int)> add_component = [&](const common_chat_schema & comp, bool is_required, int depth) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (comp.kind() == common_chat_schema::KIND_REF) {
|
||||
if (const auto * target = as<common_chat_schema_ref>(comp).target) {
|
||||
add_component(*target, is_required);
|
||||
add_component(*target, is_required, depth + 1);
|
||||
}
|
||||
} else if (comp.kind() == common_chat_schema::KIND_OBJECT) {
|
||||
for (const auto & prop : as<common_chat_schema_object>(comp).properties) {
|
||||
@@ -854,10 +884,10 @@ public:
|
||||
for (const auto & child : schema.children) {
|
||||
if (child->kind() == common_chat_schema::KIND_ANY_OF) {
|
||||
for (const auto & alt : as<common_chat_schema_any_of>(*child).children) {
|
||||
add_component(*alt, false);
|
||||
add_component(*alt, false, 0);
|
||||
}
|
||||
} else {
|
||||
add_component(*child, true);
|
||||
add_component(*child, true, 0);
|
||||
}
|
||||
}
|
||||
if (!enum_values.empty()) {
|
||||
@@ -875,6 +905,8 @@ public:
|
||||
}
|
||||
|
||||
std::string visit(const common_chat_schema & schema, const std::string & name) {
|
||||
// the $ref targets are visited too, a $ref chain is not bounded by the builder
|
||||
depth_guard guard(_depth);
|
||||
std::string rule_name = is_reserved_name(name) ? name + "-" : name.empty() ? "root" : name;
|
||||
std::string sub_name = name + (name.empty() ? "" : "-");
|
||||
|
||||
|
||||
@@ -19,5 +19,6 @@ struct common_grammar_options {
|
||||
};
|
||||
|
||||
std::string gbnf_format_literal(const std::string & literal);
|
||||
std::string gbnf_format_rule_name(const std::string & name);
|
||||
|
||||
std::string build_grammar(const std::function<void(const common_grammar_builder &)> & cb, const common_grammar_options & options = {});
|
||||
|
||||
+30
-20
@@ -9,6 +9,18 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
struct depth_guard {
|
||||
int & depth;
|
||||
depth_guard(int & depth) : depth(depth) {
|
||||
if (++depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
}
|
||||
~depth_guard() { depth--; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class common_chat_schema_builder {
|
||||
const common_json & root_;
|
||||
common_chat_schema_document & doc_;
|
||||
@@ -224,18 +236,11 @@ 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;
|
||||
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;
|
||||
depth_guard guard(depth_);
|
||||
return build_node_impl(schema, path);
|
||||
}
|
||||
|
||||
common_chat_schema_ptr build_node_impl(const common_json & schema, const std::string & path) {
|
||||
@@ -379,7 +384,9 @@ static common_chat_schema::value_type json_type(const common_json & value) {
|
||||
return common_chat_schema::TYPE_OBJECT;
|
||||
}
|
||||
|
||||
static common_chat_schema::type_set value_types_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited) {
|
||||
// depth counts the $ref, anyOf and allOf hops, a $ref chain is not bounded by the builder
|
||||
static common_chat_schema::type_set value_types_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited, int & depth) {
|
||||
depth_guard guard(depth);
|
||||
switch (s.kind()) {
|
||||
case common_chat_schema::KIND_ANY:
|
||||
return common_chat_schema::type_set::all();
|
||||
@@ -413,21 +420,21 @@ static common_chat_schema::type_set value_types_impl(const common_chat_schema &
|
||||
// a cycle contributes no type, to be safe
|
||||
return {};
|
||||
}
|
||||
auto types = value_types_impl(*target, visited);
|
||||
auto types = value_types_impl(*target, visited, depth);
|
||||
visited.erase(target);
|
||||
return types;
|
||||
}
|
||||
case common_chat_schema::KIND_ANY_OF: {
|
||||
common_chat_schema::type_set types;
|
||||
for (const auto & child : static_cast<const common_chat_schema_any_of &>(s).children) {
|
||||
types |= value_types_impl(*child, visited);
|
||||
types |= value_types_impl(*child, visited, depth);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
case common_chat_schema::KIND_ALL_OF: {
|
||||
auto types = common_chat_schema::type_set::all();
|
||||
for (const auto & child : static_cast<const common_chat_schema_all_of &>(s).children) {
|
||||
types &= value_types_impl(*child, visited);
|
||||
types &= value_types_impl(*child, visited, depth);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
@@ -437,10 +444,12 @@ static common_chat_schema::type_set value_types_impl(const common_chat_schema &
|
||||
|
||||
common_chat_schema::type_set common_chat_schema::value_types() const {
|
||||
std::unordered_set<const common_chat_schema *> visited;
|
||||
return value_types_impl(*this, visited);
|
||||
int depth = 0;
|
||||
return value_types_impl(*this, visited, depth);
|
||||
}
|
||||
|
||||
static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited) {
|
||||
static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited, int & depth) {
|
||||
depth_guard guard(depth);
|
||||
switch (s.kind()) {
|
||||
case common_chat_schema::KIND_STRING:
|
||||
return true;
|
||||
@@ -459,13 +468,13 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
if (!target || !visited.insert(target).second) {
|
||||
return false;
|
||||
}
|
||||
bool result = may_be_string_impl(*target, visited);
|
||||
bool result = may_be_string_impl(*target, visited, depth);
|
||||
visited.erase(target);
|
||||
return result;
|
||||
}
|
||||
case common_chat_schema::KIND_ANY_OF:
|
||||
for (const auto & child : static_cast<const common_chat_schema_any_of &>(s).children) {
|
||||
if (may_be_string_impl(*child, visited)) {
|
||||
if (may_be_string_impl(*child, visited, depth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -477,7 +486,7 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
if (child->kind() == common_chat_schema::KIND_ANY) {
|
||||
continue;
|
||||
}
|
||||
if (!may_be_string_impl(*child, visited)) {
|
||||
if (!may_be_string_impl(*child, visited, depth)) {
|
||||
return false;
|
||||
}
|
||||
any_string = true;
|
||||
@@ -491,7 +500,8 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
|
||||
bool common_chat_schema::may_be_string() const {
|
||||
std::unordered_set<const common_chat_schema *> visited;
|
||||
return may_be_string_impl(*this, visited);
|
||||
int depth = 0;
|
||||
return may_be_string_impl(*this, visited, depth);
|
||||
}
|
||||
|
||||
const char * common_chat_schema::kind_name(node_kind kind) {
|
||||
|
||||
+27
-15
@@ -10,10 +10,12 @@
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
// The parser can run with a lower limit than COMMON_RECURSIVE_LIMIT
|
||||
static constexpr int RULE_RECURSIVE_LIMIT = 64;
|
||||
|
||||
// Trick to catch missing branches
|
||||
template <typename T>
|
||||
inline constexpr bool is_always_false_v = false;
|
||||
@@ -186,13 +188,17 @@ std::string common_peg_ast_node::sanitized_text() const {
|
||||
}
|
||||
|
||||
void common_peg_ast_arena::visit(common_peg_ast_id id, const common_peg_ast_visitor & visitor) const {
|
||||
if (id == COMMON_PEG_INVALID_AST_ID) {
|
||||
return;
|
||||
}
|
||||
const auto & node = get(id);
|
||||
visitor(node);
|
||||
for (const auto & child : node.children) {
|
||||
visit(child, visitor);
|
||||
// no recursion: the AST is several nodes deep per level of the input
|
||||
std::vector<common_peg_ast_id> stack = { id };
|
||||
while (!stack.empty()) {
|
||||
const auto cur = stack.back();
|
||||
stack.pop_back();
|
||||
if (cur == COMMON_PEG_INVALID_AST_ID) {
|
||||
continue;
|
||||
}
|
||||
const auto & node = get(cur);
|
||||
visitor(node);
|
||||
stack.insert(stack.end(), node.children.rbegin(), node.children.rend());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,8 +742,15 @@ struct parser_executor {
|
||||
}
|
||||
|
||||
common_peg_parse_result operator()(const common_peg_rule_parser & p) {
|
||||
// only rules recurse, a recursive rule is entered once per input level
|
||||
if (ctx.rule_depth[p.child] >= RULE_RECURSIVE_LIMIT) {
|
||||
throw common_peg_recursion_error("input is nested more than " + std::to_string(RULE_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
|
||||
// Parse the child
|
||||
ctx.rule_depth[p.child]++;
|
||||
auto result = arena.parse(p.child, ctx, start_pos);
|
||||
ctx.rule_depth[p.child]--;
|
||||
|
||||
if (!result.fail()) {
|
||||
std::string_view text;
|
||||
@@ -819,6 +832,10 @@ common_peg_parse_result common_peg_arena::parse(common_peg_parse_context & ctx,
|
||||
if (root_ == COMMON_PEG_INVALID_PARSER_ID) {
|
||||
throw std::runtime_error("No root parser set");
|
||||
}
|
||||
|
||||
// Initialize rule depth for all parsers
|
||||
ctx.rule_depth.assign(parsers_.size(), 0);
|
||||
|
||||
return parse(root_, ctx, start);
|
||||
}
|
||||
|
||||
@@ -1069,11 +1086,6 @@ common_peg_parser operator|(const std::string & str, const common_peg_parser & p
|
||||
return operator|(str.c_str(), p);
|
||||
}
|
||||
|
||||
static std::string rule_name(const std::string & name) {
|
||||
static const std::regex invalid_rule_chars_re("[^a-zA-Z0-9-]+");
|
||||
return std::regex_replace(name, invalid_rule_chars_re, "-");
|
||||
}
|
||||
|
||||
common_peg_parser_builder::common_peg_parser_builder() {}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::sequence(const std::vector<common_peg_parser_id> & parsers) {
|
||||
@@ -1155,14 +1167,14 @@ common_peg_parser common_peg_parser_builder::schema(const common_peg_parser & p,
|
||||
}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::rule(const std::string & name, const common_peg_parser & p, bool trigger) {
|
||||
auto clean_name = rule_name(name);
|
||||
auto clean_name = gbnf_format_rule_name(name);
|
||||
auto rule_id = arena_.add_parser(common_peg_rule_parser{clean_name, p.id(), trigger});
|
||||
arena_.add_rule(clean_name, rule_id);
|
||||
return ref(clean_name);
|
||||
}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::rule(const std::string & name, const std::function<common_peg_parser()> & builder_fn, bool trigger) {
|
||||
auto clean_name = rule_name(name);
|
||||
auto clean_name = gbnf_format_rule_name(name);
|
||||
if (arena_.has_rule(clean_name)) {
|
||||
return ref(clean_name);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -182,6 +183,10 @@ inline common_peg_parse_flags operator~(common_peg_parse_flags a) {
|
||||
return static_cast<common_peg_parse_flags>(~int(a));
|
||||
}
|
||||
|
||||
struct common_peg_recursion_error : std::invalid_argument {
|
||||
using std::invalid_argument::invalid_argument;
|
||||
};
|
||||
|
||||
struct common_peg_parse_context {
|
||||
std::string input; // [h, e, l, l, o, _, w, o, r, l, d]
|
||||
std::vector<llama_token> tokens; // [id, -1, -1, -1, -1, id, -1, -1, -1, -1, -1]
|
||||
@@ -189,6 +194,7 @@ struct common_peg_parse_context {
|
||||
common_peg_ast_arena ast;
|
||||
|
||||
int parse_depth;
|
||||
std::vector<int> rule_depth; // index: rule child id
|
||||
|
||||
common_peg_parse_context(common_peg_parse_flags flags = COMMON_PEG_PARSE_FLAG_NONE)
|
||||
: flags(flags), parse_depth(0) {}
|
||||
|
||||
@@ -172,6 +172,10 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"D1OmniModel": "lfm2",
|
||||
"Lfm2Model": "lfm2",
|
||||
"Lfm2MoeForCausalLM": "lfm2",
|
||||
"Lfm3D1Model": "lfm3",
|
||||
"Lfm3ForCausalLM": "lfm3",
|
||||
"Lfm3OmniForConditionalGeneration": "lfm3",
|
||||
"Lfm3VlForConditionalGeneration": "lfm3",
|
||||
"Llama4ForCausalLM": "llama",
|
||||
"Llama4ForConditionalGeneration": "llama",
|
||||
"LlamaBidirectionalModel": "llama",
|
||||
@@ -197,6 +201,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MiniCPMForCausalLM": "minicpm",
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMOCRForSequenceClassification": "qwen",
|
||||
"MiniCPMOcrForConditionalGeneration": "minicpm",
|
||||
"MiniMaxText01ForCausalLM": "minimax",
|
||||
"MiniMaxM1ForCausalLM": "minimax",
|
||||
@@ -357,6 +362,9 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"Lfm2AudioForConditionalGeneration": "lfm2",
|
||||
"D1OmniModel": "lfm2",
|
||||
"Lfm2VlForConditionalGeneration": "lfm2",
|
||||
"Lfm3AudioForConditionalGeneration": "lfm3",
|
||||
"Lfm3OmniForConditionalGeneration": "lfm3",
|
||||
"Lfm3VlForConditionalGeneration": "lfm3",
|
||||
"LightOnOCRForConditionalGeneration": "lighton_ocr",
|
||||
"Llama4ForConditionalGeneration": "llama4",
|
||||
"LlavaForConditionalGeneration": "llava",
|
||||
@@ -365,6 +373,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"MiniMaxM3SparseForConditionalGeneration": "minimax",
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMOCRForSequenceClassification": "qwen3vl",
|
||||
"MiniCPMOcrForConditionalGeneration": "minicpm",
|
||||
"Mistral3ForConditionalGeneration": "llava",
|
||||
"Mistral3Model": "llava",
|
||||
|
||||
@@ -803,9 +803,6 @@ class DeepseekV4Model(TextModel):
|
||||
self._dsv4_mxfp4_generated = True
|
||||
return ()
|
||||
|
||||
def _format_dsv4_tensor_name(self, key: gguf.MODEL_TENSOR, bid: int | None, suffix: str = ".weight") -> str:
|
||||
return self.format_tensor_name(key, bid, suffix)
|
||||
|
||||
def _map_dsv4_tensor_name(self, name: str, bid: int | None) -> tuple[gguf.MODEL_TENSOR, str]:
|
||||
root_map: dict[str, tuple[gguf.MODEL_TENSOR, str]] = {
|
||||
"embed.weight": (gguf.MODEL_TENSOR.TOKEN_EMBD, ".weight"),
|
||||
@@ -879,17 +876,17 @@ class DeepseekV4Model(TextModel):
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if re.match(r"layers\.\d+\.ffn\.experts\.\d+\.w[123]\.(weight|scale)$", name):
|
||||
return []
|
||||
return
|
||||
|
||||
# hash layers route text tokens via tid2eid and image tokens via bias_vl; gate.bias is unused
|
||||
if name.endswith(".ffn.gate.bias") and bid is not None and bid < self.hparams["num_hash_layers"]:
|
||||
return []
|
||||
return
|
||||
|
||||
tensor_key, suffix = self._map_dsv4_tensor_name(name, bid)
|
||||
if tensor_key == gguf.MODEL_TENSOR.FFN_GATE_TID2EID:
|
||||
return []
|
||||
return
|
||||
|
||||
return [(self._format_dsv4_tensor_name(tensor_key, bid, suffix), data_torch)]
|
||||
yield self.format_tensor_name(tensor_key, bid, suffix), data_torch
|
||||
|
||||
def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool:
|
||||
del bid # unused
|
||||
|
||||
+10
-13
@@ -145,12 +145,6 @@ class DeepseekV41Model(DeepseekV4Model):
|
||||
# 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
|
||||
@@ -282,12 +276,13 @@ class DeepseekV41Model(DeepseekV4Model):
|
||||
|
||||
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
|
||||
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)
|
||||
yield self.format_tensor_name(gguf.MODEL_TENSOR.ENGRAM_GATE, bid), data_torch.float() * k().float()
|
||||
return
|
||||
yield from 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:
|
||||
@@ -411,13 +406,15 @@ class DeepseekV41DSparkModel(DeepseekV41Model):
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.endswith(".ffn.gate.bias_vl"):
|
||||
return []
|
||||
return
|
||||
if name in self._DSPARK_ROOT:
|
||||
return [(self.format_tensor_name(self._DSPARK_ROOT[name], None), data_torch)]
|
||||
yield self.format_tensor_name(self._DSPARK_ROOT[name], None), data_torch
|
||||
return
|
||||
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)
|
||||
yield self.format_tensor_name(self._DSPARK_ROOT[m.group(1)], None), data_torch
|
||||
return
|
||||
yield from 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":
|
||||
|
||||
+21
-14
@@ -15,25 +15,30 @@ from .base import MmprojModel, ModelBase, TextModel, gguf, jinja_str_or_json, lo
|
||||
from .gemma import ConformerAudioModel
|
||||
|
||||
|
||||
def lfm2_feed_forward_length(hparams: dict[str, Any]) -> int:
|
||||
ff_dim = hparams.get("block_ff_dim", hparams.get("intermediate_size"))
|
||||
assert ff_dim is not None
|
||||
auto_adjust_ff_dim = hparams["block_auto_adjust_ff_dim"]
|
||||
ffn_dim_multiplier = hparams["block_ffn_dim_multiplier"]
|
||||
multiple_of = hparams["block_multiple_of"]
|
||||
|
||||
if auto_adjust_ff_dim:
|
||||
ff_dim = int(2 * ff_dim / 3)
|
||||
# custom dim factor multiplier
|
||||
if ffn_dim_multiplier is not None:
|
||||
ff_dim = int(ffn_dim_multiplier * ff_dim)
|
||||
ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of)
|
||||
|
||||
return ff_dim
|
||||
|
||||
|
||||
@ModelBase.register("Lfm2ForCausalLM", "LFM2ForCausalLM")
|
||||
@ModelBase.example("LiquidAI/LFM2-1.2B", "LiquidAI/LFM2.5-350M")
|
||||
class LFM2Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.LFM2
|
||||
|
||||
def _add_feed_forward_length(self):
|
||||
ff_dim = self.find_hparam(["block_ff_dim", "intermediate_size"])
|
||||
auto_adjust_ff_dim = self.hparams["block_auto_adjust_ff_dim"]
|
||||
ffn_dim_multiplier = self.hparams["block_ffn_dim_multiplier"]
|
||||
multiple_of = self.hparams["block_multiple_of"]
|
||||
|
||||
if auto_adjust_ff_dim:
|
||||
ff_dim = int(2 * ff_dim / 3)
|
||||
# custom dim factor multiplier
|
||||
if ffn_dim_multiplier is not None:
|
||||
ff_dim = int(ffn_dim_multiplier * ff_dim)
|
||||
ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of)
|
||||
|
||||
self.gguf_writer.add_feed_forward_length(ff_dim)
|
||||
self.gguf_writer.add_feed_forward_length(lfm2_feed_forward_length(self.hparams))
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
# set num_key_value_heads only for attention layers
|
||||
@@ -72,7 +77,9 @@ def _is_d1_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
return json.load(f).get("auto_map", {}).get("AutoModel", "").endswith(".D1Model")
|
||||
config = json.load(f)
|
||||
# d1 on LFM3 is converted by conversion/lfm3.py
|
||||
return config.get("auto_map", {}).get("AutoModel", "").endswith(".D1Model") and not config.get("model_type", "").startswith("lfm3")
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_d1_checkpoint)
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
import torch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import MmprojModel, ModelBase, TextModel, gguf, logger
|
||||
from .lfm2 import D1Model, lfm2_feed_forward_length
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3ForCausalLM", "Lfm3VlForConditionalGeneration", "Lfm3OmniForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-1.7B", "LiquidAI/LFM3-1.7B-Base")
|
||||
class LFM3Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.LFM3
|
||||
|
||||
# LFM3-Audio/Omni: encoder, projector and audio-output heads live next to the text model
|
||||
_media_prefixes = ("encoder.", "mlp.", "codebook_embedding.", "depth_embeddings.", "depth_linear.", "depthformer.",
|
||||
"mask_embedding", "model.codebook_embedding.", "model.depth_decoder.")
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
|
||||
if name.startswith(cls._media_prefixes):
|
||||
return None
|
||||
|
||||
name = name.removeprefix("lfm.")
|
||||
|
||||
return super().filter_tensors((name, gen))
|
||||
|
||||
# LFM3-Audio live ASR prompt of the model card, the audio follows in the assistant turn
|
||||
_asr_template = (
|
||||
"{{- bos_token -}}"
|
||||
"{{- '<|im_start|>system\\nPerform live ASR.<|im_end|>\\n' -}}"
|
||||
"{%- if add_generation_prompt -%}{{- '<|im_start|>assistant\\n' -}}{%- endif -%}"
|
||||
)
|
||||
|
||||
# TTS prompt of the model card: a language code is looked up, anything else is taken as the language name
|
||||
_tts_langs = {
|
||||
"ar": "Arabic", "da": "Danish", "de": "German", "el": "Greek", "en": "American English", "en-us": "American English",
|
||||
"en-gb": "British English", "fi": "Finnish", "fr": "French", "he": "Hebrew", "it": "Italian", "ja": "Japanese",
|
||||
"ko": "Korean", "nl": "Dutch", "pl": "Polish", "pt": "Portuguese", "es": "Spanish", "sv": "Swedish", "tr": "Turkish",
|
||||
"zh": "Chinese",
|
||||
}
|
||||
|
||||
# "language" and "voice" are optional, the text to speak is the first message
|
||||
def _tts_template(self, think: bool) -> str:
|
||||
return (
|
||||
"{%- set names = " + json.dumps(self._tts_langs) + " -%}"
|
||||
"{%- set code = (language | lower) if language is defined and language else 'en' -%}"
|
||||
"{%- set lang = names[code] if code in names else language -%}"
|
||||
"{%- set v = (voice | lower) if voice is defined and voice and (voice | lower) in ['female', 'male'] else 'female' -%}"
|
||||
"{{- bos_token -}}"
|
||||
"{{- '<|im_start|>system\\nPerform TTS in ' + lang + ' using a ' + v + ' voice.<|im_end|>\\n' -}}"
|
||||
"{{- '<|im_start|>user\\n' + messages[0]['content'] + '<|im_end|>\\n' -}}"
|
||||
"{{- '<|im_start|>assistant\\n" + ("<think></think>" if think else "") + "<|audio_start|>' -}}"
|
||||
)
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
# hparams hold the merged text config, the checkpoint's own model_type is in config.json
|
||||
with open(self.dir_model / "config.json", encoding="utf-8") as f:
|
||||
model_type = json.load(f).get("model_type")
|
||||
is_audio = model_type == "lfm3_audio"
|
||||
is_omni = model_type == "lfm3_omni"
|
||||
templates = []
|
||||
if is_audio:
|
||||
templates.append({"name": "asr", "template": self._asr_template})
|
||||
if is_audio or is_omni:
|
||||
# LFM3-Omni opens every assistant turn with an empty think block
|
||||
templates.append({"name": "tts", "template": self._tts_template(think=is_omni)})
|
||||
if templates:
|
||||
self.gguf_writer.add_chat_template(templates)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
layer_types = self.hparams["layer_types"]
|
||||
n_kv_heads = self.hparams["num_key_value_heads"]
|
||||
|
||||
# set num_key_value_heads only for attention layers
|
||||
self.hparams["num_key_value_heads"] = [
|
||||
n_kv_heads if lt != "conv" else 0
|
||||
for lt in layer_types
|
||||
]
|
||||
|
||||
super().set_gguf_parameters()
|
||||
|
||||
self.gguf_writer.add_rope_dimension_count(self.hparams["head_dim"])
|
||||
|
||||
self.gguf_writer.add_sliding_window_pattern([lt == "sliding_attention" for lt in layer_types])
|
||||
self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
|
||||
|
||||
self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"])
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
# Partial RoPE is represented by masking trailing frequency pairs.
|
||||
# rope_parameters is keyed by layer type; only full_attention layers use partial RoPE.
|
||||
rope_parameters = self.hparams["rope_parameters"]["full_attention"]
|
||||
partial = rope_parameters.get("partial_rotary_factor", 1.0)
|
||||
head_dim = self.hparams["head_dim"]
|
||||
rope_angles = int(partial * head_dim) // 2
|
||||
n_pairs = head_dim // 2
|
||||
assert 0 < rope_angles <= n_pairs
|
||||
values = [1.0] * rope_angles + [1e30] * (n_pairs - rope_angles)
|
||||
rope_freqs = torch.tensor(values, dtype=torch.float32)
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), rope_freqs)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# conv op requires 2d tensor
|
||||
if "conv.conv" in name:
|
||||
data_torch = data_torch.squeeze(1)
|
||||
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
# d1 on LFM3-VL / LFM3-Omni: the stock weights, the d1 prompt read at its last token
|
||||
_D1_TYPES = {"lfm3_vl": gguf.DecisionType.LFM3_D1, "lfm3_omni": gguf.DecisionType.LFM3_D1_OMNI}
|
||||
|
||||
|
||||
def _is_lfm3_d1_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
return config.get("architectures") == ["D1Model"] and config.get("model_type") in _D1_TYPES
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_lfm3_d1_checkpoint)
|
||||
def _load_lfm3_d1_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
logger.info("gguf: detected LFM3 d1 checkpoint")
|
||||
hparams = ModelBase.load_hparams(dir_model, False, guess=False)
|
||||
hparams["d1_family"] = hparams["model_type"]
|
||||
# the mmproj is the base model's, the text model writes the decision
|
||||
hparams["architectures"] = ["Lfm3VlForConditionalGeneration" if hparams["model_type"] == "lfm3_vl" else "Lfm3OmniForConditionalGeneration"]
|
||||
hparams["text_config"]["architectures"] = ["Lfm3D1Model"]
|
||||
return hparams
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3D1Model")
|
||||
@ModelBase.example("LiquidAI/d1-2B", "LiquidAI/d1-omni-2B")
|
||||
class LFM3D1Model(LFM3Model):
|
||||
model_arch = gguf.MODEL_ARCH.LFM3
|
||||
|
||||
# how the checkpoint serves, hard-coded in the server: assert the checkpoint records the same
|
||||
_served = {"d1_codes": 2, "d1_attention": "set"}
|
||||
|
||||
def set_vocab(self):
|
||||
# the vocab without the speech task templates of LFM3-Audio/Omni, d1 only decides
|
||||
TextModel.set_vocab(self)
|
||||
# the d1 prompt of LFM2, the LFM3 template writes an empty thought before the answer
|
||||
self.gguf_writer.add_chat_template([{"name": "systemone", "template": D1Model._systemone_template() + "<think></think>"}])
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
|
||||
for key, value in self._served.items():
|
||||
if self.hparams.get(key) != value:
|
||||
raise ValueError(f"unsupported d1 checkpoint: {key} = {self.hparams.get(key)!r}, expected {value!r}")
|
||||
|
||||
# a lone question reads the whole prompt both ways on the full attention layers
|
||||
self.gguf_writer.add_causal_attention(False)
|
||||
self.gguf_writer.add_decision_type(_D1_TYPES[self.hparams["d1_family"]])
|
||||
|
||||
# the option answers share one temperature
|
||||
with open(self.dir_model / "calibration.json", encoding="utf-8") as f:
|
||||
calibration = json.load(f)
|
||||
if calibration.get("temperatures"):
|
||||
raise ValueError("unsupported d1 calibration: per-bucket temperatures")
|
||||
for name in ("noul", "choice", "score"):
|
||||
self.gguf_writer.add_decision_temperature(name, calibration["default"])
|
||||
|
||||
|
||||
class LFM3VLVisionMixin(MmprojModel):
|
||||
"""LFM3-VL vision tower and projector, shared with LFM3-Omni."""
|
||||
|
||||
def _set_lfm3vl_gguf_parameters(self):
|
||||
assert self.hparams_vision is not None
|
||||
# clip.cpp hardcodes the GELU projector and its input norm eps
|
||||
if (self.global_config["projector_hidden_act"] != "gelu" or not self.global_config["projector_bias"]
|
||||
or self.global_config["projector_layer_norm_eps"] != 1e-6):
|
||||
raise ValueError("unsupported projector_hidden_act / projector_bias / projector_layer_norm_eps")
|
||||
# clip.cpp hardcodes the vision rope base, the RGB patchifier and the 3x3 spatial conv
|
||||
if (self.hparams_vision["rope_parameters"]["rope_theta"] != 1e6 or self.hparams_vision["in_channels"] != 3
|
||||
or self.hparams_vision["conv_kernel_size"] != 3):
|
||||
raise ValueError("unsupported vision rope_theta / in_channels / conv_kernel_size")
|
||||
self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.LFM3VL)
|
||||
self.gguf_writer.add_vision_head_count_kv(self.hparams_vision["num_key_value_heads"])
|
||||
self.gguf_writer.add_vision_spatial_merge_size(self.global_config["downsample_factor"])
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision["norm_eps"])
|
||||
self.gguf_writer.add_vision_use_silu(True)
|
||||
size = self.preprocessor_config["size"]
|
||||
self.gguf_writer.add_vision_min_pixels(size["shortest_edge"])
|
||||
self.gguf_writer.add_vision_max_pixels(size["longest_edge"])
|
||||
# PIL resample enum, the torchvision backend has no lanczos
|
||||
resample = self.preprocessor_config["resample"]
|
||||
resize_algo = {2: "bilinear", 3: "bicubic"}.get(resample)
|
||||
if resize_algo is None:
|
||||
raise ValueError(f"unsupported resample: {resample}")
|
||||
self.gguf_writer.add_vision_image_resize_algo(resize_algo)
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if "patch_embd" in new_name:
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
if "conv2d_spatial" in new_name:
|
||||
# CUDA CONV_2D_DW requires an f32 kernel; the tensor is tiny
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
def _lfm3vl_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "model.vision_tower.patchifier.weight":
|
||||
# qwen2vl-style layout [channel, temporal, ph, pw], one conv kernel per temporal slice
|
||||
assert self.hparams_vision is not None
|
||||
t = self.hparams_vision["temporal_patch_size"]
|
||||
p = self.hparams_vision["patch_size"]
|
||||
n_embd = data_torch.shape[0]
|
||||
w = data_torch.view(n_embd, 3, t, p, p)
|
||||
yield ("v.patch_embd.weight", w[:, :, 0].contiguous())
|
||||
if t == 2:
|
||||
yield ("v.patch_embd.weight.1", w[:, :, 1].contiguous())
|
||||
else:
|
||||
assert t == 1, f"unsupported vision_temporal_patch_size {t}"
|
||||
return
|
||||
if name == "model.vision_tower.embedding_norm.weight":
|
||||
yield ("v.embedding_norm.weight", data_torch)
|
||||
return
|
||||
if "conv.in_proj.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_in_proj.weight", data_torch)
|
||||
return
|
||||
if "conv.spatial_conv.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_spatial.weight", data_torch)
|
||||
return
|
||||
if "conv.out_proj.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_out_proj.weight", data_torch)
|
||||
return
|
||||
|
||||
# projector: input LayerNorm -> linear_1 -> GELU -> linear_2
|
||||
if name.startswith("model.multi_modal_projector."):
|
||||
layer, suffix = name.removeprefix("model.multi_modal_projector.").split(".")
|
||||
yield ({"layer_norm": "mm.input_norm", "linear_1": "mm.0", "linear_2": "mm.2"}[layer] + "." + suffix, data_torch)
|
||||
return
|
||||
|
||||
yield (self.map_tensor_name(name), data_torch)
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3VlForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-VL-2B")
|
||||
class LFM3VLMmprojModel(LFM3VLVisionMixin):
|
||||
has_vision_encoder = True
|
||||
has_audio_encoder = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
assert self.hparams_vision is not None
|
||||
# dynamic resolution, image_size is only set for MmprojModel compatibility
|
||||
self.hparams_vision["image_size"] = 512
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._set_lfm3vl_gguf_parameters()
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith(("model.vision_tower.", "model.multi_modal_projector.")):
|
||||
yield from self._lfm3vl_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
def _is_lfm3_audio_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
return config.get("model_type") == "lfm3_audio" and "lfm" in config
|
||||
|
||||
|
||||
# read the release config.json as is: a transformers that knows lfm3_audio rewrites it into another layout
|
||||
@ModelBase.register_hparams_loader(_is_lfm3_audio_checkpoint)
|
||||
def _load_lfm3_audio_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
config["text_config"] = config["lfm"]
|
||||
return config
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3AudioForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-Audio-2B")
|
||||
class LFM3AudioMmprojModel(MmprojModel):
|
||||
has_vision_encoder = False
|
||||
has_audio_encoder = True
|
||||
|
||||
# the downsampler has the LFM2-Audio pre-encoder layout: conv, dw conv, pw conv, dw conv, pw conv
|
||||
_downsample_idx = {"conv_in": 0, "conv_mid": 2, "lin_mid": 3, "conv_end": 5, "lin_end": 6}
|
||||
|
||||
# audio output: MaskGIT depthformer (a.gen.code.*) and the codec decoder (a.gen.wav.*)
|
||||
_depth_layer_map = {
|
||||
"operator_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_NORM,
|
||||
"self_attn.q_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_Q,
|
||||
"self_attn.q_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_Q_NORM,
|
||||
"self_attn.k_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_K,
|
||||
"self_attn.k_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_K_NORM,
|
||||
"self_attn.v_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_V,
|
||||
"self_attn.o_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_OUT,
|
||||
"self_attn.gate_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_GATE,
|
||||
"ffn_norm": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_NORM,
|
||||
"feed_forward.w1": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_GATE,
|
||||
"feed_forward.w3": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_UP,
|
||||
"feed_forward.w2": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_DOWN,
|
||||
}
|
||||
_codec_layer_map = {
|
||||
"operator_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_NORM,
|
||||
"post_operator_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_POST_NORM,
|
||||
"self_attn.q_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q,
|
||||
"self_attn.q_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q_NORM,
|
||||
"self_attn.k_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K,
|
||||
"self_attn.k_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K_NORM,
|
||||
"self_attn.v_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_V,
|
||||
"self_attn.o_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_OUT,
|
||||
"self_attn.gate_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_GATE,
|
||||
"conv.in_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW1,
|
||||
"conv.conv": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_DW,
|
||||
"conv.out_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW2,
|
||||
"ffn_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_NORM,
|
||||
"post_ffn_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_POST_NORM,
|
||||
"feed_forward.w1": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_GATE,
|
||||
"feed_forward.w3": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_UP,
|
||||
"feed_forward.w2": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_DOWN,
|
||||
}
|
||||
# the codec graph in clip.cpp hardcodes the liquid-codec FSQ front end and decoder shape, conversion fails on a mismatch
|
||||
_codec = {
|
||||
"model_type": "liquid_codec", "fsq_levels": [8, 7, 6, 6], "n_q": 12, "quantizer_dim": 256,
|
||||
"encoder_dim": 512, "stft_n_fft": 1280, "stft_hop_length": 320, "stft_win_length": 1280, "stft_frame_stack": 6,
|
||||
"decoder_window": 30, "sampling_rate": 24000,
|
||||
}
|
||||
# the lfm3a pipeline in mtmd-helper-gen.cpp hardcodes these
|
||||
_generation = {"codebooks": 12, "maskgit_schedule": [4, 4, 4], "interleaved_n_text": 6, "interleaved_n_audio": 12}
|
||||
|
||||
# speech-code sampling of the model card, generation_config.json overrides it
|
||||
_audio_sampling = {
|
||||
"audio_temperature": 1.0, "audio_top_k": 128, "audio_repetition_penalty": 1.1, "audio_repetition_window": 4096,
|
||||
}
|
||||
|
||||
# mtmd_audio_preprocessor_lfm3a hardcodes this front end, conversion fails on a mismatch
|
||||
_feature_extractor = {
|
||||
"sampling_rate": 16000, "feature_size": 128, "n_fft": 400, "win_length": 400, "hop_length": 160,
|
||||
"log_floor": -12.0, "normalization_offset": 4.0, "normalization_divisor": 4.0, "encoder_stride": 8,
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# audio output is exported when the checkpoint ships the codec
|
||||
self._audio_output = (self.dir_model / "audio_tokenizer").is_dir()
|
||||
gen = self.get_generation_config()
|
||||
self._n_codebooks: int = gen["codebooks"]
|
||||
self._audio_vocab: int = gen["audio_vocab_size"]
|
||||
self._depth_embd: dict[int, Tensor] = {}
|
||||
self._depth_norm: dict[int, Tensor] = {}
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
enc = self.global_config["encoder"]
|
||||
cfg = dict(enc["lfm"])
|
||||
cfg["num_mel_bins"] = enc["dim_in"]
|
||||
cfg["sliding_window"] = enc["sliding_window"]
|
||||
return cfg
|
||||
|
||||
def get_generation_config(self) -> dict[str, Any]:
|
||||
c = self.global_config
|
||||
steps = c["maskgit_steps"]
|
||||
return {
|
||||
"codebooks": c["codebooks"], "audio_vocab_size": c["audio_vocab_size"],
|
||||
"maskgit_schedule": [c["codebooks"] // steps] * steps,
|
||||
"interleaved_n_text": c["interleaved_n_text"], "interleaved_n_audio": c["interleaved_n_audio"],
|
||||
}
|
||||
|
||||
def get_depth_config(self) -> dict[str, Any]:
|
||||
return self.global_config["depthformer"]
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
assert self.hparams_audio is not None
|
||||
fe = self.preprocessor_config["feature_extractor"]
|
||||
if fe["feature_extractor_type"] not in ("Lfm3AudioFeatureExtractor", "Lfm3OmniFeatureExtractor"):
|
||||
raise ValueError(f"unsupported feature_extractor_type {fe['feature_extractor_type']!r}")
|
||||
for k, v in self._feature_extractor.items():
|
||||
if fe[k] != v:
|
||||
raise ValueError(f"unsupported feature_extractor.{k}={fe[k]!r}, expected {v!r}")
|
||||
|
||||
# clip.cpp hardcodes the encoder rope base
|
||||
if self.hparams_audio["rope_parameters"]["rope_theta"] != 1e6:
|
||||
raise ValueError("unsupported audio encoder rope_theta")
|
||||
|
||||
self.hparams_audio["intermediate_size"] = lfm2_feed_forward_length(self.hparams_audio)
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.LFM3A)
|
||||
self.gguf_writer.add_audio_head_count_kv(self.hparams_audio["num_key_value_heads"])
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams_audio["norm_eps"])
|
||||
self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["num_mel_bins"])
|
||||
self.gguf_writer.add_audio_window_size(self.hparams_audio["sliding_window"])
|
||||
|
||||
if not self._audio_output:
|
||||
return
|
||||
|
||||
# audio generation: depthformer hparams, the codec ones are constant and live in clip.cpp
|
||||
gen = self.get_generation_config()
|
||||
for k, v in self._generation.items():
|
||||
if gen[k] != v:
|
||||
raise ValueError(f"unsupported {k}={gen[k]!r}, expected {v!r}")
|
||||
depth = self.get_depth_config()
|
||||
if depth.get("is_causal", False) or depth["use_sandwich_norm"] or set(depth["layer_types"]) != {"full_attention"}:
|
||||
raise ValueError("unsupported depthformer, expected a bidirectional all-attention stack")
|
||||
if depth["rope_parameters"]["full_attention"]["rope_theta"] != 1e6:
|
||||
raise ValueError("unsupported depthformer rope_theta")
|
||||
self.gguf_writer.add_clip_has_gen_audio_encoder(True)
|
||||
self.gguf_writer.add_clip_gen_audio_projector_type(gguf.VisionProjectorType.LFM3A_GEN)
|
||||
self.gguf_writer.add_gen_audio_projection_dim(self.n_embd_text)
|
||||
self.gguf_writer.add_gen_audio_embedding_length(depth["hidden_size"])
|
||||
self.gguf_writer.add_gen_audio_feed_forward_length(depth["intermediate_size"])
|
||||
self.gguf_writer.add_gen_audio_block_count(depth["num_hidden_layers"])
|
||||
self.gguf_writer.add_gen_audio_head_count(depth["num_attention_heads"])
|
||||
self.gguf_writer.add_gen_audio_head_count_kv(depth["num_key_value_heads"])
|
||||
self.gguf_writer.add_gen_audio_attention_layernorm_eps(depth["norm_eps"])
|
||||
|
||||
# sampling defaults of the speech codes, next to the text ones that become general.sampling.*
|
||||
gen = gguf.Metadata.load_generation_config(self.dir_model)
|
||||
for key, add in (("audio_temperature", self.gguf_writer.add_gen_audio_sampling_temp),
|
||||
("audio_top_k", self.gguf_writer.add_gen_audio_sampling_top_k),
|
||||
("audio_repetition_penalty", self.gguf_writer.add_gen_audio_sampling_penalty_repeat),
|
||||
("audio_repetition_window", self.gguf_writer.add_gen_audio_sampling_penalty_last_n)):
|
||||
value = gen.get(key, self._audio_sampling.get(key))
|
||||
if value is not None:
|
||||
add(value)
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if new_name.startswith("a.conv1d.") or "conv_dw" in new_name or new_name.startswith("a.gen.wav.quant."):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
# the codec decoder is small and its log-magnitude/phase output does not survive quantization
|
||||
if new_name.startswith("a.gen.wav."):
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
if self._audio_output:
|
||||
yield from self._codec_tensors()
|
||||
|
||||
def _codec_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
import json
|
||||
from safetensors.torch import load_file
|
||||
|
||||
T = gguf.MODEL_TENSOR
|
||||
codec_dir = self.dir_model / "audio_tokenizer"
|
||||
with open(codec_dir / "config.json", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
for k, v in self._codec.items():
|
||||
if cfg.get(k) != v:
|
||||
raise ValueError(f"unsupported audio_tokenizer {k}={cfg.get(k)!r}, expected {v!r}")
|
||||
if math.prod(cfg["fsq_levels"]) != self._audio_vocab - 1:
|
||||
raise ValueError(f"audio_vocab_size {self._audio_vocab} does not match the codec, expected codebook size + 1")
|
||||
|
||||
state_dict = load_file(codec_dir / "model.safetensors")
|
||||
|
||||
# FSQ code table: index -> the len(levels) quantized values, see FiniteScalarQuantization._indices_to_codes
|
||||
levels = torch.tensor(cfg["fsq_levels"], dtype=torch.int64)
|
||||
basis = torch.cumprod(torch.cat([torch.ones(1, dtype=torch.int64), levels[:-1]]), dim=0)
|
||||
half = levels // 2
|
||||
idx = torch.arange(int(levels.prod()), dtype=torch.int64)[:, None]
|
||||
codes = ((idx // basis) % levels - half) / half
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_FIRST_CB), codes.to(torch.float32))
|
||||
|
||||
# one projection over the concatenated code vectors of all codebooks, under quantizer.fsq. in newer exports
|
||||
quantizer = "quantizer.fsq." if "quantizer.fsq.project_out.weight" in state_dict else "quantizer."
|
||||
for suffix in ("weight", "bias"):
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_FIRST_OUT, suffix=f".{suffix}"), state_dict[f"{quantizer}project_out.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_IN_PROJ, suffix=f".{suffix}"), state_dict[f"from_quantizer.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_OUT_PROJ, suffix=f".{suffix}"), state_dict[f"decoder.lin.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_OUTPUT_NORM), state_dict["decoder.lfm.embedding_norm.weight"])
|
||||
|
||||
for name, data_torch in state_dict.items():
|
||||
if not name.startswith("decoder.lfm.layers."):
|
||||
continue
|
||||
bid_str, key = name.removeprefix("decoder.lfm.layers.").rsplit(".weight", 1)[0].split(".", 1)
|
||||
if key == "conv.conv":
|
||||
data_torch = data_torch.squeeze(1)
|
||||
yield (self.format_tensor_name(self._codec_layer_map[key], int(bid_str)), data_torch)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith("a.gen.wav."):
|
||||
yield (name, data_torch)
|
||||
return
|
||||
|
||||
if name.startswith(("depthformer.", "depth_", "codebook_embedding.", "mask_embedding")):
|
||||
if self._audio_output:
|
||||
yield from self._depthformer_tensor(data_torch, name, bid)
|
||||
return
|
||||
|
||||
if not name.startswith(("encoder.", "mlp.")):
|
||||
return
|
||||
|
||||
if name.startswith("encoder.downsample."):
|
||||
layer, suffix = name.removeprefix("encoder.downsample.").split(".")
|
||||
if layer == "lin_out":
|
||||
if suffix == "weight":
|
||||
data_torch = data_torch.squeeze(-1)
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_ENC_OUT, suffix=f".{suffix}"), data_torch)
|
||||
return
|
||||
if suffix == "bias":
|
||||
data_torch = data_torch[:, None, None]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_ENC_CONV1D, self._downsample_idx[layer], f".{suffix}"), data_torch)
|
||||
return
|
||||
|
||||
# conv op requires 2d tensor
|
||||
if name.endswith("conv.conv.weight"):
|
||||
data_torch = data_torch.squeeze(1)
|
||||
|
||||
yield (self.map_tensor_name(name), data_torch)
|
||||
|
||||
def _depthformer_tensor(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
T = gguf.MODEL_TENSOR
|
||||
|
||||
if name.startswith("depthformer.model.layers."):
|
||||
key = name.removeprefix("depthformer.model.layers.").split(".", 1)[1].rsplit(".weight", 1)[0]
|
||||
yield (self.format_tensor_name(self._depth_layer_map[key], bid), data_torch)
|
||||
return
|
||||
if name == "depthformer.model.embedding_norm.weight":
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_OUTPUT_NORM), data_torch)
|
||||
return
|
||||
if name.startswith("depth_linear."):
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_PROJ_IN, suffix="." + name.rsplit(".", 1)[1]), data_torch)
|
||||
return
|
||||
if name == "mask_embedding":
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_MASK_EMBD, suffix=""), data_torch)
|
||||
return
|
||||
if name == "codebook_embedding.weight":
|
||||
assert data_torch.shape[0] == self._n_codebooks * self._audio_vocab
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_OUT_EMBD), data_torch)
|
||||
return
|
||||
|
||||
# per-codebook heads: the embedding table is tied to to_logits, merge the 12 tables into one tensor
|
||||
assert name.startswith("depth_embeddings.")
|
||||
idx = int(name.split(".")[1])
|
||||
key = name.split(".", 2)[2]
|
||||
if key == "embedding.weight":
|
||||
self._depth_embd[idx] = data_torch
|
||||
if len(self._depth_embd) == self._n_codebooks:
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_EMBD), torch.stack([self._depth_embd[i] for i in range(self._n_codebooks)], dim=0))
|
||||
elif key == "embedding_norm.weight":
|
||||
self._depth_norm[idx] = data_torch
|
||||
if len(self._depth_norm) == self._n_codebooks:
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_HEAD_NORM), torch.stack([self._depth_norm[i] for i in range(self._n_codebooks)], dim=0))
|
||||
elif key == "to_logits.weight":
|
||||
if not torch.equal(data_torch, self._depth_embd[idx]):
|
||||
raise ValueError(f"depth_embeddings.{idx} is not tied")
|
||||
else:
|
||||
raise ValueError(f"unexpected tensor {name}")
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3OmniForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-Omni-2B")
|
||||
class LFM3OmniMmprojModel(LFM3VLVisionMixin, LFM3AudioMmprojModel):
|
||||
has_vision_encoder = True
|
||||
has_audio_encoder = True
|
||||
|
||||
_audio_sampling = {"audio_temperature": 0.4, "audio_repetition_penalty": 1.2, "audio_repetition_window": 64}
|
||||
|
||||
# the audio side is the LFM3-Audio model under the HF-native names
|
||||
_audio_renames = (
|
||||
("model.audio_encoder.lfm.", "encoder.lfm.lfm."),
|
||||
("model.audio_encoder.", "encoder."),
|
||||
("model.audio_projector.layer_norm.", "mlp.model.0."),
|
||||
("model.audio_projector.linear_1.", "mlp.model.1."),
|
||||
("model.audio_projector.linear_2.", "mlp.model.3."),
|
||||
("model.depth_decoder.model.", "depthformer.model."),
|
||||
("model.depth_decoder.", ""),
|
||||
("model.codebook_embedding.", "codebook_embedding."),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
assert self.hparams_vision is not None
|
||||
# dynamic resolution, image_size is only set for MmprojModel compatibility
|
||||
self.hparams_vision["image_size"] = 512
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
enc = self.global_config["audio_encoder_config"]
|
||||
cfg = dict(enc["text_config"])
|
||||
cfg["num_mel_bins"] = enc["num_mel_bins"]
|
||||
cfg["sliding_window"] = enc["sliding_window"]
|
||||
return cfg
|
||||
|
||||
def get_generation_config(self) -> dict[str, Any]:
|
||||
c = self.global_config
|
||||
return {
|
||||
"codebooks": c["num_codebooks"], "audio_vocab_size": c["codebook_size"], "maskgit_schedule": c["maskgit_schedule"],
|
||||
"interleaved_n_text": c["interleaved_n_text"], "interleaved_n_audio": c["interleaved_n_audio"],
|
||||
}
|
||||
|
||||
def get_depth_config(self) -> dict[str, Any]:
|
||||
return self.global_config["depth_config"]
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._set_lfm3vl_gguf_parameters()
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith(("model.vision_tower.", "model.multi_modal_projector.")):
|
||||
yield from self._lfm3vl_tensors(data_torch, name, bid)
|
||||
return
|
||||
for old, new in self._audio_renames:
|
||||
if name.startswith(old):
|
||||
yield from super().modify_tensors(data_torch, new + name.removeprefix(old), bid)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
@@ -690,6 +690,52 @@ class Qwen3_5TextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
self._try_set_pooling_type()
|
||||
|
||||
|
||||
@ModelBase.register("MiniCPMOCRForSequenceClassification")
|
||||
class MiniCPMOCRSeqClsModel(Qwen3_5TextModel):
|
||||
# OCR consistency judge: Qwen3.5 VL trunk with a value head read at the last token
|
||||
# the class scores are [inconsistent, consistent], the softmax temperature is 1
|
||||
model_arch = gguf.MODEL_ARCH.QWEN35
|
||||
no_mtp = True # the checkpoint has no MTP head
|
||||
|
||||
CLASS_LABELS = ["inconsistent", "consistent"]
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_chat_template([{"name": "systemone", "template": self._systemone_template()}])
|
||||
|
||||
def _systemone_template(self) -> str:
|
||||
# the prompt of the training data, the two images are the original page and the one
|
||||
# rendered from the OCR text; the task is fixed, there is nothing to fill in
|
||||
# the trailing newlines are in an expression, so the template does not drop them
|
||||
return (
|
||||
"<|im_start|>user\n{{ images[0] }}\nOCR文本:\n{{ state }}\n{{ images[1] }}\n"
|
||||
"第一张图是原图,第二张图是上述 OCR 文本渲染得到的图片。\n"
|
||||
"请判断该 OCR 文本及其对应渲染图是否与原图内容一致。\n"
|
||||
"这是一个二分类任务:一致标为 1,不一致标为 0。"
|
||||
"{{ '<|im_end|>\\n<|im_start|>assistant\\n<think>\\n\\n</think>\\n\\n' }}"
|
||||
)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
|
||||
# the config.json of the checkpoint keeps the one of the base model, it has no num_labels / id2label
|
||||
if "score.weight" not in self.model_tensors:
|
||||
raise ValueError("the checkpoint has no score.weight classification head")
|
||||
|
||||
self.gguf_writer.add_pooling_type(gguf.PoolingType.LAST)
|
||||
self.gguf_writer.add_embedding_length_out(len(self.CLASS_LABELS))
|
||||
self.gguf_writer.add_classifier_output_labels(self.CLASS_LABELS)
|
||||
self.gguf_writer.add_decision_type(gguf.DecisionType.MINICPM_OCR_JEV)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "score.weight":
|
||||
if data_torch.shape[0] != len(self.CLASS_LABELS):
|
||||
raise ValueError(f"score.weight must have {len(self.CLASS_LABELS)} rows, got {data_torch.shape[0]}")
|
||||
yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.CLS_OUT] + ".weight", data_torch)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
def _is_openjev_checkpoint(dir_model: Path) -> bool:
|
||||
return (dir_model / "helper" / "shim.py").is_file() and (dir_model / "config.json").is_file()
|
||||
|
||||
|
||||
@@ -144,6 +144,16 @@ class Qwen3VLVisionModel(MmprojModel):
|
||||
yield from MmprojModel.modify_tensors(self, data_torch, name, bid)
|
||||
|
||||
|
||||
@ModelBase.register("MiniCPMOCRForSequenceClassification")
|
||||
class MiniCPMOCRSeqClsVisionModel(Qwen3VLVisionModel):
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
# trained with the image budget of its processor, wider than the mtmd default of Qwen-VL
|
||||
size = self.preprocessor_config["size"]
|
||||
self.gguf_writer.add_vision_min_pixels(int(size["shortest_edge"]))
|
||||
self.gguf_writer.add_vision_max_pixels(int(size["longest_edge"]))
|
||||
|
||||
|
||||
@ModelBase.register("Qwen3OmniMoeForConditionalGeneration")
|
||||
@ModelBase.example("Qwen/Qwen3-Omni-30B-A3B-Instruct")
|
||||
class Qwen3OmniMmprojModel(Qwen3VLVisionModel, Qwen25AudioModel):
|
||||
|
||||
+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(
|
||||
|
||||
@@ -72,6 +72,8 @@ However, A6x GPUs in phones are likely not supported due to the outdated driver
|
||||
| MXFP4 | Support |
|
||||
| IQ4_NL | Support |
|
||||
|
||||
`MUL_MAT_ID` accepts an expert id of `-1` and writes zeros to that slot. `ADD_ID` adds no bias for `-1`, including in fused MoE operations.
|
||||
|
||||
## Model Preparation
|
||||
|
||||
Since common quantizations are supported now, it is recommanded to download GGUF models directly from Huggingface.
|
||||
|
||||
@@ -324,6 +324,9 @@ on 4 physical NPUs, or `--devices 'HTP0[0-1:0],HTP1[0-1:1]'` on 2 physical NPUs
|
||||
- `GGML_HEXAGON_NHVX=0`
|
||||
Controls the number of HVX hardware threads to use. The default is all (actual number varies depending on the hardware version).
|
||||
|
||||
- `GGML_HEXAGON_VTCM_SIZE=0`
|
||||
Overrides the VTCM size used by each session. Accepts `K`, `M` and `G` suffixes; a plain number is in MB. The default (0) uses the size the hardware reports. Values above the hardware size will fail to start the session.
|
||||
|
||||
- `GGML_HEXAGON_HOSTBUF=1` (default: 0, disabled)
|
||||
Enables allocating host buffers for debugging. By default, host buffers are disabled.
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Example client for the llama-server /v1/realtime websocket, with the openai SDK.
|
||||
|
||||
Start a server with an audio model, e.g. LFM3-Audio:
|
||||
|
||||
llama-server -hf LiquidAI/LFM3-Audio-2B-GGUF
|
||||
|
||||
Spoken conversation, the audio of each wav file is one user turn. It is sent in chunks as it would come from a
|
||||
microphone, the server prefills it as it arrives so the response starts right after the last chunk:
|
||||
|
||||
python examples/realtime_client.py --instructions "Respond with interleaved text and audio." question.wav
|
||||
|
||||
Output modalities: left out, the model picks; "text", "audio" or "text,audio" (interleaved) force the response.
|
||||
Without --instructions, a forced mode comes with the system prompt of the LFM3-Audio or LFM3-Omni model card:
|
||||
|
||||
python examples/realtime_client.py --output-modalities text,audio --out answer.wav question.wav
|
||||
|
||||
Text turns go in with --text, transcription sessions transcribe each file:
|
||||
|
||||
python examples/realtime_client.py --text "What is the capital of France?"
|
||||
python examples/realtime_client.py --type transcription --language en speech.wav
|
||||
|
||||
Live transcription models (Voxtral Realtime, LFM3-Audio live ASR) transcribe while the audio is sent, a transcription
|
||||
session opens with ?intent=transcription:
|
||||
|
||||
python examples/realtime_client.py --type transcription speech.wav
|
||||
|
||||
Requirements: pip install "openai[realtime]" numpy
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import base64
|
||||
import time
|
||||
import wave
|
||||
|
||||
import numpy as np
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
|
||||
# system prompts of the LiquidAI/LFM3-Audio-2B and LiquidAI/LFM3-Omni-2B model cards for a forced output mode
|
||||
MODE_INSTRUCTIONS = {
|
||||
"audio": {"text,audio": "Respond with interleaved text and audio."},
|
||||
"omni": {"text": "Respond in text.", "text,audio": "Respond in interleaved text and audio."},
|
||||
}
|
||||
|
||||
|
||||
def read_wav(path, rate):
|
||||
# PCM16 wav -> mono int16 at the given rate
|
||||
with wave.open(path, "rb") as f:
|
||||
assert f.getsampwidth() == 2, "the example reads PCM16 wav files"
|
||||
x = np.frombuffer(f.readframes(f.getnframes()), dtype=np.int16).astype(np.float32)
|
||||
x = x.reshape(-1, f.getnchannels()).mean(axis=1)
|
||||
sr = f.getframerate()
|
||||
if sr != rate:
|
||||
n = int(round(len(x) * rate / sr))
|
||||
x = np.interp(np.arange(n) * sr / rate, np.arange(len(x)), x)
|
||||
return np.clip(x, -32768, 32767).astype(np.int16)
|
||||
|
||||
|
||||
def write_wav(path, pcm, rate):
|
||||
with wave.open(path, "wb") as f:
|
||||
f.setnchannels(1)
|
||||
f.setsampwidth(2)
|
||||
f.setframerate(rate)
|
||||
f.writeframes(pcm.tobytes())
|
||||
|
||||
|
||||
async def main(args):
|
||||
# the SDK derives a wss:// URL from base_url, a plain http server needs ws://
|
||||
client = AsyncOpenAI(base_url=args.url, api_key=args.api_key, websocket_base_url=args.url.replace("http", "ws", 1))
|
||||
query = {"intent": "transcription"} if args.type == "transcription" else {}
|
||||
async with client.realtime.connect(model=args.model, extra_query=query) as conn:
|
||||
ev = await conn.recv()
|
||||
if ev.type == "error":
|
||||
print("error:", ev.error.message)
|
||||
return
|
||||
# the audio goes in at the rate of the model, PCM16 mono
|
||||
session_info = ev.model_dump(warnings=False)["session"]
|
||||
audio_fmt = session_info["audio"]
|
||||
card = "omni" if "omni" in str(session_info.get("model", "")).lower() else "audio"
|
||||
in_rate = audio_fmt["input"]["format"]["rate"]
|
||||
out_rate = (audio_fmt.get("output") or {}).get("format", {}).get("rate", 24000)
|
||||
|
||||
session = {"type": args.type}
|
||||
if args.type == "realtime":
|
||||
instructions = args.instructions
|
||||
if instructions is None:
|
||||
instructions = MODE_INSTRUCTIONS[card].get(args.output_modalities)
|
||||
if instructions is not None:
|
||||
session["instructions"] = instructions
|
||||
if args.output_modalities:
|
||||
session["output_modalities"] = args.output_modalities.split(",")
|
||||
elif args.language:
|
||||
session["audio"] = {"input": {"transcription": {"language": args.language}}}
|
||||
await conn.session.update(session=session) # ty: ignore[invalid-argument-type]
|
||||
ev = await conn.recv()
|
||||
if ev.type == "error":
|
||||
print("error:", ev.error.message)
|
||||
return
|
||||
|
||||
turns = [("audio", path) for path in args.wav] + [("text", text) for text in args.text]
|
||||
for i, (kind, src) in enumerate(turns):
|
||||
t_last = 0.0
|
||||
|
||||
# the input is sent while the events are read: live transcription arrives while the audio is sent
|
||||
async def send_turn():
|
||||
nonlocal t_last
|
||||
if kind == "audio":
|
||||
pcm = read_wav(src, in_rate)
|
||||
step = in_rate * args.chunk_ms // 1000
|
||||
for j in range(0, len(pcm), step):
|
||||
chunk = pcm[j:j + step]
|
||||
await conn.input_audio_buffer.append(audio=base64.b64encode(chunk.tobytes()).decode())
|
||||
if args.realtime:
|
||||
await asyncio.sleep(len(chunk) / in_rate)
|
||||
if args.type == "transcription":
|
||||
# "final" ends the input, models that transcribe with a delay then complete the last item
|
||||
await conn.send({"type": "input_audio_buffer.commit", "final": i == len(args.wav) - 1}) # ty: ignore[invalid-argument-type]
|
||||
else:
|
||||
await conn.conversation.item.create(item={
|
||||
"type": "message", "role": "user", "content": [{"type": "input_text", "text": src}]})
|
||||
if args.type == "realtime":
|
||||
# the audio not committed yet ends the user turn
|
||||
await conn.response.create()
|
||||
t_last = time.perf_counter()
|
||||
|
||||
print(f"--- turn {i} ({kind}: {src})")
|
||||
sender = asyncio.create_task(send_turn())
|
||||
t_first = None
|
||||
audio = []
|
||||
async for ev in conn:
|
||||
if ev.type == "response.output_text.delta" or ev.type == "conversation.item.input_audio_transcription.delta":
|
||||
t_first = t_first or time.perf_counter()
|
||||
print(ev.delta, end="", flush=True)
|
||||
elif ev.type == "response.output_audio.delta":
|
||||
t_first = t_first or time.perf_counter()
|
||||
audio.append(np.frombuffer(base64.b64decode(ev.delta), dtype=np.int16))
|
||||
elif ev.type in ("response.done", "conversation.item.input_audio_transcription.completed"):
|
||||
break
|
||||
elif ev.type == "error":
|
||||
print("\nerror:", ev.error.message)
|
||||
return
|
||||
await sender
|
||||
print()
|
||||
if t_first is not None and t_first > t_last:
|
||||
print(f"first output {1000 * (t_first - t_last):.0f} ms after the end of the input")
|
||||
elif t_first is not None:
|
||||
print(f"output started {1000 * (t_last - t_first):.0f} ms before the end of the input, "
|
||||
f"done {1000 * (time.perf_counter() - t_last):.0f} ms after it")
|
||||
if audio:
|
||||
pcm_out = np.concatenate(audio)
|
||||
path = f"{args.out.removesuffix('.wav')}_{i}.wav" if len(turns) > 1 else args.out
|
||||
write_wav(path, pcm_out, out_rate)
|
||||
print(f"audio: {len(pcm_out) / out_rate:.2f} s -> {path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
p.add_argument("wav", nargs="*", help="PCM16 wav files, one user turn each")
|
||||
p.add_argument("--text", action="append", default=[], help="a user turn as text, after the audio turns, repeatable")
|
||||
p.add_argument("--type", choices=["realtime", "transcription"], default="realtime", help="session type")
|
||||
p.add_argument("--instructions", help="system prompt of the conversation")
|
||||
p.add_argument("--output-modalities", choices=["text", "audio", "text,audio"], help="default: the model picks")
|
||||
p.add_argument("--language", help="transcription language, e.g. en")
|
||||
p.add_argument("--chunk-ms", type=int, default=100, help="audio chunk size")
|
||||
p.add_argument("--realtime", action=argparse.BooleanOptionalAction, default=True, help="send the audio in real time")
|
||||
p.add_argument("--out", default="response.wav", help="where the audio of the responses goes")
|
||||
p.add_argument("--url", default="http://127.0.0.1:9931/v1")
|
||||
p.add_argument("--api-key", default="none")
|
||||
p.add_argument("--model", default="default", help="model name, used by the router")
|
||||
asyncio.run(main(p.parse_args()))
|
||||
@@ -287,6 +287,9 @@ void ggml_vec_dot_q2_0_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const voi
|
||||
|
||||
sumf = vaddvq_f32(sumv);
|
||||
#else
|
||||
UNUSED(nb);
|
||||
UNUSED(x);
|
||||
UNUSED(y);
|
||||
ggml_vec_dot_q2_0_q8_0_generic(n, s, bs, vx, bx, vy, by, nrc);
|
||||
return;
|
||||
#endif
|
||||
|
||||
+90
-210
@@ -1914,213 +1914,87 @@ void ggml_compute_forward_repeat_back(
|
||||
}
|
||||
|
||||
// ggml_compute_forward_concat
|
||||
static void ggml_compute_forward_concat_any(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
// rows of dst copied together, so that a strided source is read along neighboring rows
|
||||
static constexpr int64_t GGML_CONCAT_TILE = 32;
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src1));
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
|
||||
if (dim == 0) {
|
||||
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
|
||||
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
|
||||
|
||||
o[dim] = src0->ne[dim]/ggml_blck_size(src0->type);
|
||||
} else {
|
||||
o[dim] = src0->ne[dim];
|
||||
}
|
||||
|
||||
// Region 1: copy rows from src0
|
||||
for (int i3 = 0; i3 < ne03; i3++) {
|
||||
for (int i2 = ith; i2 < ne02; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne01; i1++) {
|
||||
const char * x = (const char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03;
|
||||
char * y = ( char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3;
|
||||
memcpy(y, x, ggml_row_size(src0->type, ne00));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Region 2: copy rows from src1, offset into dst by o[]
|
||||
for (int i3 = 0; i3 < ne13; i3++) {
|
||||
for (int i2 = ith; i2 < ne12; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne11; i1++) {
|
||||
const char * x = (const char *) src1->data + i1*nb11 + i2*nb12 + i3*nb13;
|
||||
char * y = ( char *) dst->data + (i1 + o[1])*nb1 + (i2 + o[2])*nb2 + (i3 + o[3])*nb3 + o[0]*nb0;
|
||||
memcpy(y, x, ggml_row_size(src1->type, ne10));
|
||||
}
|
||||
// copy n_rows rows of n elements of es bytes from a source with element stride nb0, element by element
|
||||
template <size_t es>
|
||||
static void ggml_concat_copy_strided(char * y, size_t nby, const char * x, size_t nbx, size_t nb0, int64_t n, int64_t n_rows) {
|
||||
for (int64_t i0 = 0; i0 < n; i0++) {
|
||||
for (int64_t r = 0; r < n_rows; r++) {
|
||||
memcpy(y + r*nby + i0*es, x + r*nbx + i0*nb0, es);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_i8(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
// copy n_rows rows of n elements of src into dst, rows are nbx bytes apart in src and nby bytes apart in dst
|
||||
static void ggml_concat_copy_rows(char * y, size_t nby, const ggml_tensor * src, const char * x, size_t nbx, int64_t n, int64_t n_rows) {
|
||||
const size_t es = ggml_type_size(src->type);
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(int8_t));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const int8_t * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const int8_t *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const int8_t *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
int8_t * y = (int8_t *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_f16(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(ggml_fp16_t));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const ggml_fp16_t * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const ggml_fp16_t *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const ggml_fp16_t *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
ggml_fp16_t * y = (ggml_fp16_t *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_f32(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(float));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const float * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const float *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const float *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
float * y = (float *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
if (src->nb[0] == es) {
|
||||
const size_t row_size = ggml_row_size(src->type, n);
|
||||
for (int64_t r = 0; r < n_rows; r++) {
|
||||
memcpy(y + r*nby, x + r*nbx, row_size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (es) {
|
||||
case 1: ggml_concat_copy_strided<1>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 2: ggml_concat_copy_strided<2>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 4: ggml_concat_copy_strided<4>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 8: ggml_concat_copy_strided<8>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
default: GGML_ABORT("unsupported element size");
|
||||
}
|
||||
}
|
||||
|
||||
// each dst row comes from src0 or src1, or for dim 0 from the row of src0 followed by the row of src1
|
||||
void ggml_compute_forward_concat(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
switch (src0->type) {
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_I16:
|
||||
{
|
||||
ggml_compute_forward_concat_f16(params, dst);
|
||||
} break;
|
||||
case GGML_TYPE_I8:
|
||||
{
|
||||
ggml_compute_forward_concat_i8(params, dst);
|
||||
} break;
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_I32:
|
||||
{
|
||||
ggml_compute_forward_concat_f32(params, dst);
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
ggml_compute_forward_concat_any(params, dst);
|
||||
}
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
GGML_ASSERT(nb0 == ggml_type_size(dst->type));
|
||||
GGML_ASSERT(!ggml_is_quantized(dst->type) || (nb00 == nb0 && nb10 == nb0));
|
||||
GGML_ASSERT(dim != 0 || (ne00 % ggml_blck_size(dst->type) == 0 && ne10 % ggml_blck_size(dst->type) == 0));
|
||||
|
||||
const size_t y1 = dim == 0 ? ggml_row_size(dst->type, ne00) : 0;
|
||||
|
||||
const int64_t nr = ne1*ne2*ne3;
|
||||
const int64_t dr = (nr + params->nth - 1)/params->nth;
|
||||
|
||||
const int64_t ir0 = dr*params->ith;
|
||||
const int64_t ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
for (int64_t ir = ir0; ir < ir1; ) {
|
||||
int64_t i[4] = { 0, ir % ne1, (ir/ne1) % ne2, ir/(ne1*ne2) };
|
||||
|
||||
// a tile of consecutive rows sharing i2, i3 and their source
|
||||
int64_t nt = MIN(MIN(ir1 - ir, ne1 - i[1]), GGML_CONCAT_TILE);
|
||||
if (dim == 1 && i[1] < ne01) {
|
||||
nt = MIN(nt, ne01 - i[1]);
|
||||
}
|
||||
|
||||
char * y = (char *) dst->data + i[1]*nb1 + i[2]*nb2 + i[3]*nb3;
|
||||
|
||||
if (dim == 0 || i[dim] < src0->ne[dim]) {
|
||||
ggml_concat_copy_rows(y, nb1, src0, (const char *) src0->data + i[1]*nb01 + i[2]*nb02 + i[3]*nb03, nb01, ne00, nt);
|
||||
}
|
||||
|
||||
if (dim == 0 || i[dim] >= src0->ne[dim]) {
|
||||
i[dim] -= src0->ne[dim]; // i[0] does not address rows
|
||||
ggml_concat_copy_rows(y + y1, nb1, src1, (const char *) src1->data + i[1]*nb11 + i[2]*nb12 + i[3]*nb13, nb11, ne10, nt);
|
||||
}
|
||||
|
||||
ir += nt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4972,8 +4846,8 @@ static void ggml_compute_forward_scale_f32(
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(dst));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(dst));
|
||||
GGML_ASSERT(ggml_are_same_shape(src0, dst));
|
||||
|
||||
float s; // scale factor
|
||||
@@ -4988,6 +4862,8 @@ static void ggml_compute_forward_scale_f32(
|
||||
const int nc = src0->ne[0];
|
||||
const int nr = ggml_nrows(src0);
|
||||
|
||||
GGML_TENSOR_UNARY_OP_LOCALS
|
||||
|
||||
// rows per thread
|
||||
const int dr = (nr + nth - 1)/nth;
|
||||
|
||||
@@ -4995,25 +4871,23 @@ static void ggml_compute_forward_scale_f32(
|
||||
const int ir0 = dr*ith;
|
||||
const int ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
const size_t nb01 = src0->nb[1];
|
||||
for (int i = ir0; i < ir1; i++) {
|
||||
const int64_t i1 = i % ne01;
|
||||
const int64_t i2 = (i / ne01) % ne02;
|
||||
const int64_t i3 = i / (ne01*ne02);
|
||||
|
||||
const size_t nb1 = dst->nb[1];
|
||||
float * y = (float *) ((char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
const float * x = (const float *) ((char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03);
|
||||
|
||||
if (b == 0.0f) {
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
if (b == 0.0f) {
|
||||
if (dst->data != src0->data) {
|
||||
// src0 is same shape as dst => same indices
|
||||
// TODO: add x parameter to ggml_vec_scale_f32 and remove this memcpy
|
||||
memcpy((char *)dst->data + i1*nb1, (char *)src0->data + i1*nb01, nc * sizeof(float));
|
||||
memcpy(y, x, nc * sizeof(float));
|
||||
}
|
||||
ggml_vec_scale_f32(nc, (float *) ((char *) dst->data + i1*nb1), s);
|
||||
}
|
||||
} else {
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
ggml_vec_mad1_f32(nc,
|
||||
(float *) ((char *) dst->data + i1*nb1),
|
||||
(float *) ((char *) src0->data + i1*nb1),
|
||||
s, b);
|
||||
ggml_vec_scale_f32(nc, y, s);
|
||||
} else {
|
||||
ggml_vec_mad1_f32(nc, y, x, s, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5024,8 +4898,8 @@ static void ggml_compute_forward_scale_bf16(
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(dst));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(dst));
|
||||
GGML_ASSERT(ggml_are_same_shape(src0, dst));
|
||||
|
||||
float s; // scale factor
|
||||
@@ -5040,6 +4914,8 @@ static void ggml_compute_forward_scale_bf16(
|
||||
const int nc = src0->ne[0];
|
||||
const int nr = ggml_nrows(src0);
|
||||
|
||||
GGML_TENSOR_UNARY_OP_LOCALS
|
||||
|
||||
// rows per thread
|
||||
const int dr = (nr + nth - 1)/nth;
|
||||
|
||||
@@ -5047,9 +4923,13 @@ static void ggml_compute_forward_scale_bf16(
|
||||
const int ir0 = dr*ith;
|
||||
const int ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
const ggml_bf16_t * x = (const ggml_bf16_t *) ((const char *) src0->data + i1*src0->nb[1]);
|
||||
ggml_bf16_t * y = (ggml_bf16_t *) ((char *) dst->data + i1*dst->nb[1]);
|
||||
for (int i = ir0; i < ir1; i++) {
|
||||
const int64_t i1 = i % ne01;
|
||||
const int64_t i2 = (i / ne01) % ne02;
|
||||
const int64_t i3 = i / (ne01*ne02);
|
||||
|
||||
const ggml_bf16_t * x = (const ggml_bf16_t *) ((const char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03);
|
||||
ggml_bf16_t * y = (ggml_bf16_t *) ((char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
for (int k = 0; k < nc; k++) {
|
||||
y[k] = GGML_FP32_TO_BF16(s*GGML_BF16_TO_FP32(x[k]) + b);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
const int end = int(int64_t(tiles) * (split + 1) / split_k) * BK;
|
||||
for (int k0 = begin; k0 < end; k0 += BK) {
|
||||
if (k_total % 8 == 0 && uintptr_t(weight) % 16 == 0) {
|
||||
#pragma unroll
|
||||
for (int i = tid; i < BM * BK / 8; i += nthreads) {
|
||||
const int row = i / (BK / 8), col = 8 * (i % (BK / 8));
|
||||
const int4 v = m0 + row < oc && k0 + col < k_total ?
|
||||
@@ -189,7 +188,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
*(int4 *) &a_s[row][col / 2] = v;
|
||||
}
|
||||
} else {
|
||||
#pragma unroll
|
||||
for (int i = tid; i < BM * BK / 2; i += nthreads) {
|
||||
const int row = i / (BK / 2), col = 2 * (i % (BK / 2));
|
||||
half lo = __float2half(0.0f), hi = lo;
|
||||
@@ -202,7 +200,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
a_s[row][col / 2] = __halves2half2(lo, hi);
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int k = load_row; k < BK; k += nthreads / (BN / 2)) {
|
||||
const int ki = k0 + k;
|
||||
const int ci = ki / (kw * kh), ky = ki / kw % kh, kx = ki % kw;
|
||||
|
||||
@@ -214,52 +214,36 @@ static cudaError_t ggml_cuda_device_malloc(void ** ptr, size_t size, int device)
|
||||
}
|
||||
|
||||
#if defined(GGML_USE_HIP)
|
||||
static int ggml_cuda_parse_id(char devName[]) {
|
||||
static int ggml_cuda_parse_id(const char * devName) {
|
||||
// A list of possible Target IDs can be found under the rocclr/clr repo in device.cpp
|
||||
// these values are not stable so this is susceptible to breakage
|
||||
// https://github.com/ROCm/clr/blob/amd-staging/rocclr/device/device.cpp
|
||||
int archMajor = 0x0;
|
||||
int archMinor = 0x0;
|
||||
int archNum = GGML_CUDA_CC_OFFSET_AMD;
|
||||
int archLen = strlen(devName);
|
||||
char archName[archLen + 1];
|
||||
// Extract the base architecture model ("gfx908:sramecc+:xnack-" -> "908")
|
||||
std::string archName = devName;
|
||||
archName.erase(0, 3);
|
||||
archName = archName.substr(0, archName.find(':'));
|
||||
|
||||
// strip leading 'gfx' while copying into our buffer
|
||||
if (archLen > 3) {
|
||||
strcpy(archName, &devName[3]);
|
||||
archLen -= 3;
|
||||
}
|
||||
const auto hex = [](const std::string & s) { return (int) strtoul(s.c_str(), nullptr, 16); };
|
||||
|
||||
// trim trailing :xnack- or :sramecc- statuses
|
||||
archLen = strcspn(archName, ":");
|
||||
archName[archLen] = '\0';
|
||||
int cc = 0;
|
||||
|
||||
// tease out the version information
|
||||
if (archLen > 8) {
|
||||
if (archName.size() > 8) {
|
||||
// versions labeled generic use '-' as delimiter
|
||||
// strip the trailing "-generic" then iterate through what remains
|
||||
if ((strstr(archName, "-generic"))) {
|
||||
archName[archLen - 8] = '\0';
|
||||
char * pch;
|
||||
if ((pch = strtok(archName, "-"))) {
|
||||
archMajor = (int)strtoul(pch, 0, 16);
|
||||
if ((pch = strtok(NULL, "-"))) {
|
||||
archMinor = 0x10 * (int)strtoul(pch, 0, 16);
|
||||
}
|
||||
if (archName.compare(archName.size() - 8, 8, "-generic") == 0) {
|
||||
archName.resize(archName.size() - 8);
|
||||
const size_t sep = archName.find('-');
|
||||
cc = 0x100 * hex(archName.substr(0, sep));
|
||||
if (sep != std::string::npos) {
|
||||
cc += 0x10 * hex(archName.substr(sep + 1));
|
||||
}
|
||||
}
|
||||
} else if (archLen >= 3) {
|
||||
// last two digits should be the minor * 0x10 + stepping
|
||||
archMinor = (int)strtoul(&archName[archLen - 2], 0, 16);
|
||||
archName[archLen - 2] = '\0';
|
||||
|
||||
// only the major version remains
|
||||
archMajor = (int)strtoul(archName, 0, 16);
|
||||
} else if (archName.size() >= 3) {
|
||||
// ordinary IDs are the version in hex
|
||||
cc = hex(archName);
|
||||
}
|
||||
archNum += archMajor * 0x100;
|
||||
archNum += archMinor;
|
||||
|
||||
return archNum;
|
||||
return GGML_CUDA_CC_OFFSET_AMD + cc;
|
||||
}
|
||||
#endif // defined(GGML_USE_HIP)
|
||||
|
||||
@@ -3838,6 +3822,11 @@ static bool ggml_cuda_can_fuse(const struct ggml_cgraph * cgraph,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: make the fused softcap kernel stride aware, it reads src and writes dst as dense
|
||||
if (!ggml_is_contiguous(scale->src[0]) || !ggml_is_contiguous(scale2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ggml_get_unary_op(tanh) != GGML_UNARY_OP_TANH) {
|
||||
return false;
|
||||
}
|
||||
@@ -5988,7 +5977,8 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
case GGML_OP_LOG:
|
||||
return true;
|
||||
case GGML_OP_SCALE:
|
||||
return (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_BF16) && op->type == op->src[0]->type;
|
||||
return (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_BF16) && op->type == op->src[0]->type &&
|
||||
ggml_is_contiguous_rows(op->src[0]);
|
||||
case GGML_OP_CLAMP:
|
||||
return ggml_is_contiguous_rows(op->src[0]);
|
||||
case GGML_OP_ADD:
|
||||
|
||||
+33
-10
@@ -3,23 +3,45 @@
|
||||
|
||||
#define MAX_GRIDDIM_X 0x7FFFFFFF
|
||||
|
||||
// src and dst may be views: rows are contiguous, dims 1..3 follow the strides (in elements).
|
||||
template <typename T>
|
||||
static __global__ void scale_f32(const T * x, T * dst, const float scale, const float bias, const int64_t nelements) {
|
||||
static __global__ void scale_f32(const T * x, T * dst, const float scale, const float bias, const uint32_t k,
|
||||
const uint3 ne0, const uint3 ne1, const uint3 ne2,
|
||||
const uint32_t s01, const uint32_t s02, const uint32_t s03,
|
||||
const uint32_t s1, const uint32_t s2, const uint32_t s3) {
|
||||
ggml_cuda_pdl_lc();
|
||||
int64_t tid = (int64_t)blockIdx.x * (int64_t)blockDim.x + (int64_t)threadIdx.x;
|
||||
int64_t stride = (int64_t)blockDim.x * (int64_t)gridDim.x;
|
||||
const uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
ggml_cuda_pdl_sync();
|
||||
for (int64_t i = tid; i < nelements; i += stride) {
|
||||
dst[i] = ggml_cuda_cast<T>(scale * ggml_cuda_cast<float>(x[i]) + bias);
|
||||
if (i >= k) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint2 d0 = fast_div_modulo(i, ne0); // <i / ne0, i0>
|
||||
const uint2 d1 = fast_div_modulo(d0.x, ne1); // <i / (ne0*ne1), i1>
|
||||
const uint2 d2 = fast_div_modulo(d1.x, ne2); // <i3, i2>
|
||||
|
||||
const size_t i_src = d0.y + size_t(d1.y)*s01 + size_t(d2.y)*s02 + size_t(d2.x)*s03;
|
||||
const size_t i_dst = d0.y + size_t(d1.y)*s1 + size_t(d2.y)*s2 + size_t(d2.x)*s3;
|
||||
|
||||
dst[i_dst] = ggml_cuda_cast<T>(scale * ggml_cuda_cast<float>(x[i_src]) + bias);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void scale_f32_cuda(const T * x, T * dst, const float scale, const float bias, const int64_t nelements, cudaStream_t stream) {
|
||||
const int64_t num_blocks = (nelements + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE;
|
||||
static void scale_f32_cuda(const T * x, T * dst, const float scale, const float bias, const ggml_tensor * src0, const ggml_tensor * t, cudaStream_t stream) {
|
||||
const int64_t k = ggml_nelements(src0);
|
||||
const size_t ts = sizeof(T);
|
||||
GGML_ASSERT(k <= std::numeric_limits<uint32_t>::max());
|
||||
|
||||
const uint3 ne0 = init_fastdiv_values(src0->ne[0]);
|
||||
const uint3 ne1 = init_fastdiv_values(src0->ne[1]);
|
||||
const uint3 ne2 = init_fastdiv_values(src0->ne[2]);
|
||||
|
||||
const int64_t num_blocks = (k + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE;
|
||||
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(MIN(MAX_GRIDDIM_X, num_blocks), CUDA_SCALE_BLOCK_SIZE, 0, stream);
|
||||
ggml_cuda_kernel_launch(scale_f32<T>, launch_params, x, dst, scale, bias, nelements);
|
||||
ggml_cuda_kernel_launch(scale_f32<T>, launch_params, x, dst, scale, bias, (uint32_t) k, ne0, ne1, ne2,
|
||||
src0->nb[1]/ts, src0->nb[2]/ts, src0->nb[3]/ts,
|
||||
t->nb[1]/ts, t->nb[2]/ts, t->nb[3]/ts);
|
||||
}
|
||||
|
||||
void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
@@ -28,6 +50,7 @@ void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_BF16);
|
||||
GGML_ASSERT(dst->type == src0->type);
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0) && ggml_is_contiguous_rows(dst));
|
||||
|
||||
float scale;
|
||||
float bias;
|
||||
@@ -35,8 +58,8 @@ void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
memcpy(&bias, (float *) dst->op_params + 1, sizeof(float));
|
||||
|
||||
if (src0->type == GGML_TYPE_BF16) {
|
||||
scale_f32_cuda((const nv_bfloat16 *) src0->data, (nv_bfloat16 *) dst->data, scale, bias, ggml_nelements(src0), stream);
|
||||
scale_f32_cuda((const nv_bfloat16 *) src0->data, (nv_bfloat16 *) dst->data, scale, bias, src0, dst, stream);
|
||||
} else {
|
||||
scale_f32_cuda((const float *) src0->data, (float *) dst->data, scale, bias, ggml_nelements(src0), stream);
|
||||
scale_f32_cuda((const float *) src0->data, (float *) dst->data, scale, bias, src0, dst, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ int opt_arch = 0; // autodetect
|
||||
size_t opt_ndev = 1;
|
||||
size_t opt_nhvx = 0; // use all
|
||||
int opt_nhmx = 1; // when set, enable HMX; when 0, use HVX only
|
||||
size_t opt_vtcm_size = 0; // VTCM size override in bytes; 0 = auto-detect
|
||||
size_t opt_vmem = HTP_OP_MAX_VMEM_DEFAULT; // max available va space for buffer mappings
|
||||
int opt_etm = 0;
|
||||
int opt_verbose = 0;
|
||||
@@ -2152,6 +2153,12 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->n_hvx = this->n_threads;
|
||||
this->n_hmx = (opt_nhmx != 0) ? (uint32_t)hw_n_hmx : 0;
|
||||
this->vtcm_size = (uint64_t)hw_vtcm_size;
|
||||
if (opt_vtcm_size > 0) {
|
||||
if (opt_vtcm_size > hw_vtcm_size) {
|
||||
GGML_LOG_WARN("ggml-hex: %s VTCM override %zu exceeds HW size %llu\n", this->c_name(), opt_vtcm_size, hw_vtcm_size);
|
||||
}
|
||||
this->vtcm_size = opt_vtcm_size;
|
||||
}
|
||||
GGML_LOG_INFO("ggml-hex: %s hwinfo: threads %u, hvx %u, hmx %u, vtcm %llu MB\n",
|
||||
this->c_name(), this->n_threads, this->n_hvx, this->n_hmx,
|
||||
(unsigned long long)(this->vtcm_size / (1024 * 1024)));
|
||||
@@ -2161,7 +2168,7 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->n_threads = opt_nhvx > 0 ? (uint32_t) (std::min)(opt_nhvx, (size_t) HTP_MAX_NTHREADS) : default_n_threads;
|
||||
this->n_hvx = this->n_threads;
|
||||
this->n_hmx = (opt_nhmx != 0) ? 1 : 0;
|
||||
this->vtcm_size = 8 * 1024 * 1024;
|
||||
this->vtcm_size = opt_vtcm_size > 0 ? opt_vtcm_size : 8 * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2230,7 +2237,8 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->op_batch = new ggml_hexagon_opbatch(this, opt_opbatch, this->max_vmem);
|
||||
|
||||
// Start dspqueue/opbatch processing
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, this->max_vmem, (uint32_t) opt_power_mode);
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, opt_vtcm_size, this->max_vmem,
|
||||
(uint32_t) opt_power_mode);
|
||||
if (err != 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: %s failed to start session: 0x%08x\n", this->c_name(), (unsigned) err);
|
||||
throw std::runtime_error("ggml-hex: iface start failed (see log for details)");
|
||||
@@ -3391,6 +3399,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
const char * str_etm = getenv("GGML_HEXAGON_ETM");
|
||||
const char * str_nhvx = getenv("GGML_HEXAGON_NHVX");
|
||||
const char * str_nhmx = getenv("GGML_HEXAGON_NHMX");
|
||||
const char * str_vtcm_size = getenv("GGML_HEXAGON_VTCM_SIZE");
|
||||
const char * str_mm_select = getenv("GGML_HEXAGON_MM_SELECT");
|
||||
const char * str_fa_select = getenv("GGML_HEXAGON_FA_SELECT");
|
||||
const char * str_fa_split = getenv("GGML_HEXAGON_FA_SPLIT");
|
||||
@@ -3456,6 +3465,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
}
|
||||
opt_nhvx = str_nhvx ? strtoul(str_nhvx, NULL, 0) : opt_nhvx;
|
||||
opt_nhmx = str_nhmx ? atoi(str_nhmx) : opt_nhmx;
|
||||
opt_vtcm_size = str_vtcm_size ? parse_size(str_vtcm_size) : opt_vtcm_size;
|
||||
opt_mm_select = str_mm_select ? atoi(str_mm_select) : opt_mm_select;
|
||||
opt_fa_select = str_fa_select ? atoi(str_fa_select) : opt_fa_select;
|
||||
opt_fa_split = str_fa_split ? atoi(str_fa_split) : opt_fa_split;
|
||||
|
||||
@@ -11,7 +11,7 @@ struct htp_iface_pmu_conf {
|
||||
};
|
||||
|
||||
interface htp_iface : remote_handle64 {
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 vtcm_size, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult stop();
|
||||
AEEResult mmap(in uint32 fd, in uint64 size);
|
||||
AEEResult munmap(in uint32 fd);
|
||||
|
||||
@@ -325,8 +325,11 @@ static unsigned int vtcm_query_size(void) {
|
||||
return vtcm_size;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
static int vtcm_alloc(struct htp_context * ctx, uint64_t size_override) {
|
||||
unsigned int vtcm_size = vtcm_query_size();
|
||||
if (size_override) {
|
||||
vtcm_size = (unsigned int) size_override;
|
||||
}
|
||||
|
||||
compute_res_attr_t attr;
|
||||
HAP_compute_res_attr_init(&attr);
|
||||
@@ -438,7 +441,7 @@ static const struct htp_power_profile htp_power_profiles[HTP_POWER_MODE_COUNT] =
|
||||
|
||||
#undef HTP_POWER_PROFILE_ENTRY
|
||||
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t max_vmem, uint32_t power_mode) {
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t vtcm_size, uint64_t max_vmem, uint32_t power_mode) {
|
||||
struct htp_handle * h = (struct htp_handle *) handle;
|
||||
if (!h) {
|
||||
return AEE_EBADPARM;
|
||||
@@ -557,7 +560,7 @@ AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp
|
||||
ctx->max_vmem = max_vmem;
|
||||
ctx->dsp_queue = dsp_queue;
|
||||
|
||||
err = vtcm_alloc(ctx);
|
||||
err = vtcm_alloc(ctx, vtcm_size);
|
||||
if (err != AEE_SUCCESS) {
|
||||
FARF(ERROR, "Unable to allocate VTCM");
|
||||
htp_iface_stop(handle);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "hvx-dump.h"
|
||||
#include "hvx-arith.h"
|
||||
#include "hvx-reduce.h"
|
||||
#include "hvx-copy.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
@@ -1413,7 +1414,7 @@ static void hvx_mv_id_tiled(unsigned int nth, unsigned int ith, void * data) {
|
||||
|
||||
for (uint32_t ie1 = 0; ie1 < n_aids; ++ie1) { // for each expert
|
||||
const int32_t eid = *(const int32_t *) ((const uint8_t *) src2->data + ie1 * src2->nb[0]);
|
||||
if (eid == -1) {
|
||||
if (eid < 0) {
|
||||
continue;
|
||||
}
|
||||
assert(eid >= 0 && eid < (int32_t) n_ids);
|
||||
@@ -4111,7 +4112,7 @@ static int hvx_mm_matmul_id_nx(
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static inline void scan_expert_ids_n(
|
||||
static __attribute__((noinline)) uint32_t scan_expert_ids_n(
|
||||
const struct htp_tensor * ids,
|
||||
const uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
@@ -4121,15 +4122,17 @@ static inline void scan_expert_ids_n(
|
||||
) {
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const int32_t * row_ptr = (const int32_t *) (ids_data + iid1 * ids_nb1);
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = row_ptr[id];
|
||||
if (i02 == -1) {
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
continue;
|
||||
}
|
||||
assert(i02 >= 0 && i02 < n_as);
|
||||
assert(i02 >= 0 && i02 < (int32_t) n_as);
|
||||
|
||||
if (matrix_rows) {
|
||||
matrix_rows[i02 * mapping_stride + counts[i02]] = (struct mmid_row_mapping) { id, iid1 };
|
||||
@@ -4137,31 +4140,10 @@ static inline void scan_expert_ids_n(
|
||||
counts[i02] += 1;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
|
||||
// the matmuls only write the rows that an expert owns, so the rows of the skipped slots are zeroed here
|
||||
static inline void zero_skipped_dst_rows(
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
const struct htp_tensor * dst,
|
||||
uint32_t ne0,
|
||||
size_t nb1,
|
||||
size_t nb2
|
||||
) {
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_data + iid1 * ids->nb[1] + id * ids->nb[0]);
|
||||
if (i02 != -1) {
|
||||
continue;
|
||||
}
|
||||
memset((uint8_t *) (dst->data + id * nb1 + iid1 * nb2), 0, ne0 * sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void scan_expert_ids(
|
||||
static __attribute__((noinline)) uint32_t scan_expert_ids(
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
@@ -4173,23 +4155,25 @@ static inline void scan_expert_ids(
|
||||
|
||||
if (ids_nb0 == 4) {
|
||||
switch (n_ids) {
|
||||
case 8: scan_expert_ids_n(ids, 8, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 4: scan_expert_ids_n(ids, 4, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 2: scan_expert_ids_n(ids, 2, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
default: scan_expert_ids_n(ids, n_ids, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 8: return scan_expert_ids_n(ids, 8, n_as, counts, matrix_rows, mapping_stride);
|
||||
case 4: return scan_expert_ids_n(ids, 4, n_as, counts, matrix_rows, mapping_stride);
|
||||
case 2: return scan_expert_ids_n(ids, 2, n_as, counts, matrix_rows, mapping_stride);
|
||||
default: return scan_expert_ids_n(ids, n_ids, n_as, counts, matrix_rows, mapping_stride);
|
||||
}
|
||||
} else {
|
||||
// Strided fallback
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
uint32_t n_skipped = 0;
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const int32_t * row_ptr = (const int32_t *) (ids_data + iid1 * ids_nb1);
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) ((const uint8_t *) row_ptr + id * ids_nb0);
|
||||
if (i02 == -1) {
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
continue;
|
||||
}
|
||||
assert(i02 >= 0 && i02 < n_as);
|
||||
assert(i02 >= 0 && i02 < (int32_t) n_as);
|
||||
|
||||
if (matrix_rows) {
|
||||
matrix_rows[i02 * mapping_stride + counts[i02]] = (struct mmid_row_mapping) { id, iid1 };
|
||||
@@ -4197,9 +4181,124 @@ static inline void scan_expert_ids(
|
||||
counts[i02] += 1;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) uint32_t count_skipped_ids_single(const struct htp_tensor * ids, uint32_t n_ids) {
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
const size_t ids_nb0 = ids->nb[0];
|
||||
uint32_t n_skipped = 0;
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_data + id * ids_nb0);
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
|
||||
// Zero rows for skipped slots (ids < 0). Matmuls only write rows that an expert owns.
|
||||
static void zero_skipped_dst_rows(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
const struct htp_tensor * dst
|
||||
) {
|
||||
const uint32_t elem_size = (dst->type == HTP_TYPE_F16) ? sizeof(__fp16) : sizeof(float);
|
||||
uint32_t col_start = 0;
|
||||
uint32_t col_count = dst->ne[0];
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const bool can_split = htp_tensor_can_row_partition(dst, elem_size);
|
||||
const uint32_t chunk_elems = (dst->type == HTP_TYPE_F16) ? 64 : 32;
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
dst->ne[0], can_split ? chunk_elems : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
col_start = range.start;
|
||||
col_count = range.count;
|
||||
}
|
||||
|
||||
if (col_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
const size_t ids_nb0 = ids->nb[0];
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const size_t dst_nb1 = dst->nb[1];
|
||||
const size_t dst_nb2 = dst->nb[2];
|
||||
const uint32_t col_offset_bytes = col_start * elem_size;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const uint8_t * ids_row = ids_data + iid1 * ids_nb1;
|
||||
uint8_t * dst_row_group = (uint8_t *) dst->data + iid1 * dst_nb2 + col_offset_bytes;
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_row + id * ids_nb0);
|
||||
if (i02 < 0) {
|
||||
hvx_splat_u(dst_row_group + id * dst_nb1, Q6_V_vzero(), col_count, elem_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int htp_mmid_prepare_mapping(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
uint32_t act_nrows,
|
||||
uint8_t ** out_mapping_buf,
|
||||
uint32_t ** out_matrix_row_counts,
|
||||
struct mmid_row_mapping ** out_matrix_rows,
|
||||
uint32_t * out_mapping_stride,
|
||||
uint32_t * out_n_skipped
|
||||
) {
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
hvx_splat_u(matrix_row_counts, Q6_V_vzero(), n_as, sizeof(uint32_t));
|
||||
n_skipped = scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
hvx_splat_u(matrix_row_counts, Q6_V_vzero(), n_as, sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
} else {
|
||||
n_skipped = count_skipped_ids_single(ids, n_ids);
|
||||
}
|
||||
|
||||
*out_mapping_buf = mapping_buf;
|
||||
*out_matrix_row_counts = matrix_row_counts;
|
||||
*out_matrix_rows = matrix_rows;
|
||||
*out_mapping_stride = mapping_stride;
|
||||
*out_n_skipped = n_skipped;
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_matmul_id(struct htp_ops_context * octx) {
|
||||
htp_matmul_tensors_preamble;
|
||||
|
||||
@@ -4235,41 +4334,22 @@ int op_matmul_id(struct htp_ops_context * octx) {
|
||||
const int n_ids = ids->ne[0]; // n_expert_used
|
||||
const int n_as = ne02; // n_expert
|
||||
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
uint8_t * mapping_buf = NULL;
|
||||
uint32_t * matrix_row_counts = NULL;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
memset(matrix_row_counts, 0, matrix_row_counts_size);
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
memset(matrix_row_counts, 0, n_as * sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
int map_status = htp_mmid_prepare_mapping(octx, ids, n_ids, n_as, act_nrows,
|
||||
&mapping_buf, &matrix_row_counts, &matrix_rows,
|
||||
&mapping_stride, &n_skipped);
|
||||
if (map_status != HTP_STATUS_OK) {
|
||||
return map_status;
|
||||
}
|
||||
|
||||
zero_skipped_dst_rows(ids, n_ids, dst, ne0, nb1, nb2);
|
||||
if (n_skipped > 0) {
|
||||
zero_skipped_dst_rows(octx, ids, n_ids, dst);
|
||||
}
|
||||
|
||||
mmctx->matrix_row_counts = matrix_row_counts;
|
||||
mmctx->matrix_rows = matrix_rows;
|
||||
@@ -4360,38 +4440,26 @@ int op_matmul_id_nx(struct htp_ops_context * octx) {
|
||||
const int n_ids = ids->ne[0];
|
||||
const int n_as = src0->ne[2];
|
||||
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
uint8_t * mapping_buf = NULL;
|
||||
uint32_t * matrix_row_counts = NULL;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
int map_status = htp_mmid_prepare_mapping(octx, ids, n_ids, n_as, act_nrows,
|
||||
&mapping_buf, &matrix_row_counts, &matrix_rows,
|
||||
&mapping_stride, &n_skipped);
|
||||
if (map_status != HTP_STATUS_OK) {
|
||||
return map_status;
|
||||
}
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
memset(matrix_row_counts, 0, matrix_row_counts_size);
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
if (n_skipped > 0) {
|
||||
for (uint32_t p = 0; p < n_weights; ++p) {
|
||||
const struct htp_tensor * restrict d = octx->dsts[p];
|
||||
if (d) {
|
||||
zero_skipped_dst_rows(octx, ids, n_ids, d);
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
memset(matrix_row_counts, 0, n_as * sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
}
|
||||
|
||||
mmctx->matrix_row_counts = matrix_row_counts;
|
||||
|
||||
@@ -507,6 +507,47 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_lightning_indexe
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_mask(
|
||||
ggml_metal_library_t lib,
|
||||
const ggml_tensor * op,
|
||||
bool fill,
|
||||
bool has_score,
|
||||
bool has_blocks,
|
||||
bool has_extra,
|
||||
bool vec) {
|
||||
GGML_ASSERT(op->op == GGML_OP_TOP_K_MASK);
|
||||
|
||||
char base[256];
|
||||
char name[256];
|
||||
|
||||
snprintf(base, 256, "kernel_top_k_mask%s_%s", fill ? "_fill" : "", ggml_type_name(op->type));
|
||||
|
||||
ggml_metal_pipeline_with_params res;
|
||||
|
||||
ggml_metal_cv_t cv = ggml_metal_cv_init();
|
||||
|
||||
if (fill) {
|
||||
snprintf(name, 256, "%s_vec=%d", base, vec ? 1 : 0);
|
||||
|
||||
ggml_metal_cv_set_bool(cv, vec, FC_TOP_K_MASK + 3);
|
||||
} else {
|
||||
snprintf(name, 256, "%s_sc=%d_bl=%d_ex=%d", base, has_score ? 1 : 0, has_blocks ? 1 : 0, has_extra ? 1 : 0);
|
||||
|
||||
ggml_metal_cv_set_bool(cv, has_score, FC_TOP_K_MASK + 0);
|
||||
ggml_metal_cv_set_bool(cv, has_blocks, FC_TOP_K_MASK + 1);
|
||||
ggml_metal_cv_set_bool(cv, has_extra, FC_TOP_K_MASK + 2);
|
||||
}
|
||||
|
||||
res = ggml_metal_library_get_pipeline(lib, name);
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, base, name, cv);
|
||||
}
|
||||
|
||||
ggml_metal_cv_free(cv);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_dsv4_hc(ggml_metal_library_t lib, const ggml_tensor * op) {
|
||||
char name[256];
|
||||
const char * base = nullptr;
|
||||
|
||||
@@ -126,6 +126,7 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_cumsum_ad
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_tri (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_soft_max (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_lightning_indexer (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_mask (ggml_metal_library_t lib, const struct ggml_tensor * op, bool fill, bool has_score, bool has_blocks, bool has_extra, bool vec);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_dsv4_hc (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_ssm_conv (ggml_metal_library_t lib, const struct ggml_tensor * op, int32_t nc, bool use_silu);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_ssm_conv_batched (ggml_metal_library_t lib, const struct ggml_tensor * op, int ssm_conv_bs, int32_t nc, bool use_silu);
|
||||
|
||||
@@ -1555,6 +1555,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
|
||||
switch (op->op) {
|
||||
case GGML_OP_SCALE:
|
||||
return ggml_is_contiguous_rows(op->src[0]) && (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16 || op->src[0]->type == GGML_TYPE_BF16);
|
||||
case GGML_OP_FILL:
|
||||
case GGML_OP_CLAMP:
|
||||
case GGML_OP_SQR:
|
||||
@@ -1834,6 +1835,15 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
ggml_is_contiguous_rows(op->src[1]) &&
|
||||
ggml_is_contiguous_rows(op->src[2]) &&
|
||||
(op->src[3] == NULL || ggml_is_contiguous_rows(op->src[3]));
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
return (op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32) &&
|
||||
op->src[0]->type == op->type &&
|
||||
op->src[0]->nb[0] == ggml_type_size(op->src[0]->type) &&
|
||||
op->nb[0] == ggml_type_size(op->type) &&
|
||||
op->src[1]->type == GGML_TYPE_I32 &&
|
||||
(op->src[2] == NULL || op->src[2]->type == GGML_TYPE_F32) &&
|
||||
(op->src[3] == NULL || op->src[3]->type == GGML_TYPE_I32) &&
|
||||
(op->src[4] == NULL || op->src[4]->type == GGML_TYPE_I32);
|
||||
case GGML_OP_SSM_SCAN:
|
||||
return has_simdgroup_reduction;
|
||||
case GGML_OP_SSM_CONV:
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
#define FC_FLASH_ATTN_EXT_TENSOR 2200
|
||||
#define FC_LIGHTNING_INDEXER 2200
|
||||
#define FC_MUL_MV_MMA 2300
|
||||
#define FC_TOP_K_MASK 2400
|
||||
|
||||
// op-specific constants
|
||||
#define OP_FLASH_ATTN_EXT_NQPSG 8
|
||||
@@ -1295,6 +1296,38 @@ typedef struct {
|
||||
uint64_t nbm3;
|
||||
} ggml_metal_kargs_lightning_indexer;
|
||||
|
||||
typedef struct {
|
||||
int32_t n_kv;
|
||||
int32_t m1;
|
||||
int32_t m2;
|
||||
int32_t m3;
|
||||
int32_t n_top;
|
||||
int32_t n_pick;
|
||||
int32_t n_blk;
|
||||
int32_t n_extra;
|
||||
int64_t n_elems;
|
||||
uint64_t nb1;
|
||||
uint64_t nb2;
|
||||
uint64_t nb3;
|
||||
uint64_t nbm1;
|
||||
uint64_t nbm2;
|
||||
uint64_t nbm3;
|
||||
uint64_t nbk0;
|
||||
uint64_t nbk1;
|
||||
uint64_t nbk2;
|
||||
uint64_t nbk3;
|
||||
uint64_t nbs0;
|
||||
uint64_t nbs1;
|
||||
uint64_t nbs2;
|
||||
uint64_t nbs3;
|
||||
uint64_t nbb0;
|
||||
uint64_t nbb1;
|
||||
uint64_t nbe0;
|
||||
uint64_t nbe1;
|
||||
uint64_t nbe2;
|
||||
uint64_t nbe3;
|
||||
} ggml_metal_kargs_top_k_mask;
|
||||
|
||||
typedef struct {
|
||||
int32_t n_tokens;
|
||||
int32_t n_iter;
|
||||
|
||||
@@ -345,6 +345,10 @@ static int ggml_metal_op_encode_impl(ggml_metal_op_t ctx, int idx) {
|
||||
{
|
||||
n_fuse = ggml_metal_op_lightning_indexer(ctx, idx);
|
||||
} break;
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
{
|
||||
n_fuse = ggml_metal_op_top_k_mask(ctx, idx);
|
||||
} break;
|
||||
case GGML_OP_DSV4_HC_COMB:
|
||||
case GGML_OP_DSV4_HC_PRE:
|
||||
case GGML_OP_DSV4_HC_POST:
|
||||
@@ -1424,6 +1428,124 @@ int ggml_metal_op_lightning_indexer(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ggml_metal_op_top_k_mask(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
ggml_metal_library_t lib = ctx->lib;
|
||||
ggml_metal_encoder_t enc = ctx->enc;
|
||||
|
||||
const ggml_tensor * mask = op->src[0];
|
||||
const ggml_tensor * top_k = op->src[1];
|
||||
const ggml_tensor * score = op->src[2];
|
||||
const ggml_tensor * blocks = op->src[3];
|
||||
const ggml_tensor * extra = op->src[4];
|
||||
|
||||
GGML_ASSERT(op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(mask->type == op->type);
|
||||
GGML_ASSERT(top_k->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(!score || score->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(!blocks || blocks->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(!extra || extra->type == GGML_TYPE_I32);
|
||||
|
||||
const int32_t n_kv = mask->ne[0];
|
||||
const int32_t n_top = (int32_t) top_k->ne[0];
|
||||
const int32_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
|
||||
const int32_t n_blk = blocks ? blocks->ne[0] : 0;
|
||||
const int32_t n_extra = extra ? extra->ne[0] : 0;
|
||||
|
||||
const ggml_metal_buffer_id bid = ggml_metal_get_buffer_id(op);
|
||||
|
||||
// the vector fill stores 4 elements at a time
|
||||
const int32_t ts = ggml_type_size(op->type);
|
||||
const int64_t n_elems = ggml_nelements(op);
|
||||
|
||||
const bool has_score = score != NULL;
|
||||
const bool has_blocks = blocks != NULL;
|
||||
const bool has_extra = extra != NULL;
|
||||
|
||||
const bool vec = n_elems % 4 == 0 && bid.offs % (4*ts) == 0;
|
||||
|
||||
ggml_metal_kargs_top_k_mask args = {
|
||||
/*.n_kv =*/ n_kv,
|
||||
/*.m1 =*/ (int32_t) mask->ne[1],
|
||||
/*.m2 =*/ (int32_t) mask->ne[2],
|
||||
/*.m3 =*/ (int32_t) mask->ne[3],
|
||||
/*.n_top =*/ n_top,
|
||||
/*.n_pick =*/ n_pick,
|
||||
/*.n_blk =*/ n_blk,
|
||||
/*.n_extra =*/ n_extra,
|
||||
/*.n_elems =*/ n_elems,
|
||||
/*.nb1 =*/ op->nb[1],
|
||||
/*.nb2 =*/ op->nb[2],
|
||||
/*.nb3 =*/ op->nb[3],
|
||||
/*.nbm1 =*/ mask->nb[1],
|
||||
/*.nbm2 =*/ mask->nb[2],
|
||||
/*.nbm3 =*/ mask->nb[3],
|
||||
/*.nbk0 =*/ top_k->nb[0],
|
||||
/*.nbk1 =*/ top_k->nb[1],
|
||||
/*.nbk2 =*/ top_k->nb[2],
|
||||
/*.nbk3 =*/ top_k->nb[3],
|
||||
/*.nbs0 =*/ score ? score->nb[0] : 0,
|
||||
/*.nbs1 =*/ score ? score->nb[1] : 0,
|
||||
/*.nbs2 =*/ score ? score->nb[2] : 0,
|
||||
/*.nbs3 =*/ score ? score->nb[3] : 0,
|
||||
/*.nbb0 =*/ blocks ? blocks->nb[0] : 0,
|
||||
/*.nbb1 =*/ blocks ? blocks->nb[1] : 0,
|
||||
/*.nbe0 =*/ extra ? extra->nb[0] : 0,
|
||||
/*.nbe1 =*/ extra ? extra->nb[1] : 0,
|
||||
/*.nbe2 =*/ extra ? extra->nb[2] : 0,
|
||||
/*.nbe3 =*/ extra ? extra->nb[3] : 0,
|
||||
};
|
||||
|
||||
// the fill covers the whole dst, the scatter overwrites the kept cells with the mask values
|
||||
{
|
||||
auto pipeline_fill = ggml_metal_library_get_pipeline_top_k_mask(lib, op, true, false, false, false, vec);
|
||||
|
||||
const int64_t n_fill = n_elems/(vec ? 4 : 1);
|
||||
const int32_t nth_max = ggml_metal_pipeline_max_theads_per_threadgroup(pipeline_fill);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
|
||||
while (nth < nth_max && nth < n_fill) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_fill);
|
||||
ggml_metal_encoder_set_bytes(enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer(enc, bid, 1);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, (n_fill + nth - 1)/nth, 1, 1, nth, 1, 1);
|
||||
|
||||
ggml_metal_op_concurrency_reset(ctx);
|
||||
}
|
||||
|
||||
{
|
||||
auto pipeline = ggml_metal_library_get_pipeline_top_k_mask(lib, op, false, has_score, has_blocks, has_extra, false);
|
||||
|
||||
const int32_t n_max = n_top + n_extra;
|
||||
const int32_t nth_max = ggml_metal_pipeline_max_theads_per_threadgroup(pipeline);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
|
||||
while (nth < nth_max && nth < n_max) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline);
|
||||
ggml_metal_encoder_set_bytes(enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(mask), 1);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(top_k), 2);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(score ? score : mask), 3);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(blocks ? blocks : mask), 4);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(extra ? extra : mask), 5);
|
||||
ggml_metal_encoder_set_buffer(enc, bid, 6);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, op->ne[1], op->ne[2], op->ne[3], nth, 1, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ggml_metal_op_dsv4_hc(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ int ggml_metal_op_get_rows (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_set_rows (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_diag (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_lightning_indexer (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_top_k_mask (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_dsv4_hc (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_soft_max (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_ssm_conv (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -906,3 +906,99 @@ kernel void kernel_dsv4_hc_post_f32(
|
||||
*(device float *) (dst + i0*args.nb_d0 + idst*args.nb_d1 + it*args.nb_d2) = result[idst];
|
||||
}
|
||||
}
|
||||
|
||||
constant bool FC_top_k_mask_has_score [[function_constant(FC_TOP_K_MASK + 0)]];
|
||||
constant bool FC_top_k_mask_has_blocks [[function_constant(FC_TOP_K_MASK + 1)]];
|
||||
constant bool FC_top_k_mask_has_extra [[function_constant(FC_TOP_K_MASK + 2)]];
|
||||
constant bool FC_top_k_mask_vec [[function_constant(FC_TOP_K_MASK + 3)]];
|
||||
|
||||
// the fill runs before the scatter, both are fully parallel over the whole dst and over the picks of one row
|
||||
template <typename T, typename T4>
|
||||
kernel void kernel_top_k_mask_fill(
|
||||
constant ggml_metal_kargs_top_k_mask & args,
|
||||
device char * dst,
|
||||
uint tid[[thread_position_in_grid]],
|
||||
uint ntot[[threads_per_grid]]) {
|
||||
const T ninf = T(-INFINITY);
|
||||
|
||||
if (FC_top_k_mask_vec) {
|
||||
device T4 * dst_4 = (device T4 *) dst;
|
||||
for (int64_t i = tid; i < args.n_elems/4; i += ntot) {
|
||||
dst_4[i] = T4(ninf);
|
||||
}
|
||||
} else {
|
||||
device T * dst_t = (device T *) dst;
|
||||
for (int64_t i = tid; i < args.n_elems; i += ntot) {
|
||||
dst_t[i] = ninf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef decltype(kernel_top_k_mask_fill<half, half4>) kernel_top_k_mask_fill_h_t;
|
||||
typedef decltype(kernel_top_k_mask_fill<float, float4>) kernel_top_k_mask_fill_f_t;
|
||||
|
||||
template [[host_name("kernel_top_k_mask_fill_f16")]] kernel kernel_top_k_mask_fill_h_t kernel_top_k_mask_fill<half, half4>;
|
||||
template [[host_name("kernel_top_k_mask_fill_f32")]] kernel kernel_top_k_mask_fill_f_t kernel_top_k_mask_fill<float, float4>;
|
||||
|
||||
// one threadgroup per row: copy the mask cells of the live picks and of the extra cells over the fill
|
||||
template <typename T, typename T4>
|
||||
kernel void kernel_top_k_mask(
|
||||
constant ggml_metal_kargs_top_k_mask & args,
|
||||
device const char * mask,
|
||||
device const char * top_k,
|
||||
device const char * score,
|
||||
device const char * blocks,
|
||||
device const char * extra,
|
||||
device char * dst,
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint3 ntg_max[[threads_per_threadgroup]]) {
|
||||
const int i1 = tgpig.x;
|
||||
const int i2 = tgpig.y;
|
||||
const int i3 = tgpig.z;
|
||||
|
||||
const int tiitg = tpitg.x;
|
||||
const int ntg = ntg_max.x;
|
||||
|
||||
device const char * mask_row = mask + (i1 % args.m1)*args.nbm1 + (i2 % args.m2)*args.nbm2 + (i3 % args.m3)*args.nbm3;
|
||||
device char * dst_row = dst + i1*args.nb1 + i2*args.nb2 + i3*args.nb3;
|
||||
device const char * top_k_row = top_k + i1*args.nbk1 + i2*args.nbk2 + i3*args.nbk3;
|
||||
|
||||
device const T * mask_row_t = (device const T *) mask_row;
|
||||
device T * dst_row_t = (device T *) dst_row;
|
||||
|
||||
for (int i = tiitg; i < args.n_top; i += ntg) {
|
||||
const int32_t p = *(device const int32_t *) (top_k_row + i*args.nbk0);
|
||||
if (p < 0 || p >= args.n_pick) {
|
||||
continue;
|
||||
}
|
||||
if (FC_top_k_mask_has_score && !(*(device const float *) (score + p*args.nbs0 + i1*args.nbs1 + i2*args.nbs2 + i3*args.nbs3) > -INFINITY)) {
|
||||
continue;
|
||||
}
|
||||
if (FC_top_k_mask_has_blocks) {
|
||||
FOR_UNROLL (int k = 0; k < args.n_blk; ++k) {
|
||||
const int32_t c = *(device const int32_t *) (blocks + k*args.nbb0 + p*args.nbb1);
|
||||
if (c >= 0 && c < args.n_kv) {
|
||||
dst_row_t[c] = mask_row_t[c];
|
||||
}
|
||||
}
|
||||
} else if (p < args.n_kv) {
|
||||
dst_row_t[p] = mask_row_t[p];
|
||||
}
|
||||
}
|
||||
|
||||
if (FC_top_k_mask_has_extra) {
|
||||
for (int i = tiitg; i < args.n_extra; i += ntg) {
|
||||
const int32_t c = *(device const int32_t *) (extra + i*args.nbe0 + i1*args.nbe1 + i2*args.nbe2 + i3*args.nbe3);
|
||||
if (c >= 0 && c < args.n_kv) {
|
||||
dst_row_t[c] = mask_row_t[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef decltype(kernel_top_k_mask<half, half4>) kernel_top_k_mask_h_t;
|
||||
typedef decltype(kernel_top_k_mask<float, float4>) kernel_top_k_mask_f_t;
|
||||
|
||||
template [[host_name("kernel_top_k_mask_f16")]] kernel kernel_top_k_mask_h_t kernel_top_k_mask<half, half4>;
|
||||
template [[host_name("kernel_top_k_mask_f32")]] kernel kernel_top_k_mask_f_t kernel_top_k_mask<float, float4>;
|
||||
|
||||
@@ -323,10 +323,10 @@ kernel void kernel_mul_mm(
|
||||
// if no bounds checks on the output are needed, we can directly write to device memory
|
||||
device float * C = (device float *) dst +
|
||||
(r0 + 32*(sgitg & 1)) + \
|
||||
(r1 + 16*(sgitg >> 1)) * args.ne0 + im*args.ne1*args.ne0;
|
||||
(uint64_t) (r1 + 16*(sgitg >> 1))*args.ne0 + (uint64_t) im*args.ne1*args.ne0;
|
||||
|
||||
for (short i = 0; i < 8; i++) {
|
||||
simdgroup_store(mc[i], C + 8*(i%4) + 8*args.ne0*(i/4), args.ne0, 0, false);
|
||||
simdgroup_store(mc[i], C + 8*(i%4) + (uint64_t) 8*args.ne0*(i/4), args.ne0, 0, false);
|
||||
}
|
||||
} else {
|
||||
// block is smaller than 64x32, we should avoid writing data outside of the matrix
|
||||
@@ -342,7 +342,7 @@ kernel void kernel_mul_mm(
|
||||
|
||||
if (sgitg == 0) {
|
||||
for (int j = tiitg; j < nr1; j += NR1) {
|
||||
device float * D = (device float *) dst + r0 + (r1 + j)*args.ne0 + im*args.ne1*args.ne0;
|
||||
device float * D = (device float *) dst + r0 + (uint64_t) (r1 + j)*args.ne0 + (uint64_t) im*args.ne1*args.ne0;
|
||||
device float4 * D4 = (device float4 *) D;
|
||||
|
||||
threadgroup float * C = temp_str + (j*NR0);
|
||||
@@ -633,7 +633,7 @@ kernel void kernel_mul_mm_id(
|
||||
const int id = ids_i32[idb + lr1];
|
||||
|
||||
const short i11 = (id % args.ne20) % args.ne11;
|
||||
const short i12 = (id / args.ne20);
|
||||
const int i12 = (id / args.ne20);
|
||||
|
||||
device const block_q * x = (device const block_q *)(src0 + args.nb01*(r0 + lr0) + offset0) + offset1;
|
||||
|
||||
@@ -866,9 +866,9 @@ kernel void kernel_mul_mm_id(
|
||||
const int id = ids_i32[idb + j];
|
||||
|
||||
const short ide = id % args.ne20;
|
||||
const short idt = id / args.ne20;
|
||||
const int idt = id / args.ne20;
|
||||
|
||||
device float * D = (device float *) dst + r0 + ide*args.ne0 + idt*args.ne1*args.ne0;
|
||||
device float * D = (device float *) dst + r0 + (uint64_t) ide*args.ne0 + (uint64_t) idt*args.ne1*args.ne0;
|
||||
device float4 * D4 = (device float4 *) D;
|
||||
|
||||
threadgroup float * C = (threadgroup float *) shmem + j*NR0;
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
constant short FC_unary_op [[function_constant(FC_UNARY + 0)]];
|
||||
constant bool FC_unary_cnt[[function_constant(FC_UNARY + 1)]];
|
||||
|
||||
template <typename T, typename U>
|
||||
inline constexpr T ggml_metal_unary_cast(U x) {
|
||||
return (T) x;
|
||||
}
|
||||
|
||||
#if defined(GGML_METAL_HAS_BF16)
|
||||
// MSL has no C-style cast from float vectors to bfloat vectors
|
||||
template <>
|
||||
inline constexpr bfloat4 ggml_metal_unary_cast<bfloat4, float4>(float4 x) {
|
||||
return bfloat4(bfloat(x[0]), bfloat(x[1]), bfloat(x[2]), bfloat(x[3]));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr bfloat4 ggml_metal_unary_cast<bfloat4, float>(float x) {
|
||||
return bfloat4(bfloat(x), bfloat(x), bfloat(x), bfloat(x));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T0, typename T, typename TC>
|
||||
kernel void kernel_unary_impl(
|
||||
constant ggml_metal_kargs_unary & args,
|
||||
@@ -48,79 +66,79 @@ kernel void kernel_unary_impl(
|
||||
const TC x = (TC) src0_ptr[i0];
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SCALE) {
|
||||
dst_ptr[i0] = (T) (args.scale * x + args.bias);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(args.scale * x + args.bias);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_FILL) {
|
||||
dst_ptr[i0] = (T) args.val;
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(args.val);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_CLAMP) {
|
||||
dst_ptr[i0] = (T) clamp(x, args.min, args.max);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(clamp(x, args.min, args.max));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SQR) {
|
||||
dst_ptr[i0] = (T) (x * x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * x);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SQRT) {
|
||||
dst_ptr[i0] = (T) sqrt(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(sqrt(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SIN) {
|
||||
dst_ptr[i0] = (T) sin(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(sin(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_COS) {
|
||||
dst_ptr[i0] = (T) cos(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(cos(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_LOG) {
|
||||
dst_ptr[i0] = (T) log(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(log(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_LEAKY_RELU) {
|
||||
dst_ptr[i0] = (T) (TC(x > 0)*x + TC(x <= 0)*(x * args.slope));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(TC(x > 0)*x + TC(x <= 0)*(x * args.slope));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_TANH) {
|
||||
dst_ptr[i0] = (T) precise::tanh(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(precise::tanh(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_RELU) {
|
||||
dst_ptr[i0] = (T) fmax(0, x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fmax(0, x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SIGMOID) {
|
||||
dst_ptr[i0] = (T) (1 / (1 + exp(-x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(1 / (1 + exp(-x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU) {
|
||||
dst_ptr[i0] = (T) (0.5*x*(1 + precise::tanh(SQRT_2_OVER_PI*x*(1 + GELU_COEF_A*x*x))));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(0.5*x*(1 + precise::tanh(SQRT_2_OVER_PI*x*(1 + GELU_COEF_A*x*x))));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU_ERF) {
|
||||
dst_ptr[i0] = (T) (0.5*x*(1 + erf_approx(SQRT_2_INV*x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(0.5*x*(1 + erf_approx(SQRT_2_INV*x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU_QUICK) {
|
||||
dst_ptr[i0] = (T) (x * (1/(1 + exp(GELU_QUICK_COEF*x))));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * (1/(1 + exp(GELU_QUICK_COEF*x))));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SILU) {
|
||||
dst_ptr[i0] = (T) (x / (1 + exp(-x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x / (1 + exp(-x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ELU) {
|
||||
dst_ptr[i0] = (T) elu_approx(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(elu_approx(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_NEG) {
|
||||
dst_ptr[i0] = (T) -x;
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(-x);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ABS) {
|
||||
dst_ptr[i0] = (T) fabs(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fabs(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SGN) {
|
||||
@@ -132,40 +150,40 @@ kernel void kernel_unary_impl(
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_HARDSWISH) {
|
||||
dst_ptr[i0] = (T) (x * fmax(0, fmin(1, x/6 + 0.5)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * fmax(0, fmin(1, x/6 + 0.5)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_HARDSIGMOID) {
|
||||
dst_ptr[i0] = (T) fmax(0, fmin(1, x/6 + 0.5));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fmax(0, fmin(1, x/6 + 0.5)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_EXP) {
|
||||
dst_ptr[i0] = (T) exp(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(exp(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SOFTPLUS) {
|
||||
dst_ptr[i0] = (T) select(log(1 + exp(x)), x, x > 20);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(select(log(1 + exp(x)), x, x > 20));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_EXPM1) {
|
||||
// TODO: precise implementation
|
||||
dst_ptr[i0] = (T) (exp(x) - 1);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(exp(x) - 1);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_FLOOR) {
|
||||
dst_ptr[i0] = (T) floor(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(floor(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_CEIL) {
|
||||
dst_ptr[i0] = (T) ceil(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(ceil(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ROUND) {
|
||||
dst_ptr[i0] = (T) round(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(round(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_TRUNC) {
|
||||
dst_ptr[i0] = (T) trunc(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(trunc(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_XIELU) {
|
||||
@@ -174,7 +192,7 @@ kernel void kernel_unary_impl(
|
||||
const TC clamped = fmin(xi, TC(args.val));
|
||||
const TC y_pos = TC(args.scale) * xi * xi + TC(args.bias) * xi;
|
||||
const TC y_neg = (exp(clamped) - TC(1.0f) - xi) * TC(args.slope) + TC(args.bias) * xi;
|
||||
dst_ptr[i0] = (T) (gate * y_pos + (TC(1.0f) - gate) * y_neg);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(gate * y_pos + (TC(1.0f) - gate) * y_neg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +206,10 @@ template [[host_name("kernel_unary_f32_f32")]] kernel kernel_unary_t kernel_un
|
||||
template [[host_name("kernel_unary_f32_f32_4")]] kernel kernel_unary_t kernel_unary_impl<float4, float4, float4>;
|
||||
template [[host_name("kernel_unary_f16_f16")]] kernel kernel_unary_t kernel_unary_impl<half, half, float>;
|
||||
template [[host_name("kernel_unary_f16_f16_4")]] kernel kernel_unary_t kernel_unary_impl<half4, half4, float4>;
|
||||
#if defined(GGML_METAL_HAS_BF16)
|
||||
template [[host_name("kernel_unary_bf16_bf16")]] kernel kernel_unary_t kernel_unary_impl<bfloat, bfloat, float>;
|
||||
template [[host_name("kernel_unary_bf16_bf16_4")]] kernel kernel_unary_t kernel_unary_impl<bfloat4, bfloat4, float4>;
|
||||
#endif
|
||||
|
||||
kernel void kernel_silu_back_f32(
|
||||
constant ggml_metal_kargs_silu_back & args,
|
||||
|
||||
@@ -104,7 +104,13 @@ set(GGML_OPENCL_KERNELS
|
||||
gemm_noshuffle_q2_k_q8_1_dp4a
|
||||
gemv_noshuffle_q2_k_f32
|
||||
gemm_noshuffle_q2_k_f32
|
||||
gemm_noshuffle_q3_k_f32
|
||||
gemm_noshuffle_q3_k_q8_1_dp4a
|
||||
gemv_noshuffle_q3_k_f32
|
||||
mul_mv_q2_k_f32
|
||||
mul_mv_q3_k_f32
|
||||
mul_mv_q3_k_f32_flat
|
||||
mul_mm_q3_k_f32_l4_lm
|
||||
mul_mv_q2_k_f32_flat
|
||||
mul_mm_q2_k_f32_l4_lm
|
||||
mul_mv_q4_k_f32
|
||||
@@ -128,6 +134,7 @@ set(GGML_OPENCL_KERNELS
|
||||
mul_mv_id_q8_0_f32_flat
|
||||
mul_mv_id_mxfp4_f32
|
||||
mul_mv_id_mxfp4_f32_flat
|
||||
mul_mat_id_utils
|
||||
gemm_moe_q4_0_f32_ns
|
||||
gemm_moe_q4_0_q8_1_dp4a
|
||||
gemv_moe_q4_0_f32_ns
|
||||
|
||||
@@ -975,6 +975,17 @@ struct ggml_backend_opencl_context {
|
||||
int q2_k_mv_rows = 4;
|
||||
int q2_k_mv_nsg = 4;
|
||||
cl_kernel kernel_mul_mv_q2_K_f32;
|
||||
cl_kernel kernel_convert_block_q3_K, kernel_restore_block_q3_K;
|
||||
cl_kernel kernel_mul_mv_q3_K_f32_flat;
|
||||
cl_kernel kernel_mul_mm_q3_k_f32_l4_lm;
|
||||
cl_kernel kernel_convert_block_q3_k_noshuffle, kernel_restore_block_q3_k_noshuffle;
|
||||
cl_kernel kernel_gemv_noshuffle_q3_k_f32 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_f32 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_q8_1_dp4a = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow = nullptr;
|
||||
int q3_k_mv_rows = 2;
|
||||
int q3_k_mv_nsg = 8;
|
||||
cl_kernel kernel_mul_mv_q3_K_f32;
|
||||
cl_kernel kernel_mul_mv_q4_K_f32;
|
||||
cl_kernel kernel_mul_mv_q4_K_f32_flat;
|
||||
cl_kernel kernel_mul_mv_q5_K_f32;
|
||||
@@ -1542,6 +1553,23 @@ static std::string ggml_cl_q2_k_mm_options(const ggml_backend_opencl_context * b
|
||||
return " -DBK=" + std::to_string(bk) + " -DLM_HALF=" + std::to_string(half ? 1 : 0);
|
||||
}
|
||||
|
||||
static std::string ggml_cl_q3_k_mm_options(const ggml_backend_opencl_context * backend_ctx) {
|
||||
const bool x2 = backend_ctx->gpu_family == ADRENO &&
|
||||
(backend_ctx->adreno_gen == ADRENO_GPU_GEN::A8X || backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
int bk = x2 ? 16 : 32;
|
||||
bool half = x2;
|
||||
if (const char * env = getenv("GGML_OPENCL_LM_BK")) {
|
||||
const int value = atoi(env);
|
||||
if (value == 16 || value == 32) {
|
||||
bk = value;
|
||||
}
|
||||
}
|
||||
if (const char * env = getenv("GGML_OPENCL_LM_HALF")) {
|
||||
half = atoi(env) != 0;
|
||||
}
|
||||
return " -DBK=" + std::to_string(bk) + " -DLM_HALF=" + std::to_string(half ? 1 : 0);
|
||||
}
|
||||
|
||||
static void load_cl_kernels_argsort(ggml_backend_opencl_context *backend_ctx) {
|
||||
// compiler options for general kernels
|
||||
auto opencl_c_std =
|
||||
@@ -1815,6 +1843,21 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mat_id_utils
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mat_id_utils.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mat_id_utils.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_moe_zero_dst = clCreateKernel(prog, "kernel_moe_zero_dst", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// moe_add_id_glu
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -1992,6 +2035,10 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q5_K_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q5_K_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q2_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q2_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q2_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q2_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q3_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q3_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q3_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q3_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q3_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q3_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q3_K = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q3_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q2_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q2_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q2_K = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q2_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q6_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q6_K", &err), err));
|
||||
@@ -2398,6 +2445,53 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q3_k_f32_flat
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mv_q3_k_f32_flat.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mv_q3_k_f32_flat.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_mul_mv_q3_K_f32_flat = clCreateKernel(prog, "kernel_mul_mv_q3_K_f32_flat", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mm_q3_k_f32_l4_lm
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mm_q3_k_f32_l4_lm.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mm_q3_k_f32_l4_lm.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts + ggml_cl_q3_k_mm_options(backend_ctx));
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_q3_k_f32_l4_lm = clCreateKernel(prog, "kernel_mul_mm_q3_k_f32_l4_lm", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mv_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mv_q3_k_f32.cl");
|
||||
#endif
|
||||
cl_program prog =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_mul_mv_q3_K_f32 = clCreateKernel(prog, "kernel_mul_mv_q3_K_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q4_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -4206,6 +4300,80 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q3_k_q8_1_dp4a
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q3_k_q8_1_dp4a.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q3_k_q8_1_dp4a.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts + " -DTILESIZE_N=8");
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q3_k_f32.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_f32 = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemv_noshuffle_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemv_noshuffle_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemv_noshuffle_q3_k_f32.cl");
|
||||
#endif
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_MV_R")) {
|
||||
const int rows = atoi(env);
|
||||
if (rows == 1 || rows == 2 || rows == 4) {
|
||||
backend_ctx->q3_k_mv_rows = rows;
|
||||
}
|
||||
}
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_MV_NSG")) {
|
||||
const int nsg = atoi(env);
|
||||
if (nsg == 1 || nsg == 2 || nsg == 4 || nsg == 8) {
|
||||
backend_ctx->q3_k_mv_nsg = nsg;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
const std::string opts = compile_opts + " -DQ3_K_MV_R=" + std::to_string(backend_ctx->q3_k_mv_rows) +
|
||||
" -DQ3_K_MV_NSG=" + std::to_string(backend_ctx->q3_k_mv_nsg);
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), opts);
|
||||
cl_kernel kernel = clCreateKernel(prog, "kernel_gemv_noshuffle_q3_k_f32", &err);
|
||||
CL_CHECK(err);
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
size_t max_workgroup;
|
||||
CL_CHECK(clGetKernelWorkGroupInfo(kernel, backend_ctx->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof(max_workgroup), &max_workgroup, nullptr));
|
||||
if (64*(size_t)backend_ctx->q3_k_mv_nsg <= max_workgroup) {
|
||||
backend_ctx->kernel_gemv_noshuffle_q3_k_f32 = kernel;
|
||||
break;
|
||||
}
|
||||
CL_CHECK(clReleaseKernel(kernel));
|
||||
GGML_ASSERT(backend_ctx->q3_k_mv_nsg > 1);
|
||||
backend_ctx->q3_k_mv_nsg /= 2;
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q2_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -5737,7 +5905,6 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), CL_moe_compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_moe_histogram = clCreateKernel(prog, "kernel_moe_histogram", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_zero_dst = clCreateKernel(prog, "kernel_moe_zero_dst", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_scan = clCreateKernel(prog, "kernel_moe_scan", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_fill = clCreateKernel(prog, "kernel_moe_fill", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_scatter = clCreateKernel(prog, "kernel_moe_scatter", &err), err));
|
||||
@@ -8245,6 +8412,27 @@ struct ggml_tensor_extra_cl_q2_K {
|
||||
}
|
||||
};
|
||||
|
||||
struct ggml_tensor_extra_cl_q3_K {
|
||||
bool noshuffle = false;
|
||||
cl_mem qs = nullptr; // quantized values
|
||||
cl_mem hm = nullptr; // high bits
|
||||
cl_mem sc = nullptr; // packed 6-bit scales
|
||||
cl_mem d = nullptr; // super-block scales
|
||||
|
||||
size_t size_qs = 0, size_hm = 0, size_sc = 0, size_d = 0;
|
||||
|
||||
~ggml_tensor_extra_cl_q3_K() { reset(); }
|
||||
|
||||
void reset() {
|
||||
noshuffle = false;
|
||||
if (qs != nullptr) { CL_CHECK(clReleaseMemObject(qs)); qs = nullptr; }
|
||||
if (hm != nullptr) { CL_CHECK(clReleaseMemObject(hm)); hm = nullptr; }
|
||||
if (sc != nullptr) { CL_CHECK(clReleaseMemObject(sc)); sc = nullptr; }
|
||||
if (d != nullptr) { CL_CHECK(clReleaseMemObject(d)); d = nullptr; }
|
||||
size_qs = size_hm = size_sc = size_d = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ggml_tensor_extra_cl_q6_K {
|
||||
// Lower 4 bits of quantized weights.
|
||||
cl_mem ql = nullptr;
|
||||
@@ -9345,6 +9533,24 @@ static bool ggml_cl_q2_k_use_noshuffle(const ggml_backend_opencl_context * backe
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ggml_cl_q3_k_use_noshuffle(const ggml_backend_opencl_context * backend_ctx, const ggml_tensor * tensor) {
|
||||
#if defined(GGML_OPENCL_SOA_Q) && defined(GGML_OPENCL_USE_ADRENO_KERNELS)
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_NOSHUFFLE")) {
|
||||
if (env[0] == '0') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return tensor->type == GGML_TYPE_Q3_K &&
|
||||
backend_ctx->gpu_family == ADRENO &&
|
||||
ggml_is_contiguous(tensor) && !tensor->view_src &&
|
||||
use_adreno_kernels(backend_ctx, tensor);
|
||||
#else
|
||||
GGML_UNUSED(backend_ctx);
|
||||
GGML_UNUSED(tensor);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool use_adreno_moe_kernels(const ggml_backend_opencl_context *backend_ctx, const ggml_tensor *tensor) {
|
||||
// The moe weight repack kernels *_trans4_ns alias a private ushort8 through a uchar*.
|
||||
// Certain compilers (found with some A7x and A6x) miscompiles this, corrupting the weights.
|
||||
@@ -9868,6 +10074,12 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q4_0) {
|
||||
// Non-contig src0 routes through on-device dequant-to-f16.
|
||||
return op->src[1]->type == GGML_TYPE_F32;
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q3_K) {
|
||||
const ggml_tensor * weight = op->src[0]->view_src;
|
||||
if (weight && ggml_cl_q3_k_use_noshuffle(backend_ctx, weight)) {
|
||||
return false;
|
||||
}
|
||||
return op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous_rows(op->src[1]);
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q2_K) {
|
||||
const ggml_tensor * weight = op->src[0]->view_src;
|
||||
if (weight && ggml_cl_q2_k_use_noshuffle(backend_ctx, weight)) {
|
||||
@@ -10254,12 +10466,18 @@ struct ggml_backend_opencl_buffer_context {
|
||||
for (ggml_tensor_extra_cl_q2_K * e : temp_tensor_extras_q2_K) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q6_K * e : temp_tensor_extras_q6_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q2_K * e : temp_tensor_extras_q2_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q5_K * e : temp_tensor_extras_q5_K) {
|
||||
delete e;
|
||||
}
|
||||
@@ -10448,6 +10666,21 @@ struct ggml_backend_opencl_buffer_context {
|
||||
return extra;
|
||||
}
|
||||
|
||||
ggml_tensor_extra_cl_q3_K * ggml_opencl_alloc_temp_tensor_extra_q3_K() {
|
||||
ggml_tensor_extra_cl_q3_K * extra;
|
||||
if (temp_tensor_extras_q3_K.empty()) {
|
||||
extra = new ggml_tensor_extra_cl_q3_K();
|
||||
} else {
|
||||
extra = temp_tensor_extras_q3_K.back();
|
||||
temp_tensor_extras_q3_K.pop_back();
|
||||
}
|
||||
|
||||
temp_tensor_extras_q3_K_in_use.push_back(extra);
|
||||
|
||||
extra->reset();
|
||||
return extra;
|
||||
}
|
||||
|
||||
ggml_tensor_extra_cl_q6_K * ggml_opencl_alloc_temp_tensor_extra_q6_K() {
|
||||
ggml_tensor_extra_cl_q6_K * extra;
|
||||
if (temp_tensor_extras_q6_K.empty()) {
|
||||
@@ -10529,6 +10762,11 @@ struct ggml_backend_opencl_buffer_context {
|
||||
}
|
||||
temp_tensor_extras_q2_K_in_use.clear();
|
||||
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K_in_use) {
|
||||
temp_tensor_extras_q3_K.push_back(e);
|
||||
}
|
||||
temp_tensor_extras_q3_K_in_use.clear();
|
||||
|
||||
q8_0_soa_tensors.clear();
|
||||
q4_0_soa_tensors.clear();
|
||||
}
|
||||
@@ -10561,7 +10799,9 @@ struct ggml_backend_opencl_buffer_context {
|
||||
std::vector<ggml_tensor_extra_cl_q5_K *> temp_tensor_extras_q5_K;
|
||||
std::vector<ggml_tensor_extra_cl_q5_K *> temp_tensor_extras_q5_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q2_K *> temp_tensor_extras_q2_K;
|
||||
std::vector<ggml_tensor_extra_cl_q3_K *> temp_tensor_extras_q3_K;
|
||||
std::vector<ggml_tensor_extra_cl_q2_K *> temp_tensor_extras_q2_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q3_K *> temp_tensor_extras_q3_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q6_K *> temp_tensor_extras_q6_K;
|
||||
std::vector<ggml_tensor_extra_cl_q6_K *> temp_tensor_extras_q6_K_in_use;
|
||||
|
||||
@@ -10818,6 +11058,91 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q3_K) {
|
||||
GGML_ASSERT(offset == 0 && size == ggml_nbytes(tensor));
|
||||
ggml_tensor_extra_cl * extra_orig = (ggml_tensor_extra_cl *)tensor->extra;
|
||||
GGML_ASSERT(extra_orig && "Tensors in OpenCL backend should have been allocated and initialized");
|
||||
|
||||
ggml_backend_opencl_buffer_context * ctx = (ggml_backend_opencl_buffer_context *) buffer->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra = ctx->ggml_opencl_alloc_temp_tensor_extra_q3_K();
|
||||
extra->noshuffle = ggml_cl_q3_k_use_noshuffle(backend_ctx, tensor);
|
||||
|
||||
const size_t blck = (size_t)ggml_blck_size(tensor->type);
|
||||
const size_t n_blk = ggml_nelements(tensor) / blck;
|
||||
|
||||
const size_t size_qs = n_blk * (blck/4);
|
||||
const size_t size_hm = n_blk * (blck/8);
|
||||
const size_t size_sc = n_blk * 12;
|
||||
const size_t size_d = n_blk * sizeof(ggml_fp16_t);
|
||||
GGML_ASSERT(size_qs + size_hm + size_sc + size_d == ggml_nbytes(tensor) && "Incorrect tensor size");
|
||||
|
||||
cl_int err;
|
||||
cl_mem data_device = clCreateBuffer(context, CL_MEM_READ_WRITE,
|
||||
ggml_nbytes(tensor), NULL, &err);
|
||||
CL_CHECK(err);
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, data_device, CL_TRUE, 0,
|
||||
ggml_nbytes(tensor), data, 0, NULL, NULL));
|
||||
|
||||
cl_buffer_region region;
|
||||
region.origin = align_to(extra_orig->offset + tensor->view_offs + offset, backend_ctx->alignment);
|
||||
region.size = size_qs;
|
||||
CL_CHECK((extra->qs = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
auto previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_qs, backend_ctx->alignment);
|
||||
region.size = size_hm;
|
||||
CL_CHECK((extra->hm = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_hm, backend_ctx->alignment);
|
||||
region.size = size_sc;
|
||||
CL_CHECK((extra->sc = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_sc, backend_ctx->alignment);
|
||||
region.size = size_d;
|
||||
CL_CHECK((extra->d = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
|
||||
cl_kernel kernel = extra->noshuffle ? backend_ctx->kernel_convert_block_q3_k_noshuffle
|
||||
: backend_ctx->kernel_convert_block_q3_K;
|
||||
cl_ulong nb_arg = (cl_ulong)n_blk;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &extra->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &nb_arg));
|
||||
|
||||
size_t global_work_size[] = { (size_t)CEIL_DIV(n_blk, 64)*64, 1, 1 };
|
||||
size_t local_work_size[] = { 64, 1, 1 };
|
||||
|
||||
cl_event evt;
|
||||
CL_CHECK(clEnqueueNDRangeKernel(queue, kernel, 3, NULL, global_work_size, local_work_size, 0, NULL, &evt));
|
||||
CL_CHECK(clWaitForEvents(1, &evt));
|
||||
CL_CHECK(clReleaseEvent(evt));
|
||||
CL_CHECK(clReleaseMemObject(data_device));
|
||||
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
if (extra->noshuffle) {
|
||||
const int m = tensor->ne[1];
|
||||
const int k = tensor->ne[0];
|
||||
transpose_2d_as_8b(backend_ctx, extra->qs, extra->qs, size_qs, k/4, m, true, true);
|
||||
transpose_2d_as_8b(backend_ctx, extra->hm, extra->hm, size_hm, k/8, m, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_32_buf, extra->sc, extra->sc, size_sc, 3*k/(int)blck, m, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_16_buf, extra->d, extra->d, size_d, k/(int)blck, m, true, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
extra->size_qs = size_qs;
|
||||
extra->size_hm = size_hm;
|
||||
extra->size_sc = size_sc;
|
||||
extra->size_d = size_d;
|
||||
|
||||
tensor->extra = extra;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q1_0) {
|
||||
ggml_tensor_extra_cl * extra_orig = (ggml_tensor_extra_cl *)tensor->extra;
|
||||
GGML_ASSERT(extra_orig && "Tesnors in OpenCL backend should have been allocated and initialized");
|
||||
@@ -10901,7 +11226,16 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
// conversion kernel.
|
||||
if (tensor->type == GGML_TYPE_Q4_0) {
|
||||
// Views can't SoA-ify here — parent owns the layout (see q8_0 guard).
|
||||
// A view of an AoS tensor (the KV cache, written by a state restore)
|
||||
// is written in place.
|
||||
if (tensor->view_src != nullptr || !ggml_is_contiguous(tensor)) {
|
||||
if (!ggml_cl_is_q4_0_soa(tensor)) {
|
||||
ggml_tensor_extra_cl * extra_aos = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra_aos->data_device, CL_TRUE,
|
||||
extra_aos->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Tensors should have been preallocated, therefore they should
|
||||
@@ -11605,7 +11939,16 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
}
|
||||
if (tensor->type == GGML_TYPE_Q8_0) {
|
||||
// Views share the parent's buffer; parent owns SoA conversion.
|
||||
// A view of an AoS tensor (the KV cache, written by a state restore)
|
||||
// is written in place.
|
||||
if (tensor->view_src != nullptr || !ggml_is_contiguous(tensor)) {
|
||||
if (!ggml_cl_is_q8_0_soa(tensor)) {
|
||||
ggml_tensor_extra_cl * extra_aos = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra_aos->data_device, CL_TRUE,
|
||||
extra_aos->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12440,7 +12783,7 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
GGML_ASSERT(extra);
|
||||
|
||||
cl_ulong n_elements = size / sizeof(ggml_fp16_t);
|
||||
cl_ulong off_dst = (extra->offset + offset) / sizeof(ggml_fp16_t);
|
||||
cl_ulong off_dst = (extra->offset + tensor->view_offs + offset) / sizeof(ggml_fp16_t);
|
||||
|
||||
cl_int err;
|
||||
cl_mem data_device = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
|
||||
@@ -12468,8 +12811,9 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
ggml_tensor_extra_cl * extra = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
GGML_ASSERT(extra);
|
||||
|
||||
// a view reuses its parent's extra, whose offset does not include view_offs
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra->data_device, CL_TRUE, extra->offset + offset,
|
||||
queue, extra->data_device, CL_TRUE, extra->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
|
||||
GGML_UNUSED(buffer);
|
||||
@@ -12534,6 +12878,56 @@ static void ggml_backend_opencl_buffer_get_tensor(ggml_backend_buffer_t buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q3_K) {
|
||||
ggml_tensor_extra_cl_q3_K * extra = (ggml_tensor_extra_cl_q3_K *)tensor->extra;
|
||||
|
||||
const size_t blck = (size_t)ggml_blck_size(tensor->type);
|
||||
|
||||
ggml_cl_buffer buf_unpacked;
|
||||
buf_unpacked.allocate(backend_ctx->context, ggml_nbytes(tensor));
|
||||
|
||||
const size_t n_blk = (size_t)ggml_nelements(tensor) / blck;
|
||||
cl_ulong nb_arg = (cl_ulong)n_blk;
|
||||
|
||||
cl_mem qs = extra->qs, hm = extra->hm, sc = extra->sc, d = extra->d;
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
ggml_cl_buffer buf_qs, buf_hm, buf_sc, buf_d;
|
||||
if (extra->noshuffle) {
|
||||
buf_qs.allocate(context, extra->size_qs);
|
||||
buf_hm.allocate(context, extra->size_hm);
|
||||
buf_sc.allocate(context, extra->size_sc);
|
||||
buf_d.allocate(context, extra->size_d);
|
||||
qs = buf_qs.buffer;
|
||||
hm = buf_hm.buffer;
|
||||
sc = buf_sc.buffer;
|
||||
d = buf_d.buffer;
|
||||
const int m = tensor->ne[1];
|
||||
const int k = tensor->ne[0];
|
||||
transpose_2d_as_8b(backend_ctx, extra->qs, qs, extra->size_qs, m, k/4, true, true);
|
||||
transpose_2d_as_8b(backend_ctx, extra->hm, hm, extra->size_hm, m, k/8, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_32_buf, extra->sc, sc, extra->size_sc, m, 3*k/(int)blck, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_16_buf, extra->d, d, extra->size_d, m, k/(int)blck, true, true);
|
||||
}
|
||||
#endif
|
||||
cl_kernel kernel = extra->noshuffle ? backend_ctx->kernel_restore_block_q3_k_noshuffle
|
||||
: backend_ctx->kernel_restore_block_q3_K;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &buf_unpacked.buffer));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &nb_arg));
|
||||
|
||||
size_t gws[] = { (n_blk + 63) / 64 * 64 };
|
||||
size_t lws[] = { 64 };
|
||||
cl_event evt;
|
||||
CL_CHECK(clEnqueueNDRangeKernel(queue, kernel, 1, NULL, gws, lws, 0, NULL, &evt));
|
||||
CL_CHECK(clWaitForEvents(1, &evt));
|
||||
CL_CHECK(clReleaseEvent(evt));
|
||||
CL_CHECK(clEnqueueReadBuffer(queue, buf_unpacked.buffer, CL_TRUE, offset, size, data, 0, NULL, NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q1_0) {
|
||||
ggml_tensor_extra_cl_q1_0 * extra = (ggml_tensor_extra_cl_q1_0 *)tensor->extra;
|
||||
|
||||
@@ -24905,6 +25299,65 @@ static bool ggml_cl_mul_mat_q2_K_dp4a(ggml_backend_t backend, const ggml_tensor
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ggml_cl_mul_mat_q3_K_dp4a(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra0 = (ggml_tensor_extra_cl_q3_K *)src0->extra;
|
||||
ggml_tensor_extra_cl * extra1 = (ggml_tensor_extra_cl *)src1->extra;
|
||||
ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *)dst->extra;
|
||||
|
||||
cl_ulong offset1 = extra1->offset + src1->view_offs;
|
||||
cl_ulong offsetd = extrad->offset + dst->view_offs;
|
||||
const int m = src0->ne[1];
|
||||
const int n = src1->ne[1];
|
||||
const int k = src0->ne[0];
|
||||
|
||||
const int tile_n = n <= 16 ? 8 : 32;
|
||||
cl_kernel gemm = n <= 16 ? backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow
|
||||
: backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a;
|
||||
const char * env = getenv("GGML_OPENCL_Q3_K_DENSE_DP4A");
|
||||
if (!gemm || !backend_ctx->has_integer_dot || (env && env[0] == '0') ||
|
||||
n <= 1 || !ggml_is_contiguous(src1) || offset1 % backend_ctx->alignment != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cl_int err;
|
||||
cl_buffer_region region = { (size_t)offset1, (size_t)k*n*sizeof(float) };
|
||||
cl_mem activation = clCreateSubBuffer(extra1->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
const size_t n_blocks = (size_t)n*(k/32);
|
||||
backend_ctx->prealloc_moe_qa.allocate(backend_ctx->context, (size_t)n*k);
|
||||
backend_ctx->prealloc_moe_da.allocate(backend_ctx->context, n_blocks*sizeof(cl_half));
|
||||
backend_ctx->prealloc_moe_sa.allocate(backend_ctx->context, n_blocks*sizeof(cl_half));
|
||||
|
||||
cl_int nb = n_blocks;
|
||||
cl_kernel quant = backend_ctx->kernel_quant_a_q8_1;
|
||||
CL_CHECK(clSetKernelArg(quant, 0, sizeof(cl_mem), &activation));
|
||||
CL_CHECK(clSetKernelArg(quant, 1, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 2, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 3, sizeof(cl_mem), &backend_ctx->prealloc_moe_sa.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 4, sizeof(cl_int), &nb));
|
||||
size_t q_global[] = { CEIL_DIV(n_blocks, 64)*64 };
|
||||
size_t q_local[] = { 64 };
|
||||
backend_ctx->enqueue_ndrange_kernel(quant, 1, q_global, q_local, dst);
|
||||
|
||||
CL_CHECK(clSetKernelArg(gemm, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(gemm, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(gemm, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(gemm, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(gemm, 4, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(gemm, 5, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(gemm, 6, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemm, 7, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(gemm, 8, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(gemm, 9, sizeof(int), &n));
|
||||
CL_CHECK(clSetKernelArg(gemm, 10, sizeof(int), &k));
|
||||
size_t global[] = { 64, (size_t)CEIL_DIV(m, 64), (size_t)CEIL_DIV(n, tile_n) };
|
||||
size_t local[] = { 64, 1, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(gemm, 3, global, local, dst);
|
||||
CL_CHECK(clReleaseMemObject(activation));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ggml_cl_mul_mat_q2_k_f32_adreno(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q2_K * extra0 = (ggml_tensor_extra_cl_q2_K *)src0->extra;
|
||||
@@ -25004,6 +25457,108 @@ static void ggml_cl_mul_mat_q2_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
}
|
||||
|
||||
static void ggml_cl_mul_mat_q3_k_f32_adreno(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra0 = (ggml_tensor_extra_cl_q3_K *)src0->extra;
|
||||
ggml_tensor_extra_cl * extra1 = (ggml_tensor_extra_cl *)src1->extra;
|
||||
ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *)dst->extra;
|
||||
|
||||
cl_ulong offset1 = extra1->offset + src1->view_offs;
|
||||
cl_ulong offsetd = extrad->offset + dst->view_offs;
|
||||
const int m = src0->ne[1];
|
||||
const int n = src1->ne[1];
|
||||
const int k = src0->ne[0];
|
||||
|
||||
if (n == 1 || !ggml_is_contiguous(src1) || offset1 % backend_ctx->alignment != 0) {
|
||||
cl_kernel gemv = backend_ctx->kernel_gemv_noshuffle_q3_k_f32;
|
||||
const int stride_d = dst->ne[0];
|
||||
const int stride_b = src1->nb[1]/sizeof(float);
|
||||
CL_CHECK(clSetKernelArg(gemv, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(gemv, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(gemv, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(gemv, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(gemv, 4, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemv, 5, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(gemv, 6, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemv, 7, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(gemv, 8, sizeof(int), &k));
|
||||
CL_CHECK(clSetKernelArg(gemv, 9, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(gemv, 10, sizeof(int), &stride_b));
|
||||
CL_CHECK(clSetKernelArg(gemv, 11, sizeof(int), &stride_d));
|
||||
const size_t rows = 64*(size_t)backend_ctx->q3_k_mv_rows;
|
||||
const size_t nsg = backend_ctx->q3_k_mv_nsg;
|
||||
size_t global[] = { CEIL_DIV((size_t)m, rows)*64, (size_t)n*nsg, 1 };
|
||||
size_t local[] = { 64, nsg, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(gemv, 3, global, local, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_cl_mul_mat_q3_K_dp4a(backend, src0, src1, dst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cl_context context = backend_ctx->context;
|
||||
cl_int err;
|
||||
cl_buffer_region region = { (size_t)offset1, (size_t)k*n*sizeof(float) };
|
||||
cl_mem b_sub_buf = clCreateSubBuffer(extra1->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
cl_image_format img_fmt = { CL_RGBA, CL_FLOAT };
|
||||
cl_image_desc img_desc = {};
|
||||
img_desc.image_type = CL_MEM_OBJECT_IMAGE1D_BUFFER;
|
||||
img_desc.image_width = (size_t)k*n/4;
|
||||
img_desc.buffer = b_sub_buf;
|
||||
cl_mem b_img = clCreateImage(context, CL_MEM_READ_ONLY, &img_fmt, &img_desc, nullptr, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const int padded_n = CEIL_DIV(n, 8)*8;
|
||||
region.origin = 0;
|
||||
region.size = (size_t)k*padded_n*sizeof(cl_half);
|
||||
backend_ctx->prealloc_act_trans.allocate(context, region.size);
|
||||
cl_mem b_sub_buf_trans = clCreateSubBuffer(backend_ctx->prealloc_act_trans.buffer, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
img_fmt = { CL_RGBA, CL_HALF_FLOAT };
|
||||
img_desc.image_width = (size_t)k*padded_n/4;
|
||||
img_desc.buffer = b_sub_buf_trans;
|
||||
cl_mem b_img_trans = clCreateImage(context, 0, &img_fmt, &img_desc, nullptr, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const int height_b = std::max(n/4, 1);
|
||||
const int width_b = k/4;
|
||||
const int padded_height_b = padded_n/4;
|
||||
cl_kernel kernel = backend_ctx->kernel_transpose_32_16;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &b_img));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &b_img_trans));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(int), &height_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(int), &width_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &padded_height_b));
|
||||
size_t t_global[] = { (size_t)width_b, (size_t)padded_height_b };
|
||||
size_t t_local[] = { 1, 16 };
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 2, t_global, t_local, dst);
|
||||
|
||||
kernel = backend_ctx->kernel_gemm_noshuffle_q3_k_f32;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &b_img_trans));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(int), &padded_n));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &k));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &n));
|
||||
size_t global[] = { (size_t)CEIL_DIV(n, 8), (size_t)CEIL_DIV(m, 512)*128, 1 };
|
||||
size_t local[] = { 1, 128, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global, local, dst);
|
||||
|
||||
CL_CHECK(clReleaseMemObject(b_img_trans));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf_trans));
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
@@ -25075,6 +25630,7 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
ggml_tensor_extra_cl_q8_0 * extra0_q8_0 = (ggml_tensor_extra_cl_q8_0 *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_iq4_nl * extra0_iq4_nl = (ggml_tensor_extra_cl_iq4_nl *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q2_K * extra0_q2_K = (ggml_tensor_extra_cl_q2_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q3_K * extra0_q3_K = (ggml_tensor_extra_cl_q3_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q4_K * extra0_q4_K = (ggml_tensor_extra_cl_q4_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q5_K * extra0_q5_K = (ggml_tensor_extra_cl_q5_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q6_K * extra0_q6_K = (ggml_tensor_extra_cl_q6_K *)soa0_src->extra;
|
||||
@@ -25476,6 +26032,12 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
return;
|
||||
}
|
||||
|
||||
// q3_k x fp32
|
||||
if (src0t == GGML_TYPE_Q3_K && src1t == GGML_TYPE_F32 && extra0_q3_K->noshuffle) {
|
||||
ggml_cl_mul_mat_q3_k_f32_adreno(backend, src0, src1, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
// q4_k x fp32
|
||||
if (src0t == GGML_TYPE_Q4_K && src1t == GGML_TYPE_F32 && !use_flat_gemv_for_large_m_q4_K(backend_ctx, src0)) {
|
||||
ggml_cl_mul_mat_q4_k_f32_adreno(backend, src0, src1, dst);
|
||||
@@ -26026,6 +26588,51 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
return;
|
||||
}
|
||||
case GGML_TYPE_Q3_K: {
|
||||
if (ne11 < 32) {
|
||||
break;
|
||||
}
|
||||
if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
kernel = backend_ctx->kernel_mul_mm_q3_k_f32_l4_lm;
|
||||
nth0 = (backend_ctx->gpu_family == INTEL) ? 64 : 128;
|
||||
offset0 = src0->view_offs;
|
||||
|
||||
int batch_stride_a = ne00*ne01;
|
||||
int batch_stride_b = ne10*ne11;
|
||||
int batch_stride_d = ne0*ne1;
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0_q3_K->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0_q3_K->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0_q3_K->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0_q3_K->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_ulong), &offset0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(int), &ne02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(int), &ne11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(int), &ne10));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &batch_stride_a));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &batch_stride_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 19, sizeof(int), &batch_stride_d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 20, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 21, sizeof(int), &r3));
|
||||
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)CEIL_DIV(ne11, 64), (size_t)ne12*ne13};
|
||||
size_t local_work_size[] = {(size_t)nth0, 1, 1};
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
return;
|
||||
}
|
||||
case GGML_TYPE_Q4_K: {
|
||||
if (ne11 < 32) {
|
||||
break;
|
||||
@@ -27005,7 +27612,70 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
#endif // GGML_OPENCL_SOA_Q
|
||||
break;
|
||||
}
|
||||
case GGML_TYPE_Q3_K:
|
||||
case GGML_TYPE_Q3_K: {
|
||||
if (backend_ctx->gpu_family == INTEL) {
|
||||
nth0 = 16;
|
||||
nth1 = 1;
|
||||
ndst = 2;
|
||||
} else if (backend_ctx->gpu_family == ADRENO) {
|
||||
nth0 = 64;
|
||||
nth1 = 1;
|
||||
ndst = 2;
|
||||
} else {
|
||||
GGML_ASSERT(false && "TODO: Unknown GPU");
|
||||
}
|
||||
|
||||
#ifdef GGML_OPENCL_SOA_Q
|
||||
kernel = backend_ctx->kernel_mul_mv_q3_K_f32_flat;
|
||||
offset0 = src0->view_offs;
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0_q3_K->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0_q3_K->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0_q3_K->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0_q3_K->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_ulong), &offset0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_ulong), &nb01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &nb02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_ulong), &nb03));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(cl_ulong), &nb11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(cl_ulong), &nb12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(cl_ulong), &nb13));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 19, sizeof(int), &ne1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 20, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 21, sizeof(int), &r3));
|
||||
#else
|
||||
kernel = backend_ctx->kernel_mul_mv_q3_K_f32;
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &nb01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &nb02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &nb03));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &nb11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_ulong), &nb12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(cl_ulong), &nb13));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(int), &ne1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &r3));
|
||||
#endif // GGML_OPENCL_SOA_Q
|
||||
break;
|
||||
}
|
||||
case GGML_TYPE_Q4_K: {
|
||||
#ifdef GGML_OPENCL_SOA_Q
|
||||
kernel = backend_ctx->kernel_mul_mv_q4_K_f32_flat;
|
||||
@@ -27313,7 +27983,7 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
src0t == GGML_TYPE_Q8_0 ||
|
||||
src0t == GGML_TYPE_Q1_0 ||
|
||||
src0t == GGML_TYPE_IQ4_NL ||
|
||||
src0t == GGML_TYPE_Q2_K) {
|
||||
src0t == GGML_TYPE_Q2_K || src0t == GGML_TYPE_Q3_K) {
|
||||
// Each SIMD group produces N_DST values in the result. Assuming each
|
||||
// workgroup has N_SIMDGROUP SIMD groups, then each workgroup will
|
||||
// produce N_DST*N_SIMDGROUP values in the result. Hence, the grid size
|
||||
@@ -27329,8 +27999,6 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
size_t local_work_size[] = {(size_t)nth0, (size_t)nth1, 1};
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
} else if (src0t == GGML_TYPE_Q3_K) {
|
||||
GGML_ASSERT(false && "not implemented");
|
||||
} else if (src0t == GGML_TYPE_Q5_K) {
|
||||
size_t global_work_size[] = {(size_t)(ne01+ndst*nth1-1)/(ndst*nth1)*nth0, (size_t)ne11*nth1, (size_t)ne12*ne13};
|
||||
size_t local_work_size[] = {(size_t)nth0, (size_t)nth1, 1};
|
||||
@@ -27630,35 +28298,21 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
|
||||
// the matmul kernels only write the rows an expert owns, so zero the skipped rows here
|
||||
{
|
||||
cl_int err;
|
||||
|
||||
cl_buffer_region region;
|
||||
region.origin = offset2;
|
||||
region.size = nb21 * ne21;
|
||||
cl_mem buf_ids = clCreateSubBuffer(extra2->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
region.origin = offsetd;
|
||||
region.size = (size_t) ne0 * ne1 * ne2 * sizeof(float);
|
||||
cl_mem buf_dst = clCreateSubBuffer(extrad->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const cl_uint ids_stride = nb21 / sizeof(int);
|
||||
|
||||
const cl_ulong nb1 = dst->nb[1];
|
||||
const cl_ulong nb2 = dst->nb[2];
|
||||
cl_kernel kernel_zero = backend_ctx->kernel_moe_zero_dst;
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 0, sizeof(cl_mem), &buf_ids));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 1, sizeof(cl_mem), &buf_dst));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 2, sizeof(cl_uint), &ne21));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 3, sizeof(cl_uint), &ne20));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 4, sizeof(cl_uint), &ids_stride));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 5, sizeof(cl_uint), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 0, sizeof(cl_mem), &extra2->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 1, sizeof(cl_ulong), &offset2));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 2, sizeof(cl_ulong), &nb21));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 3, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 4, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 5, sizeof(cl_ulong), &nb1));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 6, sizeof(cl_ulong), &nb2));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 7, sizeof(int), &ne0));
|
||||
|
||||
size_t zero_local[3] = {1, 1, 64};
|
||||
size_t zero_global[3] = {(size_t) ne21, (size_t) ne20, 64};
|
||||
size_t zero_global[3] = {(size_t) ne20, (size_t) ne21, 64};
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel_zero, 3, zero_global, zero_local, dst);
|
||||
|
||||
CL_CHECK(clReleaseMemObject(buf_ids));
|
||||
CL_CHECK(clReleaseMemObject(buf_dst));
|
||||
}
|
||||
|
||||
int sgs = 32; // subgroup size
|
||||
|
||||
@@ -2773,3 +2773,156 @@ kernel void kernel_restore_block_q2_k_noshuffle(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct block_q3_K {
|
||||
uchar hmask[QK_K/8];
|
||||
uchar qs[QK_K/4];
|
||||
uchar scales[12];
|
||||
half d;
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_q3_K(
|
||||
global struct block_q3_K * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_hm,
|
||||
global uchar * dst_sc,
|
||||
global half * dst_d,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = src0 + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst_qs[64*i + j] = b->qs[j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst_hm[32*i + j] = b->hmask[j];
|
||||
}
|
||||
for (int j = 0; j < 12; ++j) {
|
||||
dst_sc[12*i + j] = b->scales[j];
|
||||
}
|
||||
dst_d[i] = b->d;
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q3_K(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_hm,
|
||||
global uchar * src_sc,
|
||||
global half * src_d,
|
||||
global struct block_q3_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = dst + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
b->qs[j] = src_qs[64*i + j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
b->hmask[j] = src_hm[32*i + j];
|
||||
}
|
||||
for (int j = 0; j < 12; ++j) {
|
||||
b->scales[j] = src_sc[12*i + j];
|
||||
}
|
||||
b->d = src_d[i];
|
||||
}
|
||||
|
||||
kernel void kernel_convert_block_q3_k_noshuffle(
|
||||
global struct block_q3_K * src0,
|
||||
global uchar * dst_qs, // QK_K/4 per block
|
||||
global uchar * dst_hm, // QK_K/8 per block
|
||||
global uint * dst_sc, // 3 per block
|
||||
global half * dst_d, // 1
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = (global struct block_q3_K *) src0 + i;
|
||||
|
||||
dst_d[i] = b->d;
|
||||
|
||||
for (int w = 0; w < 3; ++w) {
|
||||
global const uchar * p = b->scales + 4*w;
|
||||
dst_sc[3*i + w] = (uint)p[0] | ((uint)p[1] << 8) | ((uint)p[2] << 16) | ((uint)p[3] << 24);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// two groups per hmask byte, so this loop owns the byte and cannot race
|
||||
for (int t = 0; t < QK_K/8; ++t) {
|
||||
uint v = 0;
|
||||
for (int s = 0; s < 2; ++s) {
|
||||
const int g = 2*t + s;
|
||||
for (int u = 0; u < 4; ++u) {
|
||||
const int e = 4*g + u;
|
||||
const int sb = e >> 5;
|
||||
const int ee = e & 31;
|
||||
v |= (uint)(((b->hmask[ee] >> sb) & 1) << (4*s + u));
|
||||
}
|
||||
}
|
||||
dst_hm[(QK_K/8) * i + t] = (uchar)v;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q3_k_noshuffle(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_hm,
|
||||
global uint * src_sc,
|
||||
global half * src_d,
|
||||
global struct block_q3_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = (global struct block_q3_K *) dst + i;
|
||||
|
||||
b->d = src_d[i];
|
||||
|
||||
for (int w = 0; w < 3; ++w) {
|
||||
const uint x = src_sc[3*i + w];
|
||||
global uchar * p = b->scales + 4*w;
|
||||
p[0] = (uchar)( x & 0xFF);
|
||||
p[1] = (uchar)((x >> 8) & 0xFF);
|
||||
p[2] = (uchar)((x >> 16) & 0xFF);
|
||||
p[3] = (uchar)((x >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ee = 0; ee < 32; ++ee) {
|
||||
uint v = 0;
|
||||
for (int sb = 0; sb < 8; ++sb) {
|
||||
const int e = 32*sb + ee;
|
||||
const int g = e >> 2;
|
||||
v |= ((uint)(src_hm[(QK_K/8) * i + (g >> 1)] >> (4*(g & 1) + (e & 3))) & 1u) << sb;
|
||||
}
|
||||
b->hmask[ee] = (uchar)v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#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_q3_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_hm,
|
||||
global const uint * src0_sc,
|
||||
global const half * src0_d,
|
||||
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 int is = (i / 16) & 15;
|
||||
const int word = is >> 2;
|
||||
const int shift = 8 * (is & 3);
|
||||
const int sc_base = row + 3 * (i / 256) * m;
|
||||
const uint4 low = vload4(0, src0_sc + sc_base + (word & 1) * m) >> (uint4)(4 * (word >> 1));
|
||||
const uint4 high = vload4(0, src0_sc + sc_base + 2 * m) >> (uint4)(2 * word);
|
||||
const int4 sc = convert_int4(((low >> (uint4)(shift)) & (uint4)(15)) | (((high >> (uint4)(shift)) & (uint4)(3)) << (uint4)(4))) - (int4)(32);
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const half4 scale = convert_half4(convert_float4(d) * convert_float4(sc));
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uchar4 bits = vload4(0, src0_qs + row + (ki / 4) * m);
|
||||
const uchar4 hbits = vload4(0, src0_hm + row + (ki / 8) * 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 int4 low = convert_int4((bits >> (uchar4)(2 * j)) & (uchar4)(3));
|
||||
const int4 high = convert_int4((hbits >> (uchar4)((ki & 7) + j)) & (uchar4)(1));
|
||||
const half4 w = convert_half4(low - (int4)(4) + (int4)(4) * high) * scale;
|
||||
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,160 @@
|
||||
#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 int q3_k_scale(uint sc0, uint sc1, uint sc2, uint is) {
|
||||
const uint w = is >> 2;
|
||||
const uint sh = 8u * (is & 3u);
|
||||
const uint tb = (sc2 >> sh) & 0xFFu;
|
||||
uint v;
|
||||
if (w == 0u) { v = ( (sc0 >> sh) & 0xFu) | (((tb >> 0) & 3u) << 4); }
|
||||
else if (w == 1u) { v = ( (sc1 >> sh) & 0xFu) | (((tb >> 2) & 3u) << 4); }
|
||||
else if (w == 2u) { v = (((sc0 >> sh) >> 4) & 0xFu) | (((tb >> 4) & 3u) << 4); }
|
||||
else { v = (((sc1 >> sh) >> 4) & 0xFu) | (((tb >> 6) & 3u) << 4); }
|
||||
return (int)v - 32;
|
||||
}
|
||||
|
||||
inline uint q3_k_pack(uint pk, uint hb) {
|
||||
int v0 = (int)((pk ) & 3u) - 4 + 4*(int)((hb ) & 1u);
|
||||
int v1 = (int)((pk >> 2) & 3u) - 4 + 4*(int)((hb >> 1) & 1u);
|
||||
int v2 = (int)((pk >> 4) & 3u) - 4 + 4*(int)((hb >> 2) & 1u);
|
||||
int v3 = (int)((pk >> 6) & 3u) - 4 + 4*(int)((hb >> 3) & 1u);
|
||||
return ((uint)v0 & 0xFFu) | (((uint)v1 & 0xFFu) << 8)
|
||||
| (((uint)v2 & 0xFFu) << 16) | (((uint)v3 & 0xFFu) << 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_q3_k_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uchar * src0_hm,
|
||||
__global const uint * src0_sc,
|
||||
__global const half * src0_d,
|
||||
__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, // output features (rows)
|
||||
int n_no_padding, // tokens (cols)
|
||||
int k // K (== ne00)
|
||||
) {
|
||||
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 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; // super-block along K
|
||||
const uint sb = sub & 7u;
|
||||
|
||||
const uint scb = rrow + (3u * ib) * (uint)m;
|
||||
const uint sc0 = src0_sc[scb + 0u * (uint)m];
|
||||
const uint sc1 = src0_sc[scb + 1u * (uint)m];
|
||||
const uint sc2 = src0_sc[scb + 2u * (uint)m];
|
||||
|
||||
const float d_w = (float)src0_d[rrow + ib * (uint)m];
|
||||
const float dl0 = d_w * (float)q3_k_scale(sc0, sc1, sc2, 2u*sb + 0u);
|
||||
const float dl1 = d_w * (float)q3_k_scale(sc0, sc1, sc2, 2u*sb + 1u);
|
||||
|
||||
const uint qsb = rrow + (step >> 2) * (uint)m;
|
||||
const uint hmb = rrow + (step >> 3) * (uint)m;
|
||||
|
||||
uint4 qlo, qhi;
|
||||
{
|
||||
const uint h0 = (uint)src0_hm[hmb + 0u * (uint)m];
|
||||
const uint h1 = (uint)src0_hm[hmb + 1u * (uint)m];
|
||||
const uint h2 = (uint)src0_hm[hmb + 2u * (uint)m];
|
||||
const uint h3 = (uint)src0_hm[hmb + 3u * (uint)m];
|
||||
qlo.s0 = q3_k_pack((uint)src0_qs[qsb + 0u * (uint)m], h0 & 0xFu);
|
||||
qlo.s1 = q3_k_pack((uint)src0_qs[qsb + 1u * (uint)m], (h0 >> 4) & 0xFu);
|
||||
qlo.s2 = q3_k_pack((uint)src0_qs[qsb + 2u * (uint)m], h1 & 0xFu);
|
||||
qlo.s3 = q3_k_pack((uint)src0_qs[qsb + 3u * (uint)m], (h1 >> 4) & 0xFu);
|
||||
qhi.s0 = q3_k_pack((uint)src0_qs[qsb + 4u * (uint)m], h2 & 0xFu);
|
||||
qhi.s1 = q3_k_pack((uint)src0_qs[qsb + 5u * (uint)m], (h2 >> 4) & 0xFu);
|
||||
qhi.s2 = q3_k_pack((uint)src0_qs[qsb + 6u * (uint)m], h3 & 0xFu);
|
||||
qhi.s3 = q3_k_pack((uint)src0_qs[qsb + 7u * (uint)m], (h3 >> 4) & 0xFu);
|
||||
}
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? KQ_STAGE(src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? 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]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define Q3_K_COL(T) (dl0 * (float)dot4_q8a_v(qlo, (uint4)(sh_qa4[T][0])) \
|
||||
+ dl1 * (float)dot4_q8a_v(qhi, (uint4)(sh_qa4[T][1])))
|
||||
rf.s0 = Q3_K_COL(b+0); rf.s1 = Q3_K_COL(b+1);
|
||||
rf.s2 = Q3_K_COL(b+2); rf.s3 = Q3_K_COL(b+3);
|
||||
#undef Q3_K_COL
|
||||
acc[g] += LD4(sh_d, b) * rf;
|
||||
}
|
||||
#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,249 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef Q3_K_MV_NSG
|
||||
#define Q3_K_MV_NSG 8
|
||||
#endif
|
||||
|
||||
#ifndef Q3_K_MV_R
|
||||
#define Q3_K_MV_R 2
|
||||
#endif
|
||||
|
||||
inline int q3_k_scale(uint sc0, uint sc1, uint sc2, uint is) {
|
||||
const uint w = is >> 2;
|
||||
const uint sh = 8u * (is & 3u);
|
||||
const uint tb = (sc2 >> sh) & 0xFFu;
|
||||
uint v;
|
||||
if (w == 0u) { v = ( (sc0 >> sh) & 0xFu) | (((tb >> 0) & 3u) << 4); }
|
||||
else if (w == 1u) { v = ( (sc1 >> sh) & 0xFu) | (((tb >> 2) & 3u) << 4); }
|
||||
else if (w == 2u) { v = (((sc0 >> sh) >> 4) & 0xFu) | (((tb >> 4) & 3u) << 4); }
|
||||
else { v = (((sc1 >> sh) >> 4) & 0xFu) | (((tb >> 6) & 3u) << 4); }
|
||||
return (int)v - 32;
|
||||
}
|
||||
|
||||
inline float4 q3_k_vals(uint pk, uint hb) {
|
||||
float4 v;
|
||||
v.s0 = (float)((int)((pk ) & 3u) - 4 + 4*(int)((hb ) & 1u));
|
||||
v.s1 = (float)((int)((pk >> 2) & 3u) - 4 + 4*(int)((hb >> 1) & 1u));
|
||||
v.s2 = (float)((int)((pk >> 4) & 3u) - 4 + 4*(int)((hb >> 2) & 1u));
|
||||
v.s3 = (float)((int)((pk >> 6) & 3u) - 4 + 4*(int)((hb >> 3) & 1u));
|
||||
return v;
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_q3_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_hm,
|
||||
global const uint * src0_sc,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00, // K
|
||||
int ne01, // M
|
||||
int ne10, // activation row stride
|
||||
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;
|
||||
|
||||
#if Q3_K_MV_R == 4
|
||||
const uint mq = m >> 2;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 2;
|
||||
|
||||
float sumf = 0.f, sumf1 = 0.f, sumf2 = 0.f, sumf3 = 0.f;
|
||||
|
||||
if (j < mq) {
|
||||
global const uint * qsw = (global const uint *)src0_qs;
|
||||
global const uint * hmw = (global const uint *)src0_hm;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const half4 dh = vload4(j + ib * mq, src0_d);
|
||||
const uint4 s0 = vload4(j + (3u * ib + 0u) * mq, src0_sc);
|
||||
const uint4 s1 = vload4(j + (3u * ib + 1u) * mq, src0_sc);
|
||||
const uint4 s2 = vload4(j + (3u * ib + 2u) * mq, src0_sc);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f, acc2 = 0.f, acc3 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mq;
|
||||
const uint hmb = j + (grp >> 1) * mq;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) { // two 16-weight halves
|
||||
const int l0 = q3_k_scale(s0.s0, s1.s0, s2.s0, 2u*sb + h);
|
||||
const int l1 = q3_k_scale(s0.s1, s1.s1, s2.s1, 2u*sb + h);
|
||||
const int l2 = q3_k_scale(s0.s2, s1.s2, s2.s2, 2u*sb + h);
|
||||
const int l3 = q3_k_scale(s0.s3, s1.s3, s2.s3, 2u*sb + h);
|
||||
|
||||
float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = qsw[qsb + gg * mq]; // four rows, one load
|
||||
const uint hmv = hmw[hmb + (gg >> 1) * mq];
|
||||
const uint hsh = 4u * (gg & 1u);
|
||||
const float4 yv = vload4(grp + gg, y); // shared by all four
|
||||
a0 += dot(yv, q3_k_vals((qsv ) & 0xFFu, ((hmv ) >> hsh) & 0xFu));
|
||||
a1 += dot(yv, q3_k_vals((qsv >> 8) & 0xFFu, ((hmv >> 8) >> hsh) & 0xFu));
|
||||
a2 += dot(yv, q3_k_vals((qsv >> 16) & 0xFFu, ((hmv >> 16) >> hsh) & 0xFu));
|
||||
a3 += dot(yv, q3_k_vals((qsv >> 24) & 0xFFu, ((hmv >> 24) >> hsh) & 0xFu));
|
||||
}
|
||||
acc0 += (float)l0 * a0;
|
||||
acc1 += (float)l1 * a1;
|
||||
acc2 += (float)l2 * a2;
|
||||
acc3 += (float)l3 * a3;
|
||||
}
|
||||
}
|
||||
sumf += (float)dh.s0 * acc0;
|
||||
sumf1 += (float)dh.s1 * acc1;
|
||||
sumf2 += (float)dh.s2 * acc2;
|
||||
sumf3 += (float)dh.s3 * acc3;
|
||||
}
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
global const ushort * hmu = (global const ushort *)src0_hm;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const half2 dh = vload2(j + ib * mh, src0_d);
|
||||
const uint2 s0 = vload2(j + (3u * ib + 0u) * mh, src0_sc);
|
||||
const uint2 s1 = vload2(j + (3u * ib + 1u) * mh, src0_sc);
|
||||
const uint2 s2 = vload2(j + (3u * ib + 2u) * mh, src0_sc);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mh;
|
||||
const uint hmb = j + (grp >> 1) * mh;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) { // two 16-weight halves
|
||||
const int l0 = q3_k_scale(s0.s0, s1.s0, s2.s0, 2u*sb + h);
|
||||
const int l1 = q3_k_scale(s0.s1, s1.s1, s2.s1, 2u*sb + h);
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = (uint)qsu[qsb + gg * mh];
|
||||
const uint hmv = (uint)hmu[hmb + (gg >> 1) * mh];
|
||||
const uint hsh = 4u * (gg & 1u);
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
a0 += dot(yv, q3_k_vals( qsv & 0xFFu, ( hmv >> hsh) & 0xFu));
|
||||
a1 += dot(yv, q3_k_vals((qsv >> 8) & 0xFFu, ((hmv >> 8) >> hsh) & 0xFu));
|
||||
}
|
||||
acc0 += (float)l0 * a0;
|
||||
acc1 += (float)l1 * a1;
|
||||
}
|
||||
}
|
||||
sumf += (float)dh.s0 * acc0;
|
||||
sumf1 += (float)dh.s1 * acc1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
const uint row = get_group_id(0) * 64u + lid;
|
||||
|
||||
float sumf = 0.f;
|
||||
|
||||
if (row < m) {
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const float d = (float)src0_d[row + ib * m];
|
||||
const uint s0 = src0_sc[row + (3u * ib + 0u) * m];
|
||||
const uint s1 = src0_sc[row + (3u * ib + 1u) * m];
|
||||
const uint s2 = src0_sc[row + (3u * ib + 2u) * m];
|
||||
|
||||
float acc = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = row + grp * m;
|
||||
const uint hmb = row + (grp >> 1) * m;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) {
|
||||
const int l = q3_k_scale(s0, s1, s2, 2u*sb + h);
|
||||
float a = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = (uint)src0_qs[qsb + gg * m];
|
||||
const uint hmv = (uint)src0_hm[hmb + (gg >> 1) * m];
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
a += dot(yv, q3_k_vals(qsv, (hmv >> (4u * (gg & 1u))) & 0xFu));
|
||||
}
|
||||
acc += (float)l * a;
|
||||
}
|
||||
}
|
||||
sumf += d * acc;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if Q3_K_MV_NSG > 1
|
||||
#if Q3_K_MV_R == 4
|
||||
__local float4 part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float4)(sumf, sumf1, sumf2, sumf3);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
const float4 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
sumf2 += p.s2;
|
||||
sumf3 += p.s3;
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
__local float2 part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#else
|
||||
__local float part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = sumf;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
sumf += part[s][lid];
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if Q3_K_MV_R == 4
|
||||
if (j < mq) {
|
||||
vstore4((float4)(sumf, sumf1, sumf2, sumf3), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#else
|
||||
if (row < m) {
|
||||
dst[(ulong)col * (uint)ne0 + row] = sumf;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -57,13 +57,17 @@ kernel void kernel_add_id_add_id_swiglu_oai(
|
||||
|
||||
global const float * g_row = (global const float *)(src_g + i1*nb01_g + i2*nb02_g);
|
||||
global const float * u_row = (global const float *)(src_u + i1*nb01_u + i2*nb02_u);
|
||||
global const float * gb_row = (global const float *)(src_gb + i11*nb11_g);
|
||||
global const float * ub_row = (global const float *)(src_ub + i11*nb11_u);
|
||||
global float * d_row = (global float *)(dst + i1*nbd1 + i2*nbd2);
|
||||
|
||||
for (int i0 = get_local_id(0); i0 < ne0; i0 += get_local_size(0)) {
|
||||
float x0 = g_row[i0] + gb_row[i0];
|
||||
float x1 = u_row[i0] + ub_row[i0];
|
||||
float x0 = g_row[i0];
|
||||
float x1 = u_row[i0];
|
||||
if (i11 != -1) {
|
||||
global const float * gb_row = (global const float *)(src_gb + i11*nb11_g);
|
||||
global const float * ub_row = (global const float *)(src_ub + i11*nb11_u);
|
||||
x0 += gb_row[i0];
|
||||
x1 += ub_row[i0];
|
||||
}
|
||||
|
||||
x0 = min(x0, limit);
|
||||
x1 = max(min(x1, limit), -limit);
|
||||
|
||||
@@ -42,8 +42,11 @@ __kernel void kernel_moe_combine_bias_f32(
|
||||
float4 acc = (float4)(0.0f);
|
||||
for (int e = 0; e < k; ++e) {
|
||||
const int i11 = *((__global const int *)(I + (ulong)e*sizeof(int)));
|
||||
__global const float * Brow = (__global const float *)(B + (ulong)i11*nb_b1) + r4*4u;
|
||||
const float4 v = vload4(0, E + (uint)e*e1) + vload4(0, Brow);
|
||||
float4 v = vload4(0, E + (uint)e*e1);
|
||||
if (i11 != -1) {
|
||||
__global const float * Brow = (__global const float *)(B + (ulong)i11*nb_b1) + r4*4u;
|
||||
v += vload4(0, Brow);
|
||||
}
|
||||
acc = mad(v, (float4)(W[(uint)e*w1]), acc);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,32 +80,6 @@ __kernel void kernel_moe_scatter(
|
||||
emap[tile_idx] = val;
|
||||
}
|
||||
|
||||
// the gemm only writes the rows an expert owns, so the skipped rows (negative ids) are zeroed here
|
||||
__kernel void kernel_moe_zero_dst(
|
||||
__global const int * input,
|
||||
__global float * dst,
|
||||
uint N,
|
||||
uint topK,
|
||||
uint n_experts,
|
||||
uint ne0
|
||||
) {
|
||||
uint n = get_global_id(0);
|
||||
uint k = get_global_id(1);
|
||||
|
||||
if (n >= N || k >= topK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input[n * n_experts + k] != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
__global float * dst_row = dst + ((ulong)n * topK + k) * ne0;
|
||||
for (uint i = get_local_id(2); i < ne0; i += get_local_size(2)) {
|
||||
dst_row[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic replacement for kernel_moe_scatter.
|
||||
//
|
||||
// kernel_moe_scatter takes each token's slot from atomic_inc(slot_counter[expert]),
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
kernel void kernel_moe_zero_dst(
|
||||
global const char * ids,
|
||||
ulong offset_ids,
|
||||
ulong nb_ids1,
|
||||
global char * dst,
|
||||
ulong offset_dst,
|
||||
ulong nb1,
|
||||
ulong nb2,
|
||||
int ne0
|
||||
) {
|
||||
const size_t i1 = get_group_id(0);
|
||||
const size_t i2 = get_group_id(1);
|
||||
const int expert = *((global const int *)(ids + offset_ids + i1*sizeof(int) + i2*nb_ids1));
|
||||
if (expert != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
global float * row = (global float *)(dst + offset_dst + i1*nb1 + i2*nb2);
|
||||
for (size_t i0 = get_local_id(2); i0 < (size_t)ne0; i0 += get_local_size(2)) {
|
||||
row[i0] = 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_q3_K {
|
||||
uchar hmask[QK_K/8];
|
||||
uchar qs[QK_K/4];
|
||||
uchar scales[12];
|
||||
half d;
|
||||
};
|
||||
|
||||
#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_q3_k_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_hm,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_d,
|
||||
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 q3_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_q3_K);
|
||||
int e = idx % QK_K;
|
||||
|
||||
// Only qs advances in the second half of a super-block.
|
||||
int n = e >> 7;
|
||||
int rem = e & 127;
|
||||
int j = rem >> 5;
|
||||
int s = (rem >> 4) & 1;
|
||||
int l16 = rem & 15;
|
||||
int is = 8*n + 2*j + s;
|
||||
int shift = 2*j;
|
||||
uchar m = (uchar)(1u << (4*n + j));
|
||||
|
||||
global ushort * a = (global ushort *)(src0_sc + 12*ib);
|
||||
uint a0 = (uint)a[0] | ((uint)a[1] << 16);
|
||||
uint a1 = (uint)a[2] | ((uint)a[3] << 16);
|
||||
uint tmp = (uint)a[4] | ((uint)a[5] << 16);
|
||||
|
||||
uint g = is >> 2;
|
||||
uint w = (g & 1u) ? a1 : a0;
|
||||
w = ((w >> ((g >> 1)*4)) & 0x0f0f0f0fu) | (((tmp >> (2*g)) & 0x03030303u) << 4);
|
||||
|
||||
uint sc6 = (w >> (8*(is & 3))) & 0xFFu;
|
||||
float dl = (float)src0_d[ib] * ((float)sc6 - 32.0f);
|
||||
|
||||
uchar4 q = vload4(0, src0_qs + 64*ib + 32*n + 16*s + l16);
|
||||
uchar4 hb = vload4(0, src0_hm + 32*ib + 16*s + l16);
|
||||
|
||||
float4 qv = convert_float4((uchar4)((q.s0 >> shift) & 3,
|
||||
(q.s1 >> shift) & 3,
|
||||
(q.s2 >> shift) & 3,
|
||||
(q.s3 >> shift) & 3));
|
||||
float4 hv = (float4)((hb.s0 & m) ? 0.f : 4.f,
|
||||
(hb.s1 & m) ? 0.f : 4.f,
|
||||
(hb.s2 & m) ? 0.f : 4.f,
|
||||
(hb.s3 & m) ? 0.f : 4.f);
|
||||
float4 v1 = (qv - hv) * dl;
|
||||
|
||||
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,197 @@
|
||||
#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_q3_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q
|
||||
typedef struct {
|
||||
uchar hmask[QK_K/8]; // quants - high bit
|
||||
uchar qs[QK_K/4]; // quants - low 2 bits
|
||||
uchar scales[12]; // scales, quantized with 6 bits
|
||||
half d; // super-block scale
|
||||
} block_q3_K;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 2 // 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 2
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 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_q3_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 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_q3_K * x = (global block_q3_K *) (src0 + offset_src0);
|
||||
global float * yy = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
|
||||
// Eight lanes cover one super-block.
|
||||
int ix = get_sub_group_local_id() % BLOCK_STRIDE;
|
||||
int tid = get_sub_group_local_id() / BLOCK_STRIDE;
|
||||
|
||||
int ip = tid/4; // 0 or 1
|
||||
int il = 2*((tid%4)/2); // 0 or 2
|
||||
int ir = tid%2;
|
||||
int l0 = 8*ir;
|
||||
|
||||
// Shift the masks instead of indexing private arrays to avoid spills.
|
||||
ushort4 hm = (ushort4)(0x0001, 0x0100, 0x0002, 0x0200) << (ushort4)(2*(2*ip + il/2));
|
||||
int4 qm = (int4) (0x0003, 0x0300, 0x000c, 0x0c00) << (int4) (4*(il/2));
|
||||
|
||||
int shift = 2*il;
|
||||
|
||||
float v1 = il == 0 ? 4.f : 64.f;
|
||||
float v2 = 4.f * v1;
|
||||
|
||||
int s_shift1 = 4*ip;
|
||||
int s_shift2 = s_shift1 + il;
|
||||
|
||||
int q_offset = 32*ip + l0;
|
||||
int y_offset = 128*ip + 32*il + l0;
|
||||
|
||||
global float * y1 = yy + ix*QK_K + y_offset;
|
||||
|
||||
float sumf1[N_DST] = {0.f};
|
||||
float sumf2[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
for (int i = ix; i < nb; i += BLOCK_STRIDE) {
|
||||
for (int l = 0; l < 8; ++l) {
|
||||
yl[l+ 0] = y1[l+ 0];
|
||||
yl[l+ 8] = y1[l+16];
|
||||
yl[l+16] = y1[l+32];
|
||||
yl[l+24] = y1[l+48];
|
||||
}
|
||||
|
||||
global ushort * q = (global ushort *)(x[i].qs + q_offset);
|
||||
global ushort * h = (global ushort *)(x[i].hmask + l0);
|
||||
global ushort * a = (global ushort *)(x[i].scales);
|
||||
global half * dh = &x[i].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) {
|
||||
float d_all = dh[0];
|
||||
|
||||
uint s32 = (uint)a[4] | ((uint)a[5] << 16);
|
||||
uint aux32 = ((s32 >> s_shift2) << 4) & 0x30303030u;
|
||||
s32 = (uint)a[il+0] | ((uint)a[il+1] << 16);
|
||||
s32 = ((s32 >> s_shift1) & 0x0f0f0f0fu) | aux32;
|
||||
char4 sc = as_char4(s32);
|
||||
|
||||
float s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2];
|
||||
s1 += yl[l+0] * (qs & qm.s0);
|
||||
s2 += yl[l+1] * (qs & qm.s1);
|
||||
s3 += ((h[l/2] & hm.s0) ? 0.f : yl[l+0]) + ((h[l/2] & hm.s1) ? 0.f : yl[l+1]);
|
||||
s4 += yl[l+16] * (qs & qm.s2);
|
||||
s5 += yl[l+17] * (qs & qm.s3);
|
||||
s6 += ((h[l/2] & hm.s2) ? 0.f : yl[l+16]) + ((h[l/2] & hm.s3) ? 0.f : yl[l+17]);
|
||||
}
|
||||
float d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
float d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s0 - 32);
|
||||
sumf2[row] += d2 * (sc.s2 - 32);
|
||||
|
||||
s1 = s2 = s3 = s4 = s5 = s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2+8];
|
||||
s1 += yl[l+8] * (qs & qm.s0);
|
||||
s2 += yl[l+9] * (qs & qm.s1);
|
||||
s3 += ((h[l/2+8] & hm.s0) ? 0.f : yl[l+8]) + ((h[l/2+8] & hm.s1) ? 0.f : yl[l+9]);
|
||||
s4 += yl[l+24] * (qs & qm.s2);
|
||||
s5 += yl[l+25] * (qs & qm.s3);
|
||||
s6 += ((h[l/2+8] & hm.s2) ? 0.f : yl[l+24]) + ((h[l/2+8] & hm.s3) ? 0.f : yl[l+25]);
|
||||
}
|
||||
d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s1 - 32);
|
||||
sumf2[row] += d2 * (sc.s3 - 32);
|
||||
|
||||
q += nb01/2;
|
||||
h += nb01/2;
|
||||
a += nb01/2;
|
||||
dh += nb01/2;
|
||||
}
|
||||
|
||||
y1 += 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((sumf1[row] + 0.25f * sumf2[row]) / (1 << shift));
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
#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_q3_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q
|
||||
typedef struct {
|
||||
uchar hmask[QK_K/8]; // quants - high bit
|
||||
uchar qs[QK_K/4]; // quants - low 2 bits
|
||||
uchar scales[12]; // scales, quantized with 6 bits
|
||||
half d; // super-block scale
|
||||
} block_q3_K;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 2 // 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 2
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 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_q3_K_f32_flat(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_hm,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_d,
|
||||
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 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(block_q3_K);
|
||||
ulong row_stride = nb01/sizeof(block_q3_K);
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global float * yy = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
|
||||
// Eight lanes cover one super-block.
|
||||
int ix = get_sub_group_local_id() % BLOCK_STRIDE;
|
||||
int tid = get_sub_group_local_id() / BLOCK_STRIDE;
|
||||
|
||||
int ip = tid/4; // 0 or 1
|
||||
int il = 2*((tid%4)/2); // 0 or 2
|
||||
int ir = tid%2;
|
||||
int l0 = 8*ir;
|
||||
|
||||
// Shift the masks instead of indexing private arrays to avoid spills.
|
||||
ushort4 hm = (ushort4)(0x0001, 0x0100, 0x0002, 0x0200) << (ushort4)(2*(2*ip + il/2));
|
||||
int4 qm = (int4) (0x0003, 0x0300, 0x000c, 0x0c00) << (int4) (4*(il/2));
|
||||
|
||||
int shift = 2*il;
|
||||
|
||||
float v1 = il == 0 ? 4.f : 64.f;
|
||||
float v2 = 4.f * v1;
|
||||
|
||||
int s_shift1 = 4*ip;
|
||||
int s_shift2 = s_shift1 + il;
|
||||
|
||||
int q_offset = 32*ip + l0;
|
||||
int y_offset = 128*ip + 32*il + l0;
|
||||
|
||||
global float * y1 = yy + ix*QK_K + y_offset;
|
||||
|
||||
float sumf1[N_DST] = {0.f};
|
||||
float sumf2[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
for (int i = ix; i < nb; i += BLOCK_STRIDE) {
|
||||
for (int l = 0; l < 8; ++l) {
|
||||
yl[l+ 0] = y1[l+ 0];
|
||||
yl[l+ 8] = y1[l+16];
|
||||
yl[l+16] = y1[l+32];
|
||||
yl[l+24] = y1[l+48];
|
||||
}
|
||||
|
||||
global ushort * q = (global ushort *)(src0_qs + 64*(offset_src0 + i) + q_offset);
|
||||
global ushort * h = (global ushort *)(src0_hm + 32*(offset_src0 + i) + l0);
|
||||
global ushort * a = (global ushort *)(src0_sc + 12*(offset_src0 + i));
|
||||
global half * dh = src0_d + offset_src0 + i;
|
||||
|
||||
// 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) {
|
||||
float d_all = dh[0];
|
||||
|
||||
uint s32 = (uint)a[4] | ((uint)a[5] << 16);
|
||||
uint aux32 = ((s32 >> s_shift2) << 4) & 0x30303030u;
|
||||
s32 = (uint)a[il+0] | ((uint)a[il+1] << 16);
|
||||
s32 = ((s32 >> s_shift1) & 0x0f0f0f0fu) | aux32;
|
||||
char4 sc = as_char4(s32);
|
||||
|
||||
float s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2];
|
||||
s1 += yl[l+0] * (qs & qm.s0);
|
||||
s2 += yl[l+1] * (qs & qm.s1);
|
||||
s3 += ((h[l/2] & hm.s0) ? 0.f : yl[l+0]) + ((h[l/2] & hm.s1) ? 0.f : yl[l+1]);
|
||||
s4 += yl[l+16] * (qs & qm.s2);
|
||||
s5 += yl[l+17] * (qs & qm.s3);
|
||||
s6 += ((h[l/2] & hm.s2) ? 0.f : yl[l+16]) + ((h[l/2] & hm.s3) ? 0.f : yl[l+17]);
|
||||
}
|
||||
float d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
float d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s0 - 32);
|
||||
sumf2[row] += d2 * (sc.s2 - 32);
|
||||
|
||||
s1 = s2 = s3 = s4 = s5 = s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2+8];
|
||||
s1 += yl[l+8] * (qs & qm.s0);
|
||||
s2 += yl[l+9] * (qs & qm.s1);
|
||||
s3 += ((h[l/2+8] & hm.s0) ? 0.f : yl[l+8]) + ((h[l/2+8] & hm.s1) ? 0.f : yl[l+9]);
|
||||
s4 += yl[l+24] * (qs & qm.s2);
|
||||
s5 += yl[l+25] * (qs & qm.s3);
|
||||
s6 += ((h[l/2+8] & hm.s2) ? 0.f : yl[l+24]) + ((h[l/2+8] & hm.s3) ? 0.f : yl[l+25]);
|
||||
}
|
||||
d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s1 - 32);
|
||||
sumf2[row] += d2 * (sc.s3 - 32);
|
||||
|
||||
q += row_stride*32;
|
||||
h += row_stride*16;
|
||||
a += row_stride*6;
|
||||
dh += row_stride*1;
|
||||
}
|
||||
|
||||
y1 += 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((sumf1[row] + 0.25f * sumf2[row]) / (1 << shift));
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +348,10 @@ static bool recv_msg(socket_ptr sock, std::vector<uint8_t> & input) {
|
||||
}
|
||||
try {
|
||||
input.resize(size);
|
||||
} catch (const std::bad_alloc & e) {
|
||||
} catch (const std::length_error &) {
|
||||
LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
|
||||
return false;
|
||||
} catch (const std::bad_alloc &) {
|
||||
LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
+22
-232
@@ -196,7 +196,8 @@ void concat_impl_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
template <typename block_t, int qk, ggml_type qtype>
|
||||
static void concat_impl_q_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
@@ -204,25 +205,25 @@ static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(src0->type == qtype);
|
||||
GGML_ASSERT(src1->type == qtype);
|
||||
GGML_ASSERT(dst->type == qtype);
|
||||
GGML_ASSERT(src0->ne[0] % qk == 0);
|
||||
GGML_ASSERT(src1->ne[0] % qk == 0);
|
||||
GGML_ASSERT(dst->ne[0] % qk == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK4_0;
|
||||
const int ne0_blk = dst->ne[0] / QK4_0;
|
||||
const int ne00_blk = src0->ne[0] / qk;
|
||||
const int ne0_blk = dst->ne[0] / qk;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q4_0 * src0_d = (const block_q4_0 *) src0->data;
|
||||
const block_q4_0 * src1_d = (const block_q4_0 *) src1->data;
|
||||
block_q4_0 * dst_d = (block_q4_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q4_0);
|
||||
const block_t * src0_d = (const block_t *) src0->data;
|
||||
const block_t * src1_d = (const block_t *) src1->data;
|
||||
block_t * dst_d = (block_t *) dst->data;
|
||||
const size_t type_size = sizeof(block_t);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q4_0>(
|
||||
concat_T_sycl<block_t>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
@@ -237,223 +238,12 @@ static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q4_0>(
|
||||
concat_T_sycl_non_cont<block_t>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK4_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q4_1_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(src0->ne[0] % QK4_1 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK4_1 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK4_1 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK4_1;
|
||||
const int ne0_blk = dst->ne[0] / QK4_1;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q4_1 * src0_d = (const block_q4_1 *) src0->data;
|
||||
const block_q4_1 * src1_d = (const block_q4_1 *) src1->data;
|
||||
block_q4_1 * dst_d = (block_q4_1 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q4_1);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q4_1>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q4_1>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK4_1, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q5_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK5_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK5_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK5_0 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK5_0;
|
||||
const int ne0_blk = dst->ne[0] / QK5_0;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q5_0 * src0_d = (const block_q5_0 *) src0->data;
|
||||
const block_q5_0 * src1_d = (const block_q5_0 *) src1->data;
|
||||
block_q5_0 * dst_d = (block_q5_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q5_0);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q5_0>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q5_0>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK5_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q5_1_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(src0->ne[0] % QK5_1 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK5_1 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK5_1 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK5_1;
|
||||
const int ne0_blk = dst->ne[0] / QK5_1;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q5_1 * src0_d = (const block_q5_1 *) src0->data;
|
||||
const block_q5_1 * src1_d = (const block_q5_1 *) src1->data;
|
||||
block_q5_1 * dst_d = (block_q5_1 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q5_1);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q5_1>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q5_1>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK5_1, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q8_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK8_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK8_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK8_0 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK8_0;
|
||||
const int ne0_blk = dst->ne[0] / QK8_0;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q8_0 * src0_d = (const block_q8_0 *) src0->data;
|
||||
const block_q8_0 * src1_d = (const block_q8_0 *) src1->data;
|
||||
block_q8_0 * dst_d = (block_q8_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q8_0);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q8_0>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q8_0>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK8_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->ne[0] / qk, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
@@ -487,19 +277,19 @@ void ggml_sycl_op_concat(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
concat_impl_sycl<int8_t>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_0:
|
||||
concat_impl_q4_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q4_0, QK4_0, GGML_TYPE_Q4_0>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_1:
|
||||
concat_impl_q4_1_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q4_1, QK4_1, GGML_TYPE_Q4_1>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_0:
|
||||
concat_impl_q5_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q5_0, QK5_0, GGML_TYPE_Q5_0>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_1:
|
||||
concat_impl_q5_1_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q5_1, QK5_1, GGML_TYPE_Q5_1>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q8_0:
|
||||
concat_impl_q8_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q8_0, QK8_0, GGML_TYPE_Q8_0>(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unsupported types: dst: %s\n", __func__, ggml_type_name(dst->type));
|
||||
|
||||
+367
-490
File diff suppressed because it is too large
Load Diff
+160
-670
File diff suppressed because it is too large
Load Diff
@@ -848,18 +848,18 @@ struct vk_device_struct {
|
||||
vk_pipeline pipeline_set_rows_i64[2][GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_norm_f32;
|
||||
vk_pipeline pipeline_group_norm_f32;
|
||||
vk_pipeline pipeline_rms_norm_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_mul_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_add_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_add_mul_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_mul_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_set_rows_f32_f32;
|
||||
vk_pipeline pipeline_rms_norm_set_rows_f32_f16;
|
||||
vk_pipeline pipeline_rms_norm_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_rope_f32_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_rope_f32_f16;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_rope_f32_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_rope_f32_f16;
|
||||
vk_pipeline pipeline_rms_norm_back_f32;
|
||||
vk_pipeline pipeline_l2_norm_f32;
|
||||
|
||||
|
||||
@@ -3256,21 +3256,34 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_norm_f32, "norm_f32", norm_f32_len, norm_f32_data, "main", 2, sizeof(vk_op_unary_push_constants), {1, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_group_norm_f32, "group_norm_f32", group_norm_f32_len, group_norm_f32_data, "main", 2, sizeof(vk_op_push_constants), {1, 1, 1}, {}, 1);
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_f32, "rms_norm_f32", rms_norm_f32_len, rms_norm_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_f32, "rms_norm_mul_f32", rms_norm_f32_len, rms_norm_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_add_f32, "rms_norm_mul_add_f32", rms_norm_mul_add_f32_len, rms_norm_mul_add_f32_data, "main", 5, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_add_mul_f32, "rms_norm_mul_add_mul_f32", rms_norm_mul_add_f32_len, rms_norm_mul_add_f32_data, "main", 5, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 1}, 1, true);
|
||||
const bool rms_subgroups = (device->vendor_id == VK_VENDOR_ID_INTEL || device->vendor_id == VK_VENDOR_ID_NVIDIA || device->vendor_id == VK_VENDOR_ID_AMD) && device->subgroup_arithmetic && device->subgroup_require_full_support;
|
||||
const uint32_t rms_set_rows_block_size = 128;
|
||||
#define RMS_SHADER(name) (rms_subgroups ? name##_subgroup_len : name##_len), (rms_subgroups ? name##_subgroup_data : name##_data)
|
||||
// 128/256/512 block sizes, picked by row length
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
const uint32_t block_size = 128u << i;
|
||||
const std::string suffix = "_" + std::to_string(block_size);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_f32[i], "rms_norm_f32" + suffix, RMS_SHADER(rms_norm_f32), "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0, 0, block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_mul_f32[i], "rms_norm_mul_f32" + suffix, RMS_SHADER(rms_norm_f32), "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 0, block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_mul_add_f32[i], "rms_norm_mul_add_f32" + suffix, RMS_SHADER(rms_norm_mul_add_f32), "main", 5, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 0, block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_mul_add_mul_f32[i], "rms_norm_mul_add_mul_f32" + suffix, RMS_SHADER(rms_norm_mul_add_f32), "main", 5, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 1, block_size}, 1, true, rms_subgroups);
|
||||
}
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_add_partials_f32, "rms_norm_mul_add_partials_f32", rms_norm_mul_add_partials_f32_len, rms_norm_mul_add_partials_f32_data, "main", 6, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_add_mul_partials_f32, "rms_norm_mul_add_mul_partials_f32", rms_norm_mul_add_partials_f32_len, rms_norm_mul_add_partials_f32_data, "main", 6, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1, 1}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_set_rows_f32_f32, "rms_norm_set_rows_f32_f32", rms_norm_set_rows_f32_f32_len, rms_norm_set_rows_f32_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_set_rows_f32_f16, "rms_norm_set_rows_f32_f16", rms_norm_set_rows_f32_f16_len, rms_norm_set_rows_f32_f16_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_set_rows_f32_f32, "rms_norm_set_rows_f32_f32", RMS_SHADER(rms_norm_set_rows_f32_f32), "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0, 0, rms_set_rows_block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_set_rows_f32_f16, "rms_norm_set_rows_f32_f16", RMS_SHADER(rms_norm_set_rows_f32_f16), "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0, 0, rms_set_rows_block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_partials_f32, "rms_norm_partials_f32", rms_norm_partials_f32_len, rms_norm_partials_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 0}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_partials_f32, "rms_norm_mul_partials_f32", rms_norm_partials_f32_len, rms_norm_partials_f32_data, "main", 4, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {0, 1}, 1, true);
|
||||
|
||||
if (sizeof(vk_op_rms_norm_mul_rope_push_constants) <= device->properties.limits.maxPushConstantsSize) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_rope_f32_f32, "rms_norm_mul_rope_f32_f32", rms_norm_mul_rope_f32_f32_len, rms_norm_mul_rope_f32_f32_data, "main", 7, sizeof(vk_op_rms_norm_mul_rope_push_constants), {1, 1, 1}, {0, 1}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_mul_rope_f32_f16, "rms_norm_mul_rope_f32_f16", rms_norm_mul_rope_f32_f16_len, rms_norm_mul_rope_f32_f16_data, "main", 7, sizeof(vk_op_rms_norm_mul_rope_push_constants), {1, 1, 1}, {0, 1}, 1, true);
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
const uint32_t block_size = 128u << i;
|
||||
const std::string suffix = "_" + std::to_string(block_size);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_mul_rope_f32_f32[i], "rms_norm_mul_rope_f32_f32" + suffix, RMS_SHADER(rms_norm_mul_rope_f32_f32), "main", 7, sizeof(vk_op_rms_norm_mul_rope_push_constants), {1, 1, 1}, {0, 1, 0, block_size}, 1, true, rms_subgroups);
|
||||
ggml_vk_create_pipeline2(device, device->pipeline_rms_norm_mul_rope_f32_f16[i], "rms_norm_mul_rope_f32_f16" + suffix, RMS_SHADER(rms_norm_mul_rope_f32_f16), "main", 7, sizeof(vk_op_rms_norm_mul_rope_push_constants), {1, 1, 1}, {0, 1, 0, block_size}, 1, true, rms_subgroups);
|
||||
}
|
||||
}
|
||||
#undef RMS_SHADER
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_rms_norm_back_f32, "rms_norm_back_f32", rms_norm_back_f32_len, rms_norm_back_f32_data, "main", 3, sizeof(vk_op_push_constants), {1, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_l2_norm_f32, "l2_norm_f32", l2_norm_f32_len, l2_norm_f32_data, "main", 2, sizeof(vk_op_unary_push_constants), {1, 1, 1}, {}, 1);
|
||||
@@ -7097,7 +7110,7 @@ static void ggml_vk_mul_mat_vec_nc_f16_f32(ggml_backend_vk_context * ctx, vk_con
|
||||
const uint32_t nb23 = (uint32_t)(dst->nb[3] / sizeof(float));
|
||||
|
||||
GGML_ASSERT(ne11 == 1);
|
||||
GGML_ASSERT(src0->ne[3] == src1->ne[3]); // checked in supports_op
|
||||
GGML_ASSERT(src0->ne[3] == src1->ne[3]); // checked in ggml_vk_mul_mat
|
||||
|
||||
const uint32_t row_stride_x = nb01 / sizeof(ggml_fp16_t);
|
||||
const uint32_t channel_stride_x = nb02 / sizeof(ggml_fp16_t);
|
||||
@@ -7369,7 +7382,7 @@ void ggml_vk_mul_mat(ggml_backend_vk_context * ctx, vk_context& subctx, const st
|
||||
src1->ne[2] <= ctx->device->properties.limits.maxComputeWorkGroupCount[2]) {
|
||||
ggml_vk_mul_mat_vec_p021_f16_f32(ctx, subctx, cgraph, node_idx);
|
||||
} else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F32 && !ggml_is_contiguous(src0) && !ggml_is_transposed(src1) && dst->ne[1] == 1 &&
|
||||
!ggml_is_permuted(src0) && !ggml_is_permuted(src1) &&
|
||||
!ggml_is_permuted(src0) && !ggml_is_permuted(src1) && src0->ne[3] == src1->ne[3] &&
|
||||
src0->ne[3] <= ctx->device->properties.limits.maxComputeWorkGroupCount[0] &&
|
||||
src0->ne[1] <= ctx->device->properties.limits.maxComputeWorkGroupCount[1] &&
|
||||
src1->ne[2] <= ctx->device->properties.limits.maxComputeWorkGroupCount[2]) {
|
||||
@@ -8740,6 +8753,16 @@ static vk_conv_shapes ggml_vk_conv3d_select_shape(ggml_backend_vk_context * ctx,
|
||||
return ggml_vk_conv_select_shape(ctx, K, NPQ);
|
||||
}
|
||||
|
||||
static vk_pipeline ggml_vk_rms_norm_pipeline(const std::array<vk_pipeline, 3> & pipelines, int64_t ncols) {
|
||||
if (pipelines[0] && ncols <= 128) {
|
||||
return pipelines[0];
|
||||
}
|
||||
if (pipelines[1] && ncols <= 256) {
|
||||
return pipelines[1];
|
||||
}
|
||||
return pipelines[2];
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -8980,7 +9003,7 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
||||
if (ctx->do_add_rms_partials) {
|
||||
return ctx->fused_rms_norm_mode == RMS_NORM_MUL ? ctx->device->pipeline_rms_norm_mul_partials_f32 : ctx->device->pipeline_rms_norm_partials_f32;
|
||||
}
|
||||
return ctx->fused_rms_norm_mode == RMS_NORM_MUL ? ctx->device->pipeline_rms_norm_mul_f32 : ctx->device->pipeline_rms_norm_f32;
|
||||
return ggml_vk_rms_norm_pipeline(ctx->fused_rms_norm_mode == RMS_NORM_MUL ? ctx->device->pipeline_rms_norm_mul_f32 : ctx->device->pipeline_rms_norm_f32, src0->ne[0]);
|
||||
}
|
||||
return nullptr;
|
||||
case GGML_OP_RMS_NORM_BACK:
|
||||
@@ -10942,8 +10965,8 @@ void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const s
|
||||
pipeline = do_post_multiply ?
|
||||
ctx->device->pipeline_rms_norm_mul_add_mul_partials_f32 : ctx->device->pipeline_rms_norm_mul_add_partials_f32;
|
||||
} else {
|
||||
pipeline = do_post_multiply ?
|
||||
ctx->device->pipeline_rms_norm_mul_add_mul_f32 : ctx->device->pipeline_rms_norm_mul_add_f32;
|
||||
pipeline = ggml_vk_rms_norm_pipeline(do_post_multiply ?
|
||||
ctx->device->pipeline_rms_norm_mul_add_mul_f32 : ctx->device->pipeline_rms_norm_mul_add_f32, src0->ne[0]);
|
||||
}
|
||||
ggml_pipeline_request_descriptor_sets(ctx, pipeline, 1);
|
||||
if (ctx->do_add_rms_partials) {
|
||||
@@ -11016,7 +11039,8 @@ void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const s
|
||||
pc.bin = bin;
|
||||
pc.rope = ggml_vk_make_rope_constants(rope, rope->src[0], tensors[4] != nullptr, false, set_rows_stride);
|
||||
|
||||
vk_pipeline pipeline = tensors[5]->type == GGML_TYPE_F16 ? ctx->device->pipeline_rms_norm_mul_rope_f32_f16 : ctx->device->pipeline_rms_norm_mul_rope_f32_f32;
|
||||
const auto & pipelines = tensors[5]->type == GGML_TYPE_F16 ? ctx->device->pipeline_rms_norm_mul_rope_f32_f16 : ctx->device->pipeline_rms_norm_mul_rope_f32_f32;
|
||||
vk_pipeline pipeline = ggml_vk_rms_norm_pipeline(pipelines, src0->ne[0]);
|
||||
|
||||
ggml_pipeline_request_descriptor_sets(ctx, pipeline, 1);
|
||||
|
||||
@@ -15608,16 +15632,8 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
struct ggml_tensor * a;
|
||||
struct ggml_tensor * b;
|
||||
if (op->op == GGML_OP_MUL_MAT) {
|
||||
a = op->src[0];
|
||||
b = op->src[1];
|
||||
} else {
|
||||
a = op->src[2];
|
||||
b = op->src[1];
|
||||
}
|
||||
if (a->ne[3] != b->ne[3]) {
|
||||
// MUL_MAT broadcasts src0 over dim 3 like dim 2, MUL_MAT_ID needs the ids to match src1
|
||||
if (op->op == GGML_OP_MUL_MAT_ID && op->src[2]->ne[3] != op->src[1]->ne[3]) {
|
||||
return false;
|
||||
}
|
||||
if (!(ggml_vk_dim01_contiguous(op->src[0]) || op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16 || op->src[0]->type == GGML_TYPE_BF16) ||
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
#include "generic_binary_head.glsl"
|
||||
#include "types.glsl"
|
||||
|
||||
#extension GL_EXT_control_flow_attributes : enable
|
||||
#if USE_SUBGROUP_ADD
|
||||
#extension GL_KHR_shader_subgroup_basic : require
|
||||
#extension GL_KHR_shader_subgroup_arithmetic : require
|
||||
#endif
|
||||
|
||||
layout (constant_id = 3) const int BLOCK_SIZE = 512;
|
||||
layout(local_size_x_id = 3, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#if RMS_NORM_ROPE_FUSION
|
||||
|
||||
layout (binding = 0) readonly buffer A {A_TYPE data_a[];};
|
||||
@@ -11,7 +20,8 @@ layout (binding = 1) readonly buffer B {B_TYPE data_b[];};
|
||||
// data is passed from rms_norm -> rope through shared memory.
|
||||
// rms_norm calls this data_d, rope calls this rope_data_a.
|
||||
// Binding 2 is not used
|
||||
shared FLOAT_TYPE rope_data_a[1024];
|
||||
// host picks a block size such that ncols <= 2*BLOCK_SIZE
|
||||
shared FLOAT_TYPE rope_data_a[2*BLOCK_SIZE];
|
||||
#define data_d rope_data_a
|
||||
|
||||
layout (binding = 3) readonly buffer R_Y {int rope_data_pos[];};
|
||||
@@ -38,16 +48,11 @@ layout (binding = 3) readonly buffer I {uvec2 data_i[];};
|
||||
|
||||
#endif
|
||||
|
||||
#extension GL_EXT_control_flow_attributes : enable
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
layout (constant_id = 1) const bool do_multiply = false;
|
||||
#if RMS_NORM_ADD_FUSION
|
||||
layout (constant_id = 2) const bool do_post_multiply = false;
|
||||
#endif
|
||||
|
||||
layout(local_size_x = BLOCK_SIZE, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
shared FLOAT_TYPE sumsh[BLOCK_SIZE];
|
||||
|
||||
void rms_norm(uint num_iters) {
|
||||
@@ -88,17 +93,38 @@ void rms_norm(uint num_iters) {
|
||||
sum += xi * xi;
|
||||
}
|
||||
|
||||
sumsh[tid] = sum;
|
||||
// sum up partial sums and write back result
|
||||
barrier();
|
||||
[[unroll]] for (int s = BLOCK_SIZE / 2; s > 0; s >>= 1) {
|
||||
if (tid < s) {
|
||||
sum += sumsh[tid + s];
|
||||
sumsh[tid] = sum;
|
||||
#if USE_SUBGROUP_ADD
|
||||
sum = subgroupAdd(sum);
|
||||
if (subgroupElect()) {
|
||||
sumsh[gl_SubgroupID] = sum;
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
sum = sumsh[0];
|
||||
sum = FLOAT_TYPE(0.0f);
|
||||
if(gl_NumSubgroups <= 4) { // fast path to skip the 2nd subgroupAdd
|
||||
[[unroll]] for(uint i=0; i < gl_NumSubgroups; ++i) {
|
||||
sum += sumsh[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Each subgroup reads the partials, so one workgroup barrier is enough.
|
||||
for (uint i = gl_SubgroupInvocationID; i < gl_NumSubgroups; i += gl_SubgroupSize) {
|
||||
sum += sumsh[i];
|
||||
}
|
||||
sum = subgroupAdd(sum);
|
||||
}
|
||||
#else
|
||||
sumsh[tid] = sum;
|
||||
// sum up partial sums and write back result
|
||||
barrier();
|
||||
[[unroll]] for (int s = BLOCK_SIZE / 2; s > 0; s >>= 1) {
|
||||
if (tid < s) {
|
||||
sum += sumsh[tid + s];
|
||||
sumsh[tid] = sum;
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
sum = sumsh[0];
|
||||
#endif
|
||||
|
||||
const FLOAT_TYPE mean = sum / FLOAT_TYPE(ncols);
|
||||
const FLOAT_TYPE scale = inversesqrt(mean + FLOAT_TYPE(p.param1));
|
||||
|
||||
@@ -853,14 +853,18 @@ void process_shaders() {
|
||||
// Norms
|
||||
string_to_spv("norm_f32", "norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("group_norm_f32", "group_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("rms_norm_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("rms_norm_mul_add_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_ADD_FUSION", "1"}}));
|
||||
for (bool use_subgroups : {false, true}) {
|
||||
const std::string suffix = use_subgroups ? "_subgroup" : "";
|
||||
const auto rms_dict = merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", use_subgroups ? "1" : "0"}});
|
||||
string_to_spv("rms_norm_f32" + suffix, "rms_norm.comp", rms_dict);
|
||||
string_to_spv("rms_norm_mul_add_f32" + suffix, "rms_norm.comp", merge_maps(rms_dict, {{"RMS_NORM_ADD_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_set_rows_f32_f32" + suffix, "rms_norm.comp", merge_maps(rms_dict, {{"RMS_NORM_SET_ROWS_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_set_rows_f32_f16" + suffix, "rms_norm.comp", merge_maps({{"D_TYPE", "float16_t"}, {"RMS_NORM_SET_ROWS_FUSION", "1"}}, rms_dict)); // keep rms_dict last so 'merge_maps' overwrites the value for key 'D_TYPE'
|
||||
string_to_spv("rms_norm_mul_rope_f32_f32" + suffix, "rms_norm.comp", merge_maps(rms_dict, {{"ROPE_D_TYPE", "float"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_mul_rope_f32_f16" + suffix, "rms_norm.comp", merge_maps(rms_dict, {{"ROPE_D_TYPE", "float16_t"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
|
||||
}
|
||||
string_to_spv("rms_norm_mul_add_partials_f32", "rms_norm_partials.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_ADD_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_set_rows_f32_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_SET_ROWS_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_set_rows_f32_f16", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RMS_NORM_SET_ROWS_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_partials_f32", "rms_norm_partials.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("rms_norm_mul_rope_f32_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"ROPE_D_TYPE", "float"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_mul_rope_f32_f16", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"ROPE_D_TYPE", "float16_t"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
|
||||
string_to_spv("rms_norm_back_f32", "rms_norm_back.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("l2_norm_f32", "l2_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
|
||||
|
||||
@@ -670,6 +670,8 @@ static void ggml_backend_webgpu_buffer_memset(webgpu_global_context & ctx,
|
||||
uint32_t value,
|
||||
size_t offset,
|
||||
size_t size) {
|
||||
std::lock_guard<std::recursive_mutex> lock(ctx->mutex);
|
||||
|
||||
std::vector<uint32_t> params = { (uint32_t) offset, (uint32_t) size, value };
|
||||
std::vector<wgpu::BindGroupEntry> entries = { ggml_webgpu_make_bind_group_entry(0, buf, 0, buf.GetSize()) };
|
||||
size_t bytes_per_wg =
|
||||
|
||||
+1
-1
@@ -3503,7 +3503,7 @@ static struct ggml_tensor * ggml_scale_impl(
|
||||
float s,
|
||||
float b,
|
||||
bool inplace) {
|
||||
GGML_ASSERT(ggml_is_padded_1d(a));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(a));
|
||||
|
||||
struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a);
|
||||
|
||||
|
||||
@@ -501,6 +501,7 @@ class Keys:
|
||||
|
||||
class Attention:
|
||||
HEAD_COUNT = "clip.audio.attention.head_count"
|
||||
HEAD_COUNT_KV = "clip.audio.attention.head_count_kv"
|
||||
HEAD_DIM = "clip.audio.attention.head_dim" # set when qkv width != n_embd
|
||||
LAYERNORM_EPS = "clip.audio.attention.layer_norm_epsilon"
|
||||
|
||||
@@ -518,6 +519,11 @@ class Keys:
|
||||
FEED_FORWARD_LENGTH = "clip.gen.audio.feed_forward_length"
|
||||
BLOCK_COUNT = "clip.gen.audio.block_count"
|
||||
PROJECTION_DIM = "clip.gen.audio.projection_dim"
|
||||
# sampling defaults of the generated audio, like general.sampling.* for text
|
||||
SAMPLING_TEMP = "clip.gen.audio.sampling.temp"
|
||||
SAMPLING_TOP_K = "clip.gen.audio.sampling.top_k"
|
||||
SAMPLING_PENALTY_REPEAT = "clip.gen.audio.sampling.penalty_repeat"
|
||||
SAMPLING_PENALTY_LAST_N = "clip.gen.audio.sampling.penalty_last_n"
|
||||
|
||||
class Attention:
|
||||
HEAD_COUNT = "clip.gen.audio.attention.head_count"
|
||||
@@ -691,6 +697,7 @@ class MODEL_ARCH(IntEnum):
|
||||
GPT_OSS = auto()
|
||||
LFM2 = auto()
|
||||
LFM2MOE = auto()
|
||||
LFM3 = auto()
|
||||
DREAM = auto()
|
||||
SMALLTHINKER = auto()
|
||||
LLADA = auto()
|
||||
@@ -920,6 +927,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
|
||||
@@ -1171,6 +1182,8 @@ class MODEL_TENSOR(IntEnum):
|
||||
A_ENC_ATTN_Q = auto()
|
||||
A_ENC_ATTN_K = auto()
|
||||
A_ENC_ATTN_V = auto()
|
||||
A_ENC_ATTN_Q_NORM = auto() # lfm3a
|
||||
A_ENC_ATTN_K_NORM = auto() # lfm3a
|
||||
A_ENC_ATTN_POST_NORM = auto()
|
||||
A_ENC_ATTN_PRE_NORM = auto()
|
||||
A_ENC_ATTN_K_REL = auto() # gemma4
|
||||
@@ -1215,6 +1228,9 @@ class MODEL_TENSOR(IntEnum):
|
||||
A_GEN_CODE_FFN_UP = auto()
|
||||
A_GEN_CODE_FFN_DOWN = auto()
|
||||
A_GEN_CODE_OUTPUT_NORM = auto()
|
||||
A_GEN_CODE_ATTN_GATE = auto() # lfm3a: per-head sigmoid gate on the attention output
|
||||
A_GEN_CODE_HEAD_NORM = auto() # lfm3a: per-codebook norm before the output head, merged 2D [n_codebooks, dim]
|
||||
A_GEN_CODE_MASK_EMBD = auto() # lfm3a: MaskGIT mask embedding
|
||||
# qwen3tts code2wav: RVQ codes -> raw PCM
|
||||
A_GEN_WAV_QUANT_FIRST_IN = auto() # semantic RVQ, in_proj (1x1 conv, loaded as 2D)
|
||||
A_GEN_WAV_QUANT_FIRST_OUT = auto() # semantic RVQ, out_proj
|
||||
@@ -1237,6 +1253,14 @@ class MODEL_TENSOR(IntEnum):
|
||||
A_GEN_WAV_TFM_FFN_UP = auto()
|
||||
A_GEN_WAV_TFM_FFN_DOWN = auto()
|
||||
A_GEN_WAV_TFM_FFN_SCALE = auto() # layer scale (gamma) on the FFN output
|
||||
A_GEN_WAV_TFM_ATTN_Q_NORM = auto() # lfm3a
|
||||
A_GEN_WAV_TFM_ATTN_K_NORM = auto() # lfm3a
|
||||
A_GEN_WAV_TFM_CONV_PW1 = auto() # lfm3a: short conv in_proj
|
||||
A_GEN_WAV_TFM_CONV_DW = auto() # lfm3a: short conv depthwise kernel
|
||||
A_GEN_WAV_TFM_CONV_PW2 = auto() # lfm3a: short conv out_proj
|
||||
A_GEN_WAV_TFM_ATTN_GATE = auto() # lfm3a: per-head sigmoid gate on the attention output
|
||||
A_GEN_WAV_TFM_ATTN_POST_NORM = auto() # lfm3a: sandwich norm after the operator
|
||||
A_GEN_WAV_TFM_FFN_POST_NORM = auto() # lfm3a: sandwich norm after the FFN
|
||||
A_GEN_WAV_UP_CONV = auto() # causal ConvTranspose1d, 2x upsample
|
||||
A_GEN_WAV_UP_DWCONV = auto() # ConvNeXt depthwise conv
|
||||
A_GEN_WAV_UP_NORM = auto() # ConvNeXt LayerNorm
|
||||
@@ -1519,6 +1543,7 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
||||
MODEL_ARCH.GPT_OSS: "gpt-oss",
|
||||
MODEL_ARCH.LFM2: "lfm2",
|
||||
MODEL_ARCH.LFM2MOE: "lfm2moe",
|
||||
MODEL_ARCH.LFM3: "lfm3",
|
||||
MODEL_ARCH.DREAM: "dream",
|
||||
MODEL_ARCH.SMALLTHINKER: "smallthinker",
|
||||
MODEL_ARCH.LLADA: "llada",
|
||||
@@ -1746,6 +1771,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
|
||||
@@ -1994,6 +2023,8 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q: "a.blk.{bid}.attn_q",
|
||||
MODEL_TENSOR.A_ENC_ATTN_K: "a.blk.{bid}.attn_k",
|
||||
MODEL_TENSOR.A_ENC_ATTN_V: "a.blk.{bid}.attn_v",
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q_NORM: "a.blk.{bid}.attn_q_norm",
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_NORM: "a.blk.{bid}.attn_k_norm",
|
||||
MODEL_TENSOR.A_ENC_ATTN_POST_NORM: "a.blk.{bid}.attn_post_norm",
|
||||
MODEL_TENSOR.A_ENC_ATTN_PRE_NORM: "a.blk.{bid}.attn_pre_norm",
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_REL: "a.blk.{bid}.attn_k_rel",
|
||||
@@ -2037,6 +2068,9 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.A_GEN_CODE_FFN_UP: "a.gen.code.blk.{bid}.ffn_up",
|
||||
MODEL_TENSOR.A_GEN_CODE_FFN_DOWN: "a.gen.code.blk.{bid}.ffn_down",
|
||||
MODEL_TENSOR.A_GEN_CODE_OUTPUT_NORM: "a.gen.code.output_norm",
|
||||
MODEL_TENSOR.A_GEN_CODE_ATTN_GATE: "a.gen.code.blk.{bid}.attn_gate",
|
||||
MODEL_TENSOR.A_GEN_CODE_HEAD_NORM: "a.gen.code.head_norm",
|
||||
MODEL_TENSOR.A_GEN_CODE_MASK_EMBD: "a.gen.code.mask_embd",
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_IN: "a.gen.wav.quant.first.in_proj",
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_OUT: "a.gen.wav.quant.first.out_proj",
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_CB: "a.gen.wav.quant.first.codebook",
|
||||
@@ -2058,6 +2092,14 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_UP: "a.gen.wav.tfm.blk.{bid}.ffn_up",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_DOWN: "a.gen.wav.tfm.blk.{bid}.ffn_down",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_SCALE: "a.gen.wav.tfm.blk.{bid}.ls2",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q_NORM: "a.gen.wav.tfm.blk.{bid}.attn_q_norm",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K_NORM: "a.gen.wav.tfm.blk.{bid}.attn_k_norm",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW1: "a.gen.wav.tfm.blk.{bid}.conv_pw1",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_DW: "a.gen.wav.tfm.blk.{bid}.conv_dw",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW2: "a.gen.wav.tfm.blk.{bid}.conv_pw2",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_GATE: "a.gen.wav.tfm.blk.{bid}.attn_gate",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_POST_NORM: "a.gen.wav.tfm.blk.{bid}.attn_post_norm",
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_POST_NORM: "a.gen.wav.tfm.blk.{bid}.ffn_post_norm",
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_CONV: "a.gen.wav.up.blk.{bid}.conv",
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_DWCONV: "a.gen.wav.up.blk.{bid}.dwconv",
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_NORM: "a.gen.wav.up.blk.{bid}.norm",
|
||||
@@ -2354,6 +2396,8 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q,
|
||||
MODEL_TENSOR.A_ENC_ATTN_K,
|
||||
MODEL_TENSOR.A_ENC_ATTN_V,
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q_NORM,
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_NORM,
|
||||
MODEL_TENSOR.A_ENC_ATTN_POST_NORM,
|
||||
MODEL_TENSOR.A_ENC_ATTN_PRE_NORM,
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_REL,
|
||||
@@ -2421,6 +2465,9 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.A_GEN_CODE_FFN_UP,
|
||||
MODEL_TENSOR.A_GEN_CODE_FFN_DOWN,
|
||||
MODEL_TENSOR.A_GEN_CODE_OUTPUT_NORM,
|
||||
MODEL_TENSOR.A_GEN_CODE_ATTN_GATE,
|
||||
MODEL_TENSOR.A_GEN_CODE_HEAD_NORM,
|
||||
MODEL_TENSOR.A_GEN_CODE_MASK_EMBD,
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_IN,
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_OUT,
|
||||
MODEL_TENSOR.A_GEN_WAV_QUANT_FIRST_CB,
|
||||
@@ -2442,6 +2489,14 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_UP,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_DOWN,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_SCALE,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q_NORM,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K_NORM,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW1,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_DW,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW2,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_GATE,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_POST_NORM,
|
||||
MODEL_TENSOR.A_GEN_WAV_TFM_FFN_POST_NORM,
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_CONV,
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_DWCONV,
|
||||
MODEL_TENSOR.A_GEN_WAV_UP_NORM,
|
||||
@@ -3309,6 +3364,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,
|
||||
@@ -5510,6 +5569,29 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.FFN_UP_EXP,
|
||||
MODEL_TENSOR.FFN_EXP_PROBS_B,
|
||||
],
|
||||
MODEL_ARCH.LFM3: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
MODEL_TENSOR.OUTPUT,
|
||||
MODEL_TENSOR.ROPE_FREQS,
|
||||
MODEL_TENSOR.SHORTCONV_CONV,
|
||||
MODEL_TENSOR.SHORTCONV_INPROJ,
|
||||
MODEL_TENSOR.SHORTCONV_OUTPROJ,
|
||||
MODEL_TENSOR.FFN_GATE,
|
||||
MODEL_TENSOR.FFN_DOWN,
|
||||
MODEL_TENSOR.FFN_UP,
|
||||
MODEL_TENSOR.FFN_NORM,
|
||||
MODEL_TENSOR.FFN_POST_NORM,
|
||||
MODEL_TENSOR.ATTN_NORM, # operator_norm
|
||||
MODEL_TENSOR.ATTN_POST_NORM,
|
||||
MODEL_TENSOR.ATTN_Q_NORM,
|
||||
MODEL_TENSOR.ATTN_K_NORM,
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
MODEL_TENSOR.ATTN_K,
|
||||
MODEL_TENSOR.ATTN_V,
|
||||
MODEL_TENSOR.ATTN_OUT,
|
||||
MODEL_TENSOR.ATTN_GATE,
|
||||
],
|
||||
MODEL_ARCH.SMALLTHINKER: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
@@ -6462,8 +6544,11 @@ class DecisionType:
|
||||
VLLM_D3 = "vllm-d3" # same as pplx-decider, non-causal attention
|
||||
LFM2_D1 = "lfm2-d1" # same as openjev, the labels depend on the question type
|
||||
LFM2_D1_OMNI = "lfm2-d1-omni" # same head as laya on a bidirectional LFM2 trunk, other prompt layout
|
||||
LFM3_D1 = "lfm3-d1" # same as lfm2-d1 on LFM3-VL, full attention layers read the prompt both ways
|
||||
LFM3_D1_OMNI = "lfm3-d1-omni" # same as lfm3-d1 on LFM3-Omni
|
||||
GRANITE_SWITCH = "granite-switch" # same as openjev, run by the decision aLoRA of granite switch
|
||||
H2O_LIGHTNING = "h2o-lightning" # same as openjev, labels read after an answer prefill
|
||||
MINICPM_OCR_JEV = "openbmb-minicpm-ocr-jev" # class scores of a value head read at the last token, one fixed task
|
||||
|
||||
|
||||
class VisionProjectorType:
|
||||
@@ -6494,6 +6579,7 @@ class VisionProjectorType:
|
||||
VOXTRAL_RT = "voxtral_rt"
|
||||
MERALION = "meralion" # audio: Whisper + gated MLP adaptor
|
||||
LFM2 = "lfm2"
|
||||
LFM3VL = "lfm3vl"
|
||||
KIMIVL = "kimivl"
|
||||
PADDLEOCR = "paddleocr"
|
||||
KIMIK25 = "kimik25"
|
||||
@@ -6508,6 +6594,8 @@ class VisionProjectorType:
|
||||
DEEPSEEK4V = "deepseek4v"
|
||||
DEEPSEEK41V = "deepseek41v"
|
||||
LFM2A = "lfm2a" # audio
|
||||
LFM3A = "lfm3a" # audio
|
||||
LFM3A_GEN = "lfm3a_gen" # audio generation: MaskGIT depthformer + liquid-codec decoder
|
||||
MUSIC_FLAMINGO = "musicflamingo" # audio
|
||||
GLM4V = "glm4v"
|
||||
GLM5V = "glm5v"
|
||||
|
||||
@@ -1628,6 +1628,9 @@ class GGUFWriter:
|
||||
def add_audio_head_count(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipAudio.Attention.HEAD_COUNT, value)
|
||||
|
||||
def add_audio_head_count_kv(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipAudio.Attention.HEAD_COUNT_KV, value)
|
||||
|
||||
def add_audio_head_dim(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipAudio.Attention.HEAD_DIM, value)
|
||||
|
||||
@@ -1708,6 +1711,18 @@ class GGUFWriter:
|
||||
def add_gen_audio_block_count(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipGenAudio.BLOCK_COUNT, value)
|
||||
|
||||
def add_gen_audio_sampling_temp(self, value: float) -> None:
|
||||
self.add_float32(Keys.ClipGenAudio.SAMPLING_TEMP, value)
|
||||
|
||||
def add_gen_audio_sampling_top_k(self, value: int) -> None:
|
||||
self.add_int32(Keys.ClipGenAudio.SAMPLING_TOP_K, value)
|
||||
|
||||
def add_gen_audio_sampling_penalty_repeat(self, value: float) -> None:
|
||||
self.add_float32(Keys.ClipGenAudio.SAMPLING_PENALTY_REPEAT, value)
|
||||
|
||||
def add_gen_audio_sampling_penalty_last_n(self, value: int) -> None:
|
||||
self.add_int32(Keys.ClipGenAudio.SAMPLING_PENALTY_LAST_N, value)
|
||||
|
||||
def add_gen_audio_head_count(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipGenAudio.Attention.HEAD_COUNT, value)
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ class TensorNameMap:
|
||||
"model.transformer.ln_f", # llada
|
||||
"final_norm", # modern-bert
|
||||
"model.norm", # cogvlm
|
||||
"model.embedding_norm", # lfm3
|
||||
),
|
||||
|
||||
# Rope frequencies
|
||||
@@ -418,6 +419,7 @@ class TensorNameMap:
|
||||
"model.layers.{bid}.post_self_attn_layernorm", # glm-4-0414
|
||||
"model.layers.{bid}.post_attn_norm", # kolibri1
|
||||
"model.layers.layers.{bid}.post_mixer_norm.weight", # plamo2
|
||||
"model.layers.{bid}.post_operator_norm", # lfm3
|
||||
),
|
||||
|
||||
# Rotary embeddings
|
||||
@@ -504,6 +506,7 @@ class TensorNameMap:
|
||||
"model.layers.layers.{bid}.post_mlp_norm.weight", # plamo2
|
||||
"model.layers.{bid}.feed_forward.up_proj",
|
||||
"model.layers.{bid}.post_moe_norm", # grok-2
|
||||
"model.layers.{bid}.post_ffn_norm", # lfm3
|
||||
),
|
||||
|
||||
MODEL_TENSOR.FFN_POST_NORM_1: (
|
||||
@@ -1855,6 +1858,7 @@ class TensorNameMap:
|
||||
"vision_model.model.layers.{bid}.self_attn.q_proj", # llama4
|
||||
"vision_tower.transformer.layers.{bid}.attention.q_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.attention.wq", # pixtral
|
||||
"model.vision_tower.layers.{bid}.self_attn.q_proj", # lfm3vl
|
||||
"visual.blocks.{bid}.attn.q", # qwen2vl, generated
|
||||
"vision_tower.encoder.blocks.{bid}.wq", # kimi-vl, generated
|
||||
"siglip2.vision_model.encoder.layers.{bid}.self_attn.q_proj", # youtuvl
|
||||
@@ -1870,6 +1874,7 @@ class TensorNameMap:
|
||||
"model.vision_tower.encoder.layer.{bid}.attention.q_norm", # Intern-S1
|
||||
"visual.blocks.{bid}.attn.q_norm", # GLM-OCR
|
||||
"vision_model.model.layers.{bid}.self_attn.q_norm", # gemma4
|
||||
"model.vision_tower.layers.{bid}.self_attn.q_layernorm", # lfm3vl
|
||||
),
|
||||
|
||||
MODEL_TENSOR.V_ENC_ATTN_K: (
|
||||
@@ -1884,6 +1889,7 @@ class TensorNameMap:
|
||||
"vision_model.model.layers.{bid}.self_attn.k_proj", # llama4
|
||||
"vision_tower.transformer.layers.{bid}.attention.k_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.attention.wk", # pixtral
|
||||
"model.vision_tower.layers.{bid}.self_attn.k_proj", # lfm3vl
|
||||
"visual.blocks.{bid}.attn.k", # qwen2vl, generated
|
||||
"vision_tower.encoder.blocks.{bid}.wk", # kimi-vl, generated
|
||||
"model.vision_model.transformer.layers.{bid}.self_attn.k_proj", # Deepseek-OCR CLIP, generated
|
||||
@@ -1899,6 +1905,7 @@ class TensorNameMap:
|
||||
"model.vision_tower.encoder.layer.{bid}.attention.k_norm", # Intern-S1
|
||||
"visual.blocks.{bid}.attn.k_norm", # GLM-OCR
|
||||
"vision_model.model.layers.{bid}.self_attn.k_norm", # gemma4
|
||||
"model.vision_tower.layers.{bid}.self_attn.k_layernorm", # lfm3vl
|
||||
),
|
||||
|
||||
MODEL_TENSOR.V_ENC_ATTN_V: (
|
||||
@@ -1913,6 +1920,7 @@ class TensorNameMap:
|
||||
"vision_model.model.layers.{bid}.self_attn.v_proj", # llama4
|
||||
"vision_tower.transformer.layers.{bid}.attention.v_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.attention.wv", # pixtral
|
||||
"model.vision_tower.layers.{bid}.self_attn.v_proj", # lfm3vl
|
||||
"visual.blocks.{bid}.attn.v", # qwen2vl, generated
|
||||
"vision_tower.encoder.blocks.{bid}.wv", # kimi-vl, generated
|
||||
"siglip2.vision_model.encoder.layers.{bid}.self_attn.v_proj",
|
||||
@@ -1935,6 +1943,7 @@ class TensorNameMap:
|
||||
"vit.layers.{bid}.input_layernorm", # HunyuanVL
|
||||
"vision_tower.transformer.layers.{bid}.attention_norm", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.attention_norm", # pixtral
|
||||
"model.vision_tower.layers.{bid}.operator_norm", # lfm3vl
|
||||
"vision_model.model.layers.{bid}.input_layernorm", # llama4, gemma4
|
||||
"visual.blocks.{bid}.norm1", # qwen2vl
|
||||
"vision_tower.encoder.blocks.{bid}.norm0", # kimi-vl (norm0/norm1)
|
||||
@@ -1964,6 +1973,7 @@ class TensorNameMap:
|
||||
"vision_model.model.layers.{bid}.self_attn.o_proj", # llama4
|
||||
"vision_tower.transformer.layers.{bid}.attention.o_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.attention.wo", # pixtral
|
||||
"model.vision_tower.layers.{bid}.self_attn.out_proj", # lfm3vl
|
||||
"visual.blocks.{bid}.attn.proj", # qwen2vl
|
||||
"vision_tower.encoder.blocks.{bid}.wo", # kimi-vl
|
||||
"model.vision.transformer.layers.{bid}.attention.dense", # cogvlm
|
||||
@@ -1996,6 +2006,7 @@ class TensorNameMap:
|
||||
"vision_model.model.layers.{bid}.post_attention_layernorm", # llama4
|
||||
"vision_tower.transformer.layers.{bid}.ffn_norm", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.ffn_norm", # pixtral
|
||||
"model.vision_tower.layers.{bid}.ffn_norm", # lfm3vl
|
||||
"visual.blocks.{bid}.norm2", # qwen2vl
|
||||
"vision_tower.encoder.blocks.{bid}.norm1", # kimi-vl (norm0/norm1)
|
||||
"model.vision.transformer.layers.{bid}.post_attention_layernorm", # cogvlm
|
||||
@@ -2023,6 +2034,7 @@ class TensorNameMap:
|
||||
"vit.layers.{bid}.mlp.dense_h_to_4h", # HunyuanVL
|
||||
"vision_tower.transformer.layers.{bid}.feed_forward.up_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.feed_forward.w3", # pixtral
|
||||
"model.vision_tower.layers.{bid}.feed_forward.w3", # lfm3vl
|
||||
"vision_model.model.layers.{bid}.mlp.fc1", # llama4
|
||||
"visual.blocks.{bid}.mlp.fc1", # qwen2vl
|
||||
"visual.blocks.{bid}.mlp.up_proj", # qwen2.5vl
|
||||
@@ -2043,6 +2055,7 @@ class TensorNameMap:
|
||||
"vision_encoder.blocks.{bid}.mlp.fc1", # dots3note
|
||||
"vision_tower.transformer.layers.{bid}.feed_forward.gate_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.feed_forward.w1", # pixtral
|
||||
"model.vision_tower.layers.{bid}.feed_forward.w1", # lfm3vl
|
||||
"visual.blocks.{bid}.mlp.gate_proj", # qwen2.5vl
|
||||
"vision_model.model.layers.{bid}.mlp.gate_proj", # gemma4
|
||||
"model.qwen2_model.model.model.layers.{bid}.mlp.gate_proj", # Deepseek-OCR-2 qwen2
|
||||
@@ -2061,6 +2074,7 @@ class TensorNameMap:
|
||||
"vit.layers.{bid}.mlp.dense_4h_to_h", # HunyuanVL
|
||||
"vision_tower.transformer.layers.{bid}.feed_forward.down_proj", # pixtral-hf
|
||||
"vision_encoder.transformer.layers.{bid}.feed_forward.w2", # pixtral
|
||||
"model.vision_tower.layers.{bid}.feed_forward.w2", # lfm3vl
|
||||
"vision_model.model.layers.{bid}.mlp.fc2", # llama4
|
||||
"visual.blocks.{bid}.mlp.fc2", # qwen2vl
|
||||
"visual.blocks.{bid}.mlp.down_proj", # qwen2.5vl
|
||||
@@ -2540,12 +2554,14 @@ class TensorNameMap:
|
||||
"audio_encoder.dots_encoder.speech_encoder.layer_norm", # dots3note
|
||||
"audio_tower.ln_post", # qwen2omni
|
||||
"encoder.layer_norm", # mimo-audio-tokenizer
|
||||
"encoder.lfm.lfm.embedding_norm", # lfm3a
|
||||
),
|
||||
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q: (
|
||||
"audio_tower.layers.{bid}.self_attn.q_proj", # ultravox
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.self_attn.q_proj", # dots3note
|
||||
"conformer.layers.{bid}.self_attn.linear_q", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.q_proj", # lfm3a
|
||||
"conformer.layers.{bid}.attention.attn.q_proj", # gemma3n
|
||||
"conformer.layers.{bid}.self_attn.q_proj", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.self_attn.q_proj", # parakeet
|
||||
@@ -2557,6 +2573,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.self_attn.k_proj", # ultravox
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.self_attn.k_proj", # dots3note
|
||||
"conformer.layers.{bid}.self_attn.linear_k", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.k_proj", # lfm3a
|
||||
"conformer.layers.{bid}.attention.attn.k_proj", # gemma3n
|
||||
"conformer.layers.{bid}.self_attn.k_proj", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.self_attn.k_proj", # parakeet
|
||||
@@ -2568,6 +2585,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.self_attn.v_proj", # ultravox
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.self_attn.v_proj", # dots3note
|
||||
"conformer.layers.{bid}.self_attn.linear_v", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.v_proj", # lfm3a
|
||||
"conformer.layers.{bid}.attention.attn.v_proj", # gemma3n
|
||||
"conformer.layers.{bid}.self_attn.v_proj", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.self_attn.v_proj", # parakeet
|
||||
@@ -2575,6 +2593,14 @@ class TensorNameMap:
|
||||
"encoder.layers.{bid}.self_attn.v_proj", # mimo-audio-tokenizer
|
||||
),
|
||||
|
||||
MODEL_TENSOR.A_ENC_ATTN_Q_NORM: (
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.q_layernorm", # lfm3a
|
||||
),
|
||||
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_NORM: (
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.k_layernorm", # lfm3a
|
||||
),
|
||||
|
||||
MODEL_TENSOR.A_ENC_ATTN_K_REL: (
|
||||
"conformer.layers.{bid}.self_attn.relative_k_proj", # gemma4
|
||||
),
|
||||
@@ -2600,6 +2626,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.self_attn_layer_norm", # ultravox
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.self_attn_layer_norm", # dots3note
|
||||
"conformer.layers.{bid}.norm_self_att", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.operator_norm", # lfm3a
|
||||
"conformer.layers.{bid}.attention.pre_attn_norm", # gemma3n
|
||||
"sound_encoder.encoder.layers.{bid}.norm_self_att", # parakeet
|
||||
"encoder.layers.{bid}.attn.pre_norm", # granite_speech
|
||||
@@ -2611,6 +2638,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.self_attn.o_proj", # voxtral realtime
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.self_attn.out_proj", # dots3note
|
||||
"conformer.layers.{bid}.self_attn.linear_out", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.self_attn.out_proj", # lfm3a
|
||||
"conformer.layers.{bid}.attention.post", # gemma3n
|
||||
"conformer.layers.{bid}.self_attn.post", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.self_attn.o_proj", # parakeet
|
||||
@@ -2622,6 +2650,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.final_layer_norm", # ultravox
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.final_layer_norm", # dots3note
|
||||
"conformer.layers.{bid}.norm_out", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.ffn_norm", # lfm3a
|
||||
"conformer.layers.{bid}.attention.post_norm", # gemma3n
|
||||
"sound_encoder.encoder.layers.{bid}.norm_out", # parakeet
|
||||
"encoder.layers.{bid}.post_norm", # granite_speech
|
||||
@@ -2650,6 +2679,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.fc1", # ultravox
|
||||
"audio_tower.layers.{bid}.mlp.up_proj", # voxtral realtime
|
||||
"conformer.layers.{bid}.feed_forward1.linear1", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.feed_forward.w3", # lfm3a
|
||||
"conformer.layers.{bid}.ffw_layer_start.ffw_layer_1", # gemma3n
|
||||
"conformer.layers.{bid}.feed_forward1.ffw_layer_1", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.feed_forward1.linear1", # parakeet
|
||||
@@ -2659,6 +2689,7 @@ class TensorNameMap:
|
||||
|
||||
MODEL_TENSOR.A_ENC_FFN_GATE: (
|
||||
"audio_encoder.dots_encoder.speech_encoder.layers.{bid}.fc1_gate", # dots3note (split from fc1 in conversion code)
|
||||
"encoder.lfm.lfm.layers.{bid}.feed_forward.w1", # lfm3a
|
||||
"audio_tower.layers.{bid}.mlp.gate_proj", # voxtral realtime
|
||||
),
|
||||
|
||||
@@ -2667,6 +2698,7 @@ class TensorNameMap:
|
||||
"audio_tower.layers.{bid}.fc2", # ultravox
|
||||
"audio_tower.layers.{bid}.mlp.down_proj", # voxtral realtime
|
||||
"conformer.layers.{bid}.feed_forward1.linear2", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.feed_forward.w2", # lfm3a
|
||||
"conformer.layers.{bid}.ffw_layer_start.ffw_layer_2", # gemma3n
|
||||
"conformer.layers.{bid}.feed_forward1.ffw_layer_2", # gemma4
|
||||
"sound_encoder.encoder.layers.{bid}.feed_forward1.linear2", # parakeet
|
||||
@@ -2750,6 +2782,7 @@ class TensorNameMap:
|
||||
"audio.multi_modal_projector.linear_{bid}", # ultravox, meralion
|
||||
"audio_encoder.audio_adapter.proj.{bid}", # dots3note (proj.1, proj.3)
|
||||
"audio_adapter.model.{bid}", # lfm2
|
||||
"mlp.model.{bid}", # lfm3a
|
||||
"audio_tower.proj{bid}", # qwen3omni
|
||||
"sound_projection.linear{bid}", # parakeet (linear1, linear2)
|
||||
),
|
||||
@@ -2809,6 +2842,7 @@ class TensorNameMap:
|
||||
|
||||
MODEL_TENSOR.A_ENC_CONV_DW: (
|
||||
"conformer.layers.{bid}.conv.depthwise_conv", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.conv.conv", # lfm3a
|
||||
"conformer.layers.{bid}.lconv1d.depthwise_conv1d", # gemma3n
|
||||
"sound_encoder.encoder.layers.{bid}.conv.depthwise_conv", # parakeet
|
||||
"encoder.layers.{bid}.conv.depth_conv.conv", # granite_speech
|
||||
@@ -2831,6 +2865,7 @@ class TensorNameMap:
|
||||
|
||||
MODEL_TENSOR.A_ENC_CONV_PW1: (
|
||||
"conformer.layers.{bid}.conv.pointwise_conv1", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.conv.in_proj", # lfm3a
|
||||
"conformer.layers.{bid}.lconv1d.linear_start", # gemma3n
|
||||
"sound_encoder.encoder.layers.{bid}.conv.pointwise_conv1", # parakeet
|
||||
"encoder.layers.{bid}.conv.up_conv", # granite_speech
|
||||
@@ -2839,6 +2874,7 @@ class TensorNameMap:
|
||||
|
||||
MODEL_TENSOR.A_ENC_CONV_PW2: (
|
||||
"conformer.layers.{bid}.conv.pointwise_conv2", # lfm2
|
||||
"encoder.lfm.lfm.layers.{bid}.conv.out_proj", # lfm3a
|
||||
"conformer.layers.{bid}.lconv1d.linear_end", # gemma3n
|
||||
"sound_encoder.encoder.layers.{bid}.conv.pointwise_conv2", # parakeet
|
||||
"encoder.layers.{bid}.conv.down_conv", # granite_speech
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
{{- bos_token -}}
|
||||
{%- set preserve_thinking = preserve_thinking | default(false) -%}
|
||||
{%- set enable_thinking = enable_thinking | default(true) -%}
|
||||
|
||||
{%- macro render_value(v) -%}
|
||||
{%- if v is string -%}
|
||||
{{- v -}}
|
||||
{%- elif v is boolean -%}
|
||||
{{- "true" if v else "false" -}}
|
||||
{%- elif v is none -%}
|
||||
{{- "null" -}}
|
||||
{%- elif v is mapping or (v is sequence and v is not string) -%}
|
||||
{{- v | tojson -}}
|
||||
{%- else -%}
|
||||
{{- v -}}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro parse_content(content) -%}
|
||||
{%- if content is string -%}
|
||||
{{- content -}}
|
||||
{%- elif content is mapping and content.get("type") == "image" -%}
|
||||
{{- "<|vision_start|><|image|><|vision_end|>" -}}
|
||||
{%- elif content is mapping and content.get("type") == "video" -%}
|
||||
{{- "<|vision_start|><|video|><|vision_end|>" -}}
|
||||
{%- elif content is mapping and content.get("type") == "text" -%}
|
||||
{{- (content.get("text") or "") | string -}}
|
||||
{%- elif content is mapping -%}
|
||||
{{- content | tojson -}}
|
||||
{%- elif content is iterable -%}
|
||||
{%- set _ns = namespace(result="") -%}
|
||||
{%- for item in content -%}
|
||||
{%- if item is string -%}
|
||||
{%- set _ns.result = _ns.result + item -%}
|
||||
{%- elif item is mapping and item.get("type") == "image" -%}
|
||||
{%- set _ns.result = _ns.result + "<|vision_start|><|image|><|vision_end|>" -%}
|
||||
{%- elif item is mapping and item.get("type") == "video" -%}
|
||||
{%- set _ns.result = _ns.result + "<|vision_start|><|video|><|vision_end|>" -%}
|
||||
{%- elif item is mapping and item.get("type") == "text" -%}
|
||||
{%- set _ns.result = _ns.result + ((item.get("text") or "") | string) -%}
|
||||
{%- else -%}
|
||||
{%- set _ns.result = _ns.result + (item | tojson) -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{{- _ns.result -}}
|
||||
{%- elif content is not none -%}
|
||||
{{- content | tojson -}}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro render_tool_calls(tool_calls) -%}
|
||||
{%- for tool_call in tool_calls -%}
|
||||
{%- if not loop.first -%}{{- "\n\n" -}}{%- endif -%}
|
||||
{%- set func = tool_call["function"] if "function" in tool_call else tool_call -%}
|
||||
{%- set func_args = func.get("arguments") -%}
|
||||
{%- if func_args is string and (func_args | trim) not in ["", "{}", "null"] -%}
|
||||
{{- raise_exception("Tool call arguments must be a mapping, got a JSON-encoded string: parse arguments with json.loads() before applying the chat template") -}}
|
||||
{%- endif -%}
|
||||
{{- "<tool_call>\n<function=" + func["name"] + ">\n" -}}
|
||||
{%- if func_args is mapping -%}
|
||||
{%- for arg_name, arg_value in func_args.items() -%}
|
||||
{{- "<parameter=" + arg_name + ">\n" + render_value(arg_value) + "\n</parameter>\n" -}}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{{- "</function>\n</tool_call>" -}}
|
||||
{%- endfor -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro tool_to_json(tool) -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{%- set ns_t = namespace(first=true) -%}
|
||||
{{- "{" -}}
|
||||
{%- for k, v in fn.items() -%}
|
||||
{%- if k != "defer_loading" -%}
|
||||
{%- if not ns_t.first -%}{{- ", " -}}{%- endif -%}
|
||||
{%- set ns_t.first = false -%}
|
||||
{{- k | tojson -}}{{- ": " -}}{{- v | tojson -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{{- "}" -}}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro is_tool_reference(item) -%}
|
||||
{{- "1" if item is mapping and item.get("type") == "tool_reference" else "" -}}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro referenced_tool_to_json(name) -%}
|
||||
{%- set _ns = namespace(found=none) -%}
|
||||
{%- for tool in tools or [] -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{%- if fn.name == name -%}
|
||||
{%- set _ns.found = fn -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if _ns.found is none -%}
|
||||
{{- raise_exception("Tool reference '" + name | string + "' not found in available tools. Every tool referenced by a tool_reference must be in `tools` on every request.") -}}
|
||||
{%- endif -%}
|
||||
{{- tool_to_json(_ns.found) -}}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro render_tool_response(content) -%}
|
||||
{%- if content is string and "<tool_response>" in content -%}
|
||||
{{- content -}}
|
||||
{%- else -%}
|
||||
{{- "<tool_response>\n" -}}
|
||||
{%- if content is string -%}
|
||||
{{- content -}}
|
||||
{%- elif content is mapping -%}
|
||||
{{- parse_content(content) -}}
|
||||
{%- elif content is iterable -%}
|
||||
{%- for item in content -%}
|
||||
{%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
|
||||
{%- if is_tool_reference(item) -%}
|
||||
{%- if loop.first or not is_tool_reference(loop.previtem) -%}
|
||||
{{- "<additional_tools>\n" -}}
|
||||
{%- endif -%}
|
||||
{{- referenced_tool_to_json(item.name) -}}
|
||||
{%- if loop.last or not is_tool_reference(loop.nextitem) -%}
|
||||
{{- "\n</additional_tools>" -}}
|
||||
{%- endif -%}
|
||||
{%- elif item is mapping and item.result is defined and item.result is mapping and item.result.tools is defined -%}
|
||||
{{- "<additional_tools>\n" -}}
|
||||
{%- set _tl = item.result.tools -%}
|
||||
{%- if _tl is string -%}
|
||||
{{- _tl -}}
|
||||
{%- elif _tl is mapping -%}
|
||||
{{- tool_to_json(_tl) -}}
|
||||
{%- else -%}
|
||||
{%- for t in _tl -%}
|
||||
{%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
|
||||
{{- tool_to_json(t) -}}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{{- "\n</additional_tools>" -}}
|
||||
{%- else -%}
|
||||
{{- parse_content(item) -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- elif content is not none -%}
|
||||
{{- content | tojson -}}
|
||||
{%- endif -%}
|
||||
{{- "\n</tool_response>" -}}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- set ns = namespace(system_prompt="", last_user_index=-1) -%}
|
||||
{%- if messages and messages[0]["role"] == "system" -%}
|
||||
{%- if messages[0].get("content") -%}
|
||||
{%- set ns.system_prompt = parse_content(messages[0]["content"]) -%}
|
||||
{%- endif -%}
|
||||
{%- set messages = messages[1:] -%}
|
||||
{%- endif -%}
|
||||
{%- if tools -%}
|
||||
{%- set tools_ns = namespace(lines=[]) -%}
|
||||
{%- for tool in tools -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{%- set _dyn = (fn.defer_loading is defined and fn.defer_loading) or (tool.defer_loading is defined and tool.defer_loading) -%}
|
||||
{%- if not _dyn -%}
|
||||
{%- set tools_ns.lines = tools_ns.lines + [tool_to_json(fn)] -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if tools_ns.lines -%}
|
||||
{%- set ns.system_prompt = ns.system_prompt + ("\n\n" if ns.system_prompt else "") + "<tools>\n" + (tools_ns.lines | join("\n")) + "\n</tools>" -%}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- if ns.system_prompt -%}
|
||||
{{- "<|im_start|>system\n" + ns.system_prompt + "<|im_end|>\n" -}}
|
||||
{%- endif -%}
|
||||
{%- for message in messages -%}
|
||||
{%- if message["role"] == "user" -%}
|
||||
{%- set ns.last_user_index = loop.index0 -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- set carry = namespace(pending=[]) -%}
|
||||
{%- for message in messages -%}
|
||||
{%- set _next = messages[loop.index0 + 1] if not loop.last else none -%}
|
||||
{%- set _merge = message.role == "tool" and not message.get("additional_tools")
|
||||
and _next is not none and _next.role == "tool" and not _next.get("additional_tools")
|
||||
and message.get("tool_call_id") and _next.get("tool_call_id") == message.get("tool_call_id") -%}
|
||||
{%- set _items = [] -%}
|
||||
{%- if message.role == "tool" and not message.get("additional_tools") -%}
|
||||
{%- set _c = message.get("content") -%}
|
||||
{%- if _c is string -%}
|
||||
{%- set _items = [{"type": "text", "text": _c}] if _c else [] -%}
|
||||
{%- elif _c is mapping -%}
|
||||
{%- set _items = [_c] -%}
|
||||
{%- elif _c is iterable -%}
|
||||
{%- set _items = _c | list -%}
|
||||
{%- elif _c is not none -%}
|
||||
{%- set _items = [_c] -%}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- if _merge -%}
|
||||
{%- set carry.pending = carry.pending + _items -%}
|
||||
{%- else -%}
|
||||
{{- "<|im_start|>" + message.role + "\n" -}}
|
||||
{%- if message.role == "assistant" -%}
|
||||
{%- generation -%}
|
||||
{%- set thinking = message.thinking if message.thinking is defined else message.reasoning if message.reasoning is defined else message.reasoning_content -%}
|
||||
{%- set keep_thinking = preserve_thinking or loop.index0 > ns.last_user_index -%}
|
||||
{%- if thinking is defined and keep_thinking -%}
|
||||
{{- "<think>" + (thinking or "") + "</think>" -}}
|
||||
{%- elif keep_thinking and "</think>" not in (message.get("content") if message.get("content") is string else "") -%}
|
||||
{{- "<think></think>" -}}
|
||||
{%- endif -%}
|
||||
{%- set _cfm_tag = "CONTINUE_FINAL_MESSAGE_TAG " -%}
|
||||
{%- set _has_cfm = false -%}
|
||||
{%- set content = "" -%}
|
||||
{%- if message.get("content") -%}
|
||||
{%- set content = parse_content(message.content) -%}
|
||||
{%- endif -%}
|
||||
{%- if not keep_thinking -%}
|
||||
{%- if "</think>" in content -%}
|
||||
{%- set content = content.split("</think>")[-1] | trim -%}
|
||||
{%- endif -%}
|
||||
{%- set content = "<think></think>" + content -%}
|
||||
{%- endif -%}
|
||||
{%- if content.endswith(_cfm_tag) -%}
|
||||
{%- set _has_cfm = true -%}
|
||||
{%- set _trunc_len = (content | length) - (_cfm_tag | length) -%}
|
||||
{%- set content = content[:_trunc_len] -%}
|
||||
{%- endif -%}
|
||||
{{- content -}}
|
||||
{%- if message.tool_calls -%}
|
||||
{{- render_tool_calls(message.tool_calls) -}}
|
||||
{%- endif -%}
|
||||
{%- if _has_cfm -%}
|
||||
{{- _cfm_tag -}}
|
||||
{%- endif -%}
|
||||
{{- "<|im_end|>\n" -}}
|
||||
{%- endgeneration -%}
|
||||
{%- elif message.role == "tool" -%}
|
||||
{%- if message.get("additional_tools") -%}
|
||||
{%- set _at = message["additional_tools"] -%}
|
||||
{%- if _at is mapping or (_at is sequence and _at is not string) -%}
|
||||
{%- set _src = _at -%}
|
||||
{%- else -%}
|
||||
{%- set _src = message.get("content") -%}
|
||||
{%- endif -%}
|
||||
{%- if _src is string and "<tool_response>" in _src -%}
|
||||
{{- _src -}}
|
||||
{%- elif _src is string and "<additional_tools>" in _src -%}
|
||||
{{- "<tool_response>\n" + _src + "\n</tool_response>" -}}
|
||||
{%- elif _src -%}
|
||||
{{- "<tool_response>\n<additional_tools>\n" -}}
|
||||
{%- if _src is string -%}
|
||||
{{- _src -}}
|
||||
{%- elif _src is mapping -%}
|
||||
{{- tool_to_json(_src) -}}
|
||||
{%- else -%}
|
||||
{%- for t in _src -%}
|
||||
{%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
|
||||
{{- tool_to_json(t) -}}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{{- "\n</additional_tools>\n</tool_response>" -}}
|
||||
{%- endif -%}
|
||||
{%- elif carry.pending -%}
|
||||
{{- render_tool_response(carry.pending + _items) -}}
|
||||
{%- set carry.pending = [] -%}
|
||||
{%- else -%}
|
||||
{{- render_tool_response("" if message.get("content") is none else message.get("content")) -}}
|
||||
{%- endif -%}
|
||||
{{- "<|im_end|>\n" -}}
|
||||
{%- else %}
|
||||
{%- if message.get("content") -%}
|
||||
{{- parse_content(message["content"]) -}}
|
||||
{%- endif -%}
|
||||
{{- "<|im_end|>\n" -}}
|
||||
{%- endif %}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if add_generation_prompt -%}
|
||||
{{- "<|im_start|>assistant\n" -}}
|
||||
{%- if enable_thinking -%}
|
||||
{{- "<think>" -}}
|
||||
{%- else -%}
|
||||
{{- "<think></think>" -}}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
@@ -36,6 +36,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
@@ -406,11 +407,19 @@ def fetch_logs_and_parse_tests(
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
for lf in log_files:
|
||||
zip_path = os.path.join(tmpdir, "log.zip")
|
||||
dl_path = os.path.join(tmpdir, "log.bin")
|
||||
log.info("Downloading log file: %s", lf.filename)
|
||||
qdc_api.download_job_log_files(client, lf.filename, zip_path)
|
||||
try:
|
||||
shutil.unpack_archive(zip_path, tmpdir, "zip")
|
||||
qdc_api.download_job_log_files_v2(client, lf.filename, dl_path)
|
||||
except Exception as e:
|
||||
log.warning("Could not download %s: %s", lf.filename, e)
|
||||
continue
|
||||
# v2 returns some files raw instead of zipped
|
||||
if not zipfile.is_zipfile(dl_path):
|
||||
os.replace(dl_path, os.path.join(tmpdir, os.path.basename(lf.filename)))
|
||||
continue
|
||||
try:
|
||||
shutil.unpack_archive(dl_path, tmpdir, "zip")
|
||||
except Exception as e:
|
||||
log.warning("Could not unpack %s as zip: %s", lf.filename, e)
|
||||
|
||||
@@ -617,7 +626,7 @@ def main() -> int:
|
||||
args.pkg_dir, Path(tmpdir), args.test, args.model_url
|
||||
)
|
||||
log.info("Uploading artifact (%d MB) ...", zip_path.stat().st_size // 1_000_000)
|
||||
artifact_id = qdc_api.upload_file(client, str(zip_path), ArtifactType.TESTSCRIPT)
|
||||
artifact_id = qdc_api.upload_file_v2(client, str(zip_path), ArtifactType.TESTSCRIPT)
|
||||
|
||||
if artifact_id is None:
|
||||
log.error("Artifact upload failed")
|
||||
|
||||
@@ -58,6 +58,7 @@ Mandatory on every review; any finding here is **blocking**. Rule of thumb: GGUF
|
||||
- **Reused/reserved buffers:** recheck bounds after a buffer is shrunk or reused; watch `reserve()` then index-by-assumed-size, and header fields read before their length is checked.
|
||||
- **Server JSON ints:** clamp client-supplied integers (token/discard counts, offsets) to non-negative and an upper bound before they reach index/pointer arithmetic.
|
||||
- **RPC-deserialized fields:** treat every field (type/buffer/data/ne/nb/op_params) as hostile - validate before use. Null/zero buffers skipping validation, attacker data pointers, out-of-range type indices, and negative strides sign-extending past a corner-only assert all give arbitrary read/write.
|
||||
- **Recursion on untrusted input:** a function that recurses once per level (or per char) of request JSON, model output, a grammar, a template or a regex pattern overflows the stack - the HTTP threads have 512 KB on macOS, so a few hundred levels are enough. Every such function needs its own depth bound, checked in the function itself, not a pre-walk at a caller (the next caller skips it): libcommon uses `COMMON_RECURSIVE_LIMIT` (`common/common.h`), jinja `JINJA_RECURSIVE_LIMIT` (`common/jinja/utils.h`), the grammar `GRAMMAR_RECURSIVE_LIMIT` (`src/llama-grammar.h`). Count the levels of the input, not the frames, and throw `std::invalid_argument` for request input so the server answers 400. Look for recursion the code does not spell out: mutual recursion, recursive `std::function` lambdas, visitor/virtual dispatch, `std::regex` (libstdc++ recurses per char of the input), and per-char helpers that recurse where a loop would do.
|
||||
- **Lifetime/UAF:** flag stored raw pointers to caller/temporary storage, cached pointers to buffers a later free releases, async ops whose source may drop before completion, and structures not invalidated on free/realloc. Null-check conditionally-built or "not required" tensors before dereferencing.
|
||||
|
||||
## Approach and design (when a new component/infra is introduced)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "llama-arch.h"
|
||||
|
||||
#include "llama-ext.h"
|
||||
#include "llama-impl.h"
|
||||
|
||||
#include <map>
|
||||
@@ -134,6 +135,7 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
||||
{ LLM_ARCH_OPENAI_MOE, "gpt-oss" },
|
||||
{ LLM_ARCH_LFM2, "lfm2" },
|
||||
{ LLM_ARCH_LFM2MOE, "lfm2moe" },
|
||||
{ LLM_ARCH_LFM3, "lfm3" },
|
||||
{ LLM_ARCH_DREAM, "dream" },
|
||||
{ LLM_ARCH_SMALLTHINKER, "smallthinker" },
|
||||
{ LLM_ARCH_LLADA, "llada" },
|
||||
@@ -635,6 +637,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" },
|
||||
@@ -907,6 +913,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}},
|
||||
@@ -1242,6 +1252,7 @@ bool llm_arch_is_hybrid(const llm_arch & arch) {
|
||||
case LLM_ARCH_GRANITE_HYBRID:
|
||||
case LLM_ARCH_LFM2:
|
||||
case LLM_ARCH_LFM2MOE:
|
||||
case LLM_ARCH_LFM3:
|
||||
case LLM_ARCH_NEMOTRON_H:
|
||||
case LLM_ARCH_NEMOTRON_H_MOE:
|
||||
case LLM_ARCH_QWEN3NEXT:
|
||||
@@ -1286,6 +1297,7 @@ bool llm_arch_supports_rs_rollback(const llm_arch & arch) {
|
||||
case LLM_ARCH_NEMOTRON_H_MOE:
|
||||
case LLM_ARCH_LFM2:
|
||||
case LLM_ARCH_LFM2MOE:
|
||||
case LLM_ARCH_LFM3:
|
||||
case LLM_ARCH_BAILINGMOE3:
|
||||
return true;
|
||||
default:
|
||||
@@ -1332,12 +1344,14 @@ bool llm_arch_supports_sm_tensor(const llm_arch & arch) {
|
||||
case LLM_ARCH_NEMOTRON_H:
|
||||
case LLM_ARCH_NEMOTRON_H_MOE:
|
||||
case LLM_ARCH_GRANITE_HYBRID:
|
||||
case LLM_ARCH_LFM3:
|
||||
case LLM_ARCH_MINIMAX_01:
|
||||
case LLM_ARCH_MINIMAX_M2:
|
||||
case LLM_ARCH_MINIMAX_M3:
|
||||
case LLM_ARCH_MISTRAL4:
|
||||
case LLM_ARCH_MELLUM: // [TAG_MOE_AMAX_SCALE]
|
||||
case LLM_ARCH_FLINT: // [TAG_MOE_AMAX_SCALE]
|
||||
case LLM_ARCH_LAGUNA: // [TAG_MOE_AMAX_SCALE]
|
||||
case LLM_ARCH_KIMI_LINEAR:
|
||||
case LLM_ARCH_BAILINGMOE3:
|
||||
case LLM_ARCH_KIMI_K3:
|
||||
@@ -1348,3 +1362,43 @@ bool llm_arch_supports_sm_tensor(const llm_arch & arch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool llama_arch_supports_save(llm_arch arch) {
|
||||
switch (arch) {
|
||||
case LLM_ARCH_SENSENOVA_U1:
|
||||
case LLM_ARCH_GEMMA3N:
|
||||
case LLM_ARCH_BITNET:
|
||||
case LLM_ARCH_T5:
|
||||
case LLM_ARCH_APERTUS:
|
||||
case LLM_ARCH_STEP35:
|
||||
case LLM_ARCH_CLEF: // the head tensors are not saved
|
||||
case LLM_ARCH_VLLM_DECISION2:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<llm_arch> llama_arch_all() {
|
||||
return llm_arch_all();
|
||||
}
|
||||
|
||||
const char * llama_arch_name(llm_arch arch) {
|
||||
return llm_arch_name(arch);
|
||||
}
|
||||
|
||||
llm_arch llama_arch_from_string(const std::string & name) {
|
||||
return llm_arch_from_string(name);
|
||||
}
|
||||
|
||||
std::string llama_arch_kv_name(llm_arch arch, llm_kv key) {
|
||||
return LLM_KV(arch)(key);
|
||||
}
|
||||
|
||||
bool llama_arch_supports_sm_tensor(llm_arch arch) {
|
||||
return llm_arch_supports_sm_tensor(arch);
|
||||
}
|
||||
|
||||
bool llama_arch_supports_mixed_batch(llm_arch arch) {
|
||||
return llm_arch_supports_mixed_batch(arch);
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ enum llm_arch {
|
||||
LLM_ARCH_OPENAI_MOE,
|
||||
LLM_ARCH_LFM2,
|
||||
LLM_ARCH_LFM2MOE,
|
||||
LLM_ARCH_LFM3,
|
||||
LLM_ARCH_DREAM,
|
||||
LLM_ARCH_SMALLTHINKER,
|
||||
LLM_ARCH_LLADA,
|
||||
@@ -679,6 +680,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
|
||||
|
||||
+25
-20
@@ -489,6 +489,7 @@ llama_context::llama_context(
|
||||
/*.swa_full =*/ params.swa_full,
|
||||
/*.ctx_type =*/ cparams.ctx_type,
|
||||
/*.mem_other =*/ llama_get_memory(cparams.ctx_other),
|
||||
/*.no_alloc =*/ model.no_alloc(),
|
||||
};
|
||||
|
||||
memory.reset(model.create_memory(params_mem, cparams));
|
||||
@@ -559,7 +560,7 @@ llama_context::llama_context(
|
||||
}
|
||||
|
||||
if (cparams.moe_cache_size > 0) {
|
||||
moe_cache = std::make_unique<llama_moe_cache>(model, backend_ptrs, backend_buft, cparams.moe_cache_size);
|
||||
moe_cache = std::make_unique<llama_moe_cache>(model, model.no_alloc(), backend_ptrs, backend_buft, cparams.moe_cache_size);
|
||||
}
|
||||
|
||||
sched_reserve();
|
||||
@@ -587,7 +588,7 @@ llama_context::~llama_context() {
|
||||
synchronize();
|
||||
|
||||
// when training, ggml_opt allocates extra buffers through the scheduler, so the sizes no longer match the expectation
|
||||
if (!model.hparams.no_alloc && !opt_ctx) {
|
||||
if (!model.no_alloc() && !opt_ctx) {
|
||||
for (size_t i = 0; i < backend_ptrs.size(); ++i) {
|
||||
ggml_backend_t backend = backend_ptrs[i];
|
||||
ggml_backend_buffer_type_t buft = backend_buft[i];
|
||||
@@ -788,7 +789,7 @@ void llama_context::sched_reserve() {
|
||||
// reserve pp (prompt processing) graph first so that buffers are only allocated once
|
||||
{
|
||||
auto * gf = graph_reserve(n_tokens, n_seqs, n_outputs_pp, mctx.get(),
|
||||
model.hparams.no_alloc, model.hparams.no_alloc ? backend_buf_exp_size.data() : nullptr);
|
||||
model.no_alloc(), model.no_alloc() ? backend_buf_exp_size.data() : nullptr);
|
||||
if (!gf) {
|
||||
if (cparams.pipeline_parallel) {
|
||||
LLAMA_LOG_WARN("%s: compute buffer allocation failed, retrying without pipeline parallelism\n", __func__);
|
||||
@@ -815,7 +816,7 @@ void llama_context::sched_reserve() {
|
||||
n_splits_tg = n_splits_pp;
|
||||
n_nodes_tg = n_nodes_pp;
|
||||
} else {
|
||||
auto * gf = graph_reserve(n_seqs, n_seqs, n_seqs, mctx.get(), model.hparams.no_alloc);
|
||||
auto * gf = graph_reserve(n_seqs, n_seqs, n_seqs, mctx.get(), model.no_alloc());
|
||||
if (!gf) {
|
||||
throw std::runtime_error("failed to allocate compute tg buffers");
|
||||
}
|
||||
@@ -837,10 +838,10 @@ void llama_context::sched_reserve() {
|
||||
// [TAG_RESERVE_DIAG_DECAY]
|
||||
// the `inp_diag_decay` tensor size scales with `n_seq_tokens^2` which
|
||||
// makes `n_seqs == 1` use more memory for the compute graph compared to `n_seqs > 1`
|
||||
gf = graph_reserve(n_tokens, 1, n_outputs_pp, mctx.get(), model.hparams.no_alloc);
|
||||
gf = graph_reserve(n_tokens, 1, n_outputs_pp, mctx.get(), model.no_alloc());
|
||||
break;
|
||||
default:
|
||||
gf = graph_reserve(n_tokens, n_seqs, n_outputs_pp, mctx.get(), model.hparams.no_alloc);
|
||||
gf = graph_reserve(n_tokens, n_seqs, n_outputs_pp, mctx.get(), model.no_alloc());
|
||||
};
|
||||
|
||||
if (!gf) {
|
||||
@@ -851,7 +852,7 @@ void llama_context::sched_reserve() {
|
||||
for (size_t i = 0; i < backend_ptrs.size(); ++i) {
|
||||
ggml_backend_t backend = backend_ptrs[i];
|
||||
ggml_backend_buffer_type_t buft = backend_buft[i];
|
||||
if (!model.hparams.no_alloc) {
|
||||
if (!model.no_alloc()) {
|
||||
backend_buf_exp_size[i] = ggml_backend_sched_get_buffer_size(sched.get(), backend);
|
||||
}
|
||||
if (backend_buf_exp_size[i] > 1) {
|
||||
@@ -1613,8 +1614,9 @@ int llama_context::encode(const llama_batch_ext & batch_inp) {
|
||||
// eagle3/DFlash: features as encoder input, and non-draft paths fall back to model's input dim
|
||||
const int64_t n_vocab = model.vocab.n_tokens();
|
||||
|
||||
// note: during encode, we always output all tokens and skip position continuity checks (output_all=true)
|
||||
if (!balloc->init(batch_inp, model.vocab, true)) {
|
||||
// note: during encode, we skip position continuity checks and output all tokens
|
||||
// a decoder without memory (see decode()) outputs the marked tokens, unless it extracts embeddings
|
||||
if (!balloc->init(batch_inp, model.vocab, memory || cparams.embeddings)) {
|
||||
LLAMA_LOG_ERROR("%s: failed to initialize batch\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
@@ -1643,17 +1645,20 @@ int llama_context::encode(const llama_batch_ext & batch_inp) {
|
||||
|
||||
n_queued_tokens += n_tokens;
|
||||
|
||||
const uint32_t n_outputs_all = balloc->get_n_outputs();
|
||||
|
||||
// reserve output buffer
|
||||
if (output_reserve(n_tokens) < n_tokens) {
|
||||
LLAMA_LOG_ERROR("%s: could not reserve space for batch with %u outputs\n", __func__, n_tokens);
|
||||
if (output_reserve(n_outputs_all) < n_outputs_all) {
|
||||
LLAMA_LOG_ERROR("%s: could not reserve space for batch with %u outputs\n", __func__, n_outputs_all);
|
||||
return -2;
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < n_tokens; ++i) {
|
||||
output_ids[i] = i;
|
||||
const auto & out_ids = balloc->get_out_ids();
|
||||
for (uint32_t i = 0; i < n_outputs_all; ++i) {
|
||||
output_ids[out_ids[i]] = i;
|
||||
}
|
||||
|
||||
n_outputs = n_tokens;
|
||||
n_outputs = n_outputs_all;
|
||||
|
||||
const auto causal_attn_org = cparams.causal_attn;
|
||||
|
||||
@@ -1686,7 +1691,7 @@ int llama_context::encode(const llama_batch_ext & batch_inp) {
|
||||
GGML_ASSERT(backend_res != nullptr);
|
||||
GGML_ASSERT(logits.data != nullptr);
|
||||
|
||||
ggml_backend_tensor_get_async(backend_res, t_logits, logits.data, 0, n_tokens*n_vocab*sizeof(float));
|
||||
ggml_backend_tensor_get_async(backend_res, t_logits, logits.data, 0, n_outputs*n_vocab*sizeof(float));
|
||||
}
|
||||
|
||||
// extract embeddings
|
||||
@@ -1701,8 +1706,8 @@ int llama_context::encode(const llama_batch_ext & batch_inp) {
|
||||
GGML_ASSERT(embd.data != nullptr);
|
||||
const uint32_t n_embd_out = hparams.n_embd_out();
|
||||
|
||||
GGML_ASSERT(n_tokens*n_embd_out <= (int64_t) embd.size);
|
||||
ggml_backend_tensor_get_async(backend_embd, t_embd, embd.data, 0, n_tokens*n_embd_out*sizeof(float));
|
||||
GGML_ASSERT(n_outputs*n_embd_out <= (int64_t) embd.size);
|
||||
ggml_backend_tensor_get_async(backend_embd, t_embd, embd.data, 0, n_outputs*n_embd_out*sizeof(float));
|
||||
} break;
|
||||
case LLAMA_POOLING_TYPE_MEAN:
|
||||
case LLAMA_POOLING_TYPE_CLS:
|
||||
@@ -1750,8 +1755,8 @@ int llama_context::encode(const llama_batch_ext & batch_inp) {
|
||||
GGML_ASSERT(backend_h != nullptr);
|
||||
|
||||
const uint32_t n_embd = hparams.n_embd_out();
|
||||
GGML_ASSERT(n_tokens*n_embd <= (int64_t) embd_nextn.size);
|
||||
ggml_backend_tensor_get_async(backend_h, t_h_nextn, embd_nextn.data, 0, n_tokens*n_embd*sizeof(float));
|
||||
GGML_ASSERT(n_outputs*n_embd <= (int64_t) embd_nextn.size);
|
||||
ggml_backend_tensor_get_async(backend_h, t_h_nextn, embd_nextn.data, 0, n_outputs*n_embd*sizeof(float));
|
||||
}
|
||||
|
||||
// TODO: hacky solution
|
||||
@@ -3730,7 +3735,7 @@ llama_memory_breakdown llama_context::memory_breakdown() const {
|
||||
ret[buft].context += size;
|
||||
}
|
||||
}
|
||||
if (model.hparams.no_alloc) {
|
||||
if (model.no_alloc()) {
|
||||
for (size_t i = 0; i < backends.size(); ++i) {
|
||||
ggml_backend_t backend = backends[i].get();
|
||||
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type(sched.get(), backend);
|
||||
|
||||
@@ -5,11 +5,36 @@
|
||||
// try as much as possible to not include this header in the rest of the codebase
|
||||
|
||||
#include "llama.h"
|
||||
#include "llama-arch.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
//
|
||||
// llama architecture queries
|
||||
//
|
||||
|
||||
// all the LLM architectures known to the library
|
||||
LLAMA_API std::vector<llm_arch> llama_arch_all();
|
||||
|
||||
// the canonical name of an architecture (e.g. "llama"), or "unknown"
|
||||
LLAMA_API const char * llama_arch_name(llm_arch arch);
|
||||
|
||||
// the architecture with the given canonical name, or LLM_ARCH_UNKNOWN
|
||||
LLAMA_API llm_arch llama_arch_from_string(const std::string & name);
|
||||
|
||||
// the arch-resolved GGUF key of a KV id, e.g. LLM_KV_CONTEXT_LENGTH -> "llama.context_length"
|
||||
LLAMA_API std::string llama_arch_kv_name(llm_arch arch, llm_kv key);
|
||||
|
||||
LLAMA_API bool llama_arch_supports_sm_tensor(llm_arch arch);
|
||||
|
||||
LLAMA_API bool llama_arch_supports_mixed_batch(llm_arch arch);
|
||||
|
||||
// FIXME temporary function for better error messages
|
||||
LLAMA_API bool llama_arch_supports_save(llm_arch arch);
|
||||
|
||||
// Reserve a new compute graph. It is valid until the next call to llama_graph_reserve.
|
||||
LLAMA_API struct ggml_cgraph * llama_graph_reserve(
|
||||
struct llama_context * ctx,
|
||||
|
||||
+130
-64
@@ -8,10 +8,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
|
||||
#define MAX_REPETITION_THRESHOLD 2000
|
||||
#define MAX_GRAMMAR_RECURSION_DEPTH 256
|
||||
#define GRAMMAR_RECURSIVE_LIMIT 256
|
||||
//
|
||||
// helpers
|
||||
//
|
||||
@@ -598,8 +599,8 @@ 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");
|
||||
if (depth >= GRAMMAR_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("grammar recursion depth limit exceeded (max " + std::to_string(GRAMMAR_RECURSIVE_LIMIT) + "), please reduce nesting depth");
|
||||
}
|
||||
// parse nested alternates into synthesized rule
|
||||
pos = parse_space(pos + 1, true);
|
||||
@@ -947,34 +948,19 @@ static void llama_grammar_advance_stack(
|
||||
}
|
||||
}
|
||||
|
||||
static llama_grammar_candidates llama_grammar_reject_candidates(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stacks & stacks,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
GGML_ASSERT(!stacks.empty()); // REVIEW
|
||||
|
||||
if (candidates.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto rejects = llama_grammar_reject_candidates_for_stack(rules, stacks.front(), candidates);
|
||||
|
||||
for (size_t i = 1, size = stacks.size(); i < size; ++i) {
|
||||
rejects = llama_grammar_reject_candidates_for_stack(rules, stacks[i], rejects);
|
||||
}
|
||||
|
||||
return rejects;
|
||||
}
|
||||
|
||||
static bool llama_grammar_detect_left_recursion(
|
||||
const llama_grammar_rules & rules,
|
||||
size_t rule_index,
|
||||
std::vector<bool> * rules_visited,
|
||||
std::vector<bool> * rules_in_progress,
|
||||
std::vector<bool> * rules_may_be_empty) {
|
||||
std::vector<bool> * rules_may_be_empty,
|
||||
uint32_t depth = 0) {
|
||||
if ((*rules_in_progress)[rule_index]) {
|
||||
return true;
|
||||
}
|
||||
if (depth > GRAMMAR_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("grammar rules are chained more than " + std::to_string(GRAMMAR_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
|
||||
(*rules_in_progress)[rule_index] = true;
|
||||
|
||||
@@ -1000,7 +986,7 @@ static bool llama_grammar_detect_left_recursion(
|
||||
bool recurse_into_nonterminal = true;
|
||||
for (size_t i = 0; i < rule.size(); i++) {
|
||||
if (rule[i].type == LLAMA_GRETYPE_RULE_REF && recurse_into_nonterminal) {
|
||||
if (llama_grammar_detect_left_recursion(rules, (size_t)rule[i].value, rules_visited, rules_in_progress, rules_may_be_empty)) {
|
||||
if (llama_grammar_detect_left_recursion(rules, (size_t)rule[i].value, rules_visited, rules_in_progress, rules_may_be_empty, depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
if (!((*rules_may_be_empty)[(size_t)rule[i].value])) {
|
||||
@@ -1064,56 +1050,63 @@ void llama_grammar_accept(struct llama_grammar * grammar, uint32_t chr) {
|
||||
grammar->stacks = std::move(stacks_new);
|
||||
}
|
||||
|
||||
llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
// checks the code point at depth of each candidate (an index into the input) against the stack
|
||||
// returns true when some match: they go to next, and the stacks that follow to next_stacks
|
||||
static bool llama_grammar_reject_step(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stack & stack,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
|
||||
llama_grammar_candidates rejects;
|
||||
rejects.reserve(candidates.size());
|
||||
const llama_grammar_candidates & candidates,
|
||||
size_t depth,
|
||||
const std::vector<uint32_t> & cur,
|
||||
std::vector<uint32_t> & rejects,
|
||||
std::vector<uint32_t> & next,
|
||||
llama_grammar_stacks & next_stacks) {
|
||||
rejects.reserve(cur.size());
|
||||
|
||||
if (stack.empty()) {
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points != 0 || tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(tok);
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] != 0 || tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
return rejects;
|
||||
return false;
|
||||
}
|
||||
|
||||
const llama_grammar_element * stack_pos = stack.back();
|
||||
|
||||
// if the top of the stack is a token rule, then we only need to check the token id
|
||||
if (stack_pos->type == LLAMA_GRETYPE_TOKEN || stack_pos->type == LLAMA_GRETYPE_TOKEN_NOT) {
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points == 0) {
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] == 0) {
|
||||
// reached the end of a token consumed by char rules, reject iff it ended
|
||||
// in a partial response
|
||||
if (tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
} else if (!llama_grammar_match_token(stack_pos, tok.id)) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
return rejects;
|
||||
return false;
|
||||
}
|
||||
|
||||
llama_grammar_candidates next_candidates;
|
||||
next_candidates.reserve(candidates.size());
|
||||
next.reserve(cur.size());
|
||||
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points == 0) {
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] == 0) {
|
||||
// reached end of full codepoints in token, reject iff it ended in a partial sequence
|
||||
// that cannot satisfy this position in grammar
|
||||
if (tok.partial_utf8.n_remain != 0 &&
|
||||
!llama_grammar_match_partial_char(stack_pos, tok.partial_utf8)) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
} else if (llama_grammar_match_char(stack_pos, *tok.code_points).first) {
|
||||
next_candidates.push_back({ tok.index, tok.code_points + 1, tok.partial_utf8, tok.id });
|
||||
} else if (llama_grammar_match_char(stack_pos, tok.code_points[depth]).first) {
|
||||
next.push_back(i);
|
||||
} else {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,17 +1117,80 @@ llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
if (!llama_grammar_is_end_of_sequence(stack_pos_after)) {
|
||||
stack_after.push_back(stack_pos_after);
|
||||
}
|
||||
llama_grammar_stacks next_stacks;
|
||||
llama_grammar_advance_stack(rules, stack_after, next_stacks);
|
||||
|
||||
auto next_rejects = llama_grammar_reject_candidates(rules, next_stacks, next_candidates);
|
||||
for (const auto & tok : next_rejects) {
|
||||
rejects.push_back({ tok.index, tok.code_points - 1, tok.partial_utf8, tok.id });
|
||||
return true;
|
||||
}
|
||||
|
||||
// the candidates that no stack accepts, each stack narrows the rejects of the previous one
|
||||
// no recursion: a level per code point, a token from the GGUF can be long
|
||||
static llama_grammar_candidates llama_grammar_reject_candidates(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stacks & stacks,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
GGML_ASSERT(!stacks.empty()); // REVIEW
|
||||
|
||||
// a level checks the candidates that reach its code point against its stacks in turn
|
||||
struct level {
|
||||
llama_grammar_stacks stacks;
|
||||
size_t i; // the stack in progress
|
||||
std::vector<uint32_t> cur; // the candidates rejected by stacks[0, i)
|
||||
std::vector<uint32_t> rejects; // the rejects of stacks[i] at this code point
|
||||
};
|
||||
|
||||
std::vector<uint32_t> all(candidates.size());
|
||||
std::iota(all.begin(), all.end(), 0u);
|
||||
|
||||
std::vector<level> levels;
|
||||
levels.push_back({ stacks, 0, std::move(all), {} });
|
||||
|
||||
while (true) {
|
||||
level & lv = levels.back();
|
||||
|
||||
if (lv.i == lv.stacks.size() || lv.cur.empty()) {
|
||||
if (levels.size() == 1) {
|
||||
break;
|
||||
}
|
||||
// the rejects at the next code point belong to stacks[i] of the parent level
|
||||
level & parent = levels[levels.size() - 2];
|
||||
parent.rejects.insert(parent.rejects.end(), lv.cur.begin(), lv.cur.end());
|
||||
levels.pop_back();
|
||||
parent.cur = std::move(parent.rejects);
|
||||
parent.rejects = {};
|
||||
parent.i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> rejects;
|
||||
std::vector<uint32_t> next;
|
||||
llama_grammar_stacks next_stacks;
|
||||
|
||||
if (!llama_grammar_reject_step(rules, lv.stacks[lv.i], candidates, levels.size() - 1, lv.cur, rejects, next, next_stacks)) {
|
||||
lv.cur = std::move(rejects);
|
||||
lv.i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
GGML_ASSERT(!next_stacks.empty());
|
||||
lv.rejects = std::move(rejects);
|
||||
levels.push_back({ std::move(next_stacks), 0, std::move(next), {} });
|
||||
}
|
||||
|
||||
llama_grammar_candidates rejects;
|
||||
rejects.reserve(levels[0].cur.size());
|
||||
for (const uint32_t i : levels[0].cur) {
|
||||
rejects.push_back(candidates[i]);
|
||||
}
|
||||
return rejects;
|
||||
}
|
||||
|
||||
llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stack & stack,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
return llama_grammar_reject_candidates(rules, llama_grammar_stacks{ stack }, candidates);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
struct llama_grammar * llama_grammar_init_impl(
|
||||
@@ -1169,14 +1225,19 @@ struct llama_grammar * llama_grammar_init_impl(
|
||||
std::vector<bool> rules_visited(n_rules);
|
||||
std::vector<bool> rules_in_progress(n_rules);
|
||||
std::vector<bool> rules_may_be_empty(n_rules);
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu", i);
|
||||
return nullptr;
|
||||
try {
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu", i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception & err) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar: %s\n", err.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// loop over alternates of start rule to build initial stacks
|
||||
@@ -1262,14 +1323,19 @@ struct llama_grammar * llama_grammar_init_impl(
|
||||
std::vector<bool> rules_visited(n_rules);
|
||||
std::vector<bool> rules_in_progress(n_rules);
|
||||
std::vector<bool> rules_may_be_empty(n_rules);
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu\n", i);
|
||||
return nullptr;
|
||||
try {
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu\n", i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception & err) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar: %s\n", err.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// loop over alternates of start rule to build initial stacks
|
||||
|
||||
+28
-23
@@ -479,7 +479,7 @@ void llm_graph_input_attn_no_cache::set_input(const llama_ubatch * ubatch) {
|
||||
const int64_t n_kv = self_kq_mask->ne[0];
|
||||
const int64_t n_tokens = ubatch->n_tokens;
|
||||
|
||||
const auto fill_mask = [&](auto * data, int64_t ne, int n_swa, llama_swa_type swa_type) {
|
||||
const auto fill_mask = [&](auto * data, int64_t ne, int n_swa, llama_swa_type swa_type, bool causal) {
|
||||
using T = std::remove_reference_t<decltype(*data)>;
|
||||
std::fill(data, data + ne, llama_cast<T>(-INFINITY));
|
||||
|
||||
@@ -499,7 +499,7 @@ void llm_graph_input_attn_no_cache::set_input(const llama_ubatch * ubatch) {
|
||||
}
|
||||
|
||||
// mask future tokens
|
||||
if (cparams.causal_attn && p0 > p1) {
|
||||
if (causal && p0 > p1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -517,21 +517,25 @@ void llm_graph_input_attn_no_cache::set_input(const llama_ubatch * ubatch) {
|
||||
}
|
||||
};
|
||||
|
||||
// see llama_non_causal_type
|
||||
const bool causal = cparams.causal_attn;
|
||||
const bool causal_swa = cparams.causal_attn || hparams.non_causal_type == LLAMA_NON_CAUSAL_TYPE_DENSE_ONLY;
|
||||
|
||||
GGML_ASSERT(self_kq_mask);
|
||||
GGML_ASSERT(ggml_backend_buffer_is_host(self_kq_mask->buffer));
|
||||
if (self_kq_mask->type == GGML_TYPE_F16) {
|
||||
fill_mask((ggml_fp16_t *) self_kq_mask->data, ggml_nelements(self_kq_mask), 0, LLAMA_SWA_TYPE_NONE);
|
||||
fill_mask((ggml_fp16_t *) self_kq_mask->data, ggml_nelements(self_kq_mask), 0, LLAMA_SWA_TYPE_NONE, causal);
|
||||
} else {
|
||||
fill_mask((float *) self_kq_mask->data, ggml_nelements(self_kq_mask), 0, LLAMA_SWA_TYPE_NONE);
|
||||
fill_mask((float *) self_kq_mask->data, ggml_nelements(self_kq_mask), 0, LLAMA_SWA_TYPE_NONE, causal);
|
||||
}
|
||||
|
||||
if (hparams.swa_type != LLAMA_SWA_TYPE_NONE) {
|
||||
GGML_ASSERT(self_kq_mask_swa);
|
||||
GGML_ASSERT(ggml_backend_buffer_is_host(self_kq_mask_swa->buffer));
|
||||
if (self_kq_mask_swa->type == GGML_TYPE_F16) {
|
||||
fill_mask((ggml_fp16_t *) self_kq_mask_swa->data, ggml_nelements(self_kq_mask_swa), hparams.n_swa, hparams.swa_type);
|
||||
fill_mask((ggml_fp16_t *) self_kq_mask_swa->data, ggml_nelements(self_kq_mask_swa), hparams.n_swa, hparams.swa_type, causal_swa);
|
||||
} else {
|
||||
fill_mask((float *) self_kq_mask_swa->data, ggml_nelements(self_kq_mask_swa), hparams.n_swa, hparams.swa_type);
|
||||
fill_mask((float *) self_kq_mask_swa->data, ggml_nelements(self_kq_mask_swa), hparams.n_swa, hparams.swa_type, causal_swa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,7 +638,10 @@ bool llm_graph_input_attn_kv_msa::can_reuse(const llm_graph_params & params) {
|
||||
void llm_graph_input_attn_k_dsa::set_input(const llama_ubatch * ubatch) {
|
||||
mctx->get_mla()->set_input_k_idxs(self_k_idxs_mla, ubatch);
|
||||
|
||||
mctx->get_mla()->set_input_kq_mask(self_kq_mask_mla, ubatch, cparams.causal_attn);
|
||||
// leave unallocated when the top-k layers use the indexer mask
|
||||
if (self_kq_mask_mla->buffer) {
|
||||
mctx->get_mla()->set_input_kq_mask(self_kq_mask_mla, ubatch, cparams.causal_attn);
|
||||
}
|
||||
|
||||
mctx->get_lid()->set_input_k_idxs(self_k_idxs_lid, ubatch);
|
||||
|
||||
@@ -1218,7 +1225,10 @@ bool llm_graph_input_mem_hybrid::can_reuse(const llm_graph_params & params) {
|
||||
void llm_graph_input_mem_hybrid_k::set_input(const llama_ubatch * ubatch) {
|
||||
mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch);
|
||||
|
||||
mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn);
|
||||
// leave the mask unallocated if all attention layers have their own (e.g. glm5-next DSA)
|
||||
if (inp_attn->self_kq_mask->buffer) {
|
||||
mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn);
|
||||
}
|
||||
|
||||
const int64_t n_rs = mctx->get_recr()->get_n_rs();
|
||||
|
||||
@@ -2519,7 +2529,7 @@ ggml_tensor * llm_graph_context::build_moe_ffn(
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/30656#issuecomment-6269597922
|
||||
llm_graph_amax_src1 amax_src1 = { nullptr, nullptr };
|
||||
|
||||
if (arch == LLM_ARCH_MISTRAL4 || arch == LLM_ARCH_MELLUM || arch == LLM_ARCH_FLINT) {
|
||||
if (arch == LLM_ARCH_MISTRAL4 || arch == LLM_ARCH_MELLUM || arch == LLM_ARCH_FLINT || arch == LLM_ARCH_LAGUNA) {
|
||||
amax_src1 = build_amax_src1_f16(cur);
|
||||
cur = amax_src1.res;
|
||||
}
|
||||
@@ -3392,20 +3402,10 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
ggml_tensor * llm_graph_context::build_fused_top_k_mask(
|
||||
ggml_tensor * kq_mask,
|
||||
ggml_tensor * top_k,
|
||||
ggml_tensor * score,
|
||||
ggml_tensor * blocks,
|
||||
ggml_tensor * extra,
|
||||
ggml_tensor * mask,
|
||||
int il) const {
|
||||
const int64_t m1 = kq_mask->ne[1];
|
||||
const int64_t m2 = kq_mask->ne[2];
|
||||
const int64_t m3 = kq_mask->ne[3];
|
||||
top_k = ggml_reshape_4d(ctx0, top_k, top_k->ne[0], kq_mask->ne[1], kq_mask->ne[2], kq_mask->ne[3]);
|
||||
|
||||
auto rows = [&](ggml_tensor * t) {
|
||||
return t ? ggml_reshape_4d(ctx0, t, t->ne[0], m1, m2, m3) : nullptr;
|
||||
};
|
||||
|
||||
ggml_tensor * cur = ggml_top_k_mask(ctx0, mask ? mask : kq_mask, rows(top_k), rows(score), blocks, rows(extra));
|
||||
ggml_tensor * cur = ggml_top_k_mask(ctx0, kq_mask, top_k, nullptr, nullptr, nullptr);
|
||||
res->add_fused_node({LLM_FUSED_OP_TOP_K_MASK, cur, il});
|
||||
|
||||
return cur;
|
||||
@@ -3441,11 +3441,16 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
ggml_build_forward_expand(gf, mctx_cur->cpy_k(ctx0, k_cur, k_idxs, il));
|
||||
}
|
||||
|
||||
const auto & kq_mask = inp->get_kq_mask_mla();
|
||||
// top_k indexes the cells, and the indexer mask of the same type is built the same way
|
||||
// let's use it, so when no other layer reads the MLA mask is not allocated
|
||||
ggml_tensor * kq_mask = inp->get_kq_mask_mla();
|
||||
if (inp->get_kq_mask_lid()->type == kq_mask->type) {
|
||||
kq_mask = inp->get_kq_mask_lid();
|
||||
}
|
||||
|
||||
ggml_tensor * kq_mask_top_k = nullptr;
|
||||
if (cparams.fused_top_k_mask) {
|
||||
kq_mask_top_k = build_fused_top_k_mask(kq_mask, top_k, nullptr, nullptr, nullptr, nullptr, il);
|
||||
kq_mask_top_k = build_fused_top_k_mask(kq_mask, top_k, il);
|
||||
} else {
|
||||
// prepare new kq mask - starts filled with -INFINITY
|
||||
ggml_tensor * kq_mask_all = ggml_fill(ctx0, kq_mask, -INFINITY);
|
||||
|
||||
+1
-6
@@ -1336,15 +1336,10 @@ struct llm_graph_context {
|
||||
float kq_scale,
|
||||
int il) const;
|
||||
|
||||
// ggml_top_k_mask with top_k, score and extra viewed in the row layout of kq_mask, see ggml.h
|
||||
// mask replaces kq_mask as the source of the kept values, it broadcasts to the rows
|
||||
// ggml_top_k_mask with top_k viewed in the row layout of kq_mask, see ggml.h
|
||||
ggml_tensor * build_fused_top_k_mask(
|
||||
ggml_tensor * kq_mask, // [n_kv, n_tokens/n_stream, 1, n_stream]
|
||||
ggml_tensor * top_k, // [n_top, n_tokens]
|
||||
ggml_tensor * score, // [n_pick, n_tokens], can be null
|
||||
ggml_tensor * blocks, // [n_blk, n_pick], can be null
|
||||
ggml_tensor * extra, // [n_extra, n_tokens], can be null
|
||||
ggml_tensor * mask, // [n_kv], kq_mask if null
|
||||
int il) const;
|
||||
|
||||
llm_graph_input_attn_kv_iswa * build_attn_inp_kv_iswa() const;
|
||||
|
||||
+3
-1
@@ -34,6 +34,7 @@ enum llama_non_causal_type {
|
||||
LLAMA_NON_CAUSAL_TYPE_ALL = 0, // all layers non-causal, SWA still applied (gemma 3, qwen-vl, ...)
|
||||
LLAMA_NON_CAUSAL_TYPE_SWA_ONLY = 1, // SWA layers non-causal, dense layers stay causal (gemma 4)
|
||||
LLAMA_NON_CAUSAL_TYPE_SWA_FULL = 2, // all layers non-causal, SWA not applied between tokens of the current ubatch (deepseek 4)
|
||||
LLAMA_NON_CAUSAL_TYPE_DENSE_ONLY = 3, // dense layers non-causal, SWA layers stay causal (lfm3 d1)
|
||||
};
|
||||
|
||||
// M-RoPE: which input position slot feeds each RoPE section
|
||||
@@ -61,7 +62,6 @@ struct llama_hparams {
|
||||
// for example: n_embd_out() vs n_embd_out_impl
|
||||
|
||||
bool vocab_only;
|
||||
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;
|
||||
@@ -358,6 +358,8 @@ struct llama_hparams {
|
||||
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;
|
||||
// deepseek41 Engram: the token that pads the n-gram history, an index into llama_model::engram_token_map
|
||||
uint32_t engram_pad_id = 0;
|
||||
// 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
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
llama_kv_cache_dsa_iswa::llama_kv_cache_dsa_iswa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -22,7 +23,6 @@ llama_kv_cache_dsa_iswa::llama_kv_cache_dsa_iswa(
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
const layer_filter_cb & filter_mla,
|
||||
const layer_filter_cb & filter_lid,
|
||||
const layer_reuse_cb & reuse) : unified(unified) {
|
||||
@@ -63,15 +63,15 @@ llama_kv_cache_dsa_iswa::llama_kv_cache_dsa_iswa(
|
||||
LLAMA_LOG_INFO("%s: creating DSA KV cache, size = %u cells\n", __func__, size_dsa);
|
||||
|
||||
kv_dsa = std::make_unique<llama_kv_cache_dsa>(
|
||||
model, type_k, type_v,
|
||||
v_trans, offload, unified, size_dsa, n_seq_max, n_pad,
|
||||
model, no_alloc, type_k, type_v,
|
||||
v_trans, offload, unified, size_dsa, n_seq_max,
|
||||
0, LLAMA_SWA_TYPE_NONE, filter_dsa, filter_lid, reuse);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating SWA KV cache, size = %u cells\n", __func__, size_swa);
|
||||
|
||||
kv_swa = std::make_unique<llama_kv_cache>(
|
||||
model, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_swa, n_seq_max, n_pad,
|
||||
model, no_alloc, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_swa, n_seq_max,
|
||||
hparams.n_swa, hparams.swa_type, nullptr, filter_swa, reuse, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class llama_kv_cache_dsa_iswa : public llama_memory_i {
|
||||
public:
|
||||
llama_kv_cache_dsa_iswa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -23,7 +24,6 @@ public:
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
const layer_filter_cb & filter_mla,
|
||||
const layer_filter_cb & filter_lid,
|
||||
const layer_reuse_cb & reuse);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
llama_kv_cache_dsa::llama_kv_cache_dsa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -20,7 +21,6 @@ llama_kv_cache_dsa::llama_kv_cache_dsa(
|
||||
bool unified,
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_pad,
|
||||
uint32_t n_swa,
|
||||
llama_swa_type swa_type,
|
||||
const layer_filter_cb & filter_mla,
|
||||
@@ -31,8 +31,8 @@ llama_kv_cache_dsa::llama_kv_cache_dsa(
|
||||
LLAMA_LOG_INFO("%s: creating main KV cache, size = %u cells\n", __func__, kv_size);
|
||||
|
||||
kv_mla = std::make_unique<llama_kv_cache>(
|
||||
model, model.hparams, type_k, type_v,
|
||||
v_trans, offload, unified, kv_size, n_seq_max, n_pad,
|
||||
model, no_alloc, model.hparams, type_k, type_v,
|
||||
v_trans, offload, unified, kv_size, n_seq_max,
|
||||
n_swa, swa_type, nullptr, filter_mla, reuse, nullptr);
|
||||
|
||||
// we use llama_kv_cache for caching indexer keys
|
||||
@@ -48,8 +48,8 @@ llama_kv_cache_dsa::llama_kv_cache_dsa(
|
||||
LLAMA_LOG_INFO("%s: creating indexer KV cache, size = %u cells\n", __func__, kv_size);
|
||||
|
||||
kv_lid = std::make_unique<llama_kv_cache>(
|
||||
model, hparams_lid, type_k, type_v,
|
||||
v_trans, offload, unified, kv_size, n_seq_max, n_pad,
|
||||
model, no_alloc, hparams_lid, type_k, type_v,
|
||||
v_trans, offload, unified, kv_size, n_seq_max,
|
||||
n_swa, swa_type, nullptr, filter_lid, reuse, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ class llama_kv_cache_dsa : public llama_memory_i {
|
||||
public:
|
||||
llama_kv_cache_dsa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -23,7 +24,6 @@ public:
|
||||
bool unified,
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_pad,
|
||||
uint32_t n_swa,
|
||||
llama_swa_type swa_type,
|
||||
const layer_filter_cb & filter_mla,
|
||||
|
||||
+45
-74
@@ -428,8 +428,11 @@ 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;
|
||||
if (n_rows_ref == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// refill the mirrors from their sources; a mirror gets the same rows as its source, so a whole copy is exact
|
||||
for (uint32_t il : kv->get_layer_ids()) {
|
||||
const int32_t src = kv_src[il];
|
||||
if (src == (int32_t) il) {
|
||||
@@ -437,20 +440,11 @@ static void dsv4_state_read_k_cache(
|
||||
}
|
||||
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]);
|
||||
if (t_src->type != t_dst->type || !ggml_are_same_shape(t_src, t_dst)) {
|
||||
throw std::runtime_error("DSV4 K-cache mirror layout mismatch");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
ggml_backend_tensor_copy(t_src, t_dst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,21 +931,24 @@ static void dsv4_make_k_only(llama_hparams & hparams) {
|
||||
|
||||
llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
const llama_model & model,
|
||||
bool offload,
|
||||
bool unified,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t ratio,
|
||||
uint32_t state_size,
|
||||
uint32_t n_embd_state,
|
||||
uint32_t n_rs_seq,
|
||||
const char * name,
|
||||
bool no_alloc,
|
||||
bool offload,
|
||||
bool unified,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t ratio,
|
||||
uint32_t state_size,
|
||||
uint32_t n_embd_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) {
|
||||
n_rs_seq(n_rs_seq),
|
||||
ctxs_bufs(no_alloc) {
|
||||
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;
|
||||
@@ -1026,25 +1023,10 @@ llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
}
|
||||
|
||||
for (auto & [buft, ctx] : ctx_map) {
|
||||
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");
|
||||
}
|
||||
|
||||
ggml_backend_buffer_clear(buf, 0);
|
||||
ggml_backend_buffer_t buf = ctxs_bufs.emplace(std::move(ctx), buft, "DSV4 compressor state");
|
||||
|
||||
LLAMA_LOG_INFO("%s: %10s DSV4 %s state buffer size = %8.2f MiB\n",
|
||||
__func__, ggml_backend_buffer_name(buf), name, ggml_backend_buffer_get_size(buf)/1024.0/1024.0);
|
||||
|
||||
ctxs_bufs.emplace_back(std::move(ctx), buf);
|
||||
}
|
||||
|
||||
LLAMA_LOG_INFO("%s: %s ratio = %u, state = %u x %u, streams = %u, rs_seq = %u, layers = %zu, size = %7.2f MiB\n",
|
||||
@@ -1125,18 +1107,7 @@ 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 & [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;
|
||||
return ctxs_bufs.memory_breakdown();
|
||||
}
|
||||
|
||||
void llama_dsv4_comp_state::state_write(
|
||||
@@ -1271,6 +1242,7 @@ size_t llama_dsv4_comp_state::total_size() const {
|
||||
|
||||
llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -1280,7 +1252,6 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
uint32_t n_rs_seq,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse) :
|
||||
@@ -1315,8 +1286,8 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
dsv4_make_k_only(hparams_raw);
|
||||
|
||||
kv_raw = std::make_unique<llama_kv_cache_iswa>(
|
||||
model, hparams_raw, type_k, type_v,
|
||||
v_trans, offload, swa_full, unified_raw, kv_size, n_seq_max, n_ubatch, n_pad,
|
||||
model, no_alloc, hparams_raw, type_k, type_v,
|
||||
v_trans, offload, swa_full, unified_raw, kv_size, n_seq_max, n_ubatch,
|
||||
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
|
||||
@@ -1352,10 +1323,10 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
// 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);
|
||||
comp_il_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;
|
||||
comp_il_src[il] = src >= 0 ? src : (int32_t) il;
|
||||
if (model.hparams.dsv4_compress_ratios[il] == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -1366,7 +1337,7 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
// 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)) {
|
||||
if (comp_read[j] == j && comp_il_src[j] == src && model.dev_layer(j) == model.dev_layer(il)) {
|
||||
comp_read[il] = j;
|
||||
break;
|
||||
}
|
||||
@@ -1419,24 +1390,24 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
__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, ratio_csa), 256u), n_seq_max, n_pad,
|
||||
model, no_alloc, hparams_csa, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_csa), 256u), n_seq_max,
|
||||
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, 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, ratio_hca), 256u), n_seq_max, n_pad,
|
||||
model, no_alloc, hparams_hca, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_hca), 256u), n_seq_max,
|
||||
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, 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, ratio_csa), 256u), n_seq_max, n_pad,
|
||||
model, no_alloc, hparams_lid, type_k, type_v,
|
||||
v_trans, offload, unified_compressed, GGML_PAD(dsv4_comp_size(kv_size, ratio_csa), 256u), n_seq_max,
|
||||
0, LLAMA_SWA_TYPE_NONE, nullptr, filter_lid, nullptr, nullptr);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating DSV4 CSA compressor state\n", __func__);
|
||||
@@ -1445,19 +1416,19 @@ llama_kv_cache_dsv4::llama_kv_cache_dsv4(
|
||||
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, ratio_csa, csa_state_size,
|
||||
model, no_alloc, 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, ratio_hca, ratio_hca,
|
||||
model, no_alloc, 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,
|
||||
model, no_alloc, offload, unified_compressed, n_seq_max, DSV4_CSA_RATIO, 2*DSV4_CSA_RATIO,
|
||||
2*model.hparams.indexer_head_size, n_rs_seq, "lid", filter_lid);
|
||||
|
||||
// DSV4 attention reads compressed-K / compressor-state rows that the current
|
||||
@@ -1811,9 +1782,9 @@ void llama_kv_cache_dsv4::state_write(llama_io_write_i & io, llama_seq_id seq_id
|
||||
const uint32_t n_rows_lid = seq_id >= 0 ?
|
||||
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, 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);
|
||||
dsv4_state_write_k_cache(io, kv_csa.get(), seq_id, flags, n_rows_csa, comp_il_src);
|
||||
dsv4_state_write_k_cache(io, kv_hca.get(), seq_id, flags, n_rows_hca, comp_il_src);
|
||||
dsv4_state_write_k_cache(io, kv_lid.get(), seq_id, flags, n_rows_lid, comp_il_src);
|
||||
}
|
||||
|
||||
csa_state->state_write(io, seq_id, flags, rs_idx);
|
||||
@@ -1851,9 +1822,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, 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);
|
||||
dsv4_state_read_k_cache(io, kv_csa.get(), seq_id, flags, comp_il_src);
|
||||
dsv4_state_read_k_cache(io, kv_hca.get(), seq_id, flags, comp_il_src);
|
||||
dsv4_state_read_k_cache(io, kv_lid.get(), seq_id, flags, comp_il_src);
|
||||
}
|
||||
|
||||
csa_state->state_read(io, seq_id, flags);
|
||||
|
||||
+12
-13
@@ -16,14 +16,15 @@ public:
|
||||
|
||||
llama_dsv4_comp_state(
|
||||
const llama_model & model,
|
||||
bool offload,
|
||||
bool unified,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t ratio,
|
||||
uint32_t state_size,
|
||||
uint32_t n_embd_state,
|
||||
uint32_t n_rs_seq,
|
||||
const char * name,
|
||||
bool no_alloc,
|
||||
bool offload,
|
||||
bool unified,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t ratio,
|
||||
uint32_t state_size,
|
||||
uint32_t n_embd_state,
|
||||
uint32_t n_rs_seq,
|
||||
const char * name,
|
||||
const llama_memory_i::layer_filter_cb & filter);
|
||||
|
||||
void clear(llama_seq_id seq_id, bool data);
|
||||
@@ -61,15 +62,13 @@ 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;
|
||||
const uint32_t n_stream;
|
||||
const uint32_t n_rs_seq;
|
||||
|
||||
std::vector<std::pair<ggml_context_ptr, ggml_backend_buffer_ptr>> ctxs_bufs;
|
||||
llama_memory_bufs ctxs_bufs;
|
||||
|
||||
std::vector<layer> layers;
|
||||
|
||||
@@ -92,6 +91,7 @@ class llama_kv_cache_dsv4 : public llama_memory_i {
|
||||
public:
|
||||
llama_kv_cache_dsv4(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -101,7 +101,6 @@ public:
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
uint32_t n_rs_seq,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse);
|
||||
@@ -170,7 +169,7 @@ 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;
|
||||
std::vector<int32_t> comp_il_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;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
llama_kv_cache_iswa::llama_kv_cache_iswa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -22,18 +23,18 @@ llama_kv_cache_iswa::llama_kv_cache_iswa(
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
llama_memory_t mem_other,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse,
|
||||
const layer_share_cb & share) :
|
||||
llama_kv_cache_iswa(model, model.hparams, type_k, type_v, v_trans, offload, swa_full, unified,
|
||||
kv_size, n_seq_max, n_ubatch, n_pad, mem_other, filter, reuse, share) {
|
||||
llama_kv_cache_iswa(model, no_alloc, model.hparams, type_k, type_v, v_trans, offload, swa_full, unified,
|
||||
kv_size, n_seq_max, n_ubatch, mem_other, filter, reuse, share) {
|
||||
}
|
||||
|
||||
llama_kv_cache_iswa::llama_kv_cache_iswa(
|
||||
const llama_model & model,
|
||||
const llama_hparams & hparams,
|
||||
bool no_alloc,
|
||||
const llama_hparams & hparams,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -43,7 +44,6 @@ llama_kv_cache_iswa::llama_kv_cache_iswa(
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
llama_memory_t mem_other,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse,
|
||||
@@ -93,15 +93,15 @@ llama_kv_cache_iswa::llama_kv_cache_iswa(
|
||||
}
|
||||
|
||||
kv_base = std::make_unique<llama_kv_cache>(
|
||||
model, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_base, n_seq_max, n_pad,
|
||||
model, no_alloc, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_base, n_seq_max,
|
||||
0, LLAMA_SWA_TYPE_NONE, mem_other_base, filter_base, reuse, share);
|
||||
|
||||
LLAMA_LOG_INFO("%s: creating SWA KV cache, size = %u cells\n", __func__, size_swa);
|
||||
|
||||
kv_swa = std::make_unique<llama_kv_cache>(
|
||||
model, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_swa, n_seq_max, n_pad,
|
||||
model, no_alloc, hparams, type_k, type_v,
|
||||
v_trans, offload, unified, size_swa, n_seq_max,
|
||||
hparams.n_swa, hparams.swa_type, mem_other_swa, filter_swa, reuse, share);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class llama_kv_cache_iswa : public llama_memory_i {
|
||||
public:
|
||||
llama_kv_cache_iswa(
|
||||
const llama_model & model,
|
||||
bool no_alloc,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -24,7 +25,6 @@ public:
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
llama_memory_t mem_other,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse,
|
||||
@@ -32,7 +32,8 @@ public:
|
||||
|
||||
llama_kv_cache_iswa(
|
||||
const llama_model & model,
|
||||
const llama_hparams & hparams,
|
||||
bool no_alloc,
|
||||
const llama_hparams & hparams,
|
||||
ggml_type type_k,
|
||||
ggml_type type_v,
|
||||
bool v_trans,
|
||||
@@ -42,7 +43,6 @@ public:
|
||||
uint32_t kv_size,
|
||||
uint32_t n_seq_max,
|
||||
uint32_t n_ubatch,
|
||||
uint32_t n_pad,
|
||||
llama_memory_t mem_other,
|
||||
const layer_filter_cb & filter,
|
||||
const layer_reuse_cb & reuse,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user