mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-20 19:57:28 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
339f3242c7 | ||
|
|
9a3b91c680 | ||
|
|
5c0b49a23b | ||
|
|
1242be66f6 | ||
|
|
84e66d0321 | ||
|
|
52ffa02745 | ||
|
|
839a572a4c | ||
|
|
ead9f32058 | ||
|
|
b684b498b6 | ||
|
|
4a45de4f2c | ||
|
|
7e3eeee048 | ||
|
|
5ba3b9fd3d | ||
|
|
5a9a34d888 | ||
|
|
39da130fcd | ||
|
|
4e201388c3 | ||
|
|
1ba568c5ad | ||
|
|
36140dbee2 | ||
|
|
df11702136 | ||
|
|
9df0b73c70 | ||
|
|
b38960f520 | ||
|
|
a01ce9ffc6 | ||
|
|
8115b4c31f | ||
|
|
119f68d7d4 | ||
|
|
b684498579 | ||
|
|
522f2b6eed | ||
|
|
83c2d012a2 | ||
|
|
13786fbb3a | ||
|
|
d80619397c | ||
|
|
704366d76f | ||
|
|
707616feb5 | ||
|
|
d8d92973c8 | ||
|
|
7879491cf4 | ||
|
|
8466395353 | ||
|
|
dd002b91f5 | ||
|
|
1d88cb05f1 | ||
|
|
3bb63cbcbf | ||
|
|
f711c8f303 | ||
|
|
921257f500 | ||
|
|
3ffb62935b | ||
|
|
7adf160c41 | ||
|
|
ab7ed0d4b5 | ||
|
|
4c3c5810f8 |
@@ -5,8 +5,8 @@ exclude =
|
||||
# Do not traverse examples and tools
|
||||
examples,
|
||||
tools,
|
||||
# Reference scripts for skills
|
||||
skills,
|
||||
# Do not traverse .venv directories
|
||||
.venv*,
|
||||
# Do not include package initializers
|
||||
__init__.py,
|
||||
# No need to traverse our git directory
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
android-ndk-snapdragon:
|
||||
runs-on: ubuntu-24.04 # previously ubuntu-latest
|
||||
container:
|
||||
image: 'ghcr.io/snapdragon-toolchain/arm64-android:v0.7'
|
||||
image: 'ghcr.io/snapdragon-toolchain/arm64-android:v0.8'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
linux-iot-snapdragon:
|
||||
runs-on: ubuntu-24.04 # previously ubuntu-latest
|
||||
container:
|
||||
image: 'ghcr.io/snapdragon-toolchain/arm64-linux:v0.7'
|
||||
image: 'ghcr.io/snapdragon-toolchain/arm64-linux:v0.8'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
name: CI (wasi)
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allows manual triggering
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths: [
|
||||
'.github/workflows/build-wasi.yml',
|
||||
'**/CMakeLists.txt',
|
||||
'**/.cmake',
|
||||
'**/*.h',
|
||||
'**/*.hpp',
|
||||
'**/*.c',
|
||||
'**/*.cpp',
|
||||
'**/*.wgsl',
|
||||
'**/*.tmpl',
|
||||
'ggml/src/ggml-webgpu/wgsl-shaders/embed_wgsl.py'
|
||||
]
|
||||
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths: [
|
||||
'.github/workflows/build-wasi.yml',
|
||||
'**/CMakeLists.txt',
|
||||
'**/.cmake',
|
||||
'**/*.h',
|
||||
'**/*.hpp',
|
||||
'**/*.c',
|
||||
'**/*.cpp',
|
||||
'**/*.wgsl',
|
||||
'**/*.tmpl',
|
||||
'ggml/src/ggml-webgpu/wgsl-shaders/embed_wgsl.py'
|
||||
]
|
||||
|
||||
cache-mode: none
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
WASI_SDK_VERSION: 34
|
||||
DAWN_VERSION: v20260908.214631
|
||||
DAWN_COMMIT: 94c3c9cc0d5fb2e85aebb370fa8d37b71aa34655
|
||||
WASI_WEBGPU_HEADERS_COMMIT: 917765edd7cb8f8cf95bda1217514c78f71b582b
|
||||
|
||||
jobs:
|
||||
ubuntu-webgpu:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
restore: false
|
||||
save: false
|
||||
|
||||
- name: Install wasi-sdk
|
||||
uses: bytecodealliance/setup-wasi-sdk-action@77d09f3df1e8da344d8403cf8631af8977938b4c # v1.0.0
|
||||
with:
|
||||
version: ${{ env.WASI_SDK_VERSION }}
|
||||
add-to-path: false
|
||||
|
||||
- name: wasi:webgpu Dependency
|
||||
run: |
|
||||
mkdir -p wasi-webgpu/dawn-headers
|
||||
curl -fL "https://github.com/google/dawn/releases/download/${DAWN_VERSION}/dawn-headers-${DAWN_COMMIT}.tar.gz" |
|
||||
tar -xz -C wasi-webgpu/dawn-headers --strip-components=1
|
||||
|
||||
git init -q wasi-webgpu/wasi-webgpu-headers
|
||||
cd wasi-webgpu/wasi-webgpu-headers
|
||||
git fetch -q --depth 1 https://github.com/wasi-gfx/wasi-webgpu-headers.git ${WASI_WEBGPU_HEADERS_COMMIT}
|
||||
git checkout -q FETCH_HEAD
|
||||
git submodule update -q --init --depth 1
|
||||
cd ../..
|
||||
|
||||
# ggml-webgpu does find_package(Dawn) and links dawn::webgpu_dawn. There is no Dawn on WASI,
|
||||
# so this target instead compiles wasi-webgpu-headers (webgpu.h over wasi:webgpu imports)
|
||||
# and exposes Dawn's webgpu_cpp.h on top of it.
|
||||
cat > wasi-webgpu/DawnConfig.cmake << 'EOF'
|
||||
set(_wasi_webgpu "${CMAKE_CURRENT_LIST_DIR}/wasi-webgpu-headers")
|
||||
add_library(dawn::webgpu_dawn INTERFACE IMPORTED)
|
||||
set_target_properties(dawn::webgpu_dawn PROPERTIES
|
||||
INTERFACE_SOURCES "${_wasi_webgpu}/webgpu.c;${_wasi_webgpu}/async_futures.c;${_wasi_webgpu}/imports.c;${_wasi_webgpu}/imports_component_type.o"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/dawn-headers/include/webgpu_upstream;${_wasi_webgpu}")
|
||||
EOF
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: webgpu-ubuntu-24.04-arm-wasi
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
# llama-common does not build for WASI, so the executables are linked by hand
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
WASI_FLAGS="-msimd128 -D_WASI_EMULATED_SIGNAL -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
|
||||
cmake -B build-wasi \
|
||||
-G "Ninja" \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk-p3.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_FLAGS="${WASI_FLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${WASI_FLAGS}" \
|
||||
-DCMAKE_HAVE_LIBC_PTHREAD=1 \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DGGML_NATIVE=OFF \
|
||||
-DGGML_OPENMP=OFF \
|
||||
-DGGML_WEBGPU=ON \
|
||||
-DDawn_DIR=$PWD/wasi-webgpu \
|
||||
-DLLAMA_BUILD_COMMON=OFF
|
||||
|
||||
time cmake --build build-wasi --config Release --target llama -j $(nproc)
|
||||
|
||||
LIBS="build-wasi/ggml/src/libggml.a build-wasi/ggml/src/ggml-webgpu/libggml-webgpu.a build-wasi/ggml/src/libggml-cpu.a build-wasi/ggml/src/libggml-base.a"
|
||||
LDFLAGS="-lunwind -ldl -Wl,-z,stack-size=8388608"
|
||||
CXX="${WASI_SDK_PATH}/bin/clang++ --target=wasm32-wasip3 -O3 -std=c++17 ${WASI_FLAGS} -I include -I ggml/include"
|
||||
|
||||
$CXX examples/simple/simple.cpp build-wasi/src/libllama.a $LIBS $LDFLAGS -o build-wasi/llama-simple.wasm
|
||||
$CXX tests/test-backend-ops.cpp $LIBS $LDFLAGS -o build-wasi/test-backend-ops.wasm
|
||||
|
||||
- name: ccache-buckets-save
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CACHE_OUTPUT }}
|
||||
with:
|
||||
key: webgpu-ubuntu-24.04-arm-wasi
|
||||
folder: llama.cpp
|
||||
evict-old-files: 1d
|
||||
hf_bucket: ggml-org/cache
|
||||
save: true
|
||||
@@ -7,15 +7,7 @@ on:
|
||||
- master
|
||||
paths: [
|
||||
'.github/workflows/build-webgpu.yml',
|
||||
'**/CMakeLists.txt',
|
||||
'**/.cmake',
|
||||
'**/*.h',
|
||||
'**/*.hpp',
|
||||
'**/*.c',
|
||||
'**/*.cpp',
|
||||
'**/*.wgsl',
|
||||
'**/*.tmpl',
|
||||
'ggml/src/ggml-webgpu/wgsl-shaders/embed_wgsl.py'
|
||||
'ggml/src/ggml-webgpu/**'
|
||||
]
|
||||
|
||||
pull_request:
|
||||
@@ -63,135 +55,3 @@ jobs:
|
||||
-type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' \) \
|
||||
-print0 |
|
||||
xargs -0 clang-format-22 --dry-run --Werror
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
restore: false
|
||||
save: false
|
||||
|
||||
- name: Dawn Dependency
|
||||
id: dawn-depends
|
||||
run: |
|
||||
DAWN_VERSION="v20260908.214631"
|
||||
DAWN_OWNER="google"
|
||||
DAWN_REPO="dawn"
|
||||
DAWN_ASSET_NAME="Dawn-94c3c9cc0d5fb2e85aebb370fa8d37b71aa34655-macos-latest-Release"
|
||||
echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
curl -L -o artifact.tar.gz \
|
||||
"https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
mkdir dawn
|
||||
tar -xvf artifact.tar.gz -C dawn --strip-components=1
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: webgpu-macos-latest
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
export CMAKE_PREFIX_PATH=dawn
|
||||
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DGGML_WEBGPU=ON -DGGML_METAL=OFF -DGGML_BLAS=OFF
|
||||
time cmake --build build --config Release -j $(sysctl -n hw.logicalcpu)
|
||||
|
||||
- name: ccache-buckets-save
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CACHE_OUTPUT }}
|
||||
with:
|
||||
key: webgpu-macos-latest
|
||||
folder: llama.cpp
|
||||
evict-old-files: 1d
|
||||
hf_bucket: ggml-org/cache
|
||||
save: true
|
||||
|
||||
- name: Test
|
||||
id: cmake_test
|
||||
run: |
|
||||
cd build
|
||||
ctest -L main --verbose --timeout 900
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
restore: false
|
||||
save: false
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:kisak/kisak-mesa
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y build-essential mesa-vulkan-drivers \
|
||||
libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libssl-dev
|
||||
|
||||
- name: Dawn Dependency
|
||||
id: dawn-depends
|
||||
run: |
|
||||
sudo apt-get install -y libxrandr-dev libxinerama-dev libxcursor-dev mesa-common-dev libx11-xcb-dev libxi-dev
|
||||
DAWN_VERSION="v20260908.214631"
|
||||
DAWN_OWNER="google"
|
||||
DAWN_REPO="dawn"
|
||||
DAWN_ASSET_NAME="Dawn-94c3c9cc0d5fb2e85aebb370fa8d37b71aa34655-ubuntu-latest-Release"
|
||||
echo "Fetching release asset from https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
curl -L -o artifact.tar.gz \
|
||||
"https://github.com/google/dawn/releases/download/${DAWN_VERSION}/${DAWN_ASSET_NAME}.tar.gz"
|
||||
mkdir dawn
|
||||
tar -xvf artifact.tar.gz -C dawn --strip-components=1
|
||||
|
||||
- name: ccache-buckets-restore
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
|
||||
with:
|
||||
key: webgpu-ubuntu-24.04
|
||||
folder: llama.cpp
|
||||
hf_bucket: ggml-org/cache
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
export Dawn_DIR=dawn/lib64/cmake/Dawn
|
||||
cmake -B build \
|
||||
-DGGML_WEBGPU=ON
|
||||
time cmake --build build --config Release -j $(nproc)
|
||||
|
||||
- name: ccache-buckets-save
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
uses: ./.github/actions/ccache-buckets
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN_CACHE_OUTPUT }}
|
||||
with:
|
||||
key: webgpu-ubuntu-24.04
|
||||
folder: llama.cpp
|
||||
evict-old-files: 1d
|
||||
hf_bucket: ggml-org/cache
|
||||
save: true
|
||||
|
||||
- name: Test
|
||||
id: cmake_test
|
||||
run: |
|
||||
cd build
|
||||
ctest -L main --verbose --timeout 900
|
||||
|
||||
@@ -35,7 +35,7 @@ permissions:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
CMAKE_ARGS: "-DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_SERVER=ON -DGGML_RPC=ON"
|
||||
CMAKE_ARGS: "-DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_SERVER=ON -DGGML_RPC=ON -DLLAMA_CONNECT=ON"
|
||||
|
||||
# note: run this workflow one at a time for better cache reuse
|
||||
concurrency:
|
||||
@@ -547,7 +547,7 @@ jobs:
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container: 'ghcr.io/snapdragon-toolchain/arm64-android:v0.7'
|
||||
container: 'ghcr.io/snapdragon-toolchain/arm64-android:v0.8'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -600,7 +600,7 @@ jobs:
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container: 'ghcr.io/snapdragon-toolchain/arm64-linux:v0.7'
|
||||
container: 'ghcr.io/snapdragon-toolchain/arm64-linux:v0.8'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -1478,6 +1478,7 @@ jobs:
|
||||
-DCMAKE_INSTALL_RPATH='$ORIGIN' \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||
-DGGML_NATIVE=OFF \
|
||||
-DLLAMA_CONNECT=ON \
|
||||
-DGGML_SYCL_F16=${{ matrix.fp16 }}
|
||||
time cmake --build build --config Release -j $(nproc)
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ option(LLAMA_BUILD_SERVER "llama: build server example"
|
||||
option(LLAMA_BUILD_APP "llama: build the unified binary" ${LLAMA_STANDALONE})
|
||||
option(LLAMA_BUILD_UI "llama: build the embedded Web UI for server" OFF)
|
||||
option(LLAMA_USE_PREBUILT_UI "llama: use prebuilt UI from HF Bucket when available" ON)
|
||||
option(LLAMA_CONNECT "llama: include the llama-connect tool (downloads a prebuilt binary)" OFF)
|
||||
|
||||
option(LLAMA_TOOLS_INSTALL "llama: install tools" ${LLAMA_TOOLS_INSTALL_DEFAULT})
|
||||
option(LLAMA_TESTS_INSTALL "llama: install tests" ON)
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ include("ggml/cmake/common.cmake")
|
||||
|
||||
function(llama_add_compile_flags)
|
||||
if (LLAMA_FATAL_WARNINGS)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM")
|
||||
list(APPEND C_FLAGS -Werror)
|
||||
list(APPEND CXX_FLAGS -Werror)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
|
||||
@@ -3494,6 +3494,26 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.ui = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_UI"));
|
||||
add_opt(common_arg(
|
||||
{"--connect"},
|
||||
string_format("(experimental) open a peer-to-peer tunnel for server using llama-connect (default: %s)", params.server_connect ? "enabled" : "disabled"),
|
||||
[](common_params & params) {
|
||||
params.server_connect = true;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_CONNECT"));
|
||||
add_opt(common_arg(
|
||||
{"--connect-code"}, "CODE",
|
||||
"manually specify a 40-character code for --connect (default: generate a new one for each run)",
|
||||
[](common_params & params, const std::string & value) {
|
||||
std::string val = value;
|
||||
string_replace_all(val, "-", "");
|
||||
string_replace_all(val, " ", "");
|
||||
if (val.size() != 40) {
|
||||
throw std::invalid_argument(string_format("error: invalid connect code '%s', must be 40 characters\n", value.c_str()));
|
||||
}
|
||||
params.server_connect_code = val;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_CONNECT_CODE"));
|
||||
add_opt(common_arg(
|
||||
{"--embedding", "--embeddings"},
|
||||
string_format("restrict to only support embedding use case; use only with dedicated embedding models (default: %s)", params.embedding ? "enabled" : "disabled"),
|
||||
|
||||
@@ -1175,6 +1175,7 @@ static const std::map<common_decision_type, std::string> COMMON_DECISION_TYPE_NA
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1, "lfm2-d1" },
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1_OMNI, "lfm2-d1-omni" },
|
||||
{ COMMON_DECISION_TYPE_GRANITE_SWITCH, "granite-switch"},
|
||||
{ COMMON_DECISION_TYPE_H2O_LIGHTNING, "h2o-lightning" },
|
||||
// for future models, ALWAYS add org name prefix to avoid name collisions, e.g. "orgname-modelname"
|
||||
};
|
||||
|
||||
@@ -1234,6 +1235,9 @@ common_gguf_info common_get_gguf_info(const std::string & fname) {
|
||||
info.n_ctx_train = gguf_get_val_u32(gguf_ctx.get(), ctx_id);
|
||||
}
|
||||
|
||||
// same key as llama_model_chat_template(model, nullptr)
|
||||
info.has_chat_tmpl = gguf_find_key(gguf_ctx.get(), "tokenizer.chat_template") >= 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -642,6 +642,10 @@ struct common_params {
|
||||
int32_t cache_ram_mib = 8192; // -1 = no limit, 0 - disable, 1 = 1 MiB, etc.
|
||||
bool cache_ram_explicit = false; // set by --cache-ram, decision models disable the cache otherwise
|
||||
|
||||
// llama-connect params
|
||||
bool server_connect = false;
|
||||
std::string server_connect_code = "";
|
||||
|
||||
std::string public_path = ""; // NOLINT
|
||||
std::string api_prefix = ""; // NOLINT
|
||||
std::string chat_template = ""; // NOLINT
|
||||
@@ -971,6 +975,7 @@ enum common_decision_type {
|
||||
COMMON_DECISION_TYPE_LFM2_D1, // same as openjev, the labels depend on the question type
|
||||
COMMON_DECISION_TYPE_LFM2_D1_OMNI, // same as laya, other prompt layout
|
||||
COMMON_DECISION_TYPE_GRANITE_SWITCH, // same as openjev, run by the decision aLoRA of granite switch
|
||||
COMMON_DECISION_TYPE_H2O_LIGHTNING, // same as openjev, the labels are read after an answer prefill
|
||||
COMMON_DECISION_TYPE_UNKNOWN, // a decision model of a type that is not supported
|
||||
};
|
||||
|
||||
@@ -980,6 +985,7 @@ common_decision_type common_get_decision_type(const struct llama_model * model);
|
||||
struct common_gguf_info {
|
||||
common_decision_type decision_type = COMMON_DECISION_TYPE_UNKNOWN; // UNKNOWN if the file is missing, unreadable, or invalid
|
||||
uint32_t n_ctx_train = 0; // 0 if unknown
|
||||
bool has_chat_tmpl = false; // has the default tokenizer.chat_template
|
||||
};
|
||||
|
||||
common_gguf_info common_get_gguf_info(const std::string & fname);
|
||||
|
||||
+53
-55
@@ -16,6 +16,7 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <cinttypes>
|
||||
@@ -2152,9 +2153,6 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
|
||||
uint16_t n_draft;
|
||||
|
||||
bool save_dynamic;
|
||||
bool save_static;
|
||||
|
||||
struct seq_info {
|
||||
size_t cache_size = 0; // number of tokens in n-gram cache
|
||||
|
||||
@@ -2168,54 +2166,72 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
common_speculative_impl_ngram_cache(
|
||||
const common_params_speculative & params,
|
||||
uint32_t n_seq,
|
||||
uint16_t n_draft,
|
||||
const std::string & path_static,
|
||||
const std::string & path_dynamic,
|
||||
bool save_dynamic,
|
||||
bool save_static)
|
||||
uint16_t n_draft)
|
||||
: common_speculative_impl(COMMON_SPECULATIVE_TYPE_NGRAM_CACHE, n_seq, n_draft)
|
||||
, params(params.ngram_cache)
|
||||
, n_draft(n_draft)
|
||||
, save_dynamic(save_dynamic)
|
||||
, save_static(save_static)
|
||||
{
|
||||
const auto & ngram = params.ngram_cache;
|
||||
|
||||
SPC_TRC("%s", "adding speculative implementation 'ngram-cache'\n");
|
||||
SPC_TRC("- n_draft=%d, cache_static=%s, cache_dynamic=%s\n",
|
||||
n_draft,
|
||||
path_static.empty() ? "none" : path_static.c_str(),
|
||||
path_dynamic.empty() ? "none" : path_dynamic.c_str());
|
||||
ngram.lookup_cache_static.empty() ? "none" : ngram.lookup_cache_static.c_str(),
|
||||
ngram.lookup_cache_dynamic.empty() ? "none" : ngram.lookup_cache_dynamic.c_str());
|
||||
|
||||
sinfos.resize(n_seq);
|
||||
|
||||
if (!path_static.empty()) {
|
||||
if (!ngram.lookup_cache_static.empty()) {
|
||||
try {
|
||||
auto ngram_cache_static = common_ngram_cache_load(path_static);
|
||||
auto ngram_cache_static = common_ngram_cache_load(ngram.lookup_cache_static);
|
||||
|
||||
for (auto & sinfo : sinfos) {
|
||||
sinfo.ngram_cache_static = ngram_cache_static;
|
||||
}
|
||||
} catch (...) {
|
||||
SPC_ERR("failed to open static lookup cache: %s", path_static.c_str());
|
||||
} catch (const std::ifstream::failure &) {
|
||||
SPC_ERR("failed to open static lookup cache: %s\n", ngram.lookup_cache_static.c_str());
|
||||
GGML_ABORT("Couldn't read static lookup cache");
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_dynamic.empty()) {
|
||||
if (!ngram.lookup_cache_dynamic.empty()) {
|
||||
try {
|
||||
auto ngram_cache_dynamic = common_ngram_cache_load(path_dynamic);
|
||||
auto ngram_cache_dynamic = common_ngram_cache_load(ngram.lookup_cache_dynamic);
|
||||
|
||||
for (auto & sinfo : sinfos) {
|
||||
sinfo.ngram_cache_dynamic = ngram_cache_dynamic;
|
||||
}
|
||||
} catch (...) {
|
||||
SPC_ERR("failed to open dynamic lookup cache: %s", path_dynamic.c_str());
|
||||
GGML_ABORT("Couldn't read dynamic lookup cache");
|
||||
} catch (const std::ifstream::failure &) {
|
||||
std::error_code ec;
|
||||
const bool exists = std::filesystem::exists(ngram.lookup_cache_dynamic, ec);
|
||||
if (!exists && !ec) {
|
||||
// missing file is created on destruction
|
||||
SPC_WRN("dynamic lookup cache file does not exist: %s, starting with empty cache\n",
|
||||
ngram.lookup_cache_dynamic.c_str());
|
||||
} else {
|
||||
SPC_ERR("failed to open dynamic lookup cache: %s\n", ngram.lookup_cache_dynamic.c_str());
|
||||
GGML_ABORT("Couldn't read dynamic lookup cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void begin(llama_seq_id /*seq_id*/, const llama_tokens & /*prompt*/) override {
|
||||
// noop
|
||||
~common_speculative_impl_ngram_cache() override {
|
||||
if (params.lookup_cache_dynamic.empty() || sinfos.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto & dynamic_cache = sinfos.front().ngram_cache_dynamic;
|
||||
for (auto & sinfo : sinfos) {
|
||||
common_ngram_cache_merge(dynamic_cache, sinfo.ngram_cache_context);
|
||||
}
|
||||
|
||||
SPC_INF("saving dynamic lookup cache at %s\n", params.lookup_cache_dynamic.c_str());
|
||||
common_ngram_cache_save(dynamic_cache, params.lookup_cache_dynamic);
|
||||
}
|
||||
|
||||
void begin(llama_seq_id seq_id, const llama_tokens & /*prompt*/) override {
|
||||
sinfos[seq_id].cache_size = 0;
|
||||
}
|
||||
|
||||
void draft_one(
|
||||
@@ -2226,22 +2242,6 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
|
||||
const auto & prompt = *dparams.prompt;
|
||||
|
||||
if (sinfo.cache_size < prompt.size() + 1) {
|
||||
llama_tokens tokens_new;
|
||||
tokens_new.reserve(prompt.size() + 1 - sinfo.cache_size);
|
||||
for (size_t j = sinfo.cache_size; j < prompt.size(); ++j) {
|
||||
tokens_new.push_back(prompt[j]);
|
||||
}
|
||||
tokens_new.push_back(dparams.id_last); // add the last token
|
||||
|
||||
// Update context ngram cache with new dparams.prompt:
|
||||
common_ngram_cache_update(
|
||||
sinfo.ngram_cache_context,
|
||||
LLAMA_NGRAM_MIN, LLAMA_NGRAM_MAX,
|
||||
tokens_new, tokens_new.size(), false);
|
||||
sinfo.cache_size = prompt.size() + 1;
|
||||
}
|
||||
|
||||
llama_tokens inp;
|
||||
inp.reserve(prompt.size() + 1);
|
||||
for (size_t j = 0; j < prompt.size(); ++j) {
|
||||
@@ -2249,6 +2249,16 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
}
|
||||
inp.push_back(dparams.id_last);
|
||||
|
||||
if (sinfo.cache_size < inp.size()) {
|
||||
// Update context ngram cache with the full history so n-grams
|
||||
// ending at the new tokens have their preceding context
|
||||
common_ngram_cache_update(
|
||||
sinfo.ngram_cache_context,
|
||||
LLAMA_NGRAM_MIN, LLAMA_NGRAM_MAX,
|
||||
inp, inp.size() - sinfo.cache_size, false);
|
||||
sinfo.cache_size = inp.size();
|
||||
}
|
||||
|
||||
result.push_back(dparams.id_last);
|
||||
|
||||
common_ngram_cache_draft(
|
||||
@@ -2309,20 +2319,12 @@ static common_ngram_map get_common_ngram_map(
|
||||
return common_ngram_map(size_key, size_value, key_only, min_hits);
|
||||
}
|
||||
|
||||
static common_speculative_impl_ngram_cache create_state_ngram_cache(
|
||||
static std::unique_ptr<common_speculative_impl_ngram_cache> create_state_ngram_cache(
|
||||
const common_speculative_config & config,
|
||||
uint32_t n_seq,
|
||||
const std::string & path_static,
|
||||
const std::string & path_dynamic) {
|
||||
uint32_t n_seq) {
|
||||
uint16_t n_draft = 8; // TODO get from config?
|
||||
|
||||
// TODO bool param in common/common.h to set save_static/save_dynamic?
|
||||
bool save_static = false;
|
||||
bool save_dynamic = false;
|
||||
|
||||
common_speculative_impl_ngram_cache state(config.params, n_seq, n_draft, path_static, path_dynamic, save_static, save_dynamic);
|
||||
|
||||
return state;
|
||||
return std::make_unique<common_speculative_impl_ngram_cache>(config.params, n_seq, n_draft);
|
||||
}
|
||||
|
||||
std::string common_speculative_type_name_str(const std::vector<common_speculative_type> & types) {
|
||||
@@ -2822,11 +2824,7 @@ common_speculative * common_speculative_init(common_params_speculative & params,
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE: {
|
||||
auto state = create_state_ngram_cache(
|
||||
config, n_seq,
|
||||
params.ngram_cache.lookup_cache_static,
|
||||
params.ngram_cache.lookup_cache_dynamic);
|
||||
impls.push_back(std::make_unique<common_speculative_impl_ngram_cache>(state));
|
||||
impls.push_back(create_state_ngram_cache(config, n_seq));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -200,7 +200,9 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MiniMaxM3SparseForCausalLM": "minimax",
|
||||
"MiniMaxM3SparseForConditionalGeneration": "minimax",
|
||||
"Ministral3ForCausalLM": "mistral3",
|
||||
"Ministral3Model": "mistral3",
|
||||
"Mistral3ForConditionalGeneration": "mistral3",
|
||||
"Mistral3Model": "mistral3",
|
||||
"VoxtralRealtimeForConditionalGeneration": "mistral3",
|
||||
"MistralForCausalLM": "llama",
|
||||
"MixtralForCausalLM": "llama",
|
||||
@@ -226,6 +228,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MuseSparkForConditionalGeneration": "muse_spark",
|
||||
"OpenELMForCausalLM": "openelm",
|
||||
"OpenJevModel": "qwen",
|
||||
"H2OLightningModel": "h2o_lightning",
|
||||
"OrionForCausalLM": "orion",
|
||||
"PLMForCausalLM": "plm",
|
||||
"PLaMo2ForCausalLM": "plamo",
|
||||
@@ -357,6 +360,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"Mistral3ForConditionalGeneration": "llava",
|
||||
"Mistral3Model": "llava",
|
||||
"NemotronH_Omni_Reasoning_V3": "nemotron",
|
||||
"NemotronH_Nano_VL_V2": "nemotron",
|
||||
"MuseGlimmerForConditionalGeneration": "muse_glimmer",
|
||||
@@ -375,6 +379,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"Qwen3VLForConditionalGeneration": "qwen3vl",
|
||||
"Qwen3VLMoeForConditionalGeneration": "qwen3vl",
|
||||
"OpenJevModel": "qwen3vl",
|
||||
"H2OLightningModel": "h2o_lightning",
|
||||
"Qwen3_5ForConditionalGeneration": "qwen3vl",
|
||||
"Qwen3_5MoeForConditionalGeneration": "qwen3vl",
|
||||
"Qwen4ExpForConditionalGeneration": "qwen4exp",
|
||||
|
||||
+34
-10
@@ -10,6 +10,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
from enum import IntEnum
|
||||
from pathlib import Path
|
||||
from hashlib import sha256
|
||||
@@ -2169,20 +2170,34 @@ class TextModel(ModelBase):
|
||||
special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True)
|
||||
special_vocab.add_to_gguf(self.gguf_writer)
|
||||
|
||||
def _is_hybriddna(self) -> bool:
|
||||
tokenizer_config_file = self.dir_model / "tokenizer_config.json"
|
||||
if not tokenizer_config_file.is_file():
|
||||
return False
|
||||
with open(tokenizer_config_file, "r", encoding="utf-8") as f:
|
||||
return json.load(f).get("tokenizer_class") == "HybridDNATokenizer"
|
||||
|
||||
def _set_vocab_hybriddna(self):
|
||||
from transformers import AutoTokenizer
|
||||
tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True)
|
||||
vocab_size = self.hparams.get("vocab_size", len(tokenizer.vocab)) # ty: ignore[unresolved-attribute]
|
||||
assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute]
|
||||
vocab = tokenizer.get_vocab()
|
||||
vocab_size = self.hparams.get("vocab_size", len(vocab))
|
||||
assert max(vocab.values()) < vocab_size
|
||||
|
||||
reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in tokenizer.vocab.items()} # ty: ignore[unresolved-attribute]
|
||||
# k-mers can share text with a base-vocab BPE token (e.g. CCCCCC) and get
|
||||
# dropped by get_vocab(); a reserved marker suffix (U+E000) keeps each
|
||||
# k-mer's own id (llama.cpp strips it on detokenization)
|
||||
for kmer in tokenizer.kmers: # ty: ignore[unresolved-attribute]
|
||||
reverse_vocab = {id_: encoded_tok for encoded_tok, id_ in vocab.items()}
|
||||
# k-mers can share text with a base-vocab BPE token (e.g. CCCCCC) and get dropped by get_vocab()
|
||||
# a reserved marker suffix (U+E000) keeps each k-mer's own id (llama.cpp strips it on detokenization)
|
||||
# tail="pad_n" (Carbon-14B) adds N-padded tail tokens ("CT" -> "CTNNNN") that get the same marker
|
||||
# llama.cpp detects them to pad with N and strips the padding on detokenization, tail="char" is unsupported
|
||||
tail = getattr(tokenizer, "tail", "pad_a")
|
||||
if tokenizer.k != 6 or tail not in ("pad_a", "pad_n"):
|
||||
raise NotImplementedError(f"HybridDNATokenizer with k={tokenizer.k}, tail={tail!r}")
|
||||
for kmer in [*tokenizer.kmers, *getattr(tokenizer, "npad_tokens", [])]:
|
||||
reverse_vocab[tokenizer.dna_token_to_id[kmer]] = kmer + "\ue000" # ty: ignore[unresolved-attribute]
|
||||
added_vocab = tokenizer.get_added_vocab() # ty: ignore[unresolved-attribute]
|
||||
added_tokens_decoder = tokenizer.added_tokens_decoder # ty: ignore[unresolved-attribute]
|
||||
# older HybridDNATokenizer versions (Carbon-500M/3B/8B) do not register the base tokenizer's added tokens (<|im_start|>, <think>, ...) as their own
|
||||
# but they are still encoded as special tokens: take their types from the base tokenizer
|
||||
base_tokenizer = getattr(tokenizer, "_base_tokenizer", tokenizer)
|
||||
added_tokens_decoder = {**base_tokenizer.added_tokens_decoder, **tokenizer.added_tokens_decoder} # ty: ignore[unresolved-attribute]
|
||||
|
||||
tokens: list[str] = []
|
||||
toktypes: list[int] = []
|
||||
@@ -2192,7 +2207,7 @@ class TextModel(ModelBase):
|
||||
toktypes.append(gguf.TokenType.UNUSED)
|
||||
else:
|
||||
token: str = reverse_vocab[i]
|
||||
if token in added_vocab:
|
||||
if i in added_tokens_decoder:
|
||||
if added_tokens_decoder[i].special or self.does_token_look_special(token):
|
||||
toktypes.append(gguf.TokenType.CONTROL)
|
||||
else:
|
||||
@@ -2204,10 +2219,19 @@ class TextModel(ModelBase):
|
||||
tokpre = self.get_vocab_base_pre(tokenizer)
|
||||
self.gguf_writer.add_tokenizer_model("hybriddna")
|
||||
self.gguf_writer.add_tokenizer_pre(tokpre)
|
||||
self.gguf_writer.add_dna_auto_tags(getattr(tokenizer, "auto_dna_tags", False))
|
||||
self.gguf_writer.add_token_list(tokens)
|
||||
self.gguf_writer.add_token_types(toktypes)
|
||||
|
||||
special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True)
|
||||
if not special_vocab.merges:
|
||||
# Carbon-500M/3B/8B ship only tokenizer.py, no tokenizer.json
|
||||
# take the merges and special token ids from the base BPE tokenizer, keeping the model's own chat template (if any)
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
base_tokenizer.save_pretrained(tmp)
|
||||
base_special_vocab = gguf.SpecialVocab(Path(tmp), load_merges=True)
|
||||
base_special_vocab.chat_template = special_vocab.chat_template
|
||||
special_vocab = base_special_vocab
|
||||
special_vocab.add_to_gguf(self.gguf_writer)
|
||||
|
||||
def _set_vocab_qwen(self):
|
||||
|
||||
@@ -999,6 +999,8 @@ class Gemma4VisionAudioModel(MmprojModel):
|
||||
assert self.hparams_vision is not None
|
||||
self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.GEMMA4V)
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision.get("layer_norm_eps", 1e-6))
|
||||
assert self.hparams_vision.get("hidden_activation", "gelu_pytorch_tanh") == "gelu_pytorch_tanh"
|
||||
self.gguf_writer.add_vision_use_gelu(True)
|
||||
|
||||
# audio params
|
||||
if self.has_audio_encoder:
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .base import ModelBase, gguf, logger
|
||||
from .qwen import Qwen3_5TextModel
|
||||
from .qwen3vl import Qwen3VLVisionModel
|
||||
|
||||
|
||||
def _is_h2o_lightning_checkpoint(dir_model: Path) -> bool:
|
||||
return (dir_model / "h2o_lightning_shim.py").is_file() and (dir_model / "serve_config.json").is_file()
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_h2o_lightning_checkpoint)
|
||||
def _load_h2o_lightning_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
logger.info("gguf: detected H2O-Lightning checkpoint")
|
||||
hparams = ModelBase.load_hparams(dir_model, False, guess=False)
|
||||
hparams["architectures"] = ["H2OLightningModel"]
|
||||
return hparams
|
||||
|
||||
|
||||
@ModelBase.register("H2OLightningModel")
|
||||
@ModelBase.example("h2oai/h2o-lightning-4b")
|
||||
class H2OLightningModel(Qwen3_5TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.QWEN35
|
||||
|
||||
# prompt and calibration follow serve_config.json and h2o_lightning_shim.py of the model repo
|
||||
# the state budget is hard-coded in server-decision.cpp
|
||||
_MAX_STATE_TOKENS = 32000
|
||||
_DEFAULT_INSTRUCTIONS = "Answer the question below."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
with open(self.dir_model / "serve_config.json", "r", encoding="utf-8") as f:
|
||||
self._serve_config = json.load(f)
|
||||
prompt = self._serve_config["prompt"]
|
||||
expected = {
|
||||
"state_compact": (prompt.get("state_compact", True), True),
|
||||
"max_state_tokens": (prompt.get("max_state_tokens"), self._MAX_STATE_TOKENS),
|
||||
"layout": (prompt.get("layout", "plain"), "plain"),
|
||||
"chat_template": (prompt.get("chat_template", True), True),
|
||||
"enable_thinking": (prompt.get("enable_thinking", False), False),
|
||||
}
|
||||
for name, (got, want) in expected.items():
|
||||
if got != want:
|
||||
raise ValueError(f"serve_config.json: {name} is {got!r}, the h2o-lightning decision type expects {want!r}")
|
||||
|
||||
def get_vocab_base_pre(self, tokenizer) -> str:
|
||||
# transformers loads this checkpoint as Qwen2Tokenizer: the model was trained and is served with the Qwen2 split
|
||||
pre = super().get_vocab_base_pre(tokenizer)
|
||||
if pre != "qwen35":
|
||||
raise ValueError(f"expected the Qwen3.5 tokenizer, got pre-tokenizer {pre!r}")
|
||||
return "qwen2"
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_chat_template([{"name": "systemone", "template": self._systemone_template()}])
|
||||
|
||||
def _systemone_template(self) -> str:
|
||||
# the server gives the template texts rendered as the shim renders them, it only lays them out
|
||||
prompt = self._serve_config["prompt"]
|
||||
nl = "{{ '\\n' }}"
|
||||
description = (
|
||||
"{% if type == 'noul' %}"
|
||||
"{% if o.description %}{{ o.description }}{% elif o.key == 'true' %}the statement holds{% else %}it does not{% endif %}"
|
||||
"{% elif o.description is none %}{{ o.key }}{% else %}{{ o.description }}{% endif %}"
|
||||
)
|
||||
user = (
|
||||
"{% set user %}record: {{ state }}" + nl
|
||||
+ "question: {{ instructions if instructions else '" + self._DEFAULT_INSTRUCTIONS + "' }}" + nl
|
||||
+ "options:{% for o in options %}" + nl + "{{ o.label | trim }}) {{ o.key }}: " + description + "{% endfor %}{% endset %}"
|
||||
)
|
||||
return (
|
||||
"<|im_start|>system" + nl + prompt["system"].strip() + "<|im_end|>" + nl
|
||||
+ "<|im_start|>user" + nl + "{% for image in images %}{{ image }}{% endfor %}" + user + "{{ user | trim }}<|im_end|>" + nl
|
||||
+ "<|im_start|>assistant" + nl + "<think>{{ '\\n\\n' }}</think>{{ '\\n\\n' }}" + prompt["prefill"]
|
||||
)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_decision_type(gguf.DecisionType.H2O_LIGHTNING)
|
||||
# the answer follows "Answer:", so the token of a label has a leading space
|
||||
cfg = self._serve_config
|
||||
self.gguf_writer.add_decision_labels([" " + label for label in cfg["labels"][:cfg.get("max_options", len(cfg["labels"]))]])
|
||||
for name in ("choice", "noul", "score"):
|
||||
self.gguf_writer.add_decision_temperature(name, self._serve_config["temperature"][name])
|
||||
# a request with images has this temperature for all question types
|
||||
self.gguf_writer.add_decision_temperature("image", self._serve_config["image"]["temperature"])
|
||||
|
||||
|
||||
@ModelBase.register("H2OLightningModel")
|
||||
class H2OLightningVisionModel(Qwen3VLVisionModel):
|
||||
pass
|
||||
@@ -374,6 +374,16 @@ class LFM2VLModel(MmprojModel):
|
||||
if resize_algo is None:
|
||||
raise ValueError(f"unsupported resample: {resample}")
|
||||
self.gguf_writer.add_vision_image_resize_algo(resize_algo)
|
||||
# tiling params from processor_config.json (do_image_splitting=False -> single tile, i.e. no tiling)
|
||||
preproc = self.preprocessor_config
|
||||
if preproc.get("do_image_splitting", True):
|
||||
self.gguf_writer.add_vision_preproc_min_tiles(preproc.get("min_tiles", 2))
|
||||
self.gguf_writer.add_vision_preproc_max_tiles(preproc.get("max_tiles", 10))
|
||||
else:
|
||||
self.gguf_writer.add_vision_preproc_min_tiles(1)
|
||||
self.gguf_writer.add_vision_preproc_max_tiles(1)
|
||||
self.gguf_writer.add_vision_preproc_tile_size(preproc.get("tile_size", 512))
|
||||
self.gguf_writer.add_vision_preproc_max_pixels_tolerance(preproc.get("max_pixels_tolerance", 2.0))
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
|
||||
+3
-2
@@ -129,8 +129,9 @@ class LlamaModel(TextModel):
|
||||
tokenizer_config_json = json.load(f)
|
||||
if (add_prefix_space := tokenizer_config_json.get("add_prefix_space")) is not None:
|
||||
self.gguf_writer.add_add_space_prefix(add_prefix_space)
|
||||
if tokenizer_config_json.get("tokenizer_class") == "HybridDNATokenizer":
|
||||
return self._set_vocab_hybriddna()
|
||||
|
||||
if self._is_hybriddna():
|
||||
return self._set_vocab_hybriddna()
|
||||
|
||||
try:
|
||||
self._set_vocab_sentencepiece()
|
||||
|
||||
+22
-1
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from typing import Iterable, TYPE_CHECKING
|
||||
from typing import Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
@@ -128,3 +128,24 @@ class LlavaVisionModel(MmprojModel):
|
||||
yield from super().modify_tensors(img_break_embd, name, bid)
|
||||
|
||||
return # skip other tensors
|
||||
|
||||
|
||||
@ModelBase.register("Mistral3Model")
|
||||
@ModelBase.example("nvidia/Nemotron-3.5-Embed-1B-BF16")
|
||||
class Mistral3EmbeddingVisionModel(LlavaVisionModel):
|
||||
def __init__(self, dir_model, *args, **kwargs):
|
||||
hparams = ModelBase.load_hparams(dir_model, is_mistral_format=False)
|
||||
text_config = hparams.get("text_config", hparams)
|
||||
if text_config.get("is_causal") is not False:
|
||||
raise NotImplementedError("Expected a Nemotron Embed model for Mistral3Model, but is_causal is not False")
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
if item[0].endswith("embed_tokens.weight"):
|
||||
return ModelBase.filter_tensors(item)
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_vision_preproc_image_size(self.preprocessor_config["size"]["longest_edge"])
|
||||
|
||||
+28
-6
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import ModelBase, TextModel, gguf
|
||||
from .base import ModelBase, TextModel, get_model_architecture, gguf
|
||||
|
||||
from .deepseek import DeepseekV2Model
|
||||
from .llama import LlamaModel
|
||||
@@ -13,9 +13,11 @@ from .llama import LlamaModel
|
||||
|
||||
@ModelBase.register(
|
||||
"Mistral3ForConditionalGeneration",
|
||||
"Mistral3Model",
|
||||
"Ministral3ForCausalLM",
|
||||
"Ministral3Model",
|
||||
)
|
||||
@ModelBase.example("mistralai/Mistral-Small-3.1-24B-Instruct-2503", "hf-tiny-v2/tiny-random-Ministral3ForCausalLM")
|
||||
@ModelBase.example("mistralai/Mistral-Small-3.1-24B-Instruct-2503", "hf-tiny-v2/tiny-random-Ministral3ForCausalLM", "nvidia/Nemotron-3-Embed-1B-BF16", "nvidia/Nemotron-3.5-Embed-1B-BF16")
|
||||
class Mistral3Model(TextModel):
|
||||
class Ministral3Model(LlamaModel):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3
|
||||
@@ -29,6 +31,18 @@ class Mistral3Model(TextModel):
|
||||
self.gguf_writer.add_rope_scaling_yarn_log_mul(rope_params["mscale_all_dim"])
|
||||
self.gguf_writer.add_attn_temperature_scale(rope_params["llama_4_scaling_beta"])
|
||||
|
||||
class Mistral3EmbedNemotronModel(Ministral3Model):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_add_bos_token(False)
|
||||
self.gguf_writer.add_add_eos_token(False)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._try_set_pooling_type()
|
||||
|
||||
class Mistral4Model(DeepseekV2Model):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL4
|
||||
skip_mtp = False # model contains no MTP layers, so no need to skip
|
||||
@@ -42,12 +56,20 @@ class Mistral3Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.MISTRAL3 # unused
|
||||
impl: TextModel
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, dir_model, *args, **kwargs):
|
||||
hparams = ModelBase.load_hparams(dir_model, is_mistral_format=False)
|
||||
hf_arch = get_model_architecture(hparams, self.model_type)
|
||||
if hf_arch in ("Ministral3Model", "Mistral3Model"):
|
||||
text_config = hparams.get("text_config", hparams)
|
||||
if text_config.get("is_causal") is not False:
|
||||
raise NotImplementedError(f"Expected a Nemotron Embed model for {hf_arch}, but is_causal is not False")
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
if self.hparams.get("model_type") == "mistral4":
|
||||
self.impl = Mistral3Model.Mistral4Model(*args, **kwargs)
|
||||
self.impl = Mistral3Model.Mistral4Model(dir_model, *args, **kwargs)
|
||||
elif self.hf_arch in ("Ministral3Model", "Mistral3Model"):
|
||||
self.impl = Mistral3Model.Mistral3EmbedNemotronModel(dir_model, *args, **kwargs)
|
||||
else:
|
||||
self.impl = Mistral3Model.Ministral3Model(*args, **kwargs)
|
||||
self.impl = Mistral3Model.Ministral3Model(dir_model, *args, **kwargs)
|
||||
|
||||
def set_vocab(self):
|
||||
self.impl.set_vocab()
|
||||
|
||||
@@ -59,6 +59,10 @@ class Qwen2Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.QWEN2
|
||||
|
||||
def set_vocab(self):
|
||||
# HuggingFaceBio/Carbon-14B: Qwen3 with the HybridDNATokenizer (Qwen3 BPE plus DNA 6-mers)
|
||||
if self._is_hybriddna():
|
||||
self._set_vocab_hybriddna()
|
||||
return
|
||||
try:
|
||||
self._set_vocab_sentencepiece()
|
||||
except FileNotFoundError:
|
||||
|
||||
@@ -6,8 +6,8 @@ The cross-compilation toolchain images are provided by the
|
||||
[Qualcomm Snapdragon Toolchain registry](https://github.com/snapdragon-toolchain).
|
||||
These Docker images include the Android NDK, OpenCL SDK, Hexagon SDK, CMake, and the necessary cross-compilers:
|
||||
|
||||
* **Android toolchain**: `ghcr.io/snapdragon-toolchain/arm64-android:v0.7`
|
||||
* **Linux toolchain**: `ghcr.io/snapdragon-toolchain/arm64-linux:v0.7`
|
||||
* **Android toolchain**: `ghcr.io/snapdragon-toolchain/arm64-android:v0.8`
|
||||
* **Linux toolchain**: `ghcr.io/snapdragon-toolchain/arm64-linux:v0.8`
|
||||
|
||||
The unified build utility (`scripts/snapdragon/build.py`) automatically pulls
|
||||
and orchestrates these containers to perform target compilation.
|
||||
@@ -40,7 +40,7 @@ Alternatively, you can build llama.cpp manually by entering the cross-compilatio
|
||||
|
||||
```bash
|
||||
# Start the cross-compilation container manually:
|
||||
~/src/llama.cpp$ docker run -it --rm -u $(id -u):$(id -g) --volume $(pwd):/workspace --platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-android:v0.7
|
||||
~/src/llama.cpp$ docker run -it --rm -u $(id -u):$(id -g) --volume $(pwd):/workspace --platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-android:v0.8
|
||||
|
||||
# Inside the container, build the project using presets:
|
||||
[d]/workspace> cp docs/backend/snapdragon/CMakeUserPresets.json .
|
||||
@@ -53,7 +53,7 @@ Preset CMake variables:
|
||||
GGML_HEXAGON="ON"
|
||||
GGML_OPENCL="ON"
|
||||
GGML_OPENMP="OFF"
|
||||
HEXAGON_SDK_ROOT="/opt/hexagon/6.6.0.0"
|
||||
HEXAGON_SDK_ROOT="/opt/hexagon/6.7.0.0"
|
||||
...
|
||||
-- Including OpenCL backend
|
||||
-- Including Hexagon backend
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The cross-compilation is performed using the Snapdragon Linux Docker toolchain image (see
|
||||
[github.com/snapdragon-toolchain](https://github.com/snapdragon-toolchain)):
|
||||
|
||||
* **Linux toolchain**: `ghcr.io/snapdragon-toolchain/arm64-linux:v0.7`
|
||||
* **Linux toolchain**: `ghcr.io/snapdragon-toolchain/arm64-linux:v0.8`
|
||||
|
||||
The unified build utility (`scripts/snapdragon/build.py`) automatically pulls
|
||||
and orchestrates this container to perform target compilation. You only need to
|
||||
@@ -29,7 +29,7 @@ Alternatively, you can build llama.cpp manually by entering the cross-compilatio
|
||||
|
||||
```bash
|
||||
# Start the cross-compilation container manually:
|
||||
~/src/llama.cpp$ docker run -it --rm -u $(id -u):$(id -g) --volume $(pwd):/workspace --platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-linux:v0.7
|
||||
~/src/llama.cpp$ docker run -it --rm -u $(id -u):$(id -g) --volume $(pwd):/workspace --platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-linux:v0.8
|
||||
|
||||
# Inside the container, build the project using presets:
|
||||
[d]/workspace> cp docs/backend/snapdragon/CMakeUserPresets.json .
|
||||
|
||||
@@ -8,7 +8,7 @@ Native Windows 11 arm64 builds have the following tool dependencies:
|
||||
- UCRT and Driver Kit
|
||||
- LLVM core libraries and Clang compiler (winget)
|
||||
- CMake, Git, Python (winget)
|
||||
- Hexagon SDK Community Edition 6.6 or later (see below)
|
||||
- Hexagon SDK Community Edition 6.7 or later (see below)
|
||||
- OpenCL SDK 2.3 or later (see below)
|
||||
|
||||
Note: The rest of the **Windows** build process assumes that you're running natively in Powershell.
|
||||
@@ -54,15 +54,15 @@ c:\Qualcomm\OpenCL_SDK\2.3.2
|
||||
|
||||
Either use the trimmed down version (optimized for CI) from
|
||||
|
||||
https://github.com/snapdragon-toolchain/hexagon-sdk/releases/download/v6.6.0.0/hexagon-sdk-v6.6.0.0-arm64-wos.tar.xz
|
||||
https://github.com/snapdragon-toolchain/hexagon-sdk/releases/download/v6.7.0.0/hexagon-sdk-v6.7.0.0-arm64-wos.tar.xz
|
||||
|
||||
Or download the complete official version from
|
||||
|
||||
https://softwarecenter.qualcomm.com/catalog/item/Hexagon_SDK?version=6.6.0.0
|
||||
https://softwarecenter.qualcomm.com/catalog/item/Hexagon_SDK?version=6.7.0.0
|
||||
|
||||
Unzip/untar the archive into
|
||||
```
|
||||
c:\Qualcomm\Hexagon_SDK\6.6.0.0
|
||||
c:\Qualcomm\Hexagon_SDK\6.7.0.0
|
||||
```
|
||||
|
||||
## Install the latest Adreno GPU driver
|
||||
@@ -150,8 +150,8 @@ The overall Hexagon backend build procedure for Windows on Snapdragon is the sam
|
||||
However, additional settings are required for generating and signing HTP Ops libraries.
|
||||
```
|
||||
> $env:OPENCL_SDK_ROOT="C:\Qualcomm\OpenCL_SDK\2.3.2"
|
||||
> $env:HEXAGON_SDK_ROOT="C:\Qualcomm\Hexagon_SDK\6.6.0.0"
|
||||
> $env:HEXAGON_TOOLS_ROOT="C:\Qualcomm\Hexagon_SDK\6.6.0.0\tools\HEXAGON_Tools\19.0.07"
|
||||
> $env:HEXAGON_SDK_ROOT="C:\Qualcomm\Hexagon_SDK\6.7.0.0"
|
||||
> $env:HEXAGON_TOOLS_ROOT="C:\Qualcomm\Hexagon_SDK\6.7.0.0\tools\HEXAGON_Tools\21.0.04"
|
||||
> $env:HEXAGON_HTP_CERT="c:\Users\MyUsers\Certs\ggml-htp-v1.pfx"
|
||||
> $env:WINDOWS_SDK_BIN="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
> [!WARNING]
|
||||
> **Note:** zDNN is **not** the same as ZenDNN.
|
||||
>
|
||||
> - **zDNN** (this page): IBM's Deep Neural Network acceleration library for IBM Z & LinuxONE Mainframes
|
||||
> - **ZenDNN**: AMD's deep learning library for AMD EPYC CPUs ([see ZenDNN documentation](ZenDNN.md))
|
||||
|
||||
@@ -46,6 +47,14 @@ The IBM zDNN backend has the following CMake options that control the behaviour
|
||||
| --------------------- | ------------- | -------------------------------------------------------------------------------------- |
|
||||
| `GGML_ZDNN_HIGH_PREC` | `0` | Set to `1` to requantize Q8_0 weights in smaller groups, for lower loss at lower speed |
|
||||
|
||||
## Performance Tunables
|
||||
|
||||
| Flag Option | Default Value | Description |
|
||||
| ----------------------- | ------------- | ------------------------------------------------------ |
|
||||
| `--flash-attn` or `-fa` | `auto` | Set to `on` to run attention on the NNPA (Recommended) |
|
||||
|
||||
Note: Set `-fa on` explicitly. Flash Attention fuses each layer's attention (the KQ matrix multiplication, softmax and KQV matrix multiplication) into one `FLASH_ATTN_EXT` operation, which zDNN runs on the NNPA instead of the CPU. The speedup grows with the context length. With `auto`, llama.cpp currently turns Flash Attention off, because the operation runs on a different device than the layer. zDNN runs it when the KV cache is F16 (the default), the model uses no attention sinks, ALiBi or logit softcapping, and there is one KV cache stream (one sequence, or `--kv-unified`). Otherwise it falls back to the CPU.
|
||||
|
||||
## 1. Install zDNN Library
|
||||
|
||||
Note: A minimum of libzdnn 1.1.2 is required for the IBM zDNN backend to link correctly.
|
||||
|
||||
@@ -852,6 +852,36 @@ WebGPU allows cross-platform access to the GPU from supported browsers. We utili
|
||||
|
||||
Follow the instructions [here](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/) to download or build the emdawnwebgpu package (Note that it might be safer to build the emdawnwebgpu package locally, so that it stays in sync with the version of Dawn you have installed above). When building using CMake, the path to the emdawnwebgpu port file needs to be set with the flag `EMDAWNWEBGPU_DIR`.
|
||||
|
||||
## WASI
|
||||
|
||||
Requires [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) 34+ with `WASI_SDK_PATH` set. Only the libraries build (`llama-common` does not support WASI yet), so link your program against them:
|
||||
|
||||
```bash
|
||||
FLAGS="-msimd128 -D_WASI_EMULATED_SIGNAL -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
|
||||
cmake -B build-wasi -DCMAKE_TOOLCHAIN_FILE=$WASI_SDK_PATH/share/cmake/wasi-sdk-p2.cmake \
|
||||
-DCMAKE_C_FLAGS="$FLAGS" -DCMAKE_CXX_FLAGS="$FLAGS" -DCMAKE_HAVE_LIBC_PTHREAD=1 \
|
||||
-DBUILD_SHARED_LIBS=OFF -DGGML_NATIVE=OFF -DGGML_OPENMP=OFF -DLLAMA_BUILD_COMMON=OFF
|
||||
cmake --build build-wasi --target llama
|
||||
|
||||
$WASI_SDK_PATH/bin/clang++ --target=wasm32-wasip3 -O3 $FLAGS -I include -I ggml/include examples/simple/simple.cpp \
|
||||
build-wasi/src/libllama.a build-wasi/ggml/src/libggml*.a -lunwind -ldl -Wl,-z,stack-size=8388608 -o llama-simple.wasm
|
||||
wasmtime run -W exceptions=y --dir models llama-simple.wasm -m models/model.gguf "Hello"
|
||||
```
|
||||
|
||||
### wasi:webgpu
|
||||
|
||||
The WebGPU backend can target [wasi:webgpu](https://github.com/WebAssembly/wasi-webgpu) via [wasi-webgpu-headers](https://github.com/wasi-gfx/wasi-webgpu-headers) (with its submodule) and Dawn's `dawn-headers` release. Point `Dawn_DIR` at a directory containing this `DawnConfig.cmake` next to both:
|
||||
|
||||
```cmake
|
||||
set(_wasi_webgpu "${CMAKE_CURRENT_LIST_DIR}/wasi-webgpu-headers")
|
||||
add_library(dawn::webgpu_dawn INTERFACE IMPORTED)
|
||||
set_target_properties(dawn::webgpu_dawn PROPERTIES
|
||||
INTERFACE_SOURCES "${_wasi_webgpu}/webgpu.c;${_wasi_webgpu}/async_futures.c;${_wasi_webgpu}/imports.c;${_wasi_webgpu}/imports_component_type.o"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/dawn-headers/include/webgpu_upstream;${_wasi_webgpu}")
|
||||
```
|
||||
|
||||
Then build as above, but with `wasi-sdk-p3.cmake` / `--target=wasm32-wasip3`, `-DGGML_WEBGPU=ON -DDawn_DIR=...` at configure, and `build-wasi/ggml/src/ggml-webgpu/libggml-webgpu.a` added at link. See `.github/workflows/build-wasi.yml` for a more complete example.
|
||||
|
||||
## IBM Z & LinuxONE
|
||||
|
||||
To read documentation for how to build on IBM Z & LinuxONE, [click here](./build-s390x.md)
|
||||
|
||||
@@ -2,10 +2,16 @@ function(ggml_get_flags CCID CCVER)
|
||||
set(C_FLAGS "")
|
||||
set(CXX_FLAGS "")
|
||||
|
||||
if (CCID MATCHES "Clang")
|
||||
if (CCID MATCHES "Clang" OR CCID MATCHES "IntelLLVM")
|
||||
set(C_FLAGS -Wunreachable-code-break -Wunreachable-code-return)
|
||||
set(CXX_FLAGS -Wunreachable-code-break -Wunreachable-code-return -Wmissing-prototypes -Wextra-semi)
|
||||
|
||||
if (CCID STREQUAL "IntelLLVM")
|
||||
# for Intel DPC++ and LLVM SYCL, using debug does not disable optimization
|
||||
list(APPEND C_FLAGS -Rno-debug-disables-optimization)
|
||||
list(APPEND CXX_FLAGS -Rno-debug-disables-optimization)
|
||||
endif()
|
||||
|
||||
if (
|
||||
(CCID STREQUAL "Clang" AND CCVER VERSION_GREATER_EQUAL 3.8.0) OR
|
||||
(CCID STREQUAL "AppleClang" AND CCVER VERSION_GREATER_EQUAL 7.3.0)
|
||||
|
||||
@@ -2226,6 +2226,7 @@ bool ggml_op_alloc_size_may_expand(enum ggml_op op) {
|
||||
case GGML_OP_CUMSUM:
|
||||
case GGML_OP_ARGSORT:
|
||||
case GGML_OP_TOP_K:
|
||||
case GGML_OP_ARGMAX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -42,6 +42,7 @@ extern "C" {
|
||||
#define GGML_CPU_COMPUTE_FP32_TO_FP16(x) neon_compute_fp32_to_fp16(x)
|
||||
|
||||
#define GGML_CPU_FP16_TO_FP32(x) GGML_CPU_COMPUTE_FP16_TO_FP32(x)
|
||||
#define GGML_CPU_FP32_TO_FP16(x) GGML_CPU_COMPUTE_FP32_TO_FP16(x)
|
||||
|
||||
static inline float neon_compute_fp16_to_fp32(ggml_fp16_t h) {
|
||||
__fp16 tmp;
|
||||
@@ -150,9 +151,15 @@ extern float ggml_table_f32_ue4m3[1 << 8];
|
||||
#define GGML_CPU_FP32_TO_FP16(x) GGML_CPU_COMPUTE_FP32_TO_FP16(x)
|
||||
#endif
|
||||
|
||||
// On ARM NEON, it's quicker to directly convert x -> x instead of calling into ggml_lookup_fp16_to_fp32,
|
||||
// so we define GGML_CPU_FP16_TO_FP32 and GGML_CPU_FP32_TO_FP16 elsewhere for NEON.
|
||||
// This is also true for POWER9.
|
||||
// a branch that defines a GGML_CPU_COMPUTE_* macro must also select it, or the fallback takes over silently
|
||||
#if defined(GGML_CPU_COMPUTE_FP16_TO_FP32) && !defined(GGML_CPU_FP16_TO_FP32) && !defined(__F16C__) // see: https://github.com/ggml-org/llama.cpp/issues/30342
|
||||
#error "a branch defined GGML_CPU_COMPUTE_FP16_TO_FP32 without selecting it as GGML_CPU_FP16_TO_FP32"
|
||||
#endif
|
||||
#if defined(GGML_CPU_COMPUTE_FP32_TO_FP16) && !defined(GGML_CPU_FP32_TO_FP16)
|
||||
#error "a branch defined GGML_CPU_COMPUTE_FP32_TO_FP16 without selecting it as GGML_CPU_FP32_TO_FP16"
|
||||
#endif
|
||||
|
||||
// fall back to the lookup table / the portable software conversion for whichever direction no branch selected
|
||||
#if !defined(GGML_CPU_FP16_TO_FP32)
|
||||
inline static float ggml_lookup_fp16_to_fp32(ggml_fp16_t f) {
|
||||
uint16_t s;
|
||||
|
||||
@@ -1551,11 +1551,11 @@ struct ggml_backend_cuda_context {
|
||||
// pool
|
||||
std::unique_ptr<ggml_cuda_pool> pools[GGML_CUDA_MAX_DEVICES][GGML_CUDA_MAX_STREAMS];
|
||||
|
||||
static std::unique_ptr<ggml_cuda_pool> new_pool_for_device(int device, int stream_no);
|
||||
static std::unique_ptr<ggml_cuda_pool> new_pool_for_device(ggml_backend_cuda_context & ctx, int device, int stream_no);
|
||||
|
||||
ggml_cuda_pool & pool(int device) {
|
||||
if (pools[device][curr_stream_no] == nullptr) {
|
||||
pools[device][curr_stream_no] = new_pool_for_device(device, curr_stream_no);
|
||||
pools[device][curr_stream_no] = new_pool_for_device(*this, device, curr_stream_no);
|
||||
}
|
||||
return *pools[device][curr_stream_no];
|
||||
}
|
||||
|
||||
@@ -457,6 +457,7 @@ const ggml_cuda_device_info & ggml_cuda_info() {
|
||||
struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
static const int MAX_BUFFERS = 256;
|
||||
|
||||
ggml_backend_cuda_context & ctx;
|
||||
int device;
|
||||
struct ggml_cuda_buffer {
|
||||
void * ptr = nullptr;
|
||||
@@ -466,7 +467,8 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
ggml_cuda_buffer buffer_pool[MAX_BUFFERS] = {};
|
||||
size_t pool_size = 0;
|
||||
|
||||
explicit ggml_cuda_pool_leg(int device) :
|
||||
ggml_cuda_pool_leg(ggml_backend_cuda_context & ctx, int device) :
|
||||
ctx(ctx),
|
||||
device(device) {
|
||||
}
|
||||
|
||||
@@ -488,6 +490,20 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
}
|
||||
}
|
||||
|
||||
// captured CUDA graphs can still use freed buffers, so they must be captured again
|
||||
void invalidate_cuda_graphs() {
|
||||
#ifdef USE_CUDA_GRAPH
|
||||
int n_captured = 0;
|
||||
for (const auto & [key, graph] : ctx.cuda_graphs) {
|
||||
n_captured += graph->instance != nullptr;
|
||||
}
|
||||
if (n_captured > 0) {
|
||||
GGML_LOG_DEBUG(GGML_CUDA_NAME " pool[%d]: memory freed, %d captured CUDA graphs must be captured again\n", device, n_captured);
|
||||
}
|
||||
ctx.cuda_graphs.clear();
|
||||
#endif // USE_CUDA_GRAPH
|
||||
}
|
||||
|
||||
void * alloc(size_t size, size_t * actual_size) override {
|
||||
#ifdef DEBUG_CUDA_MALLOC
|
||||
int nnz = 0;
|
||||
@@ -538,6 +554,7 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
device, look_ahead_size/1024.0/1024.0, cached_bytes/1024.0/1024.0);
|
||||
CUDA_CHECK(cudaDeviceSynchronize());
|
||||
clear_pool();
|
||||
invalidate_cuda_graphs();
|
||||
err = ggml_cuda_device_malloc(&ptr, look_ahead_size, device);
|
||||
if (err == cudaSuccess) {
|
||||
GGML_LOG_DEBUG(GGML_CUDA_NAME " pool[%d]: retry succeeded\n", device);
|
||||
@@ -566,6 +583,7 @@ struct ggml_cuda_pool_leg : public ggml_cuda_pool {
|
||||
ggml_cuda_set_device(device);
|
||||
CUDA_CHECK(cudaFree(ptr));
|
||||
pool_size -= size;
|
||||
invalidate_cuda_graphs();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -720,14 +738,15 @@ struct ggml_cuda_pool_vmm : public ggml_cuda_pool {
|
||||
};
|
||||
#endif // defined(GGML_USE_VMM)
|
||||
|
||||
std::unique_ptr<ggml_cuda_pool> ggml_backend_cuda_context::new_pool_for_device(int device,
|
||||
[[maybe_unused]] int stream_no) {
|
||||
std::unique_ptr<ggml_cuda_pool> ggml_backend_cuda_context::new_pool_for_device(ggml_backend_cuda_context & ctx,
|
||||
int device,
|
||||
[[maybe_unused]] int stream_no) {
|
||||
#if defined(GGML_USE_VMM)
|
||||
if (ggml_cuda_info().devices[device].vmm) {
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_vmm(device));
|
||||
}
|
||||
#endif // defined(GGML_USE_VMM)
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_leg(device));
|
||||
return std::unique_ptr<ggml_cuda_pool>(new ggml_cuda_pool_leg(ctx, device));
|
||||
}
|
||||
|
||||
// destroying a cuBLAS handle while a graph is being captured in a different thread can result in a CUDA error
|
||||
@@ -2014,6 +2033,8 @@ static bool ggml_cuda_mul_mat_id_needs_sync(const ggml_tensor * dst, const int c
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
const int warp_size = ggml_cuda_info().devices[ggml_cuda_get_device()].warp_size;
|
||||
|
||||
if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
|
||||
return true;
|
||||
}
|
||||
@@ -2032,7 +2053,7 @@ static bool ggml_cuda_mul_mat_id_needs_sync(const ggml_tensor * dst, const int c
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, WARP_SIZE, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, warp_size, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2050,6 +2071,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int cc = ggml_cuda_info().devices[ggml_cuda_get_device()].cc;
|
||||
const int warp_size = ggml_cuda_info().devices[ggml_cuda_get_device()].warp_size;
|
||||
|
||||
// [TAG_MUL_MAT_ID_CUDA_GRAPHS]
|
||||
if (src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
@@ -2074,7 +2096,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, WARP_SIZE, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, warp_size, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
ggml_cuda_mul_mat_f(ctx, src0, src1, ids, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -151,9 +151,10 @@ static void ssm_conv_f32_cuda(const float * src0, const float * src1, const floa
|
||||
case 3: launch_kernel(std::integral_constant<int, 3 >{}); break;
|
||||
case 4: launch_kernel(std::integral_constant<int, 4 >{}); break;
|
||||
case 5: launch_kernel(std::integral_constant<int, 5 >{}); break;
|
||||
case 7: launch_kernel(std::integral_constant<int, 7 >{}); break;
|
||||
case 9: launch_kernel(std::integral_constant<int, 9 >{}); break;
|
||||
case 15: launch_kernel(std::integral_constant<int, 15>{}); break;
|
||||
default: GGML_ABORT("Only support kernel sizes 3, 4, 5, 9, 15 right now.");
|
||||
default: GGML_ABORT("Only support kernel sizes 3, 4, 5, 7, 9, 15 right now.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ message(STATUS "hexagon: using ${HEXAGON_SDK_ROOT} and ${HEXAGON_TOOLS_ROOT} for
|
||||
|
||||
include(${HEXAGON_SDK_ROOT}/build/cmake/hexagon_fun.cmake)
|
||||
include(ExternalProject)
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
option(GGML_HEXAGON_HTP_DEBUG "ggml-hexagon: enable HTP debug output" OFF)
|
||||
set(GGML_HEXAGON_HTP_BUILD_TYPE "Release" CACHE STRING "ggml-hexagon: HTP skel build type (Release, RelWithDebInfo, Debug)")
|
||||
@@ -74,7 +75,8 @@ function(build_htp_skel V)
|
||||
-DHEXAGON_TOOLS_ROOT=${HEXAGON_TOOLS_ROOT}
|
||||
-DHEXAGON_HTP_DEBUG=${GGML_HEXAGON_HTP_DEBUG}
|
||||
-DDSP_VERSION=${V}
|
||||
-DPREBUILT_LIB_DIR="toolv19_${V}")
|
||||
-DPython3_EXECUTABLE=${Python3_EXECUTABLE}
|
||||
-DPREBUILT_LIB_DIR="toolv21_${V}")
|
||||
list(APPEND HTP_SKELS ${CMAKE_CURRENT_BINARY_DIR}/libggml-htp-${V}.so)
|
||||
list(APPEND HTP_PROJECTS htp-${V})
|
||||
set(HTP_SKELS ${HTP_SKELS} PARENT_SCOPE)
|
||||
@@ -85,6 +87,7 @@ build_htp_skel(v73)
|
||||
build_htp_skel(v75)
|
||||
build_htp_skel(v79)
|
||||
build_htp_skel(v81)
|
||||
build_htp_skel(v85)
|
||||
|
||||
# Install Hexagon skels required at runtime
|
||||
install(FILES ${HTP_SKELS} TYPE LIB)
|
||||
|
||||
@@ -8399,9 +8399,9 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
if (opt_arch < 73) {
|
||||
GGML_LOG_WARN("ggml-hex: Hexagon arch v%d is under supported range, capping at v73\n", opt_arch);
|
||||
opt_arch = 73;
|
||||
} else if (opt_arch > 81) {
|
||||
GGML_LOG_WARN("ggml-hex: Hexagon arch v%d is over supported range, capping at v81\n", opt_arch);
|
||||
opt_arch = 81;
|
||||
} else if (opt_arch > 85) {
|
||||
GGML_LOG_WARN("ggml-hex: Hexagon arch v%d is over supported range, capping at v85\n", opt_arch);
|
||||
opt_arch = 85;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -53,9 +53,17 @@ target_compile_definitions(${HTP_LIB} PRIVATE
|
||||
$<IF:$<BOOL:${HEXAGON_HTP_DEBUG}>,HTP_DEBUG=1,NDEBUG=1>
|
||||
$<IF:$<BOOL:${HEXAGON_HTP_DEBUG}>,FARF_HIGH=1,>)
|
||||
|
||||
|
||||
build_idl(htp_iface.idl ${HTP_LIB})
|
||||
|
||||
set_target_properties(${HTP_LIB} PROPERTIES EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if (NOT Python3_EXECUTABLE)
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET ${HTP_LIB} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Adding SWIV section $<TARGET_FILE_NAME:${HTP_LIB}>"
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/htp-swiv.py -i $<TARGET_FILE:${HTP_LIB}> -o $<TARGET_FILE:${HTP_LIB}>
|
||||
VERBATIM)
|
||||
|
||||
install(TARGETS ${HTP_LIB})
|
||||
|
||||
@@ -0,0 +1,512 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import zlib
|
||||
|
||||
ELF_MAGIC = b'\x7fELF'
|
||||
ELF_CLASS_INDEX = 4
|
||||
ELFCLASS32 = 1
|
||||
ELFCLASS64 = 2
|
||||
ELF_VERSION_INDEX = 6
|
||||
EV_CURRENT = 1
|
||||
MAX_PHDR_COUNT = 100
|
||||
|
||||
LOAD_TYPE = 0x1
|
||||
|
||||
SHT_NOBITS = 8
|
||||
|
||||
SWIV_MAGIC = b'SWIV'
|
||||
SWIV_SEG_SIZE_MIN = 16
|
||||
# SHT_LOUSER plus ASCII "SWIV".
|
||||
SWIV_SECTION_TYPE = 0xD3574956
|
||||
|
||||
logger = logging.getLogger("htp-swiv")
|
||||
|
||||
|
||||
class Elf_Ehdr_common:
|
||||
s = struct.Struct('16sHHI')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf_Ehdr_common.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
|
||||
|
||||
class Elf32_Ehdr:
|
||||
s = struct.Struct('16sHHIIIIIHHHHHH')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Ehdr.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
self.e_entry = unpacked_data[4]
|
||||
self.e_phoff = unpacked_data[5]
|
||||
self.e_shoff = unpacked_data[6]
|
||||
self.e_flags = unpacked_data[7]
|
||||
self.e_ehsize = unpacked_data[8]
|
||||
self.e_phentsize = unpacked_data[9]
|
||||
self.e_phnum = unpacked_data[10]
|
||||
self.e_shentsize = unpacked_data[11]
|
||||
self.e_shnum = unpacked_data[12]
|
||||
self.e_shstrndx = unpacked_data[13]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.e_ident,
|
||||
self.e_type,
|
||||
self.e_machine,
|
||||
self.e_version,
|
||||
self.e_entry,
|
||||
self.e_phoff,
|
||||
self.e_shoff,
|
||||
self.e_flags,
|
||||
self.e_ehsize,
|
||||
self.e_phentsize,
|
||||
self.e_phnum,
|
||||
self.e_shentsize,
|
||||
self.e_shnum,
|
||||
self.e_shstrndx
|
||||
]
|
||||
|
||||
return (Elf32_Ehdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf32_Phdr:
|
||||
s = struct.Struct('I' * 8)
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Phdr.s).unpack(data)
|
||||
self.p_type = unpacked_data[0]
|
||||
self.p_offset = unpacked_data[1]
|
||||
self.p_vaddr = unpacked_data[2]
|
||||
self.p_paddr = unpacked_data[3]
|
||||
self.p_filesz = unpacked_data[4]
|
||||
self.p_memsz = unpacked_data[5]
|
||||
self.p_flags = unpacked_data[6]
|
||||
self.p_align = unpacked_data[7]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.p_type,
|
||||
self.p_offset,
|
||||
self.p_vaddr,
|
||||
self.p_paddr,
|
||||
self.p_filesz,
|
||||
self.p_memsz,
|
||||
self.p_flags,
|
||||
self.p_align
|
||||
]
|
||||
|
||||
return (Elf32_Phdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf32_Shdr:
|
||||
s = struct.Struct('I' * 10)
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Shdr.s).unpack(data)
|
||||
self.sh_name = unpacked_data[0]
|
||||
self.sh_type = unpacked_data[1]
|
||||
self.sh_flags = unpacked_data[2]
|
||||
self.sh_addr = unpacked_data[3]
|
||||
self.sh_offset = unpacked_data[4]
|
||||
self.sh_size = unpacked_data[5]
|
||||
self.sh_link = unpacked_data[6]
|
||||
self.sh_info = unpacked_data[7]
|
||||
self.sh_addralign = unpacked_data[8]
|
||||
self.sh_entsize = unpacked_data[9]
|
||||
self.sh_name_str = ""
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.sh_name,
|
||||
self.sh_type,
|
||||
self.sh_flags,
|
||||
self.sh_addr,
|
||||
self.sh_offset,
|
||||
self.sh_size,
|
||||
self.sh_link,
|
||||
self.sh_info,
|
||||
self.sh_addralign,
|
||||
self.sh_entsize]
|
||||
|
||||
return (Elf32_Shdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Ehdr:
|
||||
s = struct.Struct('16sHHIQQQIHHHHHH')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Ehdr.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
self.e_entry = unpacked_data[4]
|
||||
self.e_phoff = unpacked_data[5]
|
||||
self.e_shoff = unpacked_data[6]
|
||||
self.e_flags = unpacked_data[7]
|
||||
self.e_ehsize = unpacked_data[8]
|
||||
self.e_phentsize = unpacked_data[9]
|
||||
self.e_phnum = unpacked_data[10]
|
||||
self.e_shentsize = unpacked_data[11]
|
||||
self.e_shnum = unpacked_data[12]
|
||||
self.e_shstrndx = unpacked_data[13]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.e_ident,
|
||||
self.e_type,
|
||||
self.e_machine,
|
||||
self.e_version,
|
||||
self.e_entry,
|
||||
self.e_phoff,
|
||||
self.e_shoff,
|
||||
self.e_flags,
|
||||
self.e_ehsize,
|
||||
self.e_phentsize,
|
||||
self.e_phnum,
|
||||
self.e_shentsize,
|
||||
self.e_shnum,
|
||||
self.e_shstrndx
|
||||
]
|
||||
|
||||
return (Elf64_Ehdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Phdr:
|
||||
s = struct.Struct('IIQQQQQQ')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Phdr.s).unpack(data)
|
||||
self.p_type = unpacked_data[0]
|
||||
self.p_flags = unpacked_data[1]
|
||||
self.p_offset = unpacked_data[2]
|
||||
self.p_vaddr = unpacked_data[3]
|
||||
self.p_paddr = unpacked_data[4]
|
||||
self.p_filesz = unpacked_data[5]
|
||||
self.p_memsz = unpacked_data[6]
|
||||
self.p_align = unpacked_data[7]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.p_type,
|
||||
self.p_flags,
|
||||
self.p_offset,
|
||||
self.p_vaddr,
|
||||
self.p_paddr,
|
||||
self.p_filesz,
|
||||
self.p_memsz,
|
||||
self.p_align
|
||||
]
|
||||
|
||||
return (Elf64_Phdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Shdr:
|
||||
s = struct.Struct('IIQQQQIIQQ')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Shdr.s).unpack(data)
|
||||
self.sh_name = unpacked_data[0]
|
||||
self.sh_type = unpacked_data[1]
|
||||
self.sh_flags = unpacked_data[2]
|
||||
self.sh_addr = unpacked_data[3]
|
||||
self.sh_offset = unpacked_data[4]
|
||||
self.sh_size = unpacked_data[5]
|
||||
self.sh_link = unpacked_data[6]
|
||||
self.sh_info = unpacked_data[7]
|
||||
self.sh_addralign = unpacked_data[8]
|
||||
self.sh_entsize = unpacked_data[9]
|
||||
self.sh_name_str = ""
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.sh_name,
|
||||
self.sh_type,
|
||||
self.sh_flags,
|
||||
self.sh_addr,
|
||||
self.sh_offset,
|
||||
self.sh_size,
|
||||
self.sh_link,
|
||||
self.sh_info,
|
||||
self.sh_addralign,
|
||||
self.sh_entsize]
|
||||
|
||||
return (Elf64_Shdr.s).pack(*values)
|
||||
|
||||
|
||||
class SWIV_Seg:
|
||||
s = struct.Struct('4sIQ')
|
||||
|
||||
def __init__(self):
|
||||
self.magic = SWIV_MAGIC
|
||||
self.crc = 0
|
||||
self.padding = 0
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.magic,
|
||||
self.crc,
|
||||
self.padding
|
||||
]
|
||||
|
||||
return (SWIV_Seg.s).pack(*values)
|
||||
|
||||
|
||||
def is_elf64(elf_header):
|
||||
return elf_header.e_ident[ELF_CLASS_INDEX] == ELFCLASS64
|
||||
|
||||
|
||||
def verify_elf_header(elf_header):
|
||||
ident = elf_header.e_ident
|
||||
return (ident.startswith(ELF_MAGIC)
|
||||
and ident[ELF_CLASS_INDEX] in (ELFCLASS32, ELFCLASS64)
|
||||
and ident[ELF_VERSION_INDEX] == EV_CURRENT)
|
||||
|
||||
|
||||
def file_copy_offset(in_fp, in_off, out_fp, out_off, num_bytes):
|
||||
in_fp.seek(in_off)
|
||||
read_in = in_fp.read(num_bytes)
|
||||
out_fp.seek(out_off)
|
||||
out_fp.write(read_in)
|
||||
|
||||
return num_bytes
|
||||
|
||||
|
||||
def preprocess_elf_file(elf_file_name):
|
||||
with open(elf_file_name, 'rb') as elf_fp:
|
||||
elf_header = Elf_Ehdr_common(elf_fp.read(Elf_Ehdr_common.s.size))
|
||||
|
||||
if verify_elf_header(elf_header) is False:
|
||||
raise RuntimeError("Input ELF verification failed : " + elf_file_name)
|
||||
|
||||
elf_fp.seek(0)
|
||||
|
||||
if is_elf64(elf_header):
|
||||
elf_header = Elf64_Ehdr(elf_fp.read(Elf64_Ehdr.s.size))
|
||||
else:
|
||||
elf_header = Elf32_Ehdr(elf_fp.read(Elf32_Ehdr.s.size))
|
||||
|
||||
phdr_table = []
|
||||
|
||||
phdr_size = elf_header.e_phentsize
|
||||
|
||||
file_offset = elf_header.e_phoff
|
||||
elf_fp.seek(file_offset)
|
||||
|
||||
for _ in range(elf_header.e_phnum):
|
||||
if is_elf64(elf_header):
|
||||
phdr_table.append(Elf64_Phdr(elf_fp.read(phdr_size)))
|
||||
else:
|
||||
phdr_table.append(Elf32_Phdr(elf_fp.read(phdr_size)))
|
||||
|
||||
return [elf_header, phdr_table]
|
||||
|
||||
|
||||
def process_section_header(elf_file_name):
|
||||
with open(elf_file_name, 'rb') as elf_fp:
|
||||
elf_header = Elf_Ehdr_common(elf_fp.read(Elf_Ehdr_common.s.size))
|
||||
|
||||
if verify_elf_header(elf_header) is False:
|
||||
raise RuntimeError("Input ELF verification failed : " + elf_file_name)
|
||||
|
||||
elf_fp.seek(0)
|
||||
|
||||
if is_elf64(elf_header):
|
||||
elf_header = Elf64_Ehdr(elf_fp.read(Elf64_Ehdr.s.size))
|
||||
else:
|
||||
elf_header = Elf32_Ehdr(elf_fp.read(Elf32_Ehdr.s.size))
|
||||
|
||||
shdr_table = []
|
||||
|
||||
shdr_size = elf_header.e_shentsize
|
||||
elf_fp.seek(elf_header.e_shoff)
|
||||
|
||||
for _ in range(elf_header.e_shnum):
|
||||
if is_elf64(elf_header):
|
||||
shdr_table.append(Elf64_Shdr(elf_fp.read(shdr_size)))
|
||||
else:
|
||||
shdr_table.append(Elf32_Shdr(elf_fp.read(shdr_size)))
|
||||
|
||||
return shdr_table
|
||||
|
||||
|
||||
def crc32_update_file(elf_fp, offset, size, crc):
|
||||
elf_fp.seek(offset)
|
||||
remaining = size
|
||||
while remaining > 0:
|
||||
chunk = elf_fp.read(min(1024 * 1024, remaining))
|
||||
if not chunk:
|
||||
raise RuntimeError("Unexpected EOF while calculating CRC")
|
||||
crc = zlib.crc32(chunk, crc)
|
||||
remaining -= len(chunk)
|
||||
return crc
|
||||
|
||||
|
||||
def crc32_update_zeros(size, crc):
|
||||
zero_chunk = b'\x00' * min(1024 * 1024, size)
|
||||
remaining = size
|
||||
while remaining > 0:
|
||||
chunk = zero_chunk[:min(len(zero_chunk), remaining)]
|
||||
crc = zlib.crc32(chunk, crc)
|
||||
remaining -= len(chunk)
|
||||
return crc
|
||||
|
||||
|
||||
def crc32_calculate(elf_fp, phdr_table):
|
||||
crc = 0
|
||||
|
||||
# SWIV validates loadable segment contents, including BSS as zeros.
|
||||
for curr_phdr in phdr_table:
|
||||
if curr_phdr.p_type != LOAD_TYPE:
|
||||
continue
|
||||
|
||||
crc = crc32_update_file(elf_fp, curr_phdr.p_offset, curr_phdr.p_filesz, crc)
|
||||
|
||||
zero_size = curr_phdr.p_memsz - curr_phdr.p_filesz
|
||||
if zero_size > 0:
|
||||
crc = crc32_update_zeros(zero_size, crc)
|
||||
|
||||
crc &= 0xffffffff
|
||||
logger.info("calculated CRC: %#x", crc)
|
||||
|
||||
return crc
|
||||
|
||||
|
||||
def verify_swiv_seg(elf_in_fp, offset):
|
||||
|
||||
elf_in_fp.seek(offset)
|
||||
swiv_seg = SWIV_Seg()
|
||||
unpacked_data = (SWIV_Seg.s).unpack(elf_in_fp.read(SWIV_SEG_SIZE_MIN))
|
||||
swiv_seg.magic = unpacked_data[0]
|
||||
|
||||
return swiv_seg.magic == SWIV_MAGIC
|
||||
|
||||
|
||||
def same_output_file(input_elf, output_elf):
|
||||
try:
|
||||
return os.path.exists(output_elf) and os.path.samefile(input_elf, output_elf)
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def make_output_target(input_elf, output_elf):
|
||||
if not same_output_file(input_elf, output_elf):
|
||||
return output_elf, None
|
||||
|
||||
output_dir = os.path.dirname(os.path.abspath(output_elf))
|
||||
output_base = os.path.basename(output_elf)
|
||||
fd, temp_output = tempfile.mkstemp(prefix=output_base + ".", suffix=".tmp", dir=output_dir)
|
||||
os.close(fd)
|
||||
shutil.copymode(input_elf, temp_output)
|
||||
return temp_output, temp_output
|
||||
|
||||
|
||||
def swiv_generate(input_elf, output_elf):
|
||||
output_target, temp_output = make_output_target(input_elf, output_elf)
|
||||
|
||||
try:
|
||||
with open(input_elf, "rb") as elf_in_fp, open(output_target, "wb+") as elf_out_fp:
|
||||
[elf_header, phdr_table] = preprocess_elf_file(input_elf)
|
||||
|
||||
num_phdrs = elf_header.e_phnum
|
||||
num_shdrs = elf_header.e_shnum
|
||||
phdr_size = elf_header.e_phentsize
|
||||
|
||||
if num_phdrs == 0 or num_phdrs > MAX_PHDR_COUNT:
|
||||
raise RuntimeError("Input ELF has invalid number of program headers.")
|
||||
|
||||
if elf_header.e_shoff == 0 or num_shdrs == 0:
|
||||
raise RuntimeError("Input ELF does not contain a section header table.")
|
||||
|
||||
shdr_table_input = process_section_header(input_elf)
|
||||
shdr_table_output = process_section_header(input_elf)
|
||||
|
||||
last_phdr = phdr_table[num_phdrs - 1]
|
||||
if last_phdr.p_memsz == SWIV_SEG_SIZE_MIN and verify_swiv_seg(elf_in_fp, last_phdr.p_offset):
|
||||
raise RuntimeError("A SWIV segment is detected in the input ELF.")
|
||||
|
||||
last_shdr = shdr_table_input[elf_header.e_shnum - 1]
|
||||
if last_shdr.sh_size == SWIV_SEG_SIZE_MIN and verify_swiv_seg(elf_in_fp, last_shdr.sh_offset):
|
||||
raise RuntimeError("A SWIV section is detected in the input ELF.")
|
||||
|
||||
if is_elf64(elf_header):
|
||||
swiv_shdr = Elf64_Shdr(b'\0' * Elf64_Shdr.s.size)
|
||||
else:
|
||||
swiv_shdr = Elf32_Shdr(b'\0' * Elf32_Shdr.s.size)
|
||||
|
||||
swiv_shdr.sh_name = 0
|
||||
swiv_shdr.sh_type = SWIV_SECTION_TYPE
|
||||
swiv_shdr.sh_flags = 0
|
||||
swiv_shdr.sh_addr = 0
|
||||
swiv_shdr.sh_size = SWIV_SEG_SIZE_MIN
|
||||
swiv_shdr.sh_offset = elf_header.e_shoff + (elf_header.e_shnum + 1) * elf_header.e_shentsize
|
||||
shdr_table_output.append(swiv_shdr)
|
||||
elf_header.e_shnum += 1
|
||||
|
||||
if elf_header.e_phoff != 0:
|
||||
for phdr in phdr_table:
|
||||
file_copy_offset(elf_in_fp, phdr.p_offset, elf_out_fp, phdr.p_offset, phdr.p_filesz)
|
||||
|
||||
elf_out_fp.seek(0)
|
||||
elf_out_fp.write(elf_header.get_packed_data())
|
||||
|
||||
phdr_start = elf_header.e_phoff
|
||||
for phdr in phdr_table:
|
||||
elf_out_fp.seek(phdr_start)
|
||||
elf_out_fp.write(phdr.get_packed_data())
|
||||
phdr_start += phdr_size
|
||||
|
||||
for shdr in shdr_table_input[:num_shdrs]:
|
||||
if shdr.sh_type == SHT_NOBITS or shdr.sh_size == 0:
|
||||
continue
|
||||
file_copy_offset(elf_in_fp, shdr.sh_offset, elf_out_fp, shdr.sh_offset, shdr.sh_size)
|
||||
|
||||
shdr_start = elf_header.e_shoff
|
||||
for shdr in shdr_table_output:
|
||||
elf_out_fp.seek(shdr_start)
|
||||
elf_out_fp.write(shdr.get_packed_data())
|
||||
shdr_start += elf_header.e_shentsize
|
||||
|
||||
elf_out_fp.flush()
|
||||
|
||||
swiv_seg = SWIV_Seg()
|
||||
swiv_seg.crc = crc32_calculate(elf_out_fp, phdr_table)
|
||||
elf_out_fp.seek(swiv_shdr.sh_offset)
|
||||
elf_out_fp.write(swiv_seg.get_packed_data())
|
||||
|
||||
if temp_output is not None:
|
||||
os.replace(temp_output, output_elf)
|
||||
temp_output = None
|
||||
|
||||
return 0
|
||||
finally:
|
||||
if temp_output is not None and os.path.exists(temp_output):
|
||||
os.unlink(temp_output)
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description="Add a Software Image Validation (SWIV) section to an HTP ELF file.")
|
||||
parser.add_argument("-i", "--input", dest="input_file", metavar="INPUT", required=True)
|
||||
parser.add_argument("-o", "--output", dest="output_file", metavar="OUTPUT", required=True)
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="show calculated CRC")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO if args.verbose else logging.WARNING,
|
||||
format="%(message)s",
|
||||
)
|
||||
|
||||
try:
|
||||
return swiv_generate(args.input_file, args.output_file)
|
||||
except RuntimeError as exc:
|
||||
logger.error("ERROR: %s", exc)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
@@ -316,9 +316,17 @@ static int vtcm_release_callback(unsigned int rctx, void * state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
static unsigned int vtcm_query_size(void) {
|
||||
unsigned int vtcm_size = 8 * 1024 * 1024; // 8MB default
|
||||
HAP_compute_res_query_VTCM(0, &vtcm_size, NULL, NULL, NULL);
|
||||
unsigned int vtcm_avail;
|
||||
compute_res_vtcm_page_t total_layout, avail_layout;
|
||||
// some images (e.g. FuSa cDSP) fail the query when the layout out-params are NULL
|
||||
HAP_compute_res_query_VTCM(0, &vtcm_size, &total_layout, &vtcm_avail, &avail_layout);
|
||||
return vtcm_size;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
unsigned int vtcm_size = vtcm_query_size();
|
||||
|
||||
compute_res_attr_t attr;
|
||||
HAP_compute_res_attr_init(&attr);
|
||||
@@ -770,9 +778,7 @@ AEEResult htp_iface_hwinfo(remote_handle64 handle, uint32_t * n_threads, uint32_
|
||||
*n_hvx = n_hvx_val;
|
||||
*n_hmx = 1;
|
||||
|
||||
uint32_t vtcm_sz = 8 * 1024 * 1024; // 8MB default fallback
|
||||
HAP_compute_res_query_VTCM(0, (unsigned int *)&vtcm_sz, NULL, NULL, NULL);
|
||||
*vtcm_size = vtcm_sz;
|
||||
*vtcm_size = vtcm_query_size();
|
||||
|
||||
return AEE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ libggml-htp-v73.so = 1
|
||||
libggml-htp-v75.so = 1
|
||||
libggml-htp-v79.so = 1
|
||||
libggml-htp-v81.so = 1
|
||||
libggml-htp-v85.so = 1
|
||||
|
||||
[ControlFlags]
|
||||
ExcludeFromSelect = *
|
||||
@@ -30,6 +31,7 @@ libggml-htp-v73.so,,,0x10 ;COPYFLG_NO_OVERWRITE
|
||||
libggml-htp-v75.so,,,0x10 ;COPYFLG_NO_OVERWRITE
|
||||
libggml-htp-v79.so,,,0x10 ;COPYFLG_NO_OVERWRITE
|
||||
libggml-htp-v81.so,,,0x10 ;COPYFLG_NO_OVERWRITE
|
||||
libggml-htp-v85.so,,,0x10 ;COPYFLG_NO_OVERWRITE
|
||||
|
||||
[Strings]
|
||||
GGML = 'GGML'
|
||||
|
||||
@@ -1567,6 +1567,28 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax(ggml_meta
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_part(ggml_metal_library_t lib) {
|
||||
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, "kernel_argmax_part_f32");
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, "kernel_argmax_part_f32", "kernel_argmax_part_f32", nullptr);
|
||||
}
|
||||
|
||||
res.smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_final(ggml_metal_library_t lib) {
|
||||
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, "kernel_argmax_final_f32");
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, "kernel_argmax_final_f32", "kernel_argmax_final_f32", nullptr);
|
||||
}
|
||||
|
||||
res.smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort(ggml_metal_library_t lib, const ggml_tensor * op) {
|
||||
assert(op->op == GGML_OP_ARGSORT);
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm_id
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mm_id (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mv_id (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_part (ggml_metal_library_t lib);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argmax_final (ggml_metal_library_t lib);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_argsort_merge (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_fwht (ggml_metal_library_t lib, int n, enum ggml_type tsrc);
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#define N_MM_NPART_AMAX 256
|
||||
|
||||
#define N_ARGMAX_NPART_MAX 256
|
||||
#define N_ARGMAX_CHUNK 4096
|
||||
|
||||
// kernel parameters for mat-vec threadgroups
|
||||
//
|
||||
// N_R0: number of src0 rows to process per simdgroup
|
||||
@@ -1379,8 +1382,10 @@ typedef struct {
|
||||
} ggml_metal_kargs_pool_1d;
|
||||
|
||||
typedef struct {
|
||||
int64_t ne00;
|
||||
int64_t ne00;
|
||||
uint64_t nb01;
|
||||
int64_t npart;
|
||||
int64_t ne01;
|
||||
} ggml_metal_kargs_argmax;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -2319,13 +2319,18 @@ int ggml_metal_op_pool_1d(ggml_metal_op_t ctx, int idx) {
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_pool_1d(lib, op, op_pool);
|
||||
|
||||
const int nth = std::min(ggml_metal_pipeline_max_theads_per_threadgroup(pipeline), (int) np);
|
||||
const int ntg = (np + nth - 1) / nth;
|
||||
const int nth = std::min(256, std::max(1, k0));
|
||||
const int ntg = (int) np;
|
||||
|
||||
const size_t smem = op_pool == GGML_OP_POOL_AVG ?
|
||||
2*32*sizeof(float) :
|
||||
32*sizeof(float);
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline);
|
||||
ggml_metal_encoder_set_bytes (enc, &args_pool_1d, sizeof(args_pool_1d), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op->src[0]), 1);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op), 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, ntg, 1, 1, nth, 1, 1);
|
||||
|
||||
@@ -5421,6 +5426,25 @@ int ggml_metal_op_timestep_embedding(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t ggml_metal_op_argmax_extra(const ggml_tensor * op) {
|
||||
assert(op->op == GGML_OP_ARGMAX);
|
||||
|
||||
const int64_t ne00 = op->src[0]->ne[0];
|
||||
const int64_t ne01 = op->src[0]->ne[1];
|
||||
|
||||
int npart = 1;
|
||||
if (ne00 > N_ARGMAX_CHUNK) {
|
||||
npart = (int) std::min<int64_t>(N_ARGMAX_NPART_MAX, (ne00 + N_ARGMAX_CHUNK - 1)/N_ARGMAX_CHUNK);
|
||||
}
|
||||
|
||||
if (npart == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// partial max + partial argmax for every row
|
||||
return (sizeof(float) + sizeof(int32_t)) * npart * ne01;
|
||||
}
|
||||
|
||||
int ggml_metal_op_argmax(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
@@ -5432,22 +5456,61 @@ int ggml_metal_op_argmax(ggml_metal_op_t ctx, int idx) {
|
||||
GGML_TENSOR_LOCALS( int32_t, ne, op, ne);
|
||||
GGML_TENSOR_LOCALS(uint64_t, nb, op, nb);
|
||||
|
||||
const int64_t nrows = ggml_nrows(op->src[0]);
|
||||
|
||||
int npart = 1;
|
||||
if (ne00 > N_ARGMAX_CHUNK) {
|
||||
npart = (int) std::min<int64_t>(N_ARGMAX_NPART_MAX, (ne00 + N_ARGMAX_CHUNK - 1)/N_ARGMAX_CHUNK);
|
||||
}
|
||||
|
||||
ggml_metal_kargs_argmax args = {
|
||||
/*.ne00 = */ ne00,
|
||||
/*.nb01 = */ nb01,
|
||||
/*.ne00 = */ ne00,
|
||||
/*.nb01 = */ nb01,
|
||||
/*.npart = */ npart,
|
||||
/*.ne01 = */ ne01,
|
||||
};
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_argmax(lib, op);
|
||||
const size_t smem = 32*(sizeof(float) + sizeof(int32_t));
|
||||
|
||||
const int64_t nrows = ggml_nrows(op->src[0]);
|
||||
if (npart > 1) {
|
||||
auto pipeline_part = ggml_metal_library_get_pipeline_argmax_part(lib);
|
||||
auto pipeline_final = ggml_metal_library_get_pipeline_argmax_final(lib);
|
||||
|
||||
ggml_metal_buffer_id bid_dst = ggml_metal_get_buffer_id(op);
|
||||
ggml_metal_buffer_id bid_tmp = bid_dst;
|
||||
bid_tmp.offs += ggml_nbytes(op);
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_part);
|
||||
ggml_metal_encoder_set_bytes (enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op->src[0]), 1);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_tmp, 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, npart, nrows, 1, 256, 1, 1);
|
||||
|
||||
ggml_metal_op_concurrency_reset(ctx);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
while (nth < npart && nth < 256) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_final);
|
||||
ggml_metal_encoder_set_bytes (enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_tmp, 1);
|
||||
ggml_metal_encoder_set_buffer (enc, bid_dst, 2);
|
||||
ggml_metal_encoder_set_threadgroup_memory_size(enc, smem, 0);
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, nrows, 1, 1, nth, 1, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto pipeline = ggml_metal_library_get_pipeline_argmax(lib, op);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
while (nth < ne00 && nth*ne01*ne02*ne03 < 256) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
const size_t smem = pipeline.smem;
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline);
|
||||
ggml_metal_encoder_set_bytes (enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(op->src[0]), 1);
|
||||
|
||||
@@ -47,6 +47,8 @@ size_t ggml_metal_op_flash_attn_ext_extra_tmp(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_kv_f16(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_idx(const struct ggml_tensor * op);
|
||||
|
||||
size_t ggml_metal_op_argmax_extra(const struct ggml_tensor * op);
|
||||
|
||||
int ggml_metal_op_concat (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_repeat (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_acc (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -245,6 +245,10 @@ static size_t ggml_backend_metal_buffer_type_get_alloc_size(ggml_backend_buffer_
|
||||
{
|
||||
res = 2*sizeof(int32_t)*ggml_nelements(tensor->src[0]);
|
||||
} break;
|
||||
case GGML_OP_ARGMAX:
|
||||
{
|
||||
res += ggml_metal_op_argmax_extra(tensor);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,140 @@ kernel void kernel_argmax_f32(
|
||||
dst_i32[tgpig] = arg_val;
|
||||
}
|
||||
|
||||
kernel void kernel_argmax_part_f32(
|
||||
constant ggml_metal_kargs_argmax & args,
|
||||
device const char * src0,
|
||||
device char * tmp,
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint3 ntg[[threads_per_threadgroup]]) {
|
||||
const int row = tgpig.y;
|
||||
const int part = tgpig.x;
|
||||
const int ne00 = (int) args.ne00;
|
||||
const int npart = (int) args.npart;
|
||||
const int chunk = (ne00 + npart - 1) / npart;
|
||||
const int start = part * chunk;
|
||||
const int end = min(start + chunk, ne00);
|
||||
|
||||
device const float * x_row = (device const float *) ((device const char *) src0 + row * args.nb01);
|
||||
|
||||
float lmax = -INFINITY;
|
||||
int32_t larg = -1;
|
||||
|
||||
for (int i00 = start + tpitg.x; i00 < end; i00 += ntg.x) {
|
||||
if (x_row[i00] > lmax) {
|
||||
lmax = x_row[i00];
|
||||
larg = i00;
|
||||
}
|
||||
}
|
||||
|
||||
// find the argmax value in the block
|
||||
float max_val = simd_max(lmax);
|
||||
int32_t arg_val = simd_max(select(-1, larg, lmax == max_val));
|
||||
|
||||
threadgroup float * shared_maxval = (threadgroup float *) shmem;
|
||||
threadgroup int32_t * shared_argmax = (threadgroup int32_t *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg.x > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_maxval[tiisg] = -INFINITY;
|
||||
shared_argmax[tiisg] = -1;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_maxval[sgitg] = max_val;
|
||||
shared_argmax[sgitg] = arg_val;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_maxval[tiisg];
|
||||
arg_val = shared_argmax[tiisg];
|
||||
|
||||
float max_val_reduced = simd_max(max_val);
|
||||
int32_t arg_val_reduced = simd_max(select(-1, arg_val, max_val == max_val_reduced));
|
||||
|
||||
max_val = max_val_reduced;
|
||||
arg_val = arg_val_reduced;
|
||||
}
|
||||
|
||||
if (tpitg.x == 0) {
|
||||
device float * tmp_max = (device float *) tmp;
|
||||
device int32_t * tmp_arg = (device int32_t *) tmp + npart * (int) args.ne01;
|
||||
|
||||
tmp_max[row*npart + part] = max_val;
|
||||
tmp_arg[row*npart + part] = arg_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_argmax_final_f32(
|
||||
constant ggml_metal_kargs_argmax & args,
|
||||
device const char * tmp,
|
||||
device char * dst,
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint3 ntg[[threads_per_threadgroup]]) {
|
||||
const int row = tgpig.x;
|
||||
const int npart = (int) args.npart;
|
||||
|
||||
device const float * tmp_max = (device const float *) tmp;
|
||||
device const int32_t * tmp_arg = (device const int32_t *) tmp + npart * (int) args.ne01;
|
||||
|
||||
float lmax = -INFINITY;
|
||||
int32_t larg = -1;
|
||||
|
||||
for (int i = tpitg.x; i < npart; i += ntg.x) {
|
||||
if (tmp_max[row*npart + i] > lmax) {
|
||||
lmax = tmp_max[row*npart + i];
|
||||
larg = tmp_arg[row*npart + i];
|
||||
}
|
||||
}
|
||||
|
||||
// find the argmax value in the block
|
||||
float max_val = simd_max(lmax);
|
||||
int32_t arg_val = simd_max(select(-1, larg, lmax == max_val));
|
||||
|
||||
threadgroup float * shared_maxval = (threadgroup float *) shmem;
|
||||
threadgroup int32_t * shared_argmax = (threadgroup int32_t *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg.x > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_maxval[tiisg] = -INFINITY;
|
||||
shared_argmax[tiisg] = -1;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_maxval[sgitg] = max_val;
|
||||
shared_argmax[sgitg] = arg_val;
|
||||
}
|
||||
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_maxval[tiisg];
|
||||
arg_val = shared_argmax[tiisg];
|
||||
|
||||
float max_val_reduced = simd_max(max_val);
|
||||
int32_t arg_val_reduced = simd_max(select(-1, arg_val, max_val == max_val_reduced));
|
||||
|
||||
max_val = max_val_reduced;
|
||||
arg_val = arg_val_reduced;
|
||||
}
|
||||
|
||||
if (tpitg.x == 0) {
|
||||
((device int32_t *) dst)[row] = arg_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_diag_f32(
|
||||
constant ggml_metal_kargs_diag & args,
|
||||
device const char * src0,
|
||||
|
||||
@@ -593,6 +593,7 @@ template [[host_name("kernel_mul_mm_id_map0_ne20_4" )]] kernel kernel_mul_mm_id_
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_5" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<5>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_6" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<6>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_8" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<8>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_9" )]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<9>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_10")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<10>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_16")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<16>;
|
||||
template [[host_name("kernel_mul_mm_id_map0_ne20_22")]] kernel kernel_mul_mm_id_map0_t kernel_mul_mm_id_map0<22>;
|
||||
|
||||
@@ -84,65 +84,117 @@ kernel void kernel_pool_1d_max_f32(
|
||||
constant ggml_metal_kargs_pool_1d & args,
|
||||
device const float * src,
|
||||
device float * dst,
|
||||
uint gid [[thread_position_in_grid]]
|
||||
) {
|
||||
|
||||
if (gid >= args.np) {
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint tgpig[[threadgroup_position_in_grid]],
|
||||
uint tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint ntg[[threads_per_threadgroup]]) {
|
||||
if (tgpig >= args.np) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int ow = (int)gid % args.OW;
|
||||
const int row = (int)gid / args.OW;
|
||||
const int out = (int) tgpig;
|
||||
const int ow = out % (int) args.OW;
|
||||
const int row = out / (int) args.OW;
|
||||
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
float acc = -INFINITY;
|
||||
float lmax = -INFINITY;
|
||||
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
for (int ki = 0; ki < args.k0; ++ki) {
|
||||
int j = base + ki;
|
||||
if (j < 0 || j >= args.IW){
|
||||
continue;
|
||||
for (int ki = tpitg; ki < args.k0; ki += ntg) {
|
||||
const int j = base + ki;
|
||||
if (j >= 0 && j < args.IW) {
|
||||
lmax = max(lmax, src[src_off + j]);
|
||||
}
|
||||
float v = src[src_off + j];
|
||||
acc = max(acc, v);
|
||||
}
|
||||
|
||||
dst[dst_off + ow] = acc;
|
||||
float max_val = simd_max(lmax);
|
||||
|
||||
threadgroup float * shared_max = (threadgroup float *) shmem;
|
||||
|
||||
if (ntg > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_max[tiisg] = -INFINITY;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_max[sgitg] = max_val;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
max_val = shared_max[tiisg];
|
||||
max_val = simd_max(max_val);
|
||||
}
|
||||
|
||||
if (tpitg == 0) {
|
||||
dst[dst_off + ow] = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_pool_1d_avg_f32(
|
||||
constant ggml_metal_kargs_pool_1d & args,
|
||||
device const float * src,
|
||||
device float * dst,
|
||||
uint gid [[thread_position_in_grid]]
|
||||
) {
|
||||
|
||||
if (gid >= args.np) {
|
||||
threadgroup char * shmem [[threadgroup(0)]],
|
||||
uint tgpig[[threadgroup_position_in_grid]],
|
||||
uint tpitg[[thread_position_in_threadgroup]],
|
||||
uint sgitg[[simdgroup_index_in_threadgroup]],
|
||||
uint tiisg[[thread_index_in_simdgroup]],
|
||||
uint ntg[[threads_per_threadgroup]]) {
|
||||
if (tgpig >= args.np) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int ow = (int)gid % args.OW;
|
||||
const int row = (int)gid / args.OW;
|
||||
const int out = (int) tgpig;
|
||||
const int ow = out % (int) args.OW;
|
||||
const int row = out / (int) args.OW;
|
||||
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int base = ow * args.s0 - args.p0;
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
float acc = 0.0f;
|
||||
int cnt = 0;
|
||||
float lsum = 0.0f;
|
||||
int lcnt = 0;
|
||||
|
||||
const int src_off = row * args.IW;
|
||||
const int dst_off = row * args.OW;
|
||||
|
||||
for (int ki = 0; ki < args.k0; ++ki) {
|
||||
for (int ki = tpitg; ki < args.k0; ki += ntg) {
|
||||
const int j = base + ki;
|
||||
if (j < 0 || j >= args.IW) {
|
||||
continue;
|
||||
if (j >= 0 && j < args.IW) {
|
||||
lsum += src[src_off + j];
|
||||
lcnt += 1;
|
||||
}
|
||||
acc += src[src_off + j];
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
dst[dst_off + ow] = (cnt > 0) ? (acc / (float)cnt) : 0.0f;
|
||||
float sum = simd_sum(lsum);
|
||||
float cnt = simd_sum((float) lcnt);
|
||||
|
||||
threadgroup float * shared_sum = (threadgroup float *) shmem;
|
||||
threadgroup float * shared_cnt = (threadgroup float *) shmem + N_SIMDWIDTH;
|
||||
|
||||
if (ntg > N_SIMDWIDTH) {
|
||||
if (sgitg == 0) {
|
||||
shared_sum[tiisg] = 0.0f;
|
||||
shared_cnt[tiisg] = 0.0f;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
if (tiisg == 0) {
|
||||
shared_sum[sgitg] = sum;
|
||||
shared_cnt[sgitg] = cnt;
|
||||
}
|
||||
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
sum = shared_sum[tiisg];
|
||||
cnt = shared_cnt[tiisg];
|
||||
|
||||
sum = simd_sum(sum);
|
||||
cnt = simd_sum(cnt);
|
||||
}
|
||||
|
||||
if (tpitg == 0) {
|
||||
dst[dst_off + ow] = (cnt > 0.0f) ? (sum / cnt) : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,6 +831,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_program program_mul_mv_id_mxfp4_f32_flat;
|
||||
cl_program program_mul_mm_f32_f32_l4_lm;
|
||||
cl_program program_mul_mm_f16_f32_l4_lm;
|
||||
cl_program program_mul_mm_f16_f32_l4_lm_n8;
|
||||
cl_program program_mul_mm_q8_0_f32_l4_lm;
|
||||
|
||||
cl_kernel kernel_add, kernel_add_row, kernel_add_f16, kernel_add_row_f16;
|
||||
@@ -1052,6 +1053,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_mul_mm_f32_f32_l4_lm;
|
||||
cl_kernel kernel_gemv_f32_f32_mc; // multi-column (small-N) f32 GEMV for spec/MTP verify
|
||||
cl_kernel kernel_mul_mm_f16_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_f16_f32_l4_lm_n8;
|
||||
cl_kernel kernel_mul_mm_q1_0_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_q4_0_f32_l4_lm;
|
||||
cl_kernel kernel_mul_mm_q4_1_f32_l4_lm;
|
||||
@@ -1247,6 +1249,8 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemv_noshuffle_q8_0_f32_splitk; // split-K across WGs (small-M decode)
|
||||
cl_kernel kernel_gemm_noshuffle_q1_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q1_0_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q1_0_f32_nsg1 = nullptr;
|
||||
int min_m_gemv_nsg1_q1_0 = 0;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_o4; // 4-output-per-WI, long-vocab lm_head
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_tiled; // tiled-wide layout (opt-in)
|
||||
@@ -1261,6 +1265,11 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_32b_trans;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a = nullptr; // dp4a (int8) dense prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg = nullptr; // dp4a dense prefill GEMM, weights via texture (X1 opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg = nullptr;
|
||||
int tile_n_dp4a_q4_k = 32;
|
||||
int tile_n_dp4a_narrow_q4_k = 32;
|
||||
int max_n_dp4a_narrow_q4_k = 16;
|
||||
cl_kernel kernel_gemm_noshuffle_q5_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q5_K prefill GEMM
|
||||
cl_kernel kernel_gemm_noshuffle_q6_k_q8_1_dp4a = nullptr; // dp4a (int8) dense q6_K prefill GEMM
|
||||
cl_kernel kernel_quant_a_q8_1; // plain activation q8_1 pre-pass
|
||||
@@ -2781,6 +2790,11 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_f16_f32_l4_lm = clCreateKernel(backend_ctx->program_mul_mm_f16_f32_l4_lm, "kernel_mul_mm_f16_f32_l4_lm", &err), err));
|
||||
|
||||
const std::string narrow_opts = compile_opts + " -DBN=8 -DTN=1 -DKERNEL_NAME_LM=kernel_mul_mm_f16_f32_l4_lm_n8";
|
||||
backend_ctx->program_mul_mm_f16_f32_l4_lm_n8 =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), narrow_opts);
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_f16_f32_l4_lm_n8 = clCreateKernel(backend_ctx->program_mul_mm_f16_f32_l4_lm_n8, "kernel_mul_mm_f16_f32_l4_lm_n8", &err), err));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -3983,6 +3997,16 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q1_0_f32 = clCreateKernel(prog, "kernel_gemv_noshuffle_q1_0_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
|
||||
const char * nsg1_min_m_env = getenv("GGML_OPENCL_Q1_0_GEMV_NSG1_MIN_M");
|
||||
backend_ctx->min_m_gemv_nsg1_q1_0 = nsg1_min_m_env ? atoi(nsg1_min_m_env) :
|
||||
(backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E ? 8192 : 0);
|
||||
if (backend_ctx->min_m_gemv_nsg1_q1_0 > 0) {
|
||||
const std::string nsg1_opts = CL_gemv_compile_opts + " -DN_SIMDGROUP=1";
|
||||
cl_program prog_nsg1 = build_program_from_source(backend_ctx, kernel_src_CL_gemv_general.c_str(), nsg1_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q1_0_f32_nsg1 = clCreateKernel(prog_nsg1, "kernel_gemv_noshuffle_q1_0_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog_nsg1));
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -4453,16 +4477,32 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q4_k_q8_1_dp4a.cl");
|
||||
#endif
|
||||
// Per-device dp4a dense tile. The X2-tuned TILESIZE_N=32 over-occupies LDS on
|
||||
// X1 (1152 B/WG -> few resident WGs); TILESIZE_N=8 (288 B) lifts occupancy on
|
||||
// X1, byte-identical. X2E keeps 32. Env override wins.
|
||||
int q4k_dp4a_ts = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) { q4k_dp4a_ts = atoi(e); }
|
||||
std::string dp4a_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(q4k_dp4a_ts);
|
||||
// X1 uses a smaller N tile to reduce local memory and register use.
|
||||
int tile_n = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) { tile_n = atoi(e); }
|
||||
GGML_ASSERT(tile_n >= 4 && tile_n <= 64 && tile_n % 4 == 0);
|
||||
backend_ctx->tile_n_dp4a_q4_k = tile_n;
|
||||
std::string dp4a_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(tile_n);
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), dp4a_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg = clCreateKernel(prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
|
||||
// Compile a smaller N tile to avoid unused columns in small batches.
|
||||
int tile_n_narrow = MIN(tile_n, 16);
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4_K_DP4A_TS_NARROW")) { tile_n_narrow = atoi(e); }
|
||||
GGML_ASSERT(tile_n_narrow >= 4 && tile_n_narrow <= 64 && tile_n_narrow % 4 == 0);
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4_K_DP4A_NARROW_MAX")) {
|
||||
backend_ctx->max_n_dp4a_narrow_q4_k = atoi(e);
|
||||
}
|
||||
backend_ctx->tile_n_dp4a_narrow_q4_k = tile_n_narrow;
|
||||
if (tile_n_narrow != tile_n) {
|
||||
std::string narrow_opts = compile_opts + " -DTILESIZE_N=" + std::to_string(tile_n_narrow);
|
||||
cl_program narrow_prog = build_program_from_source(backend_ctx, kernel_src.c_str(), narrow_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow = clCreateKernel(narrow_prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg = clCreateKernel(narrow_prog, "kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg", &err), err));
|
||||
CL_CHECK(clReleaseProgram(narrow_prog));
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
@@ -9531,22 +9571,6 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
op->src[0]->ne[1] >= 32768) { // vocab-scale weight; no FFN/attn weight is this tall
|
||||
return false;
|
||||
}
|
||||
// The generic mul_mv (GEMV) kernels are wrong for large-batch prefill on
|
||||
// Adreno. A quant mul_mat only avoids the GEMV when it reaches the Adreno
|
||||
// trans-weight GEMM, which needs both a GEMM kernel for the type and
|
||||
// use_adreno_kernels(). Decline the large-N shapes that would otherwise
|
||||
// fall through to the GEMV.
|
||||
{
|
||||
const ggml_type t = op->src[0]->type;
|
||||
const bool type_has_gemm = (t == GGML_TYPE_Q4_0 || t == GGML_TYPE_Q4_1 ||
|
||||
t == GGML_TYPE_IQ4_NL || t == GGML_TYPE_Q8_0 ||
|
||||
t == GGML_TYPE_Q4_K || t == GGML_TYPE_Q5_K ||
|
||||
t == GGML_TYPE_Q6_K);
|
||||
const bool uses_gemm = type_has_gemm && use_adreno_kernels(backend_ctx, op->src[0]);
|
||||
if (!uses_gemm && op->src[1]->ne[1] >= 512) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[0]) && ggml_is_contiguous(op->src[1]);
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q8_0) {
|
||||
// ggml_cl_mul_mat_q8_0_f32_adreno now honors src1/dst view_offs (the
|
||||
@@ -19834,7 +19858,9 @@ static void ggml_cl_mul_mat_q1_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
img_desc.buffer = b_sub_buf;
|
||||
CL_CHECK((b_img = clCreateImage(context, CL_MEM_READ_ONLY, &img_fmt, &img_desc, NULL, &err), err));
|
||||
|
||||
kernel = backend_ctx->kernel_gemv_noshuffle_q1_0_f32;
|
||||
// Tall M has enough row parallelism to skip the K split and its reduction.
|
||||
const bool use_nsg1 = backend_ctx->min_m_gemv_nsg1_q1_0 > 0 && M >= backend_ctx->min_m_gemv_nsg1_q1_0;
|
||||
kernel = use_nsg1 ? backend_ctx->kernel_gemv_noshuffle_q1_0_f32_nsg1 : backend_ctx->kernel_gemv_noshuffle_q1_0_f32;
|
||||
|
||||
int r2 = 1;
|
||||
int r3 = 1;
|
||||
@@ -19856,8 +19882,9 @@ static void ggml_cl_mul_mat_q1_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &r3));
|
||||
|
||||
size_t wavesize = backend_ctx->adreno_wave_size;
|
||||
size_t local_work_size[] = { wavesize, 4, 1 };
|
||||
size_t global_work_size[] = { CEIL_DIV(M, wavesize)*wavesize, 4, 1 };
|
||||
const size_t nsg = use_nsg1 ? 1 : 4;
|
||||
size_t local_work_size[] = { wavesize, nsg, 1 };
|
||||
size_t global_work_size[] = { CEIL_DIV(M, wavesize)*wavesize, nsg, 1 };
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
|
||||
@@ -20321,7 +20348,14 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
// per-lane K-walk) for small M. Layout stride is fixed (4 uints/block), so only
|
||||
// the K-split count changes; the mc3 kernel reads it via get_local_size(1). The
|
||||
// ne1==1 base kernel hardcodes N_SIMDGROUP=4, so it always stays at 4.
|
||||
const int mc3_nsg = (use_q40_mc3 && ne01 < 4096) ? 8 : 4;
|
||||
int mc3_nsg = (use_q40_mc3 && ne01 < 4096) ? 8 : 4;
|
||||
if (use_q40_mc3) {
|
||||
// Register pressure can lower the kernel limit below the device limit.
|
||||
const size_t max_workgroup_size = backend_ctx->get_kernel_workgroup_size(kernel);
|
||||
while (mc3_nsg > 1 && (size_t)(64 * mc3_nsg) > max_workgroup_size) {
|
||||
mc3_nsg /= 2;
|
||||
}
|
||||
}
|
||||
size_t local_work_size[3] = {64, (size_t)mc3_nsg, 1};
|
||||
size_t global_work_size[3] = {(size_t)CEIL_DIV(ne01/2, 64)*64, (size_t)mc3_nsg, 1};
|
||||
|
||||
@@ -22667,8 +22701,13 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
}
|
||||
}
|
||||
|
||||
cl_kernel dk = use_wimg ? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a;
|
||||
const bool use_narrow = backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow != nullptr
|
||||
&& N <= backend_ctx->max_n_dp4a_narrow_q4_k;
|
||||
cl_kernel dk = use_narrow
|
||||
? (use_wimg ? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_narrow)
|
||||
: (use_wimg ? backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_wimg
|
||||
: backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a);
|
||||
int ai = 0;
|
||||
if (use_wimg) {
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_mem), &q4k_q_img));
|
||||
@@ -22689,12 +22728,9 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_d6));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_d4));
|
||||
CL_CHECK(clSetKernelArg(dk, ai++, sizeof(cl_uchar), &mask_hi2));
|
||||
// Must match the compile-time TILESIZE_N chosen at program build (per-device,
|
||||
// X1E=8 else 32; env override). Same inputs -> same value.
|
||||
int q4k_dp4a_ts = (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E) ? 8 : 32;
|
||||
if (const char * e = getenv("GGML_OPENCL_Q4K_DP4A_TS")) q4k_dp4a_ts = atoi(e);
|
||||
const int tile_n = use_narrow ? backend_ctx->tile_n_dp4a_narrow_q4_k : backend_ctx->tile_n_dp4a_q4_k;
|
||||
size_t d_local[3] = { 64, 1, 1 };
|
||||
size_t d_global[3] = { 64, (size_t)(M / 64), (size_t)CEIL_DIV(N, q4k_dp4a_ts) };
|
||||
size_t d_global[3] = { 64, (size_t)(M / 64), (size_t)CEIL_DIV(N, tile_n) };
|
||||
backend_ctx->enqueue_ndrange_kernel(dk, 3, d_global, d_local, dst);
|
||||
|
||||
if (q4k_q_img != nullptr) {
|
||||
@@ -24815,7 +24851,9 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
kernel = backend_ctx->kernel_mul_mm_f16_f32_l4_lm;
|
||||
static const char * mm_narrow_n_env = getenv("GGML_OPENCL_MM_NARROW_N");
|
||||
const bool use_narrow_n = (mm_narrow_n_env == nullptr || mm_narrow_n_env[0] != '0') && ne11 >= 2 && ne11 <= 8;
|
||||
kernel = use_narrow_n ? backend_ctx->kernel_mul_mm_f16_f32_l4_lm_n8 : backend_ctx->kernel_mul_mm_f16_f32_l4_lm;
|
||||
nth0 = 128; // calculated as (BM*BN)/(TM*TN)
|
||||
|
||||
int batch_stride_a = ne00*ne01;
|
||||
@@ -24874,8 +24912,9 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &r3));
|
||||
|
||||
// 64 is block tile size BM and BN - change here when BM and BN in the kernel are changed.
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)(CEIL_DIV(ne11, 64)), (size_t)ne12*ne13};
|
||||
// BN=8/TN=1 keeps 128 threads for small batches, including attention KQ/KQV.
|
||||
const int bn = use_narrow_n ? 8 : 64;
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)(CEIL_DIV(ne11, bn)), (size_t)ne12*ne13};
|
||||
size_t local_work_size[] = {(size_t)nth0, 1, 1};
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#endif
|
||||
|
||||
#define QK1_0 128
|
||||
#ifndef N_SIMDGROUP
|
||||
#define N_SIMDGROUP 4
|
||||
#endif
|
||||
|
||||
#define dequantizeBlockAccum_q1(total, bits, scale, regB, lb) \
|
||||
total += (2.0f*(float)((bits >> 0) & 1u) - 1.0f) * scale * sub_group_broadcast(regB.s0, lb+0); \
|
||||
@@ -106,15 +108,19 @@ __kernel void kernel_gemv_noshuffle_q1_0_f32(
|
||||
dequantizeBlockAccum_q1(totalSum, regA.s3, scale, regB, 12);
|
||||
}
|
||||
|
||||
// reduction in local memory, assumes #wave = N_SIMDGROUP = 4
|
||||
local float reduceLM[SIMDGROUP_WIDTH * 3];
|
||||
if (groupId == 1) reduceLM[SIMDGROUP_WIDTH * 0 + slid] = totalSum;
|
||||
if (groupId == 2) reduceLM[SIMDGROUP_WIDTH * 1 + slid] = totalSum;
|
||||
if (groupId == 3) reduceLM[SIMDGROUP_WIDTH * 2 + slid] = totalSum;
|
||||
#if N_SIMDGROUP > 1
|
||||
// Sum the K slices from the other subgroups.
|
||||
local float reduceLM[SIMDGROUP_WIDTH * (N_SIMDGROUP - 1)];
|
||||
if (groupId > 0) {
|
||||
reduceLM[SIMDGROUP_WIDTH * (groupId - 1) + slid] = totalSum;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 0 + slid];
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 1 + slid];
|
||||
if (groupId == 0) totalSum += reduceLM[SIMDGROUP_WIDTH * 2 + slid];
|
||||
if (groupId == 0) {
|
||||
for (uint w = 0; w < N_SIMDGROUP - 1; ++w) {
|
||||
totalSum += reduceLM[SIMDGROUP_WIDTH * w + slid];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (groupId == 0) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
@@ -122,6 +128,8 @@ __kernel void kernel_gemv_noshuffle_q1_0_f32(
|
||||
// so when ne01 is not a multiple of the wave size the tail work-items run past
|
||||
// row ne01 and would overrun dst into the adjacent tensor. No-op / byte-identical
|
||||
// when ne01 is wave-aligned (no padding).
|
||||
if (gid < M) dst[gid] = totalSum;
|
||||
if (gid < M) {
|
||||
dst[gid] = totalSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#ifndef BN
|
||||
#define BN 64
|
||||
#endif
|
||||
#define BK 16
|
||||
#define TM 4
|
||||
#ifndef TN
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_f16_f32_l4_lm(
|
||||
#ifndef KERNEL_NAME_LM
|
||||
#define KERNEL_NAME_LM kernel_mul_mm_f16_f32_l4_lm
|
||||
#endif
|
||||
|
||||
kernel void KERNEL_NAME_LM(
|
||||
global half4 * src0,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
@@ -93,7 +101,8 @@ kernel void kernel_mul_mm_f16_f32_l4_lm(
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
// BN=8 uses only the threads that load a column inside the tile.
|
||||
for (int l = 0; loadc_b + l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
const int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
|
||||
@@ -132,7 +132,7 @@ kernel void kernel_mul_mv_mxfp4_f32_flat(
|
||||
global float4 * y4 = (global float4 *)yb;
|
||||
|
||||
#pragma unroll
|
||||
for (short row = 0; row < N_R0_MXFP4; row++) {
|
||||
for (short row = 0; row < N_R0_MXFP4 && first_row + row < ne0; row++) {
|
||||
uchar xb_e = x_e[row * nb + ib];
|
||||
#ifdef SRC0Q_IMG
|
||||
ushort4 xb_q = as_ushort4(read_imageui(src0_q, (offset_q + row * nb + ib) * 2 + it).xy);
|
||||
|
||||
@@ -520,7 +520,7 @@ kernel void kernel_rope_multi_f16(
|
||||
src0 = (global void*)((global char*)src0 + offset0);
|
||||
src1 = (global int*)((global char*)src1 + offset1);
|
||||
src2 = (global float*)((global char*)src2 + offset2);
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
dst = (global half*)((global char*)dst + offsetd);
|
||||
|
||||
int i3 = get_group_id(2);
|
||||
int i2 = get_group_id(1);
|
||||
@@ -714,7 +714,7 @@ kernel void kernel_rope_vision_f16(
|
||||
src0 = (global void*)((global char*)src0 + offset0);
|
||||
src1 = (global int*)((global char*)src1 + offset1);
|
||||
src2 = (global float*)((global char*)src2 + offset2);
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
dst = (global half*)((global char*)dst + offsetd);
|
||||
|
||||
int i3 = get_group_id(2);
|
||||
int i2 = get_group_id(1);
|
||||
|
||||
@@ -44,7 +44,7 @@ if (WIN32)
|
||||
if(DEFINED ENV{LEVEL_ZERO_V1_SDK_PATH})
|
||||
set(LEVEL_ZERO_V1_SDK_PATH $ENV{LEVEL_ZERO_V1_SDK_PATH})
|
||||
if(EXISTS "${LEVEL_ZERO_V1_SDK_PATH}")
|
||||
target_include_directories(ggml-sycl PRIVATE "${LEVEL_ZERO_V1_SDK_PATH}/include")
|
||||
target_include_directories(ggml-sycl SYSTEM PRIVATE "${LEVEL_ZERO_V1_SDK_PATH}/include")
|
||||
set(LEVEL_ZERO_V1_SDK_LIB_PATH "${LEVEL_ZERO_V1_SDK_PATH}/lib")
|
||||
else()
|
||||
message(WARNING "LEVEL_ZERO_V1_SDK_PATH set but folder not found: ${LEVEL_ZERO_V1_SDK_PATH}")
|
||||
|
||||
@@ -504,6 +504,5 @@ void ggml_sycl_op_concat(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
default:
|
||||
fprintf(stderr, "%s: unsupported types: dst: %s\n", __func__, ggml_type_name(dst->type));
|
||||
GGML_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ static void dequantize_row_q2_K_sycl_reorder(const void *vx, dst_t *y, const int
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 64), sycl::range<3>(1, 1, 64)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q2_K_reorder(vx, y, item_ct1, nb);
|
||||
dequantize_block_q2_K_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), item_ct1, nb);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ static void dequantize_row_q3_K_sycl_reorder(const void *vx, dst_t *y, const int
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 64), sycl::range<3>(1, 1, 64)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q3_K_reorder(vx, y, item_ct1, nb);
|
||||
dequantize_block_q3_K_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), item_ct1, nb);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ static void dequantize_row_q4_K_sycl_reorder(const void * vx, dst_t * y, const i
|
||||
|
||||
cgh.parallel_for(sycl::nd_range<1>(sycl::range<1>(global_size), sycl::range<1>(local_size)),
|
||||
[=](sycl::nd_item<1> item_ct1) {
|
||||
dequantize_block_q4_K_reorder(vx, y, get_pointer(scale_local_acc), item_ct1, nb);
|
||||
dequantize_block_q4_K_reorder(vx, y + item_ct1.get_group(0) * QK_K, item_ct1.get_group(0), get_pointer(scale_local_acc), item_ct1, nb);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -258,7 +258,7 @@ static void dequantize_row_q5_K_sycl_reorder(const void * vx, dst_t * y, const i
|
||||
cgh.parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 64), sycl::range<3>(1, 1, 64)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q5_K_reorder(vx, y, get_pointer(scale_local_acc), item_ct1, nb);
|
||||
dequantize_block_q5_K_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), get_pointer(scale_local_acc), item_ct1, nb);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -303,7 +303,7 @@ static void dequantize_row_q6_K_sycl_reorder(const void * vx, dst_t * y, const i
|
||||
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 64), sycl::range<3>(1, 1, 64)),
|
||||
[=](sycl::nd_item<3> item_ct1) { dequantize_block_q6_K_reorder(vx, y, item_ct1, nb); });
|
||||
[=](sycl::nd_item<3> item_ct1) { dequantize_block_q6_K_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), item_ct1, nb); });
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
@@ -757,7 +757,6 @@ to_fp16_sycl_t ggml_get_to_fp16_sycl(ggml_type type, ggml_tensor * dst) {
|
||||
#endif
|
||||
default:
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,7 +860,6 @@ to_fp32_sycl_t ggml_get_to_fp32_sycl(ggml_type type, ggml_tensor *dst) {
|
||||
#endif
|
||||
default:
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,7 +875,6 @@ to_bf16_sycl_t ggml_get_to_bf16_sycl(ggml_type type, ggml_tensor * /*dst*/) {
|
||||
return convert_unary_sycl<sycl::ext::oneapi::bfloat16>;
|
||||
default:
|
||||
GGML_ABORT("fatal error: unsupport data type=%s\n", ggml_type_name(type));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -74,6 +74,5 @@ void ggml_sycl_count_equal(ggml_backend_sycl_context &ctx, ggml_tensor *dst) {
|
||||
} break;
|
||||
default:
|
||||
GGML_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -926,10 +926,9 @@ static void dequantize_block_q2_K(const void * __restrict__ vx, dst_t * __restri
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_q2_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
static void dequantize_block_q2_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t i,
|
||||
const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
#if QK_K == 256
|
||||
const int64_t i = item_ct1.get_group(2);
|
||||
if (i >= n_blocks) {
|
||||
return;
|
||||
}
|
||||
@@ -949,7 +948,7 @@ static void dequantize_block_q2_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const int64_t is = 8 * n + l / 16;
|
||||
|
||||
const uint8_t q = qs[32 * n + l];
|
||||
dst_t * y = yy + i * QK_K + 128 * n;
|
||||
dst_t * y = yy + 128 * n;
|
||||
|
||||
const float dall = (*dm)[0];
|
||||
const float dmin = (*dm)[1];
|
||||
@@ -960,6 +959,7 @@ static void dequantize_block_q2_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
GGML_UNUSED(i);
|
||||
GGML_UNUSED(item_ct1);
|
||||
GGML_UNUSED(n_blocks);
|
||||
GGML_ABORT("Q2_K reorder dequantize not supported for QK_K != 256");
|
||||
@@ -1022,10 +1022,9 @@ static void dequantize_block_q3_K(const void * __restrict__ vx, dst_t * __restri
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_q3_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
static void dequantize_block_q3_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t i,
|
||||
const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
#if QK_K == 256
|
||||
const int64_t i = item_ct1.get_group(2);
|
||||
if (i >= n_blocks) {
|
||||
return;
|
||||
}
|
||||
@@ -1062,7 +1061,7 @@ static void dequantize_block_q3_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
|
||||
const float dl = d_all * (us - 32);
|
||||
|
||||
dst_t * y = yy + i * QK_K + 128 * n + 32 * j;
|
||||
dst_t * y = yy + 128 * n + 32 * j;
|
||||
const uint8_t * q = qs + 32 * n;
|
||||
const uint8_t * hm = hmask;
|
||||
|
||||
@@ -1072,6 +1071,7 @@ static void dequantize_block_q3_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
GGML_UNUSED(i);
|
||||
GGML_UNUSED(item_ct1);
|
||||
GGML_UNUSED(n_blocks);
|
||||
GGML_ABORT("Q3_K reorder dequantize not supported for QK_K != 256");
|
||||
@@ -1160,14 +1160,13 @@ inline void dequantize_q4_K_common(dst_t * __restrict__ y, const uint8_t * __res
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
static void dequantize_block_q4_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, uint8_t * scales_local,
|
||||
const sycl::nd_item<1> & item_ct1, int64_t nb) {
|
||||
const int64_t i = item_ct1.get_group(0); // block index
|
||||
static void dequantize_block_q4_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t i,
|
||||
uint8_t * scales_local, const sycl::nd_item<1> & item_ct1, int64_t nb) {
|
||||
const int64_t tid = item_ct1.get_local_id(0); // thread index within block
|
||||
const int64_t il = tid / 8;
|
||||
const int64_t ir = tid % 8;
|
||||
|
||||
dst_t * y = yy + i * QK_K + 64 * il + 4 * ir;
|
||||
dst_t * y = yy + 64 * il + 4 * ir;
|
||||
|
||||
const uint8_t * base = static_cast<const uint8_t *>(vx);
|
||||
const size_t qs_offset = i * (QK_K / 2);
|
||||
@@ -1237,9 +1236,8 @@ static void dequantize_block_q5_K_wide(const void * __restrict__ vx, dst_t * __r
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t ib,
|
||||
uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
const int64_t ib = item_ct1.get_group(2);
|
||||
|
||||
#if QK_K == 256
|
||||
// assume 64 threads
|
||||
@@ -1248,7 +1246,7 @@ static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const int64_t ir = tid % 16; // 0...15
|
||||
const int64_t is = 2 * il;
|
||||
|
||||
dst_t * y = yy + ib * QK_K + 64 * il + 2 * ir;
|
||||
dst_t * y = yy + 64 * il + 2 * ir;
|
||||
|
||||
const uint8_t * base = static_cast<const uint8_t *>(vx);
|
||||
|
||||
@@ -1340,9 +1338,8 @@ static void dequantize_block_q6_K(const void * __restrict__ vx, dst_t * __restri
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
static void dequantize_block_q6_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy,
|
||||
static void dequantize_block_q6_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t ib,
|
||||
const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
const int64_t ib = item_ct1.get_group(2);
|
||||
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
const int64_t ip = tid / 32; // ip is 0 or 1
|
||||
@@ -1359,7 +1356,7 @@ static void dequantize_block_q6_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const uint8_t * scales_ptr = base_ptr + base_scales_offset;
|
||||
const ggml_half * d = (const ggml_half *) (base_ptr + base_d_offset) + ib;
|
||||
|
||||
dst_t * y = yy + ib * QK_K + 128 * ip + il;
|
||||
dst_t * y = yy + 128 * ip + il;
|
||||
|
||||
const uint8_t * ql = ql_ptr + 64 * ip + il;
|
||||
const uint8_t qh = *(qh_ptr + 32 * ip + il);
|
||||
|
||||
@@ -3159,7 +3159,7 @@ namespace dpct
|
||||
_local_mem_size = local_mem_size;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
static inline std::mutex kernel_function_ptr_map_mutex;
|
||||
|
||||
public:
|
||||
|
||||
@@ -1169,7 +1169,7 @@ static void swiglu_oai_sycl(const T * x,
|
||||
});
|
||||
}
|
||||
|
||||
void ggml_sycl_op_swiglu_oai(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
static void ggml_sycl_op_swiglu_oai(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
void * src0_d = src0->data;
|
||||
|
||||
@@ -832,6 +832,29 @@ static void flash_attn_combine_results(const float * __restrict__ VKQ_parts,
|
||||
dst[tid] = VKQ_numerator / VKQ_denominator;
|
||||
}
|
||||
|
||||
// Carries the sub-group size (and grf_size_automatic when use_large_grf) as
|
||||
// kernel properties via get(properties_tag), in place of the
|
||||
// reqd_sub_group_size attribute and the deprecated parallel_for overload.
|
||||
template <typename KernelFunc, uint32_t Size, bool UseGrf>
|
||||
struct props_kernel {
|
||||
KernelFunc kernel;
|
||||
|
||||
void operator()(sycl::nd_item<3> item) const {
|
||||
kernel(item);
|
||||
}
|
||||
|
||||
auto get(sycl::ext::oneapi::experimental::properties_tag) const {
|
||||
if constexpr (UseGrf) {
|
||||
return sycl::ext::oneapi::experimental::properties{
|
||||
sycl::ext::intel::experimental::grf_size_automatic,
|
||||
sycl::ext::oneapi::experimental::sub_group_size<Size> };
|
||||
} else {
|
||||
return sycl::ext::oneapi::experimental::properties{
|
||||
sycl::ext::oneapi::experimental::sub_group_size<Size> };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <fattn_kernel_t fattn_kernel, int warp_size, bool use_large_grf = false>
|
||||
static void lauch_kernel(
|
||||
dpct::dim3 group_range,
|
||||
@@ -881,7 +904,7 @@ static void lauch_kernel(
|
||||
static_cast<sycl::range<3>>(group_range * local_range),
|
||||
static_cast<sycl::range<3>>(local_range));
|
||||
|
||||
const auto kernel = [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(warp_size)]] {
|
||||
const auto kernel = [=](sycl::nd_item<3> item_ct1) {
|
||||
GGML_UNUSED(item_ct1);
|
||||
fattn_kernel(Q, K, V, mask, sinks, KV_max, dst, dst_meta, scale,
|
||||
max_bias, m0, m1, n_head_log2, logit_softcap, ne00,
|
||||
@@ -892,14 +915,7 @@ static void lauch_kernel(
|
||||
|
||||
q->submit([&](sycl::handler &cgh) {
|
||||
// grf_size_automatic lets the compiler use the large register file when the kernel needs it. grf_size<256> would say the same but is undefined on devices that do not have it.
|
||||
if constexpr (use_large_grf) {
|
||||
cgh.parallel_for(
|
||||
rng,
|
||||
sycl::ext::oneapi::experimental::properties{ sycl::ext::intel::experimental::grf_size_automatic },
|
||||
kernel);
|
||||
} else {
|
||||
cgh.parallel_for(rng, kernel);
|
||||
}
|
||||
cgh.parallel_for(rng, props_kernel<decltype(kernel), warp_size, use_large_grf>{kernel});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,8 @@ bool ggml_sycl_flash_attn_ext_sparse(ggml_backend_sycl_context & ctx, ggml_tenso
|
||||
uint8_t * d_V = v_alloc.get();
|
||||
sycl::half * d_mask = m_alloc.get();
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memset(d_cnt, 0, sizeof(int32_t))));
|
||||
// reset with a kernel: on Level Zero a queue memset is not always ordered before the next kernel
|
||||
stream->single_task([=]() { *d_cnt = 0; });
|
||||
|
||||
sparse_fa_compact_mask(stream, (const sycl::half *) mask->data,
|
||||
d_idx, d_cnt, n_kv, n_kv_g);
|
||||
|
||||
@@ -54,6 +54,6 @@ void ggml_sycl_flash_attn_ext_tile(ggml_backend_sycl_context & ctx, ggml_tensor
|
||||
} break;
|
||||
default: {
|
||||
GGML_ABORT("Unsupported head size");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +137,47 @@ static void k_get_rows_float(
|
||||
dst_row[i00] = src0_row[i00];
|
||||
}
|
||||
|
||||
// src0 is in reorder layout: all qs of the tensor, then all d
|
||||
template<int qk, int qr, dequantize_kernel_t_reorder dequantize_kernel, typename dst_t>
|
||||
static void k_get_rows_reorder(
|
||||
const void * src0_qs, const void * src0_d, const int32_t * src1, dst_t * dst,
|
||||
int64_t ne00, int64_t ne12,
|
||||
size_t s1, size_t s2, size_t s3,
|
||||
size_t s10, size_t s11, size_t s12,
|
||||
const sycl::nd_item<3> &item_ct1) {
|
||||
|
||||
const int i00 = (item_ct1.get_group(2) * item_ct1.get_local_range(2) +
|
||||
item_ct1.get_local_id(2)) *
|
||||
2;
|
||||
const int i10 = item_ct1.get_local_range(1) * item_ct1.get_group(1) +
|
||||
item_ct1.get_local_id(1);
|
||||
const int i11 = (item_ct1.get_group(0) * item_ct1.get_local_range(0) +
|
||||
item_ct1.get_local_id(0)) /
|
||||
ne12;
|
||||
const int i12 = (item_ct1.get_group(0) * item_ct1.get_local_range(0) +
|
||||
item_ct1.get_local_id(0)) %
|
||||
ne12;
|
||||
|
||||
if (i00 >= ne00) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int i01 = src1[i10*s10 + i11*s11 + i12*s12];
|
||||
|
||||
dst_t * dst_row = dst + i10*s1 + i11*s2 + i12*s3;
|
||||
|
||||
const int64_t ib = (int64_t) i01*(ne00/qk) + i00/qk; // block index in the whole tensor
|
||||
const int iqs = (i00%qk)/qr;
|
||||
const int iybs = i00 - i00%qk;
|
||||
const int y_offset = qr == 1 ? 1 : qk/2;
|
||||
|
||||
dfloat2 v;
|
||||
dequantize_kernel(src0_d, ib, src0_qs, ib*(qk/qr) + iqs, v);
|
||||
|
||||
dst_row[iybs + iqs + 0] = v.x();
|
||||
dst_row[iybs + iqs + y_offset] = v.y();
|
||||
}
|
||||
|
||||
template <int qk, int qr, dequantize_kernel_t dq>
|
||||
static void get_rows_sycl(ggml_backend_sycl_context & ctx, const ggml_tensor *src0, const ggml_tensor *src1,
|
||||
ggml_tensor *dst, const void *src0_dd,
|
||||
@@ -173,6 +214,93 @@ static void get_rows_sycl(ggml_backend_sycl_context & ctx, const ggml_tensor *sr
|
||||
GGML_UNUSED(ctx);
|
||||
}
|
||||
|
||||
template <int qk, int qr, dequantize_kernel_t_reorder dq>
|
||||
static void get_rows_sycl_reorder(const ggml_tensor *src0, const ggml_tensor *src1,
|
||||
ggml_tensor *dst, const void *src0_dd,
|
||||
const int32_t *src1_dd, float *dst_dd,
|
||||
queue_ptr stream) {
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
// reorder is done only on 2D weights
|
||||
GGML_ASSERT(ne02 == 1 && ne03 == 1);
|
||||
GGML_ASSERT(ne00 % 2 == 0);
|
||||
|
||||
const sycl::range<3> block_dims(1, 1, SYCL_GET_ROWS_BLOCK_SIZE);
|
||||
const int block_num_x = (ne00 + 2*SYCL_GET_ROWS_BLOCK_SIZE - 1) / (2*SYCL_GET_ROWS_BLOCK_SIZE);
|
||||
const sycl::range<3> block_nums(ne11 * ne12, ne10, block_num_x);
|
||||
|
||||
const size_t s1 = nb1 / ggml_element_size(dst);
|
||||
const size_t s2 = nb2 / ggml_element_size(dst);
|
||||
const size_t s3 = nb3 / ggml_element_size(dst);
|
||||
|
||||
const size_t s10 = nb10 / ggml_element_size(src1);
|
||||
const size_t s11 = nb11 / ggml_element_size(src1);
|
||||
const size_t s12 = nb12 / ggml_element_size(src1);
|
||||
|
||||
const void * src0_d = (const char *) src0_dd + ne00*ne01/qr;
|
||||
|
||||
stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
k_get_rows_reorder<qk, qr, dq>(
|
||||
src0_dd, src0_d, src1_dd, dst_dd, ne00, ne12, s1, s2,
|
||||
s3, s10, s11, s12, item_ct1);
|
||||
});
|
||||
|
||||
GGML_UNUSED(src0);
|
||||
}
|
||||
|
||||
// K-quants in reorder layout: one work-group dequantizes one QK_K block of one row
|
||||
template <int dims, int nthreads, int nlocal, typename F>
|
||||
static void get_rows_sycl_reorder_k(const ggml_tensor *src0, const ggml_tensor *src1,
|
||||
ggml_tensor *dst, const int32_t *src1_dd, float *dst_dd,
|
||||
queue_ptr stream, F dequantize_block) {
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
// reorder is done only on 2D weights
|
||||
GGML_ASSERT(ne02 == 1 && ne03 == 1);
|
||||
GGML_ASSERT(ne00 % QK_K == 0);
|
||||
|
||||
const int64_t nblk = ne00 / QK_K;
|
||||
const int64_t nb = nblk * ne01; // blocks in the whole tensor
|
||||
const int64_t ngroups = ne10 * ne11 * ne12 * nblk;
|
||||
|
||||
const size_t s1 = nb1 / ggml_element_size(dst);
|
||||
const size_t s2 = nb2 / ggml_element_size(dst);
|
||||
const size_t s3 = nb3 / ggml_element_size(dst);
|
||||
|
||||
const size_t s10 = nb10 / ggml_element_size(src1);
|
||||
const size_t s11 = nb11 / ggml_element_size(src1);
|
||||
const size_t s12 = nb12 / ggml_element_size(src1);
|
||||
|
||||
stream->submit([&](sycl::handler & cgh) {
|
||||
sycl::local_accessor<uint8_t, 1> scales_local(sycl::range<1>(nlocal), cgh);
|
||||
|
||||
auto kernel = [=](sycl::nd_item<dims> item_ct1) {
|
||||
const int64_t g = item_ct1.get_group(dims - 1);
|
||||
const int64_t b = g % nblk;
|
||||
const int64_t r = g / nblk;
|
||||
const int64_t i10 = r % ne10;
|
||||
const int64_t i11 = (r / ne10) % ne11;
|
||||
const int64_t i12 = r / (ne10 * ne11);
|
||||
|
||||
const int64_t i01 = src1_dd[i10*s10 + i11*s11 + i12*s12];
|
||||
|
||||
float * y = dst_dd + i10*s1 + i11*s2 + i12*s3 + b*QK_K;
|
||||
dequantize_block(y, i01*nblk + b, get_pointer(scales_local), item_ct1, nb);
|
||||
};
|
||||
|
||||
if constexpr (dims == 1) {
|
||||
cgh.parallel_for(sycl::nd_range<1>(sycl::range<1>(ngroups * nthreads), sycl::range<1>(nthreads)), kernel);
|
||||
} else {
|
||||
cgh.parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, ngroups * nthreads), sycl::range<3>(1, 1, nthreads)), kernel);
|
||||
}
|
||||
});
|
||||
|
||||
GGML_UNUSED(src0);
|
||||
}
|
||||
|
||||
template <int qk, int qr, dequantize_kernel_f32_t dq>
|
||||
static void get_rows_sycl_f32(ggml_backend_sycl_context & ctx, const ggml_tensor *src0, const ggml_tensor *src1,
|
||||
ggml_tensor *dst, const void *src0_dd,
|
||||
@@ -332,6 +460,9 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
GGML_ASSERT(dst->nb[0] == ggml_type_size(dst->type));
|
||||
|
||||
const int32_t * src1_i32 = (const int32_t *) dst->src[1]->data;
|
||||
// a weight shared with MUL_MAT (e.g. tied embeddings) can be in reorder layout
|
||||
const bool src0_reordered = dst->src[0]->extra &&
|
||||
((const ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder;
|
||||
/* TODO: Refactor and remove duplicates */
|
||||
switch (dst->src[0]->type) {
|
||||
case GGML_TYPE_F16:
|
||||
@@ -399,14 +530,37 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q2_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<3, 64, 1>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q2_K_reorder(vx, y, ib, item_ct1, nb);
|
||||
GGML_UNUSED(scales_local);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q2_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q3_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<3, 64, 1>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q3_K_reorder(vx, y, ib, item_ct1, nb);
|
||||
GGML_UNUSED(scales_local);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK_K, 1, dequantize_q3_K>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q4_0:
|
||||
if (src0_reordered) {
|
||||
get_rows_sycl_reorder<QK4_0, QR4_0, dequantize_q4_0_reorder>(dst->src[0], dst->src[1], dst, dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK4_0, QR4_0, dequantize_q4_0>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
@@ -415,6 +569,14 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q4_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<1, 32, K_SCALE_SIZE>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<1> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q4_K_reorder(vx, y, ib, scales_local, item_ct1, nb);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q4_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
@@ -427,14 +589,36 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q5_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<3, 64, K_SCALE_SIZE>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q5_K_reorder(vx, y, ib, scales_local, item_ct1, nb);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl_f32<QK_K, 1, dequantize_q5_K_f32>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q6_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<3, 64, 1>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q6_K_reorder(vx, y, ib, item_ct1, nb);
|
||||
GGML_UNUSED(scales_local);
|
||||
});
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK_K, 1, dequantize_q6_K>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
case GGML_TYPE_Q8_0:
|
||||
if (src0_reordered) {
|
||||
get_rows_sycl_reorder<QK8_0, QR8_0, dequantize_q8_0_reorder>(dst->src[0], dst->src[1], dst, dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
}
|
||||
get_rows_sycl<QK8_0, QR8_0, dequantize_q8_0>(ctx, dst->src[0], dst->src[1], dst, (const float *)dst->src[0]->data,
|
||||
src1_i32, (float *)dst->data, ctx.stream());
|
||||
break;
|
||||
@@ -468,6 +652,5 @@ void ggml_sycl_op_get_rows_back(ggml_backend_sycl_context & ctx, ggml_tensor * d
|
||||
break;
|
||||
default:
|
||||
GGML_ABORT("%s: unsupported src0 type: %s\n", __func__, ggml_type_name(src0->type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,14 +388,14 @@ static ze_result_t init_zes() {
|
||||
return res;
|
||||
}
|
||||
|
||||
ze_result_t get_zes_init_res() {
|
||||
static ze_result_t get_zes_init_res() {
|
||||
static ze_result_t zes_init_res = init_zes();
|
||||
GGML_SYCL_DEBUG("[SYCL] call %s: zesInit result: %d\n", __func__, (int) zes_init_res);
|
||||
return zes_init_res;
|
||||
}
|
||||
#endif
|
||||
|
||||
void initialize_sycl_begining() {
|
||||
static void initialize_sycl_begining() {
|
||||
#ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API
|
||||
//must be called in initialization stage, before any other Level Zero API calls
|
||||
GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
|
||||
@@ -1173,7 +1173,7 @@ static size_t ggml_backend_sycl_buffer_type_get_alignment(ggml_backend_buffer_ty
|
||||
GGML_UNUSED(buft);
|
||||
}
|
||||
|
||||
bool is_bmg_g31_arch(int device) {
|
||||
static bool is_bmg_g31_arch(int device) {
|
||||
return ggml_sycl_info().devices[device].hw_info.arch == gpu_arch::intel_gpu_bmg_g31;
|
||||
}
|
||||
|
||||
@@ -6326,7 +6326,7 @@ catch (sycl::exception const &exc) {
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
bool sycl_get_mem_info(int device, size_t * free, size_t * total) {
|
||||
static bool sycl_get_mem_info(int device, size_t * free, size_t * total) {
|
||||
GGML_SYCL_DEBUG("[SYCL] [%s] g_ggml_sycl_get_mem_api=%d\n",
|
||||
__func__, g_ggml_sycl_get_mem_api);
|
||||
|
||||
@@ -6952,7 +6952,6 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GGML_OP_MUL_MAT:
|
||||
case GGML_OP_MUL_MAT_ID:
|
||||
{
|
||||
@@ -7045,7 +7044,6 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
|
||||
(op->src[1]->type == GGML_TYPE_I64 || op->src[1]->type == GGML_TYPE_I32);
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
case GGML_OP_DSV4_HC_PRE:
|
||||
return op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32 &&
|
||||
op->type == GGML_TYPE_F32;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <sycl/sycl.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "gla.hpp"
|
||||
|
||||
template <u_int HEAD_SIZE>
|
||||
static void gated_linear_attn_f32_kernel(const dpct::queue_ptr stream, u_int B, u_int T, u_int C, u_int H, float scale,
|
||||
|
||||
@@ -27,7 +27,7 @@ const char * mem_api_int2str(int mem_api) {
|
||||
/*
|
||||
* Depend on to call zesInit(0) before any other Level Zero API calls, otherwise the Level Zero API calls may fail.
|
||||
*/
|
||||
bool query_free_memory_by_ze(sycl::device dev, size_t & free_bytes, size_t & total_bytes) {
|
||||
static bool query_free_memory_by_ze(sycl::device dev, size_t & free_bytes, size_t & total_bytes) {
|
||||
GGML_SYCL_DEBUG("[SYCL] call %s: Querying free memory using Level Zero API.\n", __func__);
|
||||
|
||||
free_bytes = 0;
|
||||
@@ -94,7 +94,7 @@ bool query_free_memory_by_ze(sycl::device dev, size_t & free_bytes, size_t & tot
|
||||
}
|
||||
#endif
|
||||
|
||||
bool get_memory_size_by_sycl_api(sycl::device dev, size_t & free_bytes, size_t & total_bytes) {
|
||||
static bool get_memory_size_by_sycl_api(sycl::device dev, size_t & free_bytes, size_t & total_bytes) {
|
||||
GGML_SYCL_DEBUG("[SYCL] call %s: Querying free memory using SYCL API.\n", __func__);
|
||||
total_bytes = dev.get_info<sycl::info::device::global_mem_size>();
|
||||
|
||||
|
||||
@@ -296,7 +296,6 @@ static void ggml_sycl_op_topk_moe(ggml_backend_sycl_context & ctx,
|
||||
break;
|
||||
default:
|
||||
GGML_ASSERT(false && "fatal error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,8 +183,8 @@ static void upscale_f32_bilinear_antialias(const float * src0,
|
||||
}
|
||||
|
||||
namespace bicubic_interpolation {
|
||||
static float weight1(float x, const float &a) { return ((a + 2) * x - (a + 3)) * x * x + 1; };
|
||||
static float weight2(float x, const float &a) { return ((a * x - 5 * a) * x + 8 * a) * x - 4 * a; };
|
||||
static float weight1(float x, const float &a) { return ((a + 2) * x - (a + 3)) * x * x + 1; }
|
||||
static float weight2(float x, const float &a) { return ((a * x - 5 * a) * x + 8 * a) * x - 4 * a; }
|
||||
|
||||
static float bicubic(float p0, float p1, float p2, float p3, float x, float a) {
|
||||
const float w0 = weight2(x + 1, a);
|
||||
@@ -192,7 +192,7 @@ static float bicubic(float p0, float p1, float p2, float p3, float x, float a) {
|
||||
const float w2 = weight1(1 - x, a);
|
||||
const float w3 = weight2(2 - x, a);
|
||||
return p0 * w0 + p1 * w1 + p2 * w2 + p3 * w3;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ static void upscale_f32_bicubic_sycl(const float * x,
|
||||
}
|
||||
}
|
||||
|
||||
void ggml_sycl_op_upscale(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
static void ggml_sycl_op_upscale(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const float * src0_d = (const float *)src0->data;
|
||||
float * dst_d = (float *)dst->data;
|
||||
|
||||
@@ -442,7 +442,7 @@ template <> struct reorder_vec_dot_q_sycl<GGML_TYPE_Q4_0> {
|
||||
}
|
||||
|
||||
return vec_dot_q4_0_q8_1_impl(v, u, d, *q8_1_ds);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Load four contiguous dwords per operand instead of loading each value separately.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "zdnn.h"
|
||||
|
||||
#include "scratch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <future>
|
||||
@@ -14,9 +16,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define GGML_ZDNN_NAME "zDNN"
|
||||
#define GGML_ZDNN_VERSION ZDNN_VERNUM
|
||||
#define GGML_ZDNN_SCRATCH_SLOTS 3 // int8 inputs, DFloat16 partial results, partial results for grouped matmul
|
||||
#define GGML_ZDNN_NAME "zDNN"
|
||||
#define GGML_ZDNN_VERSION ZDNN_VERNUM
|
||||
|
||||
#define ZDNN_CHECK(stmt) \
|
||||
do { \
|
||||
@@ -68,44 +69,100 @@ struct ggml_backend_zdnn_context {
|
||||
// zero bias for stacked quantized matmuls, keyed by (n_stacks, ne0)
|
||||
std::map<std::pair<int64_t, int64_t>, std::unique_ptr<ggml_backend_zdnn_buffer>> zero_bias;
|
||||
|
||||
std::vector<int8_t> scratch_q;
|
||||
std::vector<float> scratch_f32;
|
||||
|
||||
void * scratch_ztensor[GGML_ZDNN_SCRATCH_SLOTS] = {}; // 4K aligned
|
||||
size_t scratch_ztensor_size[GGML_ZDNN_SCRATCH_SLOTS] = {};
|
||||
ggml_zdnn_pool pool; // scratch memory of the ops
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static inline T * ggml_zdnn_resize_scratch_max(std::vector<T> & scratch, int64_t n) {
|
||||
if ((int64_t)scratch.size() < n) {
|
||||
scratch.resize(n);
|
||||
}
|
||||
|
||||
return scratch.data();
|
||||
}
|
||||
|
||||
//
|
||||
// Scratch buffers
|
||||
//
|
||||
|
||||
static inline void ggml_zdnn_init_scratch_ztensor(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
int32_t slot) {
|
||||
// the ztensor uses the memory of scratch, so scratch must outlive it
|
||||
static inline void ggml_zdnn_init_scratch_ztensor(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
ggml_zdnn_pool_alloc<uint8_t> & scratch) {
|
||||
|
||||
const uint64_t size = zdnn_getsize_ztensor(tfm_desc);
|
||||
|
||||
if (ctx->scratch_ztensor_size[slot] < size) {
|
||||
free(ctx->scratch_ztensor[slot]);
|
||||
ctx->scratch_ztensor[slot] = std::aligned_alloc(4096, GGML_PAD(size, 4096));
|
||||
ctx->scratch_ztensor_size[slot] = GGML_PAD(size, 4096);
|
||||
GGML_ASSERT(ctx->scratch_ztensor[slot] != nullptr);
|
||||
zdnn_init_quantized_ztensor(pre_tfm_desc, tfm_desc, 1.0f, 0.0f, ztensor);
|
||||
ztensor->buffer = scratch.alloc(ctx->pool, size);
|
||||
ztensor->buffer_size = size;
|
||||
}
|
||||
|
||||
// same as ggml_zdnn_init_scratch_ztensor, for a DLFLOAT16 ztensor
|
||||
static inline void ggml_zdnn_init_scratch_ztensor_dlf16(ggml_backend_zdnn_context * ctx,
|
||||
zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
zdnn_ztensor * ztensor,
|
||||
ggml_zdnn_pool_alloc<uint8_t> & scratch) {
|
||||
|
||||
const uint64_t size = zdnn_getsize_ztensor(tfm_desc);
|
||||
|
||||
zdnn_init_ztensor(pre_tfm_desc, tfm_desc, ztensor);
|
||||
ztensor->buffer = scratch.alloc(ctx->pool, size);
|
||||
ztensor->buffer_size = size;
|
||||
}
|
||||
|
||||
//
|
||||
// Stacked ztensors
|
||||
//
|
||||
|
||||
// view of the stacks [s0, s0 + n) of a stacked ztensor, the stacks are the outermost dim of its buffer
|
||||
// the view descs must outlive the view
|
||||
static inline zdnn_ztensor ggml_zdnn_stack_view(zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
const zdnn_ztensor & src,
|
||||
int64_t n_stacks,
|
||||
int64_t s0,
|
||||
int64_t n) {
|
||||
|
||||
GGML_ASSERT(src.buffer_size % n_stacks == 0);
|
||||
const uint64_t stack_bytes = src.buffer_size / n_stacks;
|
||||
|
||||
*pre_tfm_desc = *src.pre_transformed_desc;
|
||||
*tfm_desc = *src.transformed_desc;
|
||||
|
||||
switch (pre_tfm_desc->layout) {
|
||||
case ZDNN_3DS:
|
||||
{
|
||||
pre_tfm_desc->dim3 = n;
|
||||
} break;
|
||||
case ZDNN_2DS:
|
||||
{
|
||||
pre_tfm_desc->dim2 = n;
|
||||
} break;
|
||||
default:
|
||||
GGML_ABORT("%s: unsupported stacked layout", __func__);
|
||||
}
|
||||
|
||||
zdnn_init_quantized_ztensor(pre_tfm_desc, tfm_desc, 1.0f, 0.0f, ztensor);
|
||||
ztensor->buffer = ctx->scratch_ztensor[slot];
|
||||
ztensor->buffer_size = size;
|
||||
tfm_desc->dim4 = n;
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(tfm_desc) == n*stack_bytes);
|
||||
|
||||
zdnn_ztensor view = src;
|
||||
view.pre_transformed_desc = pre_tfm_desc;
|
||||
view.transformed_desc = tfm_desc;
|
||||
view.buffer = (char *) src.buffer + s0*stack_bytes;
|
||||
view.buffer_size = n * stack_bytes;
|
||||
return view;
|
||||
}
|
||||
|
||||
// zero bias of n_stacks stacks, created once per shape as it never changes
|
||||
static inline const zdnn_ztensor & ggml_zdnn_zero_bias(ggml_backend_zdnn_context * ctx, int64_t n_stacks, int64_t ne0) {
|
||||
std::unique_ptr<ggml_backend_zdnn_buffer> & bias = ctx->zero_bias[{ n_stacks, ne0 }];
|
||||
|
||||
if (!bias) {
|
||||
bias = std::make_unique<ggml_backend_zdnn_buffer>();
|
||||
|
||||
std::vector<float> zeros(n_stacks * ne0, 0.0f);
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias->pre_tfm_desc, n_stacks, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias->pre_tfm_desc, &bias->tfm_desc));
|
||||
ZDNN_CHECK(zdnn_init_ztensor_with_malloc(&bias->pre_tfm_desc, &bias->tfm_desc, &bias->ztensor));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias->ztensor, zeros.data()));
|
||||
}
|
||||
|
||||
return bias->ztensor;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "ggml.h"
|
||||
#include "fattn.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
// DLFLOAT16 has no infinity, so masked scores use the lowest FP16 value, exp of which is still 0
|
||||
static inline float ggml_zdnn_fattn_mask_value(ggml_fp16_t m) {
|
||||
return std::max(GGML_FP16_TO_FP32(m), -65504.0f);
|
||||
}
|
||||
|
||||
bool ggml_zdnn_supports_flash_attn_ext(const ggml_backend_zdnn_device_context * ctx_dev, const ggml_tensor * op) {
|
||||
const ggml_tensor * q = op->src[0];
|
||||
const ggml_tensor * k = op->src[1];
|
||||
const ggml_tensor * v = op->src[2];
|
||||
const ggml_tensor * mask = op->src[3];
|
||||
|
||||
float max_bias = 0.0f;
|
||||
float logit_softcap = 0.0f;
|
||||
memcpy(&max_bias, (const float *) op->op_params + 1, sizeof(float));
|
||||
memcpy(&logit_softcap, (const float *) op->op_params + 2, sizeof(float));
|
||||
|
||||
// sinks, alibi, softcap and sparse k/v sets are not implemented
|
||||
if (op->src[4] != nullptr || max_bias != 0.0f || logit_softcap != 0.0f || ggml_get_op_params_i32(op, 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->type != GGML_TYPE_F32 || q->type != GGML_TYPE_F32 || k->type != GGML_TYPE_F16 || v->type != GGML_TYPE_F16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mask != nullptr && (mask->type != GGML_TYPE_F16 || mask->ne[2] != 1 || mask->ne[3] != 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (q->ne[3] != 1 || k->ne[3] != 1 || v->ne[3] != 1 || k->ne[2] != v->ne[2] || q->ne[2] % k->ne[2] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// rows are gathered with their strides, but each row has to be contiguous
|
||||
if (q->nb[0] != sizeof(float) || k->nb[0] != sizeof(ggml_fp16_t) || v->nb[0] != sizeof(ggml_fp16_t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int64_t max_dim = ctx_dev->max_size;
|
||||
const int64_t group = q->ne[2] / k->ne[2];
|
||||
|
||||
return q->ne[0] <= max_dim && v->ne[0] <= max_dim && k->ne[1] <= max_dim &&
|
||||
q->ne[2] <= max_dim && group * q->ne[1] <= max_dim;
|
||||
}
|
||||
|
||||
void ggml_zdnn_flash_attn_ext(ggml_backend_zdnn_context * ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * q = dst->src[0];
|
||||
const ggml_tensor * k = dst->src[1];
|
||||
const ggml_tensor * v = dst->src[2];
|
||||
const ggml_tensor * mask = dst->src[3];
|
||||
|
||||
const int64_t d_k = q->ne[0];
|
||||
const int64_t n_q = q->ne[1];
|
||||
const int64_t n_head = q->ne[2];
|
||||
const int64_t n_kv = k->ne[1];
|
||||
const int64_t n_head_kv = k->ne[2];
|
||||
const int64_t d_v = v->ne[0];
|
||||
const int64_t group = n_head / n_head_kv;
|
||||
|
||||
float scale = 1.0f;
|
||||
memcpy(&scale, (const float *) dst->op_params + 0, sizeof(float));
|
||||
|
||||
const int32_t n_threads = n_q*n_kv >= 65536 ? ctx->n_threads : 1;
|
||||
|
||||
// one query runs all heads in one call with the mask row as the K*Q bias, more queries run per head and add the mask
|
||||
const bool single = n_q == 1;
|
||||
|
||||
const int64_t n_mask = mask ? (single ? n_head_kv*n_kv : n_q*n_kv) : 0;
|
||||
|
||||
ggml_zdnn_pool_alloc<float> q_host_alloc(ctx->pool, n_head*n_q*d_k);
|
||||
ggml_zdnn_pool_alloc<float> o_host_alloc(ctx->pool, n_head*n_q*d_v);
|
||||
ggml_zdnn_pool_alloc<float> mask_host_alloc(ctx->pool, n_mask);
|
||||
ggml_zdnn_pool_alloc<ggml_fp16_t> k_host_alloc(ctx->pool, n_head_kv*n_kv*d_k);
|
||||
ggml_zdnn_pool_alloc<ggml_fp16_t> v_host_alloc(ctx->pool, n_head_kv*n_kv*d_v);
|
||||
|
||||
float * q_host = q_host_alloc.get();
|
||||
float * o_host = o_host_alloc.get();
|
||||
float * mask_host = mask_host_alloc.get();
|
||||
ggml_fp16_t * k_host = k_host_alloc.get();
|
||||
ggml_fp16_t * v_host = v_host_alloc.get();
|
||||
|
||||
// q is [n_head][n_q][d_k] with the scale folded in, k and v are [n_head_kv][n_kv][d]
|
||||
ggml_zdnn_parallel_for(n_threads, n_head, [&](int64_t h_start, int64_t h_end) {
|
||||
for (int64_t h = h_start; h < h_end; h++) {
|
||||
for (int64_t iq = 0; iq < n_q; iq++) {
|
||||
const float * src = (const float *)((const char *) q->data + iq*q->nb[1] + h*q->nb[2]);
|
||||
float * row = q_host + (h*n_q + iq)*d_k;
|
||||
for (int64_t i0 = 0; i0 < d_k; i0++) {
|
||||
row[i0] = src[i0] * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ggml_zdnn_parallel_for(n_threads, n_head_kv*n_kv, [&](int64_t r_start, int64_t r_end) {
|
||||
for (int64_t r = r_start; r < r_end; r++) {
|
||||
const int64_t hk = r / n_kv;
|
||||
const int64_t ik = r % n_kv;
|
||||
memcpy(k_host + r*d_k, (const char *) k->data + ik*k->nb[1] + hk*k->nb[2], d_k*sizeof(ggml_fp16_t));
|
||||
memcpy(v_host + r*d_v, (const char *) v->data + ik*v->nb[1] + hk*v->nb[2], d_v*sizeof(ggml_fp16_t));
|
||||
}
|
||||
});
|
||||
|
||||
zdnn_tensor_desc q_pre_tfm_desc, q_tfm_desc;
|
||||
zdnn_tensor_desc k_pre_tfm_desc, k_tfm_desc;
|
||||
zdnn_tensor_desc v_pre_tfm_desc, v_tfm_desc;
|
||||
zdnn_tensor_desc o_pre_tfm_desc, o_tfm_desc;
|
||||
zdnn_ztensor q_ztensor, k_ztensor, v_ztensor, o_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> q_ztensor_alloc, k_ztensor_alloc, v_ztensor_alloc, o_ztensor_alloc;
|
||||
|
||||
// a single query keeps the heads of a kv head in one stack, which is the same memory order as one head per stack
|
||||
if (single) {
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &q_pre_tfm_desc, n_head_kv, group, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &o_pre_tfm_desc, n_head_kv, group, d_v);
|
||||
} else {
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &q_pre_tfm_desc, n_head, n_q, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &o_pre_tfm_desc, n_head, n_q, d_v);
|
||||
}
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP16, &k_pre_tfm_desc, n_head_kv, n_kv, d_k);
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP16, &v_pre_tfm_desc, n_head_kv, n_kv, d_v);
|
||||
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&q_pre_tfm_desc, &q_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&k_pre_tfm_desc, &k_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&v_pre_tfm_desc, &v_tfm_desc));
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&o_pre_tfm_desc, &o_tfm_desc));
|
||||
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &q_pre_tfm_desc, &q_tfm_desc, &q_ztensor, q_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &k_pre_tfm_desc, &k_tfm_desc, &k_ztensor, k_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &v_pre_tfm_desc, &v_tfm_desc, &v_ztensor, v_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &o_pre_tfm_desc, &o_tfm_desc, &o_ztensor, o_ztensor_alloc);
|
||||
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&q_ztensor, q_host));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&k_ztensor, k_host));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&v_ztensor, v_host));
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> softmax_save_area(ctx->pool, ZDNN_SOFTMAX_SAVEAREA_SIZE);
|
||||
|
||||
zdnn_tensor_desc s_pre_tfm_desc, s_tfm_desc;
|
||||
zdnn_ztensor s_ztensor, s2_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> s_ztensor_alloc, s2_ztensor_alloc;
|
||||
|
||||
if (single) {
|
||||
// the bias of K*Q is the mask row of the query, repeated for every kv head
|
||||
zdnn_tensor_desc bias_pre_tfm_desc, bias_tfm_desc;
|
||||
zdnn_ztensor bias_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> bias_ztensor_alloc;
|
||||
|
||||
const zdnn_ztensor * bias = &ggml_zdnn_zero_bias(ctx, n_head_kv, n_kv);
|
||||
if (mask != nullptr) {
|
||||
const ggml_fp16_t * mask_row = (const ggml_fp16_t *) mask->data;
|
||||
for (int64_t hk = 0; hk < n_head_kv; hk++) {
|
||||
for (int64_t ik = 0; ik < n_kv; ik++) {
|
||||
mask_host[hk*n_kv + ik] = ggml_zdnn_fattn_mask_value(mask_row[ik]);
|
||||
}
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias_pre_tfm_desc, n_head_kv, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias_pre_tfm_desc, &bias_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &bias_pre_tfm_desc, &bias_tfm_desc, &bias_ztensor, bias_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias_ztensor, mask_host));
|
||||
bias = &bias_ztensor;
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &s_pre_tfm_desc, n_head_kv, group, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&s_pre_tfm_desc, &s_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s_ztensor, s_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s2_ztensor, s2_ztensor_alloc);
|
||||
|
||||
ZDNN_CHECK(zdnn_matmul_transpose_op(&q_ztensor, &k_ztensor, bias, false, true, MATMUL_OP_ADDITION, &s_ztensor));
|
||||
ZDNN_CHECK(zdnn_softmax(&s_ztensor, softmax_save_area.get(), SOFTMAX_ACT_NONE, &s2_ztensor));
|
||||
ZDNN_CHECK(zdnn_matmul_op(&s2_ztensor, &v_ztensor, &ggml_zdnn_zero_bias(ctx, n_head_kv, d_v), MATMUL_OP_ADDITION, &o_ztensor));
|
||||
} else {
|
||||
zdnn_tensor_desc mask_pre_tfm_desc, mask_tfm_desc;
|
||||
zdnn_ztensor mask_ztensor;
|
||||
|
||||
ggml_zdnn_pool_alloc<uint8_t> mask_ztensor_alloc;
|
||||
|
||||
if (mask != nullptr) {
|
||||
ggml_zdnn_parallel_for(n_threads, n_q, [&](int64_t iq_start, int64_t iq_end) {
|
||||
for (int64_t iq = iq_start; iq < iq_end; iq++) {
|
||||
const ggml_fp16_t * mask_row = (const ggml_fp16_t *)((const char *) mask->data + iq*mask->nb[1]);
|
||||
for (int64_t ik = 0; ik < n_kv; ik++) {
|
||||
mask_host[iq*n_kv + ik] = ggml_zdnn_fattn_mask_value(mask_row[ik]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &mask_pre_tfm_desc, 1, n_q, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&mask_pre_tfm_desc, &mask_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &mask_pre_tfm_desc, &mask_tfm_desc, &mask_ztensor, mask_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&mask_ztensor, mask_host));
|
||||
}
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &s_pre_tfm_desc, 1, n_q, n_kv);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&s_pre_tfm_desc, &s_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s_ztensor, s_ztensor_alloc);
|
||||
ggml_zdnn_init_scratch_ztensor_dlf16(ctx, &s_pre_tfm_desc, &s_tfm_desc, &s2_ztensor, s2_ztensor_alloc);
|
||||
|
||||
const zdnn_ztensor & k_bias = ggml_zdnn_zero_bias(ctx, 1, n_kv);
|
||||
const zdnn_ztensor & v_bias = ggml_zdnn_zero_bias(ctx, 1, d_v);
|
||||
|
||||
zdnn_tensor_desc view_pre_tfm_desc[4];
|
||||
zdnn_tensor_desc view_tfm_desc[4];
|
||||
|
||||
for (int64_t h = 0; h < n_head; h++) {
|
||||
const zdnn_ztensor q_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[0], &view_tfm_desc[0], q_ztensor, n_head, h, 1);
|
||||
const zdnn_ztensor k_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[1], &view_tfm_desc[1], k_ztensor, n_head_kv, h / group, 1);
|
||||
const zdnn_ztensor v_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[2], &view_tfm_desc[2], v_ztensor, n_head_kv, h / group, 1);
|
||||
zdnn_ztensor o_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[3], &view_tfm_desc[3], o_ztensor, n_head, h, 1);
|
||||
|
||||
s_ztensor.is_transformed = false;
|
||||
s2_ztensor.is_transformed = false;
|
||||
|
||||
ZDNN_CHECK(zdnn_matmul_transpose_op(&q_view, &k_view, &k_bias, false, true, MATMUL_OP_ADDITION, &s_ztensor));
|
||||
|
||||
zdnn_ztensor * scores = &s_ztensor;
|
||||
zdnn_ztensor * probs = &s2_ztensor;
|
||||
if (mask != nullptr) {
|
||||
ZDNN_CHECK(zdnn_add(&s_ztensor, &mask_ztensor, &s2_ztensor));
|
||||
scores = &s2_ztensor;
|
||||
probs = &s_ztensor;
|
||||
probs->is_transformed = false;
|
||||
}
|
||||
|
||||
ZDNN_CHECK(zdnn_softmax(scores, softmax_save_area.get(), SOFTMAX_ACT_NONE, probs));
|
||||
ZDNN_CHECK(zdnn_matmul_op(probs, &v_view, &v_bias, MATMUL_OP_ADDITION, &o_view));
|
||||
}
|
||||
|
||||
o_ztensor.is_transformed = true;
|
||||
}
|
||||
|
||||
ZDNN_CHECK(zdnn_transform_origtensor(&o_ztensor, o_host));
|
||||
|
||||
// the result is [d_v][n_head][n_q]
|
||||
for (int64_t iq = 0; iq < n_q; iq++) {
|
||||
for (int64_t h = 0; h < n_head; h++) {
|
||||
memcpy((char *) dst->data + h*dst->nb[1] + iq*dst->nb[2], o_host + (h*n_q + iq)*d_v, d_v*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
// the result is written to dst->data directly, so the dst ztensor is stale
|
||||
ggml_backend_zdnn_buffer * dst_extra = (ggml_backend_zdnn_buffer *) dst->extra;
|
||||
if (dst_extra != nullptr) {
|
||||
dst_extra->ztensor.is_transformed = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef GGML_ZDNN_FATTN_HPP
|
||||
#define GGML_ZDNN_FATTN_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
bool ggml_zdnn_supports_flash_attn_ext(const ggml_backend_zdnn_device_context * ctx_dev, const ggml_tensor * op);
|
||||
|
||||
void ggml_zdnn_flash_attn_ext(ggml_backend_zdnn_context * ctx, ggml_tensor * dst);
|
||||
|
||||
#endif // GGML_ZDNN_FATTN_HPP
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ggml-backend-impl.h"
|
||||
|
||||
#include "ggml-zdnn/common.hpp"
|
||||
#include "ggml-zdnn/fattn.hpp"
|
||||
#include "ggml-zdnn/mmf.hpp"
|
||||
#include "ggml-zdnn/mmq.hpp"
|
||||
#include "ggml-zdnn/utils.hpp"
|
||||
@@ -37,6 +38,11 @@ static bool ggml_zdnn_compute_forward(
|
||||
ggml_zdnn_compute_forward_mul_mat(ctx, dst);
|
||||
} break;
|
||||
|
||||
case GGML_OP_FLASH_ATTN_EXT:
|
||||
{
|
||||
ggml_zdnn_flash_attn_ext(ctx, dst);
|
||||
} break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -120,6 +126,9 @@ static bool ggml_zdnn_supports_op(const ggml_backend_zdnn_device_context * ctx_d
|
||||
}
|
||||
} break;
|
||||
|
||||
case GGML_OP_FLASH_ATTN_EXT:
|
||||
return ggml_zdnn_supports_flash_attn_ext(ctx_dev, op);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -205,9 +214,6 @@ static void ggml_zdnn_free(ggml_backend_zdnn_context * ctx) {
|
||||
for (const auto & it : ctx->zero_bias) {
|
||||
ZDNN_CHECK(zdnn_free_ztensor_buffer(&it.second->ztensor));
|
||||
}
|
||||
for (void * scratch : ctx->scratch_ztensor) {
|
||||
free(scratch);
|
||||
}
|
||||
|
||||
delete ctx;
|
||||
}
|
||||
@@ -309,7 +315,19 @@ static void ggml_backend_zdnn_buffer_memset_tensor(ggml_backend_buffer_t buffer,
|
||||
static void ggml_backend_zdnn_buffer_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
||||
memcpy((char *)tensor->data + offset, data, size);
|
||||
|
||||
// views share the ztensor of their source, which is now stale, and empty tensors have none
|
||||
if (tensor->view_src != nullptr) {
|
||||
ggml_backend_zdnn_buffer * src_extra = (ggml_backend_zdnn_buffer *)tensor->view_src->extra;
|
||||
if (src_extra != nullptr && src_extra->ztensor.is_transformed) {
|
||||
zdnn_reset_ztensor(&src_extra->ztensor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ggml_backend_zdnn_buffer * extra = (ggml_backend_zdnn_buffer *)tensor->extra;
|
||||
if (extra == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_is_quantized(tensor->type)) {
|
||||
GGML_ASSERT(offset == 0 && size == ggml_nbytes(tensor));
|
||||
@@ -319,8 +337,8 @@ static void ggml_backend_zdnn_buffer_set_tensor(ggml_backend_buffer_t buffer, gg
|
||||
|
||||
// Fixes the LLAMA_SET_ROWS bug
|
||||
// see: https://github.com/ggml-org/llama.cpp/issues/15414
|
||||
// compute tensors are transformed on first use instead, as not every op reads the ztensor
|
||||
if (tensor->buffer->usage == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
|
||||
// only weights are transformed here, others on first use, as not every op reads the ztensor and some data (masks) is not finite
|
||||
if (tensor->buffer->usage != GGML_BACKEND_BUFFER_USAGE_WEIGHTS) {
|
||||
zdnn_reset_ztensor(&extra->ztensor);
|
||||
return;
|
||||
}
|
||||
|
||||
+28
-77
@@ -6,63 +6,6 @@
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
// view of the stacks [s0, s0 + n) of a stacked ztensor, the stacks are the outermost dim of its buffer
|
||||
// the view descs must outlive the view
|
||||
static zdnn_ztensor ggml_zdnn_stack_view(zdnn_tensor_desc * pre_tfm_desc,
|
||||
zdnn_tensor_desc * tfm_desc,
|
||||
const zdnn_ztensor & src,
|
||||
int64_t n_stacks,
|
||||
int64_t s0,
|
||||
int64_t n) {
|
||||
|
||||
GGML_ASSERT(src.buffer_size % n_stacks == 0);
|
||||
const uint64_t stack_bytes = src.buffer_size / n_stacks;
|
||||
|
||||
*pre_tfm_desc = *src.pre_transformed_desc;
|
||||
*tfm_desc = *src.transformed_desc;
|
||||
|
||||
switch (pre_tfm_desc->layout) {
|
||||
case ZDNN_3DS:
|
||||
{
|
||||
pre_tfm_desc->dim3 = n;
|
||||
} break;
|
||||
case ZDNN_2DS:
|
||||
{
|
||||
pre_tfm_desc->dim2 = n;
|
||||
} break;
|
||||
default:
|
||||
GGML_ABORT("%s: unsupported stacked layout", __func__);
|
||||
}
|
||||
|
||||
tfm_desc->dim4 = n;
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(tfm_desc) == n*stack_bytes);
|
||||
|
||||
zdnn_ztensor view = src;
|
||||
view.pre_transformed_desc = pre_tfm_desc;
|
||||
view.transformed_desc = tfm_desc;
|
||||
view.buffer = (char *) src.buffer + s0*stack_bytes;
|
||||
view.buffer_size = n * stack_bytes;
|
||||
return view;
|
||||
}
|
||||
|
||||
// zero bias of n_stacks stacks, created once per shape as it never changes
|
||||
static const zdnn_ztensor & ggml_zdnn_zero_bias(ggml_backend_zdnn_context * ctx, int64_t n_stacks, int64_t ne0) {
|
||||
std::unique_ptr<ggml_backend_zdnn_buffer> & bias = ctx->zero_bias[{ n_stacks, ne0 }];
|
||||
|
||||
if (!bias) {
|
||||
bias = std::make_unique<ggml_backend_zdnn_buffer>();
|
||||
|
||||
std::vector<float> zeros(n_stacks * ne0, 0.0f);
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_2DS, FP32, &bias->pre_tfm_desc, n_stacks, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_transformed_desc(&bias->pre_tfm_desc, &bias->tfm_desc));
|
||||
ZDNN_CHECK(zdnn_init_ztensor_with_malloc(&bias->pre_tfm_desc, &bias->tfm_desc, &bias->ztensor));
|
||||
ZDNN_CHECK(zdnn_transform_ztensor(&bias->ztensor, zeros.data()));
|
||||
}
|
||||
|
||||
return bias->ztensor;
|
||||
}
|
||||
|
||||
// the weights are stacked into groups along ne00, and each group of each row of the inputs and weights has its own scale
|
||||
// zDNN multiplies the groups in stacked calls, then the partial results of each group are scaled and summed here
|
||||
static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
@@ -81,18 +24,20 @@ static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
GGML_ASSERT(n_groups * gs >= ne10 && (n_groups - 1) * gs < ne10);
|
||||
|
||||
// the rounding errors of the inputs are stacked after the inputs, see ggml_zdnn_quantize_inputs
|
||||
int8_t * inputs_q = ggml_zdnn_resize_scratch_max(ctx->scratch_q, 2 * n_groups * gs * ne11);
|
||||
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * n_groups * gs * ne11);
|
||||
int8_t * inputs_q = inputs_q_alloc.get();
|
||||
std::vector<float> inputs_scales(n_groups * ne11);
|
||||
ggml_zdnn_quantize_inputs(n_threads, inputs, gs, weights_extra->channel_scales.data(),
|
||||
inputs_q, inputs_q + n_groups * gs * ne11, inputs_scales.data());
|
||||
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2 * n_groups, ne11, gs);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, 0);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
|
||||
|
||||
// each group of the partial results is read back as its own 2D ztensor
|
||||
@@ -116,19 +61,21 @@ static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
|
||||
|
||||
// two partial buffers, so the next chunk runs on the NNPA while this one is summed
|
||||
// each holds the results of a chunk of inputs, followed by the results of their rounding errors
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor[2];
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor[2];
|
||||
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc[2];
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2 * n_chunk, ne11, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
|
||||
GGML_ASSERT(zdnn_getsize_ztensor(&partial_tfm_desc) == (uint64_t)(2 * n_chunk) * group_bytes);
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor[i], 1 + i);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor[i], partial_ztensor_alloc[i]);
|
||||
}
|
||||
|
||||
const float * weights_scales = weights_extra->scales.data();
|
||||
float * partial = ggml_zdnn_resize_scratch_max(ctx->scratch_f32, 2 * ne11 * ne0);
|
||||
ggml_zdnn_pool_alloc<float> partial_alloc(ctx->pool, 2 * ne11 * ne0);
|
||||
float * partial = partial_alloc.get();
|
||||
float * partial_res = partial + ne11 * ne0;
|
||||
|
||||
const auto sum_chunk = [&](const zdnn_ztensor & chunk, int64_t g0, int64_t n) {
|
||||
@@ -262,27 +209,30 @@ void ggml_zdnn_mul_mat_q(
|
||||
|
||||
// zDNN takes one scale per zTensor, so quantize each row of the inputs with its own scale
|
||||
// the rounding errors of the inputs are stacked after the inputs, see ggml_zdnn_quantize_inputs
|
||||
int8_t * inputs_q = ggml_zdnn_resize_scratch_max(ctx->scratch_q, 2 * ne10 * ne11);
|
||||
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * ne10 * ne11);
|
||||
int8_t * inputs_q = inputs_q_alloc.get();
|
||||
std::vector<float> inputs_scales(ne11);
|
||||
ggml_zdnn_quantize_inputs(n_threads, inputs, ne10, weights_extra->channel_scales.data(),
|
||||
inputs_q, inputs_q + ne10 * ne11, inputs_scales.data());
|
||||
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
zdnn_tensor_desc inputs_pre_tfm_desc;
|
||||
zdnn_tensor_desc inputs_tfm_desc;
|
||||
zdnn_ztensor inputs_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2, ne11, ne10);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, 0);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
|
||||
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
|
||||
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor;
|
||||
zdnn_tensor_desc partial_pre_tfm_desc;
|
||||
zdnn_tensor_desc partial_tfm_desc;
|
||||
zdnn_ztensor partial_ztensor;
|
||||
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc;
|
||||
|
||||
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2, ne11, ne0);
|
||||
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor, 1);
|
||||
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor, partial_ztensor_alloc);
|
||||
|
||||
// both stacks are multiplied by the same unstacked weights in one broadcast call
|
||||
// the bias is all zeros, so it is already the pre-computed bias
|
||||
@@ -300,7 +250,8 @@ void ggml_zdnn_mul_mat_q(
|
||||
const uint64_t stack_bytes = zdnn_getsize_ztensor(&stack_tfm_desc);
|
||||
GGML_ASSERT(partial_ztensor.buffer_size == 2*stack_bytes);
|
||||
|
||||
float * partial[2] = { (float *)output->data, ggml_zdnn_resize_scratch_max(ctx->scratch_f32, ne11 * ne0) };
|
||||
ggml_zdnn_pool_alloc<float> partial_res_alloc(ctx->pool, ne11 * ne0);
|
||||
float * partial[2] = { (float *)output->data, partial_res_alloc.get() };
|
||||
|
||||
ggml_zdnn_parallel_for(n_threads, 2, [&](int64_t s_start, int64_t s_end) {
|
||||
zdnn_ztensor stack_ztensor;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "ggml.h"
|
||||
#include "scratch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
ggml_zdnn_pool::~ggml_zdnn_pool() {
|
||||
for (const buffer & b : cached) {
|
||||
std::free(b.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void * ggml_zdnn_pool::alloc(size_t size, size_t * actual_size) {
|
||||
int64_t ibest = -1;
|
||||
for (int64_t i = 0; i < (int64_t)cached.size(); i++) {
|
||||
if (cached[i].size >= size && (ibest < 0 || cached[i].size < cached[ibest].size)) {
|
||||
ibest = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (ibest >= 0) {
|
||||
void * ptr = cached[ibest].ptr;
|
||||
*actual_size = cached[ibest].size;
|
||||
cached.erase(cached.begin() + ibest);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// every cached buffer is too small, free the largest so the pool does not grow while sizes grow with the context
|
||||
if (!cached.empty()) {
|
||||
const auto largest = std::max_element(cached.begin(), cached.end(), [](const buffer & a, const buffer & b) { return a.size < b.size; });
|
||||
std::free(largest->ptr);
|
||||
cached.erase(largest);
|
||||
}
|
||||
|
||||
// 5% more, so slowly growing sizes do not allocate every time
|
||||
*actual_size = GGML_PAD(std::max<size_t>(1, (size_t)(1.05 * size)), 4096);
|
||||
void * ptr = std::aligned_alloc(4096, *actual_size);
|
||||
GGML_ASSERT(ptr != nullptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void ggml_zdnn_pool::free(void * ptr, size_t size) {
|
||||
cached.push_back({ ptr, size });
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef GGML_ZDNN_SCRATCH_HPP
|
||||
#define GGML_ZDNN_SCRATCH_HPP
|
||||
|
||||
#include "ggml.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
// 4K aligned host buffers that are cached for reuse, see ggml_cuda_pool_leg
|
||||
struct ggml_zdnn_pool {
|
||||
struct buffer {
|
||||
void * ptr = nullptr;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
std::vector<buffer> cached;
|
||||
|
||||
~ggml_zdnn_pool();
|
||||
|
||||
void * alloc(size_t size, size_t * actual_size);
|
||||
void free(void * ptr, size_t size);
|
||||
};
|
||||
|
||||
// a buffer of the pool that goes back to it at the end of its scope, see ggml_cuda_pool_alloc
|
||||
template <typename T>
|
||||
struct ggml_zdnn_pool_alloc {
|
||||
ggml_zdnn_pool * pool = nullptr;
|
||||
T * ptr = nullptr;
|
||||
size_t actual_size = 0;
|
||||
|
||||
ggml_zdnn_pool_alloc() = default;
|
||||
|
||||
ggml_zdnn_pool_alloc(ggml_zdnn_pool & pool, size_t size) {
|
||||
alloc(pool, size);
|
||||
}
|
||||
|
||||
~ggml_zdnn_pool_alloc() {
|
||||
if (ptr != nullptr) {
|
||||
pool->free(ptr, actual_size);
|
||||
}
|
||||
}
|
||||
|
||||
// size is in number of elements
|
||||
T * alloc(ggml_zdnn_pool & pool, size_t size) {
|
||||
GGML_ASSERT(ptr == nullptr);
|
||||
this->pool = &pool;
|
||||
ptr = (T *) pool.alloc(size * sizeof(T), &actual_size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
T * get() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ggml_zdnn_pool_alloc(const ggml_zdnn_pool_alloc &) = delete;
|
||||
ggml_zdnn_pool_alloc(ggml_zdnn_pool_alloc &&) = delete;
|
||||
ggml_zdnn_pool_alloc & operator=(const ggml_zdnn_pool_alloc &) = delete;
|
||||
ggml_zdnn_pool_alloc & operator=(ggml_zdnn_pool_alloc &&) = delete;
|
||||
};
|
||||
|
||||
#endif // GGML_ZDNN_SCRATCH_HPP
|
||||
@@ -344,6 +344,7 @@ class Keys:
|
||||
ROUTING_BLOCK_COUNT = "{arch}.decision.routing_block_count"
|
||||
HEAD_COUNT = "{arch}.decision.head_count"
|
||||
MAX_HEAD_TOKENS = "{arch}.decision.max_head_tokens"
|
||||
LABELS = "{arch}.decision.labels" # the text of the token read for each option, in order
|
||||
TEMPERATURE = "{arch}.decision.temperature.{name}" # name: "<type>" or "<type>.<n_opt bucket>"
|
||||
SCORE_BIAS = "{arch}.decision.score_bias.{n}.{i}" # added to the logit of level i of a score question with n levels
|
||||
|
||||
@@ -366,6 +367,7 @@ class Keys:
|
||||
ADD_BOS = "tokenizer.ggml.add_bos_token"
|
||||
ADD_EOS = "tokenizer.ggml.add_eos_token"
|
||||
ADD_SEP = "tokenizer.ggml.add_sep_token"
|
||||
DNA_AUTO_TAGS = "tokenizer.ggml.dna_auto_tags"
|
||||
ADD_PREFIX = "tokenizer.ggml.add_space_prefix"
|
||||
REMOVE_EXTRA_WS = "tokenizer.ggml.remove_extra_whitespaces"
|
||||
PRECOMPILED_CHARSMAP = "tokenizer.ggml.precompiled_charsmap"
|
||||
@@ -418,6 +420,8 @@ class Keys:
|
||||
PREPROC_MIN_TILES = "clip.vision.preproc_min_tiles"
|
||||
PREPROC_MAX_TILES = "clip.vision.preproc_max_tiles"
|
||||
PREPROC_IMAGE_SIZE = "clip.vision.preproc_image_size"
|
||||
PREPROC_TILE_SIZE = "clip.vision.preproc_tile_size"
|
||||
PREPROC_MAX_PIXELS_TOLERANCE = "clip.vision.preproc_max_pixels_tolerance"
|
||||
PATCH_SIZE = "clip.vision.patch_size"
|
||||
EMBEDDING_LENGTH = "clip.vision.embedding_length"
|
||||
FEED_FORWARD_LENGTH = "clip.vision.feed_forward_length"
|
||||
@@ -6336,6 +6340,7 @@ class DecisionType:
|
||||
LFM2_D1 = "lfm2-d1" # same as openjev, the labels depend on the question type
|
||||
LFM2_D1_OMNI = "lfm2-d1-omni" # same head as laya on a bidirectional LFM2 trunk, other prompt layout
|
||||
GRANITE_SWITCH = "granite-switch" # same as openjev, run by the decision aLoRA of granite switch
|
||||
H2O_LIGHTNING = "h2o-lightning" # same as openjev, labels read after an answer prefill
|
||||
|
||||
|
||||
class VisionProjectorType:
|
||||
|
||||
@@ -1314,6 +1314,9 @@ class GGUFWriter:
|
||||
def add_add_sep_token(self, value: bool) -> None:
|
||||
self.add_bool(Keys.Tokenizer.ADD_SEP, value)
|
||||
|
||||
def add_dna_auto_tags(self, value: bool) -> None:
|
||||
self.add_bool(Keys.Tokenizer.DNA_AUTO_TAGS, value)
|
||||
|
||||
def add_add_space_prefix(self, value: bool) -> None:
|
||||
self.add_bool(Keys.Tokenizer.ADD_PREFIX, value)
|
||||
|
||||
@@ -1398,6 +1401,9 @@ class GGUFWriter:
|
||||
def add_decision_max_head_tokens(self, value: int) -> None:
|
||||
self.add_uint32(Keys.Decision.MAX_HEAD_TOKENS.format(arch=self.arch), value)
|
||||
|
||||
def add_decision_labels(self, labels: Sequence[str]) -> None:
|
||||
self.add_array(Keys.Decision.LABELS.format(arch=self.arch), labels)
|
||||
|
||||
def add_decision_temperature(self, name: str, value: float) -> None:
|
||||
self.add_float32(Keys.Decision.TEMPERATURE.format(arch=self.arch, name=name), value)
|
||||
|
||||
@@ -1467,6 +1473,12 @@ class GGUFWriter:
|
||||
def add_vision_preproc_image_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.PREPROC_IMAGE_SIZE, value)
|
||||
|
||||
def add_vision_preproc_tile_size(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.PREPROC_TILE_SIZE, value)
|
||||
|
||||
def add_vision_preproc_max_pixels_tolerance(self, value: float) -> None:
|
||||
self.add_float32(Keys.ClipVision.PREPROC_MAX_PIXELS_TOLERANCE, value)
|
||||
|
||||
def add_vision_projector_query_side(self, value: int) -> None:
|
||||
self.add_uint32(Keys.ClipVision.Projector.QUERY_SIDE, value)
|
||||
|
||||
|
||||
@@ -244,8 +244,10 @@ elif [ "$DO_CHECK" -eq 1 ]; then
|
||||
# From here on, we don't want to exit on first error
|
||||
set +e
|
||||
|
||||
ACC_STRICT=
|
||||
[ "$STRICT" -eq 0 ] || ACC_STRICT="-strict"
|
||||
abi-compliance-checker \
|
||||
-strict \
|
||||
$ACC_STRICT \
|
||||
-source \
|
||||
-library "$PROJECT_NAME" \
|
||||
-old "$BUILD_DIR/apiabi/api.dump" \
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# download the prebuilt llama-connect binary for the given platform
|
||||
# the release assets are made by: https://github.com/ggml-org/llama-connect/blob/master/.github/workflows/build.yml
|
||||
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
set(REPO "" CACHE STRING "GitHub repository to download from (owner/name)")
|
||||
set(VERSION "" CACHE STRING "Release tag to download, or 'latest'")
|
||||
set(PLATFORM "" CACHE STRING "Platform suffix of the release asset (ex: linux-x64)")
|
||||
set(OUT_DIR "" CACHE STRING "Directory to place the binary into")
|
||||
set(WORK_DIR "" CACHE STRING "Scratch directory used for the download")
|
||||
|
||||
if (PLATFORM MATCHES "^win-")
|
||||
set(ASSET "llama-connect-${PLATFORM}.zip")
|
||||
set(BINARY "llama-connect.exe")
|
||||
else()
|
||||
set(ASSET "llama-connect-${PLATFORM}.tar.gz")
|
||||
set(BINARY "llama-connect")
|
||||
endif()
|
||||
|
||||
if (VERSION STREQUAL "latest")
|
||||
set(URL "https://github.com/${REPO}/releases/latest/download/${ASSET}")
|
||||
else()
|
||||
set(URL "https://github.com/${REPO}/releases/download/${VERSION}/${ASSET}")
|
||||
endif()
|
||||
|
||||
set(DST "${OUT_DIR}/${BINARY}")
|
||||
set(ARCHIVE "${WORK_DIR}/${ASSET}")
|
||||
set(STAMP "${WORK_DIR}/${BINARY}.url")
|
||||
|
||||
# skip if already downloaded from the same URL
|
||||
if (EXISTS "${DST}" AND EXISTS "${STAMP}")
|
||||
file(READ "${STAMP}" prev_url)
|
||||
if (prev_url STREQUAL "${URL}")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(REMOVE "${STAMP}")
|
||||
file(MAKE_DIRECTORY "${WORK_DIR}")
|
||||
|
||||
message(STATUS "llama-connect: downloading ${URL}")
|
||||
|
||||
# retry a few times, this runs in CI where a transient failure must not break the build
|
||||
foreach (attempt RANGE 1 3)
|
||||
file(DOWNLOAD "${URL}" "${ARCHIVE}" STATUS status TLS_VERIFY ON INACTIVITY_TIMEOUT 60)
|
||||
|
||||
list(GET status 0 code)
|
||||
list(GET status 1 msg)
|
||||
|
||||
if (code EQUAL 0)
|
||||
break()
|
||||
endif()
|
||||
|
||||
message(STATUS "llama-connect: download attempt ${attempt} failed (${code}: ${msg})")
|
||||
file(REMOVE "${ARCHIVE}")
|
||||
endforeach()
|
||||
|
||||
if (NOT code EQUAL 0)
|
||||
file(REMOVE "${ARCHIVE}")
|
||||
message(FATAL_ERROR
|
||||
"llama-connect: failed to download ${URL} (${code}: ${msg})\n"
|
||||
" use -DLLAMA_CONNECT_VERSION=<tag> to select another release, "
|
||||
"or -DLLAMA_CONNECT=OFF to skip this tool")
|
||||
endif()
|
||||
|
||||
set(EXTRACT_DIR "${WORK_DIR}/extract")
|
||||
|
||||
file(REMOVE_RECURSE "${EXTRACT_DIR}")
|
||||
file(MAKE_DIRECTORY "${EXTRACT_DIR}")
|
||||
file(ARCHIVE_EXTRACT INPUT "${ARCHIVE}" DESTINATION "${EXTRACT_DIR}")
|
||||
|
||||
if (NOT EXISTS "${EXTRACT_DIR}/${BINARY}")
|
||||
message(FATAL_ERROR "llama-connect: ${ASSET} does not contain ${BINARY}")
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY "${OUT_DIR}")
|
||||
file(COPY "${EXTRACT_DIR}/${BINARY}" DESTINATION "${OUT_DIR}"
|
||||
FILE_PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE
|
||||
)
|
||||
|
||||
file(WRITE "${STAMP}" "${URL}")
|
||||
|
||||
message(STATUS "llama-connect: ready at ${DST}")
|
||||
+124
-22
@@ -11,6 +11,9 @@ import platform
|
||||
import shutil
|
||||
import logging
|
||||
import glob
|
||||
import shlex
|
||||
import tarfile
|
||||
import tempfile
|
||||
|
||||
from sdk import validate_windows_sdks
|
||||
|
||||
@@ -19,19 +22,70 @@ logger = logging.getLogger("build")
|
||||
|
||||
def parse_target(target_str):
|
||||
if not target_str:
|
||||
return None, None
|
||||
if target_str.startswith("adb") or target_str.startswith("android"):
|
||||
parts = target_str.split(":", 1)
|
||||
serial = parts[1] if len(parts) > 1 else None
|
||||
return "android", serial
|
||||
elif target_str.startswith("lnx") or target_str.startswith("linux") or target_str.startswith("ubuntu"):
|
||||
parts = target_str.split(":", 1)
|
||||
host = parts[1] if len(parts) > 1 else None
|
||||
return "linux", host
|
||||
return None, None, None
|
||||
|
||||
parts = target_str.split(":")
|
||||
target_name = parts[0]
|
||||
|
||||
if target_name in ("adb", "android"):
|
||||
serial = ":".join(parts[1:]) if len(parts) > 1 else None
|
||||
return "android", serial, "adb"
|
||||
elif target_name in ("lnx", "linux", "ubuntu"):
|
||||
if len(parts) > 1 and parts[1] == "adb":
|
||||
serial = ":".join(parts[2:]) if len(parts) > 2 else None
|
||||
return "linux", serial, "adb"
|
||||
host = ":".join(parts[1:]) if len(parts) > 1 else None
|
||||
return "linux", host, "ssh"
|
||||
elif target_str in ("wos", "windows"):
|
||||
return "windows", None
|
||||
return "windows", None, None
|
||||
else:
|
||||
return None, None
|
||||
return None, None, None
|
||||
|
||||
|
||||
def default_target_dir(target_type, target_transport, home_dir):
|
||||
if target_type == "android":
|
||||
return "/data/local/tmp/llama.cpp"
|
||||
if target_type == "linux":
|
||||
if home_dir:
|
||||
return home_dir.rstrip("/") + "/llama.cpp"
|
||||
if target_transport == "adb":
|
||||
return "/home/llama.cpp"
|
||||
return "~/llama.cpp"
|
||||
return "~/llama.cpp"
|
||||
|
||||
|
||||
def normalize_target_dir(target_dir):
|
||||
target_dir = target_dir.rstrip("/")
|
||||
if not target_dir or target_dir == "/":
|
||||
raise RuntimeError("target directory must not be the filesystem root")
|
||||
return target_dir
|
||||
|
||||
|
||||
def adb_root(adb_cmd):
|
||||
root_cmd = adb_cmd + ["root"]
|
||||
logger.info(f"+ {' '.join(root_cmd)}")
|
||||
res = subprocess.run(root_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB root failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
wait_cmd = adb_cmd + ["wait-for-device"]
|
||||
logger.info(f"+ {' '.join(wait_cmd)}")
|
||||
res = subprocess.run(wait_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB wait-for-device failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
|
||||
def make_package_archive(src_path):
|
||||
fd, archive_path = tempfile.mkstemp(prefix="llama-cpp-", suffix=".tar.gz")
|
||||
os.close(fd)
|
||||
with tarfile.open(archive_path, "w:gz", dereference=False) as tar:
|
||||
for item in os.listdir(src_path):
|
||||
if item.startswith("."):
|
||||
continue
|
||||
tar.add(os.path.join(src_path, item), arcname=item, recursive=True)
|
||||
return archive_path
|
||||
|
||||
|
||||
def get_uid_gid():
|
||||
@@ -45,7 +99,7 @@ def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build llama.cpp for Snapdragon using cross-compilation docker containers or natively."
|
||||
)
|
||||
parser.add_argument("--target", default="android", help="Compilation target and deployment definition (e.g. android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: android)")
|
||||
parser.add_argument("--target", default="android", help="Compilation target and deployment definition (e.g. android[:serial]/adb[:serial], ubuntu:adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: android)")
|
||||
parser.add_argument("--build-dir", help="Build directory name (defaults to build-TARGET[-dbg], e.g. build-android)")
|
||||
parser.add_argument("--install-dir", help="Install directory name (defaults to pkg-TARGET[-dbg], e.g. pkg-android)")
|
||||
parser.add_argument("--jobs", "-j", type=int, help="Number of build jobs (defaults to CPU thread count)")
|
||||
@@ -53,21 +107,22 @@ def main():
|
||||
parser.add_argument("--preset", help="Override the CMake preset to use")
|
||||
parser.add_argument("--debug", action="store_true", help="Build in debug mode (uses -debug presets instead of -release)")
|
||||
parser.add_argument("--hex-only", "--hexagon-only", action="store_true", help="Only build and push ggml-hexagon and libggml-htp libraries")
|
||||
parser.add_argument("--hex-arch", choices=["v73", "v75", "v79", "v81", "73", "75", "79", "81"], help="Specific Hexagon DSP architecture to build with --hex-only (default: all)")
|
||||
parser.add_argument("--hex-arch", choices=["v73", "v75", "v79", "v81", "v85", "73", "75", "79", "81", "85"], help="Specific Hexagon DSP architecture to build with --hex-only (default: all)")
|
||||
|
||||
# Push options
|
||||
parser.add_argument("--push", action="store_true", help="Push built package to the target device via ADB or SSH/SCP")
|
||||
parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux)")
|
||||
parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux, /home/llama.cpp for Linux via ADB)")
|
||||
parser.add_argument("--home-dir", help="Home directory for Linux targets when deriving the default target directory")
|
||||
|
||||
# Toolchain options
|
||||
parser.add_argument("--toolchain-version", default="v0.7", help="Docker toolchain image version/tag (default: v0.7)")
|
||||
parser.add_argument("--toolchain-version", default="v0.8", help="Docker toolchain image version/tag (default: v0.8)")
|
||||
parser.add_argument("--toolchain-url", default="ghcr.io/snapdragon-toolchain", help="Docker toolchain registry URL/namespace (default: ghcr.io/snapdragon-toolchain)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
target_type, target_val = parse_target(args.target)
|
||||
target_type, target_val, target_transport = parse_target(args.target)
|
||||
if not target_type:
|
||||
logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")
|
||||
logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], ubuntu:adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")
|
||||
sys.exit(1)
|
||||
|
||||
if target_type == "windows":
|
||||
@@ -140,7 +195,7 @@ def main():
|
||||
if hex_arch:
|
||||
hex_targets = ["ggml-hexagon", f"htp-{hex_arch}"]
|
||||
else:
|
||||
hex_targets = ["ggml-hexagon", "htp-v73", "htp-v75", "htp-v79", "htp-v81"]
|
||||
hex_targets = ["ggml-hexagon", "htp-v73", "htp-v75", "htp-v79", "htp-v81", "htp-v85"]
|
||||
|
||||
if args.no_docker:
|
||||
# Native/local host build
|
||||
@@ -218,8 +273,8 @@ def main():
|
||||
# Resolve target directory on device
|
||||
target_dir = args.target_dir
|
||||
if not target_dir:
|
||||
target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp"
|
||||
target_dir = target_dir.rstrip("/")
|
||||
target_dir = default_target_dir(target_type, target_transport, args.home_dir)
|
||||
target_dir = normalize_target_dir(target_dir)
|
||||
|
||||
sub_items = [item for item in os.listdir(src_path) if not item.startswith(".")]
|
||||
|
||||
@@ -235,14 +290,23 @@ def main():
|
||||
logger.error(f"Error: no hexagon libraries found in {lib_dir} to push.")
|
||||
sys.exit(1)
|
||||
|
||||
if target_type == "android":
|
||||
if target_transport == "adb":
|
||||
adb_cmd = ["adb"]
|
||||
if target_val: # serial
|
||||
adb_cmd += ["-s", target_val]
|
||||
|
||||
if target_type == "linux":
|
||||
adb_root(adb_cmd)
|
||||
|
||||
if args.hex_only:
|
||||
logger.info("\nPushing Hexagon libraries to Android device via ADB...")
|
||||
logger.info("\nPushing Hexagon libraries to target device via ADB...")
|
||||
target_lib_dir = f"{target_dir}/lib"
|
||||
mkdir_cmd = adb_cmd + ["shell", f"mkdir -p {shlex.quote(target_lib_dir)}"]
|
||||
logger.info(f"+ {' '.join(mkdir_cmd)}")
|
||||
res = subprocess.run(mkdir_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB mkdir failed.")
|
||||
sys.exit(res.returncode)
|
||||
for lib in hex_libs:
|
||||
push_cmd = adb_cmd + ["push", lib, f"{target_lib_dir}/"]
|
||||
logger.info(f"+ {' '.join(push_cmd)}")
|
||||
@@ -251,6 +315,44 @@ def main():
|
||||
logger.error("ADB push failed.")
|
||||
sys.exit(res.returncode)
|
||||
logger.info("Hexagon libraries deployed successfully!")
|
||||
elif target_type == "linux":
|
||||
logger.info("\nDeploying Linux package to target device via ADB tar...")
|
||||
remote_archive = f"/tmp/llama.cpp-package-{os.getpid()}.tar.gz"
|
||||
archive_path = make_package_archive(src_path)
|
||||
try:
|
||||
mkdir_cmd = adb_cmd + ["shell", f"mkdir -p {shlex.quote(target_dir)}"]
|
||||
logger.info(f"+ {' '.join(mkdir_cmd)}")
|
||||
res = subprocess.run(mkdir_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB mkdir failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
if sub_items:
|
||||
clean_paths = " ".join(shlex.quote(f"{target_dir}/{item}") for item in sub_items)
|
||||
clean_cmd = adb_cmd + ["shell", f"rm -rf {clean_paths}"]
|
||||
logger.info(f"+ {' '.join(clean_cmd)}")
|
||||
subprocess.run(clean_cmd)
|
||||
|
||||
push_cmd = adb_cmd + ["push", archive_path, remote_archive]
|
||||
logger.info(f"+ {' '.join(push_cmd)}")
|
||||
res = subprocess.run(push_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB push failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
extract_cmd = adb_cmd + ["shell", f"tar -xzf {shlex.quote(remote_archive)} -C {shlex.quote(target_dir)} && rm -f {shlex.quote(remote_archive)}"]
|
||||
logger.info(f"+ {' '.join(extract_cmd)}")
|
||||
res = subprocess.run(extract_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB untar failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
chmod_cmd = adb_cmd + ["shell", f"chmod -R 755 {shlex.quote(target_dir)}/bin 2>/dev/null || true"]
|
||||
logger.info(f"+ {' '.join(chmod_cmd)}")
|
||||
subprocess.run(chmod_cmd)
|
||||
finally:
|
||||
os.unlink(archive_path)
|
||||
logger.info("ADB tar deploy completed successfully!")
|
||||
else:
|
||||
# Clean stale package files on device
|
||||
if sub_items:
|
||||
|
||||
@@ -255,7 +255,7 @@ class HexagonToolchain:
|
||||
use_docker: bool = False,
|
||||
image_url: str = "ghcr.io/snapdragon-toolchain",
|
||||
image_name: str = "arm64-android",
|
||||
image_ver: str = "v0.7",
|
||||
image_ver: str = "v0.8",
|
||||
):
|
||||
self.repo_root = repo_root
|
||||
self.image = f"{image_url}/{image_name}:{image_ver}"
|
||||
@@ -322,15 +322,14 @@ class HexagonToolchain:
|
||||
if not self.docker_bin:
|
||||
raise RuntimeError("Docker is required but not installed or found on PATH.")
|
||||
|
||||
container_tools_dir = "/opt/hexagon/6.6.0.0/tools/HEXAGON_Tools/19.0.07/Tools/bin"
|
||||
if "objdump" in tool_name:
|
||||
container_tool = f"{container_tools_dir}/hexagon-llvm-objdump"
|
||||
tool_bin = "hexagon-llvm-objdump"
|
||||
elif "addr2line" in tool_name:
|
||||
container_tool = f"{container_tools_dir}/hexagon-addr2line"
|
||||
tool_bin = "hexagon-addr2line"
|
||||
elif "nm" in tool_name:
|
||||
container_tool = f"{container_tools_dir}/llvm-nm"
|
||||
tool_bin = "llvm-nm"
|
||||
else:
|
||||
container_tool = f"{container_tools_dir}/{tool_name}"
|
||||
tool_bin = tool_name
|
||||
|
||||
# Translate file paths from host to /workspace
|
||||
translated_args = [translate_container_arg(arg, self.repo_root) for arg in args]
|
||||
@@ -350,7 +349,9 @@ class HexagonToolchain:
|
||||
if platform.system() != "Windows":
|
||||
docker_cmd += ["-u", f"{os.getuid()}:{os.getgid()}"]
|
||||
|
||||
docker_cmd += [self.image, container_tool] + translated_args
|
||||
default_tools_root = "/opt/hexagon/6.7.0.0/tools/HEXAGON_Tools/21.0.04"
|
||||
sh_cmd = f'exec "${{HEXAGON_TOOLS_ROOT:-{default_tools_root}}}/Tools/bin/{tool_bin}" "$@"'
|
||||
docker_cmd += [self.image, "sh", "-c", sh_cmd, "--"] + translated_args
|
||||
|
||||
res = subprocess.run(docker_cmd, capture_output=True, text=True, input=stdin_data)
|
||||
if res.returncode != 0:
|
||||
@@ -1457,8 +1458,8 @@ def main():
|
||||
)
|
||||
parser.add_argument(
|
||||
"--toolchain-version",
|
||||
default="v0.7",
|
||||
help="Docker toolchain tag (default: v0.7).",
|
||||
default="v0.8",
|
||||
help="Docker toolchain tag (default: v0.8).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--toolchain-url",
|
||||
|
||||
+82
-27
@@ -49,27 +49,46 @@ MANAGED_ENV_NAMES = (
|
||||
"GGML_OPENCL_ADRENO_XMEM_GEMM",
|
||||
"GGML_OPENCL_ADRENO_USE_LARGE_BUFFER",
|
||||
"GGML_SCHED_DEBUG",
|
||||
"ADSP_LIBRARY_PATH",
|
||||
"CDSP_LIBRARY_PATH",
|
||||
"CDSP1_LIBRARY_PATH",
|
||||
"CDSP2_LIBRARY_PATH",
|
||||
"CDSP3_LIBRARY_PATH",
|
||||
"MTMD_BACKEND_DEVICE",
|
||||
"D",
|
||||
"DEVICE",
|
||||
)
|
||||
|
||||
|
||||
DSP_LIBRARY_ENV_NAMES = (
|
||||
"ADSP_LIBRARY_PATH",
|
||||
"CDSP_LIBRARY_PATH",
|
||||
"CDSP1_LIBRARY_PATH",
|
||||
"CDSP2_LIBRARY_PATH",
|
||||
"CDSP3_LIBRARY_PATH",
|
||||
)
|
||||
|
||||
|
||||
def parse_target(target_str):
|
||||
if not target_str:
|
||||
return None, None
|
||||
if target_str.startswith("adb") or target_str.startswith("android"):
|
||||
parts = target_str.split(":", 1)
|
||||
serial = parts[1] if len(parts) > 1 else None
|
||||
return "android", serial
|
||||
elif target_str.startswith("lnx") or target_str.startswith("linux") or target_str.startswith("ubuntu"):
|
||||
parts = target_str.split(":", 1)
|
||||
host = parts[1] if len(parts) > 1 else None
|
||||
return "linux", host
|
||||
return None, None, None
|
||||
|
||||
parts = target_str.split(":")
|
||||
target_name = parts[0]
|
||||
|
||||
if target_name in ("adb", "android"):
|
||||
serial = ":".join(parts[1:]) if len(parts) > 1 else None
|
||||
return "android", serial, "adb"
|
||||
elif target_name in ("lnx", "linux", "ubuntu"):
|
||||
if len(parts) > 1 and parts[1] == "adb":
|
||||
serial = ":".join(parts[2:]) if len(parts) > 2 else None
|
||||
return "linux", serial, "adb"
|
||||
host = ":".join(parts[1:]) if len(parts) > 1 else None
|
||||
return "linux", host, "ssh"
|
||||
elif target_str in ("wos", "windows"):
|
||||
return "windows", None
|
||||
return "windows", None, None
|
||||
else:
|
||||
return None, None
|
||||
return None, None, None
|
||||
|
||||
|
||||
def shlex_join(args_list):
|
||||
@@ -79,6 +98,41 @@ def shlex_join(args_list):
|
||||
return " ".join(pipes.quote(x) for x in args_list)
|
||||
|
||||
|
||||
def default_target_dir(target_type, target_transport, home_dir):
|
||||
if target_type == "android":
|
||||
return "/data/local/tmp/llama.cpp"
|
||||
if target_type == "linux":
|
||||
if home_dir:
|
||||
return home_dir.rstrip("/") + "/llama.cpp"
|
||||
if target_transport == "adb":
|
||||
return "/home/llama.cpp"
|
||||
return "~/llama.cpp"
|
||||
return "~/llama.cpp"
|
||||
|
||||
|
||||
def library_env_parts(lib_dir):
|
||||
env_parts = [f"LD_LIBRARY_PATH={lib_dir}"]
|
||||
for name in DSP_LIBRARY_ENV_NAMES:
|
||||
env_parts.append(f"{name}={lib_dir}")
|
||||
return env_parts
|
||||
|
||||
|
||||
def adb_root(adb_base):
|
||||
root_cmd = adb_base + ["root"]
|
||||
logger.info(f"+ {' '.join(root_cmd)}")
|
||||
res = subprocess.run(root_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB root failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
wait_cmd = adb_base + ["wait-for-device"]
|
||||
logger.info(f"+ {' '.join(wait_cmd)}")
|
||||
res = subprocess.run(wait_cmd)
|
||||
if res.returncode != 0:
|
||||
logger.error("ADB wait-for-device failed.")
|
||||
sys.exit(res.returncode)
|
||||
|
||||
|
||||
def split_device_list(devices):
|
||||
parts = []
|
||||
curr = []
|
||||
@@ -144,8 +198,9 @@ def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Unified runner for llama.cpp tools on Snapdragon (natively, via ADB, or via SSH)."
|
||||
)
|
||||
parser.add_argument("--target", help="Execution target (e.g. android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: local run)")
|
||||
parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux)")
|
||||
parser.add_argument("--target", help="Execution target (e.g. android[:serial]/adb[:serial], ubuntu:adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: local run)")
|
||||
parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux, /home/llama.cpp for Linux via ADB)")
|
||||
parser.add_argument("--home-dir", help="Home directory for Linux targets when deriving the default target directory")
|
||||
parser.add_argument("--install-dir", help="Install directory name (defaults to pkg-TARGET or pkg-TARGET-dbg prefix based on target)")
|
||||
parser.add_argument("--debug", action="store_true", help="Use debug build (defaults to pkg-TARGET-dbg folder)")
|
||||
parser.add_argument("--devices", "--device", "-d", help="Select execution devices (split into NPU and OpenCL GPUs automatically, default: HTP0)")
|
||||
@@ -201,11 +256,12 @@ def main():
|
||||
|
||||
target_type = None
|
||||
target_val = None
|
||||
target_transport = None
|
||||
target_prefix = None
|
||||
if args.target:
|
||||
target_type, target_val = parse_target(args.target)
|
||||
target_type, target_val, target_transport = parse_target(args.target)
|
||||
if not target_type:
|
||||
logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")
|
||||
logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], ubuntu:adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")
|
||||
sys.exit(1)
|
||||
target_prefix = args.target.split(":", 1)[0]
|
||||
|
||||
@@ -435,18 +491,19 @@ def main():
|
||||
# Resolve target directory on device
|
||||
target_dir = args.target_dir
|
||||
if not target_dir:
|
||||
target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp"
|
||||
target_dir = default_target_dir(target_type, target_transport, args.home_dir)
|
||||
|
||||
if target_type == "android":
|
||||
# Run via ADB
|
||||
if target_transport == "adb":
|
||||
adb_base = ["adb"]
|
||||
if target_val: # serial
|
||||
adb_base += ["-s", target_val]
|
||||
|
||||
env_parts = [
|
||||
"LD_LIBRARY_PATH=./lib",
|
||||
"ADSP_LIBRARY_PATH=./lib"
|
||||
]
|
||||
if target_type == "linux":
|
||||
adb_root(adb_base)
|
||||
|
||||
env_parts = library_env_parts("./lib")
|
||||
if target_type == "linux":
|
||||
env_parts.insert(0, f"HOME={args.home_dir or '/home'}")
|
||||
for k, v in env_vars.items():
|
||||
env_parts.append(f"{k}={v}")
|
||||
env_str = " ".join(env_parts)
|
||||
@@ -466,10 +523,7 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
# Linux remote run via SSH
|
||||
env_parts = [
|
||||
"LD_LIBRARY_PATH=./lib",
|
||||
"ADSP_LIBRARY_PATH=./lib"
|
||||
]
|
||||
env_parts = library_env_parts("./lib")
|
||||
for k, v in env_vars.items():
|
||||
env_parts.append(f"{k}={v}")
|
||||
env_str = " ".join(env_parts)
|
||||
@@ -490,7 +544,6 @@ def main():
|
||||
# Run locally
|
||||
local_env = os.environ.copy()
|
||||
lib_dir = os.path.normpath(os.path.join(package_path, "lib"))
|
||||
local_env["ADSP_LIBRARY_PATH"] = lib_dir
|
||||
if platform.system() == "Windows":
|
||||
local_env["PATH"] = lib_dir + os.path.pathsep + local_env.get("PATH", "")
|
||||
else:
|
||||
@@ -498,6 +551,8 @@ def main():
|
||||
|
||||
for k in MANAGED_ENV_NAMES:
|
||||
local_env.pop(k, None)
|
||||
for name in DSP_LIBRARY_ENV_NAMES:
|
||||
local_env[name] = lib_dir
|
||||
for k, v in env_vars.items():
|
||||
local_env[k] = v
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ SDK_CONFIGS = (
|
||||
{
|
||||
"name": "Hexagon SDK",
|
||||
"repo": "snapdragon-toolchain/hexagon-sdk",
|
||||
"default_version": "6.6.0.0",
|
||||
"default_version": "6.7.0.0",
|
||||
"parent_dir": "Hexagon_SDK",
|
||||
"archive_prefix": "hexagon-sdk-v",
|
||||
"markers": ("hexagon_sdk.json",),
|
||||
|
||||
@@ -223,6 +223,7 @@ def patch_config(cfg: dict, families: dict[str, Family], new_vocab: int | None,
|
||||
lfam = next((f for f in families.values() if f.kind == "layer"), None)
|
||||
if lfam is None:
|
||||
continue
|
||||
assert lfam.kept is not None
|
||||
d[key] = sum(1 for i in lfam.kept if i < val)
|
||||
elif "expert" in key and fam is not None and fam.kind == "expert" and not any(x in key for x in EXPERT_KEY_EXCLUDE):
|
||||
d[key] = fam.new_count
|
||||
@@ -236,6 +237,7 @@ def patch_config(cfg: dict, families: dict[str, Family], new_vocab: int | None,
|
||||
# per-layer lists, e.g. layer_types
|
||||
fam = by_card.get(len(val))
|
||||
if fam is not None and fam.kind == "layer" and len(val) in (fam.card, fam.regular_total):
|
||||
assert fam.kept is not None
|
||||
d[key] = [val[i] for i in fam.kept if i < len(val)]
|
||||
log.append(f"{key}: list[{len(val)}] -> list[{len(d[key])}]")
|
||||
return log
|
||||
|
||||
@@ -383,6 +383,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_DECISION_BLOCK_COUNT, "%s.decision.block_count" },
|
||||
{ LLM_KV_DECISION_ROUTING_BLOCK_COUNT, "%s.decision.routing_block_count" },
|
||||
{ LLM_KV_DECISION_HEAD_COUNT, "%s.decision.head_count" },
|
||||
{ LLM_KV_DECISION_LABELS, "%s.decision.labels" },
|
||||
|
||||
{ LLM_KV_TARGET_LAYERS, "%s.target_layers" },
|
||||
{ LLM_KV_TARGET_HIDDEN_SIZE, "%s.target_hidden_size" },
|
||||
@@ -430,6 +431,7 @@ static const std::map<llm_kv, const char *> LLM_KV_NAMES = {
|
||||
{ LLM_KV_TOKENIZER_ADD_BOS, "tokenizer.ggml.add_bos_token" },
|
||||
{ LLM_KV_TOKENIZER_ADD_EOS, "tokenizer.ggml.add_eos_token" },
|
||||
{ LLM_KV_TOKENIZER_ADD_SEP, "tokenizer.ggml.add_sep_token" },
|
||||
{ LLM_KV_TOKENIZER_DNA_AUTO_TAGS, "tokenizer.ggml.dna_auto_tags" },
|
||||
{ LLM_KV_TOKENIZER_ADD_PREFIX, "tokenizer.ggml.add_space_prefix" },
|
||||
{ LLM_KV_TOKENIZER_REMOVE_EXTRA_WS, "tokenizer.ggml.remove_extra_whitespaces" },
|
||||
{ LLM_KV_TOKENIZER_PRECOMPILED_CHARSMAP, "tokenizer.ggml.precompiled_charsmap" },
|
||||
|
||||
@@ -394,6 +394,7 @@ enum llm_kv {
|
||||
LLM_KV_TOKENIZER_ADD_BOS,
|
||||
LLM_KV_TOKENIZER_ADD_EOS,
|
||||
LLM_KV_TOKENIZER_ADD_SEP,
|
||||
LLM_KV_TOKENIZER_DNA_AUTO_TAGS,
|
||||
LLM_KV_TOKENIZER_ADD_PREFIX,
|
||||
LLM_KV_TOKENIZER_REMOVE_EXTRA_WS,
|
||||
LLM_KV_TOKENIZER_PRECOMPILED_CHARSMAP,
|
||||
@@ -430,6 +431,7 @@ enum llm_kv {
|
||||
LLM_KV_DECISION_BLOCK_COUNT,
|
||||
LLM_KV_DECISION_ROUTING_BLOCK_COUNT,
|
||||
LLM_KV_DECISION_HEAD_COUNT,
|
||||
LLM_KV_DECISION_LABELS,
|
||||
|
||||
LLM_KV_TARGET_LAYERS,
|
||||
LLM_KV_TARGET_HIDDEN_SIZE,
|
||||
|
||||
@@ -122,6 +122,9 @@ enum llama_decision_order {
|
||||
// The embeddings output has one value per entry: row i is the score of option i
|
||||
LLAMA_API bool llama_batch_ext_set_decision_order(struct llama_batch_ext * batch, int32_t idx, enum llama_decision_order order);
|
||||
|
||||
// Label of option i of a decision model ("<arch>.decision.labels"), nullptr past the last one
|
||||
LLAMA_API const char * llama_model_decision_label(const struct llama_model * model, uint32_t i);
|
||||
|
||||
// mirrors:
|
||||
// LLAMA_API float * llama_get_embeddings(struct llama_context * ctx);
|
||||
LLAMA_API float * llama_get_embeddings_nextn(struct llama_context * ctx);
|
||||
|
||||
@@ -1549,6 +1549,9 @@ void llama_model_base::load_hparams(llama_model_loader & ml) {
|
||||
hparams.n_cls_out = classifier_labels.size();
|
||||
}
|
||||
|
||||
// for decision models
|
||||
ml.get_arr(LLM_KV_DECISION_LABELS, decision_labels, false);
|
||||
|
||||
// per-arch hparams
|
||||
load_arch_hparams(ml);
|
||||
|
||||
@@ -3438,6 +3441,14 @@ const char * llama_model_cls_label(const struct llama_model * model, uint32_t i)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * llama_model_decision_label(const struct llama_model * model, uint32_t i) {
|
||||
if (i < model->decision_labels.size()) {
|
||||
return model->decision_labels[i].c_str();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// deprecated
|
||||
int32_t llama_n_ctx_train(const llama_model * model) {
|
||||
return llama_model_n_ctx_train(model);
|
||||
|
||||
@@ -724,6 +724,7 @@ struct llama_model {
|
||||
|
||||
// for classifier models
|
||||
std::vector<std::string> classifier_labels;
|
||||
std::vector<std::string> decision_labels;
|
||||
|
||||
struct ggml_tensor * tok_embd = nullptr;
|
||||
struct ggml_tensor * type_embd = nullptr;
|
||||
|
||||
+176
-39
@@ -418,7 +418,7 @@ struct llm_tokenizer_bpe : llm_tokenizer {
|
||||
// original regex from tokenizer.json
|
||||
// "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+"
|
||||
regex_exprs = {
|
||||
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))*((?=[\\p{L}])([^A-Z]))+|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))+((?=[\\p{L}])([^A-Z]))*|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
||||
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))*((?=[\\p{L}\\p{M}])([^A-Z]))+|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))+((?=[\\p{L}\\p{M}])([^A-Z]))*|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
||||
};
|
||||
break;
|
||||
case LLAMA_VOCAB_PRE_TYPE_CHAMELEON:
|
||||
@@ -440,7 +440,7 @@ struct llm_tokenizer_bpe : llm_tokenizer {
|
||||
regex_exprs = {
|
||||
// original regex from tokenizer.json
|
||||
// "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]*[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{Lm}\\p{Lo}\\p{M}]+[\\p{Ll}\\p{Lm}\\p{Lo}\\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
||||
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))*((?=[\\p{L}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}])([^a-z]))+((?=[\\p{L}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
||||
"[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))*((?=[\\p{L}\\p{M}])([^A-Z]))+(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|[^\\r\\n\\p{L}\\p{N}]?((?=[\\p{L}\\p{M}])([^a-z]))+((?=[\\p{L}\\p{M}])([^A-Z]))*(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])?|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n/]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
||||
};
|
||||
break;
|
||||
case LLAMA_VOCAB_PRE_TYPE_GRANITE_EMB_MULTI:
|
||||
@@ -1776,16 +1776,15 @@ private:
|
||||
// text at load
|
||||
static const std::string dna_kmer_marker = "\xee\x80\x80";
|
||||
|
||||
// HybridDNATokenizer (HuggingFaceBio/Carbon-*): text inside <dna>...</dna> is cut into fixed 6-mers, everything else goes through the base BPE
|
||||
// Mirrors HybridDNATokenizer.encode, with optional tagging of untagged DNA (auto_dna_tags, Carbon-14B) and tail="pad_n" vocab, whose dedicated tokens pad a trailing partial k-mer with N instead of A ("CT" -> "CTNNNN")
|
||||
struct llm_tokenizer_hybriddna_session : llm_tokenizer_bpe_session {
|
||||
llm_tokenizer_hybriddna_session(const llama_vocab & vocab, const llm_tokenizer_bpe & tokenizer) : llm_tokenizer_bpe_session{vocab, tokenizer}, vocab{vocab} {}
|
||||
|
||||
void tokenize(const std::string & text, std::vector<llama_token> & output) override {
|
||||
static const std::string open_tag = "<dna>";
|
||||
static const std::string close_tag = "</dna>";
|
||||
|
||||
const auto dna_begin_id = vocab.text_to_token(open_tag);
|
||||
const auto dna_end_id = vocab.text_to_token(close_tag);
|
||||
const auto dna_oov_id = vocab.text_to_token("<oov>");
|
||||
dna_begin_id = vocab.text_to_token(open_tag);
|
||||
dna_end_id = vocab.text_to_token(close_tag);
|
||||
dna_oov_id = vocab.text_to_token("<oov>");
|
||||
|
||||
// Fall back to plain BPE if the DNA pieces aren't in the vocab.
|
||||
if (dna_begin_id == LLAMA_TOKEN_NULL || dna_end_id == LLAMA_TOKEN_NULL || dna_oov_id == LLAMA_TOKEN_NULL) {
|
||||
@@ -1793,63 +1792,187 @@ struct llm_tokenizer_hybriddna_session : llm_tokenizer_bpe_session {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t k = 6;
|
||||
size_t pos = 0;
|
||||
pad_char = vocab.text_to_token("A" + std::string(k - 1, 'N') + dna_kmer_marker) != LLAMA_TOKEN_NULL ? 'N' : 'A';
|
||||
|
||||
// split on explicit tags as HybridDNATokenizer._split_by_dna_tags does; only the text outside them is auto-tagged
|
||||
size_t pos = 0;
|
||||
while (pos < text.size()) {
|
||||
const size_t start = text.find(open_tag, pos);
|
||||
if (start == std::string::npos) {
|
||||
if (pos < text.size()) {
|
||||
llm_tokenizer_bpe_session::tokenize(text.substr(pos), output);
|
||||
}
|
||||
const size_t end = text.find(close_tag, pos);
|
||||
|
||||
if (start == std::string::npos && end == std::string::npos) {
|
||||
tokenize_text(text.substr(pos), output);
|
||||
break;
|
||||
}
|
||||
if (start > pos) {
|
||||
llm_tokenizer_bpe_session::tokenize(text.substr(pos, start - pos), output);
|
||||
if (start == std::string::npos || end < start) {
|
||||
// as in _split_by_dna_tags, text before a </dna> without a matching <dna> is DNA (no begin token)
|
||||
tokenize_dna(text.substr(pos, end - pos), false, true, output);
|
||||
pos = end + close_tag.size();
|
||||
continue;
|
||||
}
|
||||
output.push_back(dna_begin_id);
|
||||
|
||||
tokenize_text(text.substr(pos, start - pos), output);
|
||||
|
||||
const size_t content_start = start + open_tag.size();
|
||||
const size_t end = text.find(close_tag, content_start);
|
||||
const size_t content_end = (end == std::string::npos) ? text.size() : end;
|
||||
|
||||
emit_dna_kmers(text.substr(content_start, content_end - content_start), k, dna_oov_id, output);
|
||||
|
||||
if (end == std::string::npos) {
|
||||
// an unterminated <dna> runs to the end of the text (no end token)
|
||||
tokenize_dna(text.substr(content_start), true, false, output);
|
||||
break;
|
||||
}
|
||||
output.push_back(dna_end_id);
|
||||
tokenize_dna(text.substr(content_start, end - content_start), true, true, output);
|
||||
pos = end + close_tag.size();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void emit_dna_kmers(const std::string & raw, size_t k, llama_token oov_id, std::vector<llama_token> & output) {
|
||||
std::string seq = raw;
|
||||
for (char & c : seq) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c = char(c - 32);
|
||||
static bool is_letter(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); }
|
||||
static bool is_acgt (char c) { return c == 'A' || c == 'C' || c == 'G' || c == 'T'; }
|
||||
static char to_upper (char c) { return c >= 'a' && c <= 'z' ? char(c - 32) : c; }
|
||||
|
||||
// IUPAC nucleotide codes, either case (HybridDNATokenizer.IUPAC_RUN_RE)
|
||||
static bool is_iupac(char c) {
|
||||
c = to_upper(c);
|
||||
return std::string("ACGTRYSWKMBDHVN").find(c) != std::string::npos;
|
||||
}
|
||||
|
||||
// HybridDNATokenizer._normalize_spaced_dna: join displays like "ACGTA CGTAC ..." when they hold at least twelve bases
|
||||
// (at least six chunks of 1-5 uppercase ACGT separated by spaces or tabs, not touching a letter)
|
||||
static std::string normalize_spaced_dna(const std::string & text) {
|
||||
const auto is_blank = [](char c) { return c == ' ' || c == '\t'; };
|
||||
std::string out;
|
||||
size_t p = 0;
|
||||
while (p < text.size()) {
|
||||
if (is_acgt(text[p]) && (p == 0 || !is_letter(text[p - 1]))) {
|
||||
size_t match_end = std::string::npos;
|
||||
size_t chunk = p;
|
||||
for (int n_chunks = 1; ; n_chunks++) {
|
||||
size_t chunk_end = chunk;
|
||||
while (chunk_end < text.size() && chunk_end - chunk < 5 && is_acgt(text[chunk_end])) {
|
||||
chunk_end++;
|
||||
}
|
||||
if (chunk_end == chunk) {
|
||||
break;
|
||||
}
|
||||
if (n_chunks >= 6 && (chunk_end == text.size() || !is_letter(text[chunk_end]))) {
|
||||
match_end = chunk_end; // the regex is greedy, so the last valid end wins
|
||||
}
|
||||
size_t next = chunk_end;
|
||||
while (next < text.size() && is_blank(text[next])) {
|
||||
next++;
|
||||
}
|
||||
if (next == chunk_end) {
|
||||
break;
|
||||
}
|
||||
chunk = next;
|
||||
}
|
||||
if (match_end != std::string::npos) {
|
||||
std::string seq;
|
||||
for (size_t i = p; i < match_end; i++) {
|
||||
if (!is_blank(text[i])) {
|
||||
seq += text[i];
|
||||
}
|
||||
}
|
||||
out += seq.size() >= 12 ? seq : text.substr(p, match_end - p);
|
||||
p = match_end;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out += text[p++];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// text outside explicit tags: with auto_dna_tags, tag DNA runs as HybridDNATokenizer._auto_tag_text_segment does
|
||||
// (pure ACGT runs of at least 6 bases, or runs of at least 12 with at most 20% IUPAC ambiguity codes)
|
||||
void tokenize_text(const std::string & raw, std::vector<llama_token> & output) {
|
||||
auto bpe = [&](const std::string & piece) {
|
||||
if (!piece.empty()) {
|
||||
llm_tokenizer_bpe_session::tokenize(piece, output);
|
||||
}
|
||||
};
|
||||
if (!vocab.get_dna_auto_tags()) {
|
||||
bpe(raw);
|
||||
return;
|
||||
}
|
||||
|
||||
// k-mers carry the reserved marker suffix; a non-ACGT k-mer simply
|
||||
// isn't in the vocab and falls back to <oov>
|
||||
auto kmer_token = [&](const std::string & kmer) {
|
||||
const std::string text = normalize_spaced_dna(raw);
|
||||
size_t prev = 0;
|
||||
size_t i = 0;
|
||||
while (i < text.size()) {
|
||||
if (!is_iupac(text[i])) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
size_t j = i;
|
||||
size_t n_ambiguous = 0;
|
||||
for (; j < text.size() && is_iupac(text[j]); j++) {
|
||||
n_ambiguous += !is_acgt(to_upper(text[j]));
|
||||
}
|
||||
const size_t len = j - i;
|
||||
if ((n_ambiguous == 0 && len >= 6) || (n_ambiguous > 0 && len >= 12 && 5 * n_ambiguous <= len)) {
|
||||
bpe(text.substr(prev, i - prev));
|
||||
tokenize_dna(text.substr(i, len), true, true, output);
|
||||
prev = j;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
bpe(text.substr(prev));
|
||||
}
|
||||
|
||||
// HybridDNATokenizer._process_dna_sequence: strip, uppercase, then one token per k bases
|
||||
// a k-mer with anything but ACGT is <oov>, and a trailing partial k-mer is right-padded with pad_char
|
||||
void tokenize_dna(const std::string & content, bool has_start, bool has_end, std::vector<llama_token> & output) {
|
||||
if (has_start) {
|
||||
output.push_back(dna_begin_id);
|
||||
}
|
||||
|
||||
auto cpts = unicode_cpts_from_utf8(content);
|
||||
size_t first = 0;
|
||||
size_t last = cpts.size();
|
||||
while (first < last && unicode_cpt_flags_from_cpt(cpts[first]).is_whitespace) {
|
||||
first++;
|
||||
}
|
||||
while (last > first && unicode_cpt_flags_from_cpt(cpts[last - 1]).is_whitespace) {
|
||||
last--;
|
||||
}
|
||||
|
||||
// k-mers carry the reserved marker suffix, so they never resolve to a base-vocab token
|
||||
auto kmer_token = [&](size_t from, size_t to) {
|
||||
std::string kmer;
|
||||
for (size_t i = from; i < to; i++) {
|
||||
const uint32_t c = cpts[i] < 128 ? (uint32_t) to_upper((char) cpts[i]) : cpts[i];
|
||||
if (c > 127 || !is_acgt((char) c)) {
|
||||
return dna_oov_id;
|
||||
}
|
||||
kmer += (char) c;
|
||||
}
|
||||
kmer.append(k - kmer.size(), pad_char);
|
||||
const auto tok = vocab.text_to_token(kmer + dna_kmer_marker);
|
||||
return tok != LLAMA_TOKEN_NULL ? tok : oov_id;
|
||||
return tok != LLAMA_TOKEN_NULL ? tok : dna_oov_id;
|
||||
};
|
||||
|
||||
size_t i = 0;
|
||||
for (; i + k <= seq.size(); i += k) {
|
||||
output.push_back(kmer_token(seq.substr(i, k)));
|
||||
size_t i = first;
|
||||
for (; i + k <= last; i += k) {
|
||||
output.push_back(kmer_token(i, i + k));
|
||||
}
|
||||
if (i < seq.size()) {
|
||||
std::string kmer = seq.substr(i);
|
||||
kmer.append(k - kmer.size(), 'A');
|
||||
output.push_back(kmer_token(kmer));
|
||||
if (i < last) {
|
||||
output.push_back(kmer_token(i, last));
|
||||
}
|
||||
|
||||
if (has_end) {
|
||||
output.push_back(dna_end_id);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string open_tag = "<dna>";
|
||||
const std::string close_tag = "</dna>";
|
||||
const size_t k = 6;
|
||||
|
||||
llama_token dna_begin_id = LLAMA_TOKEN_NULL;
|
||||
llama_token dna_end_id = LLAMA_TOKEN_NULL;
|
||||
llama_token dna_oov_id = LLAMA_TOKEN_NULL;
|
||||
char pad_char = 'A';
|
||||
|
||||
const llama_vocab & vocab;
|
||||
};
|
||||
|
||||
@@ -1957,6 +2080,7 @@ struct llama_vocab::impl {
|
||||
bool add_bos = false;
|
||||
bool add_eos = false;
|
||||
bool add_sep = false;
|
||||
bool dna_auto_tags = false; // hybriddna: tag untagged DNA runs before tokenizing
|
||||
bool ignore_merges = false;
|
||||
bool clean_spaces = false; // clean_up_tokenization_spaces
|
||||
bool remove_extra_whitespaces = false;
|
||||
@@ -2689,6 +2813,12 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
|
||||
if (text.size() > dna_kmer_marker.size()
|
||||
&& text.compare(text.size() - dna_kmer_marker.size(), dna_kmer_marker.size(), dna_kmer_marker) == 0) {
|
||||
text.erase(text.size() - dna_kmer_marker.size());
|
||||
// tail="pad_n": "CTNNNN" detokenizes to its real bases "CT"
|
||||
// full k-mers are ACGT only, so only N-padded tail tokens end in N
|
||||
const size_t last_base = text.find_last_not_of('N');
|
||||
if (last_base != std::string::npos) {
|
||||
text.erase(last_base + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2781,6 +2911,9 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
|
||||
if (ml.get_key(LLM_KV_TOKENIZER_ADD_SEP, temp, false)) {
|
||||
add_sep = temp;
|
||||
}
|
||||
if (ml.get_key(LLM_KV_TOKENIZER_DNA_AUTO_TAGS, temp, false)) {
|
||||
dna_auto_tags = temp;
|
||||
}
|
||||
|
||||
// workaround for Gemma 4
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/21500
|
||||
@@ -4312,6 +4445,10 @@ bool llama_vocab::get_add_sep() const {
|
||||
return pimpl->add_sep;
|
||||
}
|
||||
|
||||
bool llama_vocab::get_dna_auto_tags() const {
|
||||
return pimpl->dna_auto_tags;
|
||||
}
|
||||
|
||||
bool llama_vocab::get_ignore_merges() const {
|
||||
return pimpl->ignore_merges;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ struct llama_vocab {
|
||||
bool get_add_bos () const;
|
||||
bool get_add_eos () const;
|
||||
bool get_add_sep () const;
|
||||
bool get_dna_auto_tags () const;
|
||||
bool get_ignore_merges () const;
|
||||
bool get_clean_spaces () const;
|
||||
bool get_remove_extra_whitespaces () const;
|
||||
|
||||
@@ -131,11 +131,8 @@ llama_model_kolibri1::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
cur = build_norm(ffn_inp, layer.ffn_norm, NULL, LLM_NORM_RMS, il);
|
||||
cb(cur, "ffn_norm", il);
|
||||
|
||||
// The router runs in f32: the selection adds the bias to the logits, the weights
|
||||
// are the sigmoid of the unbiased logits, so the same logits feed both.
|
||||
// The selection adds the bias to the logits, the weights are the sigmoid of the unbiased logits, so the same logits feed both.
|
||||
ggml_tensor * logits = build_lora_mm(layer.ffn_gate_inp, cur);
|
||||
ggml_prec_set_acc(logits, GGML_PREC_F32);
|
||||
ggml_prec_set_src(logits, GGML_PREC_F32, 1);
|
||||
cb(logits, "ffn_moe_logits", il);
|
||||
|
||||
ggml_tensor * selection = ggml_add(ctx0, logits, layer.ffn_exp_probs_b);
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "models.h"
|
||||
|
||||
#include "llama-memory-recurrent.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
llm_build_lfm_base::llm_build_lfm_base(const llm_graph_params & params) : llm_graph_context(params) {}
|
||||
|
||||
ggml_tensor * llm_build_lfm_base::build_lfm_shortconv(const llama_model & model,
|
||||
const llama_memory_recurrent_context * mctx_recr,
|
||||
llm_graph_input_rs * inp_recr,
|
||||
ggml_tensor * cur,
|
||||
int il) {
|
||||
const uint32_t kv_head = mctx_recr->get_head();
|
||||
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
|
||||
const int64_t n_seqs = ubatch.n_seqs;
|
||||
GGML_ASSERT(n_seqs != 0);
|
||||
GGML_ASSERT(ubatch.equal_seqs());
|
||||
GGML_ASSERT(ubatch.n_tokens == n_seq_tokens * n_seqs);
|
||||
|
||||
GGML_ASSERT(hparams.n_shortconv_l_cache > 1);
|
||||
const uint32_t d_conv = hparams.n_shortconv_l_cache - 1;
|
||||
const int64_t n_embd = hparams.n_embd;
|
||||
|
||||
// {n_embd, n_tokens} => {n_embd, n_seq_tokens, n_seqs}
|
||||
cur = ggml_reshape_3d(ctx0, cur, cur->ne[0], n_seq_tokens, n_seqs);
|
||||
|
||||
auto * bcx = build_lora_mm(model.layers[il].shortconv.in_proj, cur);
|
||||
cb(bcx, "model.layers.{}.conv.in_proj", il);
|
||||
|
||||
constexpr auto n_chunks = 3;
|
||||
GGML_ASSERT(bcx->ne[0] % n_chunks == 0);
|
||||
const auto chunk_size = bcx->ne[0] / n_chunks;
|
||||
auto * b = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
0 * chunk_size * ggml_element_size(bcx));
|
||||
auto * c = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
1 * chunk_size * ggml_element_size(bcx));
|
||||
auto * x = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
2 * chunk_size * ggml_element_size(bcx));
|
||||
|
||||
auto * bx = ggml_transpose(ctx0, ggml_mul(ctx0, b, x));
|
||||
|
||||
// read conv state
|
||||
auto * conv_state = mctx_recr->get_r_l(il);
|
||||
auto * conv_rs = build_rs(inp_recr, conv_state, hparams.n_embd_r(), n_seqs);
|
||||
auto * conv = ggml_reshape_3d(ctx0, conv_rs, d_conv, n_embd, n_seqs);
|
||||
|
||||
// causal prepends the state, non-causal pads symmetrically for a centered window
|
||||
if (hparams.causal_attn) {
|
||||
bx = ggml_concat(ctx0, conv, bx, 0);
|
||||
} else {
|
||||
const int64_t pad = (hparams.n_shortconv_l_cache - 1) / 2;
|
||||
auto * left = ggml_cont(ctx0,
|
||||
ggml_view_3d(ctx0, conv, pad, n_embd, n_seqs, conv->nb[1], conv->nb[2], (d_conv - pad) * conv->nb[0]));
|
||||
bx = ggml_pad_ext(ctx0, ggml_concat(ctx0, left, bx, 0), 0, pad, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
GGML_ASSERT(bx->ne[0] > conv->ne[0]);
|
||||
|
||||
// write conv states: slot 0 = the final state, slot s = the state s tokens back (partial rollback)
|
||||
const int64_t K = hparams.causal_attn && cparams.n_rs_seq > 0 ? (int64_t) cparams.n_rs_seq + 1 : 1;
|
||||
const int64_t n_written = std::min<int64_t>(n_seq_tokens, K);
|
||||
const auto mem_size = mctx_recr->get_size();
|
||||
const size_t row_size = ggml_row_size(conv_state->type, (int64_t) d_conv * n_embd);
|
||||
|
||||
for (int64_t slot = 0; slot < n_written; ++slot) {
|
||||
auto * conv_snap = ggml_view_3d(ctx0, bx, d_conv, bx->ne[1], bx->ne[2], bx->nb[1], bx->nb[2],
|
||||
(bx->ne[0] - d_conv - slot) * ggml_element_size(bx));
|
||||
ggml_build_forward_expand(gf, ggml_cpy(ctx0, conv_snap,
|
||||
ggml_view_2d(ctx0, conv_state, (int64_t) d_conv * n_embd, n_seqs,
|
||||
conv_state->nb[1],
|
||||
((size_t) slot * mem_size + kv_head) * row_size)));
|
||||
}
|
||||
|
||||
auto * conv_kernel = model.layers[il].shortconv.conv;
|
||||
auto * conv_out = ggml_ssm_conv(ctx0, bx, conv_kernel);
|
||||
cb(conv_out, "model.layers.{}.conv.conv", il);
|
||||
|
||||
auto * y = ggml_mul(ctx0, c, conv_out);
|
||||
y = build_lora_mm(model.layers[il].shortconv.out_proj, y);
|
||||
cb(y, "model.layers.{}.conv.out_proj", il);
|
||||
// {n_embd, n_seq_tokens, n_seqs} => {n_embd, n_tokens}
|
||||
y = ggml_reshape_2d(ctx0, y, y->ne[0], n_seq_tokens * n_seqs);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
ggml_tensor * llm_build_lfm_base::build_lfm_dense_ffn(const llama_model & model,
|
||||
ggml_tensor * cur,
|
||||
int il) {
|
||||
GGML_ASSERT(!model.layers[il].ffn_up_b);
|
||||
GGML_ASSERT(!model.layers[il].ffn_gate_b);
|
||||
GGML_ASSERT(!model.layers[il].ffn_down_b);
|
||||
return build_ffn(cur,
|
||||
model.layers[il].ffn_up, NULL, NULL,
|
||||
model.layers[il].ffn_gate, NULL, NULL,
|
||||
model.layers[il].ffn_down, NULL, NULL,
|
||||
NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
||||
}
|
||||
|
||||
ggml_tensor * llm_build_lfm_base::build_lfm_moe_ffn(const llama_model & model,
|
||||
ggml_tensor * cur,
|
||||
int il) {
|
||||
return build_moe_ffn(cur,
|
||||
model.layers[il].ffn_gate_inp,
|
||||
model.layers[il].ffn_up_exps,
|
||||
model.layers[il].ffn_gate_exps,
|
||||
model.layers[il].ffn_down_exps,
|
||||
model.layers[il].ffn_exp_probs_b,
|
||||
n_expert, n_expert_used,
|
||||
LLM_FFN_SILU, true,
|
||||
hparams.expert_weights_scale,
|
||||
static_cast<llama_expert_gating_func_type>(hparams.expert_gating_func),
|
||||
il);
|
||||
}
|
||||
+8
-104
@@ -147,35 +147,11 @@ std::unique_ptr<llm_graph_context> llama_model_lfm2::build_arch_graph(const llm_
|
||||
|
||||
template <bool iswa>
|
||||
llama_model_lfm2::graph<iswa>::graph(const llama_model & model, const llm_graph_params & params) :
|
||||
llm_graph_context(params) {
|
||||
llm_build_lfm_base(params) {
|
||||
using inp_hybrid_type = std::conditional_t<iswa, llm_graph_input_mem_hybrid_iswa, llm_graph_input_mem_hybrid>;
|
||||
using inp_attn_type = std::conditional_t<iswa, llm_graph_input_attn_kv_iswa, llm_graph_input_attn_kv>;
|
||||
using mem_hybrid_ctx = std::conditional_t<iswa, llama_memory_hybrid_iswa_context, llama_memory_hybrid_context>;
|
||||
|
||||
// lambda helpers for readability
|
||||
auto build_dense_feed_forward = [&model, this](ggml_tensor * cur, int il) -> ggml_tensor * {
|
||||
GGML_ASSERT(!model.layers[il].ffn_up_b);
|
||||
GGML_ASSERT(!model.layers[il].ffn_gate_b);
|
||||
GGML_ASSERT(!model.layers[il].ffn_down_b);
|
||||
return build_ffn(cur,
|
||||
model.layers[il].ffn_up, NULL, NULL,
|
||||
model.layers[il].ffn_gate, NULL, NULL,
|
||||
model.layers[il].ffn_down, NULL, NULL,
|
||||
NULL, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
||||
};
|
||||
auto build_moe_feed_forward = [&model, this](ggml_tensor * cur, int il) -> ggml_tensor * {
|
||||
return build_moe_ffn(cur,
|
||||
model.layers[il].ffn_gate_inp,
|
||||
model.layers[il].ffn_up_exps,
|
||||
model.layers[il].ffn_gate_exps,
|
||||
model.layers[il].ffn_down_exps,
|
||||
model.layers[il].ffn_exp_probs_b,
|
||||
n_expert, n_expert_used,
|
||||
LLM_FFN_SILU, true,
|
||||
hparams.expert_weights_scale,
|
||||
static_cast<llama_expert_gating_func_type>(hparams.expert_gating_func),
|
||||
il);
|
||||
};
|
||||
auto build_attn_block = [&model, this](ggml_tensor * cur,
|
||||
ggml_tensor * inp_pos,
|
||||
inp_attn_type * inp_attn,
|
||||
@@ -207,82 +183,6 @@ llama_model_lfm2::graph<iswa>::graph(const llama_model & model, const llm_graph_
|
||||
|
||||
return cur;
|
||||
};
|
||||
auto build_shortconv_block = [&model, this](ggml_tensor * cur,
|
||||
llm_graph_input_rs * inp_recr,
|
||||
int il) -> ggml_tensor * {
|
||||
const auto * mctx_cur = static_cast<const mem_hybrid_ctx *>(mctx)->get_recr();
|
||||
const uint32_t kv_head = mctx_cur->get_head();
|
||||
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
|
||||
const int64_t n_seqs = ubatch.n_seqs;
|
||||
GGML_ASSERT(n_seqs != 0);
|
||||
GGML_ASSERT(ubatch.equal_seqs());
|
||||
GGML_ASSERT(ubatch.n_tokens == n_seq_tokens * n_seqs);
|
||||
|
||||
GGML_ASSERT(hparams.n_shortconv_l_cache > 1);
|
||||
const uint32_t d_conv = hparams.n_shortconv_l_cache - 1;
|
||||
|
||||
// {n_embd, n_tokens} => {n_embd, n_seq_tokens, n_seqs}
|
||||
cur = ggml_reshape_3d(ctx0, cur, cur->ne[0], n_seq_tokens, n_seqs);
|
||||
|
||||
auto * bcx = build_lora_mm(model.layers[il].shortconv.in_proj, cur);
|
||||
cb(bcx, "model.layers.{}.conv.in_proj", il);
|
||||
|
||||
constexpr auto n_chunks = 3;
|
||||
GGML_ASSERT(bcx->ne[0] % n_chunks == 0);
|
||||
const auto chunk_size = bcx->ne[0] / n_chunks;
|
||||
auto * b = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
0 * chunk_size * ggml_element_size(bcx));
|
||||
auto * c = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
1 * chunk_size * ggml_element_size(bcx));
|
||||
auto * x = ggml_view_3d(ctx0, bcx, chunk_size, bcx->ne[1], bcx->ne[2], bcx->nb[1], bcx->nb[2],
|
||||
2 * chunk_size * ggml_element_size(bcx));
|
||||
|
||||
auto * bx = ggml_transpose(ctx0, ggml_mul(ctx0, b, x));
|
||||
|
||||
// read conv state
|
||||
auto * conv_state = mctx_cur->get_r_l(il);
|
||||
auto * conv_rs = build_rs(inp_recr, conv_state, hparams.n_embd_r(), n_seqs);
|
||||
auto * conv = ggml_reshape_3d(ctx0, conv_rs, d_conv, hparams.n_embd, n_seqs);
|
||||
|
||||
// causal prepends the state, non-causal pads symmetrically for a centered window
|
||||
if (hparams.causal_attn) {
|
||||
bx = ggml_concat(ctx0, conv, bx, 0);
|
||||
} else {
|
||||
const int64_t pad = (hparams.n_shortconv_l_cache - 1) / 2;
|
||||
auto * left = ggml_cont(ctx0,
|
||||
ggml_view_3d(ctx0, conv, pad, hparams.n_embd, n_seqs, conv->nb[1], conv->nb[2], (d_conv - pad) * conv->nb[0]));
|
||||
bx = ggml_pad_ext(ctx0, ggml_concat(ctx0, left, bx, 0), 0, pad, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
GGML_ASSERT(bx->ne[0] > conv->ne[0]);
|
||||
|
||||
// write conv states: slot 0 = the final state, slot s = the state s tokens back (partial rollback)
|
||||
const int64_t K = hparams.causal_attn && cparams.n_rs_seq > 0 ? (int64_t) cparams.n_rs_seq + 1 : 1;
|
||||
const int64_t n_written = std::min<int64_t>(n_seq_tokens, K);
|
||||
const auto mem_size = mctx_cur->get_size();
|
||||
const size_t row_size = ggml_row_size(conv_state->type, (int64_t) d_conv * n_embd);
|
||||
|
||||
for (int64_t slot = 0; slot < n_written; ++slot) {
|
||||
auto * conv_snap = ggml_view_3d(ctx0, bx, d_conv, bx->ne[1], bx->ne[2], bx->nb[1], bx->nb[2],
|
||||
(bx->ne[0] - d_conv - slot) * ggml_element_size(bx));
|
||||
ggml_build_forward_expand(gf, ggml_cpy(ctx0, conv_snap,
|
||||
ggml_view_2d(ctx0, conv_state, (int64_t) d_conv * n_embd, n_seqs,
|
||||
conv_state->nb[1],
|
||||
((size_t) slot * mem_size + kv_head) * row_size)));
|
||||
}
|
||||
|
||||
auto * conv_kernel = model.layers[il].shortconv.conv;
|
||||
auto * conv_out = ggml_ssm_conv(ctx0, bx, conv_kernel);
|
||||
cb(conv_out, "model.layers.{}.conv.conv", il);
|
||||
|
||||
auto * y = ggml_mul(ctx0, c, conv_out);
|
||||
y = build_lora_mm(model.layers[il].shortconv.out_proj, y);
|
||||
cb(y, "model.layers.{}.conv.out_proj", il);
|
||||
// {n_embd, n_seq_tokens, n_seqs} => {n_embd, n_tokens}
|
||||
y = ggml_reshape_2d(ctx0, y, y->ne[0], n_seq_tokens * n_seqs);
|
||||
|
||||
return y;
|
||||
};
|
||||
|
||||
// actual graph construction starts here
|
||||
ggml_tensor * cur = build_inp_embd(model.tok_embd);
|
||||
cb(cur, "model.embed_tokens", -1);
|
||||
@@ -308,8 +208,12 @@ llama_model_lfm2::graph<iswa>::graph(const llama_model & model, const llm_graph_
|
||||
cur = build_norm(cur, model.layers[il].attn_norm, NULL, LLM_NORM_RMS, il);
|
||||
cb(cur, "model.layers.{}.operator_norm", il);
|
||||
|
||||
cur = hparams.is_recr(il) ? build_shortconv_block(cur, inp_hybrid->get_recr(), il) :
|
||||
build_attn_block(cur, inp_pos, inp_hybrid->get_attn(), il);
|
||||
if (hparams.is_recr(il)) {
|
||||
const auto * mctx_recr = static_cast<const mem_hybrid_ctx *>(mctx)->get_recr();
|
||||
cur = build_lfm_shortconv(model, mctx_recr, inp_hybrid->get_recr(), cur, il);
|
||||
} else {
|
||||
cur = build_attn_block(cur, inp_pos, inp_hybrid->get_attn(), il);
|
||||
}
|
||||
|
||||
if (il == n_layer - 1 && inp_out_ids) {
|
||||
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
||||
@@ -322,7 +226,7 @@ llama_model_lfm2::graph<iswa>::graph(const llama_model & model, const llm_graph_
|
||||
cb(ffn_norm_out, "model.layers.{}.ffn_norm", il);
|
||||
|
||||
ggml_tensor * ffn_out =
|
||||
is_moe_layer ? build_moe_feed_forward(ffn_norm_out, il) : build_dense_feed_forward(ffn_norm_out, il);
|
||||
is_moe_layer ? build_lfm_moe_ffn(model, ffn_norm_out, il) : build_lfm_dense_ffn(model, ffn_norm_out, il);
|
||||
cb(ffn_norm_out, "model.layers.{}.ffn_out", il);
|
||||
|
||||
cur = ggml_add(ctx0, cur, ffn_out);
|
||||
|
||||
+21
-1
@@ -100,6 +100,26 @@ struct llm_build_delta_net_base : public llm_graph_context {
|
||||
int il);
|
||||
};
|
||||
|
||||
struct llm_build_lfm_base : public llm_graph_context {
|
||||
llm_build_lfm_base(const llm_graph_params & params);
|
||||
|
||||
virtual ~llm_build_lfm_base() = default;
|
||||
|
||||
ggml_tensor * build_lfm_shortconv(const llama_model & model,
|
||||
const llama_memory_recurrent_context * mctx_recr,
|
||||
llm_graph_input_rs * inp_recr,
|
||||
ggml_tensor * cur,
|
||||
int il);
|
||||
|
||||
ggml_tensor * build_lfm_dense_ffn(const llama_model & model,
|
||||
ggml_tensor * cur,
|
||||
int il);
|
||||
|
||||
ggml_tensor * build_lfm_moe_ffn(const llama_model & model,
|
||||
ggml_tensor * cur,
|
||||
int il);
|
||||
};
|
||||
|
||||
struct llm_build_rwkv6_base : public llm_graph_context {
|
||||
const llama_model & model;
|
||||
|
||||
@@ -2264,7 +2284,7 @@ struct llama_model_lfm2 : public llama_model_base {
|
||||
void load_arch_tensors(llama_model_loader & ml) override;
|
||||
|
||||
template <bool iswa>
|
||||
struct graph : public llm_graph_context {
|
||||
struct graph : public llm_build_lfm_base {
|
||||
graph(const llama_model & model, const llm_graph_params & params);
|
||||
};
|
||||
|
||||
|
||||
@@ -465,17 +465,14 @@ ggml_tensor * llama_model_qwen35::graph::build_layer_attn_linear(
|
||||
// Apply gated normalization: self.norm(core_attn_out, z)
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_tokens, n_seqs, n_heads * head_dim]
|
||||
ggml_tensor * final_output = ggml_reshape_3d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens, n_seqs);
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_heads * head_dim , n_tokens * n_seqs ]
|
||||
ggml_tensor * final_output = ggml_reshape_2d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens * n_seqs);
|
||||
cb(final_output, "final_output", il);
|
||||
|
||||
// Output projection
|
||||
cur = build_lora_mm(model.layers[il].ssm_out, final_output, model.layers[il].ssm_out_s);
|
||||
cb(cur, "linear_attn_out", il);
|
||||
|
||||
// Reshape back to original dimensions
|
||||
cur = ggml_reshape_2d(ctx0, cur, n_embd, n_seq_tokens * n_seqs);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
@@ -475,17 +475,13 @@ ggml_tensor * llama_model_qwen35moe::graph::build_layer_attn_linear(
|
||||
// Apply gated normalization: self.norm(core_attn_out, z)
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_tokens, n_seqs, n_heads * head_dim]
|
||||
ggml_tensor * final_output = ggml_reshape_3d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens, n_seqs);
|
||||
cb(final_output, "final_output", il);
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_heads * head_dim , n_tokens * n_seqs ]
|
||||
ggml_tensor * final_output = ggml_reshape_2d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens * n_seqs);
|
||||
|
||||
// Output projection
|
||||
cur = build_lora_mm(model.layers[il].ssm_out, final_output, model.layers[il].ssm_out_s);
|
||||
cb(cur, "linear_attn_out", il);
|
||||
|
||||
// Reshape back to original dimensions
|
||||
cur = ggml_reshape_2d(ctx0, cur, n_embd, n_seq_tokens * n_seqs);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
@@ -542,17 +542,14 @@ ggml_tensor * llama_model_qwen3next::graph::build_layer_attn_linear(
|
||||
// Apply gated normalization: self.norm(core_attn_out, z)
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_tokens, n_seqs, n_heads * head_dim]
|
||||
ggml_tensor * final_output = ggml_reshape_3d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens, n_seqs);
|
||||
// Final reshape: [head_dim, n_heads, n_tokens, n_seqs] -> [n_heads * head_dim , n_tokens * n_seqs ]
|
||||
ggml_tensor * final_output = ggml_reshape_2d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens * n_seqs);
|
||||
cb(final_output, "final_output", il);
|
||||
|
||||
// Output projection
|
||||
cur = build_lora_mm(model.layers[il].ssm_out, final_output);
|
||||
cb(cur, "linear_attn_out", il);
|
||||
|
||||
// Reshape back to original dimensions
|
||||
cur = ggml_reshape_2d(ctx0, cur, n_embd, n_seq_tokens * n_seqs);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
@@ -1131,14 +1131,12 @@ ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear(
|
||||
// gated normalization, as self.norm(core_attn_out, z) in the reference
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
ggml_tensor * final_output = ggml_reshape_3d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens, n_seqs);
|
||||
ggml_tensor * final_output = ggml_reshape_2d(ctx0, attn_out_norm, head_v_dim * num_v_heads, n_seq_tokens * n_seqs);
|
||||
cb(final_output, "final_output", il);
|
||||
|
||||
cur = build_lora_mm(model.layers[il].ssm_out, final_output, model.layers[il].ssm_out_s);
|
||||
cb(cur, "linear_attn_out", il);
|
||||
|
||||
cur = ggml_reshape_2d(ctx0, cur, n_embd, n_seq_tokens * n_seqs);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
@@ -2453,6 +2453,63 @@ struct test_get_rows : public test_case {
|
||||
}
|
||||
};
|
||||
|
||||
// GGML_OP_GET_ROWS on a weight also used by GGML_OP_MUL_MAT (tied embeddings)
|
||||
// the mul_mat runs first, so a backend that changes the weight layout must still return the right rows
|
||||
struct test_get_rows_after_mul_mat : public test_case {
|
||||
const ggml_type type;
|
||||
const int n; // cols
|
||||
const int m; // rows
|
||||
const int r; // rows to get
|
||||
|
||||
std::string vars() override {
|
||||
return VARS_TO_STR4(type, n, m, r);
|
||||
}
|
||||
|
||||
test_get_rows_after_mul_mat(ggml_type type = GGML_TYPE_Q8_0, int n = 256, int m = 16, int r = 4)
|
||||
: type(type), n(n), m(m), r(r) {}
|
||||
|
||||
double max_nmse_err() override {
|
||||
return 5e-4; // same as test_mul_mat
|
||||
}
|
||||
|
||||
ggml_tensor * build_graph(ggml_context * ctx) override {
|
||||
ggml_tensor * w = ggml_new_tensor_2d(ctx, type, n, m);
|
||||
ggml_set_name(w, "w");
|
||||
|
||||
ggml_tensor * x = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n, 1);
|
||||
ggml_set_name(x, "x");
|
||||
|
||||
ggml_tensor * rows = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, r);
|
||||
ggml_set_name(rows, "rows");
|
||||
|
||||
ggml_tensor * mm = ggml_mul_mat(ctx, w, x);
|
||||
ggml_set_name(mm, "mm");
|
||||
|
||||
ggml_tensor * gr = ggml_get_rows(ctx, w, rows);
|
||||
ggml_set_name(gr, "gr");
|
||||
|
||||
// repeat(mm) is the first source, so mul_mat is computed before get_rows
|
||||
ggml_tensor * out = ggml_add(ctx, ggml_repeat(ctx, mm, gr), gr);
|
||||
ggml_set_name(out, "out");
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void initialize_tensors(ggml_context * ctx) override {
|
||||
for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) {
|
||||
if (t->type == GGML_TYPE_I32) {
|
||||
std::vector<int> data(ggml_nelements(t));
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
data[i] = rand() % m;
|
||||
}
|
||||
ggml_backend_tensor_set(t, data.data(), 0, data.size() * sizeof(int));
|
||||
} else {
|
||||
init_tensor_uniform(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// GGML_OP_GET_ROWS_BACK
|
||||
struct test_get_rows_back : public test_case {
|
||||
const ggml_type type;
|
||||
@@ -9828,6 +9885,9 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
}
|
||||
|
||||
test_cases.emplace_back(new test_get_rows(GGML_TYPE_F32, 1, 8, 2, 1, 1, false));
|
||||
for (ggml_type type : {GGML_TYPE_Q4_0, GGML_TYPE_Q8_0, GGML_TYPE_Q2_K, GGML_TYPE_Q3_K, GGML_TYPE_Q4_K, GGML_TYPE_Q5_K, GGML_TYPE_Q6_K}) {
|
||||
test_cases.emplace_back(new test_get_rows_after_mul_mat(type));
|
||||
}
|
||||
for (ggml_type type : all_types) {
|
||||
for (int b : {1, 7}) {
|
||||
for (bool v : {false, true}) {
|
||||
@@ -10599,7 +10659,7 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
// ne[2] > 65535 exceeds the CUDA gridDim.y limit (#27901)
|
||||
test_cases.emplace_back(new test_rms_norm_mul_rope({4, 1, 65536, 1}, 1e-6f, false, false, false, GGML_ROPE_TYPE_NORMAL));
|
||||
test_cases.emplace_back(new test_rms_norm_mul_rope({4, 1, 65536, 1}, 1e-6f, false, true, false, GGML_ROPE_TYPE_NEOX));
|
||||
for (int64_t d_conv : {3, 4, 9}) {
|
||||
for (int64_t d_conv : {3, 4, 7, 9}) {
|
||||
for (int64_t d_inner: {1024, 1536, 2048}) {
|
||||
test_cases.emplace_back(new test_ssm_conv(GGML_TYPE_F32, {d_conv, d_inner, 1, 1}, {d_conv, d_inner, 1, 1}));
|
||||
test_cases.emplace_back(new test_ssm_conv(GGML_TYPE_F32, {2 * d_conv, d_inner, 1, 1}, {d_conv, d_inner, 1, 1}));
|
||||
@@ -10612,7 +10672,7 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
|
||||
// fused ssm_conv + (optional) bias_add + silu. The bias-only graph (no silu) is intentionally
|
||||
// not tested since there's no fusion for that pattern in ggml_cuda_can_fuse.
|
||||
for (int64_t d_conv : {3, 4, 9}) {
|
||||
for (int64_t d_conv : {3, 4, 7, 9}) {
|
||||
for (int64_t d_inner : {1024, 1536, 2048}) {
|
||||
for (bool fuse_bias : {false, true}) {
|
||||
// short token path (n_t <= 32)
|
||||
@@ -12139,6 +12199,17 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_perf() {
|
||||
}
|
||||
}
|
||||
|
||||
// POOL_1D max over MoE down-projection amax shapes:
|
||||
// amax is [1, n_expert, n_tokens] after reducing the whole intermediate dim.
|
||||
for (int64_t n_ff : { 2048, 4096, 8192 }) {
|
||||
for (int64_t n_expert : { 8, 16 }) {
|
||||
for (int64_t n_tokens : { 1, 512 }) {
|
||||
test_cases.emplace_back(new test_pool1d(
|
||||
GGML_OP_POOL_MAX, GGML_TYPE_F32, {n_ff, n_expert, n_tokens, 1}, n_ff, n_ff, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LEAKY_RELU at FFN activation width, for direct comparison with RELU
|
||||
for (int64_t n_tokens : {512, 2048}) {
|
||||
test_cases.emplace_back(new test_leaky_relu(GGML_TYPE_F32, { 17408, n_tokens, 1, 1 }, 0.1f));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user