diff --git a/ggml/CMakeLists.txt b/ggml/CMakeLists.txt
index b7110fa1..1df78fdd 100644
--- a/ggml/CMakeLists.txt
+++ b/ggml/CMakeLists.txt
@@ -4,8 +4,8 @@ project("ggml" C CXX ASM)
### GGML Version
set(GGML_VERSION_MAJOR 0)
-set(GGML_VERSION_MINOR 20)
-set(GGML_VERSION_PATCH 2)
+set(GGML_VERSION_MINOR 25)
+set(GGML_VERSION_PATCH 1)
set(GGML_VERSION_BASE "${GGML_VERSION_MAJOR}.${GGML_VERSION_MINOR}.${GGML_VERSION_PATCH}")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
@@ -200,12 +200,12 @@ option(GGML_CUDA "ggml: use CUDA"
option(GGML_MUSA "ggml: use MUSA" OFF)
option(GGML_CUDA_FORCE_MMQ "ggml: use mmq kernels instead of cuBLAS" OFF)
option(GGML_CUDA_FORCE_CUBLAS "ggml: always use cuBLAS instead of mmq kernels" OFF)
-set (GGML_CUDA_PEER_MAX_BATCH_SIZE "128" CACHE STRING
- "ggml: max. batch size for using peer access")
option(GGML_CUDA_NO_PEER_COPY "ggml: do not use peer to peer copies" OFF)
option(GGML_CUDA_NO_VMM "ggml: do not try to use CUDA VMM" OFF)
option(GGML_CUDA_FA "ggml: compile ggml FlashAttention CUDA kernels" ON)
option(GGML_CUDA_FA_ALL_QUANTS "ggml: compile all quants for FlashAttention" OFF)
+set (GGML_CUDA_FA_QUANTS "q4_0-q4_0;q8_0-q8_0;f16-f16;bf16-bf16" CACHE STRING
+ "ggml: FlashAttention K-V type combinations to compile, \"all\" or a list such as \"q8_0-q8_0;q8_0-q4_0\"")
option(GGML_CUDA_GRAPHS "ggml: use CUDA graphs (llama.cpp only)" ${GGML_CUDA_GRAPHS_DEFAULT})
option(GGML_CUDA_NCCL "ggml: use NVIDIA Collective Comm. Library" ON)
set (GGML_CUDA_COMPRESSION_MODE "size" CACHE STRING
@@ -242,7 +242,10 @@ option(GGML_METAL_EMBED_LIBRARY "ggml: embed Metal library"
set (GGML_METAL_MACOSX_VERSION_MIN "" CACHE STRING
"ggml: metal minimum macOS version")
set (GGML_METAL_STD "" CACHE STRING "ggml: metal standard version (-std flag)")
+set (GGML_METAL_TARGET_OS "macos" CACHE STRING
+ "ggml: metal -mtargetos OS name (macos, ios, xros, tvos)")
option(GGML_OPENMP "ggml: use OpenMP" ON)
+option(GGML_OPENMP_FETCH "ggml: fetch LLVM OpenMP" OFF)
option(GGML_RPC "ggml: use RPC" OFF)
option(GGML_SYCL "ggml: use SYCL" OFF)
option(GGML_SYCL_F16 "ggml: use 16 bit floats for sycl calculations" OFF)
@@ -341,9 +344,6 @@ set(GGML_PUBLIC_HEADERS
include/gguf.h)
set_target_properties(ggml PROPERTIES PUBLIC_HEADER "${GGML_PUBLIC_HEADERS}")
-#if (GGML_METAL)
-# set_target_properties(ggml PROPERTIES RESOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/ggml-metal.metal")
-#endif()
install(TARGETS ggml LIBRARY PUBLIC_HEADER)
install(TARGETS ggml-base LIBRARY)
@@ -406,10 +406,6 @@ write_basic_package_version_file(
VERSION ${GGML_INSTALL_VERSION}
COMPATIBILITY SameMajorVersion)
-target_compile_definitions(ggml-base PRIVATE
- GGML_VERSION="${GGML_INSTALL_VERSION}"
- GGML_COMMIT="${GGML_BUILD_COMMIT}"
-)
message(STATUS "ggml version: ${GGML_INSTALL_VERSION}")
message(STATUS "ggml commit: ${GGML_BUILD_COMMIT}")
diff --git a/ggml/README.md b/ggml/README.md
index 455c8125..3178f3a5 100644
--- a/ggml/README.md
+++ b/ggml/README.md
@@ -1,50 +1,49 @@
# ggml
-[Manifesto](https://github.com/ggerganov/llama.cpp/discussions/205)
+
-Tensor library for machine learning
+

-***Note that this project is under active development. \
-Some of the development is currently happening in the [llama.cpp](https://github.com/ggerganov/llama.cpp) and [whisper.cpp](https://github.com/ggerganov/whisper.cpp) repos***
+
Tensor library for machine learning
-## Features
+[](https://opensource.org/licenses/MIT)
+[](https://github.com/ggml-org/ggml/releases)
+[](https://github.com/ggml-org/ggml/actions/workflows/build-cpu.yml)
-- Low-level cross-platform implementation
-- Integer quantization support
-- Broad hardware support
-- Automatic differentiation
-- ADAM and L-BFGS optimizers
-- No third-party dependencies
-- Zero memory allocations during runtime
+
+
+## Quick start
-## Build
+Build from source:
```bash
git clone https://github.com/ggml-org/ggml
cd ggml
-# install python dependencies in a virtual environment
-python3.10 -m venv .venv
-source .venv/bin/activate
-pip install -r requirements.txt
-
-# build the examples
mkdir build && cd build
cmake ..
cmake --build . --config Release -j 8
```
-## GPT inference (example)
+For a minimal, fully commented example (matrix multiplication), see [examples/simple](examples/simple).
-```bash
-# run the GPT-2 small 117M model
-../examples/gpt-2/download-ggml-model.sh 117M
-./bin/gpt-2-backend -m models/gpt-2-117M/ggml-model.bin -p "This is an example"
-```
+## Description
-For more information, checkout the corresponding programs in the [examples](examples) folder.
+The main goal of `ggml` is to be a simple, portable, and efficient tensor library for machine learning with minimal setup.
-## Resources
+- Plain C/C++ implementation without any dependencies
+- Cross-platform - x86, ARM, RISC-V, LoongArch, PowerPC, s390x, and WebAssembly
+- SIMD-optimized kernels for x86, ARM, and RISC-V
+- Broad backend support - CPU, GPU, NPU, and browser
+- 2- to 8-bit integer quantization, plus MXFP4 and NVFP4 microscaling formats
+- Zero memory allocations during runtime
+
+## Documentation
+- [The GGUF file format](docs/gguf.md)
- [Introduction to ggml](https://huggingface.co/blog/introduction-to-ggml)
-- [The GGUF file format](https://github.com/ggerganov/ggml/blob/master/docs/gguf.md)
+- [GGML tips & tricks](https://github.com/ggml-org/llama.cpp/wiki/GGML-Tips-&-Tricks)
+
+## Contributing
+
+- For changes to the core `ggml` library (including to the CMake build system), please open a PR in [llama.cpp](https://github.com/ggml-org/llama.cpp) - doing so will make your PR more visible, better tested, and more likely to be reviewed
diff --git a/ggml/UPSTREAM b/ggml/UPSTREAM
index 75f5980b..20a32545 100644
--- a/ggml/UPSTREAM
+++ b/ggml/UPSTREAM
@@ -1,5 +1,5 @@
repo: git@github.com:ggml-org/ggml.git
-sha: 8c63e70982c95ceb862e3a1073a2c1beef75d60a
+sha: e565a8f4ce2e462c4973a24c51098dd3c81c0256
patches:
patches/ggml/0001-fix-threadpool-oversubscription.patch
diff --git a/ggml/cmake/common.cmake b/ggml/cmake/common.cmake
index cb663883..25eff7a5 100644
--- a/ggml/cmake/common.cmake
+++ b/ggml/cmake/common.cmake
@@ -48,3 +48,74 @@ function(ggml_get_system_arch)
set(GGML_SYSTEM_ARCH "UNKNOWN" PARENT_SCOPE)
endif()
endfunction()
+
+# Determines which FlashAttention vector kernel template instances to compile, returns them in OUT_SRCS.
+function(ggml_cuda_fattn_vec_instances DIR OUT_SRCS)
+ set(FA_TYPES q4_0 q4_1 q5_0 q5_1 q8_0 bf16 f16)
+
+ string(TOLOWER "${GGML_CUDA_FA_QUANTS}" FA_QUANTS)
+ string(STRIP "${FA_QUANTS}" FA_QUANTS)
+ if (GGML_CUDA_FA_ALL_QUANTS)
+ message(WARNING "GGML_CUDA_FA_ALL_QUANTS is deprecated, use GGML_CUDA_FA_QUANTS=all instead")
+ set(FA_QUANTS all)
+ endif()
+ if (NOT FA_QUANTS)
+ message(FATAL_ERROR "GGML_CUDA_FA_QUANTS must not be empty")
+ endif()
+
+ if (FA_QUANTS STREQUAL "all")
+ set(FA_COMBINATIONS "")
+ foreach (TYPE_V IN LISTS FA_TYPES)
+ foreach (TYPE_K IN LISTS FA_TYPES)
+ list(APPEND FA_COMBINATIONS ${TYPE_K}-${TYPE_V})
+ endforeach()
+ endforeach()
+ else()
+ set(FA_COMBINATIONS f16-f16)
+
+ string(REPLACE "," ";" FA_SELECTED "${FA_QUANTS}")
+ foreach (COMBINATION IN LISTS FA_SELECTED)
+ string(STRIP "${COMBINATION}" COMBINATION)
+ if (NOT COMBINATION MATCHES "^([a-z0-9_]+)-([a-z0-9_]+)$")
+ message(FATAL_ERROR "GGML_CUDA_FA_QUANTS: \"${COMBINATION}\" is not \"all\" or a - combination")
+ endif()
+ set(TYPE_K ${CMAKE_MATCH_1})
+ set(TYPE_V ${CMAKE_MATCH_2})
+ foreach (TYPE ${TYPE_K} ${TYPE_V})
+ if (NOT TYPE IN_LIST FA_TYPES)
+ message(FATAL_ERROR
+ "GGML_CUDA_FA_QUANTS: unknown type \"${TYPE}\" in \"${COMBINATION}\", must be one of: ${FA_TYPES}")
+ endif()
+ endforeach()
+ list(APPEND FA_COMBINATIONS ${TYPE_K}-${TYPE_V})
+ endforeach()
+ endif()
+ list(REMOVE_DUPLICATES FA_COMBINATIONS)
+
+ string(REPLACE ";" "," FA_QUANTS_DEFINE "${FA_QUANTS}")
+ add_compile_definitions(GGML_CUDA_FA_QUANTS="${FA_QUANTS_DEFINE}")
+ foreach (TYPE_V IN LISTS FA_TYPES)
+ foreach (TYPE_K IN LISTS FA_TYPES)
+ if ("${TYPE_K}-${TYPE_V}" IN_LIST FA_COMBINATIONS)
+ set(COMPILED 1)
+ else()
+ set(COMPILED 0)
+ endif()
+ string(TOUPPER "GGML_CUDA_FA_${TYPE_K}_${TYPE_V}" COMBINATION_DEF)
+ add_compile_definitions(${COMBINATION_DEF}=${COMPILED})
+ endforeach()
+ endforeach()
+
+ message(STATUS "FlashAttention K-V type combinations: ${FA_COMBINATIONS}")
+
+ set(SRCS "")
+ foreach (COMBINATION IN LISTS FA_COMBINATIONS)
+ set(SRC "${DIR}/template-instances/fattn-vec-instance-${COMBINATION}.cu")
+ if (NOT EXISTS "${SRC}")
+ message(FATAL_ERROR "FlashAttention template instance \"${SRC}\" does not exist")
+ endif()
+ list(APPEND SRCS "${SRC}")
+ endforeach()
+
+ set(${OUT_SRCS} ${SRCS} PARENT_SCOPE)
+endfunction()
diff --git a/ggml/cmake/ggml-config.cmake.in b/ggml/cmake/ggml-config.cmake.in
index abe17804..a28e49e8 100644
--- a/ggml/cmake/ggml-config.cmake.in
+++ b/ggml/cmake/ggml-config.cmake.in
@@ -110,6 +110,16 @@ set_and_check(GGML_INCLUDE_DIR "@PACKAGE_GGML_INCLUDE_INSTALL_DIR@")
set_and_check(GGML_LIB_DIR "@PACKAGE_GGML_LIB_INSTALL_DIR@")
#set_and_check(GGML_BIN_DIR "@PACKAGE_GGML_BIN_INSTALL_DIR@")
+if (NOT GGML_SHARED_LIB AND GGML_CPU_KLEIDIAI)
+ unset(KLEIDIAI_LIBRARY CACHE)
+ unset(KLEIDIAI_LIBRARY)
+ find_library(KLEIDIAI_LIBRARY kleidiai
+ REQUIRED
+ HINTS ${GGML_LIB_DIR}
+ NO_CMAKE_FIND_ROOT_PATH)
+ list(APPEND GGML_CPU_INTERFACE_LINK_LIBRARIES ${KLEIDIAI_LIBRARY})
+endif()
+
if(NOT TARGET ggml::ggml)
find_package(Threads REQUIRED)
diff --git a/ggml/examples/common.cpp b/ggml/examples/common.cpp
index 8eb633e5..1ff8b43a 100644
--- a/ggml/examples/common.cpp
+++ b/ggml/examples/common.cpp
@@ -406,6 +406,7 @@ gpt_vocab::id gpt_sample_top_k_top_p(
double temp,
std::mt19937 & rng) {
int n_logits = vocab.id_to_token.size();
+ top_k = std::min(top_k, n_logits);
std::vector> logits_id;
logits_id.reserve(n_logits);
@@ -491,6 +492,7 @@ gpt_vocab::id gpt_sample_top_k_top_p_repeat(
std::mt19937 & rng) {
int n_logits = vocab.id_to_token.size();
+ top_k = std::min(top_k, n_logits);
const auto * plogits = logits;
diff --git a/ggml/include/ggml-rpc.h b/ggml/include/ggml-rpc.h
index 276aea00..1f8cb790 100644
--- a/ggml/include/ggml-rpc.h
+++ b/ggml/include/ggml-rpc.h
@@ -6,7 +6,7 @@
extern "C" {
#endif
-#define RPC_PROTO_MAJOR_VERSION 5
+#define RPC_PROTO_MAJOR_VERSION 7
#define RPC_PROTO_MINOR_VERSION 0
#define RPC_PROTO_PATCH_VERSION 0
diff --git a/ggml/include/ggml-sycl.h b/ggml/include/ggml-sycl.h
index 418a7ba9..1e353ffa 100644
--- a/ggml/include/ggml-sycl.h
+++ b/ggml/include/ggml-sycl.h
@@ -25,7 +25,7 @@ GGML_BACKEND_API bool ggml_backend_is_sycl(ggml_backend_t backend);
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_buffer_type(int device);
// split tensor buffer that splits matrices by rows across multiple devices
-GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_split_buffer_type(const float * tensor_split);
+GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_split_buffer_type(int main_device, const float * tensor_split);
// Tensor parallelism (--split-mode tensor): comm_init/free/allreduce_tensor
// trio queried by the meta-backend via ggml_backend_reg_get_proc_address.
@@ -36,6 +36,8 @@ GGML_BACKEND_API void ggml_backend_sycl_comm_free(void * comm_ctx);
GGML_BACKEND_API bool ggml_backend_sycl_comm_allreduce_tensor(void * comm_ctx, struct ggml_tensor ** tensors);
// pinned host buffer for use with the CPU backend for faster copies between CPU and GPU
+// pins on device 0 - a copy between another device and this memory can fail,
+// use ggml_backend_dev_host_buffer_type to pin on the device that does the copy
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_host_buffer_type(void);
GGML_BACKEND_API void ggml_backend_sycl_print_sycl_devices(void);
@@ -43,7 +45,7 @@ GGML_BACKEND_API void ggml_backend_sycl_get_gpu_list(int *id_list, int max_len);
GGML_BACKEND_API void ggml_backend_sycl_get_device_description(int device,
char *description,
size_t description_size);
-GGML_BACKEND_API int ggml_backend_sycl_get_device_count();
+GGML_BACKEND_API int ggml_backend_sycl_get_device_count(void);
GGML_BACKEND_API void ggml_backend_sycl_get_device_memory(int device, size_t *free, size_t *total);
// SYCL doesn't support registering host memory, keep here for reference
diff --git a/ggml/include/ggml.h b/ggml/include/ggml.h
index c2ccd972..224bdef9 100644
--- a/ggml/include/ggml.h
+++ b/ggml/include/ggml.h
@@ -433,10 +433,21 @@ extern "C" {
GGML_TYPE_COUNT = 43,
};
- // precision
+ // [TAG_GGML_PREC]
+ // this enum is used to declare the allowed numerical precision/data-types types that can be used during the compute of an op
+ // the declared types can be:
+ // - result accumulation type
+ // - source tensor data representation type
+ // - etc.
+ // the precision parameters are stored as ggml_tensor.op_params to the respective ops
enum ggml_prec {
- GGML_PREC_DEFAULT = 0, // stored as ggml_tensor.op_params, 0 by default
- GGML_PREC_F32 = 10,
+ GGML_PREC_UNDEFINED = 0,
+ GGML_PREC_DEFAULT = 0, // note: deprecated, use GGML_PREC_UNDEFINED
+ GGML_PREC_F32 = 10,
+ GGML_PREC_BF16 = 15,
+ GGML_PREC_F16 = 20,
+ GGML_PREC_Q8 = 30,
+ GGML_PREC_Q4 = 40,
};
// op hint
@@ -627,6 +638,7 @@ extern "C" {
GGML_GLU_OP_SWIGLU_OAI,
GGML_GLU_OP_GEGLU_ERF,
GGML_GLU_OP_GEGLU_QUICK,
+ GGML_GLU_OP_SWIGLU_CLAMP,
GGML_GLU_OP_COUNT,
};
@@ -1367,6 +1379,12 @@ extern "C" {
float alpha,
float limit);
+ GGML_API struct ggml_tensor * ggml_swiglu_clamp(
+ struct ggml_context * ctx,
+ struct ggml_tensor * a,
+ struct ggml_tensor * b,
+ float limit);
+
// normalize along rows
GGML_API struct ggml_tensor * ggml_norm(
struct ggml_context * ctx,
@@ -1422,6 +1440,42 @@ extern "C" {
struct ggml_tensor * b,
float eps);
+ // [TAG_GGML_PREC]
+ // set the minimum required accumulator type for the implementation to use during the compute
+ // for example:
+ // - GGML_PREC_F32 - requires accumulation of the results in F32
+ // - GGML_PREC_BF16 - can accumulate the results in BF16, F32
+ // - GGML_PREC_F16 - can accumulate the results in F16, F32
+ // - GGML_PREC_Q8 - not allowed
+ // - GGML_PREC_Q4 - not allowed
+ //
+ // return false on faliure
+ GGML_API bool ggml_prec_set_acc(
+ struct ggml_tensor * a,
+ enum ggml_prec prec);
+
+ // [TAG_GGML_PREC]
+ // set the smallest rank that the implementation can use to internally convert the src[idx] data to
+ // ranks in decreasing order:
+ // - GGML_PREC_F32 - GGML_TYPE_F32
+ // - GGML_PREC_BF16 - GGML_TYPE_BF16
+ // - GGML_PREC_F16 - GGML_TYPE_F16,
+ // - GGML_PREC_Q8 - GGML_TYPE_Q8_0, GGML_TYPE_Q8_1, GGML_TYPE_Q8_K, etc.
+ // - GGML_PREC_Q4 - GGML_TYPE_Q4_0, GGML_TYPE_Q4_1, GGML_TYPE_Q4_K, GGML_TYPE_NVFP4, GGML_TYPE_MXFP4, etc.
+ //
+ // for example:
+ // - ggml_prec_set_src(a, GGML_PREC_Q8, 1):
+ // - allows the implementation to quantize F32, BF16, F16 data of src[1] down to GGML_TYPE_Q8_0
+ // - cannot quantize it down to GGML_TYPE_Q4_0 or GGML_TYPE_NVFP4
+ // - ggml_prec_set_src(a, GGML_PREC_Q4, 1):
+ // - allows the implementation to quantize F32, BF16, F16 data of src[1] down to 4-bit datatypes such as GGML_TYPE_Q4_K, GGML_TYPE_NVFP4 etc.
+ //
+ // return false on faliure
+ GGML_API bool ggml_prec_set_src(
+ struct ggml_tensor * a,
+ enum ggml_prec prec,
+ int idx);
+
// A: k columns, n rows => [ne03, ne02, n, k]
// B: k columns, m rows (i.e. we transpose it internally) => [ne03 * x, ne02 * y, m, k]
// result is n columns, m rows => [ne03 * x, ne02 * y, m, n]
@@ -1432,9 +1486,10 @@ extern "C" {
// change the precision of a matrix multiplication
// set to GGML_PREC_F32 for higher precision (useful for phi-2)
- GGML_API void ggml_mul_mat_set_prec(
+ GGML_DEPRECATED(GGML_API void ggml_mul_mat_set_prec(
struct ggml_tensor * a,
- enum ggml_prec prec);
+ enum ggml_prec prec),
+ "use ggml_prec_set_acc() instead");
// change the hint of a matrix multiplication
GGML_API void ggml_mul_mat_set_hint(
@@ -1724,6 +1779,19 @@ extern "C" {
struct ggml_tensor * a,
int n_past);
+ GGML_API struct ggml_tensor * ggml_clamp(
+ struct ggml_context * ctx,
+ struct ggml_tensor * a,
+ float min,
+ float max);
+
+ // in-place, returns view(a)
+ GGML_API struct ggml_tensor * ggml_clamp_inplace(
+ struct ggml_context * ctx,
+ struct ggml_tensor * a,
+ float min,
+ float max);
+
GGML_API struct ggml_tensor * ggml_soft_max(
struct ggml_context * ctx,
struct ggml_tensor * a);
@@ -1981,14 +2049,14 @@ extern "C" {
float beta_fast,
float beta_slow);
-
- // clamp
- // in-place, returns view(a)
- GGML_API struct ggml_tensor * ggml_clamp(
- struct ggml_context * ctx,
+ // set the offset dims for RoPE
+ // a must be GGML_OP_ROPE or GGML_OP_ROPE_BACK
+ // vision RoPE is not supported
+ // example: (marking: x = rotated, 0 = unrotated)
+ // n_embd = 10, n_dims = 4, offset = 2 --> [00xxxx0000]
+ GGML_API struct ggml_tensor * ggml_rope_set_offset(
struct ggml_tensor * a,
- float min,
- float max);
+ int n_offs);
// im2col
// converts data into a format that effectively results in a convolution when combined with matrix multiplication
@@ -2426,13 +2494,20 @@ extern "C" {
float max_bias,
float logit_softcap);
- GGML_API void ggml_flash_attn_ext_set_prec(
+ GGML_DEPRECATED(GGML_API void ggml_flash_attn_ext_set_prec(
struct ggml_tensor * a,
- enum ggml_prec prec);
+ enum ggml_prec prec),
+ "use ggml_prec_set_acc() instead");
GGML_API enum ggml_prec ggml_flash_attn_ext_get_prec(
const struct ggml_tensor * a);
+ // Use finite mask entries as a sparse K/V set. Set 0 to disable.
+ // n_kv_max must bound the number of finite entries in every mask row.
+ GGML_API void ggml_flash_attn_ext_set_n_kv_max(
+ struct ggml_tensor * a,
+ int32_t n_kv_max);
+
GGML_API void ggml_flash_attn_ext_add_sinks(
struct ggml_tensor * a,
struct ggml_tensor * sinks);
@@ -2628,11 +2703,21 @@ extern "C" {
struct ggml_tensor * x,
struct ggml_tensor * weights);
+ // hc_pre with a per-element gate (Qwen3.8-Flash-Next): gate [n_embd, hc, n_tokens]
+ // result[i, t] = scale*sum_h x[i, h, t]*sigmoid(gate[i, h, t])
+ //
+ GGML_API struct ggml_tensor * ggml_dsv4_hc_pre_gated(
+ struct ggml_context * ctx,
+ struct ggml_tensor * x,
+ struct ggml_tensor * gate,
+ float scale);
+
// hc_post: x [n_embd, n_tokens], residual [n_embd, hc, n_tokens],
// post [hc, n_tokens], comb [dst_hc, src_hc, n_tokens]
// -> [n_embd, hc, n_tokens]
// result[i, dst, t] = x[i, t]*post[dst, t]
// + sum_src residual[i, src, t]*comb[dst, src, t]
+ // comb == NULL uses the identity: result[i, dst, t] = x[i, t]*post[dst, t] + residual[i, dst, t]
//
GGML_API struct ggml_tensor * ggml_dsv4_hc_post(
struct ggml_context * ctx,
diff --git a/ggml/scripts/make-release-checks.sh b/ggml/scripts/make-release-checks.sh
new file mode 100755
index 00000000..c18cd998
--- /dev/null
+++ b/ggml/scripts/make-release-checks.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+# Run all pre-release checks and determine the release version.
+#
+# Usage: make-release-checks.sh [--dry-run]
+# --dry-run: warn on failures instead of aborting
+#
+# Env (when running in GitHub Actions):
+# GH_TOKEN, GITHUB_REPOSITORY, GITHUB_OUTPUT
+# RELEASE_BRANCH: when set, HEAD must belong to origin/RELEASE_BRANCH and must
+# not be older than 3 days from the branch HEAD (skipped when unset)
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+DRY_RUN=false
+CHECKS_PASSED=true
+for arg in "$@"; do
+ case "$arg" in
+ --dry-run) DRY_RUN=true ;;
+ *) echo "Unknown argument: $arg"; exit 1 ;;
+ esac
+done
+
+MAJOR=$(sed -n 's/^set(GGML_VERSION_MAJOR \([0-9][0-9]*\)).*/\1/p' "$REPO_ROOT/CMakeLists.txt")
+MINOR=$(sed -n 's/^set(GGML_VERSION_MINOR \([0-9][0-9]*\)).*/\1/p' "$REPO_ROOT/CMakeLists.txt")
+PATCH=$(sed -n 's/^set(GGML_VERSION_PATCH \([0-9][0-9]*\)).*/\1/p' "$REPO_ROOT/CMakeLists.txt")
+VERSION="v${MAJOR}.${MINOR}.${PATCH}"
+echo "Determined version: ${VERSION}"
+if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+fi
+
+SHA=$(git rev-parse HEAD)
+
+echo "Checking that commit ${SHA} belongs to the release branch..."
+if [[ -z "${RELEASE_BRANCH:-}" ]]; then
+ echo "Warning: RELEASE_BRANCH not set - skipping commit check (local run)"
+else
+ TIP="origin/${RELEASE_BRANCH}"
+ COMMIT_ERR=""
+ if ! git rev-parse --verify "${TIP}" >/dev/null 2>&1; then
+ COMMIT_ERR="branch ${RELEASE_BRANCH} not found on remote"
+ elif ! git merge-base --is-ancestor "${SHA}" "${TIP}"; then
+ COMMIT_ERR="commit ${SHA} is not part of branch ${RELEASE_BRANCH}"
+ else
+ COMMIT_TS=$(git show -s --format=%ct "${SHA}")
+ TIP_TS=$(git show -s --format=%ct "${TIP}")
+ AGE_DAYS=$(( (TIP_TS - COMMIT_TS) / 86400 ))
+ if (( TIP_TS - COMMIT_TS > 3 * 86400 )); then
+ COMMIT_ERR="commit ${SHA} is ${AGE_DAYS} day(s) older than the HEAD of ${RELEASE_BRANCH} (max: 3)"
+ fi
+ fi
+ if [[ -n "${COMMIT_ERR}" ]]; then
+ if [[ "$DRY_RUN" == "true" ]]; then
+ echo "Warning: ${COMMIT_ERR} (dry run, continuing)."
+ CHECKS_PASSED=false
+ else
+ echo "Error: ${COMMIT_ERR}"
+ exit 1
+ fi
+ else
+ echo "Commit ${SHA} is on branch ${RELEASE_BRANCH} and within 3 days of its HEAD - OK"
+ fi
+fi
+
+echo "Checking that tag ${VERSION} does not already exist..."
+if git ls-remote --tags origin "${VERSION}" | grep -q "${VERSION}"; then
+ echo "Error: tag ${VERSION} already exists on remote"
+ exit 1
+fi
+echo "Tag ${VERSION} does not exist on remote - OK"
+
+echo "Checking release.yml status for commit ${SHA}..."
+if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
+ echo "Warning: GITHUB_REPOSITORY not set - skipping CI check (local run)"
+else
+ RUNS=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/release.yml/runs?per_page=100" \
+ --jq "[.workflow_runs[] | select(.head_sha == \"${SHA}\" and .conclusion == \"success\")] | length")
+ if [[ "$RUNS" -eq 0 ]]; then
+ if [[ "$DRY_RUN" == "true" ]]; then
+ echo "Warning: no successful release.yml run found for HEAD (${SHA}) (dry run, continuing)."
+ CHECKS_PASSED=false
+ else
+ echo "Error: no successful release.yml run found for HEAD (${SHA})"
+ echo "The release workflow must complete successfully before making a release."
+ exit 1
+ fi
+ else
+ echo "Found successful release.yml run for HEAD."
+ fi
+fi
+
+if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ echo "checks_passed=${CHECKS_PASSED}" >> "$GITHUB_OUTPUT"
+fi
diff --git a/ggml/scripts/make-release-desc.sh b/ggml/scripts/make-release-desc.sh
new file mode 100755
index 00000000..2a7b6af4
--- /dev/null
+++ b/ggml/scripts/make-release-desc.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+# Generate the description of a release: the previous release version and
+# the change log.
+#
+# Usage: make-release-desc.sh
+# : current release version (v.., the leading v is optional)
+#
+# The previous version is the highest plain semver tag (v..)
+# strictly below . The change log lists all commits between the
+# previous version tag and the release commit, one line per commit.
+#
+# The release commit is the commit points at when the tag exists,
+# HEAD otherwise.
+#
+# Env (when running in GitHub Actions):
+# GITHUB_OUTPUT: previous_tag, changelog_title and changelog are written here
+set -euo pipefail
+
+if [[ $# -ne 1 ]]; then
+ echo "Usage: $(basename "$0") "
+ exit 1
+fi
+VERSION="$1"
+
+# Accept the version with or without the leading v, reject anything else
+if [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ VERSION="v${VERSION}"
+elif [[ ! "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "Error: invalid version '${VERSION}' (expected v..)"
+ exit 1
+fi
+
+# Make sure all remote tags are available locally (skipped on local runs without origin)
+if ! git fetch --tags origin 2>/dev/null; then
+ echo "Warning: could not fetch tags from origin (local run?)"
+fi
+
+# Release commit: the commit points at when the tag exists, HEAD otherwise.
+if ! RELEASE_COMMIT="$(git rev-parse -q --verify "refs/tags/${VERSION}^{commit}" 2>/dev/null)"; then
+ RELEASE_COMMIT="$(git rev-parse HEAD)"
+fi
+
+echo "Release commit: $(git rev-parse --short "${RELEASE_COMMIT}")"
+
+PREV="$( { git tag --list; echo "${VERSION}"; } \
+ | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \
+ | sort -V \
+ | awk -v cur="${VERSION}" '$0 == cur { exit } { prev = $0 } END { print prev }')"
+
+if [[ -n "${PREV}" ]]; then
+ CHANGELOG="$(git log --oneline "${PREV}..${RELEASE_COMMIT}")"
+ CHANGELOG_TITLE="Changelog since ${PREV}"
+else
+ CHANGELOG="(no previous release tag found)"
+ CHANGELOG_TITLE="Changelog"
+fi
+
+echo "Previous version: ${PREV:-none}"
+echo "${CHANGELOG}"
+
+if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ {
+ echo "previous_tag=${PREV}"
+ echo "changelog_title=${CHANGELOG_TITLE}"
+ echo "changelog<> "${GITHUB_OUTPUT}"
+fi
diff --git a/ggml/scripts/make-release-summary.txt b/ggml/scripts/make-release-summary.txt
new file mode 100644
index 00000000..8db8545e
--- /dev/null
+++ b/ggml/scripts/make-release-summary.txt
@@ -0,0 +1,35 @@
+Take a look at the changelog between the current version and the previous version - use the `./scripts/make-release-desc.sh [current-version]` to obtain it.
+
+Write a summary of the change log in a few sections:
+
+```
+## Overview
+
+[a single paragraph overview of all changes]
+
+### API changes (if applicable)
+
+[summarize the changes to `/include/*`]
+
+### Core changes (if applicable)
+
+[summarize the changes to `/src/ggml*`
+
+### Backend changes (if applicable)
+
+[summarize the changes to `/src/ggml-*`, organize in sub-sections per backend]
+
+```
+
+For all changes, you can refer to the `llama.cpp` and `whisper.cpp` repositories for more information. For example, a change comming from `(llama/XXXX)` has more information in the respective PR in the `llama.cpp` repository.
+
+Guidelines:
+
+- All bullet point in the summary should be concise and rarely exceed a single line of 120 characters (excluding PR links)
+- Avoid mentioning that the changes are synchronized - this is not relevant
+- Avoid mentioning specific models - focus on generality of the ggml framework
+- Replace `(llama/XXXX)` and `(whisper/XXXX)` with a link to the respective PR
+- Combine related topics (a full list of commits will be appended independently at end of the summary)
+- Skip minor-impact notes (e.g. "fix compile warnings", "refactored code", ...)
+
+Output just the summary in a markdown block, without any extra text. Save it to a local text file called `release-notes-vX.Y.Z.txt`.
diff --git a/ggml/scripts/release.sh b/ggml/scripts/release.sh
index e8fcea2c..91741e46 100755
--- a/ggml/scripts/release.sh
+++ b/ggml/scripts/release.sh
@@ -1,37 +1,24 @@
#!/bin/bash
#
-# Automated release script for ggml.
+# Release preparation script for ggml.
#
-# Note: Sync from llama.cpp should be done separately via PR process
-# prior to running this script.
+# Bumps the version in CMakeLists.txt on a release candidate branch.
+# The branch should then be pushed and a PR created, reviewed, and
+# merged. After the PR is merged and the build-cpu workflow has
+# completed successfully, the release is finalized by the make-release
+# workflow (.github/workflows/make-release.yml), which creates the tag.
#
# Usage:
-# ./scripts/release.sh prepare [major|minor|patch] [--dry-run]
-# ./scripts/release.sh finalize [--dry-run]
+# ./scripts/release.sh [major|minor|patch] [--dry-run]
#
-# Two-stage release process:
+# Example:
+# $ ./scripts/release.sh minor
#
-# Stage 1 - Prepare:
-# $ ./scripts/release.sh prepare minor
-# This creates a release candidate branch with version bump and removes -dev suffix.
-# The branch should then be manually pushed and a PR created, reviewed, and merged.
-#
-# Stage 2 - Finalize:
-# $ ./scripts/release.sh finalize
-# After the RC PR is merged, this reads the current version from CMakeLists.txt,
-# creates the release tag, and prepares the next development cycle.
-#
-# Prepare stage:
-# 1. Creates release candidate branch
-# 2. Updates version and removes -dev suffix
+# The script:
+# 1. Creates a release candidate branch (ggml-rc-v..)
+# 2. Bumps the version in CMakeLists.txt
# 3. Commits the version bump
#
-# Finalize stage:
-# 1. Reads current release version from CMakeLists.txt
-# 2. Creates signed git tag on master
-# 3. Adds -dev suffix back for next development cycle
-# 4. Creates branch and commit for development version
-#
set -e
@@ -41,61 +28,27 @@ if [ ! -f "CMakeLists.txt" ] || [ ! -d "scripts" ]; then
fi
# Parse command line arguments
-COMMAND=""
VERSION_TYPE=""
DRY_RUN=false
-# First argument should be the command
-if [ $# -eq 0 ]; then
- echo "Error: Missing command"
- echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
- echo " $0 finalize [--dry-run]"
- exit 1
-fi
-
-COMMAND="$1"
-shift
-
-# Parse remaining arguments
for arg in "$@"; do
case $arg in
--dry-run)
DRY_RUN=true
;;
major|minor|patch)
- if [ "$COMMAND" = "prepare" ]; then
- VERSION_TYPE="$arg"
- else
- echo "Error: Version type only valid for 'prepare' command"
- exit 1
- fi
+ VERSION_TYPE="$arg"
;;
*)
echo "Error: Unknown argument '$arg'"
- echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
- echo " $0 finalize [--dry-run]"
+ echo "Usage: $0 [major|minor|patch] [--dry-run]"
exit 1
;;
esac
done
-# Validate command
-if [[ ! "$COMMAND" =~ ^(prepare|finalize)$ ]]; then
- echo "Error: Command must be 'prepare' or 'finalize'"
- echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
- echo " $0 finalize [--dry-run]"
- exit 1
-fi
-
-# For prepare command, default to patch if no version type specified
-if [ "$COMMAND" = "prepare" ]; then
- VERSION_TYPE="${VERSION_TYPE:-patch}"
- if [[ ! "$VERSION_TYPE" =~ ^(major|minor|patch)$ ]]; then
- echo "Error: Version type must be 'major', 'minor', or 'patch'"
- echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
- exit 1
- fi
-fi
+# Default to patch if no version type specified
+VERSION_TYPE="${VERSION_TYPE:-patch}"
# Common validation functions
check_git_status() {
@@ -233,64 +186,10 @@ prepare_release() {
echo "Next steps:"
echo " • Push branch to remote: git push origin $RC_BRANCH"
echo " • Create a Pull Request from $RC_BRANCH to master"
- echo " • After PR is merged, run: ./scripts/release.sh finalize"
- fi
-}
-
-finalize_release() {
- if [ "$DRY_RUN" = true ]; then
- echo "[dry-run] Finalizing release (no changes will be made)"
- else
- echo "Starting release finalization..."
- fi
- echo ""
-
- check_git_status
- check_master_branch
- check_master_up_to_date
-
- # Read current version from CMakeLists.txt
- echo "Step 1: Reading current release version..."
- MAJOR=$(grep "set(GGML_VERSION_MAJOR" CMakeLists.txt | sed 's/.*MAJOR \([0-9]*\).*/\1/')
- MINOR=$(grep "set(GGML_VERSION_MINOR" CMakeLists.txt | sed 's/.*MINOR \([0-9]*\).*/\1/')
- PATCH=$(grep "set(GGML_VERSION_PATCH" CMakeLists.txt | sed 's/.*PATCH \([0-9]*\).*/\1/')
-
- RELEASE_VERSION="$MAJOR.$MINOR.$PATCH"
- echo "Release version: $RELEASE_VERSION"
- echo ""
-
- # Create git tag
- echo "Step 2: Creating signed git tag..."
- if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would create signed tag: v$RELEASE_VERSION with message 'Release version $RELEASE_VERSION'"
- else
- git tag -s "v$RELEASE_VERSION" -m "Release version $RELEASE_VERSION"
- echo "✓ Created signed tag: v$RELEASE_VERSION"
- fi
- echo ""
-
-
- echo ""
- if [ "$DRY_RUN" = true ]; then
- echo "[dry-run] Summary (no changes were made):"
- echo " • Would have created tag: v$RELEASE_VERSION"
- else
- echo "Release finalization completed!"
- echo "Summary:"
- echo " • Created signed tag: v$RELEASE_VERSION"
- echo ""
- echo "Next steps:"
- echo " • Push tag to remote: git push origin v$RELEASE_VERSION"
- echo " • The release is now complete!"
+ echo " • After the PR is merged and the build-cpu workflow has passed,"
+ echo " create the release with the make-release workflow"
+ echo " (.github/workflows/make-release.yml)"
fi
}
-# Execute the appropriate command
-case $COMMAND in
- prepare)
- prepare_release
- ;;
- finalize)
- finalize_release
- ;;
-esac
+prepare_release
diff --git a/ggml/scripts/sync-llama.last b/ggml/scripts/sync-llama.last
index e3baac0a..dfd80514 100644
--- a/ggml/scripts/sync-llama.last
+++ b/ggml/scripts/sync-llama.last
@@ -1 +1 @@
-27e345b574dd8c8838e2c06e47699a3135f16ec9
+66fba63af1f4161052c33024d150cac31f46ff37
diff --git a/ggml/src/CMakeLists.txt b/ggml/src/CMakeLists.txt
index 82e9480c..94773200 100644
--- a/ggml/src/CMakeLists.txt
+++ b/ggml/src/CMakeLists.txt
@@ -213,7 +213,9 @@ set_target_properties(ggml-base PROPERTIES
SOVERSION ${GGML_VERSION_MAJOR}
)
-target_include_directories(ggml-base PRIVATE .)
+configure_file(ggml-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/ggml-version.h @ONLY)
+
+target_include_directories(ggml-base PRIVATE . ${CMAKE_CURRENT_BINARY_DIR})
if (GGML_BACKEND_DL)
target_compile_definitions(ggml-base PUBLIC GGML_BACKEND_DL)
endif()
@@ -222,9 +224,123 @@ if (GGML_SCHED_NO_REALLOC)
target_compile_definitions(ggml-base PUBLIC GGML_SCHED_NO_REALLOC)
endif()
-if (GGML_OPENMP)
+if (GGML_OPENMP_FETCH)
+ if (NOT GGML_OPENMP)
+ message(FATAL_ERROR "GGML_OPENMP_FETCH requires GGML_OPENMP")
+ elseif (NOT WIN32 OR NOT (CMAKE_C_COMPILER_ID MATCHES "Clang"))
+ message(FATAL_ERROR "GGML_OPENMP_FETCH currently requires Clang on Windows")
+ endif()
+
+ set(GGML_OPENMP_LLVM_VERSION "20.1.8")
+ string(REGEX MATCH "^[0-9]+" GGML_OPENMP_LLVM_VERSION_MAJOR "${GGML_OPENMP_LLVM_VERSION}")
+ string(REGEX MATCH "^[0-9]+" GGML_OPENMP_COMPILER_VERSION_MAJOR "${CMAKE_C_COMPILER_VERSION}")
+ if (NOT GGML_OPENMP_COMPILER_VERSION_MAJOR STREQUAL GGML_OPENMP_LLVM_VERSION_MAJOR)
+ message(FATAL_ERROR "LLVM OpenMP ${GGML_OPENMP_LLVM_VERSION} requires Clang ${GGML_OPENMP_LLVM_VERSION_MAJOR}.x")
+ endif()
+
+ string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" GGML_OPENMP_SYSTEM_PROCESSOR)
+ if (GGML_OPENMP_SYSTEM_PROCESSOR MATCHES "^(amd64|x86_64)$")
+ set(GGML_OPENMP_ARCH "x64")
+ set(GGML_OPENMP_INSTALLER_SUFFIX "win64")
+ set(GGML_OPENMP_INSTALLER_SHA256 "3197846a2b19063687dd56e93e34cd941e3548d907f23a6131571321bdf9fe7b")
+ elseif (GGML_OPENMP_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
+ set(GGML_OPENMP_ARCH "arm64")
+ set(GGML_OPENMP_INSTALLER_SUFFIX "woa64")
+ set(GGML_OPENMP_INSTALLER_SHA256 "7c4ac97eb2ae6b960ca5f9caf3ff6124c8d2a18cc07a7840a4d2ea15537bad8e")
+ else()
+ message(FATAL_ERROR "GGML_OPENMP_FETCH does not support ${CMAKE_SYSTEM_PROCESSOR}")
+ endif()
+
+ set(GGML_OPENMP_CACHE_DIR "${CMAKE_BINARY_DIR}/_deps")
+ set(GGML_OPENMP_ROOT "${GGML_OPENMP_CACHE_DIR}/llvm-openmp-${GGML_OPENMP_LLVM_VERSION}-${GGML_OPENMP_ARCH}")
+ set(GGML_OPENMP_LIBRARY "${GGML_OPENMP_ROOT}/lib/libomp.lib")
+ set(GGML_OPENMP_RUNTIME "${GGML_OPENMP_ROOT}/bin/libomp.dll")
+ set(GGML_OPENMP_HEADER "${GGML_OPENMP_ROOT}/include/omp.h")
+ set(GGML_OPENMP_LICENSE "${GGML_OPENMP_ROOT}/LICENSE.TXT")
+ set(GGML_OPENMP_LICENSE_SHA256 "fdad1758a9e1f9d5a81e18879b3406772115edc92c24bfa36b70c654f325e8e4")
+
+ if (NOT EXISTS "${GGML_OPENMP_LIBRARY}" OR NOT EXISTS "${GGML_OPENMP_RUNTIME}" OR NOT EXISTS "${GGML_OPENMP_HEADER}")
+ find_program(GGML_OPENMP_7Z NAMES 7z 7zz 7za)
+ if (NOT GGML_OPENMP_7Z)
+ message(FATAL_ERROR "GGML_OPENMP_FETCH requires 7-Zip to extract the LLVM installer")
+ endif()
+
+ set(GGML_OPENMP_INSTALLER "${GGML_OPENMP_ROOT}/LLVM-${GGML_OPENMP_LLVM_VERSION}-${GGML_OPENMP_INSTALLER_SUFFIX}.exe")
+ set(GGML_OPENMP_EXTRACT_DIR "${GGML_OPENMP_ROOT}/extract")
+ set(GGML_OPENMP_INSTALLER_URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${GGML_OPENMP_LLVM_VERSION}/LLVM-${GGML_OPENMP_LLVM_VERSION}-${GGML_OPENMP_INSTALLER_SUFFIX}.exe")
+
+ file(MAKE_DIRECTORY "${GGML_OPENMP_EXTRACT_DIR}")
+ file(DOWNLOAD "${GGML_OPENMP_INSTALLER_URL}" "${GGML_OPENMP_INSTALLER}"
+ EXPECTED_HASH "SHA256=${GGML_OPENMP_INSTALLER_SHA256}"
+ SHOW_PROGRESS
+ STATUS GGML_OPENMP_DOWNLOAD_STATUS)
+ list(GET GGML_OPENMP_DOWNLOAD_STATUS 0 GGML_OPENMP_DOWNLOAD_RESULT)
+ if (NOT GGML_OPENMP_DOWNLOAD_RESULT EQUAL 0)
+ list(GET GGML_OPENMP_DOWNLOAD_STATUS 1 GGML_OPENMP_DOWNLOAD_ERROR)
+ message(FATAL_ERROR "Failed to download LLVM OpenMP: ${GGML_OPENMP_DOWNLOAD_ERROR}")
+ endif()
+
+ execute_process(
+ COMMAND "${GGML_OPENMP_7Z}" e -y "-o${GGML_OPENMP_EXTRACT_DIR}" "${GGML_OPENMP_INSTALLER}" -r libomp.lib libomp.dll omp.h
+ RESULT_VARIABLE GGML_OPENMP_EXTRACT_RESULT
+ OUTPUT_QUIET)
+ if (NOT GGML_OPENMP_EXTRACT_RESULT EQUAL 0 OR
+ NOT EXISTS "${GGML_OPENMP_EXTRACT_DIR}/libomp.lib" OR
+ NOT EXISTS "${GGML_OPENMP_EXTRACT_DIR}/libomp.dll" OR
+ NOT EXISTS "${GGML_OPENMP_EXTRACT_DIR}/omp.h")
+ message(FATAL_ERROR "Failed to extract libomp from ${GGML_OPENMP_INSTALLER}")
+ endif()
+
+ file(MAKE_DIRECTORY "${GGML_OPENMP_ROOT}/lib" "${GGML_OPENMP_ROOT}/bin" "${GGML_OPENMP_ROOT}/include")
+ file(COPY "${GGML_OPENMP_EXTRACT_DIR}/libomp.lib" DESTINATION "${GGML_OPENMP_ROOT}/lib")
+ file(COPY "${GGML_OPENMP_EXTRACT_DIR}/libomp.dll" DESTINATION "${GGML_OPENMP_ROOT}/bin")
+ file(COPY "${GGML_OPENMP_EXTRACT_DIR}/omp.h" DESTINATION "${GGML_OPENMP_ROOT}/include")
+ file(REMOVE_RECURSE "${GGML_OPENMP_INSTALLER}" "${GGML_OPENMP_EXTRACT_DIR}")
+ endif()
+
+ # The NSIS installer embeds LLVM's general license in its UI but does not install it as a file; use OpenMP's license to include its additional notices.
+ if (EXISTS "${GGML_OPENMP_LICENSE}")
+ file(SHA256 "${GGML_OPENMP_LICENSE}" GGML_OPENMP_LICENSE_ACTUAL_SHA256)
+ endif()
+ if (NOT GGML_OPENMP_LICENSE_ACTUAL_SHA256 STREQUAL GGML_OPENMP_LICENSE_SHA256)
+ file(DOWNLOAD "https://raw.githubusercontent.com/llvm/llvm-project/llvmorg-${GGML_OPENMP_LLVM_VERSION}/openmp/LICENSE.TXT" "${GGML_OPENMP_LICENSE}"
+ EXPECTED_HASH "SHA256=${GGML_OPENMP_LICENSE_SHA256}")
+ endif()
+
+ if (COMMAND license_add_file)
+ license_add_file("LLVM OpenMP" "${GGML_OPENMP_LICENSE}")
+ endif()
+
+ add_library(ggml-openmp-c INTERFACE)
+ target_compile_options(ggml-openmp-c INTERFACE "$<$:-fopenmp=libomp>")
+ target_include_directories(ggml-openmp-c SYSTEM INTERFACE "${GGML_OPENMP_ROOT}/include")
+ target_link_libraries(ggml-openmp-c INTERFACE "${GGML_OPENMP_LIBRARY}")
+
+ add_library(ggml-openmp-cxx INTERFACE)
+ target_compile_options(ggml-openmp-cxx INTERFACE "$<$:-fopenmp=libomp>")
+ target_include_directories(ggml-openmp-cxx SYSTEM INTERFACE "${GGML_OPENMP_ROOT}/include")
+ target_link_libraries(ggml-openmp-cxx INTERFACE "${GGML_OPENMP_LIBRARY}")
+
+ set(GGML_OPENMP_RUNTIME_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
+ if (CMAKE_CONFIGURATION_TYPES)
+ string(APPEND GGML_OPENMP_RUNTIME_OUTPUT_DIR "/$")
+ endif()
+ add_custom_target(ggml-openmp-runtime ALL
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GGML_OPENMP_RUNTIME_OUTPUT_DIR}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GGML_OPENMP_RUNTIME}" "${GGML_OPENMP_RUNTIME_OUTPUT_DIR}/libomp.dll"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GGML_OPENMP_LICENSE}" "${GGML_OPENMP_RUNTIME_OUTPUT_DIR}/LICENSE-LLVM-OpenMP")
+ add_dependencies(ggml-base ggml-openmp-runtime)
+ install(FILES "${GGML_OPENMP_RUNTIME}" DESTINATION ${CMAKE_INSTALL_BINDIR})
+ install(FILES "${GGML_OPENMP_LICENSE}" DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME LICENSE-LLVM-OpenMP)
+
+ set(GGML_OPENMP_TARGET_C ggml-openmp-c)
+ set(GGML_OPENMP_TARGET_CXX ggml-openmp-cxx)
+ set(GGML_OPENMP_ENABLED "ON" CACHE INTERNAL "")
+elseif (GGML_OPENMP)
find_package(OpenMP)
if (OpenMP_FOUND)
+ set(GGML_OPENMP_TARGET_C OpenMP::OpenMP_C)
+ set(GGML_OPENMP_TARGET_CXX OpenMP::OpenMP_CXX)
set(GGML_OPENMP_ENABLED "ON" CACHE INTERNAL "")
else()
set(GGML_OPENMP_ENABLED "OFF" CACHE INTERNAL "")
@@ -236,7 +352,7 @@ endif()
if (GGML_OPENMP_ENABLED)
target_compile_definitions(ggml-base PRIVATE GGML_USE_OPENMP)
- target_link_libraries(ggml-base PRIVATE OpenMP::OpenMP_C OpenMP::OpenMP_CXX)
+ target_link_libraries(ggml-base PRIVATE ${GGML_OPENMP_TARGET_C} ${GGML_OPENMP_TARGET_CXX})
endif()
add_library(ggml
diff --git a/ggml/src/ggml-alloc.c b/ggml/src/ggml-alloc.c
index 3bda9abb..a71838ea 100644
--- a/ggml/src/ggml-alloc.c
+++ b/ggml/src/ggml-alloc.c
@@ -40,6 +40,7 @@ bool ggml_op_can_inplace(enum ggml_op op) {
case GGML_OP_SILU_BACK:
case GGML_OP_RMS_NORM:
case GGML_OP_RMS_NORM_BACK:
+ case GGML_OP_CLAMP:
case GGML_OP_SOFT_MAX:
case GGML_OP_SOFT_MAX_BACK:
return true;
diff --git a/ggml/src/ggml-backend-impl.h b/ggml/src/ggml-backend-impl.h
index 9c56ec30..ef05905c 100644
--- a/ggml/src/ggml-backend-impl.h
+++ b/ggml/src/ggml-backend-impl.h
@@ -34,6 +34,11 @@ extern "C" {
void * context;
};
+ // [TAG_ALLOC_SIZE_EXPAND]
+ // returns true for ops that may require additional memory for fleeting data on some backends,
+ // i.e. the backend buffer type's get_alloc_size may return more than ggml_nbytes for the output tensor
+ GGML_API bool ggml_op_alloc_size_may_expand(enum ggml_op op);
+
//
// Backend buffer
//
@@ -83,6 +88,7 @@ extern "C" {
GGML_API ggml_backend_buffer_t ggml_backend_multi_buffer_alloc_buffer(ggml_backend_buffer_t * buffers, size_t n_buffers);
GGML_API bool ggml_backend_buffer_is_multi_buffer(ggml_backend_buffer_t buffer);
GGML_API void ggml_backend_multi_buffer_set_usage(ggml_backend_buffer_t buffer, enum ggml_backend_buffer_usage usage);
+ GGML_API void ggml_backend_meta_buffer_set_usage (ggml_backend_buffer_t buffer, enum ggml_backend_buffer_usage usage);
//
// Backend (meta)
@@ -102,6 +108,16 @@ extern "C" {
// Backend (stream)
//
+ // passed to graph_optimize so the backend can add allocation dependencies:
+ // if the backend executes parts of the graph out of order (e.g. on concurrent streams),
+ // it must keep the affected tensors allocated until a node where execution is known to have joined
+ struct ggml_backend_graph_optimize_params {
+ // keep `tensor` allocated at least until `until` (a node of the same graph) has been computed
+ // can be called multiple times for the same tensor: the longest lifetime applies
+ void (*add_alloc_dep)(void * user_data, struct ggml_tensor * tensor, struct ggml_tensor * until);
+ void * user_data;
+ };
+
struct ggml_backend_i {
const char * (*get_name)(ggml_backend_t backend);
@@ -136,7 +152,7 @@ extern "C" {
void (*event_wait) (ggml_backend_t backend, ggml_backend_event_t event);
// (optional) sort/optimize the nodes in the graph
- void (*graph_optimize) (ggml_backend_t backend, struct ggml_cgraph * cgraph);
+ void (*graph_optimize) (ggml_backend_t backend, struct ggml_cgraph * cgraph, struct ggml_backend_graph_optimize_params * params);
};
struct ggml_backend {
diff --git a/ggml/src/ggml-backend-meta.cpp b/ggml/src/ggml-backend-meta.cpp
index 7654ea1f..7c1c0b86 100644
--- a/ggml/src/ggml-backend-meta.cpp
+++ b/ggml/src/ggml-backend-meta.cpp
@@ -592,7 +592,18 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
GGML_ASSERT(split_states_equal(src_ss[0], src_ss[1]));
return {assume_sync ? GGML_BACKEND_SPLIT_AXIS_MIRRORED : GGML_BACKEND_SPLIT_AXIS_PARTIAL, {0}, {1}, 1};
}
- GGML_ABORT("fatal error");
+ if (src_ss[0].axis == src_ss[1].axis && src_ss[0].axis >= GGML_BACKEND_SPLIT_AXIS_2 &&
+ src_ss[0].axis < GGML_MAX_DIMS) {
+ GGML_ASSERT(split_states_equal(src_ss[0], src_ss[1]));
+ return src_ss[0];
+ }
+ // batched matmul with the batches split across devices and a replicated activation
+ if (src_ss[0].axis >= GGML_BACKEND_SPLIT_AXIS_2 && src_ss[0].axis < GGML_MAX_DIMS &&
+ src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED) {
+ return src_ss[0];
+ }
+ GGML_ABORT("unsupported mul_mat split states: node=%s src0=%s axis=%d src1=%s axis=%d",
+ tensor->name, tensor->src[0]->name, (int) src_ss[0].axis, tensor->src[1]->name, (int) src_ss[1].axis);
//return {GGML_BACKEND_SPLIT_AXIS_UNKNOWN, {0}, {1}, 1};
};
@@ -602,27 +613,40 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
case GGML_BACKEND_SPLIT_AXIS_1:
case GGML_BACKEND_SPLIT_AXIS_2:
case GGML_BACKEND_SPLIT_AXIS_3: {
- GGML_ASSERT(src_ss[0].n_segments == 1);
- if (src_ss[0].axis == ggml_n_dims(tensor->src[0]) - 1 && src_ss[0].nr[0] == 1) {
- return {ggml_backend_meta_split_axis(ggml_n_dims(tensor) - 1), {0}, {1}, 1};
- }
- int64_t base_ne_in = tensor->src[0]->ne[0];
- for (int dim = 1; dim <= src_ss[0].axis; dim++) {
+ int64_t base_ne_in = 1;
+ for (int dim = 0; dim <= src_ss[0].axis; dim++) {
base_ne_in *= tensor->src[0]->ne[dim];
}
- base_ne_in /= src_ss[0].nr[0];
+ if (src_ss[0].n_segments == 1) {
+ base_ne_in /= src_ss[0].nr[0];
+ if (src_ss[0].axis == ggml_n_dims(tensor->src[0]) - 1 && src_ss[0].nr[0] == 1) {
+ return {ggml_backend_meta_split_axis(ggml_n_dims(tensor) - 1), {0}, {1}, 1};
+ }
+ if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_0 && tensor->ne[0] == tensor->src[0]->ne[0] &&
+ tensor->ne[1] == 1 && src_ss[0].nr[0] == 1) {
+ bool complete_rows = true;
+ for (size_t j = 0; j < n_bufs; j++) {
+ const int64_t ne = src_ss[0].ne[j];
+ complete_rows = complete_rows && (ne == 0 || ne == tensor->src[0]->ne[0]);
+ }
+ if (complete_rows) {
+ // Move a complete dim-0 split to the following singleton dimension.
+ return {GGML_BACKEND_SPLIT_AXIS_1, {0}, {1}, 1};
+ }
+ }
+ }
+ // Reshape outputs use one segment; split-state propagation merges source segments.
int64_t base_ne_out = 1;
for (int dim = 0; dim < GGML_MAX_DIMS; dim++) {
- const int64_t base_ne_out_next = base_ne_out *= tensor->ne[dim];
- if (base_ne_out_next % base_ne_in == 0) {
- return {ggml_backend_meta_split_axis(dim), {0}, {uint32_t(base_ne_out_next/base_ne_in)}, 1};
+ base_ne_out *= tensor->ne[dim];
+ if (base_ne_out % base_ne_in == 0) {
+ return {ggml_backend_meta_split_axis(dim), {0}, {uint32_t(base_ne_out/base_ne_in)}, 1};
}
- if (base_ne_out_next > base_ne_in) {
+ if (base_ne_out > base_ne_in) {
GGML_ASSERT(src_ss[0].n_segments == 1);
GGML_ASSERT(src_ss[0].nr[0] == 1);
return {ggml_backend_meta_split_axis(dim), {0}, {1}, 1};
}
- base_ne_out = base_ne_out_next;
}
GGML_ABORT("shape mismatch for %s", ggml_op_name(tensor->op));
}
@@ -747,14 +771,33 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
};
auto handle_flash_attn_ext = [&](const std::vector & src_ss) -> ggml_backend_meta_split_state {
- GGML_ASSERT( src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_2);
- GGML_ASSERT( src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_2);
- GGML_ASSERT( src_ss[2].axis == GGML_BACKEND_SPLIT_AXIS_2);
- GGML_ASSERT(tensor->src[4] == nullptr || src_ss[3].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+ GGML_ASSERT(tensor->src[3] == nullptr || src_ss[3].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+
+ if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED) {
+ GGML_ASSERT(src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+ GGML_ASSERT(src_ss[2].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+ GGML_ASSERT(tensor->src[4] == nullptr || src_ss[4].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+ return {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1};
+ }
+
+ GGML_ASSERT(src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_2);
+ const bool kv_split = src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_2 &&
+ src_ss[2].axis == GGML_BACKEND_SPLIT_AXIS_2;
+ const bool kv_mirrored = src_ss[1].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED &&
+ src_ss[2].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED;
+ GGML_ASSERT(kv_split || kv_mirrored);
GGML_ASSERT(tensor->src[4] == nullptr || src_ss[4].axis == GGML_BACKEND_SPLIT_AXIS_0);
return {GGML_BACKEND_SPLIT_AXIS_1, {0}, {1}, 1};
};
+ auto handle_lightning_indexer = [&](
+ const std::vector & src_ss) -> ggml_backend_meta_split_state {
+ for (size_t i = 0; i < 4; i++) {
+ GGML_ASSERT(src_ss[i].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+ }
+ return {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1};
+ };
+
auto handle_ssm_conv = [&](const std::vector & src_ss) -> ggml_backend_meta_split_state {
if (src_ss[0].axis == src_ss[1].axis) {
if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_0) {
@@ -792,7 +835,7 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
ggml_backend_dev_t dev = ggml_backend_buft_get_device(ggml_backend_buffer_get_type(tensor->buffer));
const ggml_backend_meta_device_context * dev_ctx = (const ggml_backend_meta_device_context *) dev->context;
ggml_backend_meta_split_state ret = dev_ctx->get_split_state(tensor, dev_ctx->get_split_state_ud);
- if (ret.axis >= 0 && ret.axis <= GGML_MAX_DIMS) {
+ if (ret.axis >= 0 && ret.axis < GGML_MAX_DIMS) {
const int64_t granularity = ret.axis == GGML_BACKEND_SPLIT_AXIS_0 ? ggml_blck_size(tensor->type) : 1;
int64_t ne_sum = 0;
for (size_t s = 0; s < ret.n_segments; s++) {
@@ -802,6 +845,9 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
}
}
GGML_ASSERT(ne_sum == tensor->ne[ret.axis]);
+ } else if (ret.axis == GGML_BACKEND_SPLIT_AXIS_PARTIAL) {
+ GGML_ASSERT(ret.n_segments == 1);
+ GGML_ASSERT(ret.nr[0] == 1);
}
return ret;
}
@@ -922,7 +968,7 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
split_state = handle_rope(src_ss);
} break;
case GGML_OP_ROPE_BACK: {
- split_state = handle_generic(src_ss, /*scalar_only =*/ true);
+ split_state = handle_rope(src_ss);
} break;
case GGML_OP_CLAMP: {
split_state = handle_generic(src_ss, /*scalar_only =*/ false);
@@ -986,6 +1032,9 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
case GGML_OP_GATED_DELTA_NET: {
split_state = handle_gated_delta_net(src_ss);
} break;
+ case GGML_OP_LIGHTNING_INDEXER: {
+ split_state = handle_lightning_indexer(src_ss);
+ } break;
case GGML_OP_DSV4_HC_COMB:
case GGML_OP_DSV4_HC_PRE:
case GGML_OP_DSV4_HC_POST: {
@@ -1070,13 +1119,14 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
if (buf_ctx->debug > 0) {
std::string srcs_info;
for (size_t i = 0; i < GGML_MAX_SRC; i++) {
- if (tensor->src[i] == nullptr) {
+ if (tensor->src[i] == nullptr || tensor->src[i] == tensor) {
continue;
}
if (!srcs_info.empty()) {
srcs_info += ", ";
}
- const ggml_backend_meta_split_state split_state = ggml_backend_meta_get_split_state(tensor->src[0], true);
+ const ggml_backend_meta_split_state split_state =
+ ggml_backend_meta_get_split_state(tensor->src[i], true);
GGML_ASSERT(split_state.n_segments == 1);
const char * axis_name = ggml_backend_meta_split_axis_name(split_state.axis);
std::string ne_info;
@@ -1118,7 +1168,6 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
}
static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(const struct ggml_tensor * tensor, bool assume_sync) {
- GGML_ASSERT(ggml_backend_buffer_is_meta(tensor->buffer));
ggml_backend_meta_buffer_context * buf_ctx = (ggml_backend_meta_buffer_context *) tensor->buffer->context;
return ggml_backend_meta_get_split_state(buf_ctx->get_simple_tensor_container(tensor), tensor, assume_sync);
}
@@ -1151,11 +1200,6 @@ static enum ggml_status ggml_backend_meta_buffer_init_tensor_impl(ggml_backend_m
ggml_context * simple_ctx = stc.ctxs[j].get();
ggml_backend_buffer_t simple_buf = buf_ctx->bufs[j].get();
- if ((simple_buf != nullptr) && ggml_backend_buffer_is_multi_buffer(simple_buf)) {
- // see https://github.com/ggml-org/llama.cpp/issues/22197
- GGML_ABORT("multi buffers are not supported by the meta backend");
- }
-
if (split_dim >= 0 && split_dim < GGML_MAX_DIMS) {
// TODO: the following assert fails for llama-parallel even though the results are correct:
// GGML_ASSERT(ggml_is_contiguously_allocated(tensor));
@@ -1203,13 +1247,31 @@ static enum ggml_status ggml_backend_meta_buffer_init_tensor_impl(ggml_backend_m
}
}
}
+ // TODO: revisit once the graph allocator has been refactored, see https://github.com/ggml-org/llama.cpp/pull/25051#issuecomment-4842873396
+ ggml_backend_buffer_t init_buf = simple_buf;
if (t_ij->view_src != nullptr) {
t_ij->data = (char *) t_ij->view_src->data + t_ij->view_offs;
+ // views inherit the source slice's concrete sub-buffer (issue 22197)
+ if (tensor->view_src != nullptr && ggml_backend_buffer_is_meta(tensor->view_src->buffer)
+ && t_ij->view_src->buffer != nullptr) {
+ t_ij->buffer = t_ij->view_src->buffer;
+ init_buf = t_ij->view_src->buffer;
+ }
} else if (simple_buf != nullptr) {
+ if (ggml_backend_buffer_is_multi_buffer(simple_buf)) {
+ GGML_ABORT("multi buffers are not supported by the meta backend");
+ }
t_ij->data = (char *) ggml_backend_buffer_get_base(simple_buf)
+ size_t(tensor->data) - size_t(ggml_backend_buffer_get_base(tensor->buffer));
}
- t_ij->extra = tensor->extra;
+
+ if (init_buf) {
+ // the backend that owns the buffer will set .extra
+ ggml_backend_buffer_init_tensor(init_buf, t_ij);
+ } else {
+ t_ij->extra = tensor->extra;
+ }
+
for (int i = 0; i < GGML_MAX_SRC; i++) {
t_ij->src[i] = tensor->src[i];
if (tensor->src[i] == tensor) {
@@ -1255,6 +1317,108 @@ static enum ggml_status ggml_backend_meta_buffer_init_tensor(ggml_backend_buffer
return ggml_backend_meta_buffer_init_tensor_impl(buf_ctx->get_simple_tensor_container(tensor), tensor);
}
+static void ggml_backend_meta_buffer_memset_tensor(
+ ggml_backend_buffer_t buffer, ggml_tensor * tensor, uint8_t value, size_t offset, size_t size) {
+ const size_t n_bufs = ggml_backend_meta_buffer_n_bufs(buffer);
+ const ggml_backend_meta_split_state split_state =
+ ggml_backend_meta_get_split_state(tensor, /*assume_sync =*/ false);
+ GGML_ASSERT(ggml_is_contiguous(tensor) || split_state.axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED);
+
+ if (split_state.n_segments != 1 || split_state.nr[0] != 1) {
+ GGML_ASSERT(split_state.axis >= 0 && split_state.axis < GGML_MAX_DIMS);
+ GGML_ASSERT(split_state.nr[0] != 0);
+ GGML_ASSERT(tensor->ne[3] == 1);
+
+ std::vector simple_offsets(n_bufs, 0);
+ if (split_state.axis == GGML_BACKEND_SPLIT_AXIS_0) {
+ GGML_ASSERT(tensor->ne[2] == 1);
+
+ const size_t row_stride = tensor->nb[1];
+ GGML_ASSERT(offset % row_stride == 0);
+ GGML_ASSERT(size % row_stride == 0);
+ const int64_t row_start = offset / row_stride;
+ const int64_t row_count = size / row_stride;
+ GGML_ASSERT(row_start + row_count <= tensor->ne[1]);
+
+ const int64_t blck_size = ggml_blck_size(tensor->type);
+ for (size_t s = 0; s < split_state.n_segments; s++) {
+ for (size_t r = 0; r < split_state.nr[s]; r++) {
+ for (size_t j = 0; j < n_bufs; j++) {
+ ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
+ GGML_ASSERT(split_state.ne[s*n_bufs + j] % blck_size == 0);
+ const size_t nbytes = split_state.ne[s*n_bufs + j]/blck_size * tensor->nb[0];
+ for (int64_t row = 0; row < row_count; row++) {
+ ggml_backend_tensor_memset(simple_tensor, value,
+ simple_offsets[j] + (row_start + row)*simple_tensor->nb[1], nbytes);
+ }
+ simple_offsets[j] += nbytes;
+ }
+ }
+ }
+ return;
+ }
+
+ GGML_ASSERT(split_state.axis == GGML_BACKEND_SPLIT_AXIS_1);
+
+ const size_t row_stride = tensor->nb[2];
+ GGML_ASSERT(offset % row_stride == 0);
+ GGML_ASSERT(size % row_stride == 0);
+ const int64_t row_start = offset / row_stride;
+ const int64_t row_count = size / row_stride;
+ GGML_ASSERT(row_start + row_count <= tensor->ne[2]);
+
+ for (size_t s = 0; s < split_state.n_segments; s++) {
+ for (size_t r = 0; r < split_state.nr[s]; r++) {
+ for (size_t j = 0; j < n_bufs; j++) {
+ ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
+ const size_t nbytes = split_state.ne[s*n_bufs + j] * tensor->nb[1];
+ for (int64_t row = 0; row < row_count; row++) {
+ ggml_backend_tensor_memset(simple_tensor, value,
+ simple_offsets[j] + (row_start + row)*simple_tensor->nb[2], nbytes);
+ }
+ simple_offsets[j] += nbytes;
+ }
+ }
+ }
+ return;
+ }
+
+ switch (split_state.axis) {
+ case GGML_BACKEND_SPLIT_AXIS_0:
+ case GGML_BACKEND_SPLIT_AXIS_1:
+ case GGML_BACKEND_SPLIT_AXIS_2: {
+ const size_t chunk_size_full = tensor->nb[split_state.axis + 1];
+ GGML_ASSERT(offset % chunk_size_full == 0);
+ GGML_ASSERT(size % chunk_size_full == 0);
+ const int64_t i_start = offset / chunk_size_full;
+ const int64_t i_stop = (offset + size) / chunk_size_full;
+ for (size_t j = 0; j < n_bufs; j++) {
+ ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
+ const size_t chunk_size = simple_tensor->nb[split_state.axis + 1];
+ if (chunk_size == 0) {
+ continue;
+ }
+ for (int64_t i = i_start; i < i_stop; i++) {
+ ggml_backend_tensor_memset(simple_tensor, value, i*chunk_size, chunk_size);
+ }
+ }
+ } break;
+ case GGML_BACKEND_SPLIT_AXIS_PARTIAL: {
+ GGML_ASSERT(value == 0);
+ [[fallthrough]];
+ }
+ case GGML_BACKEND_SPLIT_AXIS_MIRRORED: {
+ for (size_t j = 0; j < n_bufs; j++) {
+ ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
+ ggml_backend_tensor_memset(simple_tensor, value, offset, size);
+ }
+ } break;
+ default: {
+ GGML_ABORT("fatal error");
+ }
+ }
+}
+
static void ggml_backend_meta_buffer_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
const size_t n_bufs = ggml_backend_meta_buffer_n_bufs(buffer);
const ggml_backend_meta_split_state split_state = ggml_backend_meta_get_split_state(tensor, /*assume_sync =*/ false);
@@ -1352,15 +1516,29 @@ static void ggml_backend_meta_buffer_set_tensor(ggml_backend_buffer_t buffer, gg
} break;
case GGML_BACKEND_SPLIT_AXIS_PARTIAL: {
GGML_ASSERT(tensor->type == GGML_TYPE_F32);
- const int64_t ne = ggml_nelements(tensor);
- std::vector tmp;
- tmp.reserve(ne);
- for (int64_t i = 0; i < ne; i++) {
- tmp.push_back(((const float *) data)[i] / n_bufs);
+ GGML_ASSERT(offset % sizeof(float) == 0);
+ GGML_ASSERT(size % sizeof(float) == 0);
+ const size_t n_values = size / sizeof(float);
+ size_t n_contributors = 0;
+ for (size_t j = 0; j < n_bufs; j++) {
+ n_contributors += split_state.ne[j] != 0;
+ }
+ const bool has_contributor_mask = n_contributors != 0;
+ if (!has_contributor_mask) {
+ n_contributors = n_bufs;
+ }
+ std::vector tmp(n_values);
+ for (size_t i = 0; i < n_values; i++) {
+ tmp[i] = ((const float *) data)[i] / n_contributors;
+ }
+ std::vector zero;
+ if (has_contributor_mask) {
+ zero.resize(n_values, 0.0f);
}
for (size_t j = 0; j < n_bufs; j++) {
ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
- ggml_backend_tensor_set(simple_tensor, tmp.data(), offset, size);
+ const float * partial = has_contributor_mask && split_state.ne[j] == 0 ? zero.data() : tmp.data();
+ ggml_backend_tensor_set(simple_tensor, partial, offset, size);
}
} break;
default: {
@@ -1488,7 +1666,7 @@ static const ggml_backend_buffer_i ggml_backend_meta_buffer_iface = {
/* .free_buffer = */ ggml_backend_meta_buffer_free_buffer,
/* .get_base = */ ggml_backend_meta_buffer_get_base,
/* .init_tensor = */ ggml_backend_meta_buffer_init_tensor,
- /* .memset_tensor = */ nullptr, // TODO implement
+ /* .memset_tensor = */ ggml_backend_meta_buffer_memset_tensor,
/* .set_tensor = */ ggml_backend_meta_buffer_set_tensor,
/* .get_tensor = */ ggml_backend_meta_buffer_get_tensor,
/* .set_tensor_2d = */ nullptr,
@@ -1502,6 +1680,16 @@ bool ggml_backend_buffer_is_meta(ggml_backend_buffer_t buf) {
return buf != nullptr && buf->iface.free_buffer == ggml_backend_meta_buffer_iface.free_buffer;
}
+void ggml_backend_meta_buffer_set_usage(ggml_backend_buffer_t buffer, enum ggml_backend_buffer_usage usage) {
+ GGML_ASSERT(ggml_backend_buffer_is_meta(buffer));
+ ggml_backend_meta_buffer_context * buf_ctx = (ggml_backend_meta_buffer_context *) buffer->context;
+ for (size_t i = 0; i < buf_ctx->bufs.size(); i++) {
+ if (buf_ctx->bufs[i]) {
+ ggml_backend_buffer_set_usage(buf_ctx->bufs[i].get(), usage);
+ }
+ }
+}
+
static ggml_backend_buffer_t ggml_backend_meta_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
const size_t n_simple_bufts = ggml_backend_meta_buft_n_bufts(buft);
@@ -1841,7 +2029,7 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
{
// For MoE models it may make sense to delay the AllReduce in order to reduce I/O:
- auto get_i_delayed = [&](const int i) -> int {
+ auto get_i_delayed_branch = [&](const int i) -> int {
int id = i; // i_delayed
int idr = i; // i_delayed return, last safe return value
@@ -1941,6 +2129,62 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
return idr;
};
+ // AllReduce(a) + AllReduce(b) == AllReduce(a + b) for independent partial branches.
+ auto get_i_delayed = [&](const int i) -> int {
+ const int i_delayed = get_i_delayed_branch(i);
+ ggml_tensor * node = cgraph->nodes[i_delayed];
+
+ if (ggml_node_get_use_count(cgraph, i_delayed) != 1) {
+ return i_delayed;
+ }
+
+ for (int id = i_delayed + 1; id < cgraph->n_nodes; id++) {
+ ggml_tensor * next = cgraph->nodes[id];
+ if (next->view_src == node) {
+ return i_delayed;
+ }
+ for (int s = 0; s < GGML_MAX_SRC; s++) {
+ if (next->src[s] == node) {
+ return i_delayed;
+ }
+ }
+
+ if (next->view_src != nullptr && next->view_src->op == GGML_OP_NONE && ggml_backend_buffer_is_host(next->view_src->buffer)) {
+ continue;
+ }
+ if (ggml_backend_meta_get_split_state(next, false).axis != GGML_BACKEND_SPLIT_AXIS_PARTIAL) {
+ continue;
+ }
+
+ const int i_other = id;
+ const int i_other_delayed = get_i_delayed_branch(i_other);
+ ggml_tensor * other = cgraph->nodes[i_other_delayed];
+ if (ggml_node_get_use_count(cgraph, i_other_delayed) != 1 || i_other_delayed + 1 >= cgraph->n_nodes) {
+ return i_delayed;
+ }
+
+ ggml_tensor * sum = cgraph->nodes[i_other_delayed + 1];
+ if (sum->op != GGML_OP_ADD ||
+ !ggml_are_same_shape(node, other) || node->type != other->type || sum->type != node->type ||
+ !((sum->src[0] == node && sum->src[1] == other) ||
+ (sum->src[0] == other && sum->src[1] == node)) ||
+ ggml_backend_meta_get_split_state(sum, false).axis != GGML_BACKEND_SPLIT_AXIS_MIRRORED) {
+ return i_delayed;
+ }
+
+ for (size_t j = 0; j < n_backends; j++) {
+ auto & bcj = backend_ctx->backend_configs[j];
+ const bool compute = bcj.nodes[i]->flags & GGML_TENSOR_FLAG_COMPUTE;
+ const bool compute_other = bcj.nodes[i_other]->flags & GGML_TENSOR_FLAG_COMPUTE;
+ if (compute != compute_other) {
+ return i_delayed;
+ }
+ }
+ return i_other_delayed + 1;
+ }
+ return i_delayed;
+ };
+
int i_start = 0;
for (int i = 0; i < cgraph->n_nodes; i++) {
ggml_tensor * node = cgraph->nodes[i];
diff --git a/ggml/src/ggml-backend-reg.cpp b/ggml/src/ggml-backend-reg.cpp
index e5959467..1c18b82c 100644
--- a/ggml/src/ggml-backend-reg.cpp
+++ b/ggml/src/ggml-backend-reg.cpp
@@ -490,7 +490,13 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent,
#endif
// default search paths: executable directory, current directory
search_paths.push_back(get_executable_path());
- search_paths.push_back(fs::current_path());
+ std::error_code cwd_ec;
+ const fs::path cwd = fs::current_path(cwd_ec);
+ if (cwd_ec) {
+ GGML_LOG_DEBUG("%s: current_path() failure, error-message: %s\n", __func__, cwd_ec.message().c_str());
+ } else {
+ search_paths.push_back(cwd);
+ }
} else {
search_paths.push_back(fs::u8path(user_search_path));
}
@@ -508,8 +514,14 @@ static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent,
}
continue;
}
- fs::directory_iterator dir_it(search_path, fs::directory_options::skip_permission_denied);
- for (const auto & entry : dir_it) {
+ std::error_code dir_ec;
+ fs::directory_iterator dir_it(search_path, fs::directory_options::skip_permission_denied, dir_ec);
+ if (dir_ec) {
+ GGML_LOG_DEBUG("%s: failed to enumerate %s: %s\n", __func__, path_str(search_path).c_str(), dir_ec.message().c_str());
+ continue;
+ }
+ for (const fs::directory_iterator end; dir_it != end; dir_it.increment(dir_ec)) {
+ const auto & entry = *dir_it;
if (entry.is_regular_file(ec)) {
auto filename = entry.path().filename();
auto ext = entry.path().extension();
diff --git a/ggml/src/ggml-backend.cpp b/ggml/src/ggml-backend.cpp
index f6fb9179..20bf9650 100644
--- a/ggml/src/ggml-backend.cpp
+++ b/ggml/src/ggml-backend.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#ifdef __APPLE__
@@ -64,6 +65,14 @@ size_t ggml_backend_buft_get_alloc_size(ggml_backend_buffer_type_t buft, const s
if (buft->iface.get_alloc_size) {
size_t size = buft->iface.get_alloc_size(buft, tensor);
assert(size >= ggml_nbytes(tensor));
+
+ // [TAG_ALLOC_SIZE_EXPAND]
+ // if you hit this assert, update ggml_backend_op_alloc_size_may_expand() accordingly
+ GGML_ASSERT(size <= ggml_nbytes(tensor) ||
+ ggml_op_is_empty(tensor->op) ||
+ ggml_is_quantized(tensor->type) || // [TAG_ALLOC_SIZE_EXPAND]
+ ggml_op_alloc_size_may_expand(tensor->op));
+
return size;
}
return ggml_nbytes(tensor);
@@ -182,6 +191,8 @@ void ggml_backend_buffer_set_usage(ggml_backend_buffer_t buffer, enum ggml_backe
// FIXME: add a generic callback to the buffer interface
if (ggml_backend_buffer_is_multi_buffer(buffer)) {
ggml_backend_multi_buffer_set_usage(buffer, usage);
+ } else if (ggml_backend_buffer_is_meta(buffer)) {
+ ggml_backend_meta_buffer_set_usage(buffer, usage);
}
}
@@ -556,10 +567,10 @@ void ggml_backend_event_wait(ggml_backend_t backend, ggml_backend_event_t event)
backend->iface.event_wait(backend, event);
}
-static void ggml_backend_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
+static void ggml_backend_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * cgraph, struct ggml_backend_graph_optimize_params * params) {
GGML_ASSERT(backend);
if (backend->iface.graph_optimize != NULL) {
- backend->iface.graph_optimize(backend, cgraph);
+ backend->iface.graph_optimize(backend, cgraph, params);
}
}
@@ -838,7 +849,7 @@ static void ggml_backend_sched_split_inputs_grow(struct ggml_backend_sched_split
int new_cap = GGML_SCHED_MAX_SPLIT_INPUTS;
if (split->inputs_capacity > 0) {
new_cap = 2*split->inputs_capacity;
- GGML_LOG_WARN("%s: increasing split inputs capacity from %d to %d\n", __func__, split->inputs_capacity, new_cap);
+ GGML_LOG_DEBUG("%s: increasing split inputs capacity from %d to %d\n", __func__, split->inputs_capacity, new_cap);
}
auto * pnew = (struct ggml_tensor **) realloc((void *) split->inputs, new_cap * sizeof(struct ggml_tensor *));
if (pnew == NULL) {
@@ -853,7 +864,7 @@ static void ggml_backend_sched_graph_inputs_grow(ggml_backend_sched_t sched) {
int new_cap = GGML_SCHED_MAX_SPLIT_INPUTS;
if (sched->graph_inputs_capacity > 0) {
new_cap = 2*sched->graph_inputs_capacity;
- GGML_LOG_WARN("%s: increasing graph inputs capacity from %d to %d\n", __func__, sched->graph_inputs_capacity, new_cap);
+ GGML_LOG_DEBUG("%s: increasing graph inputs capacity from %d to %d\n", __func__, sched->graph_inputs_capacity, new_cap);
}
auto * pnew = (struct ggml_tensor **) realloc((void *) sched->graph_inputs, new_cap * sizeof(struct ggml_tensor *));
if (pnew == NULL) {
@@ -1327,17 +1338,6 @@ void ggml_backend_sched_split_graph(ggml_backend_sched_t sched, struct ggml_cgra
break;
}
}
- // check if the split has too many inputs
- // FIXME: count the number of inputs instead of only checking when full
- if (split->n_inputs >= split->inputs_capacity) {
- const size_t id = hash_id(src);
- int src_backend_id = sched->hv_tensor_backend_ids[id];
- bool supported = ggml_backend_sched_buffer_supported(sched, src, cur_backend_id);
- if (src_backend_id != cur_backend_id && tensor_id_copy(id, cur_backend_id, 0) == NULL && !supported) {
- need_new_split = true;
- break;
- }
- }
}
}
@@ -1439,11 +1439,40 @@ void ggml_backend_sched_split_graph(ggml_backend_sched_t sched, struct ggml_cgra
sched->prev_leaf_backend_ids = tmp;
}
+ // optimize the split graphs and collect the allocation dependencies added by the backends
+ // this needs to happen before we make graph_copy, so they are in sync
+ // TODO: this may create many small allocations in the scheduler, restructure to use a flat array
+ std::unordered_map> alloc_deps;
+
+ struct ggml_backend_graph_optimize_params opt_params = {
+ /* .add_alloc_dep = */ [](void * user_data, ggml_tensor * tensor, ggml_tensor * until) {
+ auto & deps = *(std::unordered_map> *) user_data;
+ std::vector & keep = deps[until];
+ if (std::find(keep.begin(), keep.end(), tensor) == keep.end()) {
+ keep.push_back(tensor);
+ }
+ },
+ /* .user_data = */ &alloc_deps,
+ };
+
+ for (int i = 0; i < sched->n_splits; i++) {
+ struct ggml_backend_sched_split * split = &sched->splits[i];
+ split->graph = ggml_graph_view(graph, split->i_start, split->i_end);
+
+ ggml_backend_graph_optimize(sched->backends[split->backend_id], &split->graph, &opt_params);
+ }
+
+ // each dep is added to graph_copy as a GGML_OP_NONE node with the kept tensors as srcs
+ int n_dep_nodes = 0;
+ for (const auto & it : alloc_deps) {
+ n_dep_nodes += (it.second.size() + GGML_MAX_SRC - 1) / GGML_MAX_SRC;
+ }
+
int total_inputs = sched->n_graph_inputs;
for (int i = 0; i < sched->n_splits; i++) {
total_inputs += sched->splits[i].n_inputs;
}
- int graph_size = std::max(graph->n_nodes, graph->n_leafs) + total_inputs * 2 * sched->n_copies;
+ int graph_size = std::max(graph->n_nodes, graph->n_leafs) + total_inputs * 2 * sched->n_copies + n_dep_nodes;
// remember the actual graph_size for performing reallocation checks later [GGML_SCHED_DEBUG_REALLOC]
sched->debug_prev_graph_size = sched->debug_graph_size;
@@ -1461,13 +1490,10 @@ void ggml_backend_sched_split_graph(ggml_backend_sched_t sched, struct ggml_cgra
struct ggml_cgraph * graph_copy = &sched->graph;
+ int n_dep_nodes_added = 0;
+
for (int i = 0; i < sched->n_splits; i++) {
struct ggml_backend_sched_split * split = &sched->splits[i];
- split->graph = ggml_graph_view(graph, split->i_start, split->i_end);
-
- // Optimize this split of the graph. This needs to happen before we make graph_copy,
- // so they are in sync.
- ggml_backend_graph_optimize(sched->backends[split->backend_id], &split->graph);
// add inputs to the graph copy so that they are allocated by ggml-alloc at the start of the split
for (int j = 0; j < split->n_inputs; j++) {
@@ -1492,9 +1518,32 @@ void ggml_backend_sched_split_graph(ggml_backend_sched_t sched, struct ggml_cgra
assert(graph_copy->size > graph_copy->n_nodes);
sched->node_backend_ids[graph_copy->n_nodes] = tensor_backend_id(graph->nodes[j]);
graph_copy->nodes[graph_copy->n_nodes++] = graph->nodes[j];
+
+ if (alloc_deps.empty()) {
+ continue;
+ }
+
+ // add a dependency node so that the kept tensors are not freed before this node is computed
+ auto it = alloc_deps.find(graph->nodes[j]);
+ if (it != alloc_deps.end()) {
+ const std::vector & keep = it->second;
+ for (size_t k = 0; k < keep.size(); k += GGML_MAX_SRC) {
+ struct ggml_tensor * dep = ggml_view_tensor(sched->ctx, keep[k]);
+ for (size_t s = 0; s < GGML_MAX_SRC && k + s < keep.size(); s++) {
+ dep->src[s] = keep[k + s];
+ }
+ assert(graph_copy->size > graph_copy->n_nodes);
+ sched->node_backend_ids[graph_copy->n_nodes] = split->backend_id;
+ graph_copy->nodes[graph_copy->n_nodes++] = dep;
+ n_dep_nodes_added++;
+ }
+ }
}
}
+ // a mismatch means a backend added a dep with an `until` tensor that is not a node of the optimized graph
+ GGML_ASSERT(n_dep_nodes_added == n_dep_nodes);
+
if (sched->n_copies > 1) {
// add input copies as leafs so that they are allocated first
for (int i = 0; i < sched->n_graph_inputs; i++) {
@@ -1581,7 +1630,10 @@ static bool ggml_backend_sched_alloc_splits(ggml_backend_sched_t sched) {
ggml_backend_synchronize(sched->backends[i]);
}
- ggml_gallocr_reserve_n(sched->galloc, &sched->graph, sched->node_backend_ids, sched->leaf_backend_ids);
+ if (!ggml_gallocr_reserve_n(sched->galloc, &sched->graph, sched->node_backend_ids, sched->leaf_backend_ids)) {
+ GGML_LOG_ERROR("%s: failed to reserve graph buffers\n", __func__);
+ return false;
+ }
if (!ggml_gallocr_alloc_graph(sched->galloc, &sched->graph)) {
GGML_LOG_ERROR("%s: failed to allocate graph\n", __func__);
return false;
@@ -1599,11 +1651,23 @@ static enum ggml_status ggml_backend_sched_compute_splits(ggml_backend_sched_t s
std::vector ids;
std::vector used_ids;
+ int prev_backend_id = -1;
+
for (int split_id = 0; split_id < sched->n_splits; split_id++) {
struct ggml_backend_sched_split * split = &splits[split_id];
int split_backend_id = split->backend_id;
ggml_backend_t split_backend = sched->backends[split_backend_id];
+ // ensure the previous split's async work has completed before we start
+ // this split, the allocator may have reused buffer regions across splits
+ if (split->n_inputs == 0 && prev_backend_id >= 0 && prev_backend_id != split_backend_id) {
+ if (sched->events[prev_backend_id][sched->cur_copy] != NULL) {
+ ggml_backend_event_synchronize(sched->events[prev_backend_id][sched->cur_copy]);
+ } else {
+ ggml_backend_synchronize(sched->backends[prev_backend_id]);
+ }
+ }
+
// copy the input tensors to the split backend
for (int input_id = 0; input_id < split->n_inputs; input_id++) {
ggml_backend_t input_backend = ggml_backend_sched_get_tensor_backend(sched, split->inputs[input_id]);
@@ -1644,6 +1708,10 @@ static enum ggml_status ggml_backend_sched_compute_splits(ggml_backend_sched_t s
ggml_tensor * ids_tensor = node->src[2];
ggml_backend_t ids_backend = split_backend;
+ if (ggml_nelements(ids_tensor) == 0) {
+ continue;
+ }
+
// if the ids tensor is also an input of the split, it may not have been copied yet to the split backend
// in that case, we use the original ids tensor
for (int i = input_id + 1; i < split->n_inputs; i++) {
@@ -1766,12 +1834,12 @@ static enum ggml_status ggml_backend_sched_compute_splits(ggml_backend_sched_t s
}
}
- // record the event of this copy
- if (split->n_inputs > 0) {
- if (sched->events[split_backend_id][sched->cur_copy] != NULL) {
- ggml_backend_event_record(sched->events[split_backend_id][sched->cur_copy], split_backend);
- }
+ // record the event of this split
+ if (sched->events[split_backend_id][sched->cur_copy] != NULL) {
+ ggml_backend_event_record(sched->events[split_backend_id][sched->cur_copy], split_backend);
}
+
+ prev_backend_id = split_backend_id;
}
return GGML_STATUS_SUCCESS;
@@ -2037,6 +2105,20 @@ ggml_backend_t ggml_backend_sched_get_tensor_backend(ggml_backend_sched_t sched,
// utils
+bool ggml_op_alloc_size_may_expand(enum ggml_op op) {
+ switch (op) {
+ case GGML_OP_FLASH_ATTN_EXT:
+ case GGML_OP_MUL_MAT:
+ case GGML_OP_MUL_MAT_ID:
+ case GGML_OP_CUMSUM:
+ case GGML_OP_ARGSORT:
+ case GGML_OP_TOP_K:
+ return true;
+ default:
+ return false;
+ }
+}
+
enum ggml_status ggml_backend_view_init(struct ggml_tensor * tensor) {
GGML_ASSERT(tensor);
GGML_ASSERT(tensor->buffer == NULL);
diff --git a/ggml/src/ggml-cann/aclnn_ops.cpp b/ggml/src/ggml-cann/aclnn_ops.cpp
index 2dc0f409..902d2eda 100644
--- a/ggml/src/ggml-cann/aclnn_ops.cpp
+++ b/ggml/src/ggml-cann/aclnn_ops.cpp
@@ -211,6 +211,50 @@ void ggml_cann_swiglu(ggml_backend_cann_context & ctx, ggml_tensor * dst) {
GGML_CANN_CALL_ACLNN_OP(ctx, SwiGlu, acl_src.get(), (int64_t)2, acl_dst.get());
}
+void ggml_cann_swiglu_clamp(ggml_backend_cann_context & ctx, ggml_tensor * dst) {
+ ggml_tensor * src0 = dst->src[0];
+ ggml_tensor * src1 = dst->src[1];
+
+ GGML_ASSERT(ggml_is_contiguous_1(src0));
+ GGML_ASSERT(ggml_is_contiguous_1(dst));
+
+ const int32_t swapped = ggml_get_op_params_i32(dst, 1);
+ acl_tensor_ptr acl_gate;
+ acl_tensor_ptr acl_up;
+ if (src1) {
+ GGML_ASSERT(ggml_is_contiguous_1(src1));
+ GGML_ASSERT(src0->type == src1->type);
+ acl_gate = ggml_cann_create_tensor(src0);
+ acl_up = ggml_cann_create_tensor(src1);
+ } else {
+ int64_t ne[] = { src0->ne[0] / 2, src0->ne[1], src0->ne[2], src0->ne[3] };
+ size_t nb[] = { src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3] };
+ acl_gate = ggml_cann_create_tensor(src0, ne, nb, GGML_MAX_DIMS, ACL_FORMAT_ND, 0);
+ acl_up = ggml_cann_create_tensor(src0, ne, nb, GGML_MAX_DIMS, ACL_FORMAT_ND, ne[0] * ggml_element_size(src0));
+ if (swapped) {
+ std::swap(acl_gate, acl_up);
+ }
+ }
+
+ ggml_cann_pool_alloc temp_alloc(ctx.pool(), ggml_nbytes(dst));
+ acl_tensor_ptr acl_temp = ggml_cann_create_tensor(temp_alloc.get(), ggml_cann_type_mapping(dst->type),
+ ggml_element_size(dst), dst->ne, dst->nb, GGML_MAX_DIMS);
+ acl_tensor_ptr acl_dst = ggml_cann_create_tensor(dst);
+
+ const float limit = ggml_get_op_params_f32(dst, 3);
+ float min_gate = -INFINITY;
+ float min_up = -limit;
+ float max_value = limit;
+ acl_scalar_ptr acl_min_gate = ggml_cann_create_scalar(&min_gate, ACL_FLOAT);
+ acl_scalar_ptr acl_min_up = ggml_cann_create_scalar(&min_up, ACL_FLOAT);
+ acl_scalar_ptr acl_limit = ggml_cann_create_scalar(&max_value, ACL_FLOAT);
+
+ GGML_CANN_CALL_ACLNN_OP(ctx, Clamp, acl_gate.get(), acl_min_gate.get(), acl_limit.get(), acl_temp.get());
+ GGML_CANN_CALL_ACLNN_OP(ctx, Silu, acl_temp.get(), acl_dst.get());
+ GGML_CANN_CALL_ACLNN_OP(ctx, Clamp, acl_up.get(), acl_min_up.get(), acl_limit.get(), acl_temp.get());
+ GGML_CANN_CALL_ACLNN_OP(ctx, InplaceMul, acl_dst.get(), acl_temp.get());
+}
+
// Fused GeGLU using aclnnGeGluV3: splits input along ne[0] (CANN last dim),
// activates the LEFT half with GELU, multiplies by right half.
// approximate: 0=tanh, 1=none(erf). activateLeft=true matches GGML convention.
@@ -4433,4 +4477,3 @@ void ggml_cann_gated_linear_attn(ggml_backend_cann_context & ctx, ggml_tensor *
}
}
}
-
diff --git a/ggml/src/ggml-cann/aclnn_ops.h b/ggml/src/ggml-cann/aclnn_ops.h
index cdbf9260..678f4d65 100644
--- a/ggml/src/ggml-cann/aclnn_ops.h
+++ b/ggml/src/ggml-cann/aclnn_ops.h
@@ -76,6 +76,7 @@
void ggml_cann_repeat(ggml_backend_cann_context & ctx, ggml_tensor * dst);
void ggml_cann_swiglu(ggml_backend_cann_context & ctx, ggml_tensor * dst);
+void ggml_cann_swiglu_clamp(ggml_backend_cann_context & ctx, ggml_tensor * dst);
void ggml_cann_geglu(ggml_backend_cann_context & ctx, ggml_tensor * dst, int64_t approximate);
/**
diff --git a/ggml/src/ggml-cann/ggml-cann.cpp b/ggml/src/ggml-cann/ggml-cann.cpp
index ffa361af..c2745014 100644
--- a/ggml/src/ggml-cann/ggml-cann.cpp
+++ b/ggml/src/ggml-cann/ggml-cann.cpp
@@ -1872,6 +1872,9 @@ static bool ggml_cann_compute_forward(ggml_backend_cann_context & ctx, struct gg
case GGML_GLU_OP_SWIGLU:
ggml_cann_swiglu(ctx, dst);
break;
+ case GGML_GLU_OP_SWIGLU_CLAMP:
+ ggml_cann_swiglu_clamp(ctx, dst);
+ break;
case GGML_GLU_OP_GEGLU_QUICK:
ggml_cann_geglu_quick(ctx, dst);
break;
@@ -2428,6 +2431,7 @@ static bool ggml_backend_cann_supports_op(ggml_backend_dev_t dev, const ggml_ten
case GGML_GLU_OP_SWIGLU:
case GGML_GLU_OP_GEGLU_ERF:
case GGML_GLU_OP_GEGLU_QUICK:
+ case GGML_GLU_OP_SWIGLU_CLAMP:
return true;
default:
return false;
@@ -2534,6 +2538,9 @@ static bool ggml_backend_cann_supports_op(ggml_backend_dev_t dev, const ggml_ten
}
case GGML_OP_ROPE:
{
+ if (((const int32_t *) op->op_params)[15] != 0) {
+ return false; // FIXME: support ggml_rope_set_offset
+ }
if (op->src[0]->ne[0] > 896) {
return false;
}
diff --git a/ggml/src/ggml-common.h b/ggml/src/ggml-common.h
index 83f9118d..1dbbe326 100644
--- a/ggml/src/ggml-common.h
+++ b/ggml/src/ggml-common.h
@@ -1131,7 +1131,7 @@ GGML_TABLE_END()
#define NGRID_IQ1S 2048
#define IQ1S_DELTA 0.125f
#define IQ1M_DELTA 0.125f
-#if defined(GGML_COMMON_IMPL_C)
+#if defined(GGML_COMMON_IMPL_C) || defined(GGML_COMMON_IMPL_CPP)
GGML_TABLE_BEGIN(uint64_t, iq1s_grid, NGRID_IQ1S)
0xffffffffffffffff, 0xffffffffffffff01, 0xffffffffffff0000, 0xffffffffffff01ff,
0xffffffffffff0101, 0xffffffffff00ff00, 0xffffffffff000000, 0xffffffffff01ffff,
diff --git a/ggml/src/ggml-cpu/CMakeLists.txt b/ggml/src/ggml-cpu/CMakeLists.txt
index 836bae4d..1c7338ee 100644
--- a/ggml/src/ggml-cpu/CMakeLists.txt
+++ b/ggml/src/ggml-cpu/CMakeLists.txt
@@ -31,6 +31,8 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
ggml-cpu/ggml-cpu.cpp
ggml-cpu/repack.cpp
ggml-cpu/repack.h
+ ggml-cpu/iqp.cpp
+ ggml-cpu/iqp.h
ggml-cpu/hbm.cpp
ggml-cpu/hbm.h
ggml-cpu/quants.c
@@ -74,7 +76,7 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
if (GGML_OPENMP_ENABLED)
target_compile_definitions(${GGML_CPU_NAME} PRIVATE GGML_USE_OPENMP)
- target_link_libraries(${GGML_CPU_NAME} PRIVATE OpenMP::OpenMP_C OpenMP::OpenMP_CXX)
+ target_link_libraries(${GGML_CPU_NAME} PRIVATE ${GGML_OPENMP_TARGET_C} ${GGML_OPENMP_TARGET_CXX})
endif()
if (GGML_LLAMAFILE)
@@ -453,12 +455,16 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
ggml-cpu/spacemit/repack.h
ggml-cpu/spacemit/ime_env.cpp
ggml-cpu/spacemit/ime_env.h
- ggml-cpu/spacemit/ime1_kernels.cpp
- ggml-cpu/spacemit/ime2_kernels.cpp
ggml-cpu/spacemit/ime_kernels.h
ggml-cpu/spacemit/rvv_kernels.cpp
ggml-cpu/spacemit/rvv_kernels.h
)
+ if ("RISCV64_SPACEMIT_IME1" IN_LIST RISCV64_SPACEMIT_IME_SPEC)
+ list(APPEND GGML_CPU_SOURCES ggml-cpu/spacemit/ime1_kernels.cpp)
+ endif()
+ if ("RISCV64_SPACEMIT_IME2" IN_LIST RISCV64_SPACEMIT_IME_SPEC)
+ list(APPEND GGML_CPU_SOURCES ggml-cpu/spacemit/ime2_kernels.cpp)
+ endif()
endif()
if(NOT GGML_CPU_ALL_VARIANTS)
set(MARCH_STR "rv64gc")
@@ -514,7 +520,9 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
elseif (GGML_SYSTEM_ARCH STREQUAL "s390x")
message(STATUS "s390x detected")
list(APPEND GGML_CPU_SOURCES
- ggml-cpu/arch/s390/quants.c)
+ ggml-cpu/arch/s390/quants.c
+ ggml-cpu/arch/s390/repack.cpp
+ )
# for native compilation
if (GGML_NATIVE)
@@ -576,10 +584,25 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
endif()
if (GGML_CPU_KLEIDIAI)
- message(STATUS "Using KleidiAI optimized kernels if applicable")
+ # upstream repo requires at least cmake 3.16
+ if (CMAKE_VERSION VERSION_LESS 3.16)
+ message(FATAL_ERROR "GGML_CPU_KLEIDIAI requires CMake >= 3.16")
+ endif()
- # Disable the KleidiAI tests
- set(KLEIDIAI_BUILD_TESTS OFF)
+ set(GGML_CPU_KLEIDIAI_AARCH64 OFF)
+ if (GGML_SYSTEM_ARCH STREQUAL "ARM" AND
+ (APPLE OR WIN32 OR CMAKE_SYSTEM_NAME MATCHES "^(Linux|Android)$") AND
+ (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64|arm64-v8a)$" OR
+ CMAKE_OSX_ARCHITECTURES MATCHES "arm64" OR
+ CMAKE_GENERATOR_PLATFORM_LWR STREQUAL "arm64" OR
+ CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a"))
+ set(GGML_CPU_KLEIDIAI_AARCH64 ON)
+ endif()
+ if (NOT GGML_CPU_KLEIDIAI_AARCH64)
+ message(FATAL_ERROR "GGML_CPU_KLEIDIAI requires a Linux, Android, Apple, or Windows AArch64/arm64 target")
+ endif()
+
+ message(STATUS "Using KleidiAI optimized kernels if applicable")
# Fetch KleidiAI sources:
include(FetchContent)
@@ -595,31 +618,49 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
list(APPEND KLEIDIAI_FETCH_ARGS DOWNLOAD_EXTRACT_TIMESTAMP NEW)
endif()
- if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
- FetchContent_Declare(KleidiAI_Download
- ${KLEIDIAI_FETCH_ARGS}
- EXCLUDE_FROM_ALL
- )
+ FetchContent_Declare(kleidiai
+ ${KLEIDIAI_FETCH_ARGS}
+ )
- FetchContent_MakeAvailable(KleidiAI_Download)
- FetchContent_GetProperties(KleidiAI_Download SOURCE_DIR KLEIDIAI_SRC)
- else()
- FetchContent_Declare(KleidiAI_Download
- ${KLEIDIAI_FETCH_ARGS}
- )
+ # Disable tests and benchmark building
+ set(KLEIDIAI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
+ set(KLEIDIAI_BUILD_BENCHMARK OFF CACHE BOOL "" FORCE)
- FetchContent_GetProperties(KleidiAI_Download
+ # Use the Populate/add_subdirectory flow for compatibility with CMake 3.16.
+ FetchContent_GetProperties(kleidiai
+ SOURCE_DIR KLEIDIAI_SRC
+ BINARY_DIR KLEIDIAI_BIN
+ POPULATED KLEIDIAI_POPULATED
+ )
+ if (NOT KLEIDIAI_POPULATED)
+ FetchContent_Populate(kleidiai)
+ FetchContent_GetProperties(kleidiai
SOURCE_DIR KLEIDIAI_SRC
- POPULATED KLEIDIAI_POPULATED
+ BINARY_DIR KLEIDIAI_BIN
)
+ endif()
- if (NOT KLEIDIAI_POPULATED)
- FetchContent_Populate(KleidiAI_Download)
- FetchContent_GetProperties(KleidiAI_Download SOURCE_DIR KLEIDIAI_SRC)
+ if (NOT TARGET kleidiai)
+ add_subdirectory(
+ "${CMAKE_CURRENT_SOURCE_DIR}/ggml-cpu/kleidiai"
+ "${CMAKE_CURRENT_BINARY_DIR}/kleidiai-wrapper"
+ EXCLUDE_FROM_ALL
+ )
+ if (NOT CMAKE_SKIP_INSTALL_RULES AND
+ (NOT DEFINED BUILD_SHARED_LIBS OR NOT BUILD_SHARED_LIBS))
+ install(TARGETS kleidiai ARCHIVE)
endif()
endif()
- add_compile_definitions(GGML_USE_CPU_KLEIDIAI)
+ if (NOT TARGET kleidiai)
+ message(FATAL_ERROR "KleidiAI target was not created")
+ endif()
+
+ set_target_properties(kleidiai PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
+ target_link_libraries(${GGML_CPU_NAME} PRIVATE kleidiai)
+
+ target_compile_definitions(${GGML_CPU_NAME} PRIVATE GGML_USE_CPU_KLEIDIAI)
list(APPEND GGML_CPU_SOURCES
ggml-cpu/kleidiai/kleidiai.cpp
@@ -627,105 +668,6 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
ggml-cpu/kleidiai/kleidiai.h
ggml-cpu/kleidiai/kernels.h
)
-
- # KleidiAI
- include_directories(
- ${KLEIDIAI_SRC}/
- ${KLEIDIAI_SRC}/kai/
- ${KLEIDIAI_SRC}/kai/ukernels/
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/
- ${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}")
- if (NOT ARCH_FLAGS_TEMP)
- string(REGEX MATCH "-march=[^ ]+" ARCH_FLAGS_TEMP "${CMAKE_C_FLAGS}")
- endif()
- string(FIND "${ARCH_FLAGS_TEMP}" "+dotprod" DOTPROD_ENABLED)
- string(FIND "${ARCH_FLAGS_TEMP}" "+i8mm" I8MM_ENABLED)
- string(FIND "${ARCH_FLAGS_TEMP}" "+sme" SME_ENABLED)
- string(FIND "${ARCH_FLAGS_TEMP}" "+sve" SVE_ENABLED)
-
- set(PRIVATE_ARCH_FLAGS ${ARCH_FLAGS_TEMP})
-
- list(APPEND GGML_KLEIDIAI_SOURCES
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p_f32.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p4x8sb_f32_neon.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi4c32ps1s0scalef16_qsu4c32s16s0_neon.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p_f32_neon.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_lhs_quant_pack_qai8dxp_f32.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi8cxp_qsi8cx_neon.c)
-
- if (NOT DOTPROD_ENABLED MATCHES -1)
- list(APPEND GGML_KLEIDIAI_SOURCES
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4x4_1x4_neon_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x4_qsi4c32p4x4_16x4_neon_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp4x4_qsi8cxp4x4_16x4_neon_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4x4_1x4_neon_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x8_qsi8cxp4x8_1x4_neon_dotprod.c)
- endif()
-
- if (NOT I8MM_ENABLED MATCHES -1)
- list(APPEND GGML_KLEIDIAI_SOURCES
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p4x8_16x4_neon_i8mm.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp4x8_qsi8cxp4x8_16x4_neon_i8mm.c)
- endif()
-
- if (NOT SME_ENABLED MATCHES -1)
- list(APPEND GGML_KLEIDIAI_SME_SOURCES
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme_mopa.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme_mopa_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme_dot.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme_dot_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1b_2vlx2vl_sme_mopa.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1b_2vlx2vl_sme_mopa_asm.S)
- set_source_files_properties(${GGML_KLEIDIAI_SME_SOURCES}
- PROPERTIES COMPILE_OPTIONS "-fno-tree-vectorize;${ARCH_FLAGS_TEMP}+sve+sve2+sme")
- list(APPEND GGML_CPU_SOURCES ${GGML_KLEIDIAI_SME_SOURCES})
-
- list(APPEND GGML_KLEIDIAI_SME2_SOURCES
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4vlx4_1x4vl_sme2_sdot.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_fp32_bf16p_bf16p/kai_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa.c
- ${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_source_files_properties(${GGML_KLEIDIAI_SME2_SOURCES}
- PROPERTIES COMPILE_OPTIONS "-fno-tree-vectorize;${ARCH_FLAGS_TEMP}+sve+sve2+sme2+fp16")
- list(APPEND GGML_CPU_SOURCES ${GGML_KLEIDIAI_SME2_SOURCES})
- set(PRIVATE_ARCH_FLAGS "-fno-tree-vectorize;${PRIVATE_ARCH_FLAGS}")
- endif()
-
- if (NOT SVE_ENABLED MATCHES -1)
- list(APPEND GGML_KLEIDIAI_SOURCES
- ${KLEIDIAI_SRC}/kai/kai_common_sve_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p8x8_1x8_sve_dotprod_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p8x8_1x8_sve_dotprod.c
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p8x8_16x8_sve_i8mm_asm.S
- ${KLEIDIAI_SRC}/kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p8x8_16x8_sve_i8mm.c)
- endif()
-
- set_source_files_properties(${GGML_KLEIDIAI_SOURCES} PROPERTIES COMPILE_OPTIONS "${PRIVATE_ARCH_FLAGS}")
- list(APPEND GGML_CPU_SOURCES ${GGML_KLEIDIAI_SOURCES})
endif()
message(STATUS "Adding CPU backend variant ${GGML_CPU_NAME}: ${ARCH_FLAGS} ${ARCH_DEFINITIONS}")
@@ -737,8 +679,9 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
set_target_properties(${GGML_CPU_NAME} PROPERTIES COMPILE_FLAGS "-msimd128")
endif()
- if (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
- # The compiler automatically enables "-ffast-math" which can cause NaNs in tests due to "-fassociative-math"
- target_compile_options(${GGML_CPU_NAME} PRIVATE "-fno-associative-math")
- endif()
+ if (CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
+ # The compiler automatically enables "-ffast-math" which can cause NaNs in tests due to "-fassociative-math"
+ target_compile_options(${GGML_CPU_NAME} PRIVATE "$<$,$>:$<$:/clang:>-fno-associative-math>")
+ endif()
+
endfunction()
diff --git a/ggml/src/ggml-cpu/arch-fallback.h b/ggml/src/ggml-cpu/arch-fallback.h
index 152e0bac..4dbd1982 100644
--- a/ggml/src/ggml-cpu/arch-fallback.h
+++ b/ggml/src/ggml-cpu/arch-fallback.h
@@ -39,6 +39,8 @@
#define ggml_quantize_mat_q8_0_4x8_generic ggml_quantize_mat_q8_0_4x8
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_0_8x8_q8_0_generic ggml_gemv_q4_0_8x8_q8_0
@@ -55,6 +57,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_0_8x8_q8_0_generic ggml_gemm_q4_0_8x8_q8_0
@@ -87,6 +91,8 @@
// repack.cpp
#define ggml_quantize_mat_q8_0_4x4_generic ggml_quantize_mat_q8_0_4x4
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_K_8x4_q8_K_generic ggml_gemv_q4_K_8x4_q8_K
@@ -98,6 +104,8 @@
#define ggml_gemv_mxfp4_4x4_q8_0_generic ggml_gemv_mxfp4_4x4_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_K_8x4_q8_K_generic ggml_gemm_q4_K_8x4_q8_K
@@ -124,6 +132,8 @@
#define ggml_quantize_mat_q8_0_4x8_generic ggml_quantize_mat_q8_0_4x8
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_0_8x8_q8_0_generic ggml_gemv_q4_0_8x8_q8_0
@@ -140,6 +150,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_0_8x8_q8_0_generic ggml_gemm_q4_0_8x8_q8_0
@@ -171,6 +183,8 @@
#define ggml_quantize_mat_q8_0_4x8_generic ggml_quantize_mat_q8_0_4x8
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_0_8x8_q8_0_generic ggml_gemv_q4_0_8x8_q8_0
@@ -187,6 +201,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_0_8x8_q8_0_generic ggml_gemm_q4_0_8x8_q8_0
@@ -213,6 +229,8 @@
#define ggml_quantize_mat_q8_K_4x1_generic ggml_quantize_mat_q8_K_4x1
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q2_K_8x8_q8_K_generic ggml_gemv_q2_K_8x8_q8_K
@@ -228,6 +246,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q2_K_8x8_q8_K_generic ggml_gemm_q2_K_8x8_q8_K
@@ -247,7 +267,6 @@
// quants.c
#define quantize_row_q8_K_generic quantize_row_q8_K
#define ggml_vec_dot_nvfp4_q8_0_generic ggml_vec_dot_nvfp4_q8_0
-#define ggml_vec_dot_q1_0_q8_0_generic ggml_vec_dot_q1_0_q8_0
#define ggml_vec_dot_q2_0_q8_0_generic ggml_vec_dot_q2_0_q8_0
#define ggml_vec_dot_tq1_0_q8_K_generic ggml_vec_dot_tq1_0_q8_K
#define ggml_vec_dot_tq2_0_q8_K_generic ggml_vec_dot_tq2_0_q8_K
@@ -260,11 +279,11 @@
#define ggml_vec_dot_iq1_s_q8_K_generic ggml_vec_dot_iq1_s_q8_K
#define ggml_vec_dot_iq1_m_q8_K_generic ggml_vec_dot_iq1_m_q8_K
// repack.cpp
-#define ggml_quantize_mat_q8_0_4x4_generic ggml_quantize_mat_q8_0_4x4
#define ggml_quantize_mat_q8_0_4x8_generic ggml_quantize_mat_q8_0_4x8
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
-#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_0_8x8_q8_0_generic ggml_gemv_q4_0_8x8_q8_0
#define ggml_gemv_q2_K_8x8_q8_K_generic ggml_gemv_q2_K_8x8_q8_K
@@ -280,7 +299,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
-#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_0_8x8_q8_0_generic ggml_gemm_q4_0_8x8_q8_0
#define ggml_gemm_q2_K_8x8_q8_K_generic ggml_gemm_q2_K_8x8_q8_K
@@ -318,6 +338,8 @@
#define ggml_quantize_mat_q8_0_4x8_generic ggml_quantize_mat_q8_0_4x8
#define ggml_quantize_mat_q8_K_4x4_generic ggml_quantize_mat_q8_K_4x4
#define ggml_quantize_mat_q8_K_4x8_generic ggml_quantize_mat_q8_K_4x8
+#define ggml_gemv_q1_0_4x4_q8_0_generic ggml_gemv_q1_0_4x4_q8_0
+#define ggml_gemv_q1_0_4x8_q8_0_generic ggml_gemv_q1_0_4x8_q8_0
#define ggml_gemv_q4_0_4x4_q8_0_generic ggml_gemv_q4_0_4x4_q8_0
#define ggml_gemv_q4_0_4x8_q8_0_generic ggml_gemv_q4_0_4x8_q8_0
#define ggml_gemv_q4_0_8x8_q8_0_generic ggml_gemv_q4_0_8x8_q8_0
@@ -334,6 +356,8 @@
#define ggml_gemv_mxfp4_8x8_q8_0_generic ggml_gemv_mxfp4_8x8_q8_0
#define ggml_gemv_q8_0_4x4_q8_0_generic ggml_gemv_q8_0_4x4_q8_0
#define ggml_gemv_q8_0_4x8_q8_0_generic ggml_gemv_q8_0_4x8_q8_0
+#define ggml_gemm_q1_0_4x4_q8_0_generic ggml_gemm_q1_0_4x4_q8_0
+#define ggml_gemm_q1_0_4x8_q8_0_generic ggml_gemm_q1_0_4x8_q8_0
#define ggml_gemm_q4_0_4x4_q8_0_generic ggml_gemm_q4_0_4x4_q8_0
#define ggml_gemm_q4_0_4x8_q8_0_generic ggml_gemm_q4_0_4x8_q8_0
#define ggml_gemm_q4_0_8x8_q8_0_generic ggml_gemm_q4_0_8x8_q8_0
diff --git a/ggml/src/ggml-cpu/arch/arm/repack.cpp b/ggml/src/ggml-cpu/arch/arm/repack.cpp
index a7534443..ad0e5cca 100644
--- a/ggml/src/ggml-cpu/arch/arm/repack.cpp
+++ b/ggml/src/ggml-cpu/arch/arm/repack.cpp
@@ -48,6 +48,24 @@ static inline void decode_q_Kx8_6bit_scales(const uint8_t * scales_in, int16x8_t
}
#endif
+#if defined(__aarch64__) && defined(__ARM_NEON) && (defined(__ARM_FEATURE_DOTPROD) || defined(__ARM_FEATURE_MATMUL_INT8))
+#define B1(c,s,n) 0x ## n ## c , 0x ## n ## s
+#define B2(c,s,n) B1(c,s,n ## c), B1(c,s,n ## s)
+#define B3(c,s,n) B2(c,s,n ## c), B2(c,s,n ## s)
+#define B4(c,s,n) B3(c,s,n ## c), B3(c,s,n ## s)
+#define B5(c,s,n) B4(c,s,n ## c), B4(c,s,n ## s)
+#define B6(c,s,n) B5(c,s,n ## c), B5(c,s,n ## s)
+#define B7(c,s,n) B6(c,s,n ## c), B6(c,s,n ## s)
+#define B8(c,s ) B7(c,s, c), B7(c,s, s)
+
+static const uint64_t table_q1_signs[256] = { B8(ff, 01) };
+
+static inline int8x16_t ggml_q1_0_unpack_pair(uint8_t bits0, uint8_t bits1) {
+ return vreinterpretq_s8_u8(vcombine_u8(vcreate_u8(table_q1_signs[bits0]),
+ vcreate_u8(table_q1_signs[bits1])));
+}
+#endif
+
void ggml_quantize_mat_q8_0_4x4(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k) {
assert(QK8_0 == 32);
assert(k % QK8_0 == 0);
@@ -1823,6 +1841,132 @@ void ggml_gemv_q8_0_4x8_q8_0(int n,
ggml_gemv_q8_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
}
+void ggml_gemv_q1_0_4x4_q8_0(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
+ for (int c = 0; c < nc; c += ncols_interleaved) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (c / ncols_interleaved) * nb;
+ const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
+ float32x4_t acc = vdupq_n_f32(0);
+
+ for (int l = 0; l < nb; l++) {
+ const float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *) b_ptr[l].d));
+ float32x4_t accb = vdupq_n_f32(0);
+
+ for (int k = 0; k < 4; k++) {
+ const block_q8_0 * GGML_RESTRICT a_blk = a_ptr + l * 4 + k;
+ const float ad = GGML_CPU_FP16_TO_FP32(a_blk->d);
+ int32x4_t ret = vdupq_n_s32(0);
+
+ for (int tile = 0; tile < 8; tile += 4) {
+ const int8x16_t signs0 = ggml_q1_0_unpack_pair(b_ptr[l].qs[k * 16 + 2 * (tile + 0) + 0],
+ b_ptr[l].qs[k * 16 + 2 * (tile + 0) + 1]);
+ const int8x16_t signs1 = ggml_q1_0_unpack_pair(b_ptr[l].qs[k * 16 + 2 * (tile + 1) + 0],
+ b_ptr[l].qs[k * 16 + 2 * (tile + 1) + 1]);
+ const int8x16_t signs2 = ggml_q1_0_unpack_pair(b_ptr[l].qs[k * 16 + 2 * (tile + 2) + 0],
+ b_ptr[l].qs[k * 16 + 2 * (tile + 2) + 1]);
+ const int8x16_t signs3 = ggml_q1_0_unpack_pair(b_ptr[l].qs[k * 16 + 2 * (tile + 3) + 0],
+ b_ptr[l].qs[k * 16 + 2 * (tile + 3) + 1]);
+ const int8x16_t q_tiles = vld1q_s8(a_blk->qs + tile * 4);
+
+ ret = vdotq_laneq_s32(ret, signs0, q_tiles, 0);
+ ret = vdotq_laneq_s32(ret, signs1, q_tiles, 1);
+ ret = vdotq_laneq_s32(ret, signs2, q_tiles, 2);
+ ret = vdotq_laneq_s32(ret, signs3, q_tiles, 3);
+ }
+
+ accb = vfmaq_n_f32(accb, vcvtq_f32_s32(ret), ad);
+ }
+ acc = vfmaq_f32(acc, accb, b_d);
+ }
+ vst1q_f32(s, acc);
+ s += ncols_interleaved;
+ }
+ return;
+#endif
+ ggml_gemv_q1_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+}
+
+void ggml_gemv_q1_0_4x8_q8_0(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
+ for (int c = 0; c < nc; c += ncols_interleaved) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (c / ncols_interleaved) * nb;
+ const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
+ float32x4_t acc = vdupq_n_f32(0);
+
+ for (int l = 0; l < nb; l++) {
+ const float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *) b_ptr[l].d));
+ float32x4_t accb = vdupq_n_f32(0);
+
+ for (int k = 0; k < 4; ++k) {
+ const block_q8_0 * GGML_RESTRICT a_blk = a_ptr + l * 4 + k;
+ const uint8_t * GGML_RESTRICT b_qs = (const uint8_t *) b_ptr[l].qs + k * 16;
+ const float ad = GGML_CPU_FP16_TO_FP32(a_blk->d);
+
+ int8x8x4_t a_chunks = vld1_s8_x4(a_blk->qs);
+ int8x16_t a0 = vcombine_s8(a_chunks.val[0], a_chunks.val[0]);
+ int8x16_t a1 = vcombine_s8(a_chunks.val[1], a_chunks.val[1]);
+ int8x16_t a2 = vcombine_s8(a_chunks.val[2], a_chunks.val[2]);
+ int8x16_t a3 = vcombine_s8(a_chunks.val[3], a_chunks.val[3]);
+
+ int32x4_t ret0 = vdupq_n_s32(0);
+ int32x4_t ret1 = vdupq_n_s32(0);
+
+ ret0 = vdotq_s32(ret0, ggml_q1_0_unpack_pair(b_qs[0], b_qs[1]), a0);
+ ret1 = vdotq_s32(ret1, ggml_q1_0_unpack_pair(b_qs[2], b_qs[3]), a0);
+ ret0 = vdotq_s32(ret0, ggml_q1_0_unpack_pair(b_qs[4], b_qs[5]), a1);
+ ret1 = vdotq_s32(ret1, ggml_q1_0_unpack_pair(b_qs[6], b_qs[7]), a1);
+ ret0 = vdotq_s32(ret0, ggml_q1_0_unpack_pair(b_qs[8], b_qs[9]), a2);
+ ret1 = vdotq_s32(ret1, ggml_q1_0_unpack_pair(b_qs[10], b_qs[11]), a2);
+ ret0 = vdotq_s32(ret0, ggml_q1_0_unpack_pair(b_qs[12], b_qs[13]), a3);
+ ret1 = vdotq_s32(ret1, ggml_q1_0_unpack_pair(b_qs[14], b_qs[15]), a3);
+
+ accb = vfmaq_n_f32(accb, vcvtq_f32_s32(vpaddq_s32(ret0, ret1)), ad);
+ }
+
+ acc = vfmaq_f32(acc, accb, b_d);
+ }
+
+ vst1q_f32(s, acc);
+ s += ncols_interleaved;
+ }
+ return;
+#endif
+
+ ggml_gemv_q1_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+}
+
void ggml_gemm_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
const int qk = QK8_0;
const int nb = n / qk;
@@ -5154,3 +5298,168 @@ void ggml_gemm_q8_0_4x8_q8_0(int n,
#endif // defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
ggml_gemm_q8_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
}
+
+void ggml_gemm_q1_0_4x4_q8_0(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(n % qk == 0);
+ assert(nr % 4 == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (4 * y * nb);
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ float32x4_t sumf[4];
+ for (int m = 0; m < 4; m++) {
+ sumf[m] = vdupq_n_f32(0);
+ }
+
+ for (int l = 0; l < nb; l++) {
+ float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *) b_ptr[l].d));
+ float32x4_t blockf_0 = vdupq_n_f32(0);
+ float32x4_t blockf_1 = vdupq_n_f32(0);
+ float32x4_t blockf_2 = vdupq_n_f32(0);
+ float32x4_t blockf_3 = vdupq_n_f32(0);
+
+ for (int k = 0; k < 4; ++k) {
+ const block_q8_0x4 * GGML_RESTRICT a_blk = a_ptr + 4 * l + k;
+ float32x4_t a_d = vcvt_f32_f16(vld1_f16((const float16_t *) a_blk->d));
+
+ int32x4_t sumi_0 = vdupq_n_s32(0);
+ int32x4_t sumi_1 = vdupq_n_s32(0);
+ int32x4_t sumi_2 = vdupq_n_s32(0);
+ int32x4_t sumi_3 = vdupq_n_s32(0);
+
+ for (int tile = 0; tile < 8; ++tile) {
+ const int8x16_t signs = ggml_q1_0_unpack_pair(b_ptr[l].qs[k * 16 + 2 * tile + 0],
+ b_ptr[l].qs[k * 16 + 2 * tile + 1]);
+ const int8x16_t a_tile = vld1q_s8(a_blk->qs + tile * 16);
+
+ sumi_0 = vdotq_laneq_s32(sumi_0, signs, a_tile, 0);
+ sumi_1 = vdotq_laneq_s32(sumi_1, signs, a_tile, 1);
+ sumi_2 = vdotq_laneq_s32(sumi_2, signs, a_tile, 2);
+ sumi_3 = vdotq_laneq_s32(sumi_3, signs, a_tile, 3);
+ }
+
+ blockf_0 = vfmaq_laneq_f32(blockf_0, vcvtq_f32_s32(sumi_0), a_d, 0);
+ blockf_1 = vfmaq_laneq_f32(blockf_1, vcvtq_f32_s32(sumi_1), a_d, 1);
+ blockf_2 = vfmaq_laneq_f32(blockf_2, vcvtq_f32_s32(sumi_2), a_d, 2);
+ blockf_3 = vfmaq_laneq_f32(blockf_3, vcvtq_f32_s32(sumi_3), a_d, 3);
+ }
+
+ sumf[0] = vfmaq_f32(sumf[0], blockf_0, b_d);
+ sumf[1] = vfmaq_f32(sumf[1], blockf_1, b_d);
+ sumf[2] = vfmaq_f32(sumf[2], blockf_2, b_d);
+ sumf[3] = vfmaq_f32(sumf[3], blockf_3, b_d);
+ }
+
+ for (int m = 0; m < 4; m++) {
+ vst1q_f32(s + (y * 4 + m) * bs + x * 4, sumf[m]);
+ }
+ }
+ }
+ return;
+#endif
+ ggml_gemm_q1_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+}
+
+void ggml_gemm_q1_0_4x8_q8_0(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(n % qk == 0);
+ assert(nr % 4 == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (4 * y * nb);
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ float32x4_t sumf[4];
+ for (int m = 0; m < 4; ++m) {
+ sumf[m] = vdupq_n_f32(0);
+ }
+
+ for (int l = 0; l < nb; l++) {
+ const float32x4_t b_d = vcvt_f32_f16(vld1_f16((const float16_t *) b_ptr[l].d));
+ float32x4_t blockf[4];
+ for (int m = 0; m < 4; ++m) {
+ blockf[m] = vdupq_n_f32(0);
+ }
+
+ for (int k = 0; k < 4; ++k) {
+ const block_q8_0x4 * GGML_RESTRICT a_blk = a_ptr + 4 * l + k;
+ const uint8_t * GGML_RESTRICT b_qs = (const uint8_t *) b_ptr[l].qs + k * 16;
+
+ int32x4_t acc[4];
+ for (int i = 0; i < 4; ++i) {
+ acc[i] = vdupq_n_s32(0);
+ }
+
+ for (int chunk = 0; chunk < 4; ++chunk) {
+ const int8x16_t a01 = vld1q_s8(a_blk->qs + chunk * 32);
+ const int8x16_t a23 = vld1q_s8(a_blk->qs + chunk * 32 + 16);
+ const int8x16_t b01 = ggml_q1_0_unpack_pair(b_qs[chunk * 4 + 0], b_qs[chunk * 4 + 1]);
+ const int8x16_t b23 = ggml_q1_0_unpack_pair(b_qs[chunk * 4 + 2], b_qs[chunk * 4 + 3]);
+
+ acc[0] = vmmlaq_s32(acc[0], a01, b01);
+ acc[1] = vmmlaq_s32(acc[1], a01, b23);
+ acc[2] = vmmlaq_s32(acc[2], a23, b01);
+ acc[3] = vmmlaq_s32(acc[3], a23, b23);
+ }
+
+ const int32x4_t row0 = vcombine_s32(vget_low_s32(acc[0]), vget_low_s32(acc[1]));
+ const int32x4_t row1 = vcombine_s32(vget_high_s32(acc[0]), vget_high_s32(acc[1]));
+ const int32x4_t row2 = vcombine_s32(vget_low_s32(acc[2]), vget_low_s32(acc[3]));
+ const int32x4_t row3 = vcombine_s32(vget_high_s32(acc[2]), vget_high_s32(acc[3]));
+ const float32x4_t a_d = vcvt_f32_f16(vld1_f16((const float16_t *) a_blk->d));
+
+ blockf[0] = vfmaq_laneq_f32(blockf[0], vcvtq_f32_s32(row0), a_d, 0);
+ blockf[1] = vfmaq_laneq_f32(blockf[1], vcvtq_f32_s32(row1), a_d, 1);
+ blockf[2] = vfmaq_laneq_f32(blockf[2], vcvtq_f32_s32(row2), a_d, 2);
+ blockf[3] = vfmaq_laneq_f32(blockf[3], vcvtq_f32_s32(row3), a_d, 3);
+ }
+
+ sumf[0] = vfmaq_f32(sumf[0], blockf[0], b_d);
+ sumf[1] = vfmaq_f32(sumf[1], blockf[1], b_d);
+ sumf[2] = vfmaq_f32(sumf[2], blockf[2], b_d);
+ sumf[3] = vfmaq_f32(sumf[3], blockf[3], b_d);
+ }
+
+ for (int m = 0; m < 4; ++m) {
+ vst1q_f32(s + (y * 4 + m) * bs + x * 4, sumf[m]);
+ }
+ }
+ }
+ return;
+#endif
+
+ ggml_gemm_q1_0_4x8_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+}
diff --git a/ggml/src/ggml-cpu/arch/s390/quants.c b/ggml/src/ggml-cpu/arch/s390/quants.c
index 50085757..52344828 100644
--- a/ggml/src/ggml-cpu/arch/s390/quants.c
+++ b/ggml/src/ggml-cpu/arch/s390/quants.c
@@ -146,6 +146,74 @@ void quantize_row_q8_1(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, i
//===================================== Dot products =================================
+void ggml_vec_dot_q1_0_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) {
+ const int qk = QK1_0; // 128
+ const int nb = n / qk;
+
+ assert(n % qk == 0);
+ assert(nrc == 1);
+ UNUSED(nrc);
+ UNUSED(bx);
+ UNUSED(by);
+ UNUSED(bs);
+
+ const block_q1_0 * GGML_RESTRICT x = vx;
+ const block_q8_0 * GGML_RESTRICT y = vy;
+
+#if defined(__VXE__) || defined(__VXE2__)
+ float32x4_t v_sumf = vec_splats(0.0f);
+
+ const uint8x16_t v_zero = vec_splats((uint8_t)0x00); // zero
+ const uint8x16_t v_bias = vec_splats((uint8_t)0x80); // bias from signed to unsigned
+ // v ^ 0x80 == v + 128
+
+ const uint8x16_t v_idx = (const uint8x16_t){ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
+ const uint8x16_t v_bit = (const uint8x16_t){ 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128 };
+
+ for (int i = 0; i < nb; ++i) {
+ const uint8x16_t v_x = vec_xl(0, (const uint8_t *)x[i].qs);
+ const float32x4_t v_xd = vec_splats(GGML_CPU_FP16_TO_FP32(x[i].d));
+
+ for (int k = 0; k < 4; ++k) {
+ // sub-block k holds elements 32k .. 32k+31
+ const block_q8_0 * GGML_RESTRICT yb = &y[i*4 + k];
+ const float32x4_t v_yd = vec_splats(GGML_CPU_FP16_TO_FP32(yb->d));
+
+ const uint8x16_t v_xrl = vec_perm(v_x, v_x, vec_add(v_idx, vec_splats((uint8_t)(k*4 + 0))));
+ const uint8x16_t v_xrh = vec_perm(v_x, v_x, vec_add(v_idx, vec_splats((uint8_t)(k*4 + 2))));
+
+ // isolate each lane's bit, then set all ones where that bit is clear, the -d case
+ const int8x16_t v_ml = (int8x16_t)vec_cmpeq(vec_and(v_xrl, v_bit), v_zero);
+ const int8x16_t v_mh = (int8x16_t)vec_cmpeq(vec_and(v_xrh, v_bit), v_zero);
+
+ const int8x16_t v_yl = vec_xl(0, (const int8_t *)yb->qs);
+ const int8x16_t v_yh = vec_xl(QK8_0/2, (const int8_t *)yb->qs);
+
+ // weights are only +1 or -1, so negate y
+ const int8x16_t v_ysl = vec_sub(vec_xor(v_yl, v_ml), v_ml);
+ const int8x16_t v_ysh = vec_sub(vec_xor(v_yh, v_mh), v_mh);
+
+ // bias to unsigned, then vec_sum4 adds each group of 4 bytes into one word
+ const uint32x4_t v_p = vec_add(vec_sum4(vec_xor((uint8x16_t)v_ysl, v_bias), v_zero),
+ vec_sum4(vec_xor((uint8x16_t)v_ysh, v_bias), v_zero));
+
+ // each word summed 8 biased bytes, so take back 8 * 128
+ const int32x4_t v_xy = vec_sub((int32x4_t)v_p, vec_splats((int32_t)1024));
+
+ // apply both block scales and add into the running total
+ v_sumf = vec_madd(vec_float(v_xy), vec_mul(v_xd, v_yd), v_sumf);
+ }
+ }
+
+ *s = vec_hsum_f32x4(v_sumf);
+#else
+ UNUSED(nb);
+ UNUSED(x);
+ UNUSED(y);
+ ggml_vec_dot_q1_0_q8_0_generic(n, s, bs, vx, bx, vy, by, nrc);
+#endif
+}
+
void ggml_vec_dot_q4_0_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) {
const int qk = QK8_0;
const int nb = n / qk;
@@ -349,6 +417,7 @@ void ggml_vec_dot_mxfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const vo
sumf = vec_hsum_f32x4(v_acc);
*s = sumf;
#else
+ UNUSED(nb);
UNUSED(x);
UNUSED(y);
UNUSED(ib);
@@ -636,7 +705,7 @@ void ggml_vec_dot_q5_1_q8_1(int n, float * GGML_RESTRICT s, size_t bs, const voi
const float32x4_t v_xyf = vec_float(v_xy);
const float32x4_t v_d = vec_splats(GGML_CPU_FP16_TO_FP32(x0->d) * GGML_CPU_FP16_TO_FP32(y0->d));
- const float32x4_t v_acc = vec_madd(v_xyf, v_d, v_acc);
+ const float32x4_t v_acc = vec_madd(v_xyf, v_d, vec_splats(0.0f));
sumf += vec_hsum_f32x4(v_acc) + summs;
}
diff --git a/ggml/src/ggml-cpu/arch/s390/repack.cpp b/ggml/src/ggml-cpu/arch/s390/repack.cpp
new file mode 100644
index 00000000..abf3433a
--- /dev/null
+++ b/ggml/src/ggml-cpu/arch/s390/repack.cpp
@@ -0,0 +1,225 @@
+#define GGML_COMMON_IMPL_CPP
+#define GGML_COMMON_DECL_CPP
+#include "ggml-common.h"
+#include "ggml-backend-impl.h"
+
+#include "ggml-impl.h"
+#include "ggml-cpu.h"
+#include "ggml-cpu-impl.h"
+#include "simd-mappings.h"
+#include "traits.h"
+
+#include
+#include
+#include
+
+#define GGML_CPU_CLANG_WORKAROUND
+#include "../../repack.h"
+
+#define UNUSED GGML_UNUSED
+
+void ggml_quantize_mat_q8_0_4x4(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k) {
+ assert(QK8_0 == 32);
+ assert(k % QK8_0 == 0);
+ const int nb = k / QK8_0;
+
+ block_q8_0x4 * GGML_RESTRICT y = (block_q8_0x4 *) vy;
+
+#if defined(__VXE__) || defined(__VXE2__)
+ float32x4_t v_src[4][8];
+ float id[4];
+
+ for (int i = 0; i < nb; i++) {
+ float32x4_t v_asrc[8];
+ float32x4_t v_amax[8];
+
+ for (int row_iter = 0; row_iter < 4; row_iter++) {
+ for (int j = 0; j < 8; j++) v_src[row_iter][j] = vec_xl(0, x + row_iter * k + i * 32 + 4 * j);
+ for (int j = 0; j < 8; j++) v_asrc[j] = vec_abs(v_src[row_iter][j]);
+
+ for (int j = 0; j < 4; j++) v_amax[2 * j] = vec_max(v_asrc[2 * j], v_asrc[2 * j + 1]);
+ for (int j = 0; j < 2; j++) v_amax[4 * j] = vec_max(v_amax[4 * j], v_amax[4 * j + 2]);
+ for (int j = 0; j < 1; j++) v_amax[8 * j] = vec_max(v_amax[8 * j], v_amax[8 * j + 4]);
+
+ const float amax = MAX(MAX(vec_extract(v_amax[0], 0), vec_extract(v_amax[0], 1)),
+ MAX(vec_extract(v_amax[0], 2), vec_extract(v_amax[0], 3)));
+
+ const float d = amax / ((1 << 7) - 1);
+ id[row_iter] = d ? 1.0f / d : 0.0f;
+
+ y[i].d[row_iter] = GGML_CPU_FP32_TO_FP16(d);
+ }
+
+ for (int j = 0; j < 8; j++) {
+ /* Uses non-default rounding for vec_signed or vec_round */
+ const int32x4_t v_qs0 = vec_signed(__builtin_s390_vfisb(vec_mul(v_src[0][j], id[0]), 4, 1));
+ const int32x4_t v_qs1 = vec_signed(__builtin_s390_vfisb(vec_mul(v_src[1][j], id[1]), 4, 1));
+ const int32x4_t v_qs2 = vec_signed(__builtin_s390_vfisb(vec_mul(v_src[2][j], id[2]), 4, 1));
+ const int32x4_t v_qs3 = vec_signed(__builtin_s390_vfisb(vec_mul(v_src[3][j], id[3]), 4, 1));
+
+ const int16x8_t v_qs01 = vec_packs(v_qs0, v_qs1);
+ const int16x8_t v_qs23 = vec_packs(v_qs2, v_qs3);
+
+ vec_xst(vec_packs(v_qs01, v_qs23), 0, y[i].qs + 16 * j);
+ }
+ }
+#else
+ UNUSED(nb);
+ UNUSED(y);
+ ggml_quantize_mat_q8_0_4x4_generic(x, vy, k);
+#endif
+}
+
+#if defined(__VXE__) || defined(__VXE2__)
+static inline int16x8_t vxe_dot_acc(const int8x16_t v_x, const int8x16_t v_y, const int16x8_t v_acc) {
+ return vec_meadd(v_x, v_y, vec_moadd(v_x, v_y, v_acc));
+}
+
+static inline int8x16_t vxe_splat_granule(const int8_t * qs) {
+ uint32_t g;
+ memcpy(&g, qs, sizeof(g));
+ return (int8x16_t)vec_splats(g);
+}
+
+static inline int32x4_t vxe_fold(const int16x8_t v_sumi) {
+ const int16x8_t v_ones = vec_splats((int16_t)1);
+ return vec_add(vec_mule(v_sumi, v_ones), vec_mulo(v_sumi, v_ones));
+}
+#endif
+
+void ggml_gemv_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
+ const int qk = QK8_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(nr == 1);
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(bs);
+ UNUSED(nr);
+
+#if defined(__VXE__) || defined(__VXE2__)
+ const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
+ float * res_ptr = s;
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q4_0x4 * b_ptr = (const block_q4_0x4 *) vx + (x * nb);
+
+ float32x4_t v_sumf = vec_splats(0.0f);
+
+ for (int l = 0; l < nb; l++) {
+ const int8_t * x_qs = b_ptr[l].qs;
+
+ const int8x16_t v_x0 = vec_xl( 0, x_qs);
+ const int8x16_t v_x1 = vec_xl(16, x_qs);
+ const int8x16_t v_x2 = vec_xl(32, x_qs);
+ const int8x16_t v_x3 = vec_xl(48, x_qs);
+
+ const int8x16_t v_x0l = vec_sra(vec_sl(v_x0, 4), 4);
+ const int8x16_t v_x1l = vec_sra(vec_sl(v_x1, 4), 4);
+ const int8x16_t v_x2l = vec_sra(vec_sl(v_x2, 4), 4);
+ const int8x16_t v_x3l = vec_sra(vec_sl(v_x3, 4), 4);
+
+ const int8x16_t v_x0h = vec_sra(v_x0, 4);
+ const int8x16_t v_x1h = vec_sra(v_x1, 4);
+ const int8x16_t v_x2h = vec_sra(v_x2, 4);
+ const int8x16_t v_x3h = vec_sra(v_x3, 4);
+
+ const int8_t * y_lo = a_ptr[l].qs;
+ const int8_t * y_hi = y_lo + qk / 2;
+
+ int16x8_t v_sumi = vec_splats((int16_t)0);
+
+ v_sumi = vxe_dot_acc(v_x0l, vxe_splat_granule(y_lo + 0), v_sumi);
+ v_sumi = vxe_dot_acc(v_x1l, vxe_splat_granule(y_lo + 4), v_sumi);
+ v_sumi = vxe_dot_acc(v_x2l, vxe_splat_granule(y_lo + 8), v_sumi);
+ v_sumi = vxe_dot_acc(v_x3l, vxe_splat_granule(y_lo + 12), v_sumi);
+
+ v_sumi = vxe_dot_acc(v_x0h, vxe_splat_granule(y_hi + 0), v_sumi);
+ v_sumi = vxe_dot_acc(v_x1h, vxe_splat_granule(y_hi + 4), v_sumi);
+ v_sumi = vxe_dot_acc(v_x2h, vxe_splat_granule(y_hi + 8), v_sumi);
+ v_sumi = vxe_dot_acc(v_x3h, vxe_splat_granule(y_hi + 12), v_sumi);
+
+ const float32x4_t v_yd = vec_splats(GGML_CPU_FP16_TO_FP32(a_ptr[l].d));
+ const float32x4_t v_xd = __lzs_f16cx4_load(b_ptr[l].d);
+ const float32x4_t v_d = vec_mul(v_yd, v_xd);
+
+ v_sumf = vec_madd(vec_float(vxe_fold(v_sumi)), v_d, v_sumf);
+ }
+
+ vec_xst(v_sumf, 0, res_ptr + x * ncols_interleaved);
+ }
+#else
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+ ggml_gemv_q4_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+#endif
+}
+
+void ggml_gemm_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
+ const int qk = QK8_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(nr % 4 == 0);
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+#if defined(__VXE__) || defined(__VXE2__)
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (y * nb);
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q4_0x4 * b_ptr = (const block_q4_0x4 *) vx + (x * nb);
+
+ float32x4_t v_sumf[4];
+ for (int m = 0; m < 4; m++) {
+ v_sumf[m] = vec_splats(0.0f);
+ }
+
+ for (int l = 0; l < nb; l++) {
+ int16x8_t v_sumi0 = vec_splats((int16_t)0);
+ int16x8_t v_sumi1 = vec_splats((int16_t)0);
+ int16x8_t v_sumi2 = vec_splats((int16_t)0);
+ int16x8_t v_sumi3 = vec_splats((int16_t)0);
+
+ for (int k = 0; k < 4; k++) {
+ const int8x16_t v_x = vec_xl(0, b_ptr[l].qs + 16 * k);
+ const int8x16_t v_xl = vec_sra(vec_sl(v_x, 4), 4);
+ const int8x16_t v_xh = vec_sra(v_x, 4);
+
+ const int8_t * y_lo = a_ptr[l].qs + 16 * k;
+ const int8_t * y_hi = y_lo + qk / 2 * 4;
+
+ v_sumi0 = vxe_dot_acc(v_xl, vxe_splat_granule(y_lo + 0), v_sumi0);
+ v_sumi1 = vxe_dot_acc(v_xl, vxe_splat_granule(y_lo + 4), v_sumi1);
+ v_sumi2 = vxe_dot_acc(v_xl, vxe_splat_granule(y_lo + 8), v_sumi2);
+ v_sumi3 = vxe_dot_acc(v_xl, vxe_splat_granule(y_lo + 12), v_sumi3);
+
+ v_sumi0 = vxe_dot_acc(v_xh, vxe_splat_granule(y_hi + 0), v_sumi0);
+ v_sumi1 = vxe_dot_acc(v_xh, vxe_splat_granule(y_hi + 4), v_sumi1);
+ v_sumi2 = vxe_dot_acc(v_xh, vxe_splat_granule(y_hi + 8), v_sumi2);
+ v_sumi3 = vxe_dot_acc(v_xh, vxe_splat_granule(y_hi + 12), v_sumi3);
+ }
+
+ const float32x4_t v_yd = __lzs_f16cx4_load(a_ptr[l].d);
+ const float32x4_t v_xd = __lzs_f16cx4_load(b_ptr[l].d);
+
+ v_sumf[0] = vec_madd(vec_float(vxe_fold(v_sumi0)), vec_mul(v_xd, vec_splat(v_yd, 0)), v_sumf[0]);
+ v_sumf[1] = vec_madd(vec_float(vxe_fold(v_sumi1)), vec_mul(v_xd, vec_splat(v_yd, 1)), v_sumf[1]);
+ v_sumf[2] = vec_madd(vec_float(vxe_fold(v_sumi2)), vec_mul(v_xd, vec_splat(v_yd, 2)), v_sumf[2]);
+ v_sumf[3] = vec_madd(vec_float(vxe_fold(v_sumi3)), vec_mul(v_xd, vec_splat(v_yd, 3)), v_sumf[3]);
+ }
+
+ for (int m = 0; m < 4; m++) {
+ vec_xst(v_sumf[m], 0, s + (y * 4 + m) * bs + x * ncols_interleaved);
+ }
+ }
+ }
+#else
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+ ggml_gemm_q4_0_4x4_q8_0_generic(n, s, bs, vx, vy, nr, nc);
+#endif
+}
diff --git a/ggml/src/ggml-cpu/ggml-cpu-impl.h b/ggml/src/ggml-cpu/ggml-cpu-impl.h
index 5d1ca5ff..5dd9ec8e 100644
--- a/ggml/src/ggml-cpu/ggml-cpu-impl.h
+++ b/ggml/src/ggml-cpu/ggml-cpu-impl.h
@@ -78,7 +78,7 @@ struct ggml_compute_params {
#if defined(__ARM_NEON)
// ref: https://github.com/ggml-org/llama.cpp/pull/5404
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
#define ggml_vld1q_u32(w,x,y,z) { ((w) + ((uint64_t)(x) << 32)), ((y) + ((uint64_t)(z) << 32)) }
#else
#define ggml_vld1q_u32(w,x,y,z) { (w), (x), (y), (z) }
diff --git a/ggml/src/ggml-cpu/ggml-cpu.c b/ggml/src/ggml-cpu/ggml-cpu.c
index 21f57218..e8bfab1b 100644
--- a/ggml/src/ggml-cpu/ggml-cpu.c
+++ b/ggml/src/ggml-cpu/ggml-cpu.c
@@ -4,6 +4,7 @@
#include "ggml-backend-impl.h"
#include "ggml-backend.h"
#include "traits.h"
+#include "iqp.h"
#include "ggml-cpu-impl.h"
#include "ggml-impl.h"
#include "quants.h"
@@ -1363,7 +1364,9 @@ UseGgmlGemm1:;
const size_t nbw3 = nbw2*ne12;
assert(params->wsize >= ne13*nbw3);
- GGML_ASSERT(src1->type == GGML_TYPE_F32);
+ GGML_ASSERT(src1->type == GGML_TYPE_F32 || src1->type == GGML_TYPE_F16);
+ // the F16 path below writes plain floats into wdata, so it needs an F32 vec_dot_type
+ GGML_ASSERT(src1->type == GGML_TYPE_F32 || vec_dot_type == GGML_TYPE_F32);
#if 0
for (int64_t i13 = 0; i13 < ne13; ++i13) {
@@ -1382,9 +1385,15 @@ UseGgmlGemm1:;
size_t bs = ggml_blck_size(vec_dot_type);
int64_t ne10_block_start = (ith * ne10/bs) / nth;
int64_t ne10_block_end = ((ith + 1) * ne10/bs) / nth;
- from_float((float *)((char *) src1->data + i13*nb13 + i12*nb12 + i11*nb11 + ne10_block_start*bs*nb10),
- (void *) (wdata + i13*nbw3 + i12*nbw2 + i11*nbw1 + ne10_block_start*nbw0),
- (ne10_block_end - ne10_block_start) * bs);
+ const char * src1_block = (const char *) src1->data + i13*nb13 + i12*nb12 + i11*nb11 + ne10_block_start*bs*nb10;
+ char * dst_block = wdata + i13*nbw3 + i12*nbw2 + i11*nbw1 + ne10_block_start*nbw0;
+ const int64_t n_block = (ne10_block_end - ne10_block_start) * bs;
+
+ if (src1->type == GGML_TYPE_F32) {
+ from_float((const float *) src1_block, dst_block, n_block);
+ } else {
+ ggml_cpu_fp16_to_fp32((const ggml_fp16_t *) src1_block, (float *) dst_block, n_block);
+ }
}
}
}
@@ -1398,6 +1407,13 @@ UseGgmlGemm1:;
ggml_barrier(params->threadpool);
+ // IQ panel gemm (see iqp.h) - must come after the barrier above, it consumes the q8_K rows
+ // of src1 from the work buffer
+ if (ggml_cpu_iqp_supports_mul_mat(dst) && !params->use_ref) {
+ ggml_compute_forward_mul_mat_iqp(params, dst);
+ return;
+ }
+
#if GGML_USE_LLAMAFILE
if (src1->type != vec_dot_type) {
const void* wdata = (src1->type == vec_dot_type) ? src1->data : params->wdata;
@@ -1615,6 +1631,16 @@ static void ggml_compute_forward_mul_mat_id(
char (*atomic_current_chunk)[CACHE_LINE_SIZE] = // [n_as]
incr_ptr_aligned(&wdata_cur, CACHE_LINE_SIZE * n_as, CACHE_LINE_SIZE);
+ // IQ panel gemm (see iqp.h); per expert eligibility is decided below, but the work buffer is
+ // reserved for the whole node (ggml_graph_plan sizes it without params, use_ref only skips the dispatch)
+ const bool iqp = ggml_cpu_iqp_supports_mul_mat_id(dst) && !params->use_ref;
+
+ char * iqp_panels = NULL;
+
+ if (iqp) {
+ iqp_panels = incr_ptr_aligned(&wdata_cur, nth * ggml_cpu_iqp_scratch_size(dst), 64);
+ }
+
GGML_ASSERT(params->wsize >= (size_t)((char *) wdata_cur - (char *) params->wdata));
if (src1->type != vec_dot_type) {
@@ -1686,6 +1712,13 @@ static void ggml_compute_forward_mul_mat_id(
continue;
}
+ if (iqp && ggml_cpu_iqp_mul_mat_id_min_batch(cne1)) {
+ ggml_compute_forward_mul_mat_id_iqp(params, dst, cur_a, cne1, (const int32_t *) &MMID_MATRIX_ROW(cur_a, 0),
+ iqp_panels);
+
+ continue;
+ }
+
const char * src0_cur = (const char *) src0->data + cur_a * nb02;
const void * wdata = (src1->type == vec_dot_type) ? src1->data : params->wdata;
const size_t row_size = ggml_row_size(vec_dot_type, ne10);
@@ -2346,6 +2379,7 @@ static int ggml_get_n_tasks(struct ggml_tensor * node, int n_threads) {
case GGML_GLU_OP_SWIGLU_OAI:
case GGML_GLU_OP_GEGLU_ERF:
case GGML_GLU_OP_GEGLU_QUICK:
+ case GGML_GLU_OP_SWIGLU_CLAMP:
{
n_tasks = n_threads;
} break;
@@ -2892,6 +2926,11 @@ struct ggml_cplan ggml_graph_plan(
if (node->src[1]->type != vec_dot_type) {
cur = ggml_row_size(vec_dot_type, ggml_nelements(node->src[1]));
}
+
+ // the IQ panel path needs one scratch panel per thread past the q8_K rows
+ if (ggml_cpu_iqp_supports_mul_mat(node)) {
+ cur = GGML_PAD(cur, 64) + n_tasks * ggml_cpu_iqp_scratch_size(node);
+ }
} break;
case GGML_OP_MUL_MAT_ID:
{
@@ -2911,6 +2950,10 @@ struct ggml_cplan ggml_graph_plan(
cur += n_as*ids->ne[0]*ids->ne[1]*sizeof(struct mmid_row_mapping) + sizeof(int64_t);
// atomic_current_chunk
cur += CACHE_LINE_SIZE*n_as + CACHE_LINE_SIZE;
+ // the IQ panel path needs one scratch panel per thread on top of that
+ if (ggml_cpu_iqp_supports_mul_mat_id(node)) {
+ cur += n_tasks * ggml_cpu_iqp_scratch_size(node) + 64;
+ }
} break;
case GGML_OP_OUT_PROD:
{
@@ -2971,12 +3014,13 @@ struct ggml_cplan ggml_graph_plan(
const int64_t ne10 = node->src[1]->ne[0]; // W
const int64_t ne11 = node->src[1]->ne[1]; // H
const int64_t ne12 = node->src[1]->ne[2]; // Channels In
+ const int64_t ne13 = node->src[1]->ne[3]; // Batch
GGML_ASSERT(node->src[0]->type == GGML_TYPE_F16 || node->src[0]->type == GGML_TYPE_F32);
GGML_ASSERT(node->src[1]->type == GGML_TYPE_F32);
cur += ggml_type_size(node->src[0]->type) * ne00 * ne01 * ne02 * ne03;
- cur += ggml_type_size(node->src[0]->type) * ne10 * ne11 * ne12;
+ cur += ggml_type_size(node->src[0]->type) * ne10 * ne11 * ne12 * ne13;
} break;
case GGML_OP_TOP_K:
diff --git a/ggml/src/ggml-cpu/ggml-cpu.cpp b/ggml/src/ggml-cpu/ggml-cpu.cpp
index 8cece71f..1df0f2bb 100644
--- a/ggml/src/ggml-cpu/ggml-cpu.cpp
+++ b/ggml/src/ggml-cpu/ggml-cpu.cpp
@@ -451,6 +451,10 @@ static bool ggml_backend_cpu_device_supports_op(ggml_backend_dev_t dev, const st
op->type != GGML_TYPE_IQ1_S &&
op->type != GGML_TYPE_IQ1_M; // missing type_traits.from_float
case GGML_OP_MUL_MAT:
+ if (ggml_get_op_params_i32(op, 1) == GGML_HINT_SRC0_IS_HADAMARD &&
+ src0->type == GGML_TYPE_F32 && op->type == GGML_TYPE_F32) {
+ return src1->type == GGML_TYPE_F32 || src1->type == GGML_TYPE_F16;
+ }
return src1->type == GGML_TYPE_F32 || src1->type == ggml_get_type_traits_cpu(src0->type)->vec_dot_type;
case GGML_OP_SOFT_MAX_BACK: {
if (op->src[0]->type != GGML_TYPE_F32 || op->src[1]->type != GGML_TYPE_F32) {
diff --git a/ggml/src/ggml-cpu/iqp.cpp b/ggml/src/ggml-cpu/iqp.cpp
new file mode 100644
index 00000000..b9201db3
--- /dev/null
+++ b/ggml/src/ggml-cpu/iqp.cpp
@@ -0,0 +1,1253 @@
+#define GGML_COMMON_IMPL_CPP
+#define GGML_COMMON_DECL_CPP
+#include "ggml-common.h"
+
+#include "ggml-impl.h"
+#include "ggml-cpu.h"
+#include "ggml-cpu-impl.h"
+#include "simd-mappings.h"
+#include "traits.h"
+
+#include
+#include
+#include
+
+#include "iqp.h"
+
+#define UNUSED GGML_UNUSED
+
+// smallest src1 batch for which the decode pays for itself
+#define GGML_IQP_MIN_BATCH 8
+
+// same, per expert, for MUL_MAT_ID
+#define GGML_IQP_MIN_BATCH_ID 8
+
+bool ggml_cpu_iqp_mul_mat_id_min_batch(int64_t cne1) {
+ return cne1 >= GGML_IQP_MIN_BATCH_ID;
+}
+
+// src0 rows interleaved per panel
+#define IQP_NB_ROWS 8
+
+#define IQP_SB_SIZE 16 // weights per sub-block
+#define IQP_NSB (QK_K / IQP_SB_SIZE) // sub-blocks per super-block
+
+// one super-block of a grid based IQ type decoded to int8, 8 rows interleaved:
+// dfac[row] * iscales[sb*8 + row] * qs is bit identical to dequantize_row_iq*
+struct block_iqp_x8 {
+ float dfac[8]; // f32 super-block scale, d * 2^-k
+ int32_t bias[8]; // 128 * sum(qs * iscale), see GGML_IQP_USE_BIAS
+ int8_t iscales[IQP_NSB * 8]; // integer sub-block scales, in [-32, 31]
+ int8_t qs[QK_K * 8]; // qs[sb*128 + g*32 + row*4 + k] = column sb*16 + g*4 + k
+};
+
+static_assert(sizeof(block_iqp_x8) == 8 * sizeof(float) + 8 * sizeof(int32_t) + IQP_NSB * 8 + QK_K * 8,
+ "wrong iqp_x8 block size/padding");
+
+// feed the activations to VNNI as unsigned bytes (y + 128) and correct with bias[]; without VNNI the kernels use the maddubs sign trick instead and bias[] is not filled
+#if defined(__AVX2__) && ((defined(__AVX512VNNI__) && defined(__AVX512VL__)) || defined(__AVXVNNI__))
+# define GGML_IQP_USE_BIAS 1
+#else
+# define GGML_IQP_USE_BIAS 0
+#endif
+
+static inline size_t ggml_cpu_iqp_row_size(const struct ggml_tensor * dst) {
+ return ggml_row_size(GGML_TYPE_Q8_K, dst->src[1]->ne[0]);
+}
+
+// the low 7 bits of v are the first 7 signs and the 8th is their parity (cf. unpack_ksigns in the CUDA backend)
+static inline uint8_t iqp_unpack_ksigns(uint32_t v) {
+ uint32_t p = v ^ (v >> 4);
+
+ p ^= p >> 2;
+ p ^= p >> 1;
+
+ return (uint8_t) (v ^ ((p & 1) << 7));
+}
+
+#if defined(__AVX2__)
+
+// 0xFF in every byte whose sign bit is set; sv holds each sign byte broadcast over the 8 bytes it governs
+static inline __m256i iqp_sign_mask(__m256i sv) {
+ const __m256i sel = _mm256_set1_epi64x((int64_t) 0x8040201008040201ULL);
+
+# if defined(__GFNI__)
+ // computes the and + compare in one instruction
+ return _mm256_gf2p8affine_epi64_epi8(sel, sv, 0);
+# else
+ return _mm256_cmpeq_epi8(_mm256_and_si256(sv, sel), sel);
+# endif
+}
+
+// signs holds four sign bytes, byte l governing values 8*l .. 8*l+7 - spread each over its 8 lanes
+static inline __m256i iqp_sign_bytes(uint32_t signs) {
+ const __m256i bcast = _mm256_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, //
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
+
+ return _mm256_shuffle_epi8(_mm256_set1_epi32((int32_t) signs), bcast);
+}
+
+// x ^ m - m negates the lanes where m is 0xFF
+static inline __m256i iqp_apply_signs(__m256i x, __m256i m) {
+ return _mm256_sub_epi8(_mm256_xor_si256(x, m), m);
+}
+
+#endif
+
+// 32 values from four 8 byte grid entries, sign byte l of signs applied to group l
+static inline void iqp_store_signed_x8(int8_t * GGML_RESTRICT dst,
+ uint64_t g0,
+ uint64_t g1,
+ uint64_t g2,
+ uint64_t g3,
+ uint32_t signs) {
+#if defined(__AVX2__)
+ const __m256i g = _mm256_set_epi64x((int64_t) g3, (int64_t) g2, (int64_t) g1, (int64_t) g0);
+ const __m256i m = iqp_sign_mask(iqp_sign_bytes(signs));
+
+ _mm256_storeu_si256((__m256i *) dst, iqp_apply_signs(g, m));
+#else
+ const uint64_t g[4] = { g0, g1, g2, g3 };
+
+ for (int l = 0; l < 4; ++l) {
+ const uint8_t * grid = (const uint8_t *) &g[l];
+ const uint8_t s = (uint8_t) (signs >> 8 * l);
+
+ for (int j = 0; j < 8; ++j) {
+ dst[8 * l + j] = s & kmask_iq2xs[j] ? -grid[j] : grid[j];
+ }
+ }
+#endif
+}
+
+// same, but the eight values of group l come from two 4 byte grid entries
+static inline void iqp_store_signed_x4(int8_t * GGML_RESTRICT dst,
+ uint32_t g0a,
+ uint32_t g0b,
+ uint32_t g1a,
+ uint32_t g1b,
+ uint32_t g2a,
+ uint32_t g2b,
+ uint32_t g3a,
+ uint32_t g3b,
+ uint32_t signs) {
+#if defined(__AVX2__)
+ const __m256i g = _mm256_setr_epi32((int32_t) g0a, (int32_t) g0b, (int32_t) g1a, (int32_t) g1b, (int32_t) g2a,
+ (int32_t) g2b, (int32_t) g3a, (int32_t) g3b);
+ const __m256i m = iqp_sign_mask(iqp_sign_bytes(signs));
+
+ _mm256_storeu_si256((__m256i *) dst, iqp_apply_signs(g, m));
+#else
+ const uint32_t ga[4] = { g0a, g1a, g2a, g3a };
+ const uint32_t gb[4] = { g0b, g1b, g2b, g3b };
+
+ for (int l = 0; l < 4; ++l) {
+ const uint8_t * grid1 = (const uint8_t *) &ga[l];
+ const uint8_t * grid2 = (const uint8_t *) &gb[l];
+ const uint8_t s = (uint8_t) (signs >> 8 * l);
+
+ for (int j = 0; j < 4; ++j) {
+ dst[8 * l + j + 0] = s & kmask_iq2xs[j + 0] ? -grid1[j] : grid1[j];
+ dst[8 * l + j + 4] = s & kmask_iq2xs[j + 4] ? -grid2[j] : grid2[j];
+ }
+ }
+#endif
+}
+
+// 32 values of 8 * grid + delta from four 8 byte grid entries (grid bytes are in {-1, 0, 1}), byte l of deltas applying to group l
+static inline void iqp_store_iq1_x8(int8_t * GGML_RESTRICT dst,
+ uint64_t g0,
+ uint64_t g1,
+ uint64_t g2,
+ uint64_t g3,
+ uint32_t deltas) {
+#if defined(__AVX2__)
+ __m256i g = _mm256_set_epi64x((int64_t) g3, (int64_t) g2, (int64_t) g1, (int64_t) g0);
+
+ // no byte shift in AVX2
+ g = _mm256_add_epi8(g, g);
+ g = _mm256_add_epi8(g, g);
+ g = _mm256_add_epi8(g, g);
+
+ _mm256_storeu_si256((__m256i *) dst, _mm256_add_epi8(g, iqp_sign_bytes(deltas)));
+#else
+ const uint64_t g[4] = { g0, g1, g2, g3 };
+
+ for (int l = 0; l < 4; ++l) {
+ const int8_t * grid = (const int8_t *) &g[l];
+ const int8_t delta = (int8_t) (deltas >> 8 * l);
+
+ for (int j = 0; j < 8; ++j) {
+ dst[8 * l + j] = 8 * grid[j] + delta;
+ }
+ }
+#endif
+}
+
+// 32 values from 16 packed nibbles through the kvalues_iq4nl lookup: low nibbles first, then high
+static inline void iqp_store_iq4_x32(int8_t * GGML_RESTRICT dst, const uint8_t * GGML_RESTRICT qs) {
+#if defined(__AVX2__)
+ const __m128i q = _mm_loadu_si128((const __m128i *) qs);
+ const __m128i lut = _mm_loadu_si128((const __m128i *) kvalues_iq4nl);
+ const __m128i m4 = _mm_set1_epi8(0xf);
+
+ _mm_storeu_si128((__m128i *) (dst + 0), _mm_shuffle_epi8(lut, _mm_and_si128(q, m4)));
+ _mm_storeu_si128((__m128i *) (dst + 16), _mm_shuffle_epi8(lut, _mm_and_si128(_mm_srli_epi16(q, 4), m4)));
+#else
+ for (int j = 0; j < 16; ++j) {
+ dst[j + 0] = kvalues_iq4nl[qs[j] & 0xf];
+ dst[j + 16] = kvalues_iq4nl[qs[j] >> 4];
+ }
+#endif
+}
+
+#if GGML_IQP_USE_BIAS
+
+// sum of qs * iscale over one super-block, at most 256 * 127 * 32 = 1.04e6
+static inline int32_t iqp_weighted_sum(const int8_t * GGML_RESTRICT vals, const int8_t * GGML_RESTRICT iscales) {
+#if defined(__AVX2__)
+ static_assert(IQP_SB_SIZE == 16, "the vector path folds two sub-blocks per 32 byte load");
+
+ const __m256i ones8 = _mm256_set1_epi8(1);
+ const __m256i ones16 = _mm256_set1_epi16(1);
+
+ __m256i acc = _mm256_setzero_si256();
+
+ for (int i = 0; i < QK_K / 32; ++i) {
+ // sum groups of 4 bytes into int32, the low four lanes cover sub-block 2*i and the high four 2*i + 1
+ const __m256i v = _mm256_loadu_si256((const __m256i *) (vals + 32 * i));
+ const __m256i p = _mm256_madd_epi16(_mm256_maddubs_epi16(ones8, v), ones16);
+
+ const __m256i s = _mm256_set_m128i(_mm_set1_epi32(iscales[2 * i + 1]), _mm_set1_epi32(iscales[2 * i + 0]));
+
+ acc = _mm256_add_epi32(acc, _mm256_mullo_epi32(p, s));
+ }
+
+ __m128i sum = _mm_add_epi32(_mm256_castsi256_si128(acc), _mm256_extracti128_si256(acc, 1));
+
+ sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)));
+ sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, _MM_SHUFFLE(2, 3, 0, 1)));
+
+ return _mm_cvtsi128_si32(sum);
+#else
+ int32_t wsum = 0;
+
+ for (int sb = 0; sb < IQP_NSB; ++sb) {
+ int32_t vsum = 0;
+
+ for (int k = 0; k < IQP_SB_SIZE; ++k) {
+ vsum += vals[sb * IQP_SB_SIZE + k];
+ }
+
+ wsum += iscales[sb] * vsum;
+ }
+
+ return wsum;
+#endif
+}
+
+#endif // GGML_IQP_USE_BIAS
+
+static void iqp_decode_iq2_xxs(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq2_xxs * x = (const block_iq2_xxs *) vx;
+
+ // db = d * (0.5 + ls) * 0.25 = (d / 8) * (2 * ls + 1), ls 4 bit
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d) * 0.125f;
+
+ uint32_t aux32[2];
+ const uint8_t * aux8 = (const uint8_t *) aux32;
+
+ for (int ib32 = 0; ib32 < QK_K / 32; ++ib32) {
+ memcpy(aux32, x->qs + 4 * ib32, 2 * sizeof(uint32_t));
+ const int8_t ls = (int8_t) (2 * (aux32[1] >> 28) + 1);
+
+ iscales[2 * ib32 + 0] = ls;
+ iscales[2 * ib32 + 1] = ls;
+
+ const uint32_t signs = (uint32_t) iqp_unpack_ksigns((aux32[1] >> 0) & 127) |
+ (uint32_t) iqp_unpack_ksigns((aux32[1] >> 7) & 127) << 8 |
+ (uint32_t) iqp_unpack_ksigns((aux32[1] >> 14) & 127) << 16 |
+ (uint32_t) iqp_unpack_ksigns((aux32[1] >> 21) & 127) << 24;
+
+ iqp_store_signed_x8(vals + 32 * ib32, iq2xxs_grid[aux8[0]], iq2xxs_grid[aux8[1]], iq2xxs_grid[aux8[2]],
+ iq2xxs_grid[aux8[3]], signs);
+ }
+}
+
+static void iqp_decode_iq2_xs(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq2_xs * x = (const block_iq2_xs *) vx;
+
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d) * 0.125f;
+
+ for (int ib32 = 0; ib32 < QK_K / 32; ++ib32) {
+ iscales[2 * ib32 + 0] = (int8_t) (2 * (x->scales[ib32] & 0xf) + 1);
+ iscales[2 * ib32 + 1] = (int8_t) (2 * (x->scales[ib32] >> 4) + 1);
+
+ const uint16_t * q = x->qs + 4 * ib32;
+
+ const uint32_t signs = (uint32_t) iqp_unpack_ksigns(q[0] >> 9) | (uint32_t) iqp_unpack_ksigns(q[1] >> 9) << 8 |
+ (uint32_t) iqp_unpack_ksigns(q[2] >> 9) << 16 |
+ (uint32_t) iqp_unpack_ksigns(q[3] >> 9) << 24;
+
+ iqp_store_signed_x8(vals + 32 * ib32, iq2xs_grid[q[0] & 511], iq2xs_grid[q[1] & 511], iq2xs_grid[q[2] & 511],
+ iq2xs_grid[q[3] & 511], signs);
+ }
+}
+
+static void iqp_decode_iq2_s(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq2_s * x = (const block_iq2_s *) vx;
+
+ const uint8_t * qs = x->qs;
+ const uint8_t * qh = x->qh;
+ const uint8_t * signs = qs + QK_K / 8;
+
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d) * 0.125f;
+
+ for (int ib32 = 0; ib32 < QK_K / 32; ++ib32) {
+ iscales[2 * ib32 + 0] = (int8_t) (2 * (x->scales[ib32] & 0xf) + 1);
+ iscales[2 * ib32 + 1] = (int8_t) (2 * (x->scales[ib32] >> 4) + 1);
+
+ const uint32_t sbits =
+ (uint32_t) signs[0] | (uint32_t) signs[1] << 8 | (uint32_t) signs[2] << 16 | (uint32_t) signs[3] << 24;
+
+ iqp_store_signed_x8(vals + 32 * ib32, iq2s_grid[qs[0] | (qh[ib32] << 8 & 0x300)],
+ iq2s_grid[qs[1] | (qh[ib32] << 6 & 0x300)], iq2s_grid[qs[2] | (qh[ib32] << 4 & 0x300)],
+ iq2s_grid[qs[3] | (qh[ib32] << 2 & 0x300)], sbits);
+ qs += 4;
+ signs += 4;
+ }
+}
+
+static void iqp_decode_iq3_xxs(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq3_xxs * x = (const block_iq3_xxs *) vx;
+
+ const uint8_t * qs = x->qs;
+ const uint8_t * scales_and_signs = qs + QK_K / 4;
+
+ // db = d * (0.5 + ls) * 0.5 = (d / 4) * (2 * ls + 1), ls 4 bit
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d) * 0.25f;
+
+ uint32_t aux32;
+
+ for (int ib32 = 0; ib32 < QK_K / 32; ++ib32) {
+ memcpy(&aux32, scales_and_signs + 4 * ib32, sizeof(uint32_t));
+ const int8_t ls = (int8_t) (2 * (aux32 >> 28) + 1);
+
+ iscales[2 * ib32 + 0] = ls;
+ iscales[2 * ib32 + 1] = ls;
+
+ const uint32_t signs = (uint32_t) iqp_unpack_ksigns((aux32 >> 0) & 127) |
+ (uint32_t) iqp_unpack_ksigns((aux32 >> 7) & 127) << 8 |
+ (uint32_t) iqp_unpack_ksigns((aux32 >> 14) & 127) << 16 |
+ (uint32_t) iqp_unpack_ksigns((aux32 >> 21) & 127) << 24;
+
+ iqp_store_signed_x4(vals + 32 * ib32, iq3xxs_grid[qs[0]], iq3xxs_grid[qs[1]], iq3xxs_grid[qs[2]],
+ iq3xxs_grid[qs[3]], iq3xxs_grid[qs[4]], iq3xxs_grid[qs[5]], iq3xxs_grid[qs[6]],
+ iq3xxs_grid[qs[7]], signs);
+ qs += 8;
+ }
+}
+
+static void iqp_decode_iq3_s(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq3_s * x = (const block_iq3_s *) vx;
+
+ const uint8_t * qs = x->qs;
+ const uint8_t * qh = x->qh;
+ const uint8_t * signs = x->signs;
+
+ // db = d * (1 + 2 * ls), ls 4 bit
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d);
+
+ int k = 0;
+
+ for (int ib32 = 0; ib32 < QK_K / 32; ib32 += 2) {
+ const int8_t db1 = (int8_t) (1 + 2 * (x->scales[ib32 / 2] & 0xf));
+ const int8_t db2 = (int8_t) (1 + 2 * (x->scales[ib32 / 2] >> 4));
+
+ iscales[2 * ib32 + 0] = db1;
+ iscales[2 * ib32 + 1] = db1;
+ iscales[2 * ib32 + 2] = db2;
+ iscales[2 * ib32 + 3] = db2;
+
+ for (int h = 0; h < 2; ++h) {
+ const uint32_t sbits =
+ (uint32_t) signs[0] | (uint32_t) signs[1] << 8 | (uint32_t) signs[2] << 16 | (uint32_t) signs[3] << 24;
+
+ iqp_store_signed_x4(vals + k, iq3s_grid[qs[0] | ((qh[h] << 8) & 256)],
+ iq3s_grid[qs[1] | ((qh[h] << 7) & 256)], iq3s_grid[qs[2] | ((qh[h] << 6) & 256)],
+ iq3s_grid[qs[3] | ((qh[h] << 5) & 256)], iq3s_grid[qs[4] | ((qh[h] << 4) & 256)],
+ iq3s_grid[qs[5] | ((qh[h] << 3) & 256)], iq3s_grid[qs[6] | ((qh[h] << 2) & 256)],
+ iq3s_grid[qs[7] | ((qh[h] << 1) & 256)], sbits);
+
+ k += 32;
+ qs += 8;
+ signs += 4;
+ }
+ qh += 2;
+ }
+}
+
+// dequantize_row_iq1_* computes y = dl * (grid[j] + delta) with delta = +-1/8, so the panel stores 8 * grid[j] +- 1 and folds the /8 into dfac
+static void iqp_decode_iq1_s(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq1_s * x = (const block_iq1_s *) vx;
+
+ const uint8_t * qs = x->qs;
+ const uint16_t * qh = x->qh;
+
+ // dl = d * (2 * ls + 1) * 0.125, ls 3 bit
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d) * 0.125f;
+
+ for (int ib = 0; ib < QK_K / 32; ++ib) {
+ const int8_t dl = (int8_t) (2 * ((qh[ib] >> 12) & 7) + 1);
+ const int8_t delta = qh[ib] & 0x8000 ? -1 : 1;
+
+ iscales[2 * ib + 0] = dl;
+ iscales[2 * ib + 1] = dl;
+
+ iqp_store_iq1_x8(vals + 32 * ib, iq1s_grid[qs[0] | (((qh[ib] >> 0) & 7) << 8)],
+ iq1s_grid[qs[1] | (((qh[ib] >> 3) & 7) << 8)], iq1s_grid[qs[2] | (((qh[ib] >> 6) & 7) << 8)],
+ iq1s_grid[qs[3] | (((qh[ib] >> 9) & 7) << 8)], ((uint8_t) delta) * 0x01010101u);
+ qs += 4;
+ }
+}
+
+static void iqp_decode_iq1_m(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq1_m * x = (const block_iq1_m *) vx;
+
+ // block_iq1_m has no d field - the fp16 super-block scale is spread over the top nibbles of the four scale words
+ const uint16_t * sc = (const uint16_t *) x->scales;
+
+ iq1m_scale_t scale;
+ scale.u16 = (sc[0] >> 12) | ((sc[1] >> 8) & 0x00f0) | ((sc[2] >> 4) & 0x0f00) | (sc[3] & 0xf000);
+
+ *dfac = GGML_CPU_FP16_TO_FP32(scale.f16) * 0.125f;
+
+ const uint8_t * qs = x->qs;
+ const uint8_t * qh = x->qh;
+
+ for (int ib = 0; ib < QK_K / 32; ++ib) {
+ iscales[2 * ib + 0] = (int8_t) (2 * ((sc[ib / 2] >> (6 * (ib % 2) + 0)) & 0x7) + 1);
+ iscales[2 * ib + 1] = (int8_t) (2 * ((sc[ib / 2] >> (6 * (ib % 2) + 3)) & 0x7) + 1);
+
+ const uint16_t idx[4] = {
+ (uint16_t) (qs[0] | ((qh[0] << 8) & 0x700)),
+ (uint16_t) (qs[1] | ((qh[0] << 4) & 0x700)),
+ (uint16_t) (qs[2] | ((qh[1] << 8) & 0x700)),
+ (uint16_t) (qs[3] | ((qh[1] << 4) & 0x700)),
+ };
+ const uint32_t deltas = (uint32_t) (qh[0] & 0x08 ? 0xff : 0x01) | (uint32_t) (qh[0] & 0x80 ? 0xff : 0x01) << 8 |
+ (uint32_t) (qh[1] & 0x08 ? 0xff : 0x01) << 16 |
+ (uint32_t) (qh[1] & 0x80 ? 0xff : 0x01) << 24;
+
+ iqp_store_iq1_x8(vals + 32 * ib, iq1s_grid[idx[0]], iq1s_grid[idx[1]], iq1s_grid[idx[2]], iq1s_grid[idx[3]],
+ deltas);
+ qs += 4;
+ qh += 2;
+ }
+}
+
+static void iqp_decode_iq4_xs(const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ const block_iq4_xs * x = (const block_iq4_xs *) vx;
+
+ const uint8_t * qs = x->qs;
+
+ // dl = d * (ls - 32), ls 6 bit, so the integer scale is in [-32, 31]
+ *dfac = GGML_CPU_FP16_TO_FP32(x->d);
+
+ for (int ib = 0; ib < QK_K / 32; ++ib) {
+ const int ls = ((x->scales_l[ib / 2] >> 4 * (ib % 2)) & 0xf) | (((x->scales_h >> 2 * ib) & 3) << 4);
+ const int8_t dl = (int8_t) (ls - 32);
+
+ iscales[2 * ib + 0] = dl;
+ iscales[2 * ib + 1] = dl;
+
+ iqp_store_iq4_x32(vals + 32 * ib, qs);
+ qs += 16;
+ }
+}
+
+// expanded by the eligibility test and the decode dispatch
+#define IQP_TYPE_LIST(T) \
+ T(IQ2_XXS, iq2_xxs) \
+ T(IQ2_XS, iq2_xs) \
+ T(IQ2_S, iq2_s) \
+ T(IQ3_XXS, iq3_xxs) \
+ T(IQ3_S, iq3_s) \
+ T(IQ1_S, iq1_s) \
+ T(IQ1_M, iq1_m) \
+ T(IQ4_XS, iq4_xs)
+
+static bool iqp_decode_superblock(enum ggml_type type,
+ const void * GGML_RESTRICT vx,
+ int8_t * GGML_RESTRICT vals,
+ int8_t * GGML_RESTRICT iscales,
+ float * GGML_RESTRICT dfac) {
+ switch (type) {
+#define IQP_CASE(E, name) \
+ case GGML_TYPE_##E: \
+ iqp_decode_##name(vx, vals, iscales, dfac); \
+ return true;
+ IQP_TYPE_LIST(IQP_CASE)
+#undef IQP_CASE
+ default:
+ return false;
+ }
+}
+
+#if defined(__AVX2__)
+
+// 8x8 int32 transpose of the 32 column group starting at column off
+static inline void iqp_interleave_x8(int8_t * GGML_RESTRICT dst, const int8_t (*vals)[QK_K], int off) {
+ static_assert(IQP_NB_ROWS == 8, "the transpose is 8x8");
+
+ __m256i v[IQP_NB_ROWS];
+
+ for (int r = 0; r < IQP_NB_ROWS; ++r) {
+ v[r] = _mm256_loadu_si256((const __m256i *) (vals[r] + off));
+ }
+
+ // pair rows into dword couples, then into qword quadruples, then swap the 128 bit lanes
+ const __m256i a0 = _mm256_unpacklo_epi32(v[0], v[1]);
+ const __m256i a1 = _mm256_unpackhi_epi32(v[0], v[1]);
+ const __m256i a2 = _mm256_unpacklo_epi32(v[2], v[3]);
+ const __m256i a3 = _mm256_unpackhi_epi32(v[2], v[3]);
+ const __m256i a4 = _mm256_unpacklo_epi32(v[4], v[5]);
+ const __m256i a5 = _mm256_unpackhi_epi32(v[4], v[5]);
+ const __m256i a6 = _mm256_unpacklo_epi32(v[6], v[7]);
+ const __m256i a7 = _mm256_unpackhi_epi32(v[6], v[7]);
+
+ const __m256i b0 = _mm256_unpacklo_epi64(a0, a2);
+ const __m256i b1 = _mm256_unpackhi_epi64(a0, a2);
+ const __m256i b2 = _mm256_unpacklo_epi64(a1, a3);
+ const __m256i b3 = _mm256_unpackhi_epi64(a1, a3);
+ const __m256i b4 = _mm256_unpacklo_epi64(a4, a6);
+ const __m256i b5 = _mm256_unpackhi_epi64(a4, a6);
+ const __m256i b6 = _mm256_unpacklo_epi64(a5, a7);
+ const __m256i b7 = _mm256_unpackhi_epi64(a5, a7);
+
+ _mm256_storeu_si256((__m256i *) (dst + 0 * 32), _mm256_permute2x128_si256(b0, b4, 0x20));
+ _mm256_storeu_si256((__m256i *) (dst + 1 * 32), _mm256_permute2x128_si256(b1, b5, 0x20));
+ _mm256_storeu_si256((__m256i *) (dst + 2 * 32), _mm256_permute2x128_si256(b2, b6, 0x20));
+ _mm256_storeu_si256((__m256i *) (dst + 3 * 32), _mm256_permute2x128_si256(b3, b7, 0x20));
+ _mm256_storeu_si256((__m256i *) (dst + 4 * 32), _mm256_permute2x128_si256(b0, b4, 0x31));
+ _mm256_storeu_si256((__m256i *) (dst + 5 * 32), _mm256_permute2x128_si256(b1, b5, 0x31));
+ _mm256_storeu_si256((__m256i *) (dst + 6 * 32), _mm256_permute2x128_si256(b2, b6, 0x31));
+ _mm256_storeu_si256((__m256i *) (dst + 7 * 32), _mm256_permute2x128_si256(b3, b7, 0x31));
+}
+
+#endif
+
+// decode IQP_NB_ROWS consecutive source rows (starting at src, row stride nb01) into a panel of nblocks block_iqp_x8
+static void iqp_decode_panel_8(enum ggml_type type,
+ const char * GGML_RESTRICT src,
+ size_t nb01,
+ int64_t nblocks,
+ block_iqp_x8 * GGML_RESTRICT dst) {
+ const size_t bsize = ggml_type_size(type);
+
+ int8_t vals[IQP_NB_ROWS][QK_K];
+ int8_t iscales[IQP_NB_ROWS][IQP_NSB];
+ float dfac[IQP_NB_ROWS];
+
+ for (int64_t x = 0; x < nblocks; x++) {
+ for (int r = 0; r < IQP_NB_ROWS; r++) {
+ const char * blk = src + r * nb01 + x * bsize;
+
+ const bool ok = iqp_decode_superblock(type, blk, vals[r], iscales[r], &dfac[r]);
+ GGML_ASSERT(ok);
+
+#ifdef GGML_IQP_VERIFY
+ // check that the panel reproduces the reference dequantization bit exactly
+ float ref[QK_K];
+ ggml_get_type_traits(type)->to_float(blk, ref, QK_K);
+ for (int j = 0; j < QK_K; j++) {
+ const float scale = dfac[r] * iscales[r][j / IQP_SB_SIZE];
+ GGML_ASSERT(scale * vals[r][j] == ref[j]);
+ }
+#endif
+ }
+
+ for (int r = 0; r < IQP_NB_ROWS; r++) {
+ dst->dfac[r] = dfac[r];
+
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ dst->iscales[sb * IQP_NB_ROWS + r] = iscales[r][sb];
+ }
+
+#if GGML_IQP_USE_BIAS
+ dst->bias[r] = 128 * iqp_weighted_sum(vals[r], iscales[r]);
+#endif
+ }
+
+#if defined(__AVX2__)
+ for (int grp = 0; grp < QK_K / 32; grp++) {
+ iqp_interleave_x8(dst->qs + grp * 256, vals, grp * 32);
+ }
+#else
+ for (int r = 0; r < IQP_NB_ROWS; r++) {
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ for (int g = 0; g < IQP_SB_SIZE / 4; g++) {
+ memcpy(dst->qs + sb * 128 + g * 32 + r * 4, vals[r] + sb * IQP_SB_SIZE + g * 4, 4);
+ }
+ }
+ }
+#endif
+
+ dst++;
+ }
+}
+
+// gemm/gemv kernels: vx points at block_iqp_x8, vy at plain (non interleaved) block_q8_K rows
+
+static void iqp_gemv_8x8_q8_K_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int nb = n / QK_K;
+ const int ncols_interleaved = 8;
+
+ assert(n % QK_K == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(bs);
+ UNUSED(nr);
+
+ const block_iqp_x8 * b_ptr_start = (const block_iqp_x8 *) vx;
+ const block_q8_K * a_ptr = (const block_q8_K *) vy;
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_iqp_x8 * b_ptr = b_ptr_start + x * nb;
+
+ float sumf[8] = { 0 };
+
+ for (int l = 0; l < nb; l++) {
+ int32_t sumi[8] = { 0 };
+
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ int32_t isum[8] = { 0 };
+
+ for (int g = 0; g < 4; g++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ for (int k = 0; k < 4; k++) {
+ isum[j] += b_ptr[l].qs[sb * 128 + g * 32 + j * 4 + k] * a_ptr[l].qs[sb * 16 + g * 4 + k];
+ }
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumi[j] += isum[j] * b_ptr[l].iscales[sb * 8 + j];
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[j] += (float) sumi[j] * (b_ptr[l].dfac[j] * a_ptr[l].d);
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[x * ncols_interleaved + j] = sumf[j];
+ }
+ }
+}
+
+// one 4 row x nc column tile; s points at the first of the four output rows, bs floats apart
+static void iqp_gemm_tile_4_generic(int nb,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const block_iqp_x8 * GGML_RESTRICT b_ptr_start,
+ const block_q8_K * const a_ptr[4],
+ int nc) {
+ const int ncols_interleaved = 8;
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_iqp_x8 * b_ptr = b_ptr_start + x * nb;
+
+ float sumf[4][8];
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[m][j] = 0.0f;
+ }
+ }
+
+ for (int l = 0; l < nb; l++) {
+ for (int m = 0; m < 4; m++) {
+ int32_t sumi[8] = { 0 };
+
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ int32_t isum[8] = { 0 };
+
+ for (int g = 0; g < 4; g++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ for (int k = 0; k < 4; k++) {
+ isum[j] +=
+ b_ptr[l].qs[sb * 128 + g * 32 + j * 4 + k] * a_ptr[m][l].qs[sb * 16 + g * 4 + k];
+ }
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumi[j] += isum[j] * b_ptr[l].iscales[sb * 8 + j];
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[m][j] += (float) sumi[j] * (b_ptr[l].dfac[j] * a_ptr[m][l].d);
+ }
+ }
+ }
+
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[m * bs + x * ncols_interleaved + j] = sumf[m][j];
+ }
+ }
+ }
+}
+
+static void iqp_gemm_8x8_q8_K_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int nb = n / QK_K;
+
+ assert(n % QK_K == 0);
+ assert(nr % 4 == 0);
+ assert(nc % 8 == 0);
+
+ const block_iqp_x8 * b_ptr_start = (const block_iqp_x8 *) vx;
+ const block_q8_K * a_ptr_start = (const block_q8_K *) vy;
+
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_K * a_ptr[4];
+ for (int m = 0; m < 4; m++) {
+ a_ptr[m] = a_ptr_start + (y * 4 + m) * nb;
+ }
+
+ iqp_gemm_tile_4_generic(nb, s + y * 4 * bs, bs, b_ptr_start, a_ptr, nc);
+ }
+}
+
+static void iqp_gemm_8x8_q8_K_p4_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * const * GGML_RESTRICT vy,
+ int nc) {
+ const int nb = n / QK_K;
+
+ assert(n % QK_K == 0);
+ assert(nc % 8 == 0);
+
+ const block_q8_K * a_ptr[4];
+ for (int m = 0; m < 4; m++) {
+ a_ptr[m] = (const block_q8_K *) vy[m];
+ }
+
+ iqp_gemm_tile_4_generic(nb, s, bs, (const block_iqp_x8 *) vx, a_ptr, nc);
+}
+
+#if defined(__AVX2__)
+
+// add int16_t pairwise and return as 256 bit int vector, then add the accumulator
+static inline __m256i sum_i16_pairs_acc_int32x8(const __m256i acc, const __m256i x) {
+ const __m256i ones = _mm256_set1_epi16(1);
+ return _mm256_add_epi32(acc, _mm256_madd_epi16(ones, x));
+}
+
+static inline __m256i mul_sum_us8_pairs_acc_int32x8(const __m256i acc, const __m256i ax, const __m256i sy) {
+# if defined(__AVX512VNNI__) && defined(__AVX512VL__)
+ return _mm256_dpbusd_epi32(acc, ax, sy);
+# elif defined(__AVXVNNI__)
+ return _mm256_dpbusd_avx_epi32(acc, ax, sy);
+# else
+ // Perform multiplication and create 16-bit values
+ const __m256i dot = _mm256_maddubs_epi16(ax, sy);
+ return sum_i16_pairs_acc_int32x8(acc, dot);
+# endif
+}
+
+// Integer variant of the function defined in ggml-quants.c
+// multiply int8_t, add results pairwise twice and return as 256 bit int vector, then add the accumulator
+static inline __m256i mul_sum_i8_pairs_acc_int32x8(const __m256i acc, const __m256i x, const __m256i y) {
+# if defined(__AVXVNNIINT8__)
+ return _mm256_dpbssd_epi32(acc, x, y);
+# else
+ // Get absolute values of x vectors
+ const __m256i ax = _mm256_sign_epi8(x, x);
+ // Sign the values of the y vectors
+ const __m256i sy = _mm256_sign_epi8(y, x);
+ return mul_sum_us8_pairs_acc_int32x8(acc, ax, sy);
+# endif
+}
+
+// load the 16 activations of one sub-block, offset by 128 when they are fed to dpbusd as unsigned bytes
+static inline __m256i iqp_load_y(const int8_t * GGML_RESTRICT qs) {
+ __m128i y = _mm_loadu_si128((const __m128i *) qs);
+# if GGML_IQP_USE_BIAS
+ y = _mm_xor_si128(y, _mm_set1_epi8((char) 0x80));
+# endif
+ return _mm256_broadcastsi128_si256(y);
+}
+
+// xv: 8 rows x 4 signed weights, yb: the matching 4 activation bytes broadcast to all 8 lanes
+static inline __m256i iqp_dot4(const __m256i acc, const __m256i xv, const __m256i yb) {
+# if GGML_IQP_USE_BIAS
+ return mul_sum_us8_pairs_acc_int32x8(acc, yb, xv);
+# else
+ return mul_sum_i8_pairs_acc_int32x8(acc, xv, yb);
+# endif
+}
+
+static inline __m256i iqp_load_iscales(const int8_t * GGML_RESTRICT iscales) {
+ return _mm256_cvtepi8_epi32(_mm_loadl_epi64((const __m128i *) iscales));
+}
+
+// accumulate one super-block of 8 interleaved rows against one q8_K row in int32; worst case 16 * 32 * 16 * 255 * 127 = 2.65e8 plus a bias of at most 1.33e8 does not overflow
+static inline __m256i iqp_acc_block(const block_iqp_x8 * GGML_RESTRICT b, const block_q8_K * GGML_RESTRICT a) {
+ __m256i sumi = _mm256_setzero_si256();
+
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ const int8_t * qs = b->qs + sb * 128;
+
+ const __m256i yv = iqp_load_y(a->qs + sb * 16);
+
+ __m256i isum = _mm256_setzero_si256();
+
+ isum = iqp_dot4(isum, _mm256_loadu_si256((const __m256i *) (qs + 0)), _mm256_shuffle_epi32(yv, 0x00));
+ isum = iqp_dot4(isum, _mm256_loadu_si256((const __m256i *) (qs + 32)), _mm256_shuffle_epi32(yv, 0x55));
+ isum = iqp_dot4(isum, _mm256_loadu_si256((const __m256i *) (qs + 64)), _mm256_shuffle_epi32(yv, 0xAA));
+ isum = iqp_dot4(isum, _mm256_loadu_si256((const __m256i *) (qs + 96)), _mm256_shuffle_epi32(yv, 0xFF));
+
+ sumi = _mm256_add_epi32(sumi, _mm256_mullo_epi32(isum, iqp_load_iscales(b->iscales + sb * 8)));
+ }
+
+# if GGML_IQP_USE_BIAS
+ sumi = _mm256_sub_epi32(sumi, _mm256_loadu_si256((const __m256i *) b->bias));
+# endif
+
+ return sumi;
+}
+
+// one 4 row x nc column tile; s points at the first of the four output rows, bs floats apart
+static inline void iqp_gemm_tile_4(int nb,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const block_iqp_x8 * GGML_RESTRICT b_ptr_start,
+ const block_q8_K * const a_ptr[4],
+ int nc) {
+ const int ncols_interleaved = 8;
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_iqp_x8 * b_ptr = b_ptr_start + x * nb;
+
+ __m256 sumf[4];
+ for (int m = 0; m < 4; m++) {
+ sumf[m] = _mm256_setzero_ps();
+ }
+
+ for (int l = 0; l < nb; l++) {
+ __m256i sumi[4];
+ for (int m = 0; m < 4; m++) {
+ sumi[m] = _mm256_setzero_si256();
+ }
+
+ for (int sb = 0; sb < IQP_NSB; sb++) {
+ const int8_t * qs = b_ptr[l].qs + sb * 128;
+
+ __m256i yv[4];
+ __m256i isum[4];
+ for (int m = 0; m < 4; m++) {
+ yv[m] = iqp_load_y(a_ptr[m][l].qs + sb * 16);
+ isum[m] = _mm256_setzero_si256();
+ }
+
+ const __m256i xv0 = _mm256_loadu_si256((const __m256i *) (qs + 0));
+ const __m256i xv1 = _mm256_loadu_si256((const __m256i *) (qs + 32));
+ const __m256i xv2 = _mm256_loadu_si256((const __m256i *) (qs + 64));
+ const __m256i xv3 = _mm256_loadu_si256((const __m256i *) (qs + 96));
+
+ for (int m = 0; m < 4; m++) {
+ isum[m] = iqp_dot4(isum[m], xv0, _mm256_shuffle_epi32(yv[m], 0x00));
+ isum[m] = iqp_dot4(isum[m], xv1, _mm256_shuffle_epi32(yv[m], 0x55));
+ isum[m] = iqp_dot4(isum[m], xv2, _mm256_shuffle_epi32(yv[m], 0xAA));
+ isum[m] = iqp_dot4(isum[m], xv3, _mm256_shuffle_epi32(yv[m], 0xFF));
+ }
+
+ const __m256i isc = iqp_load_iscales(b_ptr[l].iscales + sb * 8);
+ for (int m = 0; m < 4; m++) {
+ sumi[m] = _mm256_add_epi32(sumi[m], _mm256_mullo_epi32(isum[m], isc));
+ }
+ }
+
+# if GGML_IQP_USE_BIAS
+ const __m256i bias = _mm256_loadu_si256((const __m256i *) b_ptr[l].bias);
+ for (int m = 0; m < 4; m++) {
+ sumi[m] = _mm256_sub_epi32(sumi[m], bias);
+ }
+# endif
+
+ const __m256 dfac = _mm256_loadu_ps(b_ptr[l].dfac);
+ for (int m = 0; m < 4; m++) {
+ sumf[m] = _mm256_fmadd_ps(_mm256_cvtepi32_ps(sumi[m]),
+ _mm256_mul_ps(dfac, _mm256_set1_ps(a_ptr[m][l].d)), sumf[m]);
+ }
+ }
+
+ for (int m = 0; m < 4; m++) {
+ _mm256_storeu_ps(s + m * bs + x * ncols_interleaved, sumf[m]);
+ }
+ }
+}
+
+#endif // __AVX2__
+
+static void iqp_gemv_8x8_q8_K(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int nb = n / QK_K;
+ const int ncols_interleaved = 8;
+
+ assert(n % QK_K == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(bs);
+ UNUSED(nr);
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__AVX2__)
+ const block_iqp_x8 * b_ptr_start = (const block_iqp_x8 *) vx;
+ const block_q8_K * a_ptr = (const block_q8_K *) vy;
+
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_iqp_x8 * b_ptr = b_ptr_start + x * nb;
+
+ __m256 sumf = _mm256_setzero_ps();
+
+ for (int l = 0; l < nb; l++) {
+ const __m256 dv = _mm256_mul_ps(_mm256_loadu_ps(b_ptr[l].dfac), _mm256_set1_ps(a_ptr[l].d));
+
+ sumf = _mm256_fmadd_ps(_mm256_cvtepi32_ps(iqp_acc_block(b_ptr + l, a_ptr + l)), dv, sumf);
+ }
+
+ _mm256_storeu_ps(s + x * ncols_interleaved, sumf);
+ }
+
+ return;
+#endif
+
+ iqp_gemv_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
+}
+
+static void iqp_gemm_8x8_q8_K(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int nb = n / QK_K;
+ const int ncols_interleaved = 8;
+
+ assert(n % QK_K == 0);
+ assert(nr % 4 == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__AVX2__)
+ const block_iqp_x8 * b_ptr_start = (const block_iqp_x8 *) vx;
+ const block_q8_K * a_ptr_start = (const block_q8_K *) vy;
+
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_K * a_ptr[4];
+ for (int m = 0; m < 4; m++) {
+ a_ptr[m] = a_ptr_start + (y * 4 + m) * nb;
+ }
+
+ iqp_gemm_tile_4(nb, s + y * 4 * bs, bs, b_ptr_start, a_ptr, nc);
+ }
+
+ return;
+#endif
+
+ iqp_gemm_8x8_q8_K_generic(n, s, bs, vx, vy, nr, nc);
+}
+
+// same as iqp_gemm_8x8_q8_K with nr = 4, but the activation rows are passed as separate pointers (for the scattered rows of MUL_MAT_ID)
+static void iqp_gemm_8x8_q8_K_p4(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * const * GGML_RESTRICT vy,
+ int nc) {
+ const int nb = n / QK_K;
+ const int ncols_interleaved = 8;
+
+ assert(n % QK_K == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(nb);
+ UNUSED(ncols_interleaved);
+
+#if defined(__AVX2__)
+ const block_q8_K * a_ptr[4];
+ for (int m = 0; m < 4; m++) {
+ a_ptr[m] = (const block_q8_K *) vy[m];
+ }
+
+ iqp_gemm_tile_4(nb, s, bs, (const block_iqp_x8 *) vx, a_ptr, nc);
+
+ return;
+#endif
+
+ iqp_gemm_8x8_q8_K_p4_generic(n, s, bs, vx, vy, nc);
+}
+
+static bool iqp_type_supported(enum ggml_type type) {
+ switch (type) {
+#define IQP_CASE(E, name) case GGML_TYPE_##E:
+ IQP_TYPE_LIST(IQP_CASE)
+#undef IQP_CASE
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool iqp_supported_common(const struct ggml_tensor * dst) {
+ const struct ggml_tensor * src0 = dst->src[0];
+ const struct ggml_tensor * src1 = dst->src[1];
+
+ if (!iqp_type_supported(src0->type)) {
+ return false;
+ }
+
+ // the path assumes the src1 conversion type is q8_K
+ if (ggml_get_type_traits_cpu(src0->type)->vec_dot_type != GGML_TYPE_Q8_K) {
+ return false;
+ }
+
+ // escape hatch to A/B the panel against the plain vec_dot path without rebuilding (--no-repack does not cover this path)
+ static const bool disabled = getenv("GGML_NO_IQ_PANEL") != nullptr;
+ if (disabled) {
+ return false;
+ }
+
+ if (!ggml_cpu_has_avx2()) {
+ return false;
+ }
+
+ if (src1->type != GGML_TYPE_F32) {
+ return false;
+ }
+
+ if (src0->ne[0] % QK_K != 0 || src0->ne[1] % IQP_NB_ROWS != 0) {
+ return false;
+ }
+
+ if (src0->ne[3] != 1 || src1->ne[3] != 1 || !ggml_is_contiguous(src0)) {
+ return false;
+ }
+
+ if (dst->type != GGML_TYPE_F32 || dst->nb[0] != sizeof(float)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ggml_cpu_iqp_supports_mul_mat(const struct ggml_tensor * dst) {
+ const struct ggml_tensor * src0 = dst->src[0];
+ const struct ggml_tensor * src1 = dst->src[1];
+
+ if (!iqp_supported_common(dst)) {
+ return false;
+ }
+
+ if (src1->ne[1] < GGML_IQP_MIN_BATCH) {
+ return false;
+ }
+
+ // plain 2D weight matmuls only (src1 may still be batched over ne12)
+ if (src0->ne[2] != 1) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ggml_cpu_iqp_supports_mul_mat_id(const struct ggml_tensor * dst) {
+ const struct ggml_tensor * ids = dst->src[2];
+
+ if (!iqp_supported_common(dst)) {
+ return false;
+ }
+
+ // skip the node entirely (work buffer included) if no expert can reach the per expert threshold
+ if (!ggml_cpu_iqp_mul_mat_id_min_batch(ids->ne[0] * ids->ne[1])) {
+ return false;
+ }
+
+ return true;
+}
+
+void ggml_compute_forward_mul_mat_id_iqp(const struct ggml_compute_params * params,
+ struct ggml_tensor * dst,
+ int64_t cur_a,
+ int64_t cne1,
+ const int32_t * expert_rows,
+ void * panels) {
+ const struct ggml_tensor * src0 = dst->src[0];
+ const struct ggml_tensor * src1 = dst->src[1];
+
+ GGML_TENSOR_BINARY_OP_LOCALS
+
+ const int ith = params->ith;
+ const int nth = params->nth;
+
+ const int64_t nblocks = ne00 / QK_K;
+
+ const size_t nbw1 = ggml_cpu_iqp_row_size(dst);
+
+ block_iqp_x8 * panel = (block_iqp_x8 *) ((char *) panels + (size_t) ith * ggml_cpu_iqp_scratch_size(dst));
+
+ const char * src0_cur = (const char *) src0->data + cur_a * nb02;
+
+ const int64_t ngroups = ne01 / IQP_NB_ROWS;
+
+ const int64_t g0 = (ngroups * ith) / nth;
+ const int64_t g1 = (ngroups * (ith + 1)) / nth;
+
+ for (int64_t g = g0; g < g1; g++) {
+ const int64_t r = g * IQP_NB_ROWS;
+
+ iqp_decode_panel_8(src0->type, src0_cur + r * nb01, nb01, nblocks, panel);
+
+ // the dst rows are scattered, so the gemm writes into tmp and it is copied out row by row
+ float tmp[4 * IQP_NB_ROWS];
+
+ for (int64_t k = 0; k < cne1; k += 4) {
+ const int64_t nrows = MIN(4, cne1 - k);
+
+ // a short tail tile duplicates its last row into the unused slots; the padding is never copied out
+ const void * rows[4];
+
+ for (int64_t m = 0; m < 4; m++) {
+ const int64_t kk = k + MIN(m, nrows - 1);
+
+ rows[m] = (const char *) params->wdata +
+ ((expert_rows[2 * kk + 0] % ne11) + expert_rows[2 * kk + 1] * ne11) * nbw1;
+ }
+
+ iqp_gemm_8x8_q8_K_p4(ne00, tmp, IQP_NB_ROWS, panel, rows, IQP_NB_ROWS);
+
+ for (int64_t m = 0; m < nrows; m++) {
+ float * dst_col = (float *) ((char *) dst->data + expert_rows[2 * (k + m) + 0] * nb1 +
+ expert_rows[2 * (k + m) + 1] * nb2);
+ memcpy(dst_col + r, tmp + m * IQP_NB_ROWS, IQP_NB_ROWS * sizeof(float));
+ }
+ }
+ }
+}
+
+size_t ggml_cpu_iqp_scratch_size(const struct ggml_tensor * dst) {
+ return GGML_PAD((dst->src[0]->ne[0] / QK_K) * sizeof(block_iqp_x8), 64);
+}
+
+void ggml_compute_forward_mul_mat_iqp(const struct ggml_compute_params * params, struct ggml_tensor * dst) {
+ const struct ggml_tensor * src0 = dst->src[0];
+ const struct ggml_tensor * src1 = dst->src[1];
+
+ GGML_TENSOR_BINARY_OP_LOCALS
+
+ const int ith = params->ith;
+ const int nth = params->nth;
+
+ const int64_t nblocks = ne00 / QK_K;
+
+ const size_t nbw1 = ggml_row_size(GGML_TYPE_Q8_K, ne10);
+ const size_t nbw2 = nbw1 * ne11;
+
+ const size_t scratch_size = ggml_cpu_iqp_scratch_size(dst);
+
+ const size_t scratch_offset = GGML_PAD(nbw2 * ne12, 64);
+
+ GGML_ASSERT(scratch_offset + (size_t) nth * scratch_size <= params->wsize);
+
+ block_iqp_x8 * panel = (block_iqp_x8 *) ((char *) params->wdata + scratch_offset + (size_t) ith * scratch_size);
+
+ const int64_t nrows = ne11;
+
+ const int64_t ngroups = ne01 / IQP_NB_ROWS;
+
+ // aim for 4 chunks per thread; the caller has already reset the chunk counter
+ // on NUMA systems fall back to one chunk per thread
+ const int64_t chunks_per_thread = ggml_is_numa() ? 1 : 4;
+ const int64_t groups_per_chunk = MAX(1, (ngroups + nth * chunks_per_thread - 1) / (nth * chunks_per_thread));
+ const int64_t nchunk = (ngroups + groups_per_chunk - 1) / groups_per_chunk;
+
+ int current_chunk = ith;
+
+ while (current_chunk < nchunk) {
+ const int64_t g0 = current_chunk * groups_per_chunk;
+ const int64_t g1 = MIN(g0 + groups_per_chunk, ngroups);
+
+ for (int64_t g = g0; g < g1; g++) {
+ const int64_t r = g * IQP_NB_ROWS;
+
+ iqp_decode_panel_8(src0->type, (const char *) src0->data + r * nb01, nb01, nblocks, panel);
+
+ for (int64_t i12 = 0; i12 < ne12; i12++) {
+ const char * src1_ptr = (const char *) params->wdata + i12 * nbw2;
+ char * dst_ptr = (char *) dst->data + i12 * nb2;
+
+ if (nrows > 3) {
+ iqp_gemm_8x8_q8_K(ne00, (float *) dst_ptr + r, nb1 / nb0, panel, src1_ptr, nrows - (nrows % 4),
+ IQP_NB_ROWS);
+ }
+ for (int64_t iter = nrows - (nrows % 4); iter < nrows; iter++) {
+ iqp_gemv_8x8_q8_K(ne00, (float *) (dst_ptr + iter * nb1) + r, ne01, panel, src1_ptr + nbw1 * iter,
+ 1 /* nrows */, IQP_NB_ROWS);
+ }
+ }
+ }
+
+ current_chunk = ggml_threadpool_chunk_add(params->threadpool, 1);
+ }
+}
diff --git a/ggml/src/ggml-cpu/iqp.h b/ggml/src/ggml-cpu/iqp.h
new file mode 100644
index 00000000..017b03fb
--- /dev/null
+++ b/ggml/src/ggml-cpu/iqp.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "ggml-cpu-impl.h"
+#include "ggml.h"
+
+// GGML internal header
+
+// batched mul_mat path for the grid based IQ types: decode 8 src0 rows at a time into per thread scratch
+// (block_iqp_x8, see iqp.cpp) and run an integer gemm over them against all src1 columns
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// whether cne1 rows of src1 are enough for the decode to pay for itself, per expert, for MUL_MAT_ID
+bool ggml_cpu_iqp_mul_mat_id_min_batch(int64_t cne1);
+
+bool ggml_cpu_iqp_supports_mul_mat(const struct ggml_tensor * dst);
+
+// node level test only - per expert eligibility is decided with ggml_cpu_iqp_mul_mat_id_min_batch
+bool ggml_cpu_iqp_supports_mul_mat_id(const struct ggml_tensor * dst);
+
+// per thread panel scratch bytes, padded
+size_t ggml_cpu_iqp_scratch_size(const struct ggml_tensor * dst);
+
+// must be called after src1 has been converted to q8_K into params->wdata and the threads have synchronized on it
+void ggml_compute_forward_mul_mat_iqp(const struct ggml_compute_params * params, struct ggml_tensor * dst);
+
+// one expert: expert_rows points at its row of the matrix_rows table of (i1, i2) int32 pairs, panels at the base of the per thread panel scratches
+void ggml_compute_forward_mul_mat_id_iqp(const struct ggml_compute_params * params,
+ struct ggml_tensor * dst,
+ int64_t cur_a,
+ int64_t cne1,
+ const int32_t * expert_rows,
+ void * panels);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ggml/src/ggml-cpu/kleidiai/CMakeLists.txt b/ggml/src/ggml-cpu/kleidiai/CMakeLists.txt
new file mode 100644
index 00000000..b36cb6d3
--- /dev/null
+++ b/ggml/src/ggml-cpu/kleidiai/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(BUILD_SHARED_LIBS OFF)
+set(CMAKE_SKIP_INSTALL_RULES TRUE)
+
+add_subdirectory("${KLEIDIAI_SRC}" "${KLEIDIAI_BIN}" EXCLUDE_FROM_ALL)
+
+if (NOT TARGET kleidiai)
+ message(FATAL_ERROR "KleidiAI target was not created")
+endif()
+
+if (MSVC)
+ target_compile_options(kleidiai PRIVATE $<$:/WX->)
+else()
+ target_compile_options(kleidiai PRIVATE $<$:-Wno-error>)
+endif()
diff --git a/ggml/src/ggml-cpu/kleidiai/kernels.cpp b/ggml/src/ggml-cpu/kleidiai/kernels.cpp
index 3c31ab9d..d4551298 100644
--- a/ggml/src/ggml-cpu/kleidiai/kernels.cpp
+++ b/ggml/src/ggml-cpu/kleidiai/kernels.cpp
@@ -3,43 +3,44 @@
//
// KleidiAI micro-kernels
-#include "kai_matmul_clamp_f32_qsi8d32p_qsi4c32p_interface.h"
-#include "kai_matmul_clamp_f32_qai8dxp_qsi8cxp_interface.h"
-#include "kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4x4_1x4_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qsi8d32p4x4_qsi4c32p4x4_16x4_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p4x8_16x4_neon_i8mm.h"
-#include "kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4vlx4_1x4vl_sme2_sdot.h"
-#include "kai_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa.h"
-#include "kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa.h"
-#include "kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot.h"
-#include "kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme_mopa.h"
-#include "kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme_dot.h"
-#include "kai_matmul_clamp_f32_qai8dxp1x8_qsi8cxp4x8_1x4_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4x4_1x4_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qai8dxp4x4_qsi8cxp4x4_16x4_neon_dotprod.h"
-#include "kai_matmul_clamp_f32_qai8dxp4x8_qsi8cxp4x8_16x4_neon_i8mm.h"
-#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_matmul_clamp_f32_f32p2vlx1_f32p2vlx1b_2vlx2vl_sme_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"
-#include "kai_lhs_pack_f16pmrx2_f32_neon.h"
-
-#include "kai_common.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p_qsi4c32p_interface.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp_qsi8cxp_interface.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4x4_1x4_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x4_qsi4c32p4x4_16x4_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p4x8_16x4_neon_i8mm.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4vlx4_1x4vl_sme2_sdot.h"
+#include "kai/ukernels/matmul/matmul_clamp_fp32_bf16p_bf16p/kai_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme_mopa.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme_dot.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x8_qsi8cxp4x8_1x4_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4x4_1x4_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp4x4_qsi8cxp4x4_16x4_neon_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qai8dxp_qsi8cxp/kai_matmul_clamp_f32_qai8dxp4x8_qsi8cxp4x8_16x4_neon_i8mm.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p4x8_qsi4c32p8x8_16x8_sve_i8mm.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_qsi8d32p_qsi4c32p/kai_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p8x8_1x8_sve_dotprod.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_f16p_qsi4c32p/kai_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1biasf32_sme2_mopa.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_f32_f32p/kai_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla.h"
+#include "kai/ukernels/matmul/matmul_clamp_f32_f32p_f32p/kai_matmul_clamp_f32_f32p2vlx1_f32p2vlx1b_2vlx2vl_sme_mopa.h"
+
+#include "kai/ukernels/matmul/pack/kai_lhs_pack_bf16p2vlx2_f32_sme.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_pack_f32p2vlx1_f32_sme.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p_f32.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p4x8sb_f32_neon.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_quant_pack_qsi8d32p_f32_neon.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_quant_pack_qai8dxp_f32.h"
+
+#include "kai/ukernels/matmul/pack/kai_rhs_pack_kxn_bf16p2vlx2b_f32_x32_sme.h"
+#include "kai/ukernels/matmul/pack/kai_rhs_pack_nxk_f32p2vlx1biasf32_f32_f32_sme.h"
+#include "kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi4c32pscalef16_qsu4c32s16s0.h"
+#include "kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi4c32ps1s0scalef16_qsu4c32s16s0_neon.h"
+#include "kai/ukernels/matmul/pack/kai_rhs_pack_nxk_qsi8cxp_qsi8cx_neon.h"
+#include "kai/ukernels/matmul/pack/kai_lhs_pack_f16pmrx2_f32_neon.h"
+
+#include "kai/kai_common.h"
#include "simd-mappings.h"
@@ -76,6 +77,21 @@ static inline void kernel_run_fn10(size_t m, size_t n, size_t k, size_t /*bl*/,
Fn(m, n, k, lhs, rhs, dst, dst_stride_row, dst_stride_col, clamp_min, clamp_max);
}
+template
+static inline void kernel_run_lhs_stride_fn10(size_t m,
+ size_t n,
+ size_t k,
+ size_t lhs_stride,
+ const void * lhs,
+ const void * rhs,
+ void * dst,
+ size_t dst_stride_row,
+ size_t dst_stride_col,
+ float clamp_min,
+ float clamp_max) {
+ Fn(m, n, k, lhs, lhs_stride, rhs, dst, dst_stride_row, dst_stride_col, clamp_min, clamp_max);
+}
+
template
static inline void kernel_run_float_fn10(size_t m, size_t n, size_t k, size_t /*bl*/,
const void* lhs, const void* rhs, void* dst,
@@ -312,9 +328,8 @@ static void dequantize_row_qsi8cxp(
}
static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
-#if defined(__ARM_FEATURE_SME)
{
- /* SME GEMM */
+ /* SME2 GEMM */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_f16p1vlx2_qsi4c32p4vlx2_1vlx4vl_sme2_mopa,
@@ -335,7 +350,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_size_ex = */ &lhs_ps_fn6,
/* .pack_func_ex = */ &lhs_pack_void_fn10,
},
- /* SME GEMV */
+ /* SME2 GEMV */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4vlx4_1x4vl_sme2_sdot,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_qsi8d32p1x4_qsi4c32p4vlx4_1x4vl_sme2_sdot,
@@ -362,13 +377,13 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_stride_ex = */ &rhs_stride_fn4,
/* .pack_func_ex = */ &rhs_pack_fn12,
},
- /* .required_cpu = */ CPU_FEATURE_SME2,
+ /* .required_cpu = */ CPU_FEATURE_SME2 | CPU_FEATURE_FP16,
/* .lhs_type = */ GGML_TYPE_F32,
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
{
- /* SME GEMM */
+ /* SME2 GEMM */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa,
@@ -388,7 +403,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_size_ex = */ &lhs_ps_fn5,
/* .pack_func_ex = */ &lhs_pack_void_fn9,
},
- /* SME GEMV */
+ /* SME2 GEMV */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_bf16p2vlx2_bf16p2vlx2_2vlx2vl_sme2_mopa,
@@ -420,9 +435,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .rhs_type = */ GGML_TYPE_F16,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
#if defined(__APPLE__)
-#if defined(__ARM_FEATURE_DOTPROD)
{
/* DOTPROD GEMM */
/* .kern_info = */ {
@@ -476,8 +489,6 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
-#if defined(__ARM_FEATURE_MATMUL_INT8)
{
/* i8mm GEMM */
/* .kern_info = */ {
@@ -499,7 +510,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_size_ex = */ &lhs_ps_fn6,
/* .pack_func_ex = */ &lhs_pack_float_fn10,
},
- /* i8mm GEMV */
+ /* DOTPROD GEMV */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod,
@@ -526,14 +537,12 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_stride_ex = */ &rhs_stride_fn4,
/* .pack_func_ex = */ &rhs_pack_fn12,
},
- /* .required_cpu = */ CPU_FEATURE_I8MM,
+ /* .required_cpu = */ CPU_FEATURE_I8MM | CPU_FEATURE_DOTPROD,
/* .lhs_type = */ GGML_TYPE_F32,
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
#else
-#if defined(__ARM_FEATURE_SVE)
{
/* SVE i8mm GEMM */
/* .kern_info = */ {
@@ -587,8 +596,6 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
-#if defined(__ARM_FEATURE_MATMUL_INT8)
{
/* i8mm GEMM */
/* .kern_info = */ {
@@ -610,7 +617,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_size_ex = */ &lhs_ps_fn6,
/* .pack_func_ex = */ &lhs_pack_float_fn10,
},
- /* i8mm GEMV */
+ /* DOTPROD GEMV */
/* .kern_info = */ {
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_qsi8d32p1x8_qsi4c32p4x8_1x4x32_neon_dotprod,
@@ -637,13 +644,11 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .packed_stride_ex = */ &rhs_stride_fn4,
/* .pack_func_ex = */ &rhs_pack_fn12,
},
- /* .required_cpu = */ CPU_FEATURE_I8MM,
+ /* .required_cpu = */ CPU_FEATURE_I8MM | CPU_FEATURE_DOTPROD,
/* .lhs_type = */ GGML_TYPE_F32,
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif // __ARM_FEATURE_MATMUL_INT8
-#if defined(__ARM_FEATURE_DOTPROD)
{
/* DOTPROD GEMM */
/* .kern_info = */ {
@@ -697,15 +702,13 @@ static ggml_kleidiai_kernels gemm_gemv_kernels[] = {
/* .rhs_type = */ GGML_TYPE_Q4_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
#endif
{ /* Sentinel */ }
};
static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
-#if defined(__ARM_FEATURE_SME)
{
- /* SME GEMM */
+ /* SME2 GEMM */
{
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_qai8dxp1vlx4_qsi8cxp4vlx4_1vlx4vl_sme2_mopa,
@@ -725,7 +728,7 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
/* .packed_size_ex = */ &lhs_ps_fn5,
/* .pack_func_ex = */ &lhs_pack_float_fn9_no_bl,
},
- /* SME GEMV */
+ /* SME2 GEMV */
{
/* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot,
/* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_qai8dxp1x4_qsi8cxp4vlx4_1x4vl_sme2_dot,
@@ -810,8 +813,6 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
/* .rhs_type = */ GGML_TYPE_Q8_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
-#if defined(__ARM_FEATURE_MATMUL_INT8)
{
/* I8MM GEMM */
{
@@ -860,13 +861,11 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
/* .packed_stride_ex = */ &rhs_stride_fn4,
/* .pack_func_ex = */ &rhs_pack_scale_fn12,
},
- /* .required_cpu = */ CPU_FEATURE_I8MM,
+ /* .required_cpu = */ CPU_FEATURE_I8MM | CPU_FEATURE_DOTPROD,
/* .lhs_type = */ GGML_TYPE_F32,
/* .rhs_type = */ GGML_TYPE_Q8_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
-#if defined(__ARM_FEATURE_DOTPROD)
{
/* DOTPROD GEMM */
{
@@ -920,12 +919,10 @@ static ggml_kleidiai_kernels gemm_gemv_kernels_q8[] = {
/* .rhs_type = */ GGML_TYPE_Q8_0,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
{ /* Sentinel */ }
};
static ggml_kleidiai_kernels ggml_kleidiai_kernels_f32[] = {
-#if defined(__ARM_FEATURE_SME)
{
/* SME2 GEMM */
{
@@ -947,25 +944,25 @@ static ggml_kleidiai_kernels ggml_kleidiai_kernels_f32[] = {
/* .packed_size_ex = */ &lhs_ps_fn5,
/* .pack_func_ex = */ &lhs_pack_void_fn9,
},
- /* SME GEMV */
+ /* SME2 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,
+ /* .get_m_step = */ kai_get_m_step_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_n_step = */ kai_get_n_step_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_mr = */ kai_get_m_step_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_nr = */ kai_get_nr_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_kr = */ kai_get_kr_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_sr = */ kai_get_sr_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_dst_offset = */ kai_get_dst_offset_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_dst_size = */ kai_get_dst_size_matmul_clamp_f32_f32_f32p2vlx1b_1x16vl_sme2_mla,
+ /* .get_lhs_offset_ex = */ &kernel_offs_fn2,
+ /* .get_rhs_packed_offset_ex = */ &kernel_offs_fn2,
+ /* .run_kernel_ex = */ &kernel_run_lhs_stride_fn10,
},
/* .gemv_lhs_info = */ {
- /* .get_offset = */ kai_get_lhs_offset_lhs_pack_f32p2vlx1_f32_sme,
- /* .get_packed_offset_ex = */ &lhs_offs_fn5,
- /* .packed_size_ex = */ &lhs_ps_fn5,
- /* .pack_func_ex = */ &lhs_pack_void_fn9,
+ /* .get_offset = */ nullptr,
+ /* .get_packed_offset_ex = */ nullptr,
+ /* .packed_size_ex = */ nullptr,
+ /* .pack_func_ex = */ nullptr,
},
/* .rhs_info = */ {
/* .packed_stride = */ nullptr,
@@ -1032,7 +1029,6 @@ static ggml_kleidiai_kernels ggml_kleidiai_kernels_f32[] = {
/* .rhs_type = */ GGML_TYPE_F32,
/* .op_type = */ GGML_TYPE_F32,
},
-#endif
{ /* Sentinel */ }
};
@@ -1040,10 +1036,6 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, c
ggml_kleidiai_kernels * kernel = nullptr;
if (tensor->op == GGML_OP_MUL_MAT && tensor->src[0] != nullptr && tensor->src[1] != nullptr) {
-#if defined(__ARM_FEATURE_SME) || \
- defined(__ARM_FEATURE_DOTPROD) || \
- defined(__ARM_FEATURE_MATMUL_INT8) || \
- defined(__ARM_FEATURE_SVE)
auto try_table = [&](auto & table) {
for (size_t i = 0; i < NELEMS(table) - 1; ++i) {
if ((cpu_features & table[i].required_cpu) == table[i].required_cpu &&
@@ -1064,12 +1056,6 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, c
} 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
}
return kernel;
@@ -1078,19 +1064,13 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels(cpu_feature cpu_features, c
ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_q4_0(cpu_feature features) {
ggml_kleidiai_kernels * kernels = nullptr;
-#if defined(__ARM_FEATURE_SME) || \
- defined(__ARM_FEATURE_DOTPROD) || \
- defined(__ARM_FEATURE_MATMUL_INT8) || \
- defined(__ARM_FEATURE_SVE)
for (size_t i = 0; i < NELEMS(gemm_gemv_kernels) - 1; ++i) {
- if ((features & gemm_gemv_kernels[i].required_cpu) == gemm_gemv_kernels[i].required_cpu) {
+ if ((features & gemm_gemv_kernels[i].required_cpu) == gemm_gemv_kernels[i].required_cpu &&
+ gemm_gemv_kernels[i].rhs_type == GGML_TYPE_Q4_0) {
kernels = &gemm_gemv_kernels[i];
break;
}
}
-#else
- GGML_UNUSED(features);
-#endif
return kernels;
}
@@ -1098,16 +1078,12 @@ 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 * kernels = nullptr;
-#if defined(__ARM_FEATURE_SME) || defined(__ARM_FEATURE_DOTPROD) || defined(__ARM_FEATURE_MATMUL_INT8)
for (size_t i = 0; i < NELEMS(gemm_gemv_kernels_q8) - 1; ++i) {
if ((features & gemm_gemv_kernels_q8[i].required_cpu) == gemm_gemv_kernels_q8[i].required_cpu) {
kernels = &gemm_gemv_kernels_q8[i];
break;
}
}
-#else
- GGML_UNUSED(features);
-#endif
return kernels;
}
@@ -1115,16 +1091,11 @@ ggml_kleidiai_kernels * ggml_kleidiai_select_kernels_q8_0(cpu_feature features)
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;
}
diff --git a/ggml/src/ggml-cpu/kleidiai/kernels.h b/ggml/src/ggml-cpu/kleidiai/kernels.h
index 0da5e65a..1da8610e 100644
--- a/ggml/src/ggml-cpu/kleidiai/kernels.h
+++ b/ggml/src/ggml-cpu/kleidiai/kernels.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates
+// SPDX-FileCopyrightText: Copyright 2025-2026 Arm Limited and/or its affiliates
// SPDX-License-Identifier: MIT
//
@@ -12,7 +12,8 @@ enum cpu_feature {
CPU_FEATURE_I8MM = 2,
CPU_FEATURE_SVE = 4,
CPU_FEATURE_SME = 8,
- CPU_FEATURE_SME2 = 16
+ CPU_FEATURE_SME2 = 16,
+ CPU_FEATURE_FP16 = 32
};
inline cpu_feature& operator|=(cpu_feature& lhs, cpu_feature rhs) {
diff --git a/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp b/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp
index 2266c168..dbd19878 100644
--- a/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp
+++ b/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp
@@ -48,7 +48,7 @@
#include "kernels.h"
-#include "kai_common.h"
+#include "kai/kai_common.h"
#define GGML_COMMON_DECL_CPP
#include "ggml-common.h"
@@ -316,6 +316,7 @@ static void init_kleidiai_context(void) {
ctx.features = (runtime_feat.has_dotprod ? CPU_FEATURE_DOTPROD : CPU_FEATURE_NONE) |
(runtime_feat.has_i8mm ? CPU_FEATURE_I8MM : CPU_FEATURE_NONE) |
+ (runtime_feat.has_fp16 ? CPU_FEATURE_FP16 : CPU_FEATURE_NONE) |
(runtime_feat.sve_cnt == QK8_0 ? CPU_FEATURE_SVE : CPU_FEATURE_NONE);
if (env_threads) {
@@ -696,6 +697,15 @@ class tensor_traits : public ggml::cpu::tensor_traits {
}
if (op->src[0]->type == GGML_TYPE_F32) {
+ ggml_kleidiai_kernels * primary = kernel_chain[0];
+ kernel_info * gemv_kernel = primary ? &primary->gemv : nullptr;
+ if (is_gemv && op->src[1]->nb[0] == (int64_t) sizeof(float) && gemv_kernel &&
+ gemv_kernel->get_lhs_offset_ex && gemv_kernel->get_rhs_packed_offset_ex &&
+ gemv_kernel->run_kernel_ex && gemv_kernel->get_dst_offset) {
+ size = 0;
+ return true;
+ }
+
size_t cursor = 0;
bool any_slot = false;
@@ -811,15 +821,28 @@ class tensor_traits : public ggml::cpu::tensor_traits {
return false;
}
- kernel_info * kernel = &kernels->gemm;
+ const size_t k = ne00;
+ const size_t m = ne11;
+ const size_t n = ne01;
+ const bool use_gemv = m == 1 && src1->nb[0] == (int64_t) sizeof(float) &&
+ kernels->gemv.get_lhs_offset_ex &&
+ kernels->gemv.get_rhs_packed_offset_ex &&
+ kernels->gemv.run_kernel_ex &&
+ kernels->gemv.get_dst_offset;
+
+ kernel_info * kernel = use_gemv ? &kernels->gemv : &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 ||
+ if (!kernel || !kernel->get_lhs_offset_ex ||
!kernel->get_rhs_packed_offset_ex || !kernel->run_kernel_ex || !kernel->get_dst_offset) {
return false;
}
+ if (!use_gemv && (!lhs_info || !lhs_info->get_offset || !lhs_info->get_packed_offset_ex ||
+ !lhs_info->packed_size_ex || !lhs_info->pack_func_ex)) {
+ return false;
+ }
+
const kleidiai_weight_header * header = kleidiai_weight_header_from_ptr(src0->data);
const bool has_header = kleidiai_is_weight_header_valid(header);
@@ -832,16 +855,14 @@ class tensor_traits : public ggml::cpu::tensor_traits {
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);
+ const size_t lhs_packed_size = use_gemv ? 0 : lhs_info->packed_size_ex(m, k, 0, mr, kr, sr);
+ if (!use_gemv) {
+ GGML_ASSERT(lhs_packed_size <= params->wsize);
+ }
uint8_t * lhs_packed = static_cast(params->wdata);
const size_t dst_stride = dst->nb[1];
@@ -853,7 +874,7 @@ class tensor_traits : public ggml::cpu::tensor_traits {
const uint8_t * lhs_batch_base = static_cast(src1->data) + batch_idx * src1->nb[2];
uint8_t * dst_batch_base = static_cast(dst->data) + batch_idx * dst->nb[2];
- {
+ if (!use_gemv) {
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(1, max_threads);
@@ -903,15 +924,17 @@ class tensor_traits : public ggml::cpu::tensor_traits {
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 lhs_offset = use_gemv ? kernel->get_lhs_offset_ex(0, k, 0)
+ : 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 * lhs_ptr = use_gemv ? lhs_batch_base + lhs_offset
+ : lhs_packed + lhs_offset;
const void * rhs_ptr = rhs_base + rhs_packed_offset;
float * dst_ptr = reinterpret_cast(dst_batch_base + dst_offset);
- kernel->run_kernel_ex(m, n_to_process, k, 0,
+ kernel->run_kernel_ex(m, n_to_process, k, use_gemv ? src1->nb[1] : 0,
lhs_ptr,
rhs_ptr,
dst_ptr,
@@ -1800,7 +1823,7 @@ class extra_buffer_type : ggml::cpu::extra_buffer_type {
const bool src0_is_kleidiai =
op->src[0]->buffer &&
(ggml_n_dims(op->src[0]) == 2) &&
- op->src[0]->buffer->buft == ggml_backend_cpu_kleidiai_buffer_type() &&
+ op->src[0]->buffer->buft->context == this &&
slot_total > 0;
if ((op->op == GGML_OP_MUL_MAT || op->op == GGML_OP_GET_ROWS) &&
@@ -1839,7 +1862,7 @@ class extra_buffer_type : ggml::cpu::extra_buffer_type {
ggml::cpu::tensor_traits * get_tensor_traits(const struct ggml_tensor * op) override {
if (op->op == GGML_OP_MUL_MAT || op->op == GGML_OP_GET_ROWS) {
- if (op->src[0]->buffer && op->src[0]->buffer->buft == ggml_backend_cpu_kleidiai_buffer_type()) {
+ if (op->src[0]->buffer && op->src[0]->buffer->buft->context == this) {
return (ggml::cpu::tensor_traits *) op->src[0]->extra;
} else {
// KleidiAI only has kernels for Q4_0 and Q8_0. For a quantized weight of any
diff --git a/ggml/src/ggml-cpu/ops.cpp b/ggml/src/ggml-cpu/ops.cpp
index 001e1ae8..ba00a0a7 100644
--- a/ggml/src/ggml-cpu/ops.cpp
+++ b/ggml/src/ggml-cpu/ops.cpp
@@ -1896,7 +1896,6 @@ void ggml_compute_forward_repeat_back(
}
// ggml_compute_forward_concat
-
static void ggml_compute_forward_concat_any(
const ggml_compute_params * params,
ggml_tensor * dst) {
@@ -1904,8 +1903,6 @@ static void ggml_compute_forward_concat_any(
const ggml_tensor * src0 = dst->src[0];
const ggml_tensor * src1 = dst->src[1];
- const size_t len = ggml_type_size(src0->type);
-
const int ith = params->ith;
const int nth = params->nth;
@@ -1914,31 +1911,38 @@ static void ggml_compute_forward_concat_any(
const int32_t dim = ggml_get_op_params_i32(dst, 0);
GGML_ASSERT(dim >= 0 && dim < 4);
+ GGML_ASSERT(ggml_is_contiguous_rows(src0));
+ GGML_ASSERT(ggml_is_contiguous_rows(src1));
int64_t o[4] = {0, 0, 0, 0};
+
if (dim == 0) {
+ GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
+ GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
+
o[dim] = src0->ne[dim]/ggml_blck_size(src0->type);
} else {
o[dim] = src0->ne[dim];
}
- const char * x;
-
- // TODO: smarter multi-theading
- for (int i3 = 0; i3 < ne3; i3++) {
- for (int i2 = ith; i2 < ne2; i2 += nth) {
- for (int i1 = 0; i1 < ne1; i1++) {
- for (int i0 = 0; i0 < ne0/ggml_blck_size(dst->type); i0++) {
- if (i0 < ne00/ggml_blck_size(src0->type) && i1 < ne01 && i2 < ne02 && i3 < ne03) {
- x = (const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03;
- } else {
- x = (const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13;
- }
-
- char * y = (char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3;
+ // Region 1: copy rows from src0
+ for (int i3 = 0; i3 < ne03; i3++) {
+ for (int i2 = ith; i2 < ne02; i2 += nth) {
+ for (int i1 = 0; i1 < ne01; i1++) {
+ const char * x = (const char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03;
+ char * y = ( char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3;
+ memcpy(y, x, ggml_row_size(src0->type, ne00));
+ }
+ }
+ }
- memcpy(y, x, len);
- }
+ // Region 2: copy rows from src1, offset into dst by o[]
+ for (int i3 = 0; i3 < ne13; i3++) {
+ for (int i2 = ith; i2 < ne12; i2 += nth) {
+ for (int i1 = 0; i1 < ne11; i1++) {
+ const char * x = (const char *) src1->data + i1*nb11 + i2*nb12 + i3*nb13;
+ char * y = ( char *) dst->data + (i1 + o[1])*nb1 + (i2 + o[2])*nb2 + (i3 + o[3])*nb3 + o[0]*nb0;
+ memcpy(y, x, ggml_row_size(src1->type, ne10));
}
}
}
@@ -2078,14 +2082,6 @@ void ggml_compute_forward_concat(
ggml_tensor * dst) {
const ggml_tensor * src0 = dst->src[0];
- 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(src0->ne[0] % ggml_blck_size(src0->type) == 0);
- GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
- }
switch (src0->type) {
case GGML_TYPE_F16:
@@ -3407,6 +3403,139 @@ static void ggml_compute_forward_swiglu_oai(
}
}
+// ggml_compute_forward_swiglu_clamp
+
+static void ggml_compute_forward_swiglu_clamp_f32(const ggml_compute_params * params, ggml_tensor * dst) {
+ const ggml_tensor * src0 = dst->src[0];
+ const ggml_tensor * src1 = dst->src[1];
+ char * src0_d = (char *) src0->data;
+ char * src1_d = (char *) (src1 ? src1->data : src0->data);
+ const size_t src0_o = src0->nb[1];
+ const size_t src1_o = src1 ? src1->nb[1] : src0->nb[1];
+
+ GGML_ASSERT(ggml_is_contiguous_1(src0));
+ GGML_ASSERT(ggml_is_contiguous_1(dst));
+
+ if (src1) {
+ GGML_ASSERT(ggml_is_contiguous_1(src1));
+ GGML_ASSERT(src0->type == src1->type);
+ }
+
+ const int ith = params->ith;
+ const int nth = params->nth;
+
+ const int nc = src1 ? src0->ne[0] : src0->ne[0] / 2;
+ const int nr = ggml_nrows(src0);
+
+ GGML_ASSERT(dst->ne[0] == nc);
+ GGML_ASSERT(ggml_nrows(dst) == nr);
+
+ const int32_t swapped = ggml_get_op_params_i32(dst, 1);
+ const float limit = ggml_get_op_params_f32(dst, 3);
+
+ const int dr = (nr + nth - 1) / nth;
+ const int ir0 = dr * ith;
+ const int ir1 = MIN(ir0 + dr, nr);
+
+ for (int i1 = ir0; i1 < ir1; i1++) {
+ float * src0_p = (float *) (src0_d + i1 * src0_o);
+ float * src1_p = (float *) (src1_d + i1 * src1_o);
+ float * dst_p = (float *) ((char *) dst->data + i1 * (dst->nb[1]));
+
+ if (!src1) {
+ src0_p += swapped ? nc : 0;
+ src1_p += swapped ? 0 : nc;
+ }
+
+ for (int k = 0; k < nc; k++) {
+ const float gate = std::min(src0_p[k], limit);
+ const float up = std::clamp(src1_p[k], -limit, limit);
+ dst_p[k] = gate / (1.f + expf(-gate)) * up;
+ }
+
+#ifndef NDEBUG
+ for (int k = 0; k < nc; k++) {
+ const float x = dst_p[k];
+ GGML_UNUSED(x);
+ assert(!isnan(x));
+ assert(!isinf(x));
+ }
+#endif // NDEBUG
+ }
+}
+
+static void ggml_compute_forward_swiglu_clamp_f16(const ggml_compute_params * params, ggml_tensor * dst) {
+ const ggml_tensor * src0 = dst->src[0];
+ const ggml_tensor * src1 = dst->src[1];
+ char * src0_d = (char *) src0->data;
+ char * src1_d = (char *) (src1 ? src1->data : src0->data);
+ const size_t src0_o = src0->nb[1];
+ const size_t src1_o = src1 ? src1->nb[1] : src0->nb[1];
+
+ GGML_ASSERT(ggml_is_contiguous_1(src0));
+ GGML_ASSERT(ggml_is_contiguous_1(dst));
+
+ if (src1) {
+ GGML_ASSERT(ggml_is_contiguous_1(src1));
+ GGML_ASSERT(src0->type == src1->type);
+ }
+
+ const int ith = params->ith;
+ const int nth = params->nth;
+
+ const int nc = src1 ? src0->ne[0] : src0->ne[0] / 2;
+ const int nr = ggml_nrows(src0);
+
+ GGML_ASSERT(dst->ne[0] == nc);
+ GGML_ASSERT(ggml_nrows(dst) == nr);
+
+ const int32_t swapped = ggml_get_op_params_i32(dst, 1);
+ const float limit = ggml_get_op_params_f32(dst, 3);
+
+ const int dr = (nr + nth - 1) / nth;
+ const int ir0 = dr * ith;
+ const int ir1 = MIN(ir0 + dr, nr);
+
+ for (int i1 = ir0; i1 < ir1; i1++) {
+ ggml_fp16_t * src0_p = (ggml_fp16_t *) (src0_d + i1 * src0_o);
+ ggml_fp16_t * src1_p = (ggml_fp16_t *) (src1_d + i1 * src1_o);
+ ggml_fp16_t * dst_p = (ggml_fp16_t *) ((char *) dst->data + i1 * (dst->nb[1]));
+
+ if (!src1) {
+ src0_p += swapped ? nc : 0;
+ src1_p += swapped ? 0 : nc;
+ }
+
+ for (int k = 0; k < nc; k++) {
+ const float gate = std::min(GGML_FP16_TO_FP32(src0_p[k]), limit);
+ const float up = std::clamp(GGML_FP16_TO_FP32(src1_p[k]), -limit, limit);
+ dst_p[k] = GGML_FP32_TO_FP16(gate / (1.f + expf(-gate)) * up);
+ }
+
+#ifndef NDEBUG
+ for (int k = 0; k < nc; k++) {
+ const float x = GGML_FP16_TO_FP32(dst_p[k]);
+ GGML_UNUSED(x);
+ assert(!isnan(x));
+ assert(!isinf(x));
+ }
+#endif // NDEBUG
+ }
+}
+
+static void ggml_compute_forward_swiglu_clamp(const ggml_compute_params * params, ggml_tensor * dst) {
+ switch (dst->src[0]->type) {
+ case GGML_TYPE_F32:
+ ggml_compute_forward_swiglu_clamp_f32(params, dst);
+ break;
+ case GGML_TYPE_F16:
+ ggml_compute_forward_swiglu_clamp_f16(params, dst);
+ break;
+ default:
+ GGML_ABORT("fatal error");
+ }
+}
+
// ggml_compute_forward_geglu_erf
static void ggml_compute_forward_geglu_erf_f32(
@@ -5979,6 +6108,8 @@ static void ggml_compute_forward_rope_flt(
memcpy(&beta_slow, (int32_t *) dst->op_params + 10, sizeof(float));
memcpy(§ions, (int32_t *) dst->op_params + 11, sizeof(int)*4);
+ const int n_offs = ((int32_t *) dst->op_params)[15];
+
GGML_TENSOR_UNARY_OP_LOCALS
//printf("ne0: %d, ne1: %d, ne2: %d, ne3: %d\n", ne0, ne1, ne2, ne3);
@@ -5995,6 +6126,10 @@ static void ggml_compute_forward_rope_flt(
GGML_ASSERT(n_dims <= ne0);
GGML_ASSERT(n_dims % 2 == 0);
+ GGML_ASSERT(n_offs >= 0);
+ GGML_ASSERT(n_offs % 2 == 0);
+ GGML_ASSERT(n_offs + n_dims <= ne0);
+
// rows per thread
const int dr = (nr + nth - 1)/nth;
@@ -6020,6 +6155,7 @@ static void ggml_compute_forward_rope_flt(
if (is_vision) {
GGML_ASSERT(n_dims == ne0/2);
+ GGML_ASSERT(n_offs == 0);
}
const float * freq_factors = NULL;
@@ -6068,12 +6204,12 @@ static void ggml_compute_forward_rope_flt(
switch (mode) {
case GGML_ROPE_TYPE_NORMAL:
- rotate_pairs(n_dims, 1, cache, src, dst_data, 1);
+ rotate_pairs(n_dims, 1, cache, src + n_offs, dst_data + n_offs, 1);
break;
case GGML_ROPE_TYPE_NEOX:
case GGML_ROPE_TYPE_MROPE:
case GGML_ROPE_TYPE_IMROPE:
- rotate_pairs(n_dims, n_dims/2, cache, src, dst_data);
+ rotate_pairs(n_dims, n_dims/2, cache, src + n_offs, dst_data + n_offs);
break;
case GGML_ROPE_TYPE_VISION:
rotate_pairs(ne0, n_dims, cache, src, dst_data);
@@ -6084,7 +6220,11 @@ static void ggml_compute_forward_rope_flt(
if (!is_vision) {
// fill the remain channels with data from src tensor
- for (int64_t i0 = n_dims; i0 < ne0; i0 += 2) {
+ for (int64_t i0 = 0; i0 < ne0; i0 += 2) {
+ if (i0 == n_offs) {
+ i0 += n_dims - 2; // skip the rotated channels
+ continue;
+ }
const T * const src = (T *)((char *) src0->data + i3*nb03 + i2*nb02 + i1*nb01 + i0*nb00);
T * dst_data = (T *)((char *) dst->data + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);
@@ -7260,18 +7400,21 @@ static void ggml_compute_forward_conv_transpose_2d_impl(
}
}
- // permute source data (src1) from (Sw x Sh x Cin) to (Cin x Sw x Sh)
+ // permute source data (src1) from (Sw x Sh x Cin) to (Cin x Sw x Sh), for all batches
{
kernel_t * const wdata = (kernel_t *) params->wdata + nk;
- for (int i12 = 0; i12 < ne12; i12++) {
- for (int i11 = 0; i11 < ne11; i11++) {
- const float * const src = (float *)((char *) src1->data + i12*nb12 + i11*nb11);
- kernel_t * dst_data = wdata + i11*ne10*ne12;
- for (int i10 = 0; i10 < ne10; i10++) {
- if constexpr (std::is_same_v) {
- dst_data[i10*ne12 + i12] = GGML_CPU_FP32_TO_FP16(src[i10]);
- } else {
- dst_data[i10*ne12 + i12] = src[i10];
+ for (int i13 = 0; i13 < ne13; i13++) {
+ kernel_t * const wdata_b = wdata + i13*ne10*ne11*ne12;
+ for (int i12 = 0; i12 < ne12; i12++) {
+ for (int i11 = 0; i11 < ne11; i11++) {
+ const float * const src = (float *)((char *) src1->data + i13*nb13 + i12*nb12 + i11*nb11);
+ kernel_t * dst_data = wdata_b + i11*ne10*ne12;
+ for (int i10 = 0; i10 < ne10; i10++) {
+ if constexpr (std::is_same_v) {
+ dst_data[i10*ne12 + i12] = GGML_CPU_FP32_TO_FP16(src[i10]);
+ } else {
+ dst_data[i10*ne12 + i12] = src[i10];
+ }
}
}
}
@@ -7298,24 +7441,27 @@ static void ggml_compute_forward_conv_transpose_2d_impl(
kernel_t * const wdata_src = wdata + nk;
for (int i2 = ip0; i2 < ip1; i2++) { // Cout
- float * dst_data = (float *)((char *) dst->data + i2*nb2);
kernel_t * wdata_kernel = wdata + i2*ne01*ne00*ne03;
- for (int i11 = 0; i11 < ne11; i11++) {
- for (int i10 = 0; i10 < ne10; i10++) {
- const int i1n = i11*ne10*ne12 + i10*ne12;
- for (int i01 = 0; i01 < ne01; i01++) {
- for (int i00 = 0; i00 < ne00; i00++) {
- float v = 0;
- if constexpr (std::is_same_v) {
- ggml_vec_dot_f16(ne03, &v, 0,
- wdata_src + i1n, 0,
- wdata_kernel + i01*ne00*ne03 + i00*ne03, 0, 1);
- } else {
- ggml_vec_dot_f32(ne03, &v, 0,
- wdata_src + i1n, 0,
- wdata_kernel + i01*ne00*ne03 + i00*ne03, 0, 1);
+ for (int i3 = 0; i3 < ne3; i3++) { // batch
+ float * dst_data = (float *)((char *) dst->data + i3*nb3 + i2*nb2);
+ kernel_t * wdata_src_b = wdata_src + i3*ne10*ne11*ne12;
+ for (int i11 = 0; i11 < ne11; i11++) {
+ for (int i10 = 0; i10 < ne10; i10++) {
+ const int i1n = i11*ne10*ne12 + i10*ne12;
+ for (int i01 = 0; i01 < ne01; i01++) {
+ for (int i00 = 0; i00 < ne00; i00++) {
+ float v = 0;
+ if constexpr (std::is_same_v) {
+ ggml_vec_dot_f16(ne03, &v, 0,
+ wdata_src_b + i1n, 0,
+ wdata_kernel + i01*ne00*ne03 + i00*ne03, 0, 1);
+ } else {
+ ggml_vec_dot_f32(ne03, &v, 0,
+ wdata_src_b + i1n, 0,
+ wdata_kernel + i01*ne00*ne03 + i00*ne03, 0, 1);
+ }
+ dst_data[(i11*stride + i01)*ne0 + i10*stride + i00] += v;
}
- dst_data[(i11*stride + i01)*ne0 + i10*stride + i00] += v;
}
}
}
@@ -10123,6 +10269,10 @@ void ggml_compute_forward_glu(
{
ggml_compute_forward_geglu_quick(params, dst);
} break;
+ case GGML_GLU_OP_SWIGLU_CLAMP:
+ {
+ ggml_compute_forward_swiglu_clamp(params, dst);
+ } break;
default:
{
GGML_ABORT("fatal error");
@@ -11109,10 +11259,19 @@ static void ggml_compute_forward_dsv4_hc_pre_f32(
const int64_t hc = x->ne[1];
const int64_t n_tokens = x->ne[2];
+ const float scale = ggml_get_op_params_f32(dst, 0);
+ const bool gated = ggml_get_op_params_i32(dst, 1) != 0;
+
GGML_ASSERT(dst->ne[0] == n_embd);
GGML_ASSERT(dst->ne[1] == n_tokens);
- GGML_ASSERT(weights->ne[0] == hc);
- GGML_ASSERT(weights->ne[1] == n_tokens);
+ if (gated) {
+ GGML_ASSERT(weights->ne[0] == n_embd);
+ GGML_ASSERT(weights->ne[1] == hc);
+ GGML_ASSERT(weights->ne[2] == n_tokens);
+ } else {
+ GGML_ASSERT(weights->ne[0] == hc);
+ GGML_ASSERT(weights->ne[1] == n_tokens);
+ }
GGML_TENSOR_LOCALS(size_t, nbx, x, nb);
GGML_TENSOR_LOCALS(size_t, nbw, weights, nb);
@@ -11132,12 +11291,18 @@ static void ggml_compute_forward_dsv4_hc_pre_f32(
float sum = 0.0f;
for (int64_t ih = 0; ih < hc; ++ih) {
- const float xv = *(const float *) ((const char *) x->data + i0*nbx0 + ih*nbx1 + it*nbx2);
- const float wv = *(const float *) ((const char *) weights->data + ih*nbw0 + it*nbw1);
+ const float xv = *(const float *) ((const char *) x->data + i0*nbx0 + ih*nbx1 + it*nbx2);
+ float wv;
+ if (gated) {
+ const float gv = *(const float *) ((const char *) weights->data + i0*nbw0 + ih*nbw1 + it*nbw2);
+ wv = 1.0f / (1.0f + expf(-gv));
+ } else {
+ wv = *(const float *) ((const char *) weights->data + ih*nbw0 + it*nbw1);
+ }
sum += xv * wv;
}
- *(float *) ((char *) dst->data + i0*nbd0 + it*nbd1) = sum;
+ *(float *) ((char *) dst->data + i0*nbd0 + it*nbd1) = scale * sum;
}
}
@@ -11171,7 +11336,6 @@ static void ggml_compute_forward_dsv4_hc_post_f32(
GGML_ASSERT(x->type == GGML_TYPE_F32);
GGML_ASSERT(residual->type == GGML_TYPE_F32);
GGML_ASSERT(post->type == GGML_TYPE_F32);
- GGML_ASSERT(comb->type == GGML_TYPE_F32);
GGML_ASSERT(dst->type == GGML_TYPE_F32);
const int64_t n_embd = x->ne[0];
@@ -11185,14 +11349,24 @@ static void ggml_compute_forward_dsv4_hc_post_f32(
GGML_ASSERT(residual->ne[2] == n_tokens);
GGML_ASSERT(post->ne[0] == hc);
GGML_ASSERT(post->ne[1] == n_tokens);
- GGML_ASSERT(comb->ne[0] == hc);
- GGML_ASSERT(comb->ne[1] == hc);
- GGML_ASSERT(comb->ne[2] == n_tokens);
+
+ // comb == NULL: identity mixing, each stream keeps its own residual
+ size_t nbc0 = 0;
+ size_t nbc1 = 0;
+ size_t nbc2 = 0;
+ if (comb) {
+ GGML_ASSERT(comb->type == GGML_TYPE_F32);
+ GGML_ASSERT(comb->ne[0] == hc);
+ GGML_ASSERT(comb->ne[1] == hc);
+ GGML_ASSERT(comb->ne[2] == n_tokens);
+ nbc0 = comb->nb[0];
+ nbc1 = comb->nb[1];
+ nbc2 = comb->nb[2];
+ }
GGML_TENSOR_LOCALS(size_t, nbx, x, nb);
GGML_TENSOR_LOCALS(size_t, nbr, residual, nb);
GGML_TENSOR_LOCALS(size_t, nbp, post, nb);
- GGML_TENSOR_LOCALS(size_t, nbc, comb, nb);
GGML_TENSOR_LOCALS(size_t, nbd, dst, nb);
const int ith = params->ith;
@@ -11212,10 +11386,14 @@ static void ggml_compute_forward_dsv4_hc_post_f32(
const float pv = *(const float *) ((const char *) post->data + idst*nbp0 + it*nbp1);
float sum = xv * pv;
- for (int64_t isrc = 0; isrc < hc; ++isrc) {
- const float rv = *(const float *) ((const char *) residual->data + i0*nbr0 + isrc*nbr1 + it*nbr2);
- const float cv = *(const float *) ((const char *) comb->data + idst*nbc0 + isrc*nbc1 + it*nbc2);
- sum += rv * cv;
+ if (comb) {
+ for (int64_t isrc = 0; isrc < hc; ++isrc) {
+ const float rv = *(const float *) ((const char *) residual->data + i0*nbr0 + isrc*nbr1 + it*nbr2);
+ const float cv = *(const float *) ((const char *) comb->data + idst*nbc0 + isrc*nbc1 + it*nbc2);
+ sum += rv * cv;
+ }
+ } else {
+ sum += *(const float *) ((const char *) residual->data + i0*nbr0 + idst*nbr1 + it*nbr2);
}
*(float *) ((char *) dst->data + i0*nbd0 + idst*nbd1 + it*nbd2) = sum;
@@ -11837,11 +12015,20 @@ void ggml_compute_forward_opt_step_sgd(const ggml_compute_params * params, ggml_
}
}
-static void ggml_compute_forward_fwht_f32(const ggml_compute_params * params, ggml_tensor * dst) {
+static inline float ggml_fwht_load(const float value) {
+ return value;
+}
+
+static inline float ggml_fwht_load(const ggml_fp16_t value) {
+ return ggml_fp16_to_fp32(value);
+}
+
+template
+static void ggml_compute_forward_fwht_impl(const ggml_compute_params * params, ggml_tensor * dst) {
const ggml_tensor * src0 = dst->src[0];
const ggml_tensor * src1 = dst->src[1];
- GGML_ASSERT(src1->type == GGML_TYPE_F32);
+ GGML_ASSERT(src1->type == (std::is_same_v ? GGML_TYPE_F32 : GGML_TYPE_F16));
GGML_ASSERT(dst->type == GGML_TYPE_F32);
GGML_TENSOR_BINARY_OP_LOCALS
@@ -11868,11 +12055,11 @@ static void ggml_compute_forward_fwht_f32(const ggml_compute_params * params, gg
const int64_t i12 = (r - i13 * ne11 * ne12) / ne11;
const int64_t i11 = r - i13 * ne11 * ne12 - i12 * ne11;
- const float * src_row = (const float *) ((const char *) src1->data + i11 * nb11 + i12 * nb12 + i13 * nb13);
+ const src_t * src_row = (const src_t *) ((const char *) src1->data + i11 * nb11 + i12 * nb12 + i13 * nb13);
float * dst_row = (float *) ((char *) dst->data + i11 * nb1 + i12 * nb2 + i13 * nb3);
for (int64_t j = 0; j < n; j++) {
- dst_row[j] = src_row[j] * scale;
+ dst_row[j] = ggml_fwht_load(src_row[j]) * scale;
}
// Scalar passes
@@ -11919,12 +12106,17 @@ void ggml_compute_forward_fwht(const ggml_compute_params * params, ggml_tensor *
switch (src1->type) {
case GGML_TYPE_F32:
{
- ggml_compute_forward_fwht_f32(params, dst);
+ ggml_compute_forward_fwht_impl(params, dst);
+ }
+ break;
+ case GGML_TYPE_F16:
+ {
+ ggml_compute_forward_fwht_impl(params, dst);
}
break;
default:
{
- GGML_ABORT("fatal error - fwht is F32 only");
+ GGML_ABORT("fatal error - fwht supports F32 and F16 input");
}
}
}
diff --git a/ggml/src/ggml-cpu/ops.h b/ggml/src/ggml-cpu/ops.h
index 4c1642a6..2728b08b 100644
--- a/ggml/src/ggml-cpu/ops.h
+++ b/ggml/src/ggml-cpu/ops.h
@@ -5,10 +5,10 @@
//
// cache line
//
-
-#if defined(__cpp_lib_hardware_interference_size)
-#define CACHE_LINE_SIZE std::hardware_destructive_interference_size
-#else
+// TODO: rework CACHE_LINE_SIZE so std::hardware_destructive_interference_size
+// can be used consistently between C and C++ TUs; the previous macro form
+// diverged based on include order and undersized the work buffer.
+// ref: https://github.com/ggml-org/llama.cpp/pull/28882
#if defined(__POWER9_VECTOR__)
#define CACHE_LINE_SIZE 128
#elif defined(__VXE__) || defined(__VXE2__)
@@ -16,7 +16,6 @@
#else
#define CACHE_LINE_SIZE 64
#endif
-#endif
static const size_t CACHE_LINE_SIZE_F32 = CACHE_LINE_SIZE/sizeof(float);
diff --git a/ggml/src/ggml-cpu/repack.cpp b/ggml/src/ggml-cpu/repack.cpp
index 9689ca3c..d56db980 100644
--- a/ggml/src/ggml-cpu/repack.cpp
+++ b/ggml/src/ggml-cpu/repack.cpp
@@ -1365,6 +1365,133 @@ void ggml_gemv_q8_0_4x8_q8_0_generic(int n,
}
}
+void ggml_gemv_q1_0_4x4_q8_0_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(nr == 1);
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(bs);
+ UNUSED(nr);
+
+ float sumf[4];
+
+ const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[j] = 0.0;
+ }
+
+ for (int l = 0; l < nb; l++) {
+ const float d0[4] = {
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[0]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[1]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[2]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[3]),
+ };
+
+ for (int k = 0; k < QK1_0 / QK8_0; ++k) {
+ const block_q8_0 * GGML_RESTRICT a_blk = a_ptr + l * (QK1_0 / QK8_0) + k;
+ const float d1 = GGML_CPU_FP16_TO_FP32(a_blk->d);
+ const float scale[4] = { d0[0] * d1, d0[1] * d1, d0[2] * d1, d0[3] * d1 };
+
+ for (int tile = 0; tile < QK8_0 / 4; ++tile) {
+ const uint8_t bits_lo = b_ptr[l].qs[k * 16 + 2 * tile + 0];
+ const uint8_t bits_hi = b_ptr[l].qs[k * 16 + 2 * tile + 1];
+
+ for (int p = 0; p < 4; ++p) {
+ const float q = (float) a_blk->qs[tile * 4 + p];
+
+ sumf[0] += ((bits_lo & (1u << p)) ? scale[0] : -scale[0]) * q;
+ sumf[1] += ((bits_lo & (1u << (4 + p))) ? scale[1] : -scale[1]) * q;
+ sumf[2] += ((bits_hi & (1u << p)) ? scale[2] : -scale[2]) * q;
+ sumf[3] += ((bits_hi & (1u << (4 + p))) ? scale[3] : -scale[3]) * q;
+ }
+ }
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[x * ncols_interleaved + j] = sumf[j];
+ }
+ }
+}
+
+void ggml_gemv_q1_0_4x8_q8_0_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+ const int blocklen = 8;
+
+ assert(nr == 1);
+ assert(n % qk == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ UNUSED(bs);
+ UNUSED(nr);
+
+ float sumf[4];
+
+ const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[j] = 0.0f;
+ }
+
+ for (int l = 0; l < nb; l++) {
+ const float d0[4] = {
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[0]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[1]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[2]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[3]),
+ };
+
+ for (int k = 0; k < qk / blocklen; ++k) {
+ const block_q8_0 * GGML_RESTRICT a_blk = a_ptr + l * (qk / QK8_0) + k / (QK8_0 / blocklen);
+ const float d1 = GGML_CPU_FP16_TO_FP32(a_blk->d);
+ const float scale[4] = { d0[0] * d1, d0[1] * d1, d0[2] * d1, d0[3] * d1 };
+ const uint8_t bits0 = b_ptr[l].qs[k * ncols_interleaved + 0];
+ const uint8_t bits1 = b_ptr[l].qs[k * ncols_interleaved + 1];
+ const uint8_t bits2 = b_ptr[l].qs[k * ncols_interleaved + 2];
+ const uint8_t bits3 = b_ptr[l].qs[k * ncols_interleaved + 3];
+ const int q_offset = (k % (QK8_0 / blocklen)) * blocklen;
+
+ for (int p = 0; p < blocklen; ++p) {
+ const float q = (float) a_blk->qs[q_offset + p];
+
+ sumf[0] += ((bits0 & (1u << p)) ? scale[0] : -scale[0]) * q;
+ sumf[1] += ((bits1 & (1u << p)) ? scale[1] : -scale[1]) * q;
+ sumf[2] += ((bits2 & (1u << p)) ? scale[2] : -scale[2]) * q;
+ sumf[3] += ((bits3 & (1u << p)) ? scale[3] : -scale[3]) * q;
+ }
+ }
+ }
+
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[x * ncols_interleaved + j] = sumf[j];
+ }
+ }
+}
+
// Only enable these for RISC-V.
#if defined __riscv_zvfh
void ggml_gemv_q4_0_16x1_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
@@ -2383,6 +2510,176 @@ void ggml_gemm_q8_0_4x8_q8_0_generic(int n,
}
}
+void ggml_gemm_q1_0_4x4_q8_0_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+
+ assert(n % qk == 0);
+ assert(nr % 4 == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ float sumf[4][4];
+
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (4 * y * nb);
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[m][j] = 0.0;
+ }
+ }
+
+ for (int l = 0; l < nb; l++) {
+ const float d0[4] = {
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[0]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[1]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[2]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[3]),
+ };
+
+ for (int k = 0; k < QK1_0 / QK8_0; ++k) {
+ const block_q8_0x4 * GGML_RESTRICT a_blk = a_ptr + 4 * l + k;
+ const float a_d[4] = {
+ GGML_CPU_FP16_TO_FP32(a_blk->d[0]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[1]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[2]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[3]),
+ };
+
+ for (int tile = 0; tile < QK8_0 / 4; ++tile) {
+ const uint8_t bits_lo = b_ptr[l].qs[k * 16 + 2 * tile + 0];
+ const uint8_t bits_hi = b_ptr[l].qs[k * 16 + 2 * tile + 1];
+ const int tile_offset = tile * 16;
+
+ for (int p = 0; p < 4; ++p) {
+ const int8_t q_row[4] = {
+ a_blk->qs[tile_offset + 0 * 4 + p],
+ a_blk->qs[tile_offset + 1 * 4 + p],
+ a_blk->qs[tile_offset + 2 * 4 + p],
+ a_blk->qs[tile_offset + 3 * 4 + p],
+ };
+ const int sign[4] = {
+ (bits_lo & (1u << p)) ? 1 : -1,
+ (bits_lo & (1u << (4 + p))) ? 1 : -1,
+ (bits_hi & (1u << p)) ? 1 : -1,
+ (bits_hi & (1u << (4 + p))) ? 1 : -1,
+ };
+
+ for (int m = 0; m < 4; ++m) {
+ const float row_scale = a_d[m];
+ sumf[m][0] += sign[0] * q_row[m] * d0[0] * row_scale;
+ sumf[m][1] += sign[1] * q_row[m] * d0[1] * row_scale;
+ sumf[m][2] += sign[2] * q_row[m] * d0[2] * row_scale;
+ sumf[m][3] += sign[3] * q_row[m] * d0[3] * row_scale;
+ }
+ }
+ }
+ }
+ }
+
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[(y * 4 + m) * bs + x * ncols_interleaved + j] = sumf[m][j];
+ }
+ }
+ }
+ }
+}
+
+void ggml_gemm_q1_0_4x8_q8_0_generic(int n,
+ float * GGML_RESTRICT s,
+ size_t bs,
+ const void * GGML_RESTRICT vx,
+ const void * GGML_RESTRICT vy,
+ int nr,
+ int nc) {
+ const int qk = QK1_0;
+ const int nb = n / qk;
+ const int ncols_interleaved = 4;
+ const int blocklen = 8;
+
+ assert(n % qk == 0);
+ assert(nr % 4 == 0);
+ assert(nc % ncols_interleaved == 0);
+
+ float sumf[4][4];
+
+ for (int y = 0; y < nr / 4; y++) {
+ const block_q8_0x4 * a_ptr = (const block_q8_0x4 *) vy + (4 * y * nb);
+ for (int x = 0; x < nc / ncols_interleaved; x++) {
+ const block_q1_0x4 * b_ptr = (const block_q1_0x4 *) vx + (x * nb);
+
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ sumf[m][j] = 0.0f;
+ }
+ }
+
+ for (int l = 0; l < nb; l++) {
+ const float d0[4] = {
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[0]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[1]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[2]),
+ GGML_CPU_FP16_TO_FP32(b_ptr[l].d[3]),
+ };
+
+ for (int k = 0; k < qk / blocklen; ++k) {
+ const block_q8_0x4 * GGML_RESTRICT a_blk = a_ptr + 4 * l + k / (QK8_0 / blocklen);
+ const float a_d[4] = {
+ GGML_CPU_FP16_TO_FP32(a_blk->d[0]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[1]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[2]),
+ GGML_CPU_FP16_TO_FP32(a_blk->d[3]),
+ };
+ const uint8_t bits0 = b_ptr[l].qs[k * ncols_interleaved + 0];
+ const uint8_t bits1 = b_ptr[l].qs[k * ncols_interleaved + 1];
+ const uint8_t bits2 = b_ptr[l].qs[k * ncols_interleaved + 2];
+ const uint8_t bits3 = b_ptr[l].qs[k * ncols_interleaved + 3];
+ const int q_offset = (k % (QK8_0 / blocklen)) * 4 * blocklen;
+
+ for (int p = 0; p < blocklen; ++p) {
+ const int8_t q_row[4] = {
+ a_blk->qs[q_offset + 0 * blocklen + p],
+ a_blk->qs[q_offset + 1 * blocklen + p],
+ a_blk->qs[q_offset + 2 * blocklen + p],
+ a_blk->qs[q_offset + 3 * blocklen + p],
+ };
+ const int sign[4] = {
+ (bits0 & (1u << p)) ? 1 : -1,
+ (bits1 & (1u << p)) ? 1 : -1,
+ (bits2 & (1u << p)) ? 1 : -1,
+ (bits3 & (1u << p)) ? 1 : -1,
+ };
+
+ for (int m = 0; m < 4; ++m) {
+ const float row_scale = a_d[m];
+ sumf[m][0] += sign[0] * q_row[m] * d0[0] * row_scale;
+ sumf[m][1] += sign[1] * q_row[m] * d0[1] * row_scale;
+ sumf[m][2] += sign[2] * q_row[m] * d0[2] * row_scale;
+ sumf[m][3] += sign[3] * q_row[m] * d0[3] * row_scale;
+ }
+ }
+ }
+ }
+
+ for (int m = 0; m < 4; m++) {
+ for (int j = 0; j < ncols_interleaved; j++) {
+ s[(y * 4 + m) * bs + x * ncols_interleaved + j] = sumf[m][j];
+ }
+ }
+ }
+ }
+}
+
// Only enable these for RISC-V.
#if defined __riscv_zvfh
void ggml_gemm_q4_0_16x1_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc) {
@@ -2739,6 +3036,50 @@ static block_q8_0x4 make_block_q8_0x4(block_q8_0 * in, unsigned int blck_size_in
return out;
}
+static block_q1_0x4 make_block_q1_0x4(block_q1_0 * in, unsigned int blck_size_interleave) {
+ block_q1_0x4 out;
+
+ for (int i = 0; i < 4; i++) {
+ out.d[i] = in[i].d;
+ }
+
+ GGML_ASSERT(blck_size_interleave == 4 || blck_size_interleave == 8);
+
+ if (blck_size_interleave == 4) {
+ for (int k = 0; k < QK1_0 / QK8_0; ++k) {
+ for (int tile = 0; tile < QK8_0 / 4; ++tile) {
+ uint8_t packed_lo = 0;
+ uint8_t packed_hi = 0;
+
+ const int weight_base = k * QK8_0 + tile * 4;
+ for (int pos = 0; pos < 4; ++pos) {
+ const int weight_idx = weight_base + pos;
+ const int byte_idx = weight_idx / 8;
+ const int bit_idx = weight_idx % 8;
+
+ packed_lo |= ((in[0].qs[byte_idx] >> bit_idx) & 1u) << pos;
+ packed_lo |= ((in[1].qs[byte_idx] >> bit_idx) & 1u) << (4 + pos);
+ packed_hi |= ((in[2].qs[byte_idx] >> bit_idx) & 1u) << pos;
+ packed_hi |= ((in[3].qs[byte_idx] >> bit_idx) & 1u) << (4 + pos);
+ }
+
+ out.qs[k * 16 + 2 * tile + 0] = packed_lo;
+ out.qs[k * 16 + 2 * tile + 1] = packed_hi;
+ }
+ }
+ return out;
+ }
+
+ for (int byte_idx = 0; byte_idx < QK1_0 / 8; ++byte_idx) {
+ out.qs[byte_idx * 4 + 0] = in[0].qs[byte_idx];
+ out.qs[byte_idx * 4 + 1] = in[1].qs[byte_idx];
+ out.qs[byte_idx * 4 + 2] = in[2].qs[byte_idx];
+ out.qs[byte_idx * 4 + 3] = in[3].qs[byte_idx];
+ }
+
+ return out;
+}
+
static block_q4_0x4 make_block_q4_0x4(block_q4_0 * in, int blck_size_interleave) {
block_q4_0x4 out;
@@ -3509,6 +3850,38 @@ static int repack_q8_0_to_q8_0_4_bl(struct ggml_tensor * t,
return 0;
}
+static int repack_q1_0_to_q1_0_4_bl(struct ggml_tensor * t,
+ int interleave_block,
+ const void * GGML_RESTRICT data,
+ size_t data_size) {
+ GGML_ASSERT(t->type == GGML_TYPE_Q1_0);
+ GGML_ASSERT(interleave_block == 4 || interleave_block == 8);
+ constexpr int nrows_interleaved = 4;
+
+ block_q1_0x4 * dst = (block_q1_0x4 *) t->data;
+ const block_q1_0 * src = (const block_q1_0 *) data;
+ block_q1_0 dst_tmp[4];
+ int nrow = ggml_nrows(t);
+ int nblocks = t->ne[0] / QK1_0;
+
+ GGML_ASSERT(data_size == nrow * nblocks * sizeof(block_q1_0));
+
+ if (t->ne[1] % nrows_interleaved != 0) {
+ return -1;
+ }
+
+ for (int b = 0; b < nrow; b += nrows_interleaved) {
+ for (int64_t x = 0; x < nblocks; x++) {
+ for (int i = 0; i < nrows_interleaved; i++) {
+ dst_tmp[i] = src[x + i * nblocks];
+ }
+ *dst++ = make_block_q1_0x4(dst_tmp, interleave_block);
+ }
+ src += nrows_interleaved * nblocks;
+ }
+ return 0;
+}
+
static block_q8_0x16 make_block_q8_0x16(block_q8_0 * in, unsigned int blck_size_interleave) {
block_q8_0x16 out;
@@ -3865,6 +4238,14 @@ template
int repack(struct ggml_tensor *, const void *, size_t);
// TODO: generalise.
+template <> int repack(struct ggml_tensor * t, const void * data, size_t data_size) {
+ return repack_q1_0_to_q1_0_4_bl(t, 4, data, data_size);
+}
+
+template <> int repack(struct ggml_tensor * t, const void * data, size_t data_size) {
+ return repack_q1_0_to_q1_0_4_bl(t, 8, data, data_size);
+}
+
template <> int repack(struct ggml_tensor * t, const void * data, size_t data_size) {
return repack_q4_0_to_q4_0_4_bl(t, 4, data, data_size);
}
@@ -3960,6 +4341,14 @@ template <> int repack(struct ggml_tensor * t, const void * d
template
void gemv(int, float *, size_t, const void *, const void *, int, int);
+template <> void gemv(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
+ ggml_gemv_q1_0_4x4_q8_0(n, s, bs, vx, vy, nr, nc);
+}
+
+template <> void gemv(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
+ ggml_gemv_q1_0_4x8_q8_0(n, s, bs, vx, vy, nr, nc);
+}
+
template <> void gemv(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
ggml_gemv_q4_0_4x4_q8_0(n, s, bs, vx, vy, nr, nc);
}
@@ -4057,6 +4446,14 @@ template <> void gemv(int n, float * s, size_
template
void gemm(int, float *, size_t, const void *, const void *, int, int);
+template <> void gemm(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
+ ggml_gemm_q1_0_4x4_q8_0(n, s, bs, vx, vy, nr, nc);
+}
+
+template <> void gemm(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
+ ggml_gemm_q1_0_4x8_q8_0(n, s, bs, vx, vy, nr, nc);
+}
+
template <> void gemm(int n, float * s, size_t bs, const void * vx, const void * vy, int nr, int nc) {
ggml_gemm_q4_0_4x4_q8_0(n, s, bs, vx, vy, nr, nc);
}
@@ -4526,6 +4923,10 @@ template q1_0_4x4_q8_0;
+ static const ggml::cpu::repack::tensor_traits q1_0_4x8_q8_0;
+
// instance for Q4
static const ggml::cpu::repack::tensor_traits q4_0_4x4_q8_0;
static const ggml::cpu::repack::tensor_traits q4_0_4x8_q8_0;
@@ -4586,6 +4987,11 @@ static const ggml::cpu::tensor_traits * ggml_repack_get_optimal_repack_type(cons
return &q4_0_4x4_q8_0;
}
}
+ if (ggml_cpu_has_vxe()) {
+ if (cur->ne[1] % 4 == 0) {
+ return &q4_0_4x4_q8_0;
+ }
+ }
if (ggml_cpu_has_riscv_v()) {
#if defined __riscv_zvfh
switch (__riscv_vlenb() * 8) {
@@ -4718,6 +5124,17 @@ static const ggml::cpu::tensor_traits * ggml_repack_get_optimal_repack_type(cons
}
#endif
}
+ } else if (cur->type == GGML_TYPE_Q1_0) {
+ if (ggml_cpu_has_neon() && ggml_cpu_has_matmul_int8()) {
+ if (cur->ne[1] % 4 == 0) {
+ return &q1_0_4x8_q8_0;
+ }
+ }
+ if (ggml_cpu_has_neon() && ggml_cpu_has_dotprod()) {
+ if (cur->ne[1] % 4 == 0) {
+ return &q1_0_4x4_q8_0;
+ }
+ }
}
return nullptr;
diff --git a/ggml/src/ggml-cpu/repack.h b/ggml/src/ggml-cpu/repack.h
index cb21edf6..fc6715c3 100644
--- a/ggml/src/ggml-cpu/repack.h
+++ b/ggml/src/ggml-cpu/repack.h
@@ -11,6 +11,9 @@
ggml_backend_buffer_type_t ggml_backend_cpu_repack_buffer_type(void);
template constexpr int QK_0() {
+ if constexpr (K == 1) {
+ return QK1_0;
+ }
if constexpr (K == 4) {
return QK4_0;
}
@@ -26,6 +29,7 @@ template struct block {
};
// control size
+static_assert(sizeof(block<1, 4>) == 4 * sizeof(ggml_half) + QK1_0 / 2, "wrong block<1,4> size/padding");
static_assert(sizeof(block<4, 4>) == 4 * sizeof(ggml_half) + QK8_0 * 2, "wrong block<4,4> size/padding");
static_assert(sizeof(block<4, 8>) == 8 * sizeof(ggml_half) + QK8_0 * 4, "wrong block<4,8> size/padding");
static_assert(sizeof(block<4, 16>) == 16 * sizeof(ggml_half) + QK8_0 * 8, "wrong block<4,16> size/padding");
@@ -33,6 +37,7 @@ static_assert(sizeof(block<8, 4>) == 4 * sizeof(ggml_half) + QK8_0 * 4, "wrong b
static_assert(sizeof(block<8, 8>) == 8 * sizeof(ggml_half) + QK8_0 * 8, "wrong block<8,8> size/padding");
static_assert(sizeof(block<8, 16>) == 16 * sizeof(ggml_half) + QK8_0 * 16, "wrong block<8,16> size/padding");
+using block_q1_0x4 = block<1, 4>;
using block_q4_0x4 = block<4, 4>;
using block_q4_0x8 = block<4, 8>;
using block_q4_0x16 = block<4, 16>;
@@ -141,6 +146,8 @@ void ggml_quantize_mat_q8_0_4x4(const float * GGML_RESTRICT x, void * GGML_RESTR
void ggml_quantize_mat_q8_0_4x8(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
void ggml_quantize_mat_q8_K_4x4(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
void ggml_quantize_mat_q8_K_4x8(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
+void ggml_gemv_q1_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemv_q1_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_8x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
@@ -157,6 +164,8 @@ void ggml_gemv_mxfp4_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const v
void ggml_gemv_mxfp4_8x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q8_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q8_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemm_q1_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemm_q1_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_4x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_8x8_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
@@ -193,6 +202,8 @@ void ggml_quantize_mat_q8_0_4x4_generic(const float * GGML_RESTRICT x, void * GG
void ggml_quantize_mat_q8_0_4x8_generic(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
void ggml_quantize_mat_q8_K_4x4_generic(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
void ggml_quantize_mat_q8_K_4x8_generic(const float * GGML_RESTRICT x, void * GGML_RESTRICT vy, int64_t k);
+void ggml_gemv_q1_0_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemv_q1_0_4x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_4x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q4_0_8x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
@@ -209,6 +220,8 @@ void ggml_gemv_mxfp4_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs,
void ggml_gemv_mxfp4_8x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q8_0_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemv_q8_0_4x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemm_q1_0_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
+void ggml_gemm_q1_0_4x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_4x4_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_4x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
void ggml_gemm_q4_0_8x8_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, const void * GGML_RESTRICT vy, int nr, int nc);
diff --git a/ggml/src/ggml-cpu/simd-mappings.h b/ggml/src/ggml-cpu/simd-mappings.h
index fca5119e..10ce4bfc 100644
--- a/ggml/src/ggml-cpu/simd-mappings.h
+++ b/ggml/src/ggml-cpu/simd-mappings.h
@@ -29,13 +29,15 @@ extern "C" {
// FP16 to FP32 conversion
// 16-bit float
-// on Arm, we use __fp16
+// on Arm, we use __fp16, which requires the IEEE fp16 format: implied on
+// AArch64, selected by -mfp16-format=ieee on 32 bit Arm, where the compiler
+// may otherwise reject the type
// on x86, we use uint16_t
//
// for old CUDA compilers (<= 11), we use uint16_t: ref https://github.com/ggml-org/llama.cpp/pull/10616
// for MUSA compilers , we use uint16_t: ref https://github.com/ggml-org/llama.cpp/pull/11843
//
-#if defined(__ARM_NEON) && !(defined(__CUDACC__) && __CUDACC_VER_MAJOR__ <= 11) && !defined(__MUSACC__)
+#if defined(__ARM_NEON) && defined(__ARM_FP16_FORMAT_IEEE) && !(defined(__CUDACC__) && __CUDACC_VER_MAJOR__ <= 11) && !defined(__MUSACC__)
#define GGML_CPU_COMPUTE_FP16_TO_FP32(x) neon_compute_fp16_to_fp32(x)
#define GGML_CPU_COMPUTE_FP32_TO_FP16(x) neon_compute_fp32_to_fp16(x)
@@ -326,7 +328,7 @@ inline static float ggml_lookup_fp16_to_fp32(ggml_fp16_t f) {
#define GGML_F16_VEC_REDUCE GGML_F32Cx4_REDUCE
#endif
-#elif defined(__ARM_NEON) && defined(__ARM_FEATURE_FMA)
+#elif defined(__ARM_NEON) && defined(__ARM_FEATURE_FMA) && defined(__ARM_FP16_FORMAT_IEEE)
#define GGML_SIMD
diff --git a/ggml/src/ggml-cpu/spacemit/rvv_kernels.cpp b/ggml/src/ggml-cpu/spacemit/rvv_kernels.cpp
index d2f89743..13b84dcb 100644
--- a/ggml/src/ggml-cpu/spacemit/rvv_kernels.cpp
+++ b/ggml/src/ggml-cpu/spacemit/rvv_kernels.cpp
@@ -639,7 +639,7 @@ static void permute_transpose_impl(const ggml_tensor * src0,
}
} else if (n_src_stride == sizeof(int16_t)) {
for (int64_t bi = ith; bi < batch; bi += nth) {
- rvv_transposed_s32_mn_to_nm((int8_t *) ((char *) dst->data + bi * batch_stride), n_dst_stride,
+ rvv_transposed_s16_mn_to_nm((int8_t *) ((char *) dst->data + bi * batch_stride), n_dst_stride,
(int8_t *) ((char *) src0->data + bi * batch_stride), m_src_stride, m, n);
}
} else {
diff --git a/ggml/src/ggml-cuda/CMakeLists.txt b/ggml/src/ggml-cuda/CMakeLists.txt
index d3953eee..2254090c 100644
--- a/ggml/src/ggml-cuda/CMakeLists.txt
+++ b/ggml/src/ggml-cuda/CMakeLists.txt
@@ -112,25 +112,14 @@ if (CUDAToolkit_FOUND)
file(GLOB SRCS "template-instances/mmf*.cu")
list(APPEND GGML_SOURCES_CUDA ${SRCS})
- if (GGML_CUDA_FA_ALL_QUANTS)
- file(GLOB SRCS "template-instances/fattn-vec*.cu")
- list(APPEND GGML_SOURCES_CUDA ${SRCS})
- add_compile_definitions(GGML_CUDA_FA_ALL_QUANTS)
- else()
- list(APPEND GGML_SOURCES_CUDA
- template-instances/fattn-vec-instance-f16-f16.cu
- template-instances/fattn-vec-instance-q4_0-q4_0.cu
- template-instances/fattn-vec-instance-q8_0-q8_0.cu
- template-instances/fattn-vec-instance-bf16-bf16.cu)
- endif()
+ ggml_cuda_fattn_vec_instances(${CMAKE_CURRENT_SOURCE_DIR} SRCS)
+ list(APPEND GGML_SOURCES_CUDA ${SRCS})
ggml_add_backend_library(ggml-cuda
${GGML_HEADERS_CUDA}
${GGML_SOURCES_CUDA}
)
- add_compile_definitions(GGML_CUDA_PEER_MAX_BATCH_SIZE=${GGML_CUDA_PEER_MAX_BATCH_SIZE})
-
if (GGML_CUDA_GRAPHS)
add_compile_definitions(GGML_CUDA_USE_GRAPHS)
endif()
diff --git a/ggml/src/ggml-cuda/allreduce.cu b/ggml/src/ggml-cuda/allreduce.cu
index d56129a2..39b23bed 100644
--- a/ggml/src/ggml-cuda/allreduce.cu
+++ b/ggml/src/ggml-cuda/allreduce.cu
@@ -1,6 +1,6 @@
#include "allreduce.cuh"
-#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
+#if !defined(GGML_USE_MUSA)
#include "convert.cuh"
#include "ggml-impl.h"
@@ -11,11 +11,12 @@
#include
// ---------------------------------------------------------------------------
-// CUDA AllReduce for tensor-parallel inference across two GPUs.
+// AllReduce for tensor-parallel inference across two GPUs (CUDA or
+// ROCm/HIP).
//
-// Provides an in-place sum reduction over matching tensors on two CUDA
-// devices in the same process. Used by the tensor-split path alongside
-// NCCL; targets setups without NVLink, where data is exchanged between the
+// Provides an in-place sum reduction over matching tensors on two GPUs
+// in the same process. Used by the tensor-split path alongside NCCL;
+// targets setups without NVLink/xGMI, where data is exchanged between the
// GPUs by staging it through pinned host memory over PCIe.
//
// Two reduction strategies are selected per call by tensor size:
@@ -161,11 +162,14 @@ static __global__ void ggml_cuda_ar_kernel(
__threadfence_system(); // make our signal visible system-wide
while (ggml_cuda_ar_signal_get(other_slot) != token) {
-#if __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA
+#ifdef GGML_USE_HIP
+ // Equals ~100ns at 2500 MHz (sleeps for n * [1,64] clock cycles)
+ __builtin_amdgcn_s_sleep(4);
+#elif __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA
__nanosleep(100);
#else
NO_DEVICE_CODE;
-#endif // __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA
+#endif // GGML_USE_HIP
}
}
@@ -280,7 +284,7 @@ struct ggml_cuda_ar_host_mapping {
}
rc = cudaHostGetDevicePointer(reinterpret_cast(&dev), host, 0);
if (rc != cudaSuccess) {
- cudaFreeHost(host);
+ CUDA_CHECK(cudaFreeHost(host));
host = nullptr;
dev = nullptr;
}
@@ -289,7 +293,7 @@ struct ggml_cuda_ar_host_mapping {
void free() {
if (host) {
- cudaFreeHost(host);
+ CUDA_CHECK(cudaFreeHost(host));
host = nullptr;
dev = nullptr;
}
@@ -401,7 +405,8 @@ ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int * devices, size_t n
return nullptr;
}
- // The chunked kernel uses __nanosleep, which is sm70+ (Volta+).
+ // The chunked kernel uses __nanosleep (NVIDIA, sm70+) or
+ // __builtin_amdgcn_s_sleep (AMD).
for (size_t i = 0; i < n_devices; ++i) {
const int cc = ggml_cuda_info().devices[devices[i]].cc;
if (cc < GGML_CUDA_CC_VOLTA) {
@@ -543,7 +548,7 @@ void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline * p) {
for (int i = 0; i < p->n_devices; ++i) {
if (p->streams[i]) {
ggml_cuda_set_device(p->devices[i]);
- cudaStreamSynchronize(p->streams[i]);
+ CUDA_CHECK(cudaStreamSynchronize(p->streams[i]));
}
}
@@ -552,28 +557,28 @@ void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline * p) {
p->host_large[i].free();
if (p->dev_tmp[i]) {
ggml_cuda_set_device(p->devices[i]);
- cudaFree(p->dev_tmp[i]);
+ CUDA_CHECK(cudaFree(p->dev_tmp[i]));
}
ggml_cuda_set_device(p->devices[i]);
for (int s = 0; s < GGML_CUDA_AR_POOL_SIZE; ++s) {
- if (p->ev_pool[i][s].app) { cudaEventDestroy(p->ev_pool[i][s].app); }
+ if (p->ev_pool[i][s].app) { CUDA_CHECK(cudaEventDestroy(p->ev_pool[i][s].app)); }
for (int c = 0; c < GGML_CUDA_AR_COPY_MAX_CHUNKS; ++c) {
- if (p->ev_pool[i][s].cpy[c]) { cudaEventDestroy(p->ev_pool[i][s].cpy[c]); }
+ if (p->ev_pool[i][s].cpy[c]) { CUDA_CHECK(cudaEventDestroy(p->ev_pool[i][s].cpy[c])); }
}
- if (p->ev_pool[i][s].h2d) { cudaEventDestroy(p->ev_pool[i][s].h2d); }
- if (p->ev_pool[i][s].ker) { cudaEventDestroy(p->ev_pool[i][s].ker); }
+ if (p->ev_pool[i][s].h2d) { CUDA_CHECK(cudaEventDestroy(p->ev_pool[i][s].h2d)); }
+ if (p->ev_pool[i][s].ker) { CUDA_CHECK(cudaEventDestroy(p->ev_pool[i][s].ker)); }
}
if (p->host_large_read_done[i]) {
ggml_cuda_set_device(p->devices[i]);
- cudaEventDestroy(p->host_large_read_done[i]);
+ CUDA_CHECK(cudaEventDestroy(p->host_large_read_done[i]));
}
if (p->dev_tmp_kernel_done[i]) {
ggml_cuda_set_device(p->devices[i]);
- cudaEventDestroy(p->dev_tmp_kernel_done[i]);
+ CUDA_CHECK(cudaEventDestroy(p->dev_tmp_kernel_done[i]));
}
if (p->streams[i]) {
ggml_cuda_set_device(p->devices[i]);
- cudaStreamDestroy(p->streams[i]);
+ CUDA_CHECK(cudaStreamDestroy(p->streams[i]));
}
}
p->arrival.free();
@@ -952,13 +957,14 @@ bool ggml_cuda_ar_allreduce(
return ok;
}
-#else // defined(GGML_USE_HIP) || defined(GGML_USE_MUSA)
+#else // defined(GGML_USE_MUSA)
-// HIP and MUSA lack the host-mapped pinned-memory APIs (cudaHostAllocPortable
-// / cudaHostAllocMapped / cudaHostGetDevicePointer) and __nanosleep that this
-// implementation relies on, so the internal AllReduce is a CUDA-only feature.
-// The dispatcher in ggml-cuda.cu treats a nullptr pipeline as "init failed"
-// and silently falls back to the meta backend's generic AllReduce.
+// MUSA lacks the host-mapped pinned-memory APIs (cudaHostAllocPortable
+// / cudaHostAllocMapped / cudaHostGetDevicePointer) and a device-side
+// sleep intrinsic that this implementation relies on, so the internal
+// AllReduce is unavailable there. The dispatcher in ggml-cuda.cu treats
+// a nullptr pipeline as "init failed" and silently falls back to the meta
+// backend's generic AllReduce.
ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int *, size_t) {
return nullptr;
}
@@ -968,4 +974,4 @@ bool ggml_cuda_ar_allreduce(ggml_cuda_ar_pipeline *, ggml_backend_t *, ggml_tens
return false;
}
-#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
+#endif // !defined(GGML_USE_MUSA)
diff --git a/ggml/src/ggml-cuda/allreduce.cuh b/ggml/src/ggml-cuda/allreduce.cuh
index 0f2c9518..76205d32 100644
--- a/ggml/src/ggml-cuda/allreduce.cuh
+++ b/ggml/src/ggml-cuda/allreduce.cuh
@@ -9,7 +9,7 @@
struct ggml_cuda_ar_pipeline;
// Allocate a pipeline for n_devices GPUs.
-// devices[] holds the CUDA device IDs in rank order.
+// devices[] holds the GPU device IDs in rank order.
// Returns nullptr on allocation failure.
ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(
const int * devices, size_t n_devices);
diff --git a/ggml/src/ggml-cuda/argsort.cu b/ggml/src/ggml-cuda/argsort.cu
index 26af9002..24115da0 100644
--- a/ggml/src/ggml-cuda/argsort.cu
+++ b/ggml/src/ggml-cuda/argsort.cu
@@ -51,9 +51,12 @@ void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
cudaStream_t stream) {
ggml_cuda_pool_alloc temp_indices_alloc(pool, ncols * nrows);
ggml_cuda_pool_alloc temp_keys_alloc(pool, ncols * nrows);
+ // Device*Sort algorithms currently do not allow for in-place sorting/aliasing of input/outputs
+ ggml_cuda_pool_alloc temp_keys_out_alloc(pool, ncols * nrows);
int * temp_indices = temp_indices_alloc.get();
float * temp_keys = temp_keys_alloc.get();
+ float * temp_keys_out = temp_keys_out_alloc.get();
static const int block_size = 256;
const dim3 grid_size((ncols + block_size - 1) / block_size, nrows);
@@ -85,18 +88,18 @@ void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
if (order == GGML_SORT_ORDER_ASC) {
if (nrows == 1) {
- CUDA_CHECK(DeviceRadixSort::SortPairs(nullptr, temp_storage_bytes, temp_keys, temp_keys, // keys (in-place)
+ CUDA_CHECK(DeviceRadixSort::SortPairs(nullptr, temp_storage_bytes, temp_keys, temp_keys_out, // keys in, keys out
temp_indices, dst, // values (indices)
ncols, 0, sizeof(float) * 8, stream));
} else if (is_capturing) {
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairs(
- nullptr, temp_storage_bytes, temp_keys, temp_keys, // keys (in-place)
+ nullptr, temp_storage_bytes, temp_keys, temp_keys_out, // keys in, keys out
temp_indices, dst, // values (indices)
ncols * nrows, nrows, // num items, num segments
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
} else {
CUDA_CHECK(DeviceSegmentedSort::SortPairs(nullptr, temp_storage_bytes, temp_keys,
- temp_keys, // keys (in-place)
+ temp_keys_out, // keys out
temp_indices, dst, // values (indices)
ncols * nrows, nrows, // num items, num segments
offset_iterator, offset_iterator + 1, stream));
@@ -104,15 +107,15 @@ void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
} else {
if (nrows == 1) {
CUDA_CHECK(DeviceRadixSort::SortPairsDescending(nullptr, temp_storage_bytes, temp_keys,
- temp_keys, // keys (in-place)
+ temp_keys_out, // keys out
temp_indices, dst, // values (indices)
ncols, 0, sizeof(float) * 8, stream));
} else if (is_capturing) {
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairsDescending(
- nullptr, temp_storage_bytes, temp_keys, temp_keys, temp_indices, dst, ncols * nrows, nrows,
+ nullptr, temp_storage_bytes, temp_keys, temp_keys_out, temp_indices, dst, ncols * nrows, nrows,
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
} else {
- CUDA_CHECK(DeviceSegmentedSort::SortPairsDescending(nullptr, temp_storage_bytes, temp_keys, temp_keys,
+ CUDA_CHECK(DeviceSegmentedSort::SortPairsDescending(nullptr, temp_storage_bytes, temp_keys, temp_keys_out,
temp_indices, dst, ncols * nrows, nrows,
offset_iterator, offset_iterator + 1, stream));
}
@@ -124,31 +127,31 @@ void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
if (order == GGML_SORT_ORDER_ASC) {
if (nrows == 1) {
CUDA_CHECK(DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys,
- temp_keys, // keys (in-place)
+ temp_keys_out, // keys out
temp_indices, dst, // values (indices)
ncols, 0, sizeof(float) * 8, stream));
} else if (is_capturing) {
- CUDA_CHECK(DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys,
+ CUDA_CHECK(DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys_out,
temp_indices, dst, ncols * nrows, nrows, offset_iterator,
offset_iterator + 1, 0, sizeof(float) * 8, stream));
} else {
- CUDA_CHECK(DeviceSegmentedSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys,
+ CUDA_CHECK(DeviceSegmentedSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys_out,
temp_indices, dst, ncols * nrows, nrows, offset_iterator,
offset_iterator + 1, stream));
}
} else {
if (nrows == 1) {
CUDA_CHECK(DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, temp_keys,
- temp_keys, // keys (in-place)
+ temp_keys_out, // keys out
temp_indices, dst, // values (indices)
ncols, 0, sizeof(float) * 8, stream));
} else if (is_capturing) {
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairsDescending(
- d_temp_storage, temp_storage_bytes, temp_keys, temp_keys, temp_indices, dst, ncols * nrows, nrows,
+ d_temp_storage, temp_storage_bytes, temp_keys, temp_keys_out, temp_indices, dst, ncols * nrows, nrows,
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
} else {
CUDA_CHECK(DeviceSegmentedSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, temp_keys,
- temp_keys, temp_indices, dst, ncols * nrows, nrows,
+ temp_keys_out, temp_indices, dst, ncols * nrows, nrows,
offset_iterator, offset_iterator + 1, stream));
}
}
diff --git a/ggml/src/ggml-cuda/common.cuh b/ggml/src/ggml-cuda/common.cuh
index d27d8acb..2e78ae4f 100644
--- a/ggml/src/ggml-cuda/common.cuh
+++ b/ggml/src/ggml-cuda/common.cuh
@@ -52,6 +52,7 @@
#define GGML_CUDA_CC_VOLTA 700
#define GGML_CUDA_CC_TURING 750
#define GGML_CUDA_CC_AMPERE 800
+#define GGML_CUDA_CC_ORIN 870
#define GGML_CUDA_CC_ADA_LOVELACE 890
#define GGML_CUDA_CC_HOPPER 900
// While BW spans CC 1000, 1100 & 1200, we are integrating Tensor Core instructions available to 1200 family, see
@@ -68,6 +69,8 @@
#define GGML_CUDA_CC_GCN4 (GGML_CUDA_CC_OFFSET_AMD + 0x803) // Tonga, Fiji, Polaris, minimum for fast fp16
#define GGML_CUDA_CC_VEGA (GGML_CUDA_CC_OFFSET_AMD + 0x900) // Vega56/64, minimum for fp16 dual issue
#define GGML_CUDA_CC_VEGA20 (GGML_CUDA_CC_OFFSET_AMD + 0x906) // MI50/Radeon VII, minimum for dp4a
+#define GGML_CUDA_CC_GFX909 (GGML_CUDA_CC_OFFSET_AMD + 0x909) // GCN APU
+#define GGML_CUDA_CC_GFX90C (GGML_CUDA_CC_OFFSET_AMD + 0x90c) // GCN APU
#define GGML_CUDA_CC_CDNA1 (GGML_CUDA_CC_OFFSET_AMD + 0x908) // MI100, minimum for MFMA, acc registers
#define GGML_CUDA_CC_CDNA2 (GGML_CUDA_CC_OFFSET_AMD + 0x90a) // MI210 (gfx90a), minimum acc register renaming
#define GGML_CUDA_CC_CDNA3 (GGML_CUDA_CC_OFFSET_AMD + 0x942) // MI300
@@ -88,12 +91,13 @@
#define GGML_CUDA_CC_IS_RDNA3_5(cc) (cc >= GGML_CUDA_CC_RDNA3_5 && cc < GGML_CUDA_CC_RDNA4)
#define GGML_CUDA_CC_IS_RDNA3(cc) (GGML_CUDA_CC_IS_RDNA3_0(cc) || GGML_CUDA_CC_IS_RDNA3_5(cc))
#define GGML_CUDA_CC_IS_RDNA4(cc) (cc >= GGML_CUDA_CC_RDNA4)
-#define GGML_CUDA_CC_IS_GCN(cc) (cc > GGML_CUDA_CC_OFFSET_AMD && cc < GGML_CUDA_CC_CDNA1)
-#define GGML_CUDA_CC_IS_CDNA(cc) (cc >= GGML_CUDA_CC_CDNA1 && cc < GGML_CUDA_CC_RDNA1)
-#define GGML_CUDA_CC_IS_CDNA1(cc) (cc >= GGML_CUDA_CC_CDNA1 && cc < GGML_CUDA_CC_CDNA2)
-#define GGML_CUDA_CC_IS_CDNA2(cc) (cc >= GGML_CUDA_CC_CDNA2 && cc < GGML_CUDA_CC_CDNA3)
-#define GGML_CUDA_CC_IS_CDNA3(cc) (cc >= GGML_CUDA_CC_CDNA3 && cc < GGML_CUDA_CC_CDNA4)
-#define GGML_CUDA_CC_IS_CDNA4(cc) (cc >= GGML_CUDA_CC_CDNA4 && cc < GGML_CUDA_CC_RDNA1)
+#define GGML_CUDA_CC_IS_GCN_APU(cc) ((cc) == GGML_CUDA_CC_GFX909 || (cc) == GGML_CUDA_CC_GFX90C)
+#define GGML_CUDA_CC_IS_GCN(cc) ((cc > GGML_CUDA_CC_OFFSET_AMD && cc < GGML_CUDA_CC_CDNA1) || GGML_CUDA_CC_IS_GCN_APU(cc))
+#define GGML_CUDA_CC_IS_CDNA(cc) (!GGML_CUDA_CC_IS_GCN_APU(cc) && cc >= GGML_CUDA_CC_CDNA1 && cc < GGML_CUDA_CC_RDNA1)
+#define GGML_CUDA_CC_IS_CDNA1(cc) (GGML_CUDA_CC_IS_CDNA(cc) && cc >= GGML_CUDA_CC_CDNA1 && cc < GGML_CUDA_CC_CDNA2)
+#define GGML_CUDA_CC_IS_CDNA2(cc) (GGML_CUDA_CC_IS_CDNA(cc) && cc >= GGML_CUDA_CC_CDNA2 && cc < GGML_CUDA_CC_CDNA3)
+#define GGML_CUDA_CC_IS_CDNA3(cc) (GGML_CUDA_CC_IS_CDNA(cc) && cc >= GGML_CUDA_CC_CDNA3 && cc < GGML_CUDA_CC_CDNA4)
+#define GGML_CUDA_CC_IS_CDNA4(cc) (GGML_CUDA_CC_IS_CDNA(cc) && cc >= GGML_CUDA_CC_CDNA4 && cc < GGML_CUDA_CC_RDNA1)
// Moore Threads
#define MUSART_HMASK 40300 // MUSA rc4.3, min. ver. for half2 -> uint mask comparisons
@@ -120,6 +124,12 @@
# define GGML_CUDA_USE_PDL
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) && (CUDART_VERSION >= 12030 || (!(defined(_MSC_VER) && !defined(__clang__)) && CUDART_VERSION >= 11080))
+static __device__ __forceinline__ void ggml_cuda_syncwarp() {
+#ifndef GGML_USE_HIP
+ __syncwarp();
+#endif // GGML_USE_HIP
+}
+
static __device__ __forceinline__ void ggml_cuda_pdl_sync() {
#if defined(GGML_CUDA_USE_PDL) && defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= GGML_CUDA_CC_HOPPER
cudaGridDependencySynchronize();
@@ -319,6 +329,12 @@ static bool fp16_mma_hardware_available(const int cc) {
(GGML_CUDA_CC_IS_MTHREADS(cc) && cc >= GGML_CUDA_CC_QY2);
}
+// To be used for feature selection of external libraries, e.g. cuBLAS.
+static bool fast_bf16_hardware_available(const int cc) {
+ return (GGML_CUDA_CC_IS_AMD(cc) && (cc >= GGML_CUDA_CC_RDNA3 || GGML_CUDA_CC_IS_CDNA(cc)))
+ || (GGML_CUDA_CC_IS_NVIDIA(cc) && cc >= GGML_CUDA_CC_AMPERE);
+}
+
static bool bf16_mma_hardware_available(const int cc) {
return (GGML_CUDA_CC_IS_NVIDIA(cc) && cc >= GGML_CUDA_CC_AMPERE) ||
GGML_CUDA_CC_IS_CDNA(cc) || cc >= GGML_CUDA_CC_RDNA3 ||
@@ -969,6 +985,7 @@ template<>
struct ggml_cuda_type_traits {
static constexpr int qk = 1;
static constexpr int qr = 1;
+ static constexpr int bs = sizeof(ggml_half);
};
template<>
@@ -976,6 +993,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK1_0;
static constexpr int qr = QR1_0;
static constexpr int qi = QI1_0;
+ static constexpr int bs = sizeof(block_q1_0);
};
template<>
@@ -983,6 +1001,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK2_0;
static constexpr int qr = QR2_0;
static constexpr int qi = QI2_0;
+ static constexpr int bs = sizeof(block_q2_0);
};
template<>
@@ -990,6 +1009,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK4_0;
static constexpr int qr = QR4_0;
static constexpr int qi = QI4_0;
+ static constexpr int bs = sizeof(block_q4_0);
};
template<>
@@ -997,6 +1017,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK4_1;
static constexpr int qr = QR4_1;
static constexpr int qi = QI4_1;
+ static constexpr int bs = sizeof(block_q4_1);
};
template<>
@@ -1004,6 +1025,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK5_0;
static constexpr int qr = QR5_0;
static constexpr int qi = QI5_0;
+ static constexpr int bs = sizeof(block_q5_0);
};
template<>
@@ -1011,6 +1033,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK5_1;
static constexpr int qr = QR5_1;
static constexpr int qi = QI5_1;
+ static constexpr int bs = sizeof(block_q5_1);
};
template<>
@@ -1018,6 +1041,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK8_0;
static constexpr int qr = QR8_0;
static constexpr int qi = QI8_0;
+ static constexpr int bs = sizeof(block_q8_0);
};
template<>
@@ -1025,6 +1049,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_MXFP4;
static constexpr int qr = QR_MXFP4;
static constexpr int qi = QI_MXFP4;
+ static constexpr int bs = sizeof(block_mxfp4);
};
template<>
@@ -1032,6 +1057,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_NVFP4;
static constexpr int qr = QR_NVFP4;
static constexpr int qi = QI_NVFP4;
+ static constexpr int bs = sizeof(block_nvfp4);
};
template<>
@@ -1039,6 +1065,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR2_K;
static constexpr int qi = QI2_K;
+ static constexpr int bs = sizeof(block_q2_K);
};
template<>
@@ -1046,6 +1073,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR3_K;
static constexpr int qi = QI3_K;
+ static constexpr int bs = sizeof(block_q3_K);
};
template<>
@@ -1053,6 +1081,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR4_K;
static constexpr int qi = QI4_K;
+ static constexpr int bs = sizeof(block_q4_K);
};
template<>
@@ -1060,6 +1089,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR5_K;
static constexpr int qi = QI5_K;
+ static constexpr int bs = sizeof(block_q5_K);
};
template<>
@@ -1067,6 +1097,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR6_K;
static constexpr int qi = QI6_K;
+ static constexpr int bs = sizeof(block_q6_K);
};
template<>
@@ -1074,6 +1105,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR2_XXS;
static constexpr int qi = QI2_XXS;
+ static constexpr int bs = sizeof(block_iq2_xxs);
};
template<>
@@ -1081,6 +1113,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR2_XS;
static constexpr int qi = QI2_XS;
+ static constexpr int bs = sizeof(block_iq2_xs);
};
template<>
@@ -1088,6 +1121,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR2_S;
static constexpr int qi = QI2_S;
+ static constexpr int bs = sizeof(block_iq2_s);
};
template<>
@@ -1095,6 +1129,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR3_XXS;
static constexpr int qi = QI3_XXS;
+ static constexpr int bs = sizeof(block_iq3_xxs);
};
template<>
@@ -1102,6 +1137,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR1_S;
static constexpr int qi = QI1_S;
+ static constexpr int bs = sizeof(block_iq1_s);
};
template<>
@@ -1109,6 +1145,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR1_M;
static constexpr int qi = QI1_M;
+ static constexpr int bs = sizeof(block_iq1_m);
};
template<>
@@ -1116,6 +1153,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK4_NL;
static constexpr int qr = QR4_NL;
static constexpr int qi = QI4_NL;
+ static constexpr int bs = sizeof(block_iq4_nl);
};
template<>
@@ -1123,6 +1161,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR4_XS;
static constexpr int qi = QI4_XS;
+ static constexpr int bs = sizeof(block_iq4_xs);
};
template<>
@@ -1130,6 +1169,7 @@ struct ggml_cuda_type_traits {
static constexpr int qk = QK_K;
static constexpr int qr = QR3_S;
static constexpr int qi = QI3_S;
+ static constexpr int bs = sizeof(block_iq3_s);
};
//////////////////////
@@ -1418,7 +1458,9 @@ struct ggml_backend_cuda_context {
cudaEvent_t copy_event = nullptr;
cudaStream_t streams[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS] = { { nullptr } };
- cublasHandle_t cublas_handles[GGML_CUDA_MAX_DEVICES] = {nullptr};
+ cublasHandle_t cublas_handles[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS] = {nullptr};
+ void * cublas_workspaces[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS] = {nullptr};
+ size_t cublas_workspace_sizes[GGML_CUDA_MAX_DEVICES] = {0};
int curr_stream_no = 0;
@@ -1495,17 +1537,22 @@ struct ggml_backend_cuda_context {
ggml_cuda_stream_context & stream_context() { return concurrent_stream_context; }
- cublasHandle_t cublas_handle(int device) {
- if (cublas_handles[device] == nullptr) {
+ cublasHandle_t cublas_handle() {
+ if (cublas_handles[device][curr_stream_no] == nullptr) {
ggml_cuda_set_device(device);
- CUBLAS_CHECK(cublasCreate(&cublas_handles[device]));
- CUBLAS_CHECK(cublasSetMathMode(cublas_handles[device], CUBLAS_TF32_TENSOR_OP_MATH));
+ CUBLAS_CHECK(cublasCreate(&cublas_handles[device][curr_stream_no]));
+ CUBLAS_CHECK(cublasSetMathMode(cublas_handles[device][curr_stream_no], CUBLAS_TF32_TENSOR_OP_MATH));
+ CUBLAS_CHECK(cublasSetStream(cublas_handles[device][curr_stream_no], stream()));
+#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) && (CUBLAS_VER_MAJOR > 11 || (CUBLAS_VER_MAJOR == 11 && CUBLAS_VER_MINOR >= 2))
+ if (cublas_workspace_sizes[device] == 0) {
+ const int cc = ggml_cuda_info().devices[device].cc;
+ cublas_workspace_sizes[device] = (cc >= GGML_CUDA_CC_HOPPER) ? 32 * 1024 * 1024 : 4 * 1024 * 1024;
+ }
+ CUDA_CHECK(cudaMalloc(&cublas_workspaces[device][curr_stream_no], cublas_workspace_sizes[device]));
+ CUBLAS_CHECK(cublasSetWorkspace(cublas_handles[device][curr_stream_no], cublas_workspaces[device][curr_stream_no], cublas_workspace_sizes[device]));
+#endif
}
- return cublas_handles[device];
- }
-
- cublasHandle_t cublas_handle() {
- return cublas_handle(device);
+ return cublas_handles[device][curr_stream_no];
}
// pool
@@ -1532,6 +1579,7 @@ struct ggml_cuda_mm_fusion_args_host {
const ggml_tensor * x_scale = nullptr;
const ggml_tensor * gate_scale = nullptr;
ggml_glu_op glu_op;
+ float glu_limit = 0.0f;
};
struct ggml_cuda_mm_fusion_args_device {
const void * x_bias = nullptr;
@@ -1540,6 +1588,7 @@ struct ggml_cuda_mm_fusion_args_device {
const void * x_scale = nullptr;
const void * gate_scale = nullptr;
ggml_glu_op glu_op;
+ float glu_limit = 0.0f;
};
struct ggml_cuda_kernel_launch_params {
@@ -1666,4 +1715,3 @@ static __inline__ void ggml_cuda_kernel_launch(Kernel kernel, const ggml_cuda_ke
kernel<<>>(std::forward(args)... );
CUDA_CHECK(cudaGetLastError());
}
-
diff --git a/ggml/src/ggml-cuda/conv2d.cu b/ggml/src/ggml-cuda/conv2d.cu
index 14774d4a..10109ad3 100644
--- a/ggml/src/ggml-cuda/conv2d.cu
+++ b/ggml/src/ggml-cuda/conv2d.cu
@@ -1,5 +1,6 @@
#include "conv2d.cuh"
#include "convert.cuh"
+#include "mma.cuh"
struct conv_params {
const int64_t IW, IH;
@@ -111,6 +112,220 @@ static void conv2d_cuda(const float * X_D, const T * K_D, float * Y_D, const con
conv2d_kernel<<>>(X_D, K_D, Y_D, P);
}
+static __global__ void
+conv2d_pad_f16(const float * input, half * output, int iw, int ih, int pw, int ph, int px, int py, int total) {
+ const int i = blockIdx.x * blockDim.x + threadIdx.x;
+ if (i >= total) {
+ return;
+ }
+ const int x = i % pw - px, y = i / pw % ph - py, nc = i / (pw * ph);
+ output[i] = __float2half(
+ (unsigned) x < (unsigned) iw && (unsigned) y < (unsigned) ih ? input[(nc * ih + y) * iw + x] : 0.0f);
+}
+
+template
+static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
+ const half * __restrict__ weight,
+ float * __restrict__ output,
+ const conv_params P,
+ const int split_k) {
+ using namespace ggml_cuda_mma;
+ constexpr int warp_size = ggml_cuda_get_physical_warp_size();
+ constexpr int nthreads = 4 * warp_size;
+ constexpr int BM = 64, BN = 64, BK = 64;
+ constexpr int AS = BK / 2 + 4;
+ constexpr int BS = BN / 2 + 4;
+ __shared__ __align__(16) half2 a_s[BM][AS];
+ __shared__ __align__(16) half2 b_s[BK][BS];
+
+ const int tid = threadIdx.y * warp_size + threadIdx.x;
+ const int iw = int(P.IW), ih = int(P.IH), ow = int(P.OW), oh = int(P.OH);
+ const int kw = KW ? KW : int(P.KW), kh = KH ? KH : int(P.KH);
+ const int ic = int(P.IC), oc = int(P.OC);
+ const int sx = int(P.ST_X), sy = int(P.ST_Y);
+ const int dx = int(P.DL_X), dy = int(P.DL_Y);
+ const int n = blockIdx.z / split_k, split = blockIdx.z % split_k;
+ const int m0 = blockIdx.y * BM, n0 = blockIdx.x * BN;
+
+ const int k_total = ic * kw * kh;
+ const int load_lane = warp_size == 32 ? threadIdx.x : threadIdx.x % (BN / 2);
+ const int load_row = threadIdx.y * (warp_size / (BN / 2)) + (warp_size == 32 ? 0 : threadIdx.x / (BN / 2));
+ const int spatial = n0 + 2 * load_lane;
+ const int spatial0 = min(spatial, ow * oh - 1), spatial1 = min(spatial + 1, ow * oh - 1);
+ const int y0 = spatial0 / ow, x0 = spatial0 % ow;
+ const int y1 = spatial1 / ow, x1 = spatial1 % ow;
+ const int pos0 = y0 * sy * iw + x0 * sx, pos1 = y1 * sy * iw + x1 * sx;
+
+ [[maybe_unused]] const int wm = threadIdx.y / 2 * 32, wn = threadIdx.y % 2 * 32;
+#if defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
+ using tile_ab = tile<16, 8, half2, get_input_data_layout()>;
+# if defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
+ // AMD accumulator fragments transpose the input fragment's row/column mapping.
+ using tile_c = tile<16, 16, float, DATA_LAYOUT_J_MAJOR>;
+# else
+ using tile_c = tile<16, 16, float>;
+# endif
+ [[maybe_unused]] tile_c c[2][2];
+#else
+ if constexpr (use_mma) {
+ NO_DEVICE_CODE;
+ return;
+ }
+#endif
+ constexpr int RM = 4, RN = BM * BN / (nthreads * RM);
+ [[maybe_unused]] const int simt_m = tid / (BN / RN) * RM, simt_n = tid % (BN / RN) * RN;
+ [[maybe_unused]] float c_simt[RM][RN] = {};
+ const int tiles = (k_total + BK - 1) / BK;
+ const int begin = int(int64_t(tiles) * split / split_k) * BK;
+ const int end = int(int64_t(tiles) * (split + 1) / split_k) * BK;
+ for (int k0 = begin; k0 < end; k0 += BK) {
+ if (k_total % 8 == 0 && uintptr_t(weight) % 16 == 0) {
+#pragma unroll
+ for (int i = tid; i < BM * BK / 8; i += nthreads) {
+ const int row = i / (BK / 8), col = 8 * (i % (BK / 8));
+ const int4 v = m0 + row < oc && k0 + col < k_total ?
+ ((const int4 *) weight)[((m0 + row) * k_total + k0 + col) / 8] :
+ make_int4(0, 0, 0, 0);
+ *(int4 *) &a_s[row][col / 2] = v;
+ }
+ } else {
+#pragma unroll
+ for (int i = tid; i < BM * BK / 2; i += nthreads) {
+ const int row = i / (BK / 2), col = 2 * (i % (BK / 2));
+ half lo = __float2half(0.0f), hi = lo;
+ if (m0 + row < oc && k0 + col < k_total) {
+ lo = weight[(m0 + row) * k_total + k0 + col];
+ if (k0 + col + 1 < k_total) {
+ hi = weight[(m0 + row) * k_total + k0 + col + 1];
+ }
+ }
+ a_s[row][col / 2] = __halves2half2(lo, hi);
+ }
+ }
+#pragma unroll
+ for (int k = load_row; k < BK; k += nthreads / (BN / 2)) {
+ const int ki = k0 + k;
+ const int ci = ki / (kw * kh), ky = ki / kw % kh, kx = ki % kw;
+ const int offset = ki < k_total ? (n * ic + ci) * ih * iw + ky * dy * iw + kx * dx : 0;
+ half lo = __float2half(0.0f), hi = lo;
+ if (ki < k_total && spatial < ow * oh) {
+ lo = input[offset + pos0];
+ }
+ if (ki < k_total && spatial + 1 < ow * oh) {
+ hi = input[offset + pos1];
+ }
+ b_s[k][load_lane] = __halves2half2(lo, hi);
+ }
+ __syncthreads();
+ if constexpr (use_mma) {
+#if defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
+# pragma unroll
+ for (int k = 0; k < BK; k += 16) {
+ tile_ab a[2], b[2];
+# pragma unroll
+ for (int i = 0; i < 2; ++i) {
+ load_ldmatrix(a[i], &a_s[wm + 16 * i][k / 2], AS);
+ load_ldmatrix_trans(b[i], &b_s[k][(wn + 16 * i) / 2], BS);
+ }
+# pragma unroll
+ for (int i = 0; i < 2; ++i) {
+# pragma unroll
+ for (int j = 0; j < 2; ++j) {
+ mma(c[i][j], a[i], b[j]);
+ }
+ }
+ }
+#endif
+ } else {
+#pragma unroll 4
+ for (int k = 0; k < BK; ++k) {
+ float a[RM], b[RN];
+#pragma unroll
+ for (int i = 0; i < RM; ++i) {
+ a[i] = __half2float(((const half *) a_s[simt_m + i])[k]);
+ }
+#pragma unroll
+ for (int j = 0; j < RN; ++j) {
+ b[j] = __half2float(((const half *) b_s[k])[simt_n + j]);
+ }
+#pragma unroll
+ for (int i = 0; i < RM; ++i) {
+#pragma unroll
+ for (int j = 0; j < RN; ++j) {
+ c_simt[i][j] += a[i] * b[j];
+ }
+ }
+ }
+ }
+ __syncthreads();
+ }
+ if constexpr (use_mma) {
+#if defined(TURING_MMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
+# pragma unroll
+ for (int i = 0; i < 2; ++i) {
+# pragma unroll
+ for (int j = 0; j < 2; ++j) {
+# pragma unroll
+ for (int l = 0; l < c[i][j].ne; ++l) {
+ const int co = m0 + wm + 16 * i + c[i][j].get_i(l);
+ const int pos = n0 + wn + 16 * j + c[i][j].get_j(l);
+ if (co < oc && pos < ow * oh) {
+ output[(int64_t(blockIdx.z) * oc + co) * ow * oh + pos] = c[i][j].x[l];
+ }
+ }
+ }
+ }
+#endif
+ } else {
+#pragma unroll
+ for (int i = 0; i < RM; ++i) {
+#pragma unroll
+ for (int j = 0; j < RN; ++j) {
+ const int co = m0 + simt_m + i, pos = n0 + simt_n + j;
+ if (co < oc && pos < ow * oh) {
+ output[(int64_t(blockIdx.z) * oc + co) * ow * oh + pos] = c_simt[i][j];
+ }
+ }
+ }
+ }
+}
+
+static __global__ void conv2d_reduce_split_k(const float * __restrict__ partial,
+ float * __restrict__ output,
+ const int total,
+ const int per_batch,
+ const int split_k) {
+ const int i = blockIdx.x * blockDim.x + threadIdx.x;
+ if (i >= total) {
+ return;
+ }
+ const int n = i / per_batch;
+ const float * src = partial + int64_t(n) * (split_k - 1) * per_batch + i;
+ float sum = 0.0f;
+ for (int k = 0; k < split_k; ++k) {
+ sum += src[int64_t(k) * per_batch];
+ }
+ output[i] = sum;
+}
+
+template
+static void conv2d_launch_implicit_gemm(const half * input,
+ const half * weight,
+ float * output,
+ const conv_params & params,
+ int split_k,
+ dim3 grid,
+ dim3 block,
+ cudaStream_t stream) {
+ if (params.KW == 3 && params.KH == 3) {
+ conv2d_implicit_gemm_f16<3, 3, use_mma><<>>(input, weight, output, params, split_k);
+ } else if (params.KW == 1 && params.KH == 1) {
+ conv2d_implicit_gemm_f16<1, 1, use_mma><<>>(input, weight, output, params, split_k);
+ } else {
+ conv2d_implicit_gemm_f16<0, 0, use_mma><<>>(input, weight, output, params, split_k);
+ }
+}
+
static void conv2d_cuda_f16(const float * X_D, const half * K_D, float * Y_D, const conv_params P, cudaStream_t st) {
conv2d_cuda(X_D, K_D, Y_D, P, st);
}
@@ -126,6 +341,7 @@ void ggml_cuda_op_conv2d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
const float * X_D = (const float *) input->data;
float * Y_D = (float *) dst->data;
+ GGML_ASSERT(input->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32);
GGML_ASSERT(ggml_is_contiguous(input));
GGML_ASSERT(ggml_is_contiguous(kernel));
GGML_ASSERT(kernel->type == GGML_TYPE_F16 || kernel->type == GGML_TYPE_F32);
@@ -146,19 +362,86 @@ void ggml_cuda_op_conv2d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
// No cwhn
GGML_ASSERT(p[6] == false);
- const int IW = input->ne[0]; // input_w
- const int IH = input->ne[1]; // input_h
- const int OW = dst->ne[0]; // output_w
- const int OH = dst->ne[1]; // output_h
- const int KW = kernel->ne[0]; // kernel_w
- const int KH = kernel->ne[1]; // kernel_h
- const int IC = input->ne[2]; // input_channels
- const int OC = kernel->ne[3]; // ouptut_chanles
- const int B = input->ne[3]; // n_batches
+ const int64_t IW = input->ne[0]; // input_w
+ const int64_t IH = input->ne[1]; // input_h
+ const int64_t OW = dst->ne[0]; // output_w
+ const int64_t OH = dst->ne[1]; // output_h
+ const int64_t KW = kernel->ne[0]; // kernel_w
+ const int64_t KH = kernel->ne[1]; // kernel_h
+ const int64_t IC = input->ne[2]; // input_channels
+ const int64_t OC = kernel->ne[3]; // ouptut_chanles
+ const int64_t B = input->ne[3]; // n_batches
const int64_t total = B * OC * OH * OW;
conv_params params = { IW, IH, OW, OH, KW, KH, ST_X, ST_Y, PD_X, PD_Y, DL_X, DL_Y, IC, OC, B, total };
+ const auto & device = ggml_cuda_info().devices[ctx.device];
+ const bool use_mma =
+ turing_mma_available(device.cc) || amd_wmma_available(device.cc) || amd_mfma_available(device.cc);
+ // MUSA can share the tiling without a native fragment implementation in mma.cuh.
+ const bool use_simt = GGML_CUDA_CC_IS_MTHREADS(device.cc);
+ const bool pointwise = KW == 1 && KH == 1 && ST_X == 1 && ST_Y == 1 && PD_X == 0 && PD_Y == 0;
+ const bool use_blas = pointwise && fast_fp16_hardware_available(device.cc);
+ // Short reductions on small maps do not amortize conversion and launch costs.
+ const bool small_conv = IC * KW * KH < 64 && OW * OH < 512;
+
+ const int64_t limit = INT_MAX - 256;
+ const int64_t padded_w = IW + 2 * int64_t(PD_X), padded_h = IH + 2 * int64_t(PD_Y);
+ const bool padded_fits = padded_w > 0 && padded_w <= limit && padded_h > 0 && padded_h <= limit &&
+ padded_w * padded_h <= limit && IC * B <= limit / (padded_w * padded_h);
+ if (kernel->type == GGML_TYPE_F16 && (use_mma || use_blas || use_simt) && (use_blas || !small_conv) &&
+ ggml_nelements(input) <= limit && ggml_nelements(kernel) <= limit && total <= limit && padded_fits &&
+ PD_X >= 0 && PD_Y >= 0 && ST_X > 0 && ST_Y > 0 && DL_X > 0 && DL_Y > 0 &&
+ (OW - 1) * ST_X + (KW - 1) * DL_X < padded_w && (OH - 1) * ST_Y + (KH - 1) * DL_Y < padded_h &&
+ (OC + 63) / 64 <= 65535 && B <= 65535) {
+ const int pw = int(padded_w), ph = int(padded_h);
+ const int padded_total = int(padded_w * padded_h * IC * B);
+
+ ggml_cuda_pool_alloc x_half(ctx.pool(), padded_total);
+ // Match im2col's F16 input precision, but expand patches only in shared memory and accumulate in F32.
+ if (PD_X == 0 && PD_Y == 0) {
+ ggml_get_to_fp16_cuda(input->type)(X_D, x_half.get(), padded_total, st);
+ } else {
+ conv2d_pad_f16<<<(padded_total + 255) / 256, 256, 0, st>>>(X_D, x_half.get(), int(IW), int(IH), pw, ph,
+ PD_X, PD_Y, padded_total);
+ }
+ const conv_params padded_params = { pw, ph, OW, OH, KW, KH, ST_X, ST_Y, 0, 0, DL_X, DL_Y, IC, OC, B, total };
+ if (use_blas) {
+ const float alpha = 1.0f, beta = 0.0f;
+ const int positions = int(OW * OH);
+ cublasHandle_t cublas_h = ctx.cublas_handle();
+ for (int n = 0; n < B; ++n) {
+ CUBLAS_CHECK(cublasGemmEx(cublas_h, CUBLAS_OP_N, CUBLAS_OP_N, positions, int(OC), int(IC), &alpha,
+ x_half.get() + int64_t(n) * IC * positions, CUDA_R_16F, positions, K_D,
+ CUDA_R_16F, int(IC), &beta, Y_D + int64_t(n) * OC * positions, CUDA_R_32F,
+ positions, CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT_TENSOR_OP));
+ }
+ return;
+ }
+ const int64_t blocks = ((OW * OH + 63) / 64) * ((OC + 63) / 64) * B;
+ const int target = 8 * ggml_cuda_info().devices[ctx.device].nsm;
+ // Split long reductions so small spatial maps still occupy the GPU.
+ const int split_k = int(std::min({ int64_t(32), int64_t(65535) / B, (IC * KW * KH + 63) / 64,
+ std::max(int64_t(1), (target + blocks - 1) / blocks) }));
+
+ ggml_cuda_pool_alloc partial(ctx.pool());
+ float * result = split_k == 1 ? Y_D : partial.alloc(total * split_k);
+ const dim3 block(device.warp_size, 4);
+ const dim3 grid(unsigned((OW * OH + 63) / 64), unsigned((OC + 63) / 64), unsigned(B * split_k));
+ if (use_mma) {
+ conv2d_launch_implicit_gemm(x_half.get(), (const half *) K_D, result, padded_params, split_k, grid,
+ block, st);
+ } else {
+ conv2d_launch_implicit_gemm(x_half.get(), (const half *) K_D, result, padded_params, split_k, grid,
+ block, st);
+ }
+ if (split_k > 1) {
+ conv2d_reduce_split_k<<<(total + 255) / 256, 256, 0, st>>>(result, Y_D, int(total), int(OC * OW * OH),
+ split_k);
+ }
+ return;
+ }
+
if (kernel->type == GGML_TYPE_F16) {
conv2d_cuda_f16(X_D, (half *) K_D, Y_D, params, st);
} else {
diff --git a/ggml/src/ggml-cuda/convert.cu b/ggml/src/ggml-cuda/convert.cu
index 360c614a..0619f476 100644
--- a/ggml/src/ggml-cuda/convert.cu
+++ b/ggml/src/ggml-cuda/convert.cu
@@ -439,6 +439,29 @@ static __global__ void convert_unary(
}
}
+template struct alignas(sizeof(T)*4) cvt_vec4 { T v[4]; };
+
+// four elements per thread, so a warp moves 512B (RDNA) / 1k (CDNA) per load
+template
+static __global__ void convert_unary_cont_vec4(
+ const void * __restrict__ vx, dst_t * __restrict__ y, const int64_t k4) {
+ const int64_t i = (int64_t)blockDim.x*blockIdx.x + threadIdx.x;
+
+ if (i >= k4) {
+ return;
+ }
+
+ const cvt_vec4 xv = ((const cvt_vec4 *) vx)[i];
+
+ cvt_vec4 yv;
+#pragma unroll
+ for (int j = 0; j < 4; ++j) {
+ yv.v[j] = ggml_cuda_cast(xv.v[j]);
+ }
+
+ ((cvt_vec4 *) y)[i] = yv;
+}
+
template
static void convert_unary_cuda(const void * vx, dst_t * y,
const int64_t ne00, const int64_t ne01, const int64_t ne02, const int64_t ne03,
@@ -452,6 +475,15 @@ static void convert_unary_cuda(const void * vx, dst_t * y,
template
static void convert_unary_cont_cuda(const void * vx, dst_t * y, const int64_t k, cudaStream_t stream) {
+ if (k % 4 == 0 &&
+ (uintptr_t) vx % alignof(cvt_vec4) == 0 &&
+ (uintptr_t) y % alignof(cvt_vec4) == 0) {
+ const int64_t k4 = k/4;
+ const int64_t num_blocks = (k4 + CUDA_DEQUANTIZE_BLOCK_SIZE - 1) / CUDA_DEQUANTIZE_BLOCK_SIZE;
+ convert_unary_cont_vec4<<>>(vx, y, k4);
+ return;
+ }
+
convert_unary_cuda(vx, y, k, 1, 1, 1, k, k, k, stream);
}
diff --git a/ggml/src/ggml-cuda/cpy.cu b/ggml/src/ggml-cuda/cpy.cu
index fd7ffc0b..7a998458 100644
--- a/ggml/src/ggml-cuda/cpy.cu
+++ b/ggml/src/ggml-cuda/cpy.cu
@@ -589,6 +589,14 @@ void ggml_cuda_cpy(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, gg
ggml_cpy_scalar_cuda
(src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream);
}
+ } else if (src0->type == GGML_TYPE_I16 && src1->type == GGML_TYPE_I16) {
+ if (can_be_transposed) {
+ ggml_cpy_scalar_cuda
+ (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream);
+ } else {
+ ggml_cpy_scalar_cuda
+ (src0_ddc, src1_ddc, ne, ne00, ne01, ne02, nb00, nb01, nb02, nb03, ne10, ne11, ne12, nb10, nb11, nb12, nb13, main_stream);
+ }
} else if (src0->type == GGML_TYPE_F32 && src1->type == GGML_TYPE_I32) {
if (contiguous_srcs) {
ggml_cpy_scalar_contiguous_cuda
diff --git a/ggml/src/ggml-cuda/dsv4-hc.cu b/ggml/src/ggml-cuda/dsv4-hc.cu
index c4b19a78..ca1d2dc8 100644
--- a/ggml/src/ggml-cuda/dsv4-hc.cu
+++ b/ggml/src/ggml-cuda/dsv4-hc.cu
@@ -100,6 +100,7 @@ static __global__ void dsv4_hc_comb_f32(
}
}
+template
static __global__ void dsv4_hc_pre_f32(
const float * x,
const float * weights,
@@ -112,8 +113,10 @@ static __global__ void dsv4_hc_pre_f32(
int64_t sx2,
int64_t sw0,
int64_t sw1,
+ int64_t sw2,
int64_t sd0,
- int64_t sd1) {
+ int64_t sd1,
+ float scale) {
ggml_cuda_pdl_lc();
const int64_t ir = (int64_t) blockIdx.x * blockDim.x + threadIdx.x;
const int64_t nr = n_embd * n_tokens;
@@ -127,16 +130,22 @@ static __global__ void dsv4_hc_pre_f32(
const int64_t i0 = ir % n_embd;
const int64_t it = ir / n_embd;
- float sum = x[i0*sx0 + it*sx2] * weights[it*sw1];
- for (int64_t ih = 1; ih < hc; ++ih) {
+ float sum = 0.0f;
+ for (int64_t ih = 0; ih < hc; ++ih) {
const float xv = x[i0*sx0 + ih*sx1 + it*sx2];
- const float wv = weights[ih*sw0 + it*sw1];
+ float wv;
+ if constexpr (gated) {
+ wv = 1.0f / (1.0f + expf(-weights[i0*sw0 + ih*sw1 + it*sw2]));
+ } else {
+ wv = weights[ih*sw0 + it*sw1];
+ }
sum += xv * wv;
}
- dst[i0*sd0 + it*sd1] = sum;
+ dst[i0*sd0 + it*sd1] = scale * sum;
}
+template
static __global__ void dsv4_hc_post_f32(
const float * x,
const float * residual,
@@ -174,8 +183,12 @@ static __global__ void dsv4_hc_post_f32(
const int64_t it = ir / (n_embd * hc);
float sum = x[i0*sx0 + it*sx1] * post[idst*sp0 + it*sp1];
- for (int64_t isrc = 0; isrc < hc; ++isrc) {
- sum += residual[i0*sr0 + isrc*sr1 + it*sr2] * comb[idst*sc0 + isrc*sc1 + it*sc2];
+ if constexpr (has_comb) {
+ for (int64_t isrc = 0; isrc < hc; ++isrc) {
+ sum += residual[i0*sr0 + isrc*sr1 + it*sr2] * comb[idst*sc0 + isrc*sc1 + it*sc2];
+ }
+ } else {
+ sum += residual[i0*sr0 + idst*sr1 + it*sr2];
}
dst[i0*sd0 + idst*sd1 + it*sd2] = sum;
@@ -240,18 +253,23 @@ void ggml_cuda_op_dsv4_hc_pre(ggml_backend_cuda_context & ctx, ggml_tensor * dst
const int64_t hc = x->ne[1];
const int64_t n_tokens = x->ne[2];
+ const float scale = ggml_get_op_params_f32(dst, 0);
+ const bool gated = ggml_get_op_params_i32(dst, 1) != 0;
+
const int block_size = 256;
const int64_t nr = n_embd * n_tokens;
const dim3 block_dims(block_size, 1, 1);
const dim3 grid_dims((nr + block_size - 1) / block_size, 1, 1);
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, ctx.stream());
- ggml_cuda_kernel_launch(dsv4_hc_pre_f32, launch_params,
+ auto kernel = gated ? dsv4_hc_pre_f32 : dsv4_hc_pre_f32;
+ ggml_cuda_kernel_launch(kernel, launch_params,
(const float *) x->data, (const float *) weights->data, (float *) dst->data,
n_embd, hc, n_tokens,
nbx0 / sizeof(float), nbx1 / sizeof(float), nbx2 / sizeof(float),
- nbw0 / sizeof(float), nbw1 / sizeof(float),
- nbd0 / sizeof(float), nbd1 / sizeof(float));
+ nbw0 / sizeof(float), nbw1 / sizeof(float), nbw2 / sizeof(float),
+ nbd0 / sizeof(float), nbd1 / sizeof(float),
+ scale);
}
void ggml_cuda_op_dsv4_hc_post(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
@@ -263,15 +281,18 @@ void ggml_cuda_op_dsv4_hc_post(ggml_backend_cuda_context & ctx, ggml_tensor * ds
GGML_ASSERT(x->type == GGML_TYPE_F32);
GGML_ASSERT(residual->type == GGML_TYPE_F32);
GGML_ASSERT(post->type == GGML_TYPE_F32);
- GGML_ASSERT(comb->type == GGML_TYPE_F32);
+ GGML_ASSERT(comb == nullptr || comb->type == GGML_TYPE_F32);
GGML_ASSERT(dst->type == GGML_TYPE_F32);
GGML_TENSOR_LOCALS(size_t, nbx, x, nb);
GGML_TENSOR_LOCALS(size_t, nbr, residual, nb);
GGML_TENSOR_LOCALS(size_t, nbp, post, nb);
- GGML_TENSOR_LOCALS(size_t, nbc, comb, nb);
GGML_TENSOR_LOCALS(size_t, nbd, dst, nb);
+ const size_t nbc0 = comb ? comb->nb[0] : 0;
+ const size_t nbc1 = comb ? comb->nb[1] : 0;
+ const size_t nbc2 = comb ? comb->nb[2] : 0;
+
const int64_t n_embd = x->ne[0];
const int64_t n_tokens = x->ne[1];
const int64_t hc = residual->ne[1];
@@ -282,9 +303,10 @@ void ggml_cuda_op_dsv4_hc_post(ggml_backend_cuda_context & ctx, ggml_tensor * ds
const dim3 grid_dims((nr + block_size - 1) / block_size, 1, 1);
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(grid_dims, block_dims, 0, ctx.stream());
- ggml_cuda_kernel_launch(dsv4_hc_post_f32, launch_params,
+ auto kernel = comb ? dsv4_hc_post_f32 : dsv4_hc_post_f32;
+ ggml_cuda_kernel_launch(kernel, launch_params,
(const float *) x->data, (const float *) residual->data,
- (const float *) post->data, (const float *) comb->data, (float *) dst->data,
+ (const float *) post->data, comb ? (const float *) comb->data : nullptr, (float *) dst->data,
n_embd, hc, n_tokens,
nbx0 / sizeof(float), nbx1 / sizeof(float),
nbr0 / sizeof(float), nbr1 / sizeof(float), nbr2 / sizeof(float),
diff --git a/ggml/src/ggml-cuda/fattn-common.cuh b/ggml/src/ggml-cuda/fattn-common.cuh
index e67cc7fd..6d1ce52d 100644
--- a/ggml/src/ggml-cuda/fattn-common.cuh
+++ b/ggml/src/ggml-cuda/fattn-common.cuh
@@ -718,6 +718,9 @@ static __global__ void flash_attn_mask_to_KV_max(
KV_max[sequence*ne31 + jt] = KV_max_sj;
}
+void ggml_cuda_flash_attn_ext_compact_mask(
+ const ggml_tensor * mask, int32_t * indices, int32_t * counts, int32_t n_queries, int32_t ncols1, int32_t n_kv_max, cudaStream_t stream);
+
template // D == head size
__launch_bounds__(D, 1)
static __global__ void flash_attn_stream_k_fixup_uniform(
@@ -972,7 +975,8 @@ static __global__ void flash_attn_combine_results(
template
void launch_fattn(
ggml_backend_cuda_context & ctx, ggml_tensor * dst, fattn_kernel_t fattn_kernel, const int nwarps, const size_t nbytes_shared,
- const int nbatch_fa, const bool need_f16_K, const bool need_f16_V, const bool stream_k, const int warp_size = WARP_SIZE
+ const int nbatch_fa, const bool need_f16_K, const bool need_f16_V, const bool stream_k, const bool use_sparse,
+ const int warp_size = WARP_SIZE
) {
constexpr int ncols = ncols1 * ncols2;
@@ -1088,10 +1092,24 @@ void launch_fattn(
const int ntiles_z_gqa = ((gqa_ratio + ncols2 - 1) / ncols2);
const int ntiles_dst = ntiles_x * ntiles_z_gqa * K->ne[2] * Q->ne[3];
+ // sparse: a query tile of ncols1 queries shares one index list, the union of the queries' visible columns
+ int32_t n_kv_max = 0;
+ if (use_sparse) {
+ GGML_ASSERT(mask != nullptr);
+ const int32_t n_kv_max_query = ggml_get_op_params_i32(KQV, 4);
+ GGML_ASSERT(n_kv_max_query > 0);
+ n_kv_max = std::min(K->ne[1], int64_t(ncols1)*n_kv_max_query);
+
+ const size_t n_lists = size_t(ntiles_x) * mask->ne[3];
+
+ KV_max.alloc(size_t(n_kv_max)*n_lists + n_lists);
+ ggml_cuda_flash_attn_ext_compact_mask(mask, KV_max.ptr, KV_max.ptr + size_t(n_kv_max)*n_lists, Q->ne[1], ncols1, n_kv_max, main_stream);
+ }
+
// Optional optimization where the mask is scanned to determine whether part of the calculation can be skipped.
// Only worth the overhead if there is at lease one FATTN_KQ_STRIDE x FATTN_KQ_STRIDE square to be skipped or
// multiple sequences of possibly different lengths.
- if (mask && K->ne[1] % FATTN_KQ_STRIDE == 0 && (Q->ne[1] >= 1024 || Q->ne[3] > 1)) {
+ if (!use_sparse && mask && K->ne[1] % FATTN_KQ_STRIDE == 0 && (Q->ne[1] >= 1024 || Q->ne[3] > 1)) {
const int64_t s31 = mask->nb[1] / sizeof(half2);
const int64_t s33 = mask->nb[3] / sizeof(half2);
@@ -1114,16 +1132,26 @@ void launch_fattn(
GGML_ASSERT(max_blocks_per_sm > 0);
int parallel_blocks = max_blocks_per_sm;
- const int ntiles_KV = (K->ne[1] + nbatch_fa - 1) / nbatch_fa; // Max. number of parallel blocks limited by KV cache length.
+ const int64_t n_kv = use_sparse ? n_kv_max : K->ne[1];
+ const int ntiles_KV = (n_kv + nbatch_fa - 1) / nbatch_fa; // Max. number of parallel blocks limited by KV cache length.
dim3 blocks_num;
if (stream_k) {
- // For short contexts it can be faster to have the SMs work on whole tiles because this lets us skip the fixup.
- const int max_blocks = max_blocks_per_sm*nsm;
- const int tiles_nwaves = (ntiles_dst + max_blocks - 1) / max_blocks;
- const int tiles_efficiency_percent = 100 * ntiles_dst / (max_blocks*tiles_nwaves);
+ auto should_use_stream_k = [](const int cc, const int ntiles_dst, const int max_blocks, const int DKQ) {
+ const int tiles_nwaves = (ntiles_dst + max_blocks - 1) / max_blocks;
+ const int tiles_efficiency_percent = 100 * ntiles_dst / (max_blocks*tiles_nwaves);
+
+ if (GGML_CUDA_CC_IS_NVIDIA(cc) && cc >= GGML_CUDA_CC_ADA_LOVELACE) {
+ return true;
+ }
+ if (amd_wmma_available(cc) && DKQ == 64) {
+ return true; // TODO better configuration
+ }
+ return tiles_efficiency_percent < 75;
+ };
- const bool use_stream_k = cc >= GGML_CUDA_CC_ADA_LOVELACE || amd_wmma_available(cc) || tiles_efficiency_percent < 75;
+ const int max_blocks = max_blocks_per_sm*nsm;
+ const bool use_stream_k = should_use_stream_k(cc, ntiles_dst, max_blocks, Q->ne[0]);
blocks_num.x = ntiles_dst;
blocks_num.y = 1;
@@ -1207,8 +1235,8 @@ void launch_fattn(
GGML_ASSERT(block_dim.x % warp_size == 0);
- ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(blocks_num, block_dim, nbytes_shared, main_stream);
- ggml_cuda_kernel_launch(fattn_kernel, launch_params,
+ ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(blocks_num, block_dim, nbytes_shared, main_stream);
+ ggml_cuda_kernel_launch(fattn_kernel, launch_params,
(const char *) Q->data,
K_data,
V_data,
@@ -1218,7 +1246,7 @@ void launch_fattn(
!stream_k && parallel_blocks > 1 ? dst_tmp.ptr : (float *) KQV->data, dst_tmp_meta.ptr,
scale, max_bias, m0, m1, n_head_log2, logit_softcap,
Q->ne[0], ne01, Q->ne[2], Q->ne[3], Q->nb[1], Q->nb[2], Q->nb[3],
- K->ne[0], K->ne[1], K->ne[2], K->ne[3], nb11, nb12, nb13,
+ K->ne[0], n_kv, K->ne[2], K->ne[3], nb11, nb12, nb13,
nb21, nb22, nb23,
mask ? mask->ne[1] : 0, mask ? mask->ne[2] : 0, mask ? mask->ne[3] : 0,
mask ? mask->nb[1] : 0, mask ? mask->nb[2] : 0, mask ? mask->nb[3] : 0
diff --git a/ggml/src/ggml-cuda/fattn-mma-f16.cuh b/ggml/src/ggml-cuda/fattn-mma-f16.cuh
index 7f4cfd55..449a77c5 100644
--- a/ggml/src/ggml-cuda/fattn-mma-f16.cuh
+++ b/ggml/src/ggml-cuda/fattn-mma-f16.cuh
@@ -66,17 +66,17 @@ static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_co
GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 32, 128, 2, 32, 96, 64, 64, 2, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(192, 128, 64, 128, 2, 32, 96, 64, 64, 2, true);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 64, 4, 64, 128, 128, 128, 2, true);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 64, 4, 32, 128, 128, 128, 2, true);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 128, 2, 64, 128, 128, 128, 2, true);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 256, 1, 64, 128, 128, 128, 2, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 128, 2, 32, 128, 128, 128, 2, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 128, 2, 32, 128, 128, 128, 2, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 32, 128, 2, 32, 128, 128, 128, 1, false);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 64, 256, 1, 32, 128, 128, 128, 1, false);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 8, 64, 4, 32, 256, 256, 128, 1, false);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 16, 64, 4, 32, 256, 256, 128, 1, false);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 32, 128, 2, 32, 128, 128, 128, 1, false);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 8, 128, 2, 64, 128, 128, 128, 1, false);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 16, 256, 1, 64, 128, 128, 128, 1, false);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 32, 256, 1, 32, 128, 128, 128, 1, false);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(512, 512, 64, 256, 1, 32, 128, 128, 128, 1, false);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(576, 512, 8, 64, 4, 32, 288, 256, 128, 1, false);
@@ -157,8 +157,8 @@ static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_co
GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 8, 64, 2, 32, 128, 128, 128, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 16, 64, 2, 32, 128, 128, 128, 1, true);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 128, 2, 64, 128, 128, 64, 1, true);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 128, 2, 64, 128, 128, 64, 1, true);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 32, 256, 2, 64, 128, 128, 64, 1, true);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE(256, 256, 64, 256, 2, 64, 128, 128, 64, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 32, 128, 2, 32, 160, 128, 128, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE(320, 256, 64, 128, 2, 32, 160, 128, 128, 1, true);
@@ -180,7 +180,7 @@ static constexpr __host__ __device__ fattn_mma_config ggml_cuda_fattn_mma_get_co
GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 8, 128, 1, 64, 32, 32, 32, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 16, 256, 2, 64, 32, 32, 32, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 32, 256, 2, 64, 32, 32, 32, 1, true);
- GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 64, 256, 4, 64, 32, 32, 32, 1, true);
+ GGML_CUDA_FATTN_MMA_CONFIG_CASE( 64, 64, 64, 256, 3, 64, 32, 32, 32, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 8, 256, 2, 64, 40, 40, 40, 1, true);
GGML_CUDA_FATTN_MMA_CONFIG_CASE( 80, 80, 16, 256, 2, 64, 40, 40, 40, 1, true);
@@ -326,6 +326,32 @@ static constexpr __device__ bool ggml_cuda_fattn_mma_get_Q_in_reg(const int DKQ,
return ggml_cuda_fattn_mma_get_config(DKQ, DV, ncols).Q_in_reg;
}
+// Swizzling needs a tile stride that is a multiple of 32 half2 columns.
+static constexpr __host__ __device__ bool ggml_cuda_fattn_mma_bank_aligned(const int nbatch_2) {
+ return nbatch_2 >= 32 && nbatch_2 % 32 == 0;
+}
+
+// Swizzling needs ldmatrix, on other hardware the tiles keep the row padding.
+static __host__ bool ggml_cuda_fattn_mma_get_swizzled(const int DKQ, const int DV, const int ncols1, const int ncols2, const int cc) {
+ const fattn_mma_config cfg = ggml_cuda_fattn_mma_get_config(DKQ, DV, ncols1*ncols2, cc);
+ return turing_mma_available(cc) && ggml_cuda_fattn_mma_bank_aligned(cfg.nbatch_K2) && ggml_cuda_fattn_mma_bank_aligned(cfg.nbatch_V2);
+}
+
+static constexpr __device__ bool ggml_cuda_fattn_mma_get_swizzled(const int DKQ, const int DV, const int ncols1, const int ncols2) {
+#if defined(TURING_MMA_AVAILABLE)
+ const fattn_mma_config cfg = ggml_cuda_fattn_mma_get_config(DKQ, DV, ncols1*ncols2);
+ return ggml_cuda_fattn_mma_bank_aligned(cfg.nbatch_K2) && ggml_cuda_fattn_mma_bank_aligned(cfg.nbatch_V2);
+#else
+ GGML_UNUSED_VARS(DKQ, DV, ncols1, ncols2);
+ return false;
+#endif // defined(TURING_MMA_AVAILABLE)
+}
+
+// Row padding is only needed if the tile is not swizzled.
+static constexpr __host__ __device__ int ggml_cuda_fattn_mma_get_stride_tile(const int nbatch_2, const bool swizzled) {
+ return swizzled ? nbatch_2 : nbatch_2 + 4;
+}
+
static constexpr __device__ int get_cols_per_thread() {
#if defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
return 1; // AMD has a single column per thread.
@@ -349,20 +375,24 @@ static __host__ int ggml_cuda_fattn_mma_get_nstages(const int DKQ, const int DV,
return cp_async_available(cc) && ncols2 >= 2 ? ggml_cuda_fattn_mma_get_nstages_target(DKQ, DV, ncols1*ncols2, cc) : 0;
}
-static constexpr __device__ int ggml_cuda_fattn_mma_get_nstages(const int DKQ, const int DV, const int ncols1, const int ncols2) {
+static constexpr __device__ int ggml_cuda_fattn_mma_get_nstages(
+ const int DKQ, const int DV, const int ncols1, const int ncols2, const bool use_sparse) {
#ifdef CP_ASYNC_AVAILABLE
- return ncols2 >= 2 ? ggml_cuda_fattn_mma_get_nstages_target(DKQ, DV, ncols1*ncols2) : 0;
+ const int nstages_target = ncols2 >= 2 ? ggml_cuda_fattn_mma_get_nstages_target(DKQ, DV, ncols1*ncols2) : 0;
+ // sparse gather is not implemented for multi-stage loading
+ return use_sparse && nstages_target > 1 ? 1 : nstages_target;
#else
- GGML_UNUSED_VARS(DKQ, DV, ncols1, ncols2);
+ GGML_UNUSED_VARS(DKQ, DV, ncols1, ncols2, use_sparse);
return 0;
#endif // CP_ASYNC_AVAILABLE
}
// ------------------------------------------------------------------------------------------------------------------
-template
+template
static __device__ __forceinline__ void flash_attn_ext_f16_load_tile(
- const half2 * const __restrict__ KV, half2 * const __restrict__ tile_KV, const int D2, const int stride_KV, const int i_sup) {
+ const half2 * const __restrict__ KV, half2 * const __restrict__ tile_KV, const int D2, const int stride_KV,
+ const int k_VKQ_0, const int i_sup, const int32_t * const __restrict__ indices) {
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
// K/V data is loaded with decreasing granularity for D for better memory bandwidth.
// The minimum granularity is 16 bytes.
@@ -370,7 +400,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_tile(
const int chunks_per_row = D2 / h2_per_chunk;
if constexpr (use_cp_async) {
static_assert(warp_size == 32, "bad warp_size");
- static_assert(!oob_check, "OOB check not compatible with cp_async");
+ static_assert(!oob_check || use_sparse, "OOB check not compatible with cp_async");
constexpr int preload = 64;
const unsigned int tile_KV_32 = ggml_cuda_cvta_generic_to_shared(tile_KV);
@@ -393,11 +423,20 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_tile(
break;
}
+ int64_t i_KV;
+ if constexpr (use_sparse) {
+ // padded slots gather row 0, the -inf mask removes their contribution
+ const int32_t index = i < i_sup ? indices[k_VKQ_0 + i] : 0;
+ i_KV = index >= 0 ? index : 0;
+ } else {
+ i_KV = k_VKQ_0 + i;
+ }
+
#pragma unroll
for (int k0 = k0_start; k0 < k0_stop; k0 += stride_k) {
const int k = k0 + (stride_k == warp_size ? threadIdx.x : threadIdx.x % stride_k);
- cp_async_cg_16(tile_KV_32 + i*(stride_tile*sizeof(half2)) + k*16, KV + i*stride_KV + k*h2_per_chunk);
+ cp_async_cg_16(tile_KV_32 + swizzle_bytes(i, k*h2_per_chunk, stride_tile), KV + i_KV*stride_KV + k*h2_per_chunk);
}
}
};
@@ -432,8 +471,14 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_tile(
for (int k0 = k0_start; k0 < k0_stop; k0 += stride_k) {
const int k = k0 + (stride_k == warp_size ? threadIdx.x : threadIdx.x % stride_k);
- ggml_cuda_memcpy_1<16>(tile_KV + i*stride_tile + k*4,
- !oob_check || i < i_sup ? KV + i*stride_KV + k*h2_per_chunk : zero);
+ const half2 * src;
+ if constexpr (use_sparse) {
+ const int32_t index = i < i_sup ? indices[k_VKQ_0 + i] : -1;
+ src = index >= 0 ? KV + int64_t(index)*stride_KV + k*h2_per_chunk : zero;
+ } else {
+ src = !oob_check || i < i_sup ? KV + int64_t(k_VKQ_0 + i)*stride_KV + k*h2_per_chunk : zero;
+ }
+ ggml_cuda_memcpy_1<16>((char *) tile_KV + swizzle_bytes(i, k*h2_per_chunk, stride_tile), src);
}
}
};
@@ -447,14 +492,16 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_tile(
}
}
-template
+template
static __device__ __forceinline__ void flash_attn_ext_f16_load_mask(
const half * const __restrict__ mask_h, half * const __restrict__ tile_mask,
- const int stride_mask, const int i_sup, const int j0, const uint3 ne01) {
+ const int stride_mask, const int k_VKQ_0, const int i_sup, const int j0, const uint3 ne01,
+ const int32_t * const __restrict__ indices) {
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
if constexpr (use_cp_async) {
static_assert(nbatch_fa <= 8*warp_size && nbatch_fa % 8 == 0, "bad nbatch_fa");
static_assert(!oob_check, "OOB check incompatible with cp_async");
+ static_assert(!use_sparse, "sparse gather incompatible with cp_async");
constexpr int preload = nbatch_fa >= 32 ? nbatch_fa * sizeof(half) : 64;
constexpr int cols_per_warp = 8*warp_size/nbatch_fa;
constexpr int stride_j = nwarps * cols_per_warp;
@@ -472,9 +519,9 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_mask(
const int i = 8 * (threadIdx.x % (nbatch_fa/8));
- cp_async_cg_16(tile_mask_32 + j_sram*(nbatch_fa*sizeof(half) + 16) + i*sizeof(half), mask_h + int64_t(j_vram)*stride_mask + i);
+ cp_async_cg_16(tile_mask_32 + j_sram*(nbatch_fa*sizeof(half) + 16) + i*sizeof(half), mask_h + int64_t(j_vram)*stride_mask + k_VKQ_0 + i);
}
- } else if constexpr (oob_check) {
+ } else if constexpr (oob_check || use_sparse) {
#pragma unroll
for (int j1 = 0; j1 < ncols1; j1 += nwarps) {
const int j_sram = j1 + threadIdx.y;
@@ -488,7 +535,12 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_mask(
for (int i0 = 0; i0 < nbatch_fa; i0 += warp_size) {
const int i = i0 + threadIdx.x;
- tile_mask[j_sram*(nbatch_fa + 8) + i] = i < i_sup ? mask_h[int64_t(j_vram)*stride_mask + i] : half(0.0f);
+ if constexpr (use_sparse) {
+ const int32_t index = i < i_sup ? indices[k_VKQ_0 + i] : -1;
+ tile_mask[j_sram*(nbatch_fa + 8) + i] = index >= 0 ? mask_h[int64_t(j_vram)*stride_mask + index] : half(-INFINITY);
+ } else {
+ tile_mask[j_sram*(nbatch_fa + 8) + i] = i < i_sup ? mask_h[int64_t(j_vram)*stride_mask + k_VKQ_0 + i] : half(0.0f);
+ }
}
}
} else if constexpr (nbatch_fa < 2*warp_size) {
@@ -505,7 +557,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_mask(
const int i = threadIdx.x % (warp_size/cols_per_warp);
- ggml_cuda_memcpy_1(tile_mask + j_sram*(nbatch_fa + 8) + 2*i, mask_h + int64_t(j_vram)*stride_mask + 2*i);
+ ggml_cuda_memcpy_1(tile_mask + j_sram*(nbatch_fa + 8) + 2*i, mask_h + int64_t(j_vram)*stride_mask + k_VKQ_0 + 2*i);
}
} else {
#pragma unroll
@@ -521,20 +573,21 @@ static __device__ __forceinline__ void flash_attn_ext_f16_load_mask(
for (int i0 = 0; i0 < nbatch_fa; i0 += 2*warp_size) {
const int i = i0 + 2*threadIdx.x;
- ggml_cuda_memcpy_1(tile_mask + j_sram*(nbatch_fa + 8) + i, mask_h + int64_t(j_vram)*stride_mask + i);
+ ggml_cuda_memcpy_1(tile_mask + j_sram*(nbatch_fa + 8) + i, mask_h + int64_t(j_vram)*stride_mask + k_VKQ_0 + i);
}
}
}
}
template
static __device__ __forceinline__ void flash_attn_ext_f16_iter(
const float2 * const __restrict__ Q_f2,
const half2 * const __restrict__ K_h2,
const half2 * const __restrict__ V_h2,
const half * const __restrict__ mask_h,
+ const int32_t * const __restrict__ indices,
float2 * const __restrict__ dstk,
float2 * const __restrict__ dstk_fixup,
const float scale,
@@ -566,11 +619,11 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
constexpr int nbatch_K2 = ggml_cuda_fattn_mma_get_nbatch_K2(DKQ, DV, ncols);
constexpr int nbatch_V2 = ggml_cuda_fattn_mma_get_nbatch_V2(DKQ, DV, ncols);
constexpr bool Q_in_reg = ggml_cuda_fattn_mma_get_Q_in_reg (DKQ, DV, ncols);
- constexpr int nstages = ggml_cuda_fattn_mma_get_nstages (DKQ, DV, ncols1, ncols2);
+ constexpr int nstages = ggml_cuda_fattn_mma_get_nstages (DKQ, DV, ncols1, ncols2, use_sparse);
- constexpr int stride_tile_K = nbatch_K2 + 4;
-
- constexpr int stride_tile_V = V_is_K_view ? stride_tile_K : nbatch_V2 + 4;
+ constexpr bool swz = ggml_cuda_fattn_mma_get_swizzled(DKQ, DV, ncols1, ncols2);
+ constexpr int stride_tile_K = ggml_cuda_fattn_mma_get_stride_tile(nbatch_K2, swz);
+ constexpr int stride_tile_V = V_is_K_view ? stride_tile_K : ggml_cuda_fattn_mma_get_stride_tile(nbatch_V2, swz);
const int k_VKQ_0 = kb0 * nbatch_fa;
#if defined(TURING_MMA_AVAILABLE)
@@ -588,13 +641,14 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
constexpr bool use_cp_async = true;
cp_async_wait_all();
__syncthreads();
- flash_attn_ext_f16_load_tile
- (V_h2 + int64_t(k_VKQ_0)*stride_V, tile_V, nbatch_V2, stride_V, k_VKQ_sup);
+ flash_attn_ext_f16_load_tile
+ (V_h2, tile_V, nbatch_V2, stride_V, k_VKQ_0, k_VKQ_sup, nullptr);
} else {
- constexpr bool use_cp_async = nstages == 1;
+ // the sparse mask values are gathered per element, always load them synchronously
+ constexpr bool use_cp_async = nstages == 1 && !use_sparse;
if (ncols2 > 1 || mask_h) {
- flash_attn_ext_f16_load_mask
- (mask_h + k_VKQ_0, tile_mask, stride_mask, k_VKQ_sup, jt*ncols1, ne01);
+ flash_attn_ext_f16_load_mask
+ (mask_h, tile_mask, stride_mask, k_VKQ_0, k_VKQ_sup, jt*ncols1, ne01, indices);
}
}
@@ -607,8 +661,8 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
if constexpr (nstages <= 1) {
const int k0_diff = k0_stop - k0_start;
constexpr bool use_cp_async = nstages == 1;
- flash_attn_ext_f16_load_tile
- (K_h2 + int64_t(k_VKQ_0)*stride_K + k0_start, tile_K, k0_diff, stride_K, k_VKQ_sup);
+ flash_attn_ext_f16_load_tile
+ (K_h2 + k0_start, tile_K, k0_diff, stride_K, k_VKQ_0, k_VKQ_sup, indices);
if (use_cp_async) {
cp_async_wait_all();
}
@@ -623,7 +677,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
#pragma unroll
for (int k_KQ_0 = k0_start; k_KQ_0 < k0_stop; k_KQ_0 += T_A_KQ::J) {
T_A_KQ K_A;
- load_ldmatrix(K_A, tile_K + i_KQ_0*stride_tile_K + (k_KQ_0 - k0_start), stride_tile_K);
+ load_ldmatrix(K_A, tile_K, i_KQ_0, k_KQ_0 - k0_start, stride_tile_K);
if constexpr (cols_per_warp == 8) {
mma(KQ_C[i_KQ_00/(np*T_A_KQ::I)], K_A, Q_B[k_KQ_0/T_A_KQ::J]);
} else {
@@ -649,7 +703,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
const int i_KQ_0 = i_KQ_00 + (threadIdx.y % np)*T_A_KQ::I;
T_A_KQ K_A;
- load_ldmatrix(K_A, tile_K + i_KQ_0*stride_tile_K + (k_KQ_0 - k0_start), stride_tile_K);
+ load_ldmatrix(K_A, tile_K, i_KQ_0, k_KQ_0 - k0_start, stride_tile_K);
if constexpr (cols_per_warp == 8) {
mma(KQ_C[i_KQ_00/(np*T_A_KQ::I)], K_A, Q_B[0]);
@@ -933,6 +987,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
}
if constexpr (nstages > 1) {
+ static_assert(!use_sparse, "sparse gather not implemented for multi-stage loading");
static_assert(!V_is_K_view, "K data reuse not implemented multi-stage loading");
// Preload K tile for next iteration:
constexpr bool use_cp_async = true;
@@ -940,11 +995,11 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
__syncthreads();
if (!last_iter) {
if (ncols2 > 1 || mask_h) {
- flash_attn_ext_f16_load_mask
- (mask_h + k_VKQ_0 + nbatch_fa, tile_mask, stride_mask, k_VKQ_sup, jt*ncols1, ne01);
+ flash_attn_ext_f16_load_mask
+ (mask_h, tile_mask, stride_mask, k_VKQ_0 + nbatch_fa, k_VKQ_sup, jt*ncols1, ne01, nullptr);
}
- flash_attn_ext_f16_load_tile
- (K_h2 + int64_t(k_VKQ_0 + nbatch_fa)*stride_K, tile_K, nbatch_K2, stride_K, k_VKQ_sup);
+ flash_attn_ext_f16_load_tile
+ (K_h2, tile_K, nbatch_K2, stride_K, k_VKQ_0 + nbatch_fa, k_VKQ_sup, nullptr);
}
}
@@ -959,8 +1014,8 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
const int i0_diff = i0_stop - i0_start;
if (!V_is_K_view || i0_stop > 2*nbatch_K2) {
constexpr bool use_cp_async = nstages == 1;
- flash_attn_ext_f16_load_tile
- (V_h2 + int64_t(k_VKQ_0)*stride_V + i0_start/2, tile_V, i0_diff/2, stride_V, k_VKQ_sup);
+ flash_attn_ext_f16_load_tile
+ (V_h2 + i0_start/2, tile_V, i0_diff/2, stride_V, k_VKQ_0, k_VKQ_sup, indices);
if (use_cp_async) {
cp_async_wait_all();
}
@@ -978,7 +1033,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
const int k0 = k00 + (threadIdx.y % np)*T_A_VKQ::J;
T_A_VKQ A; // Transposed in SRAM but not in registers, gets transposed on load.
- load_ldmatrix_trans(A, tile_V_i + 2*k0*stride_tile_V + (i_VKQ_0 - i0_start)/2, stride_tile_V);
+ load_ldmatrix_trans(A, tile_V, 2*k0, (int)(tile_V_i - tile_V) + (i_VKQ_0 - i0_start)/2, stride_tile_V);
if constexpr (T_B_KQ::I == 8) {
mma(VKQ_C[i_VKQ_0/T_A_VKQ::I], A, B[k00/(np*T_A_VKQ::J)]);
} else {
@@ -1004,6 +1059,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
const int k0 = k00 + (threadIdx.y % np)*T_A_VKQ::I;
T_A_VKQ A; // Transposed in both SRAM and registers, load normally.
+ static_assert(!swz, "Volta has no ldmatrix");
load_ldmatrix(A, tile_V_i + k0*stride_tile_V + (i_VKQ_0 - i0_start)/2, stride_tile_V);
mma(VKQ_C[i_VKQ_0/i0_stride], B[k00/(np*T_A_VKQ::I)], A);
}
@@ -1015,7 +1071,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
}
}
#else
- GGML_UNUSED_VARS(Q_f2, K_h2, V_h2, mask_h, dstk, dstk_fixup,
+ GGML_UNUSED_VARS(Q_f2, K_h2, V_h2, mask_h, indices, dstk, dstk_fixup,
scale, slope, logit_softcap, ne01, ne02,
stride_K, stride_V, stride_mask,
tile_Q, tile_K, tile_V, tile_mask,
@@ -1025,7 +1081,7 @@ static __device__ __forceinline__ void flash_attn_ext_f16_iter(
}
#if defined(TURING_MMA_AVAILABLE)
-template struct mma_tile_sizes {
+template struct mma_tile_sizes {
using T_A_KQ = tile<16, 8, half2>; // row-major
using T_B_KQ = tile<16, 8, half2>; // column-major
using T_C_KQ = tile<16, 16, float>; // column-major
@@ -1033,7 +1089,33 @@ template struct mma_tile_sizes {
using T_B_VKQ = tile<16, 8, half2>; // column-major
using T_C_VKQ = tile<16, 8, half2>; // column-major
};
-template struct mma_tile_sizes {
+// If there are only 8 columns, use thinner B tiles to avoid wasting compute:
+template struct mma_tile_sizes {
+ using T_A_KQ = tile<16, 8, half2>; // row-major
+ using T_B_KQ = tile< 8, 8, half2>; // column-major
+ using T_C_KQ = tile<16, 8, float>; // row-major
+ using T_A_VKQ = tile<16, 8, half2>; // row-major
+ using T_B_VKQ = tile< 8, 8, half2>; // column-major
+ using T_C_VKQ = tile<16, 4, half2>; // row-major
+};
+// For very large head sizes, use thinner B tiles to reduce register pressure:
+template<> struct mma_tile_sizes<256, 16> {
+ using T_A_KQ = tile<16, 8, half2>; // row-major
+ using T_B_KQ = tile< 8, 8, half2>; // column-major
+ using T_C_KQ = tile<16, 8, float>; // row-major
+ using T_A_VKQ = tile<16, 8, half2>; // row-major
+ using T_B_VKQ = tile< 8, 8, half2>; // column-major
+ using T_C_VKQ = tile<16, 4, half2>; // row-major
+};
+template<> struct mma_tile_sizes<512, 16> {
+ using T_A_KQ = tile<16, 8, half2>; // row-major
+ using T_B_KQ = tile< 8, 8, half2>; // column-major
+ using T_C_KQ = tile<16, 8, float>; // row-major
+ using T_A_VKQ = tile<16, 8, half2>; // row-major
+ using T_B_VKQ = tile< 8, 8, half2>; // column-major
+ using T_C_VKQ = tile<16, 4, half2>; // row-major
+};
+template<> struct mma_tile_sizes<512, 32> {
using T_A_KQ = tile<16, 8, half2>; // row-major
using T_B_KQ = tile< 8, 8, half2>; // column-major
using T_C_KQ = tile<16, 8, float>; // row-major
@@ -1043,7 +1125,7 @@ template struct mma_tile_sizes {
};
#elif defined(AMD_WMMA_AVAILABLE)
#ifdef RDNA3
-template struct mma_tile_sizes {
+template struct mma_tile_sizes {
using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major
using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // column-major
using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major
@@ -1068,7 +1150,7 @@ template struct mma_tile_sizes<112, ncols> {
using T_C_VKQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major
};
#else
-template struct mma_tile_sizes {
+template struct mma_tile_sizes {
using T_A_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR>; // row-major
using T_B_KQ = tile<16, 8, half2, DATA_LAYOUT_I_MAJOR>; // column-major
using T_C_KQ = tile<16, 16, float, DATA_LAYOUT_I_MAJOR>; // column-major
@@ -1094,16 +1176,16 @@ template struct mma_tile_sizes<112, ncols> {
};
#endif // RDNA3
#elif defined(AMD_MFMA_AVAILABLE)
-template struct mma_tile_sizes {
+template struct mma_tile_sizes {
using T_A_KQ = tile<16, 8, half2>; // row-major
using T_B_KQ = tile<16, 8, half2>; // column-major
using T_C_KQ = tile<16, 16, float>; // column-major
using T_A_VKQ = tile<16, 8, half2>; // row-major
using T_B_VKQ = tile<16, 8, half2>; // column-major
- using T_C_VKQ = tile<16, 8, half2>; // column-major
+ using T_C_VKQ = tile<16, 16, float>; // column-major
};
#else // Volta
-template struct mma_tile_sizes {
+template struct mma_tile_sizes {
using T_A_KQ = tile< 8, 4, half2, DATA_LAYOUT_I_MAJOR_MIRRORED>; // row-major
using T_B_KQ = tile<32, 4, half2, DATA_LAYOUT_I_MAJOR>; // column-major
using T_C_KQ = tile<32, 8, float, DATA_LAYOUT_I_MAJOR>; // column-major
@@ -1113,12 +1195,13 @@ template struct mma_tile_sizes {
};
#endif // defined(TURING_MMA_AVAILABLE)
-template
+template
static __device__ __forceinline__ void flash_attn_ext_f16_process_tile(
const float2 * const __restrict__ Q_f2,
const half2 * const __restrict__ K_h2,
const half2 * const __restrict__ V_h2,
const half * const __restrict__ mask_h,
+ const int32_t * const __restrict__ indices,
const float * const __restrict__ sinks_f,
float2 * const __restrict__ dstk,
float2 * const __restrict__ dstk_fixup,
@@ -1143,12 +1226,12 @@ static __device__ __forceinline__ void flash_attn_ext_f16_process_tile(
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
constexpr int ncols = ncols1 * ncols2;
- using T_A_KQ = typename mma_tile_sizes::T_A_KQ;
- using T_B_KQ = typename mma_tile_sizes::T_B_KQ;
- using T_C_KQ = typename mma_tile_sizes::T_C_KQ;
- using T_A_VKQ = typename mma_tile_sizes::T_A_VKQ;
- using T_B_VKQ = typename mma_tile_sizes::T_B_VKQ;
- using T_C_VKQ = typename mma_tile_sizes::T_C_VKQ;
+ using T_A_KQ = typename mma_tile_sizes::T_A_KQ;
+ using T_B_KQ = typename mma_tile_sizes::T_B_KQ;
+ using T_C_KQ = typename mma_tile_sizes::T_C_KQ;
+ using T_A_VKQ = typename mma_tile_sizes