mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-07-17 01:45:59 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6554db6ed0 | |||
| 9562fea713 | |||
| e1fa323932 | |||
| 941011dabb | |||
| 8dff020c64 |
@@ -6,7 +6,7 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths: [
|
||||
'.github/workflows/build-self-hosted.yml',
|
||||
'.github/workflows/build.yml',
|
||||
'**/CMakeLists.txt',
|
||||
'**/.cmake',
|
||||
'**/*.h',
|
||||
@@ -48,8 +48,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# note: this is dud token to avoid rate limiting (https://github.com/ggml-org/llama.cpp/pull/25706#issuecomment-4979941302)
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
GGML_NLOOP: 3
|
||||
GGML_N_THREADS: 1
|
||||
LLAMA_ARG_LOG_COLORS: 1
|
||||
|
||||
@@ -29,8 +29,6 @@ on:
|
||||
]
|
||||
|
||||
env:
|
||||
# note: this is dud token to avoid rate limiting (https://github.com/ggml-org/llama.cpp/pull/25706#issuecomment-4979941302)
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
LLAMA_ARG_LOG_COLORS: 1
|
||||
LLAMA_ARG_LOG_PREFIX: 1
|
||||
LLAMA_ARG_LOG_TIMESTAMPS: 1
|
||||
|
||||
+7
-44
@@ -1184,8 +1184,6 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.sampling.temp = 0.2; // lower temp by default for better quality
|
||||
} else if (ex == LLAMA_EXAMPLE_SERVER) {
|
||||
params.n_parallel = -1; // auto by default
|
||||
} else if (ex == LLAMA_EXAMPLE_TOKENIZE) {
|
||||
params.parse_special = true; // parse special tokens by default, like the old tokenize tool
|
||||
}
|
||||
|
||||
params.use_color = tty_can_use_colors();
|
||||
@@ -2753,14 +2751,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.model.path = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_EXPORT_LORA, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_MODEL"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_EXPORT_LORA, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_MODEL"));
|
||||
add_opt(common_arg(
|
||||
{"-mu", "--model-url"}, "MODEL_URL",
|
||||
"model download url (default: unused)",
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.model.url = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_MODEL_URL"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_MODEL_URL"));
|
||||
add_opt(common_arg(
|
||||
{ "-dr", "--docker-repo" }, "[<repo>/]<model>[:quant]",
|
||||
"Docker Hub model repository. repo is optional, default to ai/. quant is optional, default to :latest.\n"
|
||||
@@ -2769,7 +2767,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.model.docker_repo = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_DOCKER_REPO"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_DOCKER_REPO"));
|
||||
add_opt(common_arg(
|
||||
{"-hf", "-hfr", "--hf-repo"}, "<user>/<model>[:quant]",
|
||||
"Hugging Face model repository; quant is optional, case-insensitive, default to Q4_K_M, or falls back to the first file in the repo if Q4_K_M doesn't exist.\n"
|
||||
@@ -2779,14 +2777,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.model.hf_repo = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_HF_REPO"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_HF_REPO"));
|
||||
add_opt(common_arg(
|
||||
{"-hff", "--hf-file"}, "FILE",
|
||||
"Hugging Face model file. If specified, it will override the quant in --hf-repo (default: unused)",
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.model.hf_file = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_HF_FILE"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_HF_FILE"));
|
||||
add_opt(common_arg(
|
||||
{"-hfv", "-hfrv", "--hf-repo-v"}, "<user>/<model>[:quant]",
|
||||
"Hugging Face model repository for the vocoder model (default: unused)",
|
||||
@@ -2807,7 +2805,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.hf_token = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("HF_TOKEN"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("HF_TOKEN"));
|
||||
add_opt(common_arg(
|
||||
{"--mtp"},
|
||||
"also download the multi-token prediction (MTP) head, if available (default: unused)",
|
||||
@@ -2923,41 +2921,6 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.parse_special = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_IMATRIX}));
|
||||
add_opt(common_arg(
|
||||
{"--ids"},
|
||||
string_format("only print the token IDs, in a Python-parseable list form like [1, 2, 3] (default: %s)", params.tokenize_ids ? "true" : "false"),
|
||||
[](common_params & params) {
|
||||
params.tokenize_ids = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TOKENIZE}));
|
||||
add_opt(common_arg(
|
||||
{"--stdin"},
|
||||
string_format("read the prompt from stdin (takes precedence over -f/--file and -p/--prompt) (default: %s)", params.tokenize_stdin ? "true" : "false"),
|
||||
[](common_params & params) {
|
||||
params.tokenize_stdin = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TOKENIZE}));
|
||||
add_opt(common_arg(
|
||||
{"--no-bos"},
|
||||
string_format("do not add a BOS token to the prompt, even if the model normally uses one (default: %s)", params.tokenize_no_bos ? "true" : "false"),
|
||||
[](common_params & params) {
|
||||
params.tokenize_no_bos = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TOKENIZE}));
|
||||
add_opt(common_arg(
|
||||
{"--no-parse-special"},
|
||||
string_format("do not parse special tokens (chat, tool, etc) (default: %s)", !params.parse_special ? "true" : "false"),
|
||||
[](common_params & params) {
|
||||
params.parse_special = false;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TOKENIZE}));
|
||||
add_opt(common_arg(
|
||||
{"--show-count"},
|
||||
string_format("print the total number of tokens (default: %s)", params.tokenize_show_count ? "true" : "false"),
|
||||
[](common_params & params) {
|
||||
params.tokenize_show_count = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TOKENIZE}));
|
||||
add_opt(common_arg(
|
||||
{"-pps"},
|
||||
string_format("is the prompt shared across parallel sequences (default: %s)", params.is_pp_shared ? "true" : "false"),
|
||||
@@ -3587,7 +3550,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
[](common_params & params) {
|
||||
params.offline = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD, LLAMA_EXAMPLE_TOKENIZE}).set_env("LLAMA_ARG_OFFLINE"));
|
||||
).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_DOWNLOAD}).set_env("LLAMA_ARG_OFFLINE"));
|
||||
add_opt(common_arg(
|
||||
{"-lv", "--verbosity", "--log-verbosity"}, "N",
|
||||
string_format("Set the verbosity threshold. Messages with a higher verbosity will be ignored. Values:\n"
|
||||
|
||||
@@ -105,7 +105,6 @@ enum llama_example {
|
||||
LLAMA_EXAMPLE_RESULTS,
|
||||
LLAMA_EXAMPLE_EXPORT_GRAPH_OPS,
|
||||
LLAMA_EXAMPLE_DOWNLOAD,
|
||||
LLAMA_EXAMPLE_TOKENIZE,
|
||||
|
||||
LLAMA_EXAMPLE_COUNT,
|
||||
};
|
||||
@@ -725,12 +724,6 @@ struct common_params {
|
||||
// batched-bench params
|
||||
bool batched_bench_output_jsonl = false;
|
||||
|
||||
// tokenize params
|
||||
bool tokenize_ids = false; // if true, only print the token IDs
|
||||
bool tokenize_stdin = false; // if true, read the prompt from stdin
|
||||
bool tokenize_no_bos = false; // if true, do not add the BOS token
|
||||
bool tokenize_show_count = false; // if true, print the total token count
|
||||
|
||||
// common params
|
||||
std::string out_file; // output filename for all example programs
|
||||
// optional callback for model loading progress and cancellation:
|
||||
|
||||
@@ -260,10 +260,7 @@ struct common_speculative_impl_draft_simple : public common_speculative_impl {
|
||||
bool process(const llama_batch & batch) override {
|
||||
auto * ctx_dft = params.ctx_dft;
|
||||
|
||||
llama_batch batch_dft = batch;
|
||||
batch_dft.logits = nullptr;
|
||||
|
||||
const int ret = llama_decode(ctx_dft, batch_dft);
|
||||
const int ret = llama_decode(ctx_dft, batch);
|
||||
|
||||
if (ret != 0) {
|
||||
SPC_ERR("failed to decode draft batch, ret = %d\n", ret);
|
||||
|
||||
+1
-3
@@ -109,9 +109,7 @@ class ModelBase:
|
||||
sentence_transformers_dense_modules: bool = False
|
||||
|
||||
# MTP (multi-token prediction) export modes; set by main() before instantiation.
|
||||
# Architectures that implement the filtering/export behavior opt in by
|
||||
# setting supports_mtp_export = True on their model class or a mixin.
|
||||
supports_mtp_export: bool = False
|
||||
# Architectures opt in by overriding the handling (see _Qwen35MtpMixin).
|
||||
mtp_only: bool = False
|
||||
no_mtp: bool = False
|
||||
|
||||
|
||||
@@ -361,7 +361,6 @@ class HunyuanVLTextModel(HunYuanModel):
|
||||
@ModelBase.register("HYV3ForCausalLM")
|
||||
class HYV3Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.HY_V3
|
||||
supports_mtp_export = True
|
||||
|
||||
# Trunk layer count, stashed before indexing so the classmethod
|
||||
# filter_tensors can identify the appended MTP block(s) (mirrors
|
||||
|
||||
@@ -541,7 +541,6 @@ class _Qwen35MtpMixin:
|
||||
`mtp.*` to the standard layer-indexed nextn naming so the existing
|
||||
tensor_map handles them."""
|
||||
|
||||
supports_mtp_export = True
|
||||
hparams: dict[str, Any]
|
||||
model_arch: gguf.MODEL_ARCH
|
||||
gguf_writer: gguf.GGUFWriter
|
||||
|
||||
@@ -98,7 +98,6 @@ class Step3VLTextModel(Qwen3Model):
|
||||
@ModelBase.register("Step3p5ForCausalLM", "Step3p7ForConditionalGeneration")
|
||||
class Step35Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.STEP35
|
||||
supports_mtp_export = True
|
||||
|
||||
# The --mtp / --no-mtp toggles are ModelBase.mtp_only / no_mtp (set in
|
||||
# convert_hf_to_gguf.py main()). Unlike Qwen3.5, which stores MTP under a
|
||||
|
||||
@@ -259,8 +259,10 @@ def main() -> None:
|
||||
sys.exit(1)
|
||||
|
||||
if args.mtp or args.no_mtp:
|
||||
if not model_class.supports_mtp_export:
|
||||
logger.error("--mtp / --no-mtp are not supported for %s", model_architecture)
|
||||
from conversion.qwen import _Qwen35MtpMixin
|
||||
from conversion.step3 import Step35Model
|
||||
if not (issubclass(model_class, _Qwen35MtpMixin) or issubclass(model_class, Step35Model)):
|
||||
logger.error("--mtp / --no-mtp are only supported for Qwen3.5/3.6 and Step3.5 text variants today")
|
||||
sys.exit(1)
|
||||
if args.no_mtp:
|
||||
model_class.no_mtp = True
|
||||
|
||||
+1
-1
@@ -120,4 +120,4 @@ Legend:
|
||||
| TRI | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| TRUNC | ❌ | ❌ | ✅ | 🟡 | 🟡 | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| UPSCALE | ❌ | 🟡 | ✅ | ✅ | ❌ | ✅ | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| XIELU | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| XIELU | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
|
||||
+4
-4
@@ -11600,10 +11600,10 @@ zjy 2
|
||||
"SYCL0","CUMSUM","type=f32,ne=[242004,1,1,1]","support","1","yes","SYCL"
|
||||
"SYCL0","CUMSUM","type=f32,ne=[375960,1,1,1]","support","1","yes","SYCL"
|
||||
"SYCL0","CUMSUM","type=f32,ne=[20481,4,1,1]","support","1","yes","SYCL"
|
||||
"SYCL0","XIELU","type=f32,ne=[10,5,4,3]","support","1","yes","SYCL"
|
||||
"SYCL0","XIELU","type=f16,ne=[10,5,4,3]","support","1","yes","SYCL"
|
||||
"SYCL0","XIELU","type=f32,ne=[512,16,1,1]","support","1","yes","SYCL"
|
||||
"SYCL0","XIELU","type=f16,ne=[512,16,1,1]","support","1","yes","SYCL"
|
||||
"SYCL0","XIELU","type=f32,ne=[10,5,4,3]","support","0","no","SYCL"
|
||||
"SYCL0","XIELU","type=f16,ne=[10,5,4,3]","support","0","no","SYCL"
|
||||
"SYCL0","XIELU","type=f32,ne=[512,16,1,1]","support","0","no","SYCL"
|
||||
"SYCL0","XIELU","type=f16,ne=[512,16,1,1]","support","0","no","SYCL"
|
||||
"SYCL0","TRI","type=f32,ne=[10,10,4,3],tri_type=3","support","1","yes","SYCL"
|
||||
"SYCL0","TRI","type=f32,ne=[10,10,4,3],tri_type=2","support","1","yes","SYCL"
|
||||
"SYCL0","TRI","type=f32,ne=[10,10,4,3],tri_type=1","support","1","yes","SYCL"
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -780,10 +780,6 @@ extern "C" {
|
||||
GGML_API bool ggml_is_contiguous_1(const struct ggml_tensor * tensor); // contiguous for dims >= 1
|
||||
GGML_API bool ggml_is_contiguous_2(const struct ggml_tensor * tensor); // contiguous for dims >= 2
|
||||
|
||||
GGML_API bool ggml_is_contiguous_to_1(const struct ggml_tensor * tensor); // contiguous for dims < 1
|
||||
GGML_API bool ggml_is_contiguous_to_2(const struct ggml_tensor * tensor); // contiguous for dims < 2
|
||||
GGML_API bool ggml_is_contiguous_to_3(const struct ggml_tensor * tensor); // contiguous for dims < 3
|
||||
|
||||
// returns whether the tensor elements are allocated as one contiguous block of memory (no gaps, but permutation ok)
|
||||
GGML_API bool ggml_is_contiguously_allocated(const struct ggml_tensor * tensor);
|
||||
|
||||
|
||||
@@ -638,7 +638,6 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_fp32_bf16p_bf16p/
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f16p_qsi4c32p/
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/)
|
||||
|
||||
set(ARCH_FLAGS_TEMP "${ARCH_FLAGS}")
|
||||
@@ -688,15 +687,9 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_fp32_bf16p_bf16p/kai_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa_asm.S
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f16p_qsi4c32p/kai_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f16p_qsi4c32p/kai_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa_asm.S
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa_asm.S
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_pack_bf16p2vlx2_f32_sme.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_kxn_bf16p2vlx2b_f32_x32_sme.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_pack_f16pmrx2_f32_neon.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_pack_f32p2vlx1_f32_sme.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_pack_f32p2vlx1_f32_sme_asm.S
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.c
|
||||
${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme_asm.S
|
||||
${KLEIDIAI_SRC}/kai/kai_common_sme_asm.S)
|
||||
set(PRIVATE_ARCH_FLAGS "-fno-tree-vectorize;${PRIVATE_ARCH_FLAGS}+sve+sve2+sme2+fp16")
|
||||
endif()
|
||||
|
||||
@@ -2859,14 +2859,7 @@ struct ggml_cplan ggml_graph_plan(
|
||||
} break;
|
||||
case GGML_OP_OUT_PROD:
|
||||
{
|
||||
if (ggml_is_quantized(node->src[0]->type) ||
|
||||
node->src[0]->type == GGML_TYPE_F16) {
|
||||
cur = ggml_type_size(GGML_TYPE_F32) * node->src[0]->ne[0] * n_tasks;
|
||||
}
|
||||
} break;
|
||||
case GGML_OP_SET_ROWS:
|
||||
{
|
||||
if (node->src[0]->type == GGML_TYPE_F16 && node->type != GGML_TYPE_F16) {
|
||||
if (ggml_is_quantized(node->src[0]->type)) {
|
||||
cur = ggml_type_size(GGML_TYPE_F32) * node->src[0]->ne[0] * n_tasks;
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -462,12 +462,11 @@ static bool ggml_backend_cpu_device_supports_op(ggml_backend_dev_t dev, const st
|
||||
return max_bias == 0.0f;
|
||||
}
|
||||
case GGML_OP_IM2COL_BACK:
|
||||
return src0->type == GGML_TYPE_F32 && (src1->type == GGML_TYPE_F32 || src1->type == GGML_TYPE_F16);
|
||||
return src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F32;
|
||||
case GGML_OP_GET_ROWS_BACK:
|
||||
return src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16;
|
||||
case GGML_OP_OUT_PROD:
|
||||
return (src0->type == GGML_TYPE_F32 ||
|
||||
((src0->type == GGML_TYPE_F16 || ggml_is_quantized(src0->type)) && src0->ne[2] == src1->ne[2] && src0->ne[3] == src1->ne[3])) &&
|
||||
return (src0->type == GGML_TYPE_F32 || (ggml_is_quantized(src0->type) && src0->ne[2] == src1->ne[2] && src0->ne[3] == src1->ne[3])) &&
|
||||
src1->type == GGML_TYPE_F32 && op->type == GGML_TYPE_F32;
|
||||
default:
|
||||
return true;
|
||||
|
||||
@@ -20,17 +20,14 @@
|
||||
#include "kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p8x8_16x8_sve_i8mm.h"
|
||||
#include "kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p8x8_1x8_sve_dotprod.h"
|
||||
#include "kai_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa.h"
|
||||
#include "kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.h"
|
||||
|
||||
#include "kai_lhs_pack_bf16p2vlx2_f32_sme.h"
|
||||
#include "kai_lhs_pack_f32p2vlx1_f32_sme.h"
|
||||
#include "kai_lhs_quant_pack_qsi8d32p_f32.h"
|
||||
#include "kai_lhs_quant_pack_qsi8d32p4x8sb_f32_neon.h"
|
||||
#include "kai_lhs_quant_pack_qsi8d32p_f32_neon.h"
|
||||
#include "kai_lhs_quant_pack_qai8dxp_f32.h"
|
||||
|
||||
#include "kai_rhs_pack_kxn_bf16p2vlx2b_f32_x32_sme.h"
|
||||
#include "kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.h"
|
||||
#include "kai_rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0.h"
|
||||
#include "kai_rhs_pack_nxk_qsi4c32ps1s0scalef16_qsu4c32s16s0_neon.h"
|
||||
#include "kai_rhs_pack_nxk_qsi8cxp_qsi8cx_neon.h"
|
||||
@@ -868,65 +865,6 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
|
||||
static ggml_kleidiai_kernels ggml_kleidiai_kernels_f32[] = {
|
||||
#if defined(__ARM_FEATURE_SME)
|
||||
{
|
||||
/* SME GEMM */
|
||||
{
|
||||
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_mr = */ kai_get_mr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_nr = */ kai_get_nr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_kr = */ kai_get_kr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_sr = */ kai_get_sr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_dst_offset = */ kai_get_dst_offset_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_dst_size = */ kai_get_dst_size_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_lhs_offset_ex = */ &kernel_offs_fn2<kai_get_lhs_packed_offset_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa>,
|
||||
/* .get_rhs_packed_offset_ex = */ &kernel_offs_fn2<kai_get_rhs_packed_offset_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa>,
|
||||
/* .run_kernel_ex = */ &kernel_run_fn10<kai_run_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa>,
|
||||
},
|
||||
/* .gemm_lhs_info = */ {
|
||||
/* .get_offset = */ kai_get_lhs_offset_lhs_pack_f32p2vlx1_f32_sme,
|
||||
/* .get_packed_offset_ex = */ &lhs_offs_fn5<kai_get_lhs_packed_offset_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
/* .packed_size_ex = */ &lhs_ps_fn5<kai_get_lhs_packed_size_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
/* .pack_func_ex = */ &lhs_pack_void_fn9<kai_run_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
},
|
||||
/* SME GEMV */
|
||||
{
|
||||
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_mr = */ kai_get_mr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_nr = */ kai_get_nr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_kr = */ kai_get_kr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_sr = */ kai_get_sr_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_dst_offset = */ kai_get_dst_offset_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_dst_size = */ kai_get_dst_size_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa,
|
||||
/* .get_lhs_offset_ex = */ nullptr,
|
||||
/* .get_rhs_packed_offset_ex = */ nullptr,
|
||||
/* .run_kernel_ex = */ nullptr,
|
||||
},
|
||||
/* .gemv_lhs_info = */ {
|
||||
/* .get_offset = */ kai_get_lhs_offset_lhs_pack_f32p2vlx1_f32_sme,
|
||||
/* .get_packed_offset_ex = */ &lhs_offs_fn5<kai_get_lhs_packed_offset_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
/* .packed_size_ex = */ &lhs_ps_fn5<kai_get_lhs_packed_size_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
/* .pack_func_ex = */ &lhs_pack_void_fn9<kai_run_lhs_pack_f32p2vlx1_f32_sme>,
|
||||
},
|
||||
/* .rhs_info = */ {
|
||||
/* .packed_stride = */ nullptr,
|
||||
/* .to_float = */ nullptr,
|
||||
/* .packed_size_ex = */ &rhs_ps_fn2<kai_get_rhs_packed_size_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme>,
|
||||
/* .packed_stride_ex = */ &rhs_stride_fn1<kai_get_rhs_packed_stride_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme>,
|
||||
/* .pack_func_ex = */ &rhs_pack_fn13<kai_run_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme>,
|
||||
},
|
||||
/* .required_cpu = */ CPU_FEATURE_SME,
|
||||
/* .lhs_type = */ GGML_TYPE_F32,
|
||||
/* .rhs_type = */ GGML_TYPE_F32,
|
||||
/* .op_type = */ GGML_TYPE_F32,
|
||||
},
|
||||
#endif
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, const ggml_tensor * tensor) {
|
||||
ggml_kleidiai_kernels * kernel = nullptr;
|
||||
|
||||
@@ -950,15 +888,12 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, c
|
||||
|
||||
if (tensor->src[0]->type == GGML_TYPE_Q8_0) {
|
||||
try_table(gemm_gemv_kernels_q8);
|
||||
} else if (tensor->src[0]->type == GGML_TYPE_F32) {
|
||||
try_table(ggml_kleidiai_kernels_f32);
|
||||
} else {
|
||||
try_table(gemm_gemv_kernels);
|
||||
}
|
||||
#else
|
||||
GGML_UNUSED(gemm_gemv_kernels);
|
||||
GGML_UNUSED(gemm_gemv_kernels_q8);
|
||||
GGML_UNUSED(ggml_kleidiai_kernels_f32);
|
||||
GGML_UNUSED(cpu_features);
|
||||
#endif
|
||||
}
|
||||
@@ -1002,20 +937,3 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_q8_0(cpu_feature features)
|
||||
|
||||
return kernels;
|
||||
}
|
||||
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_f32(cpu_feature features) {
|
||||
ggml_kleidiai_kernels * kernels = nullptr;
|
||||
|
||||
#if defined(__ARM_FEATURE_SME)
|
||||
for (size_t i = 0; i < NELEMS(ggml_kleidiai_kernels_f32) - 1; ++i) {
|
||||
if ((features & ggml_kleidiai_kernels_f32[i].required_cpu) == ggml_kleidiai_kernels_f32[i].required_cpu) {
|
||||
kernels = &ggml_kleidiai_kernels_f32[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
GGML_UNUSED(features);
|
||||
#endif
|
||||
|
||||
return kernels;
|
||||
}
|
||||
|
||||
@@ -55,12 +55,6 @@ struct lhs_packing_info {
|
||||
size_t m_idx_start, const void * lhs, size_t lhs_stride, void * lhs_packed);
|
||||
};
|
||||
|
||||
enum rhs_repack_mode {
|
||||
RHS_REPACK_PER_KERNEL,
|
||||
RHS_REPACK_SHARED,
|
||||
RHS_REPACK_SINGLE_ONLY,
|
||||
};
|
||||
|
||||
struct rhs_packing_info {
|
||||
size_t (*packed_stride)(size_t k, size_t nr, size_t kr, size_t bl);
|
||||
|
||||
@@ -74,8 +68,6 @@ struct rhs_packing_info {
|
||||
|
||||
void (*pack_func_ex)(size_t num_groups, size_t n, size_t k, size_t nr, size_t kr, size_t sr, size_t bl,
|
||||
size_t rhs_stride, const void * rhs, const void * bias, const void * scale, void * rhs_packed, size_t extra_bytes, const void * params);
|
||||
|
||||
rhs_repack_mode repack_mode = RHS_REPACK_PER_KERNEL;
|
||||
};
|
||||
|
||||
struct ggml_kleidiai_kernels {
|
||||
@@ -96,4 +88,3 @@ struct ggml_kleidiai_kernels {
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, const ggml_tensor * tensor);
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_q4_0(cpu_feature features);
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_q8_0(cpu_feature features);
|
||||
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_f32(cpu_feature features);
|
||||
|
||||
@@ -60,11 +60,10 @@ struct ggml_kleidiai_context {
|
||||
cpu_feature features;
|
||||
ggml_kleidiai_kernels * kernels_q4;
|
||||
ggml_kleidiai_kernels * kernels_q8;
|
||||
ggml_kleidiai_kernels * kernels_f32;
|
||||
int sme_thread_cap; // <= 0 means “SME disabled/unknown”;
|
||||
int thread_hint; // <= 0 means “no hint”
|
||||
int chunk_multiplier;
|
||||
} static ctx = { CPU_FEATURE_NONE, nullptr, nullptr, nullptr, 0, -1, 4 };
|
||||
} static ctx = { CPU_FEATURE_NONE, nullptr, nullptr, 0, -1, 4 };
|
||||
|
||||
static const char* cpu_feature_to_string(cpu_feature f) {
|
||||
if (f == CPU_FEATURE_NONE) {
|
||||
@@ -157,10 +156,10 @@ static size_t detect_num_smcus() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
#else
|
||||
return 0;
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -193,6 +192,7 @@ static void init_kleidiai_context(void) {
|
||||
const char *env_threads = getenv("GGML_TOTAL_THREADS");
|
||||
const char *env_chunk_mult = getenv("GGML_KLEIDIAI_CHUNK_MULTIPLIER");
|
||||
|
||||
const bool cpu_has_sme = ggml_cpu_has_sme();
|
||||
size_t detected_smcus = 0;
|
||||
|
||||
ctx.features = (ggml_cpu_has_dotprod() ? CPU_FEATURE_DOTPROD : CPU_FEATURE_NONE) |
|
||||
@@ -216,47 +216,56 @@ static void init_kleidiai_context(void) {
|
||||
}
|
||||
|
||||
// SME policy:
|
||||
// - env unset => auto-detect SMCUs; enable SME only if detected > 0.
|
||||
// - env=0 => force off.
|
||||
// - env>0 => force N cores, if the binary was built with SME.
|
||||
// - If CPU doesn't support SME: SME always off.
|
||||
// - Else:
|
||||
// - env unset => auto-detect cores; enable if detected > 0.
|
||||
// - env=0 => force off.
|
||||
// - env>0 => force N cores (skip detection).
|
||||
int sme_cores = 0;
|
||||
bool sme_env_ok = false;
|
||||
bool sme_env_set = (env_sme != nullptr);
|
||||
|
||||
if (sme_env_set) {
|
||||
bool ok = false;
|
||||
int v = parse_uint_env(env_sme, "GGML_KLEIDIAI_SME", &ok);
|
||||
sme_env_ok = ok;
|
||||
if (!cpu_has_sme) {
|
||||
if (sme_env_set) {
|
||||
bool ok = false;
|
||||
int req = parse_uint_env(env_sme, "GGML_KLEIDIAI_SME", &ok);
|
||||
if (ok && req > 0) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME=%d but SME is not supported on this CPU; disabling SME\n", req);
|
||||
}
|
||||
}
|
||||
sme_cores = 0;
|
||||
} else {
|
||||
if (sme_env_set) {
|
||||
bool ok = false;
|
||||
int v = parse_uint_env(env_sme, "GGML_KLEIDIAI_SME", &ok);
|
||||
sme_env_ok = ok;
|
||||
|
||||
if (!ok) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME set but parsing failed; falling back to runtime SME-core detection\n");
|
||||
if (!ok) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME set but parsing failed; falling back to runtime SME-core detection\n");
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cores = detected_smcus > 0 ? (int)detected_smcus : 0;
|
||||
} else if (v == 0) {
|
||||
sme_cores = 0;
|
||||
} else {
|
||||
sme_cores = v;
|
||||
}
|
||||
} else {
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cores = detected_smcus > 0 ? (int)detected_smcus : 0;
|
||||
} else if (v == 0) {
|
||||
sme_cores = 0;
|
||||
} else if (!ggml_cpu_has_sme()) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME=%d but the binary was not built with SME; disabling SME\n", v);
|
||||
sme_cores = 0;
|
||||
} else {
|
||||
sme_cores = v;
|
||||
}
|
||||
} else {
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cores = detected_smcus > 0 ? (int)detected_smcus : 0;
|
||||
}
|
||||
|
||||
if (!sme_env_set && ggml_cpu_has_sme() && sme_cores == 0) {
|
||||
GGML_LOG_WARN("kleidiai: runtime SME-core detection returned 0; falling back to NEON\n");
|
||||
}
|
||||
if (!sme_env_set && sme_cores == 0) {
|
||||
GGML_LOG_WARN("kleidiai: SME supported but runtime SME-core detection returned 0; falling back to NEON\n");
|
||||
}
|
||||
|
||||
if (sme_cores > 0) {
|
||||
ctx.features |= CPU_FEATURE_SME;
|
||||
if (sme_cores > 0) {
|
||||
ctx.features |= CPU_FEATURE_SME;
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel selection
|
||||
ctx.kernels_q4 = ggml_kleidiai_select_kernels_q4_0(ctx.features);
|
||||
ctx.kernels_q8 = ggml_kleidiai_select_kernels_q8_0(ctx.features);
|
||||
ctx.kernels_f32 = ggml_kleidiai_select_kernels_f32(ctx.features);
|
||||
ctx.kernels_q4 = ggml_kleidiai_select_kernels_q4_0(ctx.features);
|
||||
ctx.kernels_q8 = ggml_kleidiai_select_kernels_q8_0(ctx.features);
|
||||
|
||||
if (!ctx.kernels_q4) {
|
||||
GGML_LOG_INFO("kleidiai: no compatible q4 kernels found for CPU features mask %d\n", (int)ctx.features);
|
||||
@@ -270,12 +279,6 @@ static void init_kleidiai_context(void) {
|
||||
GGML_LOG_INFO("kleidiai: primary q8 kernel feature %s\n", cpu_feature_to_string(ctx.kernels_q8->required_cpu));
|
||||
}
|
||||
|
||||
if (!ctx.kernels_f32) {
|
||||
GGML_LOG_INFO("kleidiai: no compatible f32 kernels found for CPU features mask %d\n", (int)ctx.features);
|
||||
} else {
|
||||
GGML_LOG_INFO("kleidiai: primary f32 kernel feature %s\n", cpu_feature_to_string(ctx.kernels_f32->required_cpu));
|
||||
}
|
||||
|
||||
ctx.sme_thread_cap = (ctx.features & CPU_FEATURE_SME) ? sme_cores : 0;
|
||||
|
||||
if (ctx.features & CPU_FEATURE_SME) {
|
||||
@@ -331,13 +334,6 @@ static inline size_t ceil_div_size(size_t a, size_t b) {
|
||||
return b == 0 ? 0 : (a + b - 1) / b;
|
||||
}
|
||||
|
||||
static inline size_t kleidiai_chunk_cols(size_t n, int nth_total, bool disable_chunking, size_t n_step) {
|
||||
const size_t multiplier = (nth_total == 1 || disable_chunking) ? 1 : std::max<size_t>(1, (size_t) ctx.chunk_multiplier);
|
||||
const size_t divisor = std::max<size_t>(1, (size_t) nth_total * multiplier);
|
||||
const size_t chunk_cols = align_up(std::max<size_t>(1, ceil_div_size(n, divisor)), n_step);
|
||||
return chunk_cols ? chunk_cols : n_step;
|
||||
}
|
||||
|
||||
struct kleidiai_block_args {
|
||||
size_t lhs_bl;
|
||||
size_t rhs_bl;
|
||||
@@ -422,10 +418,6 @@ static inline ggml_kleidiai_kernels * kleidiai_primary_kernel_q8() {
|
||||
return ctx.kernels_q8;
|
||||
}
|
||||
|
||||
static inline ggml_kleidiai_kernels * kleidiai_primary_kernel_f32() {
|
||||
return ctx.kernels_f32;
|
||||
}
|
||||
|
||||
template <typename SelectFallback>
|
||||
static int kleidiai_collect_kernel_chain_common(
|
||||
ggml_kleidiai_kernels * primary,
|
||||
@@ -438,16 +430,11 @@ static int kleidiai_collect_kernel_chain_common(
|
||||
}
|
||||
out[count++] = primary;
|
||||
|
||||
if (primary->rhs_info.repack_mode == RHS_REPACK_SINGLE_ONLY) {
|
||||
return count;
|
||||
}
|
||||
|
||||
if ((primary->required_cpu & CPU_FEATURE_SME) == CPU_FEATURE_SME) {
|
||||
const cpu_feature fallback_mask = static_cast<cpu_feature>(features & ~CPU_FEATURE_SME);
|
||||
if (fallback_mask != CPU_FEATURE_NONE) {
|
||||
ggml_kleidiai_kernels * fallback = select_fallback(fallback_mask);
|
||||
if (fallback && fallback != primary &&
|
||||
fallback->rhs_info.repack_mode != RHS_REPACK_SINGLE_ONLY &&
|
||||
fallback->lhs_type == primary->lhs_type &&
|
||||
fallback->rhs_type == primary->rhs_type &&
|
||||
fallback->op_type == primary->op_type) {
|
||||
@@ -478,12 +465,6 @@ static int kleidiai_collect_q8_chain(std::array<ggml_kleidiai_kernels *, GGML_KL
|
||||
[&](cpu_feature mask) { return ggml_kleidiai_select_kernels_q8_0(mask); });
|
||||
}
|
||||
|
||||
static int kleidiai_collect_f32_chain(std::array<ggml_kleidiai_kernels *, GGML_KLEIDIAI_MAX_KERNEL_SLOTS> & out) {
|
||||
ggml_kleidiai_kernels * primary = kleidiai_primary_kernel_f32();
|
||||
return kleidiai_collect_kernel_chain_common(primary, ctx.features, out,
|
||||
[&](cpu_feature mask) { return ggml_kleidiai_select_kernels_f32(mask); });
|
||||
}
|
||||
|
||||
static inline int64_t ggml_ne(const ggml_tensor * tensor, int dim) {
|
||||
GGML_ASSERT(dim >= 0 && dim < GGML_MAX_DIMS);
|
||||
return tensor->ne[dim];
|
||||
@@ -558,36 +539,6 @@ class tensor_traits : public ggml::cpu::tensor_traits {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op->src[0]->type == GGML_TYPE_F32) {
|
||||
size_t cursor = 0;
|
||||
bool any_slot = false;
|
||||
|
||||
for (int slot = 0; slot < slot_count; ++slot) {
|
||||
ggml_kleidiai_kernels * kernels = kernel_chain[slot];
|
||||
lhs_packing_info * lhs_info = &kernels->gemm_lhs_info;
|
||||
kernel_info * kernel = &kernels->gemm;
|
||||
|
||||
if (!lhs_info || !lhs_info->packed_size_ex || !kernel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t mr = kernel->get_mr();
|
||||
const size_t kr = kernel->get_kr();
|
||||
const size_t sr = kernel->get_sr();
|
||||
|
||||
cursor = align_up(cursor, GGML_KLEIDIAI_PACK_ALIGN);
|
||||
cursor += lhs_info->packed_size_ex(m, k, 0, mr, kr, sr);
|
||||
any_slot = true;
|
||||
}
|
||||
|
||||
if (!any_slot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size = cursor;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op->src[0]->type == GGML_TYPE_F16) {
|
||||
const int64_t lhs_batch_size0 = op->src[1]->ne[2];
|
||||
const int64_t rhs_batch_size0 = op->src[0]->ne[2];
|
||||
@@ -644,8 +595,6 @@ class tensor_traits : public ggml::cpu::tensor_traits {
|
||||
if (dst->op == GGML_OP_MUL_MAT) {
|
||||
if (dst->src[0]->type == GGML_TYPE_Q4_0 || dst->src[0]->type == GGML_TYPE_Q8_0) {
|
||||
return compute_forward_qx(params, dst);
|
||||
} else if (dst->src[0]->type == GGML_TYPE_F32) {
|
||||
return compute_forward_f32(params, dst);
|
||||
} else if (dst->src[0]->type == GGML_TYPE_F16) {
|
||||
return compute_forward_fp16(params, dst);
|
||||
}
|
||||
@@ -657,144 +606,6 @@ class tensor_traits : public ggml::cpu::tensor_traits {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compute_forward_f32(ggml_compute_params * params, struct ggml_tensor * dst) {
|
||||
GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32);
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ggml_kleidiai_kernels * kernels = kleidiai_primary_kernel_f32();
|
||||
if (!kernels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kernel_info * kernel = &kernels->gemm;
|
||||
lhs_packing_info * lhs_info = &kernels->gemm_lhs_info;
|
||||
|
||||
if (!kernel || !lhs_info || !lhs_info->get_offset || !lhs_info->get_packed_offset_ex ||
|
||||
!lhs_info->packed_size_ex || !lhs_info->pack_func_ex ||
|
||||
!kernel->get_rhs_packed_offset_ex || !kernel->run_kernel_ex || !kernel->get_dst_offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const kleidiai_weight_header * header = kleidiai_weight_header_from_ptr(src0->data);
|
||||
const bool has_header = kleidiai_is_weight_header_valid(header);
|
||||
|
||||
const uint8_t * rhs_base = has_header ? kleidiai_weight_slot_ptr(header, 0)
|
||||
: static_cast<const uint8_t *>(src0->data);
|
||||
if (!rhs_base) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int nth = params->nth > 0 ? params->nth : 1;
|
||||
const int ith = params->ith;
|
||||
|
||||
const size_t k = ne00;
|
||||
const size_t m = ne11;
|
||||
const size_t n = ne01;
|
||||
|
||||
const size_t mr = kernel->get_mr();
|
||||
const size_t kr = kernel->get_kr();
|
||||
const size_t sr = kernel->get_sr();
|
||||
|
||||
const size_t lhs_packed_size = lhs_info->packed_size_ex(m, k, 0, mr, kr, sr);
|
||||
GGML_ASSERT(lhs_packed_size <= params->wsize);
|
||||
|
||||
uint8_t * lhs_packed = static_cast<uint8_t *>(params->wdata);
|
||||
const size_t dst_stride = dst->nb[1];
|
||||
const size_t n_step = kernel->get_n_step() ? kernel->get_n_step() : 1;
|
||||
const bool disable_chunking = ggml_is_numa();
|
||||
GGML_ASSERT(n <= (size_t) INT_MAX);
|
||||
|
||||
for (int64_t batch_idx = 0; batch_idx < ne12; ++batch_idx) {
|
||||
const uint8_t * lhs_batch_base = static_cast<const uint8_t *>(src1->data) + batch_idx * src1->nb[2];
|
||||
uint8_t * dst_batch_base = static_cast<uint8_t *>(dst->data) + batch_idx * dst->nb[2];
|
||||
|
||||
{
|
||||
const int64_t m_roundup_mr = kai_roundup((int64_t)m, (int64_t)mr);
|
||||
int64_t max_threads = mr ? (m_roundup_mr / (int64_t)mr) : nth;
|
||||
max_threads = std::max<int64_t>(1, max_threads);
|
||||
const int64_t use_threads = std::min<int64_t>(nth, max_threads);
|
||||
|
||||
if (ith < use_threads) {
|
||||
const int64_t num_m_per_thread0 = round_down((size_t)(m_roundup_mr / use_threads), mr);
|
||||
const int64_t num_m_per_threadN_1 = (int64_t)m - (use_threads - 1) * num_m_per_thread0;
|
||||
|
||||
const int64_t m_start = (int64_t)ith * num_m_per_thread0;
|
||||
const int64_t m_count = (ith == use_threads - 1) ? num_m_per_threadN_1 : num_m_per_thread0;
|
||||
|
||||
const size_t base_packed_off = lhs_info->get_packed_offset_ex(m_start, k, 0, mr, kr, sr);
|
||||
const size_t next_block_off = lhs_info->get_packed_offset_ex(m_start + mr, k, 0, mr, kr, sr);
|
||||
const size_t row_stride_bytes = mr ? (next_block_off - base_packed_off) / mr : 0;
|
||||
|
||||
int64_t remaining = m_count;
|
||||
int64_t cur = m_start;
|
||||
|
||||
while (remaining > 0) {
|
||||
const int64_t take = std::min<int64_t>((int64_t)m - cur, remaining);
|
||||
const size_t src_off = lhs_info->get_offset(cur, src1->nb[1]);
|
||||
const void * src_ptr = lhs_batch_base + src_off;
|
||||
const size_t dst_off = base_packed_off + (size_t)(cur - m_start) * row_stride_bytes;
|
||||
void * dst_ptr = lhs_packed + dst_off;
|
||||
|
||||
lhs_info->pack_func_ex(take, k, 0, mr, kr, sr, 0, src_ptr, src1->nb[1], dst_ptr);
|
||||
|
||||
cur += take;
|
||||
remaining -= take;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ith == 0) {
|
||||
ggml_threadpool_chunk_set(params->threadpool, 0);
|
||||
}
|
||||
|
||||
ggml_barrier(params->threadpool);
|
||||
|
||||
const size_t chunk_cols = kleidiai_chunk_cols(n, nth, disable_chunking, n_step);
|
||||
GGML_ASSERT(chunk_cols <= (size_t) INT_MAX);
|
||||
|
||||
int current_col = ggml_threadpool_chunk_add(params->threadpool, (int) chunk_cols);
|
||||
while ((size_t) current_col < n) {
|
||||
const size_t n_start = (size_t) current_col;
|
||||
const size_t n_to_process = std::min(chunk_cols, n - n_start);
|
||||
|
||||
if (n_to_process > 0) {
|
||||
const size_t lhs_packed_offset = lhs_info->get_packed_offset_ex(0, k, 0, mr, kr, sr);
|
||||
const size_t rhs_packed_offset = kernel->get_rhs_packed_offset_ex(n_start, k, 0);
|
||||
const size_t dst_offset = kernel->get_dst_offset(0, n_start, dst_stride);
|
||||
|
||||
const void * lhs_ptr = lhs_packed + lhs_packed_offset;
|
||||
const void * rhs_ptr = rhs_base + rhs_packed_offset;
|
||||
float * dst_ptr = reinterpret_cast<float *>(dst_batch_base + dst_offset);
|
||||
|
||||
kernel->run_kernel_ex(m, n_to_process, k, 0,
|
||||
lhs_ptr,
|
||||
rhs_ptr,
|
||||
dst_ptr,
|
||||
dst_stride,
|
||||
sizeof(float),
|
||||
-FLT_MAX,
|
||||
FLT_MAX);
|
||||
}
|
||||
|
||||
current_col = ggml_threadpool_chunk_add(params->threadpool, (int) chunk_cols);
|
||||
}
|
||||
|
||||
if (batch_idx != ne12 - 1) {
|
||||
ggml_barrier(params->threadpool);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compute_forward_fp16(ggml_compute_params * params, struct ggml_tensor * dst) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
@@ -1403,7 +1214,7 @@ class tensor_traits : public ggml::cpu::tensor_traits {
|
||||
|
||||
public:
|
||||
int repack(struct ggml_tensor * tensor, const void * data, size_t data_size) {
|
||||
GGML_ASSERT(tensor->type == GGML_TYPE_Q4_0 || tensor->type == GGML_TYPE_Q8_0 || tensor->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(tensor->type == GGML_TYPE_Q4_0 || tensor->type == GGML_TYPE_Q8_0);
|
||||
const size_t n = tensor->ne[1];
|
||||
const size_t k = tensor->ne[0];
|
||||
|
||||
@@ -1422,15 +1233,12 @@ public:
|
||||
|
||||
std::array<ggml_kleidiai_kernels *, GGML_KLEIDIAI_MAX_KERNEL_SLOTS> kernel_chain;
|
||||
const bool want_q8 = tensor->type == GGML_TYPE_Q8_0;
|
||||
const bool want_f32 = tensor->type == GGML_TYPE_F32;
|
||||
const int slot_total = want_f32 ? kleidiai_collect_f32_chain(kernel_chain)
|
||||
: want_q8 ? kleidiai_collect_q8_chain(kernel_chain)
|
||||
: kleidiai_collect_q4_chain(kernel_chain);
|
||||
const int slot_total = want_q8 ? kleidiai_collect_q8_chain(kernel_chain)
|
||||
: kleidiai_collect_q4_chain(kernel_chain);
|
||||
const bool allow_fallback = kleidiai_pack_fallback_allowed();
|
||||
|
||||
std::vector<int8_t> qdata;
|
||||
std::vector<float> scales;
|
||||
std::vector<float> bias;
|
||||
|
||||
if (want_q8 && slot_total > 0) {
|
||||
qdata.resize(n * k, 0);
|
||||
@@ -1478,10 +1286,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (want_f32 && slot_total > 0) {
|
||||
bias.resize(n, 0.0f);
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < slot_total && slot < GGML_KLEIDIAI_MAX_KERNEL_SLOTS; ++slot) {
|
||||
if (!allow_fallback && slot > 0) {
|
||||
break;
|
||||
@@ -1498,9 +1302,8 @@ public:
|
||||
const size_t sr = kernel->get_sr();
|
||||
const ggml_type rhs_type = kernels->rhs_type;
|
||||
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_F32 ? 0 : SIZE_MAX;
|
||||
if (block_len == SIZE_MAX) {
|
||||
rhs_type == GGML_TYPE_Q4_0 ? QK4_0 : 0;
|
||||
if (block_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1523,10 +1326,6 @@ public:
|
||||
rhs_info->pack_func_ex(1, n, k, nr, kr, sr, 0, 0,
|
||||
qdata.data(), nullptr, scales.data(),
|
||||
dst_ptr, 0, ¶ms);
|
||||
} else if (rhs_type == GGML_TYPE_F32) {
|
||||
rhs_info->pack_func_ex(1, n, k, nr, kr, sr, 0, tensor->nb[1],
|
||||
data, bias.data(), nullptr,
|
||||
dst_ptr, 0, nullptr);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -1601,7 +1400,7 @@ static size_t ggml_backend_cpu_kleidiai_buffer_type_get_alignment(ggml_backend_b
|
||||
static size_t ggml_backend_cpu_kleidiai_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const struct ggml_tensor * tensor) {
|
||||
GGML_UNUSED(buft);
|
||||
|
||||
if (tensor->type != GGML_TYPE_Q4_0 && tensor->type != GGML_TYPE_Q8_0 && tensor->type != GGML_TYPE_F32) {
|
||||
if (tensor->type != GGML_TYPE_Q4_0 && tensor->type != GGML_TYPE_Q8_0) {
|
||||
return ggml_nbytes(tensor);
|
||||
}
|
||||
|
||||
@@ -1613,10 +1412,8 @@ static size_t ggml_backend_cpu_kleidiai_buffer_type_get_alloc_size(ggml_backend_
|
||||
|
||||
std::array<ggml_kleidiai_kernels *, GGML_KLEIDIAI_MAX_KERNEL_SLOTS> kernel_chain;
|
||||
const bool want_q8 = tensor->type == GGML_TYPE_Q8_0;
|
||||
const bool want_f32 = tensor->type == GGML_TYPE_F32;
|
||||
const int slot_total = want_f32 ? kleidiai_collect_f32_chain(kernel_chain)
|
||||
: want_q8 ? kleidiai_collect_q8_chain(kernel_chain)
|
||||
: kleidiai_collect_q4_chain(kernel_chain);
|
||||
const int slot_total = want_q8 ? kleidiai_collect_q8_chain(kernel_chain)
|
||||
: kleidiai_collect_q4_chain(kernel_chain);
|
||||
const bool allow_fallback = kleidiai_pack_fallback_allowed();
|
||||
|
||||
size_t slot_count = 0;
|
||||
@@ -1636,9 +1433,8 @@ static size_t ggml_backend_cpu_kleidiai_buffer_type_get_alloc_size(ggml_backend_
|
||||
|
||||
const ggml_type rhs_type = kernels->rhs_type;
|
||||
const size_t block_len = rhs_type == GGML_TYPE_Q4_0 ? QK4_0 :
|
||||
rhs_type == GGML_TYPE_Q8_0 ? QK8_0 :
|
||||
rhs_type == GGML_TYPE_F32 ? 0 : SIZE_MAX;
|
||||
if (block_len == SIZE_MAX) {
|
||||
rhs_type == GGML_TYPE_Q8_0 ? QK8_0 : 0;
|
||||
if (block_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1659,41 +1455,25 @@ class extra_buffer_type : ggml::cpu::extra_buffer_type {
|
||||
bool supports_op(ggml_backend_dev_t, const struct ggml_tensor * op) override {
|
||||
std::array<ggml_kleidiai_kernels *, GGML_KLEIDIAI_MAX_KERNEL_SLOTS> kernel_chain;
|
||||
const int slot_total = kleidiai_collect_kernel_chain(op, kernel_chain);
|
||||
const bool src0_is_kleidiai =
|
||||
if ((op->op == GGML_OP_MUL_MAT || op->op == GGML_OP_GET_ROWS) &&
|
||||
(op->src[0]->type == GGML_TYPE_Q4_0 || op->src[0]->type == GGML_TYPE_Q8_0) &&
|
||||
op->src[0]->buffer &&
|
||||
(ggml_n_dims(op->src[0]) == 2) &&
|
||||
op->src[0]->buffer->buft == ggml_backend_cpu_kleidiai_buffer_type() &&
|
||||
slot_total > 0;
|
||||
|
||||
if ((op->op == GGML_OP_MUL_MAT || op->op == GGML_OP_GET_ROWS) &&
|
||||
(op->src[0]->type == GGML_TYPE_Q4_0 || op->src[0]->type == GGML_TYPE_Q8_0 || op->src[0]->type == GGML_TYPE_F32) &&
|
||||
src0_is_kleidiai) {
|
||||
slot_total > 0) {
|
||||
if (op->src[0]->type == GGML_TYPE_Q4_0 && ctx.kernels_q4 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (op->src[0]->type == GGML_TYPE_Q8_0 && ctx.kernels_q8 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (op->src[0]->type == GGML_TYPE_F32 && ctx.kernels_f32 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (op->src[1]->buffer && !ggml_backend_buft_is_host(op->src[1]->buffer->buft)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->src[0]->type == GGML_TYPE_Q4_0 || op->src[0]->type == GGML_TYPE_Q8_0) {
|
||||
if ((op->src[1]->type == GGML_TYPE_F32 || op->src[1]->type == GGML_TYPE_I32) &&
|
||||
ggml_ne(op->src[1], 3) == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if ((op->src[1]->type == GGML_TYPE_F32 || op->src[1]->type == GGML_TYPE_I32) &&
|
||||
ggml_ne(op->src[1], 3) == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op->op != GGML_OP_MUL_MAT || op->src[1]->type != GGML_TYPE_F32 || op->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+20
-85
@@ -2081,8 +2081,8 @@ void ggml_compute_forward_concat(
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
if (ggml_is_quantized(src0->type)) {
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src1));
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(src1));
|
||||
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
|
||||
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
|
||||
}
|
||||
@@ -4449,70 +4449,6 @@ static void ggml_compute_forward_out_prod_q_f32(
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_out_prod_f16_f32(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS;
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F16);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
||||
|
||||
GGML_ASSERT(ne02 == ne12);
|
||||
GGML_ASSERT(ne03 == ne13);
|
||||
GGML_ASSERT(ne2 == ne12);
|
||||
GGML_ASSERT(ne3 == ne13);
|
||||
|
||||
GGML_ASSERT(nb00 == sizeof(ggml_fp16_t));
|
||||
GGML_ASSERT(nb0 == sizeof(float));
|
||||
|
||||
GGML_ASSERT(ne0 == ne00);
|
||||
GGML_ASSERT(ne1 == ne10);
|
||||
GGML_ASSERT(ne2 == ne02);
|
||||
GGML_ASSERT(ne3 == ne03);
|
||||
|
||||
if (ith == 0) {
|
||||
ggml_vec_set_f32(ne0*ne1*ne2*ne3, (float *)dst->data, 0);
|
||||
}
|
||||
ggml_barrier(params->threadpool);
|
||||
|
||||
const int64_t nr = ne1*ne2*ne3;
|
||||
const int64_t dr = (nr + nth - 1)/nth;
|
||||
const int64_t ir0 = dr*ith;
|
||||
const int64_t ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
float * wdata = (float *) params->wdata + (ne0 + CACHE_LINE_SIZE_F32) * ith;
|
||||
|
||||
for (int64_t ir = ir0; ir < ir1; ++ir) {
|
||||
const int64_t i3 = ir/(ne2*ne1);
|
||||
const int64_t i2 = (ir - i3*ne2*ne1)/ne1;
|
||||
const int64_t i1 = (ir - i3*ne2*ne1 - i2*ne1);
|
||||
|
||||
const int64_t i02 = i2;
|
||||
const int64_t i03 = i3;
|
||||
|
||||
const int64_t i12 = i2;
|
||||
const int64_t i13 = i3;
|
||||
|
||||
float * d = (float *) ((char *) dst->data + (i1*nb1 + i2*nb2 + i3*nb3));
|
||||
|
||||
for (int64_t i01 = 0; i01 < ne01; ++i01) {
|
||||
const int64_t i11 = i01;
|
||||
ggml_fp16_t * s0 = (ggml_fp16_t *) ((char *) src0->data + (i01*nb01 + i02*nb02 + i03*nb03));
|
||||
float * s1 = (float *) ((char *) src1->data + (i1*nb10 + i11*nb11 + i12*nb12 + i13*nb13));
|
||||
ggml_fp16_to_fp32_row(s0, wdata, ne0);
|
||||
ggml_vec_mad_f32(ne0, d, wdata, *s1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ggml_compute_forward_out_prod(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
@@ -4550,8 +4486,9 @@ void ggml_compute_forward_out_prod(
|
||||
} break;
|
||||
case GGML_TYPE_F16:
|
||||
{
|
||||
ggml_compute_forward_out_prod_f16_f32(params, dst);
|
||||
} break;
|
||||
GGML_ABORT("fatal error"); // todo
|
||||
// ggml_compute_forward_out_prod_f16_f32(params, dst);
|
||||
}
|
||||
case GGML_TYPE_F32:
|
||||
{
|
||||
ggml_compute_forward_out_prod_f32(params, dst);
|
||||
@@ -5104,7 +5041,7 @@ static void ggml_compute_forward_set_rows_impl(
|
||||
assert(ne0 == nc);
|
||||
assert(ne2 == ne02);
|
||||
assert(ne3 == ne03);
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16);
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32 || (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16));
|
||||
assert(ne02 % ne11 == 0);
|
||||
assert(ne03 % ne12 == 0);
|
||||
|
||||
@@ -5138,19 +5075,10 @@ static void ggml_compute_forward_set_rows_impl(
|
||||
(const float *) ((char *) src0->data + i*nb01 + i02*nb02 + i03*nb03),
|
||||
((char *) dst->data + i1*nb1 + i02*nb2 + i03*nb3), nc);
|
||||
} else if constexpr (std::is_same_v<src_t, ggml_fp16_t>) {
|
||||
if (dst->type == GGML_TYPE_F16) {
|
||||
memcpy(
|
||||
memcpy(
|
||||
((char *) dst->data + i1*nb1 + i02*nb2 + i03*nb3),
|
||||
((char *) src0->data + i*nb01 + i02*nb02 + i03*nb03),
|
||||
rs);
|
||||
} else {
|
||||
float * wdata = (float *) params->wdata + (nc + CACHE_LINE_SIZE_F32) * ith;
|
||||
ggml_fp16_to_fp32_row(
|
||||
(const ggml_fp16_t *) ((char *) src0->data + i*nb01 + i02*nb02 + i03*nb03),
|
||||
wdata, nc);
|
||||
from_float(wdata,
|
||||
((char *) dst->data + i1*nb1 + i02*nb2 + i03*nb3), nc);
|
||||
}
|
||||
} else {
|
||||
GGML_ABORT("src0->type = %d (%s) not supported", src0->type, ggml_type_name(src0->type));
|
||||
}
|
||||
@@ -5179,12 +5107,16 @@ void ggml_compute_forward_set_rows(
|
||||
} break;
|
||||
case GGML_TYPE_F16:
|
||||
{
|
||||
if (src1->type == GGML_TYPE_I64) {
|
||||
ggml_compute_forward_set_rows_impl<ggml_fp16_t, int64_t>(params, dst);
|
||||
} else if (src1->type == GGML_TYPE_I32) {
|
||||
ggml_compute_forward_set_rows_impl<ggml_fp16_t, int32_t>(params, dst);
|
||||
if (dst->type == GGML_TYPE_F16) {
|
||||
if (src1->type == GGML_TYPE_I64) {
|
||||
ggml_compute_forward_set_rows_impl<ggml_fp16_t, int64_t>(params, dst);
|
||||
} else if (src1->type == GGML_TYPE_I32) {
|
||||
ggml_compute_forward_set_rows_impl<ggml_fp16_t, int32_t>(params, dst);
|
||||
} else {
|
||||
GGML_ABORT("src1->type = %d (%s) not supported", src1->type, ggml_type_name(src1->type));
|
||||
}
|
||||
} else {
|
||||
GGML_ABORT("src1->type = %d (%s) not supported", src1->type, ggml_type_name(src1->type));
|
||||
GGML_ABORT("dst->type = %d (%s) not supported with src0->type = %d (%s)", dst->type, ggml_type_name(dst->type), src0->type, ggml_type_name(src0->type));
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
@@ -6430,6 +6362,7 @@ static void ggml_compute_forward_im2col_f16(
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F16);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F16 || src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( dst->type == GGML_TYPE_F16);
|
||||
|
||||
@@ -6460,6 +6393,7 @@ static void ggml_compute_forward_im2col_f16(
|
||||
int ofs0 = is_2D ? nb13 : nb12;
|
||||
int ofs1 = is_2D ? nb12 : nb11;
|
||||
|
||||
GGML_ASSERT(nb00 == sizeof(ggml_fp16_t));
|
||||
GGML_ASSERT(nb10 == ggml_type_size(src1->type));
|
||||
|
||||
// im2col: [N, IC, IH, IW] => [N, OH, OW, IC*KH*KW]
|
||||
@@ -6532,7 +6466,7 @@ void ggml_compute_forward_im2col_back_f32(
|
||||
const ggml_tensor * src1 = dst->src[1]; // convolution kernel
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F32 || src1->type == GGML_TYPE_F16);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( dst->type == GGML_TYPE_F32);
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS;
|
||||
@@ -6629,6 +6563,7 @@ static void ggml_compute_forward_im2col_3d_f16(
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F16);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( dst->type == GGML_TYPE_F16);
|
||||
|
||||
|
||||
@@ -141,25 +141,27 @@ static __global__ void __launch_bounds__(CUDA_CONCAT_BLOCK_SIZE)
|
||||
|
||||
template <typename T>
|
||||
static void concat_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, int dim, cudaStream_t stream) {
|
||||
if (dim != 3 && ggml_is_contiguous_to_3(src0) && ggml_is_contiguous_to_3(src1)) {
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const T * src0_d = (const T *) src0->data;
|
||||
const T * src1_d = (const T *) src1->data;
|
||||
T * dst_d = (T *) dst->data;
|
||||
|
||||
for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_cont_cuda(
|
||||
src0_d + i3*(src0->nb[3] / sizeof(T)),
|
||||
src1_d + i3*(src1->nb[3] / sizeof(T)),
|
||||
dst_d + i3*( dst->nb[3] / sizeof(T)),
|
||||
ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
|
||||
ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else if (dim == 3 && ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
if (dim != 3) {
|
||||
for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_cont_cuda(
|
||||
src0_d + i3*(src0->nb[3] / sizeof(T)),
|
||||
src1_d + i3*(src1->nb[3] / sizeof(T)),
|
||||
dst_d + i3*( dst->nb[3] / sizeof(T)),
|
||||
ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
|
||||
ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
|
||||
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
|
||||
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
|
||||
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
|
||||
}
|
||||
} else {
|
||||
GGML_ASSERT(!ggml_is_quantized(src0->type));
|
||||
|
||||
@@ -206,17 +208,12 @@ void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
GGML_ASSERT(dst->type == src0->type);
|
||||
|
||||
if (ggml_is_quantized(src0->type)) {
|
||||
if (dim == 3) {
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(src1));
|
||||
} else {
|
||||
GGML_ASSERT(ggml_is_contiguous_to_3(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_to_3(src1));
|
||||
}
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(src1));
|
||||
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
|
||||
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
|
||||
|
||||
// if first 3 dimensions are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
|
||||
// if tensors are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
|
||||
concat_cuda<uint8_t>(src0, src1, dst, dim, stream);
|
||||
} else {
|
||||
GGML_ASSERT(ggml_blck_size(src0->type) == 1);
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
#include "ggml-cuda/tri.cuh"
|
||||
#include "ggml-cuda/cumsum.cuh"
|
||||
#include "ggml-cuda/fill.cuh"
|
||||
#include "ggml-cuda/lightning-indexer.cuh"
|
||||
#include "ggml.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -2258,9 +2257,6 @@ static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct gg
|
||||
case GGML_OP_FILL:
|
||||
ggml_cuda_op_fill(ctx, dst);
|
||||
break;
|
||||
case GGML_OP_LIGHTNING_INDEXER:
|
||||
ggml_cuda_lightning_indexer(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -4820,23 +4816,13 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
{
|
||||
ggml_type src0_type = op->src[0]->type;
|
||||
ggml_type src1_type = op->src[1]->type;
|
||||
const int32_t dim = op->op_params[0];
|
||||
return src0_type == src1_type &&
|
||||
src0_type == op->type &&
|
||||
(
|
||||
(
|
||||
ggml_is_quantized(src0_type) &&
|
||||
(
|
||||
(
|
||||
dim == 3 &&
|
||||
ggml_is_contiguous(op->src[0]) &&
|
||||
ggml_is_contiguous(op->src[1])
|
||||
) || (
|
||||
dim != 3 &&
|
||||
ggml_is_contiguous_to_3(op->src[0]) &&
|
||||
ggml_is_contiguous_to_3(op->src[1])
|
||||
)
|
||||
) &&
|
||||
ggml_is_contiguous(op->src[0]) &&
|
||||
ggml_is_contiguous(op->src[1]) &&
|
||||
op->src[0]->ne[0] % ggml_blck_size(src0_type) == 0 &&
|
||||
op->src[1]->ne[0] % ggml_blck_size(src0_type) == 0
|
||||
) || (
|
||||
@@ -4991,8 +4977,6 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
case GGML_OP_DIAG:
|
||||
case GGML_OP_SOLVE_TRI:
|
||||
return true;
|
||||
case GGML_OP_LIGHTNING_INDEXER:
|
||||
return ggml_cuda_lightning_indexer_supported(dev_ctx->device, op);
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -1,588 +0,0 @@
|
||||
#include "common.cuh"
|
||||
#include "lightning-indexer.cuh"
|
||||
#include "fattn-common.cuh"
|
||||
#include "convert.cuh"
|
||||
|
||||
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
#if defined(TURING_MMA_AVAILABLE)
|
||||
|
||||
typedef union {
|
||||
int2 i2;
|
||||
half2 h2[2];
|
||||
} half4;
|
||||
|
||||
// TODO add support for AMD cards via rocWMMA
|
||||
#include <mma.h>
|
||||
namespace wmma = nvcuda::wmma;
|
||||
|
||||
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
|
||||
static __global__ void lightning_indexer_kernel_wmma(
|
||||
const float * Q, const char * K, const float * W, const half * M, float * dst,
|
||||
int64_t n_stream, int64_t n_batch, int64_t n_kv,
|
||||
size_t nb1, size_t nb2, size_t nb3,
|
||||
size_t nbq1, size_t nbq2, size_t nbq3,
|
||||
size_t nbk1, size_t nbk2, size_t nbk3,
|
||||
size_t nbw1, size_t nbw2, size_t nbw3,
|
||||
size_t nbm1, size_t nbm2, size_t nbm3,
|
||||
int64_t nem3
|
||||
) {
|
||||
|
||||
constexpr int THREADS_PER_BLOCK = WARPS_PER_BLOCK * WARP_SIZE;
|
||||
constexpr int HEADS_PER_INNER_LOOP = 8;
|
||||
constexpr int K_EMBD_PER_INNER_LOOP = 16;
|
||||
constexpr int N_EMBD_PADDED = N_EMBD + 8;
|
||||
|
||||
const int i_batch = blockIdx.y;
|
||||
const int i_stream = blockIdx.z;
|
||||
const int i_warp = threadIdx.y;
|
||||
const int i_lane = threadIdx.x;
|
||||
const int tid = i_warp * WARP_SIZE + i_lane;
|
||||
|
||||
// each block processes K_VECS_PER_BLOCK K vectors
|
||||
const int start_kv = blockIdx.x * K_VECS_PER_BLOCK;
|
||||
|
||||
const char * q_base = (const char *) Q + i_batch*nbq2 + i_stream*nbq3;
|
||||
const float * w_base = (const float *) ((const char *) W + i_batch*nbw1 + i_stream*nbw3);
|
||||
|
||||
// phase 1 - load weights and first Q tile to shared memory
|
||||
|
||||
__shared__ float w_shared[N_HEAD];
|
||||
__shared__ int2 q_shared_h[HEADS_PER_INNER_LOOP][N_EMBD_PADDED / 4];
|
||||
|
||||
if (tid < N_HEAD) {
|
||||
w_shared[tid] = w_base[tid];
|
||||
}
|
||||
|
||||
// total number of half4 elements in HEADS_PER_INNER_LOOP x N_EMBD Q tile
|
||||
constexpr int N_Q_TILE = HEADS_PER_INNER_LOOP * (N_EMBD / 4);
|
||||
// number of registers needed in each thread to store Q tile in thread block
|
||||
constexpr int N_Q_NEXT = (N_Q_TILE + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;
|
||||
|
||||
#pragma unroll
|
||||
for (int i_q = tid; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
|
||||
const int i_head = i_q / (N_EMBD / 4);
|
||||
const int i_embd = i_q % (N_EMBD / 4);
|
||||
const float4 q = *(const float4 *) (q_base + i_head*nbq1 + i_embd*sizeof(float4));
|
||||
half4 q_packed;
|
||||
q_packed.h2[0] = __float22half2_rn(make_float2(q.x, q.y));
|
||||
q_packed.h2[1] = __float22half2_rn(make_float2(q.z, q.w));
|
||||
q_shared_h[i_head][i_embd] = q_packed.i2;
|
||||
}
|
||||
|
||||
// phase 2 - load (and dequantize if needed) K to shared mem
|
||||
|
||||
__shared__ half2 k_shared_h[K_VECS_PER_BLOCK][N_EMBD_PADDED / 4][2];
|
||||
|
||||
constexpr int n_k = K_VECS_PER_BLOCK * (N_EMBD / 4);
|
||||
|
||||
if constexpr (TYPE_K == GGML_TYPE_F16) {
|
||||
#pragma unroll
|
||||
for (int i_k = tid; i_k < n_k; i_k += THREADS_PER_BLOCK) {
|
||||
const int i_k_vec = i_k / (N_EMBD / 4);
|
||||
const int i_embd = i_k % (N_EMBD / 4);
|
||||
const int i_kv = start_kv + i_k_vec;
|
||||
if (i_kv < n_kv) {
|
||||
const int2 * k_base = (const int2 *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
|
||||
*(int2*) &k_shared_h[i_k_vec][i_embd] = k_base[i_embd];
|
||||
} else {
|
||||
*(int2*) &k_shared_h[i_k_vec][i_embd] = make_int2(0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
constexpr dequantize_V_t dequantize_k = get_dequantize_V<TYPE_K, half, 4>();
|
||||
#pragma unroll
|
||||
for (int i_k = tid; i_k < n_k; i_k += THREADS_PER_BLOCK) {
|
||||
const int i_k_vec = i_k / (N_EMBD / 4);
|
||||
const int i_embd = i_k % (N_EMBD / 4);
|
||||
const int i_kv = start_kv + i_k_vec;
|
||||
if (i_kv < n_kv) {
|
||||
const void * k_base = (const void *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
|
||||
dequantize_k(k_base, &k_shared_h[i_k_vec][i_embd][0], i_embd * 4);
|
||||
} else {
|
||||
*(int2*) &k_shared_h[i_k_vec][i_embd] = make_int2(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// phase 3 - calculate lightning indexer scores
|
||||
|
||||
__shared__ float qk_shared[WARPS_PER_BLOCK][HEADS_PER_INNER_LOOP][K_VECS_PER_BLOCK];
|
||||
|
||||
// load K fragment
|
||||
wmma::fragment<wmma::matrix_b, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, half, wmma::col_major> frag_k;
|
||||
wmma::load_matrix_sync(frag_k, (half*) &k_shared_h[0][i_warp * K_EMBD_PER_INNER_LOOP / 4], N_EMBD_PADDED);
|
||||
|
||||
float score_k = 0.0f;
|
||||
|
||||
for (int i_head_0 = 0; i_head_0 < N_HEAD; i_head_0 += HEADS_PER_INNER_LOOP) {
|
||||
const int i_head_next = i_head_0 + HEADS_PER_INNER_LOOP;
|
||||
|
||||
// we don't use accumulator for anything, fill it with zeros
|
||||
wmma::fragment<wmma::accumulator, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, float> frag_acc;
|
||||
wmma::fill_fragment(frag_acc, 0.0f);
|
||||
|
||||
// load Q fragment
|
||||
wmma::fragment<wmma::matrix_a, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, half, wmma::row_major> frag_q;
|
||||
wmma::load_matrix_sync(frag_q, (half*) &q_shared_h[0][i_warp * K_EMBD_PER_INNER_LOOP / 4], N_EMBD_PADDED);
|
||||
|
||||
// preload next Q tile to registers during matrix multiplication
|
||||
float4 q_next[N_Q_NEXT];
|
||||
|
||||
if (i_head_next < N_HEAD) {
|
||||
#pragma unroll
|
||||
for (int i_q = tid, i_q_next = 0; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
|
||||
const int i_head = i_head_next + i_q / (N_EMBD / 4);
|
||||
const int i_embd = i_q % (N_EMBD / 4);
|
||||
q_next[i_q_next++] = *(const float4 *) (q_base + i_head*nbq1 + i_embd*sizeof(float4));
|
||||
}
|
||||
}
|
||||
|
||||
// perform matrix multiplication
|
||||
wmma::mma_sync(frag_acc, frag_q, frag_k, frag_acc);
|
||||
wmma::store_matrix_sync((float*) &qk_shared[i_warp][0][0], frag_acc, K_VECS_PER_BLOCK, wmma::mem_row_major);
|
||||
|
||||
// make sure all threads finished using q_shared_h so we can store next tile
|
||||
__syncthreads();
|
||||
|
||||
// write preloaded Q tile to shared memory
|
||||
if (i_head_next < N_HEAD) {
|
||||
#pragma unroll
|
||||
for (int i_q = tid, i_q_next = 0; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
|
||||
const int i_head = i_q / (N_EMBD / 4);
|
||||
const int i_embd = i_q % (N_EMBD / 4);
|
||||
half4 q_packed;
|
||||
q_packed.h2[0] = __float22half2_rn(make_float2(q_next[i_q_next].x, q_next[i_q_next].y));
|
||||
q_packed.h2[1] = __float22half2_rn(make_float2(q_next[i_q_next].z, q_next[i_q_next].w));
|
||||
q_shared_h[i_head][i_embd] = q_packed.i2;
|
||||
++i_q_next;
|
||||
}
|
||||
}
|
||||
|
||||
// accumulate QK multiplication results from all block warps
|
||||
// (there are 256 threads in block and 256 matmul outputs)
|
||||
// TODO it will break if WARP_SIZE is not 32
|
||||
const int h = tid / K_VECS_PER_BLOCK;
|
||||
const int k = tid % K_VECS_PER_BLOCK;
|
||||
const float w_val = w_shared[i_head_0 + h];
|
||||
|
||||
float sum = 0.0f;
|
||||
#pragma unroll
|
||||
for (int w = 0; w < WARPS_PER_BLOCK; ++w) {
|
||||
sum += qk_shared[w][h][k];
|
||||
}
|
||||
|
||||
// ReLU, weight
|
||||
sum = sum > 0.0f ? sum : 0.0f;
|
||||
sum *= w_val;
|
||||
|
||||
// wait until qk_shared[0] is no longer used
|
||||
__syncthreads();
|
||||
|
||||
// reuse qk_shared[0] for storing partial results
|
||||
qk_shared[0][h][k] = sum;
|
||||
|
||||
// wait until all threads write their results
|
||||
__syncthreads();
|
||||
|
||||
// accumulate result over heads
|
||||
if (tid < K_VECS_PER_BLOCK) {
|
||||
#pragma unroll
|
||||
for (int i_head = 0; i_head < HEADS_PER_INNER_LOOP; ++i_head) {
|
||||
score_k += qk_shared[0][i_head][tid];
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all threads finished using qk_shared
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// phase 4 - store output to VRAM
|
||||
|
||||
if (tid < K_VECS_PER_BLOCK) {
|
||||
const int i_kv = start_kv + tid;
|
||||
if (i_kv < n_kv) {
|
||||
const half * m_base = (const half *) ((const char *) M + i_batch*nbm1 + (i_stream%nem3)*nbm3);
|
||||
float * dst_base = (float *) ((char *) dst + i_batch*nb1 + i_stream*nb3);
|
||||
dst_base[i_kv] = score_k + __half2float(m_base[i_kv]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else // defined(TURING_MMA_AVAILABLE)
|
||||
|
||||
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
|
||||
static __global__ void lightning_indexer_kernel_wmma(
|
||||
const float * Q, const char * K, const float * W, const half * M, float * dst,
|
||||
int64_t n_stream, int64_t n_batch, int64_t n_kv,
|
||||
size_t nb1, size_t nb2, size_t nb3,
|
||||
size_t nbq1, size_t nbq2, size_t nbq3,
|
||||
size_t nbk1, size_t nbk2, size_t nbk3,
|
||||
size_t nbw1, size_t nbw2, size_t nbw3,
|
||||
size_t nbm1, size_t nbm2, size_t nbm3,
|
||||
int64_t nem3
|
||||
) {
|
||||
GGML_UNUSED_VARS(Q, K, W, M, dst,
|
||||
n_stream, n_batch, n_kv,
|
||||
nb1, nb2, nb3,
|
||||
nbq1, nbq2, nbq3,
|
||||
nbk1, nbk2, nbk3,
|
||||
nbw1, nbw2, nbw3,
|
||||
nem3);
|
||||
NO_DEVICE_CODE;
|
||||
}
|
||||
|
||||
#endif // defined(TURING_MMA_AVAILABLE)
|
||||
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
|
||||
// TODO there is one ugly assumption used in this kernel - that WARP_SIZE is equal to 32
|
||||
// thanks to that one warp operating on float4 processes whole indexer K/Q vectors
|
||||
// 32 * 4 = 128 (N_EMBD)
|
||||
|
||||
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
|
||||
static __global__ void lightning_indexer_kernel_vec(
|
||||
const float * Q, const char * K, const float * W, const half * M, float * dst,
|
||||
int64_t n_stream, int64_t n_batch, int64_t n_kv,
|
||||
size_t nb1, size_t nb2, size_t nb3,
|
||||
size_t nbq1, size_t nbq2, size_t nbq3,
|
||||
size_t nbk1, size_t nbk2, size_t nbk3,
|
||||
size_t nbw1, size_t nbw2, size_t nbw3,
|
||||
size_t nbm1, size_t nbm2, size_t nbm3,
|
||||
int64_t nem3
|
||||
) {
|
||||
|
||||
constexpr int K_VECS_PER_WARP = K_VECS_PER_BLOCK / WARPS_PER_BLOCK;
|
||||
constexpr int THREADS_PER_BLOCK = WARPS_PER_BLOCK * WARP_SIZE;
|
||||
|
||||
const int i_batch = blockIdx.y;
|
||||
const int i_stream = blockIdx.z;
|
||||
const int i_warp = threadIdx.y;
|
||||
const int i_lane = threadIdx.x;
|
||||
const int tid = i_warp * WARP_SIZE + i_lane;
|
||||
|
||||
// each warp processes K_VECS_PER_WARP K vectors
|
||||
const int start_kv_block = blockIdx.x * K_VECS_PER_BLOCK;
|
||||
const int start_kv = start_kv_block + i_warp * K_VECS_PER_WARP;
|
||||
|
||||
const char * q_base = (const char *) Q + i_batch*nbq2 + i_stream*nbq3;
|
||||
const float * w_base = (const float *) ((const char *) W + i_batch*nbw1 + i_stream*nbw3);
|
||||
|
||||
// phase 1 - load (and dequantize if needed) K to registers
|
||||
|
||||
float4 k_reg_f[K_VECS_PER_WARP];
|
||||
|
||||
if constexpr (TYPE_K == GGML_TYPE_F32) {
|
||||
// direct copy of float4
|
||||
#pragma unroll
|
||||
for (int k = 0; k < K_VECS_PER_WARP; ++k) {
|
||||
int i_kv = start_kv + k;
|
||||
if (i_kv < n_kv) {
|
||||
const float4 * k_base = (const float4 *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
|
||||
k_reg_f[k] = k_base[i_lane];
|
||||
} else {
|
||||
k_reg_f[k] = make_float4(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// dequantize remaining types to float
|
||||
constexpr dequantize_V_t dequantize_k = get_dequantize_V<TYPE_K, float, 4>();
|
||||
#pragma unroll
|
||||
for (int k = 0; k < K_VECS_PER_WARP; ++k) {
|
||||
int i_kv = start_kv + k;
|
||||
if (i_kv < n_kv) {
|
||||
const void * k_base = (const void *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
|
||||
dequantize_k(k_base, &k_reg_f[k], i_lane * 4);
|
||||
} else {
|
||||
k_reg_f[k] = make_float4(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float score_k[K_VECS_PER_WARP] = { 0.0f };
|
||||
|
||||
// load weights and Q only for N_HEAD_INNER heads at once to reduce shared memory usage
|
||||
constexpr int N_HEAD_INNER = N_HEAD / 4;
|
||||
|
||||
for (int i_head_0 = 0; i_head_0 < N_HEAD; i_head_0 += N_HEAD_INNER) {
|
||||
// phase 2 - load weights and Q to shared memory
|
||||
|
||||
__shared__ float w_shared[N_HEAD_INNER];
|
||||
__shared__ float4 q_shared_f[N_HEAD_INNER][N_EMBD / 4];
|
||||
|
||||
if (tid < N_HEAD_INNER) {
|
||||
w_shared[tid] = w_base[i_head_0 + tid];
|
||||
}
|
||||
|
||||
constexpr int n_q = N_HEAD_INNER * (N_EMBD / 4);
|
||||
#pragma unroll
|
||||
for (int i_q = tid; i_q < n_q; i_q += THREADS_PER_BLOCK) {
|
||||
const int i_head_inner = i_q / (N_EMBD / 4);
|
||||
const int i_head = i_head_0 + i_head_inner;
|
||||
const int i_embd = i_q % (N_EMBD / 4);
|
||||
q_shared_f[i_head_inner][i_embd] = *(const float4 *) (q_base + i_head*nbq1 + i_embd*sizeof(float4));
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// phase 3 - calculate lightning indexer scores
|
||||
|
||||
for (int i_head_inner = 0; i_head_inner < N_HEAD_INNER; ++i_head_inner) {
|
||||
const float w_val = w_shared[i_head_inner];
|
||||
float qk[K_VECS_PER_WARP] = { 0.0f };
|
||||
|
||||
// dot product of floats
|
||||
const float4 q_vec = q_shared_f[i_head_inner][i_lane];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < K_VECS_PER_WARP; ++k) {
|
||||
ggml_cuda_mad(qk[k], q_vec.x, k_reg_f[k].x);
|
||||
ggml_cuda_mad(qk[k], q_vec.y, k_reg_f[k].y);
|
||||
ggml_cuda_mad(qk[k], q_vec.z, k_reg_f[k].z);
|
||||
ggml_cuda_mad(qk[k], q_vec.w, k_reg_f[k].w);
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < K_VECS_PER_WARP; ++k) {
|
||||
float sum = warp_reduce_sum(qk[k]);
|
||||
|
||||
// ReLU, weight
|
||||
if (i_lane == 0) {
|
||||
sum = (sum > 0.0f) ? sum : 0.0f;
|
||||
score_k[k] += sum * w_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// phase 4 - store outputs to shared memory
|
||||
|
||||
__shared__ float dst_shared[K_VECS_PER_BLOCK];
|
||||
|
||||
if (i_lane == 0) {
|
||||
#pragma unroll
|
||||
for (int k = 0; k < K_VECS_PER_WARP; ++k) {
|
||||
dst_shared[i_warp * K_VECS_PER_WARP + k] = score_k[k];
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// phase 5 - write from shared memory to VRAM in coalesced manner
|
||||
|
||||
if (tid < K_VECS_PER_BLOCK) {
|
||||
int i_kv = start_kv_block + tid;
|
||||
if (i_kv < n_kv) {
|
||||
const half * m_base = (const half *) ((const char *) M + i_batch*nbm1 + (i_stream%nem3)*nbm3);
|
||||
float * dst_base = (float *) ((char *) dst + i_batch*nb1 + i_stream*nb3);
|
||||
dst_base[i_kv] = dst_shared[tid] + __half2float(m_base[i_kv]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define LIGHTNING_INDEXER_CASE(lightning_indexer_kernel, n_embd, n_head, K, type_K) \
|
||||
if (K->type == (type_K)) { \
|
||||
lightning_indexer_kernel<WARPS_PER_BLOCK, K_VECS_PER_BLOCK, n_embd, n_head, type_K> \
|
||||
<<<grid, block, 0, ctx.stream()>>>( \
|
||||
q_d, k_d, w_d, m_d, dst_d, \
|
||||
n_stream, n_batch, n_kv, \
|
||||
nb1, nb2, nb3, \
|
||||
nbq1, nbq2, nbq3, \
|
||||
nbk1, nbk2, nbk3, \
|
||||
nbw1, nbw2, nbw3, \
|
||||
nbm1, nbm2, nbm3, \
|
||||
nem3 \
|
||||
); \
|
||||
} else
|
||||
|
||||
void ggml_cuda_lightning_indexer(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * q = dst->src[0];
|
||||
const ggml_tensor * k = dst->src[1];
|
||||
const ggml_tensor * w = dst->src[2]; // weights
|
||||
const ggml_tensor * m = dst->src[3]; // mask
|
||||
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( q->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( w->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( m->type == GGML_TYPE_F16);
|
||||
|
||||
GGML_TENSOR_LOCALS(int64_t, neq, q, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbq, q, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, nek, k, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbk, k, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, new, w, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbw, w, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, nem, m, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbm, m, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, ne, dst, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nb, dst, nb)
|
||||
|
||||
// input tensor rows must be contiguous
|
||||
GGML_ASSERT(nbq0 == ggml_type_size(q->type));
|
||||
GGML_ASSERT(nbk0 == ggml_type_size(k->type));
|
||||
GGML_ASSERT(nbw0 == ggml_type_size(w->type));
|
||||
GGML_ASSERT(nbm0 == ggml_type_size(m->type));
|
||||
|
||||
// dst cannot be transposed or permuted
|
||||
GGML_ASSERT(nb0 == sizeof(float));
|
||||
GGML_ASSERT(nb0 <= nb1);
|
||||
GGML_ASSERT(nb1 <= nb2);
|
||||
GGML_ASSERT(nb2 <= nb3);
|
||||
|
||||
const int n_embd = q->ne[0];
|
||||
const int n_head = q->ne[1];
|
||||
const int n_batch = q->ne[2];
|
||||
const int n_stream = q->ne[3];
|
||||
const int n_kv = k->ne[2];
|
||||
|
||||
const float * q_d = (const float *) q->data;
|
||||
const char * k_d = (const char *) k->data;
|
||||
const float * w_d = (const float *) w->data;
|
||||
const half * m_d = (const half *) m->data;
|
||||
float * dst_d = ( float *) dst->data;
|
||||
|
||||
const int device = ggml_cuda_get_device();
|
||||
const int cc = ggml_cuda_info().devices[device].cc;
|
||||
|
||||
if (n_embd == 128 && n_head == 64) {
|
||||
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
if (GGML_CUDA_CC_IS_NVIDIA(cc) && turing_mma_available(cc) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
|
||||
// use wmma kernel
|
||||
constexpr int K_VECS_PER_BLOCK = 32;
|
||||
constexpr int WARPS_PER_BLOCK = 8;
|
||||
|
||||
dim3 block(32, WARPS_PER_BLOCK);
|
||||
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
|
||||
dim3 grid(num_kv_blocks, n_batch, n_stream);
|
||||
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_F16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q4_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q4_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q5_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q5_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q8_0)
|
||||
GGML_ABORT("fatal error");
|
||||
} else {
|
||||
#else // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
{
|
||||
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
// use vector kernel
|
||||
constexpr int K_VECS_PER_WARP = 8;
|
||||
constexpr int WARPS_PER_BLOCK = 8;
|
||||
constexpr int K_VECS_PER_BLOCK = K_VECS_PER_WARP * WARPS_PER_BLOCK;
|
||||
|
||||
dim3 block(32, WARPS_PER_BLOCK);
|
||||
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
|
||||
dim3 grid(num_kv_blocks, n_batch, n_stream);
|
||||
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_F16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_Q4_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_Q4_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_Q5_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_Q5_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_Q8_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_BF16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 64, k, GGML_TYPE_F32)
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
} else if (n_embd == 128 && n_head == 32) {
|
||||
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
if (GGML_CUDA_CC_IS_NVIDIA(cc) && turing_mma_available(cc) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
|
||||
// use wmma kernel
|
||||
constexpr int K_VECS_PER_BLOCK = 32;
|
||||
constexpr int WARPS_PER_BLOCK = 8;
|
||||
|
||||
dim3 block(32, WARPS_PER_BLOCK);
|
||||
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
|
||||
dim3 grid(num_kv_blocks, n_batch, n_stream);
|
||||
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_F16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q4_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q4_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q5_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q5_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q8_0)
|
||||
GGML_ABORT("fatal error");
|
||||
} else {
|
||||
#else // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
{
|
||||
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
// use vector kernel
|
||||
constexpr int K_VECS_PER_WARP = 8;
|
||||
constexpr int WARPS_PER_BLOCK = 8;
|
||||
constexpr int K_VECS_PER_BLOCK = K_VECS_PER_WARP * WARPS_PER_BLOCK;
|
||||
|
||||
dim3 block(32, WARPS_PER_BLOCK);
|
||||
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
|
||||
dim3 grid(num_kv_blocks, n_batch, n_stream);
|
||||
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_F16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_Q4_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_Q4_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_Q5_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_Q5_1)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_Q8_0)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_BF16)
|
||||
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_vec, 128, 32, k, GGML_TYPE_F32)
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
} else {
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
}
|
||||
|
||||
bool ggml_cuda_lightning_indexer_supported(int device, const ggml_tensor * dst) {
|
||||
GGML_UNUSED(device);
|
||||
|
||||
const ggml_tensor * q = dst->src[0];
|
||||
const ggml_tensor * k = dst->src[1];
|
||||
const ggml_tensor * w = dst->src[2]; // weights
|
||||
const ggml_tensor * m = dst->src[3]; // mask
|
||||
|
||||
GGML_TENSOR_LOCALS(int64_t, neq, q, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbq, q, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, nek, k, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbk, k, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, new, w, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbw, w, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, nem, m, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nbm, m, nb)
|
||||
GGML_TENSOR_LOCALS(int64_t, ne, dst, ne)
|
||||
GGML_TENSOR_LOCALS(size_t, nb, dst, nb)
|
||||
|
||||
if (neq0 != 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (neq1 != 64 && neq1 != 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// alignment checks
|
||||
for (const ggml_tensor * t : {q, k}) {
|
||||
if (ggml_is_quantized(t->type)) {
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 1; i < GGML_MAX_DIMS; ++i) {
|
||||
if (t->nb[i] % 16 != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(k->type) {
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_Q8_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q4_1:
|
||||
case GGML_TYPE_Q4_0:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include "common.cuh"
|
||||
|
||||
void ggml_cuda_lightning_indexer(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
||||
bool ggml_cuda_lightning_indexer_supported(int device, const ggml_tensor * dst);
|
||||
@@ -39,37 +39,29 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
}
|
||||
|
||||
const block_q1_0 * bxi = (const block_q1_0 *) x + kbx0 + i*stride + kbx;
|
||||
const int16_t * qxi = (const int16_t *) bxi->qs + kqsx * 2;
|
||||
const int qs_offset = 4*kqsx;
|
||||
const int qs0 = bxi->qs[qs_offset + 0] | (bxi->qs[qs_offset + 1] << 8) |
|
||||
(bxi->qs[qs_offset + 2] << 16) | (bxi->qs[qs_offset + 3] << 24);
|
||||
|
||||
int unpacked_bytes[8];
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
const int shift = j * 4;
|
||||
const int bits4 = (qs0 >> shift) & 0x0F;
|
||||
const int b0 = (bits4 & 0x01) ? 1 : -1;
|
||||
const int b1 = (bits4 & 0x02) ? 1 : -1;
|
||||
const int b2 = (bits4 & 0x04) ? 1 : -1;
|
||||
const int b3 = (bits4 & 0x08) ? 1 : -1;
|
||||
unpacked_bytes[j] = (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);
|
||||
}
|
||||
|
||||
const int dst_offset = kbx*(scale_entries_per_block*QI8_0) + kqsx*QI8_0;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
const int q = qxi[j];
|
||||
|
||||
// unpack crumbs into nibble indices
|
||||
const int n0 = __byte_perm(0x11100100, 0x11100100, q >> 0); // [0, 1, 4, 5] [ 8, 9, 12, 13]
|
||||
const int n1 = __byte_perm(0x11100100, 0x11100100, q >> 2); // [2, 3, 6, 7] [10, 11, 14, 15]
|
||||
// unpack nibbles into byte values
|
||||
const int s0 = __byte_perm(0x01FF, 0x01FF, n0 >> 0);
|
||||
const int s1 = __byte_perm(0x01FF, 0x01FF, n1 >> 0);
|
||||
const int s2 = __byte_perm(0x01FF, 0x01FF, n0 >> 16);
|
||||
const int s3 = __byte_perm(0x01FF, 0x01FF, n1 >> 16);
|
||||
// unshuffle values
|
||||
const int v0 = __byte_perm(s0, s1, 0x5410);
|
||||
const int v1 = __byte_perm(s0, s1, 0x7632);
|
||||
const int v2 = __byte_perm(s2, s3, 0x5410);
|
||||
const int v3 = __byte_perm(s2, s3, 0x7632);
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
x_qs[i*sram_stride + dst_offset + j*4+0] = v0;
|
||||
x_qs[i*sram_stride + dst_offset + j*4+1] = v1;
|
||||
x_qs[i*sram_stride + dst_offset + j*4+2] = v2;
|
||||
x_qs[i*sram_stride + dst_offset + j*4+3] = v3;
|
||||
x_qs[i*sram_stride + dst_offset + j] = unpacked_bytes[j];
|
||||
#else
|
||||
x_qs[i*(2*MMQ_TILE_NE_K + 1) + dst_offset + j*4+0] = v0;
|
||||
x_qs[i*(2*MMQ_TILE_NE_K + 1) + dst_offset + j*4+1] = v1;
|
||||
x_qs[i*(2*MMQ_TILE_NE_K + 1) + dst_offset + j*4+2] = v2;
|
||||
x_qs[i*(2*MMQ_TILE_NE_K + 1) + dst_offset + j*4+3] = v3;
|
||||
x_qs[i*(2*MMQ_TILE_NE_K + 1) + dst_offset + j] = unpacked_bytes[j];
|
||||
#endif // defined(AMD_MFMA_AVAILABLE) || defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,12 +122,11 @@ void ggml_cuda_mul_mat_q(
|
||||
|
||||
const bool fallback = ne01 % 128 != 0;
|
||||
|
||||
// TODO: tighter pool buffer size vs q8 path
|
||||
const bool use_native_fp4 = blackwell_mma_available(cc) && (src0->type == GGML_TYPE_MXFP4 || src0->type == GGML_TYPE_NVFP4);
|
||||
const size_t y_block_size = use_native_fp4 ? sizeof(block_fp4_mmq) : sizeof(block_q8_1_mmq);
|
||||
const size_t y_values_per_block = use_native_fp4 ? QK_FP4_MMQ : QK8_1_MMQ;
|
||||
|
||||
if (!ids) {
|
||||
const size_t nbytes_src1_q8_1 = ne13*ne12 * ne11*ne10_padded * y_block_size/y_values_per_block +
|
||||
const size_t nbytes_src1_q8_1 = ne13*ne12 * ne11*ne10_padded * sizeof(block_q8_1)/QK8_1 +
|
||||
ggml_cuda_mmq_get_J_max(src0->type, fallback, cc, ne11) * sizeof(block_q8_1_mmq);
|
||||
ggml_cuda_pool_alloc<char> src1_q8_1(ctx.pool(), nbytes_src1_q8_1);
|
||||
|
||||
@@ -149,7 +148,7 @@ void ggml_cuda_mul_mat_q(
|
||||
|
||||
// Stride depends on quantization format
|
||||
const int64_t s12 = use_native_fp4 ?
|
||||
ne11 * ne10_padded * sizeof(block_fp4_mmq) / (QK_FP4_MMQ * sizeof(int)) :
|
||||
ne11 * ne10_padded * sizeof(block_fp4_mmq) / (QK_K * sizeof(int)) : // block_fp4_mmq holds 256 values
|
||||
ne11 * ne10_padded * sizeof(block_q8_1) / (QK8_1 * sizeof(int));
|
||||
const int64_t s13 = ne12*s12;
|
||||
|
||||
@@ -185,7 +184,7 @@ void ggml_cuda_mul_mat_q(
|
||||
CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
const size_t nbytes_src1_q8_1 = ne12*n_expert_used*ne10_padded * y_block_size/y_values_per_block +
|
||||
const size_t nbytes_src1_q8_1 = ne12*n_expert_used*ne10_padded * sizeof(block_q8_1)/QK8_1 +
|
||||
ggml_cuda_mmq_get_J_max(src0->type, fallback, cc, ne11) * sizeof(block_q8_1_mmq);
|
||||
ggml_cuda_pool_alloc<char> src1_q8_1(ctx.pool(), nbytes_src1_q8_1);
|
||||
|
||||
@@ -208,8 +207,8 @@ void ggml_cuda_mul_mat_q(
|
||||
CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
static_assert(QK_FP4_MMQ == 8 * QK_MXFP4, "QK_FP4_MMQ needs to be 8 * QK_MXFP4");
|
||||
const int64_t s12 = use_native_fp4 ? ne11 * ne10_padded * sizeof(block_fp4_mmq) / (QK_FP4_MMQ * sizeof(int)) :
|
||||
static_assert(QK_K == 8 * QK_MXFP4, "QK_K needs to be 8 * QK_MXFP4");
|
||||
const int64_t s12 = use_native_fp4 ? ne11 * ne10_padded * sizeof(block_fp4_mmq) / (QK_K * sizeof(int)) :
|
||||
ne11 * ne10_padded * sizeof(block_q8_1) / (QK8_1 * sizeof(int));
|
||||
const int64_t s13 = ne12*s12;
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ enum mmq_q8_1_ds_layout {
|
||||
MMQ_Q8_1_DS_LAYOUT_D2S6,
|
||||
};
|
||||
|
||||
static constexpr int QK8_1_MMQ = 4*QK8_1;
|
||||
static constexpr int QK_FP4_MMQ = 2*QK8_1_MMQ;
|
||||
|
||||
struct block_q8_1_mmq {
|
||||
// The y float data is converted to a data layout that can simply be copied to shared memory as a contiguous block.
|
||||
// The y float data is first grouped as blocks of 128 values.
|
||||
@@ -42,7 +39,7 @@ struct block_q8_1_mmq {
|
||||
half d2s6[8]; // 1 16 bit scale per 64 values + 1 16 bit partial sum per 16 values for the first 96 values,
|
||||
// stored as d0,d1,s1,s2,s3,s4,s5
|
||||
};
|
||||
int8_t qs[QK8_1_MMQ];
|
||||
int8_t qs[4*QK8_1]; // 128 values quantized to 8 bit each
|
||||
};
|
||||
|
||||
// this struct is used for fp4 data types (currently only used for Blackwell)
|
||||
@@ -50,10 +47,10 @@ struct block_q8_1_mmq {
|
||||
// nvfp4 has block size 16, each int32 of d4 contains 4 ue4m3 scales
|
||||
struct block_fp4_mmq {
|
||||
uint32_t d4[4];
|
||||
int8_t qs[QK_FP4_MMQ / 2];
|
||||
int8_t qs[4 * 32]; // 256 FP4 values packed as 4-bit pairs (2 per byte)
|
||||
};
|
||||
|
||||
static_assert(sizeof(block_q8_1_mmq) == QK8_1_MMQ + 4*sizeof(half2), "Unexpected block_q8_1_mmq size");
|
||||
static_assert(sizeof(block_q8_1_mmq) == 4*QK8_1 + 4*sizeof(half2), "Unexpected block_q8_1_mmq size");
|
||||
static_assert(sizeof(block_q8_1_mmq) == 4*sizeof(block_q8_1), "Unexpected block_q8_1_mmq size");
|
||||
static_assert(sizeof(block_fp4_mmq) == sizeof(block_q8_1_mmq), "Unexpected block_fp4_mmq size");
|
||||
|
||||
@@ -836,9 +833,9 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
|
||||
#if defined(BLACKWELL_MMA_AVAILABLE)
|
||||
// FP4 tile stores 8 blocks
|
||||
constexpr int ne_block = (type == GGML_TYPE_MXFP4 || type == GGML_TYPE_NVFP4) ? QK_FP4_MMQ : QK8_1_MMQ;
|
||||
constexpr int ne_block = (type == GGML_TYPE_MXFP4 || type == GGML_TYPE_NVFP4) ? QK_K : 4 * QK8_1;
|
||||
#else
|
||||
constexpr int ne_block = QK8_1_MMQ;
|
||||
constexpr int ne_block = 4 * QK8_1;
|
||||
#endif // defined(BLACKWELL_MMA_AVAILABLE)
|
||||
|
||||
constexpr int ITER_K = ggml_cuda_mmq_get_K_vram(type, J, fallback);
|
||||
|
||||
@@ -90,8 +90,8 @@ static __global__ void quantize_mmq_nvfp4(
|
||||
const int64_t i2 = blockIdx.z % ne2;
|
||||
const int64_t i3 = blockIdx.z / ne2;
|
||||
const int64_t i01 = ids ? ids[i1] : i1;
|
||||
const int64_t k_block = i0_base / QK_FP4_MMQ;
|
||||
const int64_t blocks_per_col = (ne0 + QK_FP4_MMQ - 1) / QK_FP4_MMQ;
|
||||
const int64_t k_block = i0_base / QK_K;
|
||||
const int64_t blocks_per_col = (ne0 + QK_K - 1) / QK_K;
|
||||
if (k_block >= blocks_per_col) {
|
||||
return;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ static __global__ void quantize_mmq_nvfp4(
|
||||
block_fp4_mmq * y = (block_fp4_mmq *) vy;
|
||||
block_fp4_mmq * yb = y + ib;
|
||||
|
||||
const int sub = (i0_base % QK_FP4_MMQ) / QK_NVFP4_SUB;
|
||||
const int sub = (i0_base % QK_K) / QK_NVFP4_SUB;
|
||||
|
||||
float vals_raw[QK_NVFP4_SUB];
|
||||
float amax_raw = 0.0f;
|
||||
@@ -207,7 +207,7 @@ static __global__ void quantize_mmq_mxfp4(const float * __restrict__ x,
|
||||
|
||||
block_fp4_mmq * y = (block_fp4_mmq *) vy;
|
||||
|
||||
const int64_t block_fp4_mmq_size = QK_FP4_MMQ;
|
||||
const int64_t block_fp4_mmq_size = 8 * QK_MXFP4; // 256 values
|
||||
const int64_t ib0 = blockIdx.z * ((int64_t) ne1 * (ne0 / block_fp4_mmq_size));
|
||||
const int64_t ib = ib0 + (warp_start_offset / block_fp4_mmq_size) * ne1 + blockIdx.x;
|
||||
const int64_t quad_idx_in_block = (warp_start_offset % block_fp4_mmq_size) / vals_per_warp;
|
||||
@@ -303,8 +303,8 @@ static __global__ void quantize_mmq_q8_1(
|
||||
block_q8_1_mmq * y = (block_q8_1_mmq *) vy;
|
||||
|
||||
const int64_t ib0 = blockIdx.z*((int64_t)gridDim.x*gridDim.y*blockDim.x/QK8_1); // first block of channel
|
||||
const int64_t ib = ib0 + (i0 / QK8_1_MMQ)*ne1 + blockIdx.x; // block index in channel
|
||||
const int64_t iqs = i0 % QK8_1_MMQ; // quant index in block
|
||||
const int64_t ib = ib0 + (i0 / (4*QK8_1))*ne1 + blockIdx.x; // block index in channel
|
||||
const int64_t iqs = i0 % (4*QK8_1); // quant index in block
|
||||
|
||||
// Load 4 floats per thread and calculate max. abs. value between them:
|
||||
const float4 xi = i0 < ne00 ? x4[(i03*s03 + i02*s02 + i01*s01 + i00)/4] : make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
@@ -394,7 +394,7 @@ void quantize_mmq_q8_1_cuda(
|
||||
const int64_t ne00, const int64_t s01, const int64_t s02, const int64_t s03,
|
||||
const int64_t ne0, const int64_t ne1, const int64_t ne2, const int64_t ne3, cudaStream_t stream) {
|
||||
GGML_ASSERT(ne00 % 4 == 0);
|
||||
GGML_ASSERT(ne0 % QK8_1_MMQ == 0);
|
||||
GGML_ASSERT(ne0 % (4*QK8_1) == 0);
|
||||
|
||||
// ne1 tends to assume the highest values, therefore use it as the "x" dimension of the CUDA grid:
|
||||
const int64_t block_num_y = (ne0 + 4*CUDA_QUANTIZE_BLOCK_SIZE_MMQ - 1) / (4*CUDA_QUANTIZE_BLOCK_SIZE_MMQ);
|
||||
|
||||
@@ -681,40 +681,35 @@ static __device__ __forceinline__ float vec_dot_q1_0_q8_1(
|
||||
// Q8_1: 32 elements per block with individual scales
|
||||
// iqs selects which of the 4 chunks of 32 elements to process (0-3)
|
||||
|
||||
const float d1 = bq1_0->d;
|
||||
const int16_t * qs = (const int16_t *) bq1_0->qs + iqs * 2;
|
||||
const float d1 = bq1_0->d;
|
||||
|
||||
// Process only the chunk specified by iqs
|
||||
const block_q8_1 * bq8_1_chunk = bq8_1 + iqs;
|
||||
|
||||
// Load 32 bits (4 bytes) for this chunk from Q1_0
|
||||
const int offset = iqs * 4;
|
||||
const int v = bq1_0->qs[offset + 0] | (bq1_0->qs[offset + 1] << 8) |
|
||||
(bq1_0->qs[offset + 2] << 16) | (bq1_0->qs[offset + 3] << 24);
|
||||
|
||||
// Unpack 32 bits into 32 signed values (-1 or +1)
|
||||
int vi_bytes[8];
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
const int shift = j * 4;
|
||||
const int bits4 = (v >> shift) & 0x0F;
|
||||
const int b0 = (bits4 & 0x01) ? 1 : -1;
|
||||
const int b1 = (bits4 & 0x02) ? 1 : -1;
|
||||
const int b2 = (bits4 & 0x04) ? 1 : -1;
|
||||
const int b3 = (bits4 & 0x08) ? 1 : -1;
|
||||
vi_bytes[j] = (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);
|
||||
}
|
||||
|
||||
// Compute dot product for this 32-element chunk
|
||||
int sumi = 0;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
const int q = qs[j];
|
||||
|
||||
const int u0 = get_int_b4(bq8_1_chunk->qs, j*4+0);
|
||||
const int u1 = get_int_b4(bq8_1_chunk->qs, j*4+1);
|
||||
const int u2 = get_int_b4(bq8_1_chunk->qs, j*4+2);
|
||||
const int u3 = get_int_b4(bq8_1_chunk->qs, j*4+3);
|
||||
|
||||
// unpack crumbs into nibble indices
|
||||
const int n0 = __byte_perm(0x11100100, 0x11100100, q >> 0); // [0, 1, 4, 5] [ 8, 9, 12, 13]
|
||||
const int n1 = __byte_perm(0x11100100, 0x11100100, q >> 2); // [2, 3, 6, 7] [10, 11, 14, 15]
|
||||
// unpack nibbles into byte values
|
||||
const int s0 = __byte_perm(0x01FF, 0x01FF, n0 >> 0);
|
||||
const int s1 = __byte_perm(0x01FF, 0x01FF, n1 >> 0);
|
||||
const int s2 = __byte_perm(0x01FF, 0x01FF, n0 >> 16);
|
||||
const int s3 = __byte_perm(0x01FF, 0x01FF, n1 >> 16);
|
||||
// unshuffle values
|
||||
const int v0 = __byte_perm(s0, s1, 0x5410);
|
||||
const int v1 = __byte_perm(s0, s1, 0x7632);
|
||||
const int v2 = __byte_perm(s2, s3, 0x5410);
|
||||
const int v3 = __byte_perm(s2, s3, 0x7632);
|
||||
|
||||
sumi = ggml_cuda_dp4a(v0, u0, sumi);
|
||||
sumi = ggml_cuda_dp4a(v1, u1, sumi);
|
||||
sumi = ggml_cuda_dp4a(v2, u2, sumi);
|
||||
sumi = ggml_cuda_dp4a(v3, u3, sumi);
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
const int u = get_int_b4(bq8_1_chunk->qs, j);
|
||||
sumi = ggml_cuda_dp4a(vi_bytes[j], u, sumi);
|
||||
}
|
||||
|
||||
// Apply Q1_0's single scale and this chunk's Q8_1 scale
|
||||
|
||||
@@ -38,7 +38,7 @@ static inline void hmx_queue_process(struct hmx_queue *q, bool* killed) {
|
||||
if (!d->done) {
|
||||
FARF(HIGH, "hmx-queue-process: ir %u func %p data %p", ir, d->func, d->data);
|
||||
|
||||
uintptr_t sig = (uintptr_t) d->func;
|
||||
enum hmx_queue_signal sig = (enum hmx_queue_signal) (unsigned int) d->func;
|
||||
switch (sig) {
|
||||
case HMX_QUEUE_NOOP: /* noop */; break;
|
||||
case HMX_QUEUE_KILL: *killed = true; break;
|
||||
|
||||
@@ -1834,23 +1834,6 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_col2im_1d(ggml_m
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_snake(ggml_metal_library_t lib, enum ggml_type type) {
|
||||
GGML_ASSERT(type == GGML_TYPE_F32 || type == GGML_TYPE_F16 || type == GGML_TYPE_BF16);
|
||||
|
||||
char base[256];
|
||||
char name[256];
|
||||
|
||||
snprintf(base, 256, "kernel_snake_%s", ggml_type_name(type));
|
||||
snprintf(name, 256, "%s", base);
|
||||
|
||||
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, name);
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, base, name, nullptr);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_transpose_2d(ggml_metal_library_t lib, const ggml_tensor * op) {
|
||||
assert(op->op == GGML_OP_CONV_TRANSPOSE_2D);
|
||||
|
||||
|
||||
@@ -151,7 +151,6 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_im2col
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_transpose_1d (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_transpose_2d (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_col2im_1d (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_snake (ggml_metal_library_t lib, enum ggml_type type);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_2d (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_2d_dw (ggml_metal_library_t lib, const struct ggml_tensor * op, bool tiled);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_conv_3d (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
|
||||
@@ -1340,11 +1340,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
return op->src[0]->type != GGML_TYPE_NVFP4;
|
||||
case GGML_OP_SET_ROWS:
|
||||
{
|
||||
if (op->src[0]->type == GGML_TYPE_F16) {
|
||||
return op->type == GGML_TYPE_F16;
|
||||
}
|
||||
|
||||
if (op->src[0]->type != GGML_TYPE_F32) {
|
||||
if (op->src[0]->type != GGML_TYPE_F32 && op->src[0]->type != GGML_TYPE_F16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -616,11 +616,6 @@ typedef struct {
|
||||
int32_t p0;
|
||||
} ggml_metal_kargs_col2im_1d;
|
||||
|
||||
typedef struct {
|
||||
int32_t T;
|
||||
int32_t C;
|
||||
} ggml_metal_kargs_snake;
|
||||
|
||||
typedef struct {
|
||||
int32_t IC;
|
||||
int32_t IH;
|
||||
|
||||
@@ -3077,58 +3077,7 @@ int ggml_metal_op_flash_attn_ext(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Snake activation autofuse: mul -> sin -> sqr -> mul -> add
|
||||
static bool ggml_metal_op_can_fuse_snake(ggml_metal_op_t ctx, int idx) {
|
||||
static constexpr ggml_op snake_ops[5] = { GGML_OP_MUL, GGML_OP_SIN, GGML_OP_SQR, GGML_OP_MUL, GGML_OP_ADD };
|
||||
|
||||
if (ctx->node(idx)->op != GGML_OP_MUL || !ctx->can_fuse(idx, snake_ops, 5)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * mul0 = ctx->node(idx + 0);
|
||||
const ggml_tensor * sin_node = ctx->node(idx + 1);
|
||||
const ggml_tensor * sqr = ctx->node(idx + 2);
|
||||
const ggml_tensor * mul1 = ctx->node(idx + 3);
|
||||
const ggml_tensor * add = ctx->node(idx + 4);
|
||||
|
||||
// x carries the full activation shape, a is the broadcast operand
|
||||
const ggml_tensor * x = ggml_are_same_shape(mul0, mul0->src[0]) ? mul0->src[0] : mul0->src[1];
|
||||
const ggml_tensor * a = (x == mul0->src[0]) ? mul0->src[1] : mul0->src[0];
|
||||
|
||||
// mul1 reads sqr and inv_b in either operand order
|
||||
const ggml_tensor * inv_b = (mul1->src[0] == sqr) ? mul1->src[1] : mul1->src[0];
|
||||
|
||||
// closure check: the trailing add reads the same x as the leading mul
|
||||
const ggml_tensor * x_in_add = (add->src[0] == mul1) ? add->src[1] : add->src[0];
|
||||
|
||||
// x is in the supported whitelist and every chain intermediate shares x's type.
|
||||
// a and inv_b bind as device const float * in the kernel, so they stay F32.
|
||||
const bool types_ok =
|
||||
(x->type == GGML_TYPE_F32 || x->type == GGML_TYPE_F16 || x->type == GGML_TYPE_BF16) &&
|
||||
(a->type == GGML_TYPE_F32) && (inv_b->type == GGML_TYPE_F32) &&
|
||||
(mul0->type == x->type) && (sin_node->type == x->type) &&
|
||||
(sqr->type == x->type) && (mul1->type == x->type) &&
|
||||
(add->type == x->type);
|
||||
// a / inv_b collapse to [1, C, 1, 1], x and add stay 2D
|
||||
const bool shape_ok = ggml_are_same_shape(a, inv_b) && a->ne[0] == 1 && a->ne[1] == x->ne[1];
|
||||
const bool dim_ok =
|
||||
(x->ne[2] == 1) && (x->ne[3] == 1) &&
|
||||
(add->ne[2] == 1) && (add->ne[3] == 1) &&
|
||||
(a->ne[2] == 1) && (a->ne[3] == 1) &&
|
||||
(inv_b->ne[2] == 1) && (inv_b->ne[3] == 1);
|
||||
// kernel reads x[idx] and a[c] / inv_b[c] linearly, so every operand is contiguous
|
||||
const bool contig_ok =
|
||||
ggml_is_contiguous(x) && ggml_is_contiguous(add) &&
|
||||
ggml_is_contiguous(a) && ggml_is_contiguous(inv_b);
|
||||
|
||||
return types_ok && shape_ok && dim_ok && contig_ok && x_in_add == x;
|
||||
}
|
||||
|
||||
int ggml_metal_op_bin(ggml_metal_op_t ctx, int idx) {
|
||||
if (ctx->use_fusion && ggml_metal_op_can_fuse_snake(ctx, idx)) {
|
||||
return ggml_metal_op_snake_fused(ctx, idx);
|
||||
}
|
||||
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
ggml_metal_library_t lib = ctx->lib;
|
||||
@@ -4035,55 +3984,6 @@ int ggml_metal_op_col2im_1d(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Dispatch the fused snake kernel from the matched mul -> sin -> sqr -> mul -> add chain.
|
||||
// idx points at the leading mul. The caller has validated the chain.
|
||||
int ggml_metal_op_snake_fused(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_metal_library_t lib = ctx->lib;
|
||||
ggml_metal_encoder_t enc = ctx->enc;
|
||||
|
||||
const ggml_tensor * mul0 = ctx->node(idx + 0);
|
||||
const ggml_tensor * sqr = ctx->node(idx + 2);
|
||||
const ggml_tensor * mul1 = ctx->node(idx + 3);
|
||||
ggml_tensor * add = ctx->node(idx + 4);
|
||||
|
||||
const ggml_tensor * x = ggml_are_same_shape(mul0, mul0->src[0]) ? mul0->src[0] : mul0->src[1];
|
||||
const ggml_tensor * a = (x == mul0->src[0]) ? mul0->src[1] : mul0->src[0];
|
||||
const ggml_tensor * inv_b = (mul1->src[0] == sqr) ? mul1->src[1] : mul1->src[0];
|
||||
|
||||
const int T = (int) x->ne[0];
|
||||
const int C = (int) x->ne[1];
|
||||
const int total = T * C;
|
||||
|
||||
// the encode loop pre-checked the leading mul only, check the rest of the chain
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
if (!ggml_metal_op_concurrency_check(ctx, ctx->node(idx + i))) {
|
||||
ggml_metal_op_concurrency_reset(ctx);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_snake(lib, x->type);
|
||||
|
||||
ggml_metal_kargs_snake args = {
|
||||
/*.T =*/ T,
|
||||
/*.C =*/ C,
|
||||
};
|
||||
|
||||
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(x), 1);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(a), 2);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(inv_b), 3);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(add), 4);
|
||||
|
||||
const int nth = 256;
|
||||
const int ntg = (total + nth - 1) / nth;
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, ntg, 1, 1, nth, 1, 1);
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
int ggml_metal_op_conv_transpose_2d(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ int ggml_metal_op_conv_3d (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_conv_transpose_1d (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_conv_transpose_2d (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_col2im_1d (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_snake_fused (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_upscale (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_pad (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_pad_reflect_1d (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -5406,35 +5406,6 @@ template [[host_name("kernel_col2im_1d_bf16")]] kernel void kernel_col2im_1d<bfl
|
||||
#endif
|
||||
|
||||
|
||||
template <typename T>
|
||||
kernel void kernel_snake(
|
||||
constant ggml_metal_kargs_snake & args,
|
||||
device const T * x,
|
||||
device const float * a,
|
||||
device const float * inv_b,
|
||||
device T * dst,
|
||||
uint tgpig [[threadgroup_position_in_grid]],
|
||||
uint tpitg [[thread_position_in_threadgroup]],
|
||||
uint ntg [[threads_per_threadgroup]]) {
|
||||
|
||||
const int idx = tgpig * ntg + tpitg;
|
||||
if (idx >= args.T * args.C) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int c = idx / args.T; // x is [T, C], a / inv_b collapse to [1, C]
|
||||
const float xi = float(x[idx]);
|
||||
const float si = sin(a[c] * xi);
|
||||
dst[idx] = T(xi + si * si * inv_b[c]);
|
||||
}
|
||||
|
||||
template [[host_name("kernel_snake_f32")]] kernel void kernel_snake<float>(constant ggml_metal_kargs_snake &, device const float *, device const float *, device const float *, device float *, uint, uint, uint);
|
||||
template [[host_name("kernel_snake_f16")]] kernel void kernel_snake<half>(constant ggml_metal_kargs_snake &, device const half *, device const float *, device const float *, device half *, uint, uint, uint);
|
||||
#if defined(GGML_METAL_HAS_BF16)
|
||||
template [[host_name("kernel_snake_bf16")]] kernel void kernel_snake<bfloat>(constant ggml_metal_kargs_snake &, device const bfloat *, device const float *, device const float *, device bfloat *, uint, uint, uint);
|
||||
#endif
|
||||
|
||||
|
||||
typedef void (conv_transpose_2d_t)(
|
||||
constant ggml_metal_kargs_conv_transpose_2d & args,
|
||||
device const float * src0,
|
||||
|
||||
@@ -114,7 +114,6 @@ enum GPU_FAMILY {
|
||||
|
||||
enum ADRENO_GPU_GEN {
|
||||
ADRENO_UNKNOWN,
|
||||
A6X,
|
||||
A7X,
|
||||
A8X,
|
||||
X1E,
|
||||
@@ -244,19 +243,6 @@ static ggml_cl_version get_opencl_c_version(ggml_cl_version platform_version, cl
|
||||
}
|
||||
|
||||
static ADRENO_GPU_GEN get_adreno_gpu_gen(const char *device_name) {
|
||||
if (strstr(device_name, "610") || strstr(device_name, "612") ||
|
||||
strstr(device_name, "613") || strstr(device_name, "615") ||
|
||||
strstr(device_name, "616") || strstr(device_name, "618") ||
|
||||
strstr(device_name, "619") || strstr(device_name, "620") ||
|
||||
strstr(device_name, "630") || strstr(device_name, "640") ||
|
||||
strstr(device_name, "642") || strstr(device_name, "643") ||
|
||||
strstr(device_name, "644") || strstr(device_name, "650") ||
|
||||
strstr(device_name, "660") || strstr(device_name, "663") ||
|
||||
strstr(device_name, "680") || strstr(device_name, "685") ||
|
||||
strstr(device_name, "690")) {
|
||||
return ADRENO_GPU_GEN::A6X;
|
||||
}
|
||||
|
||||
if (strstr(device_name, "730") ||
|
||||
strstr(device_name, "740") ||
|
||||
strstr(device_name, "750")) {
|
||||
@@ -546,7 +532,6 @@ struct ggml_backend_opencl_context {
|
||||
bool fp16_support;
|
||||
bool has_vector_subgroup_broadcast;
|
||||
bool has_subgroup_shuffle = false; // cl_khr_subgroup_shuffle or cl_qcom_subgroup_shuffle
|
||||
bool has_integer_dot = false; // cl_khr_integer_dot_product or cl_qcom_dot_product8
|
||||
bool has_qcom_subgroup_shuffle = false; // specifically cl_qcom_subgroup_shuffle
|
||||
bool disable_fusion;
|
||||
|
||||
@@ -849,7 +834,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemv_moe_q5_1_f32_ns, kernel_gemm_moe_q5_1_f32_ns;
|
||||
cl_kernel kernel_gemv_moe_q4_k_f32_ns, kernel_gemm_moe_q4_k_f32_ns, kernel_gemm_moe_q4_k_f32_ns_bin;
|
||||
cl_kernel kernel_gemv_moe_q4_k_f32_ns_wimg = nullptr; // weight-as-texture MoE decode GEMV (opt-in)
|
||||
cl_kernel kernel_gemm_moe_q4_k_q8_1_dp4a = nullptr; // dp4a (int8) prefill GEMM variant
|
||||
cl_kernel kernel_gemm_moe_q4_k_q8_1_dp4a; // dp4a (int8) prefill GEMM variant
|
||||
cl_kernel kernel_moe_reorder_quant_a_q8_1; // fused reorder + q8_1 quant for the dp4a GEMM
|
||||
cl_kernel kernel_gemm_moe_q8_1_dp4a_q80 = nullptr; // generic dp4a MoE GEMM (MOE_QT=80), opt-in
|
||||
cl_kernel kernel_moe_expand_scale_q8_0 = nullptr; // q8_0 per-block d -> uniform scale[16]
|
||||
@@ -859,12 +844,12 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_moe_expand_scale_q5_K = nullptr; // q5_K 6-bit s[] -> uniform scale[16]/min[8]
|
||||
cl_kernel kernel_gemv_moe_q5_k_f32_ns, kernel_gemm_moe_q5_k_f32_ns;
|
||||
cl_kernel kernel_gemv_moe_q6_k_f32_ns, kernel_gemm_moe_q6_k_f32_ns;
|
||||
cl_kernel kernel_gemm_moe_q6_k_q8_1_dp4a = nullptr; // dp4a (int8) q6_K MoE prefill GEMM
|
||||
cl_kernel kernel_gemm_moe_q6_k_q8_1_dp4a; // dp4a (int8) q6_K MoE prefill GEMM
|
||||
cl_kernel kernel_gemv_moe_mxfp4_f32, kernel_gemm_moe_mxfp4_f32;
|
||||
cl_kernel kernel_gemv_moe_mxfp4_f32_ns, kernel_gemm_moe_mxfp4_f32_ns, kernel_gemm_moe_mxfp4_f32_ns_bin;
|
||||
cl_kernel kernel_gemv_moe_mxfp4_f32_ns_wimg = nullptr; // weight-as-texture MoE decode GEMV
|
||||
cl_kernel kernel_gemm_moe_mxfp4_q8_1_dp4a = nullptr; // dp4a (int8) mxfp4 MoE prefill GEMM
|
||||
cl_kernel kernel_gemm_moe_q4_0_q8_1_dp4a = nullptr; // dp4a (int8) q4_0 MoE prefill GEMM
|
||||
cl_kernel kernel_gemm_moe_mxfp4_q8_1_dp4a; // dp4a (int8) mxfp4 MoE prefill GEMM
|
||||
cl_kernel kernel_gemm_moe_q4_0_q8_1_dp4a; // dp4a (int8) q4_0 MoE prefill GEMM
|
||||
cl_kernel kernel_moe_reorder_b;
|
||||
cl_kernel kernel_moe_histogram, kernel_moe_scan, kernel_moe_fill, kernel_moe_scatter;
|
||||
cl_kernel kernel_moe_combine_f32 = nullptr; // fused router-weight mul + cross-expert sum
|
||||
@@ -1052,10 +1037,10 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemv_noshuffle_q1_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_f32;
|
||||
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_wimg = nullptr; // dp4a dense prefill GEMM, weights via texture (X1 opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q5_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q5_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q6_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q6_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a; // dp4a (int8) dense prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg; // dp4a dense prefill GEMM, weights via texture (X1 opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q5_k_q8_1_dp4a; // dp4a (int8) dense q5_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q6_k_q8_1_dp4a; // dp4a (int8) dense q6_K prefill GEMM
|
||||
cl_kernel kernel_quant_a_q8_1; // plain activation q8_1 pre-pass
|
||||
cl_kernel kernel_gemv_noshuffle_q6_K_f32;
|
||||
cl_kernel kernel_gemm_noshuffle_q6_K_f32;
|
||||
@@ -3505,7 +3490,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q5_0_q8_1_dp4a (dp4a dense q5_0 prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q5_0_q8_1_dp4a.cl.h"
|
||||
@@ -3595,7 +3580,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_iq4_nl_q8_1_dp4a (dp4a dense IQ4_NL prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_iq4_nl_q8_1_dp4a.cl.h"
|
||||
@@ -3610,7 +3595,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q4_0_q8_1_dp4a (dp4a dense q4_0 prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q4_0_q8_1_dp4a.cl.h"
|
||||
@@ -3723,7 +3708,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q4_k_q8_1_dp4a (dp4a dense prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q4_k_q8_1_dp4a.cl.h"
|
||||
@@ -3745,7 +3730,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q8_0_q8_1_dp4a (dp4a dense q8_0 prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q8_0_q8_1_dp4a.cl.h"
|
||||
@@ -3761,7 +3746,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q5_k_q8_1_dp4a (dp4a dense prefill GEMM for q5_K)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q5_k_q8_1_dp4a.cl.h"
|
||||
@@ -3776,7 +3761,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q6_k_q8_1_dp4a (dp4a dense prefill GEMM for q6_K ffn_down/output)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q6_k_q8_1_dp4a.cl.h"
|
||||
@@ -4106,7 +4091,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_moe_q4_k_q8_1_dp4a (dp4a prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_moe_q4_k_q8_1_dp4a.cl.h"
|
||||
@@ -4123,7 +4108,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_moe_mxfp4_q8_1_dp4a (dp4a prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_moe_mxfp4_q8_1_dp4a.cl.h"
|
||||
@@ -4140,7 +4125,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_moe_q4_0_q8_1_dp4a (dp4a prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_moe_q4_0_q8_1_dp4a.cl.h"
|
||||
@@ -4157,7 +4142,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_moe_q8_1_dp4a (generic dp4a MoE GEMM; MOE_QT=80 -> q8_0 expert variant)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_moe_q8_1_dp4a.cl.h"
|
||||
@@ -4271,7 +4256,7 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
}
|
||||
|
||||
// gemm_moe_q6_k_q8_1_dp4a (dp4a q6_K MoE prefill GEMM)
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_moe_q6_k_q8_1_dp4a.cl.h"
|
||||
@@ -5617,8 +5602,6 @@ static void ggml_opencl_print_backend_info(ggml_backend_opencl_device_context *
|
||||
backend_ctx->has_subgroup_shuffle ? "true" : "false");
|
||||
GGML_LOG_INFO("ggml_opencl: device FP16 support: %s\n",
|
||||
backend_ctx->fp16_support ? "true" : "false");
|
||||
GGML_LOG_INFO("ggml_opencl: khr dot product support: %s\n",
|
||||
backend_ctx->has_integer_dot ? "true" : "false");
|
||||
GGML_LOG_INFO("ggml_opencl: mem base addr align: %u\n",
|
||||
backend_ctx->alignment);
|
||||
GGML_LOG_INFO("ggml_opencl: global mem size: %zu MB\n",
|
||||
@@ -5827,12 +5810,6 @@ static ggml_backend_opencl_context * ggml_cl_init(ggml_backend_dev_t dev) {
|
||||
strstr(ext_buffer, "cl_khr_subgroup_shuffle") != NULL ||
|
||||
backend_ctx->has_qcom_subgroup_shuffle;
|
||||
|
||||
// check for cl_khr_integer_dot_product
|
||||
// cl_qcom_dot_product8 uses signed * unsigned
|
||||
// while cl_khr_integer_dot_product uses signed * signed -- we stick with khr for now
|
||||
backend_ctx->has_integer_dot =
|
||||
strstr(ext_buffer, "cl_khr_integer_dot_product") != NULL;
|
||||
|
||||
cl_uint base_align_in_bits;
|
||||
CL_CHECK(clGetDeviceInfo(device, CL_DEVICE_MEM_BASE_ADDR_ALIGN, sizeof(cl_uint), &base_align_in_bits, NULL));
|
||||
GGML_ASSERT(base_align_in_bits % 8u == 0);
|
||||
@@ -6947,16 +6924,7 @@ inline bool use_adreno_kernels(const ggml_backend_opencl_context *backend_ctx, c
|
||||
}
|
||||
|
||||
inline bool use_adreno_moe_kernels(const ggml_backend_opencl_context *backend_ctx, const ggml_tensor *tensor) {
|
||||
// The moe weight repack kernels *_trans4_ns alias a private ushort8 through a uchar*.
|
||||
// Certain compilers (found with some A7x and A6x) miscompiles this, corrupting the weights.
|
||||
// So, exclude A6x and A7x from using Adreno MoE kernels for now.
|
||||
// The quants that have a general mul_mat_id kernel fallback to the general version; the
|
||||
// rest fallback to CPU.
|
||||
if (backend_ctx && (backend_ctx->adreno_gen == ADRENO_GPU_GEN::A6X ||
|
||||
backend_ctx->adreno_gen == ADRENO_GPU_GEN::A7X ||
|
||||
backend_ctx->adreno_gen == ADRENO_GPU_GEN::ADRENO_UNKNOWN)) {
|
||||
return false;
|
||||
}
|
||||
GGML_UNUSED(backend_ctx);
|
||||
int ne01 = tensor->ne[1];
|
||||
return (((strstr(tensor->name, "ffn") != NULL) && (strstr(tensor->name, "exps") != NULL)) || (strstr(tensor->name, "as") != NULL)) && (ne01 % 32 == 0);
|
||||
}
|
||||
@@ -7342,14 +7310,6 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some compilers for A7x (Adreno 740, compiler E031.41) crashes when
|
||||
// building FA kernels with mixed or quant types (f32_f16, f32_q8_0, f32_q4_0)
|
||||
// Here we skip all A7x for these kernels to avoid crash
|
||||
if (backend_ctx->adreno_gen == ADRENO_GPU_GEN::A7X &&
|
||||
(is_f32_f16 || is_f32_q8_0 || is_f32_q4_0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dk == 512) {
|
||||
if (backend_ctx->gpu_family == INTEL) {
|
||||
return false;
|
||||
@@ -10556,16 +10516,10 @@ static ggml_backend_buffer_t ggml_backend_opencl_buffer_type_alloc_buffer(ggml_b
|
||||
|
||||
cl_int err;
|
||||
cl_mem mem = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, size, NULL, &err);
|
||||
#if GGML_OPENCL_TARGET_VERSION >= 300
|
||||
// clCreateBufferWithProperties and cl_mem_properties are OpenCL 3.0. Drivers older than
|
||||
// that do not export the symbol, so a build targeting them fails to link. The large
|
||||
// buffer extension is only ever enabled on drivers that are well past 3.0, so this path
|
||||
// is dead there anyway.
|
||||
if (err != CL_SUCCESS && backend_ctx->adreno_use_large_buffer) {
|
||||
cl_mem_properties props[] = { 0x41A6 /* CL_LARGE_BUFFER_QCOM */, 1, 0 };
|
||||
mem = clCreateBufferWithProperties(backend_ctx->context, props, CL_MEM_READ_WRITE, size, NULL, &err);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (err != CL_SUCCESS) {
|
||||
GGML_LOG_INFO("%s: failed to allocate %.2f MiB\n", __func__, size / 1024.0 / 1024.0);
|
||||
@@ -15865,14 +15819,18 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
} else {
|
||||
// dp4a (int8) dense prefill GEMM, default off
|
||||
// dp4a (int8) dense prefill GEMM: quant activations to q8_1, then the int8
|
||||
// dp4a inner-loop GEMM, in place of the transpose + f16 half-dot kernel.
|
||||
// q4_0 = d*(q-8); mirrors the IQ4_NL/q8_0 dense dp4a paths (+ the sum term).
|
||||
// OPT-IN / DEFAULT OFF: correct, but neutral on X2E. q4_0's dequant
|
||||
// ((q-8)*scale) is already trivial so the f16 GEMM is weight-BW-bound and the
|
||||
// int8 ALU win has nothing to beat -- same as q5_0 dense (unlike IQ4_NL, whose
|
||||
// codebook dequant is expensive enough for dp4a to help). Kept for A/B; force
|
||||
// on with GGML_OPENCL_Q4_0_DENSE_DP4A=1. Needs N>8, K%32==0, M%64==0.
|
||||
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
|
||||
const bool q4_0_dense_dp4a_on = q4_0_dense_dp4a_env
|
||||
? (atoi(q4_0_dense_dp4a_env) != 0)
|
||||
: false;
|
||||
// dot prod has to be available
|
||||
q4_0_dense_dp4a_on = backend_ctx->has_integer_dot && q4_0_dense_dp4a_on;
|
||||
|
||||
if (q4_0_dense_dp4a_on && backend_ctx->kernel_gemm_noshuffle_q4_0_q8_1_dp4a
|
||||
&& N > 8 && (K % 32 == 0) && (M % 64 == 0)) {
|
||||
cl_mem a_sub = nullptr;
|
||||
@@ -16295,16 +16253,27 @@ static void ggml_cl_mul_mat_q5_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
} else {
|
||||
// dp4a (int8) dense q5_0 prefill GEMM, default off
|
||||
// dp4a (int8) dense q5_0 prefill GEMM. Quantizes the [N,K] activations to
|
||||
// q8_1 and runs the int8 dot instead of the f16 half-dot. Large-batch
|
||||
// (ne1>8) only. q5_0 weight = (x-16)*d (x = nibble | hi<<4); x packed as a
|
||||
// 0..31 byte (dp4a), the -16 centering folded into a single min term
|
||||
// (d*16) via the q8_1 block sum. Reads the qs/qh/d buffers byte-identically
|
||||
// to the f16 kernel (greedy byte-identical, MUL_MAT NMSE-OK).
|
||||
//
|
||||
// OPT-IN / DEFAULT OFF. Unlike q8_0/q4_K dense, dp4a is not a win for q5_0 on
|
||||
// X2E: the q5_0 model is bottlenecked elsewhere, so the dense-GEMM int8 win
|
||||
// has nothing to surface and the q8_1 prepass slightly hurts. Kept correct +
|
||||
// opt-in for the X1 A/B (different texture-cache dynamic) and the
|
||||
// weight-texture variant. Env: GGML_OPENCL_Q5_DENSE_DP4A=1.
|
||||
// Weight-as-texture variant (X1 lever): routes the dominant qs nibble plane
|
||||
// through an image1d_buffer (qh stays a buffer). Opt-in
|
||||
// GGML_OPENCL_Q5_DENSE_DP4A_WIMG; when set it also forces the dp4a path on.
|
||||
static const char * q5_dense_dp4a_env = getenv("GGML_OPENCL_Q5_DENSE_DP4A");
|
||||
static const char * q5_dense_wimg_env = getenv("GGML_OPENCL_Q5_DENSE_DP4A_WIMG");
|
||||
const bool q5_dense_wimg_on = q5_dense_wimg_env && (atoi(q5_dense_wimg_env) != 0);
|
||||
bool q5_dense_dp4a_on = q5_dense_wimg_on
|
||||
const bool q5_dense_dp4a_on = q5_dense_wimg_on
|
||||
? true
|
||||
: (q5_dense_dp4a_env && (atoi(q5_dense_dp4a_env) != 0));
|
||||
// dot prod has to be available
|
||||
q5_dense_dp4a_on = backend_ctx->has_integer_dot && q5_dense_dp4a_on;
|
||||
|
||||
if (q5_dense_dp4a_on && backend_ctx->kernel_gemm_noshuffle_q5_0_q8_1_dp4a
|
||||
&& N > 8 && (K % 32 == 0) && (M % 64 == 0)) {
|
||||
cl_mem a_sub = nullptr;
|
||||
@@ -16739,14 +16708,15 @@ static void ggml_cl_mul_mat_iq4_nl_f32_adreno(ggml_backend_t backend, const ggml
|
||||
} else {
|
||||
// dp4a (int8) dense IQ4_NL prefill GEMM. Quantizes the [N,K] activations to
|
||||
// q8_1 and runs the int8 dot instead of the f16 half-dot. Large-batch
|
||||
// (ne1>8) only
|
||||
// (ne1>8) only. IQ4_NL weight = kvalues[nibble]*d; the codebook value IS the
|
||||
// int8 (no min term), so this is the q8_0 dense case plus a nibble->int8 LUT
|
||||
// unpack. Reads the q/d buffers byte-identically to the f16 kernel. No bin
|
||||
// kernel for IQ4_NL -> baseline is f16, default ON for X2E (like q4_K/q6_K
|
||||
// dense dp4a). X1 stays on f16. Env: GGML_OPENCL_IQ4NL_DENSE_DP4A.
|
||||
static const char * iq4nl_dense_dp4a_env = getenv("GGML_OPENCL_IQ4NL_DENSE_DP4A");
|
||||
bool iq4nl_dense_dp4a_on = iq4nl_dense_dp4a_env
|
||||
const bool iq4nl_dense_dp4a_on = iq4nl_dense_dp4a_env
|
||||
? (atoi(iq4nl_dense_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
// dot prod has to be available
|
||||
iq4nl_dense_dp4a_on = backend_ctx->has_integer_dot && iq4nl_dense_dp4a_on;
|
||||
|
||||
if (iq4nl_dense_dp4a_on && backend_ctx->kernel_gemm_noshuffle_iq4_nl_q8_1_dp4a
|
||||
&& N > 8 && (K % 32 == 0) && (M % 64 == 0)) {
|
||||
cl_mem a_sub = nullptr;
|
||||
@@ -16996,15 +16966,13 @@ static void ggml_cl_mul_mat_q8_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
static const char * q8_dense_wimg_env = getenv("GGML_OPENCL_Q8_DENSE_DP4A_WIMG");
|
||||
const bool q8_dense_wimg_on = q8_dense_wimg_env && (atoi(q8_dense_wimg_env) != 0);
|
||||
|
||||
const bool q8_bin_loaded = (backend_ctx->kernel_gemm_noshuffle_q8_0_f32_bin != nullptr);
|
||||
const bool q8_bin_loaded = (backend_ctx->kernel_gemm_noshuffle_q8_0_f32_bin != nullptr);
|
||||
// bin kernel takes precedence
|
||||
bool q8_dense_dp4a_on = q8_dense_wimg_on
|
||||
const bool q8_dense_dp4a_on = q8_dense_wimg_on
|
||||
? true
|
||||
: q8_dense_dp4a_env
|
||||
? (atoi(q8_dense_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E && !q8_bin_loaded);
|
||||
// dot prod has to be available
|
||||
q8_dense_dp4a_on = backend_ctx->has_integer_dot && q8_dense_dp4a_on;
|
||||
|
||||
if (q8_dense_dp4a_on && backend_ctx->kernel_gemm_noshuffle_q8_0_q8_1_dp4a
|
||||
&& N > 8 && (K % 32 == 0) && (M % 64 == 0)) {
|
||||
@@ -17411,16 +17379,13 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
static const char * q4k_dense_dp4a_env = getenv("GGML_OPENCL_Q4K_DENSE_DP4A");
|
||||
static const char * q4k_dense_wimg_env = getenv("GGML_OPENCL_Q4K_DENSE_DP4A_WIMG");
|
||||
|
||||
const bool q4k_dense_wimg_on = q4k_dense_wimg_env && (atoi(q4k_dense_wimg_env) != 0);
|
||||
bool q4k_dense_dp4a_on = q4k_dense_wimg_on
|
||||
const bool q4k_dense_wimg_on = q4k_dense_wimg_env && (atoi(q4k_dense_wimg_env) != 0);
|
||||
const bool q4k_dense_dp4a_on = q4k_dense_wimg_on
|
||||
? true
|
||||
: q4k_dense_dp4a_env
|
||||
? (atoi(q4k_dense_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
|
||||
// dp4 has to be available
|
||||
q4k_dense_dp4a_on = backend_ctx->has_integer_dot && q4k_dense_dp4a_on;
|
||||
|
||||
// Min N for the dp4a prefill GEMM, default 9, i.e., ne1 > 8
|
||||
static const char * q4k_dp4a_minn_env = getenv("GGML_OPENCL_Q4K_DP4A_MINN");
|
||||
const int q4k_dp4a_minn = q4k_dp4a_minn_env ? atoi(q4k_dp4a_minn_env) : 9;
|
||||
@@ -17643,11 +17608,9 @@ static void ggml_cl_mul_mat_q6_K_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
|
||||
// dp4a (int8) dense q6_K prefill GEMM
|
||||
static const char * q6k_dense_dp4a_env = getenv("GGML_OPENCL_Q6K_DENSE_DP4A");
|
||||
bool q6k_dense_dp4a_on = (q6k_dense_dp4a_env != nullptr)
|
||||
static const bool q6k_dense_dp4a_on = (q6k_dense_dp4a_env != nullptr)
|
||||
? (atoi(q6k_dense_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen != ADRENO_GPU_GEN::X1E);
|
||||
// dot prod has to be available
|
||||
q6k_dense_dp4a_on = backend_ctx->has_integer_dot && q6k_dense_dp4a_on;
|
||||
|
||||
const bool is_output_w_dp4a = strncmp(src0->name, "output", 6) == 0 ||
|
||||
strncmp(src0->name, "token_embd", 10) == 0;
|
||||
@@ -17938,11 +17901,9 @@ static void ggml_cl_mul_mat_q5_K_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
|
||||
// dp4a (int8) dense q5_K prefill GEMM
|
||||
static const char * q5k_dense_dp4a_env = getenv("GGML_OPENCL_Q5K_DENSE_DP4A");
|
||||
bool q5k_dense_dp4a_on = q5k_dense_dp4a_env
|
||||
const bool q5k_dense_dp4a_on = q5k_dense_dp4a_env
|
||||
? (atoi(q5k_dense_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
// dot prod has to be available
|
||||
q5k_dense_dp4a_on = backend_ctx->has_integer_dot && q5k_dense_dp4a_on;
|
||||
|
||||
if (q5k_dense_dp4a_on && ne1 > 8 && (ne00 % 32 == 0) && (ne01 % 64 == 0)) {
|
||||
const int Mm = ne01, Nn = ne1, Kk = ne00;
|
||||
@@ -20679,8 +20640,6 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
bool use_moe_dp4a = q4_0_moe_dp4a_env
|
||||
? (atoi(q4_0_moe_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
// dot prod has to be available
|
||||
use_moe_dp4a = backend_ctx->has_integer_dot && use_moe_dp4a;
|
||||
// bin kernel takes precedence
|
||||
use_moe_dp4a = use_moe_dp4a && backend_ctx->kernel_gemm_moe_q4_0_f32_ns_bin == nullptr;
|
||||
|
||||
@@ -21856,8 +21815,6 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
bool use_moe_dp4a = (q4k_moe_dp4a_env != nullptr)
|
||||
? (atoi(q4k_moe_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E || backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E);
|
||||
// dot prod has to be available
|
||||
use_moe_dp4a = backend_ctx->has_integer_dot && use_moe_dp4a;
|
||||
// bin kernel takes precedence
|
||||
use_moe_dp4a = use_moe_dp4a && backend_ctx->kernel_gemm_moe_q4_k_f32_ns_bin == nullptr;
|
||||
|
||||
@@ -22359,12 +22316,10 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
|
||||
// dp4a (int8) q6_K MoE prefill GEMM
|
||||
static const char * q6k_moe_dp4a_env = getenv("GGML_OPENCL_Q6K_MOE_DP4A");
|
||||
bool use_moe_dp4a = (q6k_moe_dp4a_env != nullptr)
|
||||
static const bool use_moe_dp4a = (q6k_moe_dp4a_env != nullptr)
|
||||
? (atoi(q6k_moe_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E
|
||||
|| backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E);
|
||||
// dot prod has to be available
|
||||
use_moe_dp4a = backend_ctx->has_integer_dot && use_moe_dp4a;
|
||||
|
||||
cl_buffer_region region;
|
||||
region.origin = 0;
|
||||
@@ -22614,8 +22569,6 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
bool use_moe_dp4a = mxfp4_moe_dp4a_env
|
||||
? (atoi(mxfp4_moe_dp4a_env) != 0)
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
// dot prod has to be available
|
||||
use_moe_dp4a = backend_ctx->has_integer_dot && use_moe_dp4a;
|
||||
// bin kernel takes precedence
|
||||
use_moe_dp4a = use_moe_dp4a && backend_ctx->kernel_gemm_moe_mxfp4_f32_ns_bin == nullptr;
|
||||
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
#elif defined(cl_qcom_subgroup_shuffle)
|
||||
#pragma OPENCL EXTENSION cl_qcom_subgroup_shuffle : enable
|
||||
#define HAS_SUBGROUP_SHUFFLE 1
|
||||
// Adreno compilers that expose only cl_qcom_subgroup_shuffle do not declare the KHR
|
||||
// name, so calling it is an implicit declaration and the program fails to build.
|
||||
// Route it to the qcom builtin.
|
||||
#define sub_group_shuffle_xor(val, mask) qcom_sub_group_shuffle_xor((val), (mask), CLK_SUB_GROUP_SHUFFLE_WIDTH_WAVE_SIZE_QCOM, 0.0f)
|
||||
#endif
|
||||
|
||||
#define ACC_TYPE float
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#elif defined(cl_qcom_subgroup_shuffle)
|
||||
#pragma OPENCL EXTENSION cl_qcom_subgroup_shuffle : enable
|
||||
#define HAS_SUBGROUP_SHUFFLE 1
|
||||
// Adreno compilers that expose only cl_qcom_subgroup_shuffle do not declare the KHR
|
||||
// name, so calling it is an implicit declaration and the program fails to build.
|
||||
// Route it to the qcom builtin.
|
||||
#define sub_group_shuffle_xor(val, mask) qcom_sub_group_shuffle_xor((val), (mask), CLK_SUB_GROUP_SHUFFLE_WIDTH_WAVE_SIZE_QCOM, 0.0f)
|
||||
#endif
|
||||
|
||||
// Flash attention: Q=f32, K=q4_0, V=q4_0.
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#elif defined(cl_qcom_subgroup_shuffle)
|
||||
#pragma OPENCL EXTENSION cl_qcom_subgroup_shuffle : enable
|
||||
#define HAS_SUBGROUP_SHUFFLE 1
|
||||
// Adreno compilers that expose only cl_qcom_subgroup_shuffle do not declare the KHR
|
||||
// name, so calling it is an implicit declaration and the program fails to build.
|
||||
// Route it to the qcom builtin.
|
||||
#define sub_group_shuffle_xor(val, mask) qcom_sub_group_shuffle_xor((val), (mask), CLK_SUB_GROUP_SHUFFLE_WIDTH_WAVE_SIZE_QCOM, 0.0f)
|
||||
#endif
|
||||
|
||||
// Flash attention: Q=f32, K=q8_0, V=q8_0.
|
||||
|
||||
@@ -296,12 +296,7 @@ kernel void kernel_gemv_noshuffle_iq4_nl_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,10 +116,6 @@ __kernel void kernel_gemv_noshuffle_q1_0_f32(
|
||||
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the output row. The x-grid is padded to CEIL_DIV(M,wavesize)*wavesize,
|
||||
// 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;
|
||||
dst[gid] = totalSum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,12 +268,7 @@ __kernel void kernel_gemv_noshuffle_q4_0_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -262,11 +262,7 @@ __kernel void kernel_gemv_noshuffle_q4_0_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows against the padded x-grid tail overrunning dst.
|
||||
// The current shape specializations are all ne01 % 128 == 0 (no padding), so
|
||||
// this is a no-op / byte-identical today; keep it in lockstep with the base kernel.
|
||||
if (gid * 2 + 0 < ne01) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < ne01) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -277,12 +277,7 @@ kernel void kernel_gemv_noshuffle_q4_1_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -312,12 +312,7 @@ kernel void kernel_gemv_noshuffle_q4_k_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -285,12 +285,7 @@ __kernel void kernel_gemv_noshuffle_q5_0_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -288,12 +288,7 @@ __kernel void kernel_gemv_noshuffle_q5_1_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -321,11 +321,6 @@ kernel void kernel_gemv_noshuffle_q5_k_f32(
|
||||
// 2 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor. No-op / byte-identical when
|
||||
// ne01 % 128 == 0 (M/2 already a multiple of 64 -> no padding).
|
||||
if (gid * 2 + 0 < M) dst[gid * 2 + 0] = totalSum.s0;
|
||||
if (gid * 2 + 1 < M) dst[gid * 2 + 1] = totalSum.s1;
|
||||
vstore2(totalSum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,11 +288,6 @@ kernel void kernel_gemv_noshuffle_q6_K_f32(
|
||||
|
||||
if (grp == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the two output rows. The x-grid is padded to CEIL_DIV(ne01/2,64)*64,
|
||||
// so when ne01 is not a multiple of 128 the tail row-pairs run past row ne01
|
||||
// and would overrun dst into the adjacent tensor (garbage downstream).
|
||||
// No-op / byte-identical when ne01 % 128 == 0 (no padding).
|
||||
if (gid * 2 + 0 < ne01) dst[gid * 2 + 0] = total_sum.s0;
|
||||
if (gid * 2 + 1 < ne01) dst[gid * 2 + 1] = total_sum.s1;
|
||||
vstore2(total_sum, 0, &(dst[gid * 2]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,10 +190,6 @@ __kernel void kernel_gemv_noshuffle_q8_0_f32(
|
||||
// 1 outputs per fiber in wave 0
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
// Guard the output row. The x-grid is padded to CEIL_DIV(M,wavesize)*wavesize,
|
||||
// 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;
|
||||
dst[gid] = totalSum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,14 +64,7 @@ kernel void kernel_mul_mat_f16_f16(
|
||||
|
||||
global half * x = (global half *) (src0 + offset_src0);
|
||||
|
||||
// The vector path below casts the row pointers to half4, which must be 8-byte aligned.
|
||||
// A row address is r0*nb01 + ..., and a permuted or strided src leaves nb01/nb11
|
||||
// unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned. Every
|
||||
// src1 row this work-item walks is src1_base + r1*nb11, so require both.
|
||||
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
|
||||
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 7) == 0 && (nb11 & 7) == 0;
|
||||
|
||||
if (ne00 < 128 || !row_aligned) {
|
||||
if (ne00 < 128) {
|
||||
for (int row = 0; row < N_F16_F16; ++row) {
|
||||
int r1 = rb + row;
|
||||
if (r1 >= ne11) {
|
||||
|
||||
@@ -64,14 +64,7 @@ kernel void kernel_mul_mat_f16_f32(
|
||||
|
||||
global half * x = (global half *) (src0 + offset_src0);
|
||||
|
||||
// The vector path below casts the row pointers to half4/float4, which must be 8- and
|
||||
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
|
||||
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
|
||||
// Every src1 row this work-item walks is src1_base + r1*nb11, so require both.
|
||||
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
|
||||
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 15) == 0 && (nb11 & 15) == 0;
|
||||
|
||||
if (ne00 < 128 || !row_aligned) {
|
||||
if (ne00 < 128) {
|
||||
for (int row = 0; row < N_F16_F32; ++row) {
|
||||
int r1 = rb + row;
|
||||
if (r1 >= ne11) {
|
||||
|
||||
@@ -64,15 +64,8 @@ kernel void kernel_mul_mat_f16_f32_1row(
|
||||
global half * x = (global half *) (src0 + offset_src0);
|
||||
global float * y = (global float *) (src1 + offset_src1);
|
||||
|
||||
// The vector path below casts the row pointers to half4/float4, which must be 8- and
|
||||
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
|
||||
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
|
||||
// Take the vector path only when the rows this work-item touches are actually aligned;
|
||||
// the scalar loop has no such requirement.
|
||||
const bool row_aligned = (((ulong) x) & 7) == 0 && (((ulong) y) & 15) == 0;
|
||||
|
||||
float sumf = 0;
|
||||
if (ne00 < 128 || !row_aligned) {
|
||||
if (ne00 < 128) {
|
||||
for (int i = get_sub_group_local_id(); i < ne00; i += get_max_sub_group_size()) {
|
||||
sumf += (float) x[i] * (float) y[i];
|
||||
}
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
#elif defined(cl_qcom_subgroup_shuffle)
|
||||
#pragma OPENCL EXTENSION cl_qcom_subgroup_shuffle : enable
|
||||
#define HAS_SUBGROUP_SHUFFLE 1
|
||||
// Adreno compilers that expose only cl_qcom_subgroup_shuffle do not declare the KHR
|
||||
// name, so calling it is an implicit declaration and the program fails to build.
|
||||
// Route it to the qcom builtin.
|
||||
#define sub_group_shuffle_xor(val, mask) qcom_sub_group_shuffle_xor((val), (mask), CLK_SUB_GROUP_SHUFFLE_WIDTH_WAVE_SIZE_QCOM, 0.0f)
|
||||
#endif
|
||||
|
||||
// Assumes row size (ne00) is a multiple of 4
|
||||
|
||||
@@ -64,7 +64,6 @@ extern int g_ggml_sycl_enable_fusion;
|
||||
extern int g_ggml_sycl_prioritize_dmmv;
|
||||
extern int g_ggml_sycl_enable_flash_attention;
|
||||
extern int g_ggml_sycl_dev2dev_memcpy;
|
||||
extern int g_ggml_sycl_fa_onednn;
|
||||
|
||||
|
||||
#if defined(__clang__) && __has_builtin(__builtin_expect)
|
||||
|
||||
@@ -71,8 +71,8 @@ struct dw_cwhn_layout {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename KernelT, typename Layout>
|
||||
static void conv2d_dw_kernel(const float * input, const KernelT * kernel, float * output,
|
||||
template <typename Layout>
|
||||
static void conv2d_dw_kernel(const float * input, const float * kernel, float * output,
|
||||
const conv2d_dw_params p, const sycl::nd_item<3> & item_ct1) {
|
||||
const int global_idx = item_ct1.get_local_id(2) +
|
||||
item_ct1.get_group(2) * item_ct1.get_local_range(2);
|
||||
@@ -93,15 +93,15 @@ static void conv2d_dw_kernel(const float * input, const KernelT * kernel, float
|
||||
for (int kx = bounds.x_min; kx < bounds.x_max; ++kx) {
|
||||
const int in_x = dw_calculate_input_coord(out_x, kx, p.stride_x, p.dilation_x, p.padding_x);
|
||||
acc += input[Layout::input_index(n, c, in_y, in_x, p)] *
|
||||
static_cast<float>(kernel[Layout::kernel_index(c, ky, kx, p)]);
|
||||
kernel[Layout::kernel_index(c, ky, kx, p)];
|
||||
}
|
||||
}
|
||||
|
||||
output[Layout::output_index(n, c, out_y, out_x, p)] = acc;
|
||||
}
|
||||
|
||||
template <typename KernelT, typename Layout>
|
||||
static void conv2d_dw_sycl(const float * x_d, const KernelT * w_d, float * y_d,
|
||||
template <typename Layout>
|
||||
static void conv2d_dw_sycl(const float * x_d, const float * w_d, float * y_d,
|
||||
const conv2d_dw_params p, const queue_ptr & stream) {
|
||||
const int total = p.batches * p.channels * p.out_h * p.out_w;
|
||||
const int num_blocks = (total + SYCL_CONV2D_DW_BLOCK_SIZE - 1) / SYCL_CONV2D_DW_BLOCK_SIZE;
|
||||
@@ -109,7 +109,7 @@ static void conv2d_dw_sycl(const float * x_d, const KernelT * w_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_dw_kernel<KernelT, Layout>(x_d, w_d, y_d, p, item_ct1);
|
||||
conv2d_dw_kernel<Layout>(x_d, w_d, y_d, p, item_ct1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,9 +119,9 @@ void ggml_sycl_op_conv2d_dw(ggml_backend_sycl_context & ctx, ggml_tensor * dst)
|
||||
const ggml_tensor * kernel = dst->src[0];
|
||||
const ggml_tensor * input = dst->src[1];
|
||||
|
||||
GGML_ASSERT((kernel->type == GGML_TYPE_F32 || kernel->type == GGML_TYPE_F16) &&
|
||||
input->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(kernel->type == GGML_TYPE_F32 && input->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32);
|
||||
|
||||
const float * w_d = (const float *) kernel->data;
|
||||
const float * x_d = (const float *) input->data;
|
||||
float * y_d = (float *) dst->data;
|
||||
|
||||
@@ -148,23 +148,11 @@ void ggml_sycl_op_conv2d_dw(ggml_backend_sycl_context & ctx, ggml_tensor * dst)
|
||||
|
||||
const queue_ptr stream = ctx.stream();
|
||||
|
||||
if (kernel->type == GGML_TYPE_F16) {
|
||||
const sycl::half * w_d = (const sycl::half *) kernel->data;
|
||||
if (ggml_is_contiguous(input)) {
|
||||
conv2d_dw_sycl<sycl::half, dw_whcn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else if (ggml_is_contiguous_channels(input)) {
|
||||
conv2d_dw_sycl<sycl::half, dw_cwhn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else {
|
||||
GGML_ABORT("Unsupported memory layout for conv2d_dw");
|
||||
}
|
||||
if (ggml_is_contiguous(input)) {
|
||||
conv2d_dw_sycl<dw_whcn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else if (ggml_is_contiguous_channels(input)) {
|
||||
conv2d_dw_sycl<dw_cwhn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else {
|
||||
const float * w_d = (const float *) kernel->data;
|
||||
if (ggml_is_contiguous(input)) {
|
||||
conv2d_dw_sycl<float, dw_whcn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else if (ggml_is_contiguous_channels(input)) {
|
||||
conv2d_dw_sycl<float, dw_cwhn_layout>(x_d, w_d, y_d, params, stream);
|
||||
} else {
|
||||
GGML_ABORT("Unsupported memory layout for conv2d_dw");
|
||||
}
|
||||
GGML_ABORT("Unsupported memory layout for conv2d_dw");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
typedef void (*dequantize_kernel_t)(const void * vx, const int64_t ib, const int iqs, dfloat2 & v);
|
||||
typedef void (*dequantize_kernel_t_reorder)(const void *d, const int64_t ib, const void *qs,
|
||||
const int iqs, dfloat2 &v);
|
||||
typedef void (*dequantize_kernel_f32_t)(const void * vx, const int64_t ib, const int iqs, float & v0, float & v1);
|
||||
|
||||
#if QK_K == 256
|
||||
static inline void get_scale_min_k4(int j, const uint8_t * q, uint8_t & d, uint8_t & m);
|
||||
@@ -86,21 +85,6 @@ static __dpct_inline__ void dequantize_q1_0_reorder(const void *d_ptr, const int
|
||||
v.y() = (2 * bit_1 - 1) * d;
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q1_0(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
const block_q1_0 * x = (const block_q1_0 *) vx;
|
||||
const dfloat d = x[ib].d;
|
||||
|
||||
const int bit_index_0 = iqs + 0;
|
||||
const int bit_index_1 = iqs + 1;
|
||||
|
||||
const int bit_0 = (x[ib].qs[bit_index_0 / 8] >> (bit_index_0 % 8)) & 1;
|
||||
const int bit_1 = (x[ib].qs[bit_index_1 / 8] >> (bit_index_1 % 8)) & 1;
|
||||
|
||||
v.x() = (2 * bit_0 - 1) * d;
|
||||
v.y() = (2 * bit_1 - 1) * d;
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q4_1(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
const block_q4_1 * x = (const block_q4_1 *) vx;
|
||||
@@ -156,39 +140,6 @@ static __dpct_inline__ void dequantize_q4_K(const void *vx, const int64_t ib,
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q4_K_f32(const void *vx, const int64_t ib,
|
||||
const int iqs, float &v0, float &v1) {
|
||||
#if QK_K == 256
|
||||
const block_q4_K * x = (const block_q4_K *) vx;
|
||||
const sycl::half2 dm = x[ib].dm;
|
||||
const float dall = dm[0];
|
||||
const float dmin = dm[1];
|
||||
|
||||
auto dequantize_one = [&](const int idx) -> float {
|
||||
const int il = idx / 64;
|
||||
const int in = idx % 64;
|
||||
const int is = 2 * il + (in >= 32 ? 1 : 0);
|
||||
const int qsi = 32 * il + (in & 31);
|
||||
|
||||
uint8_t sc;
|
||||
uint8_t m;
|
||||
get_scale_min_k4(is, x[ib].scales, sc, m);
|
||||
|
||||
const float d = dall * sc;
|
||||
const float mn = dmin * m;
|
||||
const uint8_t q = x[ib].qs[qsi];
|
||||
const uint8_t qv = (in >= 32) ? (q >> 4) : (q & 0xF);
|
||||
|
||||
return d * qv - mn;
|
||||
};
|
||||
|
||||
v0 = dequantize_one(iqs + 0);
|
||||
v1 = dequantize_one(iqs + 1);
|
||||
#else
|
||||
GGML_ABORT("Q4_K dequantize not supported for QK_K != 256");
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q2_K(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
#if QK_K == 256
|
||||
@@ -208,7 +159,7 @@ static __dpct_inline__ void dequantize_q2_K(const void *vx, const int64_t ib,
|
||||
const float d = dall * (sc & 0xF);
|
||||
const float m = dmin * (sc >> 4);
|
||||
|
||||
return (dfloat) d * (dfloat) ((q >> (2 * g)) & 3) - (dfloat) m;
|
||||
return sycl::fma((dfloat) ((q >> (2 * g)) & 3), (dfloat) d, (dfloat) (-m));
|
||||
};
|
||||
|
||||
v.x() = dequantize_one(iqs + 0);
|
||||
@@ -218,35 +169,6 @@ static __dpct_inline__ void dequantize_q2_K(const void *vx, const int64_t ib,
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q2_K_f32(const void *vx, const int64_t ib,
|
||||
const int iqs, float &v0, float &v1) {
|
||||
#if QK_K == 256
|
||||
const block_q2_K * x = (const block_q2_K *) vx;
|
||||
const float dall = x[ib].dm[0];
|
||||
const float dmin = x[ib].dm[1];
|
||||
|
||||
auto dequantize_one = [&](const int idx) -> float {
|
||||
const int n = idx / 128;
|
||||
const int r = idx % 128;
|
||||
const int g = r / 32;
|
||||
const int l = r % 32;
|
||||
const int is = 8 * n + l / 16;
|
||||
|
||||
const uint8_t q = x[ib].qs[32 * n + l];
|
||||
const uint8_t sc = x[ib].scales[is + 2 * g];
|
||||
const float d = dall * (sc & 0xF);
|
||||
const float m = dmin * (sc >> 4);
|
||||
|
||||
return d * ((q >> (2 * g)) & 3) - m;
|
||||
};
|
||||
|
||||
v0 = dequantize_one(iqs + 0);
|
||||
v1 = dequantize_one(iqs + 1);
|
||||
#else
|
||||
GGML_ABORT("Q2_K dequantize not supported for QK_K != 256");
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q3_K(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
#if QK_K == 256
|
||||
@@ -320,42 +242,6 @@ static __dpct_inline__ void dequantize_q5_K(const void *vx, const int64_t ib,
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q5_K_f32(const void *vx, const int64_t ib,
|
||||
const int iqs, float &v0, float &v1) {
|
||||
#if QK_K == 256
|
||||
const block_q5_K * x = (const block_q5_K *) vx;
|
||||
const float dall = x[ib].dm[0];
|
||||
const float dmin = x[ib].dm[1];
|
||||
|
||||
auto dequantize_one = [&](const int idx) -> float {
|
||||
const int il = idx / 64;
|
||||
const int in = idx % 64;
|
||||
const int is = 2 * il + (in >= 32 ? 1 : 0);
|
||||
const int ir = (in & 31) / 2;
|
||||
const int iq = in & 1;
|
||||
|
||||
const uint8_t q = x[ib].qs[32 * il + 2 * ir + iq];
|
||||
const uint8_t h = x[ib].qh[2 * ir + iq];
|
||||
const uint8_t qv = (in >= 32) ? (q >> 4) : (q & 0xF);
|
||||
|
||||
uint8_t sc;
|
||||
uint8_t m;
|
||||
get_scale_min_k4(is, x[ib].scales, sc, m);
|
||||
|
||||
const float d = dall * sc;
|
||||
const float mn = dmin * m;
|
||||
const uint8_t hm = 1 << (2 * il + (in >= 32 ? 1 : 0));
|
||||
|
||||
return (qv + ((h & hm) ? 16 : 0)) * d - mn;
|
||||
};
|
||||
|
||||
v0 = dequantize_one(iqs + 0);
|
||||
v1 = dequantize_one(iqs + 1);
|
||||
#else
|
||||
GGML_ABORT("Q5_K dequantize not supported for QK_K != 256");
|
||||
#endif
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q6_K(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
#if QK_K == 256
|
||||
@@ -410,6 +296,21 @@ static __dpct_inline__ void dequantize_mxfp4(const void *vx, const int64_t ib,
|
||||
v.y() = d * kvalues_mxfp4[q >> 4] * 0.5f;
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_q1_0(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
const block_q1_0 * x = (const block_q1_0 *) vx;
|
||||
const dfloat d = x[ib].d;
|
||||
|
||||
const int bit_index_0 = iqs + 0;
|
||||
const int bit_index_1 = iqs + 1;
|
||||
|
||||
const int bit_0 = (x[ib].qs[bit_index_0 / 8] >> (bit_index_0 % 8)) & 1;
|
||||
const int bit_1 = (x[ib].qs[bit_index_1 / 8] >> (bit_index_1 % 8)) & 1;
|
||||
|
||||
v.x() = (2 * bit_0 - 1) * d;
|
||||
v.y() = (2 * bit_1 - 1) * d;
|
||||
}
|
||||
|
||||
static __dpct_inline__ void dequantize_nvfp4(const void *vx, const int64_t ib,
|
||||
const int iqs, dfloat2 &v) {
|
||||
const block_nvfp4 & xb = ((const block_nvfp4 *) vx)[ib];
|
||||
|
||||
@@ -247,17 +247,6 @@ static __dpct_inline__ T op_leaky_relu(T x, float negative_slope) {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static __dpct_inline__ T op_xielu(T x, float alpha_n, float alpha_p, float beta, float eps) {
|
||||
const float xi = static_cast<float>(x);
|
||||
const float gate_pos = (xi > 0.0f);
|
||||
const float y_pos = alpha_p * xi * xi + beta * xi;
|
||||
const float min_v_eps = sycl::fmin(xi, eps);
|
||||
const float y_neg = (sycl::expm1(min_v_eps) - xi) * alpha_n + beta * xi;
|
||||
const float out = gate_pos * y_pos + (1.0f - gate_pos) * y_neg;
|
||||
return static_cast<T>(out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static __dpct_inline__ T op_sqr(T x) {
|
||||
return x * x;
|
||||
@@ -370,13 +359,6 @@ static void unary_op_leaky_relu_kernel(const T * x, T * dst, const int k, float
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void unary_op_xielu_kernel(const T * x, T * dst, const int k, float alpha_n, float alpha_p, float beta, float eps, const sycl::nd_item<1> &item_ct1) {
|
||||
SYCL_GLOBAL_ID_LOOP(k, item_ct1) {
|
||||
dst[i] = op_xielu(x[i], alpha_n, alpha_p, beta, eps);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void unary_op_sqr_kernel(const T * x, T * dst, const int k, const sycl::nd_item<1> &item_ct1) {
|
||||
SYCL_GLOBAL_ID_LOOP(k, item_ct1) {
|
||||
@@ -854,23 +836,6 @@ static inline void ggml_sycl_op_clamp(ggml_backend_sycl_context & ctx, ggml_tens
|
||||
}, min_val, max_val);
|
||||
}
|
||||
|
||||
static inline void ggml_sycl_op_xielu(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const float alpha_n = ggml_get_op_params_f32(dst, 1);
|
||||
const float alpha_p = ggml_get_op_params_f32(dst, 2);
|
||||
const float beta = ggml_get_op_params_f32(dst, 3);
|
||||
const float eps = ggml_get_op_params_f32(dst, 4);
|
||||
ggml_sycl_detail::dispatch_ggml_sycl_op_unary(ctx, dst,
|
||||
[](const auto* src, auto* dst_ptr, int k_elements, queue_ptr stream, float alpha_n_arg, float alpha_p_arg, float beta_arg, float eps_arg) {
|
||||
const int num_blocks = ceil_div(k_elements, SYCL_RELU_BLOCK_SIZE);
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<1>(sycl::range<1>(num_blocks) * sycl::range<1>(SYCL_RELU_BLOCK_SIZE),
|
||||
sycl::range<1>(SYCL_RELU_BLOCK_SIZE)),
|
||||
[=](sycl::nd_item<1> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
|
||||
unary_op_xielu_kernel(src, dst_ptr, k_elements, alpha_n_arg, alpha_p_arg, beta_arg, eps_arg, item_ct1);
|
||||
});
|
||||
}, alpha_n, alpha_p, beta, eps);
|
||||
}
|
||||
|
||||
static inline void ggml_sycl_op_floor(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
ggml_sycl_detail::ggml_sycl_op_unary(ctx, dst, [](auto x) {
|
||||
return op_floor(x);
|
||||
@@ -1188,11 +1153,6 @@ void ggml_sycl_clamp(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
ggml_sycl_op_clamp(ctx, dst);
|
||||
}
|
||||
|
||||
void ggml_sycl_xielu(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
|
||||
ggml_sycl_op_xielu(ctx, dst);
|
||||
}
|
||||
|
||||
void ggml_sycl_sgn(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/1);
|
||||
ggml_sycl_op_sgn(ctx, dst);
|
||||
|
||||
@@ -75,8 +75,6 @@ void ggml_sycl_sqr(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
void ggml_sycl_clamp(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
void ggml_sycl_xielu(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
void ggml_sycl_sgn(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
void ggml_sycl_abs(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "fattn-onednn.hpp"
|
||||
#include "fattn-tile.hpp"
|
||||
|
||||
// set minimum query length to treat as prefill (32)
|
||||
#define GGML_SYCL_FA_ONEDNN_MIN_Q 32
|
||||
|
||||
bool ggml_sycl_flash_attn_ext_onednn_supported(const ggml_tensor * dst) {
|
||||
#if !GGML_SYCL_DNNL
|
||||
GGML_UNUSED(dst);
|
||||
return false;
|
||||
#else
|
||||
if (!g_ggml_sycl_fa_onednn) {
|
||||
return false;
|
||||
}
|
||||
// Battlemage (Xe2) only, for now. On other Intel archs oneDNN's fused SDPA returns wrong results
|
||||
// for some shapes (e.g. head_dim=64 on Arc / xe_hpg) -- an oneDNN bug tracked upstream at
|
||||
// https://github.com/uxlfoundation/oneDNN/issues/5510. Remove this hardware limitation once that
|
||||
// is fixed; until then non-BMG archs fall back to the existing FA kernel.
|
||||
const gpu_arch arch = ggml_sycl_info().devices[ggml_sycl_get_device()].hw_info.arch;
|
||||
if (arch != gpu_arch::intel_gpu_bmg_g21 && arch != gpu_arch::intel_gpu_bmg_g31) {
|
||||
return false;
|
||||
}
|
||||
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 ggml_tensor * sinks = dst->src[4];
|
||||
|
||||
// gate for f16 KV only for now
|
||||
// need to implement quantized KV
|
||||
if (K->type != GGML_TYPE_F16 || V->type != GGML_TYPE_F16) {
|
||||
return false;
|
||||
}
|
||||
// gate for the following cases
|
||||
// 1. if the oneDNN graph Add node has no input --> skip
|
||||
// 2. types other than f16 need different logical_tensor declaration
|
||||
// 3. the mask must be shape [1, 1, q, seq]
|
||||
// 4. sinks: excludes attention sink (Xiao et al., 2024) that can't be modeled by oneDNN graph
|
||||
if (!mask || mask->type != GGML_TYPE_F16 || mask->ne[2] != 1 || mask->ne[3] != 1 || sinks) {
|
||||
return false;
|
||||
}
|
||||
float max_bias = 0.0f, logit_softcap = 0.0f;
|
||||
memcpy(&max_bias, (const float *) dst->op_params + 1, sizeof(float));
|
||||
memcpy(&logit_softcap, (const float *) dst->op_params + 2, sizeof(float));
|
||||
if (max_bias != 0.0f || logit_softcap != 0.0f) {
|
||||
return false;
|
||||
}
|
||||
// K and V must share head_dim: the SDPA graph uses a single `d` for both.
|
||||
const int64_t d = K->ne[0];
|
||||
if (V->ne[0] != d || Q->ne[3] != 1) {
|
||||
return false;
|
||||
}
|
||||
// GQA must divide evenly.
|
||||
if (K->ne[2] == 0 || Q->ne[2] % K->ne[2] != 0) {
|
||||
return false;
|
||||
}
|
||||
// Prefill only.
|
||||
if (Q->ne[1] < GGML_SYCL_FA_ONEDNN_MIN_Q) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GGML_SYCL_DNNL
|
||||
|
||||
#include "dnnl.hpp"
|
||||
#include "dnnl_sycl.hpp"
|
||||
#include "oneapi/dnnl/dnnl_graph.hpp" // graph API lives only under oneapi/dnnl/, not at the include root
|
||||
|
||||
using namespace dnnl;
|
||||
using namespace dnnl::graph;
|
||||
|
||||
// strided src (f16 or f32) -> contiguous f16 [ne0,ne1,ne2,ne3] (ne0 innermost). nb* are BYTE strides.
|
||||
template <typename src_t>
|
||||
static void cont_to_f16_sycl(const char * src, sycl::half * dst,
|
||||
int64_t ne0, int64_t ne1, int64_t ne2, int64_t ne3,
|
||||
size_t nb1, size_t nb2, size_t nb3, dpct::queue_ptr stream) {
|
||||
const int64_t n = ne0 * ne1 * ne2 * ne3;
|
||||
stream->parallel_for(sycl::range<1>(n), [=](sycl::id<1> ix) {
|
||||
const int64_t gid = ix[0];
|
||||
int64_t i = gid;
|
||||
const int64_t i0 = i % ne0; i /= ne0;
|
||||
const int64_t i1 = i % ne1; i /= ne1;
|
||||
const int64_t i2 = i % ne2; const int64_t i3 = i / ne2;
|
||||
const src_t * p = (const src_t *) (src + i1 * nb1 + i2 * nb2 + i3 * nb3) + i0;
|
||||
dst[gid] = (sycl::half) (*p);
|
||||
});
|
||||
}
|
||||
|
||||
// oneDNN SDPA out (f16 contiguous [mb,H,q,d]) -> ggml dst (f32 [head_dim,H,n_tok,mb], contiguous).
|
||||
static void permute_sdpa_out_sycl(const sycl::half * out, float * dst,
|
||||
int64_t mb, int64_t H, int64_t q, int64_t d, dpct::queue_ptr stream) {
|
||||
const int64_t n = mb * H * q * d;
|
||||
stream->parallel_for(sycl::range<1>(n), [=](sycl::id<1> ix) {
|
||||
const int64_t gid = ix[0];
|
||||
int64_t i = gid;
|
||||
const int64_t e = i % d; i /= d;
|
||||
const int64_t t = i % q; i /= q;
|
||||
const int64_t h = i % H; const int64_t b = i / H;
|
||||
dst[e + h * d + t * d * H + b * d * H * q] = (float) out[gid];
|
||||
});
|
||||
}
|
||||
|
||||
struct sdpa_partition {
|
||||
compiled_partition cp;
|
||||
std::vector<logical_tensor> ins;
|
||||
logical_tensor out;
|
||||
size_t id_q = 0, id_k = 0, id_v = 0, id_scale = 0, id_mask = 0;
|
||||
bool ok = false;
|
||||
};
|
||||
|
||||
// Build + compile the contiguous-input GQA SDPA graph (MatMul->Divide->Add->SoftMax->MatMul), f32 out.
|
||||
// Mirrors the hardware-verified scratch/onednn_sdpa_probe.cpp build_gqa (partitions=1, sdp_primitive_kernel_t).
|
||||
static sdpa_partition build_sdpa(const engine & eng, int H, int Hkv, int q, int seq, int d) {
|
||||
using ltype = logical_tensor::layout_type;
|
||||
using dt = logical_tensor::data_type;
|
||||
using ldims = logical_tensor::dims;
|
||||
const dt fi = dt::f32, t = dt::f16;
|
||||
const int rep = H / Hkv;
|
||||
const ldims q_sz = {1, Hkv, rep, q, d}, kv_sz = {1, Hkv, 1, seq, d}, s_sz = {1, Hkv, rep, q, seq},
|
||||
sc = {1, 1, 1, 1, 1}, msk = {1, 1, 1, q, seq}, o_sz = {1, Hkv, rep, q, d};
|
||||
int64_t id = 0;
|
||||
sdpa_partition E;
|
||||
|
||||
auto query = logical_tensor(id++, t, q_sz, ltype::strided);
|
||||
auto key = logical_tensor(id++, t, kv_sz, ltype::strided);
|
||||
auto score = logical_tensor(id++, fi, s_sz, ltype::strided);
|
||||
auto bmm1 = op(id++, op::kind::MatMul, "bmm1");
|
||||
bmm1.set_attr<bool>(op::attr::transpose_b, true); // key is [.., seq, d]
|
||||
bmm1.add_inputs({query, key}); bmm1.add_outputs({score});
|
||||
|
||||
auto scale = logical_tensor(id++, t, sc, ltype::strided);
|
||||
auto scaled = logical_tensor(id++, fi, s_sz, ltype::strided);
|
||||
auto sdiv = op(id++, op::kind::Divide, "scale_div"); // score / (1/kq_scale) == score * kq_scale
|
||||
sdiv.add_inputs({score, scale}); sdiv.add_outputs({scaled});
|
||||
|
||||
auto mask = logical_tensor(id++, t, msk, ltype::strided);
|
||||
auto masked = logical_tensor(id++, fi, s_sz, ltype::strided);
|
||||
auto madd = op(id++, op::kind::Add, "mask_add");
|
||||
madd.add_inputs({scaled, mask}); madd.add_outputs({masked});
|
||||
|
||||
auto probs = logical_tensor(id++, t, s_sz, ltype::strided);
|
||||
auto smax = op(id++, op::kind::SoftMax, "softmax");
|
||||
smax.set_attr<int64_t>(op::attr::axis, -1);
|
||||
smax.set_attr<std::string>(op::attr::mode, "inf_as_zero");
|
||||
smax.add_inputs({masked}); smax.add_outputs({probs});
|
||||
|
||||
auto value = logical_tensor(id++, t, kv_sz, ltype::strided);
|
||||
// f16 output is REQUIRED to hit sdp_primitive_kernel_t (the systolic micro-kernel); an f32 output
|
||||
// falls to larger_partition_kernel_t which materializes N^2 (confirmed: scratch/onednn_sdpa_kernel_probe.cpp).
|
||||
// converted to the f32 ggml dst in the permute below.
|
||||
auto output = logical_tensor(id++, t, o_sz, ltype::strided); // f16 contiguous [mb,Hkv,rep,q,d]
|
||||
auto bmm2 = op(id++, op::kind::MatMul, "bmm2");
|
||||
bmm2.add_inputs({probs, value}); bmm2.add_outputs({output});
|
||||
|
||||
dnnl::graph::graph g(eng.get_kind());
|
||||
g.add_op(bmm1); g.add_op(sdiv); g.add_op(madd); g.add_op(smax); g.add_op(bmm2);
|
||||
g.finalize();
|
||||
|
||||
auto parts = g.get_partitions();
|
||||
if (parts.size() != 1 || !parts[0].is_supported()) {
|
||||
return E; // ok stays false -> caller falls back to TILE
|
||||
}
|
||||
E.ins = parts[0].get_input_ports();
|
||||
E.out = parts[0].get_output_ports()[0];
|
||||
E.cp = parts[0].compile(E.ins, {E.out}, eng);
|
||||
E.out = E.cp.query_logical_tensor(E.out.get_id());
|
||||
E.id_q = query.get_id(); E.id_k = key.get_id(); E.id_v = value.get_id();
|
||||
E.id_scale = scale.get_id(); E.id_mask = mask.get_id();
|
||||
E.ok = true;
|
||||
return E;
|
||||
}
|
||||
|
||||
void ggml_sycl_flash_attn_ext_onednn(ggml_backend_sycl_context & ctx, ggml_tensor * dst) try {
|
||||
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->ne[0]; // head_dim
|
||||
const int64_t seq = K->ne[1]; // n_kv
|
||||
const int64_t Hkv = K->ne[2]; // n_head_kv
|
||||
const int64_t H = Q->ne[2]; // n_head
|
||||
const int64_t q = Q->ne[1]; // n_tok
|
||||
const int64_t mb = Q->ne[3]; // batch (== 1, gated)
|
||||
|
||||
float kq_scale = 1.0f;
|
||||
memcpy(&kq_scale, (const float *) dst->op_params + 0, sizeof(float));
|
||||
|
||||
dpct::queue_ptr stream = ctx.stream();
|
||||
dnnl::engine eng = ctx.engine_dnnl(stream);
|
||||
dnnl::stream strm = ctx.stream_dnnl(stream);
|
||||
|
||||
// cont/cast inputs to contiguous f16 (head-major) -- the layout the fast systolic path wants.
|
||||
ggml_sycl_pool_alloc<sycl::half> Qf(ctx.pool(), (size_t) H * q * d);
|
||||
ggml_sycl_pool_alloc<sycl::half> Kf(ctx.pool(), (size_t) Hkv * seq * d);
|
||||
ggml_sycl_pool_alloc<sycl::half> Vf(ctx.pool(), (size_t) Hkv * seq * d);
|
||||
cont_to_f16_sycl<float> ((const char *) Q->data, Qf.get(), d, q, H, mb, Q->nb[1], Q->nb[2], Q->nb[3], stream);
|
||||
cont_to_f16_sycl<sycl::half>((const char *) K->data, Kf.get(), d, seq, Hkv, mb, K->nb[1], K->nb[2], K->nb[3], stream);
|
||||
cont_to_f16_sycl<sycl::half>((const char *) V->data, Vf.get(), d, seq, Hkv, mb, V->nb[1], V->nb[2], V->nb[3], stream);
|
||||
|
||||
// divide-by-(1/scale) reproduces ggml's score *= kq_scale on the proven probe graph.
|
||||
const sycl::half scale_h = (sycl::half) (1.0f / kq_scale);
|
||||
ggml_sycl_pool_alloc<sycl::half> scbuf(ctx.pool(), 1);
|
||||
stream->memcpy(scbuf.get(), &scale_h, sizeof(sycl::half));
|
||||
|
||||
ggml_sycl_pool_alloc<sycl::half> outf(ctx.pool(), (size_t) H * q * d); // f16 contiguous SDPA out [mb,H,q,d]
|
||||
|
||||
// compile once per (device, shape), reuse across layers/calls.
|
||||
static std::unordered_map<std::string, sdpa_partition> cache;
|
||||
char keyb[96];
|
||||
snprintf(keyb, sizeof(keyb), "%d:%lld:%lld:%lld:%lld:%lld", ggml_sycl_get_device(),
|
||||
(long long) H, (long long) Hkv, (long long) q, (long long) seq, (long long) d);
|
||||
auto it = cache.find(keyb);
|
||||
if (it == cache.end()) {
|
||||
it = cache.emplace(keyb, build_sdpa(eng, (int) H, (int) Hkv, (int) q, (int) seq, (int) d)).first;
|
||||
}
|
||||
sdpa_partition & E = it->second;
|
||||
// _supported() is authoritative: if it accepted this op the partition must build.
|
||||
// A failure here is a gap in _supported() -- surface it, don't mask it with a fallback.
|
||||
GGML_ASSERT(E.ok && "oneDNN SDPA partition failed to build for a _supported() shape");
|
||||
|
||||
auto id2ptr = [&](size_t r) -> void * {
|
||||
if (r == E.id_q) return Qf.get();
|
||||
if (r == E.id_k) return Kf.get();
|
||||
if (r == E.id_v) return Vf.get();
|
||||
if (r == E.id_scale) return scbuf.get();
|
||||
if (r == E.id_mask) return (void *) mask->data;
|
||||
return nullptr;
|
||||
};
|
||||
std::vector<tensor> ti;
|
||||
ti.reserve(E.ins.size());
|
||||
for (auto & lt : E.ins) {
|
||||
ti.emplace_back(lt, eng, id2ptr(lt.get_id()));
|
||||
}
|
||||
tensor to(E.out, eng, outf.get());
|
||||
E.cp.execute(strm, ti, {to});
|
||||
|
||||
permute_sdpa_out_sycl(outf.get(), (float *) dst->data, mb, H, q, d, stream);
|
||||
// Single device: no sync is required, and actually PP perf is ~6% > wait_and_throw() (tested on llama-3.1-8b & qwen3.6-27b, both Q8_0, with Arc B70).
|
||||
// Any future multi-GPU refactor MUST re-measure this single-device path and keep the best
|
||||
// single-device PP speed. Otherwise (multiple devices/streams can race the reuse):
|
||||
if (ggml_sycl_info().device_count > 1) {
|
||||
// cont_to_f16 -> oneDNN execute -> permute is async on this stream, but the
|
||||
// pool_alloc*s above free their device buffers at host return. Without this wait the next
|
||||
// scheduler op re-acquires those bytes while the GPU is still computing the SDPA, turning
|
||||
// it into garbage and collapsing multi-turn output to a single repeated token ("GGGGG...").
|
||||
stream->wait_and_throw();
|
||||
}
|
||||
}
|
||||
catch (const std::exception & e) {
|
||||
// any oneDNN/SYCL failure is non-fatal: fall back to the existing kernel (strictly additive).
|
||||
GGML_LOG_WARN("%s: oneDNN SDPA failed (%s); falling back to TILE kernel\n", __func__, e.what());
|
||||
ggml_sycl_flash_attn_ext_tile(ctx, dst);
|
||||
}
|
||||
|
||||
#endif // GGML_SYCL_DNNL
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef GGML_SYCL_FATTN_ONEDNN_HPP
|
||||
#define GGML_SYCL_FATTN_ONEDNN_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
// Static-only check: fused-XMX oneDNN Graph SDPA path==flash-attn op
|
||||
// (f16 KV, no softcap/ALiBi, single stream, tuned head_dim, prefill-sized q.)
|
||||
bool ggml_sycl_flash_attn_ext_onednn_supported(const ggml_tensor * dst);
|
||||
|
||||
// Run flash attention through oneDNN's fused xmx SDPA
|
||||
// execute the cached SDPA partition, write the f32 dst. Falls back to the TILE kernel on any failure.
|
||||
void ggml_sycl_flash_attn_ext_onednn(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
#endif // GGML_SYCL_FATTN_ONEDNN_HPP
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "fattn-tile.hpp"
|
||||
#include "fattn-vec.hpp"
|
||||
#include "fattn.hpp"
|
||||
#include "fattn-onednn.hpp"
|
||||
|
||||
|
||||
#define FATTN_VEC_CASE(D, type_K, type_V) \
|
||||
@@ -97,7 +96,6 @@ static void ggml_sycl_flash_attn_ext_vec(ggml_backend_sycl_context & ctx, ggml_t
|
||||
enum best_fattn_kernel {
|
||||
BEST_FATTN_KERNEL_NONE = 0,
|
||||
BEST_FATTN_KERNEL_VEC = 100,
|
||||
BEST_FATTN_KERNEL_ONEDNN = 150, // added enum for onednn==150
|
||||
BEST_FATTN_KERNEL_TILE = 200,
|
||||
};
|
||||
|
||||
@@ -191,11 +189,7 @@ static best_fattn_kernel ggml_sycl_get_best_fattn_kernel(const int device, const
|
||||
// For small batch sizes the vector kernel may be preferable over the kernels optimized for large batch sizes:
|
||||
const bool can_use_vector_kernel = Q->ne[0] <= 512 && Q->ne[0] % 64 == 0 && K->ne[1] % FATTN_KQ_STRIDE == 0;
|
||||
|
||||
// Fused-XMX path: oneDNN Graph SDPA (flash attention). Strictly
|
||||
// additive -- taken only when statically supported, otherwise falls through to VEC/TILE below.
|
||||
if (ggml_sycl_flash_attn_ext_onednn_supported(dst)) {
|
||||
return BEST_FATTN_KERNEL_ONEDNN;
|
||||
}
|
||||
// Todo: Use the XMX kernel if possible:
|
||||
|
||||
// If there are no tensor cores available, use the generic tile kernel:
|
||||
if (can_use_vector_kernel) {
|
||||
@@ -219,13 +213,6 @@ void ggml_sycl_flash_attn_ext(ggml_backend_sycl_context & ctx, ggml_tensor * dst
|
||||
switch (ggml_sycl_get_best_fattn_kernel(ggml_sycl_get_device(), dst)) {
|
||||
case BEST_FATTN_KERNEL_NONE:
|
||||
GGML_ABORT("Not support Flash-Attention");
|
||||
case BEST_FATTN_KERNEL_ONEDNN:
|
||||
// guarded: ggml_sycl_flash_attn_ext_onednn() is only defined under GGML_SYCL_DNNL;
|
||||
// the reference must be compiled out here or the GGML_SYCL_DNNL=0 build fails to link.
|
||||
#if GGML_SYCL_DNNL
|
||||
ggml_sycl_flash_attn_ext_onednn(ctx, dst);
|
||||
#endif
|
||||
break;
|
||||
case BEST_FATTN_KERNEL_TILE:
|
||||
ggml_sycl_flash_attn_ext_tile(ctx, dst);
|
||||
break;
|
||||
|
||||
@@ -60,50 +60,6 @@ static void k_get_rows(
|
||||
dst_row[iybs + iqs + y_offset] = v.y();
|
||||
}
|
||||
|
||||
template<int qk, int qr, dequantize_kernel_f32_t dequantize_kernel, typename dst_t>
|
||||
static void k_get_rows_f32(
|
||||
const void * src0, const int32_t * src1, dst_t * dst,
|
||||
int64_t ne00,
|
||||
int64_t ne12,
|
||||
size_t s1, size_t s2, size_t s3,
|
||||
size_t nb01, size_t nb02, size_t nb03,
|
||||
size_t s10, size_t s11, size_t s12,
|
||||
const sycl::nd_item<3> &item_ct1) {
|
||||
|
||||
const int i00 = (item_ct1.get_group(2) * item_ct1.get_local_range(2) +
|
||||
item_ct1.get_local_id(2)) *
|
||||
2;
|
||||
const int i10 = item_ct1.get_local_range(1) * item_ct1.get_group(1) +
|
||||
item_ct1.get_local_id(1);
|
||||
const int i11 = (item_ct1.get_group(0) * item_ct1.get_local_range(0) +
|
||||
item_ct1.get_local_id(0)) /
|
||||
ne12;
|
||||
const int i12 = (item_ct1.get_group(0) * item_ct1.get_local_range(0) +
|
||||
item_ct1.get_local_id(0)) %
|
||||
ne12;
|
||||
|
||||
if (i00 >= ne00) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int i01 = src1[i10*s10 + i11*s11 + i12*s12];
|
||||
|
||||
dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3;
|
||||
const void * src0_row = (const char *)src0 + i01*nb01 + i11*nb02 + i12*nb03;
|
||||
|
||||
const int ib = i00/qk;
|
||||
const int iqs = (i00%qk)/qr;
|
||||
const int iybs = i00 - i00%qk;
|
||||
const int y_offset = qr == 1 ? 1 : qk/2;
|
||||
|
||||
float v0;
|
||||
float v1;
|
||||
dequantize_kernel(src0_row, ib, iqs, v0, v1);
|
||||
|
||||
dst_row[iybs + iqs + 0] = (dst_t) v0;
|
||||
dst_row[iybs + iqs + y_offset] = (dst_t) v1;
|
||||
}
|
||||
|
||||
template<typename src0_t, typename dst_t>
|
||||
static void k_get_rows_float(
|
||||
const src0_t * src0, const int32_t * src1, dst_t * dst,
|
||||
@@ -173,39 +129,6 @@ static void get_rows_sycl(ggml_backend_sycl_context & ctx, const ggml_tensor *sr
|
||||
GGML_UNUSED(ctx);
|
||||
}
|
||||
|
||||
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,
|
||||
const int32_t *src1_dd, float *dst_dd,
|
||||
queue_ptr stream) {
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const sycl::range<3> block_dims(1, 1, SYCL_GET_ROWS_BLOCK_SIZE);
|
||||
const int block_num_x = (ne00 + 2*SYCL_GET_ROWS_BLOCK_SIZE - 1) / (2*SYCL_GET_ROWS_BLOCK_SIZE);
|
||||
const sycl::range<3> block_nums(ne11 * ne12, ne10, block_num_x);
|
||||
|
||||
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);
|
||||
|
||||
GGML_ASSERT(ne00 % 2 == 0);
|
||||
|
||||
stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
k_get_rows_f32<qk, qr, dq>(
|
||||
src0_dd, src1_dd, dst_dd, ne00, ne12, s1, s2,
|
||||
s3, nb01, nb02, nb03, s10, s11, s12, item_ct1);
|
||||
});
|
||||
|
||||
GGML_UNUSED(dst);
|
||||
GGML_UNUSED(ctx);
|
||||
}
|
||||
|
||||
template <typename src0_t, typename dst_t>
|
||||
static void get_rows_sycl_float(ggml_backend_sycl_context & ctx, const ggml_tensor *src0,
|
||||
const ggml_tensor *src1, ggml_tensor *dst,
|
||||
@@ -321,7 +244,7 @@ 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_Q2_K:
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q2_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
get_rows_sycl<QK_K, 1, dequantize_q2_K>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q3_K:
|
||||
@@ -337,7 +260,7 @@ 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_Q4_K:
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q4_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
get_rows_sycl<QK_K, 1, dequantize_q4_K>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q5_0:
|
||||
@@ -349,7 +272,7 @@ 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_Q5_K:
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q5_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
get_rows_sycl<QK_K, 1, dequantize_q5_K>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q6_K:
|
||||
|
||||
@@ -84,7 +84,6 @@ int g_ggml_sycl_debug = 0;
|
||||
int g_ggml_sycl_enable_optimize = 1;
|
||||
int g_ggml_sycl_enable_graph = 0;
|
||||
int g_ggml_sycl_enable_dnn = 1;
|
||||
int g_ggml_sycl_fa_onednn = 1;
|
||||
int g_ggml_sycl_enable_vmm = 1;
|
||||
int g_ggml_sycl_enable_fusion = 1;
|
||||
int g_ggml_sycl_prioritize_dmmv = 0;
|
||||
@@ -286,7 +285,6 @@ static void ggml_check_sycl() try {
|
||||
g_ggml_sycl_enable_optimize = ggml_sycl_get_env("GGML_SYCL_ENABLE_OPT", 1);
|
||||
g_ggml_sycl_enable_graph = ggml_sycl_get_env("GGML_SYCL_ENABLE_GRAPH", 0);
|
||||
g_ggml_sycl_enable_dnn = ggml_sycl_get_env("GGML_SYCL_ENABLE_DNN", 1);
|
||||
g_ggml_sycl_fa_onednn = ggml_sycl_get_env("GGML_SYCL_FA_ONEDNN", 1);
|
||||
g_ggml_sycl_enable_vmm = ggml_sycl_get_env("GGML_SYCL_ENABLE_VMM", 1);
|
||||
g_ggml_sycl_enable_fusion = ggml_sycl_get_env("GGML_SYCL_ENABLE_FUSION", 1);
|
||||
g_ggml_sycl_prioritize_dmmv = ggml_sycl_get_env("GGML_SYCL_PRIORITIZE_DMMV", 0);
|
||||
@@ -354,10 +352,8 @@ static void ggml_check_sycl() try {
|
||||
|
||||
#if defined(GGML_SYCL_DNNL)
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_DNN: %d\n", g_ggml_sycl_enable_dnn);
|
||||
GGML_LOG_INFO(" GGML_SYCL_FA_ONEDNN: %d\n", g_ggml_sycl_fa_onednn);
|
||||
#else
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_DNN: DNN disabled by compile flag\n");
|
||||
GGML_LOG_INFO(" GGML_SYCL_FA_ONEDNN: %d\n", g_ggml_sycl_fa_onednn);
|
||||
#endif
|
||||
#ifdef SYCL_FLASH_ATTN
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_FLASH_ATTN: %d\n", g_ggml_sycl_enable_flash_attention);
|
||||
@@ -843,7 +839,7 @@ static const char * ggml_backend_sycl_buffer_type_get_name(ggml_backend_buffer_t
|
||||
}
|
||||
|
||||
static bool check_usm_system(int device, size_t size) {
|
||||
bool use_usm_system = g_ggml_sycl_usm_system && size >= ((size_t)4 * MEM_SIZE_1G);
|
||||
bool use_usm_system = g_ggml_sycl_usm_system && size >= MEM_SIZE_1G;
|
||||
|
||||
if (use_usm_system && !ggml_sycl_info().devices[device].usm_system_support) {
|
||||
GGML_LOG_INFO("Device does not support USM system allocations\n");
|
||||
@@ -882,7 +878,6 @@ ggml_backend_sycl_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft,
|
||||
|
||||
void * dev_ptr;
|
||||
if (use_usm_system) {
|
||||
GGML_SYCL_DEBUG("[SYCL] allocating %lu Bytes with USM system\n", size);
|
||||
dev_ptr = (void *)aligned_malloc_host(alignment, aligned_size);
|
||||
if (!dev_ptr) {
|
||||
GGML_LOG_ERROR("%s: can't allocate %lu Bytes of memory on host\n", __func__, size);
|
||||
@@ -5011,9 +5006,6 @@ static bool ggml_sycl_compute_forward(ggml_backend_sycl_context & ctx, struct gg
|
||||
case GGML_UNARY_OP_ELU:
|
||||
ggml_sycl_elu(ctx, dst);
|
||||
break;
|
||||
case GGML_UNARY_OP_XIELU:
|
||||
ggml_sycl_xielu(ctx, dst);
|
||||
break;
|
||||
case GGML_UNARY_OP_FLOOR:
|
||||
ggml_sycl_floor(ctx, dst);
|
||||
break;
|
||||
@@ -5676,7 +5668,6 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
|
||||
case GGML_UNARY_OP_EXPM1:
|
||||
case GGML_UNARY_OP_SOFTPLUS:
|
||||
case GGML_UNARY_OP_ELU:
|
||||
case GGML_UNARY_OP_XIELU:
|
||||
case GGML_UNARY_OP_CEIL:
|
||||
return true;
|
||||
case GGML_UNARY_OP_FLOOR:
|
||||
|
||||
@@ -723,7 +723,6 @@ struct vk_device_struct {
|
||||
bool uma;
|
||||
bool prefer_host_memory;
|
||||
bool float_controls_rte_fp16;
|
||||
bool float_controls_denorm_preserve_fp16;
|
||||
bool subgroup_basic;
|
||||
bool subgroup_arithmetic;
|
||||
bool subgroup_shuffle;
|
||||
@@ -869,9 +868,8 @@ struct vk_device_struct {
|
||||
vk_pipeline pipeline_cpy_f32_quant[GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_cpy_quant_f32[GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_cpy_transpose_16, pipeline_cpy_transpose_32;
|
||||
// [src0 0=fp32,1=fp16][dst]
|
||||
vk_pipeline pipeline_set_rows_i32[2][GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_set_rows_i64[2][GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_set_rows_i32[GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_set_rows_i64[GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_norm_f32;
|
||||
vk_pipeline pipeline_group_norm_f32;
|
||||
vk_pipeline pipeline_rms_norm_f32;
|
||||
@@ -961,7 +959,6 @@ struct vk_device_struct {
|
||||
vk_pipeline pipeline_col2im_1d_f32;
|
||||
vk_pipeline pipeline_col2im_1d_f16;
|
||||
vk_pipeline pipeline_col2im_1d_bf16;
|
||||
vk_pipeline pipeline_out_prod_f32;
|
||||
vk_pipeline pipeline_snake_f32;
|
||||
vk_pipeline pipeline_snake_f16;
|
||||
vk_pipeline pipeline_snake_bf16;
|
||||
@@ -2598,10 +2595,10 @@ static void ggml_vk_create_pipeline_func(vk_device& device, vk_pipeline& pipelin
|
||||
|
||||
vk::ShaderModuleCreateInfo shader_module_create_info({}, spv_size, reinterpret_cast<const uint32_t *>(spv_data));
|
||||
|
||||
// Patch SPIR-V to enable supported FP16 float controls, avoiding the need
|
||||
// for separate shader variants.
|
||||
// Patch SPIR-V to enable RTE rounding for FP16, avoiding the need for
|
||||
// separate shader variants compiled with -DRTE16.
|
||||
std::vector<uint32_t> spirv;
|
||||
if (device->float_controls_rte_fp16 || device->float_controls_denorm_preserve_fp16) {
|
||||
if (device->float_controls_rte_fp16) {
|
||||
const uint32_t* spv_words = reinterpret_cast<const uint32_t *>(spv_data);
|
||||
size_t word_count = spv_size / sizeof(uint32_t);
|
||||
spirv.assign(spv_words, spv_words + word_count);
|
||||
@@ -2638,17 +2635,9 @@ static void ggml_vk_create_pipeline_func(vk_device& device, vk_pipeline& pipelin
|
||||
|
||||
// Insert from latest position first so earlier indices stay valid.
|
||||
|
||||
if (device->float_controls_rte_fp16) {
|
||||
// OpExecutionMode %entrypoint RoundingModeRTE 16
|
||||
uint32_t exec_mode[] = { (4u << spv::WordCountShift) | spv::OpExecutionMode, entry_point_id, spv::ExecutionModeRoundingModeRTE, 16 };
|
||||
spirv.insert(spirv.begin() + exec_insert_pos, std::begin(exec_mode), std::end(exec_mode));
|
||||
}
|
||||
|
||||
if (device->float_controls_denorm_preserve_fp16) {
|
||||
// OpExecutionMode %entrypoint DenormPreserve 16
|
||||
uint32_t exec_mode[] = { (4u << spv::WordCountShift) | spv::OpExecutionMode, entry_point_id, spv::ExecutionModeDenormPreserve, 16 };
|
||||
spirv.insert(spirv.begin() + exec_insert_pos, std::begin(exec_mode), std::end(exec_mode));
|
||||
}
|
||||
// OpExecutionMode %entrypoint RoundingModeRTE 16
|
||||
uint32_t exec_mode[] = { (4u << spv::WordCountShift) | spv::OpExecutionMode, entry_point_id, spv::ExecutionModeRoundingModeRTE, 16 };
|
||||
spirv.insert(spirv.begin() + exec_insert_pos, std::begin(exec_mode), std::end(exec_mode));
|
||||
|
||||
// OpExtension "SPV_KHR_float_controls"
|
||||
const char ext_str[] = "SPV_KHR_float_controls";
|
||||
@@ -2658,17 +2647,9 @@ static void ggml_vk_create_pipeline_func(vk_device& device, vk_pipeline& pipelin
|
||||
memcpy(&extension[1], ext_str, sizeof(ext_str));
|
||||
spirv.insert(spirv.begin() + ext_insert_pos, extension.begin(), extension.end());
|
||||
|
||||
if (device->float_controls_rte_fp16) {
|
||||
// OpCapability RoundingModeRTE
|
||||
uint32_t capability[] = { (2u << spv::WordCountShift) | spv::OpCapability, spv::CapabilityRoundingModeRTE };
|
||||
spirv.insert(spirv.begin() + cap_insert_pos, std::begin(capability), std::end(capability));
|
||||
}
|
||||
|
||||
if (device->float_controls_denorm_preserve_fp16) {
|
||||
// OpCapability DenormPreserve
|
||||
uint32_t capability[] = { (2u << spv::WordCountShift) | spv::OpCapability, spv::CapabilityDenormPreserve };
|
||||
spirv.insert(spirv.begin() + cap_insert_pos, std::begin(capability), std::end(capability));
|
||||
}
|
||||
// OpCapability RoundingModeRTE
|
||||
uint32_t capability[] = { (2u << spv::WordCountShift) | spv::OpCapability, spv::CapabilityRoundingModeRTE };
|
||||
spirv.insert(spirv.begin() + cap_insert_pos, std::begin(capability), std::end(capability));
|
||||
|
||||
shader_module_create_info = vk::ShaderModuleCreateInfo({}, spirv.size() * sizeof(uint32_t), spirv.data());
|
||||
}
|
||||
@@ -5206,22 +5187,20 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_cpy_f32_quant[GGML_TYPE_Q8_0], "cpy_f32_q8_0", cpy_f32_q8_0_len, cpy_f32_q8_0_data, "main", 2, sizeof(vk_op_unary_push_constants), {32, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_cpy_f32_quant[GGML_TYPE_IQ4_NL], "cpy_f32_iq4_nl", cpy_f32_iq4_nl_len, cpy_f32_iq4_nl_data, "main", 2, sizeof(vk_op_unary_push_constants), {32, 1, 1}, {}, 1);
|
||||
|
||||
#define SET_ROWS(src_idx, src, itype) \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_F32], "set_rows_" #src "_f32" #itype, set_rows_ ## src ## _f32 ## itype ## _len, set_rows_ ## src ## _f32 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_F16], "set_rows_" #src "_f16" #itype, set_rows_ ## src ## _f16 ## itype ## _len, set_rows_ ## src ## _f16 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_BF16], "set_rows_" #src "_bf16" #itype, set_rows_ ## src ## _bf16 ## itype ## _len, set_rows_ ## src ## _bf16 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q1_0], "set_rows_" #src "_q1_0" #itype, set_rows_ ## src ## _q1_0 ## itype ## _len, set_rows_ ## src ## _q1_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q4_0], "set_rows_" #src "_q4_0" #itype, set_rows_ ## src ## _q4_0 ## itype ## _len, set_rows_ ## src ## _q4_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q4_1], "set_rows_" #src "_q4_1" #itype, set_rows_ ## src ## _q4_1 ## itype ## _len, set_rows_ ## src ## _q4_1 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q5_0], "set_rows_" #src "_q5_0" #itype, set_rows_ ## src ## _q5_0 ## itype ## _len, set_rows_ ## src ## _q5_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q5_1], "set_rows_" #src "_q5_1" #itype, set_rows_ ## src ## _q5_1 ## itype ## _len, set_rows_ ## src ## _q5_1 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_Q8_0], "set_rows_" #src "_q8_0" #itype, set_rows_ ## src ## _q8_0 ## itype ## _len, set_rows_ ## src ## _q8_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [src_idx][GGML_TYPE_IQ4_NL], "set_rows_" #src "_iq4_nl" #itype, set_rows_ ## src ## _iq4_nl ## itype ## _len, set_rows_ ## src ## _iq4_nl ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true);
|
||||
#define SET_ROWS(itype) \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_F32], "set_rows_f32" #itype, set_rows_f32 ## itype ## _len, set_rows_f32 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_F16], "set_rows_f16" #itype, set_rows_f16 ## itype ## _len, set_rows_f16 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_BF16], "set_rows_bf16" #itype, set_rows_bf16 ## itype ## _len, set_rows_bf16 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q1_0], "set_rows_q1_0" #itype, set_rows_q1_0 ## itype ## _len, set_rows_q1_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q4_0], "set_rows_q4_0" #itype, set_rows_q4_0 ## itype ## _len, set_rows_q4_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q4_1], "set_rows_q4_1" #itype, set_rows_q4_1 ## itype ## _len, set_rows_q4_1 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q5_0], "set_rows_q5_0" #itype, set_rows_q5_0 ## itype ## _len, set_rows_q5_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q5_1], "set_rows_q5_1" #itype, set_rows_q5_1 ## itype ## _len, set_rows_q5_1 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_Q8_0], "set_rows_q8_0" #itype, set_rows_q8_0 ## itype ## _len, set_rows_q8_0 ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true); \
|
||||
ggml_vk_create_pipeline(device, device->pipeline_set_rows ## itype [GGML_TYPE_IQ4_NL], "set_rows_iq4_nl" #itype, set_rows_iq4_nl ## itype ## _len, set_rows_iq4_nl ## itype ## _data, "main", 3, sizeof(vk_op_binary_push_constants), {1, 1, 1}, {1}, 1, true);
|
||||
|
||||
SET_ROWS(0, f32, _i32)
|
||||
SET_ROWS(0, f32, _i64)
|
||||
SET_ROWS(1, f16, _i32)
|
||||
SET_ROWS(1, f16, _i64)
|
||||
SET_ROWS(_i32)
|
||||
SET_ROWS(_i64)
|
||||
#undef SET_ROWS
|
||||
|
||||
|
||||
@@ -5480,8 +5459,6 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_col2im_1d_f16, "col2im_1d_f16", col2im_1d_f16_len, col2im_1d_f16_data, "main", 2, sizeof(vk_op_col2im_1d_push_constants), {256, 1, 1}, {}, 1, true);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_col2im_1d_bf16, "col2im_1d_bf16", col2im_1d_bf16_len, col2im_1d_bf16_data, "main", 2, sizeof(vk_op_col2im_1d_push_constants), {256, 1, 1}, {}, 1, true);
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_out_prod_f32, "out_prod_f32", out_prod_f32_len, out_prod_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {256, 1, 1}, {}, 1);
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_snake_f32, "snake_f32", snake_f32_len, snake_f32_data, "main", 4, sizeof(vk_op_snake_push_constants), {256, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_snake_f16, "snake_f16", snake_f16_len, snake_f16_data, "main", 4, sizeof(vk_op_snake_push_constants), {256, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_snake_bf16, "snake_bf16", snake_bf16_len, snake_bf16_data, "main", 4, sizeof(vk_op_snake_push_constants), {256, 1, 1}, {}, 1);
|
||||
@@ -6054,7 +6031,6 @@ static vk_device ggml_vk_get_device(size_t idx) {
|
||||
device->shader_core_count = 0;
|
||||
}
|
||||
device->float_controls_rte_fp16 = vk12_props.shaderRoundingModeRTEFloat16;
|
||||
device->float_controls_denorm_preserve_fp16 = vk12_props.shaderDenormPreserveFloat16;
|
||||
|
||||
device->subgroup_basic = (vk11_props.subgroupSupportedStages & vk::ShaderStageFlagBits::eCompute) &&
|
||||
(vk11_props.subgroupSupportedOperations & vk::SubgroupFeatureFlagBits::eBasic);
|
||||
@@ -10748,11 +10724,6 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
||||
return ctx->device->pipeline_add_id_f32;
|
||||
}
|
||||
return nullptr;
|
||||
case GGML_OP_OUT_PROD:
|
||||
if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
return ctx->device->pipeline_out_prod_f32;
|
||||
}
|
||||
return nullptr;
|
||||
case GGML_OP_CONCAT: {
|
||||
if (src0->type != src1->type || src0->type != dst->type) {
|
||||
return nullptr;
|
||||
@@ -10872,17 +10843,10 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
||||
case GGML_OP_DUP:
|
||||
return ggml_vk_get_cpy_pipeline(ctx, src0, dst, dst->type);
|
||||
case GGML_OP_SET_ROWS:
|
||||
{
|
||||
if (src0->type != GGML_TYPE_F32 && src0->type != GGML_TYPE_F16) {
|
||||
return nullptr;
|
||||
}
|
||||
const int src_idx = src0->type == GGML_TYPE_F16;
|
||||
if (src1->type == GGML_TYPE_I64) {
|
||||
return ctx->device->pipeline_set_rows_i64[src_idx][dst->type];
|
||||
} else if (src1->type == GGML_TYPE_I32) {
|
||||
return ctx->device->pipeline_set_rows_i32[src_idx][dst->type];
|
||||
}
|
||||
return nullptr;
|
||||
if (src1->type == GGML_TYPE_I64) {
|
||||
return ctx->device->pipeline_set_rows_i64[dst->type];
|
||||
} else {
|
||||
return ctx->device->pipeline_set_rows_i32[dst->type];
|
||||
}
|
||||
case GGML_OP_SILU_BACK:
|
||||
if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
@@ -11709,7 +11673,6 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co
|
||||
case GGML_OP_DIV:
|
||||
case GGML_OP_MUL:
|
||||
case GGML_OP_ADD1:
|
||||
case GGML_OP_OUT_PROD:
|
||||
case GGML_OP_ARANGE:
|
||||
case GGML_OP_FILL:
|
||||
case GGML_OP_SCALE:
|
||||
@@ -12023,24 +11986,6 @@ static void ggml_vk_add(ggml_backend_vk_context * ctx, vk_context& subctx, const
|
||||
});
|
||||
}
|
||||
|
||||
static void ggml_vk_out_prod(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
const uint32_t src0_type_size = ggml_type_size(src0->type);
|
||||
const uint32_t src1_type_size = ggml_type_size(src1->type);
|
||||
const uint32_t dst_type_size = ggml_type_size(dst->type);
|
||||
|
||||
ggml_vk_op_f32<vk_op_binary_push_constants>(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_OUT_PROD, {
|
||||
(uint32_t)ggml_nelements(dst),
|
||||
(uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3],
|
||||
(uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size,
|
||||
(uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3],
|
||||
(uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size,
|
||||
(uint32_t) dst->ne[0], (uint32_t) dst->ne[1], (uint32_t) dst->ne[2],(uint32_t) dst->ne[3],
|
||||
(uint32_t) dst->nb[0] / dst_type_size, (uint32_t) dst->nb[1] / dst_type_size, (uint32_t) dst->nb[2] / dst_type_size, (uint32_t) dst->nb[3] / dst_type_size,
|
||||
0,
|
||||
0.0f, 0.0f, 0,
|
||||
});
|
||||
}
|
||||
|
||||
static void ggml_vk_sub(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
const uint32_t src0_type_size = ggml_type_size(src0->type);
|
||||
const uint32_t src1_type_size = ggml_type_size(src1->type);
|
||||
@@ -14828,9 +14773,6 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr
|
||||
ggml_vk_add(ctx, compute_ctx, src0, src1, node);
|
||||
}
|
||||
break;
|
||||
case GGML_OP_OUT_PROD:
|
||||
ggml_vk_out_prod(ctx, compute_ctx, src0, src1, node);
|
||||
break;
|
||||
case GGML_OP_SUB:
|
||||
ggml_vk_sub(ctx, compute_ctx, src0, src1, node);
|
||||
|
||||
@@ -17558,25 +17500,24 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
return op->type == GGML_TYPE_F32 && op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_SET_ROWS:
|
||||
{
|
||||
if ((op->src[0]->type != GGML_TYPE_F32 && op->src[0]->type != GGML_TYPE_F16) ||
|
||||
(op->src[1]->type != GGML_TYPE_I32 && op->src[1]->type != GGML_TYPE_I64)) {
|
||||
return false;
|
||||
}
|
||||
switch (op->type) {
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_Q1_0:
|
||||
case GGML_TYPE_Q4_0:
|
||||
case GGML_TYPE_Q4_1:
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_Q8_0:
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
if (op->src[0]->type == GGML_TYPE_F32) {
|
||||
switch (op->type) {
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_Q1_0:
|
||||
case GGML_TYPE_Q4_0:
|
||||
case GGML_TYPE_Q4_1:
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_Q8_0:
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GGML_OP_CONT:
|
||||
case GGML_OP_CPY:
|
||||
@@ -17685,10 +17626,6 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
case GGML_OP_OPT_STEP_ADAMW:
|
||||
case GGML_OP_OPT_STEP_SGD:
|
||||
return ggml_is_contiguous(op->src[0]) && op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_OUT_PROD:
|
||||
return ggml_is_contiguous(op->src[0]) && op->src[0]->type == GGML_TYPE_F32
|
||||
&& ggml_is_contiguous(op->src[1]) && op->src[1]->type == GGML_TYPE_F32
|
||||
&& op->type == GGML_TYPE_F32;
|
||||
case GGML_OP_LOG:
|
||||
case GGML_OP_TRI:
|
||||
case GGML_OP_DIAG:
|
||||
|
||||
@@ -10,7 +10,7 @@ layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
|
||||
const uint BLOCK_SIZE = 32;
|
||||
#endif
|
||||
|
||||
layout (binding = 0) readonly buffer S {S_TYPE data_s[];};
|
||||
layout (binding = 0) readonly buffer S {float data_s[];};
|
||||
|
||||
#if defined(SET_ROWS)
|
||||
#include "generic_binary_head.glsl"
|
||||
@@ -35,7 +35,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float vmax = 0.0;
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q4_0; ++j) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
if (amax < abs(v)) {
|
||||
amax = abs(v);
|
||||
vmax = v;
|
||||
@@ -48,8 +48,8 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
data_q[dst_idx].d = float16_t(d);
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q4_0/2; ++j) {
|
||||
const float x0 = float(data_s[src_idx + 0 + j])*id;
|
||||
const float x1 = float(data_s[src_idx + QUANT_K_Q4_0/2 + j])*id;
|
||||
const float x0 = data_s[src_idx + 0 + j]*id;
|
||||
const float x1 = data_s[src_idx + QUANT_K_Q4_0/2 + j]*id;
|
||||
|
||||
const uint xi0 = min(15, int(x0 + 8.5));
|
||||
const uint xi1 = min(15, int(x1 + 8.5));
|
||||
@@ -66,7 +66,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float vmax = -vmin;
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q4_1; ++j) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
|
||||
if (v < vmin) vmin = v;
|
||||
if (v > vmax) vmax = v;
|
||||
@@ -79,8 +79,8 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
data_q[dst_idx].m = float16_t(vmin);
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q4_1/2; ++j) {
|
||||
const float x0 = (float(data_s[src_idx + 0 + j]) - vmin)*id;
|
||||
const float x1 = (float(data_s[src_idx + QUANT_K_Q4_1/2 + j]) - vmin)*id;
|
||||
const float x0 = (data_s[src_idx + 0 + j] - vmin)*id;
|
||||
const float x1 = (data_s[src_idx + QUANT_K_Q4_1/2 + j] - vmin)*id;
|
||||
|
||||
const uint xi0 = min(15, int(x0 + 0.5));
|
||||
const uint xi1 = min(15, int(x1 + 0.5));
|
||||
@@ -97,7 +97,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float vmax = 0.0;
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q5_0; ++j) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
if (amax < abs(v)) {
|
||||
amax = abs(v);
|
||||
vmax = v;
|
||||
@@ -111,8 +111,8 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
|
||||
uint32_t qh = 0;
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q5_0/2; ++j) {
|
||||
const float x0 = float(data_s[src_idx + 0 + j])*id;
|
||||
const float x1 = float(data_s[src_idx + QUANT_K_Q5_0/2 + j])*id;
|
||||
const float x0 = data_s[src_idx + 0 + j]*id;
|
||||
const float x1 = data_s[src_idx + QUANT_K_Q5_0/2 + j]*id;
|
||||
|
||||
const uint xi0 = min(31, int(x0 + 16.5));
|
||||
const uint xi1 = min(31, int(x1 + 16.5));
|
||||
@@ -129,11 +129,11 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
#if defined(DATA_A_Q5_1)
|
||||
void quantize(uint dst_idx, uint src_idx)
|
||||
{
|
||||
float min = float(data_s[src_idx + 0]);
|
||||
float min = data_s[src_idx + 0];
|
||||
float max = min;
|
||||
|
||||
[[unroll]] for (int j = 1; j < QUANT_K_Q5_1; ++j) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
min = v < min ? v : min;
|
||||
max = v > max ? v : max;
|
||||
}
|
||||
@@ -146,8 +146,8 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
|
||||
uint32_t qh = 0;
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q5_1/2; ++j) {
|
||||
const float x0 = (float(data_s[src_idx + 0 + j]) - min)*id;
|
||||
const float x1 = (float(data_s[src_idx + QUANT_K_Q5_1/2 + j]) - min)*id;
|
||||
const float x0 = (data_s[src_idx + 0 + j] - min)*id;
|
||||
const float x1 = (data_s[src_idx + QUANT_K_Q5_1/2 + j] - min)*id;
|
||||
|
||||
const uint xi0 = uint(x0 + 0.5);
|
||||
const uint xi1 = uint(x1 + 0.5);
|
||||
@@ -166,7 +166,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float amax = 0.0; // absolute max
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q8_0; j++) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
amax = max(amax, abs(v));
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
data_q[dst_idx].d = float16_t(d);
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q8_0; ++j) {
|
||||
const float x0 = float(data_s[src_idx + j])*id;
|
||||
const float x0 = data_s[src_idx + j]*id;
|
||||
|
||||
data_q[dst_idx].qs[j] = int8_t(round(x0));
|
||||
}
|
||||
@@ -189,7 +189,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float sum_abs = 0.0;
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q1_0; j++) {
|
||||
sum_abs += abs(float(data_s[src_idx + j]));
|
||||
sum_abs += abs(data_s[src_idx + j]);
|
||||
}
|
||||
|
||||
const float d = sum_abs / QUANT_K_Q1_0;
|
||||
@@ -201,7 +201,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
}
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_Q1_0; ++j) {
|
||||
if (float(data_s[src_idx + j]) >= 0.0) {
|
||||
if (data_s[src_idx + j] >= 0.0) {
|
||||
data_q[dst_idx].qs[j / 8] |= uint8_t(1 << (j % 8));
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,7 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
float vmax = 0.0;
|
||||
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_IQ4_NL; ++j) {
|
||||
const float v = float(data_s[src_idx + j]);
|
||||
const float v = data_s[src_idx + j];
|
||||
if (amax < abs(v)) {
|
||||
amax = abs(v);
|
||||
vmax = v;
|
||||
@@ -238,16 +238,16 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
|
||||
float sumqx = 0, sumq2 = 0;
|
||||
[[unroll]] for (int j = 0; j < QUANT_K_IQ4_NL/2; ++j) {
|
||||
const float x0 = float(data_s[src_idx + 0 + j])*id;
|
||||
const float x1 = float(data_s[src_idx + QUANT_K_IQ4_NL/2 + j])*id;
|
||||
const float x0 = data_s[src_idx + 0 + j]*id;
|
||||
const float x1 = data_s[src_idx + QUANT_K_IQ4_NL/2 + j]*id;
|
||||
const uint xi0 = best_index(x0);
|
||||
const uint xi1 = best_index(x1);
|
||||
data_q[dst_idx].qs[j] = uint8_t(xi0 | (xi1 << 4));
|
||||
const float v0 = kvalues_iq4nl[xi0];
|
||||
const float v1 = kvalues_iq4nl[xi1];
|
||||
const float w0 = float(data_s[src_idx + 0 + j])*float(data_s[src_idx + 0 + j]);
|
||||
const float w1 = float(data_s[src_idx + QUANT_K_IQ4_NL/2 + j])*float(data_s[src_idx + QUANT_K_IQ4_NL/2 + j]);
|
||||
sumqx += w0*v0*float(data_s[src_idx + j]) + w1*v1*float(data_s[src_idx + QUANT_K_IQ4_NL/2 + j]);
|
||||
const float w0 = data_s[src_idx + 0 + j]*data_s[src_idx + 0 + j];
|
||||
const float w1 = data_s[src_idx + QUANT_K_IQ4_NL/2 + j]*data_s[src_idx + QUANT_K_IQ4_NL/2 + j];
|
||||
sumqx += w0*v0*data_s[src_idx + j] + w1*v1*data_s[src_idx + QUANT_K_IQ4_NL/2 + j];
|
||||
sumq2 += w0*v0*v0 + w1*v1*v1;
|
||||
}
|
||||
|
||||
@@ -259,14 +259,14 @@ void quantize(uint dst_idx, uint src_idx)
|
||||
#if defined(DATA_A_F32) || defined(DATA_A_F16)
|
||||
void quantize(uint dst_idx, uint src_idx)
|
||||
{
|
||||
data_q[dst_idx] = A_TYPE(float(data_s[src_idx]));
|
||||
data_q[dst_idx] = A_TYPE(data_s[src_idx]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DATA_A_BF16)
|
||||
void quantize(uint dst_idx, uint src_idx)
|
||||
{
|
||||
data_q[dst_idx] = A_TYPE(fp32_to_bf16(float(data_s[src_idx])));
|
||||
data_q[dst_idx] = A_TYPE(fp32_to_bf16(data_s[src_idx]));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#version 450
|
||||
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
|
||||
layout (push_constant) uniform parameter
|
||||
{
|
||||
uint ne;
|
||||
uint ne00; uint ne01; uint ne02; uint ne03; uint nb00; uint nb01; uint nb02; uint nb03;
|
||||
uint ne10; uint ne11; uint ne12; uint ne13; uint nb10; uint nb11; uint nb12; uint nb13;
|
||||
uint ne20; uint ne21; uint ne22; uint ne23; uint nb20; uint nb21; uint nb22; uint nb23;
|
||||
uint misalign_offsets;
|
||||
float param1; float param2; int param3;
|
||||
} p;
|
||||
|
||||
layout (binding = 0) readonly buffer A {float data_a[];};
|
||||
layout (binding = 1) readonly buffer B {float data_b[];};
|
||||
layout (binding = 2) writeonly buffer D {float data_d[];};
|
||||
|
||||
uint get_idx() {
|
||||
return gl_GlobalInvocationID.z * 262144 + gl_GlobalInvocationID.y * 512 + gl_GlobalInvocationID.x;
|
||||
}
|
||||
|
||||
uint get_aoffset() { return p.misalign_offsets >> 16; }
|
||||
uint get_boffset() { return (p.misalign_offsets >> 8) & 0xFF; }
|
||||
uint get_doffset() { return p.misalign_offsets & 0xFF; }
|
||||
|
||||
layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
void main() {
|
||||
uint idx = get_idx();
|
||||
if (idx >= p.ne) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint tmp = idx;
|
||||
uint i0 = tmp % p.ne20; tmp /= p.ne20;
|
||||
uint i1 = tmp % p.ne21; tmp /= p.ne21;
|
||||
uint i2 = tmp % p.ne22; tmp /= p.ne22;
|
||||
uint i3 = tmp;
|
||||
|
||||
uint a_i0 = i0 % p.ne00;
|
||||
uint a_i2 = i2 / (p.ne22 / p.ne02);
|
||||
uint a_i3 = i3 / (p.ne23 / p.ne03);
|
||||
|
||||
uint b_i0 = i1 % p.ne10;
|
||||
uint b_i2 = i2;
|
||||
uint b_i3 = i3;
|
||||
|
||||
float sum = 0.0f;
|
||||
uint K = p.ne01;
|
||||
for (uint k = 0; k < K; k++) {
|
||||
uint aoff = get_aoffset() + a_i3*p.nb03 + a_i2*p.nb02 + k*p.nb01 + a_i0*p.nb00;
|
||||
uint boff = get_boffset() + b_i3*p.nb13 + b_i2*p.nb12 + k*p.nb11 + b_i0*p.nb10;
|
||||
sum += data_a[aoff] * data_b[boff];
|
||||
}
|
||||
|
||||
uint doff = get_doffset() + i3*p.nb23 + i2*p.nb22 + i1*p.nb21 + i0*p.nb20;
|
||||
data_d[doff] = sum;
|
||||
}
|
||||
@@ -824,15 +824,13 @@ void process_shaders() {
|
||||
string_to_spv("cpy_transpose_32", "copy_transpose.comp", {{"A_TYPE", "uint"}, {"D_TYPE", "uint"}});
|
||||
|
||||
for (std::string t : {"q1_0", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0", "iq4_nl"}) {
|
||||
string_to_spv("cpy_f32_" + t, "copy_to_quant.comp", {{"DATA_A_" + to_uppercase(t), "1"}, {"S_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
string_to_spv("cpy_f32_" + t, "copy_to_quant.comp", {{"DATA_A_" + to_uppercase(t), "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
string_to_spv("cpy_" + t + "_f32", "copy_from_quant.comp", {{"DATA_A_" + to_uppercase(t), "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
}
|
||||
|
||||
for (auto src : {std::pair{"f32", "float"}, std::pair{"f16", "float16_t"}}) {
|
||||
for (std::string dst : {"f32", "f16", "bf16", "q1_0", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0", "iq4_nl"}) {
|
||||
string_to_spv("set_rows_" + std::string(src.first) + "_" + dst + "_i32", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(dst), "1"}, {"B_TYPE", "uint"}, {"B_SIZE", "32"}, {"S_TYPE", src.second}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
string_to_spv("set_rows_" + std::string(src.first) + "_" + dst + "_i64", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(dst), "1"}, {"B_TYPE", "uvec2"}, {"B_SIZE", "64"}, {"S_TYPE", src.second}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
}
|
||||
for (std::string t : {"f32", "f16", "bf16", "q1_0", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0", "iq4_nl"}) {
|
||||
string_to_spv("set_rows_" + t + "_i32", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(t), "1"}, {"B_TYPE", "uint"}, {"B_SIZE", "32"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
string_to_spv("set_rows_" + t + "_i64", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(t), "1"}, {"B_TYPE", "uvec2"}, {"B_SIZE", "64"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
}
|
||||
|
||||
auto get_type_str = [](bool f16) {
|
||||
@@ -1036,8 +1034,6 @@ void process_shaders() {
|
||||
}
|
||||
}
|
||||
|
||||
string_to_spv("out_prod_f32", "out_prod.comp", {});
|
||||
|
||||
string_to_spv("timestep_embedding_f32", "timestep_embedding.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
|
||||
|
||||
string_to_spv("conv_transpose_1d_f32", "conv_transpose_1d.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}});
|
||||
|
||||
+11
-23
@@ -1464,14 +1464,14 @@ bool ggml_is_transposed(const struct ggml_tensor * tensor) {
|
||||
return tensor->nb[0] > tensor->nb[1];
|
||||
}
|
||||
|
||||
static bool ggml_is_contiguous_m_n(const struct ggml_tensor * tensor, int m, int n) {
|
||||
static bool ggml_is_contiguous_n(const struct ggml_tensor * tensor, int n) {
|
||||
size_t next_nb = ggml_type_size(tensor->type);
|
||||
if (tensor->ne[0] != ggml_blck_size(tensor->type) && tensor->nb[0] != next_nb) {
|
||||
return false;
|
||||
}
|
||||
next_nb *= tensor->ne[0]/ggml_blck_size(tensor->type);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (i > m) {
|
||||
for (int i = 1; i < GGML_MAX_DIMS; i++) {
|
||||
if (i > n) {
|
||||
if (tensor->ne[i] != 1 && tensor->nb[i] != next_nb) {
|
||||
return false;
|
||||
}
|
||||
@@ -1489,27 +1489,15 @@ bool ggml_is_contiguous(const struct ggml_tensor * tensor) {
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_0(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 0, GGML_MAX_DIMS);
|
||||
return ggml_is_contiguous_n(tensor, 0);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_1(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 1, GGML_MAX_DIMS);
|
||||
return ggml_is_contiguous_n(tensor, 1);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_2(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 2, GGML_MAX_DIMS);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_to_1(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 0, 1);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_to_2(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 0, 2);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguous_to_3(const struct ggml_tensor * tensor) {
|
||||
return ggml_is_contiguous_m_n(tensor, 0, 3);
|
||||
return ggml_is_contiguous_n(tensor, 2);
|
||||
}
|
||||
|
||||
bool ggml_is_contiguously_allocated(const struct ggml_tensor * tensor) {
|
||||
@@ -4519,7 +4507,7 @@ struct ggml_tensor * ggml_conv_1d(
|
||||
int s0,
|
||||
int p0,
|
||||
int d0) {
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, b, s0, 0, p0, 0, d0, 0, false, a->type == GGML_TYPE_BF16 ? GGML_TYPE_F32 : GGML_TYPE_F16); // [N, OL, IC * K]
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, b, s0, 0, p0, 0, d0, 0, false, GGML_TYPE_F16); // [N, OL, IC * K]
|
||||
|
||||
struct ggml_tensor * result =
|
||||
ggml_mul_mat(ctx,
|
||||
@@ -4553,7 +4541,7 @@ struct ggml_tensor * ggml_conv_1d_dw(
|
||||
int d0) {
|
||||
struct ggml_tensor * new_b = ggml_reshape_4d(ctx, b, b->ne[0], 1, b->ne[1], b->ne[2]);
|
||||
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, new_b, s0, 0, p0, 0, d0, 0, false, a->type == GGML_TYPE_BF16 ? GGML_TYPE_F32 : GGML_TYPE_F16);
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, new_b, s0, 0, p0, 0, d0, 0, false, GGML_TYPE_F16);
|
||||
|
||||
struct ggml_tensor * result = ggml_mul_mat(ctx, im2col, a);
|
||||
|
||||
@@ -4659,7 +4647,7 @@ struct ggml_tensor * ggml_conv_2d(
|
||||
int p1,
|
||||
int d0,
|
||||
int d1) {
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, b, s0, s1, p0, p1, d0, d1, true, a->type == GGML_TYPE_BF16 ? GGML_TYPE_F32 : GGML_TYPE_F16); // [N, OH, OW, IC * KH * KW]
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, a, b, s0, s1, p0, p1, d0, d1, true, a->type); // [N, OH, OW, IC * KH * KW]
|
||||
|
||||
struct ggml_tensor * result =
|
||||
ggml_mul_mat(ctx,
|
||||
@@ -4741,7 +4729,7 @@ struct ggml_tensor * ggml_conv_3d(
|
||||
int d1, // dilation height
|
||||
int d2 // dilation depth
|
||||
) {
|
||||
struct ggml_tensor * im2col = ggml_im2col_3d(ctx, a, b, IC, s0, s1, s2, p0, p1, p2, d0, d1, d2, a->type == GGML_TYPE_BF16 ? GGML_TYPE_F32 : GGML_TYPE_F16); // [N*OD, OH, OW, IC * KD * KH * KW]
|
||||
struct ggml_tensor * im2col = ggml_im2col_3d(ctx, a, b, IC, s0, s1, s2, p0, p1, p2, d0, d1, d2, a->type); // [N*OD, OH, OW, IC * KD * KH * KW]
|
||||
|
||||
int64_t OC = a->ne[3] / IC;
|
||||
int64_t N = b->ne[3] / IC;
|
||||
@@ -4791,7 +4779,7 @@ struct ggml_tensor * ggml_conv_2d_dw(
|
||||
struct ggml_tensor * new_a = ggml_reshape_4d(ctx, a, a->ne[0], a->ne[1], 1, a->ne[2] * a->ne[3]);
|
||||
struct ggml_tensor * im2col = ggml_im2col(ctx, new_a,
|
||||
ggml_reshape_4d(ctx, b, b->ne[0], b->ne[1], 1, b->ne[2] * b->ne[3]),
|
||||
s0, s1, p0, p1, d0, d1, true, a->type == GGML_TYPE_BF16 ? GGML_TYPE_F32 : GGML_TYPE_F16); // [N * IC, OH, OW, KH * KW]
|
||||
s0, s1, p0, p1, d0, d1, true, GGML_TYPE_F16); // [N * IC, OH, OW, KH * KW]
|
||||
struct ggml_tensor * new_b = ggml_reshape_4d(ctx, im2col, im2col->ne[0], im2col->ne[2] * im2col->ne[1], b->ne[2], b->ne[3]); // [N * IC, OH, OW, KH * KW] => [N, IC, OH * OW, KH * KW]
|
||||
|
||||
new_a = ggml_reshape_4d(ctx, new_a, (new_a->ne[0] * new_a->ne[1]), new_a->ne[2], new_a->ne[3], 1); // [OC,1, KH, KW] => [1, OC, 1, KH * KW]
|
||||
|
||||
+15
-85
@@ -720,7 +720,7 @@ llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
auto it = ctx_map.find(buft);
|
||||
if (it == ctx_map.end()) {
|
||||
ggml_init_params params = {
|
||||
/*.mem_size =*/ size_t(2u*(1 + n_stream)*hparams.n_layer()*ggml_tensor_overhead()),
|
||||
/*.mem_size =*/ size_t(2u*hparams.n_layer()*ggml_tensor_overhead()),
|
||||
/*.mem_buffer =*/ NULL,
|
||||
/*.no_alloc =*/ true,
|
||||
};
|
||||
@@ -767,17 +767,9 @@ llama_dsv4_comp_state::llama_dsv4_comp_state(
|
||||
ggml_format_name(kv, "dsv4_%s_state_kv_l%d", name, il);
|
||||
ggml_format_name(score, "dsv4_%s_state_score_l%d", name, il);
|
||||
|
||||
std::vector<ggml_tensor *> kv_stream;
|
||||
std::vector<ggml_tensor *> score_stream;
|
||||
|
||||
for (uint32_t s = 0; s < n_stream; ++s) {
|
||||
kv_stream.push_back(ggml_view_2d(ctx, kv, n_embd_state, state_size, kv->nb[1], s*kv->nb[2]));
|
||||
score_stream.push_back(ggml_view_2d(ctx, score, n_embd_state, state_size, score->nb[1], s*score->nb[2]));
|
||||
}
|
||||
|
||||
map_layer_ids[il] = layers.size();
|
||||
|
||||
layers.push_back({ il, kv, score, std::move(kv_stream), std::move(score_stream) });
|
||||
layers.push_back({ il, kv, score });
|
||||
}
|
||||
|
||||
for (auto & [buft, ctx] : ctx_map) {
|
||||
@@ -817,30 +809,6 @@ void llama_dsv4_comp_state::clear(llama_seq_id seq_id, bool data) {
|
||||
}
|
||||
}
|
||||
|
||||
void llama_dsv4_comp_state::seq_cp(llama_seq_id seq_id_src, llama_seq_id seq_id_dst) {
|
||||
GGML_ASSERT(seq_id_src >= 0 && (uint32_t) seq_id_src < n_stream);
|
||||
GGML_ASSERT(seq_id_dst >= 0 && (uint32_t) seq_id_dst < n_stream);
|
||||
|
||||
if (seq_id_src == seq_id_dst) {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_info.ssrc.push_back((uint32_t) seq_id_src);
|
||||
sc_info.sdst.push_back((uint32_t) seq_id_dst);
|
||||
}
|
||||
|
||||
void llama_dsv4_comp_state::apply_copies(const stream_copy_info & sc_info) const {
|
||||
for (size_t i = 0; i < sc_info.ssrc.size(); ++i) {
|
||||
const uint32_t ssrc = sc_info.ssrc[i];
|
||||
const uint32_t sdst = sc_info.sdst[i];
|
||||
|
||||
for (const auto & layer : layers) {
|
||||
ggml_backend_tensor_copy(layer.kv_stream[ssrc], layer.kv_stream[sdst]);
|
||||
ggml_backend_tensor_copy(layer.score_stream[ssrc], layer.score_stream[sdst]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t llama_dsv4_comp_state::get_ratio() const {
|
||||
return ratio;
|
||||
}
|
||||
@@ -1186,13 +1154,7 @@ llama_memory_context_ptr llama_kv_cache_dsv4::init_full() {
|
||||
}
|
||||
|
||||
llama_memory_context_ptr llama_kv_cache_dsv4::init_update(llama_context * lctx, bool optimize) {
|
||||
return std::make_unique<llama_kv_cache_dsv4_context>(
|
||||
this,
|
||||
lctx,
|
||||
optimize,
|
||||
std::move(csa_state->sc_info),
|
||||
std::move(hca_state->sc_info),
|
||||
std::move(lid_state->sc_info));
|
||||
return std::make_unique<llama_kv_cache_dsv4_context>(this, lctx, optimize);
|
||||
}
|
||||
|
||||
bool llama_kv_cache_dsv4::get_can_shift() const {
|
||||
@@ -1212,19 +1174,14 @@ bool llama_kv_cache_dsv4::seq_rm(llama_seq_id seq_id, llama_pos p0, llama_pos p1
|
||||
}
|
||||
|
||||
if (p0 > 0) {
|
||||
if (seq_id < 0 || (uint32_t) seq_id >= n_seq_max ||
|
||||
p0 <= kv_raw->seq_pos_max(seq_id)) {
|
||||
return false;
|
||||
// DSV4 compressed cache rows are derived from running compressor state,
|
||||
// so arbitrary rollback is not reconstructible from the raw cache alone.
|
||||
// Allow the common prompt-cache cleanup no-op: remove [end, infinity).
|
||||
if (seq_id >= 0 && p0 > kv_raw->seq_pos_max(seq_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
res = res & kv_raw->seq_rm(seq_id, p0, -1);
|
||||
res = res & kv_csa->seq_rm(seq_id, p0/DSV4_CSA_RATIO, -1);
|
||||
res = res & kv_hca->seq_rm(seq_id, p0/DSV4_HCA_RATIO, -1);
|
||||
res = res & kv_lid->seq_rm(seq_id, p0/DSV4_CSA_RATIO, -1);
|
||||
|
||||
return res;
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool res = kv_raw->seq_rm(seq_id, p0, p1);
|
||||
@@ -1237,16 +1194,7 @@ bool llama_kv_cache_dsv4::seq_rm(llama_seq_id seq_id, llama_pos p0, llama_pos p1
|
||||
}
|
||||
|
||||
void llama_kv_cache_dsv4::seq_cp(llama_seq_id seq_id_src, llama_seq_id seq_id_dst, llama_pos p0, llama_pos p1) {
|
||||
GGML_ASSERT(p0 <= 0 && p1 < 0 && "DSV4 only supports full sequence copies");
|
||||
|
||||
kv_raw->seq_cp(seq_id_src, seq_id_dst, p0, p1);
|
||||
kv_csa->seq_cp(seq_id_src, seq_id_dst, -1, -1);
|
||||
kv_hca->seq_cp(seq_id_src, seq_id_dst, -1, -1);
|
||||
kv_lid->seq_cp(seq_id_src, seq_id_dst, -1, -1);
|
||||
|
||||
csa_state->seq_cp(seq_id_src, seq_id_dst);
|
||||
hca_state->seq_cp(seq_id_src, seq_id_dst);
|
||||
lid_state->seq_cp(seq_id_src, seq_id_dst);
|
||||
}
|
||||
|
||||
void llama_kv_cache_dsv4::seq_keep(llama_seq_id seq_id) {
|
||||
@@ -1691,26 +1639,20 @@ llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
llama_kv_cache_dsv4 * kv,
|
||||
llama_context * lctx,
|
||||
bool optimize,
|
||||
stream_copy_info sc_info_csa,
|
||||
stream_copy_info sc_info_hca,
|
||||
stream_copy_info sc_info_lid) :
|
||||
bool optimize) :
|
||||
ctx_raw(std::make_unique<llama_kv_cache_dsv4_raw_context>(kv->get_raw(), lctx, optimize)),
|
||||
ctx_csa_mem(kv->get_csa()->init_update(lctx, optimize)),
|
||||
ctx_hca_mem(kv->get_hca()->init_update(lctx, optimize)),
|
||||
ctx_lid_mem(kv->get_lid()->init_update(lctx, optimize)),
|
||||
ctx_csa(std::make_unique<llama_kv_cache_dsv4_comp_context>(kv->get_csa())),
|
||||
ctx_hca(std::make_unique<llama_kv_cache_dsv4_comp_context>(kv->get_hca())),
|
||||
ctx_lid(std::make_unique<llama_kv_cache_dsv4_comp_context>(kv->get_lid())),
|
||||
csa_state(kv->get_csa_state()),
|
||||
hca_state(kv->get_hca_state()),
|
||||
lid_state(kv->get_lid_state()),
|
||||
sc_info_csa(std::move(sc_info_csa)),
|
||||
sc_info_hca(std::move(sc_info_hca)),
|
||||
sc_info_lid(std::move(sc_info_lid)),
|
||||
status(llama_memory_status_combine(
|
||||
llama_memory_status_combine(
|
||||
llama_memory_status_combine(ctx_raw->get_status(), ctx_csa_mem->get_status()),
|
||||
llama_memory_status_combine(ctx_hca_mem->get_status(), ctx_lid_mem->get_status())),
|
||||
this->sc_info_csa.empty() && this->sc_info_hca.empty() && this->sc_info_lid.empty() ?
|
||||
LLAMA_MEMORY_STATUS_NO_UPDATE : LLAMA_MEMORY_STATUS_SUCCESS)) {
|
||||
llama_memory_status_combine(ctx_raw->get_status(), ctx_csa_mem->get_status()),
|
||||
llama_memory_status_combine(ctx_hca_mem->get_status(), ctx_lid_mem->get_status()))) {
|
||||
}
|
||||
|
||||
llama_kv_cache_dsv4_context::llama_kv_cache_dsv4_context(
|
||||
@@ -1778,18 +1720,6 @@ bool llama_kv_cache_dsv4_context::apply() {
|
||||
|
||||
res = res & ctx_raw->apply();
|
||||
|
||||
if (ctx_csa_mem) {
|
||||
res = res & ctx_csa_mem->apply();
|
||||
res = res & ctx_hca_mem->apply();
|
||||
res = res & ctx_lid_mem->apply();
|
||||
}
|
||||
|
||||
if (ubatches.empty()) {
|
||||
csa_state->apply_copies(sc_info_csa);
|
||||
hca_state->apply_copies(sc_info_hca);
|
||||
lid_state->apply_copies(sc_info_lid);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
|
||||
class llama_dsv4_comp_state {
|
||||
public:
|
||||
using stream_copy_info = llama_kv_cache::stream_copy_info;
|
||||
|
||||
stream_copy_info sc_info;
|
||||
|
||||
llama_dsv4_comp_state(
|
||||
const llama_model & model,
|
||||
bool offload,
|
||||
@@ -26,8 +22,6 @@ public:
|
||||
const llama_memory_i::layer_filter_cb & filter);
|
||||
|
||||
void clear(llama_seq_id seq_id, bool data);
|
||||
void seq_cp(llama_seq_id seq_id_src, llama_seq_id seq_id_dst);
|
||||
void apply_copies(const stream_copy_info & sc_info) const;
|
||||
|
||||
uint32_t get_ratio() const;
|
||||
uint32_t get_state_size() const;
|
||||
@@ -50,9 +44,6 @@ private:
|
||||
|
||||
ggml_tensor * kv;
|
||||
ggml_tensor * score;
|
||||
|
||||
std::vector<ggml_tensor *> kv_stream;
|
||||
std::vector<ggml_tensor *> score_stream;
|
||||
};
|
||||
|
||||
const uint32_t ratio;
|
||||
@@ -254,7 +245,6 @@ private:
|
||||
class llama_kv_cache_dsv4_context : public llama_memory_context_i {
|
||||
public:
|
||||
using slot_info_vec_t = llama_kv_cache::slot_info_vec_t;
|
||||
using stream_copy_info = llama_kv_cache::stream_copy_info;
|
||||
|
||||
struct comp_plan {
|
||||
// Per-ubatch recipe for updating compressor state, committing completed
|
||||
@@ -301,10 +291,7 @@ public:
|
||||
llama_kv_cache_dsv4_context(
|
||||
llama_kv_cache_dsv4 * kv,
|
||||
llama_context * lctx,
|
||||
bool optimize,
|
||||
stream_copy_info sc_info_csa,
|
||||
stream_copy_info sc_info_hca,
|
||||
stream_copy_info sc_info_lid);
|
||||
bool optimize);
|
||||
|
||||
llama_kv_cache_dsv4_context(
|
||||
llama_kv_cache_dsv4 * kv,
|
||||
@@ -364,13 +351,9 @@ private:
|
||||
const std::unique_ptr<llama_kv_cache_dsv4_comp_context> ctx_hca;
|
||||
const std::unique_ptr<llama_kv_cache_dsv4_comp_context> ctx_lid;
|
||||
|
||||
llama_dsv4_comp_state * csa_state = nullptr;
|
||||
llama_dsv4_comp_state * hca_state = nullptr;
|
||||
llama_dsv4_comp_state * lid_state = nullptr;
|
||||
|
||||
stream_copy_info sc_info_csa;
|
||||
stream_copy_info sc_info_hca;
|
||||
stream_copy_info sc_info_lid;
|
||||
const llama_dsv4_comp_state * csa_state = nullptr;
|
||||
const llama_dsv4_comp_state * hca_state = nullptr;
|
||||
const llama_dsv4_comp_state * lid_state = nullptr;
|
||||
|
||||
bool reserve_plans = false;
|
||||
mutable comp_plan reserve_plan_csa;
|
||||
|
||||
+12
-14
@@ -435,29 +435,27 @@ ggml_tensor * llama_model_deepseek4::graph::build_overlap_compressed_kv_from_sta
|
||||
kv_state = dsv4_append_zero_row(ctx0, kv_state, false);
|
||||
score_state = dsv4_append_zero_row(ctx0, score_state, true);
|
||||
|
||||
const int64_t n_read = ratio*n_blocks;
|
||||
ggml_tensor * prev_idxs = dsv4_view_1d(ctx0, state_read_idxs, ratio*n_blocks, 0);
|
||||
ggml_tensor * cur_idxs = dsv4_view_1d(ctx0, state_read_idxs, ratio*n_blocks, ratio*n_blocks);
|
||||
|
||||
ggml_tensor * kv_rows = ggml_get_rows(ctx0, kv_state, state_read_idxs);
|
||||
ggml_tensor * score_rows = ggml_get_rows(ctx0, score_state, state_read_idxs);
|
||||
|
||||
ggml_tensor * kv_prev = ggml_cont(ctx0,
|
||||
ggml_view_2d(ctx0, kv_rows, n_embd_head, n_read, kv_rows->nb[1], 0));
|
||||
ggml_tensor * kv_prev = ggml_get_rows(ctx0, kv_state, prev_idxs);
|
||||
kv_prev = ggml_cont(ctx0, ggml_view_2d(ctx0, kv_prev, n_embd_head, ratio*n_blocks, kv_prev->nb[1], 0));
|
||||
kv_prev = ggml_reshape_3d(ctx0, kv_prev, n_embd_head, ratio, n_blocks);
|
||||
cb(kv_prev, name, il);
|
||||
|
||||
ggml_tensor * score_prev = ggml_cont(ctx0,
|
||||
ggml_view_2d(ctx0, score_rows, n_embd_head, n_read, score_rows->nb[1], 0));
|
||||
ggml_tensor * score_prev = ggml_get_rows(ctx0, score_state, prev_idxs);
|
||||
score_prev = ggml_cont(ctx0, ggml_view_2d(ctx0, score_prev, n_embd_head, ratio*n_blocks, score_prev->nb[1], 0));
|
||||
score_prev = ggml_reshape_3d(ctx0, score_prev, n_embd_head, ratio, n_blocks);
|
||||
cb(score_prev, name, il);
|
||||
|
||||
ggml_tensor * kv_cur = ggml_cont(ctx0,
|
||||
ggml_view_2d(ctx0, kv_rows, n_embd_head, n_read, kv_rows->nb[1],
|
||||
n_read*kv_rows->nb[1] + ggml_row_size(kv_rows->type, n_embd_head)));
|
||||
ggml_tensor * kv_cur = ggml_get_rows(ctx0, kv_state, cur_idxs);
|
||||
kv_cur = ggml_cont(ctx0, ggml_view_2d(ctx0, kv_cur, n_embd_head, ratio*n_blocks, kv_cur->nb[1],
|
||||
ggml_row_size(kv_cur->type, n_embd_head)));
|
||||
kv_cur = ggml_reshape_3d(ctx0, kv_cur, n_embd_head, ratio, n_blocks);
|
||||
|
||||
ggml_tensor * score_cur = ggml_cont(ctx0,
|
||||
ggml_view_2d(ctx0, score_rows, n_embd_head, n_read, score_rows->nb[1],
|
||||
n_read*score_rows->nb[1] + ggml_row_size(score_rows->type, n_embd_head)));
|
||||
ggml_tensor * score_cur = ggml_get_rows(ctx0, score_state, cur_idxs);
|
||||
score_cur = ggml_cont(ctx0, ggml_view_2d(ctx0, score_cur, n_embd_head, ratio*n_blocks, score_cur->nb[1],
|
||||
ggml_row_size(score_cur->type, n_embd_head)));
|
||||
score_cur = ggml_reshape_3d(ctx0, score_cur, n_embd_head, ratio, n_blocks);
|
||||
|
||||
ggml_tensor * values = ggml_concat(ctx0, kv_prev, kv_cur, 1);
|
||||
|
||||
+16
-37
@@ -2423,10 +2423,11 @@ struct test_set_rows : public test_case {
|
||||
|
||||
void initialize_tensors(ggml_context * ctx) override {
|
||||
for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) {
|
||||
if (ggml_is_view_op(t->op)) {
|
||||
continue;
|
||||
}
|
||||
if (t->type == GGML_TYPE_I64 || t->type == GGML_TYPE_I32) {
|
||||
if (ggml_is_view_op(t->op)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
init_set_rows_row_ids(t, ne[1]);
|
||||
} else {
|
||||
init_tensor_uniform(t);
|
||||
@@ -2449,9 +2450,6 @@ struct test_set_rows : public test_case {
|
||||
err_estimate /= 8.0f;
|
||||
}
|
||||
err_estimate *= err_estimate;
|
||||
if (type_src == GGML_TYPE_F16) {
|
||||
err_estimate *= 16.0f;
|
||||
}
|
||||
err_estimate /= 0.25f*float(ne[0] * r * ne[2]*nr23[0] * ne[3]*nr23[1]);
|
||||
return err_estimate;
|
||||
}
|
||||
@@ -5555,7 +5553,7 @@ struct test_concat : public test_case {
|
||||
const std::array<int64_t, 4> ne_a;
|
||||
const int64_t ne_b_d;
|
||||
const int dim;
|
||||
const int v; // view (1 << 0: non-cont a (first 3 dim), 1 << 1: non-cont b (first 3 dim), 1 << 2: non-cont a (last 2 dim), 1 << 3: non-cont b (last 2 dim))
|
||||
const int v; // view (1 << 0: non-cont a, 1 << 1: non-cont b)
|
||||
|
||||
std::string vars() override {
|
||||
return VARS_TO_STR5(type, ne_a, ne_b_d, dim, v);
|
||||
@@ -5576,13 +5574,6 @@ struct test_concat : public test_case {
|
||||
a = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
ggml_set_name(a, "a");
|
||||
|
||||
a = ggml_view_4d(ctx, a, ne_a[0], ne_a[1], ne_a[2], ne_a[3], a->nb[1], a->nb[2], a->nb[3], 0);
|
||||
ggml_set_name(a, "view_of_a");
|
||||
} else if (v & 4) {
|
||||
auto ne = ne_a; ne[2] *= 2; ne[3] *= 4;
|
||||
a = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
ggml_set_name(a, "a");
|
||||
|
||||
a = ggml_view_4d(ctx, a, ne_a[0], ne_a[1], ne_a[2], ne_a[3], a->nb[1], a->nb[2], a->nb[3], 0);
|
||||
ggml_set_name(a, "view_of_a");
|
||||
} else {
|
||||
@@ -5595,13 +5586,6 @@ struct test_concat : public test_case {
|
||||
b = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
ggml_set_name(b, "b");
|
||||
|
||||
b = ggml_view_4d(ctx, b, ne_b[0], ne_b[1], ne_b[2], ne_b[3], b->nb[1], b->nb[2], b->nb[3], 0);
|
||||
ggml_set_name(b, "view_of_b");
|
||||
} else if (v & 8) {
|
||||
auto ne = ne_b; ne[2] *= 3; ne[3] *= 2;
|
||||
b = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
ggml_set_name(b, "b");
|
||||
|
||||
b = ggml_view_4d(ctx, b, ne_b[0], ne_b[1], ne_b[2], ne_b[3], b->nb[1], b->nb[2], b->nb[3], 0);
|
||||
ggml_set_name(b, "view_of_b");
|
||||
} else {
|
||||
@@ -7944,19 +7928,17 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_I64, { 1, 8, 1, 3 }, { 1, 1 }, 2, false));
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_I32, { 1, 8, 1, 3 }, { 1, 1 }, 2, false));
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, GGML_TYPE_Q8_0, GGML_TYPE_I32, { 256, 5, 1, 3 }, { 1, 1, }, 1, false));
|
||||
for (ggml_type src_type : {GGML_TYPE_F16, GGML_TYPE_F32}) {
|
||||
for (ggml_type type : all_types) {
|
||||
for (int b : {1, 7}) {
|
||||
for (bool v : {false, true}) {
|
||||
test_cases.emplace_back(new test_set_rows(src_type, type, GGML_TYPE_I64, { 256, 5, b, 3 }, { 1, 1, }, 1, v));
|
||||
test_cases.emplace_back(new test_set_rows(src_type, type, GGML_TYPE_I64, { 256, 11, 1, b }, { 2, 3, }, 7, v));
|
||||
for (ggml_type type : all_types) {
|
||||
for (int b : {1, 7}) {
|
||||
for (bool v : {false, true}) {
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, type, GGML_TYPE_I64, { 256, 5, b, 3 }, { 1, 1, }, 1, v));
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, type, GGML_TYPE_I64, { 256, 11, 1, b }, { 2, 3, }, 7, v));
|
||||
|
||||
test_cases.emplace_back(new test_set_rows(src_type, type, GGML_TYPE_I64, { 3*ggml_blck_size(type), 3, b, 1 }, { 2, 3, }, 2, v));
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, type, GGML_TYPE_I64, { 3*ggml_blck_size(type), 3, b, 1 }, { 2, 3, }, 2, v));
|
||||
|
||||
if (ggml_blck_size(type) == 1) {
|
||||
test_cases.emplace_back(new test_set_rows(src_type, type, GGML_TYPE_I64, { 31, 3, b, 1 }, { 2, 3, }, 2, v));
|
||||
test_cases.emplace_back(new test_set_rows(src_type, type, GGML_TYPE_I64, { 33, 5, 1, b }, { 2, 3, }, 1, v));
|
||||
}
|
||||
if (ggml_blck_size(type) == 1) {
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, type, GGML_TYPE_I64, { 31, 3, b, 1 }, { 2, 3, }, 2, v));
|
||||
test_cases.emplace_back(new test_set_rows(GGML_TYPE_F32, type, GGML_TYPE_I64, { 33, 5, 1, b }, { 2, 3, }, 1, v));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8031,7 +8013,6 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
|
||||
// im2col 2D
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_F32));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F32, GGML_TYPE_F16));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_F32));
|
||||
test_cases.emplace_back(new test_im2col(GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_F16));
|
||||
for (int s0 : {1, 3}) {
|
||||
@@ -9103,10 +9084,8 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
}
|
||||
|
||||
for (ggml_type type_a : { GGML_TYPE_Q4_0, GGML_TYPE_Q4_1, GGML_TYPE_Q5_0, GGML_TYPE_Q5_1, GGML_TYPE_Q8_0 }) {
|
||||
for (int v : { 0, 4, 8, 12 }) {
|
||||
for (int dim : { 0, 1, 2, 3, }) {
|
||||
test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, v));
|
||||
}
|
||||
for (int dim : { 0, 1, 2, 3, }) {
|
||||
test_cases.emplace_back(new test_concat(type_a, {128, 12, 13, 14}, dim == 0 ? 256 : 7, dim, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,10 +152,6 @@ int main(int argc, char ** argv) {
|
||||
init_result = common_init_from_params(params);
|
||||
|
||||
ctx = init_result->context();
|
||||
if (!ctx) {
|
||||
LOG_ERR("failed to initialize params\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
#ifdef LLAMA_HF_FETCH
|
||||
auto [hf_repo, hf_quant] = common_download_split_repo_tag(params.model.hf_repo);
|
||||
|
||||
@@ -220,6 +220,8 @@ struct server_slot {
|
||||
return false;
|
||||
}
|
||||
|
||||
GGML_ASSERT(prompt.data.size() == 0);
|
||||
|
||||
const size_t cur_size_tgt = llama_state_seq_get_size_ext(ctx_tgt, id, LLAMA_STATE_SEQ_FLAGS_NONE);
|
||||
const size_t cur_size_dft = ctx_dft ? llama_state_seq_get_size_ext(ctx_dft, id, LLAMA_STATE_SEQ_FLAGS_NONE) : 0;
|
||||
|
||||
@@ -250,7 +252,11 @@ struct server_slot {
|
||||
return res;
|
||||
}
|
||||
|
||||
void prompt_clear() {
|
||||
void prompt_clear(bool allow_processing) {
|
||||
if (!allow_processing) {
|
||||
GGML_ASSERT(!is_processing());
|
||||
}
|
||||
|
||||
SLT_TRC(*this, "clearing prompt with %zu tokens\n", prompt.tokens.size());
|
||||
|
||||
common_context_seq_rm(ctx_tgt, id, -1, -1);
|
||||
@@ -258,7 +264,7 @@ struct server_slot {
|
||||
common_context_seq_rm(ctx_dft, id, -1, -1);
|
||||
}
|
||||
|
||||
prompt.clear();
|
||||
prompt.tokens.clear();
|
||||
}
|
||||
|
||||
std::vector<common_adapter_lora_info> lora;
|
||||
@@ -487,7 +493,7 @@ struct server_slot {
|
||||
|
||||
// do not keep context of the child slots - the parent's context is enough
|
||||
if (task->is_child()) {
|
||||
prompt_clear();
|
||||
prompt_clear(false);
|
||||
}
|
||||
|
||||
reset();
|
||||
@@ -1620,7 +1626,7 @@ private:
|
||||
ret->prompt_save(*prompt_cache);
|
||||
|
||||
if (!ret->prompt_load(*prompt_cache, task.tokens)) {
|
||||
ret->prompt_clear();
|
||||
ret->prompt_clear(false);
|
||||
}
|
||||
|
||||
prompt_cache->update();
|
||||
@@ -1652,7 +1658,7 @@ private:
|
||||
if (slot.prompt.n_tokens() > 0) {
|
||||
SRV_WRN("purging slot %d with %zu tokens\n", slot.id, slot.prompt.tokens.size());
|
||||
|
||||
slot.prompt_clear();
|
||||
slot.prompt_clear(false);
|
||||
|
||||
res = true;
|
||||
|
||||
@@ -1685,7 +1691,7 @@ private:
|
||||
// if lora has changed, check to see if the cache should be cleared
|
||||
if (lora_should_clear_cache(slot.lora, task_loras)) {
|
||||
SLT_TRC(slot, "clearing cache for lora change. %zu loras -> %zu loras\n", slot.lora.size(), task.params.lora.size());
|
||||
slot.prompt.clear();
|
||||
slot.prompt.tokens.clear();
|
||||
} else {
|
||||
SLT_TRC(slot, "keeping cache for alora. %zu target loras\n", task_loras.size());
|
||||
}
|
||||
@@ -2399,7 +2405,7 @@ private:
|
||||
|
||||
if (params_base.kv_unified) {
|
||||
// [TAG_IDLE_SLOT_CLEAR]
|
||||
slot.prompt_clear();
|
||||
slot.prompt_clear(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2567,12 +2573,12 @@ private:
|
||||
size_t token_count = 0;
|
||||
size_t nread = llama_state_seq_load_file(ctx_tgt, filepath.c_str(), slot->id, tokens.data(), tokens.size(), &token_count);
|
||||
if (nread == 0) {
|
||||
slot->prompt.clear(); // KV may already been invalidated?
|
||||
slot->prompt.tokens.clear(); // KV may already been invalidated?
|
||||
send_error(task, "Unable to restore slot, no available space in KV cache or invalid slot save file", ERROR_TYPE_INVALID_REQUEST);
|
||||
break;
|
||||
}
|
||||
tokens.resize(token_count);
|
||||
slot->prompt.clear();
|
||||
slot->prompt.tokens.clear();
|
||||
slot->prompt.tokens.insert(tokens);
|
||||
|
||||
const int64_t t_end = ggml_time_us();
|
||||
@@ -2609,7 +2615,7 @@ private:
|
||||
// Erase token cache
|
||||
const size_t n_erased = slot->prompt.tokens.size();
|
||||
|
||||
slot->prompt_clear();
|
||||
slot->prompt_clear(false);
|
||||
|
||||
auto res = std::make_unique<server_task_result_slot_erase>();
|
||||
res->id = task.id;
|
||||
@@ -2769,27 +2775,6 @@ private:
|
||||
abort_all_slots("pre_decode() failed: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
GGML_ASSERT(batch.slot_batched || batch.size() == 0);
|
||||
|
||||
if (batch.slot_batched) {
|
||||
auto & slot_batched = batch.slot_batched;
|
||||
auto & alora_scale = batch.alora_scale;
|
||||
auto & alora_disabled_id = batch.alora_disabled_id;
|
||||
|
||||
// TODO @ngxson : alora handling is too messy, need to refactor it to be more clear and maintainable
|
||||
// apply lora, only need to do it once per batch
|
||||
common_set_adapter_lora(ctx_tgt, slot_batched->lora);
|
||||
|
||||
// if the lora is temporarily disabled for an alora, re-enable it
|
||||
// for next time
|
||||
if (alora_scale > 0.0f) {
|
||||
SRV_DBG("re-enabling alora with scale %f\n", alora_scale);
|
||||
slot_batched->lora[alora_disabled_id].scale = alora_scale;
|
||||
}
|
||||
|
||||
llama_set_embeddings(ctx_tgt, slot_batched->need_embd());
|
||||
}
|
||||
|
||||
llama_batch batch_view;
|
||||
int32_t off_next = 0;
|
||||
int32_t n_batch = llama_n_batch(ctx_tgt);
|
||||
@@ -2829,6 +2814,7 @@ private:
|
||||
abort_all_slots("post_decode() failed: " + std::string(e.what()));
|
||||
break; // stop any further processing
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2894,7 +2880,7 @@ private:
|
||||
|
||||
new_tokens.resize(slot.prompt.tokens.size() - n_discard);
|
||||
|
||||
slot.prompt.clear();
|
||||
slot.prompt.tokens.clear();
|
||||
slot.prompt.tokens.insert(new_tokens);
|
||||
}
|
||||
|
||||
@@ -3570,6 +3556,25 @@ private:
|
||||
bool decode(int32_t & n_batch, int32_t off, llama_batch & batch_view) {
|
||||
SRV_DBG("n_batch (effective) = %d, off = %d\n", n_batch, off);
|
||||
|
||||
auto & slot_batched = batch.slot_batched;
|
||||
auto & alora_scale = batch.alora_scale;
|
||||
auto & alora_disabled_id = batch.alora_disabled_id;
|
||||
|
||||
// TODO @ngxson : alora handling is too messy, need to refactor it to be more clear and maintainable
|
||||
if (slot_batched) {
|
||||
// apply lora, only need to do it once per batch
|
||||
common_set_adapter_lora(ctx_tgt, slot_batched->lora);
|
||||
|
||||
// if the lora is temporarily disabled for an alora, re-enable it
|
||||
// for next time
|
||||
if (alora_scale > 0.0f) {
|
||||
SRV_DBG("re-enabling alora with scale %f\n", alora_scale);
|
||||
slot_batched->lora[alora_disabled_id].scale = alora_scale;
|
||||
}
|
||||
|
||||
llama_set_embeddings(ctx_tgt, slot_batched->need_embd());
|
||||
}
|
||||
|
||||
if (batch.size() == 0) {
|
||||
SRV_WRN("%s", "no tokens to decode\n");
|
||||
|
||||
@@ -3617,7 +3622,7 @@ private:
|
||||
|
||||
// note: it's complicated to keep track of how much of the current batch has been
|
||||
// processed before the error occurred, so we simply clear the entire context
|
||||
slot.prompt_clear();
|
||||
slot.prompt_clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1646,16 +1646,16 @@ size_t server_prompt_cache::n_tokens() const {
|
||||
size_t res = 0;
|
||||
|
||||
for (const auto & state : states) {
|
||||
res += state.prompt.n_tokens();
|
||||
res += state.n_tokens();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
server_prompt_cache_state * server_prompt_cache::alloc(const server_prompt & prompt, size_t state_size_tgt, size_t state_size_dft) {
|
||||
server_prompt * server_prompt_cache::alloc(const server_prompt & prompt, size_t state_size_tgt, size_t state_size_dft) {
|
||||
// first check if the current state is contained fully in the cache
|
||||
for (auto it = states.begin(); it != states.end(); ++it) {
|
||||
const int cur_lcp_len = it->prompt.tokens.get_common_prefix(prompt.tokens);
|
||||
const int cur_lcp_len = it->tokens.get_common_prefix(prompt.tokens);
|
||||
|
||||
if (cur_lcp_len == (int) prompt.tokens.size()) {
|
||||
SRV_TRC("%s", " - prompt is already in the cache, skipping\n");
|
||||
@@ -1680,9 +1680,9 @@ server_prompt_cache_state * server_prompt_cache::alloc(const server_prompt & pro
|
||||
|
||||
// remove any cached prompts that are fully contained in the current prompt
|
||||
for (auto it = states.begin(); it != states.end();) {
|
||||
const int len = it->prompt.tokens.get_common_prefix(prompt.tokens);
|
||||
const int len = it->tokens.get_common_prefix(prompt.tokens);
|
||||
|
||||
if (len == (int) it->prompt.tokens.size()) {
|
||||
if (len == (int) it->tokens.size()) {
|
||||
SRV_TRC(" - removing obsolete cached prompt with length %d\n", len);
|
||||
|
||||
it = states.erase(it);
|
||||
@@ -1721,14 +1721,12 @@ server_prompt_cache_state * server_prompt_cache::alloc(const server_prompt & pro
|
||||
}
|
||||
|
||||
states.push_back({
|
||||
/*.prompt =*/ {
|
||||
/*.tokens =*/ prompt.tokens.clone(),
|
||||
/*.checkpoints =*/ prompt.checkpoints,
|
||||
},
|
||||
/*.data =*/ {
|
||||
/*.tokens =*/ prompt.tokens.clone(),
|
||||
/*.data =*/ {
|
||||
/*.main =*/ std::move(state_data_tgt),
|
||||
/*.drft =*/ std::move(state_data_dft),
|
||||
},
|
||||
/*.checkpoints =*/ prompt.checkpoints,
|
||||
});
|
||||
|
||||
return &states.back();
|
||||
@@ -1746,9 +1744,9 @@ bool server_prompt_cache::load(server_prompt & prompt, const server_tokens & tok
|
||||
|
||||
// find the most similar cached prompt, that would also preserve the most context
|
||||
for (auto it = states.begin(); it != states.end(); ++it) {
|
||||
const int lcp_cur = it->prompt.tokens.get_common_prefix(tokens_new);
|
||||
const int lcp_cur = it->tokens.get_common_prefix(tokens_new);
|
||||
|
||||
const float f_keep_cur = float(lcp_cur) / it->prompt.tokens.size();
|
||||
const float f_keep_cur = float(lcp_cur) / it->tokens.size();
|
||||
const float sim_cur = float(lcp_cur) / tokens_new.size();
|
||||
|
||||
// don't trash large prompts
|
||||
@@ -1801,7 +1799,7 @@ bool server_prompt_cache::load(server_prompt & prompt, const server_tokens & tok
|
||||
}
|
||||
}
|
||||
|
||||
prompt = std::move(it_best->prompt);
|
||||
prompt = std::move(*it_best);
|
||||
|
||||
states.erase(it_best);
|
||||
}
|
||||
@@ -1838,6 +1836,6 @@ void server_prompt_cache::update() {
|
||||
|
||||
for (const auto & state : states) {
|
||||
SRV_TRC(" - prompt %p: %7d tokens, checkpoints: %2zu, %9.3f MiB\n",
|
||||
(const void *)&state, state.prompt.n_tokens(), state.prompt.checkpoints.size(), state.size() / (1024.0 * 1024.0));
|
||||
(const void *)&state, state.n_tokens(), state.checkpoints.size(), state.size() / (1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
|
||||
+24
-29
@@ -584,28 +584,6 @@ struct server_task_result_apply_lora : server_task_result {
|
||||
virtual json to_json() override;
|
||||
};
|
||||
|
||||
struct server_prompt {
|
||||
server_tokens tokens;
|
||||
|
||||
std::list<common_prompt_checkpoint> checkpoints;
|
||||
|
||||
void clear() {
|
||||
tokens.clear();
|
||||
checkpoints.clear();
|
||||
}
|
||||
|
||||
int n_tokens() const {
|
||||
return tokens.size();
|
||||
}
|
||||
|
||||
server_prompt clone() const {
|
||||
return server_prompt {
|
||||
tokens.clone(),
|
||||
checkpoints,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct server_prompt_data {
|
||||
std::vector<uint8_t> main;
|
||||
std::vector<uint8_t> drft;
|
||||
@@ -615,19 +593,36 @@ struct server_prompt_data {
|
||||
}
|
||||
};
|
||||
|
||||
struct server_prompt_cache_state {
|
||||
server_prompt prompt;
|
||||
struct server_prompt {
|
||||
server_tokens tokens;
|
||||
|
||||
server_prompt_data data;
|
||||
|
||||
size_t size() const {
|
||||
size_t res = data.size();
|
||||
std::list<common_prompt_checkpoint> checkpoints;
|
||||
|
||||
for (const auto & ckpt : prompt.checkpoints) {
|
||||
size_t size() const {
|
||||
size_t res = 0;
|
||||
|
||||
res += data.size();
|
||||
|
||||
for (const auto & ckpt : checkpoints) {
|
||||
res += ckpt.size();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int n_tokens() const {
|
||||
return tokens.size();
|
||||
}
|
||||
|
||||
server_prompt clone() const {
|
||||
return server_prompt {
|
||||
tokens.clone(),
|
||||
data,
|
||||
checkpoints,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct server_prompt_cache {
|
||||
@@ -636,7 +631,7 @@ struct server_prompt_cache {
|
||||
this->limit_tokens = limit_tokens;
|
||||
}
|
||||
|
||||
std::list<server_prompt_cache_state> states;
|
||||
std::list<server_prompt> states;
|
||||
|
||||
// in bytes, 0 = no limit
|
||||
size_t limit_size = 0;
|
||||
@@ -648,7 +643,7 @@ struct server_prompt_cache {
|
||||
|
||||
size_t n_tokens() const;
|
||||
|
||||
server_prompt_cache_state * alloc(const server_prompt & prompt, size_t state_size_main, size_t state_size_drft);
|
||||
server_prompt * alloc(const server_prompt & prompt, size_t state_size_main, size_t state_size_drft);
|
||||
|
||||
bool load(server_prompt & prompt, const server_tokens & tokens_new, llama_context * ctx_main, llama_context * ctx_drft, int32_t id_slot);
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ struct server_tool_read_file : server_tool {
|
||||
{"function", {
|
||||
{"name", name},
|
||||
{"description", "Read the contents of a file. Optionally specify a 1-based line range. "
|
||||
"If append_loc is true, each line is prefixed with its line number (e.g. \"1\u2192...\")."},
|
||||
"If append_loc is true, each line is prefixed with its line number (e.g. \"1\u2192 ...\")."},
|
||||
{"parameters", {
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
@@ -339,7 +339,7 @@ struct server_tool_read_file : server_tool {
|
||||
|
||||
std::string out_line;
|
||||
if (append_loc) {
|
||||
out_line = std::to_string(lineno) + "\u2192" + line + "\n";
|
||||
out_line = std::to_string(lineno) + "\u2192 " + line + "\n";
|
||||
} else {
|
||||
out_line = line + "\n";
|
||||
}
|
||||
|
||||
@@ -12,9 +12,8 @@ def create_server():
|
||||
server = ServerPreset.stories15m_moe()
|
||||
# set default values
|
||||
server.model_draft = download_file(MODEL_DRAFT_FILE_URL)
|
||||
server.spec_type = "draft-simple"
|
||||
server.spec_draft_n_min = 4
|
||||
server.spec_draft_n_max = 8
|
||||
server.draft_min = 4
|
||||
server.draft_max = 8
|
||||
server.fa = "off"
|
||||
|
||||
|
||||
@@ -26,7 +25,6 @@ def fixture_create_server():
|
||||
def test_with_and_without_draft():
|
||||
global server
|
||||
server.model_draft = None # disable draft model
|
||||
server.spec_type = None
|
||||
server.start()
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "I believe the meaning of life is",
|
||||
@@ -48,7 +46,6 @@ def test_with_and_without_draft():
|
||||
"n_predict": 16,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["draft_n"] > 0
|
||||
content_draft = res.body["content"]
|
||||
|
||||
assert content_no_draft == content_draft
|
||||
@@ -66,8 +63,8 @@ def test_different_draft_min_draft_max():
|
||||
last_content = None
|
||||
for draft_min, draft_max in test_values:
|
||||
server.stop()
|
||||
server.spec_draft_n_min = draft_min
|
||||
server.spec_draft_n_max = draft_max
|
||||
server.draft_min = draft_min
|
||||
server.draft_max = draft_max
|
||||
server.start()
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "I believe the meaning of life is",
|
||||
|
||||
@@ -95,7 +95,6 @@ class ServerProcess:
|
||||
no_models_autoload: bool | None = None
|
||||
lora_files: List[str] | None = None
|
||||
enable_ctx_shift: int | None = False
|
||||
spec_type: str | None = None
|
||||
spec_draft_n_min: int | None = None
|
||||
spec_draft_n_max: int | None = None
|
||||
no_ui: bool | None = None
|
||||
@@ -227,8 +226,6 @@ class ServerProcess:
|
||||
server_args.extend(["--lora", lora_file])
|
||||
if self.enable_ctx_shift:
|
||||
server_args.append("--context-shift")
|
||||
if self.spec_type:
|
||||
server_args.extend(["--spec-type", self.spec_type])
|
||||
if self.api_key:
|
||||
server_args.extend(["--api-key", self.api_key])
|
||||
if self.spec_draft_n_max:
|
||||
|
||||
+262
-65
@@ -1,6 +1,5 @@
|
||||
#include "arg.h"
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
//#include "log.h" // TODO: start using log.h
|
||||
#include "llama.h"
|
||||
|
||||
#include <clocale>
|
||||
@@ -9,22 +8,115 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iostream> // TODO: remove me
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <shellapi.h> // For CommandLineToArgvW
|
||||
#endif
|
||||
|
||||
static void print_usage(int argc, char ** argv) {
|
||||
(void) argc;
|
||||
static void print_usage_information(const char * argv0) {
|
||||
printf("usage: %s [options]\n\n", argv0);
|
||||
printf("The tokenize program tokenizes a prompt using a given model,\n");
|
||||
printf("and prints the resulting tokens to standard output.\n\n");
|
||||
printf("It needs a model file, a prompt, and optionally other flags\n");
|
||||
printf("to control the behavior of the tokenizer.\n\n");
|
||||
printf(" The possible options are:\n");
|
||||
printf("\n");
|
||||
printf(" -h, --help print this help and exit\n");
|
||||
printf(" -m MODEL_PATH, --model MODEL_PATH path to model.\n");
|
||||
printf(" --ids if given, only print numerical token IDs, and not token strings.\n");
|
||||
printf(" The output format looks like [1, 2, 3], i.e. parseable by Python.\n");
|
||||
printf(" -f PROMPT_FNAME, --file PROMPT_FNAME read prompt from a file.\n");
|
||||
printf(" -p PROMPT, --prompt PROMPT read prompt from the argument.\n");
|
||||
printf(" --stdin read prompt from standard input.\n");
|
||||
printf(" --no-bos do not ever add a BOS token to the prompt, even if normally the model uses a BOS token.\n");
|
||||
printf(" --no-escape do not escape input (such as \\n, \\t, etc.).\n");
|
||||
printf(" --no-parse-special do not parse control tokens.\n");
|
||||
printf(" --log-disable disable logs. Makes stderr quiet when loading the model.\n");
|
||||
printf(" --show-count print the total number of tokens.\n");
|
||||
}
|
||||
|
||||
LOG("\nexample usage:\n");
|
||||
LOG("\n %s -m your_model.gguf -p \"Hello world\"\n", argv[0]);
|
||||
LOG("\n %s -m your_model.gguf -f prompt.txt --ids\n", argv[0]);
|
||||
LOG("\n cat prompt.txt | %s -m your_model.gguf --stdin --show-count\n", argv[0]);
|
||||
LOG("\n");
|
||||
static void llama_log_callback_null(ggml_log_level level, const char * text, void * user_data) {
|
||||
(void) level;
|
||||
(void) text;
|
||||
(void) user_data;
|
||||
}
|
||||
|
||||
static std::string read_prompt_from_file(const char * filepath, bool & success) {
|
||||
success = false;
|
||||
|
||||
std::ifstream in(filepath, std::ios::binary);
|
||||
if (!in) {
|
||||
fprintf(stderr, "%s: could not open file '%s' for reading: %s\n", __func__, filepath, strerror(errno));
|
||||
return std::string();
|
||||
}
|
||||
// do not assume the file is seekable (e.g. /dev/stdin)
|
||||
std::stringstream buffer;
|
||||
buffer << in.rdbuf();
|
||||
if (in.fail()) {
|
||||
fprintf(stderr, "%s: could not read the entire file '%s': %s\n", __func__, filepath, strerror(errno));
|
||||
return std::string();
|
||||
}
|
||||
|
||||
success = true;
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
//
|
||||
// Function: ingest_args(...) -> vector<string>
|
||||
//
|
||||
// Takes argc and argv arguments, and converts them to a vector of UTF-8 encoded
|
||||
// strings, as an STL vector<string>.
|
||||
//
|
||||
// In particular, it handles character encoding shenanigans on Windows.
|
||||
//
|
||||
// Note: raw_argc and raw_argv are not actually read at all on Windows.
|
||||
// On Windows we call GetCommandLineW to get the arguments in wchar_t
|
||||
// format, ignoring the regular argc/argv arguments to main().
|
||||
//
|
||||
// TODO: potential opportunity to roll common stuff into common/console.cpp
|
||||
// in relation to Windows wchar_t shenanigans.
|
||||
static std::vector<std::string> ingest_args(int raw_argc, char ** raw_argv) {
|
||||
std::vector<std::string> argv;
|
||||
|
||||
// Handle Windows, if given non-ASCII arguments.
|
||||
// We convert wchar_t arguments into UTF-8 char* on this platform.
|
||||
// Lets you invoke 'tokenize' on Windows cmd.exe with non-ASCII characters
|
||||
// without throwing tantrums.
|
||||
#if defined(_WIN32)
|
||||
int argc;
|
||||
const LPWSTR cmdline_wargv = GetCommandLineW();
|
||||
LPWSTR * wargv = CommandLineToArgvW(cmdline_wargv, &argc);
|
||||
|
||||
// silence unused arg warnings
|
||||
(void) raw_argc;
|
||||
(void) raw_argv;
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
int length_needed = WideCharToMultiByte(CP_UTF8, 0, wargv[i], wcslen(wargv[i]), 0, 0, NULL, NULL);
|
||||
char * output_buf = (char *) calloc(length_needed+1, sizeof(char));
|
||||
GGML_ASSERT(output_buf);
|
||||
|
||||
WideCharToMultiByte(CP_UTF8, 0, wargv[i], wcslen(wargv[i]), output_buf, length_needed, NULL, NULL);
|
||||
output_buf[length_needed] = '\0';
|
||||
|
||||
argv.push_back(output_buf);
|
||||
free(output_buf);
|
||||
}
|
||||
|
||||
LocalFree((HLOCAL) wargv);
|
||||
#else
|
||||
int argc = raw_argc;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
argv.push_back(raw_argv[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
GGML_ASSERT((unsigned int) argc == argv.size());
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -92,61 +184,166 @@ static void write_utf8_cstr_to_stdout(const char * str, bool & invalid_utf8) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
int main(int raw_argc, char ** raw_argv) {
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
common_params params;
|
||||
const std::vector<std::string> argv = ingest_args(raw_argc, raw_argv);
|
||||
const int argc = argv.size();
|
||||
|
||||
common_init();
|
||||
|
||||
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_TOKENIZE, print_usage)) {
|
||||
if (argc <= 1) {
|
||||
print_usage_information(argv[0].c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -f and -p both land in params.prompt; -f also sets prompt_file. -f and -p
|
||||
// resolve like the other tools (no mutual exclusion), --stdin takes precedence.
|
||||
const bool use_stdin = params.tokenize_stdin;
|
||||
const bool use_file = !params.prompt_file.empty();
|
||||
//////
|
||||
// Read out all the command line arguments.
|
||||
//////
|
||||
|
||||
// must have some prompt
|
||||
if (!use_stdin && !use_file && params.prompt.empty()) {
|
||||
LOG_ERR("error: must specify one of: --stdin, --file or --prompt\n");
|
||||
return 1;
|
||||
}
|
||||
// variables where to put any arguments we see.
|
||||
bool printing_ids = false;
|
||||
bool no_bos = false;
|
||||
bool no_escape = false;
|
||||
bool no_parse_special = false;
|
||||
bool disable_logging = false;
|
||||
bool show_token_count = false;
|
||||
const char * model_path = NULL;
|
||||
const char * prompt_path = NULL;
|
||||
const char * prompt_arg = NULL;
|
||||
|
||||
std::string prompt;
|
||||
if (use_file) {
|
||||
// read the file verbatim: common's -f handler strips a single trailing
|
||||
// newline, but for a tokenizer the input bytes must be preserved exactly
|
||||
// (a trailing newline is itself a token). escapes are applied locally
|
||||
// to match the behavior of -p/--prompt and --stdin.
|
||||
std::ifstream in(params.prompt_file, std::ios::binary);
|
||||
if (!in) {
|
||||
LOG_ERR("error: could not open file '%s' for reading\n", params.prompt_file.c_str());
|
||||
// track which arguments were explicitly given
|
||||
// used for sanity checking down the line
|
||||
bool model_path_set = false;
|
||||
bool prompt_path_set = false;
|
||||
bool prompt_set = false;
|
||||
bool stdin_set = false;
|
||||
|
||||
int iarg = 1;
|
||||
for (; iarg < argc; ++iarg) {
|
||||
std::string arg{argv[iarg]};
|
||||
if (arg == "-h" || arg == "--help") {
|
||||
print_usage_information(argv[0].c_str());
|
||||
return 0;
|
||||
}
|
||||
else if (arg == "--ids") {
|
||||
printing_ids = true;
|
||||
}
|
||||
else if (arg == "-m" || arg == "--model") {
|
||||
if (model_path_set) {
|
||||
fprintf(stderr, "Error: -m or --model specified multiple times.\n");
|
||||
return 1;
|
||||
}
|
||||
model_path = argv[++iarg].c_str();
|
||||
model_path_set = true;
|
||||
}
|
||||
else if (arg == "--no-bos") {
|
||||
no_bos = true;
|
||||
}
|
||||
else if (arg == "--no-escape") {
|
||||
no_escape = true;
|
||||
}
|
||||
else if (arg == "--no-parse-special") {
|
||||
no_parse_special = true;
|
||||
}
|
||||
else if (arg == "-p" || arg == "--prompt") {
|
||||
if (prompt_set) {
|
||||
fprintf(stderr, "Error: -p or --prompt specified multiple times.\n");
|
||||
return 1;
|
||||
}
|
||||
prompt_arg = argv[++iarg].c_str();
|
||||
prompt_set = true;
|
||||
}
|
||||
else if (arg == "-f" || arg == "--file") {
|
||||
if (prompt_path_set) {
|
||||
fprintf(stderr, "Error: -f or --file specified multiple times.\n");
|
||||
return 1;
|
||||
}
|
||||
prompt_path = argv[++iarg].c_str();
|
||||
prompt_path_set = true;
|
||||
}
|
||||
else if (arg == "--stdin") {
|
||||
stdin_set = true;
|
||||
}
|
||||
else if (arg == "--log-disable") {
|
||||
disable_logging = true;
|
||||
}
|
||||
else if (arg == "--show-count") {
|
||||
show_token_count = true;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: unknown option '%s'\n", argv[iarg].c_str());
|
||||
return 1;
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << in.rdbuf();
|
||||
prompt = ss.str();
|
||||
if (params.escape) {
|
||||
string_process_escapes(prompt);
|
||||
}
|
||||
} else if (!use_stdin) {
|
||||
// -p/--prompt is already escape-processed by common_params_parse()
|
||||
// (controlled by --escape/--no-escape), so use it verbatim here.
|
||||
prompt = params.prompt;
|
||||
}
|
||||
// else: we read stdin *after* loading the model (early exit if the
|
||||
// model cannot be loaded, which is a nicer user experience)
|
||||
|
||||
//////
|
||||
// Sanity check the command line arguments.
|
||||
//////
|
||||
|
||||
// Check that we have the required stuff set.
|
||||
if (model_path_set && model_path == NULL) {
|
||||
fprintf(stderr, "Error: --model requires an argument.\n");
|
||||
return 1;
|
||||
}
|
||||
if (!model_path_set) {
|
||||
fprintf(stderr, "Error: must specify --model.\n");
|
||||
return 1;
|
||||
}
|
||||
if (prompt_path_set && prompt_path == NULL) {
|
||||
fprintf(stderr, "Error: --file requires an argument.\n");
|
||||
return 1;
|
||||
}
|
||||
if (prompt_set && prompt_arg == NULL) {
|
||||
fprintf(stderr, "Error: --prompt requires an argument.\n");
|
||||
return 1;
|
||||
}
|
||||
const int prompts_set = !!(prompt_path_set) + !!(prompt_set) + !!(stdin_set);
|
||||
if (prompts_set > 1) {
|
||||
fprintf(stderr, "Error: --stdin, --file and --prompt are mutually exclusive.\n");
|
||||
return 1;
|
||||
}
|
||||
// Must have some prompt.
|
||||
if (prompts_set == 0) {
|
||||
fprintf(stderr, "Error: must specify one of: --stdin, --file or --prompt.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
GGML_ASSERT(model_path);
|
||||
GGML_ASSERT(prompt_path || prompt_arg || stdin_set);
|
||||
|
||||
//////
|
||||
// Figure out where will the prompt come from.
|
||||
//////
|
||||
|
||||
std::string prompt;
|
||||
if (prompt_path_set) {
|
||||
bool success = false;
|
||||
prompt = read_prompt_from_file(prompt_path, success);
|
||||
if (!success) {
|
||||
return 1;
|
||||
}
|
||||
} else if (prompt_set) {
|
||||
prompt = prompt_arg;
|
||||
} else {
|
||||
GGML_ASSERT(stdin_set);
|
||||
// we read stdin *after* loading model (early exit if model cannot
|
||||
// be loaded, which can be a nicer user experience)
|
||||
}
|
||||
|
||||
//////
|
||||
// Start actually doing the tokenizing stuff.
|
||||
//////
|
||||
|
||||
if (disable_logging) {
|
||||
llama_log_set(llama_log_callback_null, NULL);
|
||||
}
|
||||
|
||||
llama_backend_init();
|
||||
|
||||
// load only the vocabulary (no weights), since tokenizing does not need them
|
||||
llama_model_params model_params = llama_model_default_params();
|
||||
model_params.vocab_only = true;
|
||||
llama_model * model = llama_model_load_from_file(params.model.path.c_str(), model_params);
|
||||
llama_model * model = llama_model_load_from_file(model_path, model_params);
|
||||
if (!model) {
|
||||
LOG_ERR("error: could not load model from file '%s'.\n", params.model.path.c_str());
|
||||
fprintf(stderr, "Error: could not load model from file '%s'.\n", model_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -155,41 +352,42 @@ int main(int argc, char ** argv) {
|
||||
llama_context_params ctx_params = llama_context_default_params();
|
||||
llama_context * ctx = llama_init_from_model(model, ctx_params);
|
||||
if (!ctx) {
|
||||
LOG_ERR("error: could not create context.\n");
|
||||
fprintf(stderr, "Error: could not create context.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read entire prompt from stdin?
|
||||
if (params.tokenize_stdin) {
|
||||
if (stdin_set) {
|
||||
GGML_ASSERT(!prompt_path_set && !prompt_set);
|
||||
|
||||
std::stringstream stdin_buffer;
|
||||
stdin_buffer << std::cin.rdbuf();
|
||||
if (std::cin.fail()) {
|
||||
LOG_ERR("error: could not read the entire standard input.\n");
|
||||
fprintf(stderr, "Error: could not read the entire standard input.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
prompt = stdin_buffer.str();
|
||||
|
||||
// stdin is not seen by common_params_parse(), so apply escape handling
|
||||
// here to match the behavior of -p/--prompt and -f/--file.
|
||||
if (params.escape) {
|
||||
string_process_escapes(prompt);
|
||||
}
|
||||
}
|
||||
|
||||
const bool model_wants_add_bos = llama_vocab_get_add_bos(vocab);
|
||||
const bool add_bos = model_wants_add_bos && !params.tokenize_no_bos;
|
||||
const bool parse_special = params.parse_special;
|
||||
const bool add_bos = model_wants_add_bos && !no_bos;
|
||||
const bool parse_special = !no_parse_special;
|
||||
const bool escape = !no_escape;
|
||||
|
||||
if (escape) {
|
||||
string_process_escapes(prompt);
|
||||
}
|
||||
|
||||
std::vector<llama_token> tokens;
|
||||
tokens = common_tokenize(vocab, prompt, add_bos, parse_special);
|
||||
|
||||
if (params.tokenize_ids) {
|
||||
if (printing_ids) {
|
||||
printf("[");
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int) tokens.size(); i++) {
|
||||
if (params.tokenize_ids) {
|
||||
if (printing_ids) {
|
||||
if (i > 0) {
|
||||
printf(", ");
|
||||
}
|
||||
@@ -206,14 +404,13 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (params.tokenize_ids) {
|
||||
if (printing_ids) {
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
if (params.tokenize_show_count) {
|
||||
if (show_token_count) {
|
||||
printf("Total number of tokens: %zu\n", tokens.size());
|
||||
}
|
||||
|
||||
// silence valgrind
|
||||
llama_free(ctx);
|
||||
llama_model_free(model);
|
||||
|
||||
@@ -29,13 +29,6 @@ export default ts.config(
|
||||
// This app uses hash-based routing (#/) where resolve() from $app/paths does not apply
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
|
||||
// Snippet bodies often ignore one or more of the parent's params
|
||||
// (e.g. `{#snippet children(_meta, ctx)}` when only ctx is read).
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' }
|
||||
],
|
||||
|
||||
// Enforce empty line at end of file
|
||||
'eol-last': 'error'
|
||||
}
|
||||
|
||||
@@ -193,33 +193,6 @@
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.shimmer-text {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--muted-foreground),
|
||||
var(--foreground),
|
||||
var(--muted-foreground)
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: 500;
|
||||
animation: shimmer 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
to {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.shimmer-text {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mermaidTooltip {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Copy } from '@lucide/svelte';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
import ActionIcon from './ActionIcon.svelte';
|
||||
@@ -12,7 +11,7 @@
|
||||
<ActionIcon
|
||||
icon={Copy}
|
||||
tooltip={ariaLabel}
|
||||
iconSize={ICON_CLASS_DEFAULT}
|
||||
iconSize="h-4 w-4"
|
||||
disabled={!canCopy}
|
||||
onclick={() => canCopy && copyToClipboard(text)}
|
||||
/>
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { X, Music, Video } from '@lucide/svelte';
|
||||
import {
|
||||
formatFileSize,
|
||||
@@ -110,9 +109,9 @@
|
||||
class="flex h-8 w-8 items-center justify-center rounded bg-primary/10 text-xs font-medium text-primary"
|
||||
>
|
||||
{#if isAudio}
|
||||
<Music class="{ICON_CLASS_DEFAULT} text-white/70" />
|
||||
<Music class="h-4 w-4 text-white/70" />
|
||||
{:else if isVideo}
|
||||
<Video class="{ICON_CLASS_DEFAULT} text-white/70" />
|
||||
<Video class="h-4 w-4 text-white/70" />
|
||||
{:else}
|
||||
{fileTypeLabel}
|
||||
{/if}
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import type { ChatAttachmentDisplayItem } from '$lib/types';
|
||||
import { FileText, Eye, Info } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
@@ -89,7 +88,7 @@
|
||||
onclick={() => (pdfViewMode = PdfViewMode.TEXT)}
|
||||
disabled={pdfImagesLoading}
|
||||
>
|
||||
<FileText class="mr-1 {ICON_CLASS_DEFAULT}" />
|
||||
<FileText class="mr-1 h-4 w-4" />
|
||||
Text
|
||||
</Button>
|
||||
|
||||
@@ -101,10 +100,10 @@
|
||||
>
|
||||
{#if pdfImagesLoading}
|
||||
<div
|
||||
class="mr-1 {ICON_CLASS_DEFAULT} animate-spin rounded-full border-2 border-current border-t-transparent"
|
||||
class="mr-1 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent"
|
||||
></div>
|
||||
{:else}
|
||||
<Eye class="mr-1 {ICON_CLASS_DEFAULT}" />
|
||||
<Eye class="mr-1 h-4 w-4" />
|
||||
{/if}
|
||||
Pages
|
||||
</Button>
|
||||
@@ -112,7 +111,7 @@
|
||||
|
||||
{#if !hasVisionModality && activeModelId && currentItem}
|
||||
<Alert.Root class="mb-4 max-w-4xl">
|
||||
<Info class={ICON_CLASS_DEFAULT} />
|
||||
<Info class="h-4 w-4" />
|
||||
<Alert.Title>Preview only</Alert.Title>
|
||||
<Alert.Description>
|
||||
<span class="inline-flex">
|
||||
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Music, Video, FileText } from '@lucide/svelte';
|
||||
import { HorizontalScrollCarousel } from '$lib/components/app/misc';
|
||||
|
||||
@@ -50,11 +49,11 @@
|
||||
class="bg-foreground-muted/50 flex h-12 w-12 flex-col items-center justify-center gap-0.5 py-1"
|
||||
>
|
||||
{#if item.isAudio}
|
||||
<Music class="{ICON_CLASS_DEFAULT} text-white/70" />
|
||||
<Music class="h-4 w-4 text-white/70" />
|
||||
{:else if item.isVideo}
|
||||
<Video class="{ICON_CLASS_DEFAULT} text-white/70" />
|
||||
<Video class="h-4 w-4 text-white/70" />
|
||||
{:else}
|
||||
<FileText class="{ICON_CLASS_DEFAULT} text-white/70" />
|
||||
<FileText class="h-4 w-4 text-white/70" />
|
||||
{/if}
|
||||
|
||||
<span class="font-mono text-[9px] text-white/60">{getFileExtension(item.name)}</span>
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Plus } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
@@ -24,7 +23,7 @@
|
||||
>
|
||||
<span class="sr-only">{ATTACHMENT_TOOLTIP_TEXT}</span>
|
||||
|
||||
<Plus class={ICON_CLASS_DEFAULT} />
|
||||
<Plus class="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
|
||||
|
||||
+7
-8
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Plus, File, MessageSquare, Zap, FolderOpen } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
@@ -84,7 +83,7 @@
|
||||
>
|
||||
<span class="sr-only">{ATTACHMENT_TOOLTIP_TEXT}</span>
|
||||
|
||||
<Plus class={ICON_CLASS_DEFAULT} />
|
||||
<Plus class="h-4 w-4" />
|
||||
</DropdownMenu.Trigger>
|
||||
{/snippet}
|
||||
</Tooltip.Trigger>
|
||||
@@ -101,7 +100,7 @@
|
||||
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
<File class={ICON_CLASS_DEFAULT} />
|
||||
<File class="h-4 w-4" />
|
||||
|
||||
<span>Add files</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -114,7 +113,7 @@
|
||||
class="{item.class ?? ''} flex cursor-pointer items-center gap-2"
|
||||
onclick={() => attachmentMenu.callbacks[item.action]()}
|
||||
>
|
||||
<item.icon class={ICON_CLASS_DEFAULT} />
|
||||
<item.icon class="h-4 w-4" />
|
||||
|
||||
<span>{item.label}</span>
|
||||
</DropdownMenu.Item>
|
||||
@@ -127,7 +126,7 @@
|
||||
class="{item.class ?? ''} flex items-center gap-2"
|
||||
disabled
|
||||
>
|
||||
<item.icon class={ICON_CLASS_DEFAULT} />
|
||||
<item.icon class="h-4 w-4" />
|
||||
|
||||
<span>{item.label}</span>
|
||||
</DropdownMenu.Item>
|
||||
@@ -148,7 +147,7 @@
|
||||
class="flex cursor-pointer items-center gap-2"
|
||||
onclick={onSystemPromptClick}
|
||||
>
|
||||
<MessageSquare class={ICON_CLASS_DEFAULT} />
|
||||
<MessageSquare class="h-4 w-4" />
|
||||
|
||||
<span>System Message</span>
|
||||
</DropdownMenu.Item>
|
||||
@@ -164,7 +163,7 @@
|
||||
class="flex cursor-pointer items-center gap-2"
|
||||
onclick={onMcpPromptClick}
|
||||
>
|
||||
<Zap class={ICON_CLASS_DEFAULT} />
|
||||
<Zap class="h-4 w-4" />
|
||||
|
||||
<span>MCP Prompt</span>
|
||||
</DropdownMenu.Item>
|
||||
@@ -175,7 +174,7 @@
|
||||
class="flex cursor-pointer items-center gap-2"
|
||||
onclick={onMcpResourcesClick}
|
||||
>
|
||||
<FolderOpen class={ICON_CLASS_DEFAULT} />
|
||||
<FolderOpen class="h-4 w-4" />
|
||||
|
||||
<span>MCP Resources</span>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
+8
-9
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Settings, Plus } from '@lucide/svelte';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
@@ -18,10 +17,10 @@
|
||||
let { onMcpSettingsClick }: Props = $props();
|
||||
|
||||
let mcpSearchQuery = $state('');
|
||||
// Every configured server is listed; `enabled` is an on/off state,
|
||||
// not a visibility filter, so a disabled server stays toggleable.
|
||||
let mcpServers = $derived(mcpStore.getServers());
|
||||
let allMcpServers = $derived(mcpStore.getServers());
|
||||
let mcpServers = $derived(mcpStore.visibleMcpServers);
|
||||
let hasMcpServers = $derived(mcpServers.length > 0);
|
||||
// let hasAnyMcpServers = $derived(allMcpServers.length > 0);
|
||||
let filteredMcpServers = $derived.by(() => {
|
||||
const query = mcpSearchQuery.toLowerCase().trim();
|
||||
if (!query) return mcpServers;
|
||||
@@ -47,7 +46,7 @@
|
||||
function handleMcpSubMenuOpen(open: boolean) {
|
||||
if (open) {
|
||||
mcpSearchQuery = '';
|
||||
mcpStore.runHealthChecksForServers(mcpServers);
|
||||
mcpStore.runHealthChecksForServers(allMcpServers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +60,7 @@
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Sub onOpenChange={handleMcpSubMenuOpen}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
<McpLogo class={ICON_CLASS_DEFAULT} />
|
||||
<McpLogo class="h-4 w-4" />
|
||||
|
||||
<span>MCP Servers</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -93,7 +92,7 @@
|
||||
<McpServerIdentity
|
||||
{displayName}
|
||||
{faviconUrl}
|
||||
iconClass={ICON_CLASS_DEFAULT}
|
||||
iconClass="h-4 w-4"
|
||||
iconRounded="rounded-sm"
|
||||
showVersion={false}
|
||||
nameClass="text-sm"
|
||||
@@ -124,7 +123,7 @@
|
||||
class="flex cursor-pointer items-center gap-2"
|
||||
onclick={handleMcpSettingsClick}
|
||||
>
|
||||
<Settings class={ICON_CLASS_DEFAULT} />
|
||||
<Settings class="h-4 w-4" />
|
||||
|
||||
<span>Manage MCP Servers</span>
|
||||
</DropdownMenu.Item>
|
||||
@@ -141,7 +140,7 @@
|
||||
class="flex cursor-pointer items-center gap-2"
|
||||
onclick={handleMcpSettingsClick}
|
||||
>
|
||||
<Plus class={ICON_CLASS_DEFAULT} />
|
||||
<Plus class="h-4 w-4" />
|
||||
|
||||
<span>Add MCP Servers</span>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
+76
-25
@@ -1,33 +1,86 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Lightbulb, LightbulbOff, Check, Info } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { useReasoningMenu } from '$lib/hooks/use-reasoning-menu.svelte';
|
||||
import { ReasoningEffort } from '$lib/enums';
|
||||
import { REASONING_EFFORT_TOKENS } from '$lib/constants/reasoning-effort-tokens';
|
||||
import { REASONING_EFFORT_LEVELS } from '$lib/constants/reasoning-effort';
|
||||
import type { ReasoningEffortLevel } from '$lib/types';
|
||||
import {
|
||||
modelsStore,
|
||||
checkModelSupportsThinking,
|
||||
supportsThinking,
|
||||
propsCacheVersion,
|
||||
loadedModelIds
|
||||
} from '$lib/stores/models.svelte';
|
||||
import { chatStore } from '$lib/stores/chat.svelte';
|
||||
import { conversationsStore, activeMessages } from '$lib/stores/conversations.svelte';
|
||||
import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
import type { DatabaseMessage } from '$lib/types/database';
|
||||
|
||||
let subOpen = $state(false);
|
||||
|
||||
const reasoning = useReasoningMenu();
|
||||
let conversationModel = $derived(
|
||||
chatStore.getConversationModel(activeMessages() as DatabaseMessage[])
|
||||
);
|
||||
|
||||
let modelSupportsThinkingFromMessages = $derived.by(() => {
|
||||
const modelId = isRouterMode() ? modelsStore.selectedModelName || conversationModel : null;
|
||||
if (!modelId) return false;
|
||||
|
||||
const messages = conversationsStore.activeMessages;
|
||||
|
||||
return messages.some(
|
||||
(m) => m.role === 'assistant' && m.model === modelId && !!m.reasoningContent
|
||||
);
|
||||
});
|
||||
|
||||
let modelSupportsThinking = $derived.by(() => {
|
||||
loadedModelIds();
|
||||
propsCacheVersion();
|
||||
|
||||
if (isRouterMode()) {
|
||||
const modelId = modelsStore.selectedModelName || conversationModel;
|
||||
return checkModelSupportsThinking(modelId ?? '') || modelSupportsThinkingFromMessages;
|
||||
}
|
||||
|
||||
return supportsThinking() || modelSupportsThinkingFromMessages;
|
||||
});
|
||||
|
||||
let thinkingEnabled = $derived(conversationsStore.getThinkingEnabled());
|
||||
let currentEffort = $derived(conversationsStore.getReasoningEffort());
|
||||
let isOff = $derived(!thinkingEnabled);
|
||||
|
||||
function isSelected(item: ReasoningEffortLevel): boolean {
|
||||
if (item.isOff) return isOff;
|
||||
return thinkingEnabled && currentEffort === item.value;
|
||||
}
|
||||
|
||||
function handleSelection(item: ReasoningEffortLevel) {
|
||||
if (item.isOff) {
|
||||
conversationsStore.setThinkingEnabled(false);
|
||||
} else {
|
||||
conversationsStore.setThinkingEnabled(true);
|
||||
conversationsStore.setReasoningEffort(item.value as ReasoningEffort);
|
||||
}
|
||||
subOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if reasoning.modelSupportsThinking}
|
||||
{#if modelSupportsThinking}
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{#if thinkingEnabled}
|
||||
<Lightbulb class="h-4 w-4 shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
<LightbulbOff class="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span
|
||||
class="text-sm inline-flex gap-2 {!reasoning.thinkingEnabled
|
||||
? 'text-muted-foreground'
|
||||
: ''}"
|
||||
>
|
||||
<span class="text-sm inline-flex gap-2 {!thinkingEnabled ? 'text-muted-foreground' : ''}">
|
||||
Reasoning
|
||||
|
||||
<span class="capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
{thinkingEnabled ? currentEffort : 'off'}
|
||||
</span>
|
||||
</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -35,28 +88,26 @@
|
||||
<DropdownMenu.SubContent
|
||||
class="w-60 bg-popover p-1.5 text-popover-foreground shadow-md outline-none"
|
||||
>
|
||||
{#each reasoning.levels as level (level.value)}
|
||||
{@const tokenLabel = reasoning.tokenLabel(level)}
|
||||
{#each REASONING_EFFORT_LEVELS as level (level.value)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2 py-1.75 text-left text-sm transition-colors hover:bg-accent"
|
||||
class:bg-accent={reasoning.isSelected(level)}
|
||||
onclick={() => {
|
||||
reasoning.select(level);
|
||||
subOpen = false;
|
||||
}}
|
||||
class:bg-accent={isSelected(level)}
|
||||
onclick={() => handleSelection(level)}
|
||||
>
|
||||
{#if reasoning.isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
{#if isSelected(level)}
|
||||
<Check class="h-4 w-4 shrink-0 text-foreground" />
|
||||
{:else}
|
||||
<div class="{ICON_CLASS_DEFAULT} shrink-0"></div>
|
||||
<div class="h-4 w-4 shrink-0"></div>
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">{level.label}</span>
|
||||
|
||||
{#if tokenLabel}
|
||||
{#if !level.isOff}
|
||||
<span class="text-[11px] text-muted-foreground opacity-60">
|
||||
{tokenLabel}
|
||||
{REASONING_EFFORT_TOKENS[level.value] === -1
|
||||
? 'Unlimited'
|
||||
: `Max ${REASONING_EFFORT_TOKENS[level.value].toLocaleString()} tokens`}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
+22
-90
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import type { Snippet } from 'svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
@@ -11,18 +10,10 @@
|
||||
import { ATTACHMENT_FILE_ITEMS } from '$lib/constants/attachment-menu';
|
||||
import { useAttachmentMenu } from '$lib/hooks/use-attachment-menu.svelte';
|
||||
import { useToolsPanel } from '$lib/hooks/use-tools-panel.svelte';
|
||||
import { useReasoningMenu } from '$lib/hooks/use-reasoning-menu.svelte';
|
||||
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
||||
import { mcpStore } from '$lib/stores/mcp.svelte';
|
||||
import { McpLogo } from '$lib/components/app';
|
||||
import {
|
||||
PencilRuler,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Lightbulb,
|
||||
LightbulbOff,
|
||||
Check
|
||||
} from '@lucide/svelte';
|
||||
import { PencilRuler, ChevronDown, ChevronRight } from '@lucide/svelte';
|
||||
import { HealthCheckStatus } from '$lib/enums';
|
||||
import { AttachmentAction } from '$lib/enums/attachment.enums';
|
||||
|
||||
@@ -57,7 +48,6 @@
|
||||
}: Props = $props();
|
||||
|
||||
let sheetOpen = $state(false);
|
||||
let reasoningExpanded = $state(false);
|
||||
let filesExpanded = $state(true);
|
||||
let toolsExpanded = $state(false);
|
||||
let mcpExpanded = $state(false);
|
||||
@@ -77,7 +67,6 @@
|
||||
);
|
||||
|
||||
const toolsPanel = useToolsPanel();
|
||||
const reasoning = useReasoningMenu();
|
||||
|
||||
const sheetItemClass =
|
||||
'flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent active:bg-accent disabled:cursor-not-allowed disabled:opacity-50';
|
||||
@@ -85,7 +74,7 @@
|
||||
const sheetItemRowClass =
|
||||
'flex w-full items-center justify-between gap-2 rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-accent';
|
||||
|
||||
let mcpServers = $derived(mcpStore.getServers());
|
||||
let visibleMcpServers = $derived(mcpStore.visibleMcpServers);
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-1 {className}">
|
||||
@@ -102,72 +91,15 @@
|
||||
</Sheet.Header>
|
||||
|
||||
<div class="flex flex-col gap-1 px-1.5 pb-2">
|
||||
{#if reasoning.modelSupportsThinking}
|
||||
<Collapsible.Root
|
||||
open={reasoningExpanded}
|
||||
onOpenChange={(open) => (reasoningExpanded = open)}
|
||||
>
|
||||
<Collapsible.Trigger class={sheetItemClass}>
|
||||
{#if reasoningExpanded}
|
||||
<ChevronDown class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
{:else}
|
||||
<ChevronRight class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
{/if}
|
||||
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Reasoning</span>
|
||||
|
||||
<span class="text-xs capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
</span>
|
||||
</Collapsible.Trigger>
|
||||
|
||||
<Collapsible.Content>
|
||||
<div class="flex flex-col gap-0.5 pl-4">
|
||||
{#each reasoning.levels as level (level.value)}
|
||||
{@const tokenLabel = reasoning.tokenLabel(level)}
|
||||
<button
|
||||
type="button"
|
||||
class={sheetItemRowClass}
|
||||
class:bg-accent={reasoning.isSelected(level)}
|
||||
onclick={() => reasoning.select(level)}
|
||||
>
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
{#if reasoning.isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
{:else}
|
||||
<div class="{ICON_CLASS_DEFAULT} shrink-0"></div>
|
||||
{/if}
|
||||
|
||||
<span class="text-sm">{level.label}</span>
|
||||
</div>
|
||||
|
||||
{#if tokenLabel}
|
||||
<span class="shrink-0 text-[11px] text-muted-foreground opacity-60">
|
||||
{tokenLabel}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
{/if}
|
||||
|
||||
<Collapsible.Root open={filesExpanded} onOpenChange={(open) => (filesExpanded = open)}>
|
||||
<Collapsible.Trigger class={sheetItemClass}>
|
||||
{#if filesExpanded}
|
||||
<ChevronDown class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronDown class="h-4 w-4 shrink-0" />
|
||||
{:else}
|
||||
<ChevronRight class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronRight class="h-4 w-4 shrink-0" />
|
||||
{/if}
|
||||
|
||||
<File class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<File class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span class="flex-1">Add files</span>
|
||||
</Collapsible.Trigger>
|
||||
@@ -182,7 +114,7 @@
|
||||
class={sheetItemClass}
|
||||
onclick={() => attachmentMenu.callbacks[item.action]()}
|
||||
>
|
||||
<item.icon class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<item.icon class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
@@ -190,7 +122,7 @@
|
||||
<Tooltip.Root delayDuration={TOOLTIP_DELAY_DURATION}>
|
||||
<Tooltip.Trigger>
|
||||
<button type="button" class={sheetItemClass} disabled>
|
||||
<item.icon class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<item.icon class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
@@ -209,23 +141,23 @@
|
||||
<Collapsible.Root open={mcpExpanded} onOpenChange={(open) => (mcpExpanded = open)}>
|
||||
<Collapsible.Trigger class={sheetItemClass}>
|
||||
{#if mcpExpanded}
|
||||
<ChevronDown class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronDown class="h-4 w-4 shrink-0" />
|
||||
{:else}
|
||||
<ChevronRight class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronRight class="h-4 w-4 shrink-0" />
|
||||
{/if}
|
||||
|
||||
<McpLogo class="inline {ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<McpLogo class="inline h-4 w-4 shrink-0" />
|
||||
|
||||
<span class="flex-1">MCP Servers</span>
|
||||
|
||||
<span class="text-xs text-muted-foreground">
|
||||
{mcpServers.length} server{mcpServers.length !== 1 ? 's' : ''}
|
||||
{visibleMcpServers.length} server{visibleMcpServers.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</Collapsible.Trigger>
|
||||
|
||||
<Collapsible.Content>
|
||||
<div class="flex flex-col gap-0.5 pl-4">
|
||||
{#each mcpServers as server (server.id)}
|
||||
{#each visibleMcpServers as server (server.id)}
|
||||
{@const healthState = mcpStore.getHealthCheckState(server.id)}
|
||||
{@const hasError = healthState.status === HealthCheckStatus.ERROR}
|
||||
{@const displayName = mcpStore.getServerLabel(server)}
|
||||
@@ -243,7 +175,7 @@
|
||||
<img
|
||||
src={faviconUrl}
|
||||
alt=""
|
||||
class="{ICON_CLASS_DEFAULT} shrink-0 rounded-sm"
|
||||
class="h-4 w-4 shrink-0 rounded-sm"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
@@ -268,7 +200,7 @@
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
{#if mcpServers.length === 0}
|
||||
{#if visibleMcpServers.length === 0}
|
||||
<div class="px-3 py-2 text-center text-sm text-muted-foreground">
|
||||
No MCP servers configured
|
||||
</div>
|
||||
@@ -281,12 +213,12 @@
|
||||
<Collapsible.Root open={toolsExpanded} onOpenChange={(open) => (toolsExpanded = open)}>
|
||||
<Collapsible.Trigger class={sheetItemClass}>
|
||||
{#if toolsExpanded}
|
||||
<ChevronDown class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronDown class="h-4 w-4 shrink-0" />
|
||||
{:else}
|
||||
<ChevronRight class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<ChevronRight class="h-4 w-4 shrink-0" />
|
||||
{/if}
|
||||
|
||||
<PencilRuler class="inline {ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<PencilRuler class="inline h-4 w-4 shrink-0" />
|
||||
|
||||
<span class="flex-1">Tools</span>
|
||||
|
||||
@@ -311,7 +243,7 @@
|
||||
<img
|
||||
src={favicon}
|
||||
alt=""
|
||||
class="{ICON_CLASS_DEFAULT} shrink-0 rounded-sm"
|
||||
class="h-4 w-4 shrink-0 rounded-sm"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
@@ -326,7 +258,7 @@
|
||||
|
||||
<Checkbox
|
||||
{checked}
|
||||
class="{ICON_CLASS_DEFAULT} shrink-0"
|
||||
class="h-4 w-4 shrink-0"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
|
||||
/>
|
||||
@@ -342,7 +274,7 @@
|
||||
class={sheetItemClass}
|
||||
onclick={() => attachmentMenu.callbacks[AttachmentAction.SYSTEM_PROMPT_CLICK]()}
|
||||
>
|
||||
<MessageSquare class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<MessageSquare class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span>System Message</span>
|
||||
</button>
|
||||
@@ -353,7 +285,7 @@
|
||||
class={sheetItemClass}
|
||||
onclick={() => attachmentMenu.callbacks[AttachmentAction.MCP_PROMPT_CLICK]()}
|
||||
>
|
||||
<Zap class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<Zap class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span>MCP Prompt</span>
|
||||
</button>
|
||||
@@ -365,7 +297,7 @@
|
||||
class={sheetItemClass}
|
||||
onclick={() => attachmentMenu.callbacks[AttachmentAction.MCP_RESOURCES_CLICK]()}
|
||||
>
|
||||
<FolderOpen class="{ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<FolderOpen class="h-4 w-4 shrink-0" />
|
||||
|
||||
<span>MCP Resources</span>
|
||||
</button>
|
||||
|
||||
+7
-8
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { PencilRuler, ChevronDown, ChevronRight, Loader2, Info, Check } from '@lucide/svelte';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import * as Collapsible from '$lib/components/ui/collapsible';
|
||||
@@ -16,7 +15,7 @@
|
||||
|
||||
<DropdownMenu.Sub onOpenChange={(open) => open && toolsPanel.handleOpen()}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
<PencilRuler class={ICON_CLASS_DEFAULT} />
|
||||
<PencilRuler class="h-4 w-4" />
|
||||
|
||||
<span>Tools</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -25,14 +24,14 @@
|
||||
{#if toolsPanel.totalToolCount === 0}
|
||||
{#if toolsStore.loading}
|
||||
<div class="px-3 py-4 text-center text-sm text-muted-foreground">
|
||||
<Loader2 class="mx-auto mb-1 {ICON_CLASS_DEFAULT} animate-spin" />
|
||||
<Loader2 class="mx-auto mb-1 h-4 w-4 animate-spin" />
|
||||
|
||||
Loading tools...
|
||||
</div>
|
||||
{:else if toolsStore.isToolsEndpointUnreachable}
|
||||
<div class="grid gap-2.5 px-3 py-4 text-sm text-muted-foreground">
|
||||
<span class="flex gap-2">
|
||||
<Info class="mt-0.5 {ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<Info class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
|
||||
<span>
|
||||
Run llama-server with <code>{CLI_FLAGS.TOOLS}</code> flag to enable
|
||||
@@ -42,7 +41,7 @@
|
||||
</span>
|
||||
|
||||
<span class="flex gap-2">
|
||||
<Info class="mt-0.5 {ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<Info class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
|
||||
<span>
|
||||
{hasMcpServersAvailable ? 'Enable' : 'Add'} MCP Server(s) to access
|
||||
@@ -55,7 +54,7 @@
|
||||
<div class="px-3 py-4 text-center text-sm text-muted-foreground">Failed to load tools</div>
|
||||
{:else if toolsPanel.noToolsInfoMessage}
|
||||
<div class="flex gap-2 px-3 py-4 text-sm text-muted-foreground">
|
||||
<Info class="mt-0.5 {ICON_CLASS_DEFAULT} shrink-0" />
|
||||
<Info class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
|
||||
<span>{toolsPanel.noToolsInfoMessage}</span>
|
||||
</div>
|
||||
@@ -88,7 +87,7 @@
|
||||
<img
|
||||
src={favicon}
|
||||
alt=""
|
||||
class="{ICON_CLASS_DEFAULT} shrink-0 rounded-sm"
|
||||
class="h-4 w-4 shrink-0 rounded-sm"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
@@ -110,7 +109,7 @@
|
||||
{...props}
|
||||
{checked}
|
||||
onCheckedChange={() => toolsPanel.toggleGroupByLabel(group.label)}
|
||||
class="mr-2 {ICON_CLASS_DEFAULT} shrink-0"
|
||||
class="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
{/snippet}
|
||||
</Tooltip.Trigger>
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Mic, Square } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
@@ -37,9 +36,9 @@
|
||||
<span class="sr-only">{isRecording ? 'Stop recording' : 'Start recording'}</span>
|
||||
|
||||
{#if isRecording}
|
||||
<Square class="{ICON_CLASS_DEFAULT} animate-pulse fill-white" />
|
||||
<Square class="h-4 w-4 animate-pulse fill-white" />
|
||||
{:else}
|
||||
<Mic class={ICON_CLASS_DEFAULT} />
|
||||
<Mic class="h-4 w-4" />
|
||||
{/if}
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
|
||||
+1
-4
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { Square, SkipForward } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { ChatService } from '$lib/services';
|
||||
@@ -186,9 +185,7 @@
|
||||
>
|
||||
<span class="sr-only">Skip reasoning</span>
|
||||
|
||||
<SkipForward
|
||||
class="{ICON_CLASS_DEFAULT} stroke-muted-foreground group-hover:stroke-foreground"
|
||||
/>
|
||||
<SkipForward class="h-4 w-4 stroke-muted-foreground group-hover:stroke-foreground" />
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@
|
||||
import { REASONING_EFFORT_TOKENS } from '$lib/constants/reasoning-effort-tokens';
|
||||
import { REASONING_EFFORT_LEVELS } from '$lib/constants/reasoning-effort';
|
||||
import type { ReasoningEffortLevel } from '$lib/types';
|
||||
import { DIALOG_SUBMENU_CONTENT, ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { DIALOG_SUBMENU_CONTENT } from '$lib/constants/css-classes';
|
||||
import {
|
||||
modelsStore,
|
||||
checkModelSupportsThinking,
|
||||
@@ -74,9 +74,9 @@
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
<Lightbulb class="h-4 w-4 shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
<LightbulbOff class="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Thinking</span>
|
||||
@@ -118,7 +118,7 @@
|
||||
{/if}
|
||||
|
||||
{#if isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
<Check class="h-4 w-4 shrink-0 text-foreground" />
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
@@ -384,6 +384,7 @@
|
||||
{isLastAssistantMessage}
|
||||
{message}
|
||||
{toolMessages}
|
||||
messageContent={message.content}
|
||||
onConfirmDelete={handleConfirmDelete}
|
||||
onContinue={handleContinue}
|
||||
onCopy={handleCopy}
|
||||
|
||||
+178
-23
@@ -2,20 +2,23 @@
|
||||
import {
|
||||
ChatMessageAgenticContent,
|
||||
ChatMessageActionIcons,
|
||||
ChatMessageAssistantModel,
|
||||
ChatMessageAssistantProcessingInfo,
|
||||
ChatMessageAssistantRawOutput,
|
||||
ChatMessageAssistantStatistics,
|
||||
ChatMessageEditForm
|
||||
ChatMessageEditForm,
|
||||
ChatMessageStatistics,
|
||||
ModelBadge,
|
||||
ModelsSelectorDropdown
|
||||
} from '$lib/components/app';
|
||||
import { getMessageEditContext } from '$lib/contexts';
|
||||
import { useProcessingState } from '$lib/hooks/use-processing-state.svelte';
|
||||
import { isLoading, isChatStreaming } from '$lib/stores/chat.svelte';
|
||||
import { modelLoadProgressText } from '$lib/utils';
|
||||
import { copyToClipboard, deriveAgenticSections, modelLoadProgressText } from '$lib/utils';
|
||||
import { AgenticSectionType, ChatMessageStatisticsMode } from '$lib/enums';
|
||||
import { REASONING_TAGS } from '$lib/constants/agentic';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { MessageRole } from '$lib/enums';
|
||||
import { config } from '$lib/stores/settings.svelte';
|
||||
import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
import { modelsStore } from '$lib/stores/models.svelte';
|
||||
import { ServerModelStatus } from '$lib/enums';
|
||||
|
||||
import { hasAgenticContent } from '$lib/utils';
|
||||
|
||||
@@ -30,6 +33,7 @@
|
||||
isLastAssistantMessage?: boolean;
|
||||
message: DatabaseMessage;
|
||||
toolMessages?: DatabaseMessage[];
|
||||
messageContent: string | undefined;
|
||||
onCopy: () => void;
|
||||
onConfirmDelete: () => void;
|
||||
onContinue?: () => void;
|
||||
@@ -50,6 +54,7 @@
|
||||
isLastAssistantMessage = false,
|
||||
message,
|
||||
toolMessages = [],
|
||||
messageContent,
|
||||
onConfirmDelete,
|
||||
onContinue,
|
||||
onCopy,
|
||||
@@ -72,11 +77,55 @@
|
||||
|
||||
let currentConfig = $derived(config());
|
||||
let isRouter = $derived(isRouterMode());
|
||||
|
||||
let showRawOutput = $state(false);
|
||||
|
||||
let rawOutputContent = $derived.by(() => {
|
||||
const sections = deriveAgenticSections(message, toolMessages, [], false);
|
||||
const parts: string[] = [];
|
||||
|
||||
for (const section of sections) {
|
||||
switch (section.type) {
|
||||
case AgenticSectionType.REASONING:
|
||||
case AgenticSectionType.REASONING_PENDING:
|
||||
parts.push(`${REASONING_TAGS.START}\n${section.content}\n${REASONING_TAGS.END}`);
|
||||
break;
|
||||
|
||||
case AgenticSectionType.TEXT:
|
||||
parts.push(section.content);
|
||||
break;
|
||||
|
||||
case AgenticSectionType.TOOL_CALL:
|
||||
case AgenticSectionType.TOOL_CALL_PENDING:
|
||||
case AgenticSectionType.TOOL_CALL_STREAMING: {
|
||||
const callObj: Record<string, unknown> = { name: section.toolName };
|
||||
|
||||
if (section.toolArgs) {
|
||||
try {
|
||||
callObj.arguments = JSON.parse(section.toolArgs);
|
||||
} catch {
|
||||
callObj.arguments = section.toolArgs;
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(JSON.stringify(callObj, null, 2));
|
||||
|
||||
if (section.toolResult) {
|
||||
parts.push(`[Tool Result]\n${section.toolResult}`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join('\n\n\n');
|
||||
});
|
||||
|
||||
let displayedModel = $derived(message.model ?? null);
|
||||
|
||||
// model being switched to while it loads, so the selector bar tracks it
|
||||
let pendingModel = $state<string | null>(null);
|
||||
|
||||
let isCurrentlyLoading = $derived(isLoading());
|
||||
let isStreaming = $derived(isChatStreaming());
|
||||
let hasNoContent = $derived(!message?.content?.trim());
|
||||
@@ -140,6 +189,10 @@
|
||||
};
|
||||
});
|
||||
|
||||
function handleCopyModel() {
|
||||
void copyToClipboard(displayedModel ?? '');
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (showProcessingInfoTop || showProcessingInfoBottom) {
|
||||
processingState.startMonitoring();
|
||||
@@ -158,14 +211,23 @@
|
||||
aria-label="Assistant message with actions"
|
||||
>
|
||||
{#if showProcessingInfoTop}
|
||||
<ChatMessageAssistantProcessingInfo {modelLoadingText} {processingState} position="top" />
|
||||
<div class="mt-6 w-full max-w-3xl" in:fade>
|
||||
<div class="processing-container">
|
||||
<span class="processing-text">
|
||||
{modelLoadingText ??
|
||||
processingState.getPromptProgressText() ??
|
||||
processingState.getProcessingMessage() ??
|
||||
'Processing...'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if editCtx.isEditing}
|
||||
<ChatMessageEditForm />
|
||||
{:else}
|
||||
{:else if message.role === MessageRole.ASSISTANT}
|
||||
{#if showRawOutput}
|
||||
<ChatMessageAssistantRawOutput {message} {toolMessages} />
|
||||
<pre class="raw-output">{rawOutputContent || ''}</pre>
|
||||
{:else}
|
||||
<ChatMessageAgenticContent
|
||||
{message}
|
||||
@@ -174,28 +236,78 @@
|
||||
{isLastAssistantMessage}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-sm whitespace-pre-wrap">
|
||||
{messageContent}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showProcessingInfoBottom}
|
||||
<ChatMessageAssistantProcessingInfo {modelLoadingText} {processingState} position="bottom" />
|
||||
<div class="mt-4 w-full max-w-3xl" in:fade>
|
||||
<div class="processing-container">
|
||||
<span class="processing-text">
|
||||
{modelLoadingText ??
|
||||
processingState.getPromptProgressText() ??
|
||||
processingState.getProcessingMessage() ??
|
||||
'Processing...'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="info my-6 grid gap-4 tabular-nums">
|
||||
{#if displayedModel}
|
||||
<div class="inline-flex flex-wrap items-start gap-2 text-xs text-muted-foreground">
|
||||
<ChatMessageAssistantModel
|
||||
{displayedModel}
|
||||
isLoading={isLoading()}
|
||||
{isRouter}
|
||||
{onRegenerate}
|
||||
/>
|
||||
{#if isRouter}
|
||||
<ModelsSelectorDropdown
|
||||
currentModel={pendingModel ?? displayedModel}
|
||||
disabled={isLoading()}
|
||||
onModelChange={async (modelId: string, modelName: string) => {
|
||||
const status = modelsStore.getModelStatus(modelId);
|
||||
|
||||
<ChatMessageAssistantStatistics
|
||||
{message}
|
||||
isLoading={isLoading()}
|
||||
{processingState}
|
||||
showMessageStats={currentConfig.showMessageStats}
|
||||
/>
|
||||
if (status !== ServerModelStatus.LOADED) {
|
||||
pendingModel = modelId;
|
||||
|
||||
try {
|
||||
await modelsStore.loadModel(modelId);
|
||||
} finally {
|
||||
pendingModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
onRegenerate(modelName);
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<ModelBadge model={displayedModel || undefined} onclick={handleCopyModel} />
|
||||
{/if}
|
||||
|
||||
{#if currentConfig.showMessageStats && message.timings && message.timings.predicted_n && message.timings.predicted_ms}
|
||||
{@const agentic = message.timings.agentic}
|
||||
<ChatMessageStatistics
|
||||
mode={ChatMessageStatisticsMode.GENERATION}
|
||||
promptTokens={agentic ? agentic.llm.prompt_n : message.timings.prompt_n}
|
||||
promptMs={agentic ? agentic.llm.prompt_ms : message.timings.prompt_ms}
|
||||
predictedTokens={agentic ? agentic.llm.predicted_n : message.timings.predicted_n}
|
||||
predictedMs={agentic ? agentic.llm.predicted_ms : message.timings.predicted_ms}
|
||||
agenticTimings={agentic}
|
||||
/>
|
||||
{:else if isLoading() && currentConfig.showMessageStats}
|
||||
{@const liveStats = processingState.getLiveProcessingStats()}
|
||||
{@const genStats = processingState.getLiveGenerationStats()}
|
||||
|
||||
{#if genStats}
|
||||
<ChatMessageStatistics
|
||||
mode={ChatMessageStatisticsMode.GENERATION}
|
||||
isLive
|
||||
promptTokens={liveStats?.tokensProcessed}
|
||||
promptMs={liveStats?.timeMs}
|
||||
predictedTokens={genStats.tokensGenerated}
|
||||
predictedMs={genStats.timeMs}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -241,4 +353,47 @@
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.processing-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.processing-text {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--muted-foreground),
|
||||
var(--foreground),
|
||||
var(--muted-foreground)
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: shine 1s linear infinite;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
to {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raw-output {
|
||||
width: 100%;
|
||||
max-width: 48rem;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 1rem;
|
||||
background: hsl(var(--muted) / 0.3);
|
||||
color: var(--foreground);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user