Compare commits

..
Author SHA1 Message Date
Pascal 4d35985b5a graph: build the conv slot order in each conv layer
The slot order {K-1, ..., 0} of the conv state store was one tensor
shared by every conv layer of the graph, built on the device of the
first one, so each layer on another device copied the view of its
tail. Each conv layer now builds its own order next to its set_rows,
and llm_graph_input_rs no longer holds it.
2026-11-04 18:56:21 +01:00
Pascal bd540a6e61 nits: compute the conv slot order in the graph and keep the n_head name of bailingmoe3 2026-11-04 15:37:14 +01:00
Pascal 36023ede3d graph: store the conv state of every rollback arch in one set_rows
The rollback slots of the causal conv history were written with one
copy per slot in qwen4exp, glm5-next, kimi-k3, bailingmoe3, lfm and
mamba2, while delta_net already wrote them with a single set_rows.
build_conv_state_store now does this for all of them: one copy without
rollback, otherwise one set_rows of the overlapping windows into the
min(n, n_rs_seq + 1) slots a rollback can read.

The slot order {n_rs_seq, ..., 0} is computed once per graph and each
conv layer takes a view of its tail. Its size does not depend on the
ubatch, so the reserved graph covers every ubatch shape, and the graph
input that held it is gone.

A model that is not causal never rolls back, so the context clamps
n_rs_seq to 0 for it, like for an arch without rollback support.
2026-11-04 15:37:14 +01:00
378 changed files with 5102 additions and 10710 deletions
-3
View File
@@ -84,9 +84,6 @@ RUN apt-get update \
&& find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete \
&& find /var/cache -type f -delete
# Some hosts (e.g. GKE) only mount the driver here, and CUDA 12.8.1-12.9.1 base images don't set these paths
ENV LD_LIBRARY_PATH=/usr/local/nvidia/lib64:/usr/local/nvidia/lib:${LD_LIBRARY_PATH}
COPY --from=build /app/lib/ /app
### Full
+1 -3
View File
@@ -64,7 +64,7 @@ RUN mkdir -p /app/full \
&& cp requirements.txt /app/full \
&& cp .devops/tools.sh /app/full/tools.sh
FROM docker.io/intel/deep-learning-essentials:$ONEAPI_VERSION AS base
FROM docker.io/intel/oneapi-toolkit:$ONEAPI_VERSION AS base
ARG BUILD_DATE=N/A
ARG APP_VERSION=N/A
@@ -79,8 +79,6 @@ LABEL org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.url=$IMAGE_URL \
org.opencontainers.image.source=$IMAGE_SOURCE
COPY --from=build /opt/intel/oneapi/dnnl /opt/intel/oneapi/dnnl
#Following versions are for multiple GPUs, since 26.x has known issue:
# https://github.com/ggml-org/llama.cpp/issues/21747,
# https://github.com/intel/compute-runtime/issues/921.
+46 -46
View File
@@ -100,56 +100,56 @@ runs:
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.1" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V13_1=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.1" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Install Cuda Toolkit 13.5 for x64
if: ${{ inputs.cuda_version == '13.5' && inputs.cuda_arch == 'x64' }}
- name: Install Cuda Toolkit 13.4 for x64
if: ${{ inputs.cuda_version == '13.4' && inputs.cuda_arch == 'x64' }}
shell: pwsh
run: |
mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5"
mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4"
choco install unzip -y
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-x86_64/libcublas-windows-x86_64-13.10.0.20-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libnvvm/windows-x86_64/libnvvm-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-13.5.25-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_profiler_api/windows-x86_64/cuda_profiler_api-windows-x86_64-13.5.25-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-13.5.25-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cccl/windows-x86_64/cccl-windows-x86_64-13.3.5.0.6-archive.zip"
unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5"
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_crt-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_cudart-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_nvcc-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_nvrtc-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\libcublas-windows-x86_64-13.10.0.20-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\libnvvm-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_nvtx-windows-x86_64-13.5.25-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_profiler_api-windows-x86_64-13.5.25-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\visual_studio_integration-windows-x86_64-13.5.25-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cccl-windows-x86_64-13.3.5.0.6-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V13_5=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-13.4.49-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-x86_64/libcublas-windows-x86_64-13.7.0.27-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libnvvm/windows-x86_64/libnvvm-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-13.4.49-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_profiler_api/windows-x86_64/cuda_profiler_api-windows-x86_64-13.4.49-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-13.4.49-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cccl/windows-x86_64/cccl-windows-x86_64-13.3.4.2.1-archive.zip"
unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4"
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_crt-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_cudart-windows-x86_64-13.4.49-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_nvcc-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_nvrtc-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\libcublas-windows-x86_64-13.7.0.27-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\libnvvm-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_nvtx-windows-x86_64-13.4.49-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_profiler_api-windows-x86_64-13.4.49-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\visual_studio_integration-windows-x86_64-13.4.49-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cccl-windows-x86_64-13.3.4.2.1-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V13_4=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Install Cuda Toolkit 13.5 for ARM64
if: ${{ inputs.cuda_version == '13.5' && inputs.cuda_arch == 'arm64' }}
- name: Install Cuda Toolkit 13.4 for ARM64
if: ${{ inputs.cuda_version == '13.4' && inputs.cuda_arch == 'arm64' }}
shell: pwsh
run: |
mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5"
mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4"
choco install unzip -y
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cccl/windows-x86_64/cccl-windows-x86_64-13.3.5.0.6-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libnvvm/windows-x86_64/libnvvm-windows-x86_64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-arm64/cuda_cudart-windows-arm64-13.5.37-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-arm64/libcublas-windows-arm64-13.10.0.20-archive.zip"
unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5"
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cccl-windows-x86_64-13.3.5.0.6-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_crt-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_nvcc-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\libnvvm-windows-x86_64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\cuda_cudart-windows-arm64-13.5.37-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\libcublas-windows-arm64-13.10.0.20-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" /E /I /H /Y
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V13_5=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.5" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cccl/windows-x86_64/cccl-windows-x86_64-13.3.4.2.1-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libnvvm/windows-x86_64/libnvvm-windows-x86_64-13.4.59-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-arm64/cuda_cudart-windows-arm64-13.4.49-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-arm64/libcublas-windows-arm64-13.7.0.27-archive.zip"
unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4"
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cccl-windows-x86_64-13.3.4.2.1-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_crt-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_nvcc-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\libnvvm-windows-x86_64-13.4.59-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\cuda_cudart-windows-arm64-13.4.49-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\libcublas-windows-arm64-13.7.0.27-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" /E /I /H /Y
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V13_4=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.4" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
+57
View File
@@ -0,0 +1,57 @@
name: Build relocatable cmake package
on:
workflow_dispatch:
workflow_call:
cache-mode: none
permissions:
contents: read
jobs:
linux:
runs-on: [self-hosted, Linux, fast]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Build
run: |
PREFIX="$(pwd)"/inst
cmake -S . -B build \
-DCMAKE_PREFIX_PATH="$PREFIX" \
-DLLAMA_OPENSSL=OFF \
-DLLAMA_BUILD_TESTS=OFF \
-DLLAMA_BUILD_TOOLS=OFF \
-DLLAMA_BUILD_EXAMPLES=OFF \
-DLLAMA_BUILD_APP=OFF \
-DLLAMA_BUILD_IS_DEV=OFF \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j $(nproc)
cmake --install build --prefix "$PREFIX" --config Release
export LLAMA_CONFIG="$PREFIX"/lib/cmake/llama/llama-config.cmake
build_commit=$(git rev-parse --short HEAD | xargs)
build_number=$(git rev-list --count HEAD | xargs)
major=$(grep -oE "set\(LLAMA_VERSION_MAJOR[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
minor=$(grep -oE "set\(LLAMA_VERSION_MINOR[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
patch=$(grep -oE "set\(LLAMA_VERSION_PATCH[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
build_version="$major.$minor.$patch"
checks=("set\(LLAMA_VERSION[[:space:]]+$build_version\)"
"set\(LLAMA_BUILD_COMMIT[[:space:]]+$build_commit\)"
"set\(LLAMA_BUILD_NUMBER[[:space:]]+$build_number\)")
for check in "${checks[@]}"; do
if ! grep -qE "$check" "$LLAMA_CONFIG"; then
echo "Checking llama-config.cmake version... \"$check\" failed!"
exit 1
fi
done
echo "Checking llama-config.cmake version... success."
cd examples/simple-cmake-pkg
cmake -S . -B build -DCMAKE_PREFIX_PATH="$PREFIX"/lib/cmake
cmake --build build -j $(nproc)
+5 -76
View File
@@ -7,6 +7,7 @@ on:
- master
paths: [
'.github/workflows/build-cpu.yml',
'.github/workflows/build-cmake-pkg.yml',
'**/CMakeLists.txt',
'**/.cmake',
'**/*.h',
@@ -19,6 +20,7 @@ on:
types: [opened, synchronize, reopened]
paths: [
'.github/workflows/build-cpu.yml',
'.github/workflows/build-cmake-pkg.yml',
'ggml/src/ggml-rpc/**',
'**/CMakeLists.txt',
'**/.cmake',
@@ -45,6 +47,9 @@ env:
LLAMA_ARG_LOG_TIMESTAMPS: 1
jobs:
build-cmake-pkg:
uses: ./.github/workflows/build-cmake-pkg.yml
ubuntu:
strategy:
matrix:
@@ -237,79 +242,3 @@ jobs:
older: 5m
min: 1
dry-run: ${{ github.event_name != 'push' || github.ref != 'refs/heads/master' }}
build-cmake-pkg:
runs-on: ubuntu-24.04
steps:
- name: Clone
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: ccache
uses: ggml-org/ccache-action@v1.2.24
with:
restore: false
save: false
- name: ccache-buckets-restore
uses: ./.github/actions/ccache-buckets
env:
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
with:
key: build-cmake-pkg
folder: llama.cpp
hf_bucket: ggml-org/cache
- name: Build
run: |
PREFIX="$(pwd)"/inst
cmake -S . -B build \
-DCMAKE_PREFIX_PATH="$PREFIX" \
-DLLAMA_OPENSSL=OFF \
-DLLAMA_BUILD_TESTS=OFF \
-DLLAMA_BUILD_TOOLS=OFF \
-DLLAMA_BUILD_EXAMPLES=OFF \
-DLLAMA_BUILD_APP=OFF \
-DLLAMA_BUILD_IS_DEV=OFF \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j $(nproc)
cmake --install build --prefix "$PREFIX" --config Release
export LLAMA_CONFIG="$PREFIX"/lib/cmake/llama/llama-config.cmake
build_commit=$(git rev-parse --short HEAD | xargs)
build_number=$(git rev-list --count HEAD | xargs)
major=$(grep -oE "set\(LLAMA_VERSION_MAJOR[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
minor=$(grep -oE "set\(LLAMA_VERSION_MINOR[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
patch=$(grep -oE "set\(LLAMA_VERSION_PATCH[[:space:]]+[0-9]+" CMakeLists.txt | grep -oE "[0-9]+$")
build_version="$major.$minor.$patch"
checks=("set\(LLAMA_VERSION[[:space:]]+$build_version\)"
"set\(LLAMA_BUILD_COMMIT[[:space:]]+$build_commit\)"
"set\(LLAMA_BUILD_NUMBER[[:space:]]+$build_number\)")
for check in "${checks[@]}"; do
if ! grep -qE "$check" "$LLAMA_CONFIG"; then
echo "Checking llama-config.cmake version... \"$check\" failed!"
exit 1
fi
done
echo "Checking llama-config.cmake version... success."
cd examples/simple-cmake-pkg
cmake -S . -B build -DCMAKE_PREFIX_PATH="$PREFIX"/lib/cmake
cmake --build build -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: build-cmake-pkg
folder: llama.cpp
evict-old-files: 1d
hf_bucket: ggml-org/cache
save: true
+4 -4
View File
@@ -40,12 +40,12 @@ jobs:
- cuda: '12.4'
arch: x64
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- cuda: '13.5'
- cuda: '13.4'
arch: x64
defines: ''
- cuda: '13.5'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- cuda: '13.4'
arch: arm64
defines: '-DCMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-msvc-cuda.cmake'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3 -DCMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-msvc-cuda.cmake'
steps:
- name: Clone
+25 -29
View File
@@ -43,12 +43,13 @@ jobs:
strategy:
matrix:
include:
# thread and address doesn't run properly on some self hosted machines, so run it on Github instead
- sanitizer: ADDRESS
machine: ubuntu-26.04
machine: ubuntu-24.04
- sanitizer: THREAD
machine: ubuntu-26.04
machine: ubuntu-24.04
- sanitizer: UNDEFINED
machine: ubuntu-26.04
machine: [self-hosted, X64, Linux, CPU]
runs-on: ${{ matrix.machine }}
@@ -57,24 +58,31 @@ jobs:
id: checkout
uses: actions/checkout@v6
- name: ccache
uses: ggml-org/ccache-action@v1.2.24
with:
restore: false
save: false
# - name: ccache
# uses: ggml-org/ccache-action@v1.2.24
# if: ${{ matrix.sanitizer != 'UNDEFINED' }}
# with:
# key: ctest-${{ matrix.sanitizer }}-ubuntu-24.04
# variant: ccache
# evict-old-files: 1d
# save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
- name: ccache-buckets-restore
uses: ./.github/actions/ccache-buckets
env:
HF_TOKEN: ${{ secrets.HF_TOKEN_CI }}
with:
key: ctest-sanitizer-${{ matrix.sanitizer }}
folder: llama.cpp
hf_bucket: ggml-org/cache
# with UNDEFINED sanitizer, we have to build in Debug to avoid GCC 13 false-positive warnings
- name: Build (undefined)
id: cmake_build_undefined
if: ${{ matrix.sanitizer == 'UNDEFINED' }}
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DLLAMA_FATAL_WARNINGS=ON \
-DLLAMA_SANITIZE_${{ matrix.sanitizer }}=ON \
-DGGML_SANITIZE_${{ matrix.sanitizer }}=ON
cmake --build build --config Debug -j $(nproc)
- name: Build
id: cmake_build
if: ${{ matrix.sanitizer == 'ADDRESS' || matrix.sanitizer == 'UNDEFINED' }}
if: ${{ matrix.sanitizer == 'ADDRESS' }}
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
@@ -95,18 +103,6 @@ jobs:
cmake --build build --config RelWithDebInfo -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: ctest-sanitizer-${{ matrix.sanitizer }}
folder: llama.cpp
evict-old-files: 1d
hf_bucket: ggml-org/cache
save: true
- name: Test
id: cmake_test
run: |
+1 -38
View File
@@ -46,55 +46,18 @@ env:
jobs:
cpu-x64-high-perf:
runs-on: hf-jobs-cpu-upgrade:ubuntu26_04
# note: this runner has 8 CPUs, but std::thread::hardware_concurrency reports the cores of the
# host (https://github.com/ggml-org/llama.cpp/pull/31439#issuecomment-6683386059)
env:
TEST_ARG_HARDWARE_CONCURRENCY: 8
TEST_ARG_PARALLEL_WORKERS: 4
runs-on: [self-hosted, Linux, X64, CPU]
steps:
- name: Clone
id: checkout
uses: actions/checkout@v6
- name: Dependencies
id: depends
run: |
sudo apt update
sudo apt install -y build-essential cmake libssl-dev time unzip wget python3 python3-venv python3-dev
- name: ccache
uses: ggml-org/ccache-action@v1.2.24
with:
restore: false
save: false
- name: ccache-buckets-restore
uses: ./.github/actions/ccache-buckets
with:
key: self-hosted-cpu-x64-high-perf
folder: llama.cpp
hf_bucket: ggml-org/cache
- name: Test
id: ggml-ci
run: |
LLAMA_ARG_THREADS=$(nproc) GG_BUILD_HIGH_PERF=1 GG_BUILD_EXTRA_TESTS_0=1 bash ./ci/run.sh ~/results/llama.cpp/$RUNNER_NAME ~/mnt/llama.cpp
- 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: self-hosted-cpu-x64-high-perf
folder: llama.cpp
evict-old-files: 1d
hf_bucket: ggml-org/cache
save: true
cpu-arm64-high-perf-graviton4:
runs-on: ah-ubuntu_24_04-c8g_8x
+2 -2
View File
@@ -92,8 +92,8 @@ jobs:
{ "tag": "cpu", "dockerfile": ".devops/s390x.Dockerfile", "platforms": "linux/s390x", "full": true, "light": true, "server": true, "free_disk_space": false, "runs_on": "ubuntu-24.04-s390x", "prebuilt_ui": true },
{ "tag": "cuda cuda12", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "12.8.1", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04" },
{ "tag": "cuda cuda12", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "12.8.1", "platforms": "linux/arm64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04-arm" },
{ "tag": "cuda13", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "13.5.0", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04" },
{ "tag": "cuda13", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "13.5.0", "platforms": "linux/arm64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04-arm" },
{ "tag": "cuda13", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "13.4.1", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04" },
{ "tag": "cuda13", "dockerfile": ".devops/cuda.Dockerfile", "cuda_version": "13.4.1", "platforms": "linux/arm64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04-arm" },
{ "tag": "musa", "dockerfile": ".devops/musa.Dockerfile", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04" },
{ "tag": "intel", "dockerfile": ".devops/intel.Dockerfile", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": true, "runs_on": "ubuntu-24.04" },
{ "tag": "vulkan", "dockerfile": ".devops/vulkan.Dockerfile", "platforms": "linux/amd64", "full": true, "light": true, "server": true, "free_disk_space": false, "runs_on": "ubuntu-24.04" },
+2 -14
View File
@@ -11,8 +11,7 @@ on:
'tests/fusion/**',
'tests/test-fusion.cpp',
'tests/test-llama-archs.cpp',
'src/llama-graph*',
'src/llama-kv-cache*',
'src/llama-graph.cpp',
'src/llama-model*',
'src/models/**'
]
@@ -25,8 +24,7 @@ on:
'tests/fusion/**',
'tests/test-fusion.cpp',
'tests/test-llama-archs.cpp',
'src/llama-graph*',
'src/llama-kv-cache*',
'src/llama-graph.cpp',
'src/llama-model*',
'src/models/**'
]
@@ -147,11 +145,6 @@ jobs:
rm -rf build-ci-models && mkdir -p build-ci-models
./build/bin/test-llama-archs -o build-ci-models
- name: Test KV shift
id: test_kv_shift
run: |
./build/bin/test-llama-archs --kv-shift
- name: Test fusion
id: test_fusion
run: |
@@ -300,11 +293,6 @@ jobs:
# run: |
# ./build/bin/test-fusion --models build-ci-models --device Vulkan0 --check tests/fusion/Vulkan.csv
- name: Test KV shift
id: test_kv_shift
run: |
./build/bin/test-llama-archs --kv-shift
- name: Test archs
id: test_archs
run: |
+4 -4
View File
@@ -177,8 +177,8 @@ jobs:
- [Ubuntu x64 (Vulkan)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-x64.tar.gz)
- [Ubuntu arm64 (Vulkan)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-vulkan-arm64.tar.gz)
- [Ubuntu x64 (CUDA 12)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-12.8-x64.tar.gz) - [CUDA 12.8 libraries](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-12.8-x64.tar.gz)
- [Ubuntu x64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.5-x64.tar.gz) - [CUDA 13.5 libraries](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.5-x64.tar.gz)
- [Ubuntu arm64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.5-arm64.tar.gz) - [CUDA 13.5 libraries](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.5-arm64.tar.gz)
- [Ubuntu x64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.4-x64.tar.gz) - [CUDA 13.4 libraries](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.4-x64.tar.gz)
- [Ubuntu arm64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.4-arm64.tar.gz) - [CUDA 13.4 libraries](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-${{ steps.tag.outputs.name }}-bin-ubuntu-cuda-13.4-arm64.tar.gz)
- [Ubuntu x64 (ROCm 10.0)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-10.0-x64.tar.gz)
- [Ubuntu x64 (OpenVINO)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-2026.4.1-x64.tar.gz)
- [Ubuntu x64 (SYCL FP32)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-fp32-x64.tar.gz)
@@ -194,8 +194,8 @@ jobs:
- [Windows arm64 (CPU)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cpu-arm64.zip)
- [Windows arm64 (OpenCL Adreno)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-opencl-adreno-arm64.zip)
- [Windows x64 (CUDA 12)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cuda-12.4-x64.zip) - [CUDA 12.4 DLLs](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-bin-win-cuda-12.4-x64.zip)
- [Windows x64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cuda-13.5-x64.zip) - [CUDA 13.5 DLLs](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-bin-win-cuda-13.5-x64.zip)
- [Windows arm64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cuda-13.5-arm64.zip) - [CUDA 13.5 DLLs](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-bin-win-cuda-13.5-arm64.zip)
- [Windows x64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cuda-13.4-x64.zip) - [CUDA 13.4 DLLs](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-bin-win-cuda-13.4-x64.zip)
- [Windows arm64 (CUDA 13)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-cuda-13.4-arm64.zip) - [CUDA 13.4 DLLs](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/cudart-llama-bin-win-cuda-13.4-arm64.zip)
- [Windows x64 (Vulkan)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-vulkan-x64.zip)
- [Windows arm64 (Vulkan)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-vulkan-arm64.zip)
- [Windows x64 (OpenVINO)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-win-openvino-2026.4.1-x64.zip)
+10 -10
View File
@@ -345,14 +345,14 @@ jobs:
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- build: 'x64'
os: ubuntu-24.04
cuda: '13.5.0'
label: '13.5'
defines: ''
cuda: '13.4.1'
label: '13.4'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- build: 'arm64'
os: ubuntu-24.04-arm
cuda: '13.5.0'
label: '13.5'
defines: ''
cuda: '13.4.1'
label: '13.4'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
runs-on: ${{ matrix.os }}
container: nvidia/cuda:${{ matrix.cuda }}-devel-ubuntu24.04
@@ -1211,12 +1211,12 @@ jobs:
- cuda: '12.4'
arch: x64
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- cuda: '13.5'
- cuda: '13.4'
arch: x64
defines: ''
- cuda: '13.5'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3'
- cuda: '13.4'
arch: arm64
defines: '-DCMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-msvc-cuda.cmake'
defines: '-DGGML_CUDA_CCCL_VERSION=v3.4.3 -DCMAKE_TOOLCHAIN_FILE=cmake/arm64-windows-msvc-cuda.cmake'
steps:
- name: Clone
+1 -26
View File
@@ -1,4 +1,4 @@
# date: Wed Nov 4 12:03:31 EET 2026
# date: Wed Oct 28 12:38:26 EET 2026
# this file is auto-generated by scripts/gen-authors.sh
Нияз Гарифзянов <112617865+garrnizon@users.noreply.github.com>
@@ -48,7 +48,6 @@ Abheek Gulati <abheekg@hotmail.com>
abhijain1204fujitsu <139222713+abhijain1204fujitsu@users.noreply.github.com>
Abhijit Ramesh <abhijitramesh2k@gmail.com>
abhijitb11 <113058133+abhijitb11@users.noreply.github.com>
Abhijith Jajam <129675815+jajamabhijith@users.noreply.github.com>
Abhilash Majumder <30946547+abhilash1910@users.noreply.github.com>
Abhinay Krishna <abhinaykrishna60@gmail.com>
Abhiram <78226909+geckguy@users.noreply.github.com>
@@ -204,17 +203,14 @@ Ankit Khandelwal <ankk98@gmail.com>
Ankur Verma <31362771+ankurvdev@users.noreply.github.com>
anon998 <131767832+anon998@users.noreply.github.com>
Anri Lombard <anri.m.lombard@gmail.com>
Anthony Maltsev <77708730+anthonymaltsev@users.noreply.github.com>
Anthony Umfer <aumfer@gmail.com>
Anthony Van de Gejuchte <anthonyvdgent@gmail.com>
Anthony Wang <anthonywang903@gmail.com>
antichristHater <142441588+antichristHater@users.noreply.github.com>
Antoine Viallon <antoine@lesviallon.fr>
Anton I <inflyer@hotmail.com>
Anton Mitkov <anton_b_mitkov@abv.bg>
Anton Mitkov <anton.mitkov@codeplay.com>
Antonis Makropoulos <benuix@gmail.com>
anubhavfujitsu <Anubhav.Srivastava@fujitsu.com>
Anudit Nagar <nagaranudit@gmail.com>
Anuj Attri <anujattri01@gmail.com>
anujj <ajalota@nvidia.com>
@@ -297,7 +293,6 @@ Bernat Vadell <hounter.caza@gmail.com>
Bernhard M. Wiedemann <githubbmwprimary@lsmod.de>
Bert Wagner <github@bertwagner.com>
Bertay Eren <39909689+bertaye@users.noreply.github.com>
Bhargav Krish <bkrishnamurt@nvidia.com>
Bhavik Sharda <10757940+BLSharda@users.noreply.github.com>
bhubbb <79117352+bhubbb@users.noreply.github.com>
Bill Sideris <bill88t@feline.gr>
@@ -538,7 +533,6 @@ Don Mahurin <dmahurin@users.noreply.github.com>
Dong Won Kim <63934649+ddwkim@users.noreply.github.com>
Donghyeon Jeong <54725479+djeong20@users.noreply.github.com>
Dongliang Wei <121270393+wdl339@users.noreply.github.com>
Dongsheng Luo <luods1995@outlook.com>
Doomsdayrs <38189170+Doomsdayrs@users.noreply.github.com>
DooWoong Lee (David) <manics99@naver.com>
DorianRudolph <dorianrudo97@googlemail.com>
@@ -618,7 +612,6 @@ Faez Shakil <faez.shakil@gmail.com>
fairydreaming <166155368+fairydreaming@users.noreply.github.com>
Faisal Zaghloul <faisal.zaghloul@gmail.com>
Faisal Zaghloul <quic_fzaghlou@quicinc.com>
FamousMonsterr <famousmonster@ya.ru>
Fan Shupei <dymarkfan@outlook.com>
FantasyGmm <16450052+FantasyGmm@users.noreply.github.com>
fanyang <fanyang89@outlook.com>
@@ -661,7 +654,6 @@ Frederik Vogel <Schaltfehler@users.noreply.github.com>
Fredrik Hultin <noname@nurd.se>
fredzillman <fzillman@gmail.com>
frob <rick+github@frob.com.au>
frogscooper <frogscooper@gmail.com>
Frost-54 <91168148+Frost-54@users.noreply.github.com>
Frosty40 <newjordan@gmail.com>
Funtowicz Morgan <mfuntowicz@users.noreply.github.com>
@@ -702,7 +694,6 @@ Gilad S. <7817232+giladgd@users.noreply.github.com>
Giovanni Rivera <rivera.giovanni271@gmail.com>
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
GittyBurstein <g0534163997@gmail.com>
Giulianno <lernexbusiness@gmail.com>
Giuseppe Scrivano <giuseppe@scrivano.org>
Giuseppe Scrivano <gscrivan@redhat.com>
GiviMAD <GiviMAD@users.noreply.github.com>
@@ -771,7 +762,6 @@ Henry147147 <44851451+Henry147147@users.noreply.github.com>
Herman Semenoff <GermanAizek@yandex.ru>
Herman Semenov <GermanAizek@yandex.ru>
Hesen Peng <hesen.peng@gmail.com>
hesiru <siruhe666@gmail.com>
hey-gm <heitorgm@outlook.com>
HighDoping <highdoping@gmail.com>
HimariO <dsfhe49854@gmail.com>
@@ -929,7 +919,6 @@ Jesse Ikonen <jesse.ikonen@gmail.com>
Jesse Jojo Johnson <williamsaintgeorge@gmail.com>
Jesse LaRose <jesse@taey.ai>
Jesse Posner <jesse.posner@gmail.com>
Jesse Symons <155509533+Torchit1@users.noreply.github.com>
Jesus Gulfo <jesdga95@gmail.com>
Jesus Talavera <145992175+jesus-talavera-ibm@users.noreply.github.com>
Jetson Tan <tanzongyouyi@outlook.com>
@@ -1082,7 +1071,6 @@ Kevin Liu <4396kevinliu@gmail.com>
Kevin Lo <kevlo@kevlo.org>
Kevin Pouget <kpouget@redhat.com>
Kevin Wang <kevmo314@gmail.com>
Keyu Zhu <inuebisu@outlook.com>
Khashayar Ghafouri <43180261+khashayarghafouri@users.noreply.github.com>
khimaros <me@khimaros.com>
Kilian Hu <90606809+kilian-hu@users.noreply.github.com>
@@ -1090,7 +1078,6 @@ Kilian Krampf <kilian@krampf.de>
kiltyj <kiltyj@gmail.com>
Kim S. <polydecay@users.noreply.github.com>
kimminsu <80271594+kimminsu38oo@users.noreply.github.com>
Kirill A. Korinsky <kirill@korins.ky>
KITAITI Makoto <KitaitiMakoto@gmail.com>
kiwi <122582483+kiwi142857@users.noreply.github.com>
klosax <131523366+klosax@users.noreply.github.com>
@@ -1149,7 +1136,6 @@ leejet <leejet714@gmail.com>
Leng Yue <lengyue@lengyue.me>
Lennart Austenfeld <53152202+l-austenfeld@users.noreply.github.com>
leo-pony <nengjunma@outlook.com>
Leon <118652405+LeON-Nie-code@users.noreply.github.com>
Leon Knauer <git@leonknauer.com>
Leonard Mosescu <tlemo@users.noreply.github.com>
leonardHONG <2695316095@qq.com>
@@ -1160,10 +1146,8 @@ lexasub <lexakopp2212@gmail.com>
lgai-exaone <exaonemodels@lgresearch.ai>
lhez <lih@qti.qualcomm.com>
lhez <quic_lih@quicinc.com>
Li Chuyang <1418587903@qq.com>
Li Pengzhan <151381994+Lpzhan931@users.noreply.github.com>
Li Tan <tanliboy@gmail.com>
li-lizhe <82501526+li-lizhe@users.noreply.github.com>
liangliangchang <lichang@amd.com>
liminfei-amd <91481003+liminfei-amd@users.noreply.github.com>
limitedAtonement <limitedAtonement@users.noreply.github.com>
@@ -1182,7 +1166,6 @@ LoganDark <github@logandark.mozmail.com>
Loïc Carrère <loic.carrere@gmail.com>
lon <114724657+longregen@users.noreply.github.com>
loonerin <132926317+loonerin@users.noreply.github.com>
Lorenzo Bona <lorenzobona@tuta.com>
losewayy <1134285612@qq.com>
LostRuins <39025047+LostRuins@users.noreply.github.com>
LostRuins Concedo <39025047+LostRuins@users.noreply.github.com>
@@ -1270,7 +1253,6 @@ Masaya, Kato <62578291+msy-kato@users.noreply.github.com>
mashdragon <122402293+mashdragon@users.noreply.github.com>
Mason Milburn <masonmilby@gmail.com>
MasterYi1024 <39848311+MasterYi1024@users.noreply.github.com>
Maternion <98753158+maternion@users.noreply.github.com>
Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com>
Matheus C. França <matheus-catarino@hotmail.com>
Matheus Gabriel Alves Silva <matheusgasource@gmail.com>
@@ -1368,7 +1350,6 @@ Miwa / Ensan <63481257+ensan-hcl@users.noreply.github.com>
miyan <1138989048@qq.com>
mj-shifu <77107165+mj-shifu@users.noreply.github.com>
mkoker <132301062+mkoker@users.noreply.github.com>
mmalpe-nv <mmalpe@nvidia.com>
mmyjona <jonathan.gonse@gmail.com>
mnehete32 <33429707+mnehete32@users.noreply.github.com>
Mohamed Elashri <git@elashri.com>
@@ -1387,7 +1368,6 @@ mtmcp <141645996+mtmcp@users.noreply.github.com>
muggle-stack <promuggle@qq.com>
Muhammad Saad <saadydev@gmail.com>
Muhammad Salem <salem.ebo@gmail.com>
MuoDoo <36472129+MuoDoo@users.noreply.github.com>
Murilo Santana <mvrilo@gmail.com>
Musab Gultekin <musabgultekin@users.noreply.github.com>
musoles <135031143+musoles@users.noreply.github.com>
@@ -1436,12 +1416,10 @@ Nicolas Budyn <16596345+nbud@users.noreply.github.com>
Nicolas Mowen <nickmowen213@gmail.com>
Nicolás Pérez <nicolas_perez@brown.edu>
Nicolò Scipione <nicolo.scipione@codeplay.com>
Nidhi <nidhisharma16635@gmail.com>
Nigel Bosch <pnigelb@gmail.com>
Nigel Jones <nigel.l.jones+git@gmail.com>
Nik Bogatyrev <50734723+notforu777@users.noreply.github.com>
Nikhil Jain <nikhil.jain0987@gmail.com>
Nikhil Sharma <150424968+nikhilfujitsu@users.noreply.github.com>
Nikita Gordeev <iamfobey@gmail.com>
Nikita Sarychev <42014488+sARY77@users.noreply.github.com>
Niklas Korz <niklas@niklaskorz.de>
@@ -1614,7 +1592,6 @@ Raman Shinde <raman.shinde15@gmail.com>
Rand Xie <randxiexyy29@gmail.com>
Randall Fitzgerald <randall@dasaku.net>
Random Fly <renfei8@live.cn>
randomized <286843573@qq.com>
rankaiyx <rankaiyx@foxmail.com>
rankaiyx <rankaiyx@rankaiyx.com>
RapidMark <32768622+RapidMark@users.noreply.github.com>
@@ -2032,7 +2009,6 @@ Tungsten842 <quantmint@protonmail.com>
Tushar <ditsuke@protonmail.com>
tv1wnd <55383215+tv1wnd@users.noreply.github.com>
tyronecai <tyronecai@163.com>
Uber <20715993+YagUber@users.noreply.github.com>
ubergarm <leimgrub@gmail.com>
ubik2 <ubik2@users.noreply.github.com>
UEXTM.com <84163508+uextm@users.noreply.github.com>
@@ -2266,5 +2242,4 @@ Zijun Yu <zijun.yu@intel.com>
zql <37731799+zqlcode@users.noreply.github.com>
zrm <trustiosity.zrm@gmail.com>
Zsapi <martin1.zsapka@gmail.com>
zyang-dev <267119621+zyang-dev@users.noreply.github.com>
zzzzwc <tiddar@foxmail.com>
+1 -1
View File
@@ -4,7 +4,7 @@ include(CheckIncludeFileCXX)
### llama.cpp version
set(LLAMA_VERSION_MAJOR 0)
set(LLAMA_VERSION_MINOR 9)
set(LLAMA_VERSION_MINOR 8)
set(LLAMA_VERSION_PATCH 0)
set(LLAMA_VERSION_BASE "${LLAMA_VERSION_MAJOR}.${LLAMA_VERSION_MINOR}.${LLAMA_VERSION_PATCH}")
+26 -2
View File
@@ -296,6 +296,25 @@ function gg_run_test_llama_archs_tensor_split {
set +e
}
# test_llama_archs_models
function gg_run_test_llama_archs_models {
cd ${SRC}
set -e
# TODO: fix and re-enable `test-llama-archs` on OpenVINO
# TODO: the `test-llama-archs` currently does not build on Windows, so we check if the binary exists
if [ -z ${GG_BUILD_OPENVINO} ] && [ -f ./build-ci-release/bin/test-llama-archs ]; then
rm -rf build-ci-models && mkdir -p build-ci-models
# generate the dummy models used by the model-dependent tests
./build-ci-release/bin/test-llama-archs -o build-ci-models 2>&1
fi
set +e
}
# test_scripts
function gg_run_test_scripts {
@@ -437,8 +456,12 @@ function gg_run_qwen3_0_6b {
(time ./bin/llama-imatrix --model ${model_f16} -f ${wiki_test} -ngl 99 -c 1024 -b 512 --chunks 2 ) 2>&1 | tee -a $OUT/${ci}-imatrix.log
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa off ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
# TODO: these tests are extremely slow on WebGPU - re-enable when fixed
# https://github.com/ggml-org/llama.cpp/actions/runs/39639985732
if [ -z ${GG_BUILD_WEBGPU} ]; then
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa off ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
fi
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
@@ -678,6 +701,7 @@ test $ret -eq 0 && gg_run ctest_release
test $ret -eq 0 && gg_run test_backend_ops
test $ret -eq 0 && gg_run test_llama_archs_models
test $ret -eq 0 && gg_run test_llama_archs_tensor_split
if [ -z ${GG_BUILD_LOW_PERF} ]; then
+1 -1
View File
@@ -13,7 +13,7 @@ if(WIN32)
endif()
enable_language(RC)
set(LLAMA_WIN_APP_MANIFEST_RC "${CMAKE_CURRENT_BINARY_DIR}/win-app-manifest.rc")
file(CONFIGURE OUTPUT "${LLAMA_WIN_APP_MANIFEST_RC}" CONTENT "1 24 \"@LLAMA_WIN_APP_MANIFEST@\"\n" @ONLY)
configure_file("${CMAKE_CURRENT_LIST_DIR}/win-app.rc.in" "${LLAMA_WIN_APP_MANIFEST_RC}" @ONLY)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${LLAMA_WIN_APP_MANIFEST}")
endif()
endif()
+3
View File
@@ -0,0 +1,3 @@
// generated from cmake/win-app.rc.in - do not edit
// RT_MANIFEST (24), ID 1: the application manifest shared by all llama.cpp executables
1 24 "@LLAMA_WIN_APP_MANIFEST@"
+3 -23
View File
@@ -4,7 +4,6 @@
#include "chat.h"
#include "common.h"
#include "download.h"
#include "http.h"
#include "json-schema-to-grammar.h"
#include "json.h"
#include "llama.h"
@@ -400,26 +399,7 @@ bool common_models_handler_is_preset_repo(const common_models_handler & handler)
return !handler.plan.preset.url.empty();
}
static common_download_opts download_opts_for_url(const common_download_opts & opts, const std::string & url) {
common_download_opts result = opts;
try {
const auto hf = common_http_parse_url(common_get_model_endpoint());
const auto parts = common_http_parse_url(url);
if (parts.scheme == hf.scheme &&
parts.host == hf.host &&
parts.port == hf.port &&
string_starts_with(result.bearer_token, "hf_")) {
return result;
}
} catch (const std::exception &) {
}
result.bearer_token.clear();
return result;
}
static std::vector<common_download_task> build_url_tasks(const common_params_model & model, common_download_opts opts) {
opts = download_opts_for_url(opts, model.url);
auto parts = common_download_get_all_parts(model.url);
std::vector<common_download_task> tasks;
@@ -508,7 +488,7 @@ void common_models_handler_apply(common_models_handler & handler, common_params
common_download_task task;
task.url = params.mmproj.url;
task.local_path = params.mmproj.path;
task.opts = download_opts_for_url(opts, task.url);
task.opts = opts;
tasks.push_back(task);
}
bool had_spec_url = false;
@@ -516,7 +496,7 @@ void common_models_handler_apply(common_models_handler & handler, common_params
common_download_task task;
task.url = params.speculative.draft.mparams.url;
task.local_path = params.speculative.draft.mparams.path;
task.opts = download_opts_for_url(opts, task.url);
task.opts = opts;
tasks.push_back(task);
had_spec_url = true;
}
@@ -730,7 +710,7 @@ static void common_params_apply_system_config(common_params & params, llama_exam
std::vector<std::filesystem::path> paths;
#if defined(_WIN32)
const std::filesystem::path program_data = common_get_env("PROGRAMDATA");
const std::filesystem::path program_data = common_get_path_from_env("PROGRAMDATA");
if (!program_data.empty()) {
paths.push_back(program_data / "llama.cpp" / "config.ini");
}
+38 -13
View File
@@ -895,7 +895,27 @@ bool fs_validate_filename(const std::string & filename, bool allow_subdirs) {
return true;
}
#include <iostream>
#ifdef _WIN32
std::wstring utf8_to_wstring(const std::string & str) {
if (str.empty()) {
return std::wstring();
}
int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), NULL, 0);
if (size <= 0) {
return std::wstring();
}
std::wstring wstr(size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], size);
return wstr;
}
std::string wstring_to_utf8(const std::wstring & str) {
if (str.empty()) {
return std::string();
@@ -963,9 +983,20 @@ void common_set_env(const std::string & name, const std::string & value) {
#endif
}
std::filesystem::path common_get_path_from_env(const std::string & name) {
#if defined(_WIN32)
const std::wstring wname = utf8_to_wstring(name);
const wchar_t * wvalue = _wgetenv(wname.c_str());
return wvalue ? std::filesystem::path(wvalue) : std::filesystem::path();
#else
const char * value = std::getenv(name.c_str());
return value ? std::filesystem::path(value) : std::filesystem::path();
#endif
}
#if !defined(_WIN32)
static std::filesystem::path get_home_directory() {
std::filesystem::path home = common_get_env("HOME");
std::filesystem::path home = common_get_path_from_env("HOME");
if (!home.empty()) {
return home;
}
@@ -978,19 +1009,19 @@ static std::filesystem::path get_home_directory() {
#endif
std::filesystem::path fs_get_cache_directory() {
std::filesystem::path cache_directory = common_get_env("LLAMA_CACHE");
std::filesystem::path cache_directory = common_get_path_from_env("LLAMA_CACHE");
if (!cache_directory.empty()) {
return cache_directory;
}
#if defined(_WIN32)
cache_directory = common_get_env("LOCALAPPDATA");
cache_directory = common_get_path_from_env("LOCALAPPDATA");
if (cache_directory.empty()) {
throw std::runtime_error("Failed to find %LOCALAPPDATA% directory");
}
#elif defined(__APPLE__)
cache_directory = get_home_directory() / "Library/Caches";
#else
cache_directory = common_get_env("XDG_CACHE_HOME");
cache_directory = common_get_path_from_env("XDG_CACHE_HOME");
if (cache_directory.empty()) {
cache_directory = get_home_directory() / ".cache";
}
@@ -1001,12 +1032,12 @@ std::filesystem::path fs_get_cache_directory() {
std::filesystem::path fs_get_config_directory() {
std::filesystem::path config_directory;
#if defined(_WIN32)
config_directory = common_get_env("APPDATA");
config_directory = common_get_path_from_env("APPDATA");
if (config_directory.empty()) {
throw std::runtime_error("Failed to find %APPDATA% directory");
}
#else
config_directory = common_get_env("XDG_CONFIG_HOME");
config_directory = common_get_path_from_env("XDG_CONFIG_HOME");
if (config_directory.empty()) {
config_directory = get_home_directory() / ".config";
}
@@ -1022,7 +1053,7 @@ std::filesystem::path fs_get_cache_file(const std::string & filename) {
if (ec) {
throw std::runtime_error("failed to create cache directory: " + cache_directory.string());
}
return cache_directory / filename;
return cache_directory / std::filesystem::u8path(filename);
}
//
@@ -1313,12 +1344,6 @@ common_init_result::common_init_result(common_params & params, bool model_only)
LOG_INF("%s", "decision model reads the embeddings output, enabling embedding mode\n");
}
if (llama_model_classifier_free_guidance(model)) {
params.kv_unified = true;
cparams.kv_unified = true;
cparams.n_seq_max *= 2;
}
// embeddings need the whole batch in one ubatch, so n_batch must not be larger than n_ubatch
// (server.cpp does this check for --embedding, but before the model is loaded)
if (cparams.embeddings && cparams.n_batch > cparams.n_ubatch) {
+7 -17
View File
@@ -906,7 +906,8 @@ bool glob_match(const std::string & pattern, const std::string & str);
//
#ifdef _WIN32
std::string wstring_to_utf8(const std::wstring & str);
std::wstring utf8_to_wstring(const std::string & str);
std::string wstring_to_utf8(const std::wstring & str);
#endif
//
@@ -918,6 +919,9 @@ std::string wstring_to_utf8(const std::wstring & str);
std::string common_get_env(const std::string & name);
void common_set_env(const std::string & name, const std::string & value);
// reads a path from the environment, an unset variable gives an empty path
std::filesystem::path common_get_path_from_env(const std::string & name);
//
// Filesystem utils
//
@@ -1289,20 +1293,6 @@ enum ggml_opt_optimizer_type common_opt_get_optimizer(const char *);
// prompt utils
//
// leave default-inserted elements uninitialized; callers must fill them before use
template <typename T>
struct common_no_init_allocator : std::allocator<T> {
template <typename U> struct rebind { using other = common_no_init_allocator<U>; };
common_no_init_allocator() = default;
template <typename U> common_no_init_allocator(const common_no_init_allocator<U> &) {}
template <typename U> void construct(U * p) { ::new ((void *) p) U; }
};
// sequence state data, filled by llama_state_seq_get_data_ext()
using common_state_data = std::vector<uint8_t, common_no_init_allocator<uint8_t>>;
struct common_prompt_checkpoint {
int64_t n_tokens;
@@ -1312,8 +1302,8 @@ struct common_prompt_checkpoint {
llama_pos pos_min;
llama_pos pos_max;
common_state_data data_tgt;
common_state_data data_dft;
std::vector<uint8_t> data_tgt;
std::vector<uint8_t> data_dft;
// (optional) speculative-decoding implementation state stashed with the checkpoint
// (e.g. eagle3's deferred-boundary g_embd row)
+5 -4
View File
@@ -45,7 +45,7 @@ static const std::string PARTIAL_SUFFIX = ".downloadInProgress";
static void write_etag(const std::string & path, const std::string & etag) {
const std::string etag_path = path + ".etag";
fs_write_atomic(etag_path, etag);
fs_write_atomic(std::filesystem::u8path(etag_path), etag);
LOG_DBG("%s: file etag saved: %s\n", __func__, etag_path.c_str());
}
@@ -293,7 +293,8 @@ static int common_download_file_single_online(const std::string & url,
// alt_path is the HF snapshot: the blob may have been moved there when symlinks are unavailable
auto cache_hit = [&] {
return std::filesystem::exists(path) || (!alt_path.empty() && std::filesystem::exists(alt_path));
return std::filesystem::exists(std::filesystem::u8path(path)) ||
(!alt_path.empty() && std::filesystem::exists(std::filesystem::u8path(alt_path)));
};
if (skip_etag && cache_hit()) {
@@ -510,7 +511,7 @@ int common_download_file_single(const std::string & url,
return common_download_file_single_online(url, path, online_opts, skip_etag, lock_path, alt_path);
}
if (!std::filesystem::exists(path)) {
if (!std::filesystem::exists(std::filesystem::u8path(path))) {
LOG_ERR("%s: required file is not available in cache (offline mode): %s\n", __func__, path.c_str());
return -1;
}
@@ -1211,7 +1212,7 @@ bool common_download_remove_sidecars(const std::string & hf_repo_with_tag, const
for (const auto & s : sidecars) {
const auto & f = plan.*s.file;
if (!f.local_path.empty()) {
to_remove.emplace_back(f.local_path);
to_remove.emplace_back(std::filesystem::u8path(f.local_path));
}
}
if (to_remove.empty()) {
+8 -29
View File
@@ -61,7 +61,6 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
const llama_context_params * cparams,
std::vector<ggml_backend_dev_t> & devs,
uint32_t & hp_ngl,
uint32_t & hp_ngl_skip,
uint32_t & hp_n_ctx_train,
uint32_t & hp_n_expert,
ggml_log_level log_level) {
@@ -153,8 +152,6 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
if (mparams->load_mtp) {
hp_ngl += llama_model_n_layer_nextn(model);
}
// without MTP the NextN layers hold no weights but the model loader still counts them for n_gpu_layers
hp_ngl_skip = mparams->load_mtp ? 0 : llama_model_n_layer_nextn(model);
hp_n_ctx_train = llama_model_n_ctx_train(model);
hp_n_expert = llama_model_n_expert(model);
@@ -243,12 +240,11 @@ common_device_memory_data_vec common_get_device_memory_data(
const llama_context_params * cparams,
std::vector<ggml_backend_dev_t> & devs,
uint32_t & hp_ngl,
uint32_t & hp_ngl_skip,
uint32_t & hp_n_ctx_train,
uint32_t & hp_n_expert,
ggml_log_level log_level) {
std::vector<llama_device_memory_data> impl = common_get_device_memory_data_impl(
path_model, mparams, cparams, devs, hp_ngl, hp_ngl_skip, hp_n_ctx_train, hp_n_expert, log_level);
path_model, mparams, cparams, devs, hp_ngl, hp_n_ctx_train, hp_n_expert, log_level);
common_device_memory_data_vec ret(impl.size());
for (size_t i = 0; i < impl.size(); i++) {
@@ -274,7 +270,6 @@ static void common_params_fit_impl(
std::vector<ggml_backend_dev_t> devs;
uint32_t hp_ngl = 0; // hparams.n_gpu_layers
uint32_t hp_nsk = 0; // number of skipped NextN layers, counted by the model loader but not by hp_ngl
uint32_t hp_nct = 0; // hparams.n_ctx_train
uint32_t hp_nex = 0; // hparams.n_expert
@@ -296,7 +291,6 @@ static void common_params_fit_impl(
if (dmds_extra.empty() || n_ctx_extra != cparams->n_ctx) {
std::vector<ggml_backend_dev_t> devs_extra;
uint32_t ngl_extra = 0;
uint32_t nsk_extra = 0;
uint32_t nct_extra = 0;
uint32_t nex_extra = 0;
@@ -308,7 +302,7 @@ static void common_params_fit_impl(
dmds_t measured;
try {
measured = common_get_device_memory_data_impl(
extra->path_model, extra->mparams, extra->cparams, devs_extra, ngl_extra, nsk_extra, nct_extra, nex_extra, log_level);
extra->path_model, extra->mparams, extra->cparams, devs_extra, ngl_extra, nct_extra, nex_extra, log_level);
} catch (const std::runtime_error & e) {
// the extra model is optional, fit the main model alone rather than giving up
LOG_WRN("%s: failed to measure the memory of the extra model, fitting without it: %s\n", __func__, e.what());
@@ -348,7 +342,7 @@ static void common_params_fit_impl(
// step 1: get data for default parameters and check whether any changes are necessary in the first place
LOG_TRC("%s: getting device memory data for initial parameters:\n", __func__);
dmds_t dmds_full = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, log_level);
dmds_t dmds_full = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nct, hp_nex, log_level);
// saturate instead of overflowing, this also preserves the UINT32_MAX sentinel of n_ctx_min:
const uint32_t n_ctx_max = (uint32_t) std::min<uint64_t>(uint64_t(hp_nct) * n_streams, UINT32_MAX);
@@ -360,7 +354,7 @@ static void common_params_fit_impl(
if (n_streams > 1) {
LOG_TRC("%s: context size unset and KV cache not unified -> using %" PRIu32 " for %" PRIu32 " sequences:\n",
__func__, n_ctx_max, n_streams);
dmds_full = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, log_level);
dmds_full = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nct, hp_nex, log_level);
}
}
add_extra_memory(dmds_full);
@@ -499,7 +493,7 @@ static void common_params_fit_impl(
int64_t sum_projected_used_min_ctx = 0;
cparams->n_ctx = n_ctx_min_total;
dmds_t dmds_min_ctx = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, log_level);
dmds_t dmds_min_ctx = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nct, hp_nex, log_level);
add_extra_memory(dmds_min_ctx);
if (nd == 0) {
sum_projected_used_min_ctx = dmds_min_ctx.back().mb.total();
@@ -646,20 +640,6 @@ static void common_params_fit_impl(
assert(uint32_t(mparams.n_gpu_layers) <= hp_ngl + 1);
uint32_t il0 = hp_ngl + 1 - mparams.n_gpu_layers; // start index for tensor buft overrides
// the skipped NextN layers come right before the output layer, put them on the same device as the output layer
// so that the layers assigned by the model loader match the layers above
if (hp_nsk > 0 && mparams.n_gpu_layers > 0) {
mparams.n_gpu_layers += hp_nsk;
if (nd > 1) {
// the last devices have no layers if not even the output layer fits
size_t id_last = nd - 1;
while (ngl_per_device[id_last].n_layer == 0) {
id_last--;
}
tensor_split[id_last] += hp_nsk;
}
}
mparams.tensor_split = tensor_split;
size_t itbo = 0;
@@ -695,7 +675,7 @@ static void common_params_fit_impl(
set_ngl_tensor_split_tbo(ngl_per_device, overflow_bufts, mparams_copy);
dmds_t dmd_nl = common_get_device_memory_data_impl(
path_model, &mparams_copy, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, log_level);
path_model, &mparams_copy, cparams, devs, hp_ngl, hp_nct, hp_nex, log_level);
add_extra_memory(dmd_nl);
LOG_TRC("%s: memory for test allocation by device:\n", func_name);
@@ -724,7 +704,7 @@ static void common_params_fit_impl(
LOG_TRC("%s: getting device memory data with all MoE tensors moved to system memory:\n", __func__);
dmds_t dmds_cpu_moe = common_get_device_memory_data_impl(
path_model, mparams, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, log_level);
path_model, mparams, cparams, devs, hp_ngl, hp_nct, hp_nex, log_level);
add_extra_memory(dmds_cpu_moe);
for (size_t id = 0; id < nd; id++) {
@@ -1205,11 +1185,10 @@ void common_fit_print(
llama_context_params * cparams) {
std::vector<ggml_backend_dev_t> devs;
uint32_t hp_ngl = 0; // hparams.n_gpu_layers
uint32_t hp_nsk = 0; // number of skipped NextN layers, counted by the model loader but not by hp_ngl
uint32_t hp_nct = 0; // hparams.n_ctx_train
uint32_t hp_nex = 0; // hparams.n_expert
auto dmd = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nsk, hp_nct, hp_nex, GGML_LOG_LEVEL_ERROR);
auto dmd = common_get_device_memory_data_impl(path_model, mparams, cparams, devs, hp_ngl, hp_nct, hp_nex, GGML_LOG_LEVEL_ERROR);
GGML_ASSERT(dmd.size() == devs.size() + 1);
for (size_t id = 0; id < devs.size(); id++) {
-1
View File
@@ -104,7 +104,6 @@ common_device_memory_data_vec common_get_device_memory_data(
const llama_context_params * cparams,
std::vector<ggml_backend_dev_t> & devs,
uint32_t & hp_ngl,
uint32_t & hp_ngl_skip,
uint32_t & hp_n_ctx_train,
uint32_t & hp_n_expert,
ggml_log_level log_level);
+7 -6
View File
@@ -45,7 +45,7 @@ static fs::path get_cache_directory() {
{HOME_DIR, fs::path(".cache") / "huggingface" / "hub"}
};
for (const auto & entry : entries) {
if (fs::path base = common_get_env(entry.var); !base.empty()) {
if (fs::path base = common_get_path_from_env(entry.var); !base.empty()) {
return entry.path.empty() ? base : base / entry.path;
}
}
@@ -242,7 +242,7 @@ static std::string get_repo_commit(const std::string & repo_id,
LOG_WRN("%s: skip invalid commit: %s\n", __func__, _commit.c_str());
continue;
}
const fs::path candidate = _name;
const fs::path candidate = fs::u8path(_name);
if (!is_valid_subpath(refs_path, candidate)) {
LOG_WRN("%s: skip invalid branch: %s\n", __func__, _name.c_str());
@@ -317,7 +317,7 @@ hf_files get_repo_files(const std::string & repo_id,
file.repo_id = repo_id;
file.path = item["path"].get<std::string>();
const fs::path subpath = file.path;
const fs::path subpath = fs::u8path(file.path);
if (!is_valid_subpath(commit_path, subpath)) {
LOG_WRN("%s: skip invalid path: %s\n", __func__, file.path.c_str());
@@ -434,7 +434,8 @@ hf_files get_cached_files(const std::string & repo_id) {
if (!path.empty()) {
hf_file file;
file.repo_id = _repo_id;
file.path = path.generic_string();
const auto generic_path = path.generic_u8string();
file.path = std::string(generic_path.begin(), generic_path.end());
file.local_path = entry.path().string();
file.final_path = file.local_path;
files.push_back(std::move(file));
@@ -449,8 +450,8 @@ std::string finalize_file(const hf_file & file, const std::function<bool()> & ke
static std::atomic<bool> symlinks_disabled{false};
std::error_code ec;
fs::path local_path = file.local_path;
fs::path final_path = file.final_path;
fs::path local_path = fs::u8path(file.local_path);
fs::path final_path = fs::u8path(file.final_path);
if (local_path == final_path || fs::exists(final_path, ec)) {
return file.final_path;
+1 -1
View File
@@ -385,7 +385,7 @@ static bool is_draft_file(const std::string & fname) {
}
common_presets common_preset_context::load_from_models_dir(const std::string & models_dir) const {
const std::filesystem::path dir = models_dir;
const std::filesystem::path dir = std::filesystem::u8path(models_dir);
if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
throw std::runtime_error(string_format("error: '%s' does not exist or is not a directory\n", models_dir.c_str()));
}
-5
View File
@@ -20,7 +20,6 @@ TEXT_MODEL_MAP: dict[str, str] = {
"NEOChatModel": "sensenova_u1",
"AfmoeForCausalLM": "afmoe",
"LagunaForCausalLM": "laguna",
"RicursiveForCausalLM": "ricursive",
"FlintForCausalLM": "flint",
"ApertusForCausalLM": "llama",
"ArceeForCausalLM": "llama",
@@ -63,7 +62,6 @@ TEXT_MODEL_MAP: dict[str, str] = {
"DeepseekV2ForCausalLM": "deepseek",
"DeepseekV3ForCausalLM": "deepseek",
"DeepseekV32ForCausalLM": "deepseek",
"Xing4_0ForCausalLM": "xing",
"DFlashDraftModel": "qwen",
"DFlash2DraftModel": "qwen",
"LiLiCorrDraftModel": "nemotron",
@@ -97,7 +95,6 @@ TEXT_MODEL_MAP: dict[str, str] = {
"FalconForCausalLM": "falcon",
"FalconH1ForCausalLM": "falcon_h1",
"FalconMambaForCausalLM": "mamba",
"FishOmniModel": "drama3",
"GPT2LMHeadModel": "gpt2",
"GPTBigCodeForCausalLM": "starcoder",
"GPTNeoXForCausalLM": "gptneox",
@@ -158,7 +155,6 @@ TEXT_MODEL_MAP: dict[str, str] = {
"KORMoForCausalLM": "qwen",
"KimiK25ForConditionalGeneration": "deepseek",
"KimiK3ForConditionalGeneration": "kimi_k3",
"KimiK31ForConditionalGeneration": "kimi_k3",
"KimiLinearForCausalLM": "kimi_linear",
"KimiLinearModel": "kimi_linear",
"KimiVLForConditionalGeneration": "deepseek",
@@ -350,7 +346,6 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
"DotsOCRForCausalLM": "dotsocr",
"EmbeddingGemma2Model": "gemma",
"Exaone4_5_ForConditionalGeneration": "exaone",
"FishOmniModel": "drama3",
"Gemma3ForConditionalGeneration": "gemma",
"Gemma3nForConditionalGeneration": "gemma",
"Gemma4ForConditionalGeneration": "gemma",
+15 -28
View File
@@ -167,8 +167,6 @@ class ModelBase:
self._fusable_qkv_weight_layers: set[int] = set()
self._fusable_qkv_bias_layers: set[int] = set()
self.hparams = ModelBase.load_hparams(self.dir_model, self.is_mistral_format) if hparams is None else hparams
# source tensor name to filtered tensor name mapping.
self._source_tensor_names: dict[str, str | None] = {}
self.model_tensors = self.index_tensors(remote_hf_model_id=remote_hf_model_id)
self.metadata_override = metadata_override
self.model_name = model_name
@@ -231,9 +229,7 @@ class ModelBase:
remote_tensors = gguf.utility.SafetensorRemote.get_list_tensors_hf_model(remote_hf_model_id)
for name, remote_tensor in remote_tensors.items():
data_gen = lambda r=remote_tensor: LazyTorchTensor.from_remote_tensor(r) # noqa: E731
titem = self.filter_tensors((name, data_gen))
self._source_tensor_names[name] = titem[0] if titem else None
if titem:
if titem := self.filter_tensors((name, data_gen)):
tname, tgen = titem
tensors[tname] = tgen
@@ -298,9 +294,7 @@ class ModelBase:
data_gen = lambda data=data_torch: LazyTorchTensor.from_eager(data) # noqa: E731
else:
data_gen = lambda data=data_torch: data # noqa: E731
titem = self.filter_tensors((name, data_gen))
self._source_tensor_names[name] = titem[0] if titem else None
if titem:
if titem := self.filter_tensors((name, data_gen)):
tname, tgen = titem
tensors[tname] = tgen
@@ -500,7 +494,7 @@ class ModelBase:
s = self.model_tensors[name]
self.model_tensors[weight_name] = lambda w=w, s=s: dequant_bitnet(w(), s())
tensors_to_remove.append(name)
elif quant_method in ("fp8", "bsgemm-moe"): # bsgemm-moe: drama-3, same block layout
elif quant_method == "fp8":
block_size = quant_config.get("weight_block_size")
for name in self.model_tensors.keys():
if name.endswith("_scale_inv"):
@@ -1423,25 +1417,18 @@ class ModelBase:
# Per-tensor NVFP4 precision.
self._nvfp4_layer_algo = {}
if quant_layers:
# quantized_layers keys are module paths in the source checkpoint, not tensor names
# resolve each source tensor to the closest quantized_layers parent and record the algo under its filtered name for _tag_prec_a4
layer_algo = {k: v.get("quant_algo") for k, v in quant_layers.items() if isinstance(v, dict)}
matched: set[str] = set()
for src_name, filtered_name in self._source_tensor_names.items():
module = src_name
while module := module.rpartition(".")[0]:
if module in layer_algo:
break
if not module:
# store all possible module paths and assert if a quantized layer is not in the model
modules: set[str] = set()
for name in self.model_tensors:
while name := name.rpartition(".")[0]:
modules.add(name)
for layer_name, entry in quant_layers.items():
if not isinstance(entry, dict):
continue
matched.add(module)
# filter_tensors may drop a tensors so we need to check for that here.
if filtered_name is not None:
self._nvfp4_layer_algo[filtered_name.removesuffix(".weight").removesuffix(".bias")] = layer_algo[module]
unmatched = layer_algo.keys() - matched
assert not unmatched, f"quantized_layers entries not in the model tensors: {sorted(unmatched)}"
if titem := self.filter_tensors((layer_name, lambda: torch.empty(0))):
assert titem[0] in modules, f"quantized_layers entry {layer_name!r} is not in the model tensors"
self._nvfp4_layer_algo[titem[0]] = entry.get("quant_algo")
# NVFP4 weights are repacked and written directly to gguf_writer.
# This must run before dequant_model so NVFP4 tensors are removed
@@ -3404,7 +3391,7 @@ def get_model_architecture(hparams: dict[str, Any], model_type: ModelType) -> st
# Kimi-K3's text_config reports "KimiLinearForCausalLM", which is the older
# Kimi-Linear-48B architecture and cannot load K3 (no attention residuals,
# latent MoE, situ, ...). Route on the top-level architecture instead.
if model_type == ModelType.TEXT and arch in ("NEOChatModel", "StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration", "KimiK31ForConditionalGeneration", "Nemotron3_5TranscribeForConditionalGeneration"):
if model_type == ModelType.TEXT and arch in ("NEOChatModel", "StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration", "Nemotron3_5TranscribeForConditionalGeneration"):
return arch
# if "architectures" is found in the sub-config, use that instead
-2
View File
@@ -212,8 +212,6 @@ class ChatterboxTalkerModel(TextModel):
self.gguf_writer.add_sampling_temp(sampling["temp"])
self.gguf_writer.add_sampling_penalty_repeat(sampling["penalty_repeat"])
self.gguf_writer.add_sampling_penalty_last_n(PENALTY_LAST_N)
if not self.is_turbo:
self.gguf_writer.add_classifier_free_guidance(True)
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
if self.is_turbo:
-251
View File
@@ -1,251 +0,0 @@
from __future__ import annotations
from typing import Any, Callable, Iterable, TYPE_CHECKING
import torch
if TYPE_CHECKING:
from torch import Tensor
from .base import ModelBase, MmprojModel, gguf
from .qwen import Qwen3_5MoeTextModel
# Fish Audio Drama 3 (FishOmniModel)
# text model: Qwen3.5-MoE backbone, samples the main token of each frame
# mmproj: codebook tower and Fish DAC decoder (codec.pth)
_TOWER_SHARED = "audio_decoder_mtp_shared.layers."
_TOWER_RESIDUAL = "audio_decoder_mtp.layers."
@ModelBase.register("FishOmniModel")
@ModelBase.example("fishaudio/drama-3")
class Drama3TextModel(Qwen3_5MoeTextModel):
model_arch = gguf.MODEL_ARCH.QWEN35MOE
no_mtp = True # the checkpoint has no MTP head
@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
if item[0].startswith("audio_decoder"):
return None
return super().filter_tensors(item)
@ModelBase.register("FishOmniModel")
@ModelBase.example("fishaudio/drama-3")
class Drama3MmprojModel(MmprojModel):
has_vision_encoder = False
has_audio_encoder = False
_LAYER_MAP = {
"enorm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_ENORM, True),
"hnorm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_HNORM, True),
"eh_proj.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_EH_PROJ, False),
"semantic_embedding.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_BLK_EMBD, False),
"codebook_embedding.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_BLK_EMBD, False),
"final_layernorm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_BLK_OUT_NORM, True),
"transformer_layer.attention_norm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_NORM, True),
"transformer_layer.ffn_norm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_FFN_NORM, True),
"transformer_layer.attention.q_norm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_Q_NORM, True),
"transformer_layer.attention.k_norm.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_K_NORM, True),
"transformer_layer.attention.wo.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_OUT, False),
"transformer_layer.feed_forward.w1.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_FFN_GATE, False),
"transformer_layer.feed_forward.w3.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_FFN_UP, False),
"transformer_layer.feed_forward.w2.weight": (gguf.MODEL_TENSOR.A_GEN_CODE_FFN_DOWN, False),
}
_heads: dict[int, Tensor]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._heads = {}
def get_audio_config(self) -> dict[str, Any] | None:
cfg = dict(self.global_config["audio_decoder_config"])
cfg["num_hidden_layers"] = cfg["shared_enrichment_layers"] + cfg["num_codebooks"] - 1
return cfg
def set_gguf_parameters(self):
cfg = self.hparams
self.gguf_writer.add_file_type(self.ftype)
self.gguf_writer.add_clip_has_gen_audio_encoder(True)
self.gguf_writer.add_clip_gen_audio_projector_type(gguf.VisionProjectorType.DRAMA3_GEN)
self.gguf_writer.add_gen_audio_projection_dim(self.n_embd_text)
self.gguf_writer.add_gen_audio_embedding_length(cfg["dim"])
self.gguf_writer.add_gen_audio_feed_forward_length(cfg["intermediate_size"])
self.gguf_writer.add_gen_audio_block_count(cfg["num_hidden_layers"])
self.gguf_writer.add_gen_audio_head_count(cfg["n_head"])
self.gguf_writer.add_gen_audio_head_count_kv(cfg["n_local_heads"])
self.gguf_writer.add_gen_audio_attention_layernorm_eps(cfg["norm_eps"])
# other tower and codec hparams are hardcoded in clip.cpp
@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
name = item[0]
if name.startswith("audio_decoder") or name == "model.language_model.embed_tokens.weight":
return item
return None
def tensor_force_quant(self, name, new_name, bid, n_dims):
# conv1d/conv1d_dw kernels must be F16, ggml_conv_1d(_dw) has no BF16 path
if new_name.endswith(".weight") and (
new_name in ("a.gen.wav.dac.entry.weight", "a.gen.wav.dac.post_conv.weight")
or (".up.blk." in new_name and new_name.endswith(".dwconv.weight"))
or (".dac.blk." in new_name and (new_name.endswith(".conv1.weight") or new_name.endswith(".conv2.weight")))
):
return gguf.GGMLQuantizationType.F16
# keep ConvTranspose1d kernels and folded codebook tables in F32
if new_name.endswith(".conv.weight") and (".up.blk." in new_name or ".dac.blk." in new_name):
return gguf.GGMLQuantizationType.F32
if new_name.startswith("a.gen.wav.quant."):
return gguf.GGMLQuantizationType.F32
return super().tensor_force_quant(name, new_name, bid, n_dims)
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
T = gguf.MODEL_TENSOR
cfg = self.hparams
if name.startswith("a.gen.wav."):
yield (name, data_torch)
return
if name == "model.language_model.embed_tokens.weight":
# semantic band of the backbone embeddings
begin = self.global_config["semantic_start_token_id"]
yield (self.format_tensor_name(T.A_GEN_CODE_OUT_EMBD), data_torch[begin:begin + cfg["vocab_size"]])
return
if name == "audio_decoder.codebook_embeddings.weight":
n_cb = cfg["num_codebooks"]
yield (self.format_tensor_name(T.A_GEN_CODE_EMBD), data_torch.reshape(n_cb, -1, data_torch.shape[-1]))
return
if name.startswith("audio_decoder_mtp.heads."):
n_heads = cfg["num_codebooks"] - 1
self._heads[int(name.split(".")[2])] = data_torch
if len(self._heads) < n_heads:
return
yield (self.format_tensor_name(T.A_GEN_CODE_HEAD), torch.stack([self._heads.pop(i) for i in range(n_heads)]))
return
for prefix, offset in ((_TOWER_SHARED, 0), (_TOWER_RESIDUAL, cfg["shared_enrichment_layers"])):
if not name.startswith(prefix):
continue
idx, key = name[len(prefix):].split(".", 1)
il = int(idx) + offset
if key == "transformer_layer.attention.wqkv.weight":
# rows are [query | gate | key | value]
n_q = cfg["n_head"] * cfg["head_dim"]
n_kv = cfg["n_local_heads"] * cfg["head_dim"]
q, gate, k, v = data_torch.split([n_q, n_q, n_kv, n_kv], dim=0)
yield (self.format_tensor_name(T.A_GEN_CODE_ATTN_Q, il), q)
yield (self.format_tensor_name(T.A_GEN_CODE_ATTN_GATE, il), gate)
yield (self.format_tensor_name(T.A_GEN_CODE_ATTN_K, il), k)
yield (self.format_tensor_name(T.A_GEN_CODE_ATTN_V, il), v)
return
tensor, zero_centered = self._LAYER_MAP[key]
if zero_centered:
data_torch = data_torch.float() + 1
yield (self.format_tensor_name(tensor, il), data_torch)
return
raise ValueError(f"unexpected tensor {name}")
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
# only the DAC decoder, the encoder is for voice cloning (not supported yet)
sd = torch.load(self.dir_model / "codec.pth", map_location="cpu", mmap=True, weights_only=True)
sd = sd.get("state_dict", sd)
T = gguf.MODEL_TENSOR
def get(name: str) -> Tensor:
return sd[name].float()
def conv(prefix: str) -> Tensor:
# fold weight norm
if prefix + ".conv.weight" in sd:
return get(prefix + ".conv.weight")
g = get(prefix + ".conv.parametrizations.weight.original0")
v = get(prefix + ".conv.parametrizations.weight.original1")
return g * v / v.norm(dim=(1, 2), keepdim=True)
def quant_table(prefix: str) -> Tensor:
# fold out_proj into the codebook, so decoding is a row lookup
g = get(prefix + ".out_proj.weight_g")
v = get(prefix + ".out_proj.weight_v")
w = (g * v / v.norm(dim=(1, 2), keepdim=True)).squeeze(-1)
return get(prefix + ".codebook.weight") @ w.T + get(prefix + ".out_proj.bias")
def snake(alpha: Tensor) -> tuple[Tensor, Tensor]:
alpha = alpha.flatten()
return alpha, 1.0 / (alpha + 1e-9)
q = "quantizer."
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_FIRST_CB), quant_table(q + "semantic_quantizer.quantizers.0"))
n_rest = sum(1 for k in sd if k.startswith(q + "quantizer.quantizers.") and k.endswith(".codebook.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_REST_CB),
torch.stack([quant_table(f"{q}quantizer.quantizers.{i}") for i in range(n_rest)]))
# post_module uses interleaved RoPE, permute q/k rows so NEOX RoPE gives the same result
tfm = q + "post_module."
n_head, d_head = 16, 64
order = torch.cat([torch.arange(0, d_head, 2), torch.arange(1, d_head, 2)])
def neox_rows(w: Tensor) -> Tensor:
return w.reshape(n_head, d_head, -1)[:, order].reshape(w.shape)
n_tfm = sum(1 for k in sd if k.startswith(tfm + "layers.") and k.endswith(".ffn_norm.weight"))
for il in range(n_tfm):
p = f"{tfm}layers.{il}."
wq, wk, wv = get(p + "attention.wqkv.weight").chunk(3, dim=0)
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_Q, il), neox_rows(wq))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_K, il), neox_rows(wk))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_V, il), wv)
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_OUT, il), get(p + "attention.wo.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_NORM, il), get(p + "attention_norm.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_ATTN_SCALE, il), get(p + "attention_layer_scale.gamma"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_FFN_NORM, il), get(p + "ffn_norm.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_FFN_GATE, il), get(p + "feed_forward.w1.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_FFN_UP, il), get(p + "feed_forward.w3.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_FFN_DOWN, il), get(p + "feed_forward.w2.weight"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_FFN_SCALE, il), get(p + "ffn_layer_scale.gamma"))
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_OUTPUT_NORM), get(tfm + "norm.weight"))
# upsample: 2x (ConvTranspose1d + ConvNeXt)
for il in range(2):
p = f"{q}upsample.{il}."
yield (self.format_tensor_name(T.A_GEN_WAV_UP_CONV, il, suffix=".weight"), conv(p + "0"))
yield (self.format_tensor_name(T.A_GEN_WAV_UP_CONV, il, suffix=".bias"), get(p + "0.conv.bias"))
yield (self.format_tensor_name(T.A_GEN_WAV_UP_DWCONV, il, suffix=".weight"), conv(p + "1.dwconv"))
yield (self.format_tensor_name(T.A_GEN_WAV_UP_DWCONV, il, suffix=".bias"), get(p + "1.dwconv.conv.bias"))
for key, tensor in (("norm", T.A_GEN_WAV_UP_NORM), ("pwconv1", T.A_GEN_WAV_UP_PW1), ("pwconv2", T.A_GEN_WAV_UP_PW2)):
yield (self.format_tensor_name(tensor, il, suffix=".weight"), get(f"{p}1.{key}.weight"))
yield (self.format_tensor_name(tensor, il, suffix=".bias"), get(f"{p}1.{key}.bias"))
yield (self.format_tensor_name(T.A_GEN_WAV_UP_GAMMA, il, suffix=""), get(p + "1.gamma"))
# DAC: entry conv, 4x (Snake, ConvTranspose1d, 3 residual units), Snake, post conv
d = "decoder.model."
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_ENTRY, suffix=".weight"), conv(d + "0"))
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_ENTRY, suffix=".bias"), get(d + "0.conv.bias"))
for bid in range(4):
p = f"{d}{bid + 1}.block."
a, b = snake(get(p + "0.alpha"))
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_UP_SNAKE, bid, suffix=".alpha"), a)
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_UP_SNAKE, bid, suffix=".beta"), b)
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_UP_CONV, bid, suffix=".weight"), conv(p + "1"))
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_UP_CONV, bid, suffix=".bias"), get(p + "1.conv.bias"))
for xid in range(3):
r = f"{p}{xid + 2}.block."
for act, tensor in (("0", T.A_GEN_WAV_DAC_RES_ACT1), ("2", T.A_GEN_WAV_DAC_RES_ACT2)):
a, b = snake(get(r + act + ".alpha"))
name = gguf.TENSOR_NAMES[tensor].format(bid=bid, xid=xid)
yield (name + ".alpha", a)
yield (name + ".beta", b)
for cv, tensor in (("1", T.A_GEN_WAV_DAC_RES_CONV1), ("3", T.A_GEN_WAV_DAC_RES_CONV2)):
name = gguf.TENSOR_NAMES[tensor].format(bid=bid, xid=xid)
yield (name + ".weight", conv(r + cv))
yield (name + ".bias", get(r + cv + ".conv.bias"))
a, b = snake(get(d + "5.alpha"))
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_POST_SNAKE, suffix=".alpha"), a)
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_POST_SNAKE, suffix=".beta"), b)
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_POST_CONV, suffix=".weight"), conv(d + "6"))
yield (self.format_tensor_name(T.A_GEN_WAV_DAC_POST_CONV, suffix=".bias"), get(d + "6.conv.bias"))
-30
View File
@@ -343,33 +343,3 @@ class KimiK3Model(TextModel):
return
yield from super().modify_tensors(data_torch, name, bid)
@ModelBase.register("KimiK31ForConditionalGeneration")
@ModelBase.example("moonshotai/Kimi-K3.1-Pro")
class KimiK31Model(KimiK3Model):
"""
Kimi-K3.1: K3 with the full-attention MLA layers replaced by gated DSA.
The tensor set is K3's plus a lightning indexer per full-attention layer,
and `dsa_config` adds the attention.indexer.* metadata.
"""
model_arch = gguf.MODEL_ARCH.KIMI_K31
def set_gguf_parameters(self):
super().set_gguf_parameters()
# --- DSA indexer ---
dsa = self.hparams["dsa_config"]
dsa_layers = dsa["dsa_layers"]
# the layer lists are 1-indexed, like linear_attn_config's
assert sorted(dsa_layers) == sorted(self.hparams["linear_attn_config"]["full_attn_layers"]), \
"every full-attention layer is expected to own a DSA indexer"
self.gguf_writer.add_indexer_head_count(dsa["index_num_heads"])
self.gguf_writer.add_indexer_key_length(dsa["index_head_dim"])
self.gguf_writer.add_indexer_top_k(dsa["index_topk"])
self.gguf_writer.add_indexer_types([(il + 1) in dsa_layers
for il in range(self.hparams["num_hidden_layers"])])
# eps of the indexer key LayerNorm; the config declares no separate value
self.gguf_writer.add_layer_norm_eps(dsa.get("index_norm_eps", self.hparams["rms_norm_eps"]))
-92
View File
@@ -1,92 +0,0 @@
from __future__ import annotations
import math
from typing import Iterable, TYPE_CHECKING
if TYPE_CHECKING:
from torch import Tensor
from .base import ModelBase, TextModel, gguf, logger
@ModelBase.register("RicursiveForCausalLM")
@ModelBase.example("ricursiveAI/Ripple-65B-Preview")
class RicursiveModel(TextModel):
model_arch = gguf.MODEL_ARCH.RICURSIVE
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
hparams = self.hparams
if (act := hparams.get("hidden_act", "silu")) != "silu":
raise ValueError(f"Ricursive: unsupported hidden_act {act!r}")
# RicursiveConfig derives the layer pattern from layer_types alone, by default full attention iff i % 5 == 4
if (layer_types := hparams.get("layer_types")) is None:
layer_types = ["full_attention" if i % 5 == 4 else "sliding_attention" for i in range(self.block_count)]
if len(layer_types) != self.block_count:
raise ValueError(f"Ricursive: layer_types has {len(layer_types)} entries, expected {self.block_count}")
if unknown := set(layer_types) - {"sliding_attention", "full_attention"}:
raise ValueError(f"Ricursive: unsupported layer_types {sorted(unknown)}")
self.is_swa = [t == "sliding_attention" for t in layer_types]
# only sliding layers use RoPE, an all-full-attention model would have no positional encoding
if not any(self.is_swa):
raise ValueError("Ricursive: layer_types has no sliding_attention layer, all-full-attention models are not supported")
# RicursiveConfig pops a stored hybrid_layer_pattern and recomputes it from layer_types
if (pattern := hparams.get("hybrid_layer_pattern")) is not None and list(pattern) != [int(swa) for swa in self.is_swa]:
logger.warning("Ricursive: hybrid_layer_pattern does not match layer_types, ignoring it")
if not hparams.get("gate_full_precision", True):
logger.warning("Ricursive: gate_full_precision is false, llama.cpp computes the attention gate in f32")
# RicursiveConfig defaults; the reference ignores the tensors of a disabled feature
self.use_valres = hparams.get("use_valres", False)
self.use_gate = hparams.get("use_gated_attention", True)
names = list(self.model_tensors)
if bias := [name for name in names if name.endswith("bias")]:
raise ValueError(f"Ricursive: bias tensors are not supported, found {bias[0]!r}")
for enabled, suffix in ((self.use_valres, ".self_attn.value_res_lambda"), (self.use_gate, ".self_attn.g_proj.weight")):
if enabled and sum(name.endswith(suffix) for name in names) != self.block_count:
raise ValueError(f"Ricursive: expected one {suffix!r} tensor per layer")
def set_vocab(self):
self._set_vocab_gpt2()
# eos_token_id is [<|im_end|>, <|endoftext|>]: register the turn end as EOT so generation stops on it
eos = self.hparams.get("eos_token_id")
if isinstance(eos, list):
extra = [e for e in eos if e != self.hparams.get("bos_token_id")]
if extra:
self.gguf_writer.add_eot_token_id(extra[0])
logger.info(f"gguf: eot_token_id = {extra[0]}")
def set_gguf_parameters(self):
super().set_gguf_parameters()
hparams = self.hparams
self.gguf_writer.add_vocab_size(hparams["vocab_size"])
self.gguf_writer.add_rope_dimension_count(hparams["head_dim"])
self.gguf_writer.add_sliding_window(hparams["sliding_window"])
self.gguf_writer.add_sliding_window_pattern(self.is_swa)
# sliding layers use RoPE, full attention layers use no positional encoding
self.gguf_writer.add_rope_pattern(self.is_swa)
if (embed_scale := hparams.get("embed_scale")) is not None:
if embed_scale == 0:
raise ValueError(f"Ricursive: unsupported embed_scale {embed_scale!r}")
self.gguf_writer.add_embedding_scale(float(embed_scale))
# absent and 0 both mean no clamp (the reference tests `if clamp_threshold:`)
if (clamp := hparams.get("ffn_clamp_threshold")) is not None:
if clamp < 0 or not math.isfinite(clamp):
raise ValueError(f"Ricursive: unsupported ffn_clamp_threshold {clamp!r}")
self.gguf_writer.add_swiglu_clamp([float(clamp)] * self.block_count)
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
# value_res_lambda has no .weight suffix in the checkpoint
if name.endswith(".value_res_lambda"):
if not self.use_valres:
return
name += ".weight"
elif name.endswith(".g_proj.weight") and not self.use_gate:
return
yield from super().modify_tensors(data_torch, name, bid)
-133
View File
@@ -1,133 +0,0 @@
from __future__ import annotations
import re
from typing import Callable, Iterable, TYPE_CHECKING
if TYPE_CHECKING:
from torch import Tensor
from .base import ModelBase, gguf
from .deepseek import DeepseekV2Model
@ModelBase.register("Xing4_0ForCausalLM")
class Xing4_0Model(DeepseekV2Model):
"""Xing4_0 : DeepSeek-V2/V3 backbone (MLA + MoE) + mHC residual mixing."""
model_arch = gguf.MODEL_ARCH.XING4_0
skip_mtp = False
supports_mtp_export = True
_n_main_layers: int | None = None
# map (prefix, kind) -> MODEL_TENSOR enum
_hc_tensor_map = {
("hc_attn", "fn"): gguf.MODEL_TENSOR.HC_ATTN_FN,
("hc_attn", "base"): gguf.MODEL_TENSOR.HC_ATTN_BASE,
("hc_attn", "scale"): gguf.MODEL_TENSOR.HC_ATTN_SCALE,
("hc_ffn", "fn"): gguf.MODEL_TENSOR.HC_FFN_FN,
("hc_ffn", "base"): gguf.MODEL_TENSOR.HC_FFN_BASE,
("hc_ffn", "scale"): gguf.MODEL_TENSOR.HC_FFN_SCALE,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.block_count = self.hparams["num_hidden_layers"]
if not self.no_mtp:
self.block_count += self.hparams.get("num_nextn_predict_layers", 0)
self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
def index_tensors(self, remote_hf_model_id: str | None = None):
# needed by filter_tensors() before any tensor is filtered
type(self)._n_main_layers = self.hparams["num_hidden_layers"]
return super().index_tensors(remote_hf_model_id=remote_hf_model_id)
@classmethod
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
if (titem := super().filter_tensors(item)) is None:
return None
name, gen = titem
# the NextN/MTP block lives past num_hidden_layers (model.layers.40 -> blk.40)
assert cls._n_main_layers is not None
is_mtp = (m := re.match(r"model\.layers\.(\d+)\.", name)) is not None and int(m.group(1)) >= cls._n_main_layers
# --no-mtp: drop the appended NextN block entirely.
if is_mtp and cls.no_mtp:
return None
# --mtp: keep ONLY NextN-block tensors plus the shared embeddings/
# norm/lm_head (so the resulting GGUF carries just the draft head).
if cls.mtp_only and not is_mtp and name not in (
"model.embed_tokens.weight", "model.norm.weight", "lm_head.weight",
):
return None
return name, gen
def set_gguf_parameters(self):
rope_type = self.rope_parameters.get("rope_type") or self.rope_parameters.get("type")
if rope_type == "rope":
self.rope_parameters["rope_type"] = "yarn"
if "type" in self.rope_parameters:
self.rope_parameters["type"] = "yarn"
# base.py's YARN branch accesses this with a direct key lookup
self.rope_parameters.setdefault("original_max_position_embeddings",
self.hparams.get("original_max_position_embeddings", 4096))
super().set_gguf_parameters()
hparams = self.hparams
# NextN/MTP prediction layers
if not self.no_mtp and (num_nextn_predict_layers := hparams.get("num_nextn_predict_layers")) is not None:
self.gguf_writer.add_nextn_predict_layers(num_nextn_predict_layers)
self.gguf_writer.add_hyper_connection_count(
hparams.get("hc_mult", 1))
self.gguf_writer.add_hyper_connection_sinkhorn_iterations(
hparams.get("hc_sinkhorn_iters", 20))
self.gguf_writer.add_hyper_connection_epsilon(
hparams.get("hc_eps", 1e-6))
def set_vocab(self):
# Xing4_0 uses a SentencePiece tokenizer (tokenizer.model + Xing4_0Tokenizer).
# V2's set_vocab tries GPT2/BPE first, which fails for SPM. Use the SPM path directly.
# Note: no add_tokenizer_pre override — the C++ SPM load path ignores
# tokenizer.ggml.pre entirely (pre-tokenizers only apply to BPE vocabs).
self._set_vocab_sentencepiece()
def prepare_metadata(self, vocab_only: bool):
from_dir = self.fname_out.is_dir()
super().prepare_metadata(vocab_only=vocab_only)
if not self.mtp_only or not from_dir:
return
output_type: str = self.ftype.name.partition("_")[2]
fname_default: str = gguf.naming_convention(
self.metadata.name, self.metadata.basename, self.metadata.finetune,
self.metadata.version, size_label=None, output_type=output_type, model_type=None)
self.fname_out = self.fname_out.parent / f"mtp-{fname_default}.gguf"
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
# handle mHC tensors: model.layers.{N}.{attn_hc|ffn_hc}.{hc_fn|hc_base|hc_scale}
match = re.match(r"model\.layers\.(\d+)\.(attn_hc|ffn_hc)\.(.+)$", name)
if match:
layer_idx = int(match.group(1))
hc_type = match.group(2) # "attn_hc" or "ffn_hc"
param = match.group(3) # "hc_fn", "hc_base", "hc_scale"
if bid is None:
bid = layer_idx
prefix = "hc_attn" if hc_type == "attn_hc" else "hc_ffn"
kind = {"hc_fn": "fn", "hc_base": "base", "hc_scale": "scale"}.get(param)
if kind is None:
raise ValueError(f"unexpected mHC tensor: {name}")
tensor_enum = self._hc_tensor_map[(prefix, kind)]
gguf_name = self.format_tensor_name(tensor_enum, bid)
yield (gguf_name, data_torch)
return
yield from super().modify_tensors(data_torch, name, bid)
+3 -7
View File
@@ -325,14 +325,10 @@ on 4 physical NPUs, or `--devices 'HTP0[0-1:0],HTP1[0-1:1]'` on 2 physical NPUs
Controls the number of HVX hardware threads to use. The default is all (actual number varies depending on the hardware version).
- `GGML_HEXAGON_VTCM_SIZE=0`
Overrides the VTCM size used by each session. Accepts `K`, `M` and `G` suffixes; a plain number is in MB.
The default (0) uses the size the hardware reports. Values above the hardware size will fail to start the session.
Overrides the VTCM size used by each session. Accepts `K`, `M` and `G` suffixes; a plain number is in MB. The default (0) uses the size the hardware reports. Values above the hardware size will fail to start the session.
- `GGML_HEXAGON_HOSTBUF=0` (default: 1, enabled)
Controls host buffer (`HTP*-HOST`) allocation in shared FastRPC memory to avoid host-device tensor copies.
CPU-assigned weights (input embeddings like `token_embd` and `per_layer_token_embd`, and tensors overridden with `-ot ...=CPU`)
are placed in host buffers as well. On older drivers/FW, large models can run out of IOVA space when these are mapped.
Pass `--no-host` to keep CPU-assigned weights in regular CPU memory (`-ot ...=CPU` alone is not sufficient).
- `GGML_HEXAGON_HOSTBUF=1` (default: 0, disabled)
Enables allocating host buffers for debugging. By default, host buffers are disabled.
- `GGML_HEXAGON_DMA64=0` (default: enabled on v81+)
Disables 64-bit DMA for model weights. Set to `1` to enable it explicitly on a supported architecture.
+2 -2
View File
@@ -71,7 +71,7 @@ Legend:
| LEAKY_RELU | ❌ | ✅ | ✅ | ✅ | ❌ | 🟡 | 🟡 | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| LIGHTNING_INDEXER | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | 🟡 | ✅ | ❌ | ❌ | ❌ | ❌ |
| LOG | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
| MEAN | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | 🟡 | ✅ | ✅ | ❌ | ❌ |
| MEAN | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | 🟡 | ✅ | ❌ | ❌ | ❌ |
| MUL | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
| MUL_MAT | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |
| MUL_MAT_HADAMARD | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | 🟡 |
@@ -91,7 +91,7 @@ Legend:
| POOL_2D | ❌ | 🟡 | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| REGLU | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
| RELU | ❌ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
| REPEAT | ❌ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ✅ | 🟡 | ❌ | ❌ |
| REPEAT | ❌ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | 🟡 | ❌ | ❌ |
| REPEAT_BACK | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| RMS_NORM | ❌ | ✅ | ✅ | ✅ | 🟡 | ✅ | ✅ | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
| RMS_NORM_BACK | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
+8 -8
View File
@@ -7682,19 +7682,19 @@
"OpenCL","REPEAT","type=f32,ne=[10,5,4,1],nr=[1,2,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,1],nr=[1,1,2,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,1],nr=[1,1,1,2]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f16,ne=[10,5,4,1],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=i32,ne=[10,5,4,1],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=i16,ne=[10,5,4,1],nr=[1,1,1,2]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=bf16,ne=[10,5,4,1],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f16,ne=[10,5,4,1],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=i32,ne=[10,5,4,1],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=i16,ne=[10,5,4,1],nr=[1,1,1,2]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=bf16,ne=[10,5,4,1],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,3],nr=[1,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,3],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,3],nr=[1,2,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,3],nr=[1,1,2,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f32,ne=[10,5,4,3],nr=[1,1,1,2]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f16,ne=[10,5,4,3],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=i32,ne=[10,5,4,3],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=i16,ne=[10,5,4,3],nr=[1,1,1,2]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=bf16,ne=[10,5,4,3],nr=[2,1,1,1]","support","1","yes","OpenCL"
"OpenCL","REPEAT","type=f16,ne=[10,5,4,3],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=i32,ne=[10,5,4,3],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=i16,ne=[10,5,4,3],nr=[1,1,1,2]","support","0","no","OpenCL"
"OpenCL","REPEAT","type=bf16,ne=[10,5,4,3],nr=[2,1,1,1]","support","0","no","OpenCL"
"OpenCL","REPEAT_BACK","type=f32,ne=[8,6,4,2],nr=[1,1,1,1],v=0","support","0","no","OpenCL"
"OpenCL","REPEAT_BACK","type=f32,ne=[8,6,4,2],nr=[2,1,1,1],v=0","support","0","no","OpenCL"
"OpenCL","REPEAT_BACK","type=f32,ne=[8,6,4,2],nr=[1,2,1,1],v=0","support","0","no","OpenCL"
Can't render this file because it is too large.
+7 -17
View File
@@ -12278,23 +12278,13 @@
"WebGPU: WebGPU","SUM","type=f32,ne=[11,5,6,3],permute=[0,2,1,3]","support","1","yes","WebGPU"
"WebGPU: WebGPU","SUM","type=f32,ne=[11,5,6,3],permute=[0,3,2,1]","support","1","yes","WebGPU"
"WebGPU: WebGPU","SUM","type=f32,ne=[11,5,6,3],permute=[0,1,3,2]","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[10,5,4,3],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[33,1,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[33,256,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32769,1,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32,1,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32,256,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32768,1,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[11,5,6,3],permute=1,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[11,5,6,3],permute=0,slice=1","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[11,5,6,3],permute=1,slice=1","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[1,7,3,2],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[1023,5,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[1024,5,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[1025,5,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[17,65537,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[512,1,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[281,1536,1,1],permute=0,slice=0","support","1","yes","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[10,5,4,3]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[33,1,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[33,256,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32769,1,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32,1,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32,256,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","MEAN","type=f32,ne=[32768,1,1,1]","support","0","no","WebGPU"
"WebGPU: WebGPU","SUM","type=f32,ne=[33,1,1,1]","support","1","yes","WebGPU"
"WebGPU: WebGPU","SUM","type=f32,ne=[33,1024,1,1]","support","1","yes","WebGPU"
"WebGPU: WebGPU","SUM","type=f32,ne=[33,256,1,1]","support","1","yes","WebGPU"
Can't render this file because it is too large.
+4 -5
View File
@@ -2034,6 +2034,10 @@ extern "C" {
float beta_slow),
"use ggml_rope_ext_inplace instead");
// compute correction dims for YaRN RoPE scaling
GGML_API void ggml_rope_yarn_corr_dims(
int n_dims, int n_ctx_orig, float freq_base, float beta_fast, float beta_slow, float dims[2]);
// rotary position embedding backward, i.e compute dx from dy
// a - dy
GGML_API struct ggml_tensor * ggml_rope_ext_back(
@@ -2076,11 +2080,6 @@ extern "C" {
struct ggml_tensor * a,
int n_offs);
// control rounding of YaRN correction bounds (default: true)
GGML_API struct ggml_tensor * ggml_rope_set_truncate(
struct ggml_tensor * a,
bool truncate);
// im2col
// converts data into a format that effectively results in a convolution when combined with matrix multiplication
GGML_API struct ggml_tensor * ggml_im2col(
+2 -2
View File
@@ -2934,7 +2934,7 @@ void ggml_cann_rope(ggml_backend_cann_context & ctx, ggml_tensor * dst) {
const float theta_scale = powf(freq_base, -2.0f / n_dims);
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, true, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
bool is_neox = mode & GGML_ROPE_TYPE_NEOX;
const bool is_imrope = mode == GGML_ROPE_TYPE_IMROPE; // qwen3vl apply interleaved mrope
@@ -3289,7 +3289,7 @@ void ggml_cann_rope_cache_preload(ggml_backend_cann_context & ctx, ggml_tensor *
const float theta_scale = powf(freq_base, -2.0f / n_dims);
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, true, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
bool is_neox = mode & GGML_ROPE_TYPE_NEOX;
const bool is_imrope = mode == GGML_ROPE_TYPE_IMROPE;
-3
View File
@@ -2547,9 +2547,6 @@ static bool ggml_backend_cann_supports_op(ggml_backend_dev_t dev, const ggml_ten
}
case GGML_OP_ROPE:
{
if (!ggml_rope_get_truncate(op)) {
return false;
}
if (((const int32_t *) op->op_params)[15] != 0) {
return false; // FIXME: support ggml_rope_set_offset
}
+2 -2
View File
@@ -6310,8 +6310,8 @@ static void ggml_compute_forward_rope_flt(
//const int n_past = ((int32_t *) dst->op_params)[0];
const int n_dims = ((int32_t *) dst->op_params)[1];
const int mode = ((int32_t *) dst->op_params)[2];
//const int n_ctx = ((int32_t *) dst->op_params)[3];
const int n_ctx_orig = ((int32_t *) dst->op_params)[4];
const bool truncate = ggml_rope_get_truncate(dst);
memcpy(&freq_base, (int32_t *) dst->op_params + 5, sizeof(float));
memcpy(&freq_scale, (int32_t *) dst->op_params + 6, sizeof(float));
@@ -6356,7 +6356,7 @@ static void ggml_compute_forward_rope_flt(
const float theta_scale = powf(freq_base, -2.0f/n_dims);
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, truncate, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
const bool is_imrope = mode == GGML_ROPE_TYPE_IMROPE; // qwen3vl apply interleaved mrope
const bool mrope_used = mode & GGML_ROPE_TYPE_MROPE; // ggml_rope_multi, note: also true for vision (24 & 8 == true) and for imrope
-1
View File
@@ -1585,7 +1585,6 @@ struct ggml_cuda_mm_fusion_args_host {
const ggml_tensor * gate_scale = nullptr;
ggml_glu_op glu_op;
float glu_limit = 0.0f;
ggml_prec prec_src1 = GGML_PREC_UNDEFINED; // src1 precision requested by the fused MUL_MATs (MMQ)
const ggml_tensor * shared_up = nullptr;
const ggml_tensor * shared_gate = nullptr;
ggml_tensor * shared_dst = nullptr;
+15 -20
View File
@@ -428,18 +428,17 @@ static __device__ __forceinline__ void dequantize_V_q4_0(const void * __restrict
const int8_t * q8 = (const int8_t *) &q;
if constexpr (std::is_same_v<T, half>) {
#ifdef FP16_AVAILABLE
if constexpr (std::is_same_v<T, half>) {
const half2 d = __half2half2(x[ib].d);
#pragma unroll
for (int l0 = 0; l0 < ne; l0 += 2) {
((half2 *) dst)[l0/2] = d * make_half2(q8[l0 + 0], q8[l0 + 1]);
}
#else
NO_DEVICE_CODE;
} else
#endif // FP16_AVAILABLE
} else if constexpr (std::is_same_v<T, float>) {
if constexpr (std::is_same_v<T, float>) {
const float d = x[ib].d;
#pragma unroll
@@ -467,8 +466,8 @@ static __device__ __forceinline__ void dequantize_V_q4_1(const void * __restrict
const int8_t * q8 = (const int8_t *) &q;
if constexpr (std::is_same_v<T, half>) {
#ifdef FP16_AVAILABLE
if constexpr (std::is_same_v<T, half>) {
const half2 dm = x[ib].dm;
const half2 d = __half2half2( __low2half(dm));
const half2 m = __half2half2(__high2half(dm));
@@ -477,10 +476,9 @@ static __device__ __forceinline__ void dequantize_V_q4_1(const void * __restrict
for (int l0 = 0; l0 < ne; l0 += 2) {
((half2 *) dst)[l0/2] = d * make_half2(q8[l0 + 0], q8[l0 + 1]) + m;
}
#else
NO_DEVICE_CODE;
} else
#endif // FP16_AVAILABLE
} else if constexpr (std::is_same_v<T, float>) {
if constexpr (std::is_same_v<T, float>) {
const float2 dm = __half22float2(x[ib].dm);
#pragma unroll
@@ -520,18 +518,17 @@ static __device__ __forceinline__ void dequantize_V_q5_0(const void * __restrict
const int8_t * q8 = (const int8_t *) &q;
if constexpr (std::is_same_v<T, half>) {
#ifdef FP16_AVAILABLE
if constexpr (std::is_same_v<T, half>) {
const half2 d = __half2half2(x[ib].d);
#pragma unroll
for (int l0 = 0; l0 < ne; l0 += 2) {
((half2 *) dst)[l0/2] = d * make_half2(q8[l0 + 0], q8[l0 + 1]);
}
#else
NO_DEVICE_CODE;
} else
#endif // FP16_AVAILABLE
} else if constexpr (std::is_same_v<T, float>) {
if constexpr (std::is_same_v<T, float>) {
const float d = x[ib].d;
#pragma unroll
@@ -569,8 +566,8 @@ static __device__ __forceinline__ void dequantize_V_q5_1(const void * __restrict
const int8_t * q8 = (const int8_t *) &q;
if constexpr (std::is_same_v<T, half>) {
#ifdef FP16_AVAILABLE
if constexpr (std::is_same_v<T, half>) {
const half2 dm = x[ib].dm;
const half2 d = __half2half2( __low2half(dm));
const half2 m = __half2half2(__high2half(dm));
@@ -579,10 +576,9 @@ static __device__ __forceinline__ void dequantize_V_q5_1(const void * __restrict
for (int l0 = 0; l0 < ne; l0 += 2) {
((half2 *) dst)[l0/2] = d * make_half2(q8[l0 + 0], q8[l0 + 1]) + m;
}
#else
NO_DEVICE_CODE;
} else
#endif // FP16_AVAILABLE
} else if constexpr (std::is_same_v<T, float>) {
if constexpr (std::is_same_v<T, float>) {
const float2 dm = __half22float2(x[ib].dm);
#pragma unroll
@@ -605,18 +601,17 @@ static __device__ __forceinline__ void dequantize_V_q8_0(const void * __restrict
int8_t qs[ne];
ggml_cuda_memcpy_1<ne, 2>(qs, x[ib].qs + iqs);
if constexpr (std::is_same<T, half>::value) {
#ifdef FP16_AVAILABLE
if constexpr (std::is_same<T, half>::value) {
const half2 d = __half2half2(x[ib].d);
#pragma unroll
for (int l0 = 0; l0 < ne; l0 += 2) {
((half2 *) dst)[l0/2] = d * make_half2(qs[l0 + 0], qs[l0 + 1]);
}
#else
NO_DEVICE_CODE;
} else
#endif // FP16_AVAILABLE
} else if constexpr (std::is_same<T, float>::value) {
if constexpr (std::is_same<T, float>::value) {
const float d = x[ib].d;
#pragma unroll
+48 -103
View File
@@ -2151,50 +2151,8 @@ static bool ggml_cuda_match_shared_expert(const ggml_cgraph * graph, int routed_
return true;
}
// The NVFP4 weight scale that scale_node (MUL) applies to the output of mm_node (MUL_MAT), or nullptr.
// Shared by ggml_cuda_try_fuse and graph_optimize so that both accept the same patterns.
static const ggml_tensor * ggml_cuda_get_mul_mat_scale(const ggml_tensor * scale_node, const ggml_tensor * mm_node) {
const bool scale_lhs_mm = scale_node->src[0] == mm_node;
const bool scale_rhs_mm = scale_node->src[1] == mm_node;
if (!scale_lhs_mm && !scale_rhs_mm) {
return nullptr;
}
const ggml_tensor * scale = scale_lhs_mm ? scale_node->src[1] : scale_node->src[0];
if (mm_node->src[0]->type != GGML_TYPE_NVFP4 || scale_node->type != GGML_TYPE_F32 ||
scale->type != GGML_TYPE_F32 || !ggml_is_contiguous(scale) || ggml_nelements(scale) != 1 ||
!ggml_are_same_shape(scale_node, mm_node)) {
return nullptr;
}
return scale;
}
// The same for MUL_MAT_ID with one scale per expert: RESHAPE, REPEAT and GET_ROWS (by the ids) of the scales, then the MUL.
static const ggml_tensor * ggml_cuda_get_mul_mat_id_scale(const ggml_tensor * reshape, const ggml_tensor * repeat, const ggml_tensor * getrows,
const ggml_tensor * scale_node, const ggml_tensor * mm_node) {
if (repeat->src[0] != reshape || getrows->src[0] != repeat || getrows->src[1] != mm_node->src[2]) {
return nullptr;
}
if (!((scale_node->src[0] == mm_node && scale_node->src[1] == getrows) ||
(scale_node->src[0] == getrows && scale_node->src[1] == mm_node))) {
return nullptr;
}
const ggml_tensor * scale = reshape->src[0];
if (mm_node->src[0]->type != GGML_TYPE_NVFP4 || scale_node->type != GGML_TYPE_F32 ||
scale->type != GGML_TYPE_F32 || !ggml_is_contiguous(scale) || ggml_nelements(scale) != mm_node->src[0]->ne[2] ||
!ggml_are_same_shape(scale_node, mm_node)) {
return nullptr;
}
return scale;
}
// Buffer- and batch-size independent, so graph_optimize produces the same graph topology for every ubatch size (see #29986).
// up_scale/gate_scale: the MUL nodes that apply the NVFP4 weight scales (per tensor or per expert) between the MUL_MATs and the GLU.
static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu, const int device,
const ggml_tensor * up_scale = nullptr, const ggml_tensor * gate_scale = nullptr) {
static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu, const int device) {
const ggml_tensor * x_up = up->src[0];
const ggml_tensor * y = up->src[1];
@@ -2207,12 +2165,7 @@ static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggm
return false;
}
if (!ggml_cuda_should_fuse_mul_mat(up, gate, glu, nullptr, nullptr, up_scale, gate_scale)) {
return false;
}
// The fused kernel runs both MUL_MATs at one src1 precision, see ggml_cuda_mul_mat_q.
if (ggml_prec_get_src(up, 1) != ggml_prec_get_src(gate, 1)) {
if (!ggml_cuda_should_fuse_mul_mat(up, gate, glu)) {
return false;
}
@@ -2225,8 +2178,7 @@ static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggm
return turing_mma_available(ggml_cuda_info().devices[device].cc);
}
static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu,
const ggml_tensor * up_scale = nullptr, const ggml_tensor * gate_scale = nullptr) {
static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_tensor * gate, const ggml_tensor * glu) {
// A hinted matmul can be fused, but its unfused path may be faster (e.g. FWHT for GGML_HINT_SRC0_IS_HADAMARD).
if (ggml_get_op_params_i32(up, 1) != GGML_HINT_NONE || ggml_get_op_params_i32(gate, 1) != GGML_HINT_NONE) {
return false;
@@ -2238,7 +2190,7 @@ static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_t
ggml_nbytes(x) != ggml_backend_buffer_get_alloc_size(x->buffer, x) && x->view_src;
};
const int device = ggml_cuda_get_device();
if (!ggml_cuda_mul_mat_q_fusion_matches(up, gate, glu, device, up_scale, gate_scale) || bad_padding_clear(up->src[0]) || bad_padding_clear(gate->src[0])) {
if (!ggml_cuda_mul_mat_q_fusion_matches(up, gate, glu, device) || bad_padding_clear(up->src[0]) || bad_padding_clear(gate->src[0])) {
return false;
}
@@ -2257,7 +2209,7 @@ static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_t
}
// The fused kernel needs one block per tile (no stream-k split), so only fuse if the tiling is efficient.
return ggml_cuda_mmq_fusion_is_efficient(x_up->type, ggml_prec_get_src(up, 1), x_up->ne[1], ncols, nchannels, device);
return ggml_cuda_mmq_fusion_is_efficient(x_up->type, x_up->ne[1], ncols, nchannels, device);
}
static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
@@ -4332,6 +4284,43 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
bool fused_mul_mat_vec = false;
int fused_node_count = 0;
auto get_mul_mat_scale = [](const ggml_tensor * scale_node, const ggml_tensor * mm_node) -> const ggml_tensor * {
const bool scale_lhs_mm = scale_node->src[0] == mm_node;
const bool scale_rhs_mm = scale_node->src[1] == mm_node;
if (!scale_lhs_mm && !scale_rhs_mm) {
return nullptr;
}
const ggml_tensor * scale = scale_lhs_mm ? scale_node->src[1] : scale_node->src[0];
if (mm_node->src[0]->type != GGML_TYPE_NVFP4 || scale_node->type != GGML_TYPE_F32 ||
scale->type != GGML_TYPE_F32 || !ggml_is_contiguous(scale) || ggml_nelements(scale) != 1 ||
!ggml_are_same_shape(scale_node, mm_node)) {
return nullptr;
}
return scale;
};
auto get_mul_mat_id_scale = [](const ggml_tensor * reshape, const ggml_tensor * repeat, const ggml_tensor * getrows,
const ggml_tensor * scale_node, const ggml_tensor * mm_node) -> const ggml_tensor * {
if (repeat->src[0] != reshape || getrows->src[0] != repeat || getrows->src[1] != mm_node->src[2]) {
return nullptr;
}
if (!((scale_node->src[0] == mm_node && scale_node->src[1] == getrows) ||
(scale_node->src[0] == getrows && scale_node->src[1] == mm_node))) {
return nullptr;
}
const ggml_tensor * scale = reshape->src[0];
if (mm_node->src[0]->type != GGML_TYPE_NVFP4 || scale_node->type != GGML_TYPE_F32 ||
scale->type != GGML_TYPE_F32 || !ggml_is_contiguous(scale) || ggml_nelements(scale) != mm_node->src[0]->ne[2] ||
!ggml_are_same_shape(scale_node, mm_node)) {
return nullptr;
}
return scale;
};
auto get_bias_tensor = [](const ggml_tensor * bias_node, const ggml_tensor * mul_node, ggml_op op_bias) -> const ggml_tensor * {
if (op_bias == GGML_OP_ADD) {
if (bias_node->src[0] == mul_node) {
@@ -4398,8 +4387,8 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
continue;
}
const ggml_tensor * gate_scale = ggml_cuda_get_mul_mat_scale(gate_scale_n, gate_n);
const ggml_tensor * up_scale = ggml_cuda_get_mul_mat_scale(up_scale_n, up_n);
const ggml_tensor * gate_scale = get_mul_mat_scale(gate_scale_n, gate_n);
const ggml_tensor * up_scale = get_mul_mat_scale(up_scale_n, up_n);
if (!gate_scale || !up_scale) {
continue;
}
@@ -4430,14 +4419,6 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
fused_node_count = n_ops;
break;
}
if (!with_bias && ggml_cuda_should_fuse_mul_mat_q(up_n, gate_n, glu, up_scale_n, gate_scale_n)) {
fusion_data.prec_src1 = ggml_prec_get_src(up_n, 1);
ggml_cuda_mul_mat_q(*cuda_ctx, src0, src1, ids, cgraph->nodes[glu_idx], &fusion_data);
fused_mul_mat_vec = true;
fused_node_count = n_ops;
break;
}
}
if (fused_mul_mat_vec) {
@@ -4502,9 +4483,9 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
continue;
}
const ggml_tensor * gate_scale = ggml_cuda_get_mul_mat_id_scale(cgraph->nodes[gate_idx + 1], cgraph->nodes[gate_idx + 2],
const ggml_tensor * gate_scale = get_mul_mat_id_scale(cgraph->nodes[gate_idx + 1], cgraph->nodes[gate_idx + 2],
cgraph->nodes[gate_idx + 3], gate_scale_n, gate_n);
const ggml_tensor * up_scale = ggml_cuda_get_mul_mat_id_scale(cgraph->nodes[up_idx + 1], cgraph->nodes[up_idx + 2],
const ggml_tensor * up_scale = get_mul_mat_id_scale(cgraph->nodes[up_idx + 1], cgraph->nodes[up_idx + 2],
cgraph->nodes[up_idx + 3], up_scale_n, up_n);
if (!gate_scale || !up_scale) {
continue;
@@ -4532,14 +4513,6 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
fused_node_count = n_ops;
break;
}
if (!with_bias && ggml_cuda_should_fuse_mul_mat_q(up_n, gate_n, glu, up_scale_n, gate_scale_n)) {
fusion_data.prec_src1 = ggml_prec_get_src(up_n, 1);
ggml_cuda_mul_mat_q(*cuda_ctx, src0, src1, ids, cgraph->nodes[glu_idx], &fusion_data);
fused_mul_mat_vec = true;
fused_node_count = n_ops;
break;
}
}
if (fused_mul_mat_vec) {
@@ -4655,7 +4628,6 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
fusion_data.gate = gate->src[0];
fusion_data.glu_op = ggml_get_glu_op(glu);
fusion_data.glu_limit = ggml_get_op_params_f32(glu, 3);
fusion_data.prec_src1 = ggml_prec_get_src(up, 1);
ggml_cuda_mul_mat_q(*cuda_ctx, src0, src1, ids, glu, &fusion_data);
fused_mul_mat_vec = true;
@@ -4717,9 +4689,9 @@ static int ggml_cuda_try_fuse(ggml_backend_cuda_context * cuda_ctx, ggml_cgraph
const ggml_tensor * scale = nullptr;
if (op == GGML_OP_MUL_MAT) {
scale = ggml_cuda_get_mul_mat_scale(scale_node, mm_node);
scale = get_mul_mat_scale(scale_node, mm_node);
} else {
scale = ggml_cuda_get_mul_mat_id_scale(cgraph->nodes[i + 1], cgraph->nodes[i + 2], cgraph->nodes[i + 3], scale_node, mm_node);
scale = get_mul_mat_id_scale(cgraph->nodes[i + 1], cgraph->nodes[i + 2], cgraph->nodes[i + 3], scale_node, mm_node);
}
if (!scale) {
continue;
@@ -5270,33 +5242,6 @@ static void ggml_backend_cuda_graph_optimize(ggml_backend_t backend, ggml_cgraph
continue;
}
}
// Same with the NVFP4 scales: MUL_MAT(gate), MUL(gate scale), MUL_MAT(up), MUL(up scale), GLU.
if (i + 4 < cgraph->n_nodes && cgraph->nodes[i + 4]->op == GGML_OP_GLU) {
ggml_tensor * glu = cgraph->nodes[i + 4];
ggml_tensor * gate_scale = cgraph->nodes[i + 1];
ggml_tensor * up_scale = cgraph->nodes[i + 3];
if (glu->src[0] == gate_scale && glu->src[1] == up_scale &&
ggml_cuda_mul_mat_q_fusion_matches(cgraph->nodes[i + 2], cgraph->nodes[i], glu, cuda_ctx->device, up_scale, gate_scale) &&
ggml_cuda_get_mul_mat_scale(gate_scale, cgraph->nodes[i]) && ggml_cuda_get_mul_mat_scale(up_scale, cgraph->nodes[i + 2])) {
add_alloc_deps(i, i + 4);
i += 4;
continue;
}
}
// Same with per-expert NVFP4 scales: MUL_MAT_ID, RESHAPE, REPEAT, GET_ROWS, MUL for gate and for up, GLU.
if (i + 10 < cgraph->n_nodes && cgraph->nodes[i + 10]->op == GGML_OP_GLU) {
ggml_tensor * glu = cgraph->nodes[i + 10];
ggml_tensor * gate_scale = cgraph->nodes[i + 4];
ggml_tensor * up_scale = cgraph->nodes[i + 9];
if (glu->src[0] == gate_scale && glu->src[1] == up_scale &&
ggml_cuda_mul_mat_q_fusion_matches(cgraph->nodes[i + 5], cgraph->nodes[i], glu, cuda_ctx->device, up_scale, gate_scale) &&
ggml_cuda_get_mul_mat_id_scale(cgraph->nodes[i + 1], cgraph->nodes[i + 2], cgraph->nodes[i + 3], gate_scale, cgraph->nodes[i]) &&
ggml_cuda_get_mul_mat_id_scale(cgraph->nodes[i + 6], cgraph->nodes[i + 7], cgraph->nodes[i + 8], up_scale, cgraph->nodes[i + 5])) {
add_alloc_deps(i, i + 10);
i += 10;
continue;
}
}
ggml_cuda_moe_weighted_reduction_match match;
if (ggml_cuda_match_moe_weighted_reduction(cgraph, i, match)) {
+261 -189
View File
@@ -2,7 +2,239 @@
#include "lightning-indexer.cuh"
#include "fattn-common.cuh"
#include "convert.cuh"
#include "mma.cuh"
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
#if defined(TURING_MMA_AVAILABLE)
typedef union {
int2 i2;
half2 h2[2];
} half4;
// TODO add support for AMD cards via rocWMMA
#include <mma.h>
namespace wmma = nvcuda::wmma;
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
static __global__ void lightning_indexer_kernel_wmma(
const float * Q, const char * K, const float * W, const half * M, float * dst,
int64_t n_stream, int64_t n_batch, int64_t n_kv,
size_t nb1, size_t nb2, size_t nb3,
size_t nbq1, size_t nbq2, size_t nbq3,
size_t nbk1, size_t nbk2, size_t nbk3,
size_t nbw1, size_t nbw2, size_t nbw3,
size_t nbm1, size_t nbm2, size_t nbm3,
int64_t nem3
) {
constexpr int THREADS_PER_BLOCK = WARPS_PER_BLOCK * WARP_SIZE;
constexpr int HEADS_PER_INNER_LOOP = 8;
constexpr int K_EMBD_PER_INNER_LOOP = 16;
constexpr int N_EMBD_PADDED = N_EMBD + 8;
const int i_batch = blockIdx.y;
const int i_stream = blockIdx.z;
const int i_warp = threadIdx.y;
const int i_lane = threadIdx.x;
const int tid = i_warp * WARP_SIZE + i_lane;
// each block processes K_VECS_PER_BLOCK K vectors
const int start_kv = blockIdx.x * K_VECS_PER_BLOCK;
const char * q_base = (const char *) Q + i_batch*nbq2 + i_stream*nbq3;
const float * w_base = (const float *) ((const char *) W + i_batch*nbw1 + i_stream*nbw3);
// phase 1 - load weights and first Q tile to shared memory
__shared__ float w_shared[N_HEAD];
__shared__ int2 q_shared_h[HEADS_PER_INNER_LOOP][N_EMBD_PADDED / 4];
if (tid < N_HEAD) {
w_shared[tid] = w_base[tid];
}
// total number of half4 elements in HEADS_PER_INNER_LOOP x N_EMBD Q tile
constexpr int N_Q_TILE = HEADS_PER_INNER_LOOP * (N_EMBD / 4);
// number of registers needed in each thread to store Q tile in thread block
constexpr int N_Q_NEXT = (N_Q_TILE + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;
#pragma unroll
for (int i_q = tid; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
const int i_head = i_q / (N_EMBD / 4);
const int i_embd = i_q % (N_EMBD / 4);
const float4 q = *(const float4 *) (q_base + i_head*nbq1 + i_embd*sizeof(float4));
half4 q_packed;
q_packed.h2[0] = __float22half2_rn(make_float2(q.x, q.y));
q_packed.h2[1] = __float22half2_rn(make_float2(q.z, q.w));
q_shared_h[i_head][i_embd] = q_packed.i2;
}
// phase 2 - load (and dequantize if needed) K to shared mem
__shared__ half2 k_shared_h[K_VECS_PER_BLOCK][N_EMBD_PADDED / 4][2];
constexpr int n_k = K_VECS_PER_BLOCK * (N_EMBD / 4);
if constexpr (TYPE_K == GGML_TYPE_F16) {
#pragma unroll
for (int i_k = tid; i_k < n_k; i_k += THREADS_PER_BLOCK) {
const int i_k_vec = i_k / (N_EMBD / 4);
const int i_embd = i_k % (N_EMBD / 4);
const int i_kv = start_kv + i_k_vec;
if (i_kv < n_kv) {
const int2 * k_base = (const int2 *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
*(int2*) &k_shared_h[i_k_vec][i_embd] = k_base[i_embd];
} else {
*(int2*) &k_shared_h[i_k_vec][i_embd] = make_int2(0, 0);
}
}
} else {
constexpr dequantize_V_t dequantize_k = get_dequantize_V<TYPE_K, half, 4>();
#pragma unroll
for (int i_k = tid; i_k < n_k; i_k += THREADS_PER_BLOCK) {
const int i_k_vec = i_k / (N_EMBD / 4);
const int i_embd = i_k % (N_EMBD / 4);
const int i_kv = start_kv + i_k_vec;
if (i_kv < n_kv) {
const void * k_base = (const void *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
dequantize_k(k_base, &k_shared_h[i_k_vec][i_embd][0], i_embd * 4);
} else {
*(int2*) &k_shared_h[i_k_vec][i_embd] = make_int2(0, 0);
}
}
}
__syncthreads();
// phase 3 - calculate lightning indexer scores
__shared__ float qk_shared[WARPS_PER_BLOCK][HEADS_PER_INNER_LOOP][K_VECS_PER_BLOCK];
// load K fragment
wmma::fragment<wmma::matrix_b, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, half, wmma::col_major> frag_k;
wmma::load_matrix_sync(frag_k, (half*) &k_shared_h[0][i_warp * K_EMBD_PER_INNER_LOOP / 4], N_EMBD_PADDED);
float score_k = 0.0f;
for (int i_head_0 = 0; i_head_0 < N_HEAD; i_head_0 += HEADS_PER_INNER_LOOP) {
const int i_head_next = i_head_0 + HEADS_PER_INNER_LOOP;
// we don't use accumulator for anything, fill it with zeros
wmma::fragment<wmma::accumulator, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, float> frag_acc;
wmma::fill_fragment(frag_acc, 0.0f);
// load Q fragment
wmma::fragment<wmma::matrix_a, HEADS_PER_INNER_LOOP, K_VECS_PER_BLOCK, K_EMBD_PER_INNER_LOOP, half, wmma::row_major> frag_q;
wmma::load_matrix_sync(frag_q, (half*) &q_shared_h[0][i_warp * K_EMBD_PER_INNER_LOOP / 4], N_EMBD_PADDED);
// preload next Q tile to registers during matrix multiplication
float4 q_next[N_Q_NEXT];
if (i_head_next < N_HEAD) {
#pragma unroll
for (int i_q = tid, i_q_next = 0; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
const int i_head = i_head_next + i_q / (N_EMBD / 4);
const int i_embd = i_q % (N_EMBD / 4);
q_next[i_q_next++] = *(const float4 *) (q_base + i_head*nbq1 + i_embd*sizeof(float4));
}
}
// perform matrix multiplication
wmma::mma_sync(frag_acc, frag_q, frag_k, frag_acc);
wmma::store_matrix_sync((float*) &qk_shared[i_warp][0][0], frag_acc, K_VECS_PER_BLOCK, wmma::mem_row_major);
// make sure all threads finished using q_shared_h so we can store next tile
__syncthreads();
// write preloaded Q tile to shared memory
if (i_head_next < N_HEAD) {
#pragma unroll
for (int i_q = tid, i_q_next = 0; i_q < N_Q_TILE; i_q += THREADS_PER_BLOCK) {
const int i_head = i_q / (N_EMBD / 4);
const int i_embd = i_q % (N_EMBD / 4);
half4 q_packed;
q_packed.h2[0] = __float22half2_rn(make_float2(q_next[i_q_next].x, q_next[i_q_next].y));
q_packed.h2[1] = __float22half2_rn(make_float2(q_next[i_q_next].z, q_next[i_q_next].w));
q_shared_h[i_head][i_embd] = q_packed.i2;
++i_q_next;
}
}
// accumulate QK multiplication results from all block warps
// (there are 256 threads in block and 256 matmul outputs)
// TODO it will break if WARP_SIZE is not 32
const int h = tid / K_VECS_PER_BLOCK;
const int k = tid % K_VECS_PER_BLOCK;
const float w_val = w_shared[i_head_0 + h];
float sum = 0.0f;
#pragma unroll
for (int w = 0; w < WARPS_PER_BLOCK; ++w) {
sum += qk_shared[w][h][k];
}
// ReLU, weight
sum = sum > 0.0f ? sum : 0.0f;
sum *= w_val;
// wait until qk_shared[0] is no longer used
__syncthreads();
// reuse qk_shared[0] for storing partial results
qk_shared[0][h][k] = sum;
// wait until all threads write their results
__syncthreads();
// accumulate result over heads
if (tid < K_VECS_PER_BLOCK) {
#pragma unroll
for (int i_head = 0; i_head < HEADS_PER_INNER_LOOP; ++i_head) {
score_k += qk_shared[0][i_head][tid];
}
}
// make sure all threads finished using qk_shared
__syncthreads();
}
// phase 4 - store output to VRAM
if (tid < K_VECS_PER_BLOCK) {
const int i_kv = start_kv + tid;
if (i_kv < n_kv) {
const half * m_base = (const half *) ((const char *) M + i_batch*nbm1 + (i_stream%nem3)*nbm3);
float * dst_base = (float *) ((char *) dst + i_batch*nb1 + i_stream*nb3);
dst_base[i_kv] = score_k + __half2float(m_base[i_kv]);
}
}
}
#else // defined(TURING_MMA_AVAILABLE)
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
static __global__ void lightning_indexer_kernel_wmma(
const float * Q, const char * K, const float * W, const half * M, float * dst,
int64_t n_stream, int64_t n_batch, int64_t n_kv,
size_t nb1, size_t nb2, size_t nb3,
size_t nbq1, size_t nbq2, size_t nbq3,
size_t nbk1, size_t nbk2, size_t nbk3,
size_t nbw1, size_t nbw2, size_t nbw3,
size_t nbm1, size_t nbm2, size_t nbm3,
int64_t nem3
) {
GGML_UNUSED_VARS(Q, K, W, M, dst,
n_stream, n_batch, n_kv,
nb1, nb2, nb3,
nbq1, nbq2, nbq3,
nbk1, nbk2, nbk3,
nbw1, nbw2, nbw3,
nem3);
NO_DEVICE_CODE;
}
#endif // defined(TURING_MMA_AVAILABLE)
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
// tokens scored per block by the tile kernel
#define LIGHTNING_INDEXER_TILE_TOKENS 8
@@ -15,176 +247,6 @@
#define LIGHTNING_INDEXER_TILE_HEADS_PER_PASS 4
#endif
template <int WARPS_PER_BLOCK, int K_VECS_PER_BLOCK, int64_t N_EMBD, int64_t N_HEAD, ggml_type TYPE_K>
static __global__ void lightning_indexer_kernel_mma(
const float * Q, const char * K, const float * W, const half * M, float * dst,
int64_t n_stream, int64_t n_batch, int64_t n_kv,
size_t nb1, size_t nb2, size_t nb3,
size_t nbq1, size_t nbq2, size_t nbq3,
size_t nbk1, size_t nbk2, size_t nbk3,
size_t nbw1, size_t nbw2, size_t nbw3,
size_t nbm1, size_t nbm2, size_t nbm3,
int64_t nem3
) {
constexpr int MMA_DIM = 16;
constexpr int WAVE_SIZE = ggml_cuda_get_physical_warp_size();
constexpr int THREADS_PER_BLOCK = WARPS_PER_BLOCK * WAVE_SIZE;
constexpr int N_EMBD_PADDED = N_EMBD + 8;
constexpr int HEAD_CHUNK = MMA_DIM;
constexpr int N_HEAD_CHUNKS = N_HEAD / HEAD_CHUNK;
constexpr int N_KV_TILES = K_VECS_PER_BLOCK / MMA_DIM;
constexpr int N_EMBD_TILES = N_EMBD / MMA_DIM;
static_assert(N_HEAD % MMA_DIM == 0, "N_HEAD must be a multiple of 16");
static_assert(N_EMBD % MMA_DIM == 0, "N_EMBD must be a multiple of 16");
static_assert(K_VECS_PER_BLOCK % MMA_DIM == 0, "K_VECS_PER_BLOCK must be a multiple of 16");
#if defined(AMD_WMMA_AVAILABLE) || defined(AMD_MFMA_AVAILABLE)
using tile_qk = ggml_cuda_mma::tile<MMA_DIM, MMA_DIM, float, ggml_cuda_mma::DATA_LAYOUT_J_MAJOR>;
#else
using tile_qk = ggml_cuda_mma::tile<MMA_DIM, MMA_DIM, float>;
#endif
const int i_batch = blockIdx.y;
const int i_stream = blockIdx.z;
const int i_warp = threadIdx.y;
const int i_lane = threadIdx.x;
const int tid = i_warp * WAVE_SIZE + i_lane;
const int start_kv = blockIdx.x * K_VECS_PER_BLOCK;
const char * q_base = (const char *) Q + i_batch*nbq2 + i_stream*nbq3;
const float * w_base = (const float *) ((const char *) W + i_batch*nbw1 + i_stream*nbw3);
__shared__ float w_shared[N_HEAD];
__shared__ half q_shared[2][HEAD_CHUNK][N_EMBD_PADDED];
__shared__ half k_shared[K_VECS_PER_BLOCK][N_EMBD_PADDED];
__shared__ float qk_chunk[HEAD_CHUNK][K_VECS_PER_BLOCK];
__shared__ float score_shared[K_VECS_PER_BLOCK];
for (int i = tid; i < N_HEAD; i += THREADS_PER_BLOCK) {
w_shared[i] = w_base[i];
}
for (int i = tid; i < K_VECS_PER_BLOCK; i += THREADS_PER_BLOCK) {
score_shared[i] = 0.0f;
}
constexpr int N_K_VEC = K_VECS_PER_BLOCK * (N_EMBD / 4);
if constexpr (TYPE_K == GGML_TYPE_F16) {
for (int i = tid; i < N_K_VEC; i += THREADS_PER_BLOCK) {
const int i_k_vec = i / (N_EMBD / 4);
const int i_embd4 = i % (N_EMBD / 4);
const int i_kv = start_kv + i_k_vec;
if (i_kv < n_kv) {
const int2 * k_ptr = (const int2 *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
*(int2 *) &k_shared[i_k_vec][i_embd4*4] = k_ptr[i_embd4];
} else {
*(int2 *) &k_shared[i_k_vec][i_embd4*4] = make_int2(0, 0);
}
}
} else {
constexpr dequantize_V_t dequantize_k = get_dequantize_V<TYPE_K, half, 4>();
for (int i = tid; i < N_K_VEC; i += THREADS_PER_BLOCK) {
const int i_k_vec = i / (N_EMBD / 4);
const int i_embd4 = i % (N_EMBD / 4);
const int i_kv = start_kv + i_k_vec;
if (i_kv < n_kv) {
const void * k_ptr = (const void *) ((const char *) K + i_kv*nbk2 + i_stream*nbk3);
dequantize_k(k_ptr, &k_shared[i_k_vec][i_embd4*4], i_embd4*4);
} else {
*(int2 *) &k_shared[i_k_vec][i_embd4*4] = make_int2(0, 0);
}
}
}
constexpr int N_Q_CHUNK_VEC = HEAD_CHUNK * (N_EMBD / 4);
constexpr int N_Q_NEXT = (N_Q_CHUNK_VEC + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;
for (int i = tid; i < N_Q_CHUNK_VEC; i += THREADS_PER_BLOCK) {
const int i_head = i / (N_EMBD / 4);
const int i_embd4 = i % (N_EMBD / 4);
const float4 q = *(const float4 *) (q_base + i_head*nbq1 + i_embd4*sizeof(float4));
q_shared[0][i_head][i_embd4*4 + 0] = (half) q.x;
q_shared[0][i_head][i_embd4*4 + 1] = (half) q.y;
q_shared[0][i_head][i_embd4*4 + 2] = (half) q.z;
q_shared[0][i_head][i_embd4*4 + 3] = (half) q.w;
}
__syncthreads();
constexpr ggml_cuda_mma::data_layout input_layout = ggml_cuda_mma::get_input_data_layout();
for (int hc = 0; hc < N_HEAD_CHUNKS; ++hc) {
const int buf = hc & 1;
const int nbuf = (hc + 1) & 1;
const bool has_next = (hc + 1) < N_HEAD_CHUNKS;
float4 q_next[N_Q_NEXT];
if (has_next) {
#pragma unroll
for (int i = tid, j = 0; i < N_Q_CHUNK_VEC; i += THREADS_PER_BLOCK, ++j) {
const int i_head = (hc + 1) * HEAD_CHUNK + i / (N_EMBD / 4);
const int i_embd4 = i % (N_EMBD / 4);
q_next[j] = *(const float4 *) (q_base + i_head*nbq1 + i_embd4*sizeof(float4));
}
}
for (int kt = i_warp; kt < N_KV_TILES; kt += WARPS_PER_BLOCK) {
tile_qk Dqk;
#pragma unroll
for (int et = 0; et < N_EMBD_TILES; ++et) {
ggml_cuda_mma::tile<MMA_DIM, MMA_DIM/2, half2, input_layout> Aq;
ggml_cuda_mma::tile<MMA_DIM, MMA_DIM/2, half2, input_layout> Bk;
ggml_cuda_mma::load_generic(Aq, (const half2 *) &q_shared[buf][0][et*MMA_DIM], N_EMBD_PADDED/2);
ggml_cuda_mma::load_generic(Bk, (const half2 *) &k_shared[kt*MMA_DIM][et*MMA_DIM], N_EMBD_PADDED/2);
ggml_cuda_mma::mma(Dqk, Aq, Bk);
}
#pragma unroll
for (int l = 0; l < Dqk.ne; ++l) {
const int head_local = Dqk.get_i(l);
const int kv_local = kt*MMA_DIM + Dqk.get_j(l);
qk_chunk[head_local][kv_local] = Dqk.x[l];
}
}
__syncthreads();
if (has_next) {
#pragma unroll
for (int i = tid, j = 0; i < N_Q_CHUNK_VEC; i += THREADS_PER_BLOCK, ++j) {
const int i_head = i / (N_EMBD / 4);
const int i_embd4 = i % (N_EMBD / 4);
q_shared[nbuf][i_head][i_embd4*4 + 0] = (half) q_next[j].x;
q_shared[nbuf][i_head][i_embd4*4 + 1] = (half) q_next[j].y;
q_shared[nbuf][i_head][i_embd4*4 + 2] = (half) q_next[j].z;
q_shared[nbuf][i_head][i_embd4*4 + 3] = (half) q_next[j].w;
}
}
for (int kv = tid; kv < K_VECS_PER_BLOCK; kv += THREADS_PER_BLOCK) {
float s = 0.0f;
#pragma unroll
for (int h = 0; h < HEAD_CHUNK; ++h) {
const float qk = qk_chunk[h][kv];
s += (qk > 0.0f ? qk : 0.0f) * w_shared[hc*HEAD_CHUNK + h];
}
score_shared[kv] += s;
}
__syncthreads();
}
for (int i_kv_local = tid; i_kv_local < K_VECS_PER_BLOCK; i_kv_local += THREADS_PER_BLOCK) {
const int i_kv = start_kv + i_kv_local;
if (i_kv < n_kv) {
const half * m_base = (const half *) ((const char *) M + i_batch*nbm1 + (i_stream%nem3)*nbm3);
float * dst_base = (float *) ((char *) dst + i_batch*nb1 + i_stream*nb3);
dst_base[i_kv] = score_shared[i_kv_local] + __half2float(m_base[i_kv]);
}
}
}
// TODO there is one ugly assumption used in this kernel - that WARP_SIZE is equal to 32
// thanks to that one warp operating on float4 processes whole indexer K/Q vectors
// 32 * 4 = 128 (N_EMBD)
@@ -554,22 +616,27 @@ void ggml_cuda_lightning_indexer(ggml_backend_cuda_context & ctx, ggml_tensor *
const int cc = ggml_cuda_info().devices[device].cc;
if (n_embd == 128 && n_head == 64) {
if ((turing_mma_available(cc) || amd_mfma_available(cc) || amd_wmma_available(cc)) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
if (GGML_CUDA_CC_IS_NVIDIA(cc) && turing_mma_available(cc) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
// use wmma kernel
constexpr int K_VECS_PER_BLOCK = 32;
constexpr int WARPS_PER_BLOCK = 4;
constexpr int WARPS_PER_BLOCK = 8;
dim3 block(ggml_cuda_info().devices[device].warp_size, WARPS_PER_BLOCK);
dim3 block(32, WARPS_PER_BLOCK);
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
dim3 grid(num_kv_blocks, n_batch, n_stream);
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_F16)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_Q4_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_Q4_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_Q5_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_Q5_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 64, k, GGML_TYPE_Q8_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_F16)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q4_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q4_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q5_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q5_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 64, k, GGML_TYPE_Q8_0)
GGML_ABORT("fatal error");
} else {
#else // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
{
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
// use vector kernel
constexpr int K_VECS_PER_WARP = 8;
constexpr int WARPS_PER_BLOCK = 8;
@@ -590,22 +657,27 @@ void ggml_cuda_lightning_indexer(ggml_backend_cuda_context & ctx, ggml_tensor *
GGML_ABORT("fatal error");
}
} else if (n_embd == 128 && n_head == 32) {
if ((turing_mma_available(cc) || amd_mfma_available(cc) || amd_wmma_available(cc)) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
if (GGML_CUDA_CC_IS_NVIDIA(cc) && turing_mma_available(cc) && k->type != GGML_TYPE_F32 && k->type != GGML_TYPE_BF16) {
// use wmma kernel
constexpr int K_VECS_PER_BLOCK = 32;
constexpr int WARPS_PER_BLOCK = 4;
constexpr int WARPS_PER_BLOCK = 8;
dim3 block(ggml_cuda_info().devices[device].warp_size, WARPS_PER_BLOCK);
dim3 block(32, WARPS_PER_BLOCK);
int num_kv_blocks = (n_kv + (K_VECS_PER_BLOCK) - 1) / (K_VECS_PER_BLOCK);
dim3 grid(num_kv_blocks, n_batch, n_stream);
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_F16)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_Q4_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_Q4_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_Q5_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_Q5_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_mma, 128, 32, k, GGML_TYPE_Q8_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_F16)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q4_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q4_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q5_0)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q5_1)
LIGHTNING_INDEXER_CASE(lightning_indexer_kernel_wmma, 128, 32, k, GGML_TYPE_Q8_0)
GGML_ABORT("fatal error");
} else {
#else // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
{
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
// use vector kernel
constexpr int K_VECS_PER_WARP = 8;
constexpr int WARPS_PER_BLOCK = 8;
+9 -13
View File
@@ -50,8 +50,7 @@ void ggml_cuda_mul_mat_f(ggml_backend_cuda_context & ctx, const ggml_tensor * sr
mmf_ids_data * ids_info_ptr = nullptr;
ggml_cuda_pool_alloc<int32_t> ids_src_compact_dev;
ggml_cuda_pool_alloc<int32_t> ids_dst_compact_dev;
ggml_cuda_pool_alloc<ggml_cuda_mm_id_tile_spec> tile_spec_dev;
ggml_cuda_pool_alloc<int32_t> expert_bounds_dev;
// For MUL_MAT_ID the memory layout is different than for MUL_MAT:
const int64_t ncols_dst = ids ? ne2 : ne1;
@@ -75,28 +74,25 @@ void ggml_cuda_mul_mat_f(ggml_backend_cuda_context & ctx, const ggml_tensor * sr
const int64_t n_experts = ne02;
const int64_t n_tokens = ne12;
const int64_t ne_get_rows = n_tokens * n_expert_used;
const int64_t max_tiles = (ne_get_rows + 16 - 1) / 16 + ne02;
ids_src_compact_dev.alloc(ctx.pool(), ne_get_rows);
ids_dst_compact_dev.alloc(ctx.pool(), ne_get_rows);
tile_spec_dev.alloc(ctx.pool(), max_tiles);
expert_bounds_dev.alloc(ctx.pool(), n_experts + 1);
const int si1 = static_cast<int>(ids_s1);
const int sis1 = static_cast<int>(src1->nb[2] / src1->nb[1]);
GGML_ASSERT(sis1 > 0);
ggml_cuda_launch_mm_ids_helper(ids_d, ids_src_compact_dev.get(), ids_dst_compact_dev.get(), tile_spec_dev.get(),
static_cast<int>(n_experts), static_cast<int>(n_tokens), static_cast<int>(n_expert_used), static_cast<int>(ne11), 16,
si1, sis1, /*write_inverse =*/ false, ctx.stream());
ggml_cuda_launch_mm_ids_helper(ids_d, ids_src_compact_dev.get(), ids_dst_compact_dev.get(), expert_bounds_dev.get(),
static_cast<int>(n_experts), static_cast<int>(n_tokens), static_cast<int>(n_expert_used), static_cast<int>(ne11), si1, sis1, /*write_inverse =*/ false, ctx.stream());
CUDA_CHECK(cudaGetLastError());
ids_info.ids_src_compact = ids_src_compact_dev.get();
ids_info.ids_dst_compact = ids_dst_compact_dev.get();
ids_info.tile_spec_dev = tile_spec_dev.get();
ids_info.n_experts = static_cast<int>(n_experts);
ids_info.max_tiles = static_cast<int>(max_tiles);
ids_info.sis1 = sis1;
ids_info.ids_src_compact = ids_src_compact_dev.get();
ids_info.ids_dst_compact = ids_dst_compact_dev.get();
ids_info.expert_bounds_dev = expert_bounds_dev.get();
ids_info.n_experts = static_cast<int>(n_experts);
ids_info.sis1 = sis1;
ids_info_ptr = &ids_info;
}
+26 -23
View File
@@ -1,8 +1,7 @@
#pragma once
#include "common.cuh"
#include "mma.cuh"
#include "mmid.cuh"
#include "common.cuh"
#include "convert.cuh"
using namespace ggml_cuda_mma;
@@ -37,9 +36,8 @@ static constexpr __device__ int mmf_get_padding() {
struct mmf_ids_data {
const int32_t * ids_src_compact = nullptr;
const int32_t * ids_dst_compact = nullptr;
const ggml_cuda_mm_id_tile_spec * tile_spec_dev = nullptr;
const int32_t * expert_bounds_dev = nullptr;
int n_experts = 0;
int max_tiles = 0;
int sis1 = 0;
};
@@ -310,7 +308,7 @@ __launch_bounds__(ggml_cuda_get_physical_warp_size()*nwarps, 1)
static __global__ void mul_mat_f_ids(
const T * __restrict__ x, const float * __restrict__ y,
const int32_t * __restrict__ ids_src_compact, const int32_t * __restrict__ ids_dst_compact,
const ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec, float * __restrict__ dst,
const int32_t * __restrict__ expert_bounds, float * __restrict__ dst,
const int ncols, const int ncols_dst_total, const int nchannels_dst, const int stride_row, const int stride_col_y, const int stride_col_dst,
const int channel_ratio, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
const int sample_ratio, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst,
@@ -351,20 +349,21 @@ static __global__ void mul_mat_f_ids(
constexpr int ntA = rows_per_block / tile_A::I;
constexpr int ntB = (cols_per_block + tile_B::I - 1) / tile_B::I;
const int row0 = blockIdx.x * rows_per_block;
const int tile_idx = blockIdx.y;
const int row0 = blockIdx.x * rows_per_block;
const ggml_cuda_mm_id_tile_spec ts = tile_spec[tile_idx];
const int expert_idx = ts.expert;
const int expert_start = ts.col_low;
const int expert_end = ts.col_high;
const int expert_idx = blockIdx.y;
const int expert_start = expert_bounds[expert_idx];
const int expert_end = expert_bounds[expert_idx + 1];
const int ncols_expert = expert_end - expert_start;
if (ncols_expert == 0) {
const int tiles_for_expert = (ncols_expert + cols_per_block - 1) / cols_per_block;
const int tile_idx = blockIdx.z;
if (tile_idx >= tiles_for_expert) {
return;
}
const int col_base = tile_idx * cols_per_block;
GGML_UNUSED(channel_ratio);
const int channel_x = expert_idx;
@@ -410,9 +409,10 @@ static __global__ void mul_mat_f_ids(
#pragma unroll
for (int j0 = 0; j0 < tile_B::I; ++j0) {
const int j = j0 + tile_idx_local*tile_B::I;
const int global_j = col_base + j;
float val = 0.0f;
if (j < cols_per_block && j < ncols_expert) {
const int src_entry = ids_src_expert[j];
if (j < cols_per_block && global_j < ncols_expert) {
const int src_entry = ids_src_expert[global_j];
const uint2 qrm = fast_div_modulo((uint32_t) src_entry, sis1_fd);
const int token = (int) qrm.x;
const int channel = (int) qrm.y;
@@ -462,9 +462,10 @@ static __global__ void mul_mat_f_ids(
#pragma unroll
for (int j0 = 0; j0 < tile_B::I; ++j0) {
const int j = j0 + tile_idx_local*tile_B::I;
const int global_j = col_base + j;
float2 tmp = make_float2(0.0f, 0.0f);
if (j < cols_per_block && j < ncols_expert) {
const int src_entry = ids_src_expert[j];
if (j < cols_per_block && global_j < ncols_expert) {
const int src_entry = ids_src_expert[global_j];
const uint2 qrm = fast_div_modulo((uint32_t) src_entry, sis1_fd);
const int token = (int) qrm.x;
const int channel = (int) qrm.y;
@@ -563,8 +564,9 @@ static __global__ void mul_mat_f_ids(
}
}
if (j < cols_per_block && j < ncols_expert && nchannels_dst > 0) {
const int dst_entry = ids_dst_expert[j];
const int global_j = col_base + j;
if (j < cols_per_block && global_j < ncols_expert && nchannels_dst > 0) {
const int dst_entry = ids_dst_expert[global_j];
const uint2 qrm = fast_div_modulo((uint32_t) dst_entry, nch_fd);
const int token = (int) qrm.x;
if (token < ncols_dst_total) {
@@ -578,7 +580,7 @@ static __global__ void mul_mat_f_ids(
}
}
#else
GGML_UNUSED_VARS(x, y, ids_src_compact, ids_dst_compact, tile_spec, dst,
GGML_UNUSED_VARS(x, y, ids_src_compact, ids_dst_compact, expert_bounds, dst,
ncols, ncols_dst_total, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst, sis1_fd, nch_fd);
@@ -601,16 +603,17 @@ static inline void mul_mat_f_switch_ids(
// Use the compact-ids kernel only for larger tiles; for small ncols_dst (< 16)
// we prefer the normal mul_mat_f path with has_ids=true.
if (has_ids_data && ncols_dst > 16) {
if (ids_data->max_tiles == 0) {
const int max_tiles = (int) ((ncols_dst + cols_per_block - 1) / cols_per_block);
if (max_tiles == 0) {
return;
}
dim3 block_nums_ids(block_nums.x, ids_data->max_tiles, 1);
dim3 block_nums_ids(block_nums.x, ids_data->n_experts, max_tiles);
const uint3 sis1_fd = ids_data->sis1 > 0 ? init_fastdiv_values((uint32_t) ids_data->sis1) : make_uint3(0, 0, 1);
const uint3 nch_fd = init_fastdiv_values((uint32_t) nchannels_dst);
mul_mat_f_ids<T, rows_per_block, cols_per_block, nwarps><<<block_nums_ids, block_dims, nbytes_shared_total, stream>>>
(x, y, ids_data->ids_src_compact, ids_data->ids_dst_compact, ids_data->tile_spec_dev, dst,
(x, y, ids_data->ids_src_compact, ids_data->ids_dst_compact, ids_data->expert_bounds_dev, dst,
ncols_x, ncols_dst, nchannels_dst, stride_row, stride_col_y, stride_col_dst,
channel_ratio, stride_channel_x, stride_channel_y, stride_channel_dst,
sample_ratio, stride_sample_x, stride_sample_y, stride_sample_dst,
+20 -46
View File
@@ -27,16 +27,12 @@ template <> struct mm_ids_pow2<0> { static constexpr int value = 1; };
// Helper function for mul_mat_id, converts ids to a more convenient format.
// ids_src1 describes how to permute the flattened column indices of src1 in order to get a compact src1 tensor sorted by expert.
// ids_dst describes the same mapping but for the dst tensor.
// The first (n_tokens*n_expert_used + J - 1) / tile_width elements of tile_spec contain the specication for
// matrix multiplication tiles at token positions which are exactly divided by tile_width.
// Additionally the following n_expert elements of tile_spec may contain additional tiles that fall between these seams.
// The upper and lower bounds for the ith expert in the compact src1 tensor are stored in expert_bounds[i:i+1].
template <int n_expert_used_template>
__launch_bounds__(ggml_cuda_get_physical_warp_size(), 1)
static __global__ void mm_ids_helper(
const int32_t * __restrict__ ids,
int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec,
const int n_tokens, const int n_expert_used_var, const int nchannels_y, const uint3 tile_width,
const int si1, const int sis1, const bool write_inverse) {
const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
const int n_tokens, const int n_expert_used_var, const int nchannels_y, const int si1, const int sis1, const bool write_inverse) {
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
const int n_expert_used = n_expert_used_template == 0 ? n_expert_used_var : n_expert_used_template;
const int expert = blockIdx.x;
@@ -122,29 +118,19 @@ static __global__ void mm_ids_helper(
return;
}
int col_low = nex_prev;
int col_high = nex_prev + min(int(tile_width.z), it_compact);
expert_bounds[expert] = nex_prev;
// The while loop covers all tiles at the seams containing the column with % tile_width == 0.
while (col_high > fastdiv(col_low + tile_width.z - 1, tile_width) * tile_width.z) {
tile_spec[fastdiv(col_low + tile_width.z - 1, tile_width)]
= {expert, col_low, col_high, /*padding =*/ 0};
col_low = col_high;
col_high = min(col_high + int(tile_width.z), nex_prev + it_compact);
if (expert < static_cast<int>(gridDim.x) - 1) {
return;
}
// Finally, at most one extra tile_spec between the seams per expert.
tile_spec[fastdiv(n_tokens*n_expert_used + tile_width.z - 1, tile_width) + expert]
= {expert, col_low, col_high, /*padding =*/ 0};
expert_bounds[gridDim.x] = nex_prev + it_compact;
}
template <int n_expert_used_template>
static void launch_mm_ids_helper(
const int32_t * __restrict__ ids,
int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec,
const int n_experts, const int n_tokens, const int n_expert_used_var, const int nchannels_y, const int tile_width,
const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
const int n_experts, const int n_tokens, const int n_expert_used_var, const int nchannels_y, const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
GGML_ASSERT(n_tokens < (1 << 22) && "too few bits in mm_ids_helper_store");
GGML_ASSERT(n_expert_used_var < (1 << 10) && "too few bits in mm_ids_helper_store");
@@ -153,53 +139,41 @@ static void launch_mm_ids_helper(
const size_t smpbo = ggml_cuda_info().devices[id].smpbo;
CUDA_SET_SHARED_MEMORY_LIMIT(mm_ids_helper<n_expert_used_template>, smpbo);
const uint3 tile_width_fd = init_fastdiv_values(tile_width);
const dim3 num_blocks(n_experts, 1, 1);
const dim3 block_size(warp_size, 1, 1);
const size_t nbytes_shared = n_tokens*sizeof(mm_ids_helper_store);
GGML_ASSERT(nbytes_shared <= smpbo);
mm_ids_helper<n_expert_used_template><<<num_blocks, block_size, nbytes_shared, stream>>>
(ids, ids_src1, ids_dst, tile_spec, n_tokens, n_expert_used_var, nchannels_y, tile_width_fd, si1, sis1, write_inverse);
(ids, ids_src1, ids_dst, expert_bounds, n_tokens, n_expert_used_var, nchannels_y, si1, sis1, write_inverse);
}
void ggml_cuda_launch_mm_ids_helper(
const int32_t * __restrict__ ids,
int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec,
const int n_experts, const int n_tokens, const int n_expert_used, const int nchannels_y, const int tile_width,
const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
const int32_t * __restrict__ ids, int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, int32_t * __restrict__ expert_bounds,
const int n_experts, const int n_tokens, const int n_expert_used, const int nchannels_y, const int si1, const int sis1, const bool write_inverse, cudaStream_t stream) {
switch (n_expert_used) {
case 2:
launch_mm_ids_helper< 2>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper< 2>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 4:
launch_mm_ids_helper< 4>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper< 4>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 6:
launch_mm_ids_helper< 6>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper< 6>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 8:
launch_mm_ids_helper< 8>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper< 8>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 10:
launch_mm_ids_helper<10>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper<10>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 16:
launch_mm_ids_helper<16>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper<16>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
case 32:
launch_mm_ids_helper<32>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper<32>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
default:
launch_mm_ids_helper< 0>(ids, ids_src1, ids_dst, tile_spec,
n_experts, n_tokens, n_expert_used, nchannels_y, tile_width, si1, sis1, write_inverse, stream);
launch_mm_ids_helper< 0>(ids, ids_src1, ids_dst, expert_bounds, n_experts, n_tokens, n_expert_used, nchannels_y, si1, sis1, write_inverse, stream);
break;
}
}
+2 -13
View File
@@ -1,16 +1,5 @@
#pragma once
struct ggml_cuda_mm_id_tile_spec {
int32_t expert;
int32_t col_low;
int32_t col_high;
int32_t padding;
};
static_assert(sizeof(ggml_cuda_mm_id_tile_spec) == 16, "bad ggml_cuda_mm_id_tile_spec size");
void ggml_cuda_launch_mm_ids_helper(
const int32_t * __restrict__ ids,
int32_t * __restrict__ ids_src1, int32_t * __restrict__ ids_dst, ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec,
const int n_experts, const int n_tokens, const int n_expert_used, const int nchannels_y, const int tile_width,
const int si1, const int sis1, const bool write_inverse, cudaStream_t stream);
const int32_t * ids, int32_t * ids_src1, int32_t * ids_dst, int32_t * expert_bounds,
int n_experts, int n_tokens, int n_expert_used, int nchannels_y, int si1, int sis1, bool write_inverse, cudaStream_t stream);
+9 -4
View File
@@ -1724,8 +1724,10 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
}
}
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4(
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4(
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kb0, const int i_max, const int stride) {
GGML_UNUSED(x_gate);
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / warp_size;
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
@@ -1753,7 +1755,7 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fu
i = min(i, i_max);
}
const block_nvfp4 * bxi = ggml_cuda_mmq_x_row<block_nvfp4, has_fusion>(x, x_gate, kb0, i, stride) + kbx;
const block_nvfp4 * bxi = (const block_nvfp4 *) x + kb0 + i * stride + kbx;
const uint32_t * __restrict__ src_qs = reinterpret_cast<const uint32_t *>(bxi->qs);
const int kqs = 16 * kbx;
const int ksc = 4 * kbx;
@@ -1780,8 +1782,10 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fu
}
}
template <ggml_type type, int J, bool fallback, bool has_fusion> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4_nvfp4(
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_load_tiles_nvfp4_nvfp4(
const char * __restrict__ x, const char * __restrict__ x_gate, int * __restrict__ x_tile, const int kbx0, const int i_max, const int stride) {
GGML_UNUSED(x_gate);
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, GGML_PREC_Q4) / warp_size;
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, GGML_PREC_Q4);
@@ -1796,6 +1800,7 @@ template <ggml_type type, int J, bool fallback, bool has_fusion> static __device
const int kbx = txi % threads_per_row;
const int row_in_warp = txi / threads_per_row;
const block_nvfp4 * bxi_base = (const block_nvfp4 *) x + kbx0 + kbx;
uint32_t * x_u32_scale = x_u32 + 64 + kbx;
#pragma unroll
@@ -1806,7 +1811,7 @@ template <ggml_type type, int J, bool fallback, bool has_fusion> static __device
i = min(i, i_max);
}
const block_nvfp4 * bxi = ggml_cuda_mmq_x_row<block_nvfp4, has_fusion>(x, x_gate, kbx0, i, stride) + kbx;
const block_nvfp4 * bxi = bxi_base + i * stride;
const uint32_t * src_qs = reinterpret_cast<const uint32_t *>(bxi->qs);
+26 -35
View File
@@ -114,17 +114,17 @@ static ggml_prec ggml_cuda_mmq_get_prec_env() {
// src1 is quantized to Q8_1 unless the FP4 types can use 4-bit activations, in which case they
// default to the native W4A4 instructions on Blackwell.
static ggml_prec ggml_cuda_mmq_get_prec_src1(const ggml_type type, const ggml_prec prec_requested, const int cc) {
static ggml_prec ggml_cuda_mmq_get_prec_src1(const ggml_tensor * src0, const ggml_tensor * dst, const int cc) {
static const ggml_prec prec_env = ggml_cuda_mmq_get_prec_env();
ggml_prec prec = prec_env;
if (prec == GGML_PREC_UNDEFINED) {
prec = prec_requested;
prec = (ggml_prec) ggml_get_op_params_i32(dst, 3);
}
// Q4 only for the FP4 types on Blackwell
GGML_ASSERT(prec == GGML_PREC_UNDEFINED || prec == GGML_PREC_Q8 || prec == GGML_PREC_Q4);
const bool can_use_q4 = (type == GGML_TYPE_NVFP4 || type == GGML_TYPE_MXFP4) && blackwell_mma_available(cc);
const bool can_use_q4 = (src0->type == GGML_TYPE_NVFP4 || src0->type == GGML_TYPE_MXFP4) && blackwell_mma_available(cc);
if (prec == GGML_PREC_Q8 || !can_use_q4) {
return GGML_PREC_Q8;
}
@@ -172,18 +172,11 @@ void ggml_cuda_mul_mat_q(
const ggml_tensor * gate = fusion ? fusion->gate : nullptr;
ggml_cuda_mm_fusion_args_device fusion_device = {};
if (fusion) {
GGML_ASSERT(gate && !fusion->x_bias && !fusion->gate_bias && !fusion->x_scale == !fusion->gate_scale);
GGML_ASSERT(gate && !fusion->x_bias && !fusion->gate_bias && !fusion->x_scale && !fusion->gate_scale);
GGML_ASSERT(gate->type == src0->type && ggml_are_same_shape(gate, src0) && ggml_are_same_stride(gate, src0));
// NVFP4 weight scales (only applied for NVFP4): one value per tensor, or one per expert with ids.
for (const ggml_tensor * scale : { fusion->x_scale, fusion->gate_scale }) {
GGML_ASSERT(!scale || (src0->type == GGML_TYPE_NVFP4 && scale->type == GGML_TYPE_F32 && ggml_is_contiguous(scale) &&
ggml_nelements(scale) == (ids ? src0->ne[2] : 1)));
}
fusion_device.gate = gate->data;
fusion_device.x_scale = fusion->x_scale ? fusion->x_scale->data : nullptr;
fusion_device.gate_scale = fusion->gate_scale ? fusion->gate_scale->data : nullptr;
fusion_device.glu_op = fusion->glu_op;
fusion_device.glu_limit = fusion->glu_limit;
fusion_device.gate = gate->data;
fusion_device.glu_op = fusion->glu_op;
fusion_device.glu_limit = fusion->glu_limit;
}
GGML_TENSOR_BINARY_OP_LOCALS;
@@ -231,8 +224,9 @@ void ggml_cuda_mul_mat_q(
const bool fallback = ggml_cuda_mmq_needs_fallback(ne01);
// With fusion dst is the GLU output, not a MUL_MAT; the src1 precision requested by both fused MUL_MATs is in the fusion args.
const ggml_prec prec_src1 = ggml_cuda_mmq_get_prec_src1(src0->type, gate ? fusion->prec_src1 : ggml_prec_get_src(dst, 1), cc);
// With fusion dst is the GLU output, not a MUL_MAT; the fused types always quantize src1 to Q8_1.
// TODO support for Q4 precision if both tensors allow it
const ggml_prec prec_src1 = gate ? GGML_PREC_Q8 : ggml_cuda_mmq_get_prec_src1(src0, dst, cc);
const bool use_native_fp4 = prec_src1 == GGML_PREC_Q4;
const size_t y_block_size = use_native_fp4 ? sizeof(block_fp4_mmq) : sizeof(block_q8_1_mmq);
@@ -247,7 +241,10 @@ void ggml_cuda_mul_mat_q(
ncols_opt = ne12;
// Each expert only sees ne12*n_expert_used/ne02 tokens on average.
ncols_opt = (ne12*n_expert_used + ne02 - 1) / ne02;
// On RDNA3 and RDNA4 it is faster to pick the tile size against this value instead of ne12.
if (GGML_CUDA_CC_IS_RDNA3(cc) || GGML_CUDA_CC_IS_RDNA4_M(cc) || GGML_CUDA_CC_IS_RDNA4(cc)) {
ncols_opt = (ne12*n_expert_used + ne02 - 1) / ne02;
}
}
J_best = ggml_cuda_mmq_get_J_best(src0->type, ncols_opt, fallback, cc, smpbo, prec_src1, &nthreads_best);
@@ -261,8 +258,6 @@ void ggml_cuda_mul_mat_q(
* src1_load_chunk_size;
if (!ids) {
const int ntj = (ne1 + J_best - 1) / J_best;
const size_t nbytes_src1_q8_1 = ne13*ne12 * ne11*ne10_padded * y_block_size/y_values_per_block + src1_q8_1_padding;
ggml_cuda_pool_alloc<char> src1_q8_1(ctx.pool(), nbytes_src1_q8_1);
ggml_cuda_pool_alloc<float> src1_scale(ctx.pool());
@@ -300,7 +295,7 @@ void ggml_cuda_mul_mat_q(
ne00, ne01, ne1, s01, ne11, s1,
ne02, ne12, s02, s12, s2,
ne03, ne13, s03, s13, s3,
ntj, J_best, fusion_device};
ne1, J_best, fusion_device};
ggml_cuda_mul_mat_q_switch_type(ctx, args, stream, prec_src1);
return;
}
@@ -315,9 +310,7 @@ void ggml_cuda_mul_mat_q(
ggml_cuda_pool_alloc<int32_t> ids_src1(ctx.pool(), ne_get_rows);
ggml_cuda_pool_alloc<int32_t> ids_dst(ctx.pool(), ne_get_rows + J_best-1); // Needs to be padded for unconditional memory access.
const int ntj = (ne_get_rows + J_best - 1) / J_best + ne02;
ggml_cuda_pool_alloc<ggml_cuda_mm_id_tile_spec> tile_spec(ctx.pool(), ntj);
ggml_cuda_pool_alloc<int32_t> expert_bounds(ctx.pool(), ne02 + 1);
// gate/up activations are broadcast across experts (ne11 == 1): quantize each token once and
// scatter to its slots. ids_src1 then holds the inverse map (token slot -> compact row).
@@ -328,8 +321,8 @@ void ggml_cuda_mul_mat_q(
const int si1 = ids->nb[1] / ggml_element_size(ids);
const int sis1 = nb12 / nb11;
ggml_cuda_launch_mm_ids_helper((const int32_t *) ids->data, ids_src1.get(), ids_dst.get(), tile_spec.get(),
ne02, ne12, n_expert_used, ne11, J_best, si1, sis1, /*write_inverse =*/ dedup_bcast, stream);
ggml_cuda_launch_mm_ids_helper((const int32_t *) ids->data, ids_src1.get(), ids_dst.get(), expert_bounds.get(),
ne02, ne12, n_expert_used, ne11, si1, sis1, /*write_inverse =*/ dedup_bcast, stream);
CUDA_CHECK(cudaGetLastError());
}
@@ -376,12 +369,12 @@ void ggml_cuda_mul_mat_q(
// Note that ne02 is used instead of ne12 because the number of y channels determines the z dimension of the CUDA grid.
const mmq_args args = {
src0_d, src0->type, (const int *) src1_q8_1.get(), ids_dst.get(), tile_spec.get(), dst_d,
src0_d, src0->type, (const int *) src1_q8_1.get(), ids_dst.get(), expert_bounds.get(), dst_d,
src1_scale.ptr,
ne00, ne01, ne_get_rows, s01, ne_get_rows, s1,
ne02, 1, s02, s12, s2,
ne02, ne02, s02, s12, s2,
ne03, ne13, s03, s13, s3,
ntj, J_best, fusion_device};
ne12, J_best, fusion_device};
ggml_cuda_mul_mat_q_switch_type(ctx, args, stream, prec_src1);
}
@@ -520,20 +513,18 @@ bool ggml_cuda_should_use_mmq(enum ggml_type type, int cc, int64_t ne11, int64_t
return (!GGML_CUDA_CC_IS_CDNA(cc)) || ne11 < MMQ_DP4A_MAX_BATCH_SIZE;
}
bool ggml_cuda_mmq_fusion_is_efficient(
const ggml_type type, const ggml_prec prec_requested, const int64_t nrows_x, const int64_t ncols, const int64_t nchannels, const int device) {
bool ggml_cuda_mmq_fusion_is_efficient(const ggml_type type, const int64_t nrows_x, const int64_t ncols, const int64_t nchannels, const int device) {
const int cc = ggml_cuda_info().devices[device].cc;
const int nsm = ggml_cuda_info().devices[device].nsm;
const size_t smpbo = ggml_cuda_info().devices[device].smpbo;
// Same tile width as ggml_cuda_mul_mat_q; the fused kernel has no fallback and writes I/2 output rows per tile.
const bool fallback = false;
const ggml_prec prec_src1 = ggml_cuda_mmq_get_prec_src1(type, prec_requested, cc);
const int J_best = ggml_cuda_mmq_get_J_best(type, ncols, fallback, cc, smpbo, prec_src1);
// Same tile width as ggml_cuda_mul_mat_q; the fused kernel has no fallback, quantizes src1 to Q8_1 and writes I/2 output rows per tile.
const bool fallback = false;
const int J_best = ggml_cuda_mmq_get_J_best(type, ncols, fallback, cc, smpbo, GGML_PREC_Q8);
if (J_best == 0) {
return false;
}
const int64_t nrows_tile = ggml_cuda_mmq_get_config(type, J_best, fallback, cc, prec_src1).I / 2;
const int64_t nrows_tile = ggml_cuda_mmq_get_config(type, J_best, fallback, cc, GGML_PREC_Q8).I / 2;
const int64_t ntiles = (ncols + J_best - 1)/J_best * ((nrows_x + nrows_tile - 1)/nrows_tile) * nchannels;
// Same condition as launch_mul_mat_q uses to run the stream-k kernel with one block per tile.
const int64_t tiles_nwaves = (ntiles + nsm - 1) / nsm;
+177 -169
View File
@@ -1,7 +1,6 @@
#pragma once
#include "common.cuh"
#include "mmid.cuh"
#include "unary.cuh"
#include <climits>
@@ -15,9 +14,9 @@
typedef void (*ggml_cuda_mmq_load_tiles_t)(const char * __restrict__ x, const char * __restrict__ x_gate, int * x_tile,
const int kbx0, const int i_max, const int stride);
typedef void (*ggml_cuda_mmq_vec_dot_t)(const int * __restrict__ x, const int * __restrict__ y, float * __restrict__ sum, const int k00);
typedef void (*ggml_cuda_mmq_write_back_t)(const float * __restrict__ sum, const int32_t * __restrict__ ids_dst,
float * __restrict__ dst, const float * __restrict__ y_scale, const int stride, const int i_max, const int j_sup,
const ggml_glu_op glu_op, const float glu_limit, const float * __restrict__ x_scale, const float * __restrict__ gate_scale);
typedef void (*ggml_cuda_mmq_write_back_t)(const float * __restrict__ sum, const int32_t * __restrict__ get_rows_to_sorted,
float * __restrict__ dst, const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
const ggml_glu_op glu_op, const float glu_limit);
enum mmq_q8_1_ds_layout {
MMQ_Q8_1_DS_LAYOUT_D4,
@@ -437,8 +436,7 @@ static __host__ int ggml_cuda_mmq_get_nbytes_shared_x(const ggml_cuda_mmq_config
// Weight types with a fused gate/up/GLU path (see ggml_cuda_mmq_get_util_funcs).
static constexpr __host__ __device__ bool ggml_cuda_mmq_fusion_supported(const ggml_type type) {
return type == GGML_TYPE_Q4_K || type == GGML_TYPE_Q5_K || type == GGML_TYPE_Q6_K || type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_XS ||
type == GGML_TYPE_NVFP4;
return type == GGML_TYPE_Q4_K || type == GGML_TYPE_Q5_K || type == GGML_TYPE_Q6_K || type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_XS;
}
// Fused gate/up/GLU: the weight tile alternates chunks of this many up and gate rows, so rows r and r + 8 of an MMA tile (same thread) pair up.
@@ -463,21 +461,22 @@ static __device__ __forceinline__ const block_t * ggml_cuda_mmq_x_row(
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __device__ __forceinline__ void ggml_cuda_mmq_write_back_dp4a(
const float * __restrict__ sum, const int32_t * __restrict__ ids_dst, float * __restrict__ dst,
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_sup,
const ggml_glu_op glu_op, const float glu_limit, const float * __restrict__ x_scale, const float * __restrict__ gate_scale) {
GGML_UNUSED_VARS(glu_op, glu_limit, x_scale, gate_scale);
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
const ggml_glu_op glu_op, const float glu_limit) {
GGML_UNUSED_VARS(glu_op, glu_limit);
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / warp_size;
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
static_assert(I % warp_size == 0, "DP4A MMQ tile height must be a multiple of the physical warp size");
const bool y_scale_used = type == GGML_TYPE_NVFP4 && y_scale != nullptr;
const bool y_scale_used = y_scale != nullptr;
#pragma unroll
for (int j0 = 0; j0 < J; j0 += nwarps) {
const int j = j0 + threadIdx.y;
if (j >= j_sup) {
if (j > j_max) {
return;
}
@@ -489,22 +488,26 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1> static __de
continue;
}
if (y_scale_used) {
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
if constexpr (type == GGML_TYPE_NVFP4) {
if (y_scale_used) {
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
} else {
dst[ids_dst[j]*stride + i] = sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
}
} else {
dst[ids_dst[j]*stride + i] = sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
GGML_UNUSED(y_scale_used);
}
}
}
}
// has_fusion: accumulators l and l + ne/2 hold the up and gate values of one output element, the GLU of the two is written.
// x_scale/gate_scale: optional NVFP4 scales of the up and gate weights (per tensor, or of the expert of this tile).
template<ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fusion>
static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
const float * __restrict__ sum, const int * __restrict__ ids_dst, float * __restrict__ dst,
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_sup,
const ggml_glu_op glu_op, const float glu_limit, const float * __restrict__ x_scale, const float * __restrict__ gate_scale) {
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max,
const ggml_glu_op glu_op, const float glu_limit) {
#if defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
typedef tile<16, 16, int, DATA_LAYOUT_J_MAJOR> tile_C;
@@ -518,12 +521,7 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
const int i0 = (threadIdx.y / ntx) * (ntx*tile_C::I);
const bool y_scale_used = type == GGML_TYPE_NVFP4 && y_scale != nullptr;
// Fused NVFP4: load the weight scales once, 1 without scales.
constexpr bool fused_nvfp4 = has_fusion && type == GGML_TYPE_NVFP4;
const float scale_up = fused_nvfp4 && x_scale ? *x_scale : 1.0f;
const float scale_gate = fused_nvfp4 && gate_scale ? *gate_scale : 1.0f;
const bool y_scale_used = y_scale != nullptr;
#pragma unroll
for (int j0 = 0; j0 < J; j0 += ntx*tile_C::J) {
@@ -533,7 +531,7 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
for (int l = 0; l < (has_fusion ? tile_C::ne/2 : tile_C::ne); ++l) {
const int j = j0 + (threadIdx.y % ntx) * tile_C::J + tile_C::get_j(l);
if (j >= j_sup) {
if (j > j_max) {
continue;
}
@@ -543,29 +541,24 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
continue;
}
if (has_fusion) {
if constexpr (has_fusion) {
const int sum_index = (j0/tile_C::J + n)*tile_C::ne + l;
float up = sum[sum_index];
float gate = sum[sum_index + tile_C::ne/2];
if constexpr (type == GGML_TYPE_NVFP4) {
if (y_scale_used) {
up *= y_scale[j];
gate *= y_scale[j];
}
up *= scale_up;
gate *= scale_gate;
dst[ids_dst[j]*stride + i] = ggml_cuda_op_glu_single(glu_op, sum[sum_index + tile_C::ne/2], sum[sum_index], glu_limit);
} else if constexpr (type == GGML_TYPE_NVFP4) {
if (y_scale_used) {
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/tile_C::J + n)*tile_C::ne + l];
} else {
dst[ids_dst[j]*stride + i] = sum[(j0/tile_C::J + n)*tile_C::ne + l];
}
dst[ids_dst[j]*stride + i] = ggml_cuda_op_glu_single(glu_op, gate, up, glu_limit);
} else if (y_scale_used) {
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/tile_C::J + n)*tile_C::ne + l];
} else {
dst[ids_dst[j]*stride + i] = sum[(j0/tile_C::J + n)*tile_C::ne + l];
GGML_UNUSED(y_scale_used);
}
}
}
}
GGML_UNUSED_VARS(y_scale_used, glu_op, glu_limit, x_scale, gate_scale, scale_up, scale_gate);
GGML_UNUSED_VARS(y_scale_used, glu_op, glu_limit);
}
// -------------------------------------------------------------------------------------------------------------------------------------
@@ -719,7 +712,7 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
case GGML_TYPE_NVFP4:
return ggml_cuda_mmq_util_funcs(
VDR_NVFP4_Q8_1_MMQ,
ggml_cuda_mmq_load_tiles_nvfp4<type, J, fallback, prec_src1, false>,
ggml_cuda_mmq_load_tiles_nvfp4<type, J, fallback, prec_src1>,
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_dp4a<type, J, fallback>,
ggml_cuda_mmq_write_back_dp4a<type, J, fallback, prec_src1>);
default:
@@ -744,9 +737,9 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
if (prec_src1 == GGML_PREC_Q4) {
return ggml_cuda_mmq_util_funcs(
-1,
ggml_cuda_mmq_load_tiles_nvfp4_nvfp4<type, J, fallback, has_fusion>,
ggml_cuda_mmq_load_tiles_nvfp4_nvfp4<type, J, fallback>,
ggml_cuda_mmq_vec_dot_fp4_fp4_mma<type, J, fallback>,
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
}
break;
default:
@@ -889,9 +882,9 @@ static constexpr __device__ ggml_cuda_mmq_util_funcs ggml_cuda_mmq_get_util_func
case GGML_TYPE_NVFP4:
return ggml_cuda_mmq_util_funcs(
-1,
ggml_cuda_mmq_load_tiles_nvfp4<type, J, fallback, prec_src1, has_fusion>,
ggml_cuda_mmq_load_tiles_nvfp4<type, J, fallback, prec_src1>,
ggml_cuda_mmq_vec_dot_q8_0_16_q8_1_mma<type, J, fallback, prec_src1>,
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, has_fusion>);
ggml_cuda_mmq_write_back_mma<type, J, fallback, prec_src1, false>);
default:
return ggml_cuda_mmq_util_funcs(1, nullptr, nullptr, nullptr);
}
@@ -925,9 +918,8 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
const char * __restrict__ x, const char * __restrict__ x_gate, const int offset_x, const int * __restrict__ y,
const int * __restrict__ ids_dst, float * __restrict__ dst, float * __restrict__ tmp_fixup,
const float * __restrict__ y_scale, const ggml_glu_op glu_op, const float glu_limit,
const float * __restrict__ x_scale, const float * __restrict__ gate_scale,
const int stride_row_x, const int ncols_y, const int stride_col_dst,
const int tile_x_i_max, const int tile_y_j_sup, const int kb0_start, const int kb0_stop) {
const int tile_x_max_i, const int tile_y_max_j, const int kb0_start, const int kb0_stop) {
static_assert(!(fixup && has_fusion), "the fused gate/up path does not split K");
static_assert(!(fallback && has_fusion), "the fused gate/up path needs nrows_x % 128 == 0");
@@ -960,7 +952,7 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
constexpr int sz = sizeof(block_q8_1_mmq) / sizeof(int);
for (int kb0 = kb0_start; kb0 < kb0_stop; kb0 += blocks_per_iter) {
load_tiles(x, x_gate, tile_x, offset_x + kb0, tile_x_i_max, stride_row_x);
load_tiles(x, x_gate, tile_x, offset_x + kb0, tile_x_max_i, stride_row_x);
{
const int * by0 = y + ncols_y * (kb0 * qk / ne_block) * sz;
#pragma unroll
@@ -995,9 +987,9 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
}
if (fixup) {
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), y_scale, I, I, J, glu_op, glu_limit, x_scale, gate_scale);
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), y_scale, I, I, J, glu_op, glu_limit);
} else {
write_back(sum, ids_dst, dst, y_scale, stride_col_dst, tile_x_i_max, tile_y_j_sup, glu_op, glu_limit, x_scale, gate_scale);
write_back(sum, ids_dst, dst, y_scale, stride_col_dst, tile_x_max_i, tile_y_max_j, glu_op, glu_limit);
}
}
@@ -1008,28 +1000,21 @@ template <ggml_type type, int J, bool fallback, ggml_prec prec_src1, bool has_fu
__launch_bounds__(ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1), ggml_cuda_mmq_get_occupancy(type, J, fallback, prec_src1))
static __global__ void mul_mat_q(
const char * __restrict__ x, const char * __restrict__ x_gate, const int * __restrict__ y, const int32_t * __restrict__ ids_dst,
const ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec, float * __restrict__ dst, float * __restrict__ tmp_fixup,
const int32_t * __restrict__ expert_bounds, float * __restrict__ dst, float * __restrict__ tmp_fixup,
const float * __restrict__ y_scale, const ggml_glu_op glu_op, const float glu_limit,
const float * __restrict__ x_scale, const float * __restrict__ gate_scale,
const uint3 blocks_per_ne00, const int nrows_x, const int ncols_dst, const int stride_row_x, const int ncols_y, const int stride_col_dst,
const uint3 channel_ratio, const uint3 nchannels_y, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
const uint3 sample_ratio, const uint3 nsamples_y, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst,
const uint3 ntj) {
const uint3 ntx) {
#ifdef TURING_MMA_AVAILABLE
constexpr bool fusion_available = true;
#else
constexpr bool fusion_available = false;
#endif // TURING_MMA_AVAILABLE
#ifdef BLACKWELL_MMA_AVAILABLE
constexpr bool native_fp4_available = true;
#else
constexpr bool native_fp4_available = false;
#endif // BLACKWELL_MMA_AVAILABLE
// Skip unused template specializations for faster compilation (the host only uses 4-bit src1 with native FP4):
if (ggml_cuda_mmq_get_config(type, J, fallback, prec_src1).type == GGML_TYPE_COUNT ||
(has_fusion && (!fusion_available || (prec_src1 == GGML_PREC_Q4 && !native_fp4_available)))) {
// Skip unused template specializations for faster compilation:
if (ggml_cuda_mmq_get_config(type, J, fallback, prec_src1).type == GGML_TYPE_COUNT || (has_fusion && !fusion_available)) {
NO_DEVICE_CODE;
return;
}
@@ -1039,7 +1024,7 @@ static __global__ void mul_mat_q(
constexpr int qk = ggml_cuda_type_traits<type>::qk;
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
const uint32_t nti = (nrows_x + I - 1) / I; // Number of tiles in I direction
const uint32_t nty = (nrows_x + I - 1) / I; // Number of tiles y
// Initialize the ids for writing back data with just the index.
// For regular matrix multiplications this is never changed.
@@ -1064,28 +1049,35 @@ static __global__ void mul_mat_q(
const uint2 tmp2 = fast_div_modulo(blockIdx.z, nchannels_y);
const int wt = tmp2.x;
int zt = tmp2.y;
const int zt = tmp2.y;
const int jt = blockIdx.x;
const int it = blockIdx.y;
// Defaults for regular matrix multiplication:
int col_low = jt*J;
int col_high = min(col_low + J, ncols_dst);
int col_low = 0;
int col_high = ncols_dst;
int col_diff = ncols_dst;
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
int offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
int offset_y_scale;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
} else {
GGML_UNUSED(offset_y_scale);
}
if (ids_dst) {
const ggml_cuda_mm_id_tile_spec tsj = tile_spec[jt];
zt = tsj.expert;
col_low = tsj.col_low;
col_high = tsj.col_high;
col_low = expert_bounds[zt + 0];
col_high = expert_bounds[zt + 1];
col_diff = col_high - col_low;
offset_y = 0;
offset_y_scale = 0;
offset_dst = 0;
offset_y = 0;
offset_dst = 0;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = 0;
}
if (col_low == col_high) {
if (jt*J >= col_diff) {
return;
}
@@ -1098,30 +1090,29 @@ static __global__ void mul_mat_q(
break;
}
ids_dst_shared[j] = ids_dst[col_low + j];
ids_dst_shared[j] = ids_dst[col_low + jt*J + j];
}
__syncthreads();
}
offset_y += col_low * (sizeof(block_q8_1_mmq)/sizeof(int));
offset_y_scale += col_low;
offset_dst += it*I_dst;
offset_y += (col_low + jt*J)*(sizeof(block_q8_1_mmq)/sizeof(int));
offset_dst += it*I_dst;
const float * y_scale_tile = nullptr;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale += col_low + jt*J;
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
}
const float * y_scale_tile = type == GGML_TYPE_NVFP4 && y_scale ? y_scale + offset_y_scale : nullptr;
const int tile_x_i_max = nrows_x - it*I_dst - 1;
const int tile_x_max_i = nrows_x - it*I_dst - 1;
const int tile_y_max_j = col_diff - jt*J - 1;
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I_dst*stride_row_x;
// With ids the NVFP4 weight scales are per expert.
const float * x_scale_tile = x_scale && ids_dst ? x_scale + zt : x_scale;
const float * gate_scale_tile = gate_scale && ids_dst ? gate_scale + zt : gate_scale;
constexpr bool fixup = false;
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, has_fusion>
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
glu_op, glu_limit, x_scale_tile, gate_scale_tile, stride_row_x, ncols_y, stride_col_dst,
tile_x_i_max, col_high - col_low, 0, blocks_per_ne00.z);
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
tile_x_max_i, tile_y_max_j, 0, blocks_per_ne00.z);
return;
}
@@ -1129,8 +1120,8 @@ static __global__ void mul_mat_q(
constexpr int blocks_per_iter = ITER_K / qk;
// kbc == k block continuous, current index in continuous ijk space.
int kbc = int64_t(blockIdx.x) *(nsamples_y.z*nchannels_y.z*ntj.z*nti*blocks_per_ne00.z) / gridDim.x;
int kbc_stop = int64_t(blockIdx.x + 1)*(nsamples_y.z*nchannels_y.z*ntj.z*nti*blocks_per_ne00.z) / gridDim.x;
int kbc = int64_t(blockIdx.x) *(nsamples_y.z*nchannels_y.z*ntx.z*nty*blocks_per_ne00.z) / gridDim.x;
int kbc_stop = int64_t(blockIdx.x + 1)*(nsamples_y.z*nchannels_y.z*ntx.z*nty*blocks_per_ne00.z) / gridDim.x;
kbc -= fastmodulo(kbc, blocks_per_ne00) % blocks_per_iter;
kbc_stop -= fastmodulo(kbc_stop, blocks_per_ne00) % blocks_per_iter;
@@ -1140,34 +1131,41 @@ static __global__ void mul_mat_q(
int kb0_stop = min(blocks_per_ne00.z, uint32_t(kb0_start + kbc_stop - kbc));
while (kbc < kbc_stop && kb0_stop == int(blocks_per_ne00.z)) {
int tmp = fastdiv(kbc, blocks_per_ne00);
uint2 tmp2 = fast_div_modulo(tmp, ntj);
uint2 tmp2 = fast_div_modulo(tmp, ntx);
const int jt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nchannels_y);
int zt = tmp2.y;
const int zt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nsamples_y);
const int wt = tmp2.y;
const int it = tmp2.x;
// Defaults for regular matrix multiplication:
int col_low = jt*J;
int col_high = min(col_low + J, ncols_dst);
int col_low = 0;
int col_high = ncols_dst;
int col_diff = ncols_dst;
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
int offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
int offset_y_scale;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
} else {
GGML_UNUSED(offset_y_scale);
}
if (ids_dst) {
const ggml_cuda_mm_id_tile_spec tsj = tile_spec[jt];
zt = tsj.expert;
col_low = tsj.col_low;
col_high = tsj.col_high;
col_low = expert_bounds[zt + 0];
col_high = expert_bounds[zt + 1];
col_diff = col_high - col_low;
offset_y = 0;
offset_y_scale = 0;
offset_dst = 0;
offset_y = 0;
offset_dst = 0;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = 0;
}
if (col_low == col_high) {
if (jt*J >= col_diff) {
kbc += blocks_per_ne00.z;
kbc -= fastmodulo(kbc, blocks_per_ne00);
@@ -1186,26 +1184,29 @@ static __global__ void mul_mat_q(
break;
}
ids_dst_shared[j] = ids_dst[col_low + j];
ids_dst_shared[j] = ids_dst[col_low + jt*J + j];
}
__syncthreads();
}
offset_y += col_low * (sizeof(block_q8_1_mmq) / sizeof(int));
offset_y_scale += col_low;
offset_dst += it*I;
offset_y += (col_low + jt * J) * (sizeof(block_q8_1_mmq) / sizeof(int));
offset_dst += it*I;
const float * y_scale_tile = nullptr;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale += col_low + jt * J;
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
}
const float * y_scale_tile = type == GGML_TYPE_NVFP4 && y_scale ? y_scale + offset_y_scale : nullptr;
const int tile_x_i_max = nrows_x - it*I - 1;
const int tile_x_max_i = nrows_x - it*I - 1;
const int tile_y_max_j = col_diff - jt*J - 1;
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I*stride_row_x;
constexpr bool fixup = false; // All but (potentially) the last iterations write their data to dst rather than the fixup buffer.
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, false>
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
glu_op, glu_limit, x_scale, gate_scale, stride_row_x, ncols_y, stride_col_dst,
tile_x_i_max, col_high - col_low, kb0_start, kb0_stop);
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
kbc += blocks_per_ne00.z;
kbc -= fastmodulo(kbc, blocks_per_ne00);
@@ -1219,34 +1220,41 @@ static __global__ void mul_mat_q(
}
int tmp = fastdiv(kbc, blocks_per_ne00);
uint2 tmp2 = fast_div_modulo(tmp, ntj);
uint2 tmp2 = fast_div_modulo(tmp, ntx);
const int jt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nchannels_y);
int zt = tmp2.y;
const int zt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nsamples_y);
const int wt = tmp2.y;
const int it = tmp2.x;
// Defaults for regular matrix multiplication:
int col_low = jt*J;
int col_high = min(col_low + J, ncols_dst);
int col_low = 0;
int col_high = ncols_dst;
int col_diff = ncols_dst;
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
int offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
int offset_y_scale;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
} else {
GGML_UNUSED(offset_y_scale);
}
if (ids_dst) {
const ggml_cuda_mm_id_tile_spec tsj = tile_spec[jt];
zt = tsj.expert;
col_low = tsj.col_low;
col_high = tsj.col_high;
col_low = expert_bounds[zt + 0];
col_high = expert_bounds[zt + 1];
col_diff = col_high - col_low;
offset_y = 0;
offset_y_scale = 0;
offset_dst = 0;
offset_y = 0;
offset_dst = 0;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale = 0;
}
if (col_low == col_high) {
if (jt*J >= col_diff) {
return;
}
@@ -1265,30 +1273,33 @@ static __global__ void mul_mat_q(
__syncthreads();
}
offset_y += col_low * (sizeof(block_q8_1_mmq) / sizeof(int));
offset_y_scale += col_low;
offset_dst += it*I;
offset_y += (col_low + jt * J) * (sizeof(block_q8_1_mmq) / sizeof(int));
offset_dst += it*I;
const float * y_scale_tile = nullptr;
if constexpr (type == GGML_TYPE_NVFP4) {
offset_y_scale += col_low + jt * J;
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
}
const float * y_scale_tile = type == GGML_TYPE_NVFP4 && y_scale ? y_scale + offset_y_scale : nullptr;
const int tile_x_i_max = nrows_x - it*I - 1;
const int tile_x_max_i = nrows_x - it*I - 1;
const int tile_y_max_j = col_diff - jt*J - 1;
const int offset_x = fastdiv(wt, sample_ratio)*stride_sample_x + fastdiv(zt, channel_ratio)*stride_channel_x + it*I*stride_row_x;
constexpr bool fixup = true; // Last index writes its data to fixup buffer to avoid data races with other blocks.
mul_mat_q_process_tile<type, J, fallback, fixup, prec_src1, false>
(x, x_gate, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
glu_op, glu_limit, x_scale, gate_scale, stride_row_x, ncols_y, stride_col_dst,
tile_x_i_max, col_high - col_low, kb0_start, kb0_stop);
glu_op, glu_limit, stride_row_x, ncols_y, stride_col_dst,
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
}
template <ggml_type type, int J, bool fallback, ggml_prec prec_src1>
__launch_bounds__(ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1)/2, 1)
static __global__ void mul_mat_q_stream_k_fixup(
const int32_t * __restrict__ ids_dst, const ggml_cuda_mm_id_tile_spec * __restrict__ tile_spec, float * __restrict__ dst,
const int32_t * __restrict__ ids_dst, const int32_t * __restrict__ expert_bounds, float * __restrict__ dst,
float * __restrict__ tmp_last_tile, const uint3 blocks_per_ne00, const int nrows_x, const int ncols_dst,
const int stride_col_dst, const uint3 nchannels_y, const int stride_channel_dst, const uint3 nsamples_y,
const int stride_sample_dst, const uint3 ntj) {
const int stride_sample_dst, const uint3 ntx) {
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
constexpr int nwarps = (ggml_cuda_mmq_get_nthreads(type, J, fallback, prec_src1) / 2) / warp_size;
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback, prec_src1);
@@ -1299,13 +1310,13 @@ static __global__ void mul_mat_q_stream_k_fixup(
float sum[J / nwarps] = {0.0f};
const int i = blockIdx.y*warp_size + threadIdx.x;
const int nti = (nrows_x + I - 1) / I;
const int nty = (nrows_x + I - 1) / I;
const int bidx0 = blockIdx.x;
// kbc == k block continuous, current index in continuous ijk space.
int kbc0 = int64_t(blockIdx.x) *(nsamples_y.z*nchannels_y.z*ntj.z*nti*blocks_per_ne00.z) / gridDim.x;
int kbc0_stop = int64_t(blockIdx.x + 1)*(nsamples_y.z*nchannels_y.z*ntj.z*nti*blocks_per_ne00.z) / gridDim.x;
int kbc0 = int64_t(blockIdx.x) *(nsamples_y.z*nchannels_y.z*ntx.z*nty*blocks_per_ne00.z) / gridDim.x;
int kbc0_stop = int64_t(blockIdx.x + 1)*(nsamples_y.z*nchannels_y.z*ntx.z*nty*blocks_per_ne00.z) / gridDim.x;
kbc0 -= fastmodulo(kbc0, blocks_per_ne00) % blocks_per_iter;
kbc0_stop -= fastmodulo(kbc0_stop, blocks_per_ne00) % blocks_per_iter;
@@ -1324,7 +1335,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
int bidx = bidx0 - 1;
int kbc_stop = kbc0;
while(true) {
int kbc = int64_t(bidx)*(nsamples_y.z*nchannels_y.z*ntj.z*nti*blocks_per_ne00.z) / gridDim.x;
int kbc = int64_t(bidx)*(nsamples_y.z*nchannels_y.z*ntx.z*nty*blocks_per_ne00.z) / gridDim.x;
kbc -= fastmodulo(kbc, blocks_per_ne00) % blocks_per_iter;
if (kbc == kbc_stop) { // Did not have any data.
@@ -1356,11 +1367,11 @@ static __global__ void mul_mat_q_stream_k_fixup(
}
int tmp = fastdiv(kbc0, blocks_per_ne00);
uint2 tmp2 = fast_div_modulo(tmp, ntj);
uint2 tmp2 = fast_div_modulo(tmp, ntx);
const int jt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nchannels_y);
int zt = tmp2.y;
const int zt = tmp2.y;
tmp = tmp2.x;
tmp2 = fast_div_modulo(tmp, nsamples_y);
const int wt = tmp2.y;
@@ -1371,7 +1382,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
dst += offset_dst;
const int i_max = nrows_x - it*I - 1;
const int j_sup = ncols_dst - jt*J;
const int j_max = ncols_dst - jt*J - 1;
if (fallback && i > i_max) {
return;
}
@@ -1380,7 +1391,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
for (int j0 = 0; j0 < J; j0 += nwarps) {
const int j = j0 + threadIdx.y;
if (j >= j_sup) {
if (j > j_max) {
return;
}
@@ -1390,14 +1401,12 @@ static __global__ void mul_mat_q_stream_k_fixup(
}
__shared__ int ids_dst_shared[J];
const ggml_cuda_mm_id_tile_spec tsj = tile_spec[jt];
zt = tsj.expert;
const int col_low = tsj.col_low;
const int col_high = tsj.col_high;
const int j_sup = col_high - col_low;
const int col_low = expert_bounds[zt + 0];
const int col_high = expert_bounds[zt + 1];
const int col_diff = col_high - col_low;
for (int j = threadIdx.y*warp_size + threadIdx.x; j < J; j += nwarps*warp_size) {
ids_dst_shared[j] = ids_dst[col_low + j];
ids_dst_shared[j] = ids_dst[col_low + jt*J + j];
}
__syncthreads();
@@ -1405,6 +1414,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
dst += offset_dst;
const int i_max = nrows_x - it*I - 1;
const int j_max = col_diff - jt*J - 1;
if (fallback && i > i_max) {
return;
}
@@ -1413,7 +1423,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
for (int j0 = 0; j0 < J; j0 += nwarps) {
const int j = j0 + threadIdx.y;
if (j >= j_sup) {
if (j > j_max) {
return;
}
@@ -1422,12 +1432,12 @@ static __global__ void mul_mat_q_stream_k_fixup(
}
struct mmq_args {
const char * x; ggml_type type_x; const int * y; const int32_t * ids_dst;
const ggml_cuda_mm_id_tile_spec * tile_spec; float * dst; const float * y_scale;
const char * x; ggml_type type_x; const int * y; const int32_t * ids_dst; const int32_t * expert_bounds; float * dst;
const float * y_scale;
int64_t ncols_x; int64_t nrows_x; int64_t ncols_dst; int64_t stride_row_x; int64_t ncols_y; int64_t nrows_dst;
int64_t nchannels_x; int64_t nchannels_y; int64_t stride_channel_x; int64_t stride_channel_y; int64_t stride_channel_dst;
int64_t nsamples_x; int64_t nsamples_y; int64_t stride_sample_x; int64_t stride_sample_y; int64_t stride_sample_dst;
int64_t ntj;
int64_t ncols_max;
int J_best; // Tile width in ne11(dense)/ne12(MoE) direction to use for optimal performance.
ggml_cuda_mm_fusion_args_device fusion = {}; // gate != nullptr selects the fused gate/up/GLU path
};
@@ -1459,38 +1469,38 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
}
const int I_dst = has_fusion ? config.I/2 : config.I; // Rows of dst per tile, see mul_mat_q.
const int nti = (args.nrows_x + I_dst - 1) / I_dst;
const int ntzw = args.nchannels_y * args.nsamples_y;
const int nty = (args.nrows_x + I_dst - 1) / I_dst;
const int ntx = (args.ncols_max + config.J - 1) / config.J;
const int ntzw = args.nchannels_y * args.nsamples_y;
// Column tiles innermost so that the concurrently running blocks share the same tile of x and read it from L2.
const dim3 block_nums_xy_tiling(args.ntj, nti, ntzw);
const dim3 block_nums_xy_tiling(ntx, nty, ntzw);
GGML_ASSERT(args.ids_dst || args.nchannels_y % args.nchannels_x == 0);
GGML_ASSERT( args.nsamples_y % args.nsamples_x == 0);
const int channel_ratio = args.ids_dst ? 1 : args.nchannels_y / args.nchannels_x;
const int sample_ratio = args.nsamples_y / args.nsamples_x;
GGML_ASSERT(args.nchannels_y % args.nchannels_x == 0);
GGML_ASSERT(args.nsamples_y % args.nsamples_x == 0);
const int channel_ratio = args.nchannels_y / args.nchannels_x;
const int sample_ratio = args.nsamples_y / args.nsamples_x;
const uint3 blocks_per_ne00_fd = init_fastdiv_values(args.ncols_x / ggml_cuda_type_traits<type>::qk);
const uint3 ntj_fd = init_fastdiv_values(args.ntj);
const uint3 ntx_fd = init_fastdiv_values(ntx);
const uint3 nchannels_y_fd = init_fastdiv_values(args.nchannels_y);
const uint3 nsamples_y_fd = init_fastdiv_values(args.nsamples_y);
const uint3 channel_ratio_fd = init_fastdiv_values(channel_ratio);
const uint3 sample_ratio_fd = init_fastdiv_values(sample_ratio);
if (has_fusion || !config.stream_k) {
GGML_ASSERT(nti <= 65535); // gridDim.y limit
GGML_ASSERT(nty <= 65535); // gridDim.y limit
mul_mat_q<type, J, fallback, prec_src1, has_fusion><<<block_nums_xy_tiling, block_dims, nbytes_shared, stream>>>
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.tile_spec, args.dst, nullptr,
args.y_scale, args.fusion.glu_op, args.fusion.glu_limit, (const float *) args.fusion.x_scale, (const float *) args.fusion.gate_scale,
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.expert_bounds, args.dst, nullptr, args.y_scale, args.fusion.glu_op, args.fusion.glu_limit,
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
ntj_fd);
ntx_fd);
return;
}
// For the stream-k kernel it is possible to run it with tiling by setting the number of CUDA blocks equal to the number of tiles.
// This is worthwhile if the efficiency of tiling is high and skipping the fixup kernel is more important.
const int ntiles_dst = args.ntj * nti * ntzw;
const int ntiles_dst = ntx * nty * ntzw;
const int tiles_nwaves = (ntiles_dst + nsm - 1) / nsm;
const int tiles_efficiency_percent = 100 * ntiles_dst / (nsm*tiles_nwaves);
@@ -1511,12 +1521,11 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
const dim3 block_dims_fixup(block_dims.x, block_dims.y/2, block_dims.z);
mul_mat_q<type, J, fallback, prec_src1, has_fusion><<<block_nums_stream_k, block_dims, nbytes_shared, stream>>>
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.tile_spec, args.dst, tmp_fixup.ptr,
args.y_scale, args.fusion.glu_op, args.fusion.glu_limit, (const float *) args.fusion.x_scale, (const float *) args.fusion.gate_scale,
(args.x, (const char *) args.fusion.gate, args.y, args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr, args.y_scale, args.fusion.glu_op, args.fusion.glu_limit,
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
ntj_fd);
ntx_fd);
if (!fixup_needed) {
return;
@@ -1524,9 +1533,9 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
CUDA_CHECK(cudaGetLastError());
mul_mat_q_stream_k_fixup<type, J, fallback, prec_src1><<<block_nums_fixup, block_dims_fixup, 0, stream>>>
(args.ids_dst, args.tile_spec, args.dst, tmp_fixup.ptr, blocks_per_ne00_fd, args.nrows_x, args.ncols_dst,
(args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr, blocks_per_ne00_fd, args.nrows_x, args.ncols_dst,
args.nrows_dst, nchannels_y_fd, args.stride_channel_dst, nsamples_y_fd, args.stride_sample_dst,
ntj_fd);
ntx_fd);
}
template <ggml_type type, bool fallback, ggml_prec prec_src1, bool has_fusion>
@@ -1652,5 +1661,4 @@ void ggml_cuda_mul_mat_q(
bool ggml_cuda_should_use_mmq(enum ggml_type type, int cc, int64_t ne11, int64_t n_experts);
// prec_requested: the src1 precision requested by the fused MUL_MATs.
bool ggml_cuda_mmq_fusion_is_efficient(ggml_type type, ggml_prec prec_requested, int64_t nrows_x, int64_t ncols, int64_t nchannels, int device);
bool ggml_cuda_mmq_fusion_is_efficient(ggml_type type, int64_t nrows_x, int64_t ncols, int64_t nchannels, int device);
-1
View File
@@ -285,7 +285,6 @@ static constexpr __host__ __device__ int get_mmvq_mmid_max_batch_rdna4(ggml_type
int get_mmvq_mmid_max_batch(ggml_type type, int cc) {
// NVIDIA: P100, Volta, Ada Lovelace, and Blackwell always use MMVQ for MUL_MAT_ID.
if (GGML_CUDA_CC_IS_NVIDIA(cc)) {
cc = ggml_cuda_highest_compiled_arch(cc);
if (cc == GGML_CUDA_CC_PASCAL || cc == GGML_CUDA_CC_VOLTA || cc >= GGML_CUDA_CC_ADA_LOVELACE) {
return MMVQ_MAX_BATCH_SIZE;
}
+3 -2
View File
@@ -591,6 +591,7 @@ void ggml_cuda_op_rope_impl(ggml_backend_cuda_context & ctx,
//const int n_past = ((int32_t *) dst->op_params)[0];
const int n_dims = ((int32_t *) dst->op_params)[1];
const int mode = ((int32_t *) dst->op_params)[2];
//const int n_ctx = ((int32_t *) dst->op_params)[3];
const int n_ctx_orig = ((int32_t *) dst->op_params)[4];
const int n_offs = ((int32_t *) dst->op_params)[15];
mrope_sections sections;
@@ -636,7 +637,7 @@ void ggml_cuda_op_rope_impl(ggml_backend_cuda_context & ctx,
}
rope_corr_dims corr_dims;
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, ggml_rope_get_truncate(dst), corr_dims.v);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims.v);
// compute
if (is_neox) {
@@ -928,7 +929,7 @@ void ggml_cuda_op_rms_norm_mul_rope_fused(ggml_backend_cuda_context & ctx,
const float * freq_factors = rope->src[2] != nullptr ? (const float *) rope->src[2]->data : nullptr;
rope_corr_dims corr_dims;
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, ggml_rope_get_truncate(rope), corr_dims.v);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims.v);
const size_t ts0 = ggml_type_size(x->type);
GGML_ASSERT(x->nb[0] == ts0);
-3
View File
@@ -217,9 +217,6 @@ __global__ void topk_moe_cuda(const float * logits,
const int expert = i + threadIdx.x;
selection_wt[i / WARP_SIZE] =
(n_experts % WARP_SIZE == 0 || expert < n_experts) ? wt[i / WARP_SIZE] + bias[expert] : -INFINITY;
if (__isnanf(selection_wt[i / WARP_SIZE])) {
selection_wt[i / WARP_SIZE] = -FLT_MAX;
}
}
}
+1 -1
View File
@@ -1069,7 +1069,7 @@ static bool ggml_backend_et_device_supports_op(ggml_backend_dev_t dev, const ggm
const bool zero_rot_offset = ggml_get_op_params_i32(op, 15) == 0;
supported =
ggml_rope_get_truncate(op) && zero_view_offset && zero_rot_offset && ndims <= 512 &&
zero_view_offset && zero_rot_offset && ndims <= 512 &&
(is_normal || (is_neox && ndims % 16 == 0) || (is_imrope && ndims % 16 == 0 && has_sections));
} else {
supported = false;
+156 -170
View File
@@ -90,7 +90,7 @@ size_t opt_vmem = HTP_OP_MAX_VMEM_DEFAULT; // max available va space for
int opt_etm = 0;
int opt_verbose = 0;
int opt_profile = 0; // profiling mode (0-disabled, 1-basic, 2-pmu)
bool opt_hostbuf = true;
bool opt_hostbuf = false;
bool opt_dma64 = false;
int opt_power_mode = HTP_POWER_MODE_BURST; // DCVS/HMX power profile requested at session start
@@ -150,7 +150,7 @@ int opt_opqueue = 8; // max number of pending batches
int opt_optrace = 0; // trace buffer size per thread (0 means default)
int opt_oppoll = 0; // polling for batch completions
int opt_opfusion = 1; // enable/disable op fusion
int opt_rt_repack = 1; // runtime repacking: 0 = disabled, 1 = all tileable weights (default)
int opt_rt_repack = 1; // runtime repacking: 0 = disabled, 1 = quantized only (default), 2 = always
static std::regex* opt_opfilter = NULL; // regex of ops to not claim
@@ -463,6 +463,7 @@ struct ggml_hexagon_rpcmem_block {
struct ggml_hexagon_shared_buffer {
ggml_hexagon_session * sess;
std::shared_ptr<ggml_hexagon_rpcmem_block> mem;
std::vector<htp_tensor_extra *> tensor_extra;
bool mapped;
bool pinned;
bool extended;
@@ -485,9 +486,6 @@ struct ggml_hexagon_shared_buffer {
if (err != 0) {
GGML_LOG_ERROR("ggml-hex: %s buffer mapping failed : domain_id %d size %zu fd %d error 0x%08x\n", sess->c_name(),
sess->domain_id, size(), fd(), (unsigned) err);
if (!this->pinned) {
GGML_LOG_ERROR("ggml-hex: %s possibly out of IOVA space, try --no-host\n", sess->c_name());
}
throw std::runtime_error("ggml-hex: fastrpc_mmap failed (see log for details)");
}
@@ -563,6 +561,9 @@ struct ggml_hexagon_shared_buffer {
~ggml_hexagon_shared_buffer() {
free();
for (auto * extra : tensor_extra) {
delete extra;
}
}
};
@@ -621,9 +622,11 @@ inline void ggml_hexagon_session::free_fence(void * ptr, uint32_t n_slots) {
}
inline ggml_tensor ggml_hexagon_session::make_fence_tensor(const volatile void * slot, uint32_t n_slots) const {
static htp_tensor_extra fence_extra { {}, 0, HTP_TENSOR_FENCE };
ggml_tensor t {};
t.buffer = &this->fence_buf->backend_buffer;
t.extra = (void *) (uintptr_t) HTP_TENSOR_FENCE;
t.extra = &fence_extra;
t.data = const_cast<void *>(slot);
t.type = GGML_TYPE_I8;
t.ne[0] = HTP_FENCE_SLOT_SIZE;
@@ -656,7 +659,10 @@ static enum ggml_status ggml_backend_hexagon_buffer_init_tensor(ggml_backend_buf
HEX_VERBOSE("ggml-hex: %s init-tensor %s : base %p data %p nbytes %zu\n", sess->c_name(),
tensor->name, (void *) sbuf->base(), tensor->data, ggml_nbytes(tensor));
tensor->extra = (void *) (uintptr_t) 0;
auto extra = new htp_tensor_extra();
sbuf->tensor_extra.push_back(extra);
tensor->extra = extra;
return GGML_STATUS_SUCCESS;
}
@@ -666,19 +672,42 @@ static void ggml_backend_hexagon_buffer_set_tensor(ggml_backend_buffer_t buffer,
const void * data,
size_t offset,
size_t size) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
sess->flush_sync();
auto extra = (htp_tensor_extra *) tensor->extra;
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
if (ggml_backend_buffer_get_usage(buffer) == GGML_BACKEND_BUFFER_USAGE_WEIGHTS) {
ggml_hexagon_tensor_set_flags(tensor, HTP_TENSOR_WEIGHT);
extra->flags |= HTP_TENSOR_WEIGHT;
if (htp_is_tiled_weight_tensor(tensor)) {
extra->flags |= HTP_TENSOR_TILED;
}
}
HEX_VERBOSE("ggml-hex: %s set-tensor %s : data %p offset %zu size %zu usage %d flags 0x%x\n",
sess->c_name(), tensor->name, data, offset, size, (int) buffer->usage, ggml_hexagon_tensor_get_flags(tensor));
sess->c_name(), tensor->name, data, offset, size, (int) buffer->usage, extra->flags);
memcpy((char *) tensor->data + offset, data, size);
if ((extra->flags & HTP_TENSOR_TILED) == 0) {
memcpy((char *) tensor->data + offset, data, size);
return;
}
if (offset == 0 && size == ggml_nbytes(tensor) && extra->shadow_buf.empty()) {
htp_repack_tensor_tiled(tensor, data, size);
return;
}
if (extra->shadow_buf.size() < ggml_nbytes(tensor)) {
extra->shadow_buf.resize(ggml_nbytes(tensor));
}
memcpy(extra->shadow_buf.data() + offset, data, size);
extra->shadow_size += size;
if (extra->shadow_size >= ggml_nbytes(tensor)) {
htp_repack_tensor_tiled(tensor, extra->shadow_buf.data(), extra->shadow_buf.size());
extra->shadow_buf.clear();
extra->shadow_buf.shrink_to_fit();
extra->shadow_size = 0;
}
}
static void ggml_backend_hexagon_buffer_get_tensor(ggml_backend_buffer_t buffer,
@@ -686,14 +715,14 @@ static void ggml_backend_hexagon_buffer_get_tensor(ggml_backend_buffer_t buffer,
void * data,
size_t offset,
size_t size) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
const uint32_t flags = ggml_hexagon_tensor_get_flags(tensor);
auto extra = (htp_tensor_extra *) tensor->extra;
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
HEX_VERBOSE("ggml-hex: %s get-tensor %s : data %p offset %zu size %zu usage %d flags 0x%x\n",
sess->c_name(), tensor->name, data, offset, size, (int) buffer->usage, flags);
sess->c_name(), tensor->name, data, offset, size, (int) buffer->usage, extra->flags);
if ((flags & HTP_TENSOR_TILED) == 0) {
if ((extra->flags & HTP_TENSOR_TILED) == 0) {
memcpy(data, (const char *) tensor->data + offset, size);
return;
}
@@ -713,27 +742,47 @@ static bool ggml_backend_hexagon_buffer_cpy_tensor(ggml_backend_buffer_t bu
}
static void ggml_backend_hexagon_buffer_set_tensor_2d(ggml_backend_buffer_t buffer,
ggml_tensor * tensor,
const void * data,
size_t offset,
size_t size,
size_t n_copies,
size_t stride_tensor,
size_t stride_data) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
sess->flush_sync();
ggml_tensor * tensor,
const void * data,
size_t offset,
size_t size,
size_t n_copies,
size_t stride_tensor,
size_t stride_data) {
auto extra = (htp_tensor_extra *) tensor->extra;
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
if (ggml_backend_buffer_get_usage(buffer) == GGML_BACKEND_BUFFER_USAGE_WEIGHTS) {
ggml_hexagon_tensor_set_flags(tensor, HTP_TENSOR_WEIGHT);
extra->flags |= HTP_TENSOR_WEIGHT;
if (htp_is_tiled_weight_tensor(tensor)) {
extra->flags |= HTP_TENSOR_TILED;
}
}
HEX_VERBOSE("ggml-hex: %s set-tensor-2d %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d flags 0x%x\n",
sess->c_name(), tensor->name, data, offset, size, n_copies, stride_tensor, stride_data, (int) buffer->usage, ggml_hexagon_tensor_get_flags(tensor));
sess->c_name(), tensor->name, data, offset, size, n_copies, stride_tensor, stride_data, (int) buffer->usage, extra->flags);
if ((extra->flags & HTP_TENSOR_TILED) == 0) {
for (size_t i = 0; i < n_copies; i++) {
memcpy((uint8_t *) tensor->data + offset + i * stride_tensor, (const uint8_t *) data + i * stride_data, size);
}
return;
}
if (extra->shadow_buf.size() < ggml_nbytes(tensor)) {
extra->shadow_buf.resize(ggml_nbytes(tensor));
}
for (size_t i = 0; i < n_copies; i++) {
memcpy((uint8_t *) tensor->data + offset + i * stride_tensor, (const uint8_t *) data + i * stride_data, size);
memcpy(extra->shadow_buf.data() + offset + i * stride_tensor, (const uint8_t *) data + i * stride_data, size);
}
extra->shadow_size += n_copies * size;
if (extra->shadow_size >= ggml_nbytes(tensor)) {
htp_repack_tensor_tiled(tensor, extra->shadow_buf.data(), extra->shadow_buf.size());
extra->shadow_buf.clear();
extra->shadow_buf.shrink_to_fit();
extra->shadow_size = 0;
}
}
@@ -745,14 +794,14 @@ static void ggml_backend_hexagon_buffer_get_tensor_2d(ggml_backend_buffer_t buff
size_t n_copies,
size_t stride_tensor,
size_t stride_data) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
const uint32_t flags = ggml_hexagon_tensor_get_flags(tensor);
auto extra = (htp_tensor_extra *) tensor->extra;
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
HEX_VERBOSE("ggml-hex: %s get-tensor-2d %s : data %p offset %zu size %zu n_copies %zu stride_tensor %zu stride_data %zu usage %d\n",
sess->c_name(), tensor->name, data, offset, size, n_copies, stride_tensor, stride_data, (int) buffer->usage);
if ((flags & HTP_TENSOR_TILED) == 0) {
if ((extra->flags & HTP_TENSOR_TILED) == 0) {
for (size_t i = 0; i < n_copies; i++) {
memcpy((uint8_t *)data + i * stride_data, (const uint8_t *)tensor->data + offset + i * stride_tensor, size);
}
@@ -776,29 +825,6 @@ static void ggml_backend_hexagon_buffer_get_tensor_2d(ggml_backend_buffer_t buff
}
}
static void ggml_backend_hexagon_buffer_set_tensor_n(ggml_backend_buffer_t buffer,
struct ggml_tensor * const * tensors,
const void * const * data,
const size_t * offsets,
const size_t * sizes,
size_t n_tensors) {
const bool is_weights = ggml_backend_buffer_get_usage(buffer) == GGML_BACKEND_BUFFER_USAGE_WEIGHTS;
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
sess->flush_sync();
for (size_t i = 0; i < n_tensors; i++) {
if (sizes[i] == 0) {
continue;
}
if (is_weights) {
ggml_hexagon_tensor_set_flags(tensors[i], HTP_TENSOR_WEIGHT);
}
memcpy((char *) tensors[i]->data + offsets[i], data[i], sizes[i]);
}
}
static void ggml_backend_hexagon_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
@@ -818,7 +844,7 @@ static ggml_backend_buffer_i ggml_backend_hexagon_buffer_interface = {
/* .cpy_tensor = */ ggml_backend_hexagon_buffer_cpy_tensor,
/* .clear = */ ggml_backend_hexagon_buffer_clear,
/* .reset = */ NULL,
/* .set_tensor_n = */ ggml_backend_hexagon_buffer_set_tensor_n,
/* .set_tensor_n = */ NULL,
/* .get_tensor_n = */ NULL,
/* .cpy_tensor_n = */ NULL,
};
@@ -830,12 +856,8 @@ static void ggml_backend_hexagon_host_buffer_set_tensor(ggml_backend_buffer_t bu
const void * data,
size_t offset,
size_t size) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
sess->flush_sync();
memcpy((char *) tensor->data + offset, data, size);
GGML_UNUSED(buffer);
}
static void ggml_backend_hexagon_host_buffer_get_tensor(ggml_backend_buffer_t buffer,
@@ -847,25 +869,6 @@ static void ggml_backend_hexagon_host_buffer_get_tensor(ggml_backend_buffer_t bu
GGML_UNUSED(buffer);
}
static void ggml_backend_hexagon_host_buffer_set_tensor_n(ggml_backend_buffer_t buffer,
struct ggml_tensor * const * tensors,
const void * const * data,
const size_t * offsets,
const size_t * sizes,
size_t n_tensors) {
auto sbuf = (ggml_hexagon_shared_buffer *) buffer->context;
auto sess = sbuf->sess;
sess->flush_sync();
for (size_t i = 0; i < n_tensors; i++) {
if (sizes[i] == 0) {
continue;
}
memcpy((char *) tensors[i]->data + offsets[i], data[i], sizes[i]);
}
}
static ggml_backend_buffer_i ggml_backend_hexagon_host_buffer_interface = {
/* .free_buffer = */ ggml_backend_hexagon_buffer_free_buffer,
/* .get_base = */ ggml_backend_hexagon_buffer_get_base,
@@ -878,7 +881,7 @@ static ggml_backend_buffer_i ggml_backend_hexagon_host_buffer_interface = {
/* .cpy_tensor = */ ggml_backend_hexagon_buffer_cpy_tensor,
/* .clear = */ ggml_backend_hexagon_buffer_clear,
/* .reset = */ NULL,
/* .set_tensor_n = */ ggml_backend_hexagon_host_buffer_set_tensor_n,
/* .set_tensor_n = */ NULL,
/* .get_tensor_n = */ NULL,
/* .cpy_tensor_n = */ NULL,
};
@@ -893,7 +896,7 @@ static ggml_backend_buffer_t ggml_backend_hexagon_buffer_type_alloc_buffer(
ggml_backend_buffer_type_t buffer_type, size_t size) {
auto dev_ctx = static_cast<ggml_backend_hexagon_buffer_type_context *>(buffer_type->context)->dev_ctx;
auto sess = dev_ctx->session();
if (size > sess->max_vmem) {
if (sess && sess->max_vmem && size > sess->max_vmem) {
GGML_LOG_ERROR("ggml-hex: %s buffer size %zu exceeds max_vmem %zu\n",
dev_ctx->c_name(), size, sess->max_vmem);
return nullptr;
@@ -911,7 +914,7 @@ static ggml_backend_buffer_t ggml_backend_hexagon_host_buffer_type_alloc_buffer(
ggml_backend_buffer_type_t buffer_type, size_t size) {
auto dev_ctx = static_cast<ggml_backend_hexagon_buffer_type_context *>(buffer_type->context)->dev_ctx;
auto sess = dev_ctx->session();
if (size > sess->max_vmem) {
if (sess && sess->max_vmem && size > sess->max_vmem) {
GGML_LOG_ERROR("ggml-hex: %s host buffer size %zu exceeds max_vmem %zu\n",
dev_ctx->c_name(), size, sess->max_vmem);
return nullptr;
@@ -931,7 +934,7 @@ static size_t ggml_backend_hexagon_buffer_type_get_alignment(ggml_backend_buffer
}
static size_t ggml_backend_hexagon_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const struct ggml_tensor * t) {
if (opt_rt_repack != 0 && !ggml_backend_buft_is_host(buft) && ggml_op_is_empty(t->op) && htp_is_tiled_weight_tensor(t)) {
if (!ggml_backend_buft_is_host(buft) && htp_is_tiled_weight_tensor(t)) {
int64_t ne0 = hex_round_up(t->ne[0], 32);
int64_t ne1 = hex_round_up(t->ne[1], 32);
int64_t ne2 = t->ne[2];
@@ -1209,7 +1212,8 @@ struct ggml_hexagon_opbatch {
std::vector<htp_tensor> h_tens; // htp tensor descriptors
std::vector<htp_op_desc> h_ops; // htp op descriptors
std::unordered_map<uint64_t, int> t_map; // tensor hash to index
std::unordered_map<const ggml_tensor*, int> t_map; // tensor ptr to index
std::unordered_multimap<void*, int> d_map; // tensor data to index
unsigned int n_bufs; // num buffers in the batch
unsigned int n_tens; // num tensors ...
@@ -1228,6 +1232,7 @@ struct ggml_hexagon_opbatch {
b_vmem = 0;
t_map.clear();
d_map.clear();
ops.resize(n_ops_max);
}
@@ -1247,6 +1252,7 @@ struct ggml_hexagon_opbatch {
h_ops.resize(n_ops_max);
t_map.reserve(n_tens_max);
d_map.reserve(n_tens_max);
GGML_LOG_INFO("ggml-hex: %s op batching: n-bufs %u n-tensors %u n-ops %u vmem %zu\n",
sess->c_name(), n_bufs_max, n_tens_max, n_ops_max, b_vmem_max);
@@ -1292,7 +1298,7 @@ struct ggml_hexagon_opbatch {
}
static void init_tensor_shape(htp_tensor & h, const ggml_tensor * t) {
const uint32_t flags = ggml_hexagon_tensor_get_flags(t);
auto extra = (htp_tensor_extra *) t->extra;
h.type = t->type;
h.ne[0] = t->ne[0];
@@ -1300,7 +1306,7 @@ struct ggml_hexagon_opbatch {
h.ne[2] = t->ne[2];
h.ne[3] = t->ne[3];
if (flags & HTP_TENSOR_TILED) {
if (extra->flags & HTP_TENSOR_TILED) {
if (t->type == GGML_TYPE_F32 || t->type == GGML_TYPE_BF16) {
h.type = GGML_TYPE_F16;
h.nb[0] = sizeof(ggml_fp16_t);
@@ -1321,42 +1327,31 @@ struct ggml_hexagon_opbatch {
h.size = ggml_nbytes(t);
}
h.flags = flags & (HTP_TENSOR_WEIGHT | HTP_TENSOR_TILED | HTP_TENSOR_FENCE);
h.flags = extra->flags & (HTP_TENSOR_WEIGHT | HTP_TENSOR_TILED | HTP_TENSOR_FENCE);
}
static uint64_t hash_tensor(const ggml_tensor * t, const htp_tensor & shape) {
uint64_t hash = 1469598103934665603ull;
auto hash_value = [&hash](uint64_t value) {
hash ^= value;
hash *= 1099511628211ull;
};
hash_value((uintptr_t) t->data);
hash_value(shape.type);
hash_value(shape.size);
hash_value(shape.flags);
for (int i = 0; i < GGML_MAX_DIMS; ++i) {
hash_value(shape.ne[i]);
hash_value(shape.nb[i]);
}
// avalanche high bits down to low bits for power-of-two table indexing
hash ^= hash >> 33;
hash *= 0xff51afd7ed558ccdull;
hash ^= hash >> 33;
return hash;
static bool same_shape(const htp_tensor & a, const htp_tensor & b) {
return (a.type == b.type) &&
(a.ne[0] == b.ne[0]) && (a.ne[1] == b.ne[1]) && (a.ne[2] == b.ne[2]) && (a.ne[3] == b.ne[3]) &&
(a.nb[0] == b.nb[0]) && (a.nb[1] == b.nb[1]) && (a.nb[2] == b.nb[2]) && (a.nb[3] == b.nb[3]);
}
// add tensor and return its index
int add_tensor(const ggml_tensor * t) {
// Lookup by tensor ptr
auto it = t_map.find(t);
if (it != t_map.end()) { return it->second; }
htp_tensor shape {};
init_tensor_shape(shape, t);
const uint64_t hash = hash_tensor(t, shape);
auto it = t_map.find(hash);
if (it != t_map.end()) {
return it->second;
// Lookup by tensor data and shape
auto range = d_map.equal_range(t->data);
for (auto dit = range.first; dit != range.second; ++dit) {
if (same_shape(h_tens[dit->second], shape)) {
t_map.insert({t, dit->second});
return dit->second;
}
}
auto sbuf = static_cast<ggml_hexagon_shared_buffer *>(t->buffer->context);
@@ -1365,7 +1360,8 @@ struct ggml_hexagon_opbatch {
int ti = n_tens++;
GGML_ASSERT(n_tens <= n_tens_max);
t_map[hash] = ti;
t_map.insert({t, ti});
d_map.insert({t->data, ti});
uint64_t t_offset = (uint8_t *) t->data - sbuf->base();
@@ -1395,11 +1391,8 @@ struct ggml_hexagon_opbatch {
auto fit_tensor = [&](const ggml_tensor * t) {
if (!t) return;
if (check_tens) {
htp_tensor shape {};
init_tensor_shape(shape, t);
if (t_map.count(hash_tensor(t, shape))) return;
if (t_map.count(t)) return;
extra_tens++;
}
@@ -2359,7 +2352,6 @@ ggml_hexagon_session::~ggml_hexagon_session() noexcept(true) {
// ** backend interface
static const char * ggml_backend_hexagon_name(ggml_backend_t backend) {
auto sess = static_cast<ggml_hexagon_session *>(backend->context);
return sess->c_name();
@@ -2371,33 +2363,11 @@ static void ggml_backend_hexagon_free(ggml_backend_t backend) {
delete backend;
}
static inline bool ggml_hexagon_op_is_compute(const struct ggml_tensor * node) {
static inline bool op_is_compute(ggml_tensor *node)
{
return !ggml_op_is_empty(node->op) && !ggml_is_empty(node) && (node->flags & GGML_TENSOR_FLAG_COMPUTE);
}
static void ggml_hexagon_tensor_repack(ggml_tensor * tensor, ggml_hexagon_session * sess) {
if (tensor->data == nullptr || ggml_hexagon_tensor_is_tiled(tensor) || !ggml_hexagon_tensor_can_tile(tensor)) {
return;
}
HEX_VERBOSE("ggml-hex: %s rt-repack %s|size %zu\n", sess->c_name(), htp_format_tensor(tensor, HTP_OPFMT_TENSOR).c_str(), ggml_nbytes(tensor));
std::vector<uint8_t> raw(ggml_nbytes(tensor));
memcpy(raw.data(), tensor->data, raw.size());
htp_repack_tensor_tiled(tensor, raw.data(), raw.size());
ggml_hexagon_tensor_set_flags(tensor, HTP_TENSOR_TILED);
}
static void ggml_hexagon_graph_repack(const ggml_cgraph * graph, ggml_hexagon_session * sess) {
for (int i = 0; i < graph->n_nodes; ++i) {
ggml_tensor * n = graph->nodes[i];
if (!ggml_hexagon_op_is_compute(n)) {
continue;
}
if (n->op == GGML_OP_MUL_MAT || n->op == GGML_OP_MUL_MAT_ID) {
ggml_hexagon_tensor_repack(n->src[0], sess);
}
}
}
static ggml_status ggml_backend_hexagon_graph_compute(ggml_backend_t backend, ggml_cgraph * graph) {
auto sess = static_cast<ggml_hexagon_session *>(backend->context);
@@ -2414,15 +2384,13 @@ static ggml_status ggml_backend_hexagon_graph_compute(ggml_backend_t backend, gg
if (cache_hit) {
nodes_ptr = &sess->cached_nodes;
} else {
ggml_hexagon_graph_repack(graph, sess);
htp_fusion_tag_graph(graph);
computed_nodes.reserve(graph->n_nodes);
for (int i = 0; i < graph->n_nodes; ++i) {
ggml_tensor * n = graph->nodes[i];
if (!ggml_hexagon_op_is_compute(n)) {
if (!op_is_compute(n)) {
continue;
}
@@ -2432,7 +2400,22 @@ static ggml_status ggml_backend_hexagon_graph_compute(ggml_backend_t backend, gg
}
htp_opnode node(opcode, n);
if (node.opcode == HTP_OP_MUL_MAT || node.opcode == HTP_OP_MUL_MAT_ID || node.opcode == HTP_OP_GET_ROWS) {
ggml_tensor * src0 = node.node->src[0];
if (htp_needs_repack(src0)) {
HEX_VERBOSE("ggml-hex: %s rt-repack %s|size %zu\n", sess->c_name(), htp_format_tensor(src0, HTP_OPFMT_TENSOR).c_str(), ggml_nbytes(src0));
std::vector<uint8_t> raw(ggml_nbytes(src0));
memcpy(raw.data(), src0->data, raw.size());
htp_repack_tensor_tiled(src0, raw.data(), raw.size());
auto * extra = (htp_tensor_extra *) src0->extra;
extra->flags |= HTP_TENSOR_TILED;
}
}
GGML_ASSERT(htp_kparams_solve(sess, node));
if (node.opcode == HTP_OP_CPY && node.kparams<htp_copy_kernel_params>()->total_elems == 0) {
continue;
}
computed_nodes.push_back(std::move(node));
}
@@ -2642,11 +2625,7 @@ static std::vector<int> ggml_hexagon_graph_optimize_reorder(const std::vector<no
}
static void ggml_backend_hexagon_graph_optimize(ggml_backend_t backend, ggml_cgraph * gf, ggml_backend_graph_optimize_params * params) {
auto sess = static_cast<ggml_hexagon_session *>(backend->context);
HEX_VERBOSE("ggml-hex: %s graph-optimize n_nodes %d\n", sess->c_name(), gf->n_nodes);
ggml_hexagon_graph_repack(gf, sess);
GGML_UNUSED(params);
const int n = gf->n_nodes;
@@ -2681,7 +2660,7 @@ static void ggml_backend_hexagon_graph_optimize(ggml_backend_t backend, ggml_cgr
}
}
GGML_UNUSED(params);
GGML_UNUSED(backend);
}
static uint64_t ggml_hexagon_session_key(const ggml_hexagon_session * sess) {
@@ -2743,7 +2722,14 @@ static bool ggml_backend_hexagon_cpy_tensor_async(ggml_backend_t backend_src, gg
return false;
}
dst->extra = (void *) (uintptr_t) (ggml_hexagon_tensor_get_flags(src) & ~HTP_TENSOR_FUSEABLE);
// FIXME: ggml-meta needs to call init_tensor on auxiliary tensors
if (!dst->extra) {
ggml_backend_buffer_init_tensor(dst->buffer, dst);
}
auto * dst_extra = static_cast<htp_tensor_extra *>(dst->extra);
const auto * src_extra = static_cast<const htp_tensor_extra *>(src->extra);
dst_extra->flags = src_extra->flags & ~HTP_TENSOR_FUSEABLE;
auto sess_src = static_cast<ggml_hexagon_session *>(backend_src->context);
auto sess_dst = static_cast<ggml_hexagon_session *>(backend_dst->context);
@@ -3016,18 +3002,18 @@ static bool ggml_backend_hexagon_device_supports_op(ggml_backend_dev_t dev, cons
}
bool supp = false;
if (ggml_op_is_empty(op->op) || ggml_is_empty(op)) {
supp = true;
} else {
supp = ggml_backend_hexagon_buffer_type_get_alloc_size(&dev_ctx->buffer_type, op) <= sess->max_vmem;
for (int i = 0; supp && i < GGML_MAX_SRC; i++) {
if (op->src[i] && ggml_backend_hexagon_buffer_type_get_alloc_size(&dev_ctx->buffer_type, op->src[i]) > sess->max_vmem) {
supp = false;
}
}
if (supp) {
switch (op->op) {
case GGML_OP_NONE:
case GGML_OP_RESHAPE:
case GGML_OP_VIEW:
case GGML_OP_PERMUTE:
case GGML_OP_TRANSPOSE:
supp = true;
break;
default:
supp = htp_kparams_check(sess, op);
}
break;
}
ggml_hexagon_dump_op_supp(sess->c_name(), op, supp);
+29 -28
View File
@@ -4,6 +4,12 @@
#include "htp-opnode.h"
#include "htp-repack.h"
struct htp_tensor_extra {
std::vector<uint8_t> shadow_buf;
size_t shadow_size { 0 };
uint32_t flags { 0 };
};
extern int opt_arch;
extern size_t opt_ndev;
extern size_t opt_nhvx;
@@ -40,41 +46,36 @@ extern int opt_rt_repack;
if (opt_verbose) GGML_LOG_DEBUG(__VA_ARGS__)
#endif
static inline uint32_t ggml_hexagon_tensor_get_flags(const struct ggml_tensor * t) {
return (uint32_t) (uintptr_t) t->extra;
static inline bool htp_tensor_is_fuseable(const struct ggml_tensor * t) {
if (!t || !t->extra) return false;
const auto * extra = (const struct htp_tensor_extra *) t->extra;
return (extra->flags & HTP_TENSOR_FUSEABLE) != 0;
}
static inline void ggml_hexagon_tensor_set_flags(struct ggml_tensor * t, uint32_t flags) {
t->extra = (void *) (uintptr_t) (ggml_hexagon_tensor_get_flags(t) | flags);
}
static inline void ggml_hexagon_tensor_clear_flags(struct ggml_tensor * t, uint32_t flags) {
t->extra = (void *) (uintptr_t) (ggml_hexagon_tensor_get_flags(t) & ~flags);
}
static inline bool ggml_hexagon_tensor_is_fuseable(const struct ggml_tensor * t) {
return (ggml_hexagon_tensor_get_flags(t) & HTP_TENSOR_FUSEABLE) != 0;
}
static inline bool ggml_hexagon_tensor_is_tiled(const struct ggml_tensor * t) {
return (ggml_hexagon_tensor_get_flags(t) & HTP_TENSOR_TILED) != 0;
}
static inline bool ggml_hexagon_tensor_can_tile(const struct ggml_tensor * t) {
if (opt_rt_repack == 0) {
static inline bool htp_can_rt_repack(const struct ggml_tensor * t) {
if (opt_rt_repack == 0 || !htp_is_tiled_weight_tensor(t)) {
return false;
}
if (t->buffer) {
if (ggml_backend_buft_is_host(t->buffer->buft) ||
ggml_backend_buffer_get_usage(t->buffer) == GGML_BACKEND_BUFFER_USAGE_COMPUTE) {
return false;
}
if (t->buffer && ggml_backend_buft_is_host(t->buffer->buft)) {
return false;
}
return htp_is_tiled_weight_tensor(t);
return opt_rt_repack > 1 || ggml_is_quantized(t->type);
}
static inline bool ggml_hexagon_tensor_will_tile(const struct ggml_tensor * t) {
return ggml_hexagon_tensor_is_tiled(t) || ggml_hexagon_tensor_can_tile(t);
static inline bool htp_tensor_is_tiled(const struct ggml_tensor * t) {
const auto * extra = (const struct htp_tensor_extra *) t->extra;
if (extra && (extra->flags & HTP_TENSOR_TILED)) {
return true;
}
return htp_can_rt_repack(t);
}
static inline bool htp_needs_repack(const struct ggml_tensor * t) {
const auto * extra = (const struct htp_tensor_extra *) t->extra;
if (extra && (extra->flags & HTP_TENSOR_TILED)) {
return false;
}
return htp_can_rt_repack(t);
}
static inline bool htp_tensors_overlap(const struct ggml_tensor * a, const struct ggml_tensor * b) {
+13 -36
View File
@@ -35,7 +35,7 @@ static inline bool htp_match_consumer(
return false;
}
const ggml_tensor * prev_out = prev.dst();
if (!ggml_hexagon_tensor_is_fuseable(prev_out)) {
if (!htp_tensor_is_fuseable(prev_out)) {
return false;
}
if (htp_same_tensor_data(cur.src0(), prev_out)) {
@@ -53,14 +53,14 @@ static bool is_supported_mul_mat_nx_kernel(const ggml_tensor * src0, const struc
if (kparams->n_hmx) {
return kparams->kernel_type == HTP_MM_KERNEL_HMX_2D;
}
return ggml_hexagon_tensor_is_tiled(src0) && (kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW);
return htp_tensor_is_tiled(src0) && (kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW);
}
static bool is_supported_mul_mat_id_nx_kernel(const ggml_tensor * src0, const struct htp_mm_kernel_params * kparams) {
if (kparams->n_hmx) {
return kparams->kernel_type == HTP_MM_KERNEL_HMX_2D;
}
return ggml_hexagon_tensor_is_tiled(src0) &&
return htp_tensor_is_tiled(src0) &&
(kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK);
}
@@ -74,7 +74,7 @@ static bool fuse_allreduce_add(const ggml_hexagon_session * sess, htp_opnode & p
const uint32_t n_ranks = (uint32_t) ar_kparams->n_ranks;
const ggml_tensor * ar_local = prev.inputs[rank];
if (!ggml_hexagon_tensor_is_fuseable(ar_local)) return false;
if (!htp_tensor_is_fuseable(ar_local)) return false;
const ggml_tensor * add_dst = cur.dst();
const ggml_tensor * res_tensor = nullptr;
@@ -157,30 +157,6 @@ static bool fuse_rms_norm_mul(const ggml_hexagon_session * sess, htp_opnode & pr
return true;
}
// post-norm residual: dst = rms_norm(x) * w + r
static bool fuse_rms_norm_mul_add(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
if (prev.opcode != HTP_OP_RMS_NORM_MUL) return false;
const ggml_tensor * r = nullptr;
if (!htp_match_consumer(prev, cur, /*commutative=*/ true, &r)) return false;
const ggml_tensor * src0 = prev.src0();
const ggml_tensor * weight = prev.inputs[1];
const ggml_tensor * dst = cur.dst();
if (!ggml_are_same_layout(src0, r)) return false;
if (htp_inputs_overlap_unsafe(prev, dst, r)) return false;
struct htp_unary_kernel_params kparams;
if (!htp_kparams_solve_rms_norm_mul_add(sess, src0, weight, dst, &kparams)) return false;
prev.opcode = HTP_OP_RMS_NORM_MUL_ADD;
prev.inputs.push_back(r);
prev.outputs[0] = dst;
prev.fused.push_back(cur.node);
prev.set_kparams(kparams);
return true;
}
static bool fuse_rms_norm_scale(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
GGML_UNUSED(sess);
if (prev.opcode != HTP_OP_RMS_NORM && prev.opcode != HTP_OP_RMS_NORM_MUL) return false;
@@ -274,7 +250,7 @@ static bool fuse_mul_mat_nx(const ggml_hexagon_session * sess, htp_opnode & prev
if (x_in != x_orig || w_in->type != w0->type || w_in->ne[0] != w0->ne[0] ||
w0->ne[2] != 1 || w0->ne[3] != 1 || w_in->ne[2] != 1 || w_in->ne[3] != 1 ||
ggml_hexagon_tensor_is_tiled(w_in) != ggml_hexagon_tensor_is_tiled(w0) ||
htp_tensor_is_tiled(w_in) != htp_tensor_is_tiled(w0) ||
orig_kparams->n_hmx != cur_kparams->n_hmx) {
return false;
}
@@ -319,7 +295,7 @@ static bool fuse_mul_mat_id_nx(const ggml_hexagon_session * sess, htp_opnode & p
if (x_in != x || ids_in != ids || w_in->type != w0->type ||
w_in->ne[0] != w0->ne[0] || w_in->ne[2] != w0->ne[2] ||
ggml_hexagon_tensor_is_tiled(w_in) != ggml_hexagon_tensor_is_tiled(w0) ||
htp_tensor_is_tiled(w_in) != htp_tensor_is_tiled(w0) ||
orig_kparams->n_hmx != cur_kparams->n_hmx) {
return false;
}
@@ -622,17 +598,19 @@ int htp_fusion_max(const struct ggml_cgraph * graph, int idx) {
void htp_fusion_tag_graph(struct ggml_cgraph * graph) {
for (int i = 0; i < graph->n_nodes; i++) {
ggml_tensor * node = graph->nodes[i];
ggml_hexagon_tensor_clear_flags(node, HTP_TENSOR_FUSEABLE);
auto * extra = (htp_tensor_extra *) graph->nodes[i]->extra;
if (!extra) continue;
const ggml_op op = node->op;
extra->flags &= ~HTP_TENSOR_FUSEABLE;
const ggml_op op = graph->nodes[i]->op;
if ((op == GGML_OP_RMS_NORM || op == GGML_OP_SCALE || op == GGML_OP_REPEAT ||
op == GGML_OP_UNARY || op == GGML_OP_ADD || op == GGML_OP_SUB ||
op == GGML_OP_MUL || op == GGML_OP_DIV || op == GGML_OP_MUL_MAT_ID ||
op == GGML_OP_SSM_CONV ||
(op == GGML_OP_MUL_MAT && !htp_is_hadamard(node))) &&
(op == GGML_OP_MUL_MAT && !htp_is_hadamard(graph->nodes[i]))) &&
ggml_node_has_n_uses(graph, i, 1)) {
ggml_hexagon_tensor_set_flags(node, HTP_TENSOR_FUSEABLE);
extra->flags |= HTP_TENSOR_FUSEABLE;
}
}
}
@@ -644,7 +622,6 @@ bool htp_fusion_try_fuse(const struct ggml_hexagon_session * sess, struct htp_op
case HTP_OP_ADD:
return (htp_fusion_enabled(HTP_FUSE_ALLREDUCE_ADD) && fuse_allreduce_add(sess, prev, cur)) ||
(htp_fusion_enabled(HTP_FUSE_MUL_MAT_ADD) && fuse_mul_mat_add(sess, prev, cur)) ||
(htp_fusion_enabled(HTP_FUSE_RMS_NORM_MUL_ADD) && fuse_rms_norm_mul_add(sess, prev, cur)) ||
(htp_fusion_enabled(HTP_FUSE_BINARY_NX) && fuse_binary_nx(sess, prev, cur));
case HTP_OP_MUL:
return (htp_fusion_enabled(HTP_FUSE_RMS_NORM_MUL) && fuse_rms_norm_mul(sess, prev, cur)) ||
-1
View File
@@ -16,7 +16,6 @@ enum htp_fusion_flags {
HTP_FUSE_BINARY_NX = (1 << 10), // 1024
HTP_FUSE_RMS_NORM_SCALE = (1 << 11), // 2048
HTP_FUSE_SSM_CONV_SILU = (1 << 12), // 4096
HTP_FUSE_RMS_NORM_MUL_ADD = (1 << 13), // 8192
};
static inline bool htp_fusion_enabled(int flag) {
+70 -311
View File
@@ -62,7 +62,6 @@ htp_op_code htp_op_remap(const struct ggml_tensor * t) {
case GGML_OP_IM2COL_3D: return HTP_OP_IM2COL_3D;
case GGML_OP_CONV_2D: return HTP_OP_CONV_2D;
case GGML_OP_CONV_2D_DW: return HTP_OP_CONV_2D_DW;
case GGML_OP_CONV_3D: return HTP_OP_CONV_3D;
case GGML_OP_ROLL: return HTP_OP_ROLL;
case GGML_OP_UPSCALE: return HTP_OP_UPSCALE;
case GGML_OP_LIGHTNING_INDEXER: return HTP_OP_LIGHTNING_INDEXER;
@@ -629,7 +628,7 @@ static bool htp_matmul_is_hmx_eligible(
return false;
}
if (!htp_is_tiled_weight_type((ggml_type) wtype)) {
if (!htp_is_hmx_weight_type((ggml_type) wtype)) {
return false;
}
@@ -637,7 +636,7 @@ static bool htp_matmul_is_hmx_eligible(
return false;
}
const bool is_tiled = ggml_hexagon_tensor_will_tile(src0);
const bool is_tiled = htp_tensor_is_tiled(src0);
if (ggml_is_quantized((ggml_type) wtype) && !is_tiled) {
return false;
}
@@ -675,7 +674,7 @@ static bool htp_kparams_solve_hmx_mm(
size_t vtcm_budget,
struct htp_mm_kernel_params * kparams
) {
const bool is_tiled = ggml_hexagon_tensor_will_tile(src0);
const bool is_tiled = htp_tensor_is_tiled(src0);
const int aligned_tile_size = is_tiled ? htp_tensor_get_tile_padded_size(wtype) : 0;
const int n_threads = (int) htp_session_n_threads(sess);
const uint32_t mdev_count = htp_session_mdev_count(sess);
@@ -894,7 +893,7 @@ static bool htp_kparams_solve_matmul_impl(
const int ne12 = src1->ne[2];
const int ne13 = src1->ne[3];
const bool is_tiled = ggml_hexagon_tensor_will_tile(src0);
const bool is_tiled = htp_tensor_is_tiled(src0);
const int wtype = (is_tiled && (src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_BF16)) ? GGML_TYPE_F16 : src0->type;
const int ne00_padded = is_tiled ? hex_round_up(ne00, htp_tensor_get_tile_k(wtype)) : ne00;
const int ne11_padded = hex_round_up(ne11, 32);
@@ -1110,7 +1109,7 @@ bool htp_kparams_solve_mmnx(
const int ne12 = act->ne[2];
const int ne13 = act->ne[3];
const bool is_tiled = ggml_hexagon_tensor_will_tile(src0);
const bool is_tiled = htp_tensor_is_tiled(src0);
const int wtype = (is_tiled && (src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_BF16)) ? GGML_TYPE_F16 : src0->type;
const int ne00_padded = is_tiled ? hex_round_up(ne00, 32) : ne00;
const int ne11_padded = hex_round_up(ne11, 32);
@@ -1583,15 +1582,11 @@ static bool htp_kparams_solve_unary_impl(
kparams->n_threads = n_threads;
const bool has_weight = (op == HTP_OP_RMS_NORM_MUL || op == HTP_OP_RMS_NORM_MUL_ADD);
const bool broadcast_weight = has_weight && (src1->ne[1] * src1->ne[2] * src1->ne[3] == 1);
if (op == HTP_OP_RMS_NORM_MUL_ADD && !broadcast_weight) {
return false;
}
const bool broadcast_weight = (op == HTP_OP_RMS_NORM_MUL) && (src1->ne[1] * src1->ne[2] * src1->ne[3] == 1);
kparams->broadcast_weight = broadcast_weight;
const bool src0_contig = ggml_is_contiguous_1(src0);
const bool src1_contig = (has_weight && !broadcast_weight) ? ggml_is_contiguous_1(src1) : true;
const bool src1_contig = (op == HTP_OP_RMS_NORM_MUL && !broadcast_weight) ? ggml_is_contiguous_1(src1) : true;
const uint32_t src0_nrows_per_thread = (src0_nrows + n_threads - 1) / n_threads;
const uint32_t min_block_rows = (uint32_t) (std::max<size_t>(1, 2048 / dst_data_row_size));
uint32_t max_block = (src0_contig && src1_contig) ? src0_nrows : (uint32_t) src0->ne[1];
@@ -1607,7 +1602,7 @@ static bool htp_kparams_solve_unary_impl(
uint32_t vtcm_row_per_thread = 0;
htp_unary_solve_layout(&L, op, src0->ne[0], dst->ne[0],
has_weight ? src1->ne[0] : 0,
op == HTP_OP_RMS_NORM_MUL ? src1->ne[0] : 0,
max_block, broadcast_weight, n_threads, vtcm_size, elem_size,
&col_tile, &vtcm_row_per_thread);
@@ -1646,16 +1641,6 @@ bool htp_kparams_solve_rms_norm_mul(
return htp_kparams_solve_unary_impl(sess, HTP_OP_RMS_NORM_MUL, src0, src1, dst, kparams);
}
bool htp_kparams_solve_rms_norm_mul_add(
const struct ggml_hexagon_session * sess,
const struct ggml_tensor * src0,
const struct ggml_tensor * src1,
const struct ggml_tensor * dst,
struct htp_unary_kernel_params * kparams
) {
return htp_kparams_solve_unary_impl(sess, HTP_OP_RMS_NORM_MUL_ADD, src0, src1, dst, kparams);
}
bool htp_kparams_solve_repeat_get_rows(
const struct ggml_hexagon_session * sess,
const struct ggml_tensor * src0,
@@ -1698,7 +1683,7 @@ bool htp_kparams_solve_repeat_get_rows(
const uint32_t ne12 = src1->ne[2];
const uint32_t nr = ne10 * ne11 * ne12;
const bool tiled = ggml_hexagon_tensor_is_tiled(src0);
const bool tiled = htp_tensor_is_tiled(src0);
const int wtype = (tiled && (src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_BF16)) ? GGML_TYPE_F16 : src0->type;
if (tiled) {
@@ -1889,10 +1874,6 @@ static bool htp_kparams_solve_rope(
const struct ggml_tensor * src2 = op->src[2];
const struct ggml_tensor * dst = op;
if (!ggml_rope_get_truncate(op)) {
return false;
}
if (!ggml_are_same_shape(src0, dst)) {
return false;
}
@@ -3959,84 +3940,6 @@ static bool htp_kparams_conv2d_win(
}
}
uint32_t n_threads = nth;
// Keep the fold schedule, where output traffic can dominate input packing.
if (!fold && rt_block < n_rt && ct > 0 && nth > 0) {
const uint32_t old_rt = rt_block;
const uint32_t ct_max = (std::min)((std::min)((std::max)(4 * halo, (n_tiles + 3) / 4), 512u), n_tiles);
const size_t budget = htp_session_vtcm_size(sess);
// Approximate chunk overhead in input crouton work units.
const size_t c_chunk = 8;
const uint32_t tail = n_tiles % ct;
const size_t work =
(size_t) (n_tiles / ct) * ((ct + halo + nth - 1) / nth) + (tail ? (tail + halo + nth - 1) / nth : 0);
size_t best_cost =
(size_t) ((n_rt + rt_block - 1) / rt_block) * (n_cc * work + c_chunk * (n_tiles / ct + (tail != 0)));
bool found = false;
// Keep output DMA segments. Try larger kernel blocks to avoid rebuilding input croutons.
for (uint32_t rb = n_rt; rb > old_rt && !found; rb = (rb + 1) / 2) {
for (uint32_t nt = nth; nt > 0; nt /= 2) {
for (uint32_t sb = 8; sb > 0; sb /= 2) {
const size_t input_stage = hex_round_up(cwhn ? (size_t) sb * 32 * Cin * sizeof(float) + 256 : (size_t) n_ch * sb * 32 * sizeof(float), 128);
const size_t scratch = hex_round_up(
(std::max)(2 * input_stage + (size_t) HTP_CONV2D_WIN_OBUF * 32 * seg_t * 32 * sizeof(float),
wscr),
tile);
if (n_cc == 0 || n_wt > budget / tile / n_cc / rb || halo > budget / tile / n_cc ||
scratch > budget / nt) {
continue;
}
const size_t weights = (size_t) rb * n_wt * n_cc * tile;
htp_conv2d_vtcm_layout fixed;
htp_conv2d_vtcm_layout_build(&fixed, rb, weights, (size_t) n_cc * halo * tile, 0, scratch, nt);
if (fixed.total_bytes >= budget) {
continue;
}
const size_t per_ct = 2 * ((size_t) n_cc + rb) * tile;
const uint32_t cap = (uint32_t) (std::min)((size_t) ct_max, (budget - fixed.total_bytes) / per_ct);
const uint32_t aligned = (cap + halo) / nt * nt;
const uint32_t candidates[2] = { cap, aligned > halo ? aligned - halo : 0 };
for (uint32_t i = 0; i < 2; i++) {
const uint32_t candidate = candidates[i];
if (candidate == 0 || (i > 0 && candidate == cap)) {
continue;
}
if (hex_round_up((size_t) T * sizeof(uint32_t), 128) + 2 * hex_round_up((size_t) candidate, 128) > HTP_DDR_SPAD_SIZE) {
continue;
}
const uint32_t rem = n_tiles % candidate;
const size_t input_work = (size_t) (n_tiles / candidate) * ((candidate + halo + nt - 1) / nt) + (rem ? (rem + halo + nt - 1) / nt : 0);
const size_t chunks = n_tiles / candidate + (rem != 0);
const size_t cost = (size_t) ((n_rt + rb - 1) / rb) * (n_cc * input_work + c_chunk * chunks);
if (cost < best_cost) {
const size_t input = (size_t) n_cc * (candidate + halo) * tile;
const size_t output = (size_t) rb * candidate * tile;
htp_conv2d_vtcm_layout L;
htp_conv2d_vtcm_layout_build(&L, rb, weights, input, output, scratch, nt);
if (L.total_bytes > budget) {
continue;
}
found = true;
best_cost = cost;
rt_block = rb;
ct = candidate;
seg_b = sb;
n_threads = nt;
stage = input_stage;
thr = scratch;
act = input;
out = output;
w_size = weights;
total = L.total_bytes;
n_bc = candidate + halo;
}
}
}
}
}
}
if (hex_round_up((size_t) T * sizeof(uint32_t), 128) + 2 * hex_round_up((size_t) ct, 128) > HTP_DDR_SPAD_SIZE) {
return false;
}
@@ -4047,7 +3950,7 @@ static bool htp_kparams_conv2d_win(
const uint32_t n_cs = (32 + CS - 1) / CS;
kp->kernel_type = (uint8_t) HTP_CONV2D_KERNEL_HMX_WINDOW;
kp->n_threads = (uint8_t) n_threads;
kp->n_threads = (uint8_t) nth;
kp->cwhn = cwhn ? 1 : 0;
kp->w_kmajor = !w_rows ? 1 : 0;
kp->n_row_tiles = n_rt;
@@ -4075,50 +3978,6 @@ static bool htp_kparams_conv2d_win(
return true;
}
struct htp_conv_norm_geom {
uint32_t KW, KH, Cin, Cout;
uint32_t OW, OH, N;
int32_t s0, d0;
size_t es;
bool is3d;
};
static bool htp_conv_normalize_geom(const struct ggml_tensor * dst, struct htp_conv_norm_geom * g) {
const struct ggml_tensor * src0 = dst->src[0];
const bool is3d = dst->op == GGML_OP_CONV_3D;
const int32_t s0 = ggml_get_op_params_i32(dst, 0);
const int32_t d0 = ggml_get_op_params_i32(dst, is3d ? 6 : 4);
const int64_t kh_w = is3d ? src0->ne[2] * src0->ne[1] : src0->ne[1];
const int64_t cin_w = is3d ? ggml_get_op_params_i32(dst, 9) : src0->ne[2];
const int64_t oh_w = is3d ? dst->ne[2] * dst->ne[1] : dst->ne[1];
const int64_t n_w = is3d ? ggml_get_op_params_i32(dst, 10) : dst->ne[3];
const int64_t k_w = src0->ne[0] * kh_w * cin_w;
const int64_t ct_w = n_w * oh_w * ((dst->ne[0] + 31) / 32);
if (kh_w < 1 || kh_w > 255 || k_w < 1 || k_w > (1 << 24) || ct_w < 1 || ct_w > UINT32_MAX) {
return false;
}
if (s0 <= 0 || d0 <= 0 || dst->nb[0] != sizeof(float)) {
return false;
}
*g = {
/* .KW = */ (uint32_t) src0->ne[0],
/* .KH = */ (uint32_t) kh_w,
/* .Cin = */ (uint32_t) cin_w,
/* .Cout = */ (uint32_t) (is3d ? ggml_get_op_params_i32(dst, 11) : src0->ne[3]),
/* .OW = */ (uint32_t) dst->ne[0],
/* .OH = */ (uint32_t) oh_w,
/* .N = */ (uint32_t) n_w,
/* .s0 = */ s0,
/* .d0 = */ d0,
/* .es = */ ggml_type_size(src0->type),
/* .is3d = */ is3d,
};
return true;
}
// Tiled implicit GEMM, MMA on HMX if available, else HVX
static bool htp_kparams_conv2d_tiled(
const struct ggml_hexagon_session * sess,
@@ -4129,129 +3988,92 @@ static bool htp_kparams_conv2d_tiled(
return false;
}
struct htp_conv_norm_geom g;
if (!htp_conv_normalize_geom(dst, &g)) {
const struct ggml_tensor * src0 = dst->src[0];
const struct ggml_tensor * src1 = dst->src[1];
const int32_t s0 = ggml_get_op_params_i32(dst, 0);
const int32_t d0 = ggml_get_op_params_i32(dst, 4);
const uint32_t KW = src0->ne[0];
const uint32_t KH = src0->ne[1];
const uint32_t Cin = src0->ne[2];
const uint32_t Cout = src0->ne[3];
const uint32_t OW = dst->ne[0];
const uint32_t OH = dst->ne[1];
const uint32_t N = dst->ne[3];
const size_t es = ggml_type_size(src0->type);
if (s0 != 1 || dst->nb[0] != sizeof(float)) {
return false;
}
const struct ggml_tensor * src0 = dst->src[0];
const struct ggml_tensor * src1 = dst->src[1];
// kernel k order must match the staged input: (cin, kh, kw) for WHCN input, (kh, kw, cin) for CWHN input.
// Kernel is either k-contiguous rows per cout (WHCN only) or cout-contiguous with a uniform k stride.
const bool whcn = g.is3d || src1->nb[0] == sizeof(float);
const bool cwhn = !whcn && src1->nb[2] == sizeof(float) && src1->nb[0] == g.Cin * sizeof(float);
const bool co_c = g.Cout == 1 || src0->nb[3] == g.es;
const bool w_rows = g.is3d || (whcn && src0->nb[0] == g.es && (g.KH == 1 || src0->nb[1] == g.KW * g.es) && (g.Cin == 1 || src0->nb[2] == g.KW * g.KH * g.es));
const bool whcn = src1->nb[0] == sizeof(float);
const bool cwhn = !whcn && src1->nb[2] == sizeof(float) && src1->nb[0] == Cin * sizeof(float);
const bool co_c = Cout == 1 || src0->nb[3] == es;
const bool w_rows = whcn && src0->nb[0] == es && (KH == 1 || src0->nb[1] == KW * es) && (Cin == 1 || src0->nb[2] == KW * KH * es);
const size_t ks = whcn ? src0->nb[0] : src0->nb[2];
bool kmajor = false;
if (whcn) {
kmajor = co_c && ks >= g.Cout * g.es && (g.KH == 1 || src0->nb[1] == g.KW * ks) && (g.Cin == 1 || src0->nb[2] == g.KW * g.KH * ks);
kmajor = co_c && ks >= Cout * es && (KH == 1 || src0->nb[1] == KW * ks) && (Cin == 1 || src0->nb[2] == KW * KH * ks);
} else if (cwhn) {
kmajor = co_c && ks >= g.Cout * g.es && src0->nb[0] == g.Cin * ks && (g.KH == 1 || src0->nb[1] == g.KW * g.Cin * ks);
kmajor = co_c && ks >= Cout * es && src0->nb[0] == Cin * ks && (KH == 1 || src0->nb[1] == KW * Cin * ks);
}
if (!w_rows && !kmajor) {
return false;
}
kmajor = kmajor && !w_rows;
const uint32_t K = g.KW * g.KH * g.Cin;
const uint32_t kg = !cwhn ? K : g.d0 == 1 ? g.KW * g.Cin : g.Cin;
const uint32_t K = KW * KH * Cin;
const uint32_t kg = !cwhn ? K : d0 == 1 ? KW * Cin : Cin;
const uint32_t n_grp = K / kg;
const uint32_t kg_p = hex_round_up(kg, 32);
const uint32_t k_pad = hex_round_up(n_grp * kg_p, 64);
const uint32_t n_kt = k_pad / 32;
const uint32_t n_rt = (g.Cout + 31) / 32;
const uint32_t n_owt = (g.OW + 31) / 32;
const uint32_t n_ct = g.N * g.OH * n_owt;
const uint32_t n_rt = (Cout + 31) / 32;
const uint32_t n_owt = (OW + 31) / 32;
const uint32_t n_ct = N * OH * n_owt;
const uint32_t wk = (std::min)(k_pad, 512u);
const uint32_t max_threads = htp_session_n_threads(sess);
const uint32_t nth = htp_session_n_threads(sess);
const size_t tile = 2048;
uint32_t rt_block = n_rt;
uint32_t ct = 0;
uint32_t seg = 0;
uint32_t nth = 0;
uint32_t ct = (std::min)(n_ct, 64u);
uint32_t seg = (std::min)(8u, n_owt);
uint32_t n_stage = 1;
uint32_t sr = 0;
size_t stage = 0, thr_size = 0;
size_t best_cost = SIZE_MAX;
const size_t wpack = kmajor ? 2 * (size_t) wk * 64 * sizeof(float) + 96 * 128 : 2 * (size_t) 32 * wk * (sizeof(float) + sizeof(uint16_t));
const size_t budget = htp_session_vtcm_size(sess);
// Approximate chunk overhead in patch K-tile work units.
const size_t c_chunk = 8;
size_t stage = 0, ostage = 0, prow = 0, wpack = 0;
size_t w_size = 0, act_size = 0, out_size = 0, thr_size = 0, total = 0;
// Keep the kernel resident to avoid regenerating the input for each rt block.
// Balance patch work per thread and chunk overhead before reducing rt.
for (uint32_t rb = n_rt; rb > 0 && ct == 0; rb = rb > 1 ? (rb + 1) / 2 : 0) {
for (uint32_t nt = max_threads; nt > 0; nt /= 2) {
for (uint32_t sg = (std::min)(8u, n_owt); sg > 0; --sg) {
// Keep the unused final stride lane for full-vector loads during packing.
const uint64_t wb = (uint64_t) 32 * sg * g.s0 + (uint64_t) (g.KW - 1) * g.d0;
if (wb > budget / sizeof(float) || wb > INT32_MAX) {
continue;
}
const uint64_t width = cwhn ? wb * g.Cin : wb;
if (width > budget / sizeof(float) || width > UINT32_MAX - 63) {
continue;
}
const uint32_t row = hex_round_up((uint32_t) width, cwhn ? 64 : 32);
const uint64_t rows = cwhn ? g.KH : (uint64_t) g.Cin * g.KH;
if (rows > budget / ((uint64_t) row * sizeof(float))) {
continue;
}
const size_t input_stage = cwhn ? (size_t) g.KH * row * sizeof(float) : (size_t) g.Cin * g.KH * row * sizeof(float);
const size_t output_stage = 2 * 32 * (size_t) sg * 32 * sizeof(float);
const size_t transpose = cwhn ? input_stage / 2 + 256 + 64 * 128 : 0;
const size_t scratch = hex_round_up((std::max)(input_stage + output_stage + transpose, wpack), tile);
htp_conv2d_vtcm_layout fixed;
htp_conv2d_vtcm_layout_build(&fixed, rb, (size_t) rb * n_kt * tile, 0, 0, scratch, nt);
if (fixed.total_bytes >= budget) {
continue;
}
// Each spatial tile adds two activation tiles and two output tiles per rt.
const size_t per_ct = 2 * ((size_t) n_kt + rb) * tile;
const uint32_t nc = (uint32_t) (std::min)((size_t) (std::min)(n_ct, 64u), (budget - fixed.total_bytes) / per_ct);
const uint32_t candidates[2] = { nc, nc / nt * nt };
for (uint32_t i = 0; i < 2; i++) {
const uint32_t candidate = candidates[i];
if (candidate == 0 || (i > 0 && candidate == candidates[0])) {
continue;
}
const uint32_t per_thread = (candidate + nt - 1) / nt;
if (sg > per_thread) {
continue;
}
const size_t work = (size_t) (n_ct / candidate) * per_thread + (n_ct % candidate + nt - 1) / nt;
const size_t chunks = n_ct / candidate + (n_ct % candidate != 0);
const size_t cost = work * n_kt + c_chunk * chunks;
if (cost < best_cost) {
best_cost = cost;
rt_block = rb;
ct = candidate;
seg = sg;
nth = nt;
sr = row;
stage = input_stage;
thr_size = scratch;
}
}
}
while (true) {
const uint32_t wb = 32 * seg + (KW - 1) * d0;
sr = cwhn ? hex_round_up(wb * Cin, 64) : hex_round_up(wb, 32);
stage = cwhn ? (size_t) KH * sr * sizeof(float) : (size_t) Cin * KH * sr * sizeof(float);
ostage = 2 * 32 * (size_t) seg * 32 * sizeof(float);
prow = cwhn ? stage / 2 + 256 + 64 * 128 : 0;
wpack = kmajor ? 2 * (size_t) wk * 64 * sizeof(float) + 96 * 128 : 2 * (size_t) 32 * wk * (sizeof(float) + sizeof(uint16_t));
thr_size = hex_round_up((std::max)(stage + ostage + prow, wpack), tile);
w_size = (size_t) rt_block * n_kt * tile;
act_size = (size_t) ct * n_kt * tile;
out_size = (size_t) rt_block * ct * tile;
struct htp_conv2d_vtcm_layout L;
htp_conv2d_vtcm_layout_build(&L, rt_block, w_size, act_size, out_size, thr_size, nth);
total = L.total_bytes;
if (total <= htp_session_vtcm_size(sess)) {
break;
}
if (ct > 8) {
ct /= 2;
} else if (seg > 1) {
seg--;
} else if (rt_block > 1) {
rt_block = (rt_block + 1) / 2;
} else if (ct > 1) {
ct /= 2;
} else {
return false;
}
}
if (ct == 0) {
return false;
}
const size_t w_size = (size_t) rt_block * n_kt * tile;
const size_t act_size = (size_t) ct * n_kt * tile;
const size_t out_size = (size_t) rt_block * ct * tile;
const size_t ostage = 2 * 32 * (size_t) seg * 32 * sizeof(float);
const size_t prow = cwhn ? stage / 2 + 256 + 64 * 128 : 0;
struct htp_conv2d_vtcm_layout L;
htp_conv2d_vtcm_layout_build(&L, rt_block, w_size, act_size, out_size, thr_size, nth);
size_t total = L.total_bytes;
// Second stage buffer: DMA of the next segment overlaps HVX pack of this one.
// Skip large stages. They already fill VTCM, and the extra DMA slows HMX.
@@ -4259,7 +4081,7 @@ static bool htp_kparams_conv2d_tiled(
const size_t thr2 = hex_round_up((std::max)(2 * stage + ostage + prow, wpack), tile);
struct htp_conv2d_vtcm_layout L2;
htp_conv2d_vtcm_layout_build(&L2, rt_block, w_size, act_size, out_size, thr2, nth);
if (stage <= 128 * 1024 && 2 * g.KH <= 255 && L2.total_bytes <= htp_session_vtcm_size(sess)) {
if (stage <= 128 * 1024 && L2.total_bytes <= htp_session_vtcm_size(sess)) {
n_stage = 2;
thr_size = thr2;
total = L2.total_bytes;
@@ -4288,9 +4110,7 @@ static bool htp_kparams_conv2d_tiled(
kp->tiled.n_stage = n_stage;
kp->tiled.wk_chunk = wk;
kp->tiled.div_ow_tiles = init_fastdiv_values(n_owt);
kp->tiled.div_oh = init_fastdiv_values(g.OH);
kp->tiled.div_khr = init_fastdiv_values(src0->ne[1]);
kp->tiled.div_ohr = init_fastdiv_values(dst->ne[1]);
kp->tiled.div_oh = init_fastdiv_values(OH);
return true;
}
@@ -4339,10 +4159,6 @@ static bool htp_kparams_solve_conv2d(
const int32_t d0 = ggml_get_op_params_i32(op, 4);
const int32_t d1 = ggml_get_op_params_i32(op, 5);
if (s0 <= 0 || s1 <= 0 || d0 <= 0 || d1 <= 0) {
return false;
}
const int64_t ow = (src1->ne[0] + 2 * p0 - d0 * (src0->ne[0] - 1) - 1) / s0 + 1;
const int64_t oh = (src1->ne[1] + 2 * p1 - d1 * (src0->ne[1] - 1) - 1) / s1 + 1;
if (ow != dst->ne[0] || oh != dst->ne[1] || src0->ne[3] != dst->ne[2] || src1->ne[3] != dst->ne[3]) {
@@ -4517,60 +4333,6 @@ static bool htp_kparams_solve_conv2d_dw(
return true;
}
static bool htp_kparams_solve_conv3d(
const struct ggml_hexagon_session * sess,
const struct ggml_tensor * op,
struct htp_conv2d_kernel_params * kparams
) {
memset(kparams, 0, sizeof(*kparams));
const struct ggml_tensor * src0 = op->src[0];
const struct ggml_tensor * src1 = op->src[1];
const struct ggml_tensor * dst = op;
if ((src0->type != GGML_TYPE_F16 && src0->type != GGML_TYPE_F32) ||
src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
return false;
}
const int32_t IC = ggml_get_op_params_i32(op, 9);
const int32_t N = ggml_get_op_params_i32(op, 10);
const int32_t OC = ggml_get_op_params_i32(op, 11);
if (IC <= 0 || N <= 0 || OC <= 0 || src0->ne[3] != (int64_t) IC * OC || src1->ne[3] != (int64_t) IC * N ||
dst->ne[3] != (int64_t) OC * N) {
return false;
}
for (int i = 0; i < 3; i++) {
const int32_t s = ggml_get_op_params_i32(op, i);
const int32_t p = ggml_get_op_params_i32(op, 3 + i);
const int32_t d = ggml_get_op_params_i32(op, 6 + i);
if (s <= 0 || d <= 0 || p < 0 || src0->ne[i] <= 0) {
return false;
}
const int64_t o = (src1->ne[i] + 2 * p - d * (src0->ne[i] - 1) - 1) / s + 1;
if (o != dst->ne[i]) {
return false;
}
}
const size_t es = ggml_type_size(src0->type);
const bool w_rows = src0->nb[0] == es && src0->nb[1] == src0->ne[0] * es && src0->nb[2] == src0->ne[1] * src0->nb[1] &&
((int64_t) IC * OC == 1 || src0->nb[3] == src0->ne[2] * src0->nb[2]);
if (!w_rows || src1->nb[0] != sizeof(float) || dst->nb[2] != dst->ne[1] * dst->nb[1]) {
return false;
}
const int64_t total_rows = dst->ne[1] * dst->ne[2] * dst->ne[3];
if (total_rows > UINT32_MAX) {
return false;
}
kparams->total_rows = (uint32_t) total_rows;
kparams->n_threads = total_rows == 0 ? 1 : (std::min)((uint32_t) htp_session_n_threads(sess), (uint32_t) total_rows);
return htp_kparams_conv2d_tiled(sess, op, kparams);
}
static bool htp_dsv4_hc_pre_can_collapse(
const struct ggml_tensor * x,
const struct ggml_tensor * weights,
@@ -5475,9 +5237,6 @@ bool htp_kparams_solve(
case HTP_OP_CONV_2D_DW:
return htp_kparams_invoke<htp_kparams_solve_conv2d_dw>(sess, op, kparams);
case HTP_OP_CONV_3D:
return htp_kparams_invoke<htp_kparams_solve_conv3d>(sess, op, kparams);
case HTP_OP_SUM_ROWS:
case HTP_OP_SUM:
case HTP_OP_ARGMAX:
-8
View File
@@ -95,14 +95,6 @@ bool htp_kparams_solve_rms_norm_mul(
struct htp_unary_kernel_params * kparams
);
bool htp_kparams_solve_rms_norm_mul_add(
const struct ggml_hexagon_session * sess,
const struct ggml_tensor * src0,
const struct ggml_tensor * src1,
const struct ggml_tensor * dst,
struct htp_unary_kernel_params * kparams
);
bool htp_kparams_solve_matmul_add(
const struct ggml_hexagon_session * sess,
const struct ggml_tensor * src0,
+16 -11
View File
@@ -8,30 +8,31 @@
#include "htp/htp-tensor.h"
static inline bool htp_is_tiled_weight_type(enum ggml_type type) {
return type == GGML_TYPE_Q1_0 || type == GGML_TYPE_Q2_0 ||
type == GGML_TYPE_Q4_0 || type == GGML_TYPE_Q4_1 ||
type == GGML_TYPE_MXFP4 || type == GGML_TYPE_IQ4_NL ||
type == GGML_TYPE_Q8_0 || type == GGML_TYPE_Q6_K ||
type == GGML_TYPE_Q2_K || type == GGML_TYPE_Q3_K ||
return type == GGML_TYPE_Q4_0 || type == GGML_TYPE_Q4_1 ||
type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_NL ||
type == GGML_TYPE_MXFP4 || type == GGML_TYPE_Q1_0 ||
type == GGML_TYPE_Q6_K ||
type == GGML_TYPE_Q4_K || type == GGML_TYPE_Q5_K ||
type == GGML_TYPE_Q3_K || type == GGML_TYPE_Q2_K ||
type == GGML_TYPE_Q2_0 ||
type == GGML_TYPE_F16 || type == GGML_TYPE_F32 ||
type == GGML_TYPE_BF16;
}
static inline bool htp_is_tiled_weight_tensor(const struct ggml_tensor * t) {
if (!htp_is_tiled_weight_type(t->type)) {
return false;
}
if (!ggml_is_contiguous(t) || ggml_is_view(t)) {
return false;
}
if (t->ne[0] < 32 || (t->ne[0] % 32 != 0) || t->ne[3] != 1) {
if (!htp_is_tiled_weight_type(t->type)) {
return false;
}
if (t->ne[0] < 32) {
return false;
}
if (!ggml_is_quantized(t->type)) {
return t->ne[1] > 1;
return (t->ne[0] % 32 == 0) && (t->ne[1] % 32 == 0) && (t->ne[2] == 1) && (t->ne[3] == 1);
}
return true;
return t->ne[3] == 1;
}
// Size of one tiled row in the DSP tiled layout. The Q3_K..Q6_K tiles store uncompressed scales/mins,
@@ -58,6 +59,10 @@ static inline size_t htp_tiled_row_size(enum ggml_type type, int64_t ne0) {
return ggml_row_size(type, ne0);
}
static inline bool htp_is_hmx_weight_type(enum ggml_type type) {
return htp_is_tiled_weight_type(type);
}
void htp_repack_tensor_tiled(struct ggml_tensor * t, const void * data, size_t size);
void htp_unpack_tensor_tiled(void * data, const struct ggml_tensor * t, size_t offset, size_t size);
+52 -134
View File
@@ -87,11 +87,11 @@ static inline HVX_Vector conv2d_hmx_rt_restore(const __fp16 * scales, uint32_t r
return Q6_V_vsplat_R((127 + e) << 23);
}
// Output rows [*row_base, *row_base + *n_rows) of total for this device.
// Output rows [*row_base, *row_base + *n_rows) of N * OH for this device.
// The output is written by DMA only, so device ranges need no cache line alignment.
static void conv2d_mdev_rows(const struct htp_ops_context * octx, uint32_t total, uint32_t * row_base, uint32_t * n_rows) {
static void conv2d_mdev_rows(const struct htp_ops_context * octx, uint32_t * row_base, uint32_t * n_rows) {
*row_base = 0;
*n_rows = total;
*n_rows = octx->dst->ne[3] * octx->dst->ne[1];
if (octx->ctx->mdev.count > 1) {
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
*n_rows, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
@@ -134,51 +134,9 @@ static void conv2d_hmx_worker(void * data) {
}
}
// Tiled path values whose source differs between conv_2d and conv_3d.
// conv_3d runs as conv_2d with virtual KH = KD * KHr and OH = OD * OHr, only the input stage unfolds them.
struct htp_conv_geom {
uint32_t KH, KHr, Cin, Cout;
uint32_t OH, OHr, N, ID;
int32_t s0, s1, s2, p0, p1, p2, d0, d1, d2;
size_t x_nb_d, x_nb_c, x_nb_n; // input strides for d, cin, n
size_t y_nb_c, y_nb_n; // dst strides for cout, n
size_t w_nb_co; // kernel cout stride
};
// conv_3d tensors are [W, H, D, C * N] with the channel as the inner index of ne[3]
static void conv_geom_init(struct htp_conv_geom * g, const struct htp_ops_context * octx) {
const struct htp_tensor * kernel = octx->src[0];
const struct htp_tensor * input = octx->src[1];
const struct htp_tensor * dst = octx->dst;
const int32_t * op = octx->op_params;
if (octx->op == HTP_OP_CONV_3D) {
const uint32_t IC = op[9];
const uint32_t OC = op[11];
*g = (struct htp_conv_geom) {
.KH = kernel->ne[2] * kernel->ne[1], .KHr = kernel->ne[1], .Cin = IC, .Cout = OC,
.OH = dst->ne[2] * dst->ne[1], .OHr = dst->ne[1], .N = op[10], .ID = input->ne[2],
.s0 = op[0], .s1 = op[1], .s2 = op[2], .p0 = op[3], .p1 = op[4], .p2 = op[5], .d0 = op[6], .d1 = op[7], .d2 = op[8],
.x_nb_d = input->nb[2], .x_nb_c = input->nb[3], .x_nb_n = (size_t) IC * input->nb[3],
.y_nb_c = dst->nb[3], .y_nb_n = (size_t) OC * dst->nb[3],
.w_nb_co = (size_t) IC * kernel->nb[3],
};
} else {
*g = (struct htp_conv_geom) {
.KH = kernel->ne[1], .KHr = kernel->ne[1], .Cin = kernel->ne[2], .Cout = kernel->ne[3],
.OH = dst->ne[1], .OHr = dst->ne[1], .N = dst->ne[3], .ID = 1,
.s0 = op[0], .s1 = op[1], .s2 = 1, .p0 = op[2], .p1 = op[3], .p2 = 0, .d0 = op[4], .d1 = op[5], .d2 = 1,
.x_nb_d = 0, .x_nb_c = input->nb[2], .x_nb_n = input->nb[3],
.y_nb_c = dst->nb[2], .y_nb_n = dst->nb[3],
.w_nb_co = kernel->nb[3],
};
}
}
struct htp_conv2d_hmx_context {
struct htp_ops_context * octx;
const struct htp_conv2d_kernel_params * kp;
const struct htp_conv_geom * g;
struct htp_conv2d_vtcm_layout layout;
__fp16 * scales; // [rt] HMX scales
__fp16 * w_tiles;
@@ -211,7 +169,7 @@ static void conv2d_hmx_wpack_thread(unsigned int nth, unsigned int ith, void * d
const struct htp_conv2d_kernel_params * kp = hc->kp;
const struct htp_tensor * kernel = octx->src[0];
const uint32_t Cout = hc->g->Cout;
const uint32_t Cout = kernel->ne[3];
const uint32_t K = kp->tiled.k_size;
const uint32_t k_pad = kp->tiled.n_k_tiles * HMX_FP16_TILE_N_COLS;
const uint32_t WK = kp->tiled.wk_chunk;
@@ -242,8 +200,8 @@ static void conv2d_hmx_wpack_thread(unsigned int nth, unsigned int ith, void * d
for (uint32_t i = 0; i < 2 && i * WK < k_pad; i++) {
const uint32_t kc = i * WK;
const uint32_t kv = MIN(WK, K - kc);
dma_queue_push(dmaq, dma_make_data(wdma[i], kernel->data + (size_t) co0 * hc->g->w_nb_co + (size_t) kc * es),
WK * es, hc->g->w_nb_co, kv * es, nco);
dma_queue_push(dmaq, dma_make_data(wdma[i], kernel->data + (size_t) co0 * kernel->nb[3] + (size_t) kc * es),
WK * es, kernel->nb[3], kv * es, nco);
}
for (uint32_t kc = 0; kc < k_pad; kc += WK) {
@@ -276,50 +234,42 @@ static void conv2d_hmx_wpack_thread(unsigned int nth, unsigned int ith, void * d
const uint32_t next_kc = kc + 2 * WK;
if (next_kc < k_pad) {
const uint32_t next_kv = MIN(WK, K - next_kc);
dma_queue_push(dmaq, dma_make_data(wdma[bi], kernel->data + (size_t) co0 * hc->g->w_nb_co + (size_t) next_kc * es),
WK * es, hc->g->w_nb_co, next_kv * es, nco);
dma_queue_push(dmaq, dma_make_data(wdma[bi], kernel->data + (size_t) co0 * kernel->nb[3] + (size_t) next_kc * es),
WK * es, kernel->nb[3], next_kv * es, nco);
}
bi ^= 1;
}
}
}
// Stage Cin*KH input rows for G col tiles: stage[(cin*KH + kh)*SR + j] = x[n][cin][ih(kh)][ow0*s0 - p0 + j], zero outside
// For conv_3d, kh = kd * KHr + kh' and oh = od * OHr + oh' are unfolded here
// Stage Cin*KH input rows for G col tiles: stage[(cin*KH + kh)*SR + j] = x[n][cin][ih(kh)][ow0 - p0 + j], zero outside
static void conv2d_hmx_stage(const struct htp_ops_context * octx, const struct htp_conv2d_kernel_params * kp,
const struct htp_conv_geom * g, dma_queue * dmaq, float * stage, uint32_t n, uint32_t oh,
uint32_t ow0, uint32_t G) {
dma_queue * dmaq, float * stage, uint32_t n, uint32_t oh, uint32_t ow0, uint32_t G) {
const struct htp_tensor * kernel = octx->src[0];
const struct htp_tensor * input = octx->src[1];
const uint32_t KW = kernel->ne[0];
const uint32_t KH = g->KH;
const uint32_t Cin = g->Cin;
const uint32_t KH = kernel->ne[1];
const uint32_t Cin = kernel->ne[2];
const int32_t IW = input->ne[0];
const int32_t IH = input->ne[1];
const int32_t s0 = g->s0;
const int32_t s1 = g->s1;
const int32_t p0 = g->p0;
const int32_t p1 = g->p1;
const int32_t d0 = g->d0;
const int32_t d1 = g->d1;
const int32_t s1 = octx->op_params[1];
const int32_t p0 = octx->op_params[2];
const int32_t p1 = octx->op_params[3];
const int32_t d0 = octx->op_params[4];
const int32_t d1 = octx->op_params[5];
const uint32_t SR = kp->tiled.stage_row;
const int32_t Wb = 32 * G * s0 + (KW - 1) * d0;
const int32_t iw0 = (int32_t) ow0 * s0 - p0;
const int32_t Wb = 32 * G + (KW - 1) * d0;
const int32_t iw0 = (int32_t) ow0 - p0;
const int32_t lo = MAX(0, -iw0);
const int32_t hi = MIN(Wb, IW - iw0);
const uint32_t od = fastdiv(oh, &kp->tiled.div_ohr);
oh -= od * g->OHr;
for (uint32_t kh = 0; kh < KH; kh++) {
const int32_t kd = fastdiv(kh, &kp->tiled.div_khr);
const int32_t id = (int32_t) od * g->s2 + kd * g->d2 - g->p2;
const int32_t ih = (int32_t) oh * s1 + (int32_t) (kh - kd * g->KHr) * d1 - p1;
const int32_t ih = (int32_t) oh * s1 + (int32_t) kh * d1 - p1;
float * rows = stage + (size_t) kh * SR;
if (id < 0 || id >= (int32_t) g->ID || ih < 0 || ih >= IH || hi <= lo) {
if (ih < 0 || ih >= IH || hi <= lo) {
for (uint32_t c = 0; c < Cin; c++) {
hvx_splat_f32_u(rows + (size_t) c * KH * SR, 0.0f, Wb);
}
@@ -327,8 +277,8 @@ static void conv2d_hmx_stage(const struct htp_ops_context * octx, const struct h
continue;
}
const dma_addr_t src = input->data + (size_t) n * g->x_nb_n + (size_t) id * g->x_nb_d + (size_t) ih * input->nb[1] + (size_t) (iw0 + lo) * sizeof(float);
dma_queue_push(dmaq, dma_make_data(rows + lo, src), (size_t) KH * SR * sizeof(float), g->x_nb_c,
const dma_addr_t src = input->data + (size_t) n * input->nb[3] + (size_t) ih * input->nb[1] + (size_t) (iw0 + lo) * sizeof(float);
dma_queue_push(dmaq, dma_make_data(rows + lo, src), (size_t) KH * SR * sizeof(float), input->nb[2],
(size_t) (hi - lo) * sizeof(float), Cin);
if (lo > 0 || hi < Wb) {
@@ -345,24 +295,9 @@ static void conv2d_hmx_stage(const struct htp_ops_context * octx, const struct h
}
}
static inline HVX_Vector conv2d_hmx_load_strided_f32(const float * src, uint32_t s0, HVX_Vector offsets, HVX_Vector * scratch) {
if (s0 == 1) {
return hvx_vmemu(src);
}
if (s0 == 2) {
const HVX_Vector lo = hvx_vmemu(src);
const HVX_Vector hi = hvx_vmemu(src + 32);
return Q6_V_lo_W(Q6_W_vdeal_VVR(hi, lo, -4));
}
Q6_vgather_ARMVw(scratch, (size_t) src, 32 * s0 * sizeof(float) - 1, offsets);
hvx_gather_sync(scratch);
return *scratch;
}
// One weight-side tile column: vector i holds k rows 2i and 2i+1 interleaved over 32 pixels
static inline void conv2d_hmx_pack_tile(__fp16 * restrict dst, const float * restrict src, uint32_t K, uint32_t KW,
uint32_t s0, uint32_t d0, uint32_t SR, uint32_t n_vec, HVX_Vector offsets,
HVX_Vector * scratch) {
uint32_t d0, uint32_t SR, uint32_t n_vec) {
HVX_Vector * restrict out = (HVX_Vector *) dst;
const HVX_Vector zero = Q6_V_vzero();
const uint32_t wrap = SR - KW * d0;
@@ -372,13 +307,13 @@ static inline void conv2d_hmx_pack_tile(__fp16 * restrict dst, const float * res
uint32_t i = 0;
for (; i < K / 2; i++) {
const HVX_Vector a = conv2d_hmx_load_strided_f32(p, s0, offsets, scratch);
const HVX_Vector a = hvx_vmemu(p);
p += d0;
if (++kw == KW) {
kw = 0;
p += wrap;
}
const HVX_Vector b = conv2d_hmx_load_strided_f32(p, s0, offsets, scratch);
const HVX_Vector b = hvx_vmemu(p);
p += d0;
if (++kw == KW) {
kw = 0;
@@ -387,14 +322,14 @@ static inline void conv2d_hmx_pack_tile(__fp16 * restrict dst, const float * res
out[i] = conv2d_f32_to_f16_shuff(a, b);
}
if (K & 1) {
out[i++] = conv2d_f32_to_f16_shuff(conv2d_hmx_load_strided_f32(p, s0, offsets, scratch), zero);
out[i++] = conv2d_f32_to_f16_shuff(hvx_vmemu(p), zero);
}
for (; i < n_vec; i++) {
out[i] = zero;
}
}
// CWHN: stage[kh*SR + j*Cin + cin] = x[n][ih(kh)][ow0*s0 - p0 + j][cin], zero outside
// CWHN: stage[kh*SR + j*Cin + cin] = x[n][ih(kh)][ow0 - p0 + j][cin], zero outside
static void conv2d_hmx_stage_cwhn(const struct htp_ops_context * octx, const struct htp_conv2d_kernel_params * kp,
dma_queue * dmaq, float * stage, uint32_t n, uint32_t oh, uint32_t ow0, uint32_t G) {
const struct htp_tensor * kernel = octx->src[0];
@@ -405,7 +340,6 @@ static void conv2d_hmx_stage_cwhn(const struct htp_ops_context * octx, const str
const uint32_t Cin = kernel->ne[2];
const int32_t IW = input->ne[0];
const int32_t IH = input->ne[1];
const int32_t s0 = octx->op_params[0];
const int32_t s1 = octx->op_params[1];
const int32_t p0 = octx->op_params[2];
const int32_t p1 = octx->op_params[3];
@@ -413,8 +347,8 @@ static void conv2d_hmx_stage_cwhn(const struct htp_ops_context * octx, const str
const int32_t d1 = octx->op_params[5];
const uint32_t SR = kp->tiled.stage_row;
const int32_t Wb = 32 * G * s0 + (KW - 1) * d0;
const int32_t iw0 = (int32_t) ow0 * s0 - p0;
const int32_t Wb = 32 * G + (KW - 1) * d0;
const int32_t iw0 = (int32_t) ow0 - p0;
const int32_t lo = MAX(0, -iw0);
const int32_t hi = MIN(Wb, IW - iw0);
@@ -449,8 +383,8 @@ static void conv2d_hmx_cwhn_f16(__fp16 * restrict dst, const float * restrict sr
// Stage the segment of col tiles starting at t, returns its number of col tiles
static uint32_t conv2d_hmx_issue_seg(const struct htp_ops_context * octx, const struct htp_conv2d_kernel_params * kp,
const struct htp_conv_geom * g, dma_queue * dmaq, float * stage, uint32_t t, uint32_t t1) {
const uint32_t OH = g->OH;
dma_queue * dmaq, float * stage, uint32_t t, uint32_t t1) {
const uint32_t OH = octx->dst->ne[1];
const uint32_t n_owt = kp->tiled.n_ow_tiles;
const uint32_t row = fastdiv(t, &kp->tiled.div_ow_tiles);
const uint32_t owt = t - row * n_owt;
@@ -461,7 +395,7 @@ static uint32_t conv2d_hmx_issue_seg(const struct htp_ops_context * octx, const
if (kp->cwhn) {
conv2d_hmx_stage_cwhn(octx, kp, dmaq, stage, n, oh, owt * 32, G);
} else {
conv2d_hmx_stage(octx, kp, g, dmaq, stage, n, oh, owt * 32, G);
conv2d_hmx_stage(octx, kp, dmaq, stage, n, oh, owt * 32, G);
}
return G;
}
@@ -553,7 +487,7 @@ static inline void conv2d_transpose_strided(const __fp16 * restrict base, uint32
// the second one rotated into the upper half.
static void conv2d_hmx_pack_tile_cwhn(__fp16 * restrict dst, const __fp16 * restrict src, HVX_Vector * restrict vin,
HVX_Vector * restrict scratch, const struct htp_conv2d_kernel_params * kp,
uint32_t gpr, uint32_t Cin, uint32_t s0, uint32_t d0) {
uint32_t gpr, uint32_t Cin, uint32_t d0) {
const uint32_t k_pad = kp->tiled.n_k_tiles * HMX_FP16_TILE_N_COLS;
const uint32_t KG = kp->tiled.k_group;
const uint32_t KGp = kp->tiled.k_group_pad;
@@ -589,15 +523,15 @@ static void conv2d_hmx_pack_tile_cwhn(__fp16 * restrict dst, const __fp16 * rest
}
if (va[0] == 32 && va[1] == 32 && pa[1] == pa[0] + 32) {
conv2d_transpose_strided(pa[0], Cin * s0, scratch, out + 32 * b, out + 32 * b + 16);
conv2d_transpose_strided(pa[0], Cin, scratch, out + 32 * b, out + 32 * b + 16);
continue;
}
const HVX_VectorPred q0 = Q6_Q_vsetq_R(va[0] * sizeof(__fp16));
const HVX_VectorPred q1 = Q6_Q_vsetq_R(va[1] * sizeof(__fp16));
for (uint32_t j = 0; j < 32; j++) {
const HVX_Vector v0 = Q6_V_vand_QV(q0, hvx_vmemu(pa[0] + j * Cin * s0));
const HVX_Vector v1 = Q6_V_vand_QV(q1, hvx_vmemu(pa[1] + j * Cin * s0));
const HVX_Vector v0 = Q6_V_vand_QV(q0, hvx_vmemu(pa[0] + j * Cin));
const HVX_Vector v1 = Q6_V_vand_QV(q1, hvx_vmemu(pa[1] + j * Cin));
vin[j] = Q6_V_vor_VV(v0, Q6_V_vror_VR(v1, 64));
}
@@ -784,10 +718,9 @@ static void conv2d_hmx_pack(const struct htp_conv2d_hmx_context * hc, uint32_t i
const struct htp_tensor * kernel = octx->src[0];
const uint32_t KW = kernel->ne[0];
const uint32_t KH = hc->g->KH;
const uint32_t Cin = hc->g->Cin;
const uint32_t s0 = (uint32_t) hc->g->s0;
const uint32_t d0 = (uint32_t) hc->g->d0;
const uint32_t KH = kernel->ne[1];
const uint32_t Cin = kernel->ne[2];
const uint32_t d0 = octx->op_params[4];
const uint32_t n_vec = kp->tiled.n_k_tiles * (HMX_FP16_TILE_N_ELMS / 64);
dma_queue * dmaq = octx->ctx->dma[ith];
@@ -814,14 +747,10 @@ static void conv2d_hmx_pack(const struct htp_conv2d_hmx_context * hc, uint32_t i
return;
}
// Reuse output staging after unpack has drained its DMA.
HVX_Vector * gather_tmp = (HVX_Vector *) (thr + (size_t) n_stage * kp->stage_size);
const HVX_Vector offsets = kp->cwhn || s0 <= 2 ? Q6_V_vzero() : hvx_vec_gather_offsets_w(s0 * sizeof(float));
uint32_t ng[2] = { 0, 0 };
uint32_t tq = t0;
for (uint32_t i = 0; i < n_stage && tq < t1; i++) {
ng[i] = conv2d_hmx_issue_seg(octx, kp, hc->g, dmaq, stages[i], tq, t1);
ng[i] = conv2d_hmx_issue_seg(octx, kp, dmaq, stages[i], tq, t1);
tq += ng[i];
}
@@ -836,29 +765,22 @@ static void conv2d_hmx_pack(const struct htp_conv2d_hmx_context * hc, uint32_t i
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_A_PREP, (uint16_t) t);
if (kp->cwhn) {
const uint32_t Wb = 32 * G * s0 + (KW - 1) * d0;
const uint32_t Wb = 32 * G + (KW - 1) * d0;
conv2d_hmx_cwhn_f16(stage16, stage, KH, kp->tiled.stage_row, Wb * Cin);
for (uint32_t g = 0; g < G; g++) {
__fp16 * tile = act + (size_t) (t - cs + g) * n_vec * 64;
conv2d_hmx_pack_tile_cwhn(tile, stage16 + (size_t) g * 32 * Cin * s0, vin, scratch, kp, gpr, Cin, s0, d0);
conv2d_hmx_pack_tile_cwhn(tile, stage16 + (size_t) g * 32 * Cin, vin, scratch, kp, gpr, Cin, d0);
}
} else {
for (uint32_t g = 0; g < G; g++) {
__fp16 * tile = act + (size_t) (t - cs + g) * n_vec * 64;
const float * pixels = stage + (size_t) g * 32 * s0;
if (s0 == 1) {
conv2d_hmx_pack_tile(tile, pixels, kp->tiled.k_size, KW, 1, d0, kp->tiled.stage_row, n_vec, offsets, gather_tmp);
} else if (s0 == 2) {
conv2d_hmx_pack_tile(tile, pixels, kp->tiled.k_size, KW, 2, d0, kp->tiled.stage_row, n_vec, offsets, gather_tmp);
} else {
conv2d_hmx_pack_tile(tile, pixels, kp->tiled.k_size, KW, s0, d0, kp->tiled.stage_row, n_vec, offsets, gather_tmp);
}
__fp16 * tile = act + (size_t) (t - cs + g) * n_vec * 64;
conv2d_hmx_pack_tile(tile, stage + g * 32, kp->tiled.k_size, KW, d0, kp->tiled.stage_row, n_vec);
}
}
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_A_PREP, (uint16_t) t);
if (tq < t1) {
ng[bi] = conv2d_hmx_issue_seg(octx, kp, hc->g, dmaq, stage, tq, t1);
ng[bi] = conv2d_hmx_issue_seg(octx, kp, dmaq, stage, tq, t1);
tq += ng[bi];
}
t += G;
@@ -872,8 +794,8 @@ static void conv2d_hmx_unpack(const struct htp_conv2d_hmx_context * hc, uint32_t
const struct htp_tensor * dst = octx->dst;
const uint32_t OW = dst->ne[0];
const uint32_t OH = hc->g->OH;
const uint32_t Cout = hc->g->Cout;
const uint32_t OH = dst->ne[1];
const uint32_t Cout = dst->ne[2];
const uint32_t n_owt = kp->tiled.n_ow_tiles;
const uint32_t OS = kp->tiled.seg_tiles * 32;
@@ -928,8 +850,8 @@ static void conv2d_hmx_unpack(const struct htp_conv2d_hmx_context * hc, uint32_t
}
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_O_PROC, (uint16_t) t);
const dma_addr_t ddr = dst->data + (size_t) n * hc->g->y_nb_n + (size_t) oh * dst->nb[1] + (size_t) co0 * hc->g->y_nb_c + (size_t) ow0 * sizeof(float);
dma_queue_push(dmaq, dma_make_data(ddr, ob), hc->g->y_nb_c, (size_t) OS * sizeof(float), (size_t) nv * sizeof(float), nco);
const dma_addr_t ddr = dst->data + (size_t) n * dst->nb[3] + (size_t) oh * dst->nb[1] + (size_t) co0 * dst->nb[2] + (size_t) ow0 * sizeof(float);
dma_queue_push(dmaq, dma_make_data(ddr, ob), dst->nb[2], (size_t) OS * sizeof(float), (size_t) nv * sizeof(float), nco);
n_pushed++;
b ^= 1;
}
@@ -982,11 +904,8 @@ static void conv2d_hmx_run_phase(struct htp_conv2d_hmx_context * hc, int32_t pac
}
static int conv2d_hmx_tiled(struct htp_ops_context * octx, const struct htp_conv2d_kernel_params * kp) {
struct htp_conv_geom g;
conv_geom_init(&g, octx);
uint32_t row_base, n_rows;
conv2d_mdev_rows(octx, g.N * g.OH, &row_base, &n_rows);
conv2d_mdev_rows(octx, &row_base, &n_rows);
if (n_rows == 0) {
return HTP_STATUS_OK;
}
@@ -1004,7 +923,6 @@ static int conv2d_hmx_tiled(struct htp_ops_context * octx, const struct htp_conv
struct htp_conv2d_hmx_context hc = {
.octx = octx,
.kp = kp,
.g = &g,
.layout = layout,
.scales = VTCM_LAYOUT_PTR(__fp16, base, layout.off_scales),
.w_tiles = VTCM_LAYOUT_PTR(__fp16, base, layout.off_w),
@@ -2235,7 +2153,7 @@ static int conv2d_hmx_win(struct htp_ops_context * octx, const struct htp_conv2d
const uint32_t d1 = octx->op_params[5];
uint32_t row_base, n_rows;
conv2d_mdev_rows(octx, dst->ne[3] * dst->ne[1], &row_base, &n_rows);
conv2d_mdev_rows(octx, &row_base, &n_rows);
if (n_rows == 0) {
return HTP_STATUS_OK;
}
-2
View File
@@ -35,8 +35,6 @@ struct htp_conv2d_tiled_params {
uint32_t wk_chunk;
struct fastdiv_values div_ow_tiles;
struct fastdiv_values div_oh;
struct fastdiv_values div_khr; // kernel ne[1], unfolds conv_3d kh = kd * KHr + kh'
struct fastdiv_values div_ohr; // dst ne[1], unfolds conv_3d oh = od * OHr + oh'
};
struct htp_conv2d_window_params {
-2
View File
@@ -51,7 +51,6 @@ enum htp_op_code {
HTP_OP_MUL_MAT_ADD,
HTP_OP_RMS_NORM,
HTP_OP_RMS_NORM_MUL,
HTP_OP_RMS_NORM_MUL_ADD,
HTP_OP_UNARY_SILU,
HTP_OP_UNARY_GELU,
HTP_OP_UNARY_SIGMOID,
@@ -118,7 +117,6 @@ enum htp_op_code {
HTP_OP_UNARY_CEIL,
HTP_OP_UNARY_SGN,
HTP_OP_UPSCALE,
HTP_OP_CONV_3D,
HTP_OP_DSV4_HC_PRE,
HTP_OP_DSV4_HC_POST,
HTP_OP_TOP_K_MASK,
-65
View File
@@ -141,71 +141,6 @@ static inline void hvx_fast_rms_norm_mul_f32(const uint8_t * restrict src,
}
}
static inline void hvx_fast_rms_norm_mul_add_f32(const uint8_t * restrict src,
const uint8_t * restrict weight,
const uint8_t * restrict add,
uint8_t * restrict dst,
const int num_elems,
float epsilon,
float scale) {
const HVX_Vector * restrict v_src = (const HVX_Vector *) src;
const HVX_Vector * restrict v_weight = (const HVX_Vector *) weight;
const HVX_Vector * restrict v_add = (const HVX_Vector *) add;
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
const int nvec = num_elems / VLEN_FP32;
const int nloe = num_elems % VLEN_FP32;
HVX_Vector sum_v = Q6_V_vsplat_R(0x00000000);
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
#pragma unroll(4)
for (int i = 0; i < nvec; i++) {
HVX_Vector v1 = v_src[i];
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
}
if (nloe > 0) {
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
}
sum_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
HVX_Vector mean_v = Q6_Vqf32_vmpy_VsfVsf(sum_v, denom_v);
HVX_Vector mean_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(mean_v, epsilon_v);
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(mean_epsilon_v));
if (scale != 0.0f) {
scale_v = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(scale_v, hvx_vec_splat_f32(scale)));
}
#pragma unroll(4)
for (int i = 0; i < nvec; i++) {
HVX_Vector v1 = v_src[i];
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(v2);
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(v3, v_weight[i]));
v_dst[i] = hvx_vec_add_f32_f32(v4, v_add[i]);
}
if (nloe > 0) {
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(v2);
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(v3, v_weight[nvec]));
HVX_Vector res_v = hvx_vec_add_f32_f32(v4, v_add[nvec]);
hvx_vec_store_a(&v_dst[nvec], nloe * 4, res_v);
}
}
static inline void hvx_fast_norm_f32(const uint8_t * restrict src,
uint8_t * restrict dst,
const int num_elems,
-2
View File
@@ -931,7 +931,6 @@ static int execute_op(struct htp_ops_context * octx) {
case HTP_OP_NORM:
case HTP_OP_RMS_NORM:
case HTP_OP_RMS_NORM_MUL:
case HTP_OP_RMS_NORM_MUL_ADD:
case HTP_OP_SCALE:
case HTP_OP_CLAMP:
case HTP_OP_LEAKY_RELU:
@@ -1031,7 +1030,6 @@ static int execute_op(struct htp_ops_context * octx) {
return op_im2col_3d(octx);
case HTP_OP_CONV_2D:
case HTP_OP_CONV_3D:
return op_conv_2d(octx);
case HTP_OP_CONV_2D_DW:
+7 -163
View File
@@ -36,13 +36,6 @@ typedef void (*unary_rms_norm_mul_compute_fn_t)(const void * restrict src,
uint32_t num_rows,
const struct htp_unary_context * uctx);
typedef void (*unary_rms_norm_mul_add_compute_fn_t)(const void * restrict src,
const void * restrict weight,
const void * restrict add,
void * restrict dst,
uint32_t num_rows,
const struct htp_unary_context * uctx);
typedef void (*unary_tri_compute_fn_t)(const void * restrict src,
void * restrict dst,
uint32_t num_rows,
@@ -70,7 +63,6 @@ struct htp_unary_context {
dma_addr_t data_src0;
dma_addr_t data_src1; // weight/scale tensor for RMS_NORM_MUL
dma_addr_t data_src2; // residual tensor for RMS_NORM_MUL_ADD
dma_addr_t data_dst;
size_t src0_data_row_size; // actual data bytes per row
@@ -95,12 +87,10 @@ struct htp_unary_context {
uint8_t * vtcm_src0;
uint8_t * vtcm_src1;
uint8_t * vtcm_src2;
uint8_t * vtcm_dst;
size_t vtcm_src0_size_per_thread;
size_t vtcm_src1_size_per_thread;
size_t vtcm_src2_size_per_thread;
size_t vtcm_dst_size_per_thread;
};
@@ -254,29 +244,6 @@ static void rms_norm_mul_f32(const void * restrict src,
}
}
static void rms_norm_mul_add_f32(const void * restrict src,
const void * restrict weight,
const void * restrict add,
void * restrict dst,
const uint32_t num_rows,
const struct htp_unary_context * uctx) {
htp_unary_op_preamble;
float epsilon = 0.f;
float scale = 0.f;
memcpy(&epsilon, &op_params[0], sizeof(float));
memcpy(&scale, &op_params[1], sizeof(float));
const uint8_t * restrict w_local = (const uint8_t *) weight;
for (uint32_t ir = 0; ir < num_rows; ir++) {
const uint8_t * restrict src_local = (const uint8_t *) src + (ir * src0_row_size_aligned);
const uint8_t * restrict add_local = (const uint8_t *) add + (ir * src0_row_size_aligned);
uint8_t * restrict dst_local = (uint8_t *) dst + (ir * dst_row_size_aligned);
hvx_fast_rms_norm_mul_add_f32(src_local, w_local, add_local, dst_local, ne0, epsilon, scale);
}
}
static void norm_f32(const void * restrict src,
void * restrict dst,
const uint32_t num_rows,
@@ -1463,118 +1430,6 @@ static void unary_thread_rms_norm_mul_f32(unsigned int nth, unsigned int ith, vo
dma_queue_flush(dma_q);
}
// 2b. RMS_NORM_MUL_ADD row-block task with broadcast weight and residual add.
static void unary_thread_rms_norm_mul_add_f32(unsigned int nth, unsigned int ith, void * data) {
(void) nth;
const struct htp_unary_context * uctx = (const struct htp_unary_context *) data;
struct htp_ops_context * octx = uctx->octx;
const struct htp_tensor * src = octx->src[0];
const struct htp_tensor * dst = octx->dst;
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
htp_unary_preamble;
const uint32_t src0_nrows_per_thread = uctx->src0_nrows_per_thread;
const size_t src0_data_row_size = uctx->src0_data_row_size;
const size_t dst_data_row_size = uctx->dst_data_row_size;
const size_t src0_row_size_aligned = uctx->src0_row_size_aligned;
const size_t dst_row_size_aligned = uctx->dst_row_size_aligned;
const uint32_t src0_nrows = uctx->src0_nrows;
const uint32_t src0_start_row = uctx->row_start + src0_nrows_per_thread * ith;
const uint32_t src0_end_row = MIN(src0_start_row + src0_nrows_per_thread, uctx->row_start + src0_nrows);
if (src0_start_row >= src0_end_row) {
return;
}
const dma_addr_t data_src = uctx->data_src0;
const dma_addr_t data_src1 = uctx->data_src1;
const dma_addr_t data_src2 = uctx->data_src2;
const dma_addr_t data_dst = uctx->data_dst;
uint8_t * src0_vtcm_data = uctx->vtcm_src0 + (ith * uctx->vtcm_src0_size_per_thread);
uint8_t * src1_vtcm_data = uctx->vtcm_src1 + (ith * uctx->vtcm_src1_size_per_thread);
uint8_t * src2_vtcm_data = uctx->vtcm_src2 + (ith * uctx->vtcm_src2_size_per_thread);
uint8_t * dst_vtcm_data = uctx->vtcm_dst + (ith * uctx->vtcm_dst_size_per_thread);
const size_t src0_vtcm_half_size = uctx->src0_vtcm_half_size;
const size_t dst_vtcm_half_size = uctx->dst_vtcm_half_size;
const bool src0_contig = uctx->kparams->src0_contig;
const uint32_t BLOCK = uctx->block;
const struct fastdiv_values * div_ne01 = &uctx->kparams->div_ne01;
const struct fastdiv_values * div_ne02 = &uctx->kparams->div_ne02;
const struct fastdiv_values * div_ne012 = &uctx->kparams->div_ne012;
dma_queue * dma_q = octx->ctx->dma[ith];
dma_queue_push(dma_q, dma_make_data(src1_vtcm_data, data_src1),
uctx->src1_row_size_aligned, 0, uctx->src1_data_row_size, 1);
dma_queue_flush(dma_q);
for (uint32_t ir = src0_start_row, vtcm_idx = 0; ir < src0_end_row && vtcm_idx < 2; vtcm_idx++) {
const uint32_t block_size = unary_block_size(ir, src0_end_row, BLOCK, src0_contig, ne01, div_ne01);
dma_queue_push(dma_q,
dma_make_data(data_dst, dst_vtcm_data + (vtcm_idx * dst_vtcm_half_size)),
nb1, dst_row_size_aligned, dst_data_row_size, 0);
const size_t src0_off = src0_contig ? ((size_t) ir * nb01) :
unary_row_offset(ir, ne01, ne02, div_ne01, div_ne02, div_ne012, nb01, nb02, nb03);
dma_queue_push(dma_q,
dma_make_data(src0_vtcm_data + (vtcm_idx * src0_vtcm_half_size), data_src + src0_off),
src0_row_size_aligned, nb01, src0_data_row_size, block_size);
dma_queue_push(dma_q,
dma_make_data(src2_vtcm_data + (vtcm_idx * src0_vtcm_half_size), data_src2 + src0_off),
src0_row_size_aligned, nb01, src0_data_row_size, block_size);
ir += block_size;
}
unary_rms_norm_mul_add_compute_fn_t compute = (unary_rms_norm_mul_add_compute_fn_t) uctx->compute;
for (uint32_t ir = src0_start_row; ir < src0_end_row; ) {
const uint32_t block_size = unary_block_size(ir, src0_end_row, BLOCK, src0_contig, ne01, div_ne01);
void * dst_vtcm = (void *) (uintptr_t) dma_queue_pop(dma_q).src;
void * src0_vtcm = (void *) (uintptr_t) dma_queue_pop(dma_q).dst;
void * src2_vtcm = (void *) (uintptr_t) dma_queue_pop(dma_q).dst;
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, ir);
compute(src0_vtcm, src1_vtcm_data, src2_vtcm, dst_vtcm, block_size, uctx);
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, ir);
const size_t dst_off = (size_t) ir * nb1;
dma_queue_push(dma_q,
dma_make_data(data_dst + dst_off, dst_vtcm),
nb1, dst_row_size_aligned, dst_data_row_size, block_size);
const uint32_t next_ir = ir + block_size;
if (next_ir < src0_end_row) {
const uint32_t next_block_size = unary_block_size(next_ir, src0_end_row, BLOCK, src0_contig,
ne01, div_ne01);
const uint32_t pref_ir = next_ir + next_block_size;
if (pref_ir < src0_end_row) {
const uint32_t pref_block_size = unary_block_size(pref_ir, src0_end_row, BLOCK, src0_contig,
ne01, div_ne01);
const size_t src0_pref_off = src0_contig ? ((size_t) pref_ir * nb01) :
unary_row_offset(pref_ir, ne01, ne02, div_ne01, div_ne02, div_ne012, nb01, nb02, nb03);
dma_queue_push(dma_q,
dma_make_data(src0_vtcm, data_src + src0_pref_off),
src0_row_size_aligned, nb01, src0_data_row_size, pref_block_size);
dma_queue_push(dma_q,
dma_make_data(src2_vtcm, data_src2 + src0_pref_off),
src0_row_size_aligned, nb01, src0_data_row_size, pref_block_size);
}
}
ir += block_size;
}
dma_queue_flush(dma_q);
}
// 3. TRI row-block task with row index ir.
static void unary_thread_tri_f32(unsigned int nth, unsigned int ith, void * data) {
(void) nth;
@@ -1894,10 +1749,9 @@ static int execute_op_unary(struct htp_ops_context * octx) {
const char * op_type = NULL;
switch (octx->op) {
case HTP_OP_NORM: op_type = is_f16 ? "norm-f16" : "norm-f32"; break;
case HTP_OP_RMS_NORM: op_type = is_f16 ? "rmsnorm-f16" : "rmsnorm-f32"; break;
case HTP_OP_RMS_NORM_MUL: op_type = "rmsnorm-mul-f32"; break;
case HTP_OP_RMS_NORM_MUL_ADD: op_type = "rmsnorm-mul-add-f32"; break;
case HTP_OP_NORM: op_type = is_f16 ? "norm-f16" : "norm-f32"; break;
case HTP_OP_RMS_NORM: op_type = is_f16 ? "rmsnorm-f16" : "rmsnorm-f32"; break;
case HTP_OP_RMS_NORM_MUL: op_type = "rmsnorm-mul-f32"; break;
case HTP_OP_SCALE: op_type = is_f16 ? "scale-f16" : "scale-f32"; break;
case HTP_OP_CLAMP: op_type = is_f16 ? "clamp-f16" : "clamp-f32"; break;
case HTP_OP_LEAKY_RELU: op_type = "leaky-relu-f32"; break;
@@ -1963,12 +1817,9 @@ static int execute_op_unary(struct htp_ops_context * octx) {
bool broadcast_weight = kparams->broadcast_weight;
const struct htp_tensor * src1 = NULL;
const bool has_weight = (octx->op == HTP_OP_RMS_NORM_MUL || octx->op == HTP_OP_RMS_NORM_MUL_ADD);
const bool has_src2 = (octx->op == HTP_OP_RMS_NORM_MUL_ADD);
// RMS_NORM_MUL* fusion is F32-only (its weight tensor is always F32; see
// RMS_NORM_MUL fusion is F32-only (its weight tensor is always F32; see
// try_fuse_node()'s type guard), so this never triggers when is_f16 is true.
if (has_weight) {
if (octx->op == HTP_OP_RMS_NORM_MUL) {
src1 = octx->src[1];
src1_data_row_size = src1->ne[0] * sizeof(float);
}
@@ -1999,8 +1850,7 @@ static int execute_op_unary(struct htp_ops_context * octx) {
.row_start = row_start,
.data_src0 = src0->data,
.data_src1 = has_weight ? src1->data : 0,
.data_src2 = has_src2 ? octx->src[2]->data : 0,
.data_src1 = (octx->op == HTP_OP_RMS_NORM_MUL) ? src1->data : 0,
.data_dst = dst->data,
.src0_data_row_size = src0_data_row_size,
@@ -2012,7 +1862,7 @@ static int execute_op_unary(struct htp_ops_context * octx) {
.dst_row_size_aligned = L.dst_row_size_aligned,
.src0_vtcm_half_size = L.src0_bytes >> 1,
.src1_vtcm_half_size = has_weight ? (L.src1_bytes >> (broadcast_weight ? 0 : 1)) : 0,
.src1_vtcm_half_size = (octx->op == HTP_OP_RMS_NORM_MUL) ? (L.src1_bytes >> (broadcast_weight ? 0 : 1)) : 0,
.dst_vtcm_half_size = L.dst_bytes >> 1,
.block = kparams->block,
@@ -2022,12 +1872,10 @@ static int execute_op_unary(struct htp_ops_context * octx) {
.vtcm_src0 = VTCM_LAYOUT_PTR(uint8_t, base, L.off_src0),
.vtcm_src1 = VTCM_LAYOUT_PTR_OPTIONAL(uint8_t, base, L.off_src1, L.src1_bytes > 0),
.vtcm_src2 = VTCM_LAYOUT_PTR_OPTIONAL(uint8_t, base, L.off_src2, L.src2_bytes > 0),
.vtcm_dst = VTCM_LAYOUT_PTR(uint8_t, base, L.off_dst),
.vtcm_src0_size_per_thread = L.src0_bytes,
.vtcm_src1_size_per_thread = L.src1_bytes,
.vtcm_src2_size_per_thread = L.src2_bytes,
.vtcm_dst_size_per_thread = L.dst_bytes,
};
@@ -2092,10 +1940,6 @@ static int execute_op_unary(struct htp_ops_context * octx) {
task_func = unary_thread_rms_norm_mul_f32;
compute_func = (void *) rms_norm_mul_f32;
break;
case HTP_OP_RMS_NORM_MUL_ADD:
task_func = unary_thread_rms_norm_mul_add_f32;
compute_func = (void *) rms_norm_mul_add_f32;
break;
case HTP_OP_SCALE: compute_func = (void *) scale_f32; break;
case HTP_OP_CLAMP: compute_func = (void *) clamp_f32; break;
case HTP_OP_LEAKY_RELU: compute_func = (void *) leaky_relu_f32; break;
+7 -23
View File
@@ -39,7 +39,6 @@ static inline bool htp_op_is_unary(uint32_t opcode) {
case HTP_OP_NORM:
case HTP_OP_RMS_NORM:
case HTP_OP_RMS_NORM_MUL:
case HTP_OP_RMS_NORM_MUL_ADD:
case HTP_OP_SCALE:
case HTP_OP_SQR:
case HTP_OP_SQRT:
@@ -75,7 +74,6 @@ static inline bool htp_unary_is_elementwise(uint32_t opcode) {
case HTP_OP_NORM:
case HTP_OP_RMS_NORM:
case HTP_OP_RMS_NORM_MUL:
case HTP_OP_RMS_NORM_MUL_ADD:
case HTP_OP_L2_NORM:
case HTP_OP_HADAMARD:
case HTP_OP_TRI:
@@ -89,7 +87,6 @@ struct htp_unary_vtcm_layout {
size_t total_bytes;
size_t off_src0;
size_t off_src1;
size_t off_src2;
size_t off_dst;
size_t src0_row_size_aligned;
@@ -98,7 +95,6 @@ struct htp_unary_vtcm_layout {
size_t src0_bytes;
size_t src1_bytes;
size_t src2_bytes;
size_t dst_bytes;
};
@@ -114,23 +110,19 @@ static inline void htp_unary_vtcm_layout_build(
bool broadcast_weight,
uint32_t n_threads
) {
const bool has_weight = (op == HTP_OP_RMS_NORM_MUL || op == HTP_OP_RMS_NORM_MUL_ADD);
L->src0_row_size_aligned = hex_round_up(src0_data_row_size, 128);
L->dst_row_size_aligned = hex_round_up(dst_data_row_size, 128);
L->src1_row_size_aligned = has_weight ? hex_round_up(src1_data_row_size, 128) : 0;
L->src1_row_size_aligned = (op == HTP_OP_RMS_NORM_MUL) ? hex_round_up(src1_data_row_size, 128) : 0;
if (col_tile > 0) {
const size_t col_tile_bytes = col_tile * elem_size;
L->src0_bytes = col_tile_bytes * 2;
L->dst_bytes = col_tile_bytes * 2;
L->src1_bytes = 0;
L->src2_bytes = 0;
} else {
L->src0_bytes = L->src0_row_size_aligned * block * 2;
L->src2_bytes = (op == HTP_OP_RMS_NORM_MUL_ADD) ? L->src0_bytes : 0;
L->dst_bytes = L->dst_row_size_aligned * block * 2;
if (has_weight) {
if (op == HTP_OP_RMS_NORM_MUL) {
L->src1_bytes = broadcast_weight ? L->src1_row_size_aligned : (L->src1_row_size_aligned * block * 2);
} else {
L->src1_bytes = 0;
@@ -140,7 +132,6 @@ static inline void htp_unary_vtcm_layout_build(
size_t off = 0;
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes * n_threads);
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_src1, L->src1_bytes * n_threads, L->src1_bytes > 0);
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_src2, L->src2_bytes * n_threads, L->src2_bytes > 0);
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes * n_threads);
L->total_bytes = off;
}
@@ -159,26 +150,19 @@ static inline void htp_unary_solve_layout(
uint32_t * out_col_tile,
uint32_t * out_vtcm_row_per_thread
) {
const bool has_weight = (op == HTP_OP_RMS_NORM_MUL || op == HTP_OP_RMS_NORM_MUL_ADD);
const size_t dst_elem_size = (op == HTP_OP_HADAMARD) ? sizeof(float) : elem_size;
const size_t src0_data_row_size = ne00 * elem_size;
const size_t dst_data_row_size = ne10 * dst_elem_size;
const size_t src1_data_row_size = has_weight ? (ne11 * sizeof(float)) : 0;
const size_t src1_data_row_size = (op == HTP_OP_RMS_NORM_MUL) ? (ne11 * sizeof(float)) : 0;
const size_t src0_row_size_aligned = hex_round_up(src0_data_row_size, 128);
const size_t dst_row_size_aligned = hex_round_up(dst_data_row_size, 128);
const size_t src1_row_size_aligned = has_weight ? hex_round_up(src1_data_row_size, 128) : 0;
const size_t src1_row_size_aligned = (op == HTP_OP_RMS_NORM_MUL) ? hex_round_up(src1_data_row_size, 128) : 0;
size_t vtcm_size_per_row = 0;
size_t vtcm_row_per_thread = 0;
if (op == HTP_OP_RMS_NORM_MUL_ADD) {
size_t src1_vtcm_total = n_threads * src1_row_size_aligned;
size_t available_vtcm = (vtcm_size > src1_vtcm_total) ? (vtcm_size - src1_vtcm_total) : 0;
vtcm_size_per_row = 2 * (2 * src0_row_size_aligned + dst_row_size_aligned);
vtcm_row_per_thread = available_vtcm / (n_threads * vtcm_size_per_row);
} else if (op == HTP_OP_RMS_NORM_MUL) {
if (op == HTP_OP_RMS_NORM_MUL) {
if (broadcast_weight) {
size_t available_vtcm = vtcm_size;
size_t src1_vtcm_total = n_threads * src1_row_size_aligned;
@@ -203,8 +187,8 @@ static inline void htp_unary_solve_layout(
}
const bool is_reduction = (op == HTP_OP_NORM || op == HTP_OP_RMS_NORM ||
op == HTP_OP_RMS_NORM_MUL || op == HTP_OP_RMS_NORM_MUL_ADD ||
op == HTP_OP_L2_NORM || op == HTP_OP_HADAMARD);
op == HTP_OP_RMS_NORM_MUL || op == HTP_OP_L2_NORM ||
op == HTP_OP_HADAMARD);
// The tiled fallback path below only has F32 task functions (unary_task_f32_tiled_*);
// F16 has no tiled kernels, so it must stay on the row-block path like reduction ops.
// NOTE: if F16 ends up with vtcm_row_per_thread == 0 here (row too large for the VTCM
-7
View File
@@ -107,13 +107,6 @@ static inline bool ggml_impl_is_view(const struct ggml_tensor * t) {
static inline float ggml_compute_softplus_f32(float input) {
return (input > 20.0f) ? input : logf(1 + expf(input));
}
// compute correction dims for YaRN RoPE scaling
GGML_API void ggml_rope_yarn_corr_dims(
int n_dims, int n_ctx_orig, float freq_base, float beta_fast, float beta_slow, bool truncate, float dims[2]);
GGML_API bool ggml_rope_get_truncate(const struct ggml_tensor * a);
//
// logging
//
+1 -1
View File
@@ -38,7 +38,7 @@ bool ggml_metal_op_mul_mat_use_mm(const struct ggml_tensor * op, bool has_simdgr
return !ggml_is_transposed(op->src[0]) &&
!ggml_is_transposed(op->src[1]) &&
has_simdgroup_mm && ne11 > 8 && ne00 >= 32;
has_simdgroup_mm && ne00 >= 64 && ne11 > 8;
}
bool ggml_metal_op_mul_mat_id_use_mm(const struct ggml_tensor * op, bool has_simdgroup_mm) {
+21 -22
View File
@@ -53,9 +53,9 @@ struct ggml_metal {
// command buffer state
int n_cb; // number of extra threads used to submit the command buffers
// first node of each extra command buffer, then the end of the graph
int cb_start[GGML_METAL_MAX_COMMAND_BUFFERS + 1];
int n_nodes_0; // number of nodes submitted by the main thread
int n_nodes_1; // remaining number of nodes submitted by the n_cb threads
int n_nodes_per_cb;
struct ggml_cgraph * gf;
@@ -491,7 +491,7 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
ggml_metal_device_rsets_keep_alive(ctx->dev);
// submit the ggml compute graph to the GPU by creating command buffers and encoding the ops in them
// the first n_main nodes are encoded and submitted for processing directly by the calling thread
// the first n_nodes_0 are encoded and submitted for processing directly by the calling thread
// while these nodes are processing, we start n_cb threads to enqueue the rest of the nodes
// each thread creates it's own command buffer and enqueues the ops in parallel
//
@@ -500,22 +500,16 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
@autoreleasepool {
ctx->gf = gf;
// the main thread encodes [0, cb_start[0]) and the extra command buffer i encodes [cb_start[i], cb_start[i + 1])
// a fusion cannot span two command buffers, so each boundary moves to the end of the fusable group it falls
// into and the encoders fuse the same nodes as with a single command buffer
{
const int n_rest = gf->n_nodes - MIN(n_main, gf->n_nodes);
const int n_per_cb = n_cb > 0 ? (n_rest + n_cb - 1) / n_cb : 0;
if (ctx->n_cb == 0) {
// single-threaded encoding: the whole graph is encoded by one command buffer
ctx->n_nodes_0 = gf->n_nodes;
ctx->n_nodes_1 = 0;
ctx->n_nodes_per_cb = 0;
} else {
ctx->n_nodes_0 = MIN(n_main, gf->n_nodes);
ctx->n_nodes_1 = gf->n_nodes - ctx->n_nodes_0;
int i = 0;
for (int cb_idx = 0; cb_idx < n_cb; ++cb_idx) {
const int target = MIN(n_main + cb_idx*n_per_cb, gf->n_nodes);
while (i < target) {
i += ggml_metal_fusion_max(gf, i);
}
ctx->cb_start[cb_idx] = MIN(i, gf->n_nodes);
}
ctx->cb_start[n_cb] = gf->n_nodes;
ctx->n_nodes_per_cb = (ctx->n_nodes_1 + ctx->n_cb - 1) / ctx->n_cb;
}
if (ctx->capture_compute >= 0) {
@@ -750,12 +744,17 @@ void ggml_metal_set_n_cb(ggml_metal_t ctx, int n_cb) {
const int cb_idx = iter;
const int n_cb_l = ctx->n_cb;
const int n_nodes_0 = ctx->n_nodes_0;
const int n_nodes_1 = ctx->n_nodes_1;
const int n_nodes_per_cb = ctx->n_nodes_per_cb;
int idx_start = 0;
int idx_end = ctx->cb_start[0];
int idx_end = n_nodes_0;
if (cb_idx < n_cb_l) {
idx_start = ctx->cb_start[cb_idx];
idx_end = ctx->cb_start[cb_idx + 1];
idx_start = n_nodes_0 + ( (cb_idx + 0) * n_nodes_per_cb);
idx_end = n_nodes_0 + (MIN((cb_idx == n_cb_l - 1) ? n_nodes_1 : (cb_idx + 1) * n_nodes_per_cb, n_nodes_1));
}
id<MTLCommandBuffer> cmd_buf = ctx->cmd_bufs[cb_idx].obj;
+2 -5
View File
@@ -1738,13 +1738,12 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_merge(ggml
}
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_topk_moe(
ggml_metal_library_t lib, int32_t n_expert, int32_t top_k, bool with_norm, int32_t gating, bool pbias) {
ggml_metal_library_t lib, int32_t n_expert, int32_t top_k, bool with_norm) {
char base[256];
char name[256];
snprintf(base, 256, "kernel_topk_moe_f32");
snprintf(name, 256, "%s_n_expert=%d_top_k=%d_with_norm=%d_gating=%d_pbias=%d",
base, n_expert, top_k, with_norm ? 1 : 0, gating, pbias ? 1 : 0);
snprintf(name, 256, "%s_n_expert=%d_top_k=%d_with_norm=%d", base, n_expert, top_k, with_norm ? 1 : 0);
ggml_metal_pipeline_with_params res = ggml_metal_library_get_pipeline(lib, name);
if (!res.pipeline) {
@@ -1752,8 +1751,6 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_topk_moe(
ggml_metal_cv_set_bool (cv, with_norm, FC_TOPK_MOE + 0);
ggml_metal_cv_set_int32(cv, n_expert, FC_TOPK_MOE + 1);
ggml_metal_cv_set_int32(cv, top_k, FC_TOPK_MOE + 2);
ggml_metal_cv_set_int32(cv, gating, FC_TOPK_MOE + 3);
ggml_metal_cv_set_bool (cv, pbias, FC_TOPK_MOE + 4);
res = ggml_metal_library_compile_pipeline(lib, base, name, cv);
+1 -1
View File
@@ -154,7 +154,7 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_fwht
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_radix (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_merge (ggml_metal_library_t lib, const struct ggml_tensor * op);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_topk_moe (ggml_metal_library_t lib, int32_t n_expert, int32_t top_k, bool with_norm, int32_t gating, bool pbias);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_topk_moe (ggml_metal_library_t lib, int32_t n_expert, int32_t top_k, bool with_norm);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_moe_reduce (ggml_metal_library_t lib, int32_t n_expert_used);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_bin (ggml_metal_library_t lib, const struct ggml_tensor * op, int32_t n_fuse);
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_bin_one (ggml_metal_library_t lib, enum ggml_op op);
+1 -1
View File
@@ -1696,7 +1696,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
return has_simdgroup_reduction && (ggml_is_contiguous_rows(op->src[0]));
case GGML_OP_ROPE:
case GGML_OP_ROPE_BACK:
return ggml_rope_get_truncate(op);
return true;
case GGML_OP_IM2COL:
return ggml_is_contiguous(op->src[1]) && op->src[1]->type == GGML_TYPE_F32 && (op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32);
case GGML_OP_CONV_2D:
+63 -186
View File
@@ -345,6 +345,9 @@ static bool ggml_metal_fusion_check_snake(
#define GGML_METAL_TOPK_MOE_MAX_EXPERTS 1024
// SOFT_MAX + ARGSORT + GET_ROWS (plus optional norm/scale) for MoE routing.
// This is a multi-output elision chain: the fused kernel writes both the selected
// expert ids and the gathered/normalized routing weights.
static const std::vector<ggml_op> ops_topk_moe = {
GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS
};
@@ -359,60 +362,6 @@ static const std::vector<ggml_op> ops_topk_moe_norm_scale = {
GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_norm_bias = {
GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE
};
static const std::vector<ggml_op> ops_topk_moe_norm_scale_bias = {
GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sp = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS
};
static const std::vector<ggml_op> ops_topk_moe_sp_scale = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sp_norm = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, GGML_OP_RESHAPE
};
static const std::vector<ggml_op> ops_topk_moe_sp_norm_scale = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sp_norm_bias = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE
};
static const std::vector<ggml_op> ops_topk_moe_sp_norm_scale_bias = {
GGML_OP_UNARY, GGML_OP_SQRT, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sig = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS
};
static const std::vector<ggml_op> ops_topk_moe_sig_scale = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sig_norm = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, GGML_OP_RESHAPE
};
static const std::vector<ggml_op> ops_topk_moe_sig_norm_scale = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static const std::vector<ggml_op> ops_topk_moe_sig_norm_bias = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE
};
static const std::vector<ggml_op> ops_topk_moe_sig_norm_scale_bias = {
GGML_OP_UNARY, GGML_OP_RESHAPE, GGML_OP_ADD, GGML_OP_ARGSORT, GGML_OP_VIEW, GGML_OP_GET_ROWS,
GGML_OP_RESHAPE, GGML_OP_SUM_ROWS, GGML_OP_SCALE, GGML_OP_DIV, GGML_OP_RESHAPE, GGML_OP_SCALE
};
static bool ggml_metal_fusion_check_topk_moe(
const ggml_metal_fusion * fusion,
@@ -426,11 +375,8 @@ static bool ggml_metal_fusion_check_topk_moe(
const int n_ops = (int) fusion->ops.size();
bool with_norm = false;
for (int j = 0; j < n_ops; ++j) {
with_norm |= fusion->ops[j] == GGML_OP_SUM_ROWS;
}
const bool with_scale = fusion->ops[n_ops - 1] == GGML_OP_SCALE;
const bool with_norm = n_ops >= 6;
const bool with_scale = n_ops == 4 || n_ops == 7;
// the fusion table operates on the non-empty node sequence; the raw graph also
// contains the RESHAPE/VIEW nodes that the fused kernel elides.
@@ -465,70 +411,24 @@ static bool ggml_metal_fusion_check_topk_moe(
}
}
const ggml_tensor * first = gf->nodes[raw_start];
const ggml_tensor * out = gf->nodes[raw_end];
const ggml_tensor * softmax = gf->nodes[raw_start];
const ggml_tensor * probs_reshaped = gf->nodes[raw_start + 1];
const ggml_tensor * argsort = gf->nodes[raw_start + 2];
const ggml_tensor * ids = gf->nodes[raw_start + 3];
const ggml_tensor * get_rows = gf->nodes[raw_start + 4];
const ggml_tensor * out = gf->nodes[raw_end];
const ggml_tensor * logits = softmax->src[0];
// the unary chains start at the sigmoid or the softplus node and rank biased scores
const bool with_unary = first->op == GGML_OP_UNARY;
const bool with_sp = with_unary && ggml_get_unary_op(first) == GGML_UNARY_OP_SOFTPLUS;
const bool with_sig = with_unary && ggml_get_unary_op(first) == GGML_UNARY_OP_SIGMOID;
if (with_unary && !with_sp && !with_sig) {
// the fused kernel implements plain softmax only
float scale = 1.0f;
float max_bias = 0.0f;
memcpy(&scale, ((const int32_t *) softmax->op_params) + 0, sizeof(scale));
memcpy(&max_bias, ((const int32_t *) softmax->op_params) + 1, sizeof(max_bias));
if (scale != 1.0f || max_bias != 0.0f || softmax->src[1] || softmax->src[2]) {
return false;
}
const ggml_tensor * probs; // the scoring output
const ggml_tensor * scores; // the node the argsort ranks (adds the bias for the unary chains)
const ggml_tensor * probs_reshaped;
const ggml_tensor * argsort;
const ggml_tensor * ids;
const ggml_tensor * get_rows;
const ggml_tensor * logits = first->src[0];
int ids_off;
if (with_unary) {
// the softplus chains add a SQRT node, the sigmoid chains start at the sigmoid node
const int base = with_sp ? 1 : 0;
probs = gf->nodes[raw_start + base + 0];
probs_reshaped = gf->nodes[raw_start + base + 1];
scores = gf->nodes[raw_start + base + 2];
argsort = gf->nodes[raw_start + base + 3];
ids = gf->nodes[raw_start + base + 4];
get_rows = gf->nodes[raw_start + base + 5];
ids_off = base + 4;
if (scores->op != GGML_OP_ADD || scores->src[0] != probs ||
(with_sp && (probs->op != GGML_OP_SQRT || probs->src[0] != first))) {
return false;
}
// the selection bias is a per-expert vector shared by all rows
const ggml_tensor * bias = scores->src[1];
if (bias->type != GGML_TYPE_F32 || scores->type != GGML_TYPE_F32 || !ggml_is_contiguous(bias) ||
bias->ne[0] != probs->ne[0] || bias->ne[1] != 1 || bias->ne[2] != 1 || bias->ne[3] != 1) {
return false;
}
} else {
probs = first;
scores = first;
probs_reshaped = gf->nodes[raw_start + 1];
argsort = gf->nodes[raw_start + 2];
ids = gf->nodes[raw_start + 3];
get_rows = gf->nodes[raw_start + 4];
ids_off = 3;
// the fused kernel implements plain softmax only
float scale = 1.0f;
float max_bias = 0.0f;
memcpy(&scale, ((const int32_t *) first->op_params) + 0, sizeof(scale));
memcpy(&max_bias, ((const int32_t *) first->op_params) + 1, sizeof(max_bias));
if (scale != 1.0f || max_bias != 0.0f || first->src[1] || first->src[2]) {
return false;
}
}
if (logits->type != GGML_TYPE_F32 || probs->type != GGML_TYPE_F32 ||
if (logits->type != GGML_TYPE_F32 || softmax->type != GGML_TYPE_F32 ||
out->type != GGML_TYPE_F32 || ids->type != GGML_TYPE_I32) {
return false;
}
@@ -555,40 +455,38 @@ static bool ggml_metal_fusion_check_topk_moe(
return false;
}
// the top-k gathers the unbiased scores, the argsort ranks the biased ones
if (probs_reshaped->src[0] != probs || argsort->src[0] != scores ||
if (probs_reshaped->src[0] != softmax || argsort->src[0] != softmax ||
ids->src[0] != argsort || get_rows->src[0] != probs_reshaped || get_rows->src[1] != ids) {
return false;
}
if (with_norm) {
const int norm_off = with_unary ? 6 + (with_sp ? 1 : 0) : 5;
const ggml_tensor * weights_reshaped = gf->nodes[raw_start + norm_off + 0];
const ggml_tensor * sum_rows = gf->nodes[raw_start + norm_off + 1];
const ggml_tensor * denom = gf->nodes[raw_start + norm_off + 2];
const ggml_tensor * div = gf->nodes[raw_start + norm_off + 3];
const ggml_tensor * out_reshaped = gf->nodes[raw_start + norm_off + 4];
const ggml_tensor * weights_reshaped = gf->nodes[raw_start + 5];
const ggml_tensor * sum_rows = gf->nodes[raw_start + 6];
const ggml_tensor * clamp = gf->nodes[raw_start + 7];
const ggml_tensor * div = gf->nodes[raw_start + 8];
const ggml_tensor * out_reshaped = gf->nodes[raw_start + 9];
if (weights_reshaped->src[0] != get_rows || sum_rows->src[0] != weights_reshaped ||
denom->src[0] != sum_rows || div->src[0] != weights_reshaped || div->src[1] != denom ||
clamp->src[0] != sum_rows || div->src[0] != weights_reshaped || div->src[1] != clamp ||
out_reshaped->src[0] != div) {
return false;
}
// the kernel applies the sum adjustment as a lower clamp or an additive bias, never a scale
if (denom->op == GGML_OP_SCALE && ggml_get_op_params_f32(denom, 0) != 1.0f) {
if (with_scale) {
const ggml_tensor * scale_node = gf->nodes[raw_start + 10];
if (scale_node->src[0] != out_reshaped) {
return false;
}
}
} else if (with_scale) {
const ggml_tensor * scale_node = gf->nodes[raw_start + 5];
if (scale_node->src[0] != get_rows) {
return false;
}
if (with_scale && out->src[0] != out_reshaped) {
return false;
}
} else if (with_scale && out->src[0] != get_rows) {
return false;
}
const int outputs[2] = { raw_start + ids_off, raw_end };
const int outputs[2] = { raw_start + 3, raw_end };
if (!ggml_can_fuse_subgraph_ext(gf, raw_idxs, raw_count, ops_all.data(), outputs, 2)) {
return false;
}
@@ -865,54 +763,33 @@ static const std::vector<ggml_op> ops_moe_reduce_8 = {
static const std::vector<ggml_op> ops_mul_mat_add = { GGML_OP_MUL_MAT, GGML_OP_ADD };
static const std::vector<ggml_metal_fusion> ggml_metal_fusions = {
{ GGML_METAL_FUSION_NORM_MUL, ops_norm_mul, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL_ADD, ops_norm_mul_add, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_SCALE, ops_norm_scale, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL, ops_rms_norm_mul, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL_ADD, ops_rms_norm_mul_add, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_SCALE, ops_rms_norm_scale, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_2, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_3, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_4, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_5, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_6, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_7, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_SNAKE, ops_snake, {}, false, ggml_metal_fusion_check_snake },
{ GGML_METAL_FUSION_GDN_CACHE, ops_gdn_cache, {}, true, ggml_metal_fusion_check_gdn_cache },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_scale, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm_scale, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm_bias, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm_scale_bias, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp_scale, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp_norm, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp_norm_scale, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp_norm_bias, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sp_norm_scale_bias, {3}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig_scale, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig_norm, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig_norm_scale, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig_norm_bias, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_sig_norm_scale_bias, {2}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_2, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_3, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_4, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_5, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_6, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_7, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_8, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_SSM_CONV_SILU, ops_ssm_conv_silu, {}, false, ggml_metal_fusion_check_ssm_conv_silu },
{ GGML_METAL_FUSION_MUL_MAT_ADD, ops_mul_mat_add, {}, false, ggml_metal_fusion_check_mul_mat_add },
{ GGML_METAL_FUSION_NORM_MUL, ops_norm_mul, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL_ADD, ops_norm_mul_add, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_SCALE, ops_norm_scale, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL, ops_rms_norm_mul, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_MUL_ADD, ops_rms_norm_mul_add, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_NORM_SCALE, ops_rms_norm_scale, {}, false, ggml_metal_fusion_check_norm },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_2, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_3, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_4, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_5, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_6, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_ADD_CHAIN, ops_add_7, {}, false, ggml_metal_fusion_check_add_chain },
{ GGML_METAL_FUSION_SNAKE, ops_snake, {}, false, ggml_metal_fusion_check_snake },
{ GGML_METAL_FUSION_GDN_CACHE, ops_gdn_cache, {}, true, ggml_metal_fusion_check_gdn_cache },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_scale, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_TOPK_MOE, ops_topk_moe_norm_scale, {1}, true, ggml_metal_fusion_check_topk_moe },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_2, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_3, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_4, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_5, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_6, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_7, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_MOE_REDUCE, ops_moe_reduce_8, {}, true, ggml_metal_fusion_check_moe_reduce },
{ GGML_METAL_FUSION_SSM_CONV_SILU, ops_ssm_conv_silu, {}, false, ggml_metal_fusion_check_ssm_conv_silu },
{ GGML_METAL_FUSION_MUL_MAT_ADD, ops_mul_mat_add, {}, false, ggml_metal_fusion_check_mul_mat_add },
};
// ---- alloc deps -----------------------------------------------------------
-1
View File
@@ -1270,7 +1270,6 @@ typedef struct {
uint64_t nb01; // logits row stride
uint64_t nb1_ids; // ids row stride
float clamp;
float bias;
float scale;
} ggml_metal_kargs_topk_moe;
+14 -47
View File
@@ -821,16 +821,6 @@ int ggml_metal_op_acc(ggml_metal_op_t ctx, int idx) {
int ggml_metal_op_unary(ggml_metal_op_t ctx, int idx) {
ggml_tensor * op = ctx->node(idx);
// the sigmoid and sqrt(softplus) TOPK_MOE fusions anchor at the gating unary node
if (op->op == GGML_OP_UNARY && ctx->use_fusion() &&
(ggml_get_unary_op(op) == GGML_UNARY_OP_SOFTPLUS || ggml_get_unary_op(op) == GGML_UNARY_OP_SIGMOID)) {
int n = 1;
const ggml_metal_fusion * fusion = ctx->can_fuse(idx, GGML_METAL_FUSION_FULL, &n);
if (fusion && ggml_metal_fusion_get_id(fusion) == GGML_METAL_FUSION_TOPK_MOE) {
return ggml_metal_op_topk_moe(ctx, idx);
}
}
ggml_metal_library_t lib = ctx->lib;
ggml_metal_encoder_t enc = ctx->enc;
@@ -6033,48 +6023,29 @@ int ggml_metal_op_topk_moe(ggml_metal_op_t ctx, int idx) {
return 1;
}
ggml_tensor * first = ctx->node(idx);
ggml_tensor * logits = first->src[0];
// the unary chains score with sigmoid or sqrt(softplus) and rank biased scores
const bool with_unary = first->op == GGML_OP_UNARY;
const int gating = !with_unary ? 0 : ggml_get_unary_op(first) == GGML_UNARY_OP_SOFTPLUS ? 1 : 2;
ggml_tensor * get_rows = nullptr;
ggml_tensor * sum_rows = nullptr;
ggml_tensor * prob_bias = nullptr;
int sum_rows_pos = -1;
for (int j = 0; j < n_fuse; ++j) {
ggml_tensor * node = ctx->node(idx + j);
if (node->op == GGML_OP_GET_ROWS) { get_rows = node; }
if (node->op == GGML_OP_ADD) { prob_bias = node->src[1]; }
if (node->op == GGML_OP_SUM_ROWS) { sum_rows = node; sum_rows_pos = j; }
}
ggml_tensor * ids = get_rows->src[1];
ggml_tensor * weights = ctx->node(idx + n_fuse - 1);
const bool with_norm = sum_rows != nullptr;
const bool with_scale = weights->op == GGML_OP_SCALE;
ggml_tensor * softmax = ctx->node(idx);
ggml_tensor * logits = softmax->src[0];
ggml_tensor * get_rows = ctx->node(idx + 2);
ggml_tensor * ids = get_rows->src[1];
ggml_tensor * weights = ctx->node(idx + n_fuse - 1);
const int64_t n_expert = logits->ne[0];
const int64_t n_tokens = logits->ne[1];
const int64_t n_expert_used = ids->ne[0];
const bool with_norm = n_fuse >= 6;
const bool with_scale = n_fuse == 4 || n_fuse == 7;
float clamp = -INFINITY;
float bias = 0.0f;
if (with_norm) {
ggml_tensor * denom_node = ctx->node(idx + sum_rows_pos + 1);
if (denom_node->op == GGML_OP_SCALE) {
bias = ggml_get_op_params_f32(denom_node, 1);
} else {
clamp = ggml_get_op_params_f32(denom_node, 0);
}
ggml_tensor * clamp_node = ctx->node(idx + 4);
clamp = ggml_get_op_params_f32(clamp_node, 0);
}
float scale = 1.0f;
if (with_scale) {
scale = ggml_get_op_params_f32(weights, 0);
ggml_tensor * scale_node = ctx->node(idx + n_fuse - 1);
scale = ggml_get_op_params_f32(scale_node, 0);
}
ggml_metal_kargs_topk_moe args = {
@@ -6082,27 +6053,23 @@ int ggml_metal_op_topk_moe(ggml_metal_op_t ctx, int idx) {
/*.nb01 =*/ logits->nb[1],
/*.nb1_ids =*/ ids->nb[1],
/*.clamp =*/ clamp,
/*.bias =*/ bias,
/*.scale =*/ scale,
};
auto pipeline = ggml_metal_library_get_pipeline_topk_moe(lib, (int32_t) n_expert, (int32_t) n_expert_used,
with_norm, gating, prob_bias != nullptr);
auto pipeline = ggml_metal_library_get_pipeline_topk_moe(lib, (int32_t) n_expert, (int32_t) n_expert_used, with_norm);
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(logits), 1);
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(weights), 2);
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(ids), 3);
ggml_metal_encoder_set_buffer (enc, ggml_metal_get_buffer_id(prob_bias ? prob_bias : logits), 4);
ggml_metal_encoder_dispatch_threadgroups(enc, (uint32_t) n_tokens, 1, 1, 32, 1, 1);
ctx->count_fusions(fusion);
if (ggml_metal_fusion_info_debug(ctx->finfo) > 1) {
GGML_LOG_DEBUG("%s: fuse: %s + ARGSORT + GET_ROWS\n", __func__,
gating == 1 ? "SOFTPLUS + SQRT" : gating == 2 ? "SIGMOID" : "SOFT_MAX");
GGML_LOG_DEBUG("%s: fuse: SOFT_MAX + ARGSORT + GET_ROWS\n", __func__);
}
return n_fuse;
+21 -52
View File
@@ -3,8 +3,6 @@
constant bool FC_topk_moe_with_norm [[function_constant(FC_TOPK_MOE + 0)]];
constant int FC_topk_moe_n_expert [[function_constant(FC_TOPK_MOE + 1)]];
constant int FC_topk_moe_top_k [[function_constant(FC_TOPK_MOE + 2)]];
constant int FC_topk_moe_gating [[function_constant(FC_TOPK_MOE + 3)]];
constant bool FC_topk_moe_pbias [[function_constant(FC_TOPK_MOE + 4)]];
constant int FC_moe_reduce_n_expert_used [[function_constant(FC_MOE_REDUCE + 0)]];
@@ -344,14 +342,13 @@ kernel void kernel_top_k_f32_i32(
}
}
// fused SOFT_MAX or sqrt(softplus) + top-k + GET_ROWS (+ optional norm/scale) for MoE routing.
// fused SOFT_MAX + top-k + GET_ROWS (+ optional norm/scale) for MoE routing.
// One SIMDgroup handles one token row; n_expert is limited to 1024 by the host.
kernel void kernel_topk_moe_f32(
constant ggml_metal_kargs_topk_moe & args,
device const char * src0,
device float * weights,
device int32_t * ids,
device const float * probs_bias,
uint3 tgpig[[threadgroup_position_in_grid]],
ushort tiisg[[thread_index_in_simdgroup]]) {
const int row = (int) tgpig.x;
@@ -380,78 +377,50 @@ kernel void kernel_topk_moe_f32(
wt[i / 32] = isnan(v) ? -FLT_MAX : v;
}
if (FC_topk_moe_gating == 1) {
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
const float v = wt[i];
wt[i] = sqrt(v > 20.0f ? v : log(1.0f + exp(v)));
}
} else if (FC_topk_moe_gating == 2) {
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
wt[i] = 1.0f / (1.0f + exp(-wt[i]));
}
} else {
// softmax over the expert logits
float max_val = -INFINITY;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
max_val = max(max_val, wt[i]);
}
max_val = simd_max(max_val);
float sum_val = 0.0f;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
wt[i] = exp(wt[i] - max_val);
sum_val += wt[i];
}
sum_val = simd_sum(sum_val);
const float inv_sum = 1.0f / sum_val;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
wt[i] *= inv_sum;
}
// softmax over the expert logits
float max_val = -INFINITY;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
max_val = max(max_val, wt[i]);
}
max_val = simd_max(max_val);
// the ranking keys add the selection bias, the output weights stay unbiased
float sel[32];
if (FC_topk_moe_pbias) {
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
const int expert = lane + i * 32;
sel[i] = expert < n_expert ? wt[i] + probs_bias[expert] : -INFINITY;
}
float sum_val = 0.0f;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
wt[i] = exp(wt[i] - max_val);
sum_val += wt[i];
}
sum_val = simd_sum(sum_val);
const float inv_sum = 1.0f / sum_val;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
wt[i] *= inv_sum;
}
float wt_sum = 0.0f;
for (int k = 0; k < top_k; ++k) {
float best_val = -INFINITY;
float best_key = -INFINITY;
float best_val = -INFINITY;
int best_expert = -1;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
const int expert = lane + i * 32;
const float key = FC_topk_moe_pbias ? sel[i] : wt[i];
if (expert < n_expert && (key > best_key || (key == best_key && expert < best_expert))) {
const int expert = lane + i * 32;
if (expert < n_expert && (wt[i] > best_val || (wt[i] == best_val && expert < best_expert))) {
best_val = wt[i];
best_key = key;
best_expert = expert;
}
}
FOR_UNROLL (int mask = 16; mask > 0; mask >>= 1) {
const float val = simd_shuffle_xor(best_val, mask);
const float key = simd_shuffle_xor(best_key, mask);
const int expert = simd_shuffle_xor(best_expert, mask);
if (key > best_key || (key == best_key && expert < best_expert)) {
if (val > best_val || (val == best_val && expert < best_expert)) {
best_val = val;
best_key = key;
best_expert = expert;
}
}
if ((best_expert & 31) == lane) {
wt[best_expert / 32] = -INFINITY;
if (FC_topk_moe_pbias) {
sel[best_expert / 32] = -INFINITY;
}
}
if ((k & 31) == lane) {
@@ -468,7 +437,7 @@ kernel void kernel_topk_moe_f32(
if (FC_topk_moe_with_norm) {
wt_sum = simd_sum(wt_sum);
wt_sum = max(wt_sum, args.clamp) + args.bias;
wt_sum = max(wt_sum, args.clamp);
const float inv = 1.0f / wt_sum;
FOR_UNROLL (int i = 0; i < n_per_lane; ++i) {
output_weights[i] *= inv;
+1 -2
View File
@@ -895,8 +895,7 @@ typedef decltype(kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4,
template [[host_name("kernel_mul_mm_f32_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, float4x4, 1, dequantize_f32, float, float4x4, float, float2x4>;
template [[host_name("kernel_mul_mm_f16_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, half4x4, 1, dequantize_f16, half, half4x4, float, float2x4>;
#if defined(GGML_METAL_HAS_BF16)
template [[host_name("kernel_mul_mm_bf16_f32")]] kernel mul_mm_t kernel_mul_mm<bfloat, bfloat4x4, simdgroup_bfloat8x8, bfloat, bfloat2x4, simdgroup_bfloat8x8, bfloat4x4, 1, dequantize_bf16, bfloat, bfloat4x4, float, float2x4>;
template [[host_name("kernel_mul_mm_bf16_bf16")]] kernel mul_mm_t kernel_mul_mm<bfloat, bfloat4x4, simdgroup_bfloat8x8, bfloat, bfloat2x4, simdgroup_bfloat8x8, bfloat4x4, 1, dequantize_bf16, bfloat, bfloat4x4, bfloat, bfloat2x4>;
template [[host_name("kernel_mul_mm_bf16_f32")]] kernel mul_mm_t kernel_mul_mm<bfloat, bfloat4x4, simdgroup_bfloat8x8, bfloat, bfloat2x4, simdgroup_bfloat8x8, bfloat4x4, 1, dequantize_bf16, bfloat, bfloat4x4, float, float2x4>;
#endif
template [[host_name("kernel_mul_mm_q1_0_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q1_0, 8, dequantize_q1_0, float, float4x4, float, float2x4>;
template [[host_name("kernel_mul_mm_q2_0_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_q2_0, 4, dequantize_q2_0, float, float4x4, float, float2x4>;
-1
View File
@@ -78,7 +78,6 @@ set(GGML_OPENCL_KERNELS
diag_mask_inf
diag
div
dsv4_hc
gelu
get_rows
glu
+73 -203
View File
@@ -853,7 +853,6 @@ struct ggml_backend_opencl_context {
cl_kernel kernel_gelu_quick, kernel_gelu_quick_4;
cl_kernel kernel_relu;
cl_kernel kernel_sigmoid_f32, kernel_sigmoid_f16;
cl_kernel kernel_sigmoid_f32_nc, kernel_sigmoid_f16_nc;
cl_kernel kernel_tri;
cl_kernel kernel_fill;
cl_kernel kernel_clamp, kernel_clamp_nc;
@@ -1105,12 +1104,8 @@ struct ggml_backend_opencl_context {
cl_kernel kernel_argmax_f32_i32;
cl_kernel kernel_argsort_f32_i32;
cl_kernel kernel_sum_rows_f32, kernel_sum_rows_f32_4;
cl_kernel kernel_dsv4_hc_pre = nullptr;
cl_kernel kernel_dsv4_hc_post[2] = {};
size_t local_dsv4_hc_pre = 0;
size_t local_dsv4_hc_post[2] = {};
cl_kernel kernel_cumsum_blk, kernel_cumsum_add;
cl_kernel kernel_repeat_f32, kernel_repeat_f16, kernel_repeat_i32;
cl_kernel kernel_repeat_f32;
cl_kernel kernel_pad;
cl_kernel kernel_tanh_f32, kernel_tanh_f32_4, kernel_tanh_f32_nc;
cl_kernel kernel_tanh_f16, kernel_tanh_f16_4, kernel_tanh_f16_nc;
@@ -1423,7 +1418,7 @@ struct ggml_backend_opencl_context {
cl_kernel kernel_gemv_noshuffle_q6_K_f32_o4;
cl_kernel kernel_gemv_noshuffle_q6_K_f32_o4_global; // weights via __global (opt-in)
cl_kernel kernel_gemv_noshuffle_q6_K_f32_tiled; // tiled-wide layout (opt-in)
cl_kernel kernel_gemv_noshuffle_q6_K_f32_tiled_mc3; // tiled multi-column (N 2..4) verify lm_head
cl_kernel kernel_gemv_noshuffle_q6_K_f32_tiled_mc3; // tiled multi-column (N=3) verify lm_head
cl_kernel kernel_gemm_noshuffle_q6_K_f32_tiled; // batched (N>1) over the tiled layout
cl_kernel kernel_convert_block_q6_k_tiled_ns; // tiled-wide convert (opt-in)
cl_kernel kernel_gemv_noshuffle_q6_K_f32_mc3; // multi-column (N=3) verify GEMV
@@ -1468,14 +1463,6 @@ struct ggml_backend_opencl_context {
#endif // GGML_OPENCL_USE_ADRENO_KERNELS
~ggml_backend_opencl_context() {
if (kernel_dsv4_hc_pre) {
CL_CHECK(clReleaseKernel(kernel_dsv4_hc_pre));
}
for (int i = 0; i < 2; ++i) {
if (kernel_dsv4_hc_post[i]) {
CL_CHECK(clReleaseKernel(kernel_dsv4_hc_post[i]));
}
}
if (iq1_s_grid_image) {
CL_CHECK(clReleaseMemObject(iq1_s_grid_image));
}
@@ -4240,28 +4227,6 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
GGML_LOG_CONT(".");
}
// dsv4_hc
{
#ifdef GGML_OPENCL_EMBED_KERNELS
const std::string kernel_src {
#include "dsv4_hc.cl.h"
};
#else
const std::string kernel_src = read_file("dsv4_hc.cl");
#endif
for (int i = 0; i < 2; ++i) {
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts + (i == 0 ? " -DHC_VEC=1" : " -DHC_VEC=4"));
if (i == 0) {
CL_CHECK((backend_ctx->kernel_dsv4_hc_pre = clCreateKernel(prog, "kernel_dsv4_hc_pre", &err), err));
backend_ctx->local_dsv4_hc_pre = MIN((size_t) 64, backend_ctx->get_kernel_workgroup_size(backend_ctx->kernel_dsv4_hc_pre));
}
CL_CHECK((backend_ctx->kernel_dsv4_hc_post[i] = clCreateKernel(prog, "kernel_dsv4_hc_post", &err), err));
backend_ctx->local_dsv4_hc_post[i] = MIN((size_t) 64, backend_ctx->get_kernel_workgroup_size(backend_ctx->kernel_dsv4_hc_post[i]));
CL_CHECK(clReleaseProgram(prog));
}
GGML_LOG_CONT(".");
}
// sum_rows
{
#ifdef GGML_OPENCL_EMBED_KERNELS
@@ -4311,8 +4276,6 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
CL_CHECK((backend_ctx->kernel_sigmoid_f32 = clCreateKernel(backend_ctx->program_sigmoid, "kernel_sigmoid_f32", &err), err));
CL_CHECK((backend_ctx->kernel_sigmoid_f16 = clCreateKernel(backend_ctx->program_sigmoid, "kernel_sigmoid_f16", &err), err));
CL_CHECK((backend_ctx->kernel_sigmoid_f32_nc = clCreateKernel(backend_ctx->program_sigmoid, "kernel_sigmoid_f32_nc", &err), err));
CL_CHECK((backend_ctx->kernel_sigmoid_f16_nc = clCreateKernel(backend_ctx->program_sigmoid, "kernel_sigmoid_f16_nc", &err), err));
GGML_LOG_CONT(".");
}
@@ -4345,8 +4308,6 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
cl_program prog =
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
CL_CHECK((backend_ctx->kernel_repeat_f32 = clCreateKernel(prog, "kernel_repeat_f32", &err), err));
CL_CHECK((backend_ctx->kernel_repeat_f16 = clCreateKernel(prog, "kernel_repeat_f16", &err), err));
CL_CHECK((backend_ctx->kernel_repeat_i32 = clCreateKernel(prog, "kernel_repeat_i32", &err), err));
CL_CHECK(clReleaseProgram(prog));
GGML_LOG_CONT(".");
}
@@ -6450,13 +6411,16 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
if (backend_ctx->adreno_gen == ADRENO_GPU_GEN::A6X) {
CL_gemv_compile_opts += " -DGGML_CL_A6X_CONSTFOLD_FIX";
}
// dequant-once-per-block mc3 verify GEMV
if (getenv("GGML_OPENCL_Q4_K_MC3_DQ")) {
// Opt-in: dequant-once-per-block mc3 verify GEMV (factors q4_K dequant
// out of the 3-column loop; byte-identical, lower spill). A/B vs the
// shipped inline mc3 in the same binary.
if (getenv("GGML_OPENCL_Q4K_MC3_DQ")) {
CL_gemv_compile_opts += " -DQ4K_MC3_DEQUANT_ONCE ";
}
// LM-staged dequant mc3 verify GEMV, stages the dequantized
// q4_K weights in __local instead of regs
if (getenv("GGML_OPENCL_Q4_K_MC3_LDS")) {
// Opt-in: LDS-staged dequant mc3 verify GEMV (stages the dequantized
// q4_K weights in __local instead of private regs that spill to slow
// global on Adreno; byte-identical). A/B vs inline + dequant-once.
if (getenv("GGML_OPENCL_Q4K_MC3_LDS")) {
CL_gemv_compile_opts += " -DQ4K_MC3_DEQUANT_LDS ";
}
@@ -7378,6 +7342,17 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q6_K_f32 = clCreateKernel(prog, "kernel_gemv_noshuffle_q6_K_f32", &err), err));
CL_CHECK((backend_ctx->kernel_gemv_noshuffle_q6_K_f32_mc3 = clCreateKernel(prog, "kernel_gemv_noshuffle_q6_K_f32_mc3", &err), err));
if (getenv("GGML_OPENCL_MC3_PROBE")) {
cl_ulong pm6 = 0, pm4 = 0; size_t wg6 = 0, wg4 = 0, mult = 0;
clGetKernelWorkGroupInfo(backend_ctx->kernel_gemv_noshuffle_q6_K_f32_mc3, backend_ctx->device, CL_KERNEL_PRIVATE_MEM_SIZE, sizeof(pm6), &pm6, NULL);
clGetKernelWorkGroupInfo(backend_ctx->kernel_gemv_noshuffle_q6_K_f32_mc3, backend_ctx->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wg6), &wg6, NULL);
clGetKernelWorkGroupInfo(backend_ctx->kernel_gemv_noshuffle_q4_k_f32_mc3, backend_ctx->device, CL_KERNEL_PRIVATE_MEM_SIZE, sizeof(pm4), &pm4, NULL);
clGetKernelWorkGroupInfo(backend_ctx->kernel_gemv_noshuffle_q4_k_f32_mc3, backend_ctx->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wg4), &wg4, NULL);
clGetKernelWorkGroupInfo(backend_ctx->kernel_gemv_noshuffle_q6_K_f32_mc3, backend_ctx->device, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(mult), &mult, NULL);
fprintf(stderr, "[MC3-PROBE] q4K_mc3 private=%llu wg_cap=%zu | q6K_mc3 private=%llu wg_cap=%zu | pref_mult=%zu\n",
(unsigned long long)pm4, wg4, (unsigned long long)pm6, wg6, mult);
fflush(stderr);
}
GGML_LOG_CONT(".");
}
@@ -7860,8 +7835,7 @@ static void ggml_opencl_ensure_fa_f32_f16_mq_decode(ggml_backend_opencl_context
}
const bool cluster = dk == 64 || dk == 128;
const int head_sub = gqa == 16 ? (cluster ? 4 : 8) :
cluster ? 2 : (gqa == 8 ? (dk == 512 ? 4 : 2) : 1);
const int head_sub = cluster ? 2 : (gqa == 8 ? (dk == 512 ? 4 : 2) : 1);
const int nsg_max = dk == 64 ? 1 : (dk == 128 || (gqa == 8 && dk == 256) ? 2 : 4);
const char * kernel_name = cluster ? "flash_attn_f32_f16_q1_vec_mq_split_c8" : "flash_attn_f32_f16_q1_vec_mq_split";
const std::string src = ggml_opencl_fa_kernel_src(FA_VARIANT_F32_F16);
@@ -7869,7 +7843,7 @@ static void ggml_opencl_ensure_fa_f32_f16_mq_decode(ggml_backend_opencl_context
" -D FA_MQ_ONLY -D MQ_GQA=" + std::to_string(gqa / head_sub) +
" -D FA_HEAD_SUB=" + std::to_string(head_sub) +
(cluster ? " -D MQ_NSG=" + std::to_string(nsg_max) + " -D FA_CL_C=16" : " -D FA_MQ_SPLIT_ONLY") +
(dk == 64 ? " -D FA_CL_MHRED -D FA_CL_MASK_BCAST" : (cluster && gqa == 16 ? " -D FA_CL_MHRED" : "")) +
(dk == 64 ? " -D FA_CL_MHRED -D FA_CL_MASK_BCAST" : "") +
(gqa == 8 && dk == 256 ? " -D FA_Q1_Q_REG" : "");
for (int nsg = nsg_max; nsg >= 1; nsg /= 2) {
const size_t wg = 64 * nsg;
@@ -10865,8 +10839,7 @@ static void ggml_cl_mul_mat_q4_k_glu_fused(ggml_backend_t backend, ggml_tensor *
// byte-identical to the per-op path. Keep the maxwg query as a further floor
// for any driver that reports < 512.
size_t maxwg = backend_ctx->get_kernel_workgroup_size(kernel);
// X2E: 4 is faster than 8 on every q4_K FFN shape measured there
size_t nsg_y = backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E ? 4 : 8;
size_t nsg_y = 8;
while (nsg_y > 1 && 64 * nsg_y > maxwg) { nsg_y >>= 1; }
size_t local_work_size[3] = { 64, nsg_y, 1 };
size_t global_work_size[3] = { (size_t)CEIL_DIV(M / 2, 64) * 64, nsg_y, 1 };
@@ -11750,8 +11723,7 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
case GGML_UNARY_OP_GELU_QUICK:
return ggml_is_contiguous(op->src[0]) && op->src[0]->type == GGML_TYPE_F32;
case GGML_UNARY_OP_SIGMOID:
return op->src[0]->type == op->type &&
(op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_BF16);
return ggml_is_contiguous(op->src[0]);
case GGML_UNARY_OP_TANH:
case GGML_UNARY_OP_NEG:
case GGML_UNARY_OP_EXP:
@@ -11802,16 +11774,7 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
case GGML_OP_L2_NORM:
return ggml_is_contiguous_rows(op->src[0]);
case GGML_OP_REPEAT:
switch (op->type) {
case GGML_TYPE_F32:
case GGML_TYPE_F16:
case GGML_TYPE_I32:
case GGML_TYPE_I16:
case GGML_TYPE_BF16:
return op->src[0]->type == op->type;
default:
return false;
}
return op->src[0]->type == GGML_TYPE_F32 && op->type == GGML_TYPE_F32; // Assuming F32 for now, can be expanded
case GGML_OP_PAD:
// TODO: add circular padding support for opencl, see https://github.com/ggml-org/llama.cpp/pull/16985
if (ggml_get_op_params_i32(op, 8) != 0) {
@@ -12022,9 +11985,6 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
return op->ne[3] == 1;
case GGML_OP_ROPE: {
const int mode = ((const int32_t *) op->op_params)[2];
if (!ggml_rope_get_truncate(op)) {
return false;
}
const bool is_mrope = mode & GGML_ROPE_TYPE_MROPE;
const bool is_vision = mode == GGML_ROPE_TYPE_VISION;
if (is_mrope && !is_vision) {
@@ -12086,14 +12046,6 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
case GGML_OP_SUM_ROWS:
case GGML_OP_CUMSUM:
return op->src[0]->type == GGML_TYPE_F32 && ggml_is_contiguous(op->src[0]);
case GGML_OP_DSV4_HC_PRE:
case GGML_OP_DSV4_HC_POST:
for (int i = 0; i < (op->op == GGML_OP_DSV4_HC_PRE ? 2 : 4); ++i) {
if (op->src[i] && op->src[i]->type != GGML_TYPE_F32) {
return false;
}
}
return op->type == GGML_TYPE_F32 && ggml_is_contiguous(op);
case GGML_OP_MEAN:
return op->src[0]->type == GGML_TYPE_F32;
case GGML_OP_FLASH_ATTN_EXT: {
@@ -12196,13 +12148,9 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
}
if (q->ne[1] == 1) {
const char * decode_env = getenv("GGML_OPENCL_FA_DK512_DECODE");
const int64_t gqa = k->ne[2] > 0 ? q->ne[2] / k->ne[2] : 0;
// GQA 2 and 16 decode on the X2E MQ kernels (see mq_decode_x2e in ggml_cl_flash_attn)
const bool gqa_ok = gqa == 4 || gqa == 8 ||
(backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E && (gqa == 2 || gqa == 16));
if ((decode_env && decode_env[0] == '0') ||
backend_ctx->gpu_family != ADRENO || k->ne[2] <= 0 ||
!gqa_ok || q->ne[2] % k->ne[2] != 0 ||
(q->ne[2] / k->ne[2] != 4 && q->ne[2] / k->ne[2] != 8) || q->ne[2] % k->ne[2] != 0 ||
!ggml_opencl_ensure_fa_f32_f16_vec_512(backend_ctx)) {
return false;
}
@@ -18320,7 +18268,7 @@ static void ggml_cl_add(ggml_backend_t backend, const ggml_tensor * src0, const
cl_kernel kernel;
const bool bcast_row = ggml_is_contiguous(src0) && ggml_nelements(src1) == ne10 && ggml_is_contiguous(src1) && ne00 % 4 == 0 && ne10 % 4 == 0;
const bool bcast_row = ggml_nelements(src1) == ne10 && ggml_is_contiguous(src1) && ne00 % 4 == 0 && ne10 % 4 == 0;
if (bcast_row) {
GGML_ASSERT(ggml_is_contiguous(src0));
@@ -19468,33 +19416,6 @@ static void ggml_cl_sigmoid(ggml_backend_t backend, const ggml_tensor * src0, co
cl_ulong offset0 = extra0->offset + src0->view_offs;
cl_ulong offsetd = extrad->offset + dst->view_offs;
if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(dst)) {
GGML_TENSOR_LOCALS(int, ne0, src0, ne);
GGML_TENSOR_LOCALS(cl_ulong, nb0, src0, nb);
GGML_TENSOR_LOCALS(cl_ulong, nb, dst, nb);
cl_kernel kernel = src0->type == GGML_TYPE_F32 ? backend_ctx->kernel_sigmoid_f32_nc : backend_ctx->kernel_sigmoid_f16_nc;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset0));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extrad->data_device));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offsetd));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &ne00));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &nb00));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &nb01));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_ulong), &nb02));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &nb03));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &nb0));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &nb1));
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_ulong), &nb2));
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &nb3));
size_t global_work_size[] = {(size_t)ne01 * 64, (size_t)ne02, (size_t)ne03};
size_t local_work_size[] = {64, 1, 1};
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
return;
}
cl_kernel kernel;
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
kernel = backend_ctx->kernel_sigmoid_f32;
@@ -20830,22 +20751,7 @@ static void ggml_cl_repeat(ggml_backend_t backend, const ggml_tensor * src0, con
const cl_ulong nb2 = dst->nb[2];
const cl_ulong nb3 = dst->nb[3];
cl_kernel kernel;
switch (src0->type) {
case GGML_TYPE_F32:
kernel = backend_ctx->kernel_repeat_f32;
break;
case GGML_TYPE_F16:
case GGML_TYPE_I16:
case GGML_TYPE_BF16:
kernel = backend_ctx->kernel_repeat_f16;
break;
case GGML_TYPE_I32:
kernel = backend_ctx->kernel_repeat_i32;
break;
default:
GGML_ABORT("unsupported type");
}
cl_kernel kernel = backend_ctx->kernel_repeat_f32;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset0));
@@ -22690,18 +22596,11 @@ static void ggml_cl_flash_attn(ggml_backend_t backend, const ggml_tensor * q, co
const std::pair<int, int> dk_dv = {d_head_q, d_head_v};
const int mq_gqa = n_head_kv > 0 ? n_head / n_head_kv : 0;
const std::tuple<int, int, int> mq_decode_key = {d_head_q, d_head_v, mq_gqa};
// X2E also takes GQA 2 and 6 at head size 256 (gemma-4 SWA layers, Qwen3.5-27B),
// GQA 16 at 512 and 128 (gemma-4-12b global layers, muse-glimmer) and GQA 2 at 512 (gemma-4-E4B MTP drafter)
const bool mq_decode_x2e = backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E &&
((d_head_q == 256 && (mq_gqa == 2 || mq_gqa == 6)) ||
(d_head_q == 512 && (mq_gqa == 2 || mq_gqa == 16)) ||
(d_head_q == 128 && mq_gqa == 16 && backend_ctx->has_subgroup_shuffle));
const bool mq_decode_shape = backend_ctx->gpu_family == ADRENO && is_mixed && n_q == 1 &&
d_head_q == d_head_v && n_head_kv > 0 && n_head % n_head_kv == 0 &&
((((d_head_q == 64 && mq_gqa == 8) || (d_head_q == 128 && mq_gqa == 4)) &&
backend_ctx->has_subgroup_shuffle) ||
((d_head_q == 256 || d_head_q == 512) && (mq_gqa == 4 || mq_gqa == 8)) ||
mq_decode_x2e);
((d_head_q == 256 || d_head_q == 512) && (mq_gqa == 4 || mq_gqa == 8)));
if (mq_decode_shape && n_kv >= 32) {
ggml_opencl_ensure_fa_f32_f16_mq_decode(backend_ctx, d_head_q, d_head_v, mq_gqa);
}
@@ -24727,8 +24626,14 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
int N = ne1;
int K = ne00;
// Multi-column (N=3) verify GEMV for q4_0
static const bool q40_mc3 = (getenv("GGML_OPENCL_Q4_0_MC3") != nullptr);
// Multi-column (N=3) verify GEMV for q4_0: route the spec/MTP verify batch
// (ne1==3) onto the efficient GEMV path instead of the transposed-GEMM dead-
// zone (gemm_noshuffle_q4_0 is ~50% of MTP decode on a Q4_0 model since q4_0
// weights have no cok/mc3, unlike q4_K). Reuses the ne1==1 GEMV image setup
// (activation image already sized by N=ne1). Byte-identical. Opt-in via
// GGML_OPENCL_Q40_MC3=1. Per-layer only (ne01 < 32768); q4_0 lm_head doesn't
// occur (token_embd/output stay Q6_K), guard kept for parity with q4_K mc3.
static const bool q40_mc3 = (getenv("GGML_OPENCL_Q40_MC3") != nullptr);
const bool use_q40_mc3 = q40_mc3 && (ne1 >= 2 && ne1 <= 4) && (ne01 < 32768);
const bool use_bin = use_q4_0_bin_kernels(backend_ctx, src0);
@@ -24737,7 +24642,7 @@ static void ggml_cl_mul_mat_q4_0_f32_adreno(ggml_backend_t backend, const ggml_t
if (use_q40_mc3) {
static bool warned = false;
if (!warned) {
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q4_0_MC3 is bypassed by Q4_0 binary kernels\n");
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q40_MC3 is bypassed by Q4_0 binary kernels\n");
warned = true;
}
}
@@ -25115,8 +25020,11 @@ static void ggml_cl_mul_mat_q4_1_f32_adreno(ggml_backend_t backend, const ggml_t
int N = ne1;
int K = ne00;
// Multi-column (N=3) verify GEMV for q4_1
static const bool q41_mc3 = (getenv("GGML_OPENCL_Q4_1_MC3") != nullptr);
// Multi-column (N=3) verify GEMV for q4_1: route the spec/MTP verify batch
// (ne1==3) onto the efficient GEMV path instead of the transposed-GEMM dead-
// zone (gemm_noshuffle_q4_1). Reuses the ne1==1 GEMV image setup. Opt-in via
// GGML_OPENCL_Q41_MC3=1. Per-layer only (ne01 < 32768).
static const bool q41_mc3 = (getenv("GGML_OPENCL_Q41_MC3") != nullptr);
const bool use_q41_mc3 = q41_mc3 && (ne1 >= 2 && ne1 <= 4) && (ne01 < 32768);
if (ne1 == 1 || use_q41_mc3) {
@@ -26933,8 +26841,15 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
cl_uchar mask_d4 = 0x0F;
cl_uchar mask_hi2 = 0xC0;
// Multi-column verify GEMV
static const bool q4k_mc3 = (getenv("GGML_OPENCL_Q4_K_MC3") != nullptr);
// Multi-column verify GEMV: route the spec/MTP verify batch (ne1==3 = 2
// drafts + 1 bonus) onto the efficient GEMV path (subgroup-broadcast, no
// transpose) instead of the transposed-GEMM dead-zone. Reuses the ne1==1
// GEMV setup (the activation image is already sized by N=ne1). Byte-
// identical. Opt-in via GGML_OPENCL_Q4K_MC3=1 while validating.
static const bool q4k_mc3 = (getenv("GGML_OPENCL_Q4K_MC3") != nullptr);
// Per-layer only (ne01 < 32768): the batched large-vocab lm_head at ne1==3
// is left to the existing routing (corrupts on the Adreno GEMV path; x2-
// unified routes batched Q6_K lm_head to CPU). Per-layer mc3 is byte-identical.
const bool use_mc3 = q4k_mc3 && (ne1 == 3) && (ne01 < 32768);
const bool use_bin = use_q4_k_bin_kernels(backend_ctx, src0);
@@ -26943,7 +26858,7 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
if (use_mc3) {
static bool warned = false;
if (!warned) {
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q4_K_MC3 is bypassed by Q4_K binary kernels\n");
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q4K_MC3 is bypassed by Q4_K binary kernels\n");
warned = true;
}
}
@@ -27768,15 +27683,20 @@ static void ggml_cl_mul_mat_q6_K_f32_adreno(ggml_backend_t backend, const ggml_t
cl_image_format img_fmt;
cl_image_desc img_desc;
// Multi-column verify GEMV
static const bool q6k_mc3 = (getenv("GGML_OPENCL_Q6_K_MC3") != nullptr);
// subbuffer and image for activation
// Multi-column verify GEMV: route the spec/MTP verify q6_K matmuls (ne1==3)
// onto the efficient GEMV path instead of the transposed-GEMM dead-zone.
// Reuses the ne1==1 image setup (activation image sized by N=ne1). Byte-
// identical. Opt-in via GGML_OPENCL_Q6K_MC3=1 while validating.
static const bool q6k_mc3 = (getenv("GGML_OPENCL_Q6K_MC3") != nullptr);
// Per-layer only (ne01 < 32768): batched large-vocab lm_head stays on the
// existing path (x2-unified routes batched Q6_K lm_head to CPU; the Adreno
// GEMV corrupts it). Per-layer mc3 is byte-identical.
const bool use_q6k_mc3 = q6k_mc3 && (ne1 == 3) && (ne01 < 32768);
// Multi-column tiled vocab GEMV for verify
// streams the large lm_head weight once across the verify columns
static const char * q6k_tiled_mc_env = getenv("GGML_OPENCL_Q6_K_TILED_MC");
const bool q6k_tiled_mc = q6k_tiled_mc_env ? atoi(q6k_tiled_mc_env) != 0
: (q6k_mc3 || backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
const bool use_q6k_tiled_mc = q6k_tiled_mc && (ne1 >= 2 && ne1 <= 4) && (ne01 >= 32768) && use_q6k_tiled(backend_ctx, src0);
// Batched verify lm_head/embed (ne1==3, tiled layout): multi-column tiled
// GEMV — streams the large lm_head weight once across the 3 verify columns
// (the #1 MTP bottleneck; mc3 above can't, it reads the noshuffle layout).
const bool use_q6k_tiled_mc = q6k_mc3 && (ne1 == 3) && (ne01 >= 32768) && use_q6k_tiled(backend_ctx, src0);
const bool use_bin = use_q6_k_bin_kernels(backend_ctx, src0);
@@ -27784,7 +27704,7 @@ static void ggml_cl_mul_mat_q6_K_f32_adreno(ggml_backend_t backend, const ggml_t
if (use_q6k_mc3 || use_q6k_tiled_mc) {
static bool warned = false;
if (!warned) {
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q6_K_MC3 is bypassed by Q6_K binary kernels\n");
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q6K_MC3 is bypassed by Q6_K binary kernels\n");
warned = true;
}
}
@@ -27872,9 +27792,6 @@ static void ggml_cl_mul_mat_q6_K_f32_adreno(ggml_backend_t backend, const ggml_t
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offsetd));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_int), &ne00));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_int), &ne01));
if (use_q6k_tiled_mc) {
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_int), &ne1));
}
const size_t gws_x = use_tiled
? (size_t) CEIL_DIV(ne01, 64) * 64
@@ -28442,9 +28359,8 @@ static void ggml_cl_mul_mat_q5_K_f32_adreno(ggml_backend_t backend, const ggml_t
// (ne1==3) onto the efficient GEMV path instead of the transposed-GEMM dead-
// zone (gemm_noshuffle_q5_k, the #2 chunk of MTP decode on a Q4_0-mix model
// after q4_0 mc3). Reuses the ne1==1 GEMV image setup (q + qh + activations).
// Default on for X2E, GGML_OPENCL_Q5_K_MC3=0/1 forces it. Per-layer only (ne01 < 32768).
static const char * q5k_mc3_env = getenv("GGML_OPENCL_Q5_K_MC3");
const bool q5k_mc3 = q5k_mc3_env ? atoi(q5k_mc3_env) != 0 : backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E;
// Opt-in via GGML_OPENCL_Q5K_MC3=1. Per-layer only (ne01 < 32768).
static const bool q5k_mc3 = (getenv("GGML_OPENCL_Q5K_MC3") != nullptr);
const bool use_q5k_mc3 = q5k_mc3 && (ne1 >= 2 && ne1 <= 4) && (ne01 < 32768);
const bool use_bin = use_q5_k_bin_kernels(backend_ctx, src0);
@@ -28453,7 +28369,7 @@ static void ggml_cl_mul_mat_q5_K_f32_adreno(ggml_backend_t backend, const ggml_t
if (use_q5k_mc3) {
static bool warned = false;
if (!warned) {
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q5_K_MC3 is bypassed by Q5_K binary kernels\n");
GGML_LOG_WARN("ggml_opencl: GGML_OPENCL_Q5K_MC3 is bypassed by Q5_K binary kernels\n");
warned = true;
}
}
@@ -31010,9 +30926,8 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
// skinny f32 weight (GDN ssm_alpha/ssm_beta, M=32) it launches one
// under-occupied WG at ~2.3% tile utilization. Route to a per-output
// (m,n) GEMV (64-thread WG, K-split + __local reduce) instead.
// 2D contiguous, small N + skinny M only. Default on for X2E, GGML_OPENCL_F32_MC=0/1 forces it.
static const char * f32_mc_env = getenv("GGML_OPENCL_F32_MC");
const bool f32_mc = f32_mc_env ? atoi(f32_mc_env) != 0 : backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E;
// Opt-in GGML_OPENCL_F32_MC=1; 2D contiguous, small N + skinny M only.
static const bool f32_mc = (getenv("GGML_OPENCL_F32_MC") != nullptr);
if (f32_mc && ne11 >= 2 && ne11 <= 8 && ne01 <= 512 && (ne00 % 4 == 0) &&
ne02 == 1 && ne12 == 1 && ne13 == 1 &&
ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
@@ -37911,47 +37826,6 @@ static void ggml_cl_argsort(ggml_backend_t backend, const ggml_tensor * src0, co
#endif // GGML_OPENCL_USE_ADRENO_KERNELS
}
static void ggml_cl_dsv4_hc(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
auto * backend_ctx = (ggml_backend_opencl_context *) backend->context;
const bool pre = dst->op == GGML_OP_DSV4_HC_PRE;
const bool vec = !pre && src0->ne[0] % 4 == 0 && src0->nb[0] == sizeof(float) && src1->nb[0] == sizeof(float);
cl_kernel kernel = pre ? backend_ctx->kernel_dsv4_hc_pre : backend_ctx->kernel_dsv4_hc_post[vec];
cl_uint arg = 0;
for (int i = 0; i < (pre ? 2 : 4); ++i) {
const ggml_tensor * src = dst->src[i];
auto * extra = src ? (ggml_tensor_extra_cl *) src->extra : nullptr;
const cl_mem buffer = extra ? extra->data_device : nullptr;
const cl_ulong offset = src ? extra->offset + src->view_offs : 0;
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_mem), &buffer));
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_ulong), &offset));
for (int j = 0; j < 3; ++j) {
const cl_ulong stride = src ? src->nb[j] : 0;
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_ulong), &stride));
}
}
auto * extra = (ggml_tensor_extra_cl *) dst->extra;
const cl_ulong offset = extra->offset + dst->view_offs;
const cl_long n_embd = src0->ne[0];
const cl_long n_hc = pre ? src0->ne[1] : src1->ne[1];
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_mem), &extra->data_device));
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_ulong), &offset));
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_long), &n_embd));
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_long), &n_hc));
if (pre) {
const float scale = ggml_get_op_params_f32(dst, 0);
const cl_int gated = ggml_get_op_params_i32(dst, 1);
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(float), &scale));
CL_CHECK(clSetKernelArg(kernel, arg++, sizeof(cl_int), &gated));
}
const size_t n_work = (size_t) n_embd / (vec ? 4 : 1);
size_t local_work_size[] = { pre ? backend_ctx->local_dsv4_hc_pre : backend_ctx->local_dsv4_hc_post[vec], 1, 1 };
size_t global_work_size[] = { CEIL_DIV(n_work, local_work_size[0]) * local_work_size[0], (size_t) dst->ne[1], (size_t) dst->ne[2] };
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
}
static void ggml_cl_sum_rows(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
GGML_ASSERT(src0);
GGML_ASSERT(src0->extra);
@@ -38839,10 +38713,6 @@ bool ggml_cl_compute_forward(ggml_backend_t backend, struct ggml_tensor * tensor
}
func = ggml_cl_sum_rows;
break;
case GGML_OP_DSV4_HC_PRE:
case GGML_OP_DSV4_HC_POST:
func = ggml_cl_dsv4_hc;
break;
case GGML_OP_CUMSUM:
if (!any_on_device) {
return false;
-101
View File
@@ -1,101 +0,0 @@
#if HC_VEC == 4
typedef float4 hc_float;
#define HC_LOAD(p) vload4(0, (global const float *) (p))
#define HC_STORE(v, p) vstore4(v, 0, (global float *) (p))
#else
typedef float hc_float;
#define HC_LOAD(p) (*(global const float *) (p))
#define HC_STORE(v, p) (*(global float *) (p) = (v))
#endif
#if HC_VEC == 1
kernel void kernel_dsv4_hc_pre(
global const char * x,
ulong offset_x,
ulong nbx0,
ulong nbx1,
ulong nbx2,
global const char * weights,
ulong offset_w,
ulong nbw0,
ulong nbw1,
ulong nbw2,
global char * dst,
ulong offset_dst,
long n_embd,
long n_hc,
float scale,
int gated
) {
const size_t i = get_global_id(0);
const size_t t = get_global_id(1);
if (i >= n_embd) {
return;
}
x += offset_x + i*nbx0 + t*nbx2;
weights += offset_w + (gated ? i*nbw0 + t*nbw2 : t*nbw1);
float sum = 0.0f;
for (long h = 0; h < n_hc; ++h) {
const float xv = *(global const float *) (x + h*nbx1);
float wv = *(global const float *) (weights + h*(gated ? nbw1 : nbw0));
if (gated) {
wv = 1.0f / (1.0f + exp(-wv));
}
sum += xv * wv;
}
((global float *) (dst + offset_dst))[i + t*n_embd] = scale * sum;
}
#endif
kernel void kernel_dsv4_hc_post(
global const char * x,
ulong offset_x,
ulong nbx0,
ulong nbx1,
ulong nbx2,
global const char * residual,
ulong offset_r,
ulong nbr0,
ulong nbr1,
ulong nbr2,
global const char * post,
ulong offset_p,
ulong nbp0,
ulong nbp1,
ulong nbp2,
global const char * comb,
ulong offset_c,
ulong nbc0,
ulong nbc1,
ulong nbc2,
global char * dst,
ulong offset_dst,
long n_embd,
long n_hc
) {
const size_t i = get_global_id(0) * HC_VEC;
const size_t h = get_global_id(1);
const size_t t = get_global_id(2);
if (i >= n_embd) {
return;
}
const hc_float xv = HC_LOAD(x + offset_x + i*nbx0 + t*nbx1);
const float pv = *(global const float *) (post + offset_p + h*nbp0 + t*nbp1);
residual += offset_r + i*nbr0 + t*nbr2;
hc_float sum = xv * pv;
if (comb) {
comb += offset_c + h*nbc0 + t*nbc2;
for (long s = 0; s < n_hc; ++s) {
const hc_float rv = HC_LOAD(residual + s*nbr1);
const float cv = *(global const float *) (comb + s*nbc1);
sum += rv * cv;
}
} else {
sum += HC_LOAD(residual + h*nbr1);
}
HC_STORE(sum, dst + offset_dst + (i + (h + t*n_hc)*n_embd)*sizeof(float));
}
@@ -104,11 +104,11 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled(
}
}
// Multi-column (ne1 in [2..4]) variant of the tiled q6_K decode GEMV, for the
// speculative/MTP VERIFY lm_head/embed (drafts + 1 bonus). Identical tiled weight
// layout + unpack as the ne1=1 kernel above; each WI computes up to 4 output columns,
// Multi-column (N=3) variant of the tiled q6_K decode GEMV, for the speculative/
// MTP VERIFY lm_head/embed (ne1=3 = 2 drafts + 1 bonus). Identical tiled weight
// layout + unpack as the ne1=1 kernel above; each WI computes 3 output columns,
// streaming the (large) lm_head weight ONCE per superblock and reusing it across
// the verify activation columns (dequant once per code, MAC into 4 accs). This
// the 3 verify activation columns (dequant once per code, MAC into 3 accs). This
// is the lm_head analogue of the per-layer mc3 GEMV; the multiply order matches
// the ne1=1 kernel, so each column is byte-identical to a standalone tiled GEMV.
#if defined(ADRENO_GPU)
@@ -123,8 +123,7 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled_mc3(
global float * dst,
ulong offsetd,
int ne00,
int ne01,
int ne1
int ne01
) {
int grp = get_local_id(1);
int row = get_global_id(0);
@@ -134,12 +133,7 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled_mc3(
int nb = ne00 / 256;
int col_stride = ne00 / 4; // activation float4 pixels per column
// columns past ne1 re-read the last column and are not stored
int col1 = min(1, ne1 - 1) * col_stride;
int col2 = min(2, ne1 - 1) * col_stride;
int col3 = min(3, ne1 - 1) * col_stride;
float acc0 = 0.0f, acc1 = 0.0f, acc2 = 0.0f, acc3 = 0.0f;
float acc0 = 0.0f, acc1 = 0.0f, acc2 = 0.0f;
for (int sb = grp; sb < nb; sb += NSUBGROUPS) {
int tile_blk = rt * nb + sb;
@@ -163,10 +157,9 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled_mc3(
int act_base = sb * 64;
#pragma unroll
for (int e4 = 0; e4 < 64; ++e4) {
float4 a0 = read_imagef(src1, act_base + e4);
float4 a1 = read_imagef(src1, col1 + act_base + e4);
float4 a2 = read_imagef(src1, col2 + act_base + e4);
float4 a3 = read_imagef(src1, col3 + act_base + e4);
float4 a0 = read_imagef(src1, 0*col_stride + act_base + e4);
float4 a1 = read_imagef(src1, 1*col_stride + act_base + e4);
float4 a2 = read_imagef(src1, 2*col_stride + act_base + e4);
#pragma unroll
for (int t = 0; t < 4; ++t) {
int e = e4 * 4 + t;
@@ -178,17 +171,15 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled_mc3(
float av0 = (t == 0) ? a0.x : (t == 1) ? a0.y : (t == 2) ? a0.z : a0.w;
float av1 = (t == 0) ? a1.x : (t == 1) ? a1.y : (t == 2) ? a1.z : a1.w;
float av2 = (t == 0) ? a2.x : (t == 1) ? a2.y : (t == 2) ? a2.z : a2.w;
float av3 = (t == 0) ? a3.x : (t == 1) ? a3.y : (t == 2) ? a3.z : a3.w;
acc0 += w * av0;
acc1 += w * av1;
acc2 += w * av2;
acc3 += w * av3;
}
}
}
local float4 reduce_lm[NSUBGROUPS * TILE_ROWS];
reduce_lm[grp * TILE_ROWS + rit] = (float4)(acc0, acc1, acc2, acc3);
reduce_lm[grp * TILE_ROWS + rit] = (float4)(acc0, acc1, acc2, 0.0f);
barrier(CLK_LOCAL_MEM_FENCE);
if (grp == 0) {
@@ -197,10 +188,9 @@ kernel void kernel_gemv_noshuffle_q6_K_f32_tiled_mc3(
+ reduce_lm[2 * TILE_ROWS + rit]
+ reduce_lm[3 * TILE_ROWS + rit];
dst = (global float*)((global char*)dst + offsetd);
// dst column-major [ne01 rows x ne1 cols]: (row, col) at col*ne01 + row
// dst column-major [ne01 rows x 3 cols]: (row, col) at col*ne01 + row
dst[0*ne01 + row] = total.x;
dst[1*ne01 + row] = total.y;
if (ne1 > 2) dst[2*ne01 + row] = total.z;
if (ne1 > 3) dst[3*ne01 + row] = total.w;
dst[2*ne01 + row] = total.z;
}
}
+37 -42
View File
@@ -1,43 +1,38 @@
#define KERNEL_REPEAT(SUFFIX, T) \
kernel void kernel_repeat_##SUFFIX( \
global const char * src0, \
ulong offset0, \
global char * dst, \
ulong offsetd, \
int ne00, \
int ne01, \
int ne02, \
int ne03, \
ulong nb00, \
ulong nb01, \
ulong nb02, \
ulong nb03, \
int ne0, \
ulong nb0, \
ulong nb1, \
ulong nb2, \
ulong nb3 \
) { \
src0 = src0 + offset0; \
dst = dst + offsetd; \
\
const int i3 = get_group_id(2); \
const int i2 = get_group_id(1); \
const int i1 = get_group_id(0); \
\
const int i03 = i3%ne03; \
const int i02 = i2%ne02; \
const int i01 = i1%ne01; \
\
global const char * src0_ptr = src0 + i03*nb03 + i02*nb02 + i01*nb01; \
global char * dst_ptr = dst + i3*nb3 + i2*nb2 + i1*nb1; \
\
for (int i0 = get_local_id(0); i0 < ne0; i0 += get_local_size(0)) { \
const int i00 = i0%ne00; \
*((global T *)(dst_ptr + i0*nb0)) = *((global T *)(src0_ptr + i00*nb00)); \
} \
}
kernel void kernel_repeat_f32(
global const char * src0,
ulong offset0,
global char * dst,
ulong offsetd,
int ne00,
int ne01,
int ne02,
int ne03,
ulong nb00,
ulong nb01,
ulong nb02,
ulong nb03,
int ne0,
ulong nb0,
ulong nb1,
ulong nb2,
ulong nb3
) {
src0 = src0 + offset0;
dst = dst + offsetd;
KERNEL_REPEAT(f32, float)
KERNEL_REPEAT(f16, ushort)
KERNEL_REPEAT(i32, int)
const int i3 = get_group_id(2);
const int i2 = get_group_id(1);
const int i1 = get_group_id(0);
const int i03 = i3%ne03;
const int i02 = i2%ne02;
const int i01 = i1%ne01;
global const char * src0_ptr = src0 + i03*nb03 + i02*nb02 + i01*nb01;
global char * dst_ptr = dst + i3*nb3 + i2*nb2 + i1*nb1;
for (int i0 = get_local_id(0); i0 < ne0; i0 += get_local_size(0)) {
const int i00 = i0%ne00;
*((global float *)(dst_ptr + i0*nb0)) = *((global float *)(src0_ptr + i00*nb00));
}
}
-60
View File
@@ -27,63 +27,3 @@ kernel void kernel_sigmoid_f16(
dst[get_global_id(0)] = 1.0f / (1.0f + exp(-src0[get_global_id(0)]));
}
kernel void kernel_sigmoid_f32_nc(
global const char * src0,
ulong offset0,
global char * dst,
ulong offsetd,
int ne00,
ulong nb00,
ulong nb01,
ulong nb02,
ulong nb03,
ulong nb0,
ulong nb1,
ulong nb2,
ulong nb3
) {
src0 = src0 + offset0;
dst = dst + offsetd;
const int i3 = get_group_id(2);
const int i2 = get_group_id(1);
const int i1 = get_group_id(0);
for (int i0 = get_local_id(0); i0 < ne00; i0 += get_local_size(0)) {
global const float * x = (global const float *)(src0 + i3*nb03 + i2*nb02 + i1*nb01 + i0*nb00);
global float * y = (global float *)(dst + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);
*y = 1.0f / (1.0f + exp(-*x));
}
}
kernel void kernel_sigmoid_f16_nc(
global const char * src0,
ulong offset0,
global char * dst,
ulong offsetd,
int ne00,
ulong nb00,
ulong nb01,
ulong nb02,
ulong nb03,
ulong nb0,
ulong nb1,
ulong nb2,
ulong nb3
) {
src0 = src0 + offset0;
dst = dst + offsetd;
const int i3 = get_group_id(2);
const int i2 = get_group_id(1);
const int i1 = get_group_id(0);
for (int i0 = get_local_id(0); i0 < ne00; i0 += get_local_size(0)) {
global const half * x = (global const half *)(src0 + i3*nb03 + i2*nb02 + i1*nb01 + i0*nb00);
global half * y = (global half *)(dst + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);
*y = 1.0f / (1.0f + exp(-(float)*x));
}
}
-3
View File
@@ -1636,9 +1636,6 @@ static ggml_openvino_op_support is_op_supported_case(const ggml_tensor * op) {
break;
}
case GGML_OP_ROPE: {
if (!ggml_rope_get_truncate(op)) {
return {false, "ROPE without YaRN truncation is not supported"};
}
if (op->view_src != nullptr && !ggml_is_contiguous(op->src[0])) {
return {false, "ROPE on VIEW / non-contiguous input is not supported"};
}
+1 -4
View File
@@ -3055,10 +3055,7 @@ static ggml_backend_buffer_type_t ggml_backend_rpc_device_get_buffer_type(ggml_b
static bool ggml_backend_rpc_device_supports_op(ggml_backend_dev_t dev, const struct ggml_tensor * op) {
GGML_UNUSED(dev);
if ((op->op == GGML_OP_ROPE || op->op == GGML_OP_ROPE_BACK) &&
!ggml_rope_get_truncate(op)) {
return false;
}
GGML_UNUSED(op);
//TODO: call the remote backend and cache the results
return true;
}
-1
View File
@@ -7569,7 +7569,6 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
}
case GGML_OP_ROPE:
case GGML_OP_ROPE_BACK:
return ggml_rope_get_truncate(op);
case GGML_OP_IM2COL:
case GGML_OP_IM2COL_3D:
case GGML_OP_UPSCALE:
+1 -1
View File
@@ -547,7 +547,7 @@ void ggml_sycl_op_rope_impl(ggml_backend_sycl_context &ctx, ggml_tensor *dst,
rope_corr_dims corr_dims;
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast,
beta_slow, true, corr_dims.v);
beta_slow, corr_dims.v);
// compute
if (is_neox) {
@@ -30,14 +30,11 @@ static void ggml_backend_remoting_device_get_memory(ggml_backend_dev_t dev, size
}
static bool ggml_backend_remoting_device_supports_op(ggml_backend_dev_t dev, const ggml_tensor * op) {
if ((op->op == GGML_OP_ROPE || op->op == GGML_OP_ROPE_BACK) &&
!ggml_rope_get_truncate(op)) {
return false;
}
#if USE_ALWAYS_TRUE_SUPPORTS_OP == 1
/* ggml-rpc cheats it like this */
/* with the current implementation of serialize_tensor, the src/view aren't properly passed */
UNUSED(dev);
UNUSED(op);
return true;
#else
+1 -1
View File
@@ -229,7 +229,7 @@ vk_buffer ggml_vk_create_buffer_device(vk_device& device, size_t size) {
buf = ggml_vk_create_buffer(device, size, {vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
vk::MemoryPropertyFlagBits::eDeviceLocal,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent});
} else if (!device->rebar || device->disable_host_visible_vidmem) {
} else if (device->disable_host_visible_vidmem) {
if (device->allow_sysmem_fallback) {
buf = ggml_vk_create_buffer(device, size, {vk::MemoryPropertyFlagBits::eDeviceLocal,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent});
@@ -245,12 +245,6 @@ bool ggml_backend_vk_comm_allreduce_tensor(void * comm_ctx, ggml_tensor ** tenso
#endif
// debug
void ggml_vk_count_fusion(const vk_device & device, const char * label);
void * ggml_backend_vk_fusion_get(ggml_backend_dev_t dev);
void ggml_backend_vk_fusion_stats_init(void * finfo);
void ggml_backend_vk_fusion_stats_reset(void * finfo);
int ggml_backend_vk_fusion_stats_get(void * finfo, const char ** labels, uint64_t * counts, int n);
void ggml_backend_vk_fusion_set_enabled(void * finfo, bool enabled);
int64_t ggml_vk_get_op_batch_size(const ggml_tensor * op);
#ifdef GGML_VULKAN_RUN_TESTS
template <typename X_TYPE, typename Y_TYPE>
@@ -12,58 +12,6 @@ uint32_t vk_perf_logger_frequency = 1;
std::string vk_pipeline_stats_filter;
void ggml_vk_count_fusion(const vk_device & device, const char * label) {
if (!device->fusion_stats_enabled.load(std::memory_order_relaxed)) {
return;
}
std::lock_guard<std::mutex> lock(device->fusion_mutex);
device->fusion_counts[label]++;
}
void * ggml_backend_vk_fusion_get(ggml_backend_dev_t dev) {
auto * ctx = (ggml_backend_vk_device_context *) dev->context;
return ggml_vk_get_device(ctx->device).get();
}
void ggml_backend_vk_fusion_stats_init(void * finfo) {
auto * device = (vk_device_struct *) finfo;
device->fusion_stats_enabled.store(true, std::memory_order_relaxed);
}
void ggml_backend_vk_fusion_stats_reset(void * finfo) {
auto * device = (vk_device_struct *) finfo;
std::lock_guard<std::mutex> lock(device->fusion_mutex);
// Keep labels and their order stable between fused and unfused runs.
for (auto & entry : device->fusion_counts) {
entry.second = 0;
}
}
int ggml_backend_vk_fusion_stats_get(void * finfo, const char ** labels, uint64_t * counts, int n) {
auto * device = (vk_device_struct *) finfo;
std::lock_guard<std::mutex> lock(device->fusion_mutex);
if (labels == nullptr) {
return (int) device->fusion_counts.size();
}
int i = 0;
for (const auto & entry : device->fusion_counts) {
if (i >= n) {
break;
}
labels[i] = entry.first.c_str();
if (counts) {
counts[i] = entry.second;
}
i++;
}
return i;
}
void ggml_backend_vk_fusion_set_enabled(void * finfo, bool enabled) {
auto * device = (vk_device_struct *) finfo;
device->disable_fusion = !enabled;
}
void vk_memory_logger::log_allocation(vk_buffer_ref buf_ref, size_t size) {
if (!vk_memory_logger_enabled) {
return;
+1 -16
View File
@@ -486,15 +486,6 @@ static constexpr uint32_t num_topk_moe_pipelines = 10;
static constexpr uint32_t num_topk_pipelines = 11;
// FWHT block widths with a dedicated pipeline. The subgroup shader keeps n/subgroup_size
// values per invocation, so widths above fwht_max_subgroup_n, or a subgroup narrow enough to
// exceed fwht_max_subgroup_el_w values, use the shared-memory shader with one row per
// workgroup instead.
static constexpr uint32_t num_fwht_pipelines = 8;
static constexpr uint32_t fwht_max_subgroup_n = 512;
static constexpr uint32_t fwht_max_subgroup_el_w = 64;
static constexpr uint32_t fwht_rows = 4;
static constexpr std::initializer_list<ggml_op> topk_moe_early_softmax_norm{ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT,
GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_RESHAPE,
GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV,
@@ -707,7 +698,6 @@ struct vk_device_struct {
uint32_t subgroup_size_log2;
uint32_t shader_core_count;
bool uma;
bool rebar;
bool prefer_host_memory;
bool float_controls_rte_fp16;
bool float_controls_denorm_preserve_fp16;
@@ -945,9 +935,7 @@ struct vk_device_struct {
vk_pipeline pipeline_sum_rows_f32;
vk_pipeline pipeline_cross_entropy_loss_f32, pipeline_cross_entropy_loss_f32_wg512;
vk_pipeline pipeline_cross_entropy_loss_back_f32, pipeline_cross_entropy_loss_back_f32_wg512;
vk_pipeline pipeline_fwht_f32[num_fwht_pipelines];
vk_pipeline pipeline_fwht_f16[num_fwht_pipelines];
uint32_t fwht_rows_per_wg[num_fwht_pipelines] = {};
vk_pipeline pipeline_fwht_f32[4];
vk_pipeline pipeline_cumsum_f32;
vk_pipeline pipeline_cumsum_small_f32;
vk_pipeline pipeline_cumsum_multipass1_f32;
@@ -1019,9 +1007,6 @@ struct vk_device_struct {
ggml_backend_buffer_type buffer_type;
bool disable_fusion;
std::atomic<bool> fusion_stats_enabled {false};
std::mutex fusion_mutex;
std::map<std::string, uint64_t> fusion_counts;
bool disable_descriptor_reuse;
std::atomic<uint64_t> buffer_destroy_count {};
bool disable_host_visible_vidmem;
+31 -119
View File
@@ -1678,17 +1678,6 @@ static bool ggml_vk_fa_scalar_uses_mmq(const vk_device& device, ggml_type k_type
#endif
}
// AMD proprietary drivers before 2.0.416 (Adrenalin 26.10.1) run the large coopmat matmul tile much slower than the medium one.
static bool ggml_vk_amd_large_tile_supported(const vk_device& device) {
if (device->driver_id != vk::DriverId::eAmdProprietary) {
return true;
}
// AMD encodes driverVersion as major [31:22], build [21:0], not VK_MAKE_API_VERSION.
const uint32_t major = device->properties.driverVersion >> 22;
const uint32_t build = device->properties.driverVersion & 0x3fffff;
return major > 2 || (major == 2 && build >= 416);
}
void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
VK_LOG_DEBUG("ggml_vk_load_shaders(" << device->name << ")");
@@ -1861,7 +1850,7 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
if ((device->architecture == AMD_GCN) && (device->driver_id != vk::DriverId::eAmdProprietary)) {
m_warptile_mmq = m_warptile_mmq_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 };
m_warptile_mmqid = m_warptile_mmqid_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 };
} else if (device->vendor_id == VK_VENDOR_ID_AMD && device->coopmat_support && ggml_vk_amd_large_tile_supported(device)) {
} else if (device->vendor_id == VK_VENDOR_ID_AMD && device->coopmat_support && device->driver_id != vk::DriverId::eAmdProprietary) {
// This is intentionally using tx_m values, slight performance increase
l_warptile = { 256, 128, 128, 16, mm_warp_8, 64, 2, tm_m, tn_m, tk_m, mm_warp_8 };
l_warptile_mmq = l_warptile_mmq_int = { 256, 128, 128, 32, mm_warp_8, 64, 2, tm_m, tn_m, tk_m, mm_warp_8 };
@@ -3592,31 +3581,21 @@ void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
// Intel Windows driver in range [32.0.101.8509, 32.0.101.8860) will crash when using fwht kernels so we gate that here
const bool can_use_fwht = device->driver_id != vk::DriverId::eIntelProprietaryWindows ||
!ggml_vk_intel_windows_driver_in_range(device->properties.driverVersion, 101, 8509, 101, 8860);
if (can_use_fwht) {
const bool use_subgroup = device->subgroup_basic && device->subgroup_shuffle;
const uint32_t sg = std::max(device->subgroup_size, 1u);
if (can_use_fwht && device->subgroup_basic && device->subgroup_shuffle) {
int idx = 0;
for (uint32_t n : {64, 128, 256, 512, 1024, 2048, 4096, 8192}) {
const bool wide = n > fwht_max_subgroup_n || n / sg > fwht_max_subgroup_el_w;
if (use_subgroup && !wide) {
if (device->subgroup_size <= n) {
ggml_vk_create_pipeline(device, device->pipeline_fwht_f32[idx], "fwht_f32", fwht_f32_len, fwht_f32_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { device->subgroup_size, n, fwht_rows }, 1, true, true, device->subgroup_size);
ggml_vk_create_pipeline(device, device->pipeline_fwht_f16[idx], "fwht_f16", fwht_f16_len, fwht_f16_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { device->subgroup_size, n, fwht_rows }, 1, true, true, device->subgroup_size);
device->fwht_rows_per_wg[idx] = fwht_rows;
}
} else {
// wide blocks trade rows per workgroup for a smaller register block
const uint32_t block_size = wide ? std::min(n, 256u) : std::min(sg, n);
const uint32_t rows = wide ? 1u : fwht_rows;
if ((uint64_t)rows * n * sizeof(float) <= device->properties.limits.maxComputeSharedMemorySize &&
block_size * rows <= device->properties.limits.maxComputeWorkGroupInvocations) {
ggml_vk_create_pipeline(device, device->pipeline_fwht_f32[idx], "fwht_shmem_f32", fwht_shmem_f32_len, fwht_shmem_f32_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { block_size, n, rows }, 1);
ggml_vk_create_pipeline(device, device->pipeline_fwht_f16[idx], "fwht_shmem_f16", fwht_shmem_f16_len, fwht_shmem_f16_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { block_size, n, rows }, 1);
device->fwht_rows_per_wg[idx] = rows;
}
for (uint32_t n : {64, 128, 256, 512}) {
if (device->subgroup_size <= n) {
ggml_vk_create_pipeline(device, device->pipeline_fwht_f32[idx], "fwht_f32", fwht_f32_len, fwht_f32_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { device->subgroup_size, n }, 1, true, true, device->subgroup_size);
}
++idx;
}
} else if (can_use_fwht) {
int idx = 0;
for (uint32_t n : {64, 128, 256, 512}) {
const uint32_t block_size = std::min(device->subgroup_size, n);
ggml_vk_create_pipeline(device, device->pipeline_fwht_f32[idx], "fwht_shmem_f32", fwht_shmem_f32_len, fwht_shmem_f32_data, "main", 2, sizeof(vk_op_fwht_push_constants), {1, 1, 1}, { block_size, n }, 1);
++idx;
}
}
const uint32_t cumsum_elem_per_thread = (device->vendor_id == VK_VENDOR_ID_AMD || device->vendor_id == VK_VENDOR_ID_INTEL) ? 2 : 4;
@@ -4041,24 +4020,6 @@ static bool ggml_vk_host_buffer_import_usable(uint32_t vendor_id) {
return vendor_id != VK_VENDOR_ID_AMD;
}
static bool ggml_vk_has_rebar(const vk::PhysicalDevice & physical_device) {
const vk::PhysicalDeviceMemoryProperties mem_props = physical_device.getMemoryProperties();
const vk::MemoryPropertyFlags hvv = vk::MemoryPropertyFlagBits::eDeviceLocal |
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent;
uint32_t vram_heap = UINT32_MAX;
for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) {
const vk::MemoryType & t = mem_props.memoryTypes[i];
if (vram_heap == UINT32_MAX && (t.propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal)) {
vram_heap = t.heapIndex;
}
if ((t.propertyFlags & hvv) == hvv && t.heapIndex == vram_heap) {
return true;
}
}
return false;
}
vk_device ggml_vk_get_device(size_t idx) {
VK_LOG_DEBUG("ggml_vk_get_device(" << idx << ")");
@@ -4293,7 +4254,6 @@ vk_device ggml_vk_get_device(size_t idx) {
device->subgroup_size = subgroup_props.subgroupSize;
device->subgroup_size_log2 = uint32_t(log2f(float(device->subgroup_size)));
device->uma = device->properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu;
device->rebar = ggml_vk_has_rebar(device->physical_device);
if (sm_builtins) {
device->shader_core_count = sm_props.shaderSMCount;
} else if (amd_shader_core_properties2) {
@@ -4856,7 +4816,7 @@ vk_device ggml_vk_get_device(size_t idx) {
switch (device->vendor_id) {
#ifndef GGML_VULKAN_RUN_TESTS
case VK_VENDOR_ID_AMD:
device->mul_mat_l[i] = device->coopmat_support && ggml_vk_amd_large_tile_supported(device);
device->mul_mat_l[i] = device->coopmat_support && device->driver_id != vk::DriverId::eAmdProprietary;
device->mul_mat_m[i] = true;
device->mul_mat_s[i] = true;
device->mul_mat_id_l[i] = false;
@@ -4983,7 +4943,8 @@ vk_device ggml_vk_get_device(size_t idx) {
device->disable_descriptor_reuse = getenv("GGML_VK_DISABLE_DESCRIPTOR_REUSE") != nullptr;
device->add_rms_fusion = device->subgroup_arithmetic &&
device->add_rms_fusion = !device->disable_fusion &&
device->subgroup_arithmetic &&
device->vendor_id != VK_VENDOR_ID_INTEL;
device->partials_binding_alignment =
std::max(4u, (uint32_t)device->properties.limits.minStorageBufferOffsetAlignment);
@@ -5195,7 +5156,6 @@ static void ggml_vk_print_gpu_info(size_t idx) {
uint32_t default_subgroup_size = get_subgroup_size("", device_architecture);
const size_t subgroup_size = (default_subgroup_size != 0) ? default_subgroup_size : subgroup_props.subgroupSize;
const bool uma = props2.properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu;
const bool rebar = ggml_vk_has_rebar(physical_device);
integer_dot_product = integer_dot_product
&& shader_integer_dot_product_props.integerDotProduct4x8BitPackedSignedAccelerated
@@ -5242,8 +5202,8 @@ static void ggml_vk_print_gpu_info(size_t idx) {
#endif
std::string device_name = props2.properties.deviceName.data();
GGML_LOG_DEBUG("ggml_vulkan: %zu = %s (%s) | uma: %d | rebar: %d | fp16: %s | bf16: %d | fp4: %d | warp size: %zu | shared memory: %d | int dot: %d | matrix cores: %s\n",
idx, device_name.c_str(), driver_props.driverName.data(), uma, rebar, fp16_str, bf16, fp4, subgroup_size,
GGML_LOG_DEBUG("ggml_vulkan: %zu = %s (%s) | uma: %d | fp16: %s | bf16: %d | fp4: %d | warp size: %zu | shared memory: %d | int dot: %d | matrix cores: %s\n",
idx, device_name.c_str(), driver_props.driverName.data(), uma, fp16_str, bf16, fp4, subgroup_size,
props2.properties.limits.maxComputeSharedMemorySize, integer_dot_product, matrix_cores.c_str());
if (props2.properties.deviceType == vk::PhysicalDeviceType::eCpu) {
@@ -5995,11 +5955,6 @@ static uint32_t ggml_vk_guess_split_k(ggml_backend_vk_context * ctx, uint32_t m,
split_k = ctx->device->shader_core_count / (m_tiles * n_tiles);
} else if (m_tiles * n_tiles <= ctx->device->shader_core_count * 2 / 3) {
split_k = 3;
} else if ((ctx->device->architecture == AMD_RDNA3 || ctx->device->architecture == AMD_RDNA4) &&
m_tiles * n_tiles > ctx->device->shader_core_count &&
m_tiles * n_tiles <= ctx->device->shader_core_count * 4 / 3) {
// two waves, the second one at most 1/3 full
split_k = 3;
}
// Cap the split at 8x. Unless k is huge this is a lot of overhead.
split_k = std::min(split_k, 8u);
@@ -7245,10 +7200,6 @@ static int ggml_vk_fwht_pipeline_idx(int64_t n) {
case 128: return 1;
case 256: return 2;
case 512: return 3;
case 1024: return 4;
case 2048: return 5;
case 4096: return 6;
case 8192: return 7;
default: return -1;
}
}
@@ -7262,18 +7213,12 @@ bool ggml_vk_can_use_fwht(const ggml_backend_vk_context * ctx, const ggml_tensor
return false;
}
if (dst->type != GGML_TYPE_F32) {
return false;
}
if (src1->type != GGML_TYPE_F32 && src1->type != GGML_TYPE_F16) {
return false;
}
const bool src_f16 = src1->type == GGML_TYPE_F16;
const int idx = ggml_vk_fwht_pipeline_idx(src1->ne[0]);
if (idx < 0 || (src_f16 ? ctx->device->pipeline_fwht_f16[idx] : ctx->device->pipeline_fwht_f32[idx]) == nullptr) {
if (idx < 0 || ctx->device->pipeline_fwht_f32[idx] == nullptr) {
return false;
}
if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
return false;
}
@@ -7287,10 +7232,9 @@ bool ggml_vk_can_use_fwht(const ggml_backend_vk_context * ctx, const ggml_tensor
void ggml_vk_fwht(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src, ggml_tensor * dst) {
const int idx = ggml_vk_fwht_pipeline_idx(src->ne[0]);
vk_pipeline pipeline = src->type == GGML_TYPE_F16 ? ctx->device->pipeline_fwht_f16[idx] : ctx->device->pipeline_fwht_f32[idx];
vk_pipeline pipeline = ctx->device->pipeline_fwht_f32[idx];
const uint32_t rows_per_workgroup = ctx->device->fwht_rows_per_wg[idx];
GGML_ASSERT(rows_per_workgroup > 0);
const uint32_t rows_per_workgroup = 4;
const uint32_t n_rows = (uint32_t)ggml_nrows(src);
const uint32_t max_workgroups_x = ctx->device->properties.limits.maxComputeWorkGroupCount[0];
@@ -8277,14 +8221,10 @@ void ggml_vk_flash_attn(ggml_backend_vk_context * ctx, vk_context& subctx, const
v->nb[1] != (uint64_t)HSV * ggml_type_size(v->type)) &&
(HSK % 8) == 0 && (HSV % 8) == 0;
const size_t kv_scratch_ts = kv_raw_strided ? ggml_type_size(k->type) : sizeof(ggml_fp16_t);
// K and V share one scratch buffer, so the sum must fit in one allocation
const uint64_t dequant_kv_sz = ((uint64_t)ggml_nelements(k) + (uint64_t)ggml_nelements(v)) * kv_scratch_ts;
const bool use_dequant_kv = ((k_quant && v_quant) || kv_raw_strided) && neq1 >= 64 &&
is_dense_kv_cache(k) && is_dense_kv_cache(v) &&
(uint64_t)ggml_nelements(k) * kv_scratch_ts <= ctx->device->properties.limits.maxStorageBufferRange &&
(uint64_t)ggml_nelements(v) * kv_scratch_ts <= ctx->device->properties.limits.maxStorageBufferRange &&
dequant_kv_sz <= ctx->device->max_buffer_size &&
dequant_kv_sz <= ctx->device->max_memory_allocation_size &&
(kv_raw_strided ||
(ctx->device->pipeline_dequant_transpose[k->type] != nullptr &&
ctx->device->pipeline_dequant_transpose[v->type] != nullptr)) &&
@@ -10964,7 +10904,7 @@ static vk_op_rope_push_constants ggml_vk_make_rope_constants(const ggml_tensor *
const bool is_imrope = mode == GGML_ROPE_TYPE_IMROPE;
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, true, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
const float theta_scale = powf(freq_base, -2.0f/n_dims);
@@ -11462,7 +11402,7 @@ void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_
}
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, true, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
uint32_t set_rows_stride = 0;
// Fused rope + view + set_rows passes the set_rows destination stride in set_rows_stride
@@ -12467,12 +12407,11 @@ bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgraph, in
cgraph->nodes[next_node_idx]->op == GGML_OP_RMS_NORM &&
cgraph->nodes[next_node_idx]->src[0] == cgraph->nodes[next_node_idx - 1] &&
ggml_nrows(cgraph->nodes[next_node_idx]) == 1 &&
ctx->device->add_rms_fusion && !ctx->device->disable_fusion) {
ctx->device->add_rms_fusion) {
uint32_t size = ggml_vk_rms_partials_size(ctx, cgraph->nodes[node_idx]);
ctx->do_add_rms_partials_offset_calculation = true;
if (ctx->prealloc_size_add_rms_partials_offset + size <= ctx->prealloc_size_add_rms_partials) {
ctx->do_add_rms_partials = true;
ggml_vk_count_fusion(ctx->device, "ADD_RMS_NORM_PARTIALS");
}
}
}
@@ -14240,7 +14179,7 @@ bool ggml_vk_can_fuse_rope_set_rows(ggml_backend_vk_context * ctx, const struct
return false;
}
return ggml_rope_get_truncate(rope);
return true;
}
bool ggml_vk_can_fuse_rms_norm_set_rows(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph,
@@ -14378,7 +14317,7 @@ bool ggml_vk_can_fuse_rms_norm_mul_rope(ggml_backend_vk_context * ctx, const str
return false;
}
return ggml_rope_get_truncate(rope);
return true;
}
uint32_t ggml_vk_fuse_multi_add(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, int node_idx) {
@@ -14880,16 +14819,6 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg
}
if (enqueued) {
if (fusion_string && ctx->device->fusion_stats_enabled.load(std::memory_order_relaxed)) {
std::string label = fusion_string;
if (cgraph->nodes[i]->op == GGML_OP_ADD) {
label += "_" + std::to_string(ctx->num_additional_fused_ops + 1);
}
if (ctx->fused_topk_moe_scale) {
label += "_SCALE";
}
ggml_vk_count_fusion(ctx->device, label.c_str());
}
++submitted_nodes;
#ifndef GGML_VULKAN_CHECK_RESULTS
@@ -15937,12 +15866,8 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
case GGML_OP_REPEAT_BACK:
return op->type == GGML_TYPE_F32 && op->src[0]->type == GGML_TYPE_F32;
case GGML_OP_ROPE:
if (!ggml_rope_get_truncate(op)) {
return false;
}
return ggml_is_contiguous_rows(op) && ggml_is_contiguous_rows(op->src[0]);
case GGML_OP_ROPE_BACK:
return ggml_rope_get_truncate(op);
case GGML_OP_NONE:
case GGML_OP_RESHAPE:
case GGML_OP_VIEW:
@@ -16458,22 +16383,9 @@ static void * ggml_backend_vk_reg_get_proc_address(ggml_backend_reg_t reg, const
if (strcmp(name, "ggml_backend_comm_allreduce_tensor") == 0) {
return (void *) ggml_backend_vk_comm_allreduce_tensor;
}
#else
UNUSED(name);
#endif
if (strcmp(name, "ggml_backend_fusion_get") == 0) {
return (void *) ggml_backend_vk_fusion_get;
}
if (strcmp(name, "ggml_backend_fusion_stats_init") == 0) {
return (void *) ggml_backend_vk_fusion_stats_init;
}
if (strcmp(name, "ggml_backend_fusion_stats_reset") == 0) {
return (void *) ggml_backend_vk_fusion_stats_reset;
}
if (strcmp(name, "ggml_backend_fusion_stats_get") == 0) {
return (void *) ggml_backend_vk_fusion_stats_get;
}
if (strcmp(name, "ggml_backend_fusion_set_enabled") == 0) {
return (void *) ggml_backend_vk_fusion_set_enabled;
}
return nullptr;
}
@@ -5,13 +5,11 @@
#extension GL_KHR_shader_subgroup_basic : enable
#extension GL_KHR_shader_subgroup_shuffle : enable
#endif
#extension GL_EXT_shader_16bit_storage : require
layout(constant_id = 0) const uint BLOCK_SIZE = 32;
layout(constant_id = 1) const uint N = 128;
layout(constant_id = 2) const uint ROWS = 4;
layout(local_size_x_id = 0, local_size_y_id = 2, local_size_z = 1) in;
layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in;
layout(push_constant) uniform parameter
{
@@ -21,13 +19,13 @@ layout(push_constant) uniform parameter
float scale;
};
layout(binding = 0, std430) readonly buffer A { A_TYPE data_a[]; };
layout(binding = 0, std430) readonly buffer A { float data_a[]; };
layout(binding = 1, std430) writeonly buffer D { float data_d[]; };
const uint EL_W = N / BLOCK_SIZE;
#ifdef FWHT_SHMEM
shared float shmem[ROWS * N];
shared float shmem[4 * N];
#endif
void main() {
@@ -56,7 +54,7 @@ void main() {
[[unroll]]
for (uint i = 0; i < EL_W; ++i) {
reg[i] = row < n_rows ? float(data_a[src_offset + row_offset + i * BLOCK_SIZE + tid]) * scale : 0.0;
reg[i] = row < n_rows ? data_a[src_offset + row_offset + i * BLOCK_SIZE + tid] * scale : 0.0;
}
#ifdef FWHT_SHMEM
@@ -1110,10 +1110,8 @@ void process_shaders() {
string_to_spv("sum_rows_f32", "sum_rows.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
string_to_spv("cross_entropy_loss_f32", "cross_entropy_loss.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
string_to_spv("cross_entropy_loss_back_f32", "cross_entropy_loss_back.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
string_to_spv("fwht_f32", "fwht.comp", {{"A_TYPE", "float"}});
string_to_spv("fwht_shmem_f32", "fwht.comp", {{"A_TYPE", "float"}, {"FWHT_SHMEM", "1"}});
string_to_spv("fwht_f16", "fwht.comp", {{"A_TYPE", "float16_t"}});
string_to_spv("fwht_shmem_f16", "fwht.comp", {{"A_TYPE", "float16_t"}, {"FWHT_SHMEM", "1"}});
string_to_spv("fwht_f32", "fwht.comp", {});
string_to_spv("fwht_shmem_f32", "fwht.comp", {{"FWHT_SHMEM", "1"}});
string_to_spv("count_equal_i32", "count_equal.comp", merge_maps(base_dict, {{"A_TYPE", "int"}, {"B_TYPE", "int"}, {"D_TYPE", "int"}}));
string_to_spv("dsv4_hc_comb_f32", "dsv4_hc_comb.comp", {});
string_to_spv("dsv4_hc_pre_f32", "dsv4_hc_pre.comp", {});
@@ -1228,7 +1228,7 @@ class ggml_webgpu_shader_lib {
wgpu::Device device;
pre_wgsl::Preprocessor preprocessor;
std::unordered_map<int, webgpu_pipeline> sum_rows_pipelines; // key is whether to compute the mean
std::unordered_map<int, webgpu_pipeline> sum_rows_pipelines; // key is fixed, no variants yet
std::unordered_map<int, webgpu_pipeline> argmax_pipelines; // key is vec4
std::unordered_map<int, webgpu_pipeline> argsort_pipelines; // key is order
std::unordered_map<int, webgpu_pipeline> argsort_merge_pipelines; // key is order
@@ -1319,20 +1319,16 @@ class ggml_webgpu_shader_lib {
ggml_webgpu_shader_lib(wgpu::Device device) { this->device = device; }
webgpu_pipeline get_sum_rows_pipeline(const ggml_webgpu_shader_lib_context & context) {
const bool mean = context.dst->op == GGML_OP_MEAN;
auto it = sum_rows_pipelines.find(mean);
auto it = sum_rows_pipelines.find(1);
if (it != sum_rows_pipelines.end()) {
return it->second;
}
std::vector<std::string> defines;
defines.push_back(std::string("WG_SIZE=") + std::to_string(context.max_wg_size));
if (mean) {
defines.push_back("MEAN");
}
auto processed = preprocessor.preprocess(wgsl_sum_rows, defines);
sum_rows_pipelines[mean] = ggml_webgpu_create_pipeline(device, processed, mean ? "mean" : "sum_rows");
return sum_rows_pipelines[mean];
auto processed = preprocessor.preprocess(wgsl_sum_rows, defines);
sum_rows_pipelines[1] = ggml_webgpu_create_pipeline(device, processed, "sum_rows");
return sum_rows_pipelines[1];
}
webgpu_pipeline get_row_norm_pipeline(const ggml_webgpu_shader_lib_context & context) {
+47 -291
View File
@@ -182,6 +182,7 @@ struct webgpu_capabilities {
uint32_t subgroup_size = 0;
uint32_t min_subgroup_size = 0;
uint32_t max_subgroup_size = 0;
size_t memset_bytes_per_thread;
};
// Stores global webgpu members
@@ -202,11 +203,6 @@ struct webgpu_global_context_struct {
wgpu::Buffer memset_params_buf;
webgpu_pipeline memset_pipeline;
// Packed source data and write descriptors for set_tensor_n
wgpu::Buffer set_tensor_n_src_buf;
wgpu::Buffer set_tensor_n_descs_buf;
webgpu_pipeline set_tensor_n_pipeline;
std::string vendor;
// TODO: We should rework the CPU profiling time handling to make it more useful. ref: https://github.com/ggml-org/llama.cpp/pull/22050
@@ -231,14 +227,6 @@ struct webgpu_global_context_struct {
this->memset_params_buf.Destroy();
this->memset_params_buf = nullptr;
}
if (this->set_tensor_n_src_buf) {
this->set_tensor_n_src_buf.Destroy();
this->set_tensor_n_src_buf = nullptr;
}
if (this->set_tensor_n_descs_buf) {
this->set_tensor_n_descs_buf.Destroy();
this->set_tensor_n_descs_buf = nullptr;
}
#ifdef GGML_WEBGPU_DEBUG
if (this->debug_host_buf) {
this->debug_host_buf.Destroy();
@@ -268,6 +256,8 @@ struct webgpu_context_struct {
wgpu::ComputePassEncoder active_compute_pass;
bool batch_compute_passes = true;
size_t memset_bytes_per_thread;
#ifdef GGML_WEBGPU_GPU_PROFILE
// Profiling: per-shader GPU time in ms
std::unordered_map<std::string, double> shader_gpu_time_ms;
@@ -682,54 +672,38 @@ static void ggml_backend_webgpu_buffer_memset(webgpu_global_context & ctx,
size_t size) {
std::lock_guard<std::recursive_mutex> lock(ctx->mutex);
const size_t max_bind_size = ctx->capabilities.limits.maxStorageBufferBindingSize;
const size_t max_wg_size = ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup;
std::vector<uint32_t> params = { (uint32_t) offset, (uint32_t) size, value };
std::vector<wgpu::BindGroupEntry> entries = { ggml_webgpu_make_bind_group_entry(0, buf, 0, buf.GetSize()) };
size_t bytes_per_wg =
ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup * ctx->capabilities.memset_bytes_per_thread;
uint32_t wg_x = CEIL_DIV(size + 3, bytes_per_wg);
while (size > 0) {
const size_t align_offset = offset & ~(ctx->capabilities.limits.minStorageBufferOffsetAlignment - 1);
const size_t lead = offset - align_offset;
const size_t chunk_size = std::min(size, max_bind_size - lead);
const size_t bind_size = ROUNDUP_POW2(chunk_size + lead, WEBGPU_STORAGE_BUF_BINDING_MULT);
ctx->queue.WriteBuffer(ctx->memset_params_buf, 0, params.data(), params.size() * sizeof(uint32_t));
std::vector<uint32_t> params = { (uint32_t) lead, (uint32_t) chunk_size, value };
std::vector<wgpu::BindGroupEntry> entries = { ggml_webgpu_make_bind_group_entry(0, buf, align_offset,
bind_size) };
uint32_t wg_x;
uint32_t wg_y;
uint32_t n_values = CEIL_DIV(chunk_size + lead, sizeof(uint32_t));
uint32_t total_wg = CEIL_DIV(n_values, max_wg_size);
compute_2d_workgroups(total_wg, ctx->capabilities.limits.maxComputeWorkgroupsPerDimension, wg_x, wg_y);
wgpu::BindGroupEntry params_entry = {};
params_entry.binding = 1;
params_entry.buffer = ctx->memset_params_buf;
params_entry.offset = 0;
params_entry.size = WEBGPU_PARAMS_BUF_SIZE_BYTES;
entries.push_back(params_entry);
ctx->queue.WriteBuffer(ctx->memset_params_buf, 0, params.data(), params.size() * sizeof(uint32_t));
wgpu::BindGroupDescriptor bind_group_desc;
bind_group_desc.layout = ctx->memset_pipeline.pipeline.GetBindGroupLayout(0);
bind_group_desc.entryCount = entries.size();
bind_group_desc.entries = entries.data();
bind_group_desc.label = ctx->memset_pipeline.name.c_str();
wgpu::BindGroup bind_group = ctx->device.CreateBindGroup(&bind_group_desc);
wgpu::BindGroupEntry params_entry = {};
params_entry.binding = 1;
params_entry.buffer = ctx->memset_params_buf;
params_entry.offset = 0;
params_entry.size = WEBGPU_PARAMS_BUF_SIZE_BYTES;
entries.push_back(params_entry);
wgpu::CommandEncoder encoder = ctx->device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(ctx->memset_pipeline.pipeline);
pass.SetBindGroup(0, bind_group);
pass.DispatchWorkgroups(wg_x, 1, 1);
pass.End();
wgpu::BindGroupDescriptor bind_group_desc;
bind_group_desc.layout = ctx->memset_pipeline.pipeline.GetBindGroupLayout(0);
bind_group_desc.entryCount = entries.size();
bind_group_desc.entries = entries.data();
bind_group_desc.label = ctx->memset_pipeline.name.c_str();
wgpu::BindGroup bind_group = ctx->device.CreateBindGroup(&bind_group_desc);
wgpu::CommandEncoder encoder = ctx->device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(ctx->memset_pipeline.pipeline);
pass.SetBindGroup(0, bind_group);
pass.DispatchWorkgroups(wg_x, wg_y, 1);
pass.End();
wgpu::CommandBuffer command = encoder.Finish();
std::vector<wgpu::CommandBuffer> commands = { command };
ctx->queue.Submit(commands.size(), commands.data());
offset += chunk_size;
size -= chunk_size;
}
wgpu::CommandBuffer command = encoder.Finish();
std::vector<wgpu::CommandBuffer> commands = { command };
ctx->queue.Submit(commands.size(), commands.data());
}
/** End WebGPU Actions */
@@ -2839,7 +2813,7 @@ static webgpu_encoded_op ggml_webgpu_rope(webgpu_context & ctx,
float theta_scale = powf(freq_base, -2.0f / n_dims);
float corr_dims[2];
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, true, corr_dims);
ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
std::vector<uint32_t> params = {
(uint32_t) (ggml_webgpu_tensor_misalignment(ctx, src0) / ggml_type_size(src0->type)),
@@ -3278,8 +3252,7 @@ static webgpu_encoded_op ggml_webgpu_sum_rows(webgpu_context & ctx, ggml_tensor
total_sum ? 0 : (uint32_t) (src->nb[3] / ggml_type_size(src->type)),
total_sum ? static_cast<uint32_t>(ggml_nelements(src)) : (uint32_t) src->ne[0],
total_sum ? 1 : (uint32_t) src->ne[1],
total_sum ? 1 : (uint32_t) src->ne[2],
total_sum ? 1 : (uint32_t) ggml_nrows(dst) };
total_sum ? 1 : (uint32_t) src->ne[2] };
std::vector<wgpu::BindGroupEntry> entries = { ggml_webgpu_make_tensor_bind_group_entry(ctx, 0, src),
ggml_webgpu_make_tensor_bind_group_entry(ctx, 1, dst) };
@@ -3547,7 +3520,6 @@ static std::optional<webgpu_encoded_op> ggml_webgpu_encode(webgpu_context ctx,
return ggml_webgpu_cumsum(ctx, src0, node);
case GGML_OP_SUM:
case GGML_OP_SUM_ROWS:
case GGML_OP_MEAN:
return ggml_webgpu_sum_rows(ctx, src0, node);
case GGML_OP_CONV_2D:
return ggml_webgpu_conv_2d(ctx, src0, src1, node);
@@ -3915,112 +3887,6 @@ static void ggml_backend_webgpu_buffer_set_tensor(ggml_backend_buffer_t buffer,
WEBGPU_CPU_PROFILE_TOTAL_END(set_tensor, buf_ctx->global_ctx);
}
static void ggml_backend_webgpu_buffer_set_tensor_n(ggml_backend_buffer_t buffer,
struct ggml_tensor * const * tensors,
const void * const * data,
const size_t * offsets,
const size_t * sizes,
size_t n_tensors) {
WEBGPU_CPU_PROFILE_TOTAL_START(set_tensor_n);
ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context *) buffer->context;
webgpu_global_context & ctx = buf_ctx->global_ctx;
WEBGPU_LOG_DEBUG("ggml_backend_webgpu_buffer_set_tensor_n(" << buf_ctx->label << ", " << (const void *) tensors
<< ", " << (const void *) data << ", " << offsets
<< ", " << sizes << ", " << n_tensors << ")");
const size_t max_binding_size = ctx->capabilities.limits.maxStorageBufferBindingSize;
const size_t max_wg_per_dim = ctx->capabilities.limits.maxComputeWorkgroupsPerDimension;
std::lock_guard<std::recursive_mutex> lock(ctx->mutex);
std::vector<uint8_t> src; // we use src to copy the data of many tensors
std::vector<uint32_t> descs; // [dst offset in bytes, src offset in words, size in bytes, unused]
wgpu::Buffer dst_buf;
auto grow_buffer = [&ctx](wgpu::Buffer & buf, size_t size, const char * label) {
if (buf == nullptr || buf.GetSize() < size) {
if (buf) {
buf.Destroy();
}
ggml_webgpu_create_buffer(ctx->device, buf, size, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Storage,
label);
}
};
auto flush_tensors = [&]() {
if (descs.empty()) {
return;
}
src.resize(ROUNDUP_POW2(src.size(), 4));
const size_t descs_size = descs.size() * sizeof(uint32_t);
const size_t n_dsts = descs.size() / 4;
grow_buffer(ctx->set_tensor_n_src_buf, src.size(), "set_tensor_n_src_buf");
grow_buffer(ctx->set_tensor_n_descs_buf, descs_size, "set_tensor_n_descs_buf");
ctx->queue.WriteBuffer(ctx->set_tensor_n_src_buf, 0, src.data(), src.size());
ctx->queue.WriteBuffer(ctx->set_tensor_n_descs_buf, 0, descs.data(), descs_size);
std::vector<wgpu::BindGroupEntry> entries = {
ggml_webgpu_make_bind_group_entry(0, dst_buf, 0, dst_buf.GetSize()),
ggml_webgpu_make_bind_group_entry(1, ctx->set_tensor_n_src_buf, 0, src.size()),
ggml_webgpu_make_bind_group_entry(2, ctx->set_tensor_n_descs_buf, 0, descs_size),
};
wgpu::BindGroupDescriptor bind_group_desc;
bind_group_desc.layout = ctx->set_tensor_n_pipeline.pipeline.GetBindGroupLayout(0);
bind_group_desc.entryCount = entries.size();
bind_group_desc.entries = entries.data();
bind_group_desc.label = ctx->set_tensor_n_pipeline.name.c_str();
wgpu::BindGroup bind_group = ctx->device.CreateBindGroup(&bind_group_desc);
uint32_t wg_x;
uint32_t wg_y;
uint32_t total_wg = n_dsts;
compute_2d_workgroups(total_wg, max_wg_per_dim, wg_x, wg_y);
wgpu::CommandEncoder encoder = ctx->device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(ctx->set_tensor_n_pipeline.pipeline);
pass.SetBindGroup(0, bind_group);
pass.DispatchWorkgroups(wg_x, wg_y, 1);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
ctx->queue.Submit(1, &commands);
src.clear();
descs.clear();
};
if (n_tensors > 0) {
dst_buf = ggml_webgpu_tensor_buf(tensors[0]);
}
for (size_t i = 0; i < n_tensors; i++) {
if (sizes[i] == 0) {
continue;
}
wgpu::Buffer buf = ggml_webgpu_tensor_buf(tensors[i]);
size_t offset = ggml_webgpu_tensor_offset(tensors[i]) + offsets[i];
if (!descs.empty() && (dst_buf.Get() != buf.Get() || src.size() + 4 + sizes[i] > max_binding_size ||
(descs.size() + 4) * sizeof(uint32_t) > max_binding_size)) {
flush_tensors();
}
dst_buf = buf;
// Place the data at the same in-word byte lane as its destination
size_t src_offset = ROUNDUP_POW2(src.size(), 4) + offset % 4;
src.resize(src_offset + sizes[i]);
memcpy(src.data() + src_offset, data[i], sizes[i]);
descs.insert(descs.end(), { (uint32_t) offset, (uint32_t) (src_offset / 4), (uint32_t) sizes[i], 0 });
}
flush_tensors();
WEBGPU_CPU_PROFILE_TOTAL_END(set_tensor_n, ctx);
}
static void ggml_backend_webgpu_buffer_get_tensor(ggml_backend_buffer_t buffer,
const ggml_tensor * tensor,
void * data,
@@ -4081,117 +3947,6 @@ static void ggml_backend_webgpu_buffer_get_tensor(ggml_backend_buffer_t buffer,
WEBGPU_CPU_PROFILE_TOTAL_END(get_tensor, buf_ctx->global_ctx);
}
static void ggml_backend_webgpu_buffer_get_tensor_n(ggml_backend_buffer_t buffer,
const struct ggml_tensor * const * tensors,
void * const * data,
const size_t * offsets,
const size_t * sizes,
size_t n_tensors) {
WEBGPU_CPU_PROFILE_TOTAL_START(get_tensor_n);
ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context *) buffer->context;
WEBGPU_LOG_DEBUG("ggml_backend_webgpu_buffer_get_tensor_n(" << buf_ctx->label << ", " << (const void *) tensors
<< ", " << (const void *) data << ", " << offsets
<< ", " << sizes << ", " << n_tensors << ")");
webgpu_global_context global_ctx = buf_ctx->global_ctx;
wgpu::Device device = global_ctx->device;
typedef struct {
size_t staging_size;
size_t align_offset;
size_t local_offset;
} tensor_desc;
std::vector<tensor_desc> t_descs;
std::vector<std::pair<size_t, size_t>>
staging_chunk_descs; // pair (boundary id, sum of the tensor size to the bounary id)
t_descs.reserve(n_tensors);
size_t sum_tensor_size = 0;
for (size_t i = 0; i < n_tensors; i++) {
const ggml_tensor * tensor = tensors[i];
tensor_desc td;
td.align_offset = ggml_webgpu_tensor_offset(tensor) + offsets[i];
td.local_offset = td.align_offset % 4;
if (td.local_offset != 0) {
// If offset is not a multiple of 4, we need to round it down to the previous
// multiple of 4
td.align_offset -= td.local_offset;
}
td.staging_size = sizes[i] + td.local_offset;
if (td.staging_size % 4 != 0) {
// If size is not a multiple of 4, we need to round it up to the next
// multiple of 4
td.staging_size += 4 - (td.staging_size % 4);
}
t_descs.push_back(td);
if (sum_tensor_size + td.staging_size > global_ctx->capabilities.limits.maxBufferSize) {
if (i == 0) {
GGML_ABORT("it is not expected that one tensor size exceed the one wgpu::Buffer limit size.");
}
staging_chunk_descs.push_back({ i - 1, sum_tensor_size });
sum_tensor_size = td.staging_size;
} else {
sum_tensor_size += td.staging_size;
}
}
staging_chunk_descs.push_back({ n_tensors - 1, sum_tensor_size });
std::lock_guard<std::recursive_mutex> lock(global_ctx->mutex);
size_t tsi = 0;
for (auto & [tei, sum_chunk_size] : staging_chunk_descs) {
if (global_ctx->get_tensor_staging_buf == nullptr ||
global_ctx->get_tensor_staging_buf.GetSize() < sum_chunk_size) {
// Create a new staging buffer if it doesn't exist or is too small
if (global_ctx->get_tensor_staging_buf) {
global_ctx->get_tensor_staging_buf.Destroy();
}
ggml_webgpu_create_buffer(device, global_ctx->get_tensor_staging_buf, sum_chunk_size,
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead,
"get_tensor_staging_buf");
}
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
// Copy the data of the each tensor from the buffer to the staging buffer
size_t i_offset = 0;
for (size_t i = tsi; i <= tei; i++) {
encoder.CopyBufferToBuffer(ggml_webgpu_tensor_buf(tensors[i]), t_descs[i].align_offset,
global_ctx->get_tensor_staging_buf, i_offset, t_descs[i].staging_size);
i_offset += t_descs[i].staging_size;
}
wgpu::CommandBuffer commands = encoder.Finish();
// Submit the command buffer to the queue
global_ctx->queue.Submit(1, &commands);
// Map the staging buffer to read the data
ggml_backend_webgpu_map_buffer(global_ctx, global_ctx->get_tensor_staging_buf, wgpu::MapMode::Read, 0,
sum_chunk_size);
// Must specify size here since the staging buffer might be larger than the tensor size
const void * mapped_range = global_ctx->get_tensor_staging_buf.GetConstMappedRange(0, sum_chunk_size);
i_offset = 0;
for (size_t i = tsi; i <= tei; i++) {
// Copy the data from the mapped range to the output buffer
std::memcpy(data[i], (const void *) ((const char *) mapped_range + i_offset + t_descs[i].local_offset),
sizes[i]);
i_offset += t_descs[i].staging_size;
}
tsi = tei + 1;
global_ctx->get_tensor_staging_buf.Unmap();
}
WEBGPU_CPU_PROFILE_TOTAL_END(get_tensor_n, buf_ctx->global_ctx);
}
static void ggml_backend_webgpu_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
WEBGPU_LOG_DEBUG("ggml_backend_webgpu_buffer_clear(" << buffer << ", " << (uint32_t) value << ")");
WEBGPU_CPU_PROFILE_TOTAL_START(clear);
@@ -4212,8 +3967,8 @@ static ggml_backend_buffer_i ggml_backend_webgpu_buffer_interface = {
/* .cpy_tensor = */ NULL, // TODO: optional, implement this
/* .clear = */ ggml_backend_webgpu_buffer_clear,
/* .reset = */ NULL, // TODO: optional, think it coordinates with
/* .set_tensor_n = */ ggml_backend_webgpu_buffer_set_tensor_n,
/* .get_tensor_n = */ ggml_backend_webgpu_buffer_get_tensor_n,
/* .set_tensor_n = */ NULL,
/* .get_tensor_n = */ NULL,
/* .cpy_tensor_n = */ NULL,
};
@@ -4251,10 +4006,12 @@ static size_t ggml_backend_webgpu_buffer_type_get_alignment(ggml_backend_buffer_
return dev_ctx->webgpu_global_ctx->capabilities.limits.minStorageBufferOffsetAlignment;
}
// maxBufferSize might be larger, but you can't bind more than
// maxStorageBufferBindingSize to a single binding.
static size_t ggml_backend_webgpu_buffer_type_get_max_size(ggml_backend_buffer_type_t buft) {
ggml_backend_webgpu_device_context * dev_ctx =
static_cast<ggml_backend_webgpu_device_context *>(buft->device->context);
return dev_ctx->webgpu_global_ctx->capabilities.limits.maxBufferSize;
return dev_ctx->webgpu_global_ctx->capabilities.limits.maxStorageBufferBindingSize;
}
static size_t ggml_backend_webgpu_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft,
@@ -4419,19 +4176,20 @@ static ggml_guid_t ggml_backend_webgpu_guid(void) {
}
static void ggml_webgpu_init_memset_pipeline(webgpu_global_context & ctx) {
std::vector<wgpu::ConstantEntry> constants(1);
// we use the maximum workgroup size for the memset pipeline
size_t max_threads = ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup *
ctx->capabilities.limits.maxComputeWorkgroupsPerDimension;
// Size the bytes_per_thread so that the largest buffer size can be handled
ctx->capabilities.memset_bytes_per_thread =
CEIL_DIV(ctx->capabilities.limits.maxStorageBufferBindingSize, max_threads);
std::vector<wgpu::ConstantEntry> constants(2);
constants[0].key = "wg_size";
constants[0].value = ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup;
constants[1].key = "bytes_per_thread";
constants[1].value = ctx->capabilities.memset_bytes_per_thread;
ctx->memset_pipeline = ggml_webgpu_create_pipeline(ctx->device, wgsl_memset, "memset", constants);
}
static void ggml_webgpu_init_scatter_write_pipeline(webgpu_global_context & ctx) {
std::vector<wgpu::ConstantEntry> constants(1);
constants[0].key = "wg_size";
constants[0].value = ctx->capabilities.limits.maxComputeInvocationsPerWorkgroup;
ctx->set_tensor_n_pipeline = ggml_webgpu_create_pipeline(ctx->device, wgsl_set_tensor_n, "set_tensor_n", constants);
}
static void ggml_backend_webgpu_request_adapter(wgpu::Instance & instance, wgpu::Adapter & adapter) {
wgpu::RequestAdapterOptions options = {};
@@ -4575,7 +4333,6 @@ static void create_webgpu_device(ggml_backend_webgpu_reg_context * ctx) {
GGML_ASSERT(ctx->webgpu_global_ctx->device != nullptr);
ggml_webgpu_init_memset_pipeline(ctx->webgpu_global_ctx);
ggml_webgpu_init_scatter_write_pipeline(ctx->webgpu_global_ctx);
ggml_webgpu_create_buffer(ctx->webgpu_global_ctx->device, ctx->webgpu_global_ctx->memset_params_buf,
WEBGPU_PARAMS_BUF_SIZE_BYTES, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform,
"memset_params_buf");
@@ -4934,7 +4691,7 @@ static bool ggml_backend_webgpu_device_supports_op(ggml_backend_dev_t dev, const
supports_op = (op->type == GGML_TYPE_F32 && src0->type == GGML_TYPE_F32) && ggml_is_contiguous_rows(src0);
break;
case GGML_OP_ROPE:
supports_op = ggml_rope_get_truncate(op) && (op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16);
supports_op = op->type == GGML_TYPE_F32 || op->type == GGML_TYPE_F16;
break;
case GGML_OP_GLU:
switch (ggml_get_glu_op(op)) {
@@ -5077,7 +4834,6 @@ static bool ggml_backend_webgpu_device_supports_op(ggml_backend_dev_t dev, const
supports_op = op->type == GGML_TYPE_F32 && src0->type == op->type && ggml_is_contiguously_allocated(src0);
break;
case GGML_OP_SUM_ROWS:
case GGML_OP_MEAN:
supports_op = op->type == GGML_TYPE_F32 && src0->type == op->type && ggml_is_contiguous_rows(src0);
break;
case GGML_OP_UPSCALE:

Some files were not shown because too many files have changed in this diff Show More