mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-22 04:37:44 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7170cad4d0 | ||
|
|
d88040c704 | ||
|
|
e197cef455 | ||
|
|
bf039c8496 | ||
|
|
dfeea52005 | ||
|
|
e3797b184d | ||
|
|
0b1281aeef | ||
|
|
4912ac24f1 | ||
|
|
84746c5c94 | ||
|
|
0682a576a7 | ||
|
|
a6b83d426f | ||
|
|
c7fc509417 | ||
|
|
a37cc46104 | ||
|
|
99fb997184 | ||
|
|
7775ffb7e4 | ||
|
|
d454a204ef | ||
|
|
0756e83fc1 | ||
|
|
9febe63d5a | ||
|
|
9b56ccf00d | ||
|
|
0a1ef309cd | ||
|
|
261700b896 | ||
|
|
f849095365 | ||
|
|
824eaecf48 | ||
|
|
eb182d85c0 | ||
|
|
ea9e3446b5 | ||
|
|
0efe8e4c6b | ||
|
|
b531e4c749 | ||
|
|
74b115ca6e | ||
|
|
96caa80059 | ||
|
|
479a52779f | ||
|
|
8baabd53b7 | ||
|
|
493253fe5a | ||
|
|
339f3242c7 | ||
|
|
9a3b91c680 | ||
|
|
5c0b49a23b | ||
|
|
1242be66f6 | ||
|
|
84e66d0321 | ||
|
|
52ffa02745 | ||
|
|
839a572a4c | ||
|
|
ead9f32058 | ||
|
|
b684b498b6 | ||
|
|
4a45de4f2c | ||
|
|
7e3eeee048 | ||
|
|
5ba3b9fd3d | ||
|
|
5a9a34d888 | ||
|
|
39da130fcd | ||
|
|
4e201388c3 | ||
|
|
1ba568c5ad | ||
|
|
36140dbee2 | ||
|
|
df11702136 | ||
|
|
9df0b73c70 | ||
|
|
b38960f520 | ||
|
|
a01ce9ffc6 | ||
|
|
8115b4c31f | ||
|
|
119f68d7d4 | ||
|
|
b684498579 | ||
|
|
522f2b6eed | ||
|
|
83c2d012a2 | ||
|
|
13786fbb3a | ||
|
|
d80619397c | ||
|
|
704366d76f | ||
|
|
707616feb5 | ||
|
|
d8d92973c8 | ||
|
|
7879491cf4 |
@@ -7,15 +7,7 @@ on:
|
||||
- master
|
||||
paths: [
|
||||
'.github/workflows/build-webgpu.yml',
|
||||
'**/CMakeLists.txt',
|
||||
'**/.cmake',
|
||||
'**/*.h',
|
||||
'**/*.hpp',
|
||||
'**/*.c',
|
||||
'**/*.cpp',
|
||||
'**/*.wgsl',
|
||||
'**/*.tmpl',
|
||||
'ggml/src/ggml-webgpu/wgsl-shaders/embed_wgsl.py'
|
||||
'ggml/src/ggml-webgpu/**'
|
||||
]
|
||||
|
||||
pull_request:
|
||||
@@ -63,135 +55,3 @@ jobs:
|
||||
-type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' \) \
|
||||
-print0 |
|
||||
xargs -0 clang-format-22 --dry-run --Werror
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
|
||||
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: Dawn Dependency
|
||||
id: dawn-depends
|
||||
run: |
|
||||
DAWN_VERSION="v20260908.214631"
|
||||
DAWN_OWNER="google"
|
||||
DAWN_REPO="dawn"
|
||||
DAWN_ASSET_NAME="Dawn-94c3c9cc0d5fb2e85aebb370fa8d37b71aa34655-macos-latest-Release"
|
||||
echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
curl -L -o artifact.tar.gz \
|
||||
"https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
mkdir dawn
|
||||
tar -xvf artifact.tar.gz -C dawn --strip-components=1
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: webgpu-macos-latest
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
export CMAKE_PREFIX_PATH=dawn
|
||||
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DGGML_WEBGPU=ON -DGGML_METAL=OFF -DGGML_BLAS=OFF
|
||||
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: webgpu-macos-latest
|
||||
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
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
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: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:kisak/kisak-mesa
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y build-essential mesa-vulkan-drivers \
|
||||
libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libssl-dev
|
||||
|
||||
- name: Dawn Dependency
|
||||
id: dawn-depends
|
||||
run: |
|
||||
sudo apt-get install -y libxrandr-dev libxinerama-dev libxcursor-dev mesa-common-dev libx11-xcb-dev libxi-dev
|
||||
DAWN_VERSION="v20260908.214631"
|
||||
DAWN_OWNER="google"
|
||||
DAWN_REPO="dawn"
|
||||
DAWN_ASSET_NAME="Dawn-94c3c9cc0d5fb2e85aebb370fa8d37b71aa34655-ubuntu-latest-Release"
|
||||
echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
curl -L -o artifact.tar.gz \
|
||||
"https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
mkdir dawn
|
||||
tar -xvf artifact.tar.gz -C dawn --strip-components=1
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: webgpu-ubuntu-24.04
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
export Dawn_DIR=dawn/lib64/cmake/Dawn
|
||||
cmake -B build \
|
||||
-DGGML_WEBGPU=ON
|
||||
time cmake --build build --config Release -j $(nproc)
|
||||
|
||||
- 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: webgpu-ubuntu-24.04
|
||||
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
|
||||
|
||||
@@ -1175,6 +1175,7 @@ static const std::map<common_decision_type, std::string> COMMON_DECISION_TYPE_NA
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1, "lfm2-d1" },
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1_OMNI, "lfm2-d1-omni" },
|
||||
{ COMMON_DECISION_TYPE_GRANITE_SWITCH, "granite-switch"},
|
||||
{ COMMON_DECISION_TYPE_H2O_LIGHTNING, "h2o-lightning" },
|
||||
// for future models, ALWAYS add org name prefix to avoid name collisions, e.g. "orgname-modelname"
|
||||
};
|
||||
|
||||
@@ -1234,6 +1235,9 @@ common_gguf_info common_get_gguf_info(const std::string & fname) {
|
||||
info.n_ctx_train = gguf_get_val_u32(gguf_ctx.get(), ctx_id);
|
||||
}
|
||||
|
||||
// same key as llama_model_chat_template(model, nullptr)
|
||||
info.has_chat_tmpl = gguf_find_key(gguf_ctx.get(), "tokenizer.chat_template") >= 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -975,6 +975,7 @@ enum common_decision_type {
|
||||
COMMON_DECISION_TYPE_LFM2_D1, // same as openjev, the labels depend on the question 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_UNKNOWN, // a decision model of a type that is not supported
|
||||
};
|
||||
|
||||
@@ -984,6 +985,7 @@ common_decision_type common_get_decision_type(const struct llama_model * model);
|
||||
struct common_gguf_info {
|
||||
common_decision_type decision_type = COMMON_DECISION_TYPE_UNKNOWN; // UNKNOWN if the file is missing, unreadable, or invalid
|
||||
uint32_t n_ctx_train = 0; // 0 if unknown
|
||||
bool has_chat_tmpl = false; // has the default tokenizer.chat_template
|
||||
};
|
||||
|
||||
common_gguf_info common_get_gguf_info(const std::string & fname);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "lexer.h"
|
||||
#include "runtime.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -75,6 +77,24 @@ lexer_result lexer::tokenize(const std::string & source) {
|
||||
}
|
||||
// add escaped char
|
||||
char escaped_char = src[pos++];
|
||||
if (escaped_char == 'x') {
|
||||
if (pos + 1 >= src.size()) {
|
||||
throw lexer_exception("incomplete hexadecimal escape", source, pos);
|
||||
}
|
||||
|
||||
unsigned int value = 0;
|
||||
const char * first = &src[pos];
|
||||
const char * last = first + 2;
|
||||
|
||||
auto [ptr, ec] = std::from_chars(first, last, value, 16);
|
||||
if (ec != std::errc{} || ptr != last) {
|
||||
throw lexer_exception("invalid hexadecimal escape", source, pos);
|
||||
}
|
||||
|
||||
str += common_unicode_cpt_to_utf8(value);
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (escape_chars.find(escaped_char) == escape_chars.end()) {
|
||||
throw lexer_exception(std::string("unknown escape character \\") + escaped_char, source, pos);
|
||||
}
|
||||
|
||||
@@ -425,7 +425,12 @@ const func_builtins & global_builtins() {
|
||||
throw raised_exception("Jinja Exception: " + msg);
|
||||
}},
|
||||
{"dict", toobject},
|
||||
{"namespace", toobject},
|
||||
{"namespace", [](const func_args & args) -> value {
|
||||
// unlike a dict, a namespace has no methods: ns.items is the attribute, as in jinja2
|
||||
value out = toobject(args);
|
||||
cast_val<value_object>(out)->has_builtins = false;
|
||||
return out;
|
||||
}},
|
||||
{"strftime_now", [](const func_args & args) -> value {
|
||||
args.ensure_vals<value_string>();
|
||||
std::string format = args.get_pos(0)->as_string().str();
|
||||
|
||||
+53
-55
@@ -16,6 +16,7 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <cinttypes>
|
||||
@@ -2152,9 +2153,6 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
|
||||
uint16_t n_draft;
|
||||
|
||||
bool save_dynamic;
|
||||
bool save_static;
|
||||
|
||||
struct seq_info {
|
||||
size_t cache_size = 0; // number of tokens in n-gram cache
|
||||
|
||||
@@ -2168,54 +2166,72 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
common_speculative_impl_ngram_cache(
|
||||
const common_params_speculative & params,
|
||||
uint32_t n_seq,
|
||||
uint16_t n_draft,
|
||||
const std::string & path_static,
|
||||
const std::string & path_dynamic,
|
||||
bool save_dynamic,
|
||||
bool save_static)
|
||||
uint16_t n_draft)
|
||||
: common_speculative_impl(COMMON_SPECULATIVE_TYPE_NGRAM_CACHE, n_seq, n_draft)
|
||||
, params(params.ngram_cache)
|
||||
, n_draft(n_draft)
|
||||
, save_dynamic(save_dynamic)
|
||||
, save_static(save_static)
|
||||
{
|
||||
const auto & ngram = params.ngram_cache;
|
||||
|
||||
SPC_TRC("%s", "adding speculative implementation 'ngram-cache'\n");
|
||||
SPC_TRC("- n_draft=%d, cache_static=%s, cache_dynamic=%s\n",
|
||||
n_draft,
|
||||
path_static.empty() ? "none" : path_static.c_str(),
|
||||
path_dynamic.empty() ? "none" : path_dynamic.c_str());
|
||||
ngram.lookup_cache_static.empty() ? "none" : ngram.lookup_cache_static.c_str(),
|
||||
ngram.lookup_cache_dynamic.empty() ? "none" : ngram.lookup_cache_dynamic.c_str());
|
||||
|
||||
sinfos.resize(n_seq);
|
||||
|
||||
if (!path_static.empty()) {
|
||||
if (!ngram.lookup_cache_static.empty()) {
|
||||
try {
|
||||
auto ngram_cache_static = common_ngram_cache_load(path_static);
|
||||
auto ngram_cache_static = common_ngram_cache_load(ngram.lookup_cache_static);
|
||||
|
||||
for (auto & sinfo : sinfos) {
|
||||
sinfo.ngram_cache_static = ngram_cache_static;
|
||||
}
|
||||
} catch (...) {
|
||||
SPC_ERR("failed to open static lookup cache: %s", path_static.c_str());
|
||||
} catch (const std::ifstream::failure &) {
|
||||
SPC_ERR("failed to open static lookup cache: %s\n", ngram.lookup_cache_static.c_str());
|
||||
GGML_ABORT("Couldn't read static lookup cache");
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_dynamic.empty()) {
|
||||
if (!ngram.lookup_cache_dynamic.empty()) {
|
||||
try {
|
||||
auto ngram_cache_dynamic = common_ngram_cache_load(path_dynamic);
|
||||
auto ngram_cache_dynamic = common_ngram_cache_load(ngram.lookup_cache_dynamic);
|
||||
|
||||
for (auto & sinfo : sinfos) {
|
||||
sinfo.ngram_cache_dynamic = ngram_cache_dynamic;
|
||||
}
|
||||
} catch (...) {
|
||||
SPC_ERR("failed to open dynamic lookup cache: %s", path_dynamic.c_str());
|
||||
GGML_ABORT("Couldn't read dynamic lookup cache");
|
||||
} catch (const std::ifstream::failure &) {
|
||||
std::error_code ec;
|
||||
const bool exists = std::filesystem::exists(ngram.lookup_cache_dynamic, ec);
|
||||
if (!exists && !ec) {
|
||||
// missing file is created on destruction
|
||||
SPC_WRN("dynamic lookup cache file does not exist: %s, starting with empty cache\n",
|
||||
ngram.lookup_cache_dynamic.c_str());
|
||||
} else {
|
||||
SPC_ERR("failed to open dynamic lookup cache: %s\n", ngram.lookup_cache_dynamic.c_str());
|
||||
GGML_ABORT("Couldn't read dynamic lookup cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void begin(llama_seq_id /*seq_id*/, const llama_tokens & /*prompt*/) override {
|
||||
// noop
|
||||
~common_speculative_impl_ngram_cache() override {
|
||||
if (params.lookup_cache_dynamic.empty() || sinfos.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto & dynamic_cache = sinfos.front().ngram_cache_dynamic;
|
||||
for (auto & sinfo : sinfos) {
|
||||
common_ngram_cache_merge(dynamic_cache, sinfo.ngram_cache_context);
|
||||
}
|
||||
|
||||
SPC_INF("saving dynamic lookup cache at %s\n", params.lookup_cache_dynamic.c_str());
|
||||
common_ngram_cache_save(dynamic_cache, params.lookup_cache_dynamic);
|
||||
}
|
||||
|
||||
void begin(llama_seq_id seq_id, const llama_tokens & /*prompt*/) override {
|
||||
sinfos[seq_id].cache_size = 0;
|
||||
}
|
||||
|
||||
void draft_one(
|
||||
@@ -2226,22 +2242,6 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
|
||||
const auto & prompt = *dparams.prompt;
|
||||
|
||||
if (sinfo.cache_size < prompt.size() + 1) {
|
||||
llama_tokens tokens_new;
|
||||
tokens_new.reserve(prompt.size() + 1 - sinfo.cache_size);
|
||||
for (size_t j = sinfo.cache_size; j < prompt.size(); ++j) {
|
||||
tokens_new.push_back(prompt[j]);
|
||||
}
|
||||
tokens_new.push_back(dparams.id_last); // add the last token
|
||||
|
||||
// Update context ngram cache with new dparams.prompt:
|
||||
common_ngram_cache_update(
|
||||
sinfo.ngram_cache_context,
|
||||
LLAMA_NGRAM_MIN, LLAMA_NGRAM_MAX,
|
||||
tokens_new, tokens_new.size(), false);
|
||||
sinfo.cache_size = prompt.size() + 1;
|
||||
}
|
||||
|
||||
llama_tokens inp;
|
||||
inp.reserve(prompt.size() + 1);
|
||||
for (size_t j = 0; j < prompt.size(); ++j) {
|
||||
@@ -2249,6 +2249,16 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
}
|
||||
inp.push_back(dparams.id_last);
|
||||
|
||||
if (sinfo.cache_size < inp.size()) {
|
||||
// Update context ngram cache with the full history so n-grams
|
||||
// ending at the new tokens have their preceding context
|
||||
common_ngram_cache_update(
|
||||
sinfo.ngram_cache_context,
|
||||
LLAMA_NGRAM_MIN, LLAMA_NGRAM_MAX,
|
||||
inp, inp.size() - sinfo.cache_size, false);
|
||||
sinfo.cache_size = inp.size();
|
||||
}
|
||||
|
||||
result.push_back(dparams.id_last);
|
||||
|
||||
common_ngram_cache_draft(
|
||||
@@ -2309,20 +2319,12 @@ static common_ngram_map get_common_ngram_map(
|
||||
return common_ngram_map(size_key, size_value, key_only, min_hits);
|
||||
}
|
||||
|
||||
static common_speculative_impl_ngram_cache create_state_ngram_cache(
|
||||
static std::unique_ptr<common_speculative_impl_ngram_cache> create_state_ngram_cache(
|
||||
const common_speculative_config & config,
|
||||
uint32_t n_seq,
|
||||
const std::string & path_static,
|
||||
const std::string & path_dynamic) {
|
||||
uint32_t n_seq) {
|
||||
uint16_t n_draft = 8; // TODO get from config?
|
||||
|
||||
// TODO bool param in common/common.h to set save_static/save_dynamic?
|
||||
bool save_static = false;
|
||||
bool save_dynamic = false;
|
||||
|
||||
common_speculative_impl_ngram_cache state(config.params, n_seq, n_draft, path_static, path_dynamic, save_static, save_dynamic);
|
||||
|
||||
return state;
|
||||
return std::make_unique<common_speculative_impl_ngram_cache>(config.params, n_seq, n_draft);
|
||||
}
|
||||
|
||||
std::string common_speculative_type_name_str(const std::vector<common_speculative_type> & types) {
|
||||
@@ -2822,11 +2824,7 @@ common_speculative * common_speculative_init(common_params_speculative & params,
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE: {
|
||||
auto state = create_state_ngram_cache(
|
||||
config, n_seq,
|
||||
params.ngram_cache.lookup_cache_static,
|
||||
params.ngram_cache.lookup_cache_dynamic);
|
||||
impls.push_back(std::make_unique<common_speculative_impl_ngram_cache>(state));
|
||||
impls.push_back(create_state_ngram_cache(config, n_seq));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -17,8 +17,10 @@ __all__ = [
|
||||
|
||||
|
||||
TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"NEOChatModel": "sensenova_u1",
|
||||
"AfmoeForCausalLM": "afmoe",
|
||||
"LagunaForCausalLM": "laguna",
|
||||
"FlintForCausalLM": "flint",
|
||||
"ApertusForCausalLM": "llama",
|
||||
"ArceeForCausalLM": "llama",
|
||||
"ArcticForCausalLM": "arctic",
|
||||
@@ -200,7 +202,9 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MiniMaxM3SparseForCausalLM": "minimax",
|
||||
"MiniMaxM3SparseForConditionalGeneration": "minimax",
|
||||
"Ministral3ForCausalLM": "mistral3",
|
||||
"Ministral3Model": "mistral3",
|
||||
"Mistral3ForConditionalGeneration": "mistral3",
|
||||
"Mistral3Model": "mistral3",
|
||||
"VoxtralRealtimeForConditionalGeneration": "mistral3",
|
||||
"MistralForCausalLM": "llama",
|
||||
"MixtralForCausalLM": "llama",
|
||||
@@ -226,6 +230,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MuseSparkForConditionalGeneration": "muse_spark",
|
||||
"OpenELMForCausalLM": "openelm",
|
||||
"OpenJevModel": "qwen",
|
||||
"H2OLightningModel": "h2o_lightning",
|
||||
"OrionForCausalLM": "orion",
|
||||
"PLMForCausalLM": "plm",
|
||||
"PLaMo2ForCausalLM": "plamo",
|
||||
@@ -357,12 +362,14 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"Mistral3ForConditionalGeneration": "llava",
|
||||
"Mistral3Model": "llava",
|
||||
"NemotronH_Omni_Reasoning_V3": "nemotron",
|
||||
"NemotronH_Nano_VL_V2": "nemotron",
|
||||
"MuseGlimmerForConditionalGeneration": "muse_glimmer",
|
||||
"MuseSparkForConditionalGeneration": "muse_spark",
|
||||
"PaddleOCRVisionModel": "ernie",
|
||||
"Phi4ForCausalLMV": "phi",
|
||||
"NEOVisionModel": "sensenova_u1",
|
||||
"Qwen2AudioForConditionalGeneration": "ultravox",
|
||||
"Qwen2VLForConditionalGeneration": "qwenvl",
|
||||
"Qwen2VLModel": "qwenvl",
|
||||
@@ -375,6 +382,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"Qwen3VLForConditionalGeneration": "qwen3vl",
|
||||
"Qwen3VLMoeForConditionalGeneration": "qwen3vl",
|
||||
"OpenJevModel": "qwen3vl",
|
||||
"H2OLightningModel": "h2o_lightning",
|
||||
"Qwen3_5ForConditionalGeneration": "qwen3vl",
|
||||
"Qwen3_5MoeForConditionalGeneration": "qwen3vl",
|
||||
"Qwen4ExpForConditionalGeneration": "qwen4exp",
|
||||
|
||||
+5
-2
@@ -1701,7 +1701,7 @@ class TextModel(ModelBase):
|
||||
self.gguf_writer.add_expert_group_used_count(n_group_used)
|
||||
logger.info(f"gguf: expert groups used count = {n_group_used}")
|
||||
|
||||
if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func", "expert_selection_fn", "router_score_func"], optional=True)) is not None:
|
||||
if (score_func := self.find_hparam(["score_function", "scoring_func", "score_func", "moe_router_activation", "moe_router_activation_func", "expert_selection_fn", "router_score_func", "router_score_function"], optional=True)) is not None:
|
||||
if score_func == "sigmoid":
|
||||
self.gguf_writer.add_expert_gating_func(gguf.ExpertGatingFuncType.SIGMOID)
|
||||
elif score_func == "softmax":
|
||||
@@ -1939,6 +1939,9 @@ class TextModel(ModelBase):
|
||||
if chkhsh == "52df12b4c8d4176e7481aab4b6e8454d1fd0a210a04a574f6d4e067d10e23c3e":
|
||||
# ref: https://huggingface.co/CohereLabs/North-Mini-Code-1.0
|
||||
res = "cohere2moe"
|
||||
if chkhsh == "d5420e601e979078c670bc503d9ac411c311fa76b76a76267c31e0d6ba816637":
|
||||
# ref: https://huggingface.co/RecursiveSI/flint
|
||||
res = "flint"
|
||||
if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
|
||||
# ref: https://huggingface.co/Qwen/Qwen1.5-7B
|
||||
res = "qwen2"
|
||||
@@ -3143,7 +3146,7 @@ def get_model_architecture(hparams: dict[str, Any], model_type: ModelType) -> st
|
||||
# Kimi-K3's text_config reports "KimiLinearForCausalLM", which is the older
|
||||
# Kimi-Linear-48B architecture and cannot load K3 (no attention residuals,
|
||||
# latent MoE, situ, ...). Route on the top-level architecture instead.
|
||||
if model_type == ModelType.TEXT and arch in ("StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration"):
|
||||
if model_type == ModelType.TEXT and arch in ("NEOChatModel", "StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration"):
|
||||
return arch
|
||||
|
||||
# if "architectures" is found in the sub-config, use that instead
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import ModelBase, gguf
|
||||
from .qwen import Qwen2MoeModel
|
||||
|
||||
|
||||
@ModelBase.register("FlintForCausalLM")
|
||||
@ModelBase.example("RecursiveSI/flint")
|
||||
class FlintModel(Qwen2MoeModel):
|
||||
model_arch = gguf.MODEL_ARCH.FLINT
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
dense = self.hparams["mlp_only_layers"]
|
||||
if dense != list(range(len(dense))):
|
||||
raise ValueError("Flint dense layers must precede the MoE layers")
|
||||
self.gguf_writer.add_leading_dense_block_count(len(dense))
|
||||
self.gguf_writer.add_expert_shared_count(
|
||||
self.hparams["shared_expert_intermediate_size"] // self.hparams["moe_intermediate_size"]
|
||||
)
|
||||
self.gguf_writer.add_expert_weights_norm(self.hparams["norm_topk_prob"])
|
||||
self.gguf_writer.add_expert_weights_scale(1.0)
|
||||
self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
|
||||
self.gguf_writer.add_sliding_window_pattern([
|
||||
kind == "sliding_attention" for kind in self.hparams["layer_types"]
|
||||
])
|
||||
self.gguf_writer.add_rope_pattern([
|
||||
i not in self.hparams["no_rope_layer_indices"] for i in range(self.block_count)
|
||||
])
|
||||
|
||||
def modify_tensors(self, data_torch, name, bid):
|
||||
if name.endswith(".softmax_offset"):
|
||||
name += ".weight"
|
||||
if name.endswith(".gate.expert_bias"):
|
||||
if not self.hparams["router_expert_bias"]:
|
||||
return
|
||||
name += ".bias"
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
@@ -999,6 +999,8 @@ class Gemma4VisionAudioModel(MmprojModel):
|
||||
assert self.hparams_vision is not None
|
||||
self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA4V)
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6))
|
||||
assert self.hparams_vision.get("hidden_activation", "gelu_pytorch_tanh") == "gelu_pytorch_tanh"
|
||||
self.gguf_writer.add_vision_use_gelu(True)
|
||||
|
||||
# audio params
|
||||
if self.has_audio_encoder:
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .base import ModelBase, gguf, logger
|
||||
from .qwen import Qwen3_5TextModel
|
||||
from .qwen3vl import Qwen3VLVisionModel
|
||||
|
||||
|
||||
def _is_h2o_lightning_checkpoint(dir_model: Path) -> bool:
|
||||
return (dir_model / "h2o_lightning_shim.py").is_file() and (dir_model / "serve_config.json").is_file()
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_h2o_lightning_checkpoint)
|
||||
def _load_h2o_lightning_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
logger.info("gguf: detected H2O-Lightning checkpoint")
|
||||
hparams = ModelBase.load_hparams(dir_model, False, guess=False)
|
||||
hparams["architectures"] = ["H2OLightningModel"]
|
||||
return hparams
|
||||
|
||||
|
||||
@ModelBase.register("H2OLightningModel")
|
||||
@ModelBase.example("h2oai/h2o-lightning-4b")
|
||||
class H2OLightningModel(Qwen3_5TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.QWEN35
|
||||
|
||||
# prompt and calibration follow serve_config.json and h2o_lightning_shim.py of the model repo
|
||||
# the state budget is hard-coded in server-decision.cpp
|
||||
_MAX_STATE_TOKENS = 32000
|
||||
_DEFAULT_INSTRUCTIONS = "Answer the question below."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
with open(self.dir_model / "serve_config.json", "r", encoding="utf-8") as f:
|
||||
self._serve_config = json.load(f)
|
||||
prompt = self._serve_config["prompt"]
|
||||
expected = {
|
||||
"state_compact": (prompt.get("state_compact", True), True),
|
||||
"max_state_tokens": (prompt.get("max_state_tokens"), self._MAX_STATE_TOKENS),
|
||||
"layout": (prompt.get("layout", "plain"), "plain"),
|
||||
"chat_template": (prompt.get("chat_template", True), True),
|
||||
"enable_thinking": (prompt.get("enable_thinking", False), False),
|
||||
}
|
||||
for name, (got, want) in expected.items():
|
||||
if got != want:
|
||||
raise ValueError(f"serve_config.json: {name} is {got!r}, the h2o-lightning decision type expects {want!r}")
|
||||
|
||||
def get_vocab_base_pre(self, tokenizer) -> str:
|
||||
# transformers loads this checkpoint as Qwen2Tokenizer: the model was trained and is served with the Qwen2 split
|
||||
pre = super().get_vocab_base_pre(tokenizer)
|
||||
if pre != "qwen35":
|
||||
raise ValueError(f"expected the Qwen3.5 tokenizer, got pre-tokenizer {pre!r}")
|
||||
return "qwen2"
|
||||
|
||||
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 server gives the template texts rendered as the shim renders them, it only lays them out
|
||||
prompt = self._serve_config["prompt"]
|
||||
nl = "{{ '\\n' }}"
|
||||
description = (
|
||||
"{% if type == 'noul' %}"
|
||||
"{% if o.description %}{{ o.description }}{% elif o.key == 'true' %}the statement holds{% else %}it does not{% endif %}"
|
||||
"{% elif o.description is none %}{{ o.key }}{% else %}{{ o.description }}{% endif %}"
|
||||
)
|
||||
user = (
|
||||
"{% set user %}record: {{ state }}" + nl
|
||||
+ "question: {{ instructions if instructions else '" + self._DEFAULT_INSTRUCTIONS + "' }}" + nl
|
||||
+ "options:{% for o in options %}" + nl + "{{ o.label | trim }}) {{ o.key }}: " + description + "{% endfor %}{% endset %}"
|
||||
)
|
||||
return (
|
||||
"<|im_start|>system" + nl + prompt["system"].strip() + "<|im_end|>" + nl
|
||||
+ "<|im_start|>user" + nl + "{% for image in images %}{{ image }}{% endfor %}" + user + "{{ user | trim }}<|im_end|>" + nl
|
||||
+ "<|im_start|>assistant" + nl + "<think>{{ '\\n\\n' }}</think>{{ '\\n\\n' }}" + prompt["prefill"]
|
||||
)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_decision_type(gguf.DecisionType.H2O_LIGHTNING)
|
||||
# the answer follows "Answer:", so the token of a label has a leading space
|
||||
cfg = self._serve_config
|
||||
self.gguf_writer.add_decision_labels([" " + label for label in cfg["labels"][:cfg.get("max_options", len(cfg["labels"]))]])
|
||||
for name in ("choice", "noul", "score"):
|
||||
self.gguf_writer.add_decision_temperature(name, self._serve_config["temperature"][name])
|
||||
# a request with images has this temperature for all question types
|
||||
self.gguf_writer.add_decision_temperature("image", self._serve_config["image"]["temperature"])
|
||||
|
||||
|
||||
@ModelBase.register("H2OLightningModel")
|
||||
class H2OLightningVisionModel(Qwen3VLVisionModel):
|
||||
pass
|
||||
@@ -374,6 +374,16 @@ class LFM2VLModel(MmprojModel):
|
||||
if resize_algo is None:
|
||||
raise ValueError(f"unsupported resample: {resample}")
|
||||
self.gguf_writer.add_vision_image_resize_algo(resize_algo)
|
||||
# tiling params from processor_config.json (do_image_splitting=False -> single tile, i.e. no tiling)
|
||||
preproc = self.preprocessor_config
|
||||
if preproc.get("do_image_splitting", True):
|
||||
self.gguf_writer.add_vision_preproc_min_tiles(preproc.get("min_tiles", 2))
|
||||
self.gguf_writer.add_vision_preproc_max_tiles(preproc.get("max_tiles", 10))
|
||||
else:
|
||||
self.gguf_writer.add_vision_preproc_min_tiles(1)
|
||||
self.gguf_writer.add_vision_preproc_max_tiles(1)
|
||||
self.gguf_writer.add_vision_preproc_tile_size(preproc.get("tile_size", 512))
|
||||
self.gguf_writer.add_vision_preproc_max_pixels_tolerance(preproc.get("max_pixels_tolerance", 2.0))
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
|
||||
+22
-1
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from typing import Iterable, TYPE_CHECKING
|
||||
from typing import Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
@@ -128,3 +128,24 @@ class LlavaVisionModel(MmprojModel):
|
||||
yield from super().modify_tensors(img_break_embd, name, bid)
|
||||
|
||||
return # skip other tensors
|
||||
|
||||
|
||||
@ModelBase.register("Mistral3Model")
|
||||
@ModelBase.example("nvidia/Nemotron-3.5-Embed-1B-BF16")
|
||||
class Mistral3EmbeddingVisionModel(LlavaVisionModel):
|
||||
def __init__(self, dir_model, *args, **kwargs):
|
||||
hparams = ModelBase.load_hparams(dir_model, is_mistral_format=False)
|
||||
text_config = hparams.get("text_config", hparams)
|
||||
if text_config.get("is_causal") is not False:
|
||||
raise NotImplementedError("Expected a Nemotron Embed model for Mistral3Model, but is_causal is not False")
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
if item[0].endswith("embed_tokens.weight"):
|
||||
return ModelBase.filter_tensors(item)
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_vision_preproc_image_size(self.preprocessor_config["size"]["longest_edge"])
|
||||
|
||||
+28
-6
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import ModelBase, TextModel, gguf
|
||||
from .base import ModelBase, TextModel, get_model_architecture, gguf
|
||||
|
||||
from .deepseek import DeepseekV2Model
|
||||
from .llama import LlamaModel
|
||||
@@ -13,9 +13,11 @@ from .llama import LlamaModel
|
||||
|
||||
@ModelBase.register(
|
||||
"Mistral3ForConditionalGeneration",
|
||||
"Mistral3Model",
|
||||
"Ministral3ForCausalLM",
|
||||
"Ministral3Model",
|
||||
)
|
||||
@ModelBase.example("mistralai/Mistral-Small-3.1-24B-Instruct-2503", "hf-tiny-v2/tiny-random-Ministral3ForCausalLM")
|
||||
@ModelBase.example("mistralai/Mistral-Small-3.1-24B-Instruct-2503", "hf-tiny-v2/tiny-random-Ministral3ForCausalLM", "nvidia/Nemotron-3-Embed-1B-BF16", "nvidia/Nemotron-3.5-Embed-1B-BF16")
|
||||
class Mistral3Model(TextModel):
|
||||
class Ministral3Model(LlamaModel):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3
|
||||
@@ -29,6 +31,18 @@ class Mistral3Model(TextModel):
|
||||
self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"])
|
||||
self.gguf_writer.add_attn_temperature_scale(rope_params["llama_4_scaling_beta"])
|
||||
|
||||
class Mistral3EmbedNemotronModel(Ministral3Model):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_add_bos_token(False)
|
||||
self.gguf_writer.add_add_eos_token(False)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._try_set_pooling_type()
|
||||
|
||||
class Mistral4Model(DeepseekV2Model):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL4
|
||||
skip_mtp = False # model contains no MTP layers, so no need to skip
|
||||
@@ -42,12 +56,20 @@ class Mistral3Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3 # unused
|
||||
impl: TextModel
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, dir_model, *args, **kwargs):
|
||||
hparams = ModelBase.load_hparams(dir_model, is_mistral_format=False)
|
||||
hf_arch = get_model_architecture(hparams, self.model_type)
|
||||
if hf_arch in ("Ministral3Model", "Mistral3Model"):
|
||||
text_config = hparams.get("text_config", hparams)
|
||||
if text_config.get("is_causal") is not False:
|
||||
raise NotImplementedError(f"Expected a Nemotron Embed model for {hf_arch}, but is_causal is not False")
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
if self.hparams.get("model_type") == "mistral4":
|
||||
self.impl = Mistral3Model.Mistral4Model(*args, **kwargs)
|
||||
self.impl = Mistral3Model.Mistral4Model(dir_model, *args, **kwargs)
|
||||
elif self.hf_arch in ("Ministral3Model", "Mistral3Model"):
|
||||
self.impl = Mistral3Model.Mistral3EmbedNemotronModel(dir_model, *args, **kwargs)
|
||||
else:
|
||||
self.impl = Mistral3Model.Ministral3Model(*args, **kwargs)
|
||||
self.impl = Mistral3Model.Ministral3Model(dir_model, *args, **kwargs)
|
||||
|
||||
def set_vocab(self):
|
||||
self.impl.set_vocab()
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
|
||||
from .base import MmprojModel, ModelBase, gguf
|
||||
from .qwen import Qwen2Model
|
||||
|
||||
|
||||
@ModelBase.example("sensenova/SenseNova-U1.5-8B-MoT")
|
||||
@ModelBase.register("NEOChatModel")
|
||||
class SenseNovaU1Model(Qwen2Model):
|
||||
model_arch = gguf.MODEL_ARCH.SENSENOVA_U1
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item):
|
||||
name, _ = item
|
||||
if not name.startswith("language_model.") or "_mot_gen" in name:
|
||||
return None
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
if int(self.hparams.get("num_experts") or 0) > 1:
|
||||
raise ValueError("SenseNova U1 conversion currently supports the dense understanding branch")
|
||||
if self.rope_parameters.get("rope_type", "default") not in ("default", None):
|
||||
raise ValueError("SenseNova U1 scaled RoPE is not supported")
|
||||
if self.hparams.get("attention_bias", False):
|
||||
raise ValueError("SenseNova U1 attention bias is not supported")
|
||||
if self.hparams.get("use_sliding_window", False):
|
||||
raise ValueError("SenseNova U1 sliding attention is not supported")
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_rope_freq_base_spatial(self.hparams["rope_theta_hw"])
|
||||
|
||||
def get_tensors(self):
|
||||
for name, gen in self.model_tensors.items():
|
||||
if name.endswith((".q_norm_hw.weight", ".k_norm_hw.weight")):
|
||||
continue
|
||||
data = gen()
|
||||
if name.endswith((".q_norm.weight", ".k_norm.weight")):
|
||||
spatial = self.model_tensors[name.replace("_norm.weight", "_norm_hw.weight")]()
|
||||
# Keep one weight vector; the graph normalizes its two halves independently.
|
||||
data = torch.cat((data, spatial), dim=0)
|
||||
yield name, data
|
||||
|
||||
|
||||
@ModelBase.register("NEOVisionModel")
|
||||
@ModelBase.example("sensenova/SenseNova-U1.5-8B-MoT")
|
||||
class SenseNovaU1VisionModel(MmprojModel):
|
||||
def __init__(self, dir_model: Path, *args, **kwargs):
|
||||
hparams = kwargs.pop("hparams", None)
|
||||
if hparams is None:
|
||||
hparams = ModelBase.load_hparams(dir_model, is_mistral_format=False)
|
||||
else:
|
||||
hparams = dict(hparams)
|
||||
|
||||
vision_config = dict(hparams["vision_config"])
|
||||
# NEOVisionModel has no transformer blocks
|
||||
# these defaults satisfy the generic mmproj metadata contract without replacing checkpoint values
|
||||
vision_config.setdefault("image_size", 0)
|
||||
vision_config.setdefault("intermediate_size", 0)
|
||||
vision_config.setdefault("num_hidden_layers", 0)
|
||||
vision_config.setdefault("num_attention_heads", 1)
|
||||
hparams["vision_config"] = vision_config
|
||||
super().__init__(dir_model, *args, hparams=hparams, **kwargs)
|
||||
|
||||
self.preprocessor_config.setdefault("image_mean", [0.485, 0.456, 0.406])
|
||||
self.preprocessor_config.setdefault("image_std", [0.229, 0.224, 0.225])
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item):
|
||||
name, gen = item
|
||||
if not name.startswith("vision_model."):
|
||||
return None
|
||||
return super().filter_tensors((name, gen))
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_clip_projector_type("sensenova_u1")
|
||||
vision_config = self.hparams["vision_config"]
|
||||
# These defaults match the known U1.5 checkpoint when older configs omit fields.
|
||||
hidden_size = int(vision_config.get("hidden_size", 1024))
|
||||
min_pixels = int(vision_config.get("min_pixels", 65536))
|
||||
max_pixels = int(vision_config.get("max_pixels", 16777216))
|
||||
downsample_ratio = float(vision_config.get("downsample_ratio", 0.5))
|
||||
if downsample_ratio <= 0:
|
||||
raise ValueError(f"SenseNova U1 downsample_ratio must be positive, got {downsample_ratio}")
|
||||
merge_size = round(1.0 / downsample_ratio)
|
||||
if abs((1.0 / downsample_ratio) - merge_size) > 1e-6:
|
||||
raise ValueError(f"SenseNova U1 downsample_ratio must have an integer reciprocal, got {downsample_ratio}")
|
||||
norm_eps = float(vision_config.get("layer_norm_eps", vision_config.get("rms_norm_eps", 1e-6)))
|
||||
self.gguf_writer.add_vision_head_dim(hidden_size)
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(norm_eps)
|
||||
self.gguf_writer.add_vision_min_pixels(min_pixels)
|
||||
self.gguf_writer.add_vision_max_pixels(max_pixels)
|
||||
self.gguf_writer.add_vision_spatial_merge_size(merge_size)
|
||||
|
||||
def modify_tensors(self, data_torch, name, bid):
|
||||
tensor_names = {
|
||||
"vision_model.embeddings.patch_embedding.weight": "v.patch_embd.weight",
|
||||
"vision_model.embeddings.patch_embedding.bias": "v.patch_embd.bias",
|
||||
"vision_model.embeddings.dense_embedding.weight": self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, 0),
|
||||
"vision_model.embeddings.dense_embedding.bias": self.format_tensor_name(
|
||||
gguf.MODEL_TENSOR.V_MMPROJ, 0, suffix=".bias"
|
||||
),
|
||||
}
|
||||
if name not in tensor_names:
|
||||
raise ValueError(f"Unexpected SenseNova U1 vision tensor: {name!r}")
|
||||
return [(tensor_names[name], data_torch)]
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if new_name == self.format_tensor_name(gguf.MODEL_TENSOR.V_MMPROJ, 0):
|
||||
return gguf.GGMLQuantizationType.F16 if self.ftype == gguf.LlamaFileType.MOSTLY_F16 else gguf.GGMLQuantizationType.F32
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
@@ -101,6 +101,7 @@ models = [
|
||||
{"name": "command-r", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereForAI/c4ai-command-r-v01", },
|
||||
{"name": "tiny_aya", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereLabs/tiny-aya-base", },
|
||||
{"name": "cohere2moe", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/CohereLabs/North-Mini-Code-1.0", },
|
||||
{"name": "flint", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/RecursiveSI/flint", },
|
||||
{"name": "qwen2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Qwen/Qwen1.5-7B", },
|
||||
{"name": "olmo", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/allenai/OLMo-1.7-7B-hf", },
|
||||
{"name": "dbrx", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/databricks/dbrx-base", },
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
> [!WARNING]
|
||||
> **Note:** zDNN is **not** the same as ZenDNN.
|
||||
>
|
||||
> - **zDNN** (this page): IBM's Deep Neural Network acceleration library for IBM Z & LinuxONE Mainframes
|
||||
> - **ZenDNN**: AMD's deep learning library for AMD EPYC CPUs ([see ZenDNN documentation](ZenDNN.md))
|
||||
|
||||
@@ -46,6 +47,14 @@ The IBM zDNN backend has the following CMake options that control the behaviour
|
||||
| --------------------- | ------------- | -------------------------------------------------------------------------------------- |
|
||||
| `GGML_ZDNN_HIGH_PREC` | `0` | Set to `1` to requantize Q8_0 weights in smaller groups, for lower loss at lower speed |
|
||||
|
||||
## Performance Tunables
|
||||
|
||||
| Flag Option | Default Value | Description |
|
||||
| ----------------------- | ------------- | ------------------------------------------------------ |
|
||||
| `--flash-attn` or `-fa` | `auto` | Set to `on` to run attention on the NNPA (Recommended) |
|
||||
|
||||
Note: Set `-fa on` explicitly. Flash Attention fuses each layer's attention (the KQ matrix multiplication, softmax and KQV matrix multiplication) into one `FLASH_ATTN_EXT` operation, which zDNN runs on the NNPA instead of the CPU. The speedup grows with the context length. With `auto`, llama.cpp currently turns Flash Attention off, because the operation runs on a different device than the layer. zDNN runs it when the KV cache is F16 (the default), the model uses no attention sinks, ALiBi or logit softcapping, and there is one KV cache stream (one sequence, or `--kv-unified`). Otherwise it falls back to the CPU.
|
||||
|
||||
## 1. Install zDNN Library
|
||||
|
||||
Note: A minimum of libzdnn 1.1.2 is required for the IBM zDNN backend to link correctly.
|
||||
|
||||
+5
-5
@@ -27,7 +27,7 @@ Legend:
|
||||
| COL2IM_1D | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| CONCAT | ❌ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | 🟡 | ❌ | ❌ |
|
||||
| CONT | ❌ | 🟡 | ✅ | ✅ | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | 🟡 | ❌ | ❌ |
|
||||
| CONV_1D_DW | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
||||
| CONV_1D_DW | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
||||
| CONV_2D | ❌ | ❌ | 🟡 | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | 🟡 | ✅ | ❌ | ❌ |
|
||||
| CONV_2D_DW | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| CONV_3D | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
@@ -76,10 +76,10 @@ Legend:
|
||||
| MUL_MAT | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |
|
||||
| MUL_MAT_HADAMARD | ❌ | ❌ | ✅ | ❌ | ✅ | 🟡 | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | 🟡 |
|
||||
| MUL_MAT_ID | ❌ | 🟡 | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | 🟡 | ❌ |
|
||||
| MUL_MAT_ID_W4A4 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_ID_W4A8 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_W4A4 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_W4A8 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_ID_W4A4 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_ID_W4A8 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_W4A4 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_W4A8 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| NEG | ❌ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| NORM | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | ✅ | ✅ | ✅ | ✅ | 🟡 | ❌ | ❌ |
|
||||
| OPT_STEP_ADAMW | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
|
||||
+7793
-6047
File diff suppressed because it is too large
Load Diff
@@ -8,10 +8,10 @@ extern "C" {
|
||||
|
||||
#define RPC_PROTO_MAJOR_VERSION 8
|
||||
#define RPC_PROTO_MINOR_VERSION 0
|
||||
#define RPC_PROTO_PATCH_VERSION 0
|
||||
#define RPC_PROTO_PATCH_VERSION 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(GGML_OP_COUNT == 101, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
|
||||
static_assert(GGML_OP_COUNT == 102, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
|
||||
#endif
|
||||
|
||||
#define GGML_RPC_MAX_SERVERS 16
|
||||
|
||||
@@ -585,6 +585,7 @@ extern "C" {
|
||||
GGML_OP_DSV4_HC_COMB,
|
||||
GGML_OP_DSV4_HC_PRE,
|
||||
GGML_OP_DSV4_HC_POST,
|
||||
GGML_OP_TOP_K_MASK,
|
||||
|
||||
GGML_OP_UNARY,
|
||||
|
||||
@@ -2732,6 +2733,26 @@ extern "C" {
|
||||
struct ggml_tensor * post,
|
||||
struct ggml_tensor * comb);
|
||||
|
||||
// top-k sparse attention mask: keep the mask cells of the live picks and the extra cells, set the rest to -inf
|
||||
//
|
||||
// mask: [n_kv, m1, m2, m3] f16 or f32, broadcast to the rows of top_k
|
||||
// top_k: [n_top, ne1, ne2, ne3] i32, picks of each row
|
||||
// score: [n_pick, ne1, ne2, ne3] f32, can be NULL: a pick is live when its score is above -inf, else every pick is live
|
||||
// blocks: [n_blk, n_pick] i32, can be NULL: the member cells of each pick, else a pick is a cell
|
||||
// extra: [n_extra, ne1, ne2, ne3] i32, can be NULL: cells of each row kept without a pick
|
||||
// res: [n_kv, ne1, ne2, ne3] same type as mask
|
||||
//
|
||||
// res[c, i] = mask[c, i % m] when c is a cell of a live pick of row i or an extra cell of row i, else -inf
|
||||
// picks and cells out of range are skipped
|
||||
//
|
||||
GGML_API struct ggml_tensor * ggml_top_k_mask(
|
||||
struct ggml_context * ctx,
|
||||
struct ggml_tensor * mask,
|
||||
struct ggml_tensor * top_k,
|
||||
struct ggml_tensor * score,
|
||||
struct ggml_tensor * blocks,
|
||||
struct ggml_tensor * extra);
|
||||
|
||||
// custom operators
|
||||
|
||||
typedef void (*ggml_custom1_op_t)(struct ggml_tensor * dst , const struct ggml_tensor * a, int ith, int nth, void * userdata);
|
||||
|
||||
@@ -774,6 +774,13 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
return src_ss[0];
|
||||
};
|
||||
|
||||
auto handle_pool_1d = [&](const std::vector<ggml_backend_meta_split_state> & src_ss) -> ggml_backend_meta_split_state {
|
||||
if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_0) {
|
||||
return {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1};
|
||||
}
|
||||
return src_ss[0];
|
||||
};
|
||||
|
||||
auto handle_flash_attn_ext = [&](const std::vector<ggml_backend_meta_split_state> & src_ss) -> ggml_backend_meta_split_state {
|
||||
GGML_ASSERT(tensor->src[3] == nullptr || src_ss[3].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
|
||||
|
||||
@@ -990,12 +997,14 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
case GGML_OP_CONV_3D:
|
||||
case GGML_OP_CONV_2D_DW:
|
||||
case GGML_OP_CONV_TRANSPOSE_2D:
|
||||
case GGML_OP_POOL_1D:
|
||||
case GGML_OP_POOL_2D:
|
||||
case GGML_OP_POOL_2D_BACK:
|
||||
case GGML_OP_UPSCALE: {
|
||||
split_state = handle_generic(src_ss, /*scalar_only =*/ true);
|
||||
} break;
|
||||
case GGML_OP_POOL_1D: {
|
||||
split_state = handle_pool_1d(src_ss);
|
||||
} break;
|
||||
case GGML_OP_PAD: {
|
||||
split_state = handle_pad(src_ss);
|
||||
} break;
|
||||
@@ -1046,7 +1055,8 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
} break;
|
||||
case GGML_OP_DSV4_HC_COMB:
|
||||
case GGML_OP_DSV4_HC_PRE:
|
||||
case GGML_OP_DSV4_HC_POST: {
|
||||
case GGML_OP_DSV4_HC_POST:
|
||||
case GGML_OP_TOP_K_MASK: {
|
||||
split_state = handle_generic(src_ss, /*scalar_only =*/ true);
|
||||
} break;
|
||||
case GGML_OP_UNARY: {
|
||||
|
||||
@@ -2226,6 +2226,7 @@ bool ggml_op_alloc_size_may_expand(enum ggml_op op) {
|
||||
case GGML_OP_CUMSUM:
|
||||
case GGML_OP_ARGSORT:
|
||||
case GGML_OP_TOP_K:
|
||||
case GGML_OP_ARGMAX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -650,9 +650,9 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
|
||||
|
||||
# Fetch KleidiAI sources:
|
||||
include(FetchContent)
|
||||
set(KLEIDIAI_COMMIT_TAG "v1.31.0")
|
||||
set(KLEIDIAI_COMMIT_TAG "v1.32.0")
|
||||
set(KLEIDIAI_DOWNLOAD_URL "https://github.com/ARM-software/kleidiai/releases/download/${KLEIDIAI_COMMIT_TAG}/kleidiai-${KLEIDIAI_COMMIT_TAG}-src.tar.gz")
|
||||
set(KLEIDIAI_RELEASE_ARCHIVE_MD5 "930495f3aeb16b25dc18a2bde45924f4")
|
||||
set(KLEIDIAI_RELEASE_ARCHIVE_MD5 "05f1953428ffe8e8c7f585dc6d508887")
|
||||
|
||||
set(KLEIDIAI_FETCH_ARGS
|
||||
URL ${KLEIDIAI_DOWNLOAD_URL}
|
||||
|
||||
@@ -2105,6 +2105,10 @@ static void ggml_compute_forward(struct ggml_compute_params * params, struct ggm
|
||||
{
|
||||
ggml_compute_forward_dsv4_hc_post(params, tensor);
|
||||
} break;
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
{
|
||||
ggml_compute_forward_top_k_mask(params, tensor);
|
||||
} break;
|
||||
case GGML_OP_MAP_CUSTOM1:
|
||||
{
|
||||
ggml_compute_forward_map_custom1(params, tensor);
|
||||
@@ -2288,6 +2292,7 @@ static int ggml_get_n_tasks(struct ggml_tensor * node, int n_threads) {
|
||||
case GGML_OP_DSV4_HC_COMB:
|
||||
case GGML_OP_DSV4_HC_PRE:
|
||||
case GGML_OP_DSV4_HC_POST:
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
{
|
||||
n_tasks = n_threads;
|
||||
} break;
|
||||
|
||||
@@ -201,85 +201,117 @@ static struct kai_matmul_uker_config q4_k_matmul_config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static size_t q4_k_get_m_step() {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
return api.get_step(&config).m;
|
||||
static struct kai_matmul_uker_config default_matmul_config() {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static size_t q4_k_get_n_step() {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
return api.get_step(&config).n;
|
||||
}
|
||||
template<
|
||||
struct kai_matmul_uker_api (*GetApi)(void),
|
||||
struct kai_matmul_uker_config (*GetConfig)(void),
|
||||
size_t MR, size_t NR, size_t KR, size_t SR>
|
||||
struct matmul_uker_adapter {
|
||||
static size_t get_m_step() {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
return api.get_step(&config).m;
|
||||
}
|
||||
|
||||
template<size_t Value>
|
||||
static size_t q4_k_get_constant() {
|
||||
return Value;
|
||||
}
|
||||
static size_t get_n_step() {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
return api.get_step(&config).n;
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static size_t q4_k_get_dst_offset(size_t m_idx, size_t n_idx, size_t dst_stride) {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
const struct kai_matmul_uker_dst_dim_args index = { m_idx, n_idx };
|
||||
const struct kai_matmul_uker_dst_stride_args stride = { dst_stride };
|
||||
return api.get_dst_offset(&config, &index, &stride);
|
||||
}
|
||||
static size_t get_mr() {
|
||||
if constexpr (MR == 0) {
|
||||
return get_m_step();
|
||||
}
|
||||
return MR;
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static size_t q4_k_get_lhs_packed_offset(size_t m_idx, size_t k, size_t bl) {
|
||||
GGML_ASSERT(bl == KLEIDIAI_Q4_K_BLOCK_LENGTH);
|
||||
static size_t get_nr() {
|
||||
if constexpr (NR == 0) {
|
||||
return get_n_step();
|
||||
}
|
||||
return NR;
|
||||
}
|
||||
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
const struct kai_matmul_uker_lhs_dim_args shape = { m_idx, k };
|
||||
const struct kai_matmul_uker_lhs_dim_args index = { m_idx, 0 };
|
||||
const struct kai_matmul_uker_lhs_stride_args stride = api.get_lhs_stride(&config, &shape);
|
||||
return api.get_lhs_offset(&config, &index, &stride);
|
||||
}
|
||||
static size_t get_kr() {
|
||||
return KR;
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static size_t q4_k_get_rhs_packed_offset(size_t n_idx, size_t k, size_t bl) {
|
||||
GGML_ASSERT(bl == KLEIDIAI_Q4_K_BLOCK_LENGTH);
|
||||
static size_t get_sr() {
|
||||
return SR;
|
||||
}
|
||||
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
const struct kai_matmul_uker_rhs_dim_args shape = { n_idx, k };
|
||||
const struct kai_matmul_uker_rhs_dim_args index = { n_idx, 0 };
|
||||
const struct kai_matmul_uker_rhs_stride_args stride = api.get_rhs_stride(&config, &shape);
|
||||
return api.get_rhs_offset(&config, &index, &stride);
|
||||
}
|
||||
static size_t get_dst_offset(size_t m_idx, size_t n_idx, size_t dst_stride) {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
const struct kai_matmul_uker_dst_dim_args index = { m_idx, n_idx };
|
||||
const struct kai_matmul_uker_dst_stride_args stride = { dst_stride };
|
||||
return api.get_dst_offset(&config, &index, &stride);
|
||||
}
|
||||
|
||||
template<struct kai_matmul_uker_api (*GetApi)(void)>
|
||||
static void q4_k_run_matmul(
|
||||
size_t m, size_t n, size_t k, size_t bl,
|
||||
const void * lhs_packed, const void * rhs_packed,
|
||||
void * dst, size_t dst_stride_row, size_t dst_stride_col,
|
||||
float clamp_min, float clamp_max) {
|
||||
GGML_ASSERT(bl == KLEIDIAI_Q4_K_BLOCK_LENGTH);
|
||||
GGML_ASSERT(dst_stride_col == sizeof(float));
|
||||
static size_t get_lhs_packed_offset(size_t m_idx, size_t k, size_t bl) {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
GGML_ASSERT(bl == config.format.bl);
|
||||
const struct kai_matmul_uker_lhs_dim_args shape = { m_idx, k };
|
||||
const struct kai_matmul_uker_lhs_dim_args index = { m_idx, 0 };
|
||||
const struct kai_matmul_uker_lhs_stride_args stride = api.get_lhs_stride(&config, &shape);
|
||||
return api.get_lhs_offset(&config, &index, &stride);
|
||||
}
|
||||
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = q4_k_matmul_config();
|
||||
const struct kai_matmul_uker_lhs_dim_args lhs_shape = { m, k };
|
||||
const struct kai_matmul_uker_rhs_dim_args rhs_shape = { n, k };
|
||||
static size_t get_rhs_packed_offset(size_t n_idx, size_t k, size_t bl) {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
GGML_ASSERT(bl == config.format.bl);
|
||||
const struct kai_matmul_uker_rhs_dim_args shape = { n_idx, k };
|
||||
const struct kai_matmul_uker_rhs_dim_args index = { n_idx, 0 };
|
||||
const struct kai_matmul_uker_rhs_stride_args stride = api.get_rhs_stride(&config, &shape);
|
||||
return api.get_rhs_offset(&config, &index, &stride);
|
||||
}
|
||||
|
||||
struct kai_matmul_uker_args args = {};
|
||||
args.flags = KAI_MATMUL_UKER_FLAGS_ARGS_CLAMP;
|
||||
args.shape = { m, n, k };
|
||||
args.operand.lhs.ptr = lhs_packed;
|
||||
args.operand.lhs.stride = api.get_lhs_stride(&config, &lhs_shape);
|
||||
args.operand.rhs.ptr = rhs_packed;
|
||||
args.operand.rhs.stride = api.get_rhs_stride(&config, &rhs_shape);
|
||||
args.operand.dst.ptr = dst;
|
||||
args.operand.dst.stride.m = dst_stride_row;
|
||||
args.activation.clamp.min_ptr = &clamp_min;
|
||||
args.activation.clamp.max_ptr = &clamp_max;
|
||||
api.run(&config, &args);
|
||||
}
|
||||
static void run(
|
||||
size_t m, size_t n, size_t k, size_t bl,
|
||||
const void * lhs_packed, const void * rhs_packed,
|
||||
void * dst, size_t dst_stride_row, size_t dst_stride_col,
|
||||
float clamp_min, float clamp_max) {
|
||||
const struct kai_matmul_uker_api api = GetApi();
|
||||
const struct kai_matmul_uker_config config = GetConfig();
|
||||
GGML_ASSERT(bl == config.format.bl);
|
||||
GGML_ASSERT(dst_stride_col == sizeof(float));
|
||||
const struct kai_matmul_uker_lhs_dim_args lhs_shape = { m, k };
|
||||
const struct kai_matmul_uker_rhs_dim_args rhs_shape = { n, k };
|
||||
|
||||
struct kai_matmul_uker_args args = {};
|
||||
args.flags = KAI_MATMUL_UKER_FLAGS_ARGS_CLAMP;
|
||||
args.shape = { m, n, k };
|
||||
args.operand.lhs.ptr = lhs_packed;
|
||||
args.operand.lhs.stride = api.get_lhs_stride(&config, &lhs_shape);
|
||||
args.operand.rhs.ptr = rhs_packed;
|
||||
args.operand.rhs.stride = api.get_rhs_stride(&config, &rhs_shape);
|
||||
args.operand.dst.ptr = dst;
|
||||
args.operand.dst.stride.m = dst_stride_row;
|
||||
args.activation.clamp.min_ptr = &clamp_min;
|
||||
args.activation.clamp.max_ptr = &clamp_max;
|
||||
api.run(&config, &args);
|
||||
}
|
||||
};
|
||||
|
||||
using q4_k_gemm_adapter = matmul_uker_adapter<
|
||||
kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa,
|
||||
q4_k_matmul_config, 0, 0, 4, 2>;
|
||||
using q4_k_gemv_adapter = matmul_uker_adapter<
|
||||
kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot,
|
||||
q4_k_matmul_config, 0, 0, 4, 2>;
|
||||
// GEMM and GEMV share the RHS layout only at SVE256 (NR=8, KR=8, SR=1).
|
||||
using sve_q8_gemm_adapter = matmul_uker_adapter<
|
||||
kai_matmul_clamp_f32_qai8dxp4x8sf32_qsi8cxp4vsx8sf32bf32_16x4vs_sve_i8mm,
|
||||
default_matmul_config, 4, 0, 8, 1>;
|
||||
using sve_q8_gemv_adapter = matmul_uker_adapter<
|
||||
kai_matmul_clamp_f32_qai8dxp1x8_qsi8cxp8x8_1x8_sve_dot,
|
||||
default_matmul_config, 1, 8, 8, 1>;
|
||||
|
||||
static struct kai_matmul_pack_lhs_uker_config q4_k_lhs_pack_config() {
|
||||
struct kai_matmul_pack_lhs_uker_config config = {};
|
||||
@@ -628,7 +660,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
|
||||
/* .packed_size_ex = */ &rhs_ps_fn5<kai_get_rhs_packed_size_rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0>,
|
||||
/* .pack_func_ex = */ &rhs_pack_fn12<kai_run_rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0>,
|
||||
},
|
||||
/* .required_cpu = */ CPU_FEATURE_SVE | CPU_FEATURE_I8MM | CPU_FEATURE_DOTPROD,
|
||||
/* .required_cpu = */ CPU_FEATURE_SVE | CPU_FEATURE_SVEI8MM | CPU_FEATURE_DOTPROD,
|
||||
/* .lhs_type = */ GGML_TYPE_F32,
|
||||
/* .rhs_type = */ GGML_TYPE_Q4_0,
|
||||
/* .op_type = */ GGML_TYPE_F32,
|
||||
@@ -737,16 +769,16 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q4_k[] = {
|
||||
{
|
||||
/* SME2 GEMM */
|
||||
{
|
||||
/* .get_m_step = */ &q4_k_get_m_step<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_n_step = */ &q4_k_get_n_step<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_mr = */ &q4_k_get_m_step<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_nr = */ &q4_k_get_n_step<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_kr = */ &q4_k_get_constant<4>,
|
||||
/* .get_sr = */ &q4_k_get_constant<2>,
|
||||
/* .get_dst_offset = */ &q4_k_get_dst_offset<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_lhs_offset_ex = */ &q4_k_get_lhs_packed_offset<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_rhs_packed_offset_ex = */ &q4_k_get_rhs_packed_offset<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .run_kernel_ex = */ &q4_k_run_matmul<kai_matmul_clamp_f32_f16p4vsx2_qai4c32p16vsx4s1s0sf16_4vsx16vs_sme2_mopa>,
|
||||
/* .get_m_step = */ q4_k_gemm_adapter::get_m_step,
|
||||
/* .get_n_step = */ q4_k_gemm_adapter::get_n_step,
|
||||
/* .get_mr = */ q4_k_gemm_adapter::get_mr,
|
||||
/* .get_nr = */ q4_k_gemm_adapter::get_nr,
|
||||
/* .get_kr = */ q4_k_gemm_adapter::get_kr,
|
||||
/* .get_sr = */ q4_k_gemm_adapter::get_sr,
|
||||
/* .get_dst_offset = */ q4_k_gemm_adapter::get_dst_offset,
|
||||
/* .get_lhs_offset_ex = */ q4_k_gemm_adapter::get_lhs_packed_offset,
|
||||
/* .get_rhs_packed_offset_ex = */ q4_k_gemm_adapter::get_rhs_packed_offset,
|
||||
/* .run_kernel_ex = */ q4_k_gemm_adapter::run,
|
||||
},
|
||||
{
|
||||
/* .get_offset = */ kai_get_lhs_offset_lhs_pack_f16pmrx2_f32_neon,
|
||||
@@ -756,16 +788,16 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q4_k[] = {
|
||||
},
|
||||
/* SME2 GEMV */
|
||||
{
|
||||
/* .get_m_step = */ &q4_k_get_m_step<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_n_step = */ &q4_k_get_n_step<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_mr = */ &q4_k_get_m_step<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_nr = */ &q4_k_get_n_step<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_kr = */ &q4_k_get_constant<4>,
|
||||
/* .get_sr = */ &q4_k_get_constant<2>,
|
||||
/* .get_dst_offset = */ &q4_k_get_dst_offset<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_lhs_offset_ex = */ &q4_k_get_lhs_packed_offset<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_rhs_packed_offset_ex = */ &q4_k_get_rhs_packed_offset<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .run_kernel_ex = */ &q4_k_run_matmul<kai_matmul_clamp_f32_qsi8d32p1x4_qai4c32p16vsx4s1s0sf16_1x16vs_sme2_dot>,
|
||||
/* .get_m_step = */ q4_k_gemv_adapter::get_m_step,
|
||||
/* .get_n_step = */ q4_k_gemv_adapter::get_n_step,
|
||||
/* .get_mr = */ q4_k_gemv_adapter::get_mr,
|
||||
/* .get_nr = */ q4_k_gemv_adapter::get_nr,
|
||||
/* .get_kr = */ q4_k_gemv_adapter::get_kr,
|
||||
/* .get_sr = */ q4_k_gemv_adapter::get_sr,
|
||||
/* .get_dst_offset = */ q4_k_gemv_adapter::get_dst_offset,
|
||||
/* .get_lhs_offset_ex = */ q4_k_gemv_adapter::get_lhs_packed_offset,
|
||||
/* .get_rhs_packed_offset_ex = */ q4_k_gemv_adapter::get_rhs_packed_offset,
|
||||
/* .run_kernel_ex = */ q4_k_gemv_adapter::run,
|
||||
},
|
||||
{
|
||||
/* .get_offset = */ q4_k_lhs_get_offset,
|
||||
@@ -883,6 +915,54 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
|
||||
/* .rhs_type = */ GGML_TYPE_Q8_0,
|
||||
/* .op_type = */ GGML_TYPE_F32,
|
||||
},
|
||||
{
|
||||
/* SVE I8MM GEMM */
|
||||
{
|
||||
/* .get_m_step = */ sve_q8_gemm_adapter::get_m_step,
|
||||
/* .get_n_step = */ sve_q8_gemm_adapter::get_n_step,
|
||||
/* .get_mr = */ sve_q8_gemm_adapter::get_mr,
|
||||
/* .get_nr = */ sve_q8_gemm_adapter::get_nr,
|
||||
/* .get_kr = */ sve_q8_gemm_adapter::get_kr,
|
||||
/* .get_sr = */ sve_q8_gemm_adapter::get_sr,
|
||||
/* .get_dst_offset = */ sve_q8_gemm_adapter::get_dst_offset,
|
||||
/* .get_lhs_offset_ex = */ sve_q8_gemm_adapter::get_lhs_packed_offset,
|
||||
/* .get_rhs_packed_offset_ex = */ sve_q8_gemm_adapter::get_rhs_packed_offset,
|
||||
/* .run_kernel_ex = */ sve_q8_gemm_adapter::run,
|
||||
},
|
||||
/* .gemm_lhs_info = */ {
|
||||
/* .get_offset = */ kai_get_lhs_offset_lhs_quant_pack_qai8dxp_f32,
|
||||
/* .get_packed_offset_ex = */ &lhs_offs_fn5<kai_get_lhs_packed_offset_lhs_quant_pack_qai8dxp_f32>,
|
||||
/* .packed_size_ex = */ &lhs_ps_fn5<kai_get_lhs_packed_size_lhs_quant_pack_qai8dxp_f32>,
|
||||
/* .pack_func_ex = */ &lhs_pack_float_fn9_no_bl<kai_run_lhs_quant_pack_qai8dxp_f32>,
|
||||
},
|
||||
/* SVE DOT GEMV */
|
||||
{
|
||||
/* .get_m_step = */ sve_q8_gemv_adapter::get_m_step,
|
||||
/* .get_n_step = */ sve_q8_gemv_adapter::get_n_step,
|
||||
/* .get_mr = */ sve_q8_gemv_adapter::get_mr,
|
||||
/* .get_nr = */ sve_q8_gemv_adapter::get_nr,
|
||||
/* .get_kr = */ sve_q8_gemv_adapter::get_kr,
|
||||
/* .get_sr = */ sve_q8_gemv_adapter::get_sr,
|
||||
/* .get_dst_offset = */ sve_q8_gemv_adapter::get_dst_offset,
|
||||
/* .get_lhs_offset_ex = */ sve_q8_gemv_adapter::get_lhs_packed_offset,
|
||||
/* .get_rhs_packed_offset_ex = */ sve_q8_gemv_adapter::get_rhs_packed_offset,
|
||||
/* .run_kernel_ex = */ sve_q8_gemv_adapter::run,
|
||||
},
|
||||
/* .gemv_lhs_info = */ {
|
||||
/* .get_offset = */ kai_get_lhs_offset_lhs_quant_pack_qai8dxp_f32,
|
||||
/* .get_packed_offset_ex = */ &lhs_offs_fn5<kai_get_lhs_packed_offset_lhs_quant_pack_qai8dxp_f32>,
|
||||
/* .packed_size_ex = */ &lhs_ps_fn5<kai_get_lhs_packed_size_lhs_quant_pack_qai8dxp_f32>,
|
||||
/* .pack_func_ex = */ &lhs_pack_float_fn9_no_bl<kai_run_lhs_quant_pack_qai8dxp_f32>,
|
||||
},
|
||||
/* .rhs_info = */ {
|
||||
/* .packed_size_ex = */ &rhs_ps_fn5<kai_get_rhs_packed_size_rhs_pack_nxk_qsi8cxp_qsi8cx_neon>,
|
||||
/* .pack_func_ex = */ &rhs_pack_scale_fn12<kai_run_rhs_pack_nxk_qsi8cxp_qsi8cx_neon>,
|
||||
},
|
||||
/* .required_cpu = */ CPU_FEATURE_SVE | CPU_FEATURE_SVEI8MM | CPU_FEATURE_DOTPROD,
|
||||
/* .lhs_type = */ GGML_TYPE_F32,
|
||||
/* .rhs_type = */ GGML_TYPE_Q8_0,
|
||||
/* .op_type = */ GGML_TYPE_F32,
|
||||
},
|
||||
{
|
||||
/* I8MM GEMM */
|
||||
{
|
||||
|
||||
@@ -13,7 +13,8 @@ enum cpu_feature {
|
||||
CPU_FEATURE_SVE = 4,
|
||||
CPU_FEATURE_SME = 8,
|
||||
CPU_FEATURE_SME2 = 16,
|
||||
CPU_FEATURE_FP16 = 32
|
||||
CPU_FEATURE_FP16 = 32,
|
||||
CPU_FEATURE_SVEI8MM = 64
|
||||
};
|
||||
|
||||
inline cpu_feature& operator|=(cpu_feature& lhs, cpu_feature rhs) {
|
||||
|
||||
@@ -319,8 +319,10 @@ static void init_kleidiai_context(void) {
|
||||
|
||||
size_t detected_smcus = 0;
|
||||
|
||||
// The fixed-width SVE kernels require 32-byte vectors, including the Q8 shared RHS layout.
|
||||
ctx.features = (runtime_feat.has_dotprod ? CPU_FEATURE_DOTPROD : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.has_i8mm ? CPU_FEATURE_I8MM : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.has_svei8mm ? CPU_FEATURE_SVEI8MM : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.has_fp16 ? CPU_FEATURE_FP16 : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.sve_cnt == QK8_0 ? CPU_FEATURE_SVE : CPU_FEATURE_NONE);
|
||||
|
||||
@@ -1616,6 +1618,11 @@ public:
|
||||
const size_t kr = kernel->get_kr();
|
||||
const size_t sr = kernel->get_sr();
|
||||
const ggml_type rhs_type = kernels->rhs_type;
|
||||
if (rhs_type == GGML_TYPE_Q8_0 && (kernels->required_cpu & CPU_FEATURE_SVE)) {
|
||||
GGML_ASSERT(nr == kernels->gemv.get_nr());
|
||||
GGML_ASSERT(kr == kernels->gemv.get_kr());
|
||||
GGML_ASSERT(sr == kernels->gemv.get_sr());
|
||||
}
|
||||
const size_t block_len = rhs_type == GGML_TYPE_Q8_0 ? QK8_0 :
|
||||
rhs_type == GGML_TYPE_Q4_0 ? QK4_0 :
|
||||
rhs_type == GGML_TYPE_Q4_K ? GGML_KLEIDIAI_Q4_K_BL :
|
||||
|
||||
@@ -11736,6 +11736,92 @@ void ggml_compute_forward_dsv4_hc_post(
|
||||
}
|
||||
}
|
||||
|
||||
// ggml_compute_forward_top_k_mask
|
||||
|
||||
void ggml_compute_forward_top_k_mask(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
const ggml_tensor * mask = dst->src[0];
|
||||
const ggml_tensor * top_k = dst->src[1];
|
||||
const ggml_tensor * score = dst->src[2];
|
||||
const ggml_tensor * blocks = dst->src[3];
|
||||
const ggml_tensor * extra = dst->src[4];
|
||||
|
||||
GGML_ASSERT(dst->type == mask->type);
|
||||
GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type));
|
||||
|
||||
const int64_t n_kv = mask->ne[0];
|
||||
const int64_t ne1 = dst->ne[1];
|
||||
const int64_t ne2 = dst->ne[2];
|
||||
const int64_t n_rows = ggml_nrows(dst);
|
||||
const int64_t n_top = top_k->ne[0];
|
||||
const int64_t n_blk = blocks ? blocks->ne[0] : 1;
|
||||
const int64_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
|
||||
const int64_t n_extra = extra ? extra->ne[0] : 0;
|
||||
|
||||
const size_t ts = ggml_type_size(dst->type);
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
for (int64_t ir = ith; ir < n_rows; ir += nth) {
|
||||
const int64_t i1 = ir % ne1;
|
||||
const int64_t i2 = (ir / ne1) % ne2;
|
||||
const int64_t i3 = ir / (ne1*ne2);
|
||||
|
||||
const char * mask_row = (const char *) mask->data
|
||||
+ (i1 % mask->ne[1])*mask->nb[1] + (i2 % mask->ne[2])*mask->nb[2] + (i3 % mask->ne[3])*mask->nb[3];
|
||||
char * dst_row = (char *) dst->data + i1*dst->nb[1] + i2*dst->nb[2] + i3*dst->nb[3];
|
||||
|
||||
if (dst->type == GGML_TYPE_F16) {
|
||||
const ggml_fp16_t ninf = GGML_CPU_FP32_TO_FP16(-INFINITY);
|
||||
for (int64_t c = 0; c < n_kv; ++c) {
|
||||
((ggml_fp16_t *) dst_row)[c] = ninf;
|
||||
}
|
||||
} else {
|
||||
for (int64_t c = 0; c < n_kv; ++c) {
|
||||
((float *) dst_row)[c] = -INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
auto keep = [&](int32_t c) {
|
||||
if (c >= 0 && c < n_kv) {
|
||||
memcpy(dst_row + c*ts, mask_row + c*ts, ts);
|
||||
}
|
||||
};
|
||||
|
||||
const char * top_k_row = (const char *) top_k->data + i1*top_k->nb[1] + i2*top_k->nb[2] + i3*top_k->nb[3];
|
||||
|
||||
for (int64_t j = 0; j < n_top; ++j) {
|
||||
const int32_t p = *(const int32_t *) (top_k_row + j*top_k->nb[0]);
|
||||
if (p < 0 || p >= n_pick) {
|
||||
continue;
|
||||
}
|
||||
if (score) {
|
||||
const float s = *(const float *) ((const char *) score->data
|
||||
+ p*score->nb[0] + i1*score->nb[1] + i2*score->nb[2] + i3*score->nb[3]);
|
||||
if (!(s > -INFINITY)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!blocks) {
|
||||
keep(p);
|
||||
continue;
|
||||
}
|
||||
for (int64_t k = 0; k < n_blk; ++k) {
|
||||
keep(*(const int32_t *) ((const char *) blocks->data + k*blocks->nb[0] + p*blocks->nb[1]));
|
||||
}
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
const char * extra_row = (const char *) extra->data + i1*extra->nb[1] + i2*extra->nb[2] + i3*extra->nb[3];
|
||||
for (int64_t j = 0; j < n_extra; ++j) {
|
||||
keep(*(const int32_t *) (extra_row + j*extra->nb[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ggml_compute_forward_rwkv_wkv7
|
||||
|
||||
static void ggml_compute_forward_rwkv_wkv7_f32(
|
||||
|
||||
@@ -108,6 +108,7 @@ void ggml_compute_forward_lightning_indexer(const struct ggml_compute_params * p
|
||||
void ggml_compute_forward_dsv4_hc_comb(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_dsv4_hc_pre(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_dsv4_hc_post(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_top_k_mask(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_map_custom1(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_map_custom2(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
void ggml_compute_forward_map_custom3(const struct ggml_compute_params * params, struct ggml_tensor * dst);
|
||||
|
||||
@@ -42,6 +42,7 @@ extern "C" {
|
||||
#define GGML_CPU_COMPUTE_FP32_TO_FP16(x) neon_compute_fp32_to_fp16(x)
|
||||
|
||||
#define GGML_CPU_FP16_TO_FP32(x) GGML_CPU_COMPUTE_FP16_TO_FP32(x)
|
||||
#define GGML_CPU_FP32_TO_FP16(x) GGML_CPU_COMPUTE_FP32_TO_FP16(x)
|
||||
|
||||
static inline float neon_compute_fp16_to_fp32(ggml_fp16_t h) {
|
||||
__fp16 tmp;
|
||||
@@ -150,9 +151,15 @@ extern float ggml_table_f32_ue4m3[1 << 8];
|
||||
#define GGML_CPU_FP32_TO_FP16(x) GGML_CPU_COMPUTE_FP32_TO_FP16(x)
|
||||
#endif
|
||||
|
||||
// On ARM NEON, it's quicker to directly convert x -> x instead of calling into ggml_lookup_fp16_to_fp32,
|
||||
// so we define GGML_CPU_FP16_TO_FP32 and GGML_CPU_FP32_TO_FP16 elsewhere for NEON.
|
||||
// This is also true for POWER9.
|
||||
// a branch that defines a GGML_CPU_COMPUTE_* macro must also select it, or the fallback takes over silently
|
||||
#if defined(GGML_CPU_COMPUTE_FP16_TO_FP32) && !defined(GGML_CPU_FP16_TO_FP32) && !defined(__F16C__) // see: https://github.com/ggml-org/llama.cpp/issues/30342
|
||||
#error "a branch defined GGML_CPU_COMPUTE_FP16_TO_FP32 without selecting it as GGML_CPU_FP16_TO_FP32"
|
||||
#endif
|
||||
#if defined(GGML_CPU_COMPUTE_FP32_TO_FP16) && !defined(GGML_CPU_FP32_TO_FP16)
|
||||
#error "a branch defined GGML_CPU_COMPUTE_FP32_TO_FP16 without selecting it as GGML_CPU_FP32_TO_FP16"
|
||||
#endif
|
||||
|
||||
// fall back to the lookup table / the portable software conversion for whichever direction no branch selected
|
||||
#if !defined(GGML_CPU_FP16_TO_FP32)
|
||||
inline static float ggml_lookup_fp16_to_fp32(ggml_fp16_t f) {
|
||||
uint16_t s;
|
||||
|
||||
@@ -460,6 +460,92 @@ void ggml_vec_swiglu_f32(const int n, float * y, const float * x, const float *
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GGML_GELU_QUICK_FP16
|
||||
// computed in f32 without the fp16 table, which loses precision on f32 input
|
||||
void ggml_vec_gelu_quick_f32(const int n, float * y, const float * x) {
|
||||
int i = 0;
|
||||
#if defined(__AVX512F__) && defined(__AVX512DQ__)
|
||||
for (; i + 15 < n; i += 16) {
|
||||
_mm512_storeu_ps(y + i, ggml_v_gelu_quick(_mm512_loadu_ps(x + i)));
|
||||
}
|
||||
#elif defined(__AVX2__) && defined(__FMA__)
|
||||
for (; i + 7 < n; i += 8) {
|
||||
_mm256_storeu_ps(y + i, ggml_v_gelu_quick(_mm256_loadu_ps(x + i)));
|
||||
}
|
||||
#elif defined(__SSE2__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
_mm_storeu_ps(y + i, ggml_v_gelu_quick(_mm_loadu_ps(x + i)));
|
||||
}
|
||||
#elif defined(__ARM_FEATURE_SVE) && defined(__aarch64__)
|
||||
const int vlen = svcntw();
|
||||
for (; i < n; i += vlen) {
|
||||
const svbool_t pg = svwhilelt_b32_s32(i, n);
|
||||
svst1_f32(pg, y + i, ggml_v_gelu_quick(pg, svld1_f32(pg, x + i)));
|
||||
}
|
||||
#elif defined(__ARM_NEON) && defined(__aarch64__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
vst1q_f32(y + i, ggml_v_gelu_quick(vld1q_f32(x + i)));
|
||||
}
|
||||
#elif defined(__VXE__) || defined(__VXE2__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
vec_xst(ggml_v_gelu_quick(vec_xl(0, x + i)), 0, y + i);
|
||||
}
|
||||
#elif defined(__riscv_v_intrinsic)
|
||||
for (int vl; i < n; i += vl) {
|
||||
vl = __riscv_vsetvl_e32m2(n - i);
|
||||
vfloat32m2_t vx = __riscv_vle32_v_f32m2(&x[i], vl);
|
||||
vfloat32m2_t vy = ggml_v_gelu_quick_m2(vx, vl);
|
||||
__riscv_vse32_v_f32m2(&y[i], vy, vl);
|
||||
}
|
||||
#endif
|
||||
for (; i < n; ++i) {
|
||||
y[i] = ggml_gelu_quick_f32(x[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ggml_vec_geglu_quick_f32(const int n, float * y, const float * x, const float * g) {
|
||||
int i = 0;
|
||||
#if defined(__AVX512F__) && defined(__AVX512DQ__)
|
||||
for (; i + 15 < n; i += 16) {
|
||||
_mm512_storeu_ps(y + i, _mm512_mul_ps(ggml_v_gelu_quick(_mm512_loadu_ps(x + i)), _mm512_loadu_ps(g + i)));
|
||||
}
|
||||
#elif defined(__AVX2__) && defined(__FMA__)
|
||||
for (; i + 7 < n; i += 8) {
|
||||
_mm256_storeu_ps(y + i, _mm256_mul_ps(ggml_v_gelu_quick(_mm256_loadu_ps(x + i)), _mm256_loadu_ps(g + i)));
|
||||
}
|
||||
#elif defined(__SSE2__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
_mm_storeu_ps(y + i, _mm_mul_ps(ggml_v_gelu_quick(_mm_loadu_ps(x + i)), _mm_loadu_ps(g + i)));
|
||||
}
|
||||
#elif defined(__ARM_FEATURE_SVE) && defined(__aarch64__)
|
||||
const int vlen = svcntw();
|
||||
for (; i < n; i += vlen) {
|
||||
const svbool_t pg = svwhilelt_b32_s32(i, n);
|
||||
svst1_f32(pg, y + i, svmul_f32_x(pg, ggml_v_gelu_quick(pg, svld1_f32(pg, x + i)), svld1_f32(pg, g + i)));
|
||||
}
|
||||
#elif defined(__ARM_NEON) && defined(__aarch64__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
vst1q_f32(y + i, vmulq_f32(ggml_v_gelu_quick(vld1q_f32(x + i)), vld1q_f32(g + i)));
|
||||
}
|
||||
#elif defined(__VXE__) || defined(__VXE2__)
|
||||
for (; i + 3 < n; i += 4) {
|
||||
vec_xst(vec_mul(ggml_v_gelu_quick(vec_xl(0, x + i)), vec_xl(0, g + i)), 0, y + i);
|
||||
}
|
||||
#elif defined(__riscv_v_intrinsic)
|
||||
for (int vl; i < n; i += vl) {
|
||||
vl = __riscv_vsetvl_e32m2(n - i);
|
||||
vfloat32m2_t vx = __riscv_vle32_v_f32m2(&x[i], vl);
|
||||
vfloat32m2_t vg = __riscv_vle32_v_f32m2(&g[i], vl);
|
||||
vfloat32m2_t vy = __riscv_vfmul_vv_f32m2(ggml_v_gelu_quick_m2(vx, vl), vg, vl);
|
||||
__riscv_vse32_v_f32m2(&y[i], vy, vl);
|
||||
}
|
||||
#endif
|
||||
for (; i < n; ++i) {
|
||||
y[i] = ggml_gelu_quick_f32(x[i]) * g[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ggml_float ggml_vec_cvar_f32(const int n, float * y, const float * x, const float mean) {
|
||||
int i = 0;
|
||||
ggml_float sum = 0;
|
||||
|
||||
+50
-11
@@ -44,7 +44,6 @@ inline static ggml_float ggml_sve_sum_f32x2(svfloat32_t sum_lo, svfloat32_t sum_
|
||||
#endif
|
||||
|
||||
#define GGML_GELU_FP16
|
||||
#define GGML_GELU_QUICK_FP16
|
||||
|
||||
#define GGML_SOFT_MAX_UNROLL 4
|
||||
#define GGML_VEC_DOT_UNROLL 2
|
||||
@@ -1054,11 +1053,7 @@ inline static void ggml_vec_gelu_quick_f32(const int n, float * y, const float *
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline static void ggml_vec_gelu_quick_f32(const int n, float * y, const float * x) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
y[i] = ggml_gelu_quick_f32(x[i]);
|
||||
}
|
||||
}
|
||||
void ggml_vec_gelu_quick_f32(const int n, float * y, const float * x);
|
||||
#endif
|
||||
|
||||
// Sigmoid Linear Unit (SiLU) function
|
||||
@@ -1143,6 +1138,13 @@ inline static svfloat32_t ggml_v_silu(svbool_t pg, svfloat32_t x) {
|
||||
return svdiv_f32_x(pg, x, one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static svfloat32_t ggml_v_gelu_quick(svbool_t pg, svfloat32_t x) {
|
||||
const svfloat32_t one = svdup_n_f32_x(pg, 1.0f);
|
||||
const svfloat32_t exp_cx = ggml_v_expf(pg, svmul_n_f32_x(pg, x, GELU_QUICK_COEF));
|
||||
return svdiv_f32_x(pg, x, svadd_f32_x(pg, one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__ARM_NEON) && defined(__aarch64__)
|
||||
|
||||
// adapted from arm limited optimized routine
|
||||
@@ -1182,6 +1184,13 @@ inline static float32x4_t ggml_v_silu(float32x4_t x) {
|
||||
return vdivq_f32(x, one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static float32x4_t ggml_v_gelu_quick(float32x4_t x) {
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t exp_cx = ggml_v_expf(vmulq_n_f32(x, GELU_QUICK_COEF));
|
||||
return vdivq_f32(x, vaddq_f32(one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__AVX512F__) && defined(__AVX512DQ__)
|
||||
|
||||
// adapted from arm limited optimized routine
|
||||
@@ -1225,6 +1234,13 @@ inline static __m512 ggml_v_silu(__m512 x) {
|
||||
return _mm512_div_ps(x, one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static __m512 ggml_v_gelu_quick(__m512 x) {
|
||||
const __m512 one = _mm512_set1_ps(1);
|
||||
const __m512 exp_cx = ggml_v_expf(_mm512_mul_ps(x, _mm512_set1_ps(GELU_QUICK_COEF)));
|
||||
return _mm512_div_ps(x, _mm512_add_ps(one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__AVX2__) && defined(__FMA__)
|
||||
|
||||
// adapted from arm limited optimized routine
|
||||
@@ -1280,6 +1296,13 @@ inline static __m256 ggml_v_silu(__m256 x) {
|
||||
return _mm256_div_ps(x, one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static __m256 ggml_v_gelu_quick(__m256 x) {
|
||||
const __m256 one = _mm256_set1_ps(1);
|
||||
const __m256 exp_cx = ggml_v_expf(_mm256_mul_ps(x, _mm256_set1_ps(GELU_QUICK_COEF)));
|
||||
return _mm256_div_ps(x, _mm256_add_ps(one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__SSE2__) // __AVX2__ / __ARM_NEON
|
||||
|
||||
#if defined(__FMA__)
|
||||
@@ -1334,6 +1357,13 @@ inline static __m128 ggml_v_silu(__m128 x) {
|
||||
return _mm_div_ps(x, one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static __m128 ggml_v_gelu_quick(__m128 x) {
|
||||
const __m128 one = _mm_set1_ps(1);
|
||||
const __m128 exp_cx = ggml_v_expf(_mm_mul_ps(x, _mm_set1_ps(GELU_QUICK_COEF)));
|
||||
return _mm_div_ps(x, _mm_add_ps(one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__VXE__) || defined(__VXE2__)
|
||||
|
||||
// adapted from arm limited optimized routine
|
||||
@@ -1374,6 +1404,13 @@ inline static float32x4_t ggml_v_silu(float32x4_t v_x) {
|
||||
return vec_div(v_x, v_one_plus_exp_neg_x);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static float32x4_t ggml_v_gelu_quick(float32x4_t x) {
|
||||
const float32x4_t one = vec_splats(1.0f);
|
||||
const float32x4_t exp_cx = ggml_v_expf(vec_mul(x, vec_splats(GELU_QUICK_COEF)));
|
||||
return vec_div(x, vec_add(one, exp_cx));
|
||||
}
|
||||
|
||||
#elif defined(__riscv_v_intrinsic)
|
||||
|
||||
// adapted from arm limited optimized routine
|
||||
@@ -1426,6 +1463,12 @@ inline static vfloat32m2_t ggml_v_silu_m2(vfloat32m2_t x, int vl) {
|
||||
return __riscv_vfdiv_vv_f32m2(x, one_plus_exp_neg_x, vl);
|
||||
}
|
||||
|
||||
// gelu_quick(x) = x / (1 + exp(-1.702 x)); x stays the numerator so it cannot overflow
|
||||
inline static vfloat32m2_t ggml_v_gelu_quick_m2(vfloat32m2_t x, int vl) {
|
||||
const vfloat32m2_t exp_cx = ggml_v_expf_m2(__riscv_vfmul_vf_f32m2(x, GELU_QUICK_COEF, vl), vl);
|
||||
return __riscv_vfdiv_vv_f32m2(x, __riscv_vfadd_vf_f32m2(exp_cx, 1.0f, vl), vl);
|
||||
}
|
||||
|
||||
#endif // __ARM_NEON / __AVX2__ / __SSE2__ / __VXE__ / __riscv_v_intrinsic
|
||||
|
||||
inline static void ggml_vec_silu_f16(const int n, ggml_fp16_t * y, const ggml_fp16_t * x) {
|
||||
@@ -1571,11 +1614,7 @@ inline static void ggml_vec_geglu_quick_f32(const int n, float * y, const float
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline static void ggml_vec_geglu_quick_f32(const int n, float * y, const float * x, const float * g) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
y[i] = ggml_gelu_quick_f32(x[i]) * g[i];
|
||||
}
|
||||
}
|
||||
void ggml_vec_geglu_quick_f32(const int n, float * y, const float * x, const float * g);
|
||||
#endif
|
||||
|
||||
inline static void ggml_vec_geglu_quick_f16(const int n, ggml_fp16_t * y, const ggml_fp16_t * x, const ggml_fp16_t * g) {
|
||||
|
||||
@@ -1551,11 +1551,11 @@ struct ggml_backend_cuda_context {
|
||||
// pool
|
||||
std::unique_ptr<ggml_cuda_pool> pools[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS];
|
||||
|
||||
static std::unique_ptr<ggml_cuda_pool> new_pool_for_device(int device, int stream_no);
|
||||
static std::unique_ptr<ggml_cuda_pool> new_pool_for_device(ggml_backend_cuda_context & ctx, int device, int stream_no);
|
||||
|
||||
ggml_cuda_pool & pool(int device) {
|
||||
if (pools[device][curr_stream_no] == nullptr) {
|
||||
pools[device][curr_stream_no] = new_pool_for_device(device, curr_stream_no);
|
||||
pools[device][curr_stream_no] = new_pool_for_device(*this, device, curr_stream_no);
|
||||
}
|
||||
return *pools[device][curr_stream_no];
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "ggml-cuda/cumsum.cuh"
|
||||
#include "ggml-cuda/fill.cuh"
|
||||
#include "ggml-cuda/lightning-indexer.cuh"
|
||||
#include "ggml-cuda/top-k-mask.cuh"
|
||||
#include "ggml.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -457,6 +458,7 @@ const ggml_cuda_device_info & ggml_cuda_info() {
|
||||
struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
static const int MAX_BUFFERS = 256;
|
||||
|
||||
ggml_backend_cuda_context & ctx;
|
||||
int device;
|
||||
struct ggml_cuda_buffer {
|
||||
void * ptr = nullptr;
|
||||
@@ -466,7 +468,8 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
ggml_cuda_buffer buffer_pool[MAX_BUFFERS] = {};
|
||||
size_t pool_size = 0;
|
||||
|
||||
explicit ggml_cuda_pool_leg(int device) :
|
||||
ggml_cuda_pool_leg(ggml_backend_cuda_context & ctx, int device) :
|
||||
ctx(ctx),
|
||||
device(device) {
|
||||
}
|
||||
|
||||
@@ -488,6 +491,20 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
}
|
||||
}
|
||||
|
||||
// captured CUDA graphs can still use freed buffers, so they must be captured again
|
||||
void invalidate_cuda_graphs() {
|
||||
#ifdef USE_CUDA_GRAPH
|
||||
int n_captured = 0;
|
||||
for (const auto & [key, graph] : ctx.cuda_graphs) {
|
||||
n_captured += graph->instance != nullptr;
|
||||
}
|
||||
if (n_captured > 0) {
|
||||
GGML_LOG_DEBUG(GGML_CUDA_NAME " pool[%d]: memory freed, %d captured CUDA graphs must be captured again\n", device, n_captured);
|
||||
}
|
||||
ctx.cuda_graphs.clear();
|
||||
#endif // USE_CUDA_GRAPH
|
||||
}
|
||||
|
||||
void * alloc(size_t size, size_t * actual_size) override {
|
||||
#ifdef DEBUG_CUDA_MALLOC
|
||||
int nnz = 0;
|
||||
@@ -538,6 +555,7 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
device, look_ahead_size/1024.0/1024.0, cached_bytes/1024.0/1024.0);
|
||||
CUDA_CHECK(cudaDeviceSynchronize());
|
||||
clear_pool();
|
||||
invalidate_cuda_graphs();
|
||||
err = ggml_cuda_device_malloc(&ptr, look_ahead_size, device);
|
||||
if (err == cudaSuccess) {
|
||||
GGML_LOG_DEBUG(GGML_CUDA_NAME " pool[%d]: retry succeeded\n", device);
|
||||
@@ -566,6 +584,7 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
ggml_cuda_set_device(device);
|
||||
CUDA_CHECK(cudaFree(ptr));
|
||||
pool_size -= size;
|
||||
invalidate_cuda_graphs();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -720,14 +739,15 @@ struct ggml_cuda_pool_vmm : public ggml_cuda_pool {
|
||||
};
|
||||
#endif // defined(GGML_USE_VMM)
|
||||
|
||||
std::unique_ptr<ggml_cuda_pool> ggml_backend_cuda_context::new_pool_for_device(int device,
|
||||
[[maybe_unused]] int stream_no) {
|
||||
std::unique_ptr<ggml_cuda_pool> ggml_backend_cuda_context::new_pool_for_device(ggml_backend_cuda_context & ctx,
|
||||
int device,
|
||||
[[maybe_unused]] int stream_no) {
|
||||
#if defined(GGML_USE_VMM)
|
||||
if (ggml_cuda_info().devices[device].vmm) {
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_vmm(device));
|
||||
}
|
||||
#endif // defined(GGML_USE_VMM)
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_leg(device));
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_leg(ctx, device));
|
||||
}
|
||||
|
||||
// destroying a cuBLAS handle while a graph is being captured in a different thread can result in a CUDA error
|
||||
@@ -2014,6 +2034,8 @@ static bool ggml_cuda_mul_mat_id_needs_sync(const ggml_tensor * dst, const int c
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
const int warp_size = ggml_cuda_info().devices[ggml_cuda_get_device()].warp_size;
|
||||
|
||||
if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
|
||||
return true;
|
||||
}
|
||||
@@ -2032,7 +2054,7 @@ static bool ggml_cuda_mul_mat_id_needs_sync(const ggml_tensor * dst, const int c
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, WARP_SIZE, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, warp_size, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2050,6 +2072,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int cc = ggml_cuda_info().devices[ggml_cuda_get_device()].cc;
|
||||
const int warp_size = ggml_cuda_info().devices[ggml_cuda_get_device()].warp_size;
|
||||
|
||||
// [TAG_MUL_MAT_ID_CUDA_GRAPHS]
|
||||
if (src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
@@ -2074,7 +2097,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, WARP_SIZE, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, warp_size, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
ggml_cuda_mul_mat_f(ctx, src0, src1, ids, dst);
|
||||
return;
|
||||
}
|
||||
@@ -2548,6 +2571,9 @@ static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct gg
|
||||
case GGML_OP_LIGHTNING_INDEXER:
|
||||
ggml_cuda_lightning_indexer(ctx, dst);
|
||||
break;
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
ggml_cuda_op_top_k_mask(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -5981,6 +6007,8 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
return true;
|
||||
case GGML_OP_LIGHTNING_INDEXER:
|
||||
return ggml_cuda_lightning_indexer_supported(dev_ctx->device, op);
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
return op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32;
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -151,9 +151,10 @@ static void ssm_conv_f32_cuda(const float * src0, const float * src1, const floa
|
||||
case 3: launch_kernel(std::integral_constant<int, 3 >{}); break;
|
||||
case 4: launch_kernel(std::integral_constant<int, 4 >{}); break;
|
||||
case 5: launch_kernel(std::integral_constant<int, 5 >{}); break;
|
||||
case 7: launch_kernel(std::integral_constant<int, 7 >{}); break;
|
||||
case 9: launch_kernel(std::integral_constant<int, 9 >{}); break;
|
||||
case 15: launch_kernel(std::integral_constant<int, 15>{}); break;
|
||||
default: GGML_ABORT("Only support kernel sizes 3, 4, 5, 9, 15 right now.");
|
||||
default: GGML_ABORT("Only support kernel sizes 3, 4, 5, 7, 9, 15 right now.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "top-k-mask.cuh"
|
||||
#include "convert.cuh"
|
||||
|
||||
#define CUDA_TOP_K_MASK_BLOCK_SIZE 512
|
||||
|
||||
// one block per row: fill the row with -inf, then copy the mask cells of the live picks and the extra cells
|
||||
template <typename T>
|
||||
static __global__ void top_k_mask_kernel(
|
||||
const T * mask, const int32_t * top_k, const float * score, const int32_t * blocks, const int32_t * extra, T * dst,
|
||||
const int64_t n_kv, const int64_t ne1, const int64_t ne2,
|
||||
const int64_t m1, const int64_t m2, const int64_t m3,
|
||||
const int64_t n_top, const int64_t n_pick, const int64_t n_blk, const int64_t n_extra,
|
||||
const int64_t sm1, const int64_t sm2, const int64_t sm3,
|
||||
const int64_t sd1, const int64_t sd2, const int64_t sd3,
|
||||
const int64_t sk0, const int64_t sk1, const int64_t sk2, const int64_t sk3,
|
||||
const int64_t ss0, const int64_t ss1, const int64_t ss2, const int64_t ss3,
|
||||
const int64_t sb0, const int64_t sb1,
|
||||
const int64_t se0, const int64_t se1, const int64_t se2, const int64_t se3,
|
||||
const bool vec) {
|
||||
ggml_cuda_pdl_lc();
|
||||
|
||||
const int64_t ir = blockIdx.x;
|
||||
const int64_t i1 = ir % ne1;
|
||||
const int64_t i2 = (ir / ne1) % ne2;
|
||||
const int64_t i3 = ir / (ne1*ne2);
|
||||
|
||||
const T * mask_row = mask + (i1 % m1)*sm1 + (i2 % m2)*sm2 + (i3 % m3)*sm3;
|
||||
T * dst_row = dst + i1*sd1 + i2*sd2 + i3*sd3;
|
||||
const int32_t * top_k_row = top_k + i1*sk1 + i2*sk2 + i3*sk3;
|
||||
|
||||
ggml_cuda_pdl_sync();
|
||||
|
||||
const T ninf = ggml_cuda_cast<T>(-INFINITY);
|
||||
if (vec) {
|
||||
constexpr int nv = sizeof(int4)/sizeof(T);
|
||||
T fill[nv];
|
||||
#pragma unroll
|
||||
for (int j = 0; j < nv; ++j) {
|
||||
fill[j] = ninf;
|
||||
}
|
||||
const int4 fill4 = *(const int4 *) fill;
|
||||
for (int64_t i = threadIdx.x; i < n_kv/nv; i += blockDim.x) {
|
||||
((int4 *) dst_row)[i] = fill4;
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = threadIdx.x; i < n_kv; i += blockDim.x) {
|
||||
dst_row[i] = ninf;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// one thread per pick or extra cell; cells kept twice get the same value
|
||||
for (int64_t i = threadIdx.x; i < n_top + n_extra; i += blockDim.x) {
|
||||
if (i >= n_top) {
|
||||
const int32_t c = extra[(i - n_top)*se0 + i1*se1 + i2*se2 + i3*se3];
|
||||
if (c >= 0 && c < n_kv) {
|
||||
dst_row[c] = mask_row[c];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t p = top_k_row[i*sk0];
|
||||
if (p < 0 || p >= n_pick) {
|
||||
continue;
|
||||
}
|
||||
if (score && !(score[p*ss0 + i1*ss1 + i2*ss2 + i3*ss3] > -INFINITY)) {
|
||||
continue;
|
||||
}
|
||||
if (!blocks) {
|
||||
if (p < n_kv) {
|
||||
dst_row[p] = mask_row[p];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (int64_t k = 0; k < n_blk; ++k) {
|
||||
const int32_t c = blocks[k*sb0 + p*sb1];
|
||||
if (c >= 0 && c < n_kv) {
|
||||
dst_row[c] = mask_row[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void top_k_mask_cuda(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * mask = dst->src[0];
|
||||
const ggml_tensor * top_k = dst->src[1];
|
||||
const ggml_tensor * score = dst->src[2];
|
||||
const ggml_tensor * blocks = dst->src[3];
|
||||
const ggml_tensor * extra = dst->src[4];
|
||||
|
||||
const int64_t n_kv = mask->ne[0];
|
||||
|
||||
GGML_ASSERT(dst->nb[0] == sizeof(T) && mask->nb[0] == sizeof(T));
|
||||
GGML_ASSERT(dst->nb[1] % sizeof(T) == 0 && mask->nb[1] % sizeof(T) == 0);
|
||||
|
||||
// 16-byte stores need every dst row aligned
|
||||
const bool vec = (n_kv*sizeof(T)) % sizeof(int4) == 0 &&
|
||||
dst->nb[1] % sizeof(int4) == 0 && dst->nb[2] % sizeof(int4) == 0 && dst->nb[3] % sizeof(int4) == 0 &&
|
||||
(uintptr_t) dst->data % sizeof(int4) == 0;
|
||||
|
||||
const int64_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
|
||||
|
||||
const size_t si = sizeof(int32_t);
|
||||
const size_t sf = sizeof(float);
|
||||
|
||||
const dim3 block_dims(CUDA_TOP_K_MASK_BLOCK_SIZE, 1, 1);
|
||||
const dim3 grid_dims(ggml_nrows(dst), 1, 1);
|
||||
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, ctx.stream());
|
||||
|
||||
ggml_cuda_kernel_launch(top_k_mask_kernel<T>, launch_params,
|
||||
(const T *) mask->data, (const int32_t *) top_k->data,
|
||||
score ? (const float *) score->data : nullptr,
|
||||
blocks ? (const int32_t *) blocks->data : nullptr,
|
||||
extra ? (const int32_t *) extra->data : nullptr,
|
||||
(T *) dst->data,
|
||||
n_kv, dst->ne[1], dst->ne[2],
|
||||
mask->ne[1], mask->ne[2], mask->ne[3],
|
||||
top_k->ne[0], n_pick, blocks ? blocks->ne[0] : 1, extra ? extra->ne[0] : 0,
|
||||
mask->nb[1]/sizeof(T), mask->nb[2]/sizeof(T), mask->nb[3]/sizeof(T),
|
||||
dst->nb[1]/sizeof(T), dst->nb[2]/sizeof(T), dst->nb[3]/sizeof(T),
|
||||
top_k->nb[0]/si, top_k->nb[1]/si, top_k->nb[2]/si, top_k->nb[3]/si,
|
||||
score ? score->nb[0]/sf : 0, score ? score->nb[1]/sf : 0, score ? score->nb[2]/sf : 0, score ? score->nb[3]/sf : 0,
|
||||
blocks ? blocks->nb[0]/si : 0, blocks ? blocks->nb[1]/si : 0,
|
||||
extra ? extra->nb[0]/si : 0, extra ? extra->nb[1]/si : 0, extra ? extra->nb[2]/si : 0, extra ? extra->nb[3]/si : 0,
|
||||
vec);
|
||||
}
|
||||
|
||||
void ggml_cuda_op_top_k_mask(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
switch (dst->type) {
|
||||
case GGML_TYPE_F16:
|
||||
top_k_mask_cuda<half>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_F32:
|
||||
top_k_mask_cuda<float>(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
GGML_ABORT("unsupported type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "common.cuh"
|
||||
|
||||
void ggml_cuda_op_top_k_mask(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
||||
@@ -22,6 +22,10 @@
|
||||
#define HWCAP2_I8MM (1ULL << 13)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_SVEI8MM)
|
||||
#define HWCAP2_SVEI8MM (1ULL << 9)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_ASIMDDP)
|
||||
#define HWCAP_ASIMDDP (1 << 20)
|
||||
#endif
|
||||
@@ -63,6 +67,10 @@
|
||||
#define PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE 47
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE 57
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 66
|
||||
#endif
|
||||
@@ -87,6 +95,7 @@ typedef struct ggml_feats_arch64_runtime {
|
||||
bool has_sve;
|
||||
bool has_sve2;
|
||||
bool has_i8mm;
|
||||
bool has_svei8mm;
|
||||
bool has_sme;
|
||||
bool has_sme2;
|
||||
int sve_cnt;
|
||||
@@ -104,6 +113,7 @@ static inline ggml_feats_arch64_runtime_t ggml_feats_get_arch64_runtime(void) {
|
||||
runtime_feat.has_sve = !!(hwcap & HWCAP_SVE);
|
||||
runtime_feat.has_sve2 = !!(hwcap2 & HWCAP2_SVE2);
|
||||
runtime_feat.has_i8mm = !!(hwcap2 & HWCAP2_I8MM);
|
||||
runtime_feat.has_svei8mm = !!(hwcap2 & HWCAP2_SVEI8MM);
|
||||
runtime_feat.has_sme = !!(hwcap2 & HWCAP2_SME);
|
||||
runtime_feat.has_sme2 = !!(hwcap2 & HWCAP2_SME2);
|
||||
|
||||
@@ -153,6 +163,7 @@ static inline ggml_feats_arch64_runtime_t ggml_feats_get_arch64_runtime(void) {
|
||||
runtime_feat.has_sve = IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sve2 = IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_i8mm = IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_svei8mm = IsProcessorFeaturePresent(PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sme = IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sme2 = IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <thread>
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
# include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// --- 4-bit block-32 nibble helpers (shared by Q4_0, IQ4_NL, Q4_1, MXFP4) ---
|
||||
|
||||
@@ -43,8 +48,309 @@ static inline void transpose_tile_to_nibbles(uint8_t tile_quants[32][32], const
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
static void repack_neon_transpose8x8(const uint8x8_t rows[8], uint8x8_t columns[8]) {
|
||||
const uint8x8x2_t t0 = vtrn_u8(rows[0], rows[1]);
|
||||
const uint8x8x2_t t1 = vtrn_u8(rows[2], rows[3]);
|
||||
const uint8x8x2_t t2 = vtrn_u8(rows[4], rows[5]);
|
||||
const uint8x8x2_t t3 = vtrn_u8(rows[6], rows[7]);
|
||||
const uint16x4x2_t t4 = vtrn_u16(vreinterpret_u16_u8(t0.val[0]), vreinterpret_u16_u8(t1.val[0]));
|
||||
const uint16x4x2_t t5 = vtrn_u16(vreinterpret_u16_u8(t0.val[1]), vreinterpret_u16_u8(t1.val[1]));
|
||||
const uint16x4x2_t t6 = vtrn_u16(vreinterpret_u16_u8(t2.val[0]), vreinterpret_u16_u8(t3.val[0]));
|
||||
const uint16x4x2_t t7 = vtrn_u16(vreinterpret_u16_u8(t2.val[1]), vreinterpret_u16_u8(t3.val[1]));
|
||||
const uint32x2x2_t t8 = vtrn_u32(vreinterpret_u32_u16(t4.val[0]), vreinterpret_u32_u16(t6.val[0]));
|
||||
const uint32x2x2_t t9 = vtrn_u32(vreinterpret_u32_u16(t4.val[1]), vreinterpret_u32_u16(t6.val[1]));
|
||||
const uint32x2x2_t ta = vtrn_u32(vreinterpret_u32_u16(t5.val[0]), vreinterpret_u32_u16(t7.val[0]));
|
||||
const uint32x2x2_t tb = vtrn_u32(vreinterpret_u32_u16(t5.val[1]), vreinterpret_u32_u16(t7.val[1]));
|
||||
columns[0] = vreinterpret_u8_u32(t8.val[0]);
|
||||
columns[1] = vreinterpret_u8_u32(ta.val[0]);
|
||||
columns[2] = vreinterpret_u8_u32(t9.val[0]);
|
||||
columns[3] = vreinterpret_u8_u32(tb.val[0]);
|
||||
columns[4] = vreinterpret_u8_u32(t8.val[1]);
|
||||
columns[5] = vreinterpret_u8_u32(ta.val[1]);
|
||||
columns[6] = vreinterpret_u8_u32(t9.val[1]);
|
||||
columns[7] = vreinterpret_u8_u32(tb.val[1]);
|
||||
}
|
||||
|
||||
static uint8x8_t repack_neon_pack_4bit_pairs(uint8x8_t first, uint8x8_t second, bool high) {
|
||||
const uint8x8x2_t separated = vuzp_u8(first, second);
|
||||
const uint8x8_t mask = vdup_n_u8(0x0F);
|
||||
uint8x8_t even = separated.val[0];
|
||||
uint8x8_t odd = separated.val[1];
|
||||
|
||||
if (high) {
|
||||
even = vshr_n_u8(even, 4);
|
||||
odd = vshr_n_u8(odd, 4);
|
||||
}
|
||||
|
||||
even = vand_u8(even, mask);
|
||||
odd = vand_u8(odd, mask);
|
||||
return vorr_u8(vshl_n_u8(odd, 4), even);
|
||||
}
|
||||
|
||||
static void repack_neon_4bit_rows(const uint8_t * qs[8], uint8_t * tile_dst, int row0) {
|
||||
uint8x8_t low_rows[8];
|
||||
uint8x8_t high_rows[8];
|
||||
uint8x8_t columns[8];
|
||||
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
const uint8x16_t packed = vld1q_u8(qs[rr]);
|
||||
const uint8x8_t first = vget_low_u8(packed);
|
||||
const uint8x8_t second = vget_high_u8(packed);
|
||||
low_rows[rr] = repack_neon_pack_4bit_pairs(first, second, false);
|
||||
high_rows[rr] = repack_neon_pack_4bit_pairs(first, second, true);
|
||||
}
|
||||
|
||||
repack_neon_transpose8x8(low_rows, columns);
|
||||
for (int cp = 0; cp < 8; ++cp) {
|
||||
vst1_u8(tile_dst + cp * 32 + row0, columns[cp]);
|
||||
}
|
||||
|
||||
repack_neon_transpose8x8(high_rows, columns);
|
||||
for (int cp = 0; cp < 8; ++cp) {
|
||||
vst1_u8(tile_dst + (cp + 8) * 32 + row0, columns[cp]);
|
||||
}
|
||||
}
|
||||
|
||||
static void repack_neon_k4_rows(const uint8_t * qs[8], uint8_t * tile_dst, int row0, bool high) {
|
||||
uint8x8_t first_rows[8];
|
||||
uint8x8_t second_rows[8];
|
||||
uint8x8_t columns[8];
|
||||
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
first_rows[rr] = repack_neon_pack_4bit_pairs(vld1_u8(qs[rr]), vld1_u8(qs[rr] + 8), high);
|
||||
second_rows[rr] = repack_neon_pack_4bit_pairs(vld1_u8(qs[rr] + 16), vld1_u8(qs[rr] + 24), high);
|
||||
}
|
||||
|
||||
repack_neon_transpose8x8(first_rows, columns);
|
||||
for (int cp = 0; cp < 8; ++cp) {
|
||||
vst1_u8(tile_dst + cp * 32 + row0, columns[cp]);
|
||||
}
|
||||
|
||||
repack_neon_transpose8x8(second_rows, columns);
|
||||
for (int cp = 0; cp < 8; ++cp) {
|
||||
vst1_u8(tile_dst + (cp + 8) * 32 + row0, columns[cp]);
|
||||
}
|
||||
}
|
||||
|
||||
static void repack_neon_q8_rows(const uint8_t * qs[8], uint8_t * tile_dst, int row0) {
|
||||
uint8x8_t rows[8];
|
||||
uint8x8_t columns[8];
|
||||
|
||||
for (int cp = 0; cp < 16; cp += 4) {
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
rows[rr] = vld1_u8(qs[rr] + 2 * cp);
|
||||
}
|
||||
repack_neon_transpose8x8(rows, columns);
|
||||
|
||||
for (int pair = 0; pair < 4; ++pair) {
|
||||
const uint8x8x2_t interleaved = vzip_u8(columns[2 * pair], columns[2 * pair + 1]);
|
||||
uint8_t * dst = tile_dst + (cp + pair) * 64 + 2 * row0;
|
||||
vst1_u8(dst, interleaved.val[0]);
|
||||
vst1_u8(dst + 8, interleaved.val[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void repack_store_u32(uint8_t * dst, uint32_t v) {
|
||||
memcpy(dst, &v, sizeof(v));
|
||||
}
|
||||
|
||||
static inline uint32x4_t repack_neon_transpose_2bit_4x4(uint32x4_t w) {
|
||||
uint32x4_t t = vandq_u32(veorq_u32(w, vshrq_n_u32(w, 6)), vdupq_n_u32(0x00CC00CCu));
|
||||
w = veorq_u32(w, veorq_u32(t, vshlq_n_u32(t, 6)));
|
||||
t = vandq_u32(veorq_u32(w, vshrq_n_u32(w, 12)), vdupq_n_u32(0x0000F0F0u));
|
||||
return veorq_u32(w, veorq_u32(t, vshlq_n_u32(t, 12)));
|
||||
}
|
||||
|
||||
static inline void repack_neon_q2_0_block(const uint8_t * qs, uint8_t * tile, int row) {
|
||||
const uint32x4_t w = repack_neon_transpose_2bit_4x4(vreinterpretq_u32_u8(vld1q_u8(qs)));
|
||||
repack_store_u32(tile + 0 * 128 + 4 * row, vgetq_lane_u32(w, 0));
|
||||
repack_store_u32(tile + 1 * 128 + 4 * row, vgetq_lane_u32(w, 1));
|
||||
repack_store_u32(tile + 2 * 128 + 4 * row, vgetq_lane_u32(w, 2));
|
||||
repack_store_u32(tile + 3 * 128 + 4 * row, vgetq_lane_u32(w, 3));
|
||||
}
|
||||
|
||||
static inline void repack_neon_q1_0_block(const uint8_t * qs, uint8_t * tile, int row) {
|
||||
uint32x4_t w = vreinterpretq_u32_u8(vld1q_u8(qs));
|
||||
uint32x4_t t = vandq_u32(veorq_u32(w, vshrq_n_u32(w, 1)), vdupq_n_u32(0x22222222u));
|
||||
w = veorq_u32(w, veorq_u32(t, vshlq_n_u32(t, 1)));
|
||||
t = vandq_u32(veorq_u32(w, vshrq_n_u32(w, 3)), vdupq_n_u32(0x0A0A0A0Au));
|
||||
w = veorq_u32(w, veorq_u32(t, vshlq_n_u32(t, 3)));
|
||||
w = repack_neon_transpose_2bit_4x4(w);
|
||||
repack_store_u32(tile + 0 * 128 + 4 * row, vgetq_lane_u32(w, 0));
|
||||
repack_store_u32(tile + 1 * 128 + 4 * row, vgetq_lane_u32(w, 1));
|
||||
repack_store_u32(tile + 2 * 128 + 4 * row, vgetq_lane_u32(w, 2));
|
||||
repack_store_u32(tile + 3 * 128 + 4 * row, vgetq_lane_u32(w, 3));
|
||||
}
|
||||
|
||||
static inline void repack_neon_pack_2bit_plane(uint8x16_t v0, uint8x16_t v1, uint8_t * dst, int row) {
|
||||
const uint32x4x2_t z = vuzpq_u32(vreinterpretq_u32_u8(v0), vreinterpretq_u32_u8(v1));
|
||||
const uint32x4_t p01 = vorrq_u32(z.val[0], vshlq_n_u32(z.val[1], 2));
|
||||
const uint32x2x2_t u = vuzp_u32(vget_low_u32(p01), vget_high_u32(p01));
|
||||
const uint32x2_t out = vorr_u32(u.val[0], vshl_n_u32(u.val[1], 4));
|
||||
repack_store_u32(dst + 0 * 128 + row * 4, vget_lane_u32(out, 0));
|
||||
repack_store_u32(dst + 1 * 128 + row * 4, vget_lane_u32(out, 1));
|
||||
}
|
||||
|
||||
static inline void repack_neon_q2_3_k_low2(const uint8_t * qs, int kt_local, uint8_t * lo_pl, int row) {
|
||||
const uint8_t * qs_sub = qs + (kt_local / 4) * 32;
|
||||
const int8x16_t shift = vdupq_n_s8(-2 * (kt_local % 4));
|
||||
const uint8x16_t mask = vdupq_n_u8(0x03);
|
||||
const uint8x16_t v0 = vandq_u8(vshlq_u8(vld1q_u8(qs_sub + 0), shift), mask);
|
||||
const uint8x16_t v1 = vandq_u8(vshlq_u8(vld1q_u8(qs_sub + 16), shift), mask);
|
||||
repack_neon_pack_2bit_plane(v0, v1, lo_pl, row);
|
||||
}
|
||||
|
||||
static inline void repack_neon_q3_k_hmask(const uint8_t * hmask, int kt_local, uint8_t * neg_pl, int row) {
|
||||
static const uint32_t mask_words[4] = { 0x01010101u, 0x02020202u, 0x04040404u, 0x08080808u };
|
||||
const uint8x16_t m0 = vreinterpretq_u8_u32(vld1q_u32(mask_words));
|
||||
const uint8x16_t m1 = vshlq_n_u8(m0, 4);
|
||||
const uint8x16_t hbit = vdupq_n_u8((uint8_t) (1u << kt_local));
|
||||
const uint8x16_t b0 = vbicq_u8(m0, vtstq_u8(vld1q_u8(hmask + 0), hbit));
|
||||
const uint8x16_t b1 = vbicq_u8(m1, vtstq_u8(vld1q_u8(hmask + 16), hbit));
|
||||
const uint32x4_t b01 = vreinterpretq_u32_u8(vorrq_u8(b0, b1));
|
||||
const uint32x2_t f0 = vorr_u32(vget_low_u32(b01), vget_high_u32(b01));
|
||||
const uint32x2_t f1 = vpadd_u32(f0, f0);
|
||||
repack_store_u32(neg_pl + row * 4, vget_lane_u32(f1, 0));
|
||||
}
|
||||
|
||||
static inline void repack_neon_q6_k(const block_q6_K * b, int kt_local, uint8_t * lo_pl, uint8_t * hi_pl, int row) {
|
||||
const int c = kt_local / 4;
|
||||
const int g_sb = kt_local % 4;
|
||||
const uint8_t * ql = b->ql + c * 64 + (g_sb & 1) * 32;
|
||||
const uint8_t * qh = b->qh + c * 32;
|
||||
const int8x16_t lshift = vdupq_n_s8((g_sb >= 2) ? -4 : 0);
|
||||
const int8x16_t hshift = vdupq_n_s8(-2 * g_sb);
|
||||
const uint8x16_t mask4 = vdupq_n_u8(0x0F);
|
||||
const uint8x16_t mask2 = vdupq_n_u8(0x03);
|
||||
|
||||
const uint8x16_t lo0 = vandq_u8(vshlq_u8(vld1q_u8(ql + 0), lshift), mask4);
|
||||
const uint8x16_t lo1 = vandq_u8(vshlq_u8(vld1q_u8(ql + 16), lshift), mask4);
|
||||
const uint32x4x2_t lz = vuzpq_u32(vreinterpretq_u32_u8(lo0), vreinterpretq_u32_u8(lo1));
|
||||
const uint32x4_t lout = vorrq_u32(lz.val[0], vshlq_n_u32(lz.val[1], 4));
|
||||
repack_store_u32(lo_pl + 0 * 128 + row * 4, vgetq_lane_u32(lout, 0));
|
||||
repack_store_u32(lo_pl + 1 * 128 + row * 4, vgetq_lane_u32(lout, 1));
|
||||
repack_store_u32(lo_pl + 2 * 128 + row * 4, vgetq_lane_u32(lout, 2));
|
||||
repack_store_u32(lo_pl + 3 * 128 + row * 4, vgetq_lane_u32(lout, 3));
|
||||
|
||||
const uint8x16_t hi0 = vandq_u8(vshlq_u8(vld1q_u8(qh + 0), hshift), mask2);
|
||||
const uint8x16_t hi1 = vandq_u8(vshlq_u8(vld1q_u8(qh + 16), hshift), mask2);
|
||||
repack_neon_pack_2bit_plane(hi0, hi1, hi_pl, row);
|
||||
}
|
||||
|
||||
static inline void repack_neon_q5_k_qh(const uint8_t * qh, uint8_t hbit, uint8_t * plane, int row) {
|
||||
static const uint8_t mask_bytes[16] = { 1, 2, 1, 2, 1, 2, 1, 2, 4, 8, 4, 8, 4, 8, 4, 8 };
|
||||
const uint8x16_t m0 = vld1q_u8(mask_bytes);
|
||||
const uint8x16_t m1 = vshlq_n_u8(m0, 4);
|
||||
const uint8x16_t vbit = vdupq_n_u8(hbit);
|
||||
const uint8x16_t b0 = vandq_u8(vtstq_u8(vld1q_u8(qh + 0), vbit), m0);
|
||||
const uint8x16_t b1 = vandq_u8(vtstq_u8(vld1q_u8(qh + 16), vbit), m1);
|
||||
const uint8x16_t b01 = vorrq_u8(b0, b1);
|
||||
const uint8x8_t fold = vorr_u8(vget_low_u8(b01), vget_high_u8(b01));
|
||||
const uint8x8_t out = vpadd_u8(fold, fold);
|
||||
plane[0 * 32 + row] = vget_lane_u8(out, 0);
|
||||
plane[1 * 32 + row] = vget_lane_u8(out, 1);
|
||||
plane[2 * 32 + row] = vget_lane_u8(out, 2);
|
||||
plane[3 * 32 + row] = vget_lane_u8(out, 3);
|
||||
}
|
||||
|
||||
static inline uint16x8_t repack_neon_load_f16x8(const uint16_t * src) {
|
||||
return vld1q_u16(src);
|
||||
}
|
||||
|
||||
#if defined(__aarch64__) || defined(__ARM_FP16_FORMAT_IEEE)
|
||||
static inline uint16x8_t repack_neon_load_f16x8(const float * src) {
|
||||
const float16x4_t lo = vcvt_f16_f32(vld1q_f32(src));
|
||||
const float16x4_t hi = vcvt_f16_f32(vld1q_f32(src + 4));
|
||||
return vreinterpretq_u16_f16(vcombine_f16(lo, hi));
|
||||
}
|
||||
|
||||
static inline uint16x8_t repack_neon_load_f16x8(const ggml_bf16_t * src) {
|
||||
const uint16x8_t bf = vld1q_u16((const uint16_t *) src);
|
||||
const float32x4_t f_lo = vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(bf), 16));
|
||||
const float32x4_t f_hi = vreinterpretq_f32_u32(vshll_n_u16(vget_high_u16(bf), 16));
|
||||
const float16x4_t lo = vcvt_f16_f32(f_lo);
|
||||
const float16x4_t hi = vcvt_f16_f32(f_hi);
|
||||
return vreinterpretq_u16_f16(vcombine_f16(lo, hi));
|
||||
}
|
||||
#else
|
||||
static inline uint16x8_t repack_neon_load_f16x8(const float * src) {
|
||||
uint16_t tmp[8];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
tmp[i] = GGML_FP32_TO_FP16(src[i]);
|
||||
}
|
||||
return vld1q_u16(tmp);
|
||||
}
|
||||
|
||||
static inline uint16x8_t repack_neon_load_f16x8(const ggml_bf16_t * src) {
|
||||
uint16_t tmp[8];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
tmp[i] = GGML_FP32_TO_FP16(GGML_BF16_TO_FP32(src[i]));
|
||||
}
|
||||
return vld1q_u16(tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename SrcT>
|
||||
static void repack_neon_f16_tile(uint16_t * tile_dst, const SrcT * src_slice, int64_t row_stride, int ct, int kt) {
|
||||
for (int row0 = 0; row0 < 32; row0 += 4) {
|
||||
const SrcT * r0 = src_slice + (int64_t) (ct * 32 + row0 + 0) * row_stride + kt * 32;
|
||||
const SrcT * r1 = src_slice + (int64_t) (ct * 32 + row0 + 1) * row_stride + kt * 32;
|
||||
const SrcT * r2 = src_slice + (int64_t) (ct * 32 + row0 + 2) * row_stride + kt * 32;
|
||||
const SrcT * r3 = src_slice + (int64_t) (ct * 32 + row0 + 3) * row_stride + kt * 32;
|
||||
for (int cp = 0; cp < 16; cp += 4) {
|
||||
const uint32x4_t v0 = vreinterpretq_u32_u16(repack_neon_load_f16x8(r0 + 2 * cp));
|
||||
const uint32x4_t v1 = vreinterpretq_u32_u16(repack_neon_load_f16x8(r1 + 2 * cp));
|
||||
const uint32x4_t v2 = vreinterpretq_u32_u16(repack_neon_load_f16x8(r2 + 2 * cp));
|
||||
const uint32x4_t v3 = vreinterpretq_u32_u16(repack_neon_load_f16x8(r3 + 2 * cp));
|
||||
|
||||
const uint32x2x2_t t0 = vtrn_u32(vget_low_u32(v0), vget_low_u32(v1));
|
||||
const uint32x2x2_t t1 = vtrn_u32(vget_high_u32(v0), vget_high_u32(v1));
|
||||
const uint32x2x2_t t2 = vtrn_u32(vget_low_u32(v2), vget_low_u32(v3));
|
||||
const uint32x2x2_t t3 = vtrn_u32(vget_high_u32(v2), vget_high_u32(v3));
|
||||
|
||||
vst1q_u16(tile_dst + (cp + 0) * 64 + row0 * 2, vreinterpretq_u16_u32(vcombine_u32(t0.val[0], t2.val[0])));
|
||||
vst1q_u16(tile_dst + (cp + 1) * 64 + row0 * 2, vreinterpretq_u16_u32(vcombine_u32(t0.val[1], t2.val[1])));
|
||||
vst1q_u16(tile_dst + (cp + 2) * 64 + row0 * 2, vreinterpretq_u16_u32(vcombine_u32(t1.val[0], t3.val[0])));
|
||||
vst1q_u16(tile_dst + (cp + 3) * 64 + row0 * 2, vreinterpretq_u16_u32(vcombine_u32(t1.val[1], t3.val[1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Generic 32x32 tile grid walkers for block-32 and float types ---
|
||||
|
||||
template <typename Fn>
|
||||
static void repack_parallel_for_ct(size_t slice_bytes, int n_col_tiles, Fn && fn) {
|
||||
int n_threads = 1;
|
||||
if (slice_bytes > 128 * 1024) {
|
||||
for (int nt : { 4, 3, 2 }) {
|
||||
if (n_col_tiles % nt == 0) {
|
||||
n_threads = nt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n_threads <= 1) {
|
||||
fn(0, n_col_tiles);
|
||||
return;
|
||||
}
|
||||
|
||||
const int chunk = n_col_tiles / n_threads;
|
||||
std::thread workers[3];
|
||||
for (int i = 0; i < n_threads - 1; ++i) {
|
||||
workers[i] = std::thread([&fn, i, chunk]() {
|
||||
fn(i * chunk, (i + 1) * chunk);
|
||||
});
|
||||
}
|
||||
fn((n_threads - 1) * chunk, n_col_tiles);
|
||||
for (int i = 0; i < n_threads - 1; ++i) {
|
||||
workers[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SrcElemT, int ELEMS_PER_KTILE, typename PackTileFn>
|
||||
static void repack_to_tiled_grid_32(ggml_tensor * t, const void * data, size_t offset, size_t size,
|
||||
size_t tile_size, PackTileFn && pack_tile) {
|
||||
@@ -70,12 +376,14 @@ static void repack_to_tiled_grid_32(ggml_tensor * t, const void * data, size_t o
|
||||
const SrcElemT * src_slice = src_matrix + (slice_idx - start_slice) * (ne1 * row_stride);
|
||||
uint8_t * matrix_dst = (uint8_t *) t->data + slice_idx * matrix_size;
|
||||
|
||||
for (int ct = 0; ct < n_col_tiles; ct++) {
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile_dst = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
pack_tile(tile_dst, src_slice, ne0, ne1, row_stride, ct, kt);
|
||||
repack_parallel_for_ct(matrix_size, n_col_tiles, [&](int ct0, int ct1) {
|
||||
for (int ct = ct0; ct < ct1; ct++) {
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile_dst = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
pack_tile(tile_dst, src_slice, ne0, ne1, row_stride, ct, kt);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,8 +438,24 @@ static void unpack_from_tiled_grid_32(void * data, const ggml_tensor * t, size_t
|
||||
static void repack_q4_0_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
repack_to_tiled_grid_32<block_q4_0, 1>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q4_0,
|
||||
[](uint8_t * tile_dst, const block_q4_0 * src_slice, int64_t ne0, int64_t ne1, int64_t row_stride, int ct, int kt) {
|
||||
uint8_t tile_quants[32][32];
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (ct * 32 + 32 <= ne1 && kt < ne0 / 32) {
|
||||
for (int row0 = 0; row0 < 32; row0 += 8) {
|
||||
const uint8_t * qs[8];
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
qs[rr] = src_slice[(ct * 32 + row0 + rr) * row_stride + kt].qs;
|
||||
}
|
||||
repack_neon_4bit_rows(qs, tile_dst, row0);
|
||||
}
|
||||
|
||||
for (int row = 0; row < 32; ++row) {
|
||||
scale_dst[row] = src_slice[(ct * 32 + row) * row_stride + kt].d;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t tile_quants[32][32];
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
if (r < ne1 && kt < ne0 / 32) {
|
||||
@@ -167,8 +491,26 @@ static void repack_tiled_q4_0(void * data, const ggml_tensor * t, size_t offset,
|
||||
static void repack_q4_1_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
repack_to_tiled_grid_32<block_q4_1, 1>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q4_1,
|
||||
[](uint8_t * tile_dst, const block_q4_1 * src_slice, int64_t ne0, int64_t ne1, int64_t row_stride, int ct, int kt) {
|
||||
uint8_t tile_quants[32][32];
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (ct * 32 + 32 <= ne1 && kt < ne0 / 32) {
|
||||
for (int row0 = 0; row0 < 32; row0 += 8) {
|
||||
const uint8_t * qs[8];
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
qs[rr] = src_slice[(ct * 32 + row0 + rr) * row_stride + kt].qs;
|
||||
}
|
||||
repack_neon_4bit_rows(qs, tile_dst, row0);
|
||||
}
|
||||
|
||||
for (int row = 0; row < 32; ++row) {
|
||||
const block_q4_1 & src_block = src_slice[(ct * 32 + row) * row_stride + kt];
|
||||
scale_dst[2 * row + 0] = src_block.d;
|
||||
scale_dst[2 * row + 1] = src_block.m;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t tile_quants[32][32];
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
if (r < ne1 && kt < ne0 / 32) {
|
||||
@@ -207,8 +549,24 @@ static void repack_tiled_q4_1(void * data, const ggml_tensor * t, size_t offset,
|
||||
static void repack_mxfp4_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
repack_to_tiled_grid_32<block_mxfp4, 1>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_MXFP4,
|
||||
[](uint8_t * tile_dst, const block_mxfp4 * src_slice, int64_t ne0, int64_t ne1, int64_t row_stride, int ct, int kt) {
|
||||
uint8_t tile_quants[32][32];
|
||||
uint8_t * scale_dst = tile_dst + 512;
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (ct * 32 + 32 <= ne1 && kt < ne0 / 32) {
|
||||
for (int row0 = 0; row0 < 32; row0 += 8) {
|
||||
const uint8_t * qs[8];
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
qs[rr] = src_slice[(ct * 32 + row0 + rr) * row_stride + kt].qs;
|
||||
}
|
||||
repack_neon_4bit_rows(qs, tile_dst, row0);
|
||||
}
|
||||
|
||||
for (int row = 0; row < 32; ++row) {
|
||||
scale_dst[row] = src_slice[(ct * 32 + row) * row_stride + kt].e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t tile_quants[32][32];
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
if (r < ne1 && kt < ne0 / 32) {
|
||||
@@ -244,6 +602,23 @@ static void repack_tiled_mxfp4(void * data, const ggml_tensor * t, size_t offset
|
||||
static void repack_q8_0_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
repack_to_tiled_grid_32<block_q8_0, 1>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q8_0,
|
||||
[](uint8_t * tile_dst, const block_q8_0 * src_slice, int64_t ne0, int64_t ne1, int64_t row_stride, int ct, int kt) {
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 1024);
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (ct * 32 + 32 <= ne1 && kt < ne0 / 32) {
|
||||
for (int row0 = 0; row0 < 32; row0 += 8) {
|
||||
const uint8_t * qs[8];
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
qs[rr] = (const uint8_t *) src_slice[(ct * 32 + row0 + rr) * row_stride + kt].qs;
|
||||
}
|
||||
repack_neon_q8_rows(qs, tile_dst, row0);
|
||||
}
|
||||
|
||||
for (int row = 0; row < 32; ++row) {
|
||||
scale_dst[row] = src_slice[(ct * 32 + row) * row_stride + kt].d;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (int cp = 0; cp < 16; cp++) {
|
||||
const int col0 = cp * 2;
|
||||
const int col1 = col0 + 1;
|
||||
@@ -254,7 +629,6 @@ static void repack_q8_0_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
tile_dst[cp * 64 + 2 * row + 1] = b ? b->qs[col1] : 0;
|
||||
}
|
||||
}
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 1024);
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
scale_dst[row] = (r < ne1 && kt < ne0 / 32) ? src_slice[r * row_stride + kt].d : 0;
|
||||
@@ -303,24 +677,33 @@ static void repack_q2_0_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
for (int64_t slice = 0; slice < t->ne[2] * t->ne[3]; ++slice) {
|
||||
const block_q2_0 * src_slice = src + slice * t->ne[1] * blocks_per_row;
|
||||
uint8_t * dst_slice = (uint8_t *) t->data + slice * matrix_size;
|
||||
memset(dst_slice, 0, matrix_size);
|
||||
const size_t ct_bytes = (size_t) n_weight_tiles * HTP_TENSOR_TILE_SIZE_Q2_0;
|
||||
|
||||
for (int64_t row_index = 0; row_index < t->ne[1]; ++row_index) {
|
||||
const int64_t ct = row_index / 32;
|
||||
const int row = (int) (row_index % 32);
|
||||
for (int64_t kt = 0; kt < n_weight_tiles; ++kt) {
|
||||
const block_q2_0 * block = src_slice + row_index * blocks_per_row + kt;
|
||||
uint8_t * tile = dst_slice + (ct * n_weight_tiles + kt) * HTP_TENSOR_TILE_SIZE_Q2_0;
|
||||
for (int group = 0; group < 16; ++group) {
|
||||
for (int byte = 0; byte < 4; ++byte) {
|
||||
const int k = 4 * group + byte;
|
||||
const uint8_t code = (block->qs[k / 4] >> (2 * (k % 4))) & 3;
|
||||
tile[(group >> 2) * 128 + 4 * row + byte] |= code << (2 * (group & 3));
|
||||
repack_parallel_for_ct(matrix_size, (int) n_col_tiles, [&](int ct0, int ct1) {
|
||||
memset(dst_slice + (size_t) ct0 * ct_bytes, 0, (size_t) (ct1 - ct0) * ct_bytes);
|
||||
const int64_t r0 = (int64_t) ct0 * 32;
|
||||
const int64_t r1 = (std::min)((int64_t) ct1 * 32, t->ne[1]);
|
||||
for (int64_t row_index = r0; row_index < r1; ++row_index) {
|
||||
const int64_t ct = row_index / 32;
|
||||
const int row = (int) (row_index % 32);
|
||||
for (int64_t kt = 0; kt < n_weight_tiles; ++kt) {
|
||||
const block_q2_0 * block = src_slice + row_index * blocks_per_row + kt;
|
||||
uint8_t * tile = dst_slice + (ct * n_weight_tiles + kt) * HTP_TENSOR_TILE_SIZE_Q2_0;
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q2_0_block(block->qs, tile, row);
|
||||
#else
|
||||
for (int group = 0; group < 16; ++group) {
|
||||
for (int byte = 0; byte < 4; ++byte) {
|
||||
const int k = 4 * group + byte;
|
||||
const uint8_t code = (block->qs[k / 4] >> (2 * (k % 4))) & 3;
|
||||
tile[(group >> 2) * 128 + 4 * row + byte] |= code << (2 * (group & 3));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
memcpy(tile + 512 + 2 * row, &block->d, sizeof(block->d));
|
||||
}
|
||||
memcpy(tile + 512 + 2 * row, &block->d, sizeof(block->d));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +751,12 @@ static void repack_float_to_f16_tiled(ggml_tensor * t, const void * data, size_t
|
||||
repack_to_tiled_grid_32<SrcT, 32>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_F16,
|
||||
[&](uint8_t * tile_bytes, const SrcT * src_slice, int64_t ne0, int64_t ne1, int64_t row_stride, int ct, int kt) {
|
||||
uint16_t * tile_dst = (uint16_t *) tile_bytes;
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (ct * 32 + 32 <= ne1 && kt * 32 + 32 <= ne0) {
|
||||
repack_neon_f16_tile(tile_dst, src_slice, row_stride, ct, kt);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (int cp = 0; cp < 16; cp++) {
|
||||
const int64_t c0 = kt * 32 + cp * 2;
|
||||
const int64_t c1 = c0 + 1;
|
||||
@@ -424,20 +813,25 @@ static void repack_kquant_to_tiled_grid(ggml_tensor * t, const void * data, size
|
||||
for (int64_t slice_idx = 0; slice_idx < ne2 * ne3; slice_idx++) {
|
||||
const BlockT * src_slice = src_matrix + slice_idx * (ne1 * sb_per_row);
|
||||
uint8_t * matrix_dst = (uint8_t *) t->data + slice_idx * matrix_size;
|
||||
const size_t ct_bytes = (size_t) n_k_tiles * tile_size;
|
||||
|
||||
// Padding rows and OR-ed bit planes require zeroed tiles
|
||||
memset(matrix_dst, 0, matrix_size);
|
||||
repack_parallel_for_ct(matrix_size, n_col_tiles, [&](int ct0, int ct1) {
|
||||
// Padding rows and OR-ed bit planes require zeroed tiles
|
||||
memset(matrix_dst + (size_t) ct0 * ct_bytes, 0, (size_t) (ct1 - ct0) * ct_bytes);
|
||||
|
||||
for (int64_t r = 0; r < ne1; r++) {
|
||||
const int ct = (int) (r / 32);
|
||||
const int row = (int) (r % 32);
|
||||
const BlockT * src_row = src_slice + r * sb_per_row;
|
||||
const int64_t r0 = (int64_t) ct0 * 32;
|
||||
const int64_t r1 = (std::min)((int64_t) ct1 * 32, ne1);
|
||||
for (int64_t r = r0; r < r1; r++) {
|
||||
const int ct = (int) (r / 32);
|
||||
const int row = (int) (r % 32);
|
||||
const BlockT * src_row = src_slice + r * sb_per_row;
|
||||
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
pack_kt(tile, &src_row[kt / 8], row, kt % 8);
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
pack_kt(tile, &src_row[kt / 8], row, kt % 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +887,7 @@ static void unpack_kquant_from_tiled_grid(void * data, const ggml_tensor * t, si
|
||||
|
||||
// --- K-Quant Helpers & Per-Type Implementations (Q2_K, Q3_K, Q4_K, Q5_K, Q6_K) ---
|
||||
|
||||
#if !defined(__ARM_NEON) && !defined(__aarch64__)
|
||||
// unsigned 6-bit value (0..63) of element e of a Q6_K block, same bit layout as dequantize_row_q6_K
|
||||
static inline uint8_t q6_K_get_quant(const block_q6_K * b, int e) {
|
||||
const int c = e / 128;
|
||||
@@ -510,6 +905,7 @@ static inline uint8_t q6_K_get_quant(const block_q6_K * b, int e) {
|
||||
}
|
||||
return (uint8_t) (lo | (hi << 4));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void repack_q6_K_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
repack_kquant_to_tiled_grid<block_q6_K>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q6_K,
|
||||
@@ -519,6 +915,9 @@ static void repack_q6_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
uint8_t * hi_pl = tile + 512;
|
||||
ggml_half * sc_pl = (ggml_half *) (tile + 768);
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q6_k(b, kt_local, lo_pl, hi_pl, row);
|
||||
#else
|
||||
for (int lk = 0; lk < 32; lk++) {
|
||||
const uint8_t q6 = q6_K_get_quant(b, kt_local * 32 + lk);
|
||||
const int g = lk >> 2;
|
||||
@@ -526,6 +925,7 @@ static void repack_q6_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
lo_pl[(g >> 1) * 128 + pos] |= (uint8_t) ((q6 & 0xF) << ((g & 1) * 4));
|
||||
hi_pl[(g >> 2) * 128 + pos] |= (uint8_t) ((q6 >> 4) << ((g & 3) * 2));
|
||||
}
|
||||
#endif
|
||||
for (int sub = 0; sub < 2; sub++) {
|
||||
sc_pl[sub * 32 + row] = GGML_FP32_TO_FP16(d * (float) b->scales[kt_local * 2 + sub]);
|
||||
}
|
||||
@@ -585,6 +985,7 @@ static void repack_tiled_q6_K(void * data, const ggml_tensor * t, size_t offset,
|
||||
});
|
||||
}
|
||||
|
||||
#if !defined(__ARM_NEON) && !defined(__aarch64__)
|
||||
// low 2 bits (0..3) of element e of a Q2_K or Q3_K block, same bit layout as dequantize_row_q2_K / q3_K
|
||||
static inline uint8_t q2_3_K_get_low2(const uint8_t * qs, int e) {
|
||||
const int c = e / 128;
|
||||
@@ -600,6 +1001,7 @@ static inline bool q3_K_get_hbit(const block_q3_K * b, int e) {
|
||||
const int l = e % 32;
|
||||
return (b->hmask[l] >> (c * 4 + j)) & 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// signed 6-bit scale j (-32..31) of a Q3_K block, same packing as quantize_row_q3_K_ref
|
||||
static inline int q3_K_get_scale(const uint8_t * scales, int j) {
|
||||
@@ -670,6 +1072,10 @@ static void repack_q3_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
uint8_t * neg_pl = tile + 256;
|
||||
ggml_half * sc_pl = (ggml_half *) (tile + 384);
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q2_3_k_low2(b->qs, kt_local, lo_pl, row);
|
||||
repack_neon_q3_k_hmask(b->hmask, kt_local, neg_pl, row);
|
||||
#else
|
||||
for (int lk = 0; lk < 32; lk++) {
|
||||
const int e = kt_local * 32 + lk;
|
||||
const int g = lk >> 2;
|
||||
@@ -679,6 +1085,7 @@ static void repack_q3_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
neg_pl[pos] |= (uint8_t) (1 << g);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int sub = 0; sub < 2; sub++) {
|
||||
sc_pl[sub * 32 + row] = GGML_FP32_TO_FP16(d * (float) q3_K_get_scale(b->scales, kt_local * 2 + sub));
|
||||
}
|
||||
@@ -750,11 +1157,15 @@ static void repack_q2_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
uint8_t * sc_pl = tile + 256;
|
||||
ggml_half * dd_pl = (ggml_half *) (tile + 320);
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q2_3_k_low2(b->qs, kt_local, lo_pl, row);
|
||||
#else
|
||||
for (int lk = 0; lk < 32; lk++) {
|
||||
const int g = lk >> 2;
|
||||
const int pos = row * 4 + (lk & 3);
|
||||
lo_pl[(g >> 2) * 128 + pos] |= (uint8_t) (q2_3_K_get_low2(b->qs, kt_local * 32 + lk) << ((g & 3) * 2));
|
||||
}
|
||||
#endif
|
||||
for (int sub = 0; sub < 2; sub++) {
|
||||
sc_pl[sub * 32 + row] = b->scales[kt_local * 2 + sub];
|
||||
}
|
||||
@@ -846,23 +1257,46 @@ static inline void pack_k4_scales_mins(uint8_t * scales, ggml_half * d, ggml_hal
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BlockT>
|
||||
static inline void repack_k4_quants(uint8_t * tile_dst, const BlockT * b, int row, int kt_local,
|
||||
int64_t sb_per_row, bool use_neon) {
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
if (use_neon) {
|
||||
if ((row & 7) == 0) {
|
||||
const uint8_t * qs[8];
|
||||
for (int rr = 0; rr < 8; ++rr) {
|
||||
qs[rr] = b[rr * sb_per_row].qs + (kt_local / 2) * 32;
|
||||
}
|
||||
repack_neon_k4_rows(qs, tile_dst, row, (kt_local & 1) != 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#else
|
||||
GGML_UNUSED(sb_per_row);
|
||||
GGML_UNUSED(use_neon);
|
||||
#endif
|
||||
const uint8_t * qs_sub = b->qs + (kt_local / 2) * 32;
|
||||
const int shift = (kt_local & 1) ? 4 : 0;
|
||||
|
||||
for (int cp = 0; cp < 16; cp++) {
|
||||
const uint8_t q0 = (qs_sub[2 * cp + 0] >> shift) & 0x0F;
|
||||
const uint8_t q1 = (qs_sub[2 * cp + 1] >> shift) & 0x0F;
|
||||
tile_dst[cp * 32 + row] = (uint8_t) ((q1 << 4) | q0);
|
||||
}
|
||||
}
|
||||
|
||||
static void repack_q4_K_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
const int64_t sb_per_row = t->ne[0] / QK_K;
|
||||
const bool use_neon = (t->ne[1] % 8 == 0);
|
||||
repack_kquant_to_tiled_grid<block_q4_K>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q4_K,
|
||||
[](uint8_t * tile_dst, const block_q4_K * b, int row, int kt_local) {
|
||||
[sb_per_row, use_neon](uint8_t * tile_dst, const block_q4_K * b, int row, int kt_local) {
|
||||
const float d = GGML_FP16_TO_FP32(b->d);
|
||||
const float dmin = GGML_FP16_TO_FP32(b->dmin);
|
||||
|
||||
uint8_t sc, m;
|
||||
get_scale_min_k4(kt_local, b->scales, &sc, &m);
|
||||
|
||||
const uint8_t * qs_sub = b->qs + (kt_local / 2) * 32;
|
||||
const int shift = (kt_local & 1) ? 4 : 0;
|
||||
|
||||
for (int cp = 0; cp < 16; cp++) {
|
||||
const uint8_t q0 = (qs_sub[2 * cp + 0] >> shift) & 0x0F;
|
||||
const uint8_t q1 = (qs_sub[2 * cp + 1] >> shift) & 0x0F;
|
||||
tile_dst[cp * 32 + row] = (uint8_t) ((q1 << 4) | q0);
|
||||
}
|
||||
repack_k4_quants(tile_dst, b, row, kt_local, sb_per_row, use_neon);
|
||||
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
scale_dst[2 * row + 0] = GGML_FP32_TO_FP16(d * (float) sc);
|
||||
@@ -900,8 +1334,10 @@ static void repack_tiled_q4_K(void * data, const ggml_tensor * t, size_t offset,
|
||||
}
|
||||
|
||||
static void repack_q5_K_tiled(ggml_tensor * t, const void * data, size_t offset, size_t size) {
|
||||
const int64_t sb_per_row = t->ne[0] / QK_K;
|
||||
const bool use_neon = (t->ne[1] % 8 == 0);
|
||||
repack_kquant_to_tiled_grid<block_q5_K>(t, data, offset, size, HTP_TENSOR_TILE_SIZE_Q5_K,
|
||||
[](uint8_t * tile_dst, const block_q5_K * b, int row, int kt_local) {
|
||||
[sb_per_row, use_neon](uint8_t * tile_dst, const block_q5_K * b, int row, int kt_local) {
|
||||
const float d = GGML_FP16_TO_FP32(b->d);
|
||||
const float dmin = GGML_FP16_TO_FP32(b->dmin);
|
||||
uint8_t * plane = tile_dst + 640;
|
||||
@@ -909,15 +1345,13 @@ static void repack_q5_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
uint8_t sc, m;
|
||||
get_scale_min_k4(kt_local, b->scales, &sc, &m);
|
||||
|
||||
const uint8_t * qs_sub = b->qs + (kt_local / 2) * 32;
|
||||
const int shift = (kt_local & 1) ? 4 : 0;
|
||||
repack_k4_quants(tile_dst, b, row, kt_local, sb_per_row, use_neon);
|
||||
|
||||
const uint8_t hbit = (uint8_t) (1 << kt_local);
|
||||
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q5_k_qh(b->qh, hbit, plane, row);
|
||||
#else
|
||||
for (int cp = 0; cp < 16; cp++) {
|
||||
const uint8_t q0 = (qs_sub[2 * cp + 0] >> shift) & 0x0F;
|
||||
const uint8_t q1 = (qs_sub[2 * cp + 1] >> shift) & 0x0F;
|
||||
tile_dst[cp * 32 + row] = (uint8_t) ((q1 << 4) | q0);
|
||||
|
||||
const int i = cp / 4;
|
||||
const int lane = (cp % 4) * 32 + row;
|
||||
if (b->qh[2 * cp + 0] & hbit) {
|
||||
@@ -927,6 +1361,7 @@ static void repack_q5_K_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
plane[lane] |= (uint8_t) (1 << (2 * i + 1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
scale_dst[2 * row + 0] = GGML_FP32_TO_FP16(d * (float) sc);
|
||||
@@ -1002,31 +1437,37 @@ static void repack_q1_0_tiled(ggml_tensor * t, const void * data, size_t offset,
|
||||
const block_q1_0 * src_slice = src_matrix + (slice_idx - start_slice) * ne1 * n_k_tiles;
|
||||
uint8_t * matrix_dst = (uint8_t *) t->data + slice_idx * matrix_size;
|
||||
|
||||
for (int ct = 0; ct < n_col_tiles; ct++) {
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile_dst = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
memset(tile_dst, 0, tile_size);
|
||||
repack_parallel_for_ct(matrix_size, n_col_tiles, [&](int ct0, int ct1) {
|
||||
for (int ct = ct0; ct < ct1; ct++) {
|
||||
for (int kt = 0; kt < n_k_tiles; kt++) {
|
||||
uint8_t * tile_dst = matrix_dst + ((size_t) ct * n_k_tiles + kt) * tile_size;
|
||||
ggml_half * scale_dst = (ggml_half *) (tile_dst + 512);
|
||||
memset(tile_dst, 0, tile_size);
|
||||
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
if (r >= ne1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const block_q1_0 & block = src_slice[r * n_k_tiles + kt];
|
||||
scale_dst[row] = block.d;
|
||||
for (int k = 0; k < QK1_0; k++) {
|
||||
int byte_offset;
|
||||
int bit;
|
||||
q1_0_tiled_bit_location(k, row, byte_offset, bit);
|
||||
if (block.qs[k / 8] & (1u << (k % 8))) {
|
||||
tile_dst[byte_offset] |= (uint8_t) (1u << bit);
|
||||
for (int row = 0; row < 32; row++) {
|
||||
const int64_t r = ct * 32 + row;
|
||||
if (r >= ne1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const block_q1_0 & block = src_slice[r * n_k_tiles + kt];
|
||||
scale_dst[row] = block.d;
|
||||
#if defined(__ARM_NEON) || defined(__aarch64__)
|
||||
repack_neon_q1_0_block(block.qs, tile_dst, row);
|
||||
#else
|
||||
for (int k = 0; k < QK1_0; k++) {
|
||||
int byte_offset;
|
||||
int bit;
|
||||
q1_0_tiled_bit_location(k, row, byte_offset, bit);
|
||||
if (block.qs[k / 8] & (1u << (k % 8))) {
|
||||
tile_dst[byte_offset] |= (uint8_t) (1u << bit);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,11 +1568,11 @@ void htp_repack_tensor_tiled(ggml_tensor * tensor, const void * data, size_t siz
|
||||
break;
|
||||
case GGML_TYPE_F32:
|
||||
repack_float_to_f16_tiled<float>(tensor, data, 0, size,
|
||||
[](float v) { return (uint16_t) ggml_fp32_to_fp16(v); });
|
||||
[](float v) { return (uint16_t) GGML_FP32_TO_FP16(v); });
|
||||
break;
|
||||
case GGML_TYPE_BF16:
|
||||
repack_float_to_f16_tiled<ggml_bf16_t>(tensor, data, 0, size,
|
||||
[](ggml_bf16_t v) { return (uint16_t) ggml_fp32_to_fp16(ggml_bf16_to_fp32(v)); });
|
||||
[](ggml_bf16_t v) { return (uint16_t) GGML_FP32_TO_FP16(GGML_BF16_TO_FP32(v)); });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -316,9 +316,17 @@ static int vtcm_release_callback(unsigned int rctx, void * state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
static unsigned int vtcm_query_size(void) {
|
||||
unsigned int vtcm_size = 8 * 1024 * 1024; // 8MB default
|
||||
HAP_compute_res_query_VTCM(0, &vtcm_size, NULL, NULL, NULL);
|
||||
unsigned int vtcm_avail;
|
||||
compute_res_vtcm_page_t total_layout, avail_layout;
|
||||
// some images (e.g. FuSa cDSP) fail the query when the layout out-params are NULL
|
||||
HAP_compute_res_query_VTCM(0, &vtcm_size, &total_layout, &vtcm_avail, &avail_layout);
|
||||
return vtcm_size;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
unsigned int vtcm_size = vtcm_query_size();
|
||||
|
||||
compute_res_attr_t attr;
|
||||
HAP_compute_res_attr_init(&attr);
|
||||
@@ -770,9 +778,7 @@ AEEResult htp_iface_hwinfo(remote_handle64 handle, uint32_t * n_threads, uint32_
|
||||
*n_hvx = n_hvx_val;
|
||||
*n_hmx = 1;
|
||||
|
||||
uint32_t vtcm_sz = 8 * 1024 * 1024; // 8MB default fallback
|
||||
HAP_compute_res_query_VTCM(0, (unsigned int *)&vtcm_sz, NULL, NULL, NULL);
|
||||
*vtcm_size = vtcm_sz;
|
||||
*vtcm_size = vtcm_query_size();
|
||||
|
||||
return AEE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -539,8 +539,14 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
descriptor.outputURL = [NSURL fileURLWithPath:path];
|
||||
|
||||
NSError * error = nil;
|
||||
if (![[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor error:&error]) {
|
||||
const bool ok = [[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor error:&error];
|
||||
|
||||
[descriptor release];
|
||||
|
||||
if (!ok) {
|
||||
GGML_LOG_ERROR("%s: error: unable to start capture '%s' (did you set METAL_CAPTURE_ENABLED=1 ?)\n", __func__, [[error localizedDescription] UTF8String]);
|
||||
[ctx->capture_scope release];
|
||||
ctx->capture_scope = nil;
|
||||
} else {
|
||||
[ctx->capture_scope beginScope];
|
||||
ctx->capture_started = true;
|
||||
@@ -600,6 +606,8 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
// enter here only when capturing in order to wait for all computation to finish
|
||||
// otherwise, we leave the graph to compute asynchronously
|
||||
if (use_capture && ctx->capture_started) {
|
||||
enum ggml_status res = GGML_STATUS_SUCCESS;
|
||||
|
||||
// wait for completion and check status of each command buffer
|
||||
// needed to detect if the device ran out-of-memory for example (#1881)
|
||||
{
|
||||
@@ -613,11 +621,11 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
GGML_LOG_INFO("error: %s\n", [[cmd_buf error].localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
return GGML_STATUS_FAILED;
|
||||
res = GGML_STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_cb; ++i) {
|
||||
for (int i = 0; i < n_cb && res == GGML_STATUS_SUCCESS; ++i) {
|
||||
id<MTLCommandBuffer> cmd_buf = ctx->cmd_bufs[i].obj;
|
||||
[cmd_buf waitUntilCompleted];
|
||||
|
||||
@@ -627,8 +635,8 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
if (status == MTLCommandBufferStatusError) {
|
||||
GGML_LOG_INFO("error: %s\n", [[cmd_buf error].localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
return GGML_STATUS_FAILED;
|
||||
res = GGML_STATUS_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
id<MTLCommandBuffer> next_buffer = (i + 1 < n_cb ? ctx->cmd_bufs[i + 1].obj : nil);
|
||||
@@ -643,7 +651,8 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
|
||||
if (ctx->abort_callback && ctx->abort_callback(ctx->abort_callback_data)) {
|
||||
GGML_LOG_INFO("%s: command buffer %d aborted", __func__, i);
|
||||
return GGML_STATUS_ABORTED;
|
||||
res = GGML_STATUS_ABORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
[next_buffer commit];
|
||||
@@ -652,7 +661,14 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
|
||||
[ctx->capture_scope endScope];
|
||||
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
||||
|
||||
[ctx->capture_scope release];
|
||||
ctx->capture_scope = nil;
|
||||
|
||||
ctx->capture_started = false;
|
||||
|
||||
if (res != GGML_STATUS_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1567,6 +1567,28 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax(ggml_meta
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_part(ggml_metal_library_t lib) {
|
||||
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, "kernel_argmax_part_f32");
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, "kernel_argmax_part_f32", "kernel_argmax_part_f32", nullptr);
|
||||
}
|
||||
|
||||
res.smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_final(ggml_metal_library_t lib) {
|
||||
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, "kernel_argmax_final_f32");
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, "kernel_argmax_final_f32", "kernel_argmax_final_f32", nullptr);
|
||||
}
|
||||
|
||||
res.smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort(ggml_metal_library_t lib, const ggml_tensor * op) {
|
||||
assert(op->op == GGML_OP_ARGSORT);
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm_id
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm_id (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mv_id (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_part (ggml_metal_library_t lib);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_final (ggml_metal_library_t lib);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort_merge (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_fwht (ggml_metal_library_t lib, int n, enum ggml_type tsrc);
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#define N_MM_NPART_AMAX 256
|
||||
|
||||
#define N_ARGMAX_NPART_MAX 256
|
||||
#define N_ARGMAX_CHUNK 4096
|
||||
|
||||
// kernel parameters for mat-vec threadgroups
|
||||
//
|
||||
// N_R0: number of src0 rows to process per simdgroup
|
||||
@@ -1379,8 +1382,10 @@ typedef struct {
|
||||
} ggml_metal_kargs_pool_1d;
|
||||
|
||||
typedef struct {
|
||||
int64_t ne00;
|
||||
int64_t ne00;
|
||||
uint64_t nb01;
|
||||
int64_t npart;
|
||||
int64_t ne01;
|
||||
} ggml_metal_kargs_argmax;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -2319,13 +2319,18 @@ int ggml_metal_op_pool_1d(ggml_metal_op_t ctx, int idx) {
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_pool_1d(lib, op, op_pool);
|
||||
|
||||
const int nth = std::min(ggml_metal_pipeline_max_theads_per_threadgroup(pipeline), (int) np);
|
||||
const int ntg = (np + nth - 1) / nth;
|
||||
const int nth = std::min(256, std::max(1, k0));
|
||||
const int ntg = (int) np;
|
||||
|
||||
const size_t smem = op_pool == GGML_OP_POOL_AVG ?
|
||||
2*32*sizeof(float) :
|
||||
32*sizeof(float);
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline);
|
||||
ggml_metal_encoder_set_bytes (enc, &args_pool_1d, sizeof(args_pool_1d), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op->src[0]), 1);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op), 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, ntg, 1, 1, nth, 1, 1);
|
||||
|
||||
@@ -5421,6 +5426,25 @@ int ggml_metal_op_timestep_embedding(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t ggml_metal_op_argmax_extra(const ggml_tensor * op) {
|
||||
assert(op->op == GGML_OP_ARGMAX);
|
||||
|
||||
const int64_t ne00 = op->src[0]->ne[0];
|
||||
const int64_t ne01 = op->src[0]->ne[1];
|
||||
|
||||
int npart = 1;
|
||||
if (ne00 > N_ARGMAX_CHUNK) {
|
||||
npart = (int) std::min<int64_t>(N_ARGMAX_NPART_MAX, (ne00 + N_ARGMAX_CHUNK - 1)/N_ARGMAX_CHUNK);
|
||||
}
|
||||
|
||||
if (npart == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// partial max + partial argmax for every row
|
||||
return (sizeof(float) + sizeof(int32_t)) * npart * ne01;
|
||||
}
|
||||
|
||||
int ggml_metal_op_argmax(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
@@ -5432,22 +5456,61 @@ int ggml_metal_op_argmax(ggml_metal_op_t ctx, int idx) {
|
||||
GGML_TENSOR_LOCALS( int32_t, ne, op, ne);
|
||||
GGML_TENSOR_LOCALS(uint64_t, nb, op, nb);
|
||||
|
||||
const int64_t nrows = ggml_nrows(op->src[0]);
|
||||
|
||||
int npart = 1;
|
||||
if (ne00 > N_ARGMAX_CHUNK) {
|
||||
npart = (int) std::min<int64_t>(N_ARGMAX_NPART_MAX, (ne00 + N_ARGMAX_CHUNK - 1)/N_ARGMAX_CHUNK);
|
||||
}
|
||||
|
||||
ggml_metal_kargs_argmax args = {
|
||||
/*.ne00 = */ ne00,
|
||||
/*.nb01 = */ nb01,
|
||||
/*.ne00 = */ ne00,
|
||||
/*.nb01 = */ nb01,
|
||||
/*.npart = */ npart,
|
||||
/*.ne01 = */ ne01,
|
||||
};
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_argmax(lib, op);
|
||||
const size_t smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
const int64_t nrows = ggml_nrows(op->src[0]);
|
||||
if (npart > 1) {
|
||||
auto pipeline_part = ggml_metal_library_get_pipeline_argmax_part(lib);
|
||||
auto pipeline_final = ggml_metal_library_get_pipeline_argmax_final(lib);
|
||||
|
||||
ggml_metal_buffer_id bid_dst = ggml_metal_get_buffer_id(op);
|
||||
ggml_metal_buffer_id bid_tmp = bid_dst;
|
||||
bid_tmp.offs += ggml_nbytes(op);
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_part);
|
||||
ggml_metal_encoder_set_bytes (enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op->src[0]), 1);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_tmp, 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, npart, nrows, 1, 256, 1, 1);
|
||||
|
||||
ggml_metal_op_concurrency_reset(ctx);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
while (nth < npart && nth < 256) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_final);
|
||||
ggml_metal_encoder_set_bytes (enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_tmp, 1);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_dst, 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, nrows, 1, 1, nth, 1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_argmax(lib, op);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
while (nth < ne00 && nth*ne01*ne02*ne03 < 256) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
const size_t smem = pipeline.smem;
|
||||
|
||||
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(op->src[0]), 1);
|
||||
|
||||
@@ -47,6 +47,8 @@ size_t ggml_metal_op_flash_attn_ext_extra_tmp(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_kv_f16(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_idx(const struct ggml_tensor * op);
|
||||
|
||||
size_t ggml_metal_op_argmax_extra(const struct ggml_tensor * op);
|
||||
|
||||
int ggml_metal_op_concat (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_repeat (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_acc (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -245,6 +245,10 @@ static size_t ggml_backend_metal_buffer_type_get_alloc_size(ggml_backend_buffer_
|
||||
{
|
||||
res = 2*sizeof(int32_t)*ggml_nelements(tensor->src[0]);
|
||||
} break;
|
||||
case GGML_OP_ARGMAX:
|
||||
{
|
||||
res += ggml_metal_op_argmax_extra(tensor);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,140 @@ kernel void kernel_argmax_f32(
|
||||
dst_i32[tgpig] = arg_val;
|
||||
}
|
||||
|
||||
kernel void kernel_argmax_part_f32(
|
||||
constant ggml_metal_kargs_argmax & args,
|
||||
device const char * src0,
|
||||
device char * tmp,
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint3 ntg[[threads_per_threadgroup]]) {
|
||||
const int row = tgpig.y;
|
||||
const int part = tgpig.x;
|
||||
const int ne00 = (int) args.ne00;
|
||||
const int npart = (int) args.npart;
|
||||
const int chunk = (ne00 + npart - 1) / npart;
|
||||
const int start = part * chunk;
|
||||
const int end = min(start + chunk, ne00);
|
||||
|
||||
device const float * x_row = (device const float *) ((device const char *) src0 + row * args.nb01);
|
||||
|
||||
float lmax = -INFINITY;
|
||||
int32_t larg = -1;
|
||||
|
||||
for (int i00 = start + tpitg.x; i00 < end; i00 += ntg.x) {
|
||||
if (x_row[i00] > lmax) {
|
||||
lmax = x_row[i00];
|
||||
larg = i00;
|
||||
}
|
||||
}
|
||||
|
||||
// find the argmax value in the block
|
||||
float max_val = simd_max(lmax);
|
||||
int32_t arg_val = simd_max(select(-1, larg, lmax == max_val));
|
||||
|
||||
threadgroup float * shared_maxval = (threadgroup float *) shmem;
|
||||
threadgroup int32_t * shared_argmax = (threadgroup int32_t *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg.x > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_maxval[tiisg] = -INFINITY;
|
||||
shared_argmax[tiisg] = -1;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_maxval[sgitg] = max_val;
|
||||
shared_argmax[sgitg] = arg_val;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_maxval[tiisg];
|
||||
arg_val = shared_argmax[tiisg];
|
||||
|
||||
float max_val_reduced = simd_max(max_val);
|
||||
int32_t arg_val_reduced = simd_max(select(-1, arg_val, max_val == max_val_reduced));
|
||||
|
||||
max_val = max_val_reduced;
|
||||
arg_val = arg_val_reduced;
|
||||
}
|
||||
|
||||
if (tpitg.x == 0) {
|
||||
device float * tmp_max = (device float *) tmp;
|
||||
device int32_t * tmp_arg = (device int32_t *) tmp + npart * (int) args.ne01;
|
||||
|
||||
tmp_max[row*npart + part] = max_val;
|
||||
tmp_arg[row*npart + part] = arg_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_argmax_final_f32(
|
||||
constant ggml_metal_kargs_argmax & args,
|
||||
device const char * tmp,
|
||||
device char * dst,
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint3 ntg[[threads_per_threadgroup]]) {
|
||||
const int row = tgpig.x;
|
||||
const int npart = (int) args.npart;
|
||||
|
||||
device const float * tmp_max = (device const float *) tmp;
|
||||
device const int32_t * tmp_arg = (device const int32_t *) tmp + npart * (int) args.ne01;
|
||||
|
||||
float lmax = -INFINITY;
|
||||
int32_t larg = -1;
|
||||
|
||||
for (int i = tpitg.x; i < npart; i += ntg.x) {
|
||||
if (tmp_max[row*npart + i] > lmax) {
|
||||
lmax = tmp_max[row*npart + i];
|
||||
larg = tmp_arg[row*npart + i];
|
||||
}
|
||||
}
|
||||
|
||||
// find the argmax value in the block
|
||||
float max_val = simd_max(lmax);
|
||||
int32_t arg_val = simd_max(select(-1, larg, lmax == max_val));
|
||||
|
||||
threadgroup float * shared_maxval = (threadgroup float *) shmem;
|
||||
threadgroup int32_t * shared_argmax = (threadgroup int32_t *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg.x > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_maxval[tiisg] = -INFINITY;
|
||||
shared_argmax[tiisg] = -1;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_maxval[sgitg] = max_val;
|
||||
shared_argmax[sgitg] = arg_val;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_maxval[tiisg];
|
||||
arg_val = shared_argmax[tiisg];
|
||||
|
||||
float max_val_reduced = simd_max(max_val);
|
||||
int32_t arg_val_reduced = simd_max(select(-1, arg_val, max_val == max_val_reduced));
|
||||
|
||||
max_val = max_val_reduced;
|
||||
arg_val = arg_val_reduced;
|
||||
}
|
||||
|
||||
if (tpitg.x == 0) {
|
||||
((device int32_t *) dst)[row] = arg_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_diag_f32(
|
||||
constant ggml_metal_kargs_diag & args,
|
||||
device const char * src0,
|
||||
|
||||
@@ -593,6 +593,7 @@ template [[host_name("kernel_mul_mm_id_map0_ne20_4" )]] kernel kernel_mul_mm_id_
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_5" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<5>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_6" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<6>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_8" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<8>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_9" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<9>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_10")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<10>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_16")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<16>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_22")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<22>;
|
||||
|
||||
@@ -84,65 +84,117 @@ kernel void kernel_pool_1d_max_f32(
|
||||
constant ggml_metal_kargs_pool_1d & args,
|
||||
device const float * src,
|
||||
device float * dst,
|
||||
uint gid [[thread_position_in_grid]]
|
||||
) {
|
||||
|
||||
if (gid >= args.np) {
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint tgpig[[threadgroup_position_in_grid]],
|
||||
uint tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint ntg[[threads_per_threadgroup]]) {
|
||||
if (tgpig >= args.np) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int ow = (int)gid % args.OW;
|
||||
const int row = (int)gid / args.OW;
|
||||
const int out = (int) tgpig;
|
||||
const int ow = out % (int) args.OW;
|
||||
const int row = out / (int) args.OW;
|
||||
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
float acc = -INFINITY;
|
||||
float lmax = -INFINITY;
|
||||
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
for (int ki = 0; ki < args.k0; ++ki) {
|
||||
int j = base + ki;
|
||||
if (j < 0 || j >= args.IW){
|
||||
continue;
|
||||
for (int ki = tpitg; ki < args.k0; ki += ntg) {
|
||||
const int j = base + ki;
|
||||
if (j >= 0 && j < args.IW) {
|
||||
lmax = max(lmax, src[src_off + j]);
|
||||
}
|
||||
float v = src[src_off + j];
|
||||
acc = max(acc, v);
|
||||
}
|
||||
|
||||
dst[dst_off + ow] = acc;
|
||||
float max_val = simd_max(lmax);
|
||||
|
||||
threadgroup float * shared_max = (threadgroup float *) shmem;
|
||||
|
||||
if (ntg > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_max[tiisg] = -INFINITY;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_max[sgitg] = max_val;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_max[tiisg];
|
||||
max_val = simd_max(max_val);
|
||||
}
|
||||
|
||||
if (tpitg == 0) {
|
||||
dst[dst_off + ow] = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_pool_1d_avg_f32(
|
||||
constant ggml_metal_kargs_pool_1d & args,
|
||||
device const float * src,
|
||||
device float * dst,
|
||||
uint gid [[thread_position_in_grid]]
|
||||
) {
|
||||
|
||||
if (gid >= args.np) {
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint tgpig[[threadgroup_position_in_grid]],
|
||||
uint tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint ntg[[threads_per_threadgroup]]) {
|
||||
if (tgpig >= args.np) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int ow = (int)gid % args.OW;
|
||||
const int row = (int)gid / args.OW;
|
||||
const int out = (int) tgpig;
|
||||
const int ow = out % (int) args.OW;
|
||||
const int row = out / (int) args.OW;
|
||||
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
float acc = 0.0f;
|
||||
int cnt = 0;
|
||||
float lsum = 0.0f;
|
||||
int lcnt = 0;
|
||||
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
for (int ki = 0; ki < args.k0; ++ki) {
|
||||
for (int ki = tpitg; ki < args.k0; ki += ntg) {
|
||||
const int j = base + ki;
|
||||
if (j < 0 || j >= args.IW) {
|
||||
continue;
|
||||
if (j >= 0 && j < args.IW) {
|
||||
lsum += src[src_off + j];
|
||||
lcnt += 1;
|
||||
}
|
||||
acc += src[src_off + j];
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
dst[dst_off + ow] = (cnt > 0) ? (acc / (float)cnt) : 0.0f;
|
||||
float sum = simd_sum(lsum);
|
||||
float cnt = simd_sum((float) lcnt);
|
||||
|
||||
threadgroup float * shared_sum = (threadgroup float *) shmem;
|
||||
threadgroup float * shared_cnt = (threadgroup float *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_sum[tiisg] = 0.0f;
|
||||
shared_cnt[tiisg] = 0.0f;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_sum[sgitg] = sum;
|
||||
shared_cnt[sgitg] = cnt;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
sum = shared_sum[tiisg];
|
||||
cnt = shared_cnt[tiisg];
|
||||
|
||||
sum = simd_sum(sum);
|
||||
cnt = simd_sum(cnt);
|
||||
}
|
||||
|
||||
if (tpitg == 0) {
|
||||
dst[dst_off + ow] = (cnt > 0.0f) ? (sum / cnt) : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ set(GGML_OPENCL_KERNELS
|
||||
add
|
||||
add_id
|
||||
moe_add_id_glu
|
||||
argmax
|
||||
argsort
|
||||
tri
|
||||
fill
|
||||
@@ -80,6 +81,8 @@ set(GGML_OPENCL_KERNELS
|
||||
solve_tri
|
||||
im2col_f32
|
||||
im2col_f16
|
||||
im2col_3d_f32
|
||||
im2col_3d_f16
|
||||
mean
|
||||
mul_mv_f16_f16
|
||||
mul_mv_f16_f32_1row
|
||||
|
||||
@@ -646,8 +646,8 @@ struct ggml_backend_opencl_context {
|
||||
ggml_cl_version platform_version;
|
||||
ggml_cl_version opencl_c_version;
|
||||
|
||||
// argsort is loaded in supports_op because its availability depends on how
|
||||
// many workgroups are allowed, which requires kernel compilation.
|
||||
// argmax and argsort are loaded in supports_op; argsort availability depends on the compiled kernel's workgroup limit.
|
||||
bool kernels_loaded_argmax = false;
|
||||
bool kernels_loaded_argsort = false;
|
||||
// rest of the kernels are currently always loaded in alloc_buffer.
|
||||
bool kernels_loaded = false;
|
||||
@@ -831,6 +831,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_program program_mul_mv_id_mxfp4_f32_flat;
|
||||
cl_program program_mul_mm_f32_f32_l4_lm;
|
||||
cl_program program_mul_mm_f16_f32_l4_lm;
|
||||
cl_program program_mul_mm_f16_f32_l4_lm_n8;
|
||||
cl_program program_mul_mm_q8_0_f32_l4_lm;
|
||||
|
||||
cl_kernel kernel_add, kernel_add_row, kernel_add_f16, kernel_add_row_f16;
|
||||
@@ -970,6 +971,8 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_mul_mv_iq4_nl_f32_flat;
|
||||
cl_kernel kernel_solve_tri_f32;
|
||||
cl_kernel kernel_im2col_f32, kernel_im2col_f16;
|
||||
cl_kernel kernel_im2col_3d_f32, kernel_im2col_3d_f16;
|
||||
cl_kernel kernel_argmax_f32_i32;
|
||||
cl_kernel kernel_argsort_f32_i32;
|
||||
cl_kernel kernel_sum_rows_f32, kernel_sum_rows_f32_4;
|
||||
cl_kernel kernel_cumsum_blk, kernel_cumsum_add;
|
||||
@@ -1052,6 +1055,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_mul_mm_f32_f32_l4_lm;
|
||||
cl_kernel kernel_gemv_f32_f32_mc; // multi-column (small-N) f32 GEMV for spec/MTP verify
|
||||
cl_kernel kernel_mul_mm_f16_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_f16_f32_l4_lm_n8;
|
||||
cl_kernel kernel_mul_mm_q1_0_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_q4_0_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_q4_1_f32_l4_lm;
|
||||
@@ -1241,12 +1245,15 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemm_noshuffle_q4_1_f32;
|
||||
cl_kernel kernel_gemm_noshuffle_q8_0_f32, kernel_gemm_noshuffle_q8_0_f32_bin;
|
||||
cl_kernel kernel_gemm_noshuffle_q8_0_q8_1_dp4a = nullptr; // dp4a (int8) dense q8_0 prefill GEMM (opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q8_0_q8_1_dp4a_act_vload4 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg = nullptr; // q8_0 dense dp4a, weights via texture (opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q8_0_q8_1_dp4a_ila_a8_bin = nullptr;
|
||||
cl_kernel kernel_gemv_noshuffle_q8_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q8_0_f32_splitk; // split-K across WGs (small-M decode)
|
||||
cl_kernel kernel_gemm_noshuffle_q1_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q1_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q1_0_f32_nsg1 = nullptr;
|
||||
int min_m_gemv_nsg1_q1_0 = 0;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_o4; // 4-output-per-WI, long-vocab lm_head
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_tiled; // tiled-wide layout (opt-in)
|
||||
@@ -1260,9 +1267,17 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_ila_a8_bin;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_32b_trans;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a = nullptr; // dp4a (int8) dense prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_act_vload4 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg = nullptr; // dp4a dense prefill GEMM, weights via texture (X1 opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg = nullptr;
|
||||
int tile_n_dp4a_q4_k = 32;
|
||||
int tile_n_dp4a_narrow_q4_k = 32;
|
||||
int max_n_dp4a_narrow_q4_k = 16;
|
||||
cl_kernel kernel_gemm_noshuffle_q5_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q5_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q5_k_q8_1_dp4a_act_vload4 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q6_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q6_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q6_k_q8_1_dp4a_act_vload4 = nullptr;
|
||||
cl_kernel kernel_quant_a_q8_1; // plain activation q8_1 pre-pass
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_f32_r1;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_f32_kimg;
|
||||
@@ -1296,6 +1311,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemm_noshuffle_iq4_nl_f32;
|
||||
cl_kernel kernel_gemm_noshuffle_iq4_nl_q8_1_dp4a = nullptr; // dp4a (int8) dense IQ4_NL prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_0_q8_1_dp4a = nullptr; // dp4a (int8) dense q4_0 prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_0_q8_1_dp4a_act_vload4 = nullptr;
|
||||
cl_kernel kernel_gemm_coopk_q4_0_q8_1_dp4a_c4 = nullptr;
|
||||
cl_kernel kernel_gemm_coopk_q4_0_q8_1_dp4a_c2 = nullptr;
|
||||
int nsg_coopk_dp4a_q4_0 = 8;
|
||||
@@ -1470,6 +1486,27 @@ static cl_program build_program_from_binary(cl_context ctx, cl_device_id dev, co
|
||||
return p;
|
||||
}
|
||||
|
||||
static void load_cl_kernels_argmax(ggml_backend_opencl_context *backend_ctx) {
|
||||
auto opencl_c_std =
|
||||
std::string("CL") + std::to_string(backend_ctx->opencl_c_version.major) + "." + std::to_string(backend_ctx->opencl_c_version.minor);
|
||||
std::string compile_opts = std::string("-cl-std=") + opencl_c_std;
|
||||
|
||||
if (!backend_ctx->kernels_loaded_argmax) {
|
||||
cl_int err;
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "argmax.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("argmax.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_argmax_f32_i32 = clCreateKernel(prog, "kernel_argmax_f32_i32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
backend_ctx->kernels_loaded_argmax = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_cl_kernels_argsort(ggml_backend_opencl_context *backend_ctx) {
|
||||
// compiler options for general kernels
|
||||
auto opencl_c_std =
|
||||
@@ -2087,6 +2124,40 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// im2col_3d_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "im2col_3d_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("im2col_3d_f32.cl");
|
||||
#endif
|
||||
cl_program prog =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_im2col_3d_f32 = clCreateKernel(prog, "kernel_im2col_3d_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// im2col_3d_f16
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "im2col_3d_f16.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("im2col_3d_f16.cl");
|
||||
#endif
|
||||
cl_program prog =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_im2col_3d_f16 = clCreateKernel(prog, "kernel_im2col_3d_f16", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q4_0_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -2781,6 +2852,11 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_f16_f32_l4_lm = clCreateKernel(backend_ctx->program_mul_mm_f16_f32_l4_lm, "kernel_mul_mm_f16_f32_l4_lm", &err), err));
|
||||
|
||||
const std::string narrow_opts = compile_opts + " -DBN=8 -DTN=1 -DKERNEL_NAME_LM=kernel_mul_mm_f16_f32_l4_lm_n8";
|
||||
backend_ctx->program_mul_mm_f16_f32_l4_lm_n8 =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), narrow_opts);
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_f16_f32_l4_lm_n8 = clCreateKernel(backend_ctx->program_mul_mm_f16_f32_l4_lm_n8, "kernel_mul_mm_f16_f32_l4_lm_n8", &err), err));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -3983,6 +4059,16 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q1_0_f32 = clCreateKernel(prog, "kernel_gemv_noshuffle_q1_0_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
|
||||
const char * nsg1_min_m_env = getenv("GGML_OPENCL_Q1_0_GEMV_NSG1_MIN_M");
|
||||
backend_ctx->min_m_gemv_nsg1_q1_0 = nsg1_min_m_env ? atoi(nsg1_min_m_env) :
|
||||
(backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E ? 8192 : 0);
|
||||
if (backend_ctx->min_m_gemv_nsg1_q1_0 > 0) {
|
||||
const std::string nsg1_opts = CL_gemv_compile_opts + " -DN_SIMDGROUP=1";
|
||||
cl_program prog_nsg1 = build_program_from_source(backend_ctx, kernel_src_CL_gemv_general.c_str(), nsg1_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q1_0_f32_nsg1 = clCreateKernel(prog_nsg1, "kernel_gemv_noshuffle_q1_0_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog_nsg1));
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -4323,6 +4409,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_0_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a_act_vload4 = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_0_q8_1_dp4a_act_vload4", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
@@ -4453,16 +4540,33 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q4_k_q8_1_dp4a.cl");
|
||||
#endif
|
||||
// Per-device dp4a dense tile. The X2-tuned TILESIZE_N=32 over-occupies LDS on
|
||||
// X1 (1152 B/WG -> few resident WGs); TILESIZE_N=8 (288 B) lifts occupancy on
|
||||
// X1, byte-identical. X2E keeps 32. Env override wins.
|
||||
int q4k_dp4a_ts = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) { q4k_dp4a_ts = atoi(e); }
|
||||
std::string dp4a_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(q4k_dp4a_ts);
|
||||
// X1 uses a smaller N tile to reduce local memory and register use.
|
||||
int tile_n = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) { tile_n = atoi(e); }
|
||||
GGML_ASSERT(tile_n >= 4 && tile_n <= 64 && tile_n % 4 == 0);
|
||||
backend_ctx->tile_n_dp4a_q4_k = tile_n;
|
||||
std::string dp4a_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(tile_n);
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), dp4a_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_act_vload4 = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a_act_vload4", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
|
||||
// Compile a smaller N tile to avoid unused columns in small batches.
|
||||
int tile_n_narrow = MIN(tile_n, 16);
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4_K_DP4A_TS_NARROW")) { tile_n_narrow = atoi(e); }
|
||||
GGML_ASSERT(tile_n_narrow >= 4 && tile_n_narrow <= 64 && tile_n_narrow % 4 == 0);
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4_K_DP4A_NARROW_MAX")) {
|
||||
backend_ctx->max_n_dp4a_narrow_q4_k = atoi(e);
|
||||
}
|
||||
backend_ctx->tile_n_dp4a_narrow_q4_k = tile_n_narrow;
|
||||
if (tile_n_narrow != tile_n) {
|
||||
std::string narrow_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(tile_n_narrow);
|
||||
cl_program narrow_prog = build_program_from_source(backend_ctx, kernel_src.c_str(), narrow_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow = clCreateKernel(narrow_prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg = clCreateKernel(narrow_prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg", &err), err));
|
||||
CL_CHECK(clReleaseProgram(narrow_prog));
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -4478,6 +4582,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q8_0_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg = clCreateKernel(prog, "kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a_act_vload4 = clCreateKernel(prog, "kernel_gemm_noshuffle_q8_0_q8_1_dp4a_act_vload4", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
@@ -4493,6 +4598,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q5_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q5_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q5_k_q8_1_dp4a_act_vload4 = clCreateKernel(prog, "kernel_gemm_noshuffle_q5_k_q8_1_dp4a_act_vload4", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
@@ -4508,6 +4614,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q6_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q6_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q6_k_q8_1_dp4a_act_vload4 = clCreateKernel(prog, "kernel_gemm_noshuffle_q6_k_q8_1_dp4a_act_vload4", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
@@ -9531,22 +9638,6 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
op->src[0]->ne[1] >= 32768) { // vocab-scale weight; no FFN/attn weight is this tall
|
||||
return false;
|
||||
}
|
||||
// The generic mul_mv (GEMV) kernels are wrong for large-batch prefill on
|
||||
// Adreno. A quant mul_mat only avoids the GEMV when it reaches the Adreno
|
||||
// trans-weight GEMM, which needs both a GEMM kernel for the type and
|
||||
// use_adreno_kernels(). Decline the large-N shapes that would otherwise
|
||||
// fall through to the GEMV.
|
||||
{
|
||||
const ggml_type t = op->src[0]->type;
|
||||
const bool type_has_gemm = (t == GGML_TYPE_Q4_0 || t == GGML_TYPE_Q4_1 ||
|
||||
t == GGML_TYPE_IQ4_NL || t == GGML_TYPE_Q8_0 ||
|
||||
t == GGML_TYPE_Q4_K || t == GGML_TYPE_Q5_K ||
|
||||
t == GGML_TYPE_Q6_K);
|
||||
const bool uses_gemm = type_has_gemm && use_adreno_kernels(backend_ctx, op->src[0]);
|
||||
if (!uses_gemm && op->src[1]->ne[1] >= 512) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]);
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q8_0) {
|
||||
// ggml_cl_mul_mat_q8_0_f32_adreno now honors src1/dst view_offs (the
|
||||
@@ -9622,6 +9713,13 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
return op->src[0]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[0]);
|
||||
case GGML_OP_IM2COL:
|
||||
return true;
|
||||
case GGML_OP_IM2COL_3D:
|
||||
return true;
|
||||
case GGML_OP_ARGMAX:
|
||||
load_cl_kernels_argmax(backend_ctx);
|
||||
return op->src[0]->type == GGML_TYPE_F32 && op->type == GGML_TYPE_I32 &&
|
||||
ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op) &&
|
||||
backend_ctx->get_kernel_workgroup_size(backend_ctx->kernel_argmax_f32_i32) >= 64;
|
||||
case GGML_OP_ARGSORT: {
|
||||
load_cl_kernels_argsort(backend_ctx);
|
||||
|
||||
@@ -19834,7 +19932,9 @@ static void ggml_cl_mul_mat_q1_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
img_desc.buffer = b_sub_buf;
|
||||
CL_CHECK((b_img = clCreateImage(context, CL_MEM_READ_ONLY, &img_fmt, &img_desc, NULL, &err), err));
|
||||
|
||||
kernel = backend_ctx->kernel_gemv_noshuffle_q1_0_f32;
|
||||
// Tall M has enough row parallelism to skip the K split and its reduction.
|
||||
const bool use_nsg1 = backend_ctx->min_m_gemv_nsg1_q1_0 > 0 && M >= backend_ctx->min_m_gemv_nsg1_q1_0;
|
||||
kernel = use_nsg1 ? backend_ctx->kernel_gemv_noshuffle_q1_0_f32_nsg1 : backend_ctx->kernel_gemv_noshuffle_q1_0_f32;
|
||||
|
||||
int r2 = 1;
|
||||
int r3 = 1;
|
||||
@@ -19856,8 +19956,9 @@ static void ggml_cl_mul_mat_q1_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &r3));
|
||||
|
||||
size_t wavesize = backend_ctx->adreno_wave_size;
|
||||
size_t local_work_size[] = { wavesize, 4, 1 };
|
||||
size_t global_work_size[] = { CEIL_DIV(M, wavesize)*wavesize, 4, 1 };
|
||||
const size_t nsg = use_nsg1 ? 1 : 4;
|
||||
size_t local_work_size[] = { wavesize, nsg, 1 };
|
||||
size_t global_work_size[] = { CEIL_DIV(M, wavesize)*wavesize, nsg, 1 };
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
|
||||
@@ -20321,7 +20422,14 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
// per-lane K-walk) for small M. Layout stride is fixed (4 uints/block), so only
|
||||
// the K-split count changes; the mc3 kernel reads it via get_local_size(1). The
|
||||
// ne1==1 base kernel hardcodes N_SIMDGROUP=4, so it always stays at 4.
|
||||
const int mc3_nsg = (use_q40_mc3 && ne01 < 4096) ? 8 : 4;
|
||||
int mc3_nsg = (use_q40_mc3 && ne01 < 4096) ? 8 : 4;
|
||||
if (use_q40_mc3) {
|
||||
// Register pressure can lower the kernel limit below the device limit.
|
||||
const size_t max_workgroup_size = backend_ctx->get_kernel_workgroup_size(kernel);
|
||||
while (mc3_nsg > 1 && (size_t)(64 * mc3_nsg) > max_workgroup_size) {
|
||||
mc3_nsg /= 2;
|
||||
}
|
||||
}
|
||||
size_t local_work_size[3] = {64, (size_t)mc3_nsg, 1};
|
||||
size_t global_work_size[3] = {(size_t)CEIL_DIV(ne01/2, 64)*64, (size_t)mc3_nsg, 1};
|
||||
|
||||
@@ -20396,11 +20504,13 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
return;
|
||||
}
|
||||
|
||||
// dp4a (int8) dense prefill GEMM, default off
|
||||
static const char * q4_0_act_vload4_env = getenv("GGML_OPENCL_Q4_0_DP4A_ACT_VLOAD4");
|
||||
const bool q4_0_act_vload4_on = q4_0_act_vload4_env == nullptr || atoi(q4_0_act_vload4_env) != 0;
|
||||
// Enable dense prefill by default on X2E when the uint4 activation tile is enabled.
|
||||
static const char * q4_0_dense_dp4a_env = getenv("GGML_OPENCL_Q4_0_DENSE_DP4A");
|
||||
bool q4_0_dense_dp4a_on = q4_0_dense_dp4a_env
|
||||
? (atoi(q4_0_dense_dp4a_env) != 0)
|
||||
: false;
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E && q4_0_act_vload4_on);
|
||||
// dot prod has to be available
|
||||
q4_0_dense_dp4a_on = backend_ctx->has_integer_dot && q4_0_dense_dp4a_on;
|
||||
|
||||
@@ -20427,7 +20537,9 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
size_t q_global[1] = { (size_t)(((n_blocks + 63) / 64) * 64) };
|
||||
backend_ctx->enqueue_ndrange_kernel(qk, 1, q_global, q_local, dst);
|
||||
|
||||
cl_kernel dk = backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a;
|
||||
cl_kernel dk = q4_0_act_vload4_on
|
||||
? backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a_act_vload4
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a;
|
||||
int ai = 0;
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q4_0->q));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q4_0->d));
|
||||
@@ -21720,8 +21832,13 @@ static void ggml_cl_mul_mat_q8_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
}
|
||||
}
|
||||
|
||||
cl_kernel dk = use_wimg ? backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a;
|
||||
static const char * q8_0_act_vload4_env = getenv("GGML_OPENCL_Q8_0_DP4A_ACT_VLOAD4");
|
||||
const bool q8_0_act_vload4_on = q8_0_act_vload4_env == nullptr || atoi(q8_0_act_vload4_env) != 0;
|
||||
cl_kernel dk = use_wimg
|
||||
? backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg
|
||||
: q8_0_act_vload4_on
|
||||
? backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a_act_vload4
|
||||
: backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a;
|
||||
int ai = 0;
|
||||
if (use_wimg) {
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &q8_q_img));
|
||||
@@ -22667,8 +22784,18 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
}
|
||||
}
|
||||
|
||||
cl_kernel dk = use_wimg ? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a;
|
||||
const bool use_narrow = backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow != nullptr
|
||||
&& N <= backend_ctx->max_n_dp4a_narrow_q4_k;
|
||||
static const char * q4_k_act_vload4_env = getenv("GGML_OPENCL_Q4_K_DP4A_ACT_VLOAD4");
|
||||
const bool q4_k_act_vload4_on = q4_k_act_vload4_env == nullptr || atoi(q4_k_act_vload4_env) != 0;
|
||||
cl_kernel dk = use_narrow
|
||||
? (use_wimg ? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow)
|
||||
: use_wimg
|
||||
? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg
|
||||
: q4_k_act_vload4_on
|
||||
? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_act_vload4
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a;
|
||||
int ai = 0;
|
||||
if (use_wimg) {
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &q4k_q_img));
|
||||
@@ -22689,12 +22816,9 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_d6));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_d4));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_hi2));
|
||||
// Must match the compile-time TILESIZE_N chosen at program build (per-device,
|
||||
// X1E=8 else 32; env override). Same inputs -> same value.
|
||||
int q4k_dp4a_ts = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) q4k_dp4a_ts = atoi(e);
|
||||
const int tile_n = use_narrow ? backend_ctx->tile_n_dp4a_narrow_q4_k : backend_ctx->tile_n_dp4a_q4_k;
|
||||
size_t d_local[3] = { 64, 1, 1 };
|
||||
size_t d_global[3] = { 64, (size_t)(M / 64), (size_t)CEIL_DIV(N, q4k_dp4a_ts) };
|
||||
size_t d_global[3] = { 64, (size_t)(M / 64), (size_t)CEIL_DIV(N, tile_n) };
|
||||
backend_ctx->enqueue_ndrange_kernel(dk, 3, d_global, d_local, dst);
|
||||
|
||||
if (q4k_q_img != nullptr) {
|
||||
@@ -23333,7 +23457,11 @@ static void ggml_cl_mul_mat_q6_K_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
size_t q_global[1] = { (size_t)(((n_blocks + 63) / 64) * 64) };
|
||||
backend_ctx->enqueue_ndrange_kernel(qk, 1, q_global, q_local, dst);
|
||||
|
||||
cl_kernel dk = backend_ctx->kernel_gemm_noshuffle_q6_k_q8_1_dp4a;
|
||||
static const char * q6_k_act_vload4_env = getenv("GGML_OPENCL_Q6_K_DP4A_ACT_VLOAD4");
|
||||
const bool q6_k_act_vload4_on = q6_k_act_vload4_env == nullptr || atoi(q6_k_act_vload4_env) != 0;
|
||||
cl_kernel dk = q6_k_act_vload4_on
|
||||
? backend_ctx->kernel_gemm_noshuffle_q6_k_q8_1_dp4a_act_vload4
|
||||
: backend_ctx->kernel_gemm_noshuffle_q6_k_q8_1_dp4a;
|
||||
int ai = 0;
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q6_K->ql));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q6_K->qh));
|
||||
@@ -23889,7 +24017,11 @@ static void ggml_cl_mul_mat_q5_K_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
size_t q_global[1] = { (size_t)(((n_blocks + 63) / 64) * 64) };
|
||||
backend_ctx->enqueue_ndrange_kernel(qk, 1, q_global, q_local, dst);
|
||||
|
||||
cl_kernel dk = backend_ctx->kernel_gemm_noshuffle_q5_k_q8_1_dp4a;
|
||||
static const char * q5_k_act_vload4_env = getenv("GGML_OPENCL_Q5_K_DP4A_ACT_VLOAD4");
|
||||
const bool q5_k_act_vload4_on = q5_k_act_vload4_env == nullptr || atoi(q5_k_act_vload4_env) != 0;
|
||||
cl_kernel dk = q5_k_act_vload4_on
|
||||
? backend_ctx->kernel_gemm_noshuffle_q5_k_q8_1_dp4a_act_vload4
|
||||
: backend_ctx->kernel_gemm_noshuffle_q5_k_q8_1_dp4a;
|
||||
int ai = 0;
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q5_k->q));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &extra0_q5_k->qh));
|
||||
@@ -24815,7 +24947,9 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
kernel = backend_ctx->kernel_mul_mm_f16_f32_l4_lm;
|
||||
static const char * mm_narrow_n_env = getenv("GGML_OPENCL_MM_NARROW_N");
|
||||
const bool use_narrow_n = (mm_narrow_n_env == nullptr || mm_narrow_n_env[0] != '0') && ne11 >= 2 && ne11 <= 8;
|
||||
kernel = use_narrow_n ? backend_ctx->kernel_mul_mm_f16_f32_l4_lm_n8 : backend_ctx->kernel_mul_mm_f16_f32_l4_lm;
|
||||
nth0 = 128; // calculated as (BM*BN)/(TM*TN)
|
||||
|
||||
int batch_stride_a = ne00*ne01;
|
||||
@@ -24874,8 +25008,9 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &r3));
|
||||
|
||||
// 64 is block tile size BM and BN - change here when BM and BN in the kernel are changed.
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)(CEIL_DIV(ne11, 64)), (size_t)ne12*ne13};
|
||||
// BN=8/TN=1 keeps 128 threads for small batches, including attention KQ/KQV.
|
||||
const int bn = use_narrow_n ? 8 : 64;
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)(CEIL_DIV(ne11, bn)), (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);
|
||||
@@ -29961,6 +30096,136 @@ static void ggml_cl_im2col(ggml_backend_t backend, const ggml_tensor * src0, con
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
}
|
||||
|
||||
static void ggml_cl_im2col_3d(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
GGML_ASSERT(src0);
|
||||
GGML_ASSERT(src1);
|
||||
GGML_ASSERT(src1->extra);
|
||||
GGML_ASSERT(dst);
|
||||
GGML_ASSERT(dst->extra);
|
||||
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_F16 || dst->type == GGML_TYPE_F32);
|
||||
|
||||
ggml_backend_opencl_context *backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
|
||||
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 int32_t s0 = ((const int32_t*)(dst->op_params))[0];
|
||||
const int32_t s1 = ((const int32_t*)(dst->op_params))[1];
|
||||
const int32_t s2 = ((const int32_t*)(dst->op_params))[2];
|
||||
const int32_t p0 = ((const int32_t*)(dst->op_params))[3];
|
||||
const int32_t p1 = ((const int32_t*)(dst->op_params))[4];
|
||||
const int32_t p2 = ((const int32_t*)(dst->op_params))[5];
|
||||
const int32_t d0 = ((const int32_t*)(dst->op_params))[6];
|
||||
const int32_t d1 = ((const int32_t*)(dst->op_params))[7];
|
||||
const int32_t d2 = ((const int32_t*)(dst->op_params))[8];
|
||||
const cl_long IC = ((const int32_t*)(dst->op_params))[9];
|
||||
|
||||
const cl_long N = src1->ne[3] / IC;
|
||||
const cl_long ID = src1->ne[2];
|
||||
const cl_long IH = src1->ne[1];
|
||||
const cl_long IW = src1->ne[0];
|
||||
|
||||
const cl_long KD = src0->ne[2];
|
||||
const cl_long KH = src0->ne[1];
|
||||
const cl_long KW = src0->ne[0];
|
||||
|
||||
const cl_long OD = dst->ne[3] / N;
|
||||
const cl_long OH = dst->ne[2];
|
||||
const cl_long OW = dst->ne[1];
|
||||
|
||||
const size_t es = ggml_element_size(src1);
|
||||
const cl_long stride_x = src1->nb[0] / es;
|
||||
const cl_long stride_y = src1->nb[1] / es;
|
||||
const cl_long stride_z = src1->nb[2] / es;
|
||||
const cl_long stride_q = src1->nb[3] / es;
|
||||
|
||||
const cl_long IC_KD_KH_KW = IC * KD * KH * KW;
|
||||
|
||||
cl_kernel kernel;
|
||||
|
||||
if (dst->type == GGML_TYPE_F16) {
|
||||
kernel = backend_ctx->kernel_im2col_3d_f16;
|
||||
} else {
|
||||
kernel = backend_ctx->kernel_im2col_3d_f32;
|
||||
}
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_long), &IC));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_long), &ID));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_long), &IH));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_long), &IW));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_long), &KD));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_long), &KH));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_long), &KW));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_long), &OD));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_long), &OH));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_long), &OW));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(cl_long), &N));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(cl_long), &stride_x));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(cl_long), &stride_y));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(cl_long), &stride_z));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(cl_long), &stride_q));
|
||||
CL_CHECK(clSetKernelArg(kernel, 19, sizeof(int), &s0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 20, sizeof(int), &s1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 21, sizeof(int), &s2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 22, sizeof(int), &p0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 23, sizeof(int), &p1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 24, sizeof(int), &p2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 25, sizeof(int), &d0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 26, sizeof(int), &d1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 27, sizeof(int), &d2));
|
||||
|
||||
const size_t local_col = 64;
|
||||
const size_t num_blocks = (IC_KD_KH_KW + local_col - 1) / local_col;
|
||||
size_t global_work_size[] = { num_blocks * local_col, (size_t)OW, (size_t)(N * OD * OH) };
|
||||
size_t local_work_size[] = { local_col, 1, 1 };
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
}
|
||||
|
||||
static void ggml_cl_argmax(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
GGML_ASSERT(src0);
|
||||
GGML_ASSERT(src0->extra);
|
||||
GGML_ASSERT(dst);
|
||||
GGML_ASSERT(dst->extra);
|
||||
GGML_UNUSED(src1);
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(dst));
|
||||
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *) backend->context;
|
||||
ggml_tensor_extra_cl * extra0 = (ggml_tensor_extra_cl *) src0->extra;
|
||||
ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *) dst->extra;
|
||||
|
||||
const cl_ulong offset0 = extra0->offset + src0->view_offs;
|
||||
const cl_ulong offsetd = extrad->offset + dst->view_offs;
|
||||
const int ne00 = src0->ne[0];
|
||||
const int nrows = ggml_nrows(src0);
|
||||
constexpr int nth = 64;
|
||||
|
||||
cl_kernel kernel = backend_ctx->kernel_argmax_f32_i32;
|
||||
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), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &nrows));
|
||||
|
||||
size_t global_work_size[] = { (size_t) nth, (size_t) CEIL_DIV(nrows, 2), 1 };
|
||||
size_t local_work_size[] = { (size_t) nth, 1, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
}
|
||||
|
||||
static void ggml_cl_argsort(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
GGML_ASSERT(src0);
|
||||
GGML_ASSERT(src0->extra);
|
||||
@@ -30862,6 +31127,18 @@ bool ggml_cl_compute_forward(ggml_backend_t backend, struct ggml_tensor * tensor
|
||||
}
|
||||
func = ggml_cl_im2col;
|
||||
break;
|
||||
case GGML_OP_IM2COL_3D:
|
||||
if (!any_on_device) {
|
||||
return false;
|
||||
}
|
||||
func = ggml_cl_im2col_3d;
|
||||
break;
|
||||
case GGML_OP_ARGMAX:
|
||||
if (!any_on_device) {
|
||||
return false;
|
||||
}
|
||||
func = ggml_cl_argmax;
|
||||
break;
|
||||
case GGML_OP_ARGSORT:
|
||||
if (!any_on_device) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#define ARGMAX_LANES_PER_ROW 32
|
||||
#define ARGMAX_ROWS_PER_GROUP 2
|
||||
#define ARGMAX_BLOCK_SIZE (ARGMAX_LANES_PER_ROW * ARGMAX_ROWS_PER_GROUP)
|
||||
|
||||
kernel void kernel_argmax_f32_i32(
|
||||
global const char * src0,
|
||||
ulong offset0,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int nrows) {
|
||||
const int local_id = get_local_id(0);
|
||||
const int col = local_id % ARGMAX_LANES_PER_ROW;
|
||||
const int row = get_group_id(1) * ARGMAX_ROWS_PER_GROUP + local_id / ARGMAX_LANES_PER_ROW;
|
||||
|
||||
float max_value = -INFINITY;
|
||||
int max_index = 0;
|
||||
|
||||
if (row < nrows) {
|
||||
global const float * src_row = (global const float *) (src0 + offset0) + row * ne00;
|
||||
for (int i = col; i < ne00; i += ARGMAX_LANES_PER_ROW) {
|
||||
const float value = src_row[i];
|
||||
if (value > max_value || (value == max_value && i > max_index)) {
|
||||
max_value = value;
|
||||
max_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local float max_values[ARGMAX_BLOCK_SIZE];
|
||||
local int max_indices[ARGMAX_BLOCK_SIZE];
|
||||
|
||||
max_values[local_id] = max_value;
|
||||
max_indices[local_id] = max_index;
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
const int row_offset = local_id / ARGMAX_LANES_PER_ROW * ARGMAX_LANES_PER_ROW;
|
||||
for (int stride = ARGMAX_LANES_PER_ROW / 2; stride > 0; stride /= 2) {
|
||||
if (col < stride) {
|
||||
const float value = max_values[row_offset + col + stride];
|
||||
const int index = max_indices[row_offset + col + stride];
|
||||
if (value > max_values[row_offset + col] || (value == max_values[row_offset + col] && index > max_indices[row_offset + col])) {
|
||||
max_values[row_offset + col] = value;
|
||||
max_indices[row_offset + col] = index;
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (col == 0 && row < nrows) {
|
||||
global int * dst_row = (global int *) (dst + offsetd) + row;
|
||||
dst_row[0] = max_indices[row_offset];
|
||||
}
|
||||
}
|
||||
@@ -125,3 +125,129 @@ kernel void kernel_gemm_noshuffle_q4_0_q8_1_dp4a(
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
|
||||
// Stage each 32-K activation block as two uint4 values.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q4_0_q8_1_dp4a_act_vload4(
|
||||
__global const ushort * src0_q,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global const half * src1_sa,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
__local half sh_s[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 float d_w = (float)src0_d[rrow + sub * (uint)m];
|
||||
|
||||
const uint qsbase = rrow + (step >> 2) * (uint)m;
|
||||
uint8 qw;
|
||||
qw.s0 = EXP4(src0_q[qsbase + 0 * m]);
|
||||
qw.s1 = EXP4(src0_q[qsbase + 1 * m]);
|
||||
qw.s2 = EXP4(src0_q[qsbase + 2 * m]);
|
||||
qw.s3 = EXP4(src0_q[qsbase + 3 * m]);
|
||||
qw.s4 = EXP4(src0_q[qsbase + 4 * m]);
|
||||
qw.s5 = EXP4(src0_q[qsbase + 5 * m]);
|
||||
qw.s6 = EXP4(src0_q[qsbase + 6 * m]);
|
||||
qw.s7 = EXP4(src0_q[qsbase + 7 * m]);
|
||||
|
||||
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)
|
||||
? vload4(0, 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;
|
||||
sh_s[lid] = (c < (uint)n_no_padding) ? src1_sa[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 DOTV(T) dot8_q8a_v(qw, sh_qa4[T][0], sh_qa4[T][1])
|
||||
rf.s0 = (float)DOTV(b+0);
|
||||
rf.s1 = (float)DOTV(b+1);
|
||||
rf.s2 = (float)DOTV(b+2);
|
||||
rf.s3 = (float)DOTV(b+3);
|
||||
#undef DOTV
|
||||
|
||||
acc[g] += d_w * (LD4(sh_d, b) * rf - 8.0f * LD4(sh_s, b));
|
||||
}
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -279,3 +279,141 @@ kernel void kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg(
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
|
||||
// Stage each 32-K activation block as two uint4 values.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q4_k_q8_1_dp4a_act_vload4(
|
||||
__global const ushort * src0_q,
|
||||
__global const uchar * src0_s,
|
||||
__global const half * src0_d,
|
||||
__global const half * src0_dm,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global const half * src1_sa,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k,
|
||||
uchar mask_d6,
|
||||
uchar mask_d4,
|
||||
uchar mask_hi2
|
||||
) {
|
||||
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];
|
||||
__local half sh_s[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 sb_idx = step / QK_K;
|
||||
const uint sub_idx = sub & 7;
|
||||
|
||||
const float dd = (float)src0_d [rrow + sb_idx * m];
|
||||
const float dmm = (float)src0_dm[rrow + sb_idx * m];
|
||||
global const uchar * sc = src0_s + sb_idx * K_SCALE_SIZE * (uint)m + rrow;
|
||||
uchar sv, mn;
|
||||
get_scale_min_k4(sub_idx, sc, (uint)m, &sv, &mn, mask_d6, mask_d4, mask_hi2);
|
||||
const float scale = dd * (float)sv;
|
||||
const float minv = dmm * (float)mn;
|
||||
|
||||
const uint wbase = rrow + (step >> 2) * (uint)m;
|
||||
uint8 qw;
|
||||
qw.s0 = EXP4(src0_q[wbase + 0 * m]);
|
||||
qw.s1 = EXP4(src0_q[wbase + 1 * m]);
|
||||
qw.s2 = EXP4(src0_q[wbase + 2 * m]);
|
||||
qw.s3 = EXP4(src0_q[wbase + 3 * m]);
|
||||
qw.s4 = EXP4(src0_q[wbase + 4 * m]);
|
||||
qw.s5 = EXP4(src0_q[wbase + 5 * m]);
|
||||
qw.s6 = EXP4(src0_q[wbase + 6 * m]);
|
||||
qw.s7 = EXP4(src0_q[wbase + 7 * m]);
|
||||
|
||||
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)
|
||||
? vload4(0, 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;
|
||||
sh_s[lid] = (c < (uint)n_no_padding) ? src1_sa[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 DOTV(T) dot8_q8a_v(qw, sh_qa4[T][0], sh_qa4[T][1])
|
||||
rf.s0 = (float)DOTV(b+0);
|
||||
rf.s1 = (float)DOTV(b+1);
|
||||
rf.s2 = (float)DOTV(b+2);
|
||||
rf.s3 = (float)DOTV(b+3);
|
||||
#undef DOTV
|
||||
acc[g] += scale * LD4(sh_d, b) * rf - minv * LD4(sh_s, b);
|
||||
}
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -162,3 +162,141 @@ kernel void kernel_gemm_noshuffle_q5_k_q8_1_dp4a(
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
|
||||
// Stage each 32-K activation block as two uint4 values.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q5_k_q8_1_dp4a_act_vload4(
|
||||
__global const ushort * src0_q,
|
||||
__global const uchar * src0_qh,
|
||||
__global const uchar * src0_s,
|
||||
__global const half * src0_d,
|
||||
__global const half * src0_dm,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global const half * src1_sa,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k,
|
||||
uchar mask_d6,
|
||||
uchar mask_d4,
|
||||
uchar mask_hi2
|
||||
) {
|
||||
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 num_superblocks = (uint)k / QK_K;
|
||||
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];
|
||||
__local half sh_s[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 sb_idx = step / QK_K;
|
||||
const uint sub_idx = sub & 7;
|
||||
|
||||
const float dd = (float)src0_d [rrow + sb_idx * m];
|
||||
const float dmm = (float)src0_dm[rrow + sb_idx * m];
|
||||
global const uchar * sc = src0_s + rrow * num_superblocks * K_SCALE_SIZE + sb_idx * K_SCALE_SIZE;
|
||||
uchar sv, mn;
|
||||
get_scale_min_k4(sub_idx, sc, &sv, &mn, mask_d6, mask_d4, mask_hi2);
|
||||
const float scale = dd * (float)sv;
|
||||
const float minv = dmm * (float)mn;
|
||||
|
||||
const uint wbase = rrow + (step >> 2) * (uint)m;
|
||||
const uint qhbase = rrow + (step >> 3) * (uint)m;
|
||||
uint8 qw;
|
||||
#define QWU(u) ( EXP4((uint)src0_q[wbase + (uint)(u) * m]) \
|
||||
| EXP1( (uint)((src0_qh[qhbase + (uint)((u) >> 1) * m] >> (((u) & 1) * 4)) & 0x0Fu) ) )
|
||||
qw.s0 = QWU(0); qw.s1 = QWU(1); qw.s2 = QWU(2); qw.s3 = QWU(3);
|
||||
qw.s4 = QWU(4); qw.s5 = QWU(5); qw.s6 = QWU(6); qw.s7 = QWU(7);
|
||||
#undef QWU
|
||||
|
||||
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)
|
||||
? vload4(0, 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;
|
||||
sh_s[lid] = (c < (uint)n_no_padding) ? src1_sa[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 DOTV(T) dot8_q8a_v(qw, sh_qa4[T][0], sh_qa4[T][1])
|
||||
rf.s0 = (float)DOTV(b+0);
|
||||
rf.s1 = (float)DOTV(b+1);
|
||||
rf.s2 = (float)DOTV(b+2);
|
||||
rf.s3 = (float)DOTV(b+3);
|
||||
#undef DOTV
|
||||
acc[g] += scale * LD4(sh_d, b) * rf - minv * LD4(sh_s, b);
|
||||
}
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -142,3 +142,117 @@ kernel void kernel_gemm_noshuffle_q6_k_q8_1_dp4a(
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
|
||||
// Stage each 32-K activation block as two uint4 values.
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q6_k_q8_1_dp4a_act_vload4(
|
||||
__global const ushort * src0_ql,
|
||||
__global const uchar * src0_qh,
|
||||
__global const ushort * src0_s,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local 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 sb_idx = step / QK_K;
|
||||
|
||||
const float dd = (float)src0_d[rrow + sb_idx * m];
|
||||
const char2 sc = as_char2(src0_s[rrow + sub * m]);
|
||||
const float scale0 = dd * (float)sc.s0;
|
||||
const float scale1 = dd * (float)sc.s1;
|
||||
|
||||
const uint wbase = rrow + (step >> 2) * (uint)m;
|
||||
uint qw[8];
|
||||
#pragma unroll
|
||||
for (int u = 0; u < 8; ++u) {
|
||||
const uint o = wbase + (uint)u * (uint)m;
|
||||
qw[u] = SIGN6(EXP4((uint)src0_ql[o]) | EXP2((uint)src0_qh[o]));
|
||||
}
|
||||
|
||||
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)
|
||||
? vload4(0, 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);
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define DOT_TOK(j) { \
|
||||
const uint4 a0 = sh_qa4[b + (j)][0]; \
|
||||
const uint4 a1 = sh_qa4[b + (j)][1]; \
|
||||
const int raw1 = dot4_q8a(qw[0], qw[1], qw[2], qw[3], a0.x, a0.y, a0.z, a0.w); \
|
||||
const int raw2 = dot4_q8a(qw[4], qw[5], qw[6], qw[7], a1.x, a1.y, a1.z, a1.w); \
|
||||
rf.s##j = scale0 * (float)raw1 + scale1 * (float)raw2; \
|
||||
}
|
||||
DOT_TOK(0); DOT_TOK(1); DOT_TOK(2); DOT_TOK(3);
|
||||
#undef DOT_TOK
|
||||
const float4 ad = (float4)((float)sh_d[b+0], (float)sh_d[b+1], (float)sh_d[b+2], (float)sh_d[b+3]);
|
||||
acc[g] += ad * rf;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -210,3 +210,125 @@ kernel void kernel_gemm_noshuffle_q8_0_q8_1_dp4a_wimg(
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
|
||||
// Stage each 32-K activation block as two uint4 values.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q8_0_q8_1_dp4a_act_vload4(
|
||||
__global const uint * src0_q,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local 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 float d_w = (float)src0_d[rrow + sub * (uint)m];
|
||||
|
||||
const uint wbase = rrow + (step >> 2) * (uint)m;
|
||||
uint8 qw;
|
||||
qw.s0 = src0_q[wbase + 0 * m];
|
||||
qw.s1 = src0_q[wbase + 1 * m];
|
||||
qw.s2 = src0_q[wbase + 2 * m];
|
||||
qw.s3 = src0_q[wbase + 3 * m];
|
||||
qw.s4 = src0_q[wbase + 4 * m];
|
||||
qw.s5 = src0_q[wbase + 5 * m];
|
||||
qw.s6 = src0_q[wbase + 6 * m];
|
||||
qw.s7 = src0_q[wbase + 7 * m];
|
||||
|
||||
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)
|
||||
? vload4(0, 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 DOTV(T) dot8_q8a_v(qw, sh_qa4[T][0], sh_qa4[T][1])
|
||||
rf.s0 = (float)DOTV(b+0);
|
||||
rf.s1 = (float)DOTV(b+1);
|
||||
rf.s2 = (float)DOTV(b+2);
|
||||
rf.s3 = (float)DOTV(b+3);
|
||||
#undef DOTV
|
||||
acc[g] += d_w * 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
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#endif
|
||||
|
||||
#define QK1_0 128
|
||||
#ifndef N_SIMDGROUP
|
||||
#define N_SIMDGROUP 4
|
||||
#endif
|
||||
|
||||
#define dequantizeBlockAccum_q1(total, bits, scale, regB, lb) \
|
||||
total += (2.0f*(float)((bits >> 0) & 1u) - 1.0f) * scale * sub_group_broadcast(regB.s0, lb+0); \
|
||||
@@ -106,15 +108,19 @@ __kernel void kernel_gemv_noshuffle_q1_0_f32(
|
||||
dequantizeBlockAccum_q1(totalSum, regA.s3, scale, regB, 12);
|
||||
}
|
||||
|
||||
// reduction in local memory, assumes #wave = N_SIMDGROUP = 4
|
||||
local float reduceLM[SIMDGROUP_WIDTH * 3];
|
||||
if (groupId == 1) reduceLM[SIMDGROUP_WIDTH * 0 + slid] = totalSum;
|
||||
if (groupId == 2) reduceLM[SIMDGROUP_WIDTH * 1 + slid] = totalSum;
|
||||
if (groupId == 3) reduceLM[SIMDGROUP_WIDTH * 2 + slid] = totalSum;
|
||||
#if N_SIMDGROUP > 1
|
||||
// Sum the K slices from the other subgroups.
|
||||
local float reduceLM[SIMDGROUP_WIDTH * (N_SIMDGROUP - 1)];
|
||||
if (groupId > 0) {
|
||||
reduceLM[SIMDGROUP_WIDTH * (groupId - 1) + slid] = totalSum;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 0 + slid];
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 1 + slid];
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 2 + slid];
|
||||
if (groupId == 0) {
|
||||
for (uint w = 0; w < N_SIMDGROUP - 1; ++w) {
|
||||
totalSum += reduceLM[SIMDGROUP_WIDTH * w + slid];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
@@ -122,6 +128,8 @@ __kernel void kernel_gemv_noshuffle_q1_0_f32(
|
||||
// so when ne01 is not a multiple of the wave size the tail work-items run past
|
||||
// row ne01 and would overrun dst into the adjacent tensor. No-op / byte-identical
|
||||
// when ne01 is wave-aligned (no padding).
|
||||
if (gid < M) dst[gid] = totalSum;
|
||||
if (gid < M) {
|
||||
dst[gid] = totalSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
kernel void kernel_im2col_3d_f16(
|
||||
global float * src1,
|
||||
ulong offset1,
|
||||
global half * dst,
|
||||
ulong offsetd,
|
||||
long IC,
|
||||
long ID,
|
||||
long IH,
|
||||
long IW,
|
||||
long KD,
|
||||
long KH,
|
||||
long KW,
|
||||
long OD,
|
||||
long OH,
|
||||
long OW,
|
||||
long N,
|
||||
long stride_x,
|
||||
long stride_y,
|
||||
long stride_z,
|
||||
long stride_q,
|
||||
int s0,
|
||||
int s1,
|
||||
int s2,
|
||||
int p0,
|
||||
int p1,
|
||||
int p2,
|
||||
int d0,
|
||||
int d1,
|
||||
int d2
|
||||
) {
|
||||
const int ic = (int)IC;
|
||||
const int kd = (int)KD;
|
||||
const int kh = (int)KH;
|
||||
const int kw = (int)KW;
|
||||
const int od = (int)OD;
|
||||
const int oh = (int)OH;
|
||||
const int ow = (int)OW;
|
||||
const int n = (int)N;
|
||||
|
||||
const int KD_KH_KW = kd * kh * kw;
|
||||
const int KH_KW = kh * kw;
|
||||
const int IC_KD_KH_KW = ic * KD_KH_KW;
|
||||
|
||||
const int i = (int)get_global_id(0);
|
||||
if (i >= IC_KD_KH_KW) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int iow = (int)get_global_id(1);
|
||||
if (iow >= ow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int OD_OH = od * oh;
|
||||
const int iz = (int)get_global_id(2);
|
||||
if (iz >= n * OD_OH) {
|
||||
return;
|
||||
}
|
||||
|
||||
src1 = (global float*)((global char*)src1 + offset1);
|
||||
dst = (global half*)((global char*)dst + offsetd);
|
||||
|
||||
const int iic = i / KD_KH_KW;
|
||||
const int ikd = (i - iic * KD_KH_KW) / KH_KW;
|
||||
const int ikh = (i - iic * KD_KH_KW - ikd * KH_KW) / kw;
|
||||
const int ikw = i % kw;
|
||||
|
||||
const int in = iz / OD_OH;
|
||||
const int iod = (iz - in * OD_OH) / oh;
|
||||
const int ioh = iz % oh;
|
||||
|
||||
const int iiw = iow * s0 + ikw * d0 - p0;
|
||||
const int iih = ioh * s1 + ikh * d1 - p1;
|
||||
const int iid = iod * s2 + ikd * d2 - p2;
|
||||
|
||||
const long offset_dst =
|
||||
(long)(iz * ow + iow) * (long)IC_KD_KH_KW +
|
||||
(long)(iic * KD_KH_KW + ikd * KH_KW + ikh * kw + ikw);
|
||||
|
||||
if (iih < 0 || iih >= IH || iiw < 0 || iiw >= IW || iid < 0 || iid >= ID) {
|
||||
dst[offset_dst] = 0.0f;
|
||||
} else {
|
||||
const long offset_src =
|
||||
(long)(in * ic + iic) * stride_q + (long)iid * stride_z +
|
||||
(long)iih * stride_y + (long)iiw * stride_x;
|
||||
dst[offset_dst] = convert_half_rte(src1[offset_src]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
kernel void kernel_im2col_3d_f32(
|
||||
global float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
long IC,
|
||||
long ID,
|
||||
long IH,
|
||||
long IW,
|
||||
long KD,
|
||||
long KH,
|
||||
long KW,
|
||||
long OD,
|
||||
long OH,
|
||||
long OW,
|
||||
long N,
|
||||
long stride_x,
|
||||
long stride_y,
|
||||
long stride_z,
|
||||
long stride_q,
|
||||
int s0,
|
||||
int s1,
|
||||
int s2,
|
||||
int p0,
|
||||
int p1,
|
||||
int p2,
|
||||
int d0,
|
||||
int d1,
|
||||
int d2
|
||||
) {
|
||||
const int ic = (int)IC;
|
||||
const int kd = (int)KD;
|
||||
const int kh = (int)KH;
|
||||
const int kw = (int)KW;
|
||||
const int od = (int)OD;
|
||||
const int oh = (int)OH;
|
||||
const int ow = (int)OW;
|
||||
const int n = (int)N;
|
||||
|
||||
const int KD_KH_KW = kd * kh * kw;
|
||||
const int KH_KW = kh * kw;
|
||||
const int IC_KD_KH_KW = ic * KD_KH_KW;
|
||||
|
||||
const int i = (int)get_global_id(0);
|
||||
if (i >= IC_KD_KH_KW) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int iow = (int)get_global_id(1);
|
||||
if (iow >= ow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int OD_OH = od * oh;
|
||||
const int iz = (int)get_global_id(2);
|
||||
if (iz >= n * OD_OH) {
|
||||
return;
|
||||
}
|
||||
|
||||
src1 = (global float*)((global char*)src1 + offset1);
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
|
||||
const int iic = i / KD_KH_KW;
|
||||
const int ikd = (i - iic * KD_KH_KW) / KH_KW;
|
||||
const int ikh = (i - iic * KD_KH_KW - ikd * KH_KW) / kw;
|
||||
const int ikw = i % kw;
|
||||
|
||||
const int in = iz / OD_OH;
|
||||
const int iod = (iz - in * OD_OH) / oh;
|
||||
const int ioh = iz % oh;
|
||||
|
||||
const int iiw = iow * s0 + ikw * d0 - p0;
|
||||
const int iih = ioh * s1 + ikh * d1 - p1;
|
||||
const int iid = iod * s2 + ikd * d2 - p2;
|
||||
|
||||
const long offset_dst =
|
||||
(long)(iz * ow + iow) * (long)IC_KD_KH_KW +
|
||||
(long)(iic * KD_KH_KW + ikd * KH_KW + ikh * kw + ikw);
|
||||
|
||||
if (iih < 0 || iih >= IH || iiw < 0 || iiw >= IW || iid < 0 || iid >= ID) {
|
||||
dst[offset_dst] = 0.0f;
|
||||
} else {
|
||||
const long offset_src =
|
||||
(long)(in * ic + iic) * stride_q + (long)iid * stride_z +
|
||||
(long)iih * stride_y + (long)iiw * stride_x;
|
||||
dst[offset_dst] = src1[offset_src];
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,20 @@
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#ifndef BN
|
||||
#define BN 64
|
||||
#endif
|
||||
#define BK 16
|
||||
#define TM 4
|
||||
#ifndef TN
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_f16_f32_l4_lm(
|
||||
#ifndef KERNEL_NAME_LM
|
||||
#define KERNEL_NAME_LM kernel_mul_mm_f16_f32_l4_lm
|
||||
#endif
|
||||
|
||||
kernel void KERNEL_NAME_LM(
|
||||
global half4 * src0,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
@@ -93,7 +101,8 @@ kernel void kernel_mul_mm_f16_f32_l4_lm(
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
// BN=8 uses only the threads that load a column inside the tile.
|
||||
for (int l = 0; loadc_b + l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
const 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;
|
||||
|
||||
@@ -132,7 +132,7 @@ kernel void kernel_mul_mv_mxfp4_f32_flat(
|
||||
global float4 * y4 = (global float4 *)yb;
|
||||
|
||||
#pragma unroll
|
||||
for (short row = 0; row < N_R0_MXFP4; row++) {
|
||||
for (short row = 0; row < N_R0_MXFP4 && first_row + row < ne0; row++) {
|
||||
uchar xb_e = x_e[row * nb + ib];
|
||||
#ifdef SRC0Q_IMG
|
||||
ushort4 xb_q = as_ushort4(read_imageui(src0_q, (offset_q + row * nb + ib) * 2 + it).xy);
|
||||
|
||||
@@ -520,7 +520,7 @@ kernel void kernel_rope_multi_f16(
|
||||
src0 = (global void*)((global char*)src0 + offset0);
|
||||
src1 = (global int*)((global char*)src1 + offset1);
|
||||
src2 = (global float*)((global char*)src2 + offset2);
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
dst = (global half*)((global char*)dst + offsetd);
|
||||
|
||||
int i3 = get_group_id(2);
|
||||
int i2 = get_group_id(1);
|
||||
@@ -714,7 +714,7 @@ kernel void kernel_rope_vision_f16(
|
||||
src0 = (global void*)((global char*)src0 + offset0);
|
||||
src1 = (global int*)((global char*)src1 + offset1);
|
||||
src2 = (global float*)((global char*)src2 + offset2);
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
dst = (global half*)((global char*)dst + offsetd);
|
||||
|
||||
int i3 = get_group_id(2);
|
||||
int i2 = get_group_id(1);
|
||||
|
||||
@@ -53,7 +53,16 @@ static inline int64_t whcn_output_index(int64_t n, int64_t c, int64_t y, int64_t
|
||||
return n * (P.OC * P.OW * P.OH) + c * P.OW * P.OH + y * P.OW + x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
// cwhn layout helpers: buffers are [N][H][W][C] input and [KH][KW][IC][OC] kernel
|
||||
static inline int64_t cwhn_input_index(int64_t n, int64_t c, int64_t y, int64_t x, const conv2d_params & P) {
|
||||
return ((n * P.IH + y) * P.IW + x) * P.IC + c;
|
||||
}
|
||||
|
||||
static inline int64_t cwhn_kernel_index(int64_t c_out, int64_t c_in, int64_t ky, int64_t kx, const conv2d_params & P) {
|
||||
return ((ky * P.KW + kx) * P.IC + c_in) * P.OC + c_out;
|
||||
}
|
||||
|
||||
template <typename T, bool CWHN>
|
||||
static void conv2d_kernel(const float * input, const T * kernel, float * output,
|
||||
const conv2d_params P, const sycl::nd_item<3> & item_ct1) {
|
||||
const int64_t global_idx = item_ct1.get_local_id(2) +
|
||||
@@ -77,8 +86,8 @@ static void conv2d_kernel(const float * input, const T * kernel, float * output,
|
||||
const int64_t in_y = calculate_input_coord(out_y, ky, P.ST_Y, P.DL_Y, P.PD_Y);
|
||||
for (int64_t kx = bounds.x_min; kx < bounds.x_max; ++kx) {
|
||||
const int64_t in_x = calculate_input_coord(out_x, kx, P.ST_X, P.DL_X, P.PD_X);
|
||||
const float input_val = input[whcn_input_index(n, c_in, in_y, in_x, P)];
|
||||
const T kernel_val = kernel[whcn_kernel_index(c_out, c_in, ky, kx, P)];
|
||||
const float input_val = input[CWHN ? cwhn_input_index(n, c_in, in_y, in_x, P) : whcn_input_index(n, c_in, in_y, in_x, P)];
|
||||
const T kernel_val = kernel[CWHN ? cwhn_kernel_index(c_out, c_in, ky, kx, P) : whcn_kernel_index(c_out, c_in, ky, kx, P)];
|
||||
acc += input_val * ggml_sycl_cast<float>(kernel_val);
|
||||
}
|
||||
}
|
||||
@@ -87,7 +96,7 @@ static void conv2d_kernel(const float * input, const T * kernel, float * output,
|
||||
output[whcn_output_index(n, c_out, out_y, out_x, P)] = acc;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename T, bool CWHN>
|
||||
static void conv2d_sycl(const float * X_D, const T * K_D, float * Y_D,
|
||||
const conv2d_params P, const queue_ptr & stream) {
|
||||
const int num_blocks = (P.TOTAL + SYCL_CONV2D_BLOCK_SIZE - 1) / SYCL_CONV2D_BLOCK_SIZE;
|
||||
@@ -95,7 +104,7 @@ static void conv2d_sycl(const float * X_D, const T * K_D, float * Y_D,
|
||||
const sycl::range<3> block_nums(1, 1, num_blocks);
|
||||
stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
conv2d_kernel<T>(X_D, K_D, Y_D, P, item_ct1);
|
||||
conv2d_kernel<T, CWHN>(X_D, K_D, Y_D, P, item_ct1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,7 +117,14 @@ void ggml_sycl_op_conv2d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const float * X_D = (const float *) input->data;
|
||||
float * Y_D = (float *) dst->data;
|
||||
|
||||
GGML_ASSERT(ggml_is_contiguous(kernel));
|
||||
// WHCN: contiguous [KW,KH,IC,OC] kernel and [W,H,C,N] input
|
||||
// CWHN: channel-fastest buffer [N][H][W][C] input and [KH][KW][IC][OC] kernel (OC fastest)
|
||||
const bool whcn = ggml_is_contiguous(kernel) && ggml_is_contiguous(input);
|
||||
const bool cwhn = input->nb[2] == ggml_type_size(input->type) &&
|
||||
kernel->nb[3] == ggml_type_size(kernel->type);
|
||||
GGML_ASSERT(whcn || cwhn);
|
||||
const bool cwhn_layout = !whcn && cwhn; // prefer whcn when both match (degenerate size-1 dims)
|
||||
|
||||
GGML_ASSERT(kernel->type == GGML_TYPE_F16 || kernel->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(input->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
||||
@@ -126,9 +142,6 @@ void ggml_sycl_op_conv2d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const int DL_X = p[4];
|
||||
const int DL_Y = p[5];
|
||||
|
||||
// no cwhn layout support
|
||||
GGML_ASSERT(p[6] == 0);
|
||||
|
||||
const int IW = input->ne[0];
|
||||
const int IH = input->ne[1];
|
||||
const int OW = dst->ne[0];
|
||||
@@ -142,9 +155,17 @@ void ggml_sycl_op_conv2d(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const int64_t total = (int64_t) B * OC * OH * OW;
|
||||
const conv2d_params params = { IW, IH, OW, OH, KW, KH, ST_X, ST_Y, PD_X, PD_Y, DL_X, DL_Y, IC, OC, B, total };
|
||||
|
||||
if (kernel->type == GGML_TYPE_F16) {
|
||||
conv2d_sycl<sycl::half>(X_D, (const sycl::half *) K_D, Y_D, params, stream);
|
||||
if (cwhn_layout) {
|
||||
if (kernel->type == GGML_TYPE_F16) {
|
||||
conv2d_sycl<sycl::half, true>(X_D, (const sycl::half *) K_D, Y_D, params, stream);
|
||||
} else {
|
||||
conv2d_sycl<float, true>(X_D, K_D, Y_D, params, stream);
|
||||
}
|
||||
} else {
|
||||
conv2d_sycl<float>(X_D, K_D, Y_D, params, stream);
|
||||
if (kernel->type == GGML_TYPE_F16) {
|
||||
conv2d_sycl<sycl::half, false>(X_D, (const sycl::half *) K_D, Y_D, params, stream);
|
||||
} else {
|
||||
conv2d_sycl<float, false>(X_D, K_D, Y_D, params, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-134
@@ -177,15 +177,12 @@ static void dequantize_row_q8_0_sycl_reorder(const void *vx, dst_t *y, const int
|
||||
dpct::has_capability_or_fail(stream->get_device(),
|
||||
{sycl::aspect::fp16});
|
||||
|
||||
// flat range<1>, two pairs (4 elements) per item: no nd_range/group math, one vectorized store.
|
||||
// k % QK8_0 == 0 implies k % 4 == 0, so the wide launch covers the row exactly (no tail).
|
||||
GGML_ASSERT(k % QK8_0 == 0);
|
||||
const int64_t n_groups = ceil_div(k / 2, SYCL_DEQUANTIZE_BLOCK_SIZE);
|
||||
// decrease global range when it exceeds the max int; the kernel loops to cover the rest
|
||||
const int64_t wg_size = downsample_sycl_global_range(n_groups, SYCL_DEQUANTIZE_BLOCK_SIZE);
|
||||
stream->parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, n_groups) *
|
||||
sycl::range<3>(1, 1, wg_size),
|
||||
sycl::range<3>(1, 1, wg_size)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q8_0_reorder(vx, y, k, item_ct1);
|
||||
stream->parallel_for(sycl::range<1>((size_t) (k / 4)),
|
||||
[=](sycl::id<1> idx) {
|
||||
dequantize_block_q8_0_reorder_wide(vx, y, k, idx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -440,7 +437,8 @@ static void dequantize_row_iq3_xxs_sycl_reorder(const void * vx, dst_t * y, cons
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 32), sycl::range<3>(1, 1, 32)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_iq3_xxs_reorder(
|
||||
vx, y, item_ct1, nb, iq3xxs_grid, ksigns_iq2xs, kmask_iq2xs);
|
||||
vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), item_ct1, nb,
|
||||
iq3xxs_grid, ksigns_iq2xs, kmask_iq2xs);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -472,7 +470,8 @@ static void dequantize_row_iq3_s_sycl_reorder(const void * vx, dst_t * y, const
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 32), sycl::range<3>(1, 1, 32)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_iq3_s_reorder(vx, y, item_ct1, nb, kmask_iq2xs, iq3s_grid);
|
||||
dequantize_block_iq3_s_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2),
|
||||
item_ct1, nb, kmask_iq2xs, iq3s_grid);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -658,58 +657,68 @@ static void convert_unary_sycl(const void * vx, dst_t * y, const int64_t k, dpct
|
||||
}
|
||||
|
||||
|
||||
to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
static bool is_reordered_src0(const ggml_tensor * dst) {
|
||||
return dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder;
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
static to_t_sycl_t<dst_t> ggml_get_dequant_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
return dequantize_block_sycl<QK1_0, QR1_0, dequantize_q1_0>;
|
||||
return dequantize_block_sycl<QK1_0, QR1_0, dequantize_q1_0, dst_t>;
|
||||
case GGML_TYPE_Q2_0:
|
||||
return dequantize_block_sycl<QK2_0, QR2_0, dequantize_q2_0>;
|
||||
return dequantize_block_sycl<QK2_0, QR2_0, dequantize_q2_0, dst_t>;
|
||||
case GGML_TYPE_Q4_0:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu*)dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q4_0_sycl_reorder;
|
||||
} else if constexpr (std::is_same_v<dst_t, float>) {
|
||||
return dequantize_row_q4_0_sycl;
|
||||
} else {
|
||||
return dequantize_block_sycl<QK4_0, QR4_0, dequantize_q4_0>;
|
||||
return dequantize_block_sycl<QK4_0, QR4_0, dequantize_q4_0, dst_t>;
|
||||
}
|
||||
case GGML_TYPE_Q4_1:
|
||||
return dequantize_block_sycl<QK4_1, QR4_1, dequantize_q4_1>;
|
||||
if constexpr (std::is_same_v<dst_t, float>) {
|
||||
return dequantize_row_q4_1_sycl;
|
||||
} else {
|
||||
return dequantize_block_sycl<QK4_1, QR4_1, dequantize_q4_1, dst_t>;
|
||||
}
|
||||
case GGML_TYPE_Q5_0:
|
||||
return dequantize_block_sycl<QK5_0, QR5_0, dequantize_q5_0>;
|
||||
return dequantize_block_sycl<QK5_0, QR5_0, dequantize_q5_0, dst_t>;
|
||||
case GGML_TYPE_Q5_1:
|
||||
return dequantize_block_sycl<QK5_1, QR5_1, dequantize_q5_1>;
|
||||
return dequantize_block_sycl<QK5_1, QR5_1, dequantize_q5_1, dst_t>;
|
||||
case GGML_TYPE_Q8_0:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q8_0_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
|
||||
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0, dst_t>;
|
||||
}
|
||||
case GGML_TYPE_Q2_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q2_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q2_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q3_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q3_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q3_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q4_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q4_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q4_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q5_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q5_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q5_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q6_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_q6_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q6_K_sycl;
|
||||
@@ -725,15 +734,13 @@ to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
case GGML_TYPE_IQ2_S:
|
||||
return dequantize_row_iq2_s_sycl;
|
||||
case GGML_TYPE_IQ3_XXS:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_iq3_xxs_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_iq3_xxs_sycl;
|
||||
}
|
||||
case GGML_TYPE_IQ3_S:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_iq3_s_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_iq3_s_sycl;
|
||||
@@ -743,124 +750,38 @@ to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
return dequantize_row_iq4_nl_sycl;
|
||||
case GGML_TYPE_MXFP4:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
if (is_reordered_src0(dst)) {
|
||||
return dequantize_row_mxfp4_sycl_reorder;
|
||||
}
|
||||
return dequantize_row_mxfp4_sycl;
|
||||
case GGML_TYPE_NVFP4:
|
||||
return dequantize_row_nvfp4_sycl;
|
||||
case GGML_TYPE_F32:
|
||||
return convert_unary_sycl<float>;
|
||||
if constexpr (std::is_same_v<dst_t, sycl::half>) {
|
||||
return convert_unary_sycl<float>;
|
||||
}
|
||||
break;
|
||||
case GGML_TYPE_F16:
|
||||
if constexpr (std::is_same_v<dst_t, float>) {
|
||||
return convert_unary_sycl<sycl::half>;
|
||||
}
|
||||
break;
|
||||
#ifdef GGML_SYCL_HAS_BF16
|
||||
case GGML_TYPE_BF16:
|
||||
return convert_unary_sycl<sycl::ext::oneapi::bfloat16>;
|
||||
#endif
|
||||
default:
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
break;
|
||||
}
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
}
|
||||
|
||||
to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
return ggml_get_dequant_sycl<sycl::half>(type, dst);
|
||||
}
|
||||
|
||||
to_fp32_sycl_t ggml_get_to_fp32_sycl(ggml_type type, ggml_tensor *dst) {
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
return dequantize_block_sycl<QK1_0, QR1_0, dequantize_q1_0>;
|
||||
case GGML_TYPE_Q2_0:
|
||||
return dequantize_block_sycl<QK2_0, QR2_0, dequantize_q2_0>;
|
||||
case GGML_TYPE_Q4_0:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu*)dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q4_0_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q4_0_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q4_1:
|
||||
return dequantize_row_q4_1_sycl;
|
||||
case GGML_TYPE_Q5_0:
|
||||
return dequantize_block_sycl<QK5_0, QR5_0, dequantize_q5_0>;
|
||||
case GGML_TYPE_Q5_1:
|
||||
return dequantize_block_sycl<QK5_1, QR5_1, dequantize_q5_1>;
|
||||
case GGML_TYPE_Q8_0:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu*)dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q8_0_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_block_sycl<QK8_0, QR8_0, dequantize_q8_0>;
|
||||
}
|
||||
case GGML_TYPE_Q2_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q2_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q2_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q3_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q3_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q3_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q4_K:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu*)dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q4_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q4_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q5_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q5_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q5_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_Q6_K:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_q6_K_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_q6_K_sycl;
|
||||
}
|
||||
case GGML_TYPE_IQ1_S:
|
||||
return dequantize_row_iq1_s_sycl;
|
||||
case GGML_TYPE_IQ1_M:
|
||||
return dequantize_row_iq1_m_sycl;
|
||||
case GGML_TYPE_IQ2_XXS:
|
||||
return dequantize_row_iq2_xxs_sycl;
|
||||
case GGML_TYPE_IQ2_XS:
|
||||
return dequantize_row_iq2_xs_sycl;
|
||||
case GGML_TYPE_IQ2_S:
|
||||
return dequantize_row_iq2_s_sycl;
|
||||
case GGML_TYPE_IQ3_XXS:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_iq3_xxs_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_iq3_xxs_sycl;
|
||||
}
|
||||
case GGML_TYPE_IQ3_S:
|
||||
if (dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_iq3_s_sycl_reorder;
|
||||
} else {
|
||||
return dequantize_row_iq3_s_sycl;
|
||||
}
|
||||
case GGML_TYPE_IQ4_XS:
|
||||
return dequantize_row_iq4_xs_sycl;
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
return dequantize_row_iq4_nl_sycl;
|
||||
case GGML_TYPE_MXFP4:
|
||||
if (dst->src[0]->extra && ((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
return dequantize_row_mxfp4_sycl_reorder;
|
||||
}
|
||||
return dequantize_row_mxfp4_sycl;
|
||||
case GGML_TYPE_NVFP4:
|
||||
return dequantize_row_nvfp4_sycl;
|
||||
case GGML_TYPE_F16:
|
||||
return convert_unary_sycl<sycl::half>;
|
||||
#ifdef GGML_SYCL_HAS_BF16
|
||||
case GGML_TYPE_BF16:
|
||||
return convert_unary_sycl<sycl::ext::oneapi::bfloat16>;
|
||||
#endif
|
||||
default:
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
}
|
||||
return ggml_get_dequant_sycl<float>(type, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -857,6 +857,26 @@ static void dequantize_block_q8_0_reorder(const void * __restrict__ vx, dst_t *
|
||||
}
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_q8_0_reorder_wide(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
|
||||
const sycl::id<1> idx) {
|
||||
const int64_t i = 4 * (int64_t) idx[0];
|
||||
|
||||
const int8_t * qs = (const int8_t *) vx;
|
||||
const sycl::half * d_ptr = (const sycl::half *) ((const uint8_t *) vx + k);
|
||||
|
||||
const float d0 = float(d_ptr[i / QK8_0]);
|
||||
const float d1 = float(d_ptr[(i + 2) / QK8_0]);
|
||||
|
||||
sycl::vec<float, 4> p;
|
||||
p[0] = d0 * (float) qs[i + 0];
|
||||
p[1] = d0 * (float) qs[i + 1];
|
||||
p[2] = d1 * (float) qs[i + 2];
|
||||
p[3] = d1 * (float) qs[i + 3];
|
||||
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(yy + i) = p.convert<dst_t, sycl::rounding_mode::automatic>();
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_q4_1(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t nb32,
|
||||
const sycl::nd_item<3> &item_ct1) {
|
||||
@@ -1481,12 +1501,11 @@ static void dequantize_block_iq3_xxs(const void * __restrict__ vx, dst_t * __res
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_iq3_xxs_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
static void dequantize_block_iq3_xxs_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t i,
|
||||
const sycl::nd_item<3> & item_ct1, const int64_t nb,
|
||||
const uint32_t * iq3xxs_grid,
|
||||
const uint8_t * ksigns_iq2xs,
|
||||
const uint8_t * kmask_iq2xs) {
|
||||
const int64_t i = item_ct1.get_group(2);
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
#if QK_K == 256
|
||||
const int64_t il = tid / 8;
|
||||
@@ -1501,7 +1520,7 @@ static void dequantize_block_iq3_xxs_reorder(const void * __restrict__ vx, dst_t
|
||||
const uint32_t aux32 = gas[0] | (gas[1] << 16);
|
||||
const float dl = (float) d[i] * (0.5f + (aux32 >> 28)) * 0.5f;
|
||||
const uint8_t signs = ksigns_iq2xs[(aux32 >> (7 * il)) & 127];
|
||||
dst_t * y = yy + i * QK_K + 32 * ib + 8 * il;
|
||||
dst_t * y = yy + 32 * ib + 8 * il;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
y[j+0] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
@@ -1510,6 +1529,7 @@ static void dequantize_block_iq3_xxs_reorder(const void * __restrict__ vx, dst_t
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
GGML_UNUSED(i);
|
||||
GGML_UNUSED(item_ct1);
|
||||
GGML_UNUSED(nb);
|
||||
GGML_UNUSED(iq3xxs_grid);
|
||||
@@ -1551,10 +1571,9 @@ dequantize_block_iq3_s(const void *__restrict__ vx, dst_t *__restrict__ yy,
|
||||
|
||||
template <typename dst_t>
|
||||
__dpct_inline__ static void
|
||||
dequantize_block_iq3_s_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
dequantize_block_iq3_s_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t i,
|
||||
const sycl::nd_item<3> & item_ct1, const int64_t nb,
|
||||
const uint8_t * kmask_iq2xs, const uint32_t * iq3s_grid) {
|
||||
const int64_t i = item_ct1.get_group(2);
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
#if QK_K == 256
|
||||
const int64_t il = tid / 8;
|
||||
@@ -1574,7 +1593,7 @@ dequantize_block_iq3_s_reorder(const void * __restrict__ vx, dst_t * __restrict_
|
||||
iq3s_grid + (qs_ib[2*il+1] | ((qh[ib] << (7 - 2*il)) & 256)));
|
||||
const float dl = (float) d * (1 + 2 * ((scales[ib/2] >> (4 * (ib%2))) & 0xf));
|
||||
const uint8_t signs = signs_base[4 * ib + il];
|
||||
dst_t * y = yy + i * QK_K + 32 * ib + 8 * il;
|
||||
dst_t * y = yy + 32 * ib + 8 * il;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
y[j+0] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
@@ -1583,6 +1602,7 @@ dequantize_block_iq3_s_reorder(const void * __restrict__ vx, dst_t * __restrict_
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
GGML_UNUSED(i);
|
||||
GGML_UNUSED(item_ct1);
|
||||
GGML_UNUSED(nb);
|
||||
GGML_UNUSED(kmask_iq2xs);
|
||||
|
||||
@@ -202,7 +202,8 @@ bool ggml_sycl_flash_attn_ext_sparse(ggml_backend_sycl_context & ctx, ggml_tenso
|
||||
uint8_t * d_V = v_alloc.get();
|
||||
sycl::half * d_mask = m_alloc.get();
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memset(d_cnt, 0, sizeof(int32_t))));
|
||||
// reset with a kernel: on Level Zero a queue memset is not always ordered before the next kernel
|
||||
stream->single_task([=]() { *d_cnt = 0; });
|
||||
|
||||
sparse_fa_compact_mask(stream, (const sycl::half *) mask->data,
|
||||
d_idx, d_cnt, n_kv, n_kv_g);
|
||||
|
||||
@@ -281,6 +281,22 @@ static best_fattn_kernel ggml_sycl_get_best_fattn_kernel(const int device, const
|
||||
void ggml_sycl_flash_attn_ext(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
ggml_sycl_set_device(ctx.device);
|
||||
|
||||
ggml_tensor * Q = dst->src[0];
|
||||
ggml_tensor * K = dst->src[1];
|
||||
ggml_tensor * V = dst->src[2];
|
||||
if (K && Q && K->ne[0] != Q->ne[0] && Q->ne[0] > 0 && K->ne[0] % Q->ne[0] == 0) {
|
||||
const int64_t d = Q->ne[0];
|
||||
const int64_t n_heads = (K->ne[0] * K->ne[2]) / d;
|
||||
K->ne[0] = d;
|
||||
K->ne[2] = n_heads;
|
||||
}
|
||||
if (V && Q && V->ne[0] != Q->ne[0] && Q->ne[0] > 0 && V->ne[0] % Q->ne[0] == 0) {
|
||||
const int64_t d = Q->ne[0];
|
||||
const int64_t n_heads = (V->ne[0] * V->ne[2]) / d;
|
||||
V->ne[0] = d;
|
||||
V->ne[2] = n_heads;
|
||||
}
|
||||
|
||||
// sparse nodes are gathered down to n_kv_max rows and re-dispatched here
|
||||
if (ggml_sycl_flash_attn_ext_sparse(ctx, dst)) {
|
||||
return;
|
||||
|
||||
@@ -860,7 +860,7 @@ static void fused_dequant_gemm_tile(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename S, typename block_q_t>
|
||||
template <typename S, typename block_q_t, bool reordered = false>
|
||||
static void fused_dequant_gemm_launch(const void * src0, const typename S::tsb * packed, float * dst, const int M,
|
||||
const int N, const int Npad, const int K, const int ldd,
|
||||
const int64_t groups_n, const int64_t groups_m, dpct::queue_ptr stream) {
|
||||
@@ -871,8 +871,8 @@ static void fused_dequant_gemm_launch(const void * src0, const typename S::tsb *
|
||||
sycl::nd_range<2>(sycl::range<2>(groups_n, groups_m * S::WG_SIZE), sycl::range<2>(1, S::WG_SIZE)),
|
||||
[=](sycl::nd_item<2> item) [[sycl::reqd_sub_group_size(S::SG)]] {
|
||||
const int n0 = item.get_group(0) * S::BN;
|
||||
fused_dequant_gemm_tile<S, block_q_t, false>((const block_q_t *) src0, packed, dst, M, Npad, K, ldd,
|
||||
n0, n0, N, tile_a, tile_c, item);
|
||||
fused_dequant_gemm_tile<S, block_q_t, reordered>((const block_q_t *) src0, packed, dst, M, Npad, K,
|
||||
ldd, n0, n0, N, tile_a, tile_c, item);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -974,7 +974,7 @@ template <typename F> static bool fg_visit_type(ggml_type type, bool reordered,
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
static bool fg_fused_run(ggml_type src0_type, const void * src0, const void * src1, ggml_type src1_type,
|
||||
static bool fg_fused_run(ggml_type src0_type, bool src0_reordered, const void * src0, const void * src1, ggml_type src1_type,
|
||||
float * dst, int64_t M, int64_t N, int64_t K, int64_t ldd, ggml_sycl_pool & pool,
|
||||
dpct::queue_ptr stream) {
|
||||
const int64_t groups_n = (N + S::BN - 1) / S::BN;
|
||||
@@ -992,21 +992,27 @@ static bool fg_fused_run(ggml_type src0_type, const void * src0, const void * sr
|
||||
}
|
||||
|
||||
const typename S::tsb * packed = packed_b.get();
|
||||
return fg_visit_type(src0_type, false, [&](auto tag) {
|
||||
return fg_visit_type(src0_type, src0_reordered, [&](auto tag) {
|
||||
using block_q_t = typename decltype(tag)::type;
|
||||
if constexpr (fg_plain_ok<block_q_t>()) {
|
||||
fused_dequant_gemm_launch<S, block_q_t>(src0, packed, dst, (int) M, (int) N, Npad, (int) K, (int) ldd,
|
||||
groups_n, groups_m, stream);
|
||||
fused_dequant_gemm_launch<S, block_q_t, decltype(tag)::reordered>(src0, packed, dst, (int) M, (int) N,
|
||||
Npad, (int) K, (int) ldd, groups_n,
|
||||
groups_m, stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool ggml_sycl_fused_dequant_gemm(ggml_type src0_type, const void * src0, const void * src1, ggml_type src1_type,
|
||||
bool ggml_sycl_fused_dequant_gemm(ggml_type src0_type, bool src0_reordered, const void * src0, const void * src1, ggml_type src1_type,
|
||||
int32_t src1_prec, float * dst, int64_t M, int64_t N, int64_t K, int64_t ldd,
|
||||
ggml_sycl_pool & pool, dpct::queue_ptr stream) {
|
||||
// every BN columns dequantize A again, so wide N is left to the library GEMM
|
||||
// every BN columns dequantize A again, so wide N is left to the library GEMM.
|
||||
// src0_reordered: the persistent weight reorder may have been installed on this
|
||||
// tensor by an earlier small-batch mul_mat; the A stage then has to decode the
|
||||
// SoA layout. Types without an SoA stage (fg_soa<T>::supported == false)
|
||||
// decline via fg_visit_layout and the caller falls through to the
|
||||
// reorder-aware conversion route.
|
||||
bool plain_ok = false;
|
||||
fg_visit_type(src0_type, false, [&](auto tag) { plain_ok = fg_plain_ok<typename decltype(tag)::type>(); });
|
||||
fg_visit_type(src0_type, src0_reordered, [&](auto tag) { plain_ok = fg_plain_ok<typename decltype(tag)::type>(); });
|
||||
if (g_ggml_sycl_dynamic_precision == GGML_SYCL_DYNAMIC_PRECISION_F32 ||
|
||||
!ggml_sycl_xmx_gather_type_enabled(src0_type) || !plain_ok) {
|
||||
return false;
|
||||
@@ -1023,7 +1029,7 @@ bool ggml_sycl_fused_dequant_gemm(ggml_type src0_type, const void * src0, const
|
||||
}
|
||||
bool launched = false;
|
||||
fg_visit_combo(combo, [&](auto s) {
|
||||
launched = fg_fused_run<decltype(s)>(src0_type, src0, src1, src1_type, dst, M, N, K, ldd, pool, stream);
|
||||
launched = fg_fused_run<decltype(s)>(src0_type, src0_reordered, src0, src1, src1_type, dst, M, N, K, ldd, pool, stream);
|
||||
});
|
||||
return launched;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ inline bool ggml_sycl_xmx_gather_type_enabled(ggml_type src0_type) {
|
||||
// (op_params[3], [TAG_GGML_PREC]); the accumulator is f32, which meets any request.
|
||||
// q8_0 and the k-quants are not handled here, only in the grouped path below.
|
||||
// Returns false when the case is not handled (type, device, precision, or shape).
|
||||
bool ggml_sycl_fused_dequant_gemm(ggml_type src0_type, const void * src0, const void * src1, ggml_type src1_type,
|
||||
bool ggml_sycl_fused_dequant_gemm(ggml_type src0_type, bool src0_reordered, const void * src0, const void * src1, ggml_type src1_type,
|
||||
int32_t src1_prec, float * dst, int64_t M, int64_t N, int64_t K, int64_t ldd,
|
||||
ggml_sycl_pool & pool, dpct::queue_ptr stream);
|
||||
|
||||
|
||||
@@ -301,6 +301,52 @@ static void get_rows_sycl_reorder_k(const ggml_tensor *src0, const ggml_tensor *
|
||||
GGML_UNUSED(src0);
|
||||
}
|
||||
|
||||
// IQ3_S / IQ3_XXS in the persistent reorder layout: one work-group of 32 threads
|
||||
// dequantizes one QK_K block of one requested row, reusing the reorder-aware
|
||||
// block dequantizers that the dequantize-based mul_mat path already uses.
|
||||
template <typename F>
|
||||
static void get_rows_sycl_reorder_iq3(ggml_backend_sycl_context & ctx, const ggml_tensor * src0,
|
||||
const ggml_tensor * src1, ggml_tensor * dst,
|
||||
const int32_t * src1_dd, float * dst_dd,
|
||||
F dequantize_block) {
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
// the persistent reorder is only installed for 2D weights
|
||||
GGML_ASSERT(ne02 == 1 && ne03 == 1);
|
||||
GGML_ASSERT(ne00 % QK_K == 0);
|
||||
|
||||
const int64_t nblk = ne00 / QK_K;
|
||||
const int64_t nb = nblk * ne01; // blocks in the whole tensor
|
||||
const int64_t ngroups = ne10 * ne11 * ne12 * nblk;
|
||||
|
||||
const size_t s1 = nb1 / ggml_element_size(dst);
|
||||
const size_t s2 = nb2 / ggml_element_size(dst);
|
||||
const size_t s3 = nb3 / ggml_element_size(dst);
|
||||
|
||||
const size_t s10 = nb10 / ggml_element_size(src1);
|
||||
const size_t s11 = nb11 / ggml_element_size(src1);
|
||||
const size_t s12 = nb12 / ggml_element_size(src1);
|
||||
|
||||
queue_ptr stream = ctx.stream();
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, ngroups * 32), sycl::range<3>(1, 1, 32)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
const int64_t g = item_ct1.get_group(2);
|
||||
const int64_t b = g % nblk;
|
||||
const int64_t r = g / nblk;
|
||||
const int64_t i10 = r % ne10;
|
||||
const int64_t i11 = (r / ne10) % ne11;
|
||||
const int64_t i12 = r / (ne10 * ne11);
|
||||
|
||||
const int64_t i01 = src1_dd[i10*s10 + i11*s11 + i12*s12];
|
||||
|
||||
float * y = dst_dd + i10*s1 + i11*s2 + i12*s3 + b*QK_K;
|
||||
dequantize_block(y, i01*nblk + b, item_ct1, nb);
|
||||
});
|
||||
|
||||
GGML_UNUSED(dst);
|
||||
}
|
||||
|
||||
template <int qk, int qr, dequantize_kernel_f32_t dq>
|
||||
static void get_rows_sycl_f32(ggml_backend_sycl_context & ctx, const ggml_tensor *src0, const ggml_tensor *src1,
|
||||
ggml_tensor *dst, const void *src0_dd,
|
||||
@@ -460,7 +506,9 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type));
|
||||
|
||||
const int32_t * src1_i32 = (const int32_t *) dst->src[1]->data;
|
||||
// a weight shared with MUL_MAT (e.g. tied embeddings) can be in reorder layout
|
||||
// a weight shared with MUL_MAT (e.g. tied embeddings) can be in the
|
||||
// persistent reorder layout (incl. IQ3_S / IQ3_XXS since the SYCL IQ3
|
||||
// reorder work)
|
||||
const bool src0_reordered = dst->src[0]->extra &&
|
||||
((const ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder;
|
||||
/* TODO: Refactor and remove duplicates */
|
||||
@@ -506,6 +554,15 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_IQ3_XXS:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_iq3(ctx, dst->src[0], dst->src[1], dst, src1_i32, (float *) dst->data,
|
||||
[=](float * y, int64_t iblk, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_iq3_xxs_reorder(vx, y, iblk, item_ct1, nb,
|
||||
iq3xxs_grid, ksigns_iq2xs, kmask_iq2xs);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK_K, 1, dequantize_iq3_xxs>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
@@ -518,6 +575,15 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_IQ3_S:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_iq3(ctx, dst->src[0], dst->src[1], dst, src1_i32, (float *) dst->data,
|
||||
[=](float * y, int64_t iblk, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_iq3_s_reorder(vx, y, iblk, item_ct1, nb,
|
||||
kmask_iq2xs, iq3s_grid);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK_K, 1, dequantize_iq3_s>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
|
||||
@@ -3154,9 +3154,16 @@ inline void ggml_sycl_op_mul_mat_sycl(
|
||||
#endif
|
||||
|
||||
// dequantize inside the GEMM instead of writing the f16 weights out and reading them back; src1
|
||||
// goes in its own type, so there is no separate conversion pass
|
||||
// goes in its own type, so there is no separate conversion pass. The A stage
|
||||
// decodes the weight bytes in place, so an installed persistent reorder must be
|
||||
// visible to it: pass the flag through; types without an SoA A-stage decline
|
||||
// inside and this falls through to the reorder-aware conversion route below.
|
||||
bool src0_reordered = false;
|
||||
if (const auto * src0_extra = static_cast<const ggml_tensor_extra_gpu *>(src0->extra)) {
|
||||
src0_reordered = src0_extra->optimized_feature.reorder;
|
||||
}
|
||||
if (ggml_is_quantized(src0->type) && ggml_is_contiguous(src0) && row_diff == src0->ne[1] &&
|
||||
ggml_sycl_fused_dequant_gemm(src0->type, src0_dd_i, src1_ddf_i, src1->type, ggml_sycl_src1_prec(dst), dst_dd_i,
|
||||
ggml_sycl_fused_dequant_gemm(src0->type, src0_reordered, src0_dd_i, src1_ddf_i, src1->type, ggml_sycl_src1_prec(dst), dst_dd_i,
|
||||
row_diff, src1_ncols, ne10, ldc, ctx.pool(), stream)) {
|
||||
return;
|
||||
}
|
||||
@@ -6905,6 +6912,11 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
|
||||
return false;
|
||||
}
|
||||
case GGML_OP_CONV_2D:
|
||||
// WHCN: contiguous [KW,KH,IC,OC] kernel and [W,H,C,N] input
|
||||
// CWHN: channel-fastest input (C fastest) and kernel with OC fastest
|
||||
return (ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1])) ||
|
||||
(op->src[1]->nb[2] == ggml_type_size(op->src[1]->type) &&
|
||||
op->src[0]->nb[3] == ggml_type_size(op->src[0]->type));
|
||||
case GGML_OP_CONV_2D_DW:
|
||||
case GGML_OP_CONV_TRANSPOSE_2D:
|
||||
return true;
|
||||
|
||||
+318
-656
File diff suppressed because it is too large
Load Diff
@@ -1655,6 +1655,7 @@ class ggml_webgpu_shader_lib {
|
||||
|
||||
switch (key.src_type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
case GGML_TYPE_Q2_0:
|
||||
case GGML_TYPE_Q4_0:
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q8_0:
|
||||
@@ -1699,7 +1700,7 @@ class ggml_webgpu_shader_lib {
|
||||
} else if ((key.src_type >= GGML_TYPE_Q4_0 && key.src_type <= GGML_TYPE_Q8_1) ||
|
||||
key.src_type == GGML_TYPE_IQ4_NL || key.src_type == GGML_TYPE_MXFP4) {
|
||||
defines.push_back("BLOCK_SIZE=32u");
|
||||
} else if (key.src_type == GGML_TYPE_NVFP4) {
|
||||
} else if (key.src_type == GGML_TYPE_Q2_0 || key.src_type == GGML_TYPE_NVFP4) {
|
||||
defines.push_back("BLOCK_SIZE=64u");
|
||||
} else if (key.src_type >= GGML_TYPE_Q2_K) {
|
||||
defines.push_back("BLOCK_SIZE=256u");
|
||||
|
||||
@@ -4395,6 +4395,7 @@ static bool ggml_backend_webgpu_device_supports_buft(ggml_backend_dev_t dev, ggm
|
||||
static bool ggml_webgpu_supported_qtype(ggml_type type) {
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
case GGML_TYPE_Q2_0:
|
||||
case GGML_TYPE_Q4_0:
|
||||
case GGML_TYPE_Q4_1:
|
||||
case GGML_TYPE_Q5_0:
|
||||
@@ -4500,6 +4501,7 @@ static bool ggml_backend_webgpu_device_supports_op(ggml_backend_dev_t dev, const
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_Q1_0:
|
||||
case GGML_TYPE_Q2_0:
|
||||
case GGML_TYPE_Q4_0:
|
||||
case GGML_TYPE_Q4_1:
|
||||
case GGML_TYPE_Q5_0:
|
||||
|
||||
@@ -51,6 +51,20 @@ fn copy_elements(src_base: u32, dst_base: u32, offset: u32) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q2_0
|
||||
fn copy_elements(src_base: u32, dst_base: u32, offset: u32) {
|
||||
let block_byte_base = (src_base + offset) * 18u;
|
||||
let d = load_f16_as_f32_at_src(block_byte_base);
|
||||
for (var j = 0u; j < 4u; j++) {
|
||||
let q_packed = load_u32_at_src(block_byte_base + 2u + j * 4u);
|
||||
for (var k = 0u; k < 16u; k++) {
|
||||
let q = (q_packed >> (2u * k)) & 3u;
|
||||
dst[dst_base + offset * 64u + j * 16u + k] = (f32(q) - 1.0) * d;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q4_0
|
||||
fn copy_elements(src_base: u32, dst_base: u32, offset: u32) {
|
||||
let block_byte_base = (src_base + offset) * 18; // Block stride: 18 bytes
|
||||
|
||||
@@ -111,6 +111,29 @@ fn init_shmem_src0(thread_id: u32, batch_offset: u32, offset_m: u32, k_outer: u3
|
||||
}
|
||||
#endif // INIT_SRC0_SHMEM_Q1_0
|
||||
|
||||
#ifdef INIT_SRC0_SHMEM_Q2_0
|
||||
const BLOCK_SIZE = 64u;
|
||||
const BLOCK_SIZE_BYTES = 18u;
|
||||
const NQ = 16u;
|
||||
|
||||
fn init_shmem_src0(thread_id: u32, batch_offset: u32, offset_m: u32, k_outer: u32) {
|
||||
for (var i = thread_id * NQ; i < TILE_SRC0_SHMEM; i += TOTAL_WORKGROUP_SIZE * NQ) {
|
||||
let global_m = offset_m + i / TILE_K;
|
||||
let global_k = k_outer + i % TILE_K;
|
||||
if (global_m < params.m && global_k < params.k) {
|
||||
let block_idx = batch_offset + global_m * params.stride_01 + global_k / BLOCK_SIZE;
|
||||
let block_byte_base = block_idx * BLOCK_SIZE_BYTES;
|
||||
let d = load_f16_at_src0(block_byte_base);
|
||||
let q_packed = load_u32_at_src0(block_byte_base + 2u + (global_k % BLOCK_SIZE) / 4u);
|
||||
for (var k = 0u; k < NQ; k++) {
|
||||
let q = (q_packed >> (2u * k)) & 3u;
|
||||
shmem[i + k] = (f16(q) - 1.0) * d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // INIT_SRC0_SHMEM_Q2_0
|
||||
|
||||
// legacy-quants
|
||||
#if defined(INIT_SRC0_SHMEM_Q4_0) || defined(INIT_SRC0_SHMEM_Q4_1) || defined(INIT_SRC0_SHMEM_Q5_0) || defined(INIT_SRC0_SHMEM_Q5_1) || defined(INIT_SRC0_SHMEM_Q8_0) || defined(INIT_SRC0_SHMEM_Q8_1) || defined(INIT_SRC0_SHMEM_MXFP4)
|
||||
const BLOCK_SIZE = 32u;
|
||||
|
||||
@@ -119,6 +119,46 @@ fn accumulate_vec_dot(thread_id: u32, row_base: u32, src0_batch_offset: u32, src
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MUL_ACC_Q2_0
|
||||
#define BLOCK_SIZE 64
|
||||
#define BLOCK_SIZE_BYTES 18
|
||||
#define THREADS_PER_BLOCK 4
|
||||
#define ELEMS_PER_THREAD (BLOCK_SIZE/THREADS_PER_BLOCK)
|
||||
fn accumulate_vec_dot(thread_id: u32, row_base: u32, src0_batch_offset: u32, src1_idx_base: u32) -> array<array<f32, OUTPUTS_PER_WG>, NUM_COLS> {
|
||||
var acc: array<array<f32, OUTPUTS_PER_WG>, NUM_COLS>;
|
||||
|
||||
let num_blocks = params.k / BLOCK_SIZE;
|
||||
let thread_within_block = thread_id % THREADS_PER_BLOCK;
|
||||
for (var block = thread_id / THREADS_PER_BLOCK; block < num_blocks; block += WG_SIZE / THREADS_PER_BLOCK) {
|
||||
let x_base = src1_idx_base + block * BLOCK_SIZE + thread_within_block * ELEMS_PER_THREAD;
|
||||
var x_block: array<array<f32, ELEMS_PER_THREAD>, NUM_COLS>;
|
||||
for (var col = 0u; col < NUM_COLS; col++) {
|
||||
for (var i = 0u; i < ELEMS_PER_THREAD; i++) {
|
||||
x_block[col][i] = f32(SRC1[x_base + col * params.stride_11 + i]);
|
||||
}
|
||||
}
|
||||
for (var row = 0u; row < OUTPUTS_PER_WG; row++) {
|
||||
let output_row = row_base + row;
|
||||
if (output_row < params.m) {
|
||||
let block_byte_base = (src0_batch_offset + output_row * params.stride_01 + block) * BLOCK_SIZE_BYTES;
|
||||
let d = f32(load_f16_at_src0(block_byte_base));
|
||||
let q_packed = load_u32_at_src0(block_byte_base + 2u + 4u * thread_within_block);
|
||||
for (var col = 0u; col < NUM_COLS; col++) {
|
||||
var row_sum = 0.0;
|
||||
for (var i = 0u; i < ELEMS_PER_THREAD; i++) {
|
||||
let q = (q_packed >> (2u * i)) & 3u;
|
||||
row_sum += (f32(q) - 1.0) * x_block[col][i];
|
||||
}
|
||||
acc[col][row] += d * row_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MUL_ACC_Q4_0
|
||||
#define BLOCK_SIZE 32
|
||||
#define BLOCK_SIZE_BYTES 18
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "zdnn.h"
|
||||
|
||||
#include "scratch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <future>
|
||||
@@ -14,9 +16,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define GGML_ZDNN_NAME "zDNN"
|
||||
#define GGML_ZDNN_VERSION ZDNN_VERNUM
|
||||
#define GGML_ZDNN_SCRATCH_SLOTS 3 // int8 inputs, DFloat16 partial results, partial results for grouped matmul
|
||||
#define GGML_ZDNN_NAME "zDNN"
|
||||
#define GGML_ZDNN_VERSION ZDNN_VERNUM
|
||||
|
||||
#define ZDNN_CHECK(stmt) \
|
||||
do { \
|
||||
@@ -68,44 +69,100 @@ struct ggml_backend_zdnn_context {
|
||||
// zero bias for stacked quantized matmuls, keyed by (n_stacks, ne0)
|
||||
std::map<std::pair<int64_t, int64_t>, std::unique_ptr<ggml_backend_zdnn_buffer>> zero_bias;
|
||||
|
||||
std::vector<int8_t> scratch_q;
|
||||
std::vector<float> scratch_f32;
|
||||
|
||||
void * scratch_ztensor[GGML_ZDNN_SCRATCH_SLOTS] = {}; // 4K aligned
|
||||
size_t scratch_ztensor_size[GGML_ZDNN_SCRATCH_SLOTS] = {};
|
||||
ggml_zdnn_pool pool; // scratch memory of the ops
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static inline T * ggml_zdnn_resize_scratch_max(std::vector<T> & scratch, int64_t n) {
|
||||
if ((int64_t)scratch.size() < n) {
|
||||
scratch.resize(n);
|
||||
}
|
||||
|
||||
return scratch.data();
|
||||
}
|
||||
|
||||
//
|
||||
// Scratch buffers
|
||||
//
|
||||
|
||||
static inline void ggml_zdnn_init_scratch_ztensor(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
int32_t slot) {
|
||||
// the ztensor uses the memory of scratch, so scratch must outlive it
|
||||
static inline void ggml_zdnn_init_scratch_ztensor(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
ggml_zdnn_pool_alloc<uint8_t> & scratch) {
|
||||
|
||||
const uint64_t size = zdnn_getsize_ztensor(tfm_desc);
|
||||
|
||||
if (ctx->scratch_ztensor_size[slot] < size) {
|
||||
free(ctx->scratch_ztensor[slot]);
|
||||
ctx->scratch_ztensor[slot] = std::aligned_alloc(4096, GGML_PAD(size, 4096));
|
||||
ctx->scratch_ztensor_size[slot] = GGML_PAD(size, 4096);
|
||||
GGML_ASSERT(ctx->scratch_ztensor[slot] != nullptr);
|
||||
zdnn_init_quantized_ztensor(pre_tfm_desc, tfm_desc, 1.0f, 0.0f, ztensor);
|
||||
ztensor->buffer = scratch.alloc(ctx->pool, size);
|
||||
ztensor->buffer_size = size;
|
||||
}
|
||||
|
||||
// same as ggml_zdnn_init_scratch_ztensor, for a DLFLOAT16 ztensor
|
||||
static inline void ggml_zdnn_init_scratch_ztensor_dlf16(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
ggml_zdnn_pool_alloc<uint8_t> & scratch) {
|
||||
|
||||
const uint64_t size = zdnn_getsize_ztensor(tfm_desc);
|
||||
|
||||
zdnn_init_ztensor(pre_tfm_desc, tfm_desc, ztensor);
|
||||
ztensor->buffer = scratch.alloc(ctx->pool, size);
|
||||
ztensor->buffer_size = size;
|
||||
}
|
||||
|
||||
//
|
||||
// Stacked ztensors
|
||||
//
|
||||
|
||||
// view of the stacks [s0, s0 + n) of a stacked ztensor, the stacks are the outermost dim of its buffer
|
||||
// the view descs must outlive the view
|
||||
static inline zdnn_ztensor ggml_zdnn_stack_view(zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
const zdnn_ztensor & src,
|
||||
int64_t n_stacks,
|
||||
int64_t s0,
|
||||
int64_t n) {
|
||||
|
||||
GGML_ASSERT(src.buffer_size % n_stacks == 0);
|
||||
const uint64_t stack_bytes = src.buffer_size / n_stacks;
|
||||
|
||||
*pre_tfm_desc = *src.pre_transformed_desc;
|
||||
*tfm_desc = *src.transformed_desc;
|
||||
|
||||
switch (pre_tfm_desc->layout) {
|
||||
case ZDNN_3DS:
|
||||
{
|
||||
pre_tfm_desc->dim3 = n;
|
||||
} break;
|
||||
case ZDNN_2DS:
|
||||
{
|
||||
pre_tfm_desc->dim2 = n;
|
||||
} break;
|
||||
default:
|
||||
GGML_ABORT("%s: unsupported stacked layout", __func__);
|
||||
}
|
||||
|
||||
zdnn_init_quantized_ztensor(pre_tfm_desc, tfm_desc, 1.0f, 0.0f, ztensor);
|
||||
ztensor->buffer = ctx->scratch_ztensor[slot];
|
||||
ztensor->buffer_size = size;
|
||||
tfm_desc->dim4 = n;
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(tfm_desc) == n*stack_bytes);
|
||||
|
||||
zdnn_ztensor view = src;
|
||||
view.pre_transformed_desc = pre_tfm_desc;
|
||||
view.transformed_desc = tfm_desc;
|
||||
view.buffer = (char *) src.buffer + s0*stack_bytes;
|
||||
view.buffer_size = n * stack_bytes;
|
||||
return view;
|
||||
}
|
||||
|
||||
// zero bias of n_stacks stacks, created once per shape as it never changes
|
||||
static inline const zdnn_ztensor & ggml_zdnn_zero_bias(ggml_backend_zdnn_context * ctx, int64_t n_stacks, int64_t ne0) {
|
||||
std::unique_ptr<ggml_backend_zdnn_buffer> & bias = ctx->zero_bias[{ n_stacks, ne0 }];
|
||||
|
||||
if (!bias) {
|
||||
bias = std::make_unique<ggml_backend_zdnn_buffer>();
|
||||
|
||||
std::vector<float> zeros(n_stacks * ne0, 0.0f);
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias->pre_tfm_desc, n_stacks, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias->pre_tfm_desc, &bias->tfm_desc));
|
||||
ZDNN_CHECK(zdnn_init_ztensor_with_malloc(&bias->pre_tfm_desc, &bias->tfm_desc, &bias->ztensor));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias->ztensor, zeros.data()));
|
||||
}
|
||||
|
||||
return bias->ztensor;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "ggml.h"
|
||||
#include "fattn.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
// DLFLOAT16 has no infinity, so masked scores use the lowest FP16 value, exp of which is still 0
|
||||
static inline float ggml_zdnn_fattn_mask_value(ggml_fp16_t m) {
|
||||
return std::max(GGML_FP16_TO_FP32(m), -65504.0f);
|
||||
}
|
||||
|
||||
bool ggml_zdnn_supports_flash_attn_ext(const ggml_backend_zdnn_device_context * ctx_dev, const ggml_tensor * op) {
|
||||
const ggml_tensor * q = op->src[0];
|
||||
const ggml_tensor * k = op->src[1];
|
||||
const ggml_tensor * v = op->src[2];
|
||||
const ggml_tensor * mask = op->src[3];
|
||||
|
||||
float max_bias = 0.0f;
|
||||
float logit_softcap = 0.0f;
|
||||
memcpy(&max_bias, (const float *) op->op_params + 1, sizeof(float));
|
||||
memcpy(&logit_softcap, (const float *) op->op_params + 2, sizeof(float));
|
||||
|
||||
// sinks, alibi, softcap and sparse k/v sets are not implemented
|
||||
if (op->src[4] != nullptr || max_bias != 0.0f || logit_softcap != 0.0f || ggml_get_op_params_i32(op, 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->type != GGML_TYPE_F32 || q->type != GGML_TYPE_F32 || k->type != GGML_TYPE_F16 || v->type != GGML_TYPE_F16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mask != nullptr && (mask->type != GGML_TYPE_F16 || mask->ne[2] != 1 || mask->ne[3] != 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (q->ne[3] != 1 || k->ne[3] != 1 || v->ne[3] != 1 || k->ne[2] != v->ne[2] || q->ne[2] % k->ne[2] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// rows are gathered with their strides, but each row has to be contiguous
|
||||
if (q->nb[0] != sizeof(float) || k->nb[0] != sizeof(ggml_fp16_t) || v->nb[0] != sizeof(ggml_fp16_t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int64_t max_dim = ctx_dev->max_size;
|
||||
const int64_t group = q->ne[2] / k->ne[2];
|
||||
|
||||
return q->ne[0] <= max_dim && v->ne[0] <= max_dim && k->ne[1] <= max_dim &&
|
||||
q->ne[2] <= max_dim && group * q->ne[1] <= max_dim;
|
||||
}
|
||||
|
||||
void ggml_zdnn_flash_attn_ext(ggml_backend_zdnn_context * ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * q = dst->src[0];
|
||||
const ggml_tensor * k = dst->src[1];
|
||||
const ggml_tensor * v = dst->src[2];
|
||||
const ggml_tensor * mask = dst->src[3];
|
||||
|
||||
const int64_t d_k = q->ne[0];
|
||||
const int64_t n_q = q->ne[1];
|
||||
const int64_t n_head = q->ne[2];
|
||||
const int64_t n_kv = k->ne[1];
|
||||
const int64_t n_head_kv = k->ne[2];
|
||||
const int64_t d_v = v->ne[0];
|
||||
const int64_t group = n_head / n_head_kv;
|
||||
|
||||
float scale = 1.0f;
|
||||
memcpy(&scale, (const float *) dst->op_params + 0, sizeof(float));
|
||||
|
||||
const int32_t n_threads = n_q*n_kv >= 65536 ? ctx->n_threads : 1;
|
||||
|
||||
// one query runs all heads in one call with the mask row as the K*Q bias, more queries run per head and add the mask
|
||||
const bool single = n_q == 1;
|
||||
|
||||
const int64_t n_mask = mask ? (single ? n_head_kv*n_kv : n_q*n_kv) : 0;
|
||||
|
||||
ggml_zdnn_pool_alloc<float> q_host_alloc(ctx->pool, n_head*n_q*d_k);
|
||||
ggml_zdnn_pool_alloc<float> o_host_alloc(ctx->pool, n_head*n_q*d_v);
|
||||
ggml_zdnn_pool_alloc<float> mask_host_alloc(ctx->pool, n_mask);
|
||||
ggml_zdnn_pool_alloc<ggml_fp16_t> k_host_alloc(ctx->pool, n_head_kv*n_kv*d_k);
|
||||
ggml_zdnn_pool_alloc<ggml_fp16_t> v_host_alloc(ctx->pool, n_head_kv*n_kv*d_v);
|
||||
|
||||
float * q_host = q_host_alloc.get();
|
||||
float * o_host = o_host_alloc.get();
|
||||
float * mask_host = mask_host_alloc.get();
|
||||
ggml_fp16_t * k_host = k_host_alloc.get();
|
||||
ggml_fp16_t * v_host = v_host_alloc.get();
|
||||
|
||||
// q is [n_head][n_q][d_k] with the scale folded in, k and v are [n_head_kv][n_kv][d]
|
||||
ggml_zdnn_parallel_for(n_threads, n_head, [&](int64_t h_start, int64_t h_end) {
|
||||
for (int64_t h = h_start; h < h_end; h++) {
|
||||
for (int64_t iq = 0; iq < n_q; iq++) {
|
||||
const float * src = (const float *)((const char *) q->data + iq*q->nb[1] + h*q->nb[2]);
|
||||
float * row = q_host + (h*n_q + iq)*d_k;
|
||||
for (int64_t i0 = 0; i0 < d_k; i0++) {
|
||||
row[i0] = src[i0] * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ggml_zdnn_parallel_for(n_threads, n_head_kv*n_kv, [&](int64_t r_start, int64_t r_end) {
|
||||
for (int64_t r = r_start; r < r_end; r++) {
|
||||
const int64_t hk = r / n_kv;
|
||||
const int64_t ik = r % n_kv;
|
||||
memcpy(k_host + r*d_k, (const char *) k->data + ik*k->nb[1] + hk*k->nb[2], d_k*sizeof(ggml_fp16_t));
|
||||
memcpy(v_host + r*d_v, (const char *) v->data + ik*v->nb[1] + hk*v->nb[2], d_v*sizeof(ggml_fp16_t));
|
||||
}
|
||||
});
|
||||
|
||||
zdnn_tensor_desc q_pre_tfm_desc, q_tfm_desc;
|
||||
zdnn_tensor_desc k_pre_tfm_desc, k_tfm_desc;
|
||||
zdnn_tensor_desc v_pre_tfm_desc, v_tfm_desc;
|
||||
zdnn_tensor_desc o_pre_tfm_desc, o_tfm_desc;
|
||||
zdnn_ztensor q_ztensor, k_ztensor, v_ztensor, o_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> q_ztensor_alloc, k_ztensor_alloc, v_ztensor_alloc, o_ztensor_alloc;
|
||||
|
||||
// a single query keeps the heads of a kv head in one stack, which is the same memory order as one head per stack
|
||||
if (single) {
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &q_pre_tfm_desc, n_head_kv, group, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &o_pre_tfm_desc, n_head_kv, group, d_v);
|
||||
} else {
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &q_pre_tfm_desc, n_head, n_q, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &o_pre_tfm_desc, n_head, n_q, d_v);
|
||||
}
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP16, &k_pre_tfm_desc, n_head_kv, n_kv, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP16, &v_pre_tfm_desc, n_head_kv, n_kv, d_v);
|
||||
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&q_pre_tfm_desc, &q_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&k_pre_tfm_desc, &k_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&v_pre_tfm_desc, &v_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&o_pre_tfm_desc, &o_tfm_desc));
|
||||
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &q_pre_tfm_desc, &q_tfm_desc, &q_ztensor, q_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &k_pre_tfm_desc, &k_tfm_desc, &k_ztensor, k_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &v_pre_tfm_desc, &v_tfm_desc, &v_ztensor, v_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &o_pre_tfm_desc, &o_tfm_desc, &o_ztensor, o_ztensor_alloc);
|
||||
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&q_ztensor, q_host));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&k_ztensor, k_host));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&v_ztensor, v_host));
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> softmax_save_area(ctx->pool, ZDNN_SOFTMAX_SAVEAREA_SIZE);
|
||||
|
||||
zdnn_tensor_desc s_pre_tfm_desc, s_tfm_desc;
|
||||
zdnn_ztensor s_ztensor, s2_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> s_ztensor_alloc, s2_ztensor_alloc;
|
||||
|
||||
if (single) {
|
||||
// the bias of K*Q is the mask row of the query, repeated for every kv head
|
||||
zdnn_tensor_desc bias_pre_tfm_desc, bias_tfm_desc;
|
||||
zdnn_ztensor bias_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> bias_ztensor_alloc;
|
||||
|
||||
const zdnn_ztensor * bias = &ggml_zdnn_zero_bias(ctx, n_head_kv, n_kv);
|
||||
if (mask != nullptr) {
|
||||
const ggml_fp16_t * mask_row = (const ggml_fp16_t *) mask->data;
|
||||
for (int64_t hk = 0; hk < n_head_kv; hk++) {
|
||||
for (int64_t ik = 0; ik < n_kv; ik++) {
|
||||
mask_host[hk*n_kv + ik] = ggml_zdnn_fattn_mask_value(mask_row[ik]);
|
||||
}
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias_pre_tfm_desc, n_head_kv, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias_pre_tfm_desc, &bias_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &bias_pre_tfm_desc, &bias_tfm_desc, &bias_ztensor, bias_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias_ztensor, mask_host));
|
||||
bias = &bias_ztensor;
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &s_pre_tfm_desc, n_head_kv, group, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&s_pre_tfm_desc, &s_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s_ztensor, s_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s2_ztensor, s2_ztensor_alloc);
|
||||
|
||||
ZDNN_CHECK(zdnn_matmul_transpose_op(&q_ztensor, &k_ztensor, bias, false, true, MATMUL_OP_ADDITION, &s_ztensor));
|
||||
ZDNN_CHECK(zdnn_softmax(&s_ztensor, softmax_save_area.get(), SOFTMAX_ACT_NONE, &s2_ztensor));
|
||||
ZDNN_CHECK(zdnn_matmul_op(&s2_ztensor, &v_ztensor, &ggml_zdnn_zero_bias(ctx, n_head_kv, d_v), MATMUL_OP_ADDITION, &o_ztensor));
|
||||
} else {
|
||||
zdnn_tensor_desc mask_pre_tfm_desc, mask_tfm_desc;
|
||||
zdnn_ztensor mask_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> mask_ztensor_alloc;
|
||||
|
||||
if (mask != nullptr) {
|
||||
ggml_zdnn_parallel_for(n_threads, n_q, [&](int64_t iq_start, int64_t iq_end) {
|
||||
for (int64_t iq = iq_start; iq < iq_end; iq++) {
|
||||
const ggml_fp16_t * mask_row = (const ggml_fp16_t *)((const char *) mask->data + iq*mask->nb[1]);
|
||||
for (int64_t ik = 0; ik < n_kv; ik++) {
|
||||
mask_host[iq*n_kv + ik] = ggml_zdnn_fattn_mask_value(mask_row[ik]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &mask_pre_tfm_desc, 1, n_q, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&mask_pre_tfm_desc, &mask_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &mask_pre_tfm_desc, &mask_tfm_desc, &mask_ztensor, mask_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&mask_ztensor, mask_host));
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &s_pre_tfm_desc, 1, n_q, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&s_pre_tfm_desc, &s_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s_ztensor, s_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s2_ztensor, s2_ztensor_alloc);
|
||||
|
||||
const zdnn_ztensor & k_bias = ggml_zdnn_zero_bias(ctx, 1, n_kv);
|
||||
const zdnn_ztensor & v_bias = ggml_zdnn_zero_bias(ctx, 1, d_v);
|
||||
|
||||
zdnn_tensor_desc view_pre_tfm_desc[4];
|
||||
zdnn_tensor_desc view_tfm_desc[4];
|
||||
|
||||
for (int64_t h = 0; h < n_head; h++) {
|
||||
const zdnn_ztensor q_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[0], &view_tfm_desc[0], q_ztensor, n_head, h, 1);
|
||||
const zdnn_ztensor k_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[1], &view_tfm_desc[1], k_ztensor, n_head_kv, h / group, 1);
|
||||
const zdnn_ztensor v_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[2], &view_tfm_desc[2], v_ztensor, n_head_kv, h / group, 1);
|
||||
zdnn_ztensor o_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[3], &view_tfm_desc[3], o_ztensor, n_head, h, 1);
|
||||
|
||||
s_ztensor.is_transformed = false;
|
||||
s2_ztensor.is_transformed = false;
|
||||
|
||||
ZDNN_CHECK(zdnn_matmul_transpose_op(&q_view, &k_view, &k_bias, false, true, MATMUL_OP_ADDITION, &s_ztensor));
|
||||
|
||||
zdnn_ztensor * scores = &s_ztensor;
|
||||
zdnn_ztensor * probs = &s2_ztensor;
|
||||
if (mask != nullptr) {
|
||||
ZDNN_CHECK(zdnn_add(&s_ztensor, &mask_ztensor, &s2_ztensor));
|
||||
scores = &s2_ztensor;
|
||||
probs = &s_ztensor;
|
||||
probs->is_transformed = false;
|
||||
}
|
||||
|
||||
ZDNN_CHECK(zdnn_softmax(scores, softmax_save_area.get(), SOFTMAX_ACT_NONE, probs));
|
||||
ZDNN_CHECK(zdnn_matmul_op(probs, &v_view, &v_bias, MATMUL_OP_ADDITION, &o_view));
|
||||
}
|
||||
|
||||
o_ztensor.is_transformed = true;
|
||||
}
|
||||
|
||||
ZDNN_CHECK(zdnn_transform_origtensor(&o_ztensor, o_host));
|
||||
|
||||
// the result is [d_v][n_head][n_q]
|
||||
for (int64_t iq = 0; iq < n_q; iq++) {
|
||||
for (int64_t h = 0; h < n_head; h++) {
|
||||
memcpy((char *) dst->data + h*dst->nb[1] + iq*dst->nb[2], o_host + (h*n_q + iq)*d_v, d_v*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
// the result is written to dst->data directly, so the dst ztensor is stale
|
||||
ggml_backend_zdnn_buffer * dst_extra = (ggml_backend_zdnn_buffer *) dst->extra;
|
||||
if (dst_extra != nullptr) {
|
||||
dst_extra->ztensor.is_transformed = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef GGML_ZDNN_FATTN_HPP
|
||||
#define GGML_ZDNN_FATTN_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
bool ggml_zdnn_supports_flash_attn_ext(const ggml_backend_zdnn_device_context * ctx_dev, const ggml_tensor * op);
|
||||
|
||||
void ggml_zdnn_flash_attn_ext(ggml_backend_zdnn_context * ctx, ggml_tensor * dst);
|
||||
|
||||
#endif // GGML_ZDNN_FATTN_HPP
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ggml-backend-impl.h"
|
||||
|
||||
#include "ggml-zdnn/common.hpp"
|
||||
#include "ggml-zdnn/fattn.hpp"
|
||||
#include "ggml-zdnn/mmf.hpp"
|
||||
#include "ggml-zdnn/mmq.hpp"
|
||||
#include "ggml-zdnn/utils.hpp"
|
||||
@@ -37,6 +38,11 @@ static bool ggml_zdnn_compute_forward(
|
||||
ggml_zdnn_compute_forward_mul_mat(ctx, dst);
|
||||
} break;
|
||||
|
||||
case GGML_OP_FLASH_ATTN_EXT:
|
||||
{
|
||||
ggml_zdnn_flash_attn_ext(ctx, dst);
|
||||
} break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -120,6 +126,9 @@ static bool ggml_zdnn_supports_op(const ggml_backend_zdnn_device_context * ctx_d
|
||||
}
|
||||
} break;
|
||||
|
||||
case GGML_OP_FLASH_ATTN_EXT:
|
||||
return ggml_zdnn_supports_flash_attn_ext(ctx_dev, op);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -205,9 +214,6 @@ static void ggml_zdnn_free(ggml_backend_zdnn_context * ctx) {
|
||||
for (const auto & it : ctx->zero_bias) {
|
||||
ZDNN_CHECK(zdnn_free_ztensor_buffer(&it.second->ztensor));
|
||||
}
|
||||
for (void * scratch : ctx->scratch_ztensor) {
|
||||
free(scratch);
|
||||
}
|
||||
|
||||
delete ctx;
|
||||
}
|
||||
@@ -309,7 +315,19 @@ static void ggml_backend_zdnn_buffer_memset_tensor(ggml_backend_buffer_t buffer,
|
||||
static void ggml_backend_zdnn_buffer_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
||||
memcpy((char *)tensor->data + offset, data, size);
|
||||
|
||||
// views share the ztensor of their source, which is now stale, and empty tensors have none
|
||||
if (tensor->view_src != nullptr) {
|
||||
ggml_backend_zdnn_buffer * src_extra = (ggml_backend_zdnn_buffer *)tensor->view_src->extra;
|
||||
if (src_extra != nullptr && src_extra->ztensor.is_transformed) {
|
||||
zdnn_reset_ztensor(&src_extra->ztensor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ggml_backend_zdnn_buffer * extra = (ggml_backend_zdnn_buffer *)tensor->extra;
|
||||
if (extra == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_is_quantized(tensor->type)) {
|
||||
GGML_ASSERT(offset == 0 && size == ggml_nbytes(tensor));
|
||||
@@ -319,8 +337,8 @@ static void ggml_backend_zdnn_buffer_set_tensor(ggml_backend_buffer_t buffer, gg
|
||||
|
||||
// Fixes the LLAMA_SET_ROWS bug
|
||||
// see: https://github.com/ggml-org/llama.cpp/issues/15414
|
||||
// compute tensors are transformed on first use instead, as not every op reads the ztensor
|
||||
if (tensor->buffer->usage == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
|
||||
// only weights are transformed here, others on first use, as not every op reads the ztensor and some data (masks) is not finite
|
||||
if (tensor->buffer->usage != GGML_BACKEND_BUFFER_USAGE_WEIGHTS) {
|
||||
zdnn_reset_ztensor(&extra->ztensor);
|
||||
return;
|
||||
}
|
||||
|
||||
+28
-77
@@ -6,63 +6,6 @@
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
// view of the stacks [s0, s0 + n) of a stacked ztensor, the stacks are the outermost dim of its buffer
|
||||
// the view descs must outlive the view
|
||||
static zdnn_ztensor ggml_zdnn_stack_view(zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
const zdnn_ztensor & src,
|
||||
int64_t n_stacks,
|
||||
int64_t s0,
|
||||
int64_t n) {
|
||||
|
||||
GGML_ASSERT(src.buffer_size % n_stacks == 0);
|
||||
const uint64_t stack_bytes = src.buffer_size / n_stacks;
|
||||
|
||||
*pre_tfm_desc = *src.pre_transformed_desc;
|
||||
*tfm_desc = *src.transformed_desc;
|
||||
|
||||
switch (pre_tfm_desc->layout) {
|
||||
case ZDNN_3DS:
|
||||
{
|
||||
pre_tfm_desc->dim3 = n;
|
||||
} break;
|
||||
case ZDNN_2DS:
|
||||
{
|
||||
pre_tfm_desc->dim2 = n;
|
||||
} break;
|
||||
default:
|
||||
GGML_ABORT("%s: unsupported stacked layout", __func__);
|
||||
}
|
||||
|
||||
tfm_desc->dim4 = n;
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(tfm_desc) == n*stack_bytes);
|
||||
|
||||
zdnn_ztensor view = src;
|
||||
view.pre_transformed_desc = pre_tfm_desc;
|
||||
view.transformed_desc = tfm_desc;
|
||||
view.buffer = (char *) src.buffer + s0*stack_bytes;
|
||||
view.buffer_size = n * stack_bytes;
|
||||
return view;
|
||||
}
|
||||
|
||||
// zero bias of n_stacks stacks, created once per shape as it never changes
|
||||
static const zdnn_ztensor & ggml_zdnn_zero_bias(ggml_backend_zdnn_context * ctx, int64_t n_stacks, int64_t ne0) {
|
||||
std::unique_ptr<ggml_backend_zdnn_buffer> & bias = ctx->zero_bias[{ n_stacks, ne0 }];
|
||||
|
||||
if (!bias) {
|
||||
bias = std::make_unique<ggml_backend_zdnn_buffer>();
|
||||
|
||||
std::vector<float> zeros(n_stacks * ne0, 0.0f);
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias->pre_tfm_desc, n_stacks, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias->pre_tfm_desc, &bias->tfm_desc));
|
||||
ZDNN_CHECK(zdnn_init_ztensor_with_malloc(&bias->pre_tfm_desc, &bias->tfm_desc, &bias->ztensor));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias->ztensor, zeros.data()));
|
||||
}
|
||||
|
||||
return bias->ztensor;
|
||||
}
|
||||
|
||||
// the weights are stacked into groups along ne00, and each group of each row of the inputs and weights has its own scale
|
||||
// zDNN multiplies the groups in stacked calls, then the partial results of each group are scaled and summed here
|
||||
static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
@@ -81,18 +24,20 @@ static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
GGML_ASSERT(n_groups * gs >= ne10 && (n_groups - 1) * gs < ne10);
|
||||
|
||||
// the rounding errors of the inputs are stacked after the inputs, see ggml_zdnn_quantize_inputs
|
||||
int8_t * inputs_q = ggml_zdnn_resize_scratch_max(ctx->scratch_q, 2 * n_groups * gs * ne11);
|
||||
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * n_groups * gs * ne11);
|
||||
int8_t * inputs_q = inputs_q_alloc.get();
|
||||
std::vector<float> inputs_scales(n_groups * ne11);
|
||||
ggml_zdnn_quantize_inputs(n_threads, inputs, gs, weights_extra->channel_scales.data(),
|
||||
inputs_q, inputs_q + n_groups * gs * ne11, inputs_scales.data());
|
||||
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2 * n_groups, ne11, gs);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, 0);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
|
||||
|
||||
// each group of the partial results is read back as its own 2D ztensor
|
||||
@@ -116,19 +61,21 @@ static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
|
||||
// two partial buffers, so the next chunk runs on the NNPA while this one is summed
|
||||
// each holds the results of a chunk of inputs, followed by the results of their rounding errors
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor[2];
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor[2];
|
||||
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc[2];
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2 * n_chunk, ne11, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(&partial_tfm_desc) == (uint64_t)(2 * n_chunk) * group_bytes);
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor[i], 1 + i);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor[i], partial_ztensor_alloc[i]);
|
||||
}
|
||||
|
||||
const float * weights_scales = weights_extra->scales.data();
|
||||
float * partial = ggml_zdnn_resize_scratch_max(ctx->scratch_f32, 2 * ne11 * ne0);
|
||||
ggml_zdnn_pool_alloc<float> partial_alloc(ctx->pool, 2 * ne11 * ne0);
|
||||
float * partial = partial_alloc.get();
|
||||
float * partial_res = partial + ne11 * ne0;
|
||||
|
||||
const auto sum_chunk = [&](const zdnn_ztensor & chunk, int64_t g0, int64_t n) {
|
||||
@@ -262,27 +209,30 @@ void ggml_zdnn_mul_mat_q(
|
||||
|
||||
// zDNN takes one scale per zTensor, so quantize each row of the inputs with its own scale
|
||||
// the rounding errors of the inputs are stacked after the inputs, see ggml_zdnn_quantize_inputs
|
||||
int8_t * inputs_q = ggml_zdnn_resize_scratch_max(ctx->scratch_q, 2 * ne10 * ne11);
|
||||
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * ne10 * ne11);
|
||||
int8_t * inputs_q = inputs_q_alloc.get();
|
||||
std::vector<float> inputs_scales(ne11);
|
||||
ggml_zdnn_quantize_inputs(n_threads, inputs, ne10, weights_extra->channel_scales.data(),
|
||||
inputs_q, inputs_q + ne10 * ne11, inputs_scales.data());
|
||||
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2, ne11, ne10);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, 0);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
|
||||
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor;
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2, ne11, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor, 1);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor, partial_ztensor_alloc);
|
||||
|
||||
// both stacks are multiplied by the same unstacked weights in one broadcast call
|
||||
// the bias is all zeros, so it is already the pre-computed bias
|
||||
@@ -300,7 +250,8 @@ void ggml_zdnn_mul_mat_q(
|
||||
const uint64_t stack_bytes = zdnn_getsize_ztensor(&stack_tfm_desc);
|
||||
GGML_ASSERT(partial_ztensor.buffer_size == 2*stack_bytes);
|
||||
|
||||
float * partial[2] = { (float *)output->data, ggml_zdnn_resize_scratch_max(ctx->scratch_f32, ne11 * ne0) };
|
||||
ggml_zdnn_pool_alloc<float> partial_res_alloc(ctx->pool, ne11 * ne0);
|
||||
float * partial[2] = { (float *)output->data, partial_res_alloc.get() };
|
||||
|
||||
ggml_zdnn_parallel_for(n_threads, 2, [&](int64_t s_start, int64_t s_end) {
|
||||
zdnn_ztensor stack_ztensor;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "ggml.h"
|
||||
#include "scratch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
ggml_zdnn_pool::~ggml_zdnn_pool() {
|
||||
for (const buffer & b : cached) {
|
||||
std::free(b.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void * ggml_zdnn_pool::alloc(size_t size, size_t * actual_size) {
|
||||
int64_t ibest = -1;
|
||||
for (int64_t i = 0; i < (int64_t)cached.size(); i++) {
|
||||
if (cached[i].size >= size && (ibest < 0 || cached[i].size < cached[ibest].size)) {
|
||||
ibest = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (ibest >= 0) {
|
||||
void * ptr = cached[ibest].ptr;
|
||||
*actual_size = cached[ibest].size;
|
||||
cached.erase(cached.begin() + ibest);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// every cached buffer is too small, free the largest so the pool does not grow while sizes grow with the context
|
||||
if (!cached.empty()) {
|
||||
const auto largest = std::max_element(cached.begin(), cached.end(), [](const buffer & a, const buffer & b) { return a.size < b.size; });
|
||||
std::free(largest->ptr);
|
||||
cached.erase(largest);
|
||||
}
|
||||
|
||||
// 5% more, so slowly growing sizes do not allocate every time
|
||||
*actual_size = GGML_PAD(std::max<size_t>(1, (size_t)(1.05 * size)), 4096);
|
||||
void * ptr = std::aligned_alloc(4096, *actual_size);
|
||||
GGML_ASSERT(ptr != nullptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void ggml_zdnn_pool::free(void * ptr, size_t size) {
|
||||
cached.push_back({ ptr, size });
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef GGML_ZDNN_SCRATCH_HPP
|
||||
#define GGML_ZDNN_SCRATCH_HPP
|
||||
|
||||
#include "ggml.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
// 4K aligned host buffers that are cached for reuse, see ggml_cuda_pool_leg
|
||||
struct ggml_zdnn_pool {
|
||||
struct buffer {
|
||||
void * ptr = nullptr;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
std::vector<buffer> cached;
|
||||
|
||||
~ggml_zdnn_pool();
|
||||
|
||||
void * alloc(size_t size, size_t * actual_size);
|
||||
void free(void * ptr, size_t size);
|
||||
};
|
||||
|
||||
// a buffer of the pool that goes back to it at the end of its scope, see ggml_cuda_pool_alloc
|
||||
template <typename T>
|
||||
struct ggml_zdnn_pool_alloc {
|
||||
ggml_zdnn_pool * pool = nullptr;
|
||||
T * ptr = nullptr;
|
||||
size_t actual_size = 0;
|
||||
|
||||
ggml_zdnn_pool_alloc() = default;
|
||||
|
||||
ggml_zdnn_pool_alloc(ggml_zdnn_pool & pool, size_t size) {
|
||||
alloc(pool, size);
|
||||
}
|
||||
|
||||
~ggml_zdnn_pool_alloc() {
|
||||
if (ptr != nullptr) {
|
||||
pool->free(ptr, actual_size);
|
||||
}
|
||||
}
|
||||
|
||||
// size is in number of elements
|
||||
T * alloc(ggml_zdnn_pool & pool, size_t size) {
|
||||
GGML_ASSERT(ptr == nullptr);
|
||||
this->pool = &pool;
|
||||
ptr = (T *) pool.alloc(size * sizeof(T), &actual_size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
T * get() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ggml_zdnn_pool_alloc(const ggml_zdnn_pool_alloc &) = delete;
|
||||
ggml_zdnn_pool_alloc(ggml_zdnn_pool_alloc &&) = delete;
|
||||
ggml_zdnn_pool_alloc & operator=(const ggml_zdnn_pool_alloc &) = delete;
|
||||
ggml_zdnn_pool_alloc & operator=(ggml_zdnn_pool_alloc &&) = delete;
|
||||
};
|
||||
|
||||
#endif // GGML_ZDNN_SCRATCH_HPP
|
||||
+46
-2
@@ -1095,6 +1095,7 @@ static const char * GGML_OP_NAME[GGML_OP_COUNT] = {
|
||||
"DSV4_HC_COMB",
|
||||
"DSV4_HC_PRE",
|
||||
"DSV4_HC_POST",
|
||||
"TOP_K_MASK",
|
||||
|
||||
"UNARY",
|
||||
|
||||
@@ -1112,7 +1113,7 @@ static const char * GGML_OP_NAME[GGML_OP_COUNT] = {
|
||||
"GLU",
|
||||
};
|
||||
|
||||
static_assert(GGML_OP_COUNT == 101, "GGML_OP_COUNT != 101");
|
||||
static_assert(GGML_OP_COUNT == 102, "GGML_OP_COUNT != 102");
|
||||
|
||||
static const char * GGML_OP_SYMBOL[GGML_OP_COUNT] = {
|
||||
"none",
|
||||
@@ -1210,6 +1211,7 @@ static const char * GGML_OP_SYMBOL[GGML_OP_COUNT] = {
|
||||
"dsv4_hc_comb(mixes, scale, base)",
|
||||
"dsv4_hc_pre(x, weights)",
|
||||
"dsv4_hc_post(x, residual, post, comb)",
|
||||
"top_k_mask(mask, top_k, score, blocks, extra)",
|
||||
|
||||
"unary(x)",
|
||||
|
||||
@@ -1227,7 +1229,7 @@ static const char * GGML_OP_SYMBOL[GGML_OP_COUNT] = {
|
||||
"glu(x)",
|
||||
};
|
||||
|
||||
static_assert(GGML_OP_COUNT == 101, "GGML_OP_COUNT != 101");
|
||||
static_assert(GGML_OP_COUNT == 102, "GGML_OP_COUNT != 102");
|
||||
|
||||
static_assert(GGML_OP_POOL_COUNT == 2, "GGML_OP_POOL_COUNT != 2");
|
||||
|
||||
@@ -6630,6 +6632,48 @@ struct ggml_tensor * ggml_dsv4_hc_post(
|
||||
return result;
|
||||
}
|
||||
|
||||
// ggml_top_k_mask
|
||||
|
||||
struct ggml_tensor * ggml_top_k_mask(
|
||||
struct ggml_context * ctx,
|
||||
struct ggml_tensor * mask,
|
||||
struct ggml_tensor * top_k,
|
||||
struct ggml_tensor * score,
|
||||
struct ggml_tensor * blocks,
|
||||
struct ggml_tensor * extra) {
|
||||
GGML_ASSERT(mask->type == GGML_TYPE_F16 || mask->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(mask->nb[0] == ggml_type_size(mask->type));
|
||||
GGML_ASSERT(top_k->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(top_k->ne[1] % mask->ne[1] == 0 && top_k->ne[2] % mask->ne[2] == 0 && top_k->ne[3] % mask->ne[3] == 0);
|
||||
|
||||
if (score) {
|
||||
GGML_ASSERT(score->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(score->ne[1] == top_k->ne[1] && score->ne[2] == top_k->ne[2] && score->ne[3] == top_k->ne[3]);
|
||||
}
|
||||
|
||||
if (blocks) {
|
||||
GGML_ASSERT(blocks->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(blocks->ne[2] == 1 && blocks->ne[3] == 1);
|
||||
GGML_ASSERT(!score || blocks->ne[1] == score->ne[0]);
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
GGML_ASSERT(extra->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(extra->ne[1] == top_k->ne[1] && extra->ne[2] == top_k->ne[2] && extra->ne[3] == top_k->ne[3]);
|
||||
}
|
||||
|
||||
struct ggml_tensor * result = ggml_new_tensor_4d(ctx, mask->type, mask->ne[0], top_k->ne[1], top_k->ne[2], top_k->ne[3]);
|
||||
|
||||
result->op = GGML_OP_TOP_K_MASK;
|
||||
result->src[0] = mask;
|
||||
result->src[1] = top_k;
|
||||
result->src[2] = score;
|
||||
result->src[3] = blocks;
|
||||
result->src[4] = extra;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ggml_hash_set ggml_hash_set_new(size_t size) {
|
||||
|
||||
@@ -263,6 +263,7 @@ class Keys:
|
||||
DIMENSION_SECTIONS = "{arch}.rope.dimension_sections"
|
||||
SECTION_ORDER = "{arch}.rope.section_order"
|
||||
FREQ_BASE = "{arch}.rope.freq_base"
|
||||
FREQ_BASE_SPATIAL = "{arch}.rope.freq_base_spatial"
|
||||
FREQ_BASE_SWA = "{arch}.rope.freq_base_swa"
|
||||
SCALING_TYPE = "{arch}.rope.scaling.type"
|
||||
SCALING_FACTOR = "{arch}.rope.scaling.factor"
|
||||
@@ -344,6 +345,7 @@ class Keys:
|
||||
ROUTING_BLOCK_COUNT = "{arch}.decision.routing_block_count"
|
||||
HEAD_COUNT = "{arch}.decision.head_count"
|
||||
MAX_HEAD_TOKENS = "{arch}.decision.max_head_tokens"
|
||||
LABELS = "{arch}.decision.labels" # the text of the token read for each option, in order
|
||||
TEMPERATURE = "{arch}.decision.temperature.{name}" # name: "<type>" or "<type>.<n_opt bucket>"
|
||||
SCORE_BIAS = "{arch}.decision.score_bias.{n}.{i}" # added to the logit of level i of a score question with n levels
|
||||
|
||||
@@ -369,6 +371,7 @@ class Keys:
|
||||
DNA_AUTO_TAGS = "tokenizer.ggml.dna_auto_tags"
|
||||
ADD_PREFIX = "tokenizer.ggml.add_space_prefix"
|
||||
REMOVE_EXTRA_WS = "tokenizer.ggml.remove_extra_whitespaces"
|
||||
CLEAN_SPACES = "tokenizer.ggml.clean_spaces"
|
||||
PRECOMPILED_CHARSMAP = "tokenizer.ggml.precompiled_charsmap"
|
||||
SUPPRESS_TOKENS = "tokenizer.ggml.suppress_tokens"
|
||||
HF_JSON = "tokenizer.huggingface.json"
|
||||
@@ -419,6 +422,8 @@ class Keys:
|
||||
PREPROC_MIN_TILES = "clip.vision.preproc_min_tiles"
|
||||
PREPROC_MAX_TILES = "clip.vision.preproc_max_tiles"
|
||||
PREPROC_IMAGE_SIZE = "clip.vision.preproc_image_size"
|
||||
PREPROC_TILE_SIZE = "clip.vision.preproc_tile_size"
|
||||
PREPROC_MAX_PIXELS_TOLERANCE = "clip.vision.preproc_max_pixels_tolerance"
|
||||
PATCH_SIZE = "clip.vision.patch_size"
|
||||
EMBEDDING_LENGTH = "clip.vision.embedding_length"
|
||||
FEED_FORWARD_LENGTH = "clip.vision.feed_forward_length"
|
||||
@@ -570,8 +575,10 @@ class MODEL_ARCH(IntEnum):
|
||||
QWEN2 = auto()
|
||||
QWEN2MOE = auto()
|
||||
QWEN2VL = auto()
|
||||
SENSENOVA_U1 = auto()
|
||||
QWEN3 = auto()
|
||||
QWEN3MOE = auto()
|
||||
FLINT = auto()
|
||||
QWEN3NEXT = auto()
|
||||
HRM_TEXT = auto()
|
||||
BEAM = auto()
|
||||
@@ -1387,7 +1394,9 @@ MODEL_ARCH_NAMES: dict[MODEL_ARCH, str] = {
|
||||
MODEL_ARCH.QWEN2: "qwen2",
|
||||
MODEL_ARCH.QWEN2MOE: "qwen2moe",
|
||||
MODEL_ARCH.QWEN2VL: "qwen2vl",
|
||||
MODEL_ARCH.SENSENOVA_U1: "sensenova_u1",
|
||||
MODEL_ARCH.QWEN3: "qwen3",
|
||||
MODEL_ARCH.FLINT: "flint",
|
||||
MODEL_ARCH.QWEN3MOE: "qwen3moe",
|
||||
MODEL_ARCH.QWEN3NEXT: "qwen3next",
|
||||
MODEL_ARCH.HRM_TEXT: "hrm_text",
|
||||
@@ -2913,6 +2922,24 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.FFN_DOWN_SHEXP,
|
||||
MODEL_TENSOR.FFN_UP_SHEXP,
|
||||
],
|
||||
MODEL_ARCH.SENSENOVA_U1: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
MODEL_TENSOR.OUTPUT,
|
||||
MODEL_TENSOR.ROPE_FREQS,
|
||||
MODEL_TENSOR.ATTN_NORM,
|
||||
MODEL_TENSOR.ATTN_QKV,
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
MODEL_TENSOR.ATTN_Q_NORM,
|
||||
MODEL_TENSOR.ATTN_K,
|
||||
MODEL_TENSOR.ATTN_K_NORM,
|
||||
MODEL_TENSOR.ATTN_V,
|
||||
MODEL_TENSOR.ATTN_OUT,
|
||||
MODEL_TENSOR.FFN_NORM,
|
||||
MODEL_TENSOR.FFN_GATE,
|
||||
MODEL_TENSOR.FFN_DOWN,
|
||||
MODEL_TENSOR.FFN_UP,
|
||||
],
|
||||
MODEL_ARCH.QWEN3: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
@@ -2950,6 +2977,33 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.FFN_DOWN_EXP,
|
||||
MODEL_TENSOR.FFN_UP_EXP,
|
||||
],
|
||||
|
||||
MODEL_ARCH.FLINT: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
MODEL_TENSOR.OUTPUT,
|
||||
MODEL_TENSOR.ATTN_NORM,
|
||||
MODEL_TENSOR.ATTN_QKV,
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
MODEL_TENSOR.ATTN_Q_NORM,
|
||||
MODEL_TENSOR.ATTN_K,
|
||||
MODEL_TENSOR.ATTN_K_NORM,
|
||||
MODEL_TENSOR.ATTN_V,
|
||||
MODEL_TENSOR.ATTN_OUT,
|
||||
MODEL_TENSOR.FFN_NORM,
|
||||
MODEL_TENSOR.FFN_GATE_INP,
|
||||
MODEL_TENSOR.FFN_GATE_EXP,
|
||||
MODEL_TENSOR.FFN_DOWN_EXP,
|
||||
MODEL_TENSOR.FFN_UP_EXP,
|
||||
MODEL_TENSOR.ATTN_SINKS,
|
||||
MODEL_TENSOR.FFN_GATE,
|
||||
MODEL_TENSOR.FFN_UP,
|
||||
MODEL_TENSOR.FFN_DOWN,
|
||||
MODEL_TENSOR.FFN_GATE_SHEXP,
|
||||
MODEL_TENSOR.FFN_UP_SHEXP,
|
||||
MODEL_TENSOR.FFN_DOWN_SHEXP,
|
||||
MODEL_TENSOR.FFN_EXP_PROBS_B,
|
||||
],
|
||||
MODEL_ARCH.QWEN3NEXT: [
|
||||
MODEL_TENSOR.TOKEN_EMBD,
|
||||
MODEL_TENSOR.OUTPUT_NORM,
|
||||
@@ -6337,6 +6391,7 @@ class DecisionType:
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
class VisionProjectorType:
|
||||
@@ -6481,6 +6536,7 @@ KEY_ATTENTION_LAYERNORM_RMS_EPS = Keys.Attention.LAYERNORM_RMS_EPS
|
||||
# RoPE
|
||||
KEY_ROPE_DIMENSION_COUNT = Keys.Rope.DIMENSION_COUNT
|
||||
KEY_ROPE_FREQ_BASE = Keys.Rope.FREQ_BASE
|
||||
KEY_ROPE_FREQ_BASE_SPATIAL = Keys.Rope.FREQ_BASE_SPATIAL
|
||||
KEY_ROPE_SCALING_TYPE = Keys.Rope.SCALING_TYPE
|
||||
KEY_ROPE_SCALING_FACTOR = Keys.Rope.SCALING_FACTOR
|
||||
KEY_ROPE_SCALING_ORIG_CTX_LEN = Keys.Rope.SCALING_ORIG_CTX_LEN
|
||||
|
||||
@@ -1167,6 +1167,9 @@ class GGUFWriter:
|
||||
def add_rope_freq_base(self, value: float) -> None:
|
||||
self.add_float32(Keys.Rope.FREQ_BASE.format(arch=self.arch), value)
|
||||
|
||||
def add_rope_freq_base_spatial(self, value: float) -> None:
|
||||
self.add_float32(Keys.Rope.FREQ_BASE_SPATIAL.format(arch=self.arch), value)
|
||||
|
||||
def add_rope_scaling_type(self, value: RopeScalingType) -> None:
|
||||
self.add_string(Keys.Rope.SCALING_TYPE.format(arch=self.arch), value.value)
|
||||
|
||||
@@ -1323,6 +1326,9 @@ class GGUFWriter:
|
||||
def add_remove_extra_whitespaces(self, value: bool) -> None:
|
||||
self.add_bool(Keys.Tokenizer.REMOVE_EXTRA_WS, value)
|
||||
|
||||
def add_clean_spaces(self, value: bool) -> None:
|
||||
self.add_bool(Keys.Tokenizer.CLEAN_SPACES, value)
|
||||
|
||||
def add_precompiled_charsmap(self, charsmap: bytes) -> None:
|
||||
self.add_array(Keys.Tokenizer.PRECOMPILED_CHARSMAP, charsmap)
|
||||
|
||||
@@ -1401,6 +1407,9 @@ class GGUFWriter:
|
||||
def add_decision_max_head_tokens(self, value: int) -> None:
|
||||
self.add_uint32(Keys.Decision.MAX_HEAD_TOKENS.format(arch=self.arch), value)
|
||||
|
||||
def add_decision_labels(self, labels: Sequence[str]) -> None:
|
||||
self.add_array(Keys.Decision.LABELS.format(arch=self.arch), labels)
|
||||
|
||||
def add_decision_temperature(self, name: str, value: float) -> None:
|
||||
self.add_float32(Keys.Decision.TEMPERATURE.format(arch=self.arch, name=name), value)
|
||||
|
||||
@@ -1470,6 +1479,12 @@ class GGUFWriter:
|
||||
def add_vision_preproc_image_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.PREPROC_IMAGE_SIZE, value)
|
||||
|
||||
def add_vision_preproc_tile_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.PREPROC_TILE_SIZE, value)
|
||||
|
||||
def add_vision_preproc_max_pixels_tolerance(self, value: float) -> None:
|
||||
self.add_float32(Keys.ClipVision.PREPROC_MAX_PIXELS_TOLERANCE, value)
|
||||
|
||||
def add_vision_projector_query_side(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.Projector.QUERY_SIDE, value)
|
||||
|
||||
|
||||
@@ -429,6 +429,7 @@ class TensorNameMap:
|
||||
),
|
||||
|
||||
MODEL_TENSOR.ATTN_SINKS: (
|
||||
"model.layers.{bid}.self_attn.softmax_offset", # flint
|
||||
"model.layers.{bid}.self_attn.sinks", # openai-moe
|
||||
"model.layers.{bid}.self_attn.attention_sink_bias", # mimov2
|
||||
"model.layers.{bid}.self_attn.learnable_sink_param", # hy-v4
|
||||
|
||||
@@ -55,6 +55,7 @@ class SpecialVocab:
|
||||
normalizer_nfc: bool | None
|
||||
normalizer_lowercase: bool | None
|
||||
normalizer_strip_accents: bool | None
|
||||
clean_spaces: bool | None
|
||||
|
||||
def __init__(
|
||||
self, path: str | os.PathLike[str], load_merges: bool = False,
|
||||
@@ -70,6 +71,7 @@ class SpecialVocab:
|
||||
self.normalizer_nfc = None
|
||||
self.normalizer_lowercase = None
|
||||
self.normalizer_strip_accents = None
|
||||
self.clean_spaces = None
|
||||
if special_token_types is not None:
|
||||
self.special_token_types = special_token_types
|
||||
else:
|
||||
@@ -120,6 +122,10 @@ class SpecialVocab:
|
||||
if not quiet:
|
||||
logger.info(f'Setting normalizer_strip_accents to {self.normalizer_strip_accents}')
|
||||
gw.add_normalizer_strip_accents(self.normalizer_strip_accents)
|
||||
if self.clean_spaces is not None:
|
||||
if not quiet:
|
||||
logger.info(f'Setting clean_spaces to {self.clean_spaces}')
|
||||
gw.add_clean_spaces(self.clean_spaces)
|
||||
|
||||
def _load(self, path: Path) -> None:
|
||||
self._try_load_from_tokenizer_json(path)
|
||||
@@ -345,6 +351,10 @@ class SpecialVocab:
|
||||
self.chat_template = chat_template
|
||||
else:
|
||||
logger.warning(f'Bad type for chat_template field in {tokenizer_config_file!r} - ignoring')
|
||||
# decode clean-up of spaces before punctuation (transformers clean_up_tokenization_spaces)
|
||||
clean_spaces = tokenizer_config.get('clean_up_tokenization_spaces')
|
||||
if isinstance(clean_spaces, bool):
|
||||
self.clean_spaces = clean_spaces
|
||||
for typ in self.special_token_types:
|
||||
add_entry = tokenizer_config.get(f'add_{typ}_token')
|
||||
if isinstance(add_entry, bool):
|
||||
|
||||
@@ -244,8 +244,10 @@ elif [ "$DO_CHECK" -eq 1 ]; then
|
||||
# From here on, we don't want to exit on first error
|
||||
set +e
|
||||
|
||||
ACC_STRICT=
|
||||
[ "$STRICT" -eq 0 ] || ACC_STRICT="-strict"
|
||||
abi-compliance-checker \
|
||||
-strict \
|
||||
$ACC_STRICT \
|
||||
-source \
|
||||
-library "$PROJECT_NAME" \
|
||||
-old "$BUILD_DIR/apiabi/api.dump" \
|
||||
|
||||
@@ -179,13 +179,13 @@ static void llama_adapter_lora_init_impl(llama_model & model, FILE * file, llama
|
||||
? format("%s[%s,%zu]", gguf_type_name(type), gguf_type_name(gguf_get_arr_type(gguf_ctx, i)), gguf_get_arr_n(gguf_ctx, i))
|
||||
: gguf_type_name(type);
|
||||
const char * name = gguf_get_key(gguf_ctx, i);
|
||||
const std::string value = gguf_kv_to_str(gguf_ctx, i);
|
||||
const size_t MAX_VALUE_LEN = 40;
|
||||
const std::string value = gguf_kv_to_str(gguf_ctx, i, MAX_VALUE_LEN);
|
||||
|
||||
if (type != GGUF_TYPE_ARRAY) {
|
||||
adapter.gguf_kv.emplace(name, value);
|
||||
}
|
||||
|
||||
const size_t MAX_VALUE_LEN = 40;
|
||||
std::string print_value = value.size() > MAX_VALUE_LEN ? format("%s...", value.substr(0, MAX_VALUE_LEN - 3).c_str()) : value;
|
||||
replace_all(print_value, "\n", "\\n");
|
||||
|
||||
@@ -334,9 +334,10 @@ static void llama_adapter_lora_init_impl(llama_model & model, FILE * file, llama
|
||||
if (!model_tensor) {
|
||||
throw std::runtime_error("LoRA tensor '" + name + "' does not exist in base model (hint: maybe wrong base model?)");
|
||||
}
|
||||
// the LoRA delta of a Hadamard-folded weight reads the transformed input, so the result is wrong
|
||||
if (model.hdmd.weight_blocks.count(name) || model.hdmd.inverse_blocks.count(name)) {
|
||||
throw std::runtime_error("LoRA tensor '" + name + "' targets a prism.hadamard folded weight, which is not supported");
|
||||
// build_lora_mm feeds a folded weight's LoRA delta the primal input, so folded matmul weights are fine;
|
||||
// a latent lookup table (token_embd, also the head when the output is tied) stores rotated rows, refuse it
|
||||
if (model.hdmd.inverse_blocks.count(name)) {
|
||||
throw std::runtime_error("LoRA tensor '" + name + "' targets a prism.hadamard latent lookup table, which is not supported");
|
||||
}
|
||||
|
||||
auto * buft = ggml_backend_buffer_get_type(model_tensor->buffer);
|
||||
|
||||
+6
-1
@@ -33,7 +33,9 @@ static const std::map<llm_arch, const char *> LLM_ARCH_NAMES = {
|
||||
{ LLM_ARCH_QWEN2, "qwen2" },
|
||||
{ LLM_ARCH_QWEN2MOE, "qwen2moe" },
|
||||
{ LLM_ARCH_QWEN2VL, "qwen2vl" },
|
||||
{ LLM_ARCH_SENSENOVA_U1, "sensenova_u1" },
|
||||
{ LLM_ARCH_QWEN3, "qwen3" },
|
||||
{ LLM_ARCH_FLINT, "flint" },
|
||||
{ LLM_ARCH_QWEN3MOE, "qwen3moe" },
|
||||
{ LLM_ARCH_QWEN3NEXT, "qwen3next" },
|
||||
{ LLM_ARCH_QWEN3VL, "qwen3vl" },
|
||||
@@ -339,6 +341,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_ROPE_DIMENSION_SECTIONS, "%s.rope.dimension_sections" },
|
||||
{ LLM_KV_ROPE_SECTION_ORDER, "%s.rope.section_order" },
|
||||
{ LLM_KV_ROPE_FREQ_BASE, "%s.rope.freq_base" },
|
||||
{ LLM_KV_ROPE_FREQ_BASE_SPATIAL, "%s.rope.freq_base_spatial" },
|
||||
{ LLM_KV_ROPE_FREQ_BASE_SWA, "%s.rope.freq_base_swa" },
|
||||
{ LLM_KV_ROPE_SCALE_LINEAR, "%s.rope.scale_linear" },
|
||||
{ LLM_KV_ROPE_SCALING_TYPE, "%s.rope.scaling.type" },
|
||||
@@ -383,6 +386,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_DECISION_BLOCK_COUNT, "%s.decision.block_count" },
|
||||
{ LLM_KV_DECISION_ROUTING_BLOCK_COUNT, "%s.decision.routing_block_count" },
|
||||
{ LLM_KV_DECISION_HEAD_COUNT, "%s.decision.head_count" },
|
||||
{ LLM_KV_DECISION_LABELS, "%s.decision.labels" },
|
||||
|
||||
{ LLM_KV_TARGET_LAYERS, "%s.target_layers" },
|
||||
{ LLM_KV_TARGET_HIDDEN_SIZE, "%s.target_hidden_size" },
|
||||
@@ -433,6 +437,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_TOKENIZER_DNA_AUTO_TAGS, "tokenizer.ggml.dna_auto_tags" },
|
||||
{ LLM_KV_TOKENIZER_ADD_PREFIX, "tokenizer.ggml.add_space_prefix" },
|
||||
{ LLM_KV_TOKENIZER_REMOVE_EXTRA_WS, "tokenizer.ggml.remove_extra_whitespaces" },
|
||||
{ LLM_KV_TOKENIZER_CLEAN_SPACES, "tokenizer.ggml.clean_spaces" },
|
||||
{ LLM_KV_TOKENIZER_PRECOMPILED_CHARSMAP, "tokenizer.ggml.precompiled_charsmap" },
|
||||
{ LLM_KV_TOKENIZER_HF_JSON, "tokenizer.huggingface.json" },
|
||||
{ LLM_KV_TOKENIZER_RWKV, "tokenizer.rwkv.world" },
|
||||
@@ -1109,7 +1114,7 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
|
||||
{LLM_TENSOR_MASKED_EMBD_ORDERING, {LLM_TENSOR_LAYER_INPUT, GGML_OP_NONE}},
|
||||
// eagle3
|
||||
{LLM_TENSOR_FC, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_D2T, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_GET_ROWS}},
|
||||
{LLM_TENSOR_D2T, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_SET_ROWS}},
|
||||
// dspark
|
||||
{LLM_TENSOR_DSPARK_MARKOV_W1, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_GET_ROWS}},
|
||||
{LLM_TENSOR_DSPARK_MARKOV_W2, {LLM_TENSOR_LAYER_OUTPUT, GGML_OP_MUL_MAT}},
|
||||
|
||||
@@ -38,8 +38,10 @@ enum llm_arch {
|
||||
LLM_ARCH_QWEN2,
|
||||
LLM_ARCH_QWEN2MOE,
|
||||
LLM_ARCH_QWEN2VL,
|
||||
LLM_ARCH_SENSENOVA_U1,
|
||||
LLM_ARCH_QWEN3,
|
||||
LLM_ARCH_QWEN3MOE,
|
||||
LLM_ARCH_FLINT,
|
||||
LLM_ARCH_QWEN3NEXT,
|
||||
LLM_ARCH_QWEN3VL,
|
||||
LLM_ARCH_QWEN3VLMOE,
|
||||
@@ -344,6 +346,7 @@ enum llm_kv {
|
||||
LLM_KV_ROPE_DIMENSION_SECTIONS,
|
||||
LLM_KV_ROPE_SECTION_ORDER,
|
||||
LLM_KV_ROPE_FREQ_BASE,
|
||||
LLM_KV_ROPE_FREQ_BASE_SPATIAL,
|
||||
LLM_KV_ROPE_FREQ_BASE_SWA,
|
||||
LLM_KV_ROPE_SCALE_LINEAR,
|
||||
LLM_KV_ROPE_SCALING_TYPE,
|
||||
@@ -397,6 +400,7 @@ enum llm_kv {
|
||||
LLM_KV_TOKENIZER_DNA_AUTO_TAGS,
|
||||
LLM_KV_TOKENIZER_ADD_PREFIX,
|
||||
LLM_KV_TOKENIZER_REMOVE_EXTRA_WS,
|
||||
LLM_KV_TOKENIZER_CLEAN_SPACES,
|
||||
LLM_KV_TOKENIZER_PRECOMPILED_CHARSMAP,
|
||||
LLM_KV_TOKENIZER_HF_JSON,
|
||||
LLM_KV_TOKENIZER_RWKV,
|
||||
@@ -431,6 +435,7 @@ enum llm_kv {
|
||||
LLM_KV_DECISION_BLOCK_COUNT,
|
||||
LLM_KV_DECISION_ROUTING_BLOCK_COUNT,
|
||||
LLM_KV_DECISION_HEAD_COUNT,
|
||||
LLM_KV_DECISION_LABELS,
|
||||
|
||||
LLM_KV_TARGET_LAYERS,
|
||||
LLM_KV_TARGET_HIDDEN_SIZE,
|
||||
|
||||
@@ -165,6 +165,12 @@ static const llm_fused_op_probe llm_fused_op_dsv4_hc_post_probe = {
|
||||
/*.n_tokens_per_seq =*/ 1,
|
||||
};
|
||||
|
||||
static const llm_fused_op_probe llm_fused_op_top_k_mask_probe = {
|
||||
/*.op =*/ LLM_FUSED_OP_TOP_K_MASK,
|
||||
/*.name =*/ "fused top-k mask",
|
||||
/*.n_tokens_per_seq =*/ 1,
|
||||
};
|
||||
|
||||
llama_context::llama_context(
|
||||
const llama_model & model,
|
||||
llama_context_params params) :
|
||||
@@ -328,6 +334,10 @@ llama_context::llama_context(
|
||||
cparams.fused_dsv4_hc_post = true;
|
||||
cparams.auto_fhc = true;
|
||||
|
||||
// only the indexer models build this mask, skip the probe for the others
|
||||
cparams.fused_top_k_mask = true;
|
||||
cparams.auto_ftkm = hparams.indexer_top_k > 0;
|
||||
|
||||
// with causal attention, the batch size is limited by the context size
|
||||
cparams.n_batch = cparams.causal_attn ? std::min(cparams.n_ctx, params.n_batch) : params.n_batch;
|
||||
|
||||
@@ -671,6 +681,12 @@ void llama_context::resolve_fused_ops(const llama_memory_context_i * mctx, uint3
|
||||
resolve(llm_fused_op_dsv4_hc_post_probe, cparams.fused_dsv4_hc_post);
|
||||
cparams.auto_fhc = false;
|
||||
}
|
||||
|
||||
if (cparams.auto_ftkm) {
|
||||
LLAMA_LOG_INFO("%s: resolving fused top-k mask support:\n", func);
|
||||
resolve(llm_fused_op_top_k_mask_probe, cparams.fused_top_k_mask);
|
||||
cparams.auto_ftkm = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int llama_graph_n_input_tensors(ggml_cgraph * gf) {
|
||||
|
||||
@@ -48,6 +48,8 @@ struct llama_cparams {
|
||||
bool fused_dsv4_hc_comb;
|
||||
bool fused_dsv4_hc_post;
|
||||
bool auto_fhc;
|
||||
bool fused_top_k_mask; // use fused top-k sparse attention mask
|
||||
bool auto_ftkm;
|
||||
bool no_perf;
|
||||
bool warmup; // TODO: remove [TAG_LLAMA_GRAPH_NO_WARMUP]
|
||||
bool op_offload;
|
||||
|
||||
@@ -122,6 +122,9 @@ enum llama_decision_order {
|
||||
// The embeddings output has one value per entry: row i is the score of option i
|
||||
LLAMA_API bool llama_batch_ext_set_decision_order(struct llama_batch_ext * batch, int32_t idx, enum llama_decision_order order);
|
||||
|
||||
// Label of option i of a decision model ("<arch>.decision.labels"), nullptr past the last one
|
||||
LLAMA_API const char * llama_model_decision_label(const struct llama_model * model, uint32_t i);
|
||||
|
||||
// mirrors:
|
||||
// LLAMA_API float * llama_get_embeddings(struct llama_context * ctx);
|
||||
LLAMA_API float * llama_get_embeddings_nextn(struct llama_context * ctx);
|
||||
|
||||
+107
-29
@@ -1630,8 +1630,9 @@ ggml_tensor * llm_graph_context::build_lora_mm(
|
||||
ggml_tensor * w,
|
||||
ggml_tensor * cur,
|
||||
ggml_tensor * w_s) const {
|
||||
cur = build_hadamard_input(w, cur);
|
||||
ggml_tensor * res = ggml_mul_mat(ctx0, w, cur);
|
||||
// a Hadamard-folded weight reads the transformed input; a LoRA delta lives in the primal basis and reads `cur`
|
||||
ggml_tensor * cur_mm = build_hadamard_input(w, cur);
|
||||
ggml_tensor * res = ggml_mul_mat(ctx0, w, cur_mm);
|
||||
|
||||
if (prec_policy) {
|
||||
prec_policy->apply(res);
|
||||
@@ -1666,17 +1667,55 @@ ggml_tensor * llm_graph_context::build_lora_mm(
|
||||
return res;
|
||||
}
|
||||
|
||||
llm_graph_amax_src1 llm_graph_context::build_amax_src1_f16(
|
||||
ggml_tensor * cur) const {
|
||||
llm_graph_amax_src1 result = {
|
||||
/*.res =*/ cur,
|
||||
/*.scale_s =*/ nullptr,
|
||||
};
|
||||
|
||||
const int64_t n_embd = cur->ne[0];
|
||||
|
||||
// amax = max|cur| along the rows for each [expert, token] -> [1, n_expert, n_tokens]
|
||||
ggml_tensor * ab = ggml_abs(ctx0, cur);
|
||||
ggml_tensor * amax = ggml_pool_1d(ctx0, ab, GGML_OP_POOL_MAX, n_embd, n_embd, 0);
|
||||
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/26223
|
||||
constexpr float k_log2_e = 1.4426950408889634f; // 1 / ln(2)
|
||||
constexpr float k_ln_2 = 0.6931471805599453f;
|
||||
|
||||
ggml_tensor * e;
|
||||
// note: this can technically be:
|
||||
// e = ggml_scale(ctx0, amax, 1.0f/32768.0f);
|
||||
// but we leave x32 headroom for the F16 sums of 32 values in Q8 activation blocks
|
||||
e = ggml_scale(ctx0, amax, 1.0f/1024.0f);
|
||||
e = ggml_log (ctx0, e);
|
||||
e = ggml_scale(ctx0, e, k_log2_e);
|
||||
e = ggml_ceil (ctx0, e);
|
||||
e = ggml_clamp(ctx0, e, 0.0f, 127.0f);
|
||||
e = ggml_scale(ctx0, e, k_ln_2);
|
||||
e = ggml_exp (ctx0, e);
|
||||
|
||||
result = {
|
||||
/*.res =*/ ggml_div(ctx0, cur, e),
|
||||
/*.scale_s =*/ e,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ggml_tensor * llm_graph_context::build_lora_mm_id(
|
||||
ggml_tensor * w, // ggml_tensor * as
|
||||
ggml_tensor * cur, // ggml_tensor * b
|
||||
ggml_tensor * ids,
|
||||
ggml_tensor * w_s,
|
||||
ggml_tensor * slots) const {
|
||||
cur = build_hadamard_input(w, cur);
|
||||
// as in build_lora_mm: the folded weight reads the transformed input, the LoRA delta reads `cur`
|
||||
ggml_tensor * cur_mm = build_hadamard_input(w, cur);
|
||||
// the experts in the MoE cache are selected by their slots
|
||||
ggml_tensor * res = slots == nullptr ?
|
||||
ggml_mul_mat_id(ctx0, w, cur, ids) :
|
||||
ggml_mul_mat_id(ctx0, moe_cache->get_experts(w), cur, slots);
|
||||
ggml_mul_mat_id(ctx0, w, cur_mm, ids) :
|
||||
ggml_mul_mat_id(ctx0, moe_cache->get_experts(w), cur_mm, slots);
|
||||
|
||||
if (prec_policy) {
|
||||
prec_policy->apply(res);
|
||||
@@ -2465,12 +2504,25 @@ ggml_tensor * llm_graph_context::build_moe_ffn(
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
|
||||
experts = build_lora_mm_id(down_exps, cur, selected_experts, down_exps_s, slots); // [n_embd, n_expert_used, n_tokens]
|
||||
if (arch == LLM_ARCH_MISTRAL4 || arch == LLM_ARCH_MELLUM) {
|
||||
// src1 can exceed F16 range
|
||||
ggml_prec_set_src(experts, GGML_PREC_F32, 1);
|
||||
{
|
||||
// TODO: this graph-level rescaling should only be applied when the corresponding backend does not support
|
||||
// ggml_prec_set_src(experts, GGML_PREC_BF16, 1);
|
||||
// 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) {
|
||||
amax_src1 = build_amax_src1_f16(cur);
|
||||
cur = amax_src1.res;
|
||||
}
|
||||
|
||||
experts = build_lora_mm_id(down_exps, cur, selected_experts, down_exps_s, slots); // [n_embd, n_expert_used, n_tokens]
|
||||
//ggml_prec_set_src(experts, GGML_PREC_BF16, 1); // (see TODO above)
|
||||
cb(experts, "ffn_moe_down", il);
|
||||
|
||||
if (amax_src1.scale_s) {
|
||||
experts = ggml_mul(ctx0, experts, amax_src1.scale_s);
|
||||
}
|
||||
}
|
||||
cb(experts, "ffn_moe_down", il);
|
||||
|
||||
if (down_exps_s) {
|
||||
cb(experts, "ffn_moe_down_scaled", il);
|
||||
@@ -3328,6 +3380,27 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
return cur;
|
||||
}
|
||||
|
||||
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,
|
||||
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];
|
||||
|
||||
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, kq_mask, rows(top_k), rows(score), blocks, rows(extra));
|
||||
res->add_fused_node({LLM_FUSED_OP_TOP_K_MASK, cur, il});
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
ggml_tensor * llm_graph_context::build_attn(
|
||||
llm_graph_input_attn_k_dsa * inp,
|
||||
ggml_tensor * wo,
|
||||
@@ -3360,31 +3433,36 @@ ggml_tensor * llm_graph_context::build_attn(
|
||||
|
||||
const auto & kq_mask = inp->get_kq_mask_mla();
|
||||
|
||||
// prepare new kq mask - starts filled with -INFINITY
|
||||
ggml_tensor * kq_mask_all = ggml_fill(ctx0, kq_mask, -INFINITY);
|
||||
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, il);
|
||||
} else {
|
||||
// prepare new kq mask - starts filled with -INFINITY
|
||||
ggml_tensor * kq_mask_all = ggml_fill(ctx0, kq_mask, -INFINITY);
|
||||
|
||||
// reshape KQ mask into tensor with rows of size 1:
|
||||
// [n_kv, n_batch, 1, n_stream] -> [1, n_kv, n_batch, n_stream]
|
||||
kq_mask_all = ggml_view_4d(ctx0, kq_mask_all, 1, kq_mask_all->ne[0], kq_mask_all->ne[1], kq_mask_all->ne[3], kq_mask_all->nb[0], kq_mask_all->nb[1], kq_mask_all->nb[2], 0);
|
||||
// reshape KQ mask into tensor with rows of size 1:
|
||||
// [n_kv, n_batch, 1, n_stream] -> [1, n_kv, n_batch, n_stream]
|
||||
kq_mask_all = ggml_view_4d(ctx0, kq_mask_all, 1, kq_mask_all->ne[0], kq_mask_all->ne[1], kq_mask_all->ne[3], kq_mask_all->nb[0], kq_mask_all->nb[1], kq_mask_all->nb[2], 0);
|
||||
|
||||
// reshape top_k indices: [n_top_k, n_batch, 1, n_stream] -> [n_top_k, n_batch, n_stream, 1]
|
||||
ggml_tensor * top_k_3d = ggml_view_4d(ctx0, top_k, top_k->ne[0], top_k->ne[1], top_k->ne[3], 1, top_k->nb[1], top_k->nb[2], top_k->ne[3]*top_k->nb[3], 0);
|
||||
// reshape top_k indices: [n_top_k, n_batch, 1, n_stream] -> [n_top_k, n_batch, n_stream, 1]
|
||||
ggml_tensor * top_k_3d = ggml_view_4d(ctx0, top_k, top_k->ne[0], top_k->ne[1], top_k->ne[3], 1, top_k->nb[1], top_k->nb[2], top_k->ne[3]*top_k->nb[3], 0);
|
||||
|
||||
// prepare zero-filled tensor with rows of size 1: [1, n_top_k, n_batch, n_stream]
|
||||
// this will be our source of zero values for unmasking top k mask elements
|
||||
ggml_tensor * zeros = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, 1, top_k_3d->ne[0], top_k_3d->ne[1], top_k_3d->ne[2]);
|
||||
zeros = ggml_fill(ctx0, zeros, 0.0f);
|
||||
// prepare zero-filled tensor with rows of size 1: [1, n_top_k, n_batch, n_stream]
|
||||
// this will be our source of zero values for unmasking top k mask elements
|
||||
ggml_tensor * zeros = ggml_new_tensor_4d(ctx0, GGML_TYPE_F32, 1, top_k_3d->ne[0], top_k_3d->ne[1], top_k_3d->ne[2]);
|
||||
zeros = ggml_fill(ctx0, zeros, 0.0f);
|
||||
|
||||
// modify KQ mask by unmasking elements that are in top_k indices
|
||||
// ggml_set_rows([1, n_kv, n_batch, n_stream], [1, n_top_k, n_batch, n_stream], [n_top_k, n_batch, n_stream, 1])
|
||||
ggml_tensor * kq_mask_top_k = ggml_set_rows(ctx0, kq_mask_all, zeros, top_k_3d);
|
||||
// modify KQ mask by unmasking elements that are in top_k indices
|
||||
// ggml_set_rows([1, n_kv, n_batch, n_stream], [1, n_top_k, n_batch, n_stream], [n_top_k, n_batch, n_stream, 1])
|
||||
kq_mask_top_k = ggml_set_rows(ctx0, kq_mask_all, zeros, top_k_3d);
|
||||
|
||||
// reshape to restore the original shape of KQ mask:
|
||||
// [1, n_kv, n_batch, n_stream] -> [n_kv, n_batch, 1, n_stream]
|
||||
kq_mask_top_k = ggml_view_4d(ctx0, kq_mask_top_k, kq_mask_top_k->ne[1], kq_mask_top_k->ne[2], 1, kq_mask_top_k->ne[3], kq_mask_top_k->nb[2], kq_mask_top_k->nb[3], kq_mask_top_k->nb[3], 0);
|
||||
// reshape to restore the original shape of KQ mask:
|
||||
// [1, n_kv, n_batch, n_stream] -> [n_kv, n_batch, 1, n_stream]
|
||||
kq_mask_top_k = ggml_view_4d(ctx0, kq_mask_top_k, kq_mask_top_k->ne[1], kq_mask_top_k->ne[2], 1, kq_mask_top_k->ne[3], kq_mask_top_k->nb[2], kq_mask_top_k->nb[3], kq_mask_top_k->nb[3], 0);
|
||||
|
||||
// combine with the original kq mask
|
||||
kq_mask_top_k = ggml_add(ctx0, kq_mask_top_k, kq_mask);
|
||||
// combine with the original kq mask
|
||||
kq_mask_top_k = ggml_add(ctx0, kq_mask_top_k, kq_mask);
|
||||
}
|
||||
|
||||
ggml_tensor * q = q_cur;
|
||||
ggml_tensor * k = mctx_cur->get_k(ctx0, il);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user