mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-11-06 19:07:33 -06:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d35985b5a | ||
|
|
bd540a6e61 | ||
|
|
36023ede3d |
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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
@@ -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}")
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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++) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"))
|
||||
@@ -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"]))
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) ||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 -----------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -78,7 +78,6 @@ set(GGML_OPENCL_KERNELS
|
||||
diag_mask_inf
|
||||
diag
|
||||
div
|
||||
dsv4_hc
|
||||
gelu
|
||||
get_rows
|
||||
glu
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user