mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-11-02 08:57:39 -06:00
Compare commits
83
Commits
xsn/ple_split
...
b11982
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a199ad31a | ||
|
|
da6c30ade8 | ||
|
|
f7c01c5226 | ||
|
|
bf48103ad1 | ||
|
|
6d5ac3c8d5 | ||
|
|
7ac5727d60 | ||
|
|
2e0d86bebc | ||
|
|
e09a78a25d | ||
|
|
369dcf309c | ||
|
|
d0ff6317cc | ||
|
|
cf24317bea | ||
|
|
74a4fb0037 | ||
|
|
5220213255 | ||
|
|
c3df4b7e22 | ||
|
|
ac8e1694d8 | ||
|
|
c02b5c641e | ||
|
|
ff0d396e3d | ||
|
|
838093f192 | ||
|
|
604cc43023 | ||
|
|
39febd2adb | ||
|
|
f5222f25c1 | ||
|
|
28cb47c7c8 | ||
|
|
8642541b25 | ||
|
|
d4f1b18b3a | ||
|
|
d1eb0debad | ||
|
|
5d7793d6fc | ||
|
|
ec941ee852 | ||
|
|
e486a4b559 | ||
|
|
f1a7dea99a | ||
|
|
a7af149a2a | ||
|
|
2864937a77 | ||
|
|
a71c00f0bb | ||
|
|
f4d8af608a | ||
|
|
02d502f314 | ||
|
|
b1756b23f3 | ||
|
|
6886628cb1 | ||
|
|
8ca4060b7e | ||
|
|
d05706b4f3 | ||
|
|
1b4554eff1 | ||
|
|
a919179942 | ||
|
|
338fce6511 | ||
|
|
19ff817fb2 | ||
|
|
eb7818c0b8 | ||
|
|
6c44591049 | ||
|
|
19cce69199 | ||
|
|
e86f5fd1e9 | ||
|
|
27b7a8a009 | ||
|
|
7ecb57f7ad | ||
|
|
8542e94350 | ||
|
|
922f53ed33 | ||
|
|
0c8c59ef3d | ||
|
|
3ab37aedfe | ||
|
|
85cc432e2c | ||
|
|
74a1fd3d03 | ||
|
|
7eed6b7685 | ||
|
|
276378515c | ||
|
|
d78f40d9d3 | ||
|
|
fae9ac561f | ||
|
|
a8276b70cc | ||
|
|
de5122d879 | ||
|
|
6666a7330c | ||
|
|
7a2e5aba90 | ||
|
|
0d5ca60098 | ||
|
|
d930fe3c26 | ||
|
|
a2b3e121ce | ||
|
|
823df7d0fb | ||
|
|
1a97e387d3 | ||
|
|
bc262f33cc | ||
|
|
f5892ca5ad | ||
|
|
7aca0d2ea8 | ||
|
|
961c376a2d | ||
|
|
4244ad7acd | ||
|
|
63b4b303bd | ||
|
|
caa8191df1 | ||
|
|
a4699400e3 | ||
|
|
4b851fbee8 | ||
|
|
4d71b42fd8 | ||
|
|
70c5f26324 | ||
|
|
5cd62ffee9 | ||
|
|
38645c2ad9 | ||
|
|
02269b4c00 | ||
|
|
5320c87ff0 | ||
|
|
74b104c9ba |
@@ -107,3 +107,52 @@ jobs:
|
||||
source .venv/bin/activate
|
||||
cd build
|
||||
ctest -L behavior -LE 'model|backend' --verbose --timeout 900
|
||||
|
||||
windows:
|
||||
runs-on: windows-2025
|
||||
|
||||
cache-mode: write
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
key: behavior-check-windows-2025
|
||||
evict-old-files: 1d
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Python Dependencies
|
||||
id: python_depends
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -m venv .venv
|
||||
source .venv/Scripts/activate
|
||||
pip install jinja2==3.1.6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cmake -B build -G "Ninja Multi-Config" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=cmake/x64-windows-llvm.cmake ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DGGML_NATIVE=OFF ^
|
||||
-DGGML_BACKEND_DL=OFF
|
||||
set /A NINJA_JOBS=%NUMBER_OF_PROCESSORS%-1
|
||||
cmake --build build --config Release -j %NINJA_JOBS%
|
||||
|
||||
- name: Test
|
||||
id: cmake_test
|
||||
shell: bash
|
||||
run: |
|
||||
source .venv/Scripts/activate
|
||||
cd build
|
||||
ctest -C Release -L behavior -LE 'model|backend' --verbose --timeout 900
|
||||
|
||||
@@ -43,9 +43,9 @@ env:
|
||||
LLAMA_ARG_LOG_TIMESTAMPS: 1
|
||||
|
||||
jobs:
|
||||
ubuntu-24-s390x:
|
||||
name: ubuntu-24-s390x (VXE ${{ matrix.vxe }})
|
||||
runs-on: ubuntu-24.04-s390x
|
||||
ubuntu-26-s390x:
|
||||
name: ubuntu-26-s390x (VXE ${{ matrix.vxe }})
|
||||
runs-on: ubuntu-26.04-s390x
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -66,12 +66,6 @@ jobs:
|
||||
libjpeg-dev build-essential libssl-dev \
|
||||
git-lfs
|
||||
|
||||
- name: Toolchain workaround (GCC 14)
|
||||
run: |
|
||||
sudo apt-get install -y gcc-14 g++-14
|
||||
echo "CC=gcc-14" >> "$GITHUB_ENV"
|
||||
echo "CXX=g++-14" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Python Dependencies
|
||||
id: python_depends
|
||||
run: |
|
||||
@@ -116,30 +110,19 @@ jobs:
|
||||
|
||||
ubuntu-26-zdnn-s390x:
|
||||
name: ubuntu-26-zdnn-s390x
|
||||
runs-on: ubuntu-24.04-s390x
|
||||
container: ubuntu:26.04 # required to get GCC 15.1 and binutils 2.44
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ubuntu-26.04-s390x
|
||||
|
||||
steps:
|
||||
- name: Build Dependencies
|
||||
id: build_depends
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential cmake git ca-certificates \
|
||||
libssl-dev libzdnn-dev
|
||||
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Toolchain workaround (GCC 15)
|
||||
- name: Build Dependencies
|
||||
id: build_depends
|
||||
run: |
|
||||
apt-get install -y gcc-15 g++-15
|
||||
echo "CC=gcc-15" >> "$GITHUB_ENV"
|
||||
echo "CXX=g++-15" >> "$GITHUB_ENV"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential libssl-dev libzdnn-dev
|
||||
|
||||
- name: Build with zDNN Backend
|
||||
id: cmake_build
|
||||
|
||||
@@ -58,7 +58,12 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libvulkan1 mesa-vulkan-drivers libglvnd0 libgl1 libglx0 libegl1 libgles2 libssl-dev time unzip wget python3 python3-venv python3-pip
|
||||
sudo apt install -y software-properties-common
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev libvulkan1 mesa-vulkan-drivers libglvnd0 libgl1 libglx0 libegl1 libgles2 libssl-dev time unzip wget python3.12 python3.12-venv
|
||||
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1
|
||||
sudo update-alternatives --config python3
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
|
||||
@@ -172,6 +172,8 @@ jobs:
|
||||
- [Ubuntu x64 (CPU)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-x64.tar.gz)
|
||||
- [Ubuntu arm64 (CPU)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-arm64.tar.gz)
|
||||
- [Ubuntu s390x (CPU)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-s390x.tar.gz)
|
||||
- [Ubuntu 26.04 s390x (CPU)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-26.04-s390x.tar.gz)
|
||||
- [Ubuntu 26.04 s390x (zDNN)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-zdnn-s390x.tar.gz)
|
||||
- [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)
|
||||
|
||||
@@ -180,6 +180,8 @@ jobs:
|
||||
os: ubuntu-24.04-arm
|
||||
- build: 's390x'
|
||||
os: ubuntu-24.04-s390x
|
||||
- build: '26.04-s390x'
|
||||
os: ubuntu-26.04-s390x
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -200,7 +202,7 @@ jobs:
|
||||
id: depends
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential libssl-dev
|
||||
sudo apt-get install -y build-essential libssl-dev
|
||||
|
||||
- name: Toolchain workaround (GCC 14)
|
||||
if: ${{ contains(matrix.os, 'ubuntu-24.04') }}
|
||||
@@ -210,7 +212,7 @@ jobs:
|
||||
echo "CXX=g++-14" >> "$GITHUB_ENV"
|
||||
|
||||
- name: ccache
|
||||
if: ${{ matrix.build != 's390x' }}
|
||||
if: ${{ !contains(matrix.os, 's390x') }}
|
||||
uses: ggml-org/ccache-action@v1.2.24
|
||||
with:
|
||||
key: release-${{ matrix.os }}-cpu
|
||||
@@ -242,7 +244,7 @@ jobs:
|
||||
archive: false
|
||||
|
||||
- name: ccache-clear
|
||||
if: ${{ matrix.build != 's390x' }}
|
||||
if: ${{ !contains(matrix.os, 's390x') }}
|
||||
uses: ./.github/actions/ccache-clear
|
||||
with:
|
||||
key: release-${{ matrix.os }}-cpu
|
||||
@@ -968,6 +970,11 @@ jobs:
|
||||
evict-old-files: 1d
|
||||
max-size: "1G"
|
||||
|
||||
- name: Tune ccache for ROCm
|
||||
run: |
|
||||
# since ROCm 10.1, clang -E output has no GPU code, so the preprocessor fallback can reuse objects with old GPU code
|
||||
ccache --set-config=depend_mode=true
|
||||
|
||||
# - name: Cache ROCm Installation
|
||||
# id: cache-rocm
|
||||
# uses: actions/cache@v5
|
||||
@@ -1545,6 +1552,8 @@ jobs:
|
||||
# builds hit.
|
||||
ccache --set-config=compiler_check=content
|
||||
ccache --set-config=sloppiness=time_macros,include_file_mtime,include_file_ctime
|
||||
# since ROCm 10.1, clang -E output has no GPU code, so the preprocessor fallback can reuse objects with old GPU code
|
||||
ccache --set-config=depend_mode=true
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
@@ -1619,6 +1628,60 @@ jobs:
|
||||
with:
|
||||
key: release-ubuntu-24.04-rocm-${{ matrix.ROCM_VERSION }}-${{ matrix.build }}
|
||||
|
||||
ubuntu-26-zdnn:
|
||||
needs: [check-release, ui-build]
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
runs-on: ubuntu-26.04-s390x
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download UI build
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: llama-ui.zip
|
||||
path: tools/ui/dist
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential libssl-dev libzdnn-dev
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_RPATH='$ORIGIN' \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||
-DGGML_BACKEND_DL=ON \
|
||||
-DGGML_NATIVE=OFF \
|
||||
-DGGML_VXE=ON \
|
||||
-DGGML_ZDNN=ON \
|
||||
-DCMAKE_C_FLAGS="-march=arch15" \
|
||||
-DCMAKE_CXX_FLAGS="-march=arch15" \
|
||||
-DLLAMA_FATAL_WARNINGS=ON \
|
||||
${{ env.CMAKE_ARGS }}
|
||||
cmake --build build --config Release -j $(nproc)
|
||||
|
||||
- name: Pack artifacts
|
||||
id: pack_artifacts
|
||||
run: |
|
||||
cp LICENSE ./build/bin/
|
||||
tar -czvf llama-${{ needs.check-release.outputs.tag_name }}-bin-ubuntu-zdnn-s390x.tar.gz --transform "s,^\.,llama-${{ needs.check-release.outputs.tag_name }}," -C ./build/bin .
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: llama-${{ needs.check-release.outputs.tag_name }}-bin-ubuntu-zdnn-s390x.tar.gz
|
||||
archive: false
|
||||
|
||||
ios-xcode:
|
||||
needs: [check-release]
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ include(CheckIncludeFileCXX)
|
||||
|
||||
### llama.cpp version
|
||||
set(LLAMA_VERSION_MAJOR 0)
|
||||
set(LLAMA_VERSION_MINOR 7)
|
||||
set(LLAMA_VERSION_MINOR 8)
|
||||
set(LLAMA_VERSION_PATCH 0)
|
||||
set(LLAMA_VERSION_BASE "${LLAMA_VERSION_MAJOR}.${LLAMA_VERSION_MINOR}.${LLAMA_VERSION_PATCH}")
|
||||
|
||||
@@ -103,7 +103,7 @@ endif()
|
||||
|
||||
# subprocess spawning isn't a supported/sandbox-friendly operation on mobile OSes or in WASM
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android" OR ANDROID
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN)
|
||||
OR CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN OR CMAKE_SYSTEM_NAME STREQUAL "WASI")
|
||||
set(LLAMA_SUBPROCESS_DEFAULT OFF)
|
||||
else()
|
||||
set(LLAMA_SUBPROCESS_DEFAULT ON)
|
||||
|
||||
+22
-29
@@ -2,20 +2,29 @@
|
||||
# multiple collaborators per item can be specified
|
||||
#
|
||||
# ggml-org/ci : CISC, danbev, ggerganov, netrunnereve, ngxson, taronaeo
|
||||
# ggml-org/ggml-base : aendk, fairydreaming, ggerganov
|
||||
# ggml-org/ggml-cann : hipudding
|
||||
# ggml-org/ggml-cuda : JohannesGaessler, am17an, IMbackK, ORippler
|
||||
# ggml-org/ggml-cpu : angt, chaxu01, ggerganov, max-krasnyansky, taronaeo
|
||||
# ggml-org/ggml-cuda : am17an, gaugarg-nv, IMbackK, JohannesGaessler, ORippler, thelittlefireman, ynankani
|
||||
# ggml-org/ggml-et : marty1885
|
||||
# ggml-org/ggml-hexagon : lhez, max-krasnyansky
|
||||
# ggml-org/ggml-metal : ggerganov
|
||||
# ggml-org/ggml-hip : IMbackK, thelittlefireman
|
||||
# ggml-org/ggml-metal : forforever73, ggerganov, jhen0409, masterFoad
|
||||
# ggml-org/ggml-opencl : lhez, max-krasnyansky
|
||||
# ggml-org/ggml-rpc : rgerganov
|
||||
# ggml-org/ggml-sycl : arthw
|
||||
# ggml-org/ggml-openvino : ravi9, wine99
|
||||
# ggml-org/ggml-riscv : xctan
|
||||
# ggml-org/ggml-rpc : am17an, rgerganov
|
||||
# ggml-org/ggml-sycl : arthw, Titaniumtown
|
||||
# ggml-org/ggml-vulkan : 0cc4m, jeffbolznv
|
||||
# ggml-org/ggml-webgpu : reeselevine, yomaytk
|
||||
# ggml-org/ggml-zdnn : taronaeo
|
||||
# ggml-org/llama-common : ggerganov, aldehir, angt, danbev, ngxson, pwilkin
|
||||
# ggml-org/ggml-zdnn : AlekseiNikiforovIBM, Andreas-Krebbel, taronaeo
|
||||
# ggml-org/llama-base : am17an, CISC, ggerganov, ngxson, sanmai, ServeurpersoCom
|
||||
# ggml-org/llama-common : aldehir, angt, bartowski1182, danbev, ggerganov, ngxson, nikwen, pwilkin, ruixiang63, sanmai
|
||||
# ggml-org/llama-examples : ggerganov
|
||||
# ggml-org/llama-models : CISC, danbev, ggerganov, ngxson
|
||||
# ggml-org/llama-mtmd : ngxson
|
||||
# ggml-org/llama-server : ggerganov, ngxson, allozaur, angt, ServeurpersoCom
|
||||
# ggml-org/llama-ui : allozaur
|
||||
# ggml-org/llama-server : allozaur, angt, ggerganov, ngxson, ServeurpersoCom
|
||||
# ggml-org/llama-ui : allozaur
|
||||
|
||||
/.devops/*.Dockerfile @ngxson
|
||||
/.github/actions/ @ggml-org/ci
|
||||
@@ -29,28 +38,12 @@
|
||||
/conversion/ @CISC
|
||||
/convert_*.py @CISC
|
||||
/docs/backend/snapdragon/ @ggml-org/ggml-hexagon
|
||||
/examples/batched.swift/ @ggerganov
|
||||
/examples/batched/ @ggerganov
|
||||
/examples/convert-llama2c-to-ggml/ @ggerganov
|
||||
/examples/ @ggml-org/llama-examples
|
||||
/examples/debug/ @danbev @pwilkin
|
||||
/examples/deprecation-warning/ @ggerganov
|
||||
/examples/diffusion/ @am17an
|
||||
/examples/embedding/ @ggerganov
|
||||
/examples/eval-callback/ @ggerganov
|
||||
/examples/export-docs/ @ggerganov
|
||||
/examples/gen-docs/ @ggerganov
|
||||
/examples/gguf/ @ggerganov
|
||||
/examples/llama.android/ @ggerganov @hanyin-arm @naco-siren
|
||||
/examples/llama.swiftui/ @ggerganov
|
||||
/examples/llama.vim @ggerganov
|
||||
/examples/lookahead/ @ggerganov
|
||||
/examples/lookup/ @JohannesGaessler
|
||||
/examples/model-conversion/ @danbev
|
||||
/examples/parallel/ @ggerganov
|
||||
/examples/passkey/ @ggerganov
|
||||
/examples/retrieval/ @ggerganov
|
||||
/examples/speculative-simple/ @ggerganov
|
||||
/examples/speculative/ @ggerganov
|
||||
/ggml/cmake/ @ggerganov
|
||||
/ggml/include/ @ggerganov
|
||||
/ggml/src/ggml-backend-meta.cpp @JohannesGaessler
|
||||
@@ -62,7 +55,7 @@
|
||||
/ggml/src/ggml-cuda/ @ggml-org/ggml-cuda
|
||||
/ggml/src/ggml-cuda/vendors/hip.h @IMbackK
|
||||
/ggml/src/ggml-hexagon/ @ggml-org/ggml-hexagon
|
||||
/ggml/src/ggml-hip/ @IMbackK
|
||||
/ggml/src/ggml-hip/ @ggml-org/ggml-hip
|
||||
/ggml/src/ggml-et/ @marty1885
|
||||
/ggml/src/ggml-impl.h @ggerganov
|
||||
/ggml/src/ggml-metal/ @ggml-org/ggml-metal
|
||||
@@ -76,7 +69,7 @@
|
||||
/ggml/src/ggml-virtgpu/ @kpouget
|
||||
/ggml/src/ggml-vulkan/ @ggml-org/ggml-vulkan
|
||||
/ggml/src/ggml-webgpu/ @ggml-org/ggml-webgpu
|
||||
/ggml/src/ggml-zdnn/ @ggml-org/ggml-zdnn @Andreas-Krebbel @AlekseiNikiforovIBM
|
||||
/ggml/src/ggml-zdnn/ @ggml-org/ggml-zdnn
|
||||
/ggml/src/ggml-zendnn/ @avinashcpandey @Jiten1parmar
|
||||
/ggml/src/ggml.c @ggerganov
|
||||
/ggml/src/ggml.cpp @ggerganov
|
||||
@@ -87,14 +80,14 @@
|
||||
/scripts/get* @ggerganov
|
||||
/scripts/sync* @ggerganov
|
||||
/scripts/snapdragon/ @ggml-org/ggml-hexagon
|
||||
/src/ @ggerganov
|
||||
/src/ @ggml-org/llama-base
|
||||
/src/llama-adapter.* @CISC
|
||||
/src/llama-arch.* @CISC
|
||||
/src/llama-chat.* @ngxson
|
||||
/src/llama-graph.* @CISC
|
||||
/src/llama-model.* @CISC
|
||||
/src/llama-vocab.* @CISC
|
||||
/src/models/ @CISC
|
||||
/src/models/ @ggml-org/llama-models
|
||||
/tests/ @ggerganov
|
||||
/tests/test-chat.* @pwilkin
|
||||
/tests/test-server-component.cpp @ggml-org/llama-server
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[](https://github.com/ggml-org/llama.cpp/actions/workflows/docker.yml)
|
||||
[](https://github.com/ggml-org/llama.cpp/actions/workflows/winget.yml)
|
||||
|
||||
[ggml](https://github.com/ggml-org/ggml) / [ops](https://github.com/ggml-org/llama.cpp/blob/master/docs/ops.md) / [maintainer PRs](https://github.com/ggml-org/llama.cpp/issues?q=is%3Apr%20is%3Aopen%20draft%3AFalse%20(author%3Argerganov%20OR%20author%3AKitaitiMakoto%20OR%20author%3Adanbev%20OR%20author%3Aaldehir%20OR%20author%3Amax-krasnyansky%20OR%20author%3ACISC%20OR%20author%3Aggerganov%20OR%20author%3Aam17an%20OR%20author%3Ajhen0409%20OR%20author%3Abartowski1182%20OR%20author%3Anikwen%20OR%20author%3Ahipudding%20OR%20author%3Aravi9%20OR%20author%3AServeurpersoCom%20OR%20author%3Apwilkin%20OR%20author%3Areeselevine%20OR%20author%3Angxson%20OR%20author%3Ajeffbolznv%20OR%20author%3Amarty1885%20OR%20author%3A0cc4m%20OR%20author%3ATitaniumtown%20OR%20author%3Aangt%20OR%20author%3AIMbackK%20OR%20author%3Aarthw%20OR%20author%3AJohannesGaessler%20OR%20author%3AORippler%20OR%20author%3Aruixiang63%20OR%20author%3Axctan%20OR%20author%3Aallozaur%20OR%20author%3Ayomaytk%20OR%20author%3Aaendk%20OR%20author%3Awine99%20OR%20author%3Agaugarg-nv%20OR%20author%3Ataronaeo%20OR%20author%3Aforforever73%20OR%20author%3Alhez%20OR%20author%3Anetrunnereve%20OR%20author%3Afairydreaming)%20sort%3Aupdated-desc) / [dev stats](https://github.com/ggml-org/llama.cpp-dev) / [lib llama API](https://github.com/ggml-org/llama.cpp/issues/9289) / [llama-server REST API](https://github.com/ggml-org/llama.cpp/issues/9291)
|
||||
[ggml](https://github.com/ggml-org/ggml) / [ops](https://github.com/ggml-org/llama.cpp/blob/master/docs/ops.md) / [maintainer PRs](https://github.com/ggml-org/llama.cpp/issues?q=is%3Apr%20is%3Aopen%20draft%3AFalse%20(author%3Asanmai%20OR%20author%3Argerganov%20OR%20author%3AKitaitiMakoto%20OR%20author%3Adanbev%20OR%20author%3Aaldehir%20OR%20author%3Amax-krasnyansky%20OR%20author%3ACISC%20OR%20author%3Aggerganov%20OR%20author%3Aam17an%20OR%20author%3Ajhen0409%20OR%20author%3Abartowski1182%20OR%20author%3Anikwen%20OR%20author%3Ahipudding%20OR%20author%3Aravi9%20OR%20author%3AServeurpersoCom%20OR%20author%3Athelittlefireman%20OR%20author%3Apwilkin%20OR%20author%3Areeselevine%20OR%20author%3Angxson%20OR%20author%3Ajeffbolznv%20OR%20author%3Amarty1885%20OR%20author%3A0cc4m%20OR%20author%3ATitaniumtown%20OR%20author%3Aangt%20OR%20author%3AIMbackK%20OR%20author%3Aarthw%20OR%20author%3AJohannesGaessler%20OR%20author%3AORippler%20OR%20author%3Aruixiang63%20OR%20author%3Axctan%20OR%20author%3AmasterFoad%20OR%20author%3Aallozaur%20OR%20author%3AAndreas-Krebbel%20OR%20author%3Ayomaytk%20OR%20author%3Aaendk%20OR%20author%3Awine99%20OR%20author%3Agaugarg-nv%20OR%20author%3Ataronaeo%20OR%20author%3Aforforever73%20OR%20author%3Achaxu01%20OR%20author%3Alhez%20OR%20author%3AAlekseiNikiforovIBM%20OR%20author%3Anetrunnereve%20OR%20author%3Afairydreaming%20OR%20author%3Aynankani)%20sort%3Aupdated-desc) / [dev stats](https://github.com/ggml-org/llama.cpp-dev) / [lib llama API](https://github.com/ggml-org/llama.cpp/issues/9289) / [llama-server REST API](https://github.com/ggml-org/llama.cpp/issues/9291)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+16
-1
@@ -924,7 +924,7 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context
|
||||
// TODO @ngxson : maybe show a list of available models in CLI in this case
|
||||
bool can_skip_model = params.usage || params.completion || !params.server_base.empty();
|
||||
if (!can_skip_model && params.model.path.empty()) {
|
||||
throw std::invalid_argument("error: --model is required\n");
|
||||
throw std::invalid_argument("error: --model or -hf is required\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3708,6 +3708,13 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.models_max = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_MODELS_MAX"));
|
||||
add_opt(common_arg(
|
||||
{"--models-memory-margin"}, "N",
|
||||
string_format("for router server, MiB of device memory to keep free per device, unload LRU models until a new one fits (default: %d, 0 = disabled)", params.models_memory_margin),
|
||||
[](common_params & params, int value) {
|
||||
params.models_memory_margin = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_SERVER}).set_env("LLAMA_ARG_MODELS_MEMORY_MARGIN"));
|
||||
add_opt(common_arg(
|
||||
{"--models-autoload"},
|
||||
{"--no-models-autoload"},
|
||||
@@ -4505,6 +4512,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
params.tts_lang = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TTS}));
|
||||
add_opt(common_arg(
|
||||
{"--tts-voice"}, "VOICE",
|
||||
"voice for audio generation (model-dependent)\n"
|
||||
"see tts/README.md for per-model usage notes",
|
||||
[](common_params & params, const std::string & value) {
|
||||
params.tts_voice = value;
|
||||
}
|
||||
).set_examples({LLAMA_EXAMPLE_TTS}));
|
||||
add_opt(common_arg(
|
||||
{"--tts-speaker-file"}, "FNAME",
|
||||
"speaker file path for audio generation",
|
||||
|
||||
+113
-23
@@ -190,10 +190,17 @@ json common_chat_msg::to_json_oaicompat(bool concat_typed_text) const {
|
||||
} else {
|
||||
auto & parts = jmsg["content"] = json::array();
|
||||
for (const auto & part : content_parts) {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"text", part.text},
|
||||
});
|
||||
if (part.type == "tool_reference") {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"name", part.name},
|
||||
});
|
||||
} else {
|
||||
parts.push_back({
|
||||
{"type", part.type},
|
||||
{"text", part.text},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -309,6 +316,7 @@ struct common_chat_templates {
|
||||
bool has_explicit_template; // Model had builtin template or template overridden was specified.
|
||||
std::unique_ptr<common_chat_template> template_default; // always set (defaults to chatml)
|
||||
std::unique_ptr<common_chat_template> template_tool_use;
|
||||
std::unique_ptr<common_chat_template> template_tts; // the prompt of a text to speak
|
||||
};
|
||||
|
||||
common_chat_tool_choice common_chat_tool_choice_parse_oaicompat(const std::string & tool_choice) {
|
||||
@@ -370,12 +378,16 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
|
||||
throw std::invalid_argument("Missing content part type: " + part.dump());
|
||||
}
|
||||
const auto & type = part.at("type");
|
||||
if (type != "text" && type != "media_marker") {
|
||||
throw std::invalid_argument("Unsupported content part type: " + type.dump());
|
||||
}
|
||||
common_chat_msg_content_part msg_part;
|
||||
msg_part.type = type;
|
||||
msg_part.text = part.at("text");
|
||||
if (type == "text" || type == "media_marker") {
|
||||
msg_part.text = part.at("text");
|
||||
} else if (type == "tool_reference") {
|
||||
// names a request tool, the template renders its definition
|
||||
msg_part.name = part.at("name");
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported content part type: " + type.dump());
|
||||
}
|
||||
msg.content_parts.push_back(msg_part);
|
||||
}
|
||||
} else if (!content.is_null()) {
|
||||
@@ -610,13 +622,17 @@ json common_chat_tools_to_json_oaicompat(const std::vector<common_chat_tool> & t
|
||||
|
||||
auto result = json::array();
|
||||
for (const auto & tool : tools) {
|
||||
json function {
|
||||
{ "name", tool.name },
|
||||
{ "description", tool.description },
|
||||
{ "parameters", json::parse(tool.parameters) },
|
||||
};
|
||||
if (tool.defer_loading) {
|
||||
function["defer_loading"] = true;
|
||||
}
|
||||
result.push_back({
|
||||
{ "type", "function" },
|
||||
{ "function", {
|
||||
{ "name", tool.name },
|
||||
{ "description", tool.description },
|
||||
{ "parameters", json::parse(tool.parameters) },
|
||||
}},
|
||||
{ "function", function },
|
||||
});
|
||||
}
|
||||
return result;
|
||||
@@ -653,10 +669,19 @@ std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const json & too
|
||||
}
|
||||
|
||||
const auto & function = tool.at("function");
|
||||
|
||||
// "defer_loading" sits on the function, or on the tool next to "type"; the function wins
|
||||
const json & defer = function.contains("defer_loading") ? function.at("defer_loading")
|
||||
: tool.value("defer_loading", json(false));
|
||||
if (!defer.is_boolean()) {
|
||||
throw std::invalid_argument("Expected 'defer_loading' to be a boolean, got " + defer.dump());
|
||||
}
|
||||
|
||||
result.push_back({
|
||||
/* .name = */ function.at("name"),
|
||||
/* .description = */ function.value("description", ""),
|
||||
/* .parameters = */ function.value("parameters", json::object()).dump(),
|
||||
/* .defer_loading = */ defer.get<bool>(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -779,19 +804,70 @@ bool common_chat_templates_was_explicit(const struct common_chat_templates * tmp
|
||||
return tmpls->has_explicit_template;
|
||||
}
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates) {
|
||||
// renders a task template (e.g. "tts") with the given variables
|
||||
static std::string common_chat_template_render(const common_chat_template & tmpl, const json & vars) {
|
||||
jinja::context ctx(tmpl.source());
|
||||
jinja::global_from_json(ctx, vars, false);
|
||||
ctx.set_val("bos_token", jinja::mk_val<jinja::value_string>(tmpl.bos_token()));
|
||||
ctx.set_val("eos_token", jinja::mk_val<jinja::value_string>(tmpl.eos_token()));
|
||||
jinja::runtime runtime(ctx);
|
||||
return jinja::runtime::gather_string_parts(runtime.execute(tmpl.prog))->as_string().str();
|
||||
}
|
||||
|
||||
// LFM3 format detection: template renders deferred tools inside <additional_tools>...</additional_tools>
|
||||
static bool is_lfm3_template(const std::string & src) {
|
||||
return src.find("<additional_tools>") != std::string::npos;
|
||||
}
|
||||
|
||||
// LFM3-Audio: a known language code maps to its name, no language lets the model detect it
|
||||
static std::string lfm3_asr_system_prompt(const std::string & language) {
|
||||
static const std::map<std::string, std::string> names = {
|
||||
{ "ar", "Arabic" }, { "da", "Danish" }, { "de", "German" }, { "el", "Greek" }, { "en", "English" },
|
||||
{ "es", "Spanish" }, { "fi", "Finnish" }, { "fr", "French" }, { "he", "Hebrew" }, { "it", "Italian" },
|
||||
{ "ja", "Japanese" }, { "ko", "Korean" }, { "nl", "Dutch" }, { "pl", "Polish" }, { "pt", "Portuguese" },
|
||||
{ "sv", "Swedish" }, { "tr", "Turkish" }, { "zh", "Chinese" },
|
||||
};
|
||||
if (language.empty()) {
|
||||
return "Perform ASR.";
|
||||
}
|
||||
std::string code = language;
|
||||
std::transform(code.begin(), code.end(), code.begin(), [](unsigned char c) { return (char) std::tolower(c); });
|
||||
const auto it = names.find(code);
|
||||
return "Perform ASR in " + (it != names.end() ? it->second : language) + ".";
|
||||
}
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates, const std::string & language) {
|
||||
common_chat_prompt_preset asr_preset;
|
||||
asr_preset.system = "";
|
||||
asr_preset.user = "Transcribe audio to text";
|
||||
|
||||
if (chat_templates && chat_templates->template_default && is_lfm2_template(chat_templates->template_default->source())) {
|
||||
asr_preset.system = "Perform ASR.";
|
||||
asr_preset.user = "";
|
||||
if (chat_templates && chat_templates->template_default) {
|
||||
const std::string src = chat_templates->template_default->source();
|
||||
if (is_lfm2_template(src)) {
|
||||
asr_preset.system = "Perform ASR.";
|
||||
asr_preset.user = "";
|
||||
} else if (is_lfm3_template(src)) {
|
||||
asr_preset.system = lfm3_asr_system_prompt(language);
|
||||
asr_preset.user = "";
|
||||
asr_preset.has_language = true;
|
||||
}
|
||||
}
|
||||
|
||||
return asr_preset;
|
||||
}
|
||||
|
||||
std::string common_chat_get_tts_prompt(const common_chat_templates * chat_templates, const std::string & text,
|
||||
const std::string & language, const std::string & voice) {
|
||||
if (!chat_templates || !chat_templates->template_tts) {
|
||||
return "";
|
||||
}
|
||||
return common_chat_template_render(*chat_templates->template_tts, {
|
||||
{"messages", json::array({{{"role", "user"}, {"content", text}}})},
|
||||
{"language", language},
|
||||
{"voice", voice},
|
||||
});
|
||||
}
|
||||
|
||||
std::string common_chat_templates_source(const struct common_chat_templates * tmpls, const std::string & variant) {
|
||||
if (!variant.empty()) {
|
||||
if (variant == "tool_use") {
|
||||
@@ -902,6 +978,16 @@ common_chat_templates_ptr common_chat_templates_init(const struct llama_model *
|
||||
LOG_ERR("%s: failed to parse tool use chat template (ignoring it): %s\n", __func__, e.what());
|
||||
}
|
||||
}
|
||||
// task templates of the model, kept with a chat template override
|
||||
if (model) {
|
||||
if (const char * src = llama_model_chat_template(model, "tts")) {
|
||||
try {
|
||||
tmpls->template_tts = std::make_unique<common_chat_template>(src, token_bos, token_eos);
|
||||
} catch (const std::exception & e) {
|
||||
LOG_ERR("%s: failed to parse the tts chat template (ignoring it): %s\n", __func__, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmpls;
|
||||
}
|
||||
|
||||
@@ -1717,6 +1803,9 @@ common_chat_msg common_chat_peg_parse(const common_peg_arena & src_pars
|
||||
//LOG_DBG("Parsing PEG input with format %s: %s\n", common_chat_format_name(params.format), effective_input.text.c_str());
|
||||
|
||||
common_peg_parse_flags flags = COMMON_PEG_PARSE_FLAG_LENIENT;
|
||||
if (!is_partial) {
|
||||
flags |= COMMON_PEG_PARSE_FLAG_COMPLETE;
|
||||
}
|
||||
if (params.debug) {
|
||||
flags |= COMMON_PEG_PARSE_FLAG_DEBUG;
|
||||
}
|
||||
@@ -1854,12 +1943,13 @@ const common_chat_msg & common_chat_session::finish(const common_chat_input & ch
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<std::string, bool> common_chat_templates_get_caps(const common_chat_templates * chat_templates) {
|
||||
common_json common_chat_templates_get_caps(const common_chat_templates * chat_templates) {
|
||||
GGML_ASSERT(chat_templates != nullptr);
|
||||
GGML_ASSERT(chat_templates->template_default != nullptr);
|
||||
if (chat_templates->template_tool_use != nullptr) {
|
||||
// take the more expressive template when available
|
||||
return chat_templates->template_tool_use->caps.to_map();
|
||||
}
|
||||
return chat_templates->template_default->caps.to_map();
|
||||
// take the more expressive template when available
|
||||
const auto & caps = chat_templates->template_tool_use != nullptr ? chat_templates->template_tool_use->caps
|
||||
: chat_templates->template_default->caps;
|
||||
common_json result = caps.to_map();
|
||||
result["reasoning_effort_levels"] = caps.reasoning_effort_levels;
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-3
@@ -45,6 +45,7 @@ struct common_chat_tool_call {
|
||||
struct common_chat_msg_content_part {
|
||||
std::string type;
|
||||
std::string text;
|
||||
std::string name; // tool_reference: name of a tool from the request tools
|
||||
|
||||
// TODO @ngxson : no known chat templates support reasoning_content in content parts yet
|
||||
// this can be useful for models with interleaved thinking (like Kimi-K2)
|
||||
@@ -52,7 +53,7 @@ struct common_chat_msg_content_part {
|
||||
// std::string reasoning_content;
|
||||
|
||||
bool operator==(const common_chat_msg_content_part & other) const {
|
||||
return type == other.type && text == other.text;
|
||||
return type == other.type && text == other.text && name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -226,6 +227,9 @@ struct common_chat_tool {
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string parameters;
|
||||
|
||||
// withheld from the system prompt, a tool_reference reveals it later in the conversation
|
||||
bool defer_loading = false;
|
||||
};
|
||||
|
||||
enum common_chat_tool_choice {
|
||||
@@ -468,7 +472,7 @@ common_json common_chat_tools_to_json_oaicompat(const std::vector<common_chat_to
|
||||
common_json common_chat_tool_parameters(const common_json & function);
|
||||
|
||||
// get template caps, useful for reporting to server /props endpoint
|
||||
std::map<std::string, bool> common_chat_templates_get_caps(const common_chat_templates * chat_templates);
|
||||
common_json common_chat_templates_get_caps(const common_chat_templates * chat_templates);
|
||||
|
||||
common_chat_prompt common_chat_template_direct_apply(
|
||||
const common_chat_template & tmpl,
|
||||
@@ -487,6 +491,12 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
|
||||
struct common_chat_prompt_preset {
|
||||
std::string system;
|
||||
std::string user;
|
||||
bool has_language = false; // the preset already names the requested language
|
||||
};
|
||||
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates);
|
||||
common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates, const std::string & language = "");
|
||||
|
||||
// prompt of the model's "tts" chat template for the text to speak, empty if the model has none
|
||||
// language and voice are optional, the template defines their defaults
|
||||
std::string common_chat_get_tts_prompt(const common_chat_templates * chat_templates, const std::string & text,
|
||||
const std::string & language = "", const std::string & voice = "");
|
||||
|
||||
+59
-42
@@ -1187,6 +1187,9 @@ static const std::map<common_decision_type, std::string> COMMON_DECISION_TYPE_NA
|
||||
{ COMMON_DECISION_TYPE_LFM2_D1_OMNI, "lfm2-d1-omni" },
|
||||
{ COMMON_DECISION_TYPE_GRANITE_SWITCH, "granite-switch"},
|
||||
{ COMMON_DECISION_TYPE_H2O_LIGHTNING, "h2o-lightning" },
|
||||
{ COMMON_DECISION_TYPE_MINICPM_OCR_JEV, "openbmb-minicpm-ocr-jev" },
|
||||
{ COMMON_DECISION_TYPE_LFM3_D1, "lfm3-d1" },
|
||||
{ COMMON_DECISION_TYPE_LFM3_D1_OMNI, "lfm3-d1-omni" },
|
||||
// for future models, ALWAYS add org name prefix to avoid name collisions, e.g. "orgname-modelname"
|
||||
};
|
||||
|
||||
@@ -1252,44 +1255,50 @@ common_gguf_info common_get_gguf_info(const std::string & fname) {
|
||||
return info;
|
||||
}
|
||||
|
||||
void common_fit_apply(common_params & params, llama_model_params & mparams, llama_context_params & cparams) {
|
||||
if (!params.fit_params) {
|
||||
return;
|
||||
}
|
||||
|
||||
COM_TRC("%s", "fitting params to device memory ...\n");
|
||||
COM_TRC("%s", "(for bugs during this step try to reproduce them with -fit off, or provide --verbose logs if the bug only occurs with -fit on)\n");
|
||||
|
||||
// the draft context is created from the same base params and follows the main context, fit both together
|
||||
const bool has_draft = params.speculative.has_dft();
|
||||
const bool spec_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(),
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
|
||||
|
||||
common_params params_dft = common_base_params_to_speculative(params);
|
||||
|
||||
auto mparams_dft = common_model_params_to_llama(params_dft);
|
||||
auto cparams_dft = common_context_params_to_llama(params_dft);
|
||||
if (spec_mtp) {
|
||||
cparams_dft.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
|
||||
}
|
||||
cparams_dft.n_rs_seq = 0;
|
||||
|
||||
const common_fit_extra_model extra = {
|
||||
/*.path_model =*/ params_dft.model.path.c_str(),
|
||||
/*.mparams =*/ &mparams_dft,
|
||||
/*.cparams =*/ &cparams_dft,
|
||||
/*.shares_model =*/ !has_draft, // an MTP context runs on the weights of the main model
|
||||
};
|
||||
|
||||
common_fit_params(params.model.path.c_str(), &mparams, &cparams,
|
||||
params.tensor_split,
|
||||
params.tensor_buft_overrides.data(),
|
||||
params.fit_params_target.data(),
|
||||
params.fit_params_min_ctx,
|
||||
has_draft || spec_mtp ? &extra : nullptr,
|
||||
params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR);
|
||||
}
|
||||
|
||||
common_init_result::common_init_result(common_params & params, bool model_only) :
|
||||
pimpl(new impl{}) {
|
||||
auto mparams = common_model_params_to_llama(params);
|
||||
auto cparams = common_context_params_to_llama(params);
|
||||
|
||||
if (params.fit_params) {
|
||||
COM_TRC("%s", "fitting params to device memory ...\n");
|
||||
COM_TRC("%s", "(for bugs during this step try to reproduce them with -fit off, or provide --verbose logs if the bug only occurs with -fit on)\n");
|
||||
|
||||
// the draft context is created from the same base params and follows the main context, fit both together
|
||||
const bool has_draft = params.speculative.has_dft();
|
||||
const bool spec_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(),
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
|
||||
|
||||
common_params params_dft = common_base_params_to_speculative(params);
|
||||
|
||||
auto mparams_dft = common_model_params_to_llama(params_dft);
|
||||
auto cparams_dft = common_context_params_to_llama(params_dft);
|
||||
if (spec_mtp) {
|
||||
cparams_dft.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
|
||||
}
|
||||
cparams_dft.n_rs_seq = 0;
|
||||
|
||||
const common_fit_extra_model extra = {
|
||||
/*.path_model =*/ params_dft.model.path.c_str(),
|
||||
/*.mparams =*/ &mparams_dft,
|
||||
/*.cparams =*/ &cparams_dft,
|
||||
/*.shares_model =*/ !has_draft, // an MTP context runs on the weights of the main model
|
||||
};
|
||||
|
||||
common_fit_params(params.model.path.c_str(), &mparams, &cparams,
|
||||
params.tensor_split,
|
||||
params.tensor_buft_overrides.data(),
|
||||
params.fit_params_target.data(),
|
||||
params.fit_params_min_ctx,
|
||||
has_draft || spec_mtp ? &extra : nullptr,
|
||||
params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR);
|
||||
}
|
||||
common_fit_apply(params, mparams, cparams);
|
||||
|
||||
llama_model * model = llama_model_load_from_file(params.model.path.c_str(), mparams);
|
||||
if (model == NULL) {
|
||||
@@ -1307,13 +1316,17 @@ common_init_result::common_init_result(common_params & params, bool model_only)
|
||||
// these decision models return a score for each token via the embeddings output
|
||||
// TODO: maybe improve this in the future
|
||||
const auto decision_type = common_get_decision_type(model);
|
||||
// minicpm-ocr-jev reads its class scores from the pooled output, the pooling type is the one of the GGUF
|
||||
if (decision_type == COMMON_DECISION_TYPE_LAYA || decision_type == COMMON_DECISION_TYPE_KEV || decision_type == COMMON_DECISION_TYPE_CLEF ||
|
||||
decision_type == COMMON_DECISION_TYPE_LFM2_D1_OMNI || decision_type == COMMON_DECISION_TYPE_VLLM_DECISION2) {
|
||||
decision_type == COMMON_DECISION_TYPE_LFM2_D1_OMNI || decision_type == COMMON_DECISION_TYPE_VLLM_DECISION2 ||
|
||||
decision_type == COMMON_DECISION_TYPE_MINICPM_OCR_JEV) {
|
||||
const auto pooling_type = decision_type == COMMON_DECISION_TYPE_MINICPM_OCR_JEV ? LLAMA_POOLING_TYPE_UNSPECIFIED : LLAMA_POOLING_TYPE_NONE;
|
||||
|
||||
params.embedding = true;
|
||||
params.pooling_type = LLAMA_POOLING_TYPE_NONE;
|
||||
params.pooling_type = pooling_type;
|
||||
|
||||
cparams.embeddings = true;
|
||||
cparams.pooling_type = LLAMA_POOLING_TYPE_NONE;
|
||||
cparams.pooling_type = pooling_type;
|
||||
cparams.n_outputs_max = cparams.n_batch;
|
||||
cparams.n_outputs_max_per_seq = 1;
|
||||
|
||||
@@ -1460,7 +1473,7 @@ common_init_result_ptr common_init_from_params(common_params & params, bool mode
|
||||
if (params.control_vector_layer_start <= 0) params.control_vector_layer_start = 1;
|
||||
if (params.control_vector_layer_end <= 0) params.control_vector_layer_end = llama_model_n_layer(model);
|
||||
|
||||
const auto cvec = common_control_vector_load(params.control_vectors);
|
||||
const auto cvec = common_control_vector_load(params.control_vectors, llama_model_n_layer(model));
|
||||
if (cvec.n_embd == -1) {
|
||||
return res;
|
||||
}
|
||||
@@ -1982,7 +1995,7 @@ float common_embd_similarity_cos(const float * embd1, const float * embd2, int n
|
||||
// Control vector utils
|
||||
//
|
||||
|
||||
static common_control_vector_data common_control_vector_load_one(const common_control_vector_load_info & load_info) {
|
||||
static common_control_vector_data common_control_vector_load_one(const common_control_vector_load_info & load_info, int32_t n_layer) {
|
||||
common_control_vector_data result = { -1, {} };
|
||||
|
||||
ggml_context * ctx = nullptr;
|
||||
@@ -2023,6 +2036,10 @@ static common_control_vector_data common_control_vector_load_one(const common_co
|
||||
COM_ERR("invalid (zero) direction tensor layer index in %s\n", load_info.fname.c_str());
|
||||
result.n_embd = -1;
|
||||
break;
|
||||
} else if (layer_idx > n_layer) {
|
||||
COM_ERR("direction tensor layer index %d exceeds the model layer count %d in %s\n", layer_idx, n_layer, load_info.fname.c_str());
|
||||
result.n_embd = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
struct ggml_tensor * tensor = ggml_get_tensor(ctx, name.c_str());
|
||||
@@ -2046,10 +2063,10 @@ static common_control_vector_data common_control_vector_load_one(const common_co
|
||||
}
|
||||
|
||||
// extend if necessary - do not store data for layer 0 (it's not used)
|
||||
result.data.resize(std::max(result.data.size(), static_cast<size_t>(result.n_embd * layer_idx)), 0.0f);
|
||||
result.data.resize(std::max(result.data.size(), (size_t) result.n_embd * layer_idx), 0.0f);
|
||||
|
||||
const float * src = (const float *) tensor->data;
|
||||
float * dst = result.data.data() + result.n_embd * (layer_idx - 1); // layer 1 at [0]
|
||||
float * dst = result.data.data() + (size_t) result.n_embd * (layer_idx - 1); // layer 1 at [0]
|
||||
for (int j = 0; j < result.n_embd; j++) {
|
||||
dst[j] += src[j] * load_info.strength; // allows multiple directions for same layer in same file
|
||||
}
|
||||
@@ -2067,11 +2084,11 @@ static common_control_vector_data common_control_vector_load_one(const common_co
|
||||
return result;
|
||||
}
|
||||
|
||||
common_control_vector_data common_control_vector_load(const std::vector<common_control_vector_load_info> & load_infos) {
|
||||
common_control_vector_data common_control_vector_load(const std::vector<common_control_vector_load_info> & load_infos, int32_t n_layer) {
|
||||
common_control_vector_data result = { -1, {} };
|
||||
|
||||
for (const auto & info : load_infos) {
|
||||
auto cur = common_control_vector_load_one(info);
|
||||
auto cur = common_control_vector_load_one(info, n_layer);
|
||||
|
||||
if (cur.n_embd == -1) {
|
||||
result.n_embd = -1;
|
||||
|
||||
+10
-1
@@ -701,6 +701,7 @@ struct common_params {
|
||||
std::string models_dir = ""; // directory containing models for the router server
|
||||
std::string models_preset = ""; // directory containing model presets for the router server
|
||||
int models_max = 4; // maximum number of models to load simultaneously
|
||||
int models_memory_margin = 0; // MiB of device memory to keep free when loading models (0 = no memory accounting)
|
||||
bool models_autoload = true; // automatically load models when requested via the router server
|
||||
std::string models_preset_hf = ""; // show a warning about remote presets on router loaded (if not empty)
|
||||
|
||||
@@ -771,6 +772,7 @@ struct common_params {
|
||||
|
||||
// TTS params
|
||||
std::string tts_lang = "";
|
||||
std::string tts_voice = "";
|
||||
std::string tts_speaker_file = "";
|
||||
|
||||
bool is_gen_docs = false; // whether we are running inside llama-gen-docs
|
||||
@@ -978,6 +980,9 @@ enum common_decision_type {
|
||||
COMMON_DECISION_TYPE_LFM2_D1_OMNI, // same as laya, other prompt layout
|
||||
COMMON_DECISION_TYPE_GRANITE_SWITCH, // same as openjev, run by the decision aLoRA of granite switch
|
||||
COMMON_DECISION_TYPE_H2O_LIGHTNING, // same as openjev, the labels are read after an answer prefill
|
||||
COMMON_DECISION_TYPE_MINICPM_OCR_JEV, // one score per class, the pooled embeddings output of the last token
|
||||
COMMON_DECISION_TYPE_LFM3_D1, // same as lfm2-d1, non-causal, label codes A..Z then AA, AB..
|
||||
COMMON_DECISION_TYPE_LFM3_D1_OMNI, // same as lfm3-d1
|
||||
COMMON_DECISION_TYPE_UNKNOWN, // a decision model of a type that is not supported
|
||||
};
|
||||
|
||||
@@ -1017,6 +1022,9 @@ common_init_result_ptr common_init_from_params(common_params & params, bool mode
|
||||
struct llama_model_params common_model_params_to_llama ( common_params & params);
|
||||
struct llama_context_params common_context_params_to_llama(const common_params & params);
|
||||
|
||||
// apply --fit to mparams and cparams, no-op if params.fit_params is false
|
||||
void common_fit_apply(common_params & params, struct llama_model_params & mparams, struct llama_context_params & cparams);
|
||||
|
||||
// clear LoRA adapters from context, then apply new list of adapters
|
||||
void common_set_adapter_lora(struct llama_context * ctx, std::vector<common_adapter_lora_info> & lora);
|
||||
|
||||
@@ -1229,8 +1237,9 @@ struct common_control_vector_load_info {
|
||||
};
|
||||
|
||||
// Load control vectors, scale each by strength, and add them together.
|
||||
// Direction layer indices must be in [1, n_layer].
|
||||
// On error, returns {-1, empty}
|
||||
common_control_vector_data common_control_vector_load(const std::vector<common_control_vector_load_info> & load_infos);
|
||||
common_control_vector_data common_control_vector_load(const std::vector<common_control_vector_load_info> & load_infos, int32_t n_layer);
|
||||
|
||||
//
|
||||
// Split utils
|
||||
|
||||
+197
-103
@@ -1,6 +1,8 @@
|
||||
#include "fit.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "json.h"
|
||||
#include "llama-cpp.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "../src/llama-ext.h"
|
||||
@@ -27,6 +29,32 @@ class common_params_fit_exception : public std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
// RAII: send llama logs below min_level to the debug log
|
||||
struct common_fit_log_scope {
|
||||
common_fit_log_scope(ggml_log_level min_level) : min_level(min_level) {
|
||||
llama_log_get(&original_logger.callback, &original_logger.user_data);
|
||||
llama_log_set([](ggml_log_level level, const char * text, void * user_data) {
|
||||
const common_fit_log_scope * self = (const common_fit_log_scope *) user_data;
|
||||
const ggml_log_level level_eff = level >= self->min_level ? level : GGML_LOG_LEVEL_DEBUG;
|
||||
self->original_logger.callback(level_eff, text, self->original_logger.user_data);
|
||||
}, this);
|
||||
}
|
||||
|
||||
~common_fit_log_scope() {
|
||||
llama_log_set(original_logger.callback, original_logger.user_data);
|
||||
}
|
||||
|
||||
common_fit_log_scope(const common_fit_log_scope &) = delete;
|
||||
common_fit_log_scope & operator=(const common_fit_log_scope &) = delete;
|
||||
|
||||
private:
|
||||
struct {
|
||||
ggml_log_callback callback;
|
||||
void * user_data;
|
||||
} original_logger;
|
||||
ggml_log_level min_level;
|
||||
};
|
||||
|
||||
static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
@@ -36,22 +64,7 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
uint32_t & hp_n_ctx_train,
|
||||
uint32_t & hp_n_expert,
|
||||
ggml_log_level log_level) {
|
||||
struct user_data_t {
|
||||
struct {
|
||||
ggml_log_callback callback;
|
||||
void * user_data;
|
||||
} original_logger;
|
||||
ggml_log_level min_level; // prints below this log level go to debug log
|
||||
};
|
||||
user_data_t ud;
|
||||
llama_log_get(&ud.original_logger.callback, &ud.original_logger.user_data);
|
||||
ud.min_level = log_level;
|
||||
|
||||
llama_log_set([](ggml_log_level level, const char * text, void * user_data) {
|
||||
const user_data_t * ud = (const user_data_t *) user_data;
|
||||
const ggml_log_level level_eff = level >= ud->min_level ? level : GGML_LOG_LEVEL_DEBUG;
|
||||
ud->original_logger.callback(level_eff, text, ud->original_logger.user_data);
|
||||
}, &ud);
|
||||
common_fit_log_scope log_scope(log_level);
|
||||
|
||||
llama_model_params mparams_copy = *mparams;
|
||||
mparams_copy.no_alloc = true;
|
||||
@@ -59,14 +72,12 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
|
||||
llama_model * model = llama_model_load_from_file(path_model, mparams_copy);
|
||||
if (model == nullptr) {
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
throw std::runtime_error("failed to load model");
|
||||
}
|
||||
|
||||
llama_context * ctx = llama_init_from_model(model, *cparams);
|
||||
if (ctx == nullptr) {
|
||||
llama_model_free(model);
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
throw std::runtime_error("failed to create llama_context from model");
|
||||
}
|
||||
|
||||
@@ -148,11 +159,81 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
|
||||
llama_free(ctx);
|
||||
llama_model_free(model);
|
||||
llama_log_set(ud.original_logger.callback, ud.original_logger.user_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<common_memory_breakdown_row> common_fit_measure(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
const llama_context_params * cparams,
|
||||
uint32_t * n_ctx_train,
|
||||
uint32_t * n_ctx,
|
||||
ggml_log_level log_level) {
|
||||
common_fit_log_scope log_scope(log_level);
|
||||
|
||||
llama_model_params mparams_copy = *mparams;
|
||||
mparams_copy.no_alloc = true;
|
||||
mparams_copy.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
|
||||
llama_model_ptr model(llama_model_load_from_file(path_model, mparams_copy));
|
||||
if (!model) {
|
||||
throw std::runtime_error("failed to load model");
|
||||
}
|
||||
|
||||
llama_context_ptr ctx(llama_init_from_model(model.get(), *cparams));
|
||||
if (!ctx) {
|
||||
throw std::runtime_error("failed to create llama_context from model");
|
||||
}
|
||||
|
||||
if (n_ctx_train) {
|
||||
*n_ctx_train = llama_model_n_ctx_train(model.get());
|
||||
}
|
||||
if (n_ctx) {
|
||||
*n_ctx = llama_n_ctx(ctx.get());
|
||||
}
|
||||
|
||||
return common_memory_breakdown_rows(ctx.get());
|
||||
}
|
||||
|
||||
common_memory_reports common_measure_memory(common_params & params) {
|
||||
const ggml_log_level log_level = params.verbosity >= LOG_LEVEL_DEBUG ? GGML_LOG_LEVEL_DEBUG : GGML_LOG_LEVEL_ERROR;
|
||||
const char * path_model = params.model.path.c_str();
|
||||
|
||||
common_memory_reports res;
|
||||
|
||||
auto mparams = common_model_params_to_llama(params);
|
||||
auto cparams = common_context_params_to_llama(params);
|
||||
|
||||
// max: what --fit picks now, no-op with -fit off
|
||||
{
|
||||
auto mparams_fit = mparams;
|
||||
auto cparams_fit = cparams;
|
||||
common_fit_apply(params, mparams_fit, cparams_fit);
|
||||
res.info_max = common_fit_measure(path_model, &mparams_fit, &cparams_fit, &res.n_ctx_train, &res.n_ctx_max, log_level);
|
||||
}
|
||||
|
||||
// min: the smallest context --fit may pick (--fit only changes n_ctx when it is 0)
|
||||
uint32_t n_ctx_min = cparams.n_ctx;
|
||||
if (n_ctx_min == 0 && params.fit_params) {
|
||||
const uint32_t n_streams = cparams.kv_unified ? 1 : std::max<uint32_t>(1, cparams.n_seq_max);
|
||||
// -c 0 sets fit_params_min_ctx to UINT32_MAX, cap at n_ctx_train like --fit
|
||||
n_ctx_min = (uint32_t) std::min<uint64_t>(
|
||||
uint64_t(params.fit_params_min_ctx) * n_streams,
|
||||
uint64_t(res.n_ctx_train) * n_streams);
|
||||
}
|
||||
if (n_ctx_min == 0 || n_ctx_min >= res.n_ctx_max) {
|
||||
// nothing to reduce, or -fit off with the trained context
|
||||
res.n_ctx_min = res.n_ctx_max;
|
||||
res.info_min = res.info_max;
|
||||
} else {
|
||||
cparams.n_ctx = n_ctx_min;
|
||||
res.info_min = common_fit_measure(path_model, &mparams, &cparams, nullptr, &res.n_ctx_min, log_level);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
common_device_memory_data_vec common_get_device_memory_data(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
@@ -903,8 +984,7 @@ enum common_params_fit_status common_fit_params(
|
||||
return status;
|
||||
}
|
||||
|
||||
void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
//const auto & devices = ctx->get_model().devices;
|
||||
std::vector<common_memory_breakdown_row> common_memory_breakdown_rows(const struct llama_context * ctx) {
|
||||
const auto * model = llama_get_model(ctx);
|
||||
|
||||
std::vector<ggml_backend_dev_t> devices;
|
||||
@@ -914,18 +994,6 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
|
||||
llama_memory_breakdown memory_breakdown = llama_get_memory_breakdown(ctx);
|
||||
|
||||
std::vector<std::array<std::string, 9>> table_data;
|
||||
table_data.reserve(devices.size());
|
||||
|
||||
// same data as the table below, for --log-jsonl consumers
|
||||
common_json rows = common_json::array();
|
||||
const std::string template_header = "%s: | %s | %s %s %s %s %s %s %s |\n";
|
||||
const std::string template_gpu = "%s: | %s | %s = %s + (%s = %s + %s + %s) + %s |\n";
|
||||
const std::string template_other = "%s: | %s | %s %s %s = %s + %s + %s %s |\n";
|
||||
|
||||
table_data.push_back({template_header, "memory breakdown [MiB]", "total", "free", "self", "model", "context", "compute", "unaccounted"});
|
||||
|
||||
constexpr size_t MiB = 1024 * 1024;
|
||||
const std::vector<std::string> desc_prefixes_strip = {"NVIDIA ", "GeForce ", "Tesla ", "AMD ", "Radeon ", "Instinct "};
|
||||
|
||||
// track seen buffer types to avoid double counting:
|
||||
@@ -964,104 +1032,130 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// print memory breakdown for each device:
|
||||
std::vector<common_memory_breakdown_row> rows;
|
||||
rows.reserve(devices.size() + 1 + memory_breakdown.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); i++) {
|
||||
ggml_backend_dev_t dev = devices[i];
|
||||
llama_memory_breakdown_data mb = mb_dev[i];
|
||||
|
||||
const std::string name = ggml_backend_dev_name(dev);
|
||||
std::string desc = ggml_backend_dev_description(dev);
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::DEVICE;
|
||||
row.name = ggml_backend_dev_name(dev);
|
||||
row.description = ggml_backend_dev_description(dev);
|
||||
for (const std::string & prefix : desc_prefixes_strip) {
|
||||
if (desc.length() >= prefix.length() && desc.substr(0, prefix.length()) == prefix) {
|
||||
desc = desc.substr(prefix.length());
|
||||
if (row.description.length() >= prefix.length() && row.description.substr(0, prefix.length()) == prefix) {
|
||||
row.description = row.description.substr(prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
size_t free, total;
|
||||
ggml_backend_dev_memory(dev, &free, &total);
|
||||
|
||||
const size_t self = mb.model + mb.context + mb.compute;
|
||||
const int64_t unaccounted = static_cast<int64_t>(total) - static_cast<int64_t>(free) - static_cast<int64_t>(self);
|
||||
|
||||
table_data.push_back({
|
||||
template_gpu,
|
||||
" - " + name + " (" + desc + ")",
|
||||
std::to_string(total / MiB),
|
||||
std::to_string(free / MiB),
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb.model / MiB),
|
||||
std::to_string(mb.context / MiB),
|
||||
std::to_string(mb.compute / MiB),
|
||||
std::to_string(unaccounted / static_cast<int64_t>(MiB))});
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "device"},
|
||||
{"name", name},
|
||||
{"description", desc},
|
||||
{"total", total / MiB},
|
||||
{"free", free / MiB},
|
||||
{"self", self / MiB},
|
||||
{"model", mb.model / MiB},
|
||||
{"context", mb.context / MiB},
|
||||
{"compute", mb.compute / MiB},
|
||||
{"unaccounted", unaccounted / static_cast<int64_t>(MiB)},
|
||||
});
|
||||
row.total = total;
|
||||
row.free = free;
|
||||
row.model = mb_dev[i].model;
|
||||
row.context = mb_dev[i].context;
|
||||
row.compute = mb_dev[i].compute;
|
||||
rows.push_back(std::move(row));
|
||||
}
|
||||
|
||||
// print memory breakdown for host:
|
||||
{
|
||||
const size_t self = mb_host.model + mb_host.context + mb_host.compute;
|
||||
table_data.push_back({
|
||||
template_other,
|
||||
" - Host",
|
||||
"", // total
|
||||
"", // free
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb_host.model / MiB),
|
||||
std::to_string(mb_host.context / MiB),
|
||||
std::to_string(mb_host.compute / MiB),
|
||||
""}); // unaccounted
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "host"},
|
||||
{"name", "Host"},
|
||||
{"self", self / MiB},
|
||||
{"model", mb_host.model / MiB},
|
||||
{"context", mb_host.context / MiB},
|
||||
{"compute", mb_host.compute / MiB},
|
||||
});
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::HOST;
|
||||
row.name = "Host";
|
||||
row.model = mb_host.model;
|
||||
row.context = mb_host.context;
|
||||
row.compute = mb_host.compute;
|
||||
rows.push_back(std::move(row));
|
||||
}
|
||||
|
||||
// print memory breakdown for all remaining buffer types:
|
||||
// all remaining buffer types:
|
||||
for (const auto & buft_mb : memory_breakdown) {
|
||||
ggml_backend_buffer_type_t buft = buft_mb.first;
|
||||
const llama_memory_breakdown_data & mb = buft_mb.second;
|
||||
if (seen_buffer_types.count(buft) == 1) {
|
||||
continue;
|
||||
}
|
||||
const std::string name = ggml_backend_buft_name(buft);
|
||||
const size_t self = mb.model + mb.context + mb.compute;
|
||||
common_memory_breakdown_row row;
|
||||
row.kind = common_memory_breakdown_row::BUFFER_TYPE;
|
||||
row.name = ggml_backend_buft_name(buft);
|
||||
row.model = mb.model;
|
||||
row.context = mb.context;
|
||||
row.compute = mb.compute;
|
||||
rows.push_back(std::move(row));
|
||||
|
||||
seen_buffer_types.insert(buft);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
const std::vector<common_memory_breakdown_row> rows = common_memory_breakdown_rows(ctx);
|
||||
|
||||
std::vector<std::array<std::string, 9>> table_data;
|
||||
table_data.reserve(rows.size() + 1);
|
||||
|
||||
// same data as the table below, for --log-jsonl consumers
|
||||
common_json json_rows = common_json::array();
|
||||
const std::string template_header = "%s: | %s | %s %s %s %s %s %s %s |\n";
|
||||
const std::string template_gpu = "%s: | %s | %s = %s + (%s = %s + %s + %s) + %s |\n";
|
||||
const std::string template_other = "%s: | %s | %s %s %s = %s + %s + %s %s |\n";
|
||||
|
||||
table_data.push_back({template_header, "memory breakdown [MiB]", "total", "free", "self", "model", "context", "compute", "unaccounted"});
|
||||
|
||||
constexpr size_t MiB = 1024 * 1024;
|
||||
|
||||
for (const auto & row : rows) {
|
||||
const size_t self = row.model + row.context + row.compute;
|
||||
|
||||
if (row.kind == common_memory_breakdown_row::DEVICE) {
|
||||
const int64_t unaccounted = row.total - row.free - static_cast<int64_t>(self);
|
||||
|
||||
table_data.push_back({
|
||||
template_gpu,
|
||||
" - " + row.name + " (" + row.description + ")",
|
||||
std::to_string(row.total / MiB),
|
||||
std::to_string(row.free / MiB),
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(row.model / MiB),
|
||||
std::to_string(row.context / MiB),
|
||||
std::to_string(row.compute / MiB),
|
||||
std::to_string(unaccounted / static_cast<int64_t>(MiB))});
|
||||
|
||||
json_rows.push_back({
|
||||
{"kind", "device"},
|
||||
{"name", row.name},
|
||||
{"description", row.description},
|
||||
{"total", row.total / MiB},
|
||||
{"free", row.free / MiB},
|
||||
{"self", self / MiB},
|
||||
{"model", row.model / MiB},
|
||||
{"context", row.context / MiB},
|
||||
{"compute", row.compute / MiB},
|
||||
{"unaccounted", unaccounted / static_cast<int64_t>(MiB)},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
table_data.push_back({
|
||||
template_other,
|
||||
" - " + name,
|
||||
" - " + row.name,
|
||||
"", // total
|
||||
"", // free
|
||||
std::to_string(self / MiB),
|
||||
std::to_string(mb.model / MiB),
|
||||
std::to_string(mb.context / MiB),
|
||||
std::to_string(mb.compute / MiB),
|
||||
std::to_string(row.model / MiB),
|
||||
std::to_string(row.context / MiB),
|
||||
std::to_string(row.compute / MiB),
|
||||
""}); // unaccounted
|
||||
|
||||
rows.push_back({
|
||||
{"kind", "buffer_type"},
|
||||
{"name", name},
|
||||
json_rows.push_back({
|
||||
{"kind", row.kind == common_memory_breakdown_row::HOST ? "host" : "buffer_type"},
|
||||
{"name", row.name},
|
||||
{"self", self / MiB},
|
||||
{"model", mb.model / MiB},
|
||||
{"context", mb.context / MiB},
|
||||
{"compute", mb.compute / MiB},
|
||||
{"model", row.model / MiB},
|
||||
{"context", row.context / MiB},
|
||||
{"compute", row.compute / MiB},
|
||||
});
|
||||
|
||||
seen_buffer_types.insert(buft);
|
||||
}
|
||||
|
||||
for (size_t j = 1; j < table_data[0].size(); j++) {
|
||||
@@ -1081,7 +1175,7 @@ void common_memory_breakdown_print(const struct llama_context * ctx) {
|
||||
|
||||
LOG_JSON("fit_memory_breakdown", common_json({
|
||||
{"unit", "MiB"},
|
||||
{"rows", rows},
|
||||
{"rows", json_rows},
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ggml.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum common_params_fit_status {
|
||||
@@ -45,6 +46,47 @@ void common_fit_print(
|
||||
|
||||
void common_memory_breakdown_print(const llama_context * ctx);
|
||||
|
||||
// one row of the memory breakdown: a device, the host or another buffer type
|
||||
struct common_memory_breakdown_row {
|
||||
enum kind_t { DEVICE, HOST, BUFFER_TYPE };
|
||||
|
||||
kind_t kind = DEVICE;
|
||||
std::string name; // device or buffer type name, "Host" for the host row
|
||||
std::string description; // device description, empty otherwise
|
||||
int64_t total = 0; // device memory in bytes, 0 unless kind == DEVICE
|
||||
int64_t free = 0; // free device memory in bytes, 0 unless kind == DEVICE
|
||||
size_t model = 0; // bytes held by this context, by kind of buffer
|
||||
size_t context = 0;
|
||||
size_t compute = 0;
|
||||
};
|
||||
|
||||
// memory a model needs at the min and max context --fit may pick
|
||||
struct common_memory_reports {
|
||||
uint32_t n_ctx_train = 0;
|
||||
uint32_t n_ctx_max = 0; // the largest context that fits the free device memory right now
|
||||
uint32_t n_ctx_min = 0; // --fit-ctx (the smallest context --fit may pick), with the placement set in params
|
||||
std::vector<common_memory_breakdown_row> info_max; // breakdown at n_ctx_max
|
||||
std::vector<common_memory_breakdown_row> info_min; // breakdown at n_ctx_min
|
||||
};
|
||||
|
||||
// rows of common_memory_breakdown_print: model devices first, then host
|
||||
std::vector<common_memory_breakdown_row> common_memory_breakdown_rows(const llama_context * ctx);
|
||||
|
||||
// load model + context with no_alloc and return the memory breakdown, params are used as-is
|
||||
// n_ctx_train and n_ctx are optional outputs; throws on failure
|
||||
std::vector<common_memory_breakdown_row> common_fit_measure(
|
||||
const char * path_model,
|
||||
const llama_model_params * mparams,
|
||||
const llama_context_params * cparams,
|
||||
uint32_t * n_ctx_train,
|
||||
uint32_t * n_ctx,
|
||||
ggml_log_level log_level);
|
||||
|
||||
struct common_params;
|
||||
|
||||
// measure min and max without allocating, params may be modified as with --fit; throws on failure
|
||||
common_memory_reports common_measure_memory(common_params & params);
|
||||
|
||||
struct common_device_memory_data {
|
||||
int64_t total;
|
||||
int64_t free;
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
// we can remove it in the future when we figure out a better way to define inputs using jinja::value
|
||||
#include "json.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#define FILENAME "jinja-caps"
|
||||
@@ -184,6 +186,10 @@ caps caps_get(jinja::program & prog) {
|
||||
{
|
||||
{"role", "user"},
|
||||
{"content", json::array({
|
||||
{
|
||||
{"type", "text"},
|
||||
{"text", content_marker}
|
||||
}
|
||||
})}
|
||||
}
|
||||
});
|
||||
@@ -584,6 +590,58 @@ caps caps_get(jinja::program & prog) {
|
||||
}
|
||||
);
|
||||
|
||||
if (result.supports_reasoning_effort) {
|
||||
JJ_DEBUG("%s\n", ">>> Running capability check: reasoning effort levels");
|
||||
|
||||
static const std::vector<std::string> candidates = { "minimal", "low", "medium", "high", "xhigh", "max" };
|
||||
|
||||
// renders a single user turn with the given effort, nothing when the template raises
|
||||
const auto render = [&](const std::string & effort) {
|
||||
std::optional<std::string> rendered;
|
||||
caps_try_execute(
|
||||
prog,
|
||||
[&]() {
|
||||
// messages
|
||||
return json::array({
|
||||
{
|
||||
{"role", "user"},
|
||||
{"content", "User message"}
|
||||
},
|
||||
});
|
||||
},
|
||||
[&](context & ctx) {
|
||||
ctx.set_val("enable_thinking", mk_val<value_bool>(true));
|
||||
caps_apply_reasoning_effort(ctx, effort);
|
||||
},
|
||||
nullptr, // tools_fn
|
||||
[&](context &, bool success, value &, value &, const std::string & output) {
|
||||
if (success) {
|
||||
rendered = output;
|
||||
}
|
||||
}
|
||||
);
|
||||
return rendered;
|
||||
};
|
||||
|
||||
// an unknown level shows the render of the template fallback, a level that renders the same is not honored
|
||||
// levels sharing a render are aliases and the highest one is kept
|
||||
const auto unknown = render("REASONING_EFFORT_UNKNOWN");
|
||||
std::vector<std::string> renders;
|
||||
for (const auto & level : candidates) {
|
||||
const auto rendered = render(level);
|
||||
if (!rendered || rendered == unknown) {
|
||||
continue;
|
||||
}
|
||||
const auto it = std::find(renders.begin(), renders.end(), *rendered);
|
||||
if (it != renders.end()) {
|
||||
result.reasoning_effort_levels[it - renders.begin()] = level;
|
||||
} else {
|
||||
renders.push_back(*rendered);
|
||||
result.reasoning_effort_levels.push_back(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JJ_DEBUG("%s\n", result.to_string().c_str());
|
||||
|
||||
return result;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace jinja {
|
||||
|
||||
@@ -19,6 +20,9 @@ struct caps {
|
||||
// supports reasoning effort levels
|
||||
bool supports_reasoning_effort = false;
|
||||
|
||||
// reasoning effort levels the template renders distinctly, from lowest to highest
|
||||
std::vector<std::string> reasoning_effort_levels;
|
||||
|
||||
// one of the 2 content capabilities must be true
|
||||
bool supports_string_content = true;
|
||||
bool supports_typed_content = false;
|
||||
|
||||
@@ -76,9 +76,10 @@ common_chat_params common_chat_params_init_qwen3_coder(const common_chat_templat
|
||||
|
||||
auto reasoning = p.eps();
|
||||
if (supports_reasoning && extract_reasoning) {
|
||||
// The template strips the newline before </think>, so leave it out of the reasoning
|
||||
reasoning = p.optional("<think>" + p.space() +
|
||||
p.reasoning(p.until_one_of({ "</think>", "<tool_call>" })) +
|
||||
(p.literal("</think>") | p.peek(p.literal("<tool_call>"))));
|
||||
p.reasoning(p.until_one_of({ "\n</think>", "</think>", "<tool_call>" })) +
|
||||
(p.literal("\n</think>") | p.literal("</think>") | p.peek(p.literal("<tool_call>"))));
|
||||
}
|
||||
|
||||
// Response format parser
|
||||
@@ -166,8 +167,15 @@ common_chat_params common_chat_params_init_qwen3_coder(const common_chat_templat
|
||||
auto calls = inputs.parallel_tool_calls ? tool_call_first + p.zero_or_more(tool_call) : tool_call_first;
|
||||
auto tool_calls = p.trigger_rule("tool-call-root", p.repeat(calls, min_calls, 1));
|
||||
|
||||
// The templates separate content from the tool calls with one or two newlines,
|
||||
// stop the content before them
|
||||
std::vector<std::string> content_ends;
|
||||
for (const auto & start : tool_call_starts) {
|
||||
content_ends.insert(content_ends.end(), { "\n\n" + start, "\n" + start, start });
|
||||
}
|
||||
|
||||
return generation_prompt +
|
||||
(reasoning << p.content(p.until_one_of(tool_call_starts)) << tool_calls);
|
||||
(reasoning << p.content(p.until_one_of(content_ends)) << tool_calls);
|
||||
}
|
||||
|
||||
// Content only parser
|
||||
|
||||
@@ -721,8 +721,9 @@ struct parser_executor {
|
||||
return common_peg_parse_result(COMMON_PEG_PARSE_RESULT_SUCCESS, start_pos, pos, {}, std::move(invalid_utf8));
|
||||
}
|
||||
|
||||
if (match == common_trie::PARTIAL_MATCH) {
|
||||
// Found a partial match extending to end of input, return everything before it
|
||||
if (match == common_trie::PARTIAL_MATCH && !ctx.is_complete()) {
|
||||
// Found a partial match extending to end of input, return everything before it.
|
||||
// On a complete input the delimiter can no longer form, so the bytes belong to the text
|
||||
return common_peg_parse_result(COMMON_PEG_PARSE_RESULT_SUCCESS, start_pos, pos, {}, std::move(invalid_utf8));
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -162,9 +162,10 @@ struct common_peg_parse_result {
|
||||
};
|
||||
|
||||
enum common_peg_parse_flags {
|
||||
COMMON_PEG_PARSE_FLAG_NONE = 0,
|
||||
COMMON_PEG_PARSE_FLAG_LENIENT = 1 << 0,
|
||||
COMMON_PEG_PARSE_FLAG_DEBUG = 1 << 1,
|
||||
COMMON_PEG_PARSE_FLAG_NONE = 0,
|
||||
COMMON_PEG_PARSE_FLAG_LENIENT = 1 << 0,
|
||||
COMMON_PEG_PARSE_FLAG_DEBUG = 1 << 1,
|
||||
COMMON_PEG_PARSE_FLAG_COMPLETE = 1 << 2,
|
||||
};
|
||||
|
||||
inline common_peg_parse_flags operator|(common_peg_parse_flags a, common_peg_parse_flags b) {
|
||||
@@ -209,6 +210,7 @@ struct common_peg_parse_context {
|
||||
|
||||
bool is_lenient() const { return flags & COMMON_PEG_PARSE_FLAG_LENIENT; }
|
||||
bool is_debug() const { return flags & COMMON_PEG_PARSE_FLAG_DEBUG; }
|
||||
bool is_complete() const { return flags & COMMON_PEG_PARSE_FLAG_COMPLETE; }
|
||||
};
|
||||
|
||||
class common_peg_arena;
|
||||
|
||||
@@ -172,6 +172,10 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"D1OmniModel": "lfm2",
|
||||
"Lfm2Model": "lfm2",
|
||||
"Lfm2MoeForCausalLM": "lfm2",
|
||||
"Lfm3D1Model": "lfm3",
|
||||
"Lfm3ForCausalLM": "lfm3",
|
||||
"Lfm3OmniForConditionalGeneration": "lfm3",
|
||||
"Lfm3VlForConditionalGeneration": "lfm3",
|
||||
"Llama4ForCausalLM": "llama",
|
||||
"Llama4ForConditionalGeneration": "llama",
|
||||
"LlamaBidirectionalModel": "llama",
|
||||
@@ -197,6 +201,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MiniCPMForCausalLM": "minicpm",
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMOCRForSequenceClassification": "qwen",
|
||||
"MiniCPMOcrForConditionalGeneration": "minicpm",
|
||||
"MiniMaxText01ForCausalLM": "minimax",
|
||||
"MiniMaxM1ForCausalLM": "minimax",
|
||||
@@ -357,6 +362,9 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"Lfm2AudioForConditionalGeneration": "lfm2",
|
||||
"D1OmniModel": "lfm2",
|
||||
"Lfm2VlForConditionalGeneration": "lfm2",
|
||||
"Lfm3AudioForConditionalGeneration": "lfm3",
|
||||
"Lfm3OmniForConditionalGeneration": "lfm3",
|
||||
"Lfm3VlForConditionalGeneration": "lfm3",
|
||||
"LightOnOCRForConditionalGeneration": "lighton_ocr",
|
||||
"Llama4ForConditionalGeneration": "llama4",
|
||||
"LlavaForConditionalGeneration": "llava",
|
||||
@@ -365,6 +373,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"MiniMaxM3SparseForConditionalGeneration": "minimax",
|
||||
"MiniCPMV4_6ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMV4_7ForConditionalGeneration": "minicpm",
|
||||
"MiniCPMOCRForSequenceClassification": "qwen3vl",
|
||||
"MiniCPMOcrForConditionalGeneration": "minicpm",
|
||||
"Mistral3ForConditionalGeneration": "llava",
|
||||
"Mistral3Model": "llava",
|
||||
|
||||
+21
-14
@@ -15,25 +15,30 @@ from .base import MmprojModel, ModelBase, TextModel, gguf, jinja_str_or_json, lo
|
||||
from .gemma import ConformerAudioModel
|
||||
|
||||
|
||||
def lfm2_feed_forward_length(hparams: dict[str, Any]) -> int:
|
||||
ff_dim = hparams.get("block_ff_dim", hparams.get("intermediate_size"))
|
||||
assert ff_dim is not None
|
||||
auto_adjust_ff_dim = hparams["block_auto_adjust_ff_dim"]
|
||||
ffn_dim_multiplier = hparams["block_ffn_dim_multiplier"]
|
||||
multiple_of = hparams["block_multiple_of"]
|
||||
|
||||
if auto_adjust_ff_dim:
|
||||
ff_dim = int(2 * ff_dim / 3)
|
||||
# custom dim factor multiplier
|
||||
if ffn_dim_multiplier is not None:
|
||||
ff_dim = int(ffn_dim_multiplier * ff_dim)
|
||||
ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of)
|
||||
|
||||
return ff_dim
|
||||
|
||||
|
||||
@ModelBase.register("Lfm2ForCausalLM", "LFM2ForCausalLM")
|
||||
@ModelBase.example("LiquidAI/LFM2-1.2B", "LiquidAI/LFM2.5-350M")
|
||||
class LFM2Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.LFM2
|
||||
|
||||
def _add_feed_forward_length(self):
|
||||
ff_dim = self.find_hparam(["block_ff_dim", "intermediate_size"])
|
||||
auto_adjust_ff_dim = self.hparams["block_auto_adjust_ff_dim"]
|
||||
ffn_dim_multiplier = self.hparams["block_ffn_dim_multiplier"]
|
||||
multiple_of = self.hparams["block_multiple_of"]
|
||||
|
||||
if auto_adjust_ff_dim:
|
||||
ff_dim = int(2 * ff_dim / 3)
|
||||
# custom dim factor multiplier
|
||||
if ffn_dim_multiplier is not None:
|
||||
ff_dim = int(ffn_dim_multiplier * ff_dim)
|
||||
ff_dim = multiple_of * ((ff_dim + multiple_of - 1) // multiple_of)
|
||||
|
||||
self.gguf_writer.add_feed_forward_length(ff_dim)
|
||||
self.gguf_writer.add_feed_forward_length(lfm2_feed_forward_length(self.hparams))
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
# set num_key_value_heads only for attention layers
|
||||
@@ -72,7 +77,9 @@ def _is_d1_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
return json.load(f).get("auto_map", {}).get("AutoModel", "").endswith(".D1Model")
|
||||
config = json.load(f)
|
||||
# d1 on LFM3 is converted by conversion/lfm3.py
|
||||
return config.get("auto_map", {}).get("AutoModel", "").endswith(".D1Model") and not config.get("model_type", "").startswith("lfm3")
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_d1_checkpoint)
|
||||
|
||||
@@ -0,0 +1,632 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
import torch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import MmprojModel, ModelBase, TextModel, gguf, logger
|
||||
from .lfm2 import D1Model, lfm2_feed_forward_length
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3ForCausalLM", "Lfm3VlForConditionalGeneration", "Lfm3OmniForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-1.7B", "LiquidAI/LFM3-1.7B-Base")
|
||||
class LFM3Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.LFM3
|
||||
|
||||
# LFM3-Audio/Omni: encoder, projector and audio-output heads live next to the text model
|
||||
_media_prefixes = ("encoder.", "mlp.", "codebook_embedding.", "depth_embeddings.", "depth_linear.", "depthformer.",
|
||||
"mask_embedding", "model.codebook_embedding.", "model.depth_decoder.")
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
|
||||
if name.startswith(cls._media_prefixes):
|
||||
return None
|
||||
|
||||
name = name.removeprefix("lfm.")
|
||||
|
||||
return super().filter_tensors((name, gen))
|
||||
|
||||
# LFM3-Audio live ASR prompt of the model card, the audio follows in the assistant turn
|
||||
_asr_template = (
|
||||
"{{- bos_token -}}"
|
||||
"{{- '<|im_start|>system\\nPerform live ASR.<|im_end|>\\n' -}}"
|
||||
"{%- if add_generation_prompt -%}{{- '<|im_start|>assistant\\n' -}}{%- endif -%}"
|
||||
)
|
||||
|
||||
# TTS prompt of the model card: a language code is looked up, anything else is taken as the language name
|
||||
_tts_langs = {
|
||||
"ar": "Arabic", "da": "Danish", "de": "German", "el": "Greek", "en": "American English", "en-us": "American English",
|
||||
"en-gb": "British English", "fi": "Finnish", "fr": "French", "he": "Hebrew", "it": "Italian", "ja": "Japanese",
|
||||
"ko": "Korean", "nl": "Dutch", "pl": "Polish", "pt": "Portuguese", "es": "Spanish", "sv": "Swedish", "tr": "Turkish",
|
||||
"zh": "Chinese",
|
||||
}
|
||||
|
||||
# "language" and "voice" are optional, the text to speak is the first message
|
||||
def _tts_template(self, think: bool) -> str:
|
||||
return (
|
||||
"{%- set names = " + json.dumps(self._tts_langs) + " -%}"
|
||||
"{%- set code = (language | lower) if language is defined and language else 'en' -%}"
|
||||
"{%- set lang = names[code] if code in names else language -%}"
|
||||
"{%- set v = (voice | lower) if voice is defined and voice and (voice | lower) in ['female', 'male'] else 'female' -%}"
|
||||
"{{- bos_token -}}"
|
||||
"{{- '<|im_start|>system\\nPerform TTS in ' + lang + ' using a ' + v + ' voice.<|im_end|>\\n' -}}"
|
||||
"{{- '<|im_start|>user\\n' + messages[0]['content'] + '<|im_end|>\\n' -}}"
|
||||
"{{- '<|im_start|>assistant\\n" + ("<think></think>" if think else "") + "<|audio_start|>' -}}"
|
||||
)
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
# hparams hold the merged text config, the checkpoint's own model_type is in config.json
|
||||
with open(self.dir_model / "config.json", encoding="utf-8") as f:
|
||||
model_type = json.load(f).get("model_type")
|
||||
is_audio = model_type == "lfm3_audio"
|
||||
is_omni = model_type == "lfm3_omni"
|
||||
templates = []
|
||||
if is_audio:
|
||||
templates.append({"name": "asr", "template": self._asr_template})
|
||||
if is_audio or is_omni:
|
||||
# LFM3-Omni opens every assistant turn with an empty think block
|
||||
templates.append({"name": "tts", "template": self._tts_template(think=is_omni)})
|
||||
if templates:
|
||||
self.gguf_writer.add_chat_template(templates)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
layer_types = self.hparams["layer_types"]
|
||||
n_kv_heads = self.hparams["num_key_value_heads"]
|
||||
|
||||
# set num_key_value_heads only for attention layers
|
||||
self.hparams["num_key_value_heads"] = [
|
||||
n_kv_heads if lt != "conv" else 0
|
||||
for lt in layer_types
|
||||
]
|
||||
|
||||
super().set_gguf_parameters()
|
||||
|
||||
self.gguf_writer.add_rope_dimension_count(self.hparams["head_dim"])
|
||||
|
||||
self.gguf_writer.add_sliding_window_pattern([lt == "sliding_attention" for lt in layer_types])
|
||||
self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
|
||||
|
||||
self.gguf_writer.add_shortconv_l_cache(self.hparams["conv_L_cache"])
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
# Partial RoPE is represented by masking trailing frequency pairs.
|
||||
# rope_parameters is keyed by layer type; only full_attention layers use partial RoPE.
|
||||
rope_parameters = self.hparams["rope_parameters"]["full_attention"]
|
||||
partial = rope_parameters.get("partial_rotary_factor", 1.0)
|
||||
head_dim = self.hparams["head_dim"]
|
||||
rope_angles = int(partial * head_dim) // 2
|
||||
n_pairs = head_dim // 2
|
||||
assert 0 < rope_angles <= n_pairs
|
||||
values = [1.0] * rope_angles + [1e30] * (n_pairs - rope_angles)
|
||||
rope_freqs = torch.tensor(values, dtype=torch.float32)
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), rope_freqs)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# conv op requires 2d tensor
|
||||
if "conv.conv" in name:
|
||||
data_torch = data_torch.squeeze(1)
|
||||
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
# d1 on LFM3-VL / LFM3-Omni: the stock weights, the d1 prompt read at its last token
|
||||
_D1_TYPES = {"lfm3_vl": gguf.DecisionType.LFM3_D1, "lfm3_omni": gguf.DecisionType.LFM3_D1_OMNI}
|
||||
|
||||
|
||||
def _is_lfm3_d1_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
return config.get("architectures") == ["D1Model"] and config.get("model_type") in _D1_TYPES
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_lfm3_d1_checkpoint)
|
||||
def _load_lfm3_d1_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
logger.info("gguf: detected LFM3 d1 checkpoint")
|
||||
hparams = ModelBase.load_hparams(dir_model, False, guess=False)
|
||||
hparams["d1_family"] = hparams["model_type"]
|
||||
# the mmproj is the base model's, the text model writes the decision
|
||||
hparams["architectures"] = ["Lfm3VlForConditionalGeneration" if hparams["model_type"] == "lfm3_vl" else "Lfm3OmniForConditionalGeneration"]
|
||||
hparams["text_config"]["architectures"] = ["Lfm3D1Model"]
|
||||
return hparams
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3D1Model")
|
||||
@ModelBase.example("LiquidAI/d1-2B", "LiquidAI/d1-omni-2B")
|
||||
class LFM3D1Model(LFM3Model):
|
||||
model_arch = gguf.MODEL_ARCH.LFM3
|
||||
|
||||
# how the checkpoint serves, hard-coded in the server: assert the checkpoint records the same
|
||||
_served = {"d1_codes": 2, "d1_attention": "set"}
|
||||
|
||||
def set_vocab(self):
|
||||
# the vocab without the speech task templates of LFM3-Audio/Omni, d1 only decides
|
||||
TextModel.set_vocab(self)
|
||||
# the d1 prompt of LFM2, the LFM3 template writes an empty thought before the answer
|
||||
self.gguf_writer.add_chat_template([{"name": "systemone", "template": D1Model._systemone_template() + "<think></think>"}])
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
|
||||
for key, value in self._served.items():
|
||||
if self.hparams.get(key) != value:
|
||||
raise ValueError(f"unsupported d1 checkpoint: {key} = {self.hparams.get(key)!r}, expected {value!r}")
|
||||
|
||||
# a lone question reads the whole prompt both ways on the full attention layers
|
||||
self.gguf_writer.add_causal_attention(False)
|
||||
self.gguf_writer.add_decision_type(_D1_TYPES[self.hparams["d1_family"]])
|
||||
|
||||
# the option answers share one temperature
|
||||
with open(self.dir_model / "calibration.json", encoding="utf-8") as f:
|
||||
calibration = json.load(f)
|
||||
if calibration.get("temperatures"):
|
||||
raise ValueError("unsupported d1 calibration: per-bucket temperatures")
|
||||
for name in ("noul", "choice", "score"):
|
||||
self.gguf_writer.add_decision_temperature(name, calibration["default"])
|
||||
|
||||
|
||||
class LFM3VLVisionMixin(MmprojModel):
|
||||
"""LFM3-VL vision tower and projector, shared with LFM3-Omni."""
|
||||
|
||||
def _set_lfm3vl_gguf_parameters(self):
|
||||
assert self.hparams_vision is not None
|
||||
# clip.cpp hardcodes the GELU projector and its input norm eps
|
||||
if (self.global_config["projector_hidden_act"] != "gelu" or not self.global_config["projector_bias"]
|
||||
or self.global_config["projector_layer_norm_eps"] != 1e-6):
|
||||
raise ValueError("unsupported projector_hidden_act / projector_bias / projector_layer_norm_eps")
|
||||
# clip.cpp hardcodes the vision rope base, the RGB patchifier and the 3x3 spatial conv
|
||||
if (self.hparams_vision["rope_parameters"]["rope_theta"] != 1e6 or self.hparams_vision["in_channels"] != 3
|
||||
or self.hparams_vision["conv_kernel_size"] != 3):
|
||||
raise ValueError("unsupported vision rope_theta / in_channels / conv_kernel_size")
|
||||
self.gguf_writer.add_clip_vision_projector_type(gguf.VisionProjectorType.LFM3VL)
|
||||
self.gguf_writer.add_vision_head_count_kv(self.hparams_vision["num_key_value_heads"])
|
||||
self.gguf_writer.add_vision_spatial_merge_size(self.global_config["downsample_factor"])
|
||||
self.gguf_writer.add_vision_attention_layernorm_eps(self.hparams_vision["norm_eps"])
|
||||
self.gguf_writer.add_vision_use_silu(True)
|
||||
size = self.preprocessor_config["size"]
|
||||
self.gguf_writer.add_vision_min_pixels(size["shortest_edge"])
|
||||
self.gguf_writer.add_vision_max_pixels(size["longest_edge"])
|
||||
# PIL resample enum, the torchvision backend has no lanczos
|
||||
resample = self.preprocessor_config["resample"]
|
||||
resize_algo = {2: "bilinear", 3: "bicubic"}.get(resample)
|
||||
if resize_algo is None:
|
||||
raise ValueError(f"unsupported resample: {resample}")
|
||||
self.gguf_writer.add_vision_image_resize_algo(resize_algo)
|
||||
self._check_video_config()
|
||||
|
||||
# mtmd_get_video_ts_style and the lfm3vl video frame sizing hardcode these, conversion fails on a mismatch
|
||||
_video = {"fps": 2.0, "min_frames": 4, "temporal_patch_size": 2, "size": {"shortest_edge": 4096, "longest_edge": 25165824}}
|
||||
_video_max_frames = 768
|
||||
|
||||
def _check_video_config(self):
|
||||
import json
|
||||
video = self.preprocessor_config.get("video_processor")
|
||||
path = self.dir_model / "video_preprocessor_config.json"
|
||||
if video is None and path.is_file():
|
||||
with open(path, encoding="utf-8") as f:
|
||||
video = json.load(f)
|
||||
if video is None:
|
||||
return
|
||||
for k, v in {**self._video, "max_frames": self._video_max_frames}.items():
|
||||
if video.get(k) != v:
|
||||
raise ValueError(f"unsupported video {k}={video.get(k)!r}, expected {v!r}")
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if "patch_embd" in new_name:
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
if "conv2d_spatial" in new_name:
|
||||
# CUDA CONV_2D_DW requires an f32 kernel; the tensor is tiny
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
def _lfm3vl_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "model.vision_tower.patchifier.weight":
|
||||
# qwen2vl-style layout [channel, temporal, ph, pw], one conv kernel per temporal slice
|
||||
assert self.hparams_vision is not None
|
||||
t = self.hparams_vision["temporal_patch_size"]
|
||||
p = self.hparams_vision["patch_size"]
|
||||
n_embd = data_torch.shape[0]
|
||||
w = data_torch.view(n_embd, 3, t, p, p)
|
||||
yield ("v.patch_embd.weight", w[:, :, 0].contiguous())
|
||||
if t == 2:
|
||||
yield ("v.patch_embd.weight.1", w[:, :, 1].contiguous())
|
||||
else:
|
||||
assert t == 1, f"unsupported vision_temporal_patch_size {t}"
|
||||
return
|
||||
if name == "model.vision_tower.embedding_norm.weight":
|
||||
yield ("v.embedding_norm.weight", data_torch)
|
||||
return
|
||||
if "conv.in_proj.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_in_proj.weight", data_torch)
|
||||
return
|
||||
if "conv.spatial_conv.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_spatial.weight", data_torch)
|
||||
return
|
||||
if "conv.out_proj.weight" in name:
|
||||
yield (f"v.blk.{bid}.conv2d_out_proj.weight", data_torch)
|
||||
return
|
||||
|
||||
# projector: input LayerNorm -> linear_1 -> GELU -> linear_2
|
||||
if name.startswith("model.multi_modal_projector."):
|
||||
layer, suffix = name.removeprefix("model.multi_modal_projector.").split(".")
|
||||
yield ({"layer_norm": "mm.input_norm", "linear_1": "mm.0", "linear_2": "mm.2"}[layer] + "." + suffix, data_torch)
|
||||
return
|
||||
|
||||
yield (self.map_tensor_name(name), data_torch)
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3VlForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-VL-2B")
|
||||
class LFM3VLMmprojModel(LFM3VLVisionMixin):
|
||||
has_vision_encoder = True
|
||||
has_audio_encoder = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
assert self.hparams_vision is not None
|
||||
# dynamic resolution, image_size is only set for MmprojModel compatibility
|
||||
self.hparams_vision["image_size"] = 512
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._set_lfm3vl_gguf_parameters()
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith(("model.vision_tower.", "model.multi_modal_projector.")):
|
||||
yield from self._lfm3vl_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
def _is_lfm3_audio_checkpoint(dir_model: Path) -> bool:
|
||||
if not (dir_model / "config.json").is_file():
|
||||
return False
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
return config.get("model_type") == "lfm3_audio" and "lfm" in config
|
||||
|
||||
|
||||
# read the release config.json as is: a transformers that knows lfm3_audio rewrites it into another layout
|
||||
@ModelBase.register_hparams_loader(_is_lfm3_audio_checkpoint)
|
||||
def _load_lfm3_audio_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
with open(dir_model / "config.json", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
config["text_config"] = config["lfm"]
|
||||
return config
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3AudioForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-Audio-2B")
|
||||
class LFM3AudioMmprojModel(MmprojModel):
|
||||
has_vision_encoder = False
|
||||
has_audio_encoder = True
|
||||
|
||||
# the downsampler has the LFM2-Audio pre-encoder layout: conv, dw conv, pw conv, dw conv, pw conv
|
||||
_downsample_idx = {"conv_in": 0, "conv_mid": 2, "lin_mid": 3, "conv_end": 5, "lin_end": 6}
|
||||
|
||||
# audio output: MaskGIT depthformer (a.gen.code.*) and the codec decoder (a.gen.wav.*)
|
||||
_depth_layer_map = {
|
||||
"operator_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_NORM,
|
||||
"self_attn.q_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_Q,
|
||||
"self_attn.q_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_Q_NORM,
|
||||
"self_attn.k_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_K,
|
||||
"self_attn.k_norm": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_K_NORM,
|
||||
"self_attn.v_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_V,
|
||||
"self_attn.o_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_OUT,
|
||||
"self_attn.gate_proj": gguf.MODEL_TENSOR.A_GEN_CODE_ATTN_GATE,
|
||||
"ffn_norm": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_NORM,
|
||||
"feed_forward.w1": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_GATE,
|
||||
"feed_forward.w3": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_UP,
|
||||
"feed_forward.w2": gguf.MODEL_TENSOR.A_GEN_CODE_FFN_DOWN,
|
||||
}
|
||||
_codec_layer_map = {
|
||||
"operator_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_NORM,
|
||||
"post_operator_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_POST_NORM,
|
||||
"self_attn.q_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q,
|
||||
"self_attn.q_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q_NORM,
|
||||
"self_attn.k_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K,
|
||||
"self_attn.k_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K_NORM,
|
||||
"self_attn.v_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_V,
|
||||
"self_attn.o_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_OUT,
|
||||
"self_attn.gate_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_GATE,
|
||||
"conv.in_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW1,
|
||||
"conv.conv": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_DW,
|
||||
"conv.out_proj": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_CONV_PW2,
|
||||
"ffn_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_NORM,
|
||||
"post_ffn_norm": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_POST_NORM,
|
||||
"feed_forward.w1": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_GATE,
|
||||
"feed_forward.w3": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_UP,
|
||||
"feed_forward.w2": gguf.MODEL_TENSOR.A_GEN_WAV_TFM_FFN_DOWN,
|
||||
}
|
||||
# the codec graph in clip.cpp hardcodes the liquid-codec FSQ front end and decoder shape, conversion fails on a mismatch
|
||||
_codec = {
|
||||
"model_type": "liquid_codec", "fsq_levels": [8, 7, 6, 6], "n_q": 12, "quantizer_dim": 256,
|
||||
"encoder_dim": 512, "stft_n_fft": 1280, "stft_hop_length": 320, "stft_win_length": 1280, "stft_frame_stack": 6,
|
||||
"decoder_window": 30, "sampling_rate": 24000,
|
||||
}
|
||||
# the lfm3a pipeline in mtmd-helper-gen.cpp hardcodes these
|
||||
_generation = {"codebooks": 12, "maskgit_schedule": [4, 4, 4], "interleaved_n_text": 6, "interleaved_n_audio": 12}
|
||||
|
||||
# speech-code sampling of the model card, generation_config.json overrides it
|
||||
_audio_sampling = {
|
||||
"audio_temperature": 1.0, "audio_top_k": 128, "audio_repetition_penalty": 1.1, "audio_repetition_window": 4096,
|
||||
}
|
||||
|
||||
# mtmd_audio_preprocessor_lfm3a hardcodes this front end, conversion fails on a mismatch
|
||||
_feature_extractor = {
|
||||
"sampling_rate": 16000, "feature_size": 128, "n_fft": 400, "win_length": 400, "hop_length": 160,
|
||||
"log_floor": -12.0, "normalization_offset": 4.0, "normalization_divisor": 4.0, "encoder_stride": 8,
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# audio output is exported when the checkpoint ships the codec
|
||||
self._audio_output = (self.dir_model / "audio_tokenizer").is_dir()
|
||||
gen = self.get_generation_config()
|
||||
self._n_codebooks: int = gen["codebooks"]
|
||||
self._audio_vocab: int = gen["audio_vocab_size"]
|
||||
self._depth_embd: dict[int, Tensor] = {}
|
||||
self._depth_norm: dict[int, Tensor] = {}
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
enc = self.global_config["encoder"]
|
||||
cfg = dict(enc["lfm"])
|
||||
cfg["num_mel_bins"] = enc["dim_in"]
|
||||
cfg["sliding_window"] = enc["sliding_window"]
|
||||
return cfg
|
||||
|
||||
def get_generation_config(self) -> dict[str, Any]:
|
||||
c = self.global_config
|
||||
steps = c["maskgit_steps"]
|
||||
return {
|
||||
"codebooks": c["codebooks"], "audio_vocab_size": c["audio_vocab_size"],
|
||||
"maskgit_schedule": [c["codebooks"] // steps] * steps,
|
||||
"interleaved_n_text": c["interleaved_n_text"], "interleaved_n_audio": c["interleaved_n_audio"],
|
||||
}
|
||||
|
||||
def get_depth_config(self) -> dict[str, Any]:
|
||||
return self.global_config["depthformer"]
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
assert self.hparams_audio is not None
|
||||
fe = self.preprocessor_config["feature_extractor"]
|
||||
if fe["feature_extractor_type"] not in ("Lfm3AudioFeatureExtractor", "Lfm3OmniFeatureExtractor"):
|
||||
raise ValueError(f"unsupported feature_extractor_type {fe['feature_extractor_type']!r}")
|
||||
for k, v in self._feature_extractor.items():
|
||||
if fe[k] != v:
|
||||
raise ValueError(f"unsupported feature_extractor.{k}={fe[k]!r}, expected {v!r}")
|
||||
|
||||
# clip.cpp hardcodes the encoder rope base
|
||||
if self.hparams_audio["rope_parameters"]["rope_theta"] != 1e6:
|
||||
raise ValueError("unsupported audio encoder rope_theta")
|
||||
|
||||
self.hparams_audio["intermediate_size"] = lfm2_feed_forward_length(self.hparams_audio)
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.LFM3A)
|
||||
self.gguf_writer.add_audio_head_count_kv(self.hparams_audio["num_key_value_heads"])
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(self.hparams_audio["norm_eps"])
|
||||
self.gguf_writer.add_audio_num_mel_bins(self.hparams_audio["num_mel_bins"])
|
||||
self.gguf_writer.add_audio_window_size(self.hparams_audio["sliding_window"])
|
||||
|
||||
if not self._audio_output:
|
||||
return
|
||||
|
||||
# audio generation: depthformer hparams, the codec ones are constant and live in clip.cpp
|
||||
gen = self.get_generation_config()
|
||||
for k, v in self._generation.items():
|
||||
if gen[k] != v:
|
||||
raise ValueError(f"unsupported {k}={gen[k]!r}, expected {v!r}")
|
||||
depth = self.get_depth_config()
|
||||
if depth.get("is_causal", False) or depth["use_sandwich_norm"] or set(depth["layer_types"]) != {"full_attention"}:
|
||||
raise ValueError("unsupported depthformer, expected a bidirectional all-attention stack")
|
||||
if depth["rope_parameters"]["full_attention"]["rope_theta"] != 1e6:
|
||||
raise ValueError("unsupported depthformer rope_theta")
|
||||
self.gguf_writer.add_clip_has_gen_audio_encoder(True)
|
||||
self.gguf_writer.add_clip_gen_audio_projector_type(gguf.VisionProjectorType.LFM3A_GEN)
|
||||
self.gguf_writer.add_gen_audio_projection_dim(self.n_embd_text)
|
||||
self.gguf_writer.add_gen_audio_embedding_length(depth["hidden_size"])
|
||||
self.gguf_writer.add_gen_audio_feed_forward_length(depth["intermediate_size"])
|
||||
self.gguf_writer.add_gen_audio_block_count(depth["num_hidden_layers"])
|
||||
self.gguf_writer.add_gen_audio_head_count(depth["num_attention_heads"])
|
||||
self.gguf_writer.add_gen_audio_head_count_kv(depth["num_key_value_heads"])
|
||||
self.gguf_writer.add_gen_audio_attention_layernorm_eps(depth["norm_eps"])
|
||||
|
||||
# sampling defaults of the speech codes, next to the text ones that become general.sampling.*
|
||||
gen = gguf.Metadata.load_generation_config(self.dir_model)
|
||||
for key, add in (("audio_temperature", self.gguf_writer.add_gen_audio_sampling_temp),
|
||||
("audio_top_k", self.gguf_writer.add_gen_audio_sampling_top_k),
|
||||
("audio_repetition_penalty", self.gguf_writer.add_gen_audio_sampling_penalty_repeat),
|
||||
("audio_repetition_window", self.gguf_writer.add_gen_audio_sampling_penalty_last_n)):
|
||||
value = gen.get(key, self._audio_sampling.get(key))
|
||||
if value is not None:
|
||||
add(value)
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if new_name.startswith("a.conv1d.") or "conv_dw" in new_name or new_name.startswith("a.gen.wav.quant."):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
# the codec decoder is small and its log-magnitude/phase output does not survive quantization
|
||||
if new_name.startswith("a.gen.wav."):
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
if self._audio_output:
|
||||
yield from self._codec_tensors()
|
||||
|
||||
def _codec_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
import json
|
||||
from safetensors.torch import load_file
|
||||
|
||||
T = gguf.MODEL_TENSOR
|
||||
codec_dir = self.dir_model / "audio_tokenizer"
|
||||
with open(codec_dir / "config.json", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
for k, v in self._codec.items():
|
||||
if cfg.get(k) != v:
|
||||
raise ValueError(f"unsupported audio_tokenizer {k}={cfg.get(k)!r}, expected {v!r}")
|
||||
if math.prod(cfg["fsq_levels"]) != self._audio_vocab - 1:
|
||||
raise ValueError(f"audio_vocab_size {self._audio_vocab} does not match the codec, expected codebook size + 1")
|
||||
|
||||
state_dict = load_file(codec_dir / "model.safetensors")
|
||||
|
||||
# FSQ code table: index -> the len(levels) quantized values, see FiniteScalarQuantization._indices_to_codes
|
||||
levels = torch.tensor(cfg["fsq_levels"], dtype=torch.int64)
|
||||
basis = torch.cumprod(torch.cat([torch.ones(1, dtype=torch.int64), levels[:-1]]), dim=0)
|
||||
half = levels // 2
|
||||
idx = torch.arange(int(levels.prod()), dtype=torch.int64)[:, None]
|
||||
codes = ((idx // basis) % levels - half) / half
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_FIRST_CB), codes.to(torch.float32))
|
||||
|
||||
# one projection over the concatenated code vectors of all codebooks, under quantizer.fsq. in newer exports
|
||||
quantizer = "quantizer.fsq." if "quantizer.fsq.project_out.weight" in state_dict else "quantizer."
|
||||
for suffix in ("weight", "bias"):
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_QUANT_FIRST_OUT, suffix=f".{suffix}"), state_dict[f"{quantizer}project_out.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_IN_PROJ, suffix=f".{suffix}"), state_dict[f"from_quantizer.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_OUT_PROJ, suffix=f".{suffix}"), state_dict[f"decoder.lin.{suffix}"])
|
||||
yield (self.format_tensor_name(T.A_GEN_WAV_TFM_OUTPUT_NORM), state_dict["decoder.lfm.embedding_norm.weight"])
|
||||
|
||||
for name, data_torch in state_dict.items():
|
||||
if not name.startswith("decoder.lfm.layers."):
|
||||
continue
|
||||
bid_str, key = name.removeprefix("decoder.lfm.layers.").rsplit(".weight", 1)[0].split(".", 1)
|
||||
if key == "conv.conv":
|
||||
data_torch = data_torch.squeeze(1)
|
||||
yield (self.format_tensor_name(self._codec_layer_map[key], int(bid_str)), data_torch)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith("a.gen.wav."):
|
||||
yield (name, data_torch)
|
||||
return
|
||||
|
||||
if name.startswith(("depthformer.", "depth_", "codebook_embedding.", "mask_embedding")):
|
||||
if self._audio_output:
|
||||
yield from self._depthformer_tensor(data_torch, name, bid)
|
||||
return
|
||||
|
||||
if not name.startswith(("encoder.", "mlp.")):
|
||||
return
|
||||
|
||||
if name.startswith("encoder.downsample."):
|
||||
layer, suffix = name.removeprefix("encoder.downsample.").split(".")
|
||||
if layer == "lin_out":
|
||||
if suffix == "weight":
|
||||
data_torch = data_torch.squeeze(-1)
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_ENC_OUT, suffix=f".{suffix}"), data_torch)
|
||||
return
|
||||
if suffix == "bias":
|
||||
data_torch = data_torch[:, None, None]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.A_ENC_CONV1D, self._downsample_idx[layer], f".{suffix}"), data_torch)
|
||||
return
|
||||
|
||||
# conv op requires 2d tensor
|
||||
if name.endswith("conv.conv.weight"):
|
||||
data_torch = data_torch.squeeze(1)
|
||||
|
||||
yield (self.map_tensor_name(name), data_torch)
|
||||
|
||||
def _depthformer_tensor(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
T = gguf.MODEL_TENSOR
|
||||
|
||||
if name.startswith("depthformer.model.layers."):
|
||||
key = name.removeprefix("depthformer.model.layers.").split(".", 1)[1].rsplit(".weight", 1)[0]
|
||||
yield (self.format_tensor_name(self._depth_layer_map[key], bid), data_torch)
|
||||
return
|
||||
if name == "depthformer.model.embedding_norm.weight":
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_OUTPUT_NORM), data_torch)
|
||||
return
|
||||
if name.startswith("depth_linear."):
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_PROJ_IN, suffix="." + name.rsplit(".", 1)[1]), data_torch)
|
||||
return
|
||||
if name == "mask_embedding":
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_MASK_EMBD, suffix=""), data_torch)
|
||||
return
|
||||
if name == "codebook_embedding.weight":
|
||||
assert data_torch.shape[0] == self._n_codebooks * self._audio_vocab
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_OUT_EMBD), data_torch)
|
||||
return
|
||||
|
||||
# per-codebook heads: the embedding table is tied to to_logits, merge the 12 tables into one tensor
|
||||
assert name.startswith("depth_embeddings.")
|
||||
idx = int(name.split(".")[1])
|
||||
key = name.split(".", 2)[2]
|
||||
if key == "embedding.weight":
|
||||
self._depth_embd[idx] = data_torch
|
||||
if len(self._depth_embd) == self._n_codebooks:
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_EMBD), torch.stack([self._depth_embd[i] for i in range(self._n_codebooks)], dim=0))
|
||||
elif key == "embedding_norm.weight":
|
||||
self._depth_norm[idx] = data_torch
|
||||
if len(self._depth_norm) == self._n_codebooks:
|
||||
yield (self.format_tensor_name(T.A_GEN_CODE_HEAD_NORM), torch.stack([self._depth_norm[i] for i in range(self._n_codebooks)], dim=0))
|
||||
elif key == "to_logits.weight":
|
||||
if not torch.equal(data_torch, self._depth_embd[idx]):
|
||||
raise ValueError(f"depth_embeddings.{idx} is not tied")
|
||||
else:
|
||||
raise ValueError(f"unexpected tensor {name}")
|
||||
|
||||
|
||||
@ModelBase.register("Lfm3OmniForConditionalGeneration")
|
||||
@ModelBase.example("LiquidAI/LFM3-Omni-2B")
|
||||
class LFM3OmniMmprojModel(LFM3VLVisionMixin, LFM3AudioMmprojModel):
|
||||
has_vision_encoder = True
|
||||
has_audio_encoder = True
|
||||
|
||||
_audio_sampling = {"audio_temperature": 0.4, "audio_repetition_penalty": 1.2, "audio_repetition_window": 64}
|
||||
_video_max_frames = 384
|
||||
|
||||
# the audio side is the LFM3-Audio model under the HF-native names
|
||||
_audio_renames = (
|
||||
("model.audio_encoder.lfm.", "encoder.lfm.lfm."),
|
||||
("model.audio_encoder.", "encoder."),
|
||||
("model.audio_projector.layer_norm.", "mlp.model.0."),
|
||||
("model.audio_projector.linear_1.", "mlp.model.1."),
|
||||
("model.audio_projector.linear_2.", "mlp.model.3."),
|
||||
("model.depth_decoder.model.", "depthformer.model."),
|
||||
("model.depth_decoder.", ""),
|
||||
("model.codebook_embedding.", "codebook_embedding."),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
assert self.hparams_vision is not None
|
||||
# dynamic resolution, image_size is only set for MmprojModel compatibility
|
||||
self.hparams_vision["image_size"] = 512
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
enc = self.global_config["audio_encoder_config"]
|
||||
cfg = dict(enc["text_config"])
|
||||
cfg["num_mel_bins"] = enc["num_mel_bins"]
|
||||
cfg["sliding_window"] = enc["sliding_window"]
|
||||
return cfg
|
||||
|
||||
def get_generation_config(self) -> dict[str, Any]:
|
||||
c = self.global_config
|
||||
return {
|
||||
"codebooks": c["num_codebooks"], "audio_vocab_size": c["codebook_size"], "maskgit_schedule": c["maskgit_schedule"],
|
||||
"interleaved_n_text": c["interleaved_n_text"], "interleaved_n_audio": c["interleaved_n_audio"],
|
||||
}
|
||||
|
||||
def get_depth_config(self) -> dict[str, Any]:
|
||||
return self.global_config["depth_config"]
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self._set_lfm3vl_gguf_parameters()
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.startswith(("model.vision_tower.", "model.multi_modal_projector.")):
|
||||
yield from self._lfm3vl_tensors(data_torch, name, bid)
|
||||
return
|
||||
for old, new in self._audio_renames:
|
||||
if name.startswith(old):
|
||||
yield from super().modify_tensors(data_torch, new + name.removeprefix(old), bid)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
@@ -690,6 +690,52 @@ class Qwen3_5TextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
self._try_set_pooling_type()
|
||||
|
||||
|
||||
@ModelBase.register("MiniCPMOCRForSequenceClassification")
|
||||
class MiniCPMOCRSeqClsModel(Qwen3_5TextModel):
|
||||
# OCR consistency judge: Qwen3.5 VL trunk with a value head read at the last token
|
||||
# the class scores are [inconsistent, consistent], the softmax temperature is 1
|
||||
model_arch = gguf.MODEL_ARCH.QWEN35
|
||||
no_mtp = True # the checkpoint has no MTP head
|
||||
|
||||
CLASS_LABELS = ["inconsistent", "consistent"]
|
||||
|
||||
def set_vocab(self):
|
||||
super().set_vocab()
|
||||
self.gguf_writer.add_chat_template([{"name": "systemone", "template": self._systemone_template()}])
|
||||
|
||||
def _systemone_template(self) -> str:
|
||||
# the prompt of the training data, the two images are the original page and the one
|
||||
# rendered from the OCR text; the task is fixed, there is nothing to fill in
|
||||
# the trailing newlines are in an expression, so the template does not drop them
|
||||
return (
|
||||
"<|im_start|>user\n{{ images[0] }}\nOCR文本:\n{{ state }}\n{{ images[1] }}\n"
|
||||
"第一张图是原图,第二张图是上述 OCR 文本渲染得到的图片。\n"
|
||||
"请判断该 OCR 文本及其对应渲染图是否与原图内容一致。\n"
|
||||
"这是一个二分类任务:一致标为 1,不一致标为 0。"
|
||||
"{{ '<|im_end|>\\n<|im_start|>assistant\\n<think>\\n\\n</think>\\n\\n' }}"
|
||||
)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
|
||||
# the config.json of the checkpoint keeps the one of the base model, it has no num_labels / id2label
|
||||
if "score.weight" not in self.model_tensors:
|
||||
raise ValueError("the checkpoint has no score.weight classification head")
|
||||
|
||||
self.gguf_writer.add_pooling_type(gguf.PoolingType.LAST)
|
||||
self.gguf_writer.add_embedding_length_out(len(self.CLASS_LABELS))
|
||||
self.gguf_writer.add_classifier_output_labels(self.CLASS_LABELS)
|
||||
self.gguf_writer.add_decision_type(gguf.DecisionType.MINICPM_OCR_JEV)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "score.weight":
|
||||
if data_torch.shape[0] != len(self.CLASS_LABELS):
|
||||
raise ValueError(f"score.weight must have {len(self.CLASS_LABELS)} rows, got {data_torch.shape[0]}")
|
||||
yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.CLS_OUT] + ".weight", data_torch)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
def _is_openjev_checkpoint(dir_model: Path) -> bool:
|
||||
return (dir_model / "helper" / "shim.py").is_file() and (dir_model / "config.json").is_file()
|
||||
|
||||
|
||||
@@ -144,6 +144,16 @@ class Qwen3VLVisionModel(MmprojModel):
|
||||
yield from MmprojModel.modify_tensors(self, data_torch, name, bid)
|
||||
|
||||
|
||||
@ModelBase.register("MiniCPMOCRForSequenceClassification")
|
||||
class MiniCPMOCRSeqClsVisionModel(Qwen3VLVisionModel):
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
# trained with the image budget of its processor, wider than the mtmd default of Qwen-VL
|
||||
size = self.preprocessor_config["size"]
|
||||
self.gguf_writer.add_vision_min_pixels(int(size["shortest_edge"]))
|
||||
self.gguf_writer.add_vision_max_pixels(int(size["longest_edge"]))
|
||||
|
||||
|
||||
@ModelBase.register("Qwen3OmniMoeForConditionalGeneration")
|
||||
@ModelBase.example("Qwen/Qwen3-Omni-30B-A3B-Instruct")
|
||||
class Qwen3OmniMmprojModel(Qwen3VLVisionModel, Qwen25AudioModel):
|
||||
|
||||
@@ -72,6 +72,8 @@ However, A6x GPUs in phones are likely not supported due to the outdated driver
|
||||
| MXFP4 | Support |
|
||||
| IQ4_NL | Support |
|
||||
|
||||
`MUL_MAT_ID` accepts an expert id of `-1` and writes zeros to that slot. `ADD_ID` adds no bias for `-1`, including in fused MoE operations.
|
||||
|
||||
## Model Preparation
|
||||
|
||||
Since common quantizations are supported now, it is recommanded to download GGUF models directly from Huggingface.
|
||||
|
||||
@@ -784,6 +784,7 @@ User can use the device management in [docs/multi-gpu.md](https://github.com/ggm
|
||||
| GGML_SYCL_TARGET | INTEL *(default)* | Set the SYCL target device type. |
|
||||
| GGML_SYCL_DEVICE_ARCH | Optional | Set the SYCL device architecture. Setting the device architecture can improve the performance. See the table [--offload-arch](https://github.com/intel/llvm/blob/sycl/sycl/doc/design/OffloadDesign.md#--offload-arch) for a list of valid architectures. |
|
||||
| GGML_SYCL_F16 | OFF *(default)* \|ON *(optional)* | Enable FP16 build with SYCL code path. [1.](#sycl-f16-footnote) |
|
||||
| GGML_SYCL_SUPPORT_XMX | ON *(default)* \|OFF *(Optional)* | Build the code paths that use XMX (DPAS) instructions. Set OFF for AOT builds targeting iGPUs without XMX, which otherwise fail to compile. |
|
||||
| GGML_SYCL_GRAPH | ON *(default)* \|OFF *(Optional)* | Enable build with [SYCL Graph extension](https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/experimental/sycl_ext_oneapi_graph.asciidoc). |
|
||||
| GGML_SYCL_DNN | ON *(default)* \|OFF *(Optional)* | Enable build with oneDNN. |
|
||||
| GGML_SYCL_HOST_MEM_FALLBACK | ON *(default)* \|OFF *(Optional)* | Allow host memory fallback when device memory is full during quantized weight reorder. Enables inference to continue at reduced speed (reading over PCIe) instead of failing. Requires Linux kernel 6.8+. |
|
||||
@@ -812,6 +813,7 @@ User can use the device management in [docs/multi-gpu.md](https://github.com/ggm
|
||||
| GGML_SYCL_ENABLE_DNN | 0 or 1 (default)| Enable running computations through oneDNN and always use oneMKL. |
|
||||
| GGML_SYCL_FA_ONEDNN | 1 (default) or 0 | Enable the oneDNN fused SDPA (flash-attention) path on supported GPUs. Set to 0 to always use the native SYCL flash-attention kernel. |
|
||||
| GGML_SYCL_FA_ONEDNN_MAX_KV | 0 (default, disabled) or positive integer | By default (0), all sequences are handled by the oneDNN fused SDPA path, regardless of KV length; a positive value caps that length, past which sequences fall back to the native kernel. If GPU driver watchdog resets (DEVICE_LOST) occur during long-context inference, set this near the context depth where they start, e.g. 24576. |
|
||||
| GGML_SYCL_FA_DPAS | 1 (default) or 0 | Enable the XMX flash-attention kernel for a q4_0 or q8_0 KV cache (`-ctk q4_0 -ctv q4_0` or `-ctk q8_0 -ctv q8_0`) on Xe2 or newer GPUs (Arc B-series, Lunar Lake, Panther Lake, Wildcat Lake). It reads the quantized cache directly for decode and batches of up to 31 tokens, head sizes 64, 96, 128, 256 and 512, up to 32 query heads per KV head. Set to 0 to use the native SYCL flash-attention kernels. |
|
||||
| GGML_SYCL_ENABLE_VMM | 0 or 1 (default) | Enable the virtual-memory device pool. |
|
||||
| GGML_SYCL_ENABLE_MKL_FA | 1 (default) or 0 | Enable oneMKL GEMM flash attention for XMX-accelerated prompt processing with quantized KV cache. Automatically activates during prefill (prompt processing) when all conditions are met: (1) flash-attn enabled (`-fa` or `--flash-attn on`), (2) KV cache quantized (`--cache-type-k q8_0 --cache-type-v q8_0` or other `*_0/*_1` types), (3) batch size ≥ 1024 (`--batch-size 1024`), (4) prompt length ≥ 1024 tokens. Set to 0 to force the TILE kernel for A/B testing. Example minimum command: `llama-cli -m model.gguf -fa -ngl 99 --cache-type-k q8_0 --cache-type-v q8_0 --batch-size 1024 -p "your prompt"` |
|
||||
| GGML_SYCL_MKL_FA_DEBUG | 0 (default) or 1 | Enable per-call diagnostic logging for MKL flash attention: GEMM/softmax timings, interleaved-head detection, and buffer memory usage. |
|
||||
@@ -846,6 +848,18 @@ User can use the device management in [docs/multi-gpu.md](https://github.com/ggm
|
||||
Then benchmark with your preferred tool, such as `llama-bench`.
|
||||
For graph activity details (including reuse), set `GGML_SYCL_DEBUG=1` and look for `[SYCL-GRAPH]` messages.
|
||||
|
||||
### Flash-attention kernels
|
||||
|
||||
Attention dispatches on shape and hardware.
|
||||
|
||||
DPAS is the quantized path. q4_0 or q8_0 KV cache, decode and batches up to 31 tokens, Xe2 and newer only. It issues dpas straight on the quantized blocks and never materialises an f16 copy. vISA gets used here because joint_matrix has no s4 type.
|
||||
|
||||
oneDNN does XMX through SDPA but converts KV to f16 first, so it ends up on prefill: 32+ tokens and 1K+ keys. MKL is the prompt processing equivalent through oneMKL GEMM, also f16.
|
||||
|
||||
VEC/TILE are plain kernels with no XMX in them. They run when DPAS is off or the hardware doesn't support it.
|
||||
|
||||
joint_matrix belongs to the matmul GEMMs. Attention doesn't use it.
|
||||
|
||||
## Compile-time Flags
|
||||
|
||||
Pass these via `CXXFLAGS` or add a one-off `#define` to enable a flag on the spot.
|
||||
|
||||
@@ -324,6 +324,9 @@ on 4 physical NPUs, or `--devices 'HTP0[0-1:0],HTP1[0-1:1]'` on 2 physical NPUs
|
||||
- `GGML_HEXAGON_NHVX=0`
|
||||
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.
|
||||
|
||||
- `GGML_HEXAGON_HOSTBUF=1` (default: 0, disabled)
|
||||
Enables allocating host buffers for debugging. By default, host buffers are disabled.
|
||||
|
||||
|
||||
+3
-2
@@ -100,8 +100,9 @@ llama-server -m Qwen3-4B.gguf -md Qwen3-4B-DSpark.gguf \
|
||||
|
||||
`--spec-draft-n-max` is clamped to the draft model's trained block size.
|
||||
|
||||
`--spec-draft-conf-min P` truncates each drafted block at the first position whose predicted
|
||||
acceptance (from the draft's confidence head, if present) falls below `P` (default 0 = disabled).
|
||||
`--spec-draft-p-min P` truncates each drafted block at the first position whose predicted
|
||||
acceptance (from the draft's confidence head) falls below `P` (default 0 = disabled).
|
||||
Values above 0 require a draft with a confidence head.
|
||||
|
||||
Currently only drafts with a Qwen3 backbone are supported; support for other backbones
|
||||
(e.g. Gemma4) is planned.
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Example client for the llama-server /v1/realtime websocket, with the openai SDK.
|
||||
|
||||
Start a server with an audio model, e.g. LFM3-Audio:
|
||||
|
||||
llama-server -hf LiquidAI/LFM3-Audio-2B-GGUF
|
||||
|
||||
Spoken conversation, the audio of each wav file is one user turn. It is sent in chunks as it would come from a
|
||||
microphone, the server prefills it as it arrives so the response starts right after the last chunk:
|
||||
|
||||
python examples/realtime_client.py --instructions "Respond with interleaved text and audio." question.wav
|
||||
|
||||
Output modalities: left out, the model picks; "text", "audio" or "text,audio" (interleaved) force the response.
|
||||
Without --instructions, a forced mode comes with the system prompt of the LFM3-Audio or LFM3-Omni model card:
|
||||
|
||||
python examples/realtime_client.py --output-modalities text,audio --out answer.wav question.wav
|
||||
|
||||
Tool calls: --tools gives the conversation a demo tool, the client runs its calls and sends the results back:
|
||||
|
||||
python examples/realtime_client.py --tools --text "Set the volume to 30."
|
||||
|
||||
Text turns go in with --text, transcription sessions transcribe each file:
|
||||
|
||||
python examples/realtime_client.py --text "What is the capital of France?"
|
||||
python examples/realtime_client.py --type transcription --language en speech.wav
|
||||
|
||||
Live transcription models (Voxtral Realtime, LFM3-Audio live ASR) transcribe while the audio is sent, a transcription
|
||||
session opens with ?intent=transcription:
|
||||
|
||||
python examples/realtime_client.py --type transcription speech.wav
|
||||
|
||||
Requirements: pip install "openai[realtime]" numpy
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import time
|
||||
import wave
|
||||
|
||||
import numpy as np
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
|
||||
# system prompts of the LiquidAI/LFM3-Audio-2B and LiquidAI/LFM3-Omni-2B model cards for a forced output mode
|
||||
MODE_INSTRUCTIONS = {
|
||||
"audio": {"text,audio": "Respond with interleaved text and audio."},
|
||||
"omni": {"text": "Respond in text.", "text,audio": "Respond in interleaved text and audio."},
|
||||
}
|
||||
# and with tools: calls, or an answer in interleaved text and speech
|
||||
TOOLS_INSTRUCTIONS = "Respond with function calls, and interleaved text and audio.\n\nList of tools:"
|
||||
|
||||
# the demo tool of --tools
|
||||
DEMO_TOOLS = [{
|
||||
"type": "function",
|
||||
"name": "set_volume",
|
||||
"description": "Set the device speaker volume.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"volume": {"type": "integer", "description": "Volume from 0 to 100."}},
|
||||
"required": ["volume"],
|
||||
},
|
||||
}]
|
||||
|
||||
|
||||
def run_tool(name, arguments):
|
||||
if name == "set_volume":
|
||||
return {"volume": json.loads(arguments)["volume"]}
|
||||
return {"error": f"unknown tool {name}"}
|
||||
|
||||
|
||||
def read_wav(path, rate):
|
||||
# PCM16 wav -> mono int16 at the given rate
|
||||
with wave.open(path, "rb") as f:
|
||||
assert f.getsampwidth() == 2, "the example reads PCM16 wav files"
|
||||
x = np.frombuffer(f.readframes(f.getnframes()), dtype=np.int16).astype(np.float32)
|
||||
x = x.reshape(-1, f.getnchannels()).mean(axis=1)
|
||||
sr = f.getframerate()
|
||||
if sr != rate:
|
||||
n = int(round(len(x) * rate / sr))
|
||||
x = np.interp(np.arange(n) * sr / rate, np.arange(len(x)), x)
|
||||
return np.clip(x, -32768, 32767).astype(np.int16)
|
||||
|
||||
|
||||
def write_wav(path, pcm, rate):
|
||||
with wave.open(path, "wb") as f:
|
||||
f.setnchannels(1)
|
||||
f.setsampwidth(2)
|
||||
f.setframerate(rate)
|
||||
f.writeframes(pcm.tobytes())
|
||||
|
||||
|
||||
async def main(args):
|
||||
# the SDK derives a wss:// URL from base_url, a plain http server needs ws://
|
||||
client = AsyncOpenAI(base_url=args.url, api_key=args.api_key, websocket_base_url=args.url.replace("http", "ws", 1))
|
||||
query = {"intent": "transcription"} if args.type == "transcription" else {}
|
||||
async with client.realtime.connect(model=args.model, extra_query=query) as conn:
|
||||
ev = await conn.recv()
|
||||
if ev.type == "error":
|
||||
print("error:", ev.error.message)
|
||||
return
|
||||
# the audio goes in at the rate of the model, PCM16 mono
|
||||
session_info = ev.model_dump(warnings=False)["session"]
|
||||
audio_fmt = session_info["audio"]
|
||||
card = "omni" if "omni" in str(session_info.get("model", "")).lower() else "audio"
|
||||
in_rate = audio_fmt["input"]["format"]["rate"]
|
||||
out_rate = (audio_fmt.get("output") or {}).get("format", {}).get("rate", 24000)
|
||||
|
||||
session = {"type": args.type}
|
||||
if args.type == "realtime":
|
||||
instructions = args.instructions
|
||||
if instructions is None:
|
||||
instructions = TOOLS_INSTRUCTIONS if args.tools else MODE_INSTRUCTIONS[card].get(args.output_modalities)
|
||||
if args.tools:
|
||||
session["tools"] = DEMO_TOOLS
|
||||
if instructions is not None:
|
||||
session["instructions"] = instructions
|
||||
if args.output_modalities:
|
||||
session["output_modalities"] = args.output_modalities.split(",")
|
||||
elif args.language:
|
||||
session["audio"] = {"input": {"transcription": {"language": args.language}}}
|
||||
await conn.session.update(session=session) # ty: ignore[invalid-argument-type]
|
||||
ev = await conn.recv()
|
||||
if ev.type == "error":
|
||||
print("error:", ev.error.message)
|
||||
return
|
||||
|
||||
turns = [("audio", path) for path in args.wav] + [("text", text) for text in args.text]
|
||||
for i, (kind, src) in enumerate(turns):
|
||||
t_last = 0.0
|
||||
|
||||
# the input is sent while the events are read: live transcription arrives while the audio is sent
|
||||
async def send_turn():
|
||||
nonlocal t_last
|
||||
if kind == "audio":
|
||||
pcm = read_wav(src, in_rate)
|
||||
step = in_rate * args.chunk_ms // 1000
|
||||
for j in range(0, len(pcm), step):
|
||||
chunk = pcm[j:j + step]
|
||||
await conn.input_audio_buffer.append(audio=base64.b64encode(chunk.tobytes()).decode())
|
||||
if args.realtime:
|
||||
await asyncio.sleep(len(chunk) / in_rate)
|
||||
if args.type == "transcription":
|
||||
# "final" ends the input, models that transcribe with a delay then complete the last item
|
||||
await conn.send({"type": "input_audio_buffer.commit", "final": i == len(args.wav) - 1}) # ty: ignore[invalid-argument-type]
|
||||
else:
|
||||
await conn.conversation.item.create(item={
|
||||
"type": "message", "role": "user", "content": [{"type": "input_text", "text": src}]})
|
||||
if args.type == "realtime":
|
||||
# the audio not committed yet ends the user turn
|
||||
await conn.response.create()
|
||||
t_last = time.perf_counter()
|
||||
|
||||
print(f"--- turn {i} ({kind}: {src})")
|
||||
sender = asyncio.create_task(send_turn())
|
||||
t_first = None
|
||||
audio = []
|
||||
async for ev in conn:
|
||||
if ev.type == "response.output_text.delta" or ev.type == "conversation.item.input_audio_transcription.delta":
|
||||
t_first = t_first or time.perf_counter()
|
||||
print(ev.delta, end="", flush=True)
|
||||
elif ev.type == "response.output_audio.delta":
|
||||
t_first = t_first or time.perf_counter()
|
||||
audio.append(np.frombuffer(base64.b64decode(ev.delta), dtype=np.int16))
|
||||
elif ev.type == "response.done":
|
||||
# the calls of the response are run, their results go back and the model answers them
|
||||
calls = [o for o in ev.model_dump()["response"]["output"] if o["type"] == "function_call"]
|
||||
for call in calls:
|
||||
result = json.dumps(run_tool(call["name"], call["arguments"]))
|
||||
print(f"\n[call {call['name']}({call['arguments']}) -> {result}]", flush=True)
|
||||
await conn.conversation.item.create(item={
|
||||
"type": "function_call_output", "call_id": call["call_id"], "output": result})
|
||||
if not calls:
|
||||
break
|
||||
await conn.response.create()
|
||||
elif ev.type == "conversation.item.input_audio_transcription.completed":
|
||||
break
|
||||
elif ev.type == "error":
|
||||
print("\nerror:", ev.error.message)
|
||||
return
|
||||
await sender
|
||||
print()
|
||||
if t_first is not None and t_first > t_last:
|
||||
print(f"first output {1000 * (t_first - t_last):.0f} ms after the end of the input")
|
||||
elif t_first is not None:
|
||||
print(f"output started {1000 * (t_last - t_first):.0f} ms before the end of the input, "
|
||||
f"done {1000 * (time.perf_counter() - t_last):.0f} ms after it")
|
||||
if audio:
|
||||
pcm_out = np.concatenate(audio)
|
||||
path = f"{args.out.removesuffix('.wav')}_{i}.wav" if len(turns) > 1 else args.out
|
||||
write_wav(path, pcm_out, out_rate)
|
||||
print(f"audio: {len(pcm_out) / out_rate:.2f} s -> {path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
p.add_argument("wav", nargs="*", help="PCM16 wav files, one user turn each")
|
||||
p.add_argument("--text", action="append", default=[], help="a user turn as text, after the audio turns, repeatable")
|
||||
p.add_argument("--type", choices=["realtime", "transcription"], default="realtime", help="session type")
|
||||
p.add_argument("--instructions", help="system prompt of the conversation")
|
||||
p.add_argument("--tools", action="store_true", help="give the conversation a demo tool (set_volume), its calls are run")
|
||||
p.add_argument("--output-modalities", choices=["text", "audio", "text,audio"], help="default: the model picks")
|
||||
p.add_argument("--language", help="transcription language, e.g. en")
|
||||
p.add_argument("--chunk-ms", type=int, default=100, help="audio chunk size")
|
||||
p.add_argument("--realtime", action=argparse.BooleanOptionalAction, default=True, help="send the audio in real time")
|
||||
p.add_argument("--out", default="response.wav", help="where the audio of the responses goes")
|
||||
p.add_argument("--url", default="http://127.0.0.1:9931/v1")
|
||||
p.add_argument("--api-key", default="none")
|
||||
p.add_argument("--model", default="default", help="model name, used by the router")
|
||||
asyncio.run(main(p.parse_args()))
|
||||
+2
-1
@@ -4,7 +4,7 @@ project("ggml" C CXX ASM)
|
||||
|
||||
### GGML Version
|
||||
set(GGML_VERSION_MAJOR 0)
|
||||
set(GGML_VERSION_MINOR 27)
|
||||
set(GGML_VERSION_MINOR 28)
|
||||
set(GGML_VERSION_PATCH 0)
|
||||
set(GGML_VERSION_BASE "${GGML_VERSION_MAJOR}.${GGML_VERSION_MINOR}.${GGML_VERSION_PATCH}")
|
||||
|
||||
@@ -250,6 +250,7 @@ option(GGML_OPENMP_FETCH "ggml: fetch LLVM OpenMP"
|
||||
option(GGML_RPC "ggml: use RPC" OFF)
|
||||
option(GGML_SYCL "ggml: use SYCL" OFF)
|
||||
option(GGML_SYCL_F16 "ggml: use 16 bit floats for sycl calculations" OFF)
|
||||
option(GGML_SYCL_SUPPORT_XMX "ggml: build the XMX (DPAS) code paths in the SYCL backend" ON)
|
||||
option(GGML_SYCL_GRAPH "ggml: enable graphs in the SYCL backend" ON)
|
||||
option(GGML_SYCL_HOST_MEM_FALLBACK "ggml: allow host memory fallback in SYCL reorder (requires kernel 6.8+)" ON)
|
||||
option(GGML_SYCL_SUPPORT_LEVEL_ZERO_API "ggml: use Level Zero API in SYCL backend" ON)
|
||||
|
||||
@@ -535,6 +535,60 @@ void ggml_gemv_mxfp4_4x4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const v
|
||||
UNUSED(ncols_interleaved);
|
||||
UNUSED(blocklen);
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_SVE)
|
||||
if (svcntb() * 8 == 256) {
|
||||
const svbool_t pg16 = svptrue_pat_b8(SV_VL16);
|
||||
const svbool_t pg4 = svptrue_pat_b32(SV_VL4);
|
||||
const svbool_t pgb = svptrue_b8();
|
||||
const svbool_t pgw = svptrue_b32();
|
||||
const svbool_t pg_hi4 = svnot_b_z(pgw, pg4);
|
||||
const svuint8_t m4b = svdup_n_u8(0x0f);
|
||||
const svuint8_t values = svld1_u8(pg16, (const uint8_t *) kvalues_mxfp4);
|
||||
|
||||
// Reorder Q8 four-byte groups as [0,2,4,6,1,3,5,7]
|
||||
const svuint32_t q8_group_index = svsub_n_u32_m(pg_hi4, svindex_u32(0, 2), 7);
|
||||
|
||||
const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
|
||||
float * res_ptr = s;
|
||||
|
||||
for (int x = 0; x < nc / ncols_interleaved; x++) {
|
||||
const block_mxfp4x4 * b_ptr = (const block_mxfp4x4 *) vx + (x * nb);
|
||||
svfloat32_t sumf = svdup_n_f32(0.0f);
|
||||
|
||||
for (int l = 0; l < nb; l++) {
|
||||
const svint8_t q8 = svld1_s8(pgb, a_ptr[l].qs);
|
||||
const svint8_t q8_even_odd = svreinterpret_s8_u32(svtbl_u32(svreinterpret_u32_s8(q8), q8_group_index));
|
||||
const svuint8_t qmx01 = svld1_u8(pgb, b_ptr[l].qs + 0);
|
||||
const svuint8_t qmx_lo01 = svtbl_u8(values, svand_u8_x(pgb, qmx01, m4b));
|
||||
const svuint8_t qmx_hi01 = svtbl_u8(values, svlsr_n_u8_x(pgb, qmx01, 4));
|
||||
const svuint8_t qmx23 = svld1_u8(pgb, b_ptr[l].qs + 32);
|
||||
const svuint8_t qmx_lo23 = svtbl_u8(values, svand_u8_x(pgb, qmx23, m4b));
|
||||
const svuint8_t qmx_hi23 = svtbl_u8(values, svlsr_n_u8_x(pgb, qmx23, 4));
|
||||
|
||||
svint32_t sumi = svdup_n_s32(0);
|
||||
sumi = svdot_lane_s32(sumi, svreinterpret_s8_u8(qmx_lo01), q8_even_odd, 0);
|
||||
sumi = svdot_lane_s32(sumi, svreinterpret_s8_u8(qmx_lo23), q8_even_odd, 1);
|
||||
sumi = svdot_lane_s32(sumi, svreinterpret_s8_u8(qmx_hi01), q8_even_odd, 2);
|
||||
sumi = svdot_lane_s32(sumi, svreinterpret_s8_u8(qmx_hi23), q8_even_odd, 3);
|
||||
const svint32_t sumi_cols = svadd_s32_z(pg4, sumi, svext_s32(sumi, sumi, 4));
|
||||
|
||||
// Vector form of GGML_CPU_E8M0_TO_FP32_HALF
|
||||
const svuint32_t e_u32 = svld1ub_u32(pg4, b_ptr[l].e);
|
||||
const svbool_t e_is_denormal = svcmplt_n_u32(pg4, e_u32, 2);
|
||||
const svuint32_t e_denormal_bits = svlsl_u32_x(pg4, svdup_n_u32(0x00200000), e_u32);
|
||||
const svuint32_t e_normal_bits = svlsl_n_u32_x(pg4, svsub_n_u32_x(pg4, e_u32, 1), 23);
|
||||
const svuint32_t e_bits = svsel_u32(e_is_denormal, e_denormal_bits, e_normal_bits);
|
||||
const svfloat16_t d_f16 = svreinterpret_f16_u16(svdup_n_u16(a_ptr[l].d));
|
||||
const svfloat32_t d_f32 = svcvt_f32_f16_x(pg4, d_f16);
|
||||
const svfloat32_t scale = svmul_f32_x(pg4, svreinterpret_f32_u32(e_bits), d_f32);
|
||||
sumf = svmla_f32_m(pg4, sumf,svcvt_f32_s32_m(svdup_n_f32(0.0f), pg4, sumi_cols), scale);
|
||||
}
|
||||
svst1_f32(pg4, res_ptr + x * 4, sumf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif // defined(__aarch64__) && defined(__ARM_FEATURE_SVE)
|
||||
|
||||
#if ! ((defined(_MSC_VER)) && ! defined(__clang__)) && defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
|
||||
const int8x16_t kvalues = vld1q_s8(kvalues_mxfp4);
|
||||
const block_q8_0 * a_ptr = (const block_q8_0 *) vy;
|
||||
@@ -1791,6 +1845,71 @@ void ggml_gemv_q8_0_4x8_q8_0(int n,
|
||||
UNUSED(ncols_interleaved);
|
||||
UNUSED(blocklen);
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_SVE)
|
||||
// 256-bit SVE: 32 x int8 -> 8 x int32
|
||||
if (ggml_cpu_get_sve_cnt() == QK8_0) {
|
||||
const block_q8_0x4 * GGML_RESTRICT b_ptr = (const block_q8_0x4 *) vx;
|
||||
|
||||
const svbool_t pg = svptrue_b8();
|
||||
const svbool_t pg4w = svptrue_pat_b32(SV_VL4);
|
||||
|
||||
// after svtbl + svcvt the scale lanes are [d0,d0,d1,d1,d2,d2,d3,d3]: lane pair j = row j
|
||||
const svuint16_t d_idx = svreinterpret_u16_u32(svlsr_n_u32_x(pg, svindex_u32(0, 1), 1));
|
||||
|
||||
for (int c = 0; c < nc; c += ncols_interleaved) {
|
||||
const block_q8_0 * GGML_RESTRICT a_ptr = (const block_q8_0 *) vy;
|
||||
|
||||
svfloat32_t acc = svdup_n_f32(0.0f);
|
||||
|
||||
for (int b = 0; b < nb; ++b) {
|
||||
const int8_t * GGML_RESTRICT bq = (const int8_t *) b_ptr->qs;
|
||||
|
||||
uint64_t a_chunk0;
|
||||
uint64_t a_chunk1;
|
||||
uint64_t a_chunk2;
|
||||
uint64_t a_chunk3;
|
||||
uint64_t b_d;
|
||||
|
||||
memcpy(&a_chunk0, a_ptr->qs + 0, sizeof(a_chunk0));
|
||||
memcpy(&a_chunk1, a_ptr->qs + 8, sizeof(a_chunk1));
|
||||
memcpy(&a_chunk2, a_ptr->qs + 16, sizeof(a_chunk2));
|
||||
memcpy(&a_chunk3, a_ptr->qs + 24, sizeof(a_chunk3));
|
||||
memcpy(&b_d, b_ptr->d, sizeof(b_d));
|
||||
|
||||
const svint8_t aq0 = svreinterpret_s8_u64(svdup_n_u64(a_chunk0));
|
||||
const svint8_t aq1 = svreinterpret_s8_u64(svdup_n_u64(a_chunk1));
|
||||
const svint8_t aq2 = svreinterpret_s8_u64(svdup_n_u64(a_chunk2));
|
||||
const svint8_t aq3 = svreinterpret_s8_u64(svdup_n_u64(a_chunk3));
|
||||
|
||||
const svint8_t bq0 = svld1_s8(pg, bq + 0);
|
||||
const svint8_t bq1 = svld1_s8(pg, bq + 32);
|
||||
const svint8_t bq2 = svld1_s8(pg, bq + 64);
|
||||
const svint8_t bq3 = svld1_s8(pg, bq + 96);
|
||||
|
||||
const svint32_t iacc0 = svdot_s32(svdup_n_s32(0), bq0, aq0);
|
||||
const svint32_t iacc1 = svdot_s32(svdup_n_s32(0), bq1, aq1);
|
||||
const svint32_t iacc2 = svdot_s32(svdup_n_s32(0), bq2, aq2);
|
||||
const svint32_t iacc3 = svdot_s32(svdup_n_s32(0), bq3, aq3);
|
||||
|
||||
const svint32_t isum = svadd_s32_x(pg, svadd_s32_x(pg, iacc0, iacc1), svadd_s32_x(pg, iacc2, iacc3));
|
||||
|
||||
const svfloat16_t bd_h = svtbl_f16(svreinterpret_f16_u64(svdup_n_u64(b_d)), d_idx);
|
||||
const svfloat32_t scale = svmul_n_f32_x(pg, svcvt_f32_f16_x(pg, bd_h), GGML_CPU_FP16_TO_FP32(a_ptr->d));
|
||||
|
||||
acc = svmla_f32_x(pg, acc, svcvt_f32_s32_x(pg, isum), scale);
|
||||
|
||||
++a_ptr;
|
||||
++b_ptr;
|
||||
}
|
||||
|
||||
svst1_f32(pg4w, s, svadd_f32_x(pg, svuzp1_f32(acc, acc), svuzp2_f32(acc, acc)));
|
||||
s += ncols_interleaved;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif // defined(__aarch64__) && defined(__ARM_FEATURE_SVE)
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_DOTPROD)
|
||||
const block_q8_0x4 * b_ptr = (const block_q8_0x4 *) vx;
|
||||
|
||||
@@ -4874,6 +4993,7 @@ void ggml_gemm_q6_K_8x8_q8_K(int n,
|
||||
|
||||
constexpr int ncols_interleaved = 8;
|
||||
constexpr int blocklen = 8;
|
||||
constexpr int q8_k_blocklen = 4;
|
||||
|
||||
assert(n % qk == 0);
|
||||
assert(nr % 4 == 0);
|
||||
@@ -4882,9 +5002,195 @@ void ggml_gemm_q6_K_8x8_q8_K(int n,
|
||||
UNUSED(nb);
|
||||
UNUSED(ncols_interleaved);
|
||||
UNUSED(blocklen);
|
||||
UNUSED(q8_k_blocklen);
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_FEATURE_SVE) && defined(__ARM_FEATURE_MATMUL_INT8)
|
||||
if (svcntb() * 8 == 256) {
|
||||
const svbool_t pg8 = svptrue_b8();
|
||||
const svbool_t pg32 = svptrue_b32();
|
||||
|
||||
const svuint8_t low_nibble = svdup_n_u8(0x0f);
|
||||
const svuint8_t high_lo2 = svdup_n_u8(0x03);
|
||||
const svuint8_t high_hi2 = svdup_n_u8(0x30);
|
||||
const svint8_t q6_bias = svdup_n_s8(32);
|
||||
const svint32_t zero_s32 = svdup_n_s32(0);
|
||||
|
||||
for (int y = 0; y < nr / q8_k_blocklen; ++y) {
|
||||
const block_q8_Kx4 * GGML_RESTRICT q8_ptr = (const block_q8_Kx4 *) vy + y * nb;
|
||||
|
||||
for (int x = 0; x < nc / ncols_interleaved; ++x) {
|
||||
const block_q6_Kx8 * GGML_RESTRICT q6_ptr = (const block_q6_Kx8 *) vx + x * nb;
|
||||
|
||||
// one accumlator per output row
|
||||
svfloat32_t acc_f32_r0 = svdup_n_f32(0);
|
||||
svfloat32_t acc_f32_r1 = svdup_n_f32(0);
|
||||
svfloat32_t acc_f32_r2 = svdup_n_f32(0);
|
||||
svfloat32_t acc_f32_r3 = svdup_n_f32(0);
|
||||
|
||||
for (int b = 0; b < nb; ++b) {
|
||||
svint32_t acc_c03_r01 = zero_s32;
|
||||
svint32_t acc_c03_r23 = zero_s32;
|
||||
svint32_t acc_c47_r01 = zero_s32;
|
||||
svint32_t acc_c47_r23 = zero_s32;
|
||||
|
||||
for (int half = 0; half < 2; ++half) {
|
||||
const uint8_t * ql_base = q6_ptr[b].ql + half * 512;
|
||||
const uint8_t * qh_base = q6_ptr[b].qh + half * 256;
|
||||
|
||||
for (int sb = 0; sb < QK_K / 64; ++sb) {
|
||||
const int8_t * q8_base_l = q8_ptr[b].qs + half * 512 + sb * 64;
|
||||
const int8_t * q8_base_h = q8_ptr[b].qs + half * 512 + 256 + sb * 64;
|
||||
|
||||
// load 16 bytes (8B of R0, 8B of R1) and repeat in upper half
|
||||
// low lanes will multiply with 16 bytes of C0 and high lanes will multiply with 16 bytes of C1
|
||||
const svint8_t q8_l_r01_k0 = svld1rq_s8(pg8, q8_base_l + 0);
|
||||
const svint8_t q8_l_r01_k1 = svld1rq_s8(pg8, q8_base_l + 32);
|
||||
const svint8_t q8_l_r23_k0 = svld1rq_s8(pg8, q8_base_l + 16);
|
||||
const svint8_t q8_l_r23_k1 = svld1rq_s8(pg8, q8_base_l + 48);
|
||||
|
||||
const svint8_t q8_h_r01_k0 = svld1rq_s8(pg8, q8_base_h + 0);
|
||||
const svint8_t q8_h_r01_k1 = svld1rq_s8(pg8, q8_base_h + 32);
|
||||
const svint8_t q8_h_r23_k0 = svld1rq_s8(pg8, q8_base_h + 16);
|
||||
const svint8_t q8_h_r23_k1 = svld1rq_s8(pg8, q8_base_h + 48);
|
||||
|
||||
const int ql_off = sb * QK_K / 2;
|
||||
const int qh_off = ql_off & 255;
|
||||
|
||||
const int scale_idx_l = half * 8 + sb;
|
||||
const int scale_idx_h = scale_idx_l + 4;
|
||||
|
||||
const svint32_t scales_l_all = svld1sb_s32(pg32, q6_ptr[b].scales + scale_idx_l * 8);
|
||||
const svint32_t scales_h_all = svld1sb_s32(pg32, q6_ptr[b].scales + scale_idx_h * 8);
|
||||
|
||||
const svint32_t scales_l_c03 = svzip1_s32(scales_l_all, scales_l_all);
|
||||
const svint32_t scales_h_c03 = svzip1_s32(scales_h_all, scales_h_all);
|
||||
|
||||
{
|
||||
svuint8_t q6_ql0_01 = svld1_u8(pg8, ql_base + ql_off);
|
||||
svuint8_t q6_ql1_01 = svld1_u8(pg8, ql_base + ql_off + 64);
|
||||
svuint8_t q6_qh0_01 = svld1_u8(pg8, qh_base + qh_off);
|
||||
svuint8_t q6_qh1_01 = svld1_u8(pg8, qh_base + qh_off + 64);
|
||||
|
||||
if (sb > 1) {
|
||||
q6_qh0_01 = svlsr_n_u8_x(pg8, q6_qh0_01, 2);
|
||||
q6_qh1_01 = svlsr_n_u8_x(pg8, q6_qh1_01, 2);
|
||||
}
|
||||
|
||||
const svuint8_t q6_qh0_01_hi = svand_u8_x(pg8, q6_qh0_01, high_hi2);
|
||||
const svuint8_t q6_qh1_01_hi = svand_u8_x(pg8, q6_qh1_01, high_hi2);
|
||||
|
||||
const svuint8_t q6_l0_01_bits = svorr_u8_x(pg8, svand_u8_x(pg8, q6_ql0_01, low_nibble), svlsl_n_u8_x(pg8, svand_u8_x(pg8, q6_qh0_01, high_lo2), 4));
|
||||
const svuint8_t q6_l1_01_bits = svorr_u8_x(pg8, svand_u8_x(pg8, q6_ql1_01, low_nibble), svlsl_n_u8_x(pg8, svand_u8_x(pg8, q6_qh1_01, high_lo2), 4));
|
||||
const svuint8_t q6_h0_01_bits = svorr_u8_x(pg8, svlsr_n_u8_x(pg8, q6_ql0_01, 4), q6_qh0_01_hi);
|
||||
const svuint8_t q6_h1_01_bits = svorr_u8_x(pg8, svlsr_n_u8_x(pg8, q6_ql1_01, 4), q6_qh1_01_hi);
|
||||
|
||||
const svint8_t q6_l0_01 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_l0_01_bits), q6_bias);
|
||||
const svint8_t q6_l1_01 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_l1_01_bits), q6_bias);
|
||||
const svint8_t q6_h0_01 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_h0_01_bits), q6_bias);
|
||||
const svint8_t q6_h1_01 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_h1_01_bits), q6_bias);
|
||||
|
||||
svint32_t dot_l_r01 = svmmla_s32(zero_s32, q6_l0_01, q8_l_r01_k0);
|
||||
dot_l_r01 = svmmla_s32(dot_l_r01, q6_l1_01, q8_l_r01_k1);
|
||||
|
||||
svint32_t dot_h_r01 = svmmla_s32(zero_s32, q6_h0_01, q8_h_r01_k0);
|
||||
dot_h_r01 = svmmla_s32(dot_h_r01, q6_h1_01, q8_h_r01_k1);
|
||||
|
||||
svint32_t dot_l_r23 = svmmla_s32(zero_s32, q6_l0_01, q8_l_r23_k0);
|
||||
dot_l_r23 = svmmla_s32(dot_l_r23, q6_l1_01, q8_l_r23_k1);
|
||||
|
||||
svint32_t dot_h_r23 = svmmla_s32(zero_s32, q6_h0_01, q8_h_r23_k0);
|
||||
dot_h_r23 = svmmla_s32(dot_h_r23, q6_h1_01, q8_h_r23_k1);
|
||||
|
||||
acc_c03_r01 = svmla_s32_x(pg32, acc_c03_r01, dot_l_r01, scales_l_c03);
|
||||
acc_c03_r01 = svmla_s32_x(pg32, acc_c03_r01, dot_h_r01, scales_h_c03);
|
||||
acc_c03_r23 = svmla_s32_x(pg32, acc_c03_r23, dot_l_r23, scales_l_c03);
|
||||
acc_c03_r23 = svmla_s32_x(pg32, acc_c03_r23, dot_h_r23, scales_h_c03);
|
||||
}
|
||||
|
||||
const svint32_t scales_l_c47 = svzip2_s32(scales_l_all, scales_l_all);
|
||||
const svint32_t scales_h_c47 = svzip2_s32(scales_h_all, scales_h_all);
|
||||
|
||||
{
|
||||
svuint8_t q6_ql0_23 = svld1_u8(pg8, ql_base + ql_off + 32);
|
||||
svuint8_t q6_ql1_23 = svld1_u8(pg8, ql_base + ql_off + 96);
|
||||
svuint8_t q6_qh0_23 = svld1_u8(pg8, qh_base + qh_off + 32);
|
||||
svuint8_t q6_qh1_23 = svld1_u8(pg8, qh_base + qh_off + 96);
|
||||
|
||||
if (sb > 1) {
|
||||
q6_qh0_23 = svlsr_n_u8_x(pg8, q6_qh0_23, 2);
|
||||
q6_qh1_23 = svlsr_n_u8_x(pg8, q6_qh1_23, 2);
|
||||
}
|
||||
|
||||
const svuint8_t q6_qh0_23_hi = svand_u8_x(pg8, q6_qh0_23, high_hi2);
|
||||
const svuint8_t q6_qh1_23_hi = svand_u8_x(pg8, q6_qh1_23, high_hi2);
|
||||
|
||||
const svuint8_t q6_l0_23_bits = svorr_u8_x(pg8, svand_u8_x(pg8, q6_ql0_23, low_nibble), svlsl_n_u8_x(pg8, svand_u8_x(pg8, q6_qh0_23, high_lo2), 4));
|
||||
const svuint8_t q6_l1_23_bits = svorr_u8_x(pg8, svand_u8_x(pg8, q6_ql1_23, low_nibble), svlsl_n_u8_x(pg8, svand_u8_x(pg8, q6_qh1_23, high_lo2), 4));
|
||||
const svuint8_t q6_h0_23_bits = svorr_u8_x(pg8, svlsr_n_u8_x(pg8, q6_ql0_23, 4), q6_qh0_23_hi);
|
||||
const svuint8_t q6_h1_23_bits = svorr_u8_x(pg8, svlsr_n_u8_x(pg8, q6_ql1_23, 4), q6_qh1_23_hi);
|
||||
|
||||
const svint8_t q6_l0_23 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_l0_23_bits), q6_bias);
|
||||
const svint8_t q6_l1_23 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_l1_23_bits), q6_bias);
|
||||
const svint8_t q6_h0_23 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_h0_23_bits), q6_bias);
|
||||
const svint8_t q6_h1_23 = svsub_s8_x(pg8, svreinterpret_s8_u8(q6_h1_23_bits), q6_bias);
|
||||
|
||||
svint32_t dot_l_r01 = svmmla_s32(zero_s32, q6_l0_23, q8_l_r01_k0);
|
||||
dot_l_r01 = svmmla_s32(dot_l_r01, q6_l1_23, q8_l_r01_k1);
|
||||
|
||||
svint32_t dot_h_r01 = svmmla_s32(zero_s32, q6_h0_23, q8_h_r01_k0);
|
||||
dot_h_r01 = svmmla_s32(dot_h_r01, q6_h1_23, q8_h_r01_k1);
|
||||
|
||||
svint32_t dot_l_r23 = svmmla_s32(zero_s32, q6_l0_23, q8_l_r23_k0);
|
||||
dot_l_r23 = svmmla_s32(dot_l_r23, q6_l1_23, q8_l_r23_k1);
|
||||
|
||||
svint32_t dot_h_r23 = svmmla_s32(zero_s32, q6_h0_23, q8_h_r23_k0);
|
||||
dot_h_r23 = svmmla_s32(dot_h_r23, q6_h1_23, q8_h_r23_k1);
|
||||
|
||||
acc_c47_r01 = svmla_s32_x(pg32, acc_c47_r01, dot_l_r01, scales_l_c47);
|
||||
acc_c47_r01 = svmla_s32_x(pg32, acc_c47_r01, dot_h_r01, scales_h_c47);
|
||||
acc_c47_r23 = svmla_s32_x(pg32, acc_c47_r23, dot_l_r23, scales_l_c47);
|
||||
acc_c47_r23 = svmla_s32_x(pg32, acc_c47_r23, dot_h_r23, scales_h_c47);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// acc_c03_r01 -> c0-r0,c0-r1,c1-r0,c1-r1,c2-r0,c2-r1,c3-r0,c3-r1
|
||||
// acc_c47_r01 -> c4-r0,c4-r1,c5-r0,c5-r1,c6-r0,c6-r1,c7-r0,c7-r1
|
||||
// svuzp1_s32 picks even lanes -> c0-r0,c1-r0,c2-r0,c3-r0,c4-r0,c5-r0,c6-r0,c7-r0
|
||||
// similarly svuzp2_s32 picks remaining values from odd lanes
|
||||
const svint32_t dot_r0 = svuzp1_s32(acc_c03_r01, acc_c47_r01);
|
||||
const svint32_t dot_r1 = svuzp2_s32(acc_c03_r01, acc_c47_r01);
|
||||
const svint32_t dot_r2 = svuzp1_s32(acc_c03_r23, acc_c47_r23);
|
||||
const svint32_t dot_r3 = svuzp2_s32(acc_c03_r23, acc_c47_r23);
|
||||
|
||||
const svuint32_t q6_d_bits = svld1uh_u32(pg32, (const uint16_t *) q6_ptr[b].d);
|
||||
const svfloat32_t q6_d_f32 = svcvt_f32_f16_x(pg32, svreinterpret_f16_u32(q6_d_bits));
|
||||
|
||||
const svfloat32_t scale_r0 = svmul_n_f32_x(pg32, q6_d_f32, q8_ptr[b].d[0]);
|
||||
const svfloat32_t scale_r1 = svmul_n_f32_x(pg32, q6_d_f32, q8_ptr[b].d[1]);
|
||||
const svfloat32_t scale_r2 = svmul_n_f32_x(pg32, q6_d_f32, q8_ptr[b].d[2]);
|
||||
const svfloat32_t scale_r3 = svmul_n_f32_x(pg32, q6_d_f32, q8_ptr[b].d[3]);
|
||||
|
||||
acc_f32_r0 = svmla_f32_x(pg32, acc_f32_r0, svcvt_f32_s32_x(pg32, dot_r0), scale_r0);
|
||||
acc_f32_r1 = svmla_f32_x(pg32, acc_f32_r1, svcvt_f32_s32_x(pg32, dot_r1), scale_r1);
|
||||
acc_f32_r2 = svmla_f32_x(pg32, acc_f32_r2, svcvt_f32_s32_x(pg32, dot_r2), scale_r2);
|
||||
acc_f32_r3 = svmla_f32_x(pg32, acc_f32_r3, svcvt_f32_s32_x(pg32, dot_r3), scale_r3);
|
||||
}
|
||||
|
||||
const int row = y * q8_k_blocklen;
|
||||
const int col = x * ncols_interleaved;
|
||||
|
||||
svst1_f32(pg32, s + (row + 0) * bs + col, acc_f32_r0);
|
||||
svst1_f32(pg32, s + (row + 1) * bs + col, acc_f32_r1);
|
||||
svst1_f32(pg32, s + (row + 2) * bs + col, acc_f32_r2);
|
||||
svst1_f32(pg32, s + (row + 3) * bs + col, acc_f32_r3);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif // SVE compile-time end
|
||||
|
||||
#if defined(__aarch64__) && defined(__ARM_NEON) && defined(__ARM_FEATURE_MATMUL_INT8)
|
||||
constexpr int q8_k_blocklen = 4;
|
||||
const uint8x16_t m4b = vdupq_n_u8(0x0f);
|
||||
const uint8x16_t mask_lo = vdupq_n_u8(0x03);
|
||||
const uint8x16_t mask_hi = vdupq_n_u8(0x30);
|
||||
|
||||
+90
-210
@@ -1914,213 +1914,87 @@ void ggml_compute_forward_repeat_back(
|
||||
}
|
||||
|
||||
// ggml_compute_forward_concat
|
||||
static void ggml_compute_forward_concat_any(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
// rows of dst copied together, so that a strided source is read along neighboring rows
|
||||
static constexpr int64_t GGML_CONCAT_TILE = 32;
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src1));
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
|
||||
if (dim == 0) {
|
||||
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
|
||||
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
|
||||
|
||||
o[dim] = src0->ne[dim]/ggml_blck_size(src0->type);
|
||||
} else {
|
||||
o[dim] = src0->ne[dim];
|
||||
}
|
||||
|
||||
// Region 1: copy rows from src0
|
||||
for (int i3 = 0; i3 < ne03; i3++) {
|
||||
for (int i2 = ith; i2 < ne02; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne01; i1++) {
|
||||
const char * x = (const char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03;
|
||||
char * y = ( char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3;
|
||||
memcpy(y, x, ggml_row_size(src0->type, ne00));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Region 2: copy rows from src1, offset into dst by o[]
|
||||
for (int i3 = 0; i3 < ne13; i3++) {
|
||||
for (int i2 = ith; i2 < ne12; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne11; i1++) {
|
||||
const char * x = (const char *) src1->data + i1*nb11 + i2*nb12 + i3*nb13;
|
||||
char * y = ( char *) dst->data + (i1 + o[1])*nb1 + (i2 + o[2])*nb2 + (i3 + o[3])*nb3 + o[0]*nb0;
|
||||
memcpy(y, x, ggml_row_size(src1->type, ne10));
|
||||
}
|
||||
// copy n_rows rows of n elements of es bytes from a source with element stride nb0, element by element
|
||||
template <size_t es>
|
||||
static void ggml_concat_copy_strided(char * y, size_t nby, const char * x, size_t nbx, size_t nb0, int64_t n, int64_t n_rows) {
|
||||
for (int64_t i0 = 0; i0 < n; i0++) {
|
||||
for (int64_t r = 0; r < n_rows; r++) {
|
||||
memcpy(y + r*nby + i0*es, x + r*nbx + i0*nb0, es);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_i8(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
// copy n_rows rows of n elements of src into dst, rows are nbx bytes apart in src and nby bytes apart in dst
|
||||
static void ggml_concat_copy_rows(char * y, size_t nby, const ggml_tensor * src, const char * x, size_t nbx, int64_t n, int64_t n_rows) {
|
||||
const size_t es = ggml_type_size(src->type);
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(int8_t));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const int8_t * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const int8_t *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const int8_t *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
int8_t * y = (int8_t *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_f16(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(ggml_fp16_t));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const ggml_fp16_t * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const ggml_fp16_t *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const ggml_fp16_t *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
ggml_fp16_t * y = (ggml_fp16_t *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ggml_compute_forward_concat_f32(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
GGML_ASSERT(ggml_type_size(src0->type) == sizeof(float));
|
||||
|
||||
const int ith = params->ith;
|
||||
const int nth = params->nth;
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
|
||||
int64_t o[4] = {0, 0, 0, 0};
|
||||
o[dim] = src0->ne[dim];
|
||||
|
||||
const float * x;
|
||||
|
||||
// TODO: smarter multi-theading
|
||||
for (int i3 = 0; i3 < ne3; i3++) {
|
||||
for (int i2 = ith; i2 < ne2; i2 += nth) {
|
||||
for (int i1 = 0; i1 < ne1; i1++) {
|
||||
for (int i0 = 0; i0 < ne0; i0++) {
|
||||
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
||||
x = (const float *) ((const char *)src0->data + (i0 )*nb00 + (i1 )*nb01 + (i2 )*nb02 + (i3 )*nb03);
|
||||
} else {
|
||||
x = (const float *) ((const char *)src1->data + (i0 - o[0])*nb10 + (i1 - o[1])*nb11 + (i2 - o[2])*nb12 + (i3 - o[3])*nb13);
|
||||
}
|
||||
|
||||
float * y = (float *)((char *)dst->data + i0*nb0 + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
|
||||
*y = *x;
|
||||
}
|
||||
}
|
||||
if (src->nb[0] == es) {
|
||||
const size_t row_size = ggml_row_size(src->type, n);
|
||||
for (int64_t r = 0; r < n_rows; r++) {
|
||||
memcpy(y + r*nby, x + r*nbx, row_size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (es) {
|
||||
case 1: ggml_concat_copy_strided<1>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 2: ggml_concat_copy_strided<2>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 4: ggml_concat_copy_strided<4>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
case 8: ggml_concat_copy_strided<8>(y, nby, x, nbx, src->nb[0], n, n_rows); break;
|
||||
default: GGML_ABORT("unsupported element size");
|
||||
}
|
||||
}
|
||||
|
||||
// each dst row comes from src0 or src1, or for dim 0 from the row of src0 followed by the row of src1
|
||||
void ggml_compute_forward_concat(
|
||||
const ggml_compute_params * params,
|
||||
ggml_tensor * dst) {
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
switch (src0->type) {
|
||||
case GGML_TYPE_F16:
|
||||
case GGML_TYPE_BF16:
|
||||
case GGML_TYPE_I16:
|
||||
{
|
||||
ggml_compute_forward_concat_f16(params, dst);
|
||||
} break;
|
||||
case GGML_TYPE_I8:
|
||||
{
|
||||
ggml_compute_forward_concat_i8(params, dst);
|
||||
} break;
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_I32:
|
||||
{
|
||||
ggml_compute_forward_concat_f32(params, dst);
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
ggml_compute_forward_concat_any(params, dst);
|
||||
}
|
||||
GGML_TENSOR_BINARY_OP_LOCALS
|
||||
|
||||
const int32_t dim = ggml_get_op_params_i32(dst, 0);
|
||||
|
||||
GGML_ASSERT(dim >= 0 && dim < 4);
|
||||
GGML_ASSERT(nb0 == ggml_type_size(dst->type));
|
||||
GGML_ASSERT(!ggml_is_quantized(dst->type) || (nb00 == nb0 && nb10 == nb0));
|
||||
GGML_ASSERT(dim != 0 || (ne00 % ggml_blck_size(dst->type) == 0 && ne10 % ggml_blck_size(dst->type) == 0));
|
||||
|
||||
const size_t y1 = dim == 0 ? ggml_row_size(dst->type, ne00) : 0;
|
||||
|
||||
const int64_t nr = ne1*ne2*ne3;
|
||||
const int64_t dr = (nr + params->nth - 1)/params->nth;
|
||||
|
||||
const int64_t ir0 = dr*params->ith;
|
||||
const int64_t ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
for (int64_t ir = ir0; ir < ir1; ) {
|
||||
int64_t i[4] = { 0, ir % ne1, (ir/ne1) % ne2, ir/(ne1*ne2) };
|
||||
|
||||
// a tile of consecutive rows sharing i2, i3 and their source
|
||||
int64_t nt = MIN(MIN(ir1 - ir, ne1 - i[1]), GGML_CONCAT_TILE);
|
||||
if (dim == 1 && i[1] < ne01) {
|
||||
nt = MIN(nt, ne01 - i[1]);
|
||||
}
|
||||
|
||||
char * y = (char *) dst->data + i[1]*nb1 + i[2]*nb2 + i[3]*nb3;
|
||||
|
||||
if (dim == 0 || i[dim] < src0->ne[dim]) {
|
||||
ggml_concat_copy_rows(y, nb1, src0, (const char *) src0->data + i[1]*nb01 + i[2]*nb02 + i[3]*nb03, nb01, ne00, nt);
|
||||
}
|
||||
|
||||
if (dim == 0 || i[dim] >= src0->ne[dim]) {
|
||||
i[dim] -= src0->ne[dim]; // i[0] does not address rows
|
||||
ggml_concat_copy_rows(y + y1, nb1, src1, (const char *) src1->data + i[1]*nb11 + i[2]*nb12 + i[3]*nb13, nb11, ne10, nt);
|
||||
}
|
||||
|
||||
ir += nt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4972,8 +4846,8 @@ static void ggml_compute_forward_scale_f32(
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(dst));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(dst));
|
||||
GGML_ASSERT(ggml_are_same_shape(src0, dst));
|
||||
|
||||
float s; // scale factor
|
||||
@@ -4988,6 +4862,8 @@ static void ggml_compute_forward_scale_f32(
|
||||
const int nc = src0->ne[0];
|
||||
const int nr = ggml_nrows(src0);
|
||||
|
||||
GGML_TENSOR_UNARY_OP_LOCALS
|
||||
|
||||
// rows per thread
|
||||
const int dr = (nr + nth - 1)/nth;
|
||||
|
||||
@@ -4995,25 +4871,23 @@ static void ggml_compute_forward_scale_f32(
|
||||
const int ir0 = dr*ith;
|
||||
const int ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
const size_t nb01 = src0->nb[1];
|
||||
for (int i = ir0; i < ir1; i++) {
|
||||
const int64_t i1 = i % ne01;
|
||||
const int64_t i2 = (i / ne01) % ne02;
|
||||
const int64_t i3 = i / (ne01*ne02);
|
||||
|
||||
const size_t nb1 = dst->nb[1];
|
||||
float * y = (float *) ((char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
const float * x = (const float *) ((char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03);
|
||||
|
||||
if (b == 0.0f) {
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
if (b == 0.0f) {
|
||||
if (dst->data != src0->data) {
|
||||
// src0 is same shape as dst => same indices
|
||||
// TODO: add x parameter to ggml_vec_scale_f32 and remove this memcpy
|
||||
memcpy((char *)dst->data + i1*nb1, (char *)src0->data + i1*nb01, nc * sizeof(float));
|
||||
memcpy(y, x, nc * sizeof(float));
|
||||
}
|
||||
ggml_vec_scale_f32(nc, (float *) ((char *) dst->data + i1*nb1), s);
|
||||
}
|
||||
} else {
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
ggml_vec_mad1_f32(nc,
|
||||
(float *) ((char *) dst->data + i1*nb1),
|
||||
(float *) ((char *) src0->data + i1*nb1),
|
||||
s, b);
|
||||
ggml_vec_scale_f32(nc, y, s);
|
||||
} else {
|
||||
ggml_vec_mad1_f32(nc, y, x, s, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5024,8 +4898,8 @@ static void ggml_compute_forward_scale_bf16(
|
||||
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
|
||||
GGML_ASSERT(ggml_is_contiguous(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous(dst));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0));
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(dst));
|
||||
GGML_ASSERT(ggml_are_same_shape(src0, dst));
|
||||
|
||||
float s; // scale factor
|
||||
@@ -5040,6 +4914,8 @@ static void ggml_compute_forward_scale_bf16(
|
||||
const int nc = src0->ne[0];
|
||||
const int nr = ggml_nrows(src0);
|
||||
|
||||
GGML_TENSOR_UNARY_OP_LOCALS
|
||||
|
||||
// rows per thread
|
||||
const int dr = (nr + nth - 1)/nth;
|
||||
|
||||
@@ -5047,9 +4923,13 @@ static void ggml_compute_forward_scale_bf16(
|
||||
const int ir0 = dr*ith;
|
||||
const int ir1 = MIN(ir0 + dr, nr);
|
||||
|
||||
for (int i1 = ir0; i1 < ir1; i1++) {
|
||||
const ggml_bf16_t * x = (const ggml_bf16_t *) ((const char *) src0->data + i1*src0->nb[1]);
|
||||
ggml_bf16_t * y = (ggml_bf16_t *) ((char *) dst->data + i1*dst->nb[1]);
|
||||
for (int i = ir0; i < ir1; i++) {
|
||||
const int64_t i1 = i % ne01;
|
||||
const int64_t i2 = (i / ne01) % ne02;
|
||||
const int64_t i3 = i / (ne01*ne02);
|
||||
|
||||
const ggml_bf16_t * x = (const ggml_bf16_t *) ((const char *) src0->data + i1*nb01 + i2*nb02 + i3*nb03);
|
||||
ggml_bf16_t * y = (ggml_bf16_t *) ((char *) dst->data + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
for (int k = 0; k < nc; k++) {
|
||||
y[k] = GGML_FP32_TO_BF16(s*GGML_BF16_TO_FP32(x[k]) + b);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
const int end = int(int64_t(tiles) * (split + 1) / split_k) * BK;
|
||||
for (int k0 = begin; k0 < end; k0 += BK) {
|
||||
if (k_total % 8 == 0 && uintptr_t(weight) % 16 == 0) {
|
||||
#pragma unroll
|
||||
for (int i = tid; i < BM * BK / 8; i += nthreads) {
|
||||
const int row = i / (BK / 8), col = 8 * (i % (BK / 8));
|
||||
const int4 v = m0 + row < oc && k0 + col < k_total ?
|
||||
@@ -189,7 +188,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
*(int4 *) &a_s[row][col / 2] = v;
|
||||
}
|
||||
} else {
|
||||
#pragma unroll
|
||||
for (int i = tid; i < BM * BK / 2; i += nthreads) {
|
||||
const int row = i / (BK / 2), col = 2 * (i % (BK / 2));
|
||||
half lo = __float2half(0.0f), hi = lo;
|
||||
@@ -202,7 +200,6 @@ static __global__ void conv2d_implicit_gemm_f16(const half * __restrict__ input,
|
||||
a_s[row][col / 2] = __halves2half2(lo, hi);
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int k = load_row; k < BK; k += nthreads / (BN / 2)) {
|
||||
const int ki = k0 + k;
|
||||
const int ci = ki / (kw * kh), ky = ki / kw % kh, kx = ki % kw;
|
||||
|
||||
@@ -1962,7 +1962,7 @@ static bool ggml_cuda_mul_mat_q_fusion_matches(const ggml_tensor * up, const ggm
|
||||
const ggml_tensor * x_up = up->src[0];
|
||||
const ggml_tensor * y = up->src[1];
|
||||
|
||||
if (up->op != GGML_OP_MUL_MAT || !ggml_cuda_mmq_fusion_supported(x_up->type) || y->type != GGML_TYPE_F32) {
|
||||
if ((up->op != GGML_OP_MUL_MAT && up->op != GGML_OP_MUL_MAT_ID) || !ggml_cuda_mmq_fusion_supported(x_up->type) || y->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2004,12 +2004,18 @@ static bool ggml_cuda_should_fuse_mul_mat_q(const ggml_tensor * up, const ggml_t
|
||||
const ggml_tensor * x_up = up->src[0];
|
||||
const ggml_tensor * y = up->src[1];
|
||||
const int cc = ggml_cuda_info().devices[device].cc;
|
||||
if (ggml_cuda_should_use_mmvq(x_up->type, cc, y->ne[1]) || !ggml_cuda_should_use_mmq(x_up->type, cc, y->ne[1], 0)) {
|
||||
|
||||
// For MUL_MAT_ID: same kernel choice as ggml_cuda_mul_mat_id, with the nominal tiles of all experts (the per-expert token counts are only known on the GPU).
|
||||
const bool is_id = up->op == GGML_OP_MUL_MAT_ID;
|
||||
const int64_t ncols = is_id ? y->ne[2] : y->ne[1];
|
||||
const int64_t nchannels = is_id ? x_up->ne[2] : y->ne[2]*y->ne[3];
|
||||
const bool use_mmvq = is_id ? ncols <= get_mmvq_mmid_max_batch(x_up->type, cc) : ggml_cuda_should_use_mmvq(x_up->type, cc, ncols);
|
||||
if (use_mmvq || !ggml_cuda_should_use_mmq(x_up->type, cc, ncols, /*n_experts =*/ is_id ? x_up->ne[2] : 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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, x_up->ne[1], y->ne[1], y->ne[2]*y->ne[3], 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) {
|
||||
@@ -3822,6 +3828,11 @@ static bool ggml_cuda_can_fuse(const struct ggml_cgraph * cgraph,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: make the fused softcap kernel stride aware, it reads src and writes dst as dense
|
||||
if (!ggml_is_contiguous(scale->src[0]) || !ggml_is_contiguous(scale2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ggml_get_unary_op(tanh) != GGML_UNARY_OP_TANH) {
|
||||
return false;
|
||||
}
|
||||
@@ -5972,7 +5983,8 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
case GGML_OP_LOG:
|
||||
return true;
|
||||
case GGML_OP_SCALE:
|
||||
return (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_BF16) && op->type == op->src[0]->type;
|
||||
return (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_BF16) && op->type == op->src[0]->type &&
|
||||
ggml_is_contiguous_rows(op->src[0]);
|
||||
case GGML_OP_CLAMP:
|
||||
return ggml_is_contiguous_rows(op->src[0]);
|
||||
case GGML_OP_ADD:
|
||||
|
||||
@@ -170,10 +170,13 @@ void ggml_cuda_mul_mat_q(
|
||||
|
||||
// Fused gate/up/GLU: src0 is the up weight, dst the GLU output.
|
||||
const ggml_tensor * gate = fusion ? fusion->gate : nullptr;
|
||||
ggml_cuda_mm_fusion_args_device fusion_device = {};
|
||||
if (fusion) {
|
||||
// TODO MoE support
|
||||
GGML_ASSERT(gate && !ids && !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));
|
||||
fusion_device.gate = gate->data;
|
||||
fusion_device.glu_op = fusion->glu_op;
|
||||
fusion_device.glu_limit = fusion->glu_limit;
|
||||
}
|
||||
|
||||
GGML_TENSOR_BINARY_OP_LOCALS;
|
||||
@@ -286,13 +289,6 @@ void ggml_cuda_mul_mat_q(
|
||||
ne11 * ne10_padded * sizeof(block_q8_1) / (QK8_1 * sizeof(int));
|
||||
const int64_t s13 = ne12*s12;
|
||||
|
||||
ggml_cuda_mm_fusion_args_device fusion_device = {};
|
||||
if (gate) {
|
||||
fusion_device.gate = gate->data;
|
||||
fusion_device.glu_op = fusion->glu_op;
|
||||
fusion_device.glu_limit = fusion->glu_limit;
|
||||
}
|
||||
|
||||
const mmq_args args = {
|
||||
src0_d, src0->type, (const int *) src1_q8_1.ptr, nullptr, nullptr, dst_d,
|
||||
src0->type == GGML_TYPE_NVFP4 && use_native_fp4 ? src1_scale.ptr : nullptr,
|
||||
@@ -378,7 +374,7 @@ void ggml_cuda_mul_mat_q(
|
||||
ne00, ne01, ne_get_rows, s01, ne_get_rows, s1,
|
||||
ne02, ne02, s02, s12, s2,
|
||||
ne03, ne13, s03, s13, s3,
|
||||
ne12, J_best};
|
||||
ne12, J_best, fusion_device};
|
||||
|
||||
ggml_cuda_mul_mat_q_switch_type(ctx, args, stream, prec_src1);
|
||||
}
|
||||
|
||||
+33
-10
@@ -3,23 +3,45 @@
|
||||
|
||||
#define MAX_GRIDDIM_X 0x7FFFFFFF
|
||||
|
||||
// src and dst may be views: rows are contiguous, dims 1..3 follow the strides (in elements).
|
||||
template <typename T>
|
||||
static __global__ void scale_f32(const T * x, T * dst, const float scale, const float bias, const int64_t nelements) {
|
||||
static __global__ void scale_f32(const T * x, T * dst, const float scale, const float bias, const uint32_t k,
|
||||
const uint3 ne0, const uint3 ne1, const uint3 ne2,
|
||||
const uint32_t s01, const uint32_t s02, const uint32_t s03,
|
||||
const uint32_t s1, const uint32_t s2, const uint32_t s3) {
|
||||
ggml_cuda_pdl_lc();
|
||||
int64_t tid = (int64_t)blockIdx.x * (int64_t)blockDim.x + (int64_t)threadIdx.x;
|
||||
int64_t stride = (int64_t)blockDim.x * (int64_t)gridDim.x;
|
||||
const uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
|
||||
ggml_cuda_pdl_sync();
|
||||
for (int64_t i = tid; i < nelements; i += stride) {
|
||||
dst[i] = ggml_cuda_cast<T>(scale * ggml_cuda_cast<float>(x[i]) + bias);
|
||||
if (i >= k) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint2 d0 = fast_div_modulo(i, ne0); // <i / ne0, i0>
|
||||
const uint2 d1 = fast_div_modulo(d0.x, ne1); // <i / (ne0*ne1), i1>
|
||||
const uint2 d2 = fast_div_modulo(d1.x, ne2); // <i3, i2>
|
||||
|
||||
const size_t i_src = d0.y + size_t(d1.y)*s01 + size_t(d2.y)*s02 + size_t(d2.x)*s03;
|
||||
const size_t i_dst = d0.y + size_t(d1.y)*s1 + size_t(d2.y)*s2 + size_t(d2.x)*s3;
|
||||
|
||||
dst[i_dst] = ggml_cuda_cast<T>(scale * ggml_cuda_cast<float>(x[i_src]) + bias);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void scale_f32_cuda(const T * x, T * dst, const float scale, const float bias, const int64_t nelements, cudaStream_t stream) {
|
||||
const int64_t num_blocks = (nelements + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE;
|
||||
static void scale_f32_cuda(const T * x, T * dst, const float scale, const float bias, const ggml_tensor * src0, const ggml_tensor * t, cudaStream_t stream) {
|
||||
const int64_t k = ggml_nelements(src0);
|
||||
const size_t ts = sizeof(T);
|
||||
GGML_ASSERT(k <= std::numeric_limits<uint32_t>::max());
|
||||
|
||||
const uint3 ne0 = init_fastdiv_values(src0->ne[0]);
|
||||
const uint3 ne1 = init_fastdiv_values(src0->ne[1]);
|
||||
const uint3 ne2 = init_fastdiv_values(src0->ne[2]);
|
||||
|
||||
const int64_t num_blocks = (k + CUDA_SCALE_BLOCK_SIZE - 1) / CUDA_SCALE_BLOCK_SIZE;
|
||||
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(MIN(MAX_GRIDDIM_X, num_blocks), CUDA_SCALE_BLOCK_SIZE, 0, stream);
|
||||
ggml_cuda_kernel_launch(scale_f32<T>, launch_params, x, dst, scale, bias, nelements);
|
||||
ggml_cuda_kernel_launch(scale_f32<T>, launch_params, x, dst, scale, bias, (uint32_t) k, ne0, ne1, ne2,
|
||||
src0->nb[1]/ts, src0->nb[2]/ts, src0->nb[3]/ts,
|
||||
t->nb[1]/ts, t->nb[2]/ts, t->nb[3]/ts);
|
||||
}
|
||||
|
||||
void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
@@ -28,6 +50,7 @@ void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_BF16);
|
||||
GGML_ASSERT(dst->type == src0->type);
|
||||
GGML_ASSERT(ggml_is_contiguous_rows(src0) && ggml_is_contiguous_rows(dst));
|
||||
|
||||
float scale;
|
||||
float bias;
|
||||
@@ -35,8 +58,8 @@ void ggml_cuda_op_scale(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
memcpy(&bias, (float *) dst->op_params + 1, sizeof(float));
|
||||
|
||||
if (src0->type == GGML_TYPE_BF16) {
|
||||
scale_f32_cuda((const nv_bfloat16 *) src0->data, (nv_bfloat16 *) dst->data, scale, bias, ggml_nelements(src0), stream);
|
||||
scale_f32_cuda((const nv_bfloat16 *) src0->data, (nv_bfloat16 *) dst->data, scale, bias, src0, dst, stream);
|
||||
} else {
|
||||
scale_f32_cuda((const float *) src0->data, (float *) dst->data, scale, bias, ggml_nelements(src0), stream);
|
||||
scale_f32_cuda((const float *) src0->data, (float *) dst->data, scale, bias, src0, dst, stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,6 +402,10 @@ static void launch_topk_moe_cuda(ggml_backend_cuda_context & ctx,
|
||||
ggml_cuda_kernel_launch(topk_moe_cuda<288, has_bias>, launch_params,
|
||||
logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config);
|
||||
break;
|
||||
case 384: // DeepSeek V4.1
|
||||
ggml_cuda_kernel_launch(topk_moe_cuda<384, has_bias>, launch_params,
|
||||
logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config);
|
||||
break;
|
||||
case 512:
|
||||
ggml_cuda_kernel_launch(topk_moe_cuda<512, has_bias>, launch_params,
|
||||
logits, weights, ids, bias, n_rows, n_expert_used, clamp_val, scale_val, config);
|
||||
@@ -472,7 +476,7 @@ bool ggml_cuda_should_use_topk_moe(const ggml_tensor * gating_op,
|
||||
// must match an instantiation of launch_topk_moe_cuda: a power of 2 up to 512,
|
||||
// or one of the non-power-of-2 expert counts of supported models
|
||||
const int n_expert = ids->nb[1] / ids->nb[0];
|
||||
if (((n_expert & (n_expert - 1)) != 0 || n_expert > 512) && n_expert != 288 && n_expert != 576) {
|
||||
if (((n_expert & (n_expert - 1)) != 0 || n_expert > 512) && n_expert != 288 && n_expert != 384 && n_expert != 576) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ int opt_arch = 0; // autodetect
|
||||
size_t opt_ndev = 1;
|
||||
size_t opt_nhvx = 0; // use all
|
||||
int opt_nhmx = 1; // when set, enable HMX; when 0, use HVX only
|
||||
size_t opt_vtcm_size = 0; // VTCM size override in bytes; 0 = auto-detect
|
||||
size_t opt_vmem = HTP_OP_MAX_VMEM_DEFAULT; // max available va space for buffer mappings
|
||||
int opt_etm = 0;
|
||||
int opt_verbose = 0;
|
||||
@@ -2152,6 +2153,12 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->n_hvx = this->n_threads;
|
||||
this->n_hmx = (opt_nhmx != 0) ? (uint32_t)hw_n_hmx : 0;
|
||||
this->vtcm_size = (uint64_t)hw_vtcm_size;
|
||||
if (opt_vtcm_size > 0) {
|
||||
if (opt_vtcm_size > hw_vtcm_size) {
|
||||
GGML_LOG_WARN("ggml-hex: %s VTCM override %zu exceeds HW size %llu\n", this->c_name(), opt_vtcm_size, hw_vtcm_size);
|
||||
}
|
||||
this->vtcm_size = opt_vtcm_size;
|
||||
}
|
||||
GGML_LOG_INFO("ggml-hex: %s hwinfo: threads %u, hvx %u, hmx %u, vtcm %llu MB\n",
|
||||
this->c_name(), this->n_threads, this->n_hvx, this->n_hmx,
|
||||
(unsigned long long)(this->vtcm_size / (1024 * 1024)));
|
||||
@@ -2161,7 +2168,7 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->n_threads = opt_nhvx > 0 ? (uint32_t) (std::min)(opt_nhvx, (size_t) HTP_MAX_NTHREADS) : default_n_threads;
|
||||
this->n_hvx = this->n_threads;
|
||||
this->n_hmx = (opt_nhmx != 0) ? 1 : 0;
|
||||
this->vtcm_size = 8 * 1024 * 1024;
|
||||
this->vtcm_size = opt_vtcm_size > 0 ? opt_vtcm_size : 8 * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2230,7 +2237,8 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
|
||||
this->op_batch = new ggml_hexagon_opbatch(this, opt_opbatch, this->max_vmem);
|
||||
|
||||
// Start dspqueue/opbatch processing
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, this->max_vmem, (uint32_t) opt_power_mode);
|
||||
err = htp_iface_start(this->handle, this->session_id, this->queue_id, this->n_threads, opt_nhmx, opt_vtcm_size, this->max_vmem,
|
||||
(uint32_t) opt_power_mode);
|
||||
if (err != 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: %s failed to start session: 0x%08x\n", this->c_name(), (unsigned) err);
|
||||
throw std::runtime_error("ggml-hex: iface start failed (see log for details)");
|
||||
@@ -3391,6 +3399,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
const char * str_etm = getenv("GGML_HEXAGON_ETM");
|
||||
const char * str_nhvx = getenv("GGML_HEXAGON_NHVX");
|
||||
const char * str_nhmx = getenv("GGML_HEXAGON_NHMX");
|
||||
const char * str_vtcm_size = getenv("GGML_HEXAGON_VTCM_SIZE");
|
||||
const char * str_mm_select = getenv("GGML_HEXAGON_MM_SELECT");
|
||||
const char * str_fa_select = getenv("GGML_HEXAGON_FA_SELECT");
|
||||
const char * str_fa_split = getenv("GGML_HEXAGON_FA_SPLIT");
|
||||
@@ -3456,6 +3465,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
}
|
||||
opt_nhvx = str_nhvx ? strtoul(str_nhvx, NULL, 0) : opt_nhvx;
|
||||
opt_nhmx = str_nhmx ? atoi(str_nhmx) : opt_nhmx;
|
||||
opt_vtcm_size = str_vtcm_size ? parse_size(str_vtcm_size) : opt_vtcm_size;
|
||||
opt_mm_select = str_mm_select ? atoi(str_mm_select) : opt_mm_select;
|
||||
opt_fa_select = str_fa_select ? atoi(str_fa_select) : opt_fa_select;
|
||||
opt_fa_split = str_fa_split ? atoi(str_fa_split) : opt_fa_split;
|
||||
|
||||
@@ -57,7 +57,10 @@ htp_op_code htp_op_remap(const struct ggml_tensor * t) {
|
||||
case GGML_OP_PAD: return HTP_OP_PAD;
|
||||
case GGML_OP_IM2COL: return HTP_OP_IM2COL;
|
||||
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_ROLL: return HTP_OP_ROLL;
|
||||
case GGML_OP_UPSCALE: return HTP_OP_UPSCALE;
|
||||
|
||||
case GGML_OP_UNARY:
|
||||
switch (ggml_get_unary_op(t)) {
|
||||
@@ -1790,11 +1793,6 @@ static bool htp_kparams_solve_softmax(
|
||||
return false;
|
||||
}
|
||||
|
||||
const int64_t ne0 = src0->ne[0];
|
||||
if ((ne0 > 32 && (ne0 & (32 - 1)) != 0) || ne0 > 131072) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t src0_nrows = src0->ne[1] * src0->ne[2] * src0->ne[3];
|
||||
const size_t dst_data_row_size = op->ne[0] * ggml_type_size(op->type);
|
||||
const uint32_t min_rows_per_thread = (uint32_t) (std::max<size_t>(1, (128 + dst_data_row_size - 1) / dst_data_row_size));
|
||||
@@ -2188,7 +2186,8 @@ static bool htp_kparams_solve_ssm_conv(
|
||||
target_max_tile = (std::max)(target_max_tile, 32u);
|
||||
target_max_tile = (std::min)(target_max_tile, 128u);
|
||||
|
||||
uint32_t d_inner_tile = (uint32_t) (avail_for_tiles / (2 * (ncs + n_t) * sizeof(float)));
|
||||
const size_t raw_stride_bytes = ncs * sizeof(float) + sizeof(float);
|
||||
uint32_t d_inner_tile = (uint32_t) (avail_for_tiles / (2 * (raw_stride_bytes + n_t * sizeof(float))));
|
||||
d_inner_tile = (d_inner_tile / 32) * 32;
|
||||
if (d_inner_tile == 0) {
|
||||
d_inner_tile = 32;
|
||||
@@ -2375,6 +2374,183 @@ static bool htp_kparams_solve_pool_2d(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool htp_kparams_solve_upscale(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_upscale_kernel_params * kparams
|
||||
) {
|
||||
memset(kparams, 0, sizeof(*kparams));
|
||||
|
||||
if (src0->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t src_x = (uint32_t) src0->ne[0];
|
||||
const uint32_t src_y = (uint32_t) src0->ne[1];
|
||||
const uint32_t src_z = (uint32_t) src0->ne[2];
|
||||
const uint32_t src_w = (uint32_t) src0->ne[3];
|
||||
|
||||
const uint32_t dst_x = (uint32_t) dst->ne[0];
|
||||
const uint32_t dst_y = (uint32_t) dst->ne[1];
|
||||
const uint32_t dst_z = (uint32_t) dst->ne[2];
|
||||
const uint32_t dst_w = (uint32_t) dst->ne[3];
|
||||
|
||||
if (src_x == 0 || src_y == 0 || src_z == 0 || src_w == 0 ||
|
||||
dst_x == 0 || dst_y == 0 || dst_z == 0 || dst_w == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is_src_contig_row = (src0->nb[0] == sizeof(float));
|
||||
const bool is_src_strided_2d = (src0->nb[0] > sizeof(float)) &&
|
||||
(src0->nb[0] % sizeof(float) == 0) &&
|
||||
(src0->nb[1] == (size_t) src_x * src0->nb[0]) &&
|
||||
((size_t) src_x * src_y <= 0xFFFFu);
|
||||
|
||||
if ((!is_src_contig_row && !is_src_strided_2d) || dst->nb[0] != sizeof(float)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t mode_flags = dst->op_params[0];
|
||||
const uint32_t mode_raw = (uint32_t) (mode_flags & 0xFF);
|
||||
if (mode_raw >= GGML_SCALE_MODE_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool align_corners = (mode_flags & GGML_SCALE_FLAG_ALIGN_CORNERS) != 0;
|
||||
const bool antialias = (mode_flags & GGML_SCALE_FLAG_ANTIALIAS) != 0;
|
||||
|
||||
float sf0 = (float) dst_x / (float) src_x;
|
||||
float sf1 = (float) dst_y / (float) src_y;
|
||||
float sf2 = (float) dst_z / (float) src_z;
|
||||
float sf3 = (float) dst_w / (float) src_w;
|
||||
float pixel_offset = 0.5f;
|
||||
|
||||
if (align_corners) {
|
||||
pixel_offset = 0.0f;
|
||||
sf0 = (dst_x > 1 && src_x > 1) ? (float) (dst_x - 1) / (float) (src_x - 1) : sf0;
|
||||
sf1 = (dst_y > 1 && src_y > 1) ? (float) (dst_y - 1) / (float) (src_y - 1) : sf1;
|
||||
}
|
||||
|
||||
const float inv_sf0 = 1.0f / sf0;
|
||||
const float inv_sf1 = 1.0f / sf1;
|
||||
const float inv_sf2 = 1.0f / sf2;
|
||||
const float inv_sf3 = 1.0f / sf3;
|
||||
|
||||
const float filter_radius0 = antialias ? (std::max)(1.0f, inv_sf0) : 0.0f;
|
||||
const float filter_radius1 = antialias ? (std::max)(1.0f, inv_sf1) : 0.0f;
|
||||
const float invscale0 = antialias ? (1.0f / filter_radius0) : 0.0f;
|
||||
const float invscale1 = antialias ? (1.0f / filter_radius1) : 0.0f;
|
||||
|
||||
const uint32_t total_slices = dst_z * dst_w;
|
||||
uint32_t n_threads = htp_session_n_threads(sess);
|
||||
|
||||
struct htp_upscale_vtcm_layout layout;
|
||||
uint32_t dst_nrows_per_chunk = 0;
|
||||
while (n_threads > 0) {
|
||||
if (htp_upscale_solve_layout(&layout, src_x, src_y, dst_x, dst_y, inv_sf1, filter_radius1, n_threads,
|
||||
htp_session_vtcm_size(sess), &dst_nrows_per_chunk)) {
|
||||
break;
|
||||
}
|
||||
n_threads--;
|
||||
}
|
||||
if (n_threads == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t lut_x_size = 0;
|
||||
size_t elem_y_sz = sizeof(uint32_t);
|
||||
if (mode_raw == HTP_UPSCALE_MODE_BICUBIC) {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(struct upscale_bicubic_x), 128);
|
||||
elem_y_sz = sizeof(struct upscale_bicubic_y);
|
||||
} else if (antialias) {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(struct upscale_antialias_x), 128);
|
||||
elem_y_sz = sizeof(struct upscale_antialias_y);
|
||||
} else if (mode_raw == HTP_UPSCALE_MODE_BILINEAR) {
|
||||
const uint32_t n_chunks = (dst_x + 31) / 32;
|
||||
lut_x_size = hex_round_up(n_chunks * sizeof(struct upscale_bilinear_x_chunk), 128);
|
||||
elem_y_sz = sizeof(struct upscale_bilinear_y);
|
||||
} else {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(uint32_t), 128);
|
||||
}
|
||||
const size_t total_lut_size = lut_x_size + hex_round_up(dst_y * elem_y_sz, 128);
|
||||
if (total_lut_size > HTP_DDR_SPAD_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t chunks_per_slice = (dst_y + dst_nrows_per_chunk - 1) / dst_nrows_per_chunk;
|
||||
const uint32_t total_chunks = total_slices * chunks_per_slice;
|
||||
|
||||
n_threads = (std::min)(n_threads, total_chunks > 0 ? total_chunks : 1);
|
||||
htp_upscale_vtcm_layout_build(&layout, src_x, src_y, dst_x, dst_y, dst_nrows_per_chunk, inv_sf1, filter_radius1, n_threads);
|
||||
|
||||
const uint32_t slice_dst_bytes = dst_x * dst_y * sizeof(float);
|
||||
const uint32_t chunk_dst_bytes = dst_x * dst_nrows_per_chunk * sizeof(float);
|
||||
const bool dst_contig = (dst->nb[1] == (size_t) dst_x * sizeof(float)) &&
|
||||
(dst_z <= 1 || dst->nb[2] == (size_t) dst_y * dst->nb[1]) &&
|
||||
(dst_w <= 1 || dst->nb[3] == (size_t) dst_z * dst->nb[2]);
|
||||
|
||||
uint32_t chunks_per_group = 0;
|
||||
if (dst_contig) {
|
||||
const uint32_t slices_per_group = (slice_dst_bytes > 0)
|
||||
? (HEX_L2_LINE_SIZE / hex_gcd_u32(slice_dst_bytes, HEX_L2_LINE_SIZE))
|
||||
: 1;
|
||||
if (slices_per_group == 1 && (chunk_dst_bytes % HEX_L2_LINE_SIZE == 0)) {
|
||||
chunks_per_group = 1;
|
||||
} else {
|
||||
chunks_per_group = slices_per_group * chunks_per_slice;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t kernel_type = HTP_UPSCALE_KERNEL_BILINEAR;
|
||||
if (mode_raw == HTP_UPSCALE_MODE_NEAREST) {
|
||||
if (dst_x == 2 * src_x && dst_y == 2 * src_y && !align_corners) {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_NEAREST_2X;
|
||||
} else if (dst_x == 4 * src_x && dst_y == 4 * src_y && !align_corners) {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_NEAREST_4X;
|
||||
} else {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_NEAREST;
|
||||
}
|
||||
} else if (mode_raw == HTP_UPSCALE_MODE_BICUBIC) {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_BICUBIC;
|
||||
} else if (antialias) {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_BILINEAR_AA;
|
||||
} else if (dst_x == 2 * src_x && dst_y == 2 * src_y && !align_corners && (src_x % 32 == 0)) {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_BILINEAR_2X;
|
||||
} else {
|
||||
kernel_type = HTP_UPSCALE_KERNEL_BILINEAR;
|
||||
}
|
||||
|
||||
kparams->kernel_type = kernel_type;
|
||||
kparams->mode = (uint8_t) mode_raw;
|
||||
kparams->align_corners = align_corners ? 1 : 0;
|
||||
kparams->antialias = antialias ? 1 : 0;
|
||||
|
||||
kparams->inv_sf0 = inv_sf0;
|
||||
kparams->inv_sf1 = inv_sf1;
|
||||
kparams->inv_sf2 = inv_sf2;
|
||||
kparams->inv_sf3 = inv_sf3;
|
||||
kparams->pixel_offset = pixel_offset;
|
||||
|
||||
kparams->filter_radius0 = filter_radius0;
|
||||
kparams->filter_radius1 = filter_radius1;
|
||||
kparams->invscale0 = invscale0;
|
||||
kparams->invscale1 = invscale1;
|
||||
|
||||
kparams->n_threads = n_threads;
|
||||
kparams->chunks_per_group = chunks_per_group;
|
||||
|
||||
kparams->dst_nrows_per_chunk = dst_nrows_per_chunk;
|
||||
kparams->chunks_per_slice = chunks_per_slice;
|
||||
kparams->total_chunks = total_chunks;
|
||||
kparams->vtcm_size = (uint32_t) layout.total_bytes;
|
||||
|
||||
kparams->div_chunks_per_slice = init_fastdiv_values(chunks_per_slice > 0 ? chunks_per_slice : 1);
|
||||
kparams->div_dst_z = init_fastdiv_values(dst_z > 0 ? dst_z : 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool htp_kparams_solve_concat(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
@@ -2729,7 +2905,8 @@ static bool htp_kparams_solve_im2col(
|
||||
const uint32_t dst_row_bytes = hex_round_up((uint32_t) dst_row_bytes_raw, 256);
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads,
|
||||
htp_im2col_scratch_bytes(HTP_IM2COL_KERNEL_PATCHEMBED_DMA, KH, patch_stride));
|
||||
|
||||
if (vtcm_size == 0 || layout.total_bytes <= vtcm_size) {
|
||||
kparams->kernel_type = HTP_IM2COL_KERNEL_PATCHEMBED_DMA;
|
||||
@@ -2778,18 +2955,24 @@ static bool htp_kparams_solve_im2col(
|
||||
const uint32_t src_row_bytes = hex_round_up((uint32_t) src_row_bytes_raw, 256);
|
||||
const uint32_t dst_row_bytes = hex_round_up((uint32_t) dst_row_bytes_raw, 256);
|
||||
|
||||
// threads work on output-col blocks, so a single output row (1D) can use them all
|
||||
const uint32_t nbpr = (OW + owb - 1) / owb;
|
||||
const uint64_t nblocks = (uint64_t) total_rows * nbpr;
|
||||
const uint32_t blk_threads = (uint32_t) (nblocks < max_threads ? nblocks : max_threads);
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, blk_threads,
|
||||
htp_im2col_scratch_bytes(HTP_IM2COL_KERNEL_BLOCKED_DMA, KH, patch_stride));
|
||||
|
||||
if (vtcm_size == 0 || layout.total_bytes <= vtcm_size) {
|
||||
kparams->kernel_type = HTP_IM2COL_KERNEL_BLOCKED_DMA;
|
||||
kparams->n_threads = (uint8_t) n_threads;
|
||||
kparams->n_threads = (uint8_t) blk_threads;
|
||||
kparams->vtcm_size = (uint32_t) layout.total_bytes;
|
||||
kparams->src_row_bytes = src_row_bytes;
|
||||
kparams->dst_row_bytes = dst_row_bytes;
|
||||
kparams->owb = owb;
|
||||
kparams->wb = (uint32_t) Wb;
|
||||
kparams->nbpr = (OW + owb - 1) / owb;
|
||||
kparams->nbpr = nbpr;
|
||||
kparams->N = N;
|
||||
kparams->IC = IC;
|
||||
kparams->ID = 1;
|
||||
@@ -2806,6 +2989,7 @@ static bool htp_kparams_solve_im2col(
|
||||
kparams->rows_per_chunk = rows_per_chunk;
|
||||
kparams->div_OH = init_fastdiv_values(OH);
|
||||
kparams->div_OD = init_fastdiv_values(1);
|
||||
kparams->div_nbpr = init_fastdiv_values(nbpr);
|
||||
return true;
|
||||
}
|
||||
if (owb == 1) break;
|
||||
@@ -2907,7 +3091,7 @@ static bool htp_kparams_solve_im2col_3d(
|
||||
const uint32_t dst_row_bytes = hex_round_up((uint32_t) dst_row_bytes_raw, 256);
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads, 256);
|
||||
|
||||
if (vtcm_size == 0 || layout.total_bytes <= vtcm_size) {
|
||||
kparams->kernel_type = HTP_IM2COL_KERNEL_PATCHEMBED_DMA;
|
||||
@@ -2957,7 +3141,7 @@ static bool htp_kparams_solve_im2col_3d(
|
||||
const uint32_t dst_row_bytes = hex_round_up((uint32_t) dst_row_bytes_raw, 256);
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, src_row_bytes, dst_row_bytes, n_threads, 256);
|
||||
|
||||
if (vtcm_size == 0 || layout.total_bytes <= vtcm_size) {
|
||||
kparams->kernel_type = HTP_IM2COL_KERNEL_BLOCKED_DMA;
|
||||
@@ -3286,6 +3470,545 @@ static bool htp_kparams_solve_solve_tri(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implicit GEMM over sliding windows of fp16 input croutons (HMX activation:single), no im2col
|
||||
static bool htp_kparams_conv2d_win(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_conv2d_kernel_params * kp
|
||||
) {
|
||||
if (htp_session_n_hmx(sess) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 s1 = ggml_get_op_params_i32(dst, 1);
|
||||
const int32_t p0 = ggml_get_op_params_i32(dst, 2);
|
||||
const int32_t p1 = ggml_get_op_params_i32(dst, 3);
|
||||
const int32_t d0 = ggml_get_op_params_i32(dst, 4);
|
||||
const int32_t d1 = ggml_get_op_params_i32(dst, 5);
|
||||
|
||||
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 IW = src1->ne[0];
|
||||
const uint32_t IH = src1->ne[1];
|
||||
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 || s1 != 1 || p0 < 0 || p1 < 0 || dst->nb[0] != sizeof(float)) {
|
||||
return false;
|
||||
}
|
||||
if (dst->nb[1] < OW * sizeof(float) || (Cout > 1 && dst->nb[2] < OW * sizeof(float))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool whcn = src1->nb[0] == sizeof(float) && src1->nb[1] >= IW * sizeof(float) && (Cin == 1 || src1->nb[2] >= IH * src1->nb[1]);
|
||||
const bool cwhn = !whcn && src1->nb[2] == sizeof(float) && src1->nb[0] == Cin * sizeof(float) && src1->nb[1] >= IW * src1->nb[0];
|
||||
if (!whcn && !cwhn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool w_rows = src0->nb[0] == es && (KH == 1 || src0->nb[1] == KW * es) && (Cin == 1 || src0->nb[2] == KW * KH * es);
|
||||
const bool co_c = (Cout == 1 || src0->nb[3] == es) && (Cin == 1 || src0->nb[2] >= (std::min)(Cout, 32u) * es);
|
||||
if (!w_rows && !co_c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// short output rows: fold the kw taps into channels so the virtual row is OW wide and output rows are contiguous
|
||||
const bool fold = (whcn || (cwhn && d0 == 1)) && KW > 1 && KW * Cin <= 32 && OW < 64 && KW * KH <= 16 && Cout >= 16;
|
||||
|
||||
const uint32_t T = KW * KH;
|
||||
const uint32_t n_ch = fold ? KW * Cin : Cin;
|
||||
const uint32_t n_cc = (n_ch + 31) / 32;
|
||||
const uint32_t rem = n_ch - (n_cc - 1) * 32;
|
||||
const uint32_t stop = (rem + 7) / 8 * 8 - 1;
|
||||
const uint32_t SR = fold ? OW : IW + 2 * p0;
|
||||
const uint32_t IHS = IH + 2 * p1;
|
||||
const uint32_t n_wt = fold ? KH : T;
|
||||
const uint32_t moff = (KH - 1) * d1 * SR + (fold ? 0 : (KW - 1) * d0);
|
||||
const uint32_t halo = moff / 32 + 1;
|
||||
const uint32_t n_rt = (Cout + 31) / 32;
|
||||
const uint64_t n_q = (uint64_t) (N * IHS - (IHS - OH) - 1) * SR + OW;
|
||||
const uint32_t nth = kp->n_threads;
|
||||
const size_t tile = 2048;
|
||||
|
||||
if ((uint64_t) N * IHS * SR + moff + 64 >= (1ull << 31)) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t n_tiles = (uint32_t) ((n_q + 31) / 32);
|
||||
|
||||
// weight packing scratch
|
||||
uint32_t w_cs = 0;
|
||||
size_t wraw = 0;
|
||||
size_t wscr = 0;
|
||||
if (w_rows) {
|
||||
const uint32_t kpw = es == 2 ? 64 : 32;
|
||||
for (uint32_t cs = 32; cs >= 2; cs /= 2) {
|
||||
w_cs = cs;
|
||||
wraw = (size_t) 32 * ((cs * T + kpw - 1) / kpw) * 128;
|
||||
if (3 * wraw + 64 * 128 <= 256 * 1024 || (fold && cs == 32)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// enough packing units for all threads
|
||||
const uint32_t kpw2 = es == 2 ? 64 : 32;
|
||||
while (!fold && w_cs > 2 && n_rt * ((Cin + 31) / 32) * (32 / w_cs) < kp->n_threads) {
|
||||
w_cs /= 2;
|
||||
}
|
||||
wraw = (std::min)(wraw, (size_t) 32 * ((w_cs * T + kpw2 - 1) / kpw2) * 128);
|
||||
wscr = 3 * wraw + 64 * 128 + 128;
|
||||
} else {
|
||||
w_cs = fold ? KW : (std::min)(T, 16u);
|
||||
while (!fold && w_cs > 1 && n_rt * ((Cin + 31) / 32) * ((T + w_cs - 1) / w_cs) < kp->n_threads) {
|
||||
w_cs = (w_cs + 1) / 2;
|
||||
}
|
||||
wscr = 2 * (size_t) w_cs * 32 * 128;
|
||||
}
|
||||
|
||||
uint32_t rt_block = n_rt;
|
||||
uint32_t ct = (std::max)(4 * halo, (n_tiles + 3) / 4);
|
||||
ct = (std::min)((std::max)(ct, 8u), 512u);
|
||||
ct = (std::min)(ct, n_tiles);
|
||||
uint32_t seg_b = 8;
|
||||
// longer output rows make the output DMA more efficient
|
||||
uint32_t seg_t = (std::min)(32u, (std::max)(8u, (OW + 31) / 32));
|
||||
|
||||
while (rt_block > 1 && (size_t) rt_block * n_wt * n_cc * tile > htp_session_vtcm_size(sess) / 2) {
|
||||
rt_block = (rt_block + 1) / 2;
|
||||
}
|
||||
|
||||
size_t stage = 0, thr = 0, act = 0, out = 0, w_size = 0, total = 0;
|
||||
uint32_t n_bc = 0;
|
||||
while (true) {
|
||||
n_bc = ct + halo;
|
||||
stage = cwhn ? (size_t) seg_b * 32 * Cin * sizeof(float) + 256 : (size_t) n_ch * seg_b * 32 * sizeof(float);
|
||||
if (fold) {
|
||||
// raw input rows of the staged pixels with 128 bytes of read slack on both sides
|
||||
stage = (size_t) Cin * ((seg_b * 32 + OW - 1) / OW + 1) * IW * sizeof(float) + 256;
|
||||
}
|
||||
stage = hex_round_up(stage, 128);
|
||||
thr = hex_round_up((std::max)(2 * stage + (size_t) HTP_CONV2D_WIN_OBUF * 32 * seg_t * 32 * sizeof(float), wscr), tile);
|
||||
w_size = (size_t) rt_block * n_wt * n_cc * tile;
|
||||
act = (size_t) n_cc * n_bc * tile;
|
||||
out = (size_t) rt_block * ct * tile;
|
||||
struct htp_conv2d_vtcm_layout L;
|
||||
htp_conv2d_vtcm_layout_build(&L, rt_block, w_size, act, out, thr, nth);
|
||||
total = L.total_bytes;
|
||||
if (total <= htp_session_vtcm_size(sess)) {
|
||||
break;
|
||||
}
|
||||
if (ct > 64) {
|
||||
ct /= 2;
|
||||
} else if (rt_block > 1) {
|
||||
rt_block = (rt_block + 1) / 2;
|
||||
} else if (seg_b > 1 || seg_t > 1) {
|
||||
seg_b = (std::max)(seg_b / 2, 1u);
|
||||
seg_t = (std::max)(seg_t / 2, 1u);
|
||||
} else if (ct > 1) {
|
||||
ct /= 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const uint32_t TG = w_cs;
|
||||
const uint32_t CS = w_cs;
|
||||
const uint32_t n_tg = (T + TG - 1) / TG;
|
||||
const uint32_t n_cs = (32 + CS - 1) / CS;
|
||||
|
||||
kp->kernel_type = (uint8_t) HTP_CONV2D_KERNEL_HMX_WINDOW;
|
||||
kp->n_threads = (uint8_t) nth;
|
||||
kp->cwhn = cwhn ? 1 : 0;
|
||||
kp->w_kmajor = !w_rows ? 1 : 0;
|
||||
kp->n_row_tiles = n_rt;
|
||||
kp->ct_chunk = ct;
|
||||
kp->rt_block = rt_block;
|
||||
kp->stage_size = stage;
|
||||
kp->thr_size = thr;
|
||||
kp->vtcm_size = total;
|
||||
kp->window.fold = fold;
|
||||
kp->window.n_cc = n_cc;
|
||||
kp->window.act_rt_last = 0x780 | 0x2 | (stop << 2);
|
||||
kp->window.sr = SR;
|
||||
kp->window.ihs = IHS;
|
||||
kp->window.max_off = moff;
|
||||
kp->window.n_bc = n_bc;
|
||||
kp->window.seg_b = seg_b;
|
||||
kp->window.seg_t = seg_t;
|
||||
kp->window.w_cs = w_cs;
|
||||
kp->window.div_sr = init_fastdiv_values(SR);
|
||||
kp->window.div_ihs = init_fastdiv_values(IHS);
|
||||
kp->window.div_kw = init_fastdiv_values(KW);
|
||||
kp->window.div_tg = init_fastdiv_values(n_tg);
|
||||
kp->window.div_cc = init_fastdiv_values(n_cc);
|
||||
kp->window.div_cs = init_fastdiv_values(n_cs);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tiled implicit GEMM, MMA on HMX if available, else HVX
|
||||
static bool htp_kparams_conv2d_tiled(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_conv2d_kernel_params * kp
|
||||
) {
|
||||
if (htp_session_n_hmx(sess) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 = 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 >= Cout * es && (KH == 1 || src0->nb[1] == KW * ks) && (Cin == 1 || src0->nb[2] == KW * KH * ks);
|
||||
} else if (cwhn) {
|
||||
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 = 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 = (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 nth = htp_session_n_threads(sess);
|
||||
const size_t tile = 2048;
|
||||
|
||||
uint32_t rt_block = n_rt;
|
||||
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, ostage = 0, prow = 0, wpack = 0;
|
||||
size_t w_size = 0, act_size = 0, out_size = 0, thr_size = 0, total = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
{
|
||||
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 && L2.total_bytes <= htp_session_vtcm_size(sess)) {
|
||||
n_stage = 2;
|
||||
thr_size = thr2;
|
||||
total = L2.total_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
kp->kernel_type = (uint8_t) HTP_CONV2D_KERNEL_HMX_TILED;
|
||||
kp->n_threads = (uint8_t) nth;
|
||||
kp->cwhn = cwhn ? 1 : 0;
|
||||
kp->w_kmajor = kmajor ? 1 : 0;
|
||||
kp->n_row_tiles = n_rt;
|
||||
kp->ct_chunk = ct;
|
||||
kp->rt_block = rt_block;
|
||||
kp->stage_size = stage;
|
||||
kp->thr_size = thr_size;
|
||||
kp->vtcm_size = total;
|
||||
kp->tiled.w_kstride = ks;
|
||||
kp->tiled.k_size = K;
|
||||
kp->tiled.k_group = kg;
|
||||
kp->tiled.k_group_pad = kg_p;
|
||||
kp->tiled.n_groups = n_grp;
|
||||
kp->tiled.n_k_tiles = n_kt;
|
||||
kp->tiled.n_ow_tiles = n_owt;
|
||||
kp->tiled.seg_tiles = seg;
|
||||
kp->tiled.stage_row = sr;
|
||||
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(OH);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool htp_kparams_solve_conv2d_unchecked(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_conv2d_kernel_params * kparams
|
||||
) {
|
||||
memset(kparams, 0, sizeof(*kparams));
|
||||
|
||||
const uint32_t total_rows = (uint32_t) (dst->ne[1] * dst->ne[2] * dst->ne[3]);
|
||||
kparams->total_rows = total_rows;
|
||||
kparams->n_threads = total_rows == 0 ? 1 : (std::min)((uint32_t) htp_session_n_threads(sess), total_rows);
|
||||
|
||||
if (htp_kparams_conv2d_win(sess, dst, kparams)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return htp_kparams_conv2d_tiled(sess, dst, kparams);
|
||||
}
|
||||
|
||||
static bool htp_kparams_solve_conv2d(
|
||||
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;
|
||||
}
|
||||
|
||||
if (src0->ne[2] != src1->ne[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t s0 = ggml_get_op_params_i32(op, 0);
|
||||
const int32_t s1 = ggml_get_op_params_i32(op, 1);
|
||||
const int32_t p0 = ggml_get_op_params_i32(op, 2);
|
||||
const int32_t p1 = ggml_get_op_params_i32(op, 3);
|
||||
const int32_t d0 = ggml_get_op_params_i32(op, 4);
|
||||
const int32_t d1 = ggml_get_op_params_i32(op, 5);
|
||||
|
||||
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]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return htp_kparams_solve_conv2d_unchecked(sess, op, kparams);
|
||||
}
|
||||
|
||||
static bool htp_kparams_solve_conv2d_dw(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
struct htp_conv2d_dw_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;
|
||||
}
|
||||
|
||||
if (src0->ne[2] != 1 || src0->ne[3] != src1->ne[2]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (src0->ne[0] * src0->ne[1] > 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t s0 = ggml_get_op_params_i32(op, 0);
|
||||
const int32_t s1 = ggml_get_op_params_i32(op, 1);
|
||||
const int32_t p0 = ggml_get_op_params_i32(op, 2);
|
||||
const int32_t p1 = ggml_get_op_params_i32(op, 3);
|
||||
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] || dst->ne[2] != src1->ne[2] || dst->ne[3] != src1->ne[3]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool cwhn = ggml_is_contiguous_channels(src1);
|
||||
if (!cwhn && !ggml_is_contiguous(src1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cwhn && (d0 != 1 || (s0 != 1 && s0 != 2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t c = (uint32_t) src1->ne[2];
|
||||
const uint32_t n = (uint32_t) src1->ne[3];
|
||||
const uint32_t w = (uint32_t) src1->ne[0];
|
||||
const uint32_t h = (uint32_t) src1->ne[1];
|
||||
const uint32_t kw = (uint32_t) src0->ne[0];
|
||||
const uint32_t kh = (uint32_t) src0->ne[1];
|
||||
const uint32_t ow_u = (uint32_t) ow;
|
||||
const uint32_t oh_u = (uint32_t) oh;
|
||||
|
||||
const uint32_t row_src_bytes = cwhn
|
||||
? (w * c * (uint32_t) sizeof(float))
|
||||
: (w * (uint32_t) sizeof(float));
|
||||
const uint32_t row_dst_bytes = cwhn
|
||||
? (ow_u * c * (uint32_t) sizeof(float))
|
||||
: (ow_u * (uint32_t) sizeof(float));
|
||||
|
||||
uint8_t kernel_type = HTP_CONV2D_DW_KERNEL_UNSUPPORTED;
|
||||
if (cwhn) {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_CWHN;
|
||||
} else if ((w & 31) == 0 && kw == 3 && kh == 3 && p0 == 1 && p1 == 1 && d0 == 1 && d1 == 1 && s0 == 1 && s1 == 1) {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S1_K3X3;
|
||||
} else if ((w & 31) == 0 && kw == 3 && kh == 3 && p0 == 1 && p1 == 1 && d0 == 1 && d1 == 1 && s0 == 2 && s1 == 2) {
|
||||
if (w == 32 && ow_u == 16) {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3_W32;
|
||||
} else if (w == 64 && ow_u == 32) {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3_W64;
|
||||
} else {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3;
|
||||
}
|
||||
} else if (s0 == 1) {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S1;
|
||||
} else {
|
||||
kernel_type = HTP_CONV2D_DW_KERNEL_WHCN_S2;
|
||||
}
|
||||
|
||||
const bool fast_k3x3 = kernel_type >= HTP_CONV2D_DW_KERNEL_WHCN_S1_K3X3;
|
||||
const uint32_t pad_rows = fast_k3x3 ? 4 : 0;
|
||||
const uint32_t pad_top_stride = fast_k3x3 ? w : 0;
|
||||
|
||||
const size_t vtcm_budget = (sess && htp_session_vtcm_size(sess) > 0) ? htp_session_vtcm_size(sess) : 0;
|
||||
if (vtcm_budget == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t w_size = (uint32_t) (kw * kh * c * ggml_type_size(src0->type));
|
||||
|
||||
uint32_t max_threads_sess = (uint32_t) htp_session_n_threads(sess);
|
||||
uint32_t oh_chunk = oh_u;
|
||||
|
||||
uint32_t n_threads = max_threads_sess;
|
||||
struct htp_conv2d_dw_vtcm_layout layout;
|
||||
|
||||
while (true) {
|
||||
const uint32_t max_ih_chunk = (std::min)(h, (oh_chunk - 1) * (uint32_t) s1 + (kh - 1) * (uint32_t) d1 + 1);
|
||||
const uint32_t max_src_bytes = (max_ih_chunk + pad_rows) * row_src_bytes + 128;
|
||||
const uint32_t max_dst_bytes = oh_chunk * row_dst_bytes + 128;
|
||||
|
||||
htp_conv2d_dw_vtcm_layout_build(&layout, max_src_bytes, max_dst_bytes, w_size, n_threads);
|
||||
|
||||
if (layout.total_bytes <= vtcm_budget && layout.total_bytes > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (oh_chunk > 1) {
|
||||
oh_chunk = (oh_chunk + 1) / 2;
|
||||
} else if (n_threads > 1) {
|
||||
n_threads--;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t n_chunks = (oh_u + oh_chunk - 1) / oh_chunk;
|
||||
const uint32_t total_tasks = cwhn ? (n * n_chunks) : (c * n * n_chunks);
|
||||
n_threads = (std::min)(n_threads, total_tasks);
|
||||
if (n_threads == 0) {
|
||||
n_threads = 1;
|
||||
}
|
||||
|
||||
const uint32_t max_ih_chunk = (std::min)(h, (oh_chunk - 1) * (uint32_t) s1 + (kh - 1) * (uint32_t) d1 + 1);
|
||||
const uint32_t max_src_bytes = (max_ih_chunk + pad_rows) * row_src_bytes + 128;
|
||||
const uint32_t max_dst_bytes = oh_chunk * row_dst_bytes + 128;
|
||||
htp_conv2d_dw_vtcm_layout_build(&layout, max_src_bytes, max_dst_bytes, w_size, n_threads);
|
||||
|
||||
kparams->kernel_type = kernel_type;
|
||||
kparams->n_threads = (uint8_t) n_threads;
|
||||
kparams->cwhn = cwhn ? 1 : 0;
|
||||
kparams->is_f16 = (src0->type == GGML_TYPE_F16) ? 1 : 0;
|
||||
kparams->total_tasks = total_tasks;
|
||||
kparams->vtcm_size = (uint32_t) layout.total_bytes;
|
||||
kparams->w = w;
|
||||
kparams->h = h;
|
||||
kparams->c = c;
|
||||
kparams->n = n;
|
||||
kparams->kw = kw;
|
||||
kparams->kh = kh;
|
||||
kparams->ow = ow_u;
|
||||
kparams->oh = oh_u;
|
||||
kparams->s0 = s0;
|
||||
kparams->s1 = s1;
|
||||
kparams->p0 = p0;
|
||||
kparams->p1 = p1;
|
||||
kparams->d0 = d0;
|
||||
kparams->d1 = d1;
|
||||
kparams->oh_chunk = oh_chunk;
|
||||
kparams->n_chunks = n_chunks;
|
||||
kparams->row_src_bytes = row_src_bytes;
|
||||
kparams->row_dst_bytes = row_dst_bytes;
|
||||
kparams->pad_top_stride = pad_top_stride;
|
||||
kparams->div_c = init_fastdiv_values(c);
|
||||
kparams->div_n_chunks = init_fastdiv_values(n_chunks > 0 ? n_chunks : 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <auto SolveFn>
|
||||
static inline bool htp_kparams_invoke(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
@@ -3372,6 +4095,31 @@ void htp_opnode_collapse(const struct ggml_hexagon_session * sess, htp_opnode &
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.opcode == HTP_OP_UPSCALE) {
|
||||
if (node.inputs.empty() || !node.inputs[0] || node.outputs.empty() || !node.outputs[0]) {
|
||||
return;
|
||||
}
|
||||
const struct ggml_tensor * src0 = node.inputs[0];
|
||||
const struct ggml_tensor * dst = node.outputs[0];
|
||||
|
||||
if ((src0->ne[3] > 1 || dst->ne[3] > 1) &&
|
||||
src0->ne[2] == dst->ne[2] && src0->ne[3] == dst->ne[3] &&
|
||||
src0->nb[3] == (size_t) src0->ne[2] * src0->nb[2] &&
|
||||
dst->nb[3] == (size_t) dst->ne[2] * dst->nb[2]) {
|
||||
struct ggml_tensor src0_c = *src0;
|
||||
struct ggml_tensor dst_c = *dst;
|
||||
src0_c.ne[2] *= src0_c.ne[3];
|
||||
src0_c.ne[3] = 1;
|
||||
src0_c.nb[3] = (size_t) src0_c.ne[2] * src0_c.nb[2];
|
||||
dst_c.ne[2] *= dst_c.ne[3];
|
||||
dst_c.ne[3] = 1;
|
||||
dst_c.nb[3] = (size_t) dst_c.ne[2] * dst_c.nb[2];
|
||||
node.inputs[0] = node.add_dummy(src0_c);
|
||||
node.outputs[0] = node.add_dummy(dst_c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool htp_kparams_solve(
|
||||
@@ -3401,6 +4149,9 @@ bool htp_kparams_solve(
|
||||
const size_t src2_size = src2 ? hex_round_up(ggml_nbytes(src2), 128) : 0;
|
||||
return htp_kparams_solve_matmul_impl(sess, src0, src1, dst, src2 ? src2->nb[1] : 0, src2_size, node.kparams<htp_mm_kernel_params>());
|
||||
}
|
||||
if (node.opcode == HTP_OP_UPSCALE) {
|
||||
return htp_kparams_solve_upscale(sess, src0, dst, node.kparams<htp_upscale_kernel_params>());
|
||||
}
|
||||
|
||||
return htp_kparams_solve(sess, node.node, node.kernel_params);
|
||||
}
|
||||
@@ -3411,7 +4162,7 @@ bool htp_kparams_solve(
|
||||
void * kparams
|
||||
) {
|
||||
const htp_op_code opcode = htp_op_remap(op);
|
||||
if (htp_op_is_unary(opcode) || htp_op_is_glu(opcode)) {
|
||||
if (htp_op_is_unary(opcode) || htp_op_is_glu(opcode) || opcode == HTP_OP_UPSCALE) {
|
||||
htp_opnode tmp(opcode, op);
|
||||
if (!htp_kparams_solve(sess, tmp)) {
|
||||
return false;
|
||||
@@ -3476,6 +4227,12 @@ bool htp_kparams_solve(
|
||||
case HTP_OP_IM2COL_3D:
|
||||
return htp_kparams_invoke<htp_kparams_solve_im2col_3d>(sess, op, kparams);
|
||||
|
||||
case HTP_OP_CONV_2D:
|
||||
return htp_kparams_invoke<htp_kparams_solve_conv2d>(sess, op, kparams);
|
||||
|
||||
case HTP_OP_CONV_2D_DW:
|
||||
return htp_kparams_invoke<htp_kparams_solve_conv2d_dw>(sess, op, kparams);
|
||||
|
||||
case HTP_OP_SUM_ROWS:
|
||||
case HTP_OP_SUM:
|
||||
case HTP_OP_ARGMAX:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "htp/argsort-ops.h"
|
||||
#include "htp/binary-ops.h"
|
||||
#include "htp/concat-ops.h"
|
||||
#include "htp/conv2d-ops.h"
|
||||
#include "htp/cpy-ops.h"
|
||||
#include "htp/cumsum-ops.h"
|
||||
#include "htp/diag-ops.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
#include "htp/upscale-ops.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -133,4 +135,19 @@ bool htp_kparams_solve_unary_mul_glu(
|
||||
struct htp_glu_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_upscale(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_upscale_kernel_params * kparams
|
||||
);
|
||||
|
||||
static inline bool htp_kparams_solve_upscale(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
struct htp_upscale_kernel_params * kparams
|
||||
) {
|
||||
return htp_kparams_solve_upscale(sess, op->src[0], op, kparams);
|
||||
}
|
||||
|
||||
#endif // HTP_KPARAMS_H
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
#include "htp/upscale-ops.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -224,6 +225,19 @@ static std::string format_kernel_params(const htp_opnode & node) {
|
||||
} else if (node.opcode == HTP_OP_SOLVE_TRI) {
|
||||
const auto * kparams = node.kparams<htp_solve_tri_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d", kparams->batched ? "batched" : "chunked", (int) kparams->n_threads);
|
||||
} else if (node.opcode == HTP_OP_UPSCALE) {
|
||||
const auto * kparams = node.kparams<htp_upscale_kernel_params>();
|
||||
const char * ktype_str = "unknown";
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_UPSCALE_KERNEL_NEAREST: ktype_str = "nearest"; break;
|
||||
case HTP_UPSCALE_KERNEL_NEAREST_2X: ktype_str = "nearest-2x"; break;
|
||||
case HTP_UPSCALE_KERNEL_NEAREST_4X: ktype_str = "nearest-4x"; break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR: ktype_str = kparams->align_corners ? "bilinear-ac" : "bilinear"; break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR_2X: ktype_str = "bilinear-2x"; break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR_AA: ktype_str = "bilinear-aa"; break;
|
||||
case HTP_UPSCALE_KERNEL_BICUBIC: ktype_str = "bicubic"; break;
|
||||
}
|
||||
snprintf(str, max_size, "%s nth %d chk %d vtcm %u", ktype_str, (int) kparams->n_threads, (int) kparams->dst_nrows_per_chunk, (unsigned int) kparams->vtcm_size);
|
||||
} else {
|
||||
snprintf(str, max_size, "----");
|
||||
}
|
||||
|
||||
@@ -44,8 +44,10 @@ add_library(${HTP_LIB} SHARED
|
||||
pad-ops.c
|
||||
argsort-ops.c
|
||||
im2col-ops.c
|
||||
conv2d-ops.c
|
||||
roll-ops.c
|
||||
pool-ops.c
|
||||
upscale-ops.c
|
||||
allreduce-ops.c
|
||||
)
|
||||
|
||||
|
||||
@@ -47,87 +47,108 @@ static void concat_2d_f32_transposed(unsigned int nth, unsigned int ith, void *
|
||||
const uint32_t src0_ne0 = src0->ne[0];
|
||||
const uint32_t src1_ne0 = src1->ne[0];
|
||||
|
||||
const uint32_t row_end = cctx->row_start + cctx->nrows;
|
||||
const uint32_t start_i = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t end_i = (start_i + cctx->nrows_per_thread < row_end) ? (start_i + cctx->nrows_per_thread) : row_end;
|
||||
if (start_i >= end_i) return;
|
||||
// dst rows [row_begin, row_end) of this thread; a dst row is a src0 row followed by a src1 column
|
||||
const uint32_t row_begin = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t row_end = MIN(row_begin + cctx->nrows_per_thread, cctx->row_start + cctx->nrows);
|
||||
if (row_begin >= row_end) return;
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
|
||||
const uint32_t block_i = 32;
|
||||
const uint32_t spad1_stride = block_i * sizeof(float);
|
||||
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_w(spad1_stride);
|
||||
const uint32_t src1_ne0_padded = hex_round_up(src1_ne0, 32);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(float);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_row_bytes = src0_row_padded + src1_ne0_padded * sizeof(float);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const struct fastdiv_values * div_ne2 = &cctx->div_ne2;
|
||||
const uint32_t ne2 = dst->ne[2];
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t i = start_i;
|
||||
// tile: block_rows dst rows x block_cols src1 columns
|
||||
const uint32_t block_rows = 128;
|
||||
const uint32_t block_cols = 256;
|
||||
// +1 element spreads the gather lanes across VTCM, so they do not serialize on one path
|
||||
const uint32_t spad1_stride = block_rows * sizeof(float) + sizeof(float);
|
||||
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, p, div_ne2, ne2) + i * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(end_i - i, block_i) * sizeof(float), src1_ne0);
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_w(spad1_stride);
|
||||
const uint32_t block_cols_padded = MIN(hex_round_up(src1_ne0, 32), block_cols);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(float);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_stride = src0_row_padded + block_cols_padded * sizeof(float);
|
||||
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, p, div_ne2, ne2) + i * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, MIN(end_i - i, block_i));
|
||||
// two slots per scratchpad: tile b gathers in slot b&1 while dst[b-1] and src1[b+1] DMA in the other
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
const uint32_t spad0_slot = block_rows * spad0_stride;
|
||||
const uint32_t spad1_slot = block_cols_padded * spad1_stride;
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
// tiles walk (plane, row, col) with col fastest; src0 goes out with the col == 0 tile only
|
||||
uint32_t plane = 0;
|
||||
uint32_t row = row_begin;
|
||||
uint32_t col = 0;
|
||||
uint32_t slot = 0;
|
||||
bool dst_pending = false;
|
||||
|
||||
while (p < cctx->nplanes) {
|
||||
const uint32_t current_block_i = MIN(end_i - i, block_i);
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, plane, div_ne2, ne2) + row * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(row_end - row, block_rows) * sizeof(float), MIN(src1_ne0, block_cols));
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
for (uint32_t j = 0; j < src1_ne0; j += 32) {
|
||||
const uint8_t * src_ptr = spad1_base + j * spad1_stride;
|
||||
uint8_t * dst_ptr = spad0_base + src0_row_padded + j * sizeof(float);
|
||||
hvx_transpose_32x32_w_gather(dst_ptr, spad0_row_bytes, src_ptr, spad1_stride, offsets, current_block_i, MIN(src1_ne0 - j, 32));
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, plane, div_ne2, ne2) + row * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_stride, src0->nb[1], src0_row_bytes, MIN(row_end - row, block_rows));
|
||||
|
||||
while (plane < cctx->nplanes) {
|
||||
const uint32_t nrows = MIN(row_end - row, block_rows);
|
||||
const uint32_t ncols = MIN(src1_ne0 - col, block_cols);
|
||||
|
||||
uint8_t * spad0 = spad0_base + slot * spad0_slot;
|
||||
uint8_t * spad1 = spad1_base + slot * spad1_slot;
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
if (col == 0) {
|
||||
dma_queue_pop(dma_q); // src0
|
||||
}
|
||||
hvx_gather_sync(spad0_base + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
uint32_t np = p;
|
||||
uint32_t ni = i + block_i;
|
||||
if (ni >= end_i) {
|
||||
ni = start_i;
|
||||
np++;
|
||||
uint32_t next_plane = plane;
|
||||
uint32_t next_row = row;
|
||||
uint32_t next_col = col + block_cols;
|
||||
if (next_col >= src1_ne0) {
|
||||
next_col = 0;
|
||||
next_row = row + block_rows;
|
||||
if (next_row >= row_end) {
|
||||
next_row = row_begin;
|
||||
next_plane++;
|
||||
}
|
||||
}
|
||||
const bool has_next = np < cctx->nplanes;
|
||||
const uint32_t next_block_i = MIN(end_i - ni, block_i);
|
||||
const bool has_next = next_plane < cctx->nplanes;
|
||||
const uint32_t next_nrows = MIN(row_end - next_row, block_rows);
|
||||
const uint32_t next_ncols = MIN(src1_ne0 - next_col, block_cols);
|
||||
|
||||
// spad1 is free after the gather sync, prefetch next src1 ahead of the dst write
|
||||
// other spad1 slot is free since the previous gather sync
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc1_addr = concat_plane_addr(src1, np, div_ne2, ne2) + ni * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, nsrc1_addr), spad1_stride, src1->nb[0], next_block_i * sizeof(float), src1_ne0);
|
||||
const dma_addr_t addr = concat_plane_addr(src1, next_plane, div_ne2, ne2) + next_row * src1->nb[1] + next_col * src1->nb[0];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base + (slot ^ 1) * spad1_slot, addr), spad1_stride, src1->nb[0], next_nrows * sizeof(float), next_ncols);
|
||||
}
|
||||
|
||||
dma_queue_pop(dma_q); // src0
|
||||
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, p, div_ne2, ne2) + i * dst->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0_base + src0_pre), dst->nb[1], spad0_row_bytes, (src0_ne0 + src1_ne0) * sizeof(float), current_block_i);
|
||||
|
||||
if (has_next) {
|
||||
dma_queue_pop(dma_q); // next src1
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) row);
|
||||
for (uint32_t c = 0; c < ncols; c += 32) {
|
||||
hvx_transpose_32x32_w_gather(spad0 + src0_row_padded + c * sizeof(float), spad0_stride, spad1 + c * spad1_stride, spad1_stride, offsets, nrows, MIN(ncols - c, 32));
|
||||
}
|
||||
dma_queue_pop(dma_q); // dst
|
||||
hvx_gather_sync(spad0 + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) row);
|
||||
|
||||
// spad0 is free after the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc0_addr = concat_plane_addr(src0, np, div_ne2, ne2) + ni * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, nsrc0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, next_block_i);
|
||||
// other spad0 slot is free once dst[b-1] is written
|
||||
if (dst_pending) {
|
||||
dma_queue_pop(dma_q); // dst
|
||||
}
|
||||
if (has_next && next_col == 0) {
|
||||
const dma_addr_t addr = concat_plane_addr(src0, next_plane, div_ne2, ne2) + next_row * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + (slot ^ 1) * spad0_slot + src0_pre, addr), spad0_stride, src0->nb[1], src0_row_bytes, next_nrows);
|
||||
}
|
||||
|
||||
p = np;
|
||||
i = ni;
|
||||
// the col == 0 tile also carries the src0 columns in front
|
||||
const uint32_t lead = (col == 0) ? src0_row_bytes : 0;
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, plane, div_ne2, ne2) + row * dst->nb[1] + src0_row_bytes + col * sizeof(float) - lead;
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0 + src0_row_padded - lead), dst->nb[1], spad0_stride, lead + ncols * sizeof(float), nrows);
|
||||
dst_pending = true;
|
||||
|
||||
plane = next_plane;
|
||||
row = next_row;
|
||||
col = next_col;
|
||||
slot ^= 1;
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
@@ -143,87 +164,108 @@ static void concat_2d_f16_transposed(unsigned int nth, unsigned int ith, void *
|
||||
const uint32_t src0_ne0 = src0->ne[0];
|
||||
const uint32_t src1_ne0 = src1->ne[0];
|
||||
|
||||
const uint32_t row_end = cctx->row_start + cctx->nrows;
|
||||
const uint32_t start_i = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t end_i = (start_i + cctx->nrows_per_thread < row_end) ? (start_i + cctx->nrows_per_thread) : row_end;
|
||||
if (start_i >= end_i) return;
|
||||
// dst rows [row_begin, row_end) of this thread; a dst row is a src0 row followed by a src1 column
|
||||
const uint32_t row_begin = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t row_end = MIN(row_begin + cctx->nrows_per_thread, cctx->row_start + cctx->nrows);
|
||||
if (row_begin >= row_end) return;
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
|
||||
const uint32_t block_i = 64;
|
||||
const uint32_t spad1_stride = block_i * sizeof(__fp16);
|
||||
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_h(spad1_stride);
|
||||
const uint32_t src1_ne0_padded = hex_round_up(src1_ne0, 64);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(__fp16);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_row_bytes = src0_row_padded + src1_ne0_padded * sizeof(__fp16);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const struct fastdiv_values * div_ne2 = &cctx->div_ne2;
|
||||
const uint32_t ne2 = dst->ne[2];
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t i = start_i;
|
||||
// tile: block_rows dst rows x block_cols src1 columns
|
||||
const uint32_t block_rows = 128;
|
||||
const uint32_t block_cols = 512;
|
||||
// +1 element spreads the gather lanes across VTCM, so they do not serialize on one path
|
||||
const uint32_t spad1_stride = block_rows * sizeof(__fp16) + sizeof(__fp16);
|
||||
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, p, div_ne2, ne2) + i * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(end_i - i, block_i) * sizeof(__fp16), src1_ne0);
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_h(spad1_stride);
|
||||
const uint32_t block_cols_padded = MIN(hex_round_up(src1_ne0, 64), block_cols);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(__fp16);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_stride = src0_row_padded + block_cols_padded * sizeof(__fp16);
|
||||
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, p, div_ne2, ne2) + i * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, MIN(end_i - i, block_i));
|
||||
// two slots per scratchpad: tile b gathers in slot b&1 while dst[b-1] and src1[b+1] DMA in the other
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
const uint32_t spad0_slot = block_rows * spad0_stride;
|
||||
const uint32_t spad1_slot = block_cols_padded * spad1_stride;
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
// tiles walk (plane, row, col) with col fastest; src0 goes out with the col == 0 tile only
|
||||
uint32_t plane = 0;
|
||||
uint32_t row = row_begin;
|
||||
uint32_t col = 0;
|
||||
uint32_t slot = 0;
|
||||
bool dst_pending = false;
|
||||
|
||||
while (p < cctx->nplanes) {
|
||||
const uint32_t current_block_i = MIN(end_i - i, block_i);
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, plane, div_ne2, ne2) + row * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(row_end - row, block_rows) * sizeof(__fp16), MIN(src1_ne0, block_cols));
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
for (uint32_t j = 0; j < src1_ne0; j += 64) {
|
||||
const uint8_t * src_ptr = spad1_base + j * spad1_stride;
|
||||
uint8_t * dst_ptr = spad0_base + src0_row_padded + j * sizeof(__fp16);
|
||||
hvx_transpose_64x64_h_gather(dst_ptr, spad0_row_bytes, src_ptr, spad1_stride, offsets, current_block_i, MIN(src1_ne0 - j, 64));
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, plane, div_ne2, ne2) + row * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_stride, src0->nb[1], src0_row_bytes, MIN(row_end - row, block_rows));
|
||||
|
||||
while (plane < cctx->nplanes) {
|
||||
const uint32_t nrows = MIN(row_end - row, block_rows);
|
||||
const uint32_t ncols = MIN(src1_ne0 - col, block_cols);
|
||||
|
||||
uint8_t * spad0 = spad0_base + slot * spad0_slot;
|
||||
uint8_t * spad1 = spad1_base + slot * spad1_slot;
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
if (col == 0) {
|
||||
dma_queue_pop(dma_q); // src0
|
||||
}
|
||||
hvx_gather_sync(spad0_base + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
uint32_t np = p;
|
||||
uint32_t ni = i + block_i;
|
||||
if (ni >= end_i) {
|
||||
ni = start_i;
|
||||
np++;
|
||||
uint32_t next_plane = plane;
|
||||
uint32_t next_row = row;
|
||||
uint32_t next_col = col + block_cols;
|
||||
if (next_col >= src1_ne0) {
|
||||
next_col = 0;
|
||||
next_row = row + block_rows;
|
||||
if (next_row >= row_end) {
|
||||
next_row = row_begin;
|
||||
next_plane++;
|
||||
}
|
||||
}
|
||||
const bool has_next = np < cctx->nplanes;
|
||||
const uint32_t next_block_i = MIN(end_i - ni, block_i);
|
||||
const bool has_next = next_plane < cctx->nplanes;
|
||||
const uint32_t next_nrows = MIN(row_end - next_row, block_rows);
|
||||
const uint32_t next_ncols = MIN(src1_ne0 - next_col, block_cols);
|
||||
|
||||
// spad1 is free after the gather sync, prefetch next src1 ahead of the dst write
|
||||
// other spad1 slot is free since the previous gather sync
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc1_addr = concat_plane_addr(src1, np, div_ne2, ne2) + ni * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, nsrc1_addr), spad1_stride, src1->nb[0], next_block_i * sizeof(__fp16), src1_ne0);
|
||||
const dma_addr_t addr = concat_plane_addr(src1, next_plane, div_ne2, ne2) + next_row * src1->nb[1] + next_col * src1->nb[0];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base + (slot ^ 1) * spad1_slot, addr), spad1_stride, src1->nb[0], next_nrows * sizeof(__fp16), next_ncols);
|
||||
}
|
||||
|
||||
dma_queue_pop(dma_q); // src0
|
||||
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, p, div_ne2, ne2) + i * dst->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0_base + src0_pre), dst->nb[1], spad0_row_bytes, (src0_ne0 + src1_ne0) * sizeof(__fp16), current_block_i);
|
||||
|
||||
if (has_next) {
|
||||
dma_queue_pop(dma_q); // next src1
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) row);
|
||||
for (uint32_t c = 0; c < ncols; c += 64) {
|
||||
hvx_transpose_64x64_h_gather(spad0 + src0_row_padded + c * sizeof(__fp16), spad0_stride, spad1 + c * spad1_stride, spad1_stride, offsets, nrows, MIN(ncols - c, 64));
|
||||
}
|
||||
dma_queue_pop(dma_q); // dst
|
||||
hvx_gather_sync(spad0 + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) row);
|
||||
|
||||
// spad0 is free after the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc0_addr = concat_plane_addr(src0, np, div_ne2, ne2) + ni * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, nsrc0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, next_block_i);
|
||||
// other spad0 slot is free once dst[b-1] is written
|
||||
if (dst_pending) {
|
||||
dma_queue_pop(dma_q); // dst
|
||||
}
|
||||
if (has_next && next_col == 0) {
|
||||
const dma_addr_t addr = concat_plane_addr(src0, next_plane, div_ne2, ne2) + next_row * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + (slot ^ 1) * spad0_slot + src0_pre, addr), spad0_stride, src0->nb[1], src0_row_bytes, next_nrows);
|
||||
}
|
||||
|
||||
p = np;
|
||||
i = ni;
|
||||
// the col == 0 tile also carries the src0 columns in front
|
||||
const uint32_t lead = (col == 0) ? src0_row_bytes : 0;
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, plane, div_ne2, ne2) + row * dst->nb[1] + src0_row_bytes + col * sizeof(__fp16) - lead;
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0 + src0_row_padded - lead), dst->nb[1], spad0_stride, lead + ncols * sizeof(__fp16), nrows);
|
||||
dst_pending = true;
|
||||
|
||||
plane = next_plane;
|
||||
row = next_row;
|
||||
col = next_col;
|
||||
slot ^= 1;
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
@@ -53,13 +53,17 @@ static inline void htp_concat_transposed_vtcm_layout_build(
|
||||
uint32_t type_size,
|
||||
uint32_t n_threads) {
|
||||
|
||||
uint32_t block_i = (type_size == 4) ? 32 : 64;
|
||||
uint32_t spad1_stride = block_i * type_size;
|
||||
uint32_t src1_ne0_padded = hex_round_up(src1_ne0, block_i);
|
||||
uint32_t spad0_row_bytes = hex_round_up(src0_ne0 * type_size, 128) + src1_ne0_padded * type_size;
|
||||
// src1_ne0 is processed in chunks of block_cols tokens, so VTCM does not grow with the batch
|
||||
uint32_t block_rows = 128;
|
||||
uint32_t block_cols = (type_size == 4) ? 256 : 512;
|
||||
// +1 element spreads the gather lanes across VTCM, so they do not serialize on one path
|
||||
uint32_t spad1_stride = block_rows * type_size + type_size;
|
||||
uint32_t block_cols_padded = MIN(hex_round_up(src1_ne0, (type_size == 4) ? 32 : 64), block_cols);
|
||||
uint32_t spad0_row_bytes = hex_round_up(src0_ne0 * type_size, 128) + block_cols_padded * type_size;
|
||||
|
||||
L->src0_spad_size_per_thread = block_i * spad0_row_bytes;
|
||||
L->src1_spad_size_per_thread = src1_ne0_padded * spad1_stride;
|
||||
// double-buffered: two slots per thread
|
||||
L->src0_spad_size_per_thread = 2 * block_rows * spad0_row_bytes;
|
||||
L->src1_spad_size_per_thread = 2 * block_cols_padded * spad1_stride;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_spad0, (size_t) n_threads * L->src0_spad_size_per_thread);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,197 @@
|
||||
#ifndef HTP_CONV2D_OPS_H
|
||||
#define HTP_CONV2D_OPS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
enum htp_conv2d_kernel_type {
|
||||
HTP_CONV2D_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_CONV2D_KERNEL_HMX_WINDOW, // HMX convolution with sliding-window staging
|
||||
HTP_CONV2D_KERNEL_HMX_TILED, // tiled implicit-GEMM convolution on HMX
|
||||
};
|
||||
|
||||
// HMX_WINDOW output staging buffers per thread
|
||||
#define HTP_CONV2D_WIN_OBUF 2
|
||||
|
||||
// HMX scales of one row tile at the start of VTCM: 32 (scale, bias) fp16 pairs and 128 zero bytes
|
||||
#define HTP_CONV2D_HMX_SCALES_SIZE 256
|
||||
|
||||
struct htp_conv2d_tiled_params {
|
||||
uint32_t w_kstride;
|
||||
uint32_t k_size;
|
||||
uint32_t k_group;
|
||||
uint32_t k_group_pad;
|
||||
uint32_t n_groups;
|
||||
uint32_t n_k_tiles;
|
||||
uint32_t n_ow_tiles;
|
||||
uint32_t seg_tiles;
|
||||
uint32_t stage_row;
|
||||
uint32_t n_stage;
|
||||
uint32_t wk_chunk;
|
||||
struct fastdiv_values div_ow_tiles;
|
||||
struct fastdiv_values div_oh;
|
||||
};
|
||||
|
||||
struct htp_conv2d_window_params {
|
||||
uint32_t fold;
|
||||
uint32_t n_cc;
|
||||
uint32_t act_rt_last;
|
||||
uint32_t sr;
|
||||
uint32_t ihs;
|
||||
uint32_t max_off;
|
||||
uint32_t n_bc;
|
||||
uint32_t seg_b;
|
||||
uint32_t seg_t;
|
||||
uint32_t w_cs;
|
||||
struct fastdiv_values div_sr;
|
||||
struct fastdiv_values div_ihs;
|
||||
struct fastdiv_values div_kw;
|
||||
struct fastdiv_values div_tg;
|
||||
struct fastdiv_values div_cc;
|
||||
struct fastdiv_values div_cs;
|
||||
};
|
||||
|
||||
struct htp_conv2d_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t n_threads;
|
||||
uint8_t cwhn;
|
||||
uint8_t w_kmajor;
|
||||
|
||||
uint32_t total_rows;
|
||||
uint32_t n_row_tiles;
|
||||
uint32_t ct_chunk;
|
||||
uint32_t rt_block;
|
||||
uint32_t stage_size;
|
||||
uint32_t thr_size;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
union {
|
||||
struct htp_conv2d_tiled_params tiled;
|
||||
struct htp_conv2d_window_params window;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_conv2d_kernel_params) <= 128, "htp_conv2d_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_conv2d_kernel_params) <= 128, "htp_conv2d_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
enum htp_conv2d_dw_kernel_type {
|
||||
HTP_CONV2D_DW_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_CONV2D_DW_KERNEL_CWHN, // CWHN channel-contiguous layout
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S1, // WHCN general stride-1
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S2, // WHCN general stride-2
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S1_K3X3, // WHCN aligned w%32==0, 3x3 s=1 p=1 d=1
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3_W32, // WHCN w=32->ow=16, 3x3 s=2 p=1 d=1 (2 rows per vector)
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3_W64, // WHCN w=64->ow=32, 3x3 s=2 p=1 d=1 (1 vector rolling kh)
|
||||
HTP_CONV2D_DW_KERNEL_WHCN_S2_K3X3, // WHCN aligned w%32==0 (w>=96), 3x3 s=2 p=1 d=1
|
||||
HTP_CONV2D_DW_KERNEL_COUNT,
|
||||
};
|
||||
|
||||
struct htp_conv2d_dw_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t n_threads;
|
||||
uint8_t cwhn;
|
||||
uint8_t is_f16;
|
||||
|
||||
uint32_t total_tasks;
|
||||
uint32_t vtcm_size;
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
uint32_t c;
|
||||
uint32_t n;
|
||||
uint32_t kw;
|
||||
uint32_t kh;
|
||||
uint32_t ow;
|
||||
uint32_t oh;
|
||||
int32_t s0;
|
||||
int32_t s1;
|
||||
int32_t p0;
|
||||
int32_t p1;
|
||||
int32_t d0;
|
||||
int32_t d1;
|
||||
uint32_t oh_chunk;
|
||||
uint32_t n_chunks;
|
||||
uint32_t row_src_bytes;
|
||||
uint32_t row_dst_bytes;
|
||||
uint32_t pad_top_stride;
|
||||
struct fastdiv_values div_c;
|
||||
struct fastdiv_values div_n_chunks;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_conv2d_dw_kernel_params) <= 128, "htp_conv2d_dw_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_conv2d_dw_kernel_params) <= 128, "htp_conv2d_dw_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct htp_conv2d_dw_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_w;
|
||||
size_t off_src;
|
||||
size_t off_dst;
|
||||
size_t src_spad_half_size;
|
||||
size_t dst_spad_half_size;
|
||||
size_t src_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_conv2d_dw_vtcm_layout_build(
|
||||
struct htp_conv2d_dw_vtcm_layout * L,
|
||||
uint32_t max_src_bytes,
|
||||
uint32_t max_dst_bytes,
|
||||
uint32_t w_size,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
L->src_spad_half_size = hex_round_up(max_src_bytes + 256, 128);
|
||||
L->dst_spad_half_size = hex_round_up(max_dst_bytes + 128, 128);
|
||||
L->src_bytes_per_thread = 2 * L->src_spad_half_size;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_spad_half_size;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_w, hex_round_up(w_size, 128));
|
||||
VTCM_LAYOUT_ALLOC(off, off_src, L->src_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_conv2d_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_scales;
|
||||
size_t off_w;
|
||||
size_t off_act[2];
|
||||
size_t off_out[2];
|
||||
size_t off_thr;
|
||||
|
||||
size_t act_size;
|
||||
};
|
||||
|
||||
static inline void htp_conv2d_vtcm_layout_build(
|
||||
struct htp_conv2d_vtcm_layout * L,
|
||||
uint32_t rt_block,
|
||||
size_t w_size,
|
||||
size_t act_size,
|
||||
size_t out_size,
|
||||
size_t thr_size,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
const size_t sc = hex_round_up((size_t) rt_block * HTP_CONV2D_HMX_SCALES_SIZE, 2048);
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_scales, sc);
|
||||
VTCM_LAYOUT_ALLOC(off, off_w, w_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_act[0], act_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_act[1], act_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_out[0], out_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_out[1], out_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_thr, (size_t) n_threads * thr_size);
|
||||
L->act_size = act_size;
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
#endif // HTP_CONV2D_OPS_H
|
||||
@@ -1456,9 +1456,10 @@ static inline void gdn_dma_push_chunk_gb(
|
||||
const dma_addr_t g_dma = g->data + (uint64_t) iv3 * g->nb[3] + (uint64_t) t_chunk * g->nb[2] + (uint64_t) iv1 * g->nb[1];
|
||||
const dma_addr_t beta_dma = beta->data + (uint64_t) iv3 * beta->nb[3] + (uint64_t) t_chunk * beta->nb[2] + (uint64_t) iv1 * beta->nb[1];
|
||||
const uint32_t row_bytes = n_batch * sizeof(float);
|
||||
const uint32_t dst_stride = (n_batch > 1) ? row_bytes + sizeof(float) : row_bytes;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(vtcm_g_raw, g_dma), row_bytes, g->nb[2], row_bytes, chunk_size);
|
||||
dma_queue_push(dma_q, dma_make_data(vtcm_b_raw, beta_dma), row_bytes, beta->nb[2], row_bytes, chunk_size);
|
||||
dma_queue_push(dma_q, dma_make_data(vtcm_g_raw, g_dma), dst_stride, g->nb[2], row_bytes, chunk_size);
|
||||
dma_queue_push(dma_q, dma_make_data(vtcm_b_raw, beta_dma), dst_stride, beta->nb[2], row_bytes, chunk_size);
|
||||
}
|
||||
|
||||
static inline void gdn_pack_s_col_tiles(
|
||||
@@ -1707,19 +1708,16 @@ static void gdn_hvx_phase1a_worker(unsigned int n, unsigned int i, void * data)
|
||||
hvx_vmem(head->b_f32[curr_buf] + 0) = hvx_vmem(bctx->vtcm_b_raw + 0);
|
||||
hvx_vmem(head->b_f32[curr_buf] + 32) = hvx_vmem(bctx->vtcm_b_raw + 32);
|
||||
} else {
|
||||
int32_t offsets[32] __attribute__((aligned(128)));
|
||||
for (int k = 0; k < 32; ++k) {
|
||||
offsets[k] = k * n_batch * sizeof(float);
|
||||
}
|
||||
HVX_Vector vv = *(const HVX_Vector *) offsets;
|
||||
const uint32_t vtcm_stride = (n_batch + 1) * sizeof(float);
|
||||
const HVX_Vector vv = hvx_vec_gather_offsets_w(vtcm_stride);
|
||||
const size_t rt_g = (size_t) ((const uint8_t *) bctx->vtcm_g_raw + i * sizeof(float));
|
||||
const size_t rt_b = (size_t) ((const uint8_t *) bctx->vtcm_b_raw + i * sizeof(float));
|
||||
const size_t mu = 64 * n_batch * sizeof(float);
|
||||
const size_t mu = 64 * vtcm_stride;
|
||||
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->g_f32[curr_buf] + 0), rt_g, mu, vv);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->g_f32[curr_buf] + 32), rt_g + 32 * n_batch * sizeof(float), mu, vv);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->g_f32[curr_buf] + 32), rt_g + 32 * vtcm_stride, mu, vv);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->b_f32[curr_buf] + 0), rt_b, mu, vv);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->b_f32[curr_buf] + 32), rt_b + 32 * n_batch * sizeof(float), mu, vv);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (head->b_f32[curr_buf] + 32), rt_b + 32 * vtcm_stride, mu, vv);
|
||||
}
|
||||
|
||||
const uint32_t t_chunk = bctx->c * 64;
|
||||
|
||||
@@ -204,7 +204,7 @@ static inline void htp_gdn_hmx_vtcm_layout_build(
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_g_f32[1], bh * dma_scalar_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_b_f32[0], bh * dma_scalar_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_b_f32[1], bh * dma_scalar_sz, pipeline);
|
||||
const size_t raw_gb_sz = hex_round_up(bh * chunk_size * sizeof(float), 128);
|
||||
const size_t raw_gb_sz = hex_round_up((bh + 1) * chunk_size * sizeof(float), 128);
|
||||
VTCM_LAYOUT_ALLOC(off, off_g_raw[0], raw_gb_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_g_raw[1], raw_gb_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_b_raw[0], raw_gb_sz);
|
||||
|
||||
@@ -182,9 +182,12 @@ int op_gated_delta_net(struct htp_ops_context * octx);
|
||||
int op_pad(struct htp_ops_context * octx);
|
||||
int op_im2col(struct htp_ops_context * octx);
|
||||
int op_im2col_3d(struct htp_ops_context * octx);
|
||||
int op_conv_2d(struct htp_ops_context * octx);
|
||||
int op_conv_2d_dw(struct htp_ops_context * octx);
|
||||
int op_pool_2d(struct htp_ops_context * octx);
|
||||
int op_pool_1d(struct htp_ops_context * octx);
|
||||
int op_allreduce(struct htp_ops_context * octx);
|
||||
int op_roll(struct htp_ops_context * octx);
|
||||
int op_upscale(struct htp_ops_context * octx);
|
||||
|
||||
#endif /* HTP_CTX_H */
|
||||
|
||||
@@ -96,6 +96,8 @@ enum htp_op_code {
|
||||
HTP_OP_CLAMP,
|
||||
HTP_OP_LEAKY_RELU,
|
||||
HTP_OP_IM2COL,
|
||||
HTP_OP_CONV_2D,
|
||||
HTP_OP_CONV_2D_DW,
|
||||
HTP_OP_FENCE,
|
||||
HTP_OP_ALLREDUCE,
|
||||
HTP_OP_ALLREDUCE_ADD,
|
||||
@@ -114,6 +116,7 @@ enum htp_op_code {
|
||||
HTP_OP_GLU_SIGMOID,
|
||||
HTP_OP_UNARY_CEIL,
|
||||
HTP_OP_UNARY_SGN,
|
||||
HTP_OP_UPSCALE,
|
||||
|
||||
HTP_OP_INVALID
|
||||
};
|
||||
@@ -134,6 +137,8 @@ enum htp_op_code {
|
||||
|
||||
#define HTP_MMAP_MAX_VMEM (2147483648u)
|
||||
|
||||
#define HTP_DDR_SPAD_SIZE (512 * 1024)
|
||||
|
||||
enum htp_tensor_flags {
|
||||
HTP_TENSOR_WEIGHT = (1U << 0), // Tensor buffer model weight data (not compute)
|
||||
HTP_TENSOR_TILED = (1U << 1), // Tensor is in tiled format
|
||||
|
||||
@@ -11,7 +11,7 @@ struct htp_iface_pmu_conf {
|
||||
};
|
||||
|
||||
interface htp_iface : remote_handle64 {
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 vtcm_size, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult stop();
|
||||
AEEResult mmap(in uint32 fd, in uint64 size);
|
||||
AEEResult munmap(in uint32 fd);
|
||||
|
||||
@@ -26,6 +26,7 @@ struct htp_im2col_context {
|
||||
uint32_t pe_row_base; // first output row index assigned to this dev (DMA path)
|
||||
uint32_t pe_nrows; // number of output rows assigned to this dev (DMA path)
|
||||
uint32_t pe_rows_per_thread; // output rows per worker
|
||||
uint32_t pe_blocks_per_thread; // output-col blocks per worker (blocked DMA path)
|
||||
uint32_t pe_src_row_bytes; // one output row's source, rounded 256
|
||||
uint32_t pe_dst_row_bytes; // one output row's dst: OW*patch_stride*2, rounded 256
|
||||
|
||||
@@ -38,6 +39,7 @@ struct htp_im2col_context {
|
||||
uint32_t pe_owb; // output-col block size
|
||||
uint32_t pe_wb; // staged source window width
|
||||
uint32_t pe_nbpr; // blocks per row
|
||||
uint32_t pe_gather_nvec; // vectors per gathered patch (KH == 1 blocked path)
|
||||
|
||||
// Precomputed dimensions for worker threads
|
||||
uint32_t N;
|
||||
@@ -51,6 +53,7 @@ struct htp_im2col_context {
|
||||
uint32_t thread_in[HTP_MAX_NTHREADS];
|
||||
uint32_t thread_iod[HTP_MAX_NTHREADS];
|
||||
uint32_t thread_ioh[HTP_MAX_NTHREADS];
|
||||
uint32_t thread_c0[HTP_MAX_NTHREADS];
|
||||
|
||||
void * compute;
|
||||
uint32_t dst_elem;
|
||||
@@ -215,6 +218,109 @@ static void FNAME(
|
||||
IM2COL_BLOCKED_COMPUTE_BODY(im2col_blocked_compute_f16, uint16_t, hvx_copy_f16_f32_uu, hvx_splat_u16_u)
|
||||
IM2COL_BLOCKED_COMPUTE_BODY(im2col_blocked_compute_f32, float, hvx_copy_f32_uu, hvx_splat_f32_u)
|
||||
|
||||
// Offsets of one output patch in the staged window, for KH == 1: lane k reads channel k / KW at tap k % KW.
|
||||
// Lanes past the patch read the first element and are not stored.
|
||||
// The first vector is built lane by lane; each next one is 32 lanes further: the tap advances by 32 % KW
|
||||
// and the channel by 32 / KW, plus one more when the tap wraps past KW.
|
||||
static __attribute__((noinline)) void im2col_gather_table_build(const struct htp_im2col_context * ictx,
|
||||
HVX_Vector * restrict table) {
|
||||
const int32_t d0 = ictx->octx->op_params[4];
|
||||
const uint32_t KW = ictx->KW, Wb = ictx->pe_wb;
|
||||
|
||||
int32_t tap0[32] __attribute__((aligned(128)));
|
||||
int32_t off0[32] __attribute__((aligned(128)));
|
||||
uint32_t iic = 0, ikw = 0;
|
||||
for (uint32_t k = 0; k < 32; k++) {
|
||||
tap0[k] = (int32_t) ikw;
|
||||
off0[k] = (int32_t) ((iic * Wb + ikw * d0) * sizeof(float));
|
||||
if (++ikw == KW) {
|
||||
ikw = 0;
|
||||
iic++;
|
||||
}
|
||||
}
|
||||
// (iic, ikw) is now (32 / KW, 32 % KW)
|
||||
const HVX_Vector v_kw = Q6_V_vsplat_R(KW);
|
||||
const HVX_Vector v_kw_m1 = Q6_V_vsplat_R(KW - 1);
|
||||
const HVX_Vector v_tap_step = Q6_V_vsplat_R(ikw);
|
||||
const HVX_Vector v_off_step = Q6_V_vsplat_R((iic * Wb + ikw * d0) * sizeof(float));
|
||||
const HVX_Vector v_off_wrap = Q6_V_vsplat_R((Wb - KW * d0) * sizeof(float));
|
||||
const HVX_Vector v_32 = Q6_V_vsplat_R(32);
|
||||
const HVX_Vector v_n = Q6_V_vsplat_R(ictx->patch_stride);
|
||||
|
||||
HVX_Vector lane = hvx_vec_gather_offsets_w(1);
|
||||
HVX_Vector tap = *(HVX_Vector *) tap0;
|
||||
HVX_Vector off = *(HVX_Vector *) off0;
|
||||
for (uint32_t g = 0; g < ictx->pe_gather_nvec; g++) {
|
||||
table[g] = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VwVw(v_n, lane), off, Q6_V_vzero());
|
||||
|
||||
lane = Q6_Vw_vadd_VwVw(lane, v_32);
|
||||
tap = Q6_Vw_vadd_VwVw(tap, v_tap_step);
|
||||
off = Q6_Vw_vadd_VwVw(off, v_off_step);
|
||||
|
||||
const HVX_VectorPred wrap = Q6_Q_vcmp_gt_VwVw(tap, v_kw_m1);
|
||||
tap = Q6_V_vmux_QVV(wrap, Q6_Vw_vsub_VwVw(tap, v_kw), tap);
|
||||
off = Q6_V_vmux_QVV(wrap, Q6_Vw_vadd_VwVw(off, v_off_wrap), off);
|
||||
}
|
||||
}
|
||||
|
||||
// KH == 1: each output row is one patch, gathered from the staged [IC][Wb] window with a fixed offset
|
||||
// table shifted by s0 per row, then converted with a single copy.
|
||||
// scratch holds the offset table followed by the gathered row (both VTCM).
|
||||
#define IM2COL_GATHER_COMPUTE_BODY(FNAME, DST_CTYPE, COPY_FN) \
|
||||
static void FNAME( \
|
||||
const struct htp_im2col_context * ictx, \
|
||||
void * restrict dstb_raw, const float * restrict srcb_c, \
|
||||
float * restrict scratch, \
|
||||
uint32_t nb, uint32_t c0, uint32_t ioh) { \
|
||||
DST_CTYPE * restrict dstb = (DST_CTYPE *) dstb_raw; \
|
||||
float * restrict srcb = (float *) srcb_c; \
|
||||
const int32_t s0 = ictx->octx->op_params[0], p0 = ictx->octx->op_params[2]; \
|
||||
const int32_t s1 = ictx->octx->op_params[1], p1 = ictx->octx->op_params[3]; \
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW, Wb = ictx->pe_wb; \
|
||||
const uint32_t patch_stride = ictx->patch_stride; \
|
||||
const uint32_t ng = ictx->pe_gather_nvec; \
|
||||
const int32_t win0 = (int32_t) c0 * s0 - p0; \
|
||||
const int32_t iih = (int32_t) ioh * s1 - p1; \
|
||||
\
|
||||
/* the DMA stage fills only the columns inside [0, IW): zero the rest of the window */ \
|
||||
const int32_t lo = win0 < 0 ? -win0 : 0; \
|
||||
const int32_t hi = MIN((int32_t) Wb, (int32_t) IW - win0); \
|
||||
if (iih < 0 || iih >= (int32_t) IH || hi <= lo) { \
|
||||
for (uint32_t iic = 0; iic < IC; iic++) { \
|
||||
hvx_splat_f32_u(srcb + (size_t) iic * Wb, 0, Wb); \
|
||||
} \
|
||||
} else if (lo > 0 || hi < (int32_t) Wb) { \
|
||||
for (uint32_t iic = 0; iic < IC; iic++) { \
|
||||
float * row = srcb + (size_t) iic * Wb; \
|
||||
if (lo > 0) { \
|
||||
hvx_splat_f32_u(row, 0, (uint32_t) lo); \
|
||||
} \
|
||||
if (hi < (int32_t) Wb) { \
|
||||
hvx_splat_f32_u(row + hi, 0, Wb - (uint32_t) hi); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
const HVX_Vector * table = (const HVX_Vector *) scratch; \
|
||||
float * row = scratch + (size_t) ng * 32; \
|
||||
const uint32_t region = IC * Wb * sizeof(float); \
|
||||
\
|
||||
for (uint32_t j = 0; j < nb; j++) { \
|
||||
const uint32_t shift = j * (uint32_t) s0 * sizeof(float); \
|
||||
const size_t base = (size_t) ((const uint8_t *) srcb + shift); \
|
||||
const uint32_t mu = region - shift - 1; \
|
||||
for (uint32_t g = 0; g < ng; g++) { \
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (row + g * 32), base, mu, table[g]); \
|
||||
} \
|
||||
hvx_gather_sync(row + (ng - 1) * 32); \
|
||||
DST_CTYPE * out = dstb + (size_t) j * patch_stride; \
|
||||
COPY_FN((uint8_t *) out, (const uint8_t *) row, patch_stride); \
|
||||
} \
|
||||
}
|
||||
|
||||
IM2COL_GATHER_COMPUTE_BODY(im2col_gather_compute_f16, uint16_t, hvx_copy_f16_f32_uu)
|
||||
IM2COL_GATHER_COMPUTE_BODY(im2col_gather_compute_f32, float, hvx_copy_f32_uu)
|
||||
|
||||
static void im2col_blocked_dma_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
struct htp_im2col_context * ictx = (struct htp_im2col_context *) data;
|
||||
@@ -234,24 +340,24 @@ static void im2col_blocked_dma_thread(unsigned int nth, unsigned int ith, void *
|
||||
float * srcb2[2] = { (float *) srcb_base, (float *) (srcb_base + ictx->pe_src_row_bytes) };
|
||||
void * dstb2[2] = { dstb_base, dstb_base + ictx->pe_dst_row_bytes };
|
||||
float * scratch = (float *) (srcb_base + 2 * ictx->pe_src_row_bytes);
|
||||
const uint32_t row_end_max = ictx->pe_row_base + ictx->pe_nrows;
|
||||
const uint32_t row_start = ictx->pe_row_base + ictx->pe_rows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + ictx->pe_rows_per_thread, row_end_max);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
const uint32_t nrows_local = row_end - row_start;
|
||||
const uint32_t total_blocks = nrows_local * nbpr;
|
||||
if (total_blocks == 0) {
|
||||
// work is split over output-col blocks, so a single output row (1D) still uses every thread
|
||||
const uint32_t blk_end_max = ictx->pe_nrows * nbpr;
|
||||
const uint32_t blk_start = ictx->pe_blocks_per_thread * ith;
|
||||
const uint32_t blk_end = MIN(blk_start + ictx->pe_blocks_per_thread, blk_end_max);
|
||||
if (blk_start >= blk_end) {
|
||||
return;
|
||||
}
|
||||
const uint32_t total_blocks = blk_end - blk_start;
|
||||
struct im2col_cursor cur = {
|
||||
.r = row_start,
|
||||
.r = ictx->thread_in[ith] * ictx->OH + ictx->thread_ioh[ith],
|
||||
.in = ictx->thread_in[ith],
|
||||
.iod = 0,
|
||||
.ioh = ictx->thread_ioh[ith],
|
||||
.c0 = 0,
|
||||
.c0 = ictx->thread_c0[ith],
|
||||
};
|
||||
if (KH == 1) {
|
||||
im2col_gather_table_build(ictx, (HVX_Vector *) scratch);
|
||||
}
|
||||
struct im2col_cursor stage = cur;
|
||||
for (uint32_t i = 0; i < total_blocks && i < 2; i++) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, dstb2[i]), 0, 0, 0, 0);
|
||||
@@ -307,6 +413,37 @@ static void im2col_patchembed_compute_f16(
|
||||
const uint32_t KH = ictx->KH, KW = ictx->KW;
|
||||
const uint32_t OW = ictx->OW, patch_stride = ictx->patch_stride;
|
||||
|
||||
if (KW == 16 && (KH % 4 == 0)) {
|
||||
const HVX_VectorPred q_lo = Q6_Q_vsetq_R(64);
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
uint16_t * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh += 4) {
|
||||
const int32_t iih0 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 0);
|
||||
const int32_t iih1 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 1);
|
||||
const int32_t iih2 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 2);
|
||||
const int32_t iih3 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 3);
|
||||
|
||||
const float * s0 = srcb + (size_t) (iic * KH + ikh + 0) * IW + (size_t) iow * 16;
|
||||
const float * s1 = srcb + (size_t) (iic * KH + ikh + 1) * IW + (size_t) iow * 16;
|
||||
const float * s2 = srcb + (size_t) (iic * KH + ikh + 2) * IW + (size_t) iow * 16;
|
||||
const float * s3 = srcb + (size_t) (iic * KH + ikh + 3) * IW + (size_t) iow * 16;
|
||||
|
||||
const HVX_Vector v0 = (iih0 >= 0 && iih0 < (int32_t) IH) ? *((const HVX_UVector *) s0) : Q6_V_vzero();
|
||||
const HVX_Vector v1 = (iih1 >= 0 && iih1 < (int32_t) IH) ? *((const HVX_UVector *) s1) : Q6_V_vzero();
|
||||
const HVX_Vector v2 = (iih2 >= 0 && iih2 < (int32_t) IH) ? *((const HVX_UVector *) s2) : Q6_V_vzero();
|
||||
const HVX_Vector v3 = (iih3 >= 0 && iih3 < (int32_t) IH) ? *((const HVX_UVector *) s3) : Q6_V_vzero();
|
||||
|
||||
const HVX_Vector w01 = Q6_V_vmux_QVV(q_lo, v0, Q6_V_vror_VR(v1, 64));
|
||||
const HVX_Vector w23 = Q6_V_vmux_QVV(q_lo, v2, Q6_V_vror_VR(v3, 64));
|
||||
|
||||
*((HVX_Vector *) (dst_patch + (size_t) (iic * KH + ikh) * 16)) = hvx_vec_f32_to_f16(w01, w23);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
uint16_t * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
@@ -332,6 +469,30 @@ static void im2col_patchembed_compute_f32(
|
||||
const uint32_t KH = ictx->KH, KW = ictx->KW;
|
||||
const uint32_t OW = ictx->OW, patch_stride = ictx->patch_stride;
|
||||
|
||||
if (KW == 16 && (KH % 2 == 0)) {
|
||||
const HVX_VectorPred q_lo = Q6_Q_vsetq_R(64);
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
float * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh += 2) {
|
||||
const int32_t iih0 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 0);
|
||||
const int32_t iih1 = (int32_t) ioh * (int32_t) KH + (int32_t) (ikh + 1);
|
||||
|
||||
const float * s0 = srcb + (size_t) (iic * KH + ikh + 0) * IW + (size_t) iow * 16;
|
||||
const float * s1 = srcb + (size_t) (iic * KH + ikh + 1) * IW + (size_t) iow * 16;
|
||||
|
||||
const HVX_Vector v0 = (iih0 >= 0 && iih0 < (int32_t) IH) ? *((const HVX_UVector *) s0) : Q6_V_vzero();
|
||||
const HVX_Vector v1 = (iih1 >= 0 && iih1 < (int32_t) IH) ? *((const HVX_UVector *) s1) : Q6_V_vzero();
|
||||
|
||||
const HVX_Vector w01 = Q6_V_vmux_QVV(q_lo, v0, Q6_V_vror_VR(v1, 64));
|
||||
|
||||
*((HVX_Vector *) (dst_patch + (size_t) (iic * KH + ikh) * 16)) = w01;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
float * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
@@ -784,12 +945,16 @@ int op_im2col(struct htp_ops_context * octx) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN((uint32_t) kparams->n_threads, nrows))) {
|
||||
// the blocked path splits work over output-col blocks, so a single output row (1D) can use every thread
|
||||
const bool blocked = kparams->kernel_type == HTP_IM2COL_KERNEL_BLOCKED_DMA;
|
||||
const uint32_t nwork = blocked ? nrows * kparams->nbpr : nrows;
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN((uint32_t) kparams->n_threads, nwork))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads,
|
||||
htp_im2col_scratch_bytes(kparams->kernel_type, kparams->KH, kparams->patch_stride));
|
||||
|
||||
struct htp_im2col_context ictx = { 0 };
|
||||
uint8_t * const base = octx->ctx->vtcm_base;
|
||||
@@ -799,6 +964,7 @@ int op_im2col(struct htp_ops_context * octx) {
|
||||
ictx.pe_row_base = row_base;
|
||||
ictx.pe_nrows = nrows;
|
||||
ictx.pe_rows_per_thread = fastdiv(nrows + pth - 1, &octx->n_threads_div);
|
||||
ictx.pe_blocks_per_thread = fastdiv(nwork + pth - 1, &octx->n_threads_div);
|
||||
ictx.pe_src_row_bytes = kparams->src_row_bytes;
|
||||
ictx.pe_dst_row_bytes = kparams->dst_row_bytes;
|
||||
ictx.pe_vtcm_src = VTCM_LAYOUT_PTR(uint8_t, base, layout.off_src);
|
||||
@@ -820,12 +986,22 @@ int op_im2col(struct htp_ops_context * octx) {
|
||||
ictx.OH = kparams->OH;
|
||||
ictx.OW = kparams->OW;
|
||||
ictx.patch_stride = kparams->patch_stride;
|
||||
ictx.pe_gather_nvec = hex_round_up(kparams->patch_stride, 32) / 32;
|
||||
|
||||
// a thread starts at a row, or at an output-col block (possibly mid-row) on the blocked path
|
||||
for (uint32_t ith = 0; ith < pth; ith++) {
|
||||
const uint32_t r = row_base + ictx.pe_rows_per_thread * ith;
|
||||
uint32_t r = row_base + ictx.pe_rows_per_thread * ith;
|
||||
uint32_t c0 = 0;
|
||||
if (blocked) {
|
||||
const uint32_t blk = ictx.pe_blocks_per_thread * ith;
|
||||
const uint32_t q = fastdiv(blk, &kparams->div_nbpr);
|
||||
r = row_base + q;
|
||||
c0 = (blk - q * kparams->nbpr) * kparams->owb;
|
||||
}
|
||||
const uint32_t in = fastdiv(r, &kparams->div_OH);
|
||||
ictx.thread_ioh[ith] = r - in * kparams->OH;
|
||||
ictx.thread_in[ith] = in;
|
||||
ictx.thread_c0[ith] = c0;
|
||||
}
|
||||
|
||||
ictx.dst_elem = dst->type == HTP_TYPE_F16 ? sizeof(__fp16) : sizeof(float);
|
||||
@@ -835,9 +1011,14 @@ int op_im2col(struct htp_ops_context * octx) {
|
||||
: (void *) im2col_patchembed_compute_f32;
|
||||
work_queue_run(octx->ctx->work_queue, im2col_patchembed_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
} else if (kparams->kernel_type == HTP_IM2COL_KERNEL_BLOCKED_DMA) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_blocked_compute_f16
|
||||
: (void *) im2col_blocked_compute_f32;
|
||||
} else if (blocked) {
|
||||
if (kparams->KH == 1) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_gather_compute_f16
|
||||
: (void *) im2col_gather_compute_f32;
|
||||
} else {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_blocked_compute_f16
|
||||
: (void *) im2col_blocked_compute_f32;
|
||||
}
|
||||
work_queue_run(octx->ctx->work_queue, im2col_blocked_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -873,7 +1054,7 @@ int op_im2col_3d(struct htp_ops_context * octx) {
|
||||
}
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads);
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads, 256);
|
||||
|
||||
struct htp_im2col_context ictx = { 0 };
|
||||
uint8_t * const base = octx->ctx->vtcm_base;
|
||||
|
||||
@@ -20,13 +20,15 @@ struct htp_im2col_vtcm_layout {
|
||||
uint32_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
// scratch_bytes: per-thread VTCM after the two src buffers (at least 256)
|
||||
static inline void htp_im2col_vtcm_layout_build(
|
||||
struct htp_im2col_vtcm_layout * L,
|
||||
uint32_t src_row_bytes,
|
||||
uint32_t dst_row_bytes,
|
||||
uint32_t n_threads) {
|
||||
uint32_t n_threads,
|
||||
uint32_t scratch_bytes) {
|
||||
|
||||
L->src_bytes_per_thread = 2 * src_row_bytes + 256;
|
||||
L->src_bytes_per_thread = 2 * src_row_bytes + (scratch_bytes > 256 ? scratch_bytes : 256);
|
||||
L->dst_bytes_per_thread = 2 * dst_row_bytes;
|
||||
|
||||
size_t off = 0;
|
||||
@@ -35,6 +37,15 @@ static inline void htp_im2col_vtcm_layout_build(
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
// per-thread scratch: the KH == 1 blocked path keeps its offset table and one gathered row there,
|
||||
// both padded to whole vectors
|
||||
static inline uint32_t htp_im2col_scratch_bytes(uint32_t kernel_type, uint32_t KH, uint32_t patch_stride) {
|
||||
if (kernel_type == HTP_IM2COL_KERNEL_BLOCKED_DMA && KH == 1) {
|
||||
return 2 * hex_round_up(patch_stride, 32) * (uint32_t) sizeof(float);
|
||||
}
|
||||
return 256;
|
||||
}
|
||||
|
||||
struct htp_im2col_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t n_threads;
|
||||
@@ -58,6 +69,7 @@ struct htp_im2col_kernel_params {
|
||||
|
||||
struct fastdiv_values div_OH;
|
||||
struct fastdiv_values div_OD;
|
||||
struct fastdiv_values div_nbpr;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
@@ -325,8 +325,11 @@ static unsigned int vtcm_query_size(void) {
|
||||
return vtcm_size;
|
||||
}
|
||||
|
||||
static int vtcm_alloc(struct htp_context * ctx) {
|
||||
static int vtcm_alloc(struct htp_context * ctx, uint64_t size_override) {
|
||||
unsigned int vtcm_size = vtcm_query_size();
|
||||
if (size_override) {
|
||||
vtcm_size = (unsigned int) size_override;
|
||||
}
|
||||
|
||||
compute_res_attr_t attr;
|
||||
HAP_compute_res_attr_init(&attr);
|
||||
@@ -438,7 +441,7 @@ static const struct htp_power_profile htp_power_profiles[HTP_POWER_MODE_COUNT] =
|
||||
|
||||
#undef HTP_POWER_PROFILE_ENTRY
|
||||
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t max_vmem, uint32_t power_mode) {
|
||||
AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp_queue_id, uint32_t n_hvx, uint32_t n_hmx, uint64_t vtcm_size, uint64_t max_vmem, uint32_t power_mode) {
|
||||
struct htp_handle * h = (struct htp_handle *) handle;
|
||||
if (!h) {
|
||||
return AEE_EBADPARM;
|
||||
@@ -557,7 +560,7 @@ AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp
|
||||
ctx->max_vmem = max_vmem;
|
||||
ctx->dsp_queue = dsp_queue;
|
||||
|
||||
err = vtcm_alloc(ctx);
|
||||
err = vtcm_alloc(ctx, vtcm_size);
|
||||
if (err != AEE_SUCCESS) {
|
||||
FARF(ERROR, "Unable to allocate VTCM");
|
||||
htp_iface_stop(handle);
|
||||
@@ -674,7 +677,7 @@ AEEResult htp_iface_start(remote_handle64 handle, uint32_t sess_id, uint64_t dsp
|
||||
dma_ptr_curr += size_dma_q;
|
||||
}
|
||||
|
||||
ctx->ddr_spad_size = 512 * 1024; // 512 KB
|
||||
ctx->ddr_spad_size = HTP_DDR_SPAD_SIZE;
|
||||
ctx->ddr_spad_base = memalign(128, ctx->ddr_spad_size);
|
||||
|
||||
void * wq_ptr = (void *) ((uintptr_t) block + offset_wq);
|
||||
@@ -1022,6 +1025,12 @@ static int execute_op(struct htp_ops_context * octx) {
|
||||
case HTP_OP_IM2COL_3D:
|
||||
return op_im2col_3d(octx);
|
||||
|
||||
case HTP_OP_CONV_2D:
|
||||
return op_conv_2d(octx);
|
||||
|
||||
case HTP_OP_CONV_2D_DW:
|
||||
return op_conv_2d_dw(octx);
|
||||
|
||||
case HTP_OP_ROLL:
|
||||
return op_roll(octx);
|
||||
|
||||
@@ -1040,6 +1049,9 @@ static int execute_op(struct htp_ops_context * octx) {
|
||||
case HTP_OP_TRI:
|
||||
return op_unary(octx);
|
||||
|
||||
case HTP_OP_UPSCALE:
|
||||
return op_upscale(octx);
|
||||
|
||||
case HTP_OP_INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "hvx-dump.h"
|
||||
#include "hvx-arith.h"
|
||||
#include "hvx-reduce.h"
|
||||
#include "hvx-copy.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
@@ -1413,7 +1414,7 @@ static void hvx_mv_id_tiled(unsigned int nth, unsigned int ith, void * data) {
|
||||
|
||||
for (uint32_t ie1 = 0; ie1 < n_aids; ++ie1) { // for each expert
|
||||
const int32_t eid = *(const int32_t *) ((const uint8_t *) src2->data + ie1 * src2->nb[0]);
|
||||
if (eid == -1) {
|
||||
if (eid < 0) {
|
||||
continue;
|
||||
}
|
||||
assert(eid >= 0 && eid < (int32_t) n_ids);
|
||||
@@ -4111,7 +4112,7 @@ static int hvx_mm_matmul_id_nx(
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static inline void scan_expert_ids_n(
|
||||
static __attribute__((noinline)) uint32_t scan_expert_ids_n(
|
||||
const struct htp_tensor * ids,
|
||||
const uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
@@ -4121,15 +4122,17 @@ static inline void scan_expert_ids_n(
|
||||
) {
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const int32_t * row_ptr = (const int32_t *) (ids_data + iid1 * ids_nb1);
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = row_ptr[id];
|
||||
if (i02 == -1) {
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
continue;
|
||||
}
|
||||
assert(i02 >= 0 && i02 < n_as);
|
||||
assert(i02 >= 0 && i02 < (int32_t) n_as);
|
||||
|
||||
if (matrix_rows) {
|
||||
matrix_rows[i02 * mapping_stride + counts[i02]] = (struct mmid_row_mapping) { id, iid1 };
|
||||
@@ -4137,31 +4140,10 @@ static inline void scan_expert_ids_n(
|
||||
counts[i02] += 1;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
|
||||
// the matmuls only write the rows that an expert owns, so the rows of the skipped slots are zeroed here
|
||||
static inline void zero_skipped_dst_rows(
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
const struct htp_tensor * dst,
|
||||
uint32_t ne0,
|
||||
size_t nb1,
|
||||
size_t nb2
|
||||
) {
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_data + iid1 * ids->nb[1] + id * ids->nb[0]);
|
||||
if (i02 != -1) {
|
||||
continue;
|
||||
}
|
||||
memset((uint8_t *) (dst->data + id * nb1 + iid1 * nb2), 0, ne0 * sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void scan_expert_ids(
|
||||
static __attribute__((noinline)) uint32_t scan_expert_ids(
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
@@ -4173,23 +4155,25 @@ static inline void scan_expert_ids(
|
||||
|
||||
if (ids_nb0 == 4) {
|
||||
switch (n_ids) {
|
||||
case 8: scan_expert_ids_n(ids, 8, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 4: scan_expert_ids_n(ids, 4, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 2: scan_expert_ids_n(ids, 2, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
default: scan_expert_ids_n(ids, n_ids, n_as, counts, matrix_rows, mapping_stride); break;
|
||||
case 8: return scan_expert_ids_n(ids, 8, n_as, counts, matrix_rows, mapping_stride);
|
||||
case 4: return scan_expert_ids_n(ids, 4, n_as, counts, matrix_rows, mapping_stride);
|
||||
case 2: return scan_expert_ids_n(ids, 2, n_as, counts, matrix_rows, mapping_stride);
|
||||
default: return scan_expert_ids_n(ids, n_ids, n_as, counts, matrix_rows, mapping_stride);
|
||||
}
|
||||
} else {
|
||||
// Strided fallback
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
uint32_t n_skipped = 0;
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const int32_t * row_ptr = (const int32_t *) (ids_data + iid1 * ids_nb1);
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) ((const uint8_t *) row_ptr + id * ids_nb0);
|
||||
if (i02 == -1) {
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
continue;
|
||||
}
|
||||
assert(i02 >= 0 && i02 < n_as);
|
||||
assert(i02 >= 0 && i02 < (int32_t) n_as);
|
||||
|
||||
if (matrix_rows) {
|
||||
matrix_rows[i02 * mapping_stride + counts[i02]] = (struct mmid_row_mapping) { id, iid1 };
|
||||
@@ -4197,9 +4181,124 @@ static inline void scan_expert_ids(
|
||||
counts[i02] += 1;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) uint32_t count_skipped_ids_single(const struct htp_tensor * ids, uint32_t n_ids) {
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
const size_t ids_nb0 = ids->nb[0];
|
||||
uint32_t n_skipped = 0;
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_data + id * ids_nb0);
|
||||
if (i02 < 0) {
|
||||
n_skipped++;
|
||||
}
|
||||
}
|
||||
return n_skipped;
|
||||
}
|
||||
|
||||
// Zero rows for skipped slots (ids < 0). Matmuls only write rows that an expert owns.
|
||||
static void zero_skipped_dst_rows(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
const struct htp_tensor * dst
|
||||
) {
|
||||
const uint32_t elem_size = (dst->type == HTP_TYPE_F16) ? sizeof(__fp16) : sizeof(float);
|
||||
uint32_t col_start = 0;
|
||||
uint32_t col_count = dst->ne[0];
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const bool can_split = htp_tensor_can_row_partition(dst, elem_size);
|
||||
const uint32_t chunk_elems = (dst->type == HTP_TYPE_F16) ? 64 : 32;
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
dst->ne[0], can_split ? chunk_elems : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
col_start = range.start;
|
||||
col_count = range.count;
|
||||
}
|
||||
|
||||
if (col_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t * ids_data = (const uint8_t *) ids->data;
|
||||
const size_t ids_nb0 = ids->nb[0];
|
||||
const size_t ids_nb1 = ids->nb[1];
|
||||
const size_t dst_nb1 = dst->nb[1];
|
||||
const size_t dst_nb2 = dst->nb[2];
|
||||
const uint32_t col_offset_bytes = col_start * elem_size;
|
||||
|
||||
for (uint32_t iid1 = 0; iid1 < ids->ne[1]; ++iid1) {
|
||||
const uint8_t * ids_row = ids_data + iid1 * ids_nb1;
|
||||
uint8_t * dst_row_group = (uint8_t *) dst->data + iid1 * dst_nb2 + col_offset_bytes;
|
||||
for (uint32_t id = 0; id < n_ids; ++id) {
|
||||
const int32_t i02 = *(const int32_t *) (ids_row + id * ids_nb0);
|
||||
if (i02 < 0) {
|
||||
hvx_splat_u(dst_row_group + id * dst_nb1, Q6_V_vzero(), col_count, elem_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int htp_mmid_prepare_mapping(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * ids,
|
||||
uint32_t n_ids,
|
||||
uint32_t n_as,
|
||||
uint32_t act_nrows,
|
||||
uint8_t ** out_mapping_buf,
|
||||
uint32_t ** out_matrix_row_counts,
|
||||
struct mmid_row_mapping ** out_matrix_rows,
|
||||
uint32_t * out_mapping_stride,
|
||||
uint32_t * out_n_skipped
|
||||
) {
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
hvx_splat_u(matrix_row_counts, Q6_V_vzero(), n_as, sizeof(uint32_t));
|
||||
n_skipped = scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
hvx_splat_u(matrix_row_counts, Q6_V_vzero(), n_as, sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
} else {
|
||||
n_skipped = count_skipped_ids_single(ids, n_ids);
|
||||
}
|
||||
|
||||
*out_mapping_buf = mapping_buf;
|
||||
*out_matrix_row_counts = matrix_row_counts;
|
||||
*out_matrix_rows = matrix_rows;
|
||||
*out_mapping_stride = mapping_stride;
|
||||
*out_n_skipped = n_skipped;
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_matmul_id(struct htp_ops_context * octx) {
|
||||
htp_matmul_tensors_preamble;
|
||||
|
||||
@@ -4235,41 +4334,22 @@ int op_matmul_id(struct htp_ops_context * octx) {
|
||||
const int n_ids = ids->ne[0]; // n_expert_used
|
||||
const int n_as = ne02; // n_expert
|
||||
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
uint8_t * mapping_buf = NULL;
|
||||
uint32_t * matrix_row_counts = NULL;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
memset(matrix_row_counts, 0, matrix_row_counts_size);
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
memset(matrix_row_counts, 0, n_as * sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
int map_status = htp_mmid_prepare_mapping(octx, ids, n_ids, n_as, act_nrows,
|
||||
&mapping_buf, &matrix_row_counts, &matrix_rows,
|
||||
&mapping_stride, &n_skipped);
|
||||
if (map_status != HTP_STATUS_OK) {
|
||||
return map_status;
|
||||
}
|
||||
|
||||
zero_skipped_dst_rows(ids, n_ids, dst, ne0, nb1, nb2);
|
||||
if (n_skipped > 0) {
|
||||
zero_skipped_dst_rows(octx, ids, n_ids, dst);
|
||||
}
|
||||
|
||||
mmctx->matrix_row_counts = matrix_row_counts;
|
||||
mmctx->matrix_rows = matrix_rows;
|
||||
@@ -4360,38 +4440,26 @@ int op_matmul_id_nx(struct htp_ops_context * octx) {
|
||||
const int n_ids = ids->ne[0];
|
||||
const int n_as = src0->ne[2];
|
||||
|
||||
uint8_t * mapping_buf = octx->ctx->ddr_spad_base;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t * matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
uint8_t * mapping_buf = NULL;
|
||||
uint32_t * matrix_row_counts = NULL;
|
||||
struct mmid_row_mapping * matrix_rows = NULL;
|
||||
uint32_t mapping_stride = 1;
|
||||
uint32_t n_skipped = 0;
|
||||
|
||||
if (act_nrows > 1) {
|
||||
const size_t matrix_row_counts_size = n_as * sizeof(uint32_t);
|
||||
assert(octx->ctx->ddr_spad_size >= matrix_row_counts_size);
|
||||
int map_status = htp_mmid_prepare_mapping(octx, ids, n_ids, n_as, act_nrows,
|
||||
&mapping_buf, &matrix_row_counts, &matrix_rows,
|
||||
&mapping_stride, &n_skipped);
|
||||
if (map_status != HTP_STATUS_OK) {
|
||||
return map_status;
|
||||
}
|
||||
|
||||
hex_l2fetch_block((const void *) ids->data, ids->ne[1] * ids->nb[1]);
|
||||
|
||||
memset(matrix_row_counts, 0, matrix_row_counts_size);
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, NULL, 0);
|
||||
|
||||
uint32_t max_count = hvx_reduce_max_i32((const uint8_t *) matrix_row_counts, n_as);
|
||||
mapping_stride = max_count > 0 ? max_count : 1;
|
||||
|
||||
size_t matrix_row_map_size = n_as * mapping_stride * sizeof(struct mmid_row_mapping);
|
||||
const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size;
|
||||
|
||||
if (total_map_size > octx->ctx->ddr_spad_size) {
|
||||
mapping_buf = memalign(128, total_map_size);
|
||||
if (!mapping_buf) {
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
if (n_skipped > 0) {
|
||||
for (uint32_t p = 0; p < n_weights; ++p) {
|
||||
const struct htp_tensor * restrict d = octx->dsts[p];
|
||||
if (d) {
|
||||
zero_skipped_dst_rows(octx, ids, n_ids, d);
|
||||
}
|
||||
}
|
||||
|
||||
matrix_row_counts = (uint32_t *) mapping_buf;
|
||||
matrix_rows = (struct mmid_row_mapping *) (mapping_buf + matrix_row_counts_size);
|
||||
|
||||
memset(matrix_row_counts, 0, n_as * sizeof(uint32_t));
|
||||
scan_expert_ids(ids, n_ids, n_as, matrix_row_counts, matrix_rows, mapping_stride);
|
||||
}
|
||||
|
||||
mmctx->matrix_row_counts = matrix_row_counts;
|
||||
|
||||
@@ -173,11 +173,12 @@ static inline void hvx_ssm_conv_decode_4(const float * x, const float * w, float
|
||||
// the lanes past the channel tail.
|
||||
static inline void hvx_ssm_conv_transpose_block(const float * raw_block,
|
||||
float * T,
|
||||
uint32_t raw_stride_bytes,
|
||||
uint32_t ncs,
|
||||
uint32_t cb_n,
|
||||
HVX_Vector vv) {
|
||||
const size_t base = (size_t) raw_block;
|
||||
const uint32_t mu = cb_n * ncs * sizeof(float) - 1;
|
||||
const uint32_t mu = cb_n * raw_stride_bytes - 1;
|
||||
|
||||
for (uint32_t t = 0; t < ncs; ++t) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (T + (size_t) t * VLEN_FP32), base + t * sizeof(float), mu, vv);
|
||||
@@ -393,7 +394,8 @@ static void ssm_conv_thread_f32_prefill(unsigned int nth, unsigned int ith, void
|
||||
|
||||
// src0 spad holds two raw tiles (fetch of tile n+1 overlaps compute of tile n) plus
|
||||
// the transposed block. dst spad holds two tiles so a writeback can stay in flight.
|
||||
const size_t src0_tile_raw_bytes = hex_round_up(d_inner_tile * ncs * sizeof(float), 128);
|
||||
const size_t raw_stride_bytes = ncs * sizeof(float) + sizeof(float);
|
||||
const size_t src0_tile_raw_bytes = hex_round_up(d_inner_tile * raw_stride_bytes, 128);
|
||||
const size_t dst_tile_bytes = hex_round_up(d_inner_tile * n_t * sizeof(float), 128);
|
||||
|
||||
float * src0_tile_raw[2] = { (float *) src0_spad_base, (float *) (src0_spad_base + src0_tile_raw_bytes) };
|
||||
@@ -419,7 +421,7 @@ static void ssm_conv_thread_f32_prefill(unsigned int nth, unsigned int ith, void
|
||||
(ir0 + tile_off_fetch) * src0_stride_inner_bytes; \
|
||||
dma_queue_push(dma_q, \
|
||||
dma_make_data((uint8_t *) src0_tile_raw[(c) & 1], fetch_ddr), \
|
||||
row_bytes, src0_stride_inner_bytes, row_bytes, cur_tile_n); \
|
||||
raw_stride_bytes, src0_stride_inner_bytes, row_bytes, cur_tile_n); \
|
||||
tile_off_fetch += d_inner_tile; \
|
||||
if (tile_off_fetch >= d_inner_per_thread) { \
|
||||
tile_off_fetch = 0; \
|
||||
@@ -446,11 +448,7 @@ static void ssm_conv_thread_f32_prefill(unsigned int nth, unsigned int ith, void
|
||||
const uint32_t C_TILE = VLEN_FP32;
|
||||
|
||||
// gather offsets: lane r reads channel r of the raw tile block
|
||||
uint32_t __attribute__((aligned(VLEN))) gather_off[VLEN_FP32];
|
||||
for (uint32_t r = 0; r < VLEN_FP32; ++r) {
|
||||
gather_off[r] = r * ncs * sizeof(float);
|
||||
}
|
||||
const HVX_Vector vv = *(const HVX_Vector *) gather_off;
|
||||
const HVX_Vector vv = hvx_vec_gather_offsets_w(raw_stride_bytes);
|
||||
|
||||
uint32_t i3 = 0;
|
||||
uint32_t tile_off = 0;
|
||||
@@ -474,7 +472,8 @@ static void ssm_conv_thread_f32_prefill(unsigned int nth, unsigned int ith, void
|
||||
const uint32_t cb_n = MIN(C_TILE, tile_n - cb);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_A_PREP, (uint16_t) tile_off);
|
||||
hvx_ssm_conv_transpose_block(raw + (size_t) cb * ncs, src0_T, ncs, cb_n, vv);
|
||||
const float * raw_ch = (const float *) ((const uint8_t *) raw + (size_t) cb * raw_stride_bytes);
|
||||
hvx_ssm_conv_transpose_block(raw_ch, src0_T, raw_stride_bytes, ncs, cb_n, vv);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_A_PREP, (uint16_t) tile_off);
|
||||
|
||||
const float * restrict wp = src1_T + tile_off + cb;
|
||||
|
||||
@@ -54,11 +54,12 @@ static inline void htp_ssm_conv_vtcm_layout_build(
|
||||
L->src0_bytes_per_thread = 2 * src0_tile_raw_bytes + src0_T_bytes;
|
||||
L->dst_bytes_per_thread = 2 * hex_round_up((size_t) d_inner_tile * sizeof(float), 128);
|
||||
} else {
|
||||
const uint32_t ncs = d_conv - 1 + n_t;
|
||||
const size_t src0_block_T = hex_round_up((size_t) ncs * 32 * sizeof(float), 128);
|
||||
const size_t src0_tile_raw = hex_round_up((size_t) d_inner_tile * ncs * sizeof(float), 128);
|
||||
L->src0_bytes_per_thread = 2 * src0_tile_raw + src0_block_T;
|
||||
L->dst_bytes_per_thread = 2 * hex_round_up((size_t) d_inner_tile * n_t * sizeof(float), 128);
|
||||
const uint32_t ncs = d_conv - 1 + n_t;
|
||||
const size_t raw_stride_bytes = ncs * sizeof(float) + sizeof(float);
|
||||
const size_t src0_block_T = hex_round_up((size_t) ncs * 32 * sizeof(float), 128);
|
||||
const size_t src0_tile_raw = hex_round_up((size_t) d_inner_tile * raw_stride_bytes, 128);
|
||||
L->src0_bytes_per_thread = 2 * src0_tile_raw + src0_block_T;
|
||||
L->dst_bytes_per_thread = 2 * hex_round_up((size_t) d_inner_tile * n_t * sizeof(float), 128);
|
||||
}
|
||||
|
||||
L->src0_bytes = L->src0_bytes_per_thread * n_threads;
|
||||
|
||||
@@ -0,0 +1,958 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "dma-queue.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-types.h"
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "upscale-ops.h"
|
||||
|
||||
static inline int32_t clamp_i32(int32_t v, int32_t lo, int32_t hi) {
|
||||
return v < lo ? lo : (v > hi ? hi : v);
|
||||
}
|
||||
|
||||
static inline float clamp_f32(float v, float lo, float hi) {
|
||||
return v < lo ? lo : (v > hi ? hi : v);
|
||||
}
|
||||
|
||||
static inline float bicubic_weight1(float x) {
|
||||
const float a = -0.75f;
|
||||
return ((a + 2.0f) * x - (a + 3.0f)) * x * x + 1.0f;
|
||||
}
|
||||
|
||||
static inline float bicubic_weight2(float x) {
|
||||
const float a = -0.75f;
|
||||
return ((a * x - 5.0f * a) * x + 8.0f * a) * x - 4.0f * a;
|
||||
}
|
||||
|
||||
static inline void bicubic_weights(float x, float w[4]) {
|
||||
w[0] = bicubic_weight2(x + 1.0f);
|
||||
w[1] = bicubic_weight1(x);
|
||||
w[2] = bicubic_weight1(1.0f - x);
|
||||
w[3] = bicubic_weight2(2.0f - x);
|
||||
}
|
||||
|
||||
static void upscale_init_luts_nearest(
|
||||
uint32_t * lut_x,
|
||||
uint32_t * lut_y,
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p
|
||||
) {
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_x = dst->ne[0];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
const uint32_t dst_x_aligned = (dst_x + VLEN_FP32 - 1) & ~(VLEN_FP32 - 1);
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x_aligned; ++ox) {
|
||||
if (ox < dst_x) {
|
||||
uint32_t x0 = (uint32_t) ((float) ox * p->inv_sf0);
|
||||
lut_x[ox] = MIN(x0, src_x - 1) * sizeof(float);
|
||||
} else {
|
||||
lut_x[ox] = 0;
|
||||
}
|
||||
}
|
||||
for (uint32_t oy = 0; oy < dst_y; ++oy) {
|
||||
uint32_t y0 = (uint32_t) ((float) oy * p->inv_sf1);
|
||||
lut_y[oy] = MIN(y0, src_y - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void upscale_init_luts_bilinear(
|
||||
struct upscale_bilinear_x_chunk * lut_x,
|
||||
struct upscale_bilinear_y * lut_y,
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p
|
||||
) {
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_x = dst->ne[0];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
const uint32_t n_chunks = (dst_x + VLEN_FP32 - 1) / VLEN_FP32;
|
||||
|
||||
for (uint32_t c = 0; c < n_chunks; ++c) {
|
||||
struct upscale_bilinear_x_chunk * chunk = &lut_x[c];
|
||||
const uint32_t ox_start = c * VLEN_FP32;
|
||||
for (uint32_t l = 0; l < VLEN_FP32; ++l) {
|
||||
const uint32_t ox = ox_start + l;
|
||||
if (ox < dst_x) {
|
||||
float xf = ((float) ox + p->pixel_offset) * p->inv_sf0 - p->pixel_offset;
|
||||
int32_t x0 = (int32_t) floorf(xf);
|
||||
int32_t x1 = x0 + 1;
|
||||
x0 = clamp_i32(x0, 0, (int32_t) src_x - 1);
|
||||
x1 = clamp_i32(x1, 0, (int32_t) src_x - 1);
|
||||
float dx = clamp_f32(xf - (float) x0, 0.0f, 1.0f);
|
||||
|
||||
chunk->off0[l] = x0 * (int32_t) sizeof(float);
|
||||
chunk->off1[l] = x1 * (int32_t) sizeof(float);
|
||||
chunk->w0[l] = 1.0f - dx;
|
||||
chunk->dx[l] = dx;
|
||||
} else {
|
||||
chunk->off0[l] = 0;
|
||||
chunk->off1[l] = 0;
|
||||
chunk->w0[l] = 0.0f;
|
||||
chunk->dx[l] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t oy = 0; oy < dst_y; ++oy) {
|
||||
float yf = ((float) oy + p->pixel_offset) * p->inv_sf1 - p->pixel_offset;
|
||||
int32_t y0 = (int32_t) floorf(yf);
|
||||
int32_t y1 = y0 + 1;
|
||||
y0 = clamp_i32(y0, 0, (int32_t) src_y - 1);
|
||||
y1 = clamp_i32(y1, 0, (int32_t) src_y - 1);
|
||||
float dy = clamp_f32(yf - (float) y0, 0.0f, 1.0f);
|
||||
|
||||
lut_y[oy].y0 = (uint32_t) y0;
|
||||
lut_y[oy].y1 = (uint32_t) y1;
|
||||
lut_y[oy].wy1 = dy;
|
||||
lut_y[oy].wy0 = 1.0f - dy;
|
||||
}
|
||||
}
|
||||
|
||||
static void upscale_init_luts_bicubic(
|
||||
struct upscale_bicubic_x * lut_x,
|
||||
struct upscale_bicubic_y * lut_y,
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p
|
||||
) {
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_x = dst->ne[0];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x; ++ox) {
|
||||
float xf = ((float) ox + p->pixel_offset) * p->inv_sf0 - p->pixel_offset;
|
||||
int32_t x0 = (int32_t) floorf(xf);
|
||||
float dx = xf - (float) x0;
|
||||
bicubic_weights(dx, lut_x[ox].w);
|
||||
|
||||
lut_x[ox].x[0] = clamp_i32(x0 - 1, 0, (int32_t) src_x - 1);
|
||||
lut_x[ox].x[1] = clamp_i32(x0 + 0, 0, (int32_t) src_x - 1);
|
||||
lut_x[ox].x[2] = clamp_i32(x0 + 1, 0, (int32_t) src_x - 1);
|
||||
lut_x[ox].x[3] = clamp_i32(x0 + 2, 0, (int32_t) src_x - 1);
|
||||
}
|
||||
for (uint32_t oy = 0; oy < dst_y; ++oy) {
|
||||
float yf = ((float) oy + p->pixel_offset) * p->inv_sf1 - p->pixel_offset;
|
||||
int32_t y0 = (int32_t) floorf(yf);
|
||||
float dy = yf - (float) y0;
|
||||
bicubic_weights(dy, lut_y[oy].w);
|
||||
|
||||
lut_y[oy].y[0] = clamp_i32(y0 - 1, 0, (int32_t) src_y - 1);
|
||||
lut_y[oy].y[1] = clamp_i32(y0 + 0, 0, (int32_t) src_y - 1);
|
||||
lut_y[oy].y[2] = clamp_i32(y0 + 1, 0, (int32_t) src_y - 1);
|
||||
lut_y[oy].y[3] = clamp_i32(y0 + 2, 0, (int32_t) src_y - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void upscale_init_luts_antialias(
|
||||
struct upscale_antialias_x * lut_x,
|
||||
struct upscale_antialias_y * lut_y,
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p
|
||||
) {
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_x = dst->ne[0];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
|
||||
const float filter_radius0 = p->filter_radius0;
|
||||
const float filter_radius1 = p->filter_radius1;
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x; ++ox) {
|
||||
const float xf = ((float) ox + p->pixel_offset) * p->inv_sf0;
|
||||
int32_t x_min = (int32_t) MAX(xf - filter_radius0 + p->pixel_offset, 0.0f);
|
||||
int32_t x_max = (int32_t) MIN(xf + filter_radius0 + p->pixel_offset, (float) src_x);
|
||||
lut_x[ox].x_min = clamp_i32(x_min, 0, (int32_t) src_x);
|
||||
lut_x[ox].x_max = clamp_i32(x_max, 0, (int32_t) src_x);
|
||||
lut_x[ox].xf = xf;
|
||||
}
|
||||
for (uint32_t oy = 0; oy < dst_y; ++oy) {
|
||||
const float yf = ((float) oy + p->pixel_offset) * p->inv_sf1;
|
||||
int32_t y_min = (int32_t) MAX(yf - filter_radius1 + p->pixel_offset, 0.0f);
|
||||
int32_t y_max = (int32_t) MIN(yf + filter_radius1 + p->pixel_offset, (float) src_y);
|
||||
lut_y[oy].y_min = clamp_i32(y_min, 0, (int32_t) src_y);
|
||||
lut_y[oy].y_max = clamp_i32(y_max, 0, (int32_t) src_y);
|
||||
lut_y[oy].yf = yf;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void get_src_y_window(
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p,
|
||||
const void * lut_y,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t * out_src_y_start,
|
||||
uint32_t * out_src_y_count
|
||||
) {
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
|
||||
if (dst_y_count == dst_y) {
|
||||
*out_src_y_start = 0;
|
||||
*out_src_y_count = src_y;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t last_oy = dst_y_start + dst_y_count - 1;
|
||||
|
||||
if (p->mode == HTP_UPSCALE_MODE_NEAREST) {
|
||||
const uint32_t * ly = (const uint32_t *) lut_y;
|
||||
const uint32_t y0 = ly[dst_y_start];
|
||||
const uint32_t y1 = ly[last_oy];
|
||||
*out_src_y_start = y0;
|
||||
*out_src_y_count = y1 - y0 + 1;
|
||||
} else if (p->mode == HTP_UPSCALE_MODE_BICUBIC) {
|
||||
const struct upscale_bicubic_y * ly = (const struct upscale_bicubic_y *) lut_y;
|
||||
const int32_t y0 = ly[dst_y_start].y[0];
|
||||
const int32_t y1 = ly[last_oy].y[3];
|
||||
*out_src_y_start = (uint32_t) y0;
|
||||
*out_src_y_count = (uint32_t) (y1 - y0 + 1);
|
||||
} else if (p->antialias) {
|
||||
const struct upscale_antialias_y * ly = (const struct upscale_antialias_y *) lut_y;
|
||||
const int32_t y0 = ly[dst_y_start].y_min;
|
||||
const int32_t y1 = ly[last_oy].y_max;
|
||||
*out_src_y_start = (uint32_t) y0;
|
||||
*out_src_y_count = (uint32_t) (y1 - y0);
|
||||
} else {
|
||||
const struct upscale_bilinear_y * ly = (const struct upscale_bilinear_y *) lut_y;
|
||||
const uint32_t y0 = ly[dst_y_start].y0;
|
||||
const uint32_t y1 = ly[last_oy].y1;
|
||||
*out_src_y_start = y0;
|
||||
*out_src_y_count = y1 - y0 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void resolve_dst_slice(
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p,
|
||||
uint32_t chunk_idx,
|
||||
dma_addr_t * out_slice_dst_off,
|
||||
uint32_t * out_dst_ys,
|
||||
uint32_t * out_dst_yc
|
||||
) {
|
||||
const uint32_t s_idx = fastdiv(chunk_idx, &p->div_chunks_per_slice);
|
||||
const uint32_t c_idx = chunk_idx - s_idx * p->chunks_per_slice;
|
||||
const uint32_t d_ys = c_idx * p->dst_nrows_per_chunk;
|
||||
const uint32_t d_yc = MIN(p->dst_nrows_per_chunk, dst->ne[1] - d_ys);
|
||||
const uint32_t i3 = fastdiv(s_idx, &p->div_dst_z);
|
||||
const uint32_t i2 = s_idx - i3 * dst->ne[2];
|
||||
|
||||
*out_slice_dst_off = (dma_addr_t) i2 * dst->nb[2] + (dma_addr_t) i3 * dst->nb[3];
|
||||
*out_dst_ys = d_ys;
|
||||
*out_dst_yc = d_yc;
|
||||
}
|
||||
|
||||
static inline void resolve_src_slice(
|
||||
const struct htp_tensor * src0,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_upscale_kernel_params * p,
|
||||
const void * lut_y,
|
||||
uint32_t chunk_idx,
|
||||
dma_addr_t * out_slice_src_off,
|
||||
uint32_t * out_src_ys,
|
||||
uint32_t * out_src_yc
|
||||
) {
|
||||
const uint32_t s_idx = fastdiv(chunk_idx, &p->div_chunks_per_slice);
|
||||
const uint32_t c_idx = chunk_idx - s_idx * p->chunks_per_slice;
|
||||
const uint32_t d_ys = c_idx * p->dst_nrows_per_chunk;
|
||||
const uint32_t d_yc = MIN(p->dst_nrows_per_chunk, dst->ne[1] - d_ys);
|
||||
uint32_t s_ys, s_yc;
|
||||
get_src_y_window(src0, dst, p, lut_y, d_ys, d_yc, &s_ys, &s_yc);
|
||||
|
||||
const uint32_t i3 = fastdiv(s_idx, &p->div_dst_z);
|
||||
const uint32_t i2 = s_idx - i3 * dst->ne[2];
|
||||
const uint32_t i03 = (uint32_t) ((float) i3 * p->inv_sf3);
|
||||
const uint32_t i02 = (uint32_t) ((float) i2 * p->inv_sf2);
|
||||
|
||||
*out_slice_src_off = (dma_addr_t) i02 * src0->nb[2] + (dma_addr_t) i03 * src0->nb[3];
|
||||
*out_src_ys = s_ys;
|
||||
*out_src_yc = s_yc;
|
||||
}
|
||||
|
||||
struct upscale_context;
|
||||
|
||||
typedef void (*upscale_chunk_fn_t)(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
);
|
||||
|
||||
struct upscale_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_upscale_kernel_params * kparams;
|
||||
struct htp_upscale_vtcm_layout layout;
|
||||
upscale_chunk_fn_t compute_chunk;
|
||||
const void * lut_x;
|
||||
const void * lut_y;
|
||||
uint32_t chunk_start;
|
||||
uint32_t chunk_count;
|
||||
uint32_t chunks_per_thread;
|
||||
};
|
||||
|
||||
static void compute_nearest_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
const uint32_t * lut_x = (const uint32_t *) ctx->lut_x;
|
||||
const uint32_t * lut_y = (const uint32_t *) ctx->lut_y;
|
||||
const uint32_t region_size = hex_round_up(src_x * sizeof(float), 128);
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const uint32_t y0 = lut_y[oy];
|
||||
const float * r0 = srcb + (y0 - src_y_start) * src_x;
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
*(HVX_Vector *) (R_buf + ix) = hvx_vmemu(r0 + ix);
|
||||
}
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x; ox += VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
const HVX_Vector voff = hvx_vmem(lut_x + ox);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) gather_tmp, (size_t) R_buf, region_size, voff);
|
||||
hvx_gather_sync(gather_tmp);
|
||||
hvx_vec_store_u(dst_row + ox, nbytes, hvx_vmem(gather_tmp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_nearest_2x_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
(void) R_buf;
|
||||
(void) gather_tmp;
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const uint32_t sy = oy >> 1;
|
||||
const float * r0 = srcb + (sy - src_y_start) * src_x;
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
const HVX_Vector vin = hvx_vmemu(r0 + ix);
|
||||
|
||||
const HVX_VectorPair p = Q6_W_vshuff_VVR(vin, vin, -4);
|
||||
const HVX_Vector v_lo = Q6_V_lo_W(p);
|
||||
const HVX_Vector v_hi = Q6_V_hi_W(p);
|
||||
|
||||
const uint32_t ox0 = 2 * ix;
|
||||
const uint32_t rem_out0 = (dst_x > ox0) ? (dst_x - ox0) : 0;
|
||||
const uint32_t nbytes_out0 = (rem_out0 < VLEN_FP32) ? (rem_out0 * sizeof(float)) : VLEN;
|
||||
hvx_vec_store_u(dst_row + ox0, nbytes_out0, v_lo);
|
||||
|
||||
const uint32_t ox1 = ox0 + VLEN_FP32;
|
||||
if (dst_x > ox1) {
|
||||
const uint32_t rem_out1 = dst_x - ox1;
|
||||
const uint32_t nbytes_out1 = (rem_out1 < VLEN_FP32) ? (rem_out1 * sizeof(float)) : VLEN;
|
||||
hvx_vec_store_u(dst_row + ox1, nbytes_out1, v_hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_nearest_4x_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
(void) R_buf;
|
||||
(void) gather_tmp;
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const uint32_t sy = oy >> 2;
|
||||
const float * r0 = srcb + (sy - src_y_start) * src_x;
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
const HVX_Vector vin = hvx_vmemu(r0 + ix);
|
||||
|
||||
const HVX_VectorPair p = Q6_W_vshuff_VVR(vin, vin, -4);
|
||||
const HVX_Vector v_lo = Q6_V_lo_W(p);
|
||||
const HVX_Vector v_hi = Q6_V_hi_W(p);
|
||||
|
||||
const HVX_VectorPair p0 = Q6_W_vshuff_VVR(v_lo, v_lo, -4);
|
||||
const HVX_VectorPair p1 = Q6_W_vshuff_VVR(v_hi, v_hi, -4);
|
||||
|
||||
const HVX_Vector out0 = Q6_V_lo_W(p0);
|
||||
const HVX_Vector out1 = Q6_V_hi_W(p0);
|
||||
const HVX_Vector out2 = Q6_V_lo_W(p1);
|
||||
const HVX_Vector out3 = Q6_V_hi_W(p1);
|
||||
|
||||
const uint32_t ox = 4 * ix;
|
||||
if (dst_x > ox) {
|
||||
const uint32_t rem = dst_x - ox;
|
||||
hvx_vec_store_u(dst_row + ox, (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN, out0);
|
||||
}
|
||||
if (dst_x > ox + VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - (ox + VLEN_FP32);
|
||||
hvx_vec_store_u(dst_row + ox + VLEN_FP32, (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN, out1);
|
||||
}
|
||||
if (dst_x > ox + 2 * VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - (ox + 2 * VLEN_FP32);
|
||||
hvx_vec_store_u(dst_row + ox + 2 * VLEN_FP32, (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN, out2);
|
||||
}
|
||||
if (dst_x > ox + 3 * VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - (ox + 3 * VLEN_FP32);
|
||||
hvx_vec_store_u(dst_row + ox + 3 * VLEN_FP32, (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN, out3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void compute_bilinear_chunk_32(
|
||||
float * restrict dst_chunk,
|
||||
const float * restrict R_buf,
|
||||
float * restrict tmp_a,
|
||||
float * restrict tmp_b,
|
||||
uint32_t region_size,
|
||||
const struct upscale_bilinear_x_chunk * restrict chunk,
|
||||
uint32_t nbytes
|
||||
) {
|
||||
const HVX_Vector voff0 = hvx_vmem(chunk->off0);
|
||||
const HVX_Vector voff1 = hvx_vmem(chunk->off1);
|
||||
const HVX_Vector vw0 = hvx_vmem(chunk->w0);
|
||||
const HVX_Vector vdx = hvx_vmem(chunk->dx);
|
||||
|
||||
Q6_vgather_ARMVw((HVX_Vector *) tmp_a, (size_t) R_buf, region_size, voff0);
|
||||
Q6_vgather_ARMVw((HVX_Vector *) tmp_b, (size_t) R_buf, region_size, voff1);
|
||||
hvx_gather_sync(tmp_b);
|
||||
|
||||
const HVX_Vector va = hvx_vmem(tmp_a);
|
||||
const HVX_Vector vb = hvx_vmem(tmp_b);
|
||||
|
||||
const HVX_Vector val = hvx_vec_add_f32_f32(
|
||||
hvx_vec_mul_f32_f32(vw0, va),
|
||||
hvx_vec_mul_f32_f32(vdx, vb)
|
||||
);
|
||||
|
||||
hvx_vec_store_u(dst_chunk, nbytes, val);
|
||||
}
|
||||
|
||||
static void compute_bilinear_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
const struct upscale_bilinear_x_chunk * lut_x = (const struct upscale_bilinear_x_chunk *) ctx->lut_x;
|
||||
const struct upscale_bilinear_y * lut_y = (const struct upscale_bilinear_y *) ctx->lut_y;
|
||||
const uint32_t n_chunks = (dst_x + VLEN_FP32 - 1) / VLEN_FP32;
|
||||
const uint32_t region_size = hex_round_up(src_x * sizeof(float), 128);
|
||||
float * tmp_a = gather_tmp;
|
||||
float * tmp_b = gather_tmp + VLEN_FP32;
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const struct upscale_bilinear_y * ey = &lut_y[oy];
|
||||
const float * r0 = srcb + (ey->y0 - src_y_start) * src_x;
|
||||
const float * r1 = srcb + (ey->y1 - src_y_start) * src_x;
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
if (ey->y0 == ey->y1) {
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
*(HVX_Vector *) (R_buf + ix) = hvx_vmemu(r0 + ix);
|
||||
}
|
||||
} else {
|
||||
const HVX_Vector vwy0 = hvx_vec_splat_f32(ey->wy0);
|
||||
const HVX_Vector vwy1 = hvx_vec_splat_f32(ey->wy1);
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
const HVX_Vector vr0 = hvx_vmemu(r0 + ix);
|
||||
const HVX_Vector vr1 = hvx_vmemu(r1 + ix);
|
||||
*(HVX_Vector *) (R_buf + ix) = hvx_vec_add_f32_f32(
|
||||
hvx_vec_mul_f32_f32(vwy0, vr0),
|
||||
hvx_vec_mul_f32_f32(vwy1, vr1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t c = 0; c < n_chunks; ++c) {
|
||||
const uint32_t ox = c * VLEN_FP32;
|
||||
const uint32_t rem = dst_x - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
compute_bilinear_chunk_32(dst_row + ox, R_buf, tmp_a, tmp_b, region_size, &lut_x[c], nbytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_bilinear_2x_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
(void) R_buf;
|
||||
(void) gather_tmp;
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
const struct upscale_bilinear_y * lut_y = (const struct upscale_bilinear_y *) ctx->lut_y;
|
||||
const HVX_Vector c_075 = hvx_vec_splat_f32(0.75f);
|
||||
const HVX_Vector c_025 = hvx_vec_splat_f32(0.25f);
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const struct upscale_bilinear_y * ey = &lut_y[oy];
|
||||
const float * r0 = srcb + (ey->y0 - src_y_start) * src_x;
|
||||
const float * r1 = srcb + (ey->y1 - src_y_start) * src_x;
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
const bool same_row = (ey->y0 == ey->y1);
|
||||
const HVX_Vector vwy0 = hvx_vec_splat_f32(ey->wy0);
|
||||
const HVX_Vector vwy1 = hvx_vec_splat_f32(ey->wy1);
|
||||
|
||||
HVX_Vector v_curr = hvx_vmem(r0);
|
||||
if (!same_row) {
|
||||
const HVX_Vector v1 = hvx_vmem(r1);
|
||||
v_curr = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(vwy0, v_curr), hvx_vec_mul_f32_f32(vwy1, v1));
|
||||
}
|
||||
|
||||
HVX_Vector v_prev = Q6_V_vror_VR(v_curr, 4);
|
||||
|
||||
for (uint32_t ix = 0; ix < src_x; ix += VLEN_FP32) {
|
||||
HVX_Vector v_next_chunk;
|
||||
const uint32_t next_ix = ix + VLEN_FP32;
|
||||
if (next_ix < src_x) {
|
||||
v_next_chunk = hvx_vmem(r0 + next_ix);
|
||||
if (!same_row) {
|
||||
const HVX_Vector v1 = hvx_vmem(r1 + next_ix);
|
||||
v_next_chunk = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(vwy0, v_next_chunk), hvx_vec_mul_f32_f32(vwy1, v1));
|
||||
}
|
||||
} else {
|
||||
v_next_chunk = Q6_V_vror_VR(v_curr, 124);
|
||||
}
|
||||
|
||||
const HVX_Vector v_shl = Q6_V_valign_VVR(v_curr, v_prev, 124);
|
||||
const HVX_Vector v_shr = Q6_V_valign_VVR(v_next_chunk, v_curr, 4);
|
||||
|
||||
const HVX_Vector ve = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(c_075, v_curr), hvx_vec_mul_f32_f32(c_025, v_shl));
|
||||
const HVX_Vector vo = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(c_075, v_curr), hvx_vec_mul_f32_f32(c_025, v_shr));
|
||||
|
||||
const HVX_VectorPair p = Q6_W_vshuff_VVR(vo, ve, -4);
|
||||
const HVX_Vector out0 = Q6_V_lo_W(p);
|
||||
const HVX_Vector out1 = Q6_V_hi_W(p);
|
||||
|
||||
const uint32_t ox0 = 2 * ix;
|
||||
hvx_vmem(dst_row + ox0) = out0;
|
||||
hvx_vmem(dst_row + ox0 + VLEN_FP32) = out1;
|
||||
|
||||
v_prev = v_curr;
|
||||
v_curr = v_next_chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_bilinear_antialias_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
(void) R_buf;
|
||||
(void) gather_tmp;
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
const struct htp_upscale_kernel_params * p = ctx->kparams;
|
||||
const struct upscale_antialias_x * lut_x = (const struct upscale_antialias_x *) ctx->lut_x;
|
||||
const struct upscale_antialias_y * lut_y = (const struct upscale_antialias_y *) ctx->lut_y;
|
||||
|
||||
const float invscale1 = p->invscale1;
|
||||
const float invscale0 = p->invscale0;
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const struct upscale_antialias_y * ey = &lut_y[oy];
|
||||
const float yf = ey->yf;
|
||||
const int32_t y_min = ey->y_min;
|
||||
const int32_t y_max = ey->y_max;
|
||||
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x; ox += VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
float tmp[VLEN_FP32] __attribute__((aligned(128)));
|
||||
const uint32_t cnt = rem < VLEN_FP32 ? rem : VLEN_FP32;
|
||||
|
||||
for (uint32_t l = 0; l < cnt; ++l) {
|
||||
const uint32_t cur_ox = ox + l;
|
||||
const struct upscale_antialias_x * ex = &lut_x[cur_ox];
|
||||
const float xf = ex->xf;
|
||||
const int32_t x_min = ex->x_min;
|
||||
const int32_t x_max = ex->x_max;
|
||||
|
||||
float val = 0.0f;
|
||||
float total_weight = 0.0f;
|
||||
|
||||
for (int32_t sy = y_min; sy < y_max; ++sy) {
|
||||
const float diff_y = fabsf((float) sy - yf + p->pixel_offset) * invscale1;
|
||||
const float weight_y = MAX(1.0f - diff_y, 0.0f);
|
||||
if (weight_y <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
const float * r = srcb + (uint32_t) (sy - (int32_t) src_y_start) * src_x;
|
||||
|
||||
for (int32_t sx = x_min; sx < x_max; ++sx) {
|
||||
const float diff_x = fabsf((float) sx - xf + p->pixel_offset) * invscale0;
|
||||
const float weight_x = MAX(1.0f - diff_x, 0.0f);
|
||||
const float weight = weight_x * weight_y;
|
||||
if (weight <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
val += r[sx] * weight;
|
||||
total_weight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
tmp[l] = (total_weight > 0.0f) ? (val / total_weight) : 0.0f;
|
||||
}
|
||||
|
||||
hvx_vec_store_u(dst_row + ox, nbytes, *(const HVX_Vector *) tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_bicubic_chunk(
|
||||
const struct upscale_context * ctx,
|
||||
const float * srcb,
|
||||
float * dstb,
|
||||
float * R_buf,
|
||||
float * gather_tmp,
|
||||
uint32_t dst_y_start,
|
||||
uint32_t dst_y_count,
|
||||
uint32_t src_y_start
|
||||
) {
|
||||
(void) R_buf;
|
||||
(void) gather_tmp;
|
||||
const uint32_t src_x = ctx->octx->src[0]->ne[0];
|
||||
const uint32_t dst_x = ctx->octx->dst->ne[0];
|
||||
const struct upscale_bicubic_x * lut_x = (const struct upscale_bicubic_x *) ctx->lut_x;
|
||||
const struct upscale_bicubic_y * lut_y = (const struct upscale_bicubic_y *) ctx->lut_y;
|
||||
|
||||
for (uint32_t oy = dst_y_start; oy < dst_y_start + dst_y_count; ++oy) {
|
||||
const struct upscale_bicubic_y * ey = &lut_y[oy];
|
||||
const float * r0 = srcb + (uint32_t) (ey->y[0] - (int32_t) src_y_start) * src_x;
|
||||
const float * r1 = srcb + (uint32_t) (ey->y[1] - (int32_t) src_y_start) * src_x;
|
||||
const float * r2 = srcb + (uint32_t) (ey->y[2] - (int32_t) src_y_start) * src_x;
|
||||
const float * r3 = srcb + (uint32_t) (ey->y[3] - (int32_t) src_y_start) * src_x;
|
||||
|
||||
float * dst_row = dstb + (oy - dst_y_start) * dst_x;
|
||||
|
||||
const HVX_Vector vwy0 = hvx_vec_splat_f32(ey->w[0]);
|
||||
const HVX_Vector vwy1 = hvx_vec_splat_f32(ey->w[1]);
|
||||
const HVX_Vector vwy2 = hvx_vec_splat_f32(ey->w[2]);
|
||||
const HVX_Vector vwy3 = hvx_vec_splat_f32(ey->w[3]);
|
||||
|
||||
for (uint32_t ox = 0; ox < dst_x; ox += VLEN_FP32) {
|
||||
const uint32_t rem = dst_x - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
|
||||
float v0[VLEN_FP32] __attribute__((aligned(128)));
|
||||
float v1[VLEN_FP32] __attribute__((aligned(128)));
|
||||
float v2[VLEN_FP32] __attribute__((aligned(128)));
|
||||
float v3[VLEN_FP32] __attribute__((aligned(128)));
|
||||
|
||||
const uint32_t cnt = rem < VLEN_FP32 ? rem : VLEN_FP32;
|
||||
for (uint32_t l = 0; l < cnt; ++l) {
|
||||
const struct upscale_bicubic_x * e = &lut_x[ox + l];
|
||||
v0[l] = r0[e->x[0]] * e->w[0] + r0[e->x[1]] * e->w[1] + r0[e->x[2]] * e->w[2] + r0[e->x[3]] * e->w[3];
|
||||
v1[l] = r1[e->x[0]] * e->w[0] + r1[e->x[1]] * e->w[1] + r1[e->x[2]] * e->w[2] + r1[e->x[3]] * e->w[3];
|
||||
v2[l] = r2[e->x[0]] * e->w[0] + r2[e->x[1]] * e->w[1] + r2[e->x[2]] * e->w[2] + r2[e->x[3]] * e->w[3];
|
||||
v3[l] = r3[e->x[0]] * e->w[0] + r3[e->x[1]] * e->w[1] + r3[e->x[2]] * e->w[2] + r3[e->x[3]] * e->w[3];
|
||||
}
|
||||
for (uint32_t l = cnt; l < VLEN_FP32; ++l) {
|
||||
v0[l] = v1[l] = v2[l] = v3[l] = 0.0f;
|
||||
}
|
||||
|
||||
const HVX_Vector vv0 = *(const HVX_Vector *) v0;
|
||||
const HVX_Vector vv1 = *(const HVX_Vector *) v1;
|
||||
const HVX_Vector vv2 = *(const HVX_Vector *) v2;
|
||||
const HVX_Vector vv3 = *(const HVX_Vector *) v3;
|
||||
|
||||
const HVX_Vector acc0 = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(vwy0, vv0), hvx_vec_mul_f32_f32(vwy1, vv1));
|
||||
const HVX_Vector acc1 = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(vwy2, vv2), hvx_vec_mul_f32_f32(vwy3, vv3));
|
||||
const HVX_Vector val = hvx_vec_add_f32_f32(acc0, acc1);
|
||||
|
||||
hvx_vec_store_u(dst_row + ox, nbytes, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dma_push_src_chunk(
|
||||
dma_queue * dma_q,
|
||||
float * spad,
|
||||
const struct htp_tensor * src0,
|
||||
dma_addr_t slice_src_off,
|
||||
uint32_t src_ys,
|
||||
uint32_t src_yc
|
||||
) {
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const dma_addr_t src_addr = src0->data + slice_src_off + (dma_addr_t) src_ys * src0->nb[1];
|
||||
|
||||
if (src0->nb[0] == sizeof(float)) {
|
||||
const uint32_t row_size = src_x * sizeof(float);
|
||||
const uint32_t dst_stride = row_size;
|
||||
const uint32_t src_stride = src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad, src_addr), dst_stride, src_stride, row_size, src_yc);
|
||||
} else {
|
||||
const uint32_t row_size = sizeof(float);
|
||||
const uint32_t dst_stride = sizeof(float);
|
||||
const uint32_t src_stride = src0->nb[0];
|
||||
const uint32_t nrows = src_x * src_yc;
|
||||
dma_queue_push(dma_q, dma_make_data(spad, src_addr), dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
}
|
||||
|
||||
static void upscale_worker(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct upscale_context * ctx = (struct upscale_context *) data;
|
||||
const struct htp_upscale_kernel_params * p = ctx->kparams;
|
||||
const struct htp_upscale_vtcm_layout * L = &ctx->layout;
|
||||
const struct htp_tensor * src0 = ctx->octx->src[0];
|
||||
const struct htp_tensor * dst = ctx->octx->dst;
|
||||
|
||||
const uint32_t first = ctx->chunk_start + ith * ctx->chunks_per_thread;
|
||||
const uint32_t last = MIN(first + ctx->chunks_per_thread, ctx->chunk_start + ctx->chunk_count);
|
||||
|
||||
if (first >= last) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &ctx->octx->ctx->trace[ith];
|
||||
dma_queue * dma_q = ctx->octx->ctx->dma[ith];
|
||||
|
||||
const uint32_t src_spad_half = (uint32_t) L->src_spad_half_size;
|
||||
const uint32_t dst_spad_half = (uint32_t) L->dst_spad_half_size;
|
||||
|
||||
uint8_t * vtcm_base = (uint8_t *) ctx->octx->ctx->vtcm_base;
|
||||
uint8_t * src_spad = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, L->off_src) + ith * L->src_bytes_per_thread;
|
||||
uint8_t * dst_spad = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, L->off_dst) + ith * L->dst_bytes_per_thread;
|
||||
float * R_buf = VTCM_LAYOUT_PTR(float, vtcm_base, L->off_r) + ith * (L->r_bytes_per_thread / sizeof(float));
|
||||
float * gather_tmp = VTCM_LAYOUT_PTR(float, vtcm_base, L->off_gather) + ith * (L->gather_bytes_per_thread / sizeof(float));
|
||||
const void * lut_y = ctx->lut_y;
|
||||
|
||||
float * srcb2[2] = { (float *) src_spad, (float *) (src_spad + src_spad_half) };
|
||||
float * dstb2[2] = { (float *) dst_spad, (float *) (dst_spad + dst_spad_half) };
|
||||
|
||||
const uint32_t dst_row_size = dst->ne[0] * sizeof(float);
|
||||
const uint32_t dst_stride = dst->nb[1];
|
||||
|
||||
const uint32_t total = last - first;
|
||||
|
||||
// Warmup: prime pipeline with initial transfers
|
||||
for (uint32_t i = 0; i < total && i < 2; ++i) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst->data, dstb2[i]), dst_stride, dst_row_size, dst_row_size, 0);
|
||||
|
||||
dma_addr_t slice_src_off;
|
||||
uint32_t src_ys, src_yc;
|
||||
resolve_src_slice(src0, dst, p, lut_y, first + i, &slice_src_off, &src_ys, &src_yc);
|
||||
|
||||
dma_push_src_chunk(dma_q, srcb2[i], src0, slice_src_off, src_ys, src_yc);
|
||||
}
|
||||
|
||||
// Steady state loop
|
||||
for (uint32_t i = 0; i < total; ++i) {
|
||||
const uint32_t buf = i & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
float * dstb = dstb2[buf];
|
||||
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
dma_addr_t slice_dst_off;
|
||||
uint32_t dst_ys, dst_yc;
|
||||
resolve_dst_slice(dst, p, first + i, &slice_dst_off, &dst_ys, &dst_yc);
|
||||
|
||||
uint32_t src_ys, src_yc;
|
||||
get_src_y_window(src0, dst, p, lut_y, dst_ys, dst_yc, &src_ys, &src_yc);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) (first + i));
|
||||
ctx->compute_chunk(ctx, srcb, dstb, R_buf, gather_tmp, dst_ys, dst_yc, src_ys);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) (first + i));
|
||||
|
||||
const dma_addr_t dst_addr = dst->data + slice_dst_off + (dma_addr_t) dst_ys * dst_stride;
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, dstb), dst_stride, dst_row_size, dst_row_size, dst_yc);
|
||||
|
||||
const uint32_t next = i + 2;
|
||||
if (next < total) {
|
||||
dma_addr_t next_src_off;
|
||||
uint32_t next_src_ys, next_src_yc;
|
||||
resolve_src_slice(src0, dst, p, lut_y, first + next, &next_src_off, &next_src_ys, &next_src_yc);
|
||||
|
||||
dma_push_src_chunk(dma_q, srcb, src0, next_src_off, next_src_ys, next_src_yc);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
int op_upscale(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_upscale_kernel_params * p =
|
||||
(const struct htp_upscale_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t src_x = src0->ne[0];
|
||||
const uint32_t src_y = src0->ne[1];
|
||||
const uint32_t dst_x = dst->ne[0];
|
||||
const uint32_t dst_y = dst->ne[1];
|
||||
|
||||
uint32_t chunk_start = 0;
|
||||
uint32_t chunk_count = p->total_chunks;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
chunk_count, htp_tensor_mdev_data_aligned(dst) ? p->chunks_per_group : 0,
|
||||
octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
chunk_start = range.start;
|
||||
chunk_count = range.count;
|
||||
}
|
||||
|
||||
if (chunk_count == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = MIN(p->n_threads, chunk_count);
|
||||
if (!htp_ops_context_set_n_threads(octx, n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t chunks_per_thread = fastdiv(chunk_count + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
size_t lut_x_size = 0;
|
||||
size_t elem_y_sz = sizeof(uint32_t);
|
||||
|
||||
if (p->mode == HTP_UPSCALE_MODE_BICUBIC) {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(struct upscale_bicubic_x), 128);
|
||||
elem_y_sz = sizeof(struct upscale_bicubic_y);
|
||||
} else if (p->antialias) {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(struct upscale_antialias_x), 128);
|
||||
elem_y_sz = sizeof(struct upscale_antialias_y);
|
||||
} else if (p->mode == HTP_UPSCALE_MODE_BILINEAR) {
|
||||
const uint32_t n_chunks = (dst_x + VLEN_FP32 - 1) / VLEN_FP32;
|
||||
lut_x_size = hex_round_up(n_chunks * sizeof(struct upscale_bilinear_x_chunk), 128);
|
||||
elem_y_sz = sizeof(struct upscale_bilinear_y);
|
||||
} else {
|
||||
lut_x_size = hex_round_up(dst_x * sizeof(uint32_t), 128);
|
||||
}
|
||||
|
||||
const size_t lut_y_size = hex_round_up(dst_y * elem_y_sz, 128);
|
||||
const size_t total_lut_size = lut_x_size + lut_y_size;
|
||||
|
||||
if (total_lut_size > octx->ctx->ddr_spad_size) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
void * base_scratch = octx->ctx->ddr_spad_base;
|
||||
void * lut_x = base_scratch;
|
||||
void * lut_y = (uint8_t *) base_scratch + lut_x_size;
|
||||
|
||||
if (p->mode == HTP_UPSCALE_MODE_NEAREST) {
|
||||
upscale_init_luts_nearest((uint32_t *) lut_x, (uint32_t *) lut_y, src0, dst, p);
|
||||
} else if (p->mode == HTP_UPSCALE_MODE_BICUBIC) {
|
||||
upscale_init_luts_bicubic((struct upscale_bicubic_x *) lut_x, (struct upscale_bicubic_y *) lut_y, src0, dst, p);
|
||||
} else if (p->antialias) {
|
||||
upscale_init_luts_antialias((struct upscale_antialias_x *) lut_x, (struct upscale_antialias_y *) lut_y, src0, dst, p);
|
||||
} else {
|
||||
upscale_init_luts_bilinear((struct upscale_bilinear_x_chunk *) lut_x, (struct upscale_bilinear_y *) lut_y, src0, dst, p);
|
||||
}
|
||||
|
||||
upscale_chunk_fn_t compute_chunk;
|
||||
switch (p->kernel_type) {
|
||||
case HTP_UPSCALE_KERNEL_NEAREST:
|
||||
compute_chunk = compute_nearest_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_NEAREST_2X:
|
||||
compute_chunk = compute_nearest_2x_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_NEAREST_4X:
|
||||
compute_chunk = compute_nearest_4x_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR:
|
||||
compute_chunk = compute_bilinear_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR_2X:
|
||||
compute_chunk = compute_bilinear_2x_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_BILINEAR_AA:
|
||||
compute_chunk = compute_bilinear_antialias_chunk;
|
||||
break;
|
||||
case HTP_UPSCALE_KERNEL_BICUBIC:
|
||||
compute_chunk = compute_bicubic_chunk;
|
||||
break;
|
||||
default:
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
struct upscale_context ctx = {
|
||||
.octx = octx,
|
||||
.kparams = p,
|
||||
.compute_chunk = compute_chunk,
|
||||
.lut_x = lut_x,
|
||||
.lut_y = lut_y,
|
||||
.chunk_start = chunk_start,
|
||||
.chunk_count = chunk_count,
|
||||
.chunks_per_thread = chunks_per_thread,
|
||||
};
|
||||
|
||||
htp_upscale_vtcm_layout_build(&ctx.layout, src_x, src_y, dst_x, dst_y,
|
||||
p->dst_nrows_per_chunk, p->inv_sf1, p->filter_radius1, n_threads);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, upscale_worker, &ctx, n_threads);
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
#ifndef HTP_UPSCALE_OPS_H
|
||||
#define HTP_UPSCALE_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
enum htp_upscale_mode {
|
||||
HTP_UPSCALE_MODE_NEAREST = 0,
|
||||
HTP_UPSCALE_MODE_BILINEAR = 1,
|
||||
HTP_UPSCALE_MODE_BICUBIC = 2,
|
||||
};
|
||||
|
||||
enum htp_upscale_kernel_type {
|
||||
HTP_UPSCALE_KERNEL_NEAREST = 0,
|
||||
HTP_UPSCALE_KERNEL_NEAREST_2X = 1,
|
||||
HTP_UPSCALE_KERNEL_NEAREST_4X = 2,
|
||||
HTP_UPSCALE_KERNEL_BILINEAR = 3,
|
||||
HTP_UPSCALE_KERNEL_BILINEAR_2X = 4,
|
||||
HTP_UPSCALE_KERNEL_BILINEAR_AA = 5,
|
||||
HTP_UPSCALE_KERNEL_BICUBIC = 6,
|
||||
};
|
||||
|
||||
struct htp_upscale_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t mode;
|
||||
uint8_t align_corners;
|
||||
uint8_t antialias;
|
||||
|
||||
float inv_sf0;
|
||||
float inv_sf1;
|
||||
float inv_sf2;
|
||||
float inv_sf3;
|
||||
float pixel_offset;
|
||||
|
||||
float filter_radius0;
|
||||
float filter_radius1;
|
||||
float invscale0;
|
||||
float invscale1;
|
||||
|
||||
uint32_t n_threads;
|
||||
uint32_t chunks_per_group;
|
||||
|
||||
uint32_t dst_nrows_per_chunk;
|
||||
uint32_t chunks_per_slice;
|
||||
uint32_t total_chunks;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
struct fastdiv_values div_chunks_per_slice;
|
||||
struct fastdiv_values div_dst_z;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_upscale_kernel_params) <= 128, "htp_upscale_kernel_params is too large");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_upscale_kernel_params) <= 128, "htp_upscale_kernel_params is too large");
|
||||
#endif
|
||||
|
||||
struct __attribute__((aligned(128))) upscale_bilinear_x_chunk {
|
||||
int32_t off0[32];
|
||||
int32_t off1[32];
|
||||
float w0[32];
|
||||
float dx[32];
|
||||
};
|
||||
|
||||
struct upscale_bilinear_y {
|
||||
uint32_t y0;
|
||||
uint32_t y1;
|
||||
float wy0;
|
||||
float wy1;
|
||||
};
|
||||
|
||||
struct upscale_bicubic_x {
|
||||
int32_t x[4];
|
||||
float w[4];
|
||||
};
|
||||
|
||||
struct upscale_bicubic_y {
|
||||
int32_t y[4];
|
||||
float w[4];
|
||||
};
|
||||
|
||||
struct upscale_antialias_x {
|
||||
int32_t x_min;
|
||||
int32_t x_max;
|
||||
float xf;
|
||||
};
|
||||
|
||||
struct upscale_antialias_y {
|
||||
int32_t y_min;
|
||||
int32_t y_max;
|
||||
float yf;
|
||||
};
|
||||
|
||||
struct htp_upscale_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src;
|
||||
size_t off_dst;
|
||||
size_t off_r;
|
||||
size_t off_gather;
|
||||
size_t src_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
size_t r_bytes_per_thread;
|
||||
size_t gather_bytes_per_thread;
|
||||
size_t src_spad_half_size;
|
||||
size_t dst_spad_half_size;
|
||||
uint32_t max_src_chunk_rows;
|
||||
uint32_t dst_chunk_rows;
|
||||
};
|
||||
|
||||
static inline void htp_upscale_vtcm_layout_build(
|
||||
struct htp_upscale_vtcm_layout * L,
|
||||
uint32_t src_x,
|
||||
uint32_t src_y,
|
||||
uint32_t dst_x,
|
||||
uint32_t dst_y,
|
||||
uint32_t dst_chunk_rows,
|
||||
float inv_sf1,
|
||||
float filter_radius1,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
uint32_t max_src_rows;
|
||||
if (dst_chunk_rows >= dst_y) {
|
||||
max_src_rows = src_y;
|
||||
} else {
|
||||
const float est = (float) dst_chunk_rows * inv_sf1 + 2.0f * filter_radius1;
|
||||
max_src_rows = (uint32_t) ceilf(est) + 4;
|
||||
if (max_src_rows > src_y) {
|
||||
max_src_rows = src_y;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t src_chunk_bytes = src_x * max_src_rows * sizeof(float);
|
||||
const uint32_t dst_chunk_bytes = dst_x * dst_chunk_rows * sizeof(float);
|
||||
|
||||
L->max_src_chunk_rows = max_src_rows;
|
||||
L->dst_chunk_rows = dst_chunk_rows;
|
||||
L->src_spad_half_size = hex_round_up(src_chunk_bytes + 256, 128);
|
||||
L->dst_spad_half_size = hex_round_up(dst_chunk_bytes, 128);
|
||||
L->src_bytes_per_thread = 2 * L->src_spad_half_size;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_spad_half_size;
|
||||
L->r_bytes_per_thread = hex_round_up(src_x * sizeof(float), 128);
|
||||
L->gather_bytes_per_thread = 256;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src, L->src_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_r, L->r_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_gather, L->gather_bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline bool htp_upscale_solve_layout(
|
||||
struct htp_upscale_vtcm_layout * layout,
|
||||
uint32_t src_x,
|
||||
uint32_t src_y,
|
||||
uint32_t dst_x,
|
||||
uint32_t dst_y,
|
||||
float inv_sf1,
|
||||
float filter_radius1,
|
||||
uint32_t n_threads,
|
||||
size_t vtcm_budget,
|
||||
uint32_t * out_dst_chunk_rows
|
||||
) {
|
||||
// 1. Try full plane
|
||||
uint32_t chunk_rows = dst_y;
|
||||
htp_upscale_vtcm_layout_build(layout, src_x, src_y, dst_x, dst_y, chunk_rows, inv_sf1, filter_radius1, n_threads);
|
||||
if (layout->total_bytes <= vtcm_budget) {
|
||||
*out_dst_chunk_rows = chunk_rows;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Try smaller chunk bands: 64, 32, 16, 8, 4, 2, 1
|
||||
chunk_rows = 64;
|
||||
while (chunk_rows > 1) {
|
||||
if (chunk_rows < dst_y) {
|
||||
htp_upscale_vtcm_layout_build(layout, src_x, src_y, dst_x, dst_y, chunk_rows, inv_sf1, filter_radius1, n_threads);
|
||||
if (layout->total_bytes <= vtcm_budget) {
|
||||
*out_dst_chunk_rows = chunk_rows;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
chunk_rows /= 2;
|
||||
}
|
||||
|
||||
// 3. Fallback to 1 row
|
||||
chunk_rows = 1;
|
||||
htp_upscale_vtcm_layout_build(layout, src_x, src_y, dst_x, dst_y, chunk_rows, inv_sf1, filter_radius1, n_threads);
|
||||
if (layout->total_bytes <= vtcm_budget) {
|
||||
*out_dst_chunk_rows = chunk_rows;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int op_upscale(struct htp_ops_context * octx);
|
||||
|
||||
#endif // HTP_UPSCALE_OPS_H
|
||||
@@ -507,6 +507,47 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_lightning_indexe
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_mask(
|
||||
ggml_metal_library_t lib,
|
||||
const ggml_tensor * op,
|
||||
bool fill,
|
||||
bool has_score,
|
||||
bool has_blocks,
|
||||
bool has_extra,
|
||||
bool vec) {
|
||||
GGML_ASSERT(op->op == GGML_OP_TOP_K_MASK);
|
||||
|
||||
char base[256];
|
||||
char name[256];
|
||||
|
||||
snprintf(base, 256, "kernel_top_k_mask%s_%s", fill ? "_fill" : "", ggml_type_name(op->type));
|
||||
|
||||
ggml_metal_pipeline_with_params res;
|
||||
|
||||
ggml_metal_cv_t cv = ggml_metal_cv_init();
|
||||
|
||||
if (fill) {
|
||||
snprintf(name, 256, "%s_vec=%d", base, vec ? 1 : 0);
|
||||
|
||||
ggml_metal_cv_set_bool(cv, vec, FC_TOP_K_MASK + 3);
|
||||
} else {
|
||||
snprintf(name, 256, "%s_sc=%d_bl=%d_ex=%d", base, has_score ? 1 : 0, has_blocks ? 1 : 0, has_extra ? 1 : 0);
|
||||
|
||||
ggml_metal_cv_set_bool(cv, has_score, FC_TOP_K_MASK + 0);
|
||||
ggml_metal_cv_set_bool(cv, has_blocks, FC_TOP_K_MASK + 1);
|
||||
ggml_metal_cv_set_bool(cv, has_extra, FC_TOP_K_MASK + 2);
|
||||
}
|
||||
|
||||
res = ggml_metal_library_get_pipeline(lib, name);
|
||||
if (!res.pipeline) {
|
||||
res = ggml_metal_library_compile_pipeline(lib, base, name, cv);
|
||||
}
|
||||
|
||||
ggml_metal_cv_free(cv);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_dsv4_hc(ggml_metal_library_t lib, const ggml_tensor * op) {
|
||||
char name[256];
|
||||
const char * base = nullptr;
|
||||
|
||||
@@ -126,6 +126,7 @@ struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_cumsum_ad
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_tri (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_soft_max (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_lightning_indexer (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_top_k_mask (ggml_metal_library_t lib, const struct ggml_tensor * op, bool fill, bool has_score, bool has_blocks, bool has_extra, bool vec);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_dsv4_hc (ggml_metal_library_t lib, const struct ggml_tensor * op);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_ssm_conv (ggml_metal_library_t lib, const struct ggml_tensor * op, int32_t nc, bool use_silu);
|
||||
struct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_ssm_conv_batched (ggml_metal_library_t lib, const struct ggml_tensor * op, int ssm_conv_bs, int32_t nc, bool use_silu);
|
||||
|
||||
@@ -1555,6 +1555,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
|
||||
switch (op->op) {
|
||||
case GGML_OP_SCALE:
|
||||
return ggml_is_contiguous_rows(op->src[0]) && (op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16 || op->src[0]->type == GGML_TYPE_BF16);
|
||||
case GGML_OP_FILL:
|
||||
case GGML_OP_CLAMP:
|
||||
case GGML_OP_SQR:
|
||||
@@ -1834,6 +1835,15 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
ggml_is_contiguous_rows(op->src[1]) &&
|
||||
ggml_is_contiguous_rows(op->src[2]) &&
|
||||
(op->src[3] == NULL || ggml_is_contiguous_rows(op->src[3]));
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
return (op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32) &&
|
||||
op->src[0]->type == op->type &&
|
||||
op->src[0]->nb[0] == ggml_type_size(op->src[0]->type) &&
|
||||
op->nb[0] == ggml_type_size(op->type) &&
|
||||
op->src[1]->type == GGML_TYPE_I32 &&
|
||||
(op->src[2] == NULL || op->src[2]->type == GGML_TYPE_F32) &&
|
||||
(op->src[3] == NULL || op->src[3]->type == GGML_TYPE_I32) &&
|
||||
(op->src[4] == NULL || op->src[4]->type == GGML_TYPE_I32);
|
||||
case GGML_OP_SSM_SCAN:
|
||||
return has_simdgroup_reduction;
|
||||
case GGML_OP_SSM_CONV:
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
#define FC_FLASH_ATTN_EXT_TENSOR 2200
|
||||
#define FC_LIGHTNING_INDEXER 2200
|
||||
#define FC_MUL_MV_MMA 2300
|
||||
#define FC_TOP_K_MASK 2400
|
||||
|
||||
// op-specific constants
|
||||
#define OP_FLASH_ATTN_EXT_NQPSG 8
|
||||
@@ -1295,6 +1296,38 @@ typedef struct {
|
||||
uint64_t nbm3;
|
||||
} ggml_metal_kargs_lightning_indexer;
|
||||
|
||||
typedef struct {
|
||||
int32_t n_kv;
|
||||
int32_t m1;
|
||||
int32_t m2;
|
||||
int32_t m3;
|
||||
int32_t n_top;
|
||||
int32_t n_pick;
|
||||
int32_t n_blk;
|
||||
int32_t n_extra;
|
||||
int64_t n_elems;
|
||||
uint64_t nb1;
|
||||
uint64_t nb2;
|
||||
uint64_t nb3;
|
||||
uint64_t nbm1;
|
||||
uint64_t nbm2;
|
||||
uint64_t nbm3;
|
||||
uint64_t nbk0;
|
||||
uint64_t nbk1;
|
||||
uint64_t nbk2;
|
||||
uint64_t nbk3;
|
||||
uint64_t nbs0;
|
||||
uint64_t nbs1;
|
||||
uint64_t nbs2;
|
||||
uint64_t nbs3;
|
||||
uint64_t nbb0;
|
||||
uint64_t nbb1;
|
||||
uint64_t nbe0;
|
||||
uint64_t nbe1;
|
||||
uint64_t nbe2;
|
||||
uint64_t nbe3;
|
||||
} ggml_metal_kargs_top_k_mask;
|
||||
|
||||
typedef struct {
|
||||
int32_t n_tokens;
|
||||
int32_t n_iter;
|
||||
|
||||
@@ -345,6 +345,10 @@ static int ggml_metal_op_encode_impl(ggml_metal_op_t ctx, int idx) {
|
||||
{
|
||||
n_fuse = ggml_metal_op_lightning_indexer(ctx, idx);
|
||||
} break;
|
||||
case GGML_OP_TOP_K_MASK:
|
||||
{
|
||||
n_fuse = ggml_metal_op_top_k_mask(ctx, idx);
|
||||
} break;
|
||||
case GGML_OP_DSV4_HC_COMB:
|
||||
case GGML_OP_DSV4_HC_PRE:
|
||||
case GGML_OP_DSV4_HC_POST:
|
||||
@@ -1424,6 +1428,124 @@ int ggml_metal_op_lightning_indexer(ggml_metal_op_t ctx, int idx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ggml_metal_op_top_k_mask(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
ggml_metal_library_t lib = ctx->lib;
|
||||
ggml_metal_encoder_t enc = ctx->enc;
|
||||
|
||||
const ggml_tensor * mask = op->src[0];
|
||||
const ggml_tensor * top_k = op->src[1];
|
||||
const ggml_tensor * score = op->src[2];
|
||||
const ggml_tensor * blocks = op->src[3];
|
||||
const ggml_tensor * extra = op->src[4];
|
||||
|
||||
GGML_ASSERT(op->type == GGML_TYPE_F16 || op->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(mask->type == op->type);
|
||||
GGML_ASSERT(top_k->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(!score || score->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT(!blocks || blocks->type == GGML_TYPE_I32);
|
||||
GGML_ASSERT(!extra || extra->type == GGML_TYPE_I32);
|
||||
|
||||
const int32_t n_kv = mask->ne[0];
|
||||
const int32_t n_top = (int32_t) top_k->ne[0];
|
||||
const int32_t n_pick = blocks ? blocks->ne[1] : score ? score->ne[0] : n_kv;
|
||||
const int32_t n_blk = blocks ? blocks->ne[0] : 0;
|
||||
const int32_t n_extra = extra ? extra->ne[0] : 0;
|
||||
|
||||
const ggml_metal_buffer_id bid = ggml_metal_get_buffer_id(op);
|
||||
|
||||
// the vector fill stores 4 elements at a time
|
||||
const int32_t ts = ggml_type_size(op->type);
|
||||
const int64_t n_elems = ggml_nelements(op);
|
||||
|
||||
const bool has_score = score != NULL;
|
||||
const bool has_blocks = blocks != NULL;
|
||||
const bool has_extra = extra != NULL;
|
||||
|
||||
const bool vec = n_elems % 4 == 0 && bid.offs % (4*ts) == 0;
|
||||
|
||||
ggml_metal_kargs_top_k_mask args = {
|
||||
/*.n_kv =*/ n_kv,
|
||||
/*.m1 =*/ (int32_t) mask->ne[1],
|
||||
/*.m2 =*/ (int32_t) mask->ne[2],
|
||||
/*.m3 =*/ (int32_t) mask->ne[3],
|
||||
/*.n_top =*/ n_top,
|
||||
/*.n_pick =*/ n_pick,
|
||||
/*.n_blk =*/ n_blk,
|
||||
/*.n_extra =*/ n_extra,
|
||||
/*.n_elems =*/ n_elems,
|
||||
/*.nb1 =*/ op->nb[1],
|
||||
/*.nb2 =*/ op->nb[2],
|
||||
/*.nb3 =*/ op->nb[3],
|
||||
/*.nbm1 =*/ mask->nb[1],
|
||||
/*.nbm2 =*/ mask->nb[2],
|
||||
/*.nbm3 =*/ mask->nb[3],
|
||||
/*.nbk0 =*/ top_k->nb[0],
|
||||
/*.nbk1 =*/ top_k->nb[1],
|
||||
/*.nbk2 =*/ top_k->nb[2],
|
||||
/*.nbk3 =*/ top_k->nb[3],
|
||||
/*.nbs0 =*/ score ? score->nb[0] : 0,
|
||||
/*.nbs1 =*/ score ? score->nb[1] : 0,
|
||||
/*.nbs2 =*/ score ? score->nb[2] : 0,
|
||||
/*.nbs3 =*/ score ? score->nb[3] : 0,
|
||||
/*.nbb0 =*/ blocks ? blocks->nb[0] : 0,
|
||||
/*.nbb1 =*/ blocks ? blocks->nb[1] : 0,
|
||||
/*.nbe0 =*/ extra ? extra->nb[0] : 0,
|
||||
/*.nbe1 =*/ extra ? extra->nb[1] : 0,
|
||||
/*.nbe2 =*/ extra ? extra->nb[2] : 0,
|
||||
/*.nbe3 =*/ extra ? extra->nb[3] : 0,
|
||||
};
|
||||
|
||||
// the fill covers the whole dst, the scatter overwrites the kept cells with the mask values
|
||||
{
|
||||
auto pipeline_fill = ggml_metal_library_get_pipeline_top_k_mask(lib, op, true, false, false, false, vec);
|
||||
|
||||
const int64_t n_fill = n_elems/(vec ? 4 : 1);
|
||||
const int32_t nth_max = ggml_metal_pipeline_max_theads_per_threadgroup(pipeline_fill);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
|
||||
while (nth < nth_max && nth < n_fill) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
ggml_metal_encoder_set_pipeline(enc, pipeline_fill);
|
||||
ggml_metal_encoder_set_bytes(enc, &args, sizeof(args), 0);
|
||||
ggml_metal_encoder_set_buffer(enc, bid, 1);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, (n_fill + nth - 1)/nth, 1, 1, nth, 1, 1);
|
||||
|
||||
ggml_metal_op_concurrency_reset(ctx);
|
||||
}
|
||||
|
||||
{
|
||||
auto pipeline = ggml_metal_library_get_pipeline_top_k_mask(lib, op, false, has_score, has_blocks, has_extra, false);
|
||||
|
||||
const int32_t n_max = n_top + n_extra;
|
||||
const int32_t nth_max = ggml_metal_pipeline_max_theads_per_threadgroup(pipeline);
|
||||
|
||||
int nth = 32; // SIMD width
|
||||
|
||||
while (nth < nth_max && nth < n_max) {
|
||||
nth *= 2;
|
||||
}
|
||||
|
||||
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(mask), 1);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(top_k), 2);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(score ? score : mask), 3);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(blocks ? blocks : mask), 4);
|
||||
ggml_metal_encoder_set_buffer(enc, ggml_metal_get_buffer_id(extra ? extra : mask), 5);
|
||||
ggml_metal_encoder_set_buffer(enc, bid, 6);
|
||||
|
||||
ggml_metal_encoder_dispatch_threadgroups(enc, op->ne[1], op->ne[2], op->ne[3], nth, 1, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ggml_metal_op_dsv4_hc(ggml_metal_op_t ctx, int idx) {
|
||||
ggml_tensor * op = ctx->node(idx);
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ int ggml_metal_op_get_rows (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_set_rows (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_diag (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_lightning_indexer (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_top_k_mask (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_dsv4_hc (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_soft_max (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_ssm_conv (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -906,3 +906,99 @@ kernel void kernel_dsv4_hc_post_f32(
|
||||
*(device float *) (dst + i0*args.nb_d0 + idst*args.nb_d1 + it*args.nb_d2) = result[idst];
|
||||
}
|
||||
}
|
||||
|
||||
constant bool FC_top_k_mask_has_score [[function_constant(FC_TOP_K_MASK + 0)]];
|
||||
constant bool FC_top_k_mask_has_blocks [[function_constant(FC_TOP_K_MASK + 1)]];
|
||||
constant bool FC_top_k_mask_has_extra [[function_constant(FC_TOP_K_MASK + 2)]];
|
||||
constant bool FC_top_k_mask_vec [[function_constant(FC_TOP_K_MASK + 3)]];
|
||||
|
||||
// the fill runs before the scatter, both are fully parallel over the whole dst and over the picks of one row
|
||||
template <typename T, typename T4>
|
||||
kernel void kernel_top_k_mask_fill(
|
||||
constant ggml_metal_kargs_top_k_mask & args,
|
||||
device char * dst,
|
||||
uint tid[[thread_position_in_grid]],
|
||||
uint ntot[[threads_per_grid]]) {
|
||||
const T ninf = T(-INFINITY);
|
||||
|
||||
if (FC_top_k_mask_vec) {
|
||||
device T4 * dst_4 = (device T4 *) dst;
|
||||
for (int64_t i = tid; i < args.n_elems/4; i += ntot) {
|
||||
dst_4[i] = T4(ninf);
|
||||
}
|
||||
} else {
|
||||
device T * dst_t = (device T *) dst;
|
||||
for (int64_t i = tid; i < args.n_elems; i += ntot) {
|
||||
dst_t[i] = ninf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef decltype(kernel_top_k_mask_fill<half, half4>) kernel_top_k_mask_fill_h_t;
|
||||
typedef decltype(kernel_top_k_mask_fill<float, float4>) kernel_top_k_mask_fill_f_t;
|
||||
|
||||
template [[host_name("kernel_top_k_mask_fill_f16")]] kernel kernel_top_k_mask_fill_h_t kernel_top_k_mask_fill<half, half4>;
|
||||
template [[host_name("kernel_top_k_mask_fill_f32")]] kernel kernel_top_k_mask_fill_f_t kernel_top_k_mask_fill<float, float4>;
|
||||
|
||||
// one threadgroup per row: copy the mask cells of the live picks and of the extra cells over the fill
|
||||
template <typename T, typename T4>
|
||||
kernel void kernel_top_k_mask(
|
||||
constant ggml_metal_kargs_top_k_mask & args,
|
||||
device const char * mask,
|
||||
device const char * top_k,
|
||||
device const char * score,
|
||||
device const char * blocks,
|
||||
device const char * extra,
|
||||
device char * dst,
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint3 tpitg[[thread_position_in_threadgroup]],
|
||||
uint3 ntg_max[[threads_per_threadgroup]]) {
|
||||
const int i1 = tgpig.x;
|
||||
const int i2 = tgpig.y;
|
||||
const int i3 = tgpig.z;
|
||||
|
||||
const int tiitg = tpitg.x;
|
||||
const int ntg = ntg_max.x;
|
||||
|
||||
device const char * mask_row = mask + (i1 % args.m1)*args.nbm1 + (i2 % args.m2)*args.nbm2 + (i3 % args.m3)*args.nbm3;
|
||||
device char * dst_row = dst + i1*args.nb1 + i2*args.nb2 + i3*args.nb3;
|
||||
device const char * top_k_row = top_k + i1*args.nbk1 + i2*args.nbk2 + i3*args.nbk3;
|
||||
|
||||
device const T * mask_row_t = (device const T *) mask_row;
|
||||
device T * dst_row_t = (device T *) dst_row;
|
||||
|
||||
for (int i = tiitg; i < args.n_top; i += ntg) {
|
||||
const int32_t p = *(device const int32_t *) (top_k_row + i*args.nbk0);
|
||||
if (p < 0 || p >= args.n_pick) {
|
||||
continue;
|
||||
}
|
||||
if (FC_top_k_mask_has_score && !(*(device const float *) (score + p*args.nbs0 + i1*args.nbs1 + i2*args.nbs2 + i3*args.nbs3) > -INFINITY)) {
|
||||
continue;
|
||||
}
|
||||
if (FC_top_k_mask_has_blocks) {
|
||||
FOR_UNROLL (int k = 0; k < args.n_blk; ++k) {
|
||||
const int32_t c = *(device const int32_t *) (blocks + k*args.nbb0 + p*args.nbb1);
|
||||
if (c >= 0 && c < args.n_kv) {
|
||||
dst_row_t[c] = mask_row_t[c];
|
||||
}
|
||||
}
|
||||
} else if (p < args.n_kv) {
|
||||
dst_row_t[p] = mask_row_t[p];
|
||||
}
|
||||
}
|
||||
|
||||
if (FC_top_k_mask_has_extra) {
|
||||
for (int i = tiitg; i < args.n_extra; i += ntg) {
|
||||
const int32_t c = *(device const int32_t *) (extra + i*args.nbe0 + i1*args.nbe1 + i2*args.nbe2 + i3*args.nbe3);
|
||||
if (c >= 0 && c < args.n_kv) {
|
||||
dst_row_t[c] = mask_row_t[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef decltype(kernel_top_k_mask<half, half4>) kernel_top_k_mask_h_t;
|
||||
typedef decltype(kernel_top_k_mask<float, float4>) kernel_top_k_mask_f_t;
|
||||
|
||||
template [[host_name("kernel_top_k_mask_f16")]] kernel kernel_top_k_mask_h_t kernel_top_k_mask<half, half4>;
|
||||
template [[host_name("kernel_top_k_mask_f32")]] kernel kernel_top_k_mask_f_t kernel_top_k_mask<float, float4>;
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
constant short FC_unary_op [[function_constant(FC_UNARY + 0)]];
|
||||
constant bool FC_unary_cnt[[function_constant(FC_UNARY + 1)]];
|
||||
|
||||
template <typename T, typename U>
|
||||
inline constexpr T ggml_metal_unary_cast(U x) {
|
||||
return (T) x;
|
||||
}
|
||||
|
||||
#if defined(GGML_METAL_HAS_BF16)
|
||||
// MSL has no C-style cast from float vectors to bfloat vectors
|
||||
template <>
|
||||
inline constexpr bfloat4 ggml_metal_unary_cast<bfloat4, float4>(float4 x) {
|
||||
return bfloat4(bfloat(x[0]), bfloat(x[1]), bfloat(x[2]), bfloat(x[3]));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr bfloat4 ggml_metal_unary_cast<bfloat4, float>(float x) {
|
||||
return bfloat4(bfloat(x), bfloat(x), bfloat(x), bfloat(x));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T0, typename T, typename TC>
|
||||
kernel void kernel_unary_impl(
|
||||
constant ggml_metal_kargs_unary & args,
|
||||
@@ -48,79 +66,79 @@ kernel void kernel_unary_impl(
|
||||
const TC x = (TC) src0_ptr[i0];
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SCALE) {
|
||||
dst_ptr[i0] = (T) (args.scale * x + args.bias);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(args.scale * x + args.bias);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_FILL) {
|
||||
dst_ptr[i0] = (T) args.val;
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(args.val);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_CLAMP) {
|
||||
dst_ptr[i0] = (T) clamp(x, args.min, args.max);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(clamp(x, args.min, args.max));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SQR) {
|
||||
dst_ptr[i0] = (T) (x * x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * x);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SQRT) {
|
||||
dst_ptr[i0] = (T) sqrt(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(sqrt(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SIN) {
|
||||
dst_ptr[i0] = (T) sin(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(sin(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_COS) {
|
||||
dst_ptr[i0] = (T) cos(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(cos(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_LOG) {
|
||||
dst_ptr[i0] = (T) log(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(log(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_LEAKY_RELU) {
|
||||
dst_ptr[i0] = (T) (TC(x > 0)*x + TC(x <= 0)*(x * args.slope));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(TC(x > 0)*x + TC(x <= 0)*(x * args.slope));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_TANH) {
|
||||
dst_ptr[i0] = (T) precise::tanh(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(precise::tanh(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_RELU) {
|
||||
dst_ptr[i0] = (T) fmax(0, x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fmax(0, x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SIGMOID) {
|
||||
dst_ptr[i0] = (T) (1 / (1 + exp(-x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(1 / (1 + exp(-x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU) {
|
||||
dst_ptr[i0] = (T) (0.5*x*(1 + precise::tanh(SQRT_2_OVER_PI*x*(1 + GELU_COEF_A*x*x))));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(0.5*x*(1 + precise::tanh(SQRT_2_OVER_PI*x*(1 + GELU_COEF_A*x*x))));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU_ERF) {
|
||||
dst_ptr[i0] = (T) (0.5*x*(1 + erf_approx(SQRT_2_INV*x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(0.5*x*(1 + erf_approx(SQRT_2_INV*x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_GELU_QUICK) {
|
||||
dst_ptr[i0] = (T) (x * (1/(1 + exp(GELU_QUICK_COEF*x))));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * (1/(1 + exp(GELU_QUICK_COEF*x))));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SILU) {
|
||||
dst_ptr[i0] = (T) (x / (1 + exp(-x)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x / (1 + exp(-x)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ELU) {
|
||||
dst_ptr[i0] = (T) elu_approx(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(elu_approx(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_NEG) {
|
||||
dst_ptr[i0] = (T) -x;
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(-x);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ABS) {
|
||||
dst_ptr[i0] = (T) fabs(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fabs(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SGN) {
|
||||
@@ -132,40 +150,40 @@ kernel void kernel_unary_impl(
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_HARDSWISH) {
|
||||
dst_ptr[i0] = (T) (x * fmax(0, fmin(1, x/6 + 0.5)));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(x * fmax(0, fmin(1, x/6 + 0.5)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_HARDSIGMOID) {
|
||||
dst_ptr[i0] = (T) fmax(0, fmin(1, x/6 + 0.5));
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(fmax(0, fmin(1, x/6 + 0.5)));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_EXP) {
|
||||
dst_ptr[i0] = (T) exp(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(exp(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_SOFTPLUS) {
|
||||
dst_ptr[i0] = (T) select(log(1 + exp(x)), x, x > 20);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(select(log(1 + exp(x)), x, x > 20));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_EXPM1) {
|
||||
// TODO: precise implementation
|
||||
dst_ptr[i0] = (T) (exp(x) - 1);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(exp(x) - 1);
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_FLOOR) {
|
||||
dst_ptr[i0] = (T) floor(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(floor(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_CEIL) {
|
||||
dst_ptr[i0] = (T) ceil(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(ceil(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_ROUND) {
|
||||
dst_ptr[i0] = (T) round(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(round(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_TRUNC) {
|
||||
dst_ptr[i0] = (T) trunc(x);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(trunc(x));
|
||||
}
|
||||
|
||||
if (FC_OP == OP_UNARY_NUM_XIELU) {
|
||||
@@ -174,7 +192,7 @@ kernel void kernel_unary_impl(
|
||||
const TC clamped = fmin(xi, TC(args.val));
|
||||
const TC y_pos = TC(args.scale) * xi * xi + TC(args.bias) * xi;
|
||||
const TC y_neg = (exp(clamped) - TC(1.0f) - xi) * TC(args.slope) + TC(args.bias) * xi;
|
||||
dst_ptr[i0] = (T) (gate * y_pos + (TC(1.0f) - gate) * y_neg);
|
||||
dst_ptr[i0] = ggml_metal_unary_cast<T>(gate * y_pos + (TC(1.0f) - gate) * y_neg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +206,10 @@ template [[host_name("kernel_unary_f32_f32")]] kernel kernel_unary_t kernel_un
|
||||
template [[host_name("kernel_unary_f32_f32_4")]] kernel kernel_unary_t kernel_unary_impl<float4, float4, float4>;
|
||||
template [[host_name("kernel_unary_f16_f16")]] kernel kernel_unary_t kernel_unary_impl<half, half, float>;
|
||||
template [[host_name("kernel_unary_f16_f16_4")]] kernel kernel_unary_t kernel_unary_impl<half4, half4, float4>;
|
||||
#if defined(GGML_METAL_HAS_BF16)
|
||||
template [[host_name("kernel_unary_bf16_bf16")]] kernel kernel_unary_t kernel_unary_impl<bfloat, bfloat, float>;
|
||||
template [[host_name("kernel_unary_bf16_bf16_4")]] kernel kernel_unary_t kernel_unary_impl<bfloat4, bfloat4, float4>;
|
||||
#endif
|
||||
|
||||
kernel void kernel_silu_back_f32(
|
||||
constant ggml_metal_kargs_silu_back & args,
|
||||
|
||||
@@ -104,7 +104,13 @@ set(GGML_OPENCL_KERNELS
|
||||
gemm_noshuffle_q2_k_q8_1_dp4a
|
||||
gemv_noshuffle_q2_k_f32
|
||||
gemm_noshuffle_q2_k_f32
|
||||
gemm_noshuffle_q3_k_f32
|
||||
gemm_noshuffle_q3_k_q8_1_dp4a
|
||||
gemv_noshuffle_q3_k_f32
|
||||
mul_mv_q2_k_f32
|
||||
mul_mv_q3_k_f32
|
||||
mul_mv_q3_k_f32_flat
|
||||
mul_mm_q3_k_f32_l4_lm
|
||||
mul_mv_q2_k_f32_flat
|
||||
mul_mm_q2_k_f32_l4_lm
|
||||
mul_mv_q4_k_f32
|
||||
@@ -128,6 +134,7 @@ set(GGML_OPENCL_KERNELS
|
||||
mul_mv_id_q8_0_f32_flat
|
||||
mul_mv_id_mxfp4_f32
|
||||
mul_mv_id_mxfp4_f32_flat
|
||||
mul_mat_id_utils
|
||||
gemm_moe_q4_0_f32_ns
|
||||
gemm_moe_q4_0_q8_1_dp4a
|
||||
gemv_moe_q4_0_f32_ns
|
||||
@@ -199,6 +206,7 @@ set(GGML_OPENCL_KERNELS
|
||||
gemv_noshuffle_q4_k_f32
|
||||
gemv_noshuffle_q4_k_f32_o4
|
||||
gemv_noshuffle_q4_k_f32_tiled
|
||||
gemm_noshuffle_q4_K_f32_tiled_dp4a
|
||||
gemm_noshuffle_q4_k_f32
|
||||
gemv_noshuffle_q4_k_f32_32b_trans
|
||||
gemv_noshuffle_q6_k_f32
|
||||
|
||||
@@ -975,6 +975,17 @@ struct ggml_backend_opencl_context {
|
||||
int q2_k_mv_rows = 4;
|
||||
int q2_k_mv_nsg = 4;
|
||||
cl_kernel kernel_mul_mv_q2_K_f32;
|
||||
cl_kernel kernel_convert_block_q3_K, kernel_restore_block_q3_K;
|
||||
cl_kernel kernel_mul_mv_q3_K_f32_flat;
|
||||
cl_kernel kernel_mul_mm_q3_k_f32_l4_lm;
|
||||
cl_kernel kernel_convert_block_q3_k_noshuffle, kernel_restore_block_q3_k_noshuffle;
|
||||
cl_kernel kernel_gemv_noshuffle_q3_k_f32 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_f32 = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_q8_1_dp4a = nullptr;
|
||||
cl_kernel kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow = nullptr;
|
||||
int q3_k_mv_rows = 2;
|
||||
int q3_k_mv_nsg = 8;
|
||||
cl_kernel kernel_mul_mv_q3_K_f32;
|
||||
cl_kernel kernel_mul_mv_q4_K_f32;
|
||||
cl_kernel kernel_mul_mv_q4_K_f32_flat;
|
||||
cl_kernel kernel_mul_mv_q5_K_f32;
|
||||
@@ -1275,6 +1286,7 @@ struct ggml_backend_opencl_context {
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_o4; // 4-output-per-WI, long-vocab lm_head
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_tiled; // tiled-wide layout (opt-in)
|
||||
cl_kernel kernel_gemm_noshuffle_q4_K_f32_tiled_dp4a = nullptr;
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_splitk; // split-K across WGs (small-M decode)
|
||||
cl_kernel kernel_gemv_splitk_reduce_f32; // sums split-K per-slice partials
|
||||
cl_kernel kernel_gemv_noshuffle_q4_k_f32_glu; // fused gate+up GEMV + GLU (FFN)
|
||||
@@ -1542,6 +1554,23 @@ static std::string ggml_cl_q2_k_mm_options(const ggml_backend_opencl_context * b
|
||||
return " -DBK=" + std::to_string(bk) + " -DLM_HALF=" + std::to_string(half ? 1 : 0);
|
||||
}
|
||||
|
||||
static std::string ggml_cl_q3_k_mm_options(const ggml_backend_opencl_context * backend_ctx) {
|
||||
const bool x2 = backend_ctx->gpu_family == ADRENO &&
|
||||
(backend_ctx->adreno_gen == ADRENO_GPU_GEN::A8X || backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E);
|
||||
int bk = x2 ? 16 : 32;
|
||||
bool half = x2;
|
||||
if (const char * env = getenv("GGML_OPENCL_LM_BK")) {
|
||||
const int value = atoi(env);
|
||||
if (value == 16 || value == 32) {
|
||||
bk = value;
|
||||
}
|
||||
}
|
||||
if (const char * env = getenv("GGML_OPENCL_LM_HALF")) {
|
||||
half = atoi(env) != 0;
|
||||
}
|
||||
return " -DBK=" + std::to_string(bk) + " -DLM_HALF=" + std::to_string(half ? 1 : 0);
|
||||
}
|
||||
|
||||
static void load_cl_kernels_argsort(ggml_backend_opencl_context *backend_ctx) {
|
||||
// compiler options for general kernels
|
||||
auto opencl_c_std =
|
||||
@@ -1815,6 +1844,21 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mat_id_utils
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mat_id_utils.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mat_id_utils.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_moe_zero_dst = clCreateKernel(prog, "kernel_moe_zero_dst", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// moe_add_id_glu
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -1992,6 +2036,10 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q5_K_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q5_K_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q2_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q2_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q2_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q2_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q3_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q3_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q3_k_noshuffle = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q3_k_noshuffle", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q3_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q3_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q3_K = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q3_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q2_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q2_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_restore_block_q2_K = clCreateKernel(backend_ctx->program_cvt, "kernel_restore_block_q2_K", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_convert_block_q6_K = clCreateKernel(backend_ctx->program_cvt, "kernel_convert_block_q6_K", &err), err));
|
||||
@@ -2398,6 +2446,53 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q3_k_f32_flat
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mv_q3_k_f32_flat.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mv_q3_k_f32_flat.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_mul_mv_q3_K_f32_flat = clCreateKernel(prog, "kernel_mul_mv_q3_K_f32_flat", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mm_q3_k_f32_l4_lm
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mm_q3_k_f32_l4_lm.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mm_q3_k_f32_l4_lm.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts + ggml_cl_q3_k_mm_options(backend_ctx));
|
||||
CL_CHECK((backend_ctx->kernel_mul_mm_q3_k_f32_l4_lm = clCreateKernel(prog, "kernel_mul_mm_q3_k_f32_l4_lm", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "mul_mv_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("mul_mv_q3_k_f32.cl");
|
||||
#endif
|
||||
cl_program prog =
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_mul_mv_q3_K_f32 = clCreateKernel(prog, "kernel_mul_mv_q3_K_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// mul_mv_q4_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -4206,6 +4301,80 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q3_k_q8_1_dp4a
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q3_k_q8_1_dp4a.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q3_k_q8_1_dp4a.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts + " -DTILESIZE_N=8");
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_q8_1_dp4a", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q3_k_f32.cl");
|
||||
#endif
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q3_k_f32 = clCreateKernel(prog, "kernel_gemm_noshuffle_q3_k_f32", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemv_noshuffle_q3_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemv_noshuffle_q3_k_f32.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemv_noshuffle_q3_k_f32.cl");
|
||||
#endif
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_MV_R")) {
|
||||
const int rows = atoi(env);
|
||||
if (rows == 1 || rows == 2 || rows == 4) {
|
||||
backend_ctx->q3_k_mv_rows = rows;
|
||||
}
|
||||
}
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_MV_NSG")) {
|
||||
const int nsg = atoi(env);
|
||||
if (nsg == 1 || nsg == 2 || nsg == 4 || nsg == 8) {
|
||||
backend_ctx->q3_k_mv_nsg = nsg;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
const std::string opts = compile_opts + " -DQ3_K_MV_R=" + std::to_string(backend_ctx->q3_k_mv_rows) +
|
||||
" -DQ3_K_MV_NSG=" + std::to_string(backend_ctx->q3_k_mv_nsg);
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), opts);
|
||||
cl_kernel kernel = clCreateKernel(prog, "kernel_gemv_noshuffle_q3_k_f32", &err);
|
||||
CL_CHECK(err);
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
size_t max_workgroup;
|
||||
CL_CHECK(clGetKernelWorkGroupInfo(kernel, backend_ctx->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof(max_workgroup), &max_workgroup, nullptr));
|
||||
if (64*(size_t)backend_ctx->q3_k_mv_nsg <= max_workgroup) {
|
||||
backend_ctx->kernel_gemv_noshuffle_q3_k_f32 = kernel;
|
||||
break;
|
||||
}
|
||||
CL_CHECK(clReleaseKernel(kernel));
|
||||
GGML_ASSERT(backend_ctx->q3_k_mv_nsg > 1);
|
||||
backend_ctx->q3_k_mv_nsg /= 2;
|
||||
}
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q2_k_f32
|
||||
{
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
@@ -4924,6 +5093,23 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
// gemm_noshuffle_q4_K_f32_tiled_dp4a
|
||||
if (backend_ctx->has_integer_dot) {
|
||||
#ifdef GGML_OPENCL_EMBED_KERNELS
|
||||
const std::string kernel_src {
|
||||
#include "gemm_noshuffle_q4_K_f32_tiled_dp4a.cl.h"
|
||||
};
|
||||
#else
|
||||
const std::string kernel_src = read_file("gemm_noshuffle_q4_K_f32_tiled_dp4a.cl");
|
||||
#endif
|
||||
std::string compile_opts = std::string("-cl-std=") + opencl_c_std + " -cl-mad-enable ";
|
||||
cl_program prog = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
|
||||
CL_CHECK((backend_ctx->kernel_gemm_noshuffle_q4_K_f32_tiled_dp4a =
|
||||
clCreateKernel(prog, "kernel_gemm_noshuffle_q4_K_f32_tiled_dp4a", &err), err));
|
||||
CL_CHECK(clReleaseProgram(prog));
|
||||
GGML_LOG_CONT(".");
|
||||
}
|
||||
|
||||
backend_ctx->kernel_gemv_noshuffle_q4_k_f32_32b_trans = nullptr;
|
||||
backend_ctx->kernel_gemm_noshuffle_q4_k_f32_32b_trans_ila_a8_bin = nullptr;
|
||||
backend_ctx->kernel_gemm_noshuffle_q4_k_q8_1_dp4a_ila_a8_bin = nullptr;
|
||||
@@ -5737,7 +5923,6 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
|
||||
build_program_from_source(backend_ctx, kernel_src.c_str(), CL_moe_compile_opts);
|
||||
|
||||
CL_CHECK((backend_ctx->kernel_moe_histogram = clCreateKernel(prog, "kernel_moe_histogram", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_zero_dst = clCreateKernel(prog, "kernel_moe_zero_dst", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_scan = clCreateKernel(prog, "kernel_moe_scan", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_fill = clCreateKernel(prog, "kernel_moe_fill", &err), err));
|
||||
CL_CHECK((backend_ctx->kernel_moe_scatter = clCreateKernel(prog, "kernel_moe_scatter", &err), err));
|
||||
@@ -8245,6 +8430,27 @@ struct ggml_tensor_extra_cl_q2_K {
|
||||
}
|
||||
};
|
||||
|
||||
struct ggml_tensor_extra_cl_q3_K {
|
||||
bool noshuffle = false;
|
||||
cl_mem qs = nullptr; // quantized values
|
||||
cl_mem hm = nullptr; // high bits
|
||||
cl_mem sc = nullptr; // packed 6-bit scales
|
||||
cl_mem d = nullptr; // super-block scales
|
||||
|
||||
size_t size_qs = 0, size_hm = 0, size_sc = 0, size_d = 0;
|
||||
|
||||
~ggml_tensor_extra_cl_q3_K() { reset(); }
|
||||
|
||||
void reset() {
|
||||
noshuffle = false;
|
||||
if (qs != nullptr) { CL_CHECK(clReleaseMemObject(qs)); qs = nullptr; }
|
||||
if (hm != nullptr) { CL_CHECK(clReleaseMemObject(hm)); hm = nullptr; }
|
||||
if (sc != nullptr) { CL_CHECK(clReleaseMemObject(sc)); sc = nullptr; }
|
||||
if (d != nullptr) { CL_CHECK(clReleaseMemObject(d)); d = nullptr; }
|
||||
size_qs = size_hm = size_sc = size_d = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ggml_tensor_extra_cl_q6_K {
|
||||
// Lower 4 bits of quantized weights.
|
||||
cl_mem ql = nullptr;
|
||||
@@ -9345,6 +9551,24 @@ static bool ggml_cl_q2_k_use_noshuffle(const ggml_backend_opencl_context * backe
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ggml_cl_q3_k_use_noshuffle(const ggml_backend_opencl_context * backend_ctx, const ggml_tensor * tensor) {
|
||||
#if defined(GGML_OPENCL_SOA_Q) && defined(GGML_OPENCL_USE_ADRENO_KERNELS)
|
||||
if (const char * env = getenv("GGML_OPENCL_Q3_K_NOSHUFFLE")) {
|
||||
if (env[0] == '0') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return tensor->type == GGML_TYPE_Q3_K &&
|
||||
backend_ctx->gpu_family == ADRENO &&
|
||||
ggml_is_contiguous(tensor) && !tensor->view_src &&
|
||||
use_adreno_kernels(backend_ctx, tensor);
|
||||
#else
|
||||
GGML_UNUSED(backend_ctx);
|
||||
GGML_UNUSED(tensor);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool use_adreno_moe_kernels(const ggml_backend_opencl_context *backend_ctx, const ggml_tensor *tensor) {
|
||||
// The moe weight repack kernels *_trans4_ns alias a private ushort8 through a uchar*.
|
||||
// Certain compilers (found with some A7x and A6x) miscompiles this, corrupting the weights.
|
||||
@@ -9416,9 +9640,11 @@ inline bool use_q6k_tiled(const ggml_backend_opencl_context *backend_ctx, const
|
||||
use_adreno_kernels(backend_ctx, tensor);
|
||||
}
|
||||
|
||||
// q4_K analog of the tiled-wide lm_head/embed GEMV (default OFF;
|
||||
// GGML_OPENCL_Q4K_GEMV_TILED forces either way: =0 off, else on). Same gate.
|
||||
// Select untiled Q4_K before upload if the batched DP4A reader is unavailable.
|
||||
inline bool q4k_gemv_tiled_enabled(const ggml_backend_opencl_context *backend_ctx) {
|
||||
if (!backend_ctx->has_integer_dot) {
|
||||
return false;
|
||||
}
|
||||
static const char * e = std::getenv("GGML_OPENCL_Q4K_GEMV_TILED");
|
||||
if (e && e[0] != '\0') {
|
||||
return e[0] != '0';
|
||||
@@ -9868,6 +10094,12 @@ static bool ggml_opencl_supports_op(ggml_backend_dev_t dev, const struct ggml_te
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q4_0) {
|
||||
// Non-contig src0 routes through on-device dequant-to-f16.
|
||||
return op->src[1]->type == GGML_TYPE_F32;
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q3_K) {
|
||||
const ggml_tensor * weight = op->src[0]->view_src;
|
||||
if (weight && ggml_cl_q3_k_use_noshuffle(backend_ctx, weight)) {
|
||||
return false;
|
||||
}
|
||||
return op->src[1]->type == GGML_TYPE_F32 && ggml_is_contiguous_rows(op->src[1]);
|
||||
} else if (op->src[0]->type == GGML_TYPE_Q2_K) {
|
||||
const ggml_tensor * weight = op->src[0]->view_src;
|
||||
if (weight && ggml_cl_q2_k_use_noshuffle(backend_ctx, weight)) {
|
||||
@@ -10254,12 +10486,18 @@ struct ggml_backend_opencl_buffer_context {
|
||||
for (ggml_tensor_extra_cl_q2_K * e : temp_tensor_extras_q2_K) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q6_K * e : temp_tensor_extras_q6_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q2_K * e : temp_tensor_extras_q2_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K_in_use) {
|
||||
delete e;
|
||||
}
|
||||
for (ggml_tensor_extra_cl_q5_K * e : temp_tensor_extras_q5_K) {
|
||||
delete e;
|
||||
}
|
||||
@@ -10448,6 +10686,21 @@ struct ggml_backend_opencl_buffer_context {
|
||||
return extra;
|
||||
}
|
||||
|
||||
ggml_tensor_extra_cl_q3_K * ggml_opencl_alloc_temp_tensor_extra_q3_K() {
|
||||
ggml_tensor_extra_cl_q3_K * extra;
|
||||
if (temp_tensor_extras_q3_K.empty()) {
|
||||
extra = new ggml_tensor_extra_cl_q3_K();
|
||||
} else {
|
||||
extra = temp_tensor_extras_q3_K.back();
|
||||
temp_tensor_extras_q3_K.pop_back();
|
||||
}
|
||||
|
||||
temp_tensor_extras_q3_K_in_use.push_back(extra);
|
||||
|
||||
extra->reset();
|
||||
return extra;
|
||||
}
|
||||
|
||||
ggml_tensor_extra_cl_q6_K * ggml_opencl_alloc_temp_tensor_extra_q6_K() {
|
||||
ggml_tensor_extra_cl_q6_K * extra;
|
||||
if (temp_tensor_extras_q6_K.empty()) {
|
||||
@@ -10529,6 +10782,11 @@ struct ggml_backend_opencl_buffer_context {
|
||||
}
|
||||
temp_tensor_extras_q2_K_in_use.clear();
|
||||
|
||||
for (ggml_tensor_extra_cl_q3_K * e : temp_tensor_extras_q3_K_in_use) {
|
||||
temp_tensor_extras_q3_K.push_back(e);
|
||||
}
|
||||
temp_tensor_extras_q3_K_in_use.clear();
|
||||
|
||||
q8_0_soa_tensors.clear();
|
||||
q4_0_soa_tensors.clear();
|
||||
}
|
||||
@@ -10561,7 +10819,9 @@ struct ggml_backend_opencl_buffer_context {
|
||||
std::vector<ggml_tensor_extra_cl_q5_K *> temp_tensor_extras_q5_K;
|
||||
std::vector<ggml_tensor_extra_cl_q5_K *> temp_tensor_extras_q5_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q2_K *> temp_tensor_extras_q2_K;
|
||||
std::vector<ggml_tensor_extra_cl_q3_K *> temp_tensor_extras_q3_K;
|
||||
std::vector<ggml_tensor_extra_cl_q2_K *> temp_tensor_extras_q2_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q3_K *> temp_tensor_extras_q3_K_in_use;
|
||||
std::vector<ggml_tensor_extra_cl_q6_K *> temp_tensor_extras_q6_K;
|
||||
std::vector<ggml_tensor_extra_cl_q6_K *> temp_tensor_extras_q6_K_in_use;
|
||||
|
||||
@@ -10818,6 +11078,91 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q3_K) {
|
||||
GGML_ASSERT(offset == 0 && size == ggml_nbytes(tensor));
|
||||
ggml_tensor_extra_cl * extra_orig = (ggml_tensor_extra_cl *)tensor->extra;
|
||||
GGML_ASSERT(extra_orig && "Tensors in OpenCL backend should have been allocated and initialized");
|
||||
|
||||
ggml_backend_opencl_buffer_context * ctx = (ggml_backend_opencl_buffer_context *) buffer->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra = ctx->ggml_opencl_alloc_temp_tensor_extra_q3_K();
|
||||
extra->noshuffle = ggml_cl_q3_k_use_noshuffle(backend_ctx, tensor);
|
||||
|
||||
const size_t blck = (size_t)ggml_blck_size(tensor->type);
|
||||
const size_t n_blk = ggml_nelements(tensor) / blck;
|
||||
|
||||
const size_t size_qs = n_blk * (blck/4);
|
||||
const size_t size_hm = n_blk * (blck/8);
|
||||
const size_t size_sc = n_blk * 12;
|
||||
const size_t size_d = n_blk * sizeof(ggml_fp16_t);
|
||||
GGML_ASSERT(size_qs + size_hm + size_sc + size_d == ggml_nbytes(tensor) && "Incorrect tensor size");
|
||||
|
||||
cl_int err;
|
||||
cl_mem data_device = clCreateBuffer(context, CL_MEM_READ_WRITE,
|
||||
ggml_nbytes(tensor), NULL, &err);
|
||||
CL_CHECK(err);
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, data_device, CL_TRUE, 0,
|
||||
ggml_nbytes(tensor), data, 0, NULL, NULL));
|
||||
|
||||
cl_buffer_region region;
|
||||
region.origin = align_to(extra_orig->offset + tensor->view_offs + offset, backend_ctx->alignment);
|
||||
region.size = size_qs;
|
||||
CL_CHECK((extra->qs = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
auto previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_qs, backend_ctx->alignment);
|
||||
region.size = size_hm;
|
||||
CL_CHECK((extra->hm = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_hm, backend_ctx->alignment);
|
||||
region.size = size_sc;
|
||||
CL_CHECK((extra->sc = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
previous_origin = region.origin;
|
||||
|
||||
region.origin = align_to(previous_origin + size_sc, backend_ctx->alignment);
|
||||
region.size = size_d;
|
||||
CL_CHECK((extra->d = clCreateSubBuffer(extra_orig->data_device, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
|
||||
cl_kernel kernel = extra->noshuffle ? backend_ctx->kernel_convert_block_q3_k_noshuffle
|
||||
: backend_ctx->kernel_convert_block_q3_K;
|
||||
cl_ulong nb_arg = (cl_ulong)n_blk;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &extra->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &nb_arg));
|
||||
|
||||
size_t global_work_size[] = { (size_t)CEIL_DIV(n_blk, 64)*64, 1, 1 };
|
||||
size_t local_work_size[] = { 64, 1, 1 };
|
||||
|
||||
cl_event evt;
|
||||
CL_CHECK(clEnqueueNDRangeKernel(queue, kernel, 3, NULL, global_work_size, local_work_size, 0, NULL, &evt));
|
||||
CL_CHECK(clWaitForEvents(1, &evt));
|
||||
CL_CHECK(clReleaseEvent(evt));
|
||||
CL_CHECK(clReleaseMemObject(data_device));
|
||||
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
if (extra->noshuffle) {
|
||||
const int m = tensor->ne[1];
|
||||
const int k = tensor->ne[0];
|
||||
transpose_2d_as_8b(backend_ctx, extra->qs, extra->qs, size_qs, k/4, m, true, true);
|
||||
transpose_2d_as_8b(backend_ctx, extra->hm, extra->hm, size_hm, k/8, m, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_32_buf, extra->sc, extra->sc, size_sc, 3*k/(int)blck, m, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_16_buf, extra->d, extra->d, size_d, k/(int)blck, m, true, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
extra->size_qs = size_qs;
|
||||
extra->size_hm = size_hm;
|
||||
extra->size_sc = size_sc;
|
||||
extra->size_d = size_d;
|
||||
|
||||
tensor->extra = extra;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q1_0) {
|
||||
ggml_tensor_extra_cl * extra_orig = (ggml_tensor_extra_cl *)tensor->extra;
|
||||
GGML_ASSERT(extra_orig && "Tesnors in OpenCL backend should have been allocated and initialized");
|
||||
@@ -10901,7 +11246,16 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
// conversion kernel.
|
||||
if (tensor->type == GGML_TYPE_Q4_0) {
|
||||
// Views can't SoA-ify here — parent owns the layout (see q8_0 guard).
|
||||
// A view of an AoS tensor (the KV cache, written by a state restore)
|
||||
// is written in place.
|
||||
if (tensor->view_src != nullptr || !ggml_is_contiguous(tensor)) {
|
||||
if (!ggml_cl_is_q4_0_soa(tensor)) {
|
||||
ggml_tensor_extra_cl * extra_aos = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra_aos->data_device, CL_TRUE,
|
||||
extra_aos->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Tensors should have been preallocated, therefore they should
|
||||
@@ -11605,7 +11959,16 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
}
|
||||
if (tensor->type == GGML_TYPE_Q8_0) {
|
||||
// Views share the parent's buffer; parent owns SoA conversion.
|
||||
// A view of an AoS tensor (the KV cache, written by a state restore)
|
||||
// is written in place.
|
||||
if (tensor->view_src != nullptr || !ggml_is_contiguous(tensor)) {
|
||||
if (!ggml_cl_is_q8_0_soa(tensor)) {
|
||||
ggml_tensor_extra_cl * extra_aos = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra_aos->data_device, CL_TRUE,
|
||||
extra_aos->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12440,7 +12803,7 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
GGML_ASSERT(extra);
|
||||
|
||||
cl_ulong n_elements = size / sizeof(ggml_fp16_t);
|
||||
cl_ulong off_dst = (extra->offset + offset) / sizeof(ggml_fp16_t);
|
||||
cl_ulong off_dst = (extra->offset + tensor->view_offs + offset) / sizeof(ggml_fp16_t);
|
||||
|
||||
cl_int err;
|
||||
cl_mem data_device = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
|
||||
@@ -12468,8 +12831,9 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
ggml_tensor_extra_cl * extra = (ggml_tensor_extra_cl *) tensor->extra;
|
||||
GGML_ASSERT(extra);
|
||||
|
||||
// a view reuses its parent's extra, whose offset does not include view_offs
|
||||
CL_CHECK(clEnqueueWriteBuffer(
|
||||
queue, extra->data_device, CL_TRUE, extra->offset + offset,
|
||||
queue, extra->data_device, CL_TRUE, extra->offset + tensor->view_offs + offset,
|
||||
size, data, 0, NULL, NULL));
|
||||
|
||||
GGML_UNUSED(buffer);
|
||||
@@ -12534,6 +12898,56 @@ static void ggml_backend_opencl_buffer_get_tensor(ggml_backend_buffer_t buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q3_K) {
|
||||
ggml_tensor_extra_cl_q3_K * extra = (ggml_tensor_extra_cl_q3_K *)tensor->extra;
|
||||
|
||||
const size_t blck = (size_t)ggml_blck_size(tensor->type);
|
||||
|
||||
ggml_cl_buffer buf_unpacked;
|
||||
buf_unpacked.allocate(backend_ctx->context, ggml_nbytes(tensor));
|
||||
|
||||
const size_t n_blk = (size_t)ggml_nelements(tensor) / blck;
|
||||
cl_ulong nb_arg = (cl_ulong)n_blk;
|
||||
|
||||
cl_mem qs = extra->qs, hm = extra->hm, sc = extra->sc, d = extra->d;
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
ggml_cl_buffer buf_qs, buf_hm, buf_sc, buf_d;
|
||||
if (extra->noshuffle) {
|
||||
buf_qs.allocate(context, extra->size_qs);
|
||||
buf_hm.allocate(context, extra->size_hm);
|
||||
buf_sc.allocate(context, extra->size_sc);
|
||||
buf_d.allocate(context, extra->size_d);
|
||||
qs = buf_qs.buffer;
|
||||
hm = buf_hm.buffer;
|
||||
sc = buf_sc.buffer;
|
||||
d = buf_d.buffer;
|
||||
const int m = tensor->ne[1];
|
||||
const int k = tensor->ne[0];
|
||||
transpose_2d_as_8b(backend_ctx, extra->qs, qs, extra->size_qs, m, k/4, true, true);
|
||||
transpose_2d_as_8b(backend_ctx, extra->hm, hm, extra->size_hm, m, k/8, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_32_buf, extra->sc, sc, extra->size_sc, m, 3*k/(int)blck, true, true);
|
||||
transpose_2d(backend_ctx, backend_ctx->kernel_transpose_16_buf, extra->d, d, extra->size_d, m, k/(int)blck, true, true);
|
||||
}
|
||||
#endif
|
||||
cl_kernel kernel = extra->noshuffle ? backend_ctx->kernel_restore_block_q3_k_noshuffle
|
||||
: backend_ctx->kernel_restore_block_q3_K;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &buf_unpacked.buffer));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &nb_arg));
|
||||
|
||||
size_t gws[] = { (n_blk + 63) / 64 * 64 };
|
||||
size_t lws[] = { 64 };
|
||||
cl_event evt;
|
||||
CL_CHECK(clEnqueueNDRangeKernel(queue, kernel, 1, NULL, gws, lws, 0, NULL, &evt));
|
||||
CL_CHECK(clWaitForEvents(1, &evt));
|
||||
CL_CHECK(clReleaseEvent(evt));
|
||||
CL_CHECK(clEnqueueReadBuffer(queue, buf_unpacked.buffer, CL_TRUE, offset, size, data, 0, NULL, NULL));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tensor->type == GGML_TYPE_Q1_0) {
|
||||
ggml_tensor_extra_cl_q1_0 * extra = (ggml_tensor_extra_cl_q1_0 *)tensor->extra;
|
||||
|
||||
@@ -23112,6 +23526,48 @@ static void ggml_cl_mul_mat_q4_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
region.size = K * N * sizeof(float);
|
||||
CL_CHECK((b_sub_buf = clCreateSubBuffer(extra1->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err), err));
|
||||
|
||||
// Only the tiled kernels can read the tiled layout (use_q4k_tiled).
|
||||
if (use_q4k_tiled(backend_ctx, src0)) {
|
||||
const size_t n_blocks = (size_t)N * (K / 32);
|
||||
backend_ctx->prealloc_moe_qa.allocate(context, (size_t)N * K * sizeof(cl_char));
|
||||
backend_ctx->prealloc_moe_da.allocate(context, n_blocks * sizeof(cl_half));
|
||||
backend_ctx->prealloc_moe_sa.allocate(context, n_blocks * sizeof(cl_half));
|
||||
|
||||
cl_int tbq = (cl_int)n_blocks;
|
||||
cl_kernel qk = backend_ctx->kernel_quant_a_q8_1;
|
||||
CL_CHECK(clSetKernelArg(qk, 0, sizeof(cl_mem), &b_sub_buf));
|
||||
CL_CHECK(clSetKernelArg(qk, 1, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(qk, 2, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(qk, 3, sizeof(cl_mem), &backend_ctx->prealloc_moe_sa.buffer));
|
||||
CL_CHECK(clSetKernelArg(qk, 4, sizeof(cl_int), &tbq));
|
||||
size_t q_local[1] = { 64 };
|
||||
size_t q_global[1] = { (size_t)(((n_blocks + 63) / 64) * 64) };
|
||||
backend_ctx->enqueue_ndrange_kernel(qk, 1, q_global, q_local, dst);
|
||||
|
||||
cl_kernel dk = backend_ctx->kernel_gemm_noshuffle_q4_K_f32_tiled_dp4a;
|
||||
CL_CHECK(clSetKernelArg(dk, 0, sizeof(cl_mem), &extra0_q4_k->q));
|
||||
CL_CHECK(clSetKernelArg(dk, 1, sizeof(cl_mem), &extra0_q4_k->d));
|
||||
CL_CHECK(clSetKernelArg(dk, 2, sizeof(cl_mem), &extra0_q4_k->dm));
|
||||
CL_CHECK(clSetKernelArg(dk, 3, sizeof(cl_mem), &extra0_q4_k->s));
|
||||
CL_CHECK(clSetKernelArg(dk, 4, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(dk, 5, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(dk, 6, sizeof(cl_mem), &backend_ctx->prealloc_moe_sa.buffer));
|
||||
CL_CHECK(clSetKernelArg(dk, 7, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(dk, 8, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(dk, 9, sizeof(cl_int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(dk, 10, sizeof(cl_int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(dk, 11, sizeof(cl_int), &ne1));
|
||||
|
||||
const int WROWS_D = 4 * 64;
|
||||
const int BN_DP = 8;
|
||||
size_t dlocal[3] = {64, 4, 1};
|
||||
size_t dglobal[3] = {(size_t)CEIL_DIV(ne01, WROWS_D) * 64, 4, (size_t)CEIL_DIV(ne1, BN_DP)};
|
||||
backend_ctx->enqueue_ndrange_kernel(dk, 3, dglobal, dlocal, dst);
|
||||
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
return;
|
||||
}
|
||||
|
||||
// CoopK handles without _bin use the same untiled weight layout as gemm_noshuffle_q4_k_f32.
|
||||
// Activations use K-major Q8_1 SoA (K contiguous).
|
||||
if (ggml_cl_coopk_dp4a_narrow_on(backend_ctx, dst, backend_ctx->rows_coopk_dp4a_q4_k)
|
||||
@@ -24905,6 +25361,65 @@ static bool ggml_cl_mul_mat_q2_K_dp4a(ggml_backend_t backend, const ggml_tensor
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ggml_cl_mul_mat_q3_K_dp4a(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra0 = (ggml_tensor_extra_cl_q3_K *)src0->extra;
|
||||
ggml_tensor_extra_cl * extra1 = (ggml_tensor_extra_cl *)src1->extra;
|
||||
ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *)dst->extra;
|
||||
|
||||
cl_ulong offset1 = extra1->offset + src1->view_offs;
|
||||
cl_ulong offsetd = extrad->offset + dst->view_offs;
|
||||
const int m = src0->ne[1];
|
||||
const int n = src1->ne[1];
|
||||
const int k = src0->ne[0];
|
||||
|
||||
const int tile_n = n <= 16 ? 8 : 32;
|
||||
cl_kernel gemm = n <= 16 ? backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a_narrow
|
||||
: backend_ctx->kernel_gemm_noshuffle_q3_k_q8_1_dp4a;
|
||||
const char * env = getenv("GGML_OPENCL_Q3_K_DENSE_DP4A");
|
||||
if (!gemm || !backend_ctx->has_integer_dot || (env && env[0] == '0') ||
|
||||
n <= 1 || !ggml_is_contiguous(src1) || offset1 % backend_ctx->alignment != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cl_int err;
|
||||
cl_buffer_region region = { (size_t)offset1, (size_t)k*n*sizeof(float) };
|
||||
cl_mem activation = clCreateSubBuffer(extra1->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
const size_t n_blocks = (size_t)n*(k/32);
|
||||
backend_ctx->prealloc_moe_qa.allocate(backend_ctx->context, (size_t)n*k);
|
||||
backend_ctx->prealloc_moe_da.allocate(backend_ctx->context, n_blocks*sizeof(cl_half));
|
||||
backend_ctx->prealloc_moe_sa.allocate(backend_ctx->context, n_blocks*sizeof(cl_half));
|
||||
|
||||
cl_int nb = n_blocks;
|
||||
cl_kernel quant = backend_ctx->kernel_quant_a_q8_1;
|
||||
CL_CHECK(clSetKernelArg(quant, 0, sizeof(cl_mem), &activation));
|
||||
CL_CHECK(clSetKernelArg(quant, 1, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 2, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 3, sizeof(cl_mem), &backend_ctx->prealloc_moe_sa.buffer));
|
||||
CL_CHECK(clSetKernelArg(quant, 4, sizeof(cl_int), &nb));
|
||||
size_t q_global[] = { CEIL_DIV(n_blocks, 64)*64 };
|
||||
size_t q_local[] = { 64 };
|
||||
backend_ctx->enqueue_ndrange_kernel(quant, 1, q_global, q_local, dst);
|
||||
|
||||
CL_CHECK(clSetKernelArg(gemm, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(gemm, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(gemm, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(gemm, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(gemm, 4, sizeof(cl_mem), &backend_ctx->prealloc_moe_qa.buffer));
|
||||
CL_CHECK(clSetKernelArg(gemm, 5, sizeof(cl_mem), &backend_ctx->prealloc_moe_da.buffer));
|
||||
CL_CHECK(clSetKernelArg(gemm, 6, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemm, 7, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(gemm, 8, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(gemm, 9, sizeof(int), &n));
|
||||
CL_CHECK(clSetKernelArg(gemm, 10, sizeof(int), &k));
|
||||
size_t global[] = { 64, (size_t)CEIL_DIV(m, 64), (size_t)CEIL_DIV(n, tile_n) };
|
||||
size_t local[] = { 64, 1, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(gemm, 3, global, local, dst);
|
||||
CL_CHECK(clReleaseMemObject(activation));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ggml_cl_mul_mat_q2_k_f32_adreno(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q2_K * extra0 = (ggml_tensor_extra_cl_q2_K *)src0->extra;
|
||||
@@ -25004,6 +25519,108 @@ static void ggml_cl_mul_mat_q2_k_f32_adreno(ggml_backend_t backend, const ggml_t
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
}
|
||||
|
||||
static void ggml_cl_mul_mat_q3_k_f32_adreno(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *)backend->context;
|
||||
ggml_tensor_extra_cl_q3_K * extra0 = (ggml_tensor_extra_cl_q3_K *)src0->extra;
|
||||
ggml_tensor_extra_cl * extra1 = (ggml_tensor_extra_cl *)src1->extra;
|
||||
ggml_tensor_extra_cl * extrad = (ggml_tensor_extra_cl *)dst->extra;
|
||||
|
||||
cl_ulong offset1 = extra1->offset + src1->view_offs;
|
||||
cl_ulong offsetd = extrad->offset + dst->view_offs;
|
||||
const int m = src0->ne[1];
|
||||
const int n = src1->ne[1];
|
||||
const int k = src0->ne[0];
|
||||
|
||||
if (n == 1 || !ggml_is_contiguous(src1) || offset1 % backend_ctx->alignment != 0) {
|
||||
cl_kernel gemv = backend_ctx->kernel_gemv_noshuffle_q3_k_f32;
|
||||
const int stride_d = dst->ne[0];
|
||||
const int stride_b = src1->nb[1]/sizeof(float);
|
||||
CL_CHECK(clSetKernelArg(gemv, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(gemv, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(gemv, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(gemv, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(gemv, 4, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemv, 5, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(gemv, 6, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(gemv, 7, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(gemv, 8, sizeof(int), &k));
|
||||
CL_CHECK(clSetKernelArg(gemv, 9, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(gemv, 10, sizeof(int), &stride_b));
|
||||
CL_CHECK(clSetKernelArg(gemv, 11, sizeof(int), &stride_d));
|
||||
const size_t rows = 64*(size_t)backend_ctx->q3_k_mv_rows;
|
||||
const size_t nsg = backend_ctx->q3_k_mv_nsg;
|
||||
size_t global[] = { CEIL_DIV((size_t)m, rows)*64, (size_t)n*nsg, 1 };
|
||||
size_t local[] = { 64, nsg, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(gemv, 3, global, local, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ggml_cl_mul_mat_q3_K_dp4a(backend, src0, src1, dst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cl_context context = backend_ctx->context;
|
||||
cl_int err;
|
||||
cl_buffer_region region = { (size_t)offset1, (size_t)k*n*sizeof(float) };
|
||||
cl_mem b_sub_buf = clCreateSubBuffer(extra1->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
cl_image_format img_fmt = { CL_RGBA, CL_FLOAT };
|
||||
cl_image_desc img_desc = {};
|
||||
img_desc.image_type = CL_MEM_OBJECT_IMAGE1D_BUFFER;
|
||||
img_desc.image_width = (size_t)k*n/4;
|
||||
img_desc.buffer = b_sub_buf;
|
||||
cl_mem b_img = clCreateImage(context, CL_MEM_READ_ONLY, &img_fmt, &img_desc, nullptr, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const int padded_n = CEIL_DIV(n, 8)*8;
|
||||
region.origin = 0;
|
||||
region.size = (size_t)k*padded_n*sizeof(cl_half);
|
||||
backend_ctx->prealloc_act_trans.allocate(context, region.size);
|
||||
cl_mem b_sub_buf_trans = clCreateSubBuffer(backend_ctx->prealloc_act_trans.buffer, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
img_fmt = { CL_RGBA, CL_HALF_FLOAT };
|
||||
img_desc.image_width = (size_t)k*padded_n/4;
|
||||
img_desc.buffer = b_sub_buf_trans;
|
||||
cl_mem b_img_trans = clCreateImage(context, 0, &img_fmt, &img_desc, nullptr, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const int height_b = std::max(n/4, 1);
|
||||
const int width_b = k/4;
|
||||
const int padded_height_b = padded_n/4;
|
||||
cl_kernel kernel = backend_ctx->kernel_transpose_32_16;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &b_img));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &b_img_trans));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(int), &height_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(int), &width_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &padded_height_b));
|
||||
size_t t_global[] = { (size_t)width_b, (size_t)padded_height_b };
|
||||
size_t t_local[] = { 1, 16 };
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 2, t_global, t_local, dst);
|
||||
|
||||
kernel = backend_ctx->kernel_gemm_noshuffle_q3_k_f32;
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &b_img_trans));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &m));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(int), &padded_n));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &k));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &n));
|
||||
size_t global[] = { (size_t)CEIL_DIV(n, 8), (size_t)CEIL_DIV(m, 512)*128, 1 };
|
||||
size_t local[] = { 1, 128, 1 };
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global, local, dst);
|
||||
|
||||
CL_CHECK(clReleaseMemObject(b_img_trans));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf_trans));
|
||||
CL_CHECK(clReleaseMemObject(b_img));
|
||||
CL_CHECK(clReleaseMemObject(b_sub_buf));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) {
|
||||
@@ -25075,6 +25692,7 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
ggml_tensor_extra_cl_q8_0 * extra0_q8_0 = (ggml_tensor_extra_cl_q8_0 *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_iq4_nl * extra0_iq4_nl = (ggml_tensor_extra_cl_iq4_nl *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q2_K * extra0_q2_K = (ggml_tensor_extra_cl_q2_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q3_K * extra0_q3_K = (ggml_tensor_extra_cl_q3_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q4_K * extra0_q4_K = (ggml_tensor_extra_cl_q4_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q5_K * extra0_q5_K = (ggml_tensor_extra_cl_q5_K *)soa0_src->extra;
|
||||
ggml_tensor_extra_cl_q6_K * extra0_q6_K = (ggml_tensor_extra_cl_q6_K *)soa0_src->extra;
|
||||
@@ -25476,6 +26094,12 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
return;
|
||||
}
|
||||
|
||||
// q3_k x fp32
|
||||
if (src0t == GGML_TYPE_Q3_K && src1t == GGML_TYPE_F32 && extra0_q3_K->noshuffle) {
|
||||
ggml_cl_mul_mat_q3_k_f32_adreno(backend, src0, src1, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
// q4_k x fp32
|
||||
if (src0t == GGML_TYPE_Q4_K && src1t == GGML_TYPE_F32 && !use_flat_gemv_for_large_m_q4_K(backend_ctx, src0)) {
|
||||
ggml_cl_mul_mat_q4_k_f32_adreno(backend, src0, src1, dst);
|
||||
@@ -26026,6 +26650,51 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
return;
|
||||
}
|
||||
case GGML_TYPE_Q3_K: {
|
||||
if (ne11 < 32) {
|
||||
break;
|
||||
}
|
||||
if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
kernel = backend_ctx->kernel_mul_mm_q3_k_f32_l4_lm;
|
||||
nth0 = (backend_ctx->gpu_family == INTEL) ? 64 : 128;
|
||||
offset0 = src0->view_offs;
|
||||
|
||||
int batch_stride_a = ne00*ne01;
|
||||
int batch_stride_b = ne10*ne11;
|
||||
int batch_stride_d = ne0*ne1;
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0_q3_K->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0_q3_K->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0_q3_K->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0_q3_K->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_ulong), &offset0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(int), &ne02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(int), &ne11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(int), &ne10));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &batch_stride_a));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &batch_stride_b));
|
||||
CL_CHECK(clSetKernelArg(kernel, 19, sizeof(int), &batch_stride_d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 20, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 21, sizeof(int), &r3));
|
||||
|
||||
size_t global_work_size[] = {(size_t)(CEIL_DIV(ne01, 64)*nth0), (size_t)CEIL_DIV(ne11, 64), (size_t)ne12*ne13};
|
||||
size_t local_work_size[] = {(size_t)nth0, 1, 1};
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
return;
|
||||
}
|
||||
case GGML_TYPE_Q4_K: {
|
||||
if (ne11 < 32) {
|
||||
break;
|
||||
@@ -27005,7 +27674,70 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
#endif // GGML_OPENCL_SOA_Q
|
||||
break;
|
||||
}
|
||||
case GGML_TYPE_Q3_K:
|
||||
case GGML_TYPE_Q3_K: {
|
||||
if (backend_ctx->gpu_family == INTEL) {
|
||||
nth0 = 16;
|
||||
nth1 = 1;
|
||||
ndst = 2;
|
||||
} else if (backend_ctx->gpu_family == ADRENO) {
|
||||
nth0 = 64;
|
||||
nth1 = 1;
|
||||
ndst = 2;
|
||||
} else {
|
||||
GGML_ASSERT(false && "TODO: Unknown GPU");
|
||||
}
|
||||
|
||||
#ifdef GGML_OPENCL_SOA_Q
|
||||
kernel = backend_ctx->kernel_mul_mv_q3_K_f32_flat;
|
||||
offset0 = src0->view_offs;
|
||||
|
||||
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra0_q3_K->qs));
|
||||
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &extra0_q3_K->hm));
|
||||
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra0_q3_K->sc));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &extra0_q3_K->d));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_ulong), &offset0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_mem), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_ulong), &nb01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &nb02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_ulong), &nb03));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(cl_ulong), &nb11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(cl_ulong), &nb12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(cl_ulong), &nb13));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 19, sizeof(int), &ne1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 20, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 21, sizeof(int), &r3));
|
||||
#else
|
||||
kernel = backend_ctx->kernel_mul_mv_q3_K_f32;
|
||||
|
||||
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), &extra1->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offset1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &ne00));
|
||||
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &ne01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &nb01));
|
||||
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &nb02));
|
||||
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &nb03));
|
||||
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(int), &ne12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &nb11));
|
||||
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_ulong), &nb12));
|
||||
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(cl_ulong), &nb13));
|
||||
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(int), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel, 16, sizeof(int), &ne1));
|
||||
CL_CHECK(clSetKernelArg(kernel, 17, sizeof(int), &r2));
|
||||
CL_CHECK(clSetKernelArg(kernel, 18, sizeof(int), &r3));
|
||||
#endif // GGML_OPENCL_SOA_Q
|
||||
break;
|
||||
}
|
||||
case GGML_TYPE_Q4_K: {
|
||||
#ifdef GGML_OPENCL_SOA_Q
|
||||
kernel = backend_ctx->kernel_mul_mv_q4_K_f32_flat;
|
||||
@@ -27313,7 +28045,7 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
src0t == GGML_TYPE_Q8_0 ||
|
||||
src0t == GGML_TYPE_Q1_0 ||
|
||||
src0t == GGML_TYPE_IQ4_NL ||
|
||||
src0t == GGML_TYPE_Q2_K) {
|
||||
src0t == GGML_TYPE_Q2_K || src0t == GGML_TYPE_Q3_K) {
|
||||
// Each SIMD group produces N_DST values in the result. Assuming each
|
||||
// workgroup has N_SIMDGROUP SIMD groups, then each workgroup will
|
||||
// produce N_DST*N_SIMDGROUP values in the result. Hence, the grid size
|
||||
@@ -27329,8 +28061,6 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
size_t local_work_size[] = {(size_t)nth0, (size_t)nth1, 1};
|
||||
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel, 3, global_work_size, local_work_size, dst);
|
||||
} else if (src0t == GGML_TYPE_Q3_K) {
|
||||
GGML_ASSERT(false && "not implemented");
|
||||
} else if (src0t == GGML_TYPE_Q5_K) {
|
||||
size_t global_work_size[] = {(size_t)(ne01+ndst*nth1-1)/(ndst*nth1)*nth0, (size_t)ne11*nth1, (size_t)ne12*ne13};
|
||||
size_t local_work_size[] = {(size_t)nth0, (size_t)nth1, 1};
|
||||
@@ -27630,35 +28360,21 @@ static void ggml_cl_mul_mat_id(ggml_backend_t backend, const ggml_tensor * src0,
|
||||
|
||||
// the matmul kernels only write the rows an expert owns, so zero the skipped rows here
|
||||
{
|
||||
cl_int err;
|
||||
|
||||
cl_buffer_region region;
|
||||
region.origin = offset2;
|
||||
region.size = nb21 * ne21;
|
||||
cl_mem buf_ids = clCreateSubBuffer(extra2->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
region.origin = offsetd;
|
||||
region.size = (size_t) ne0 * ne1 * ne2 * sizeof(float);
|
||||
cl_mem buf_dst = clCreateSubBuffer(extrad->data_device, 0, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err);
|
||||
CL_CHECK(err);
|
||||
|
||||
const cl_uint ids_stride = nb21 / sizeof(int);
|
||||
|
||||
const cl_ulong nb1 = dst->nb[1];
|
||||
const cl_ulong nb2 = dst->nb[2];
|
||||
cl_kernel kernel_zero = backend_ctx->kernel_moe_zero_dst;
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 0, sizeof(cl_mem), &buf_ids));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 1, sizeof(cl_mem), &buf_dst));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 2, sizeof(cl_uint), &ne21));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 3, sizeof(cl_uint), &ne20));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 4, sizeof(cl_uint), &ids_stride));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 5, sizeof(cl_uint), &ne0));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 0, sizeof(cl_mem), &extra2->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 1, sizeof(cl_ulong), &offset2));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 2, sizeof(cl_ulong), &nb21));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 3, sizeof(cl_mem), &extrad->data_device));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 4, sizeof(cl_ulong), &offsetd));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 5, sizeof(cl_ulong), &nb1));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 6, sizeof(cl_ulong), &nb2));
|
||||
CL_CHECK(clSetKernelArg(kernel_zero, 7, sizeof(int), &ne0));
|
||||
|
||||
size_t zero_local[3] = {1, 1, 64};
|
||||
size_t zero_global[3] = {(size_t) ne21, (size_t) ne20, 64};
|
||||
size_t zero_global[3] = {(size_t) ne20, (size_t) ne21, 64};
|
||||
backend_ctx->enqueue_ndrange_kernel(kernel_zero, 3, zero_global, zero_local, dst);
|
||||
|
||||
CL_CHECK(clReleaseMemObject(buf_ids));
|
||||
CL_CHECK(clReleaseMemObject(buf_dst));
|
||||
}
|
||||
|
||||
int sgs = 32; // subgroup size
|
||||
|
||||
@@ -2773,3 +2773,156 @@ kernel void kernel_restore_block_q2_k_noshuffle(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct block_q3_K {
|
||||
uchar hmask[QK_K/8];
|
||||
uchar qs[QK_K/4];
|
||||
uchar scales[12];
|
||||
half d;
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_q3_K(
|
||||
global struct block_q3_K * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_hm,
|
||||
global uchar * dst_sc,
|
||||
global half * dst_d,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = src0 + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst_qs[64*i + j] = b->qs[j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst_hm[32*i + j] = b->hmask[j];
|
||||
}
|
||||
for (int j = 0; j < 12; ++j) {
|
||||
dst_sc[12*i + j] = b->scales[j];
|
||||
}
|
||||
dst_d[i] = b->d;
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q3_K(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_hm,
|
||||
global uchar * src_sc,
|
||||
global half * src_d,
|
||||
global struct block_q3_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = dst + i;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
b->qs[j] = src_qs[64*i + j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
b->hmask[j] = src_hm[32*i + j];
|
||||
}
|
||||
for (int j = 0; j < 12; ++j) {
|
||||
b->scales[j] = src_sc[12*i + j];
|
||||
}
|
||||
b->d = src_d[i];
|
||||
}
|
||||
|
||||
kernel void kernel_convert_block_q3_k_noshuffle(
|
||||
global struct block_q3_K * src0,
|
||||
global uchar * dst_qs, // QK_K/4 per block
|
||||
global uchar * dst_hm, // QK_K/8 per block
|
||||
global uint * dst_sc, // 3 per block
|
||||
global half * dst_d, // 1
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = (global struct block_q3_K *) src0 + i;
|
||||
|
||||
dst_d[i] = b->d;
|
||||
|
||||
for (int w = 0; w < 3; ++w) {
|
||||
global const uchar * p = b->scales + 4*w;
|
||||
dst_sc[3*i + w] = (uint)p[0] | ((uint)p[1] << 8) | ((uint)p[2] << 16) | ((uint)p[3] << 24);
|
||||
}
|
||||
|
||||
for (int g = 0; g < QK_K/4; ++g) {
|
||||
uint pk = 0;
|
||||
for (int t = 0; t < 4; ++t) {
|
||||
const int e = 4*g + t;
|
||||
const int sb = e >> 5;
|
||||
const int ee = e & 31;
|
||||
pk |= (uint)(((b->qs[32*(sb >> 2) + ee] >> (2*(sb & 3))) & 3) << (2*t));
|
||||
}
|
||||
dst_qs[(QK_K/4) * i + g] = (uchar)pk;
|
||||
}
|
||||
|
||||
// two groups per hmask byte, so this loop owns the byte and cannot race
|
||||
for (int t = 0; t < QK_K/8; ++t) {
|
||||
uint v = 0;
|
||||
for (int s = 0; s < 2; ++s) {
|
||||
const int g = 2*t + s;
|
||||
for (int u = 0; u < 4; ++u) {
|
||||
const int e = 4*g + u;
|
||||
const int sb = e >> 5;
|
||||
const int ee = e & 31;
|
||||
v |= (uint)(((b->hmask[ee] >> sb) & 1) << (4*s + u));
|
||||
}
|
||||
}
|
||||
dst_hm[(QK_K/8) * i + t] = (uchar)v;
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_q3_k_noshuffle(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_hm,
|
||||
global uint * src_sc,
|
||||
global half * src_d,
|
||||
global struct block_q3_K * dst,
|
||||
ulong n_blk
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blk) {
|
||||
return;
|
||||
}
|
||||
global struct block_q3_K * b = (global struct block_q3_K *) dst + i;
|
||||
|
||||
b->d = src_d[i];
|
||||
|
||||
for (int w = 0; w < 3; ++w) {
|
||||
const uint x = src_sc[3*i + w];
|
||||
global uchar * p = b->scales + 4*w;
|
||||
p[0] = (uchar)( x & 0xFF);
|
||||
p[1] = (uchar)((x >> 8) & 0xFF);
|
||||
p[2] = (uchar)((x >> 16) & 0xFF);
|
||||
p[3] = (uchar)((x >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
for (int n2 = 0; n2 < 2; ++n2) {
|
||||
for (int ee = 0; ee < 32; ++ee) {
|
||||
uint v = 0;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
const int sb = 4*n2 + j;
|
||||
const int e = 32*sb + ee;
|
||||
v |= ((uint)(src_qs[(QK_K/4) * i + (e >> 2)] >> (2*(e & 3))) & 3u) << (2*j);
|
||||
}
|
||||
b->qs[32*n2 + ee] = (uchar)v;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ee = 0; ee < 32; ++ee) {
|
||||
uint v = 0;
|
||||
for (int sb = 0; sb < 8; ++sb) {
|
||||
const int e = 32*sb + ee;
|
||||
const int g = e >> 2;
|
||||
v |= ((uint)(src_hm[(QK_K/8) * i + (g >> 1)] >> (4*(g & 1) + (e & 3))) & 1u) << sb;
|
||||
}
|
||||
b->hmask[ee] = (uchar)v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_q3_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_hm,
|
||||
global const uint * src0_sc,
|
||||
global const half * src0_d,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const int is = (i / 16) & 15;
|
||||
const int word = is >> 2;
|
||||
const int shift = 8 * (is & 3);
|
||||
const int sc_base = row + 3 * (i / 256) * m;
|
||||
const uint4 low = vload4(0, src0_sc + sc_base + (word & 1) * m) >> (uint4)(4 * (word >> 1));
|
||||
const uint4 high = vload4(0, src0_sc + sc_base + 2 * m) >> (uint4)(2 * word);
|
||||
const int4 sc = convert_int4(((low >> (uint4)(shift)) & (uint4)(15)) | (((high >> (uint4)(shift)) & (uint4)(3)) << (uint4)(4))) - (int4)(32);
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const half4 scale = convert_half4(convert_float4(d) * convert_float4(sc));
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uchar4 bits = vload4(0, src0_qs + row + (ki / 4) * m);
|
||||
const uchar4 hbits = vload4(0, src0_hm + row + (ki / 8) * m);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const int4 low = convert_int4((bits >> (uchar4)(2 * j)) & (uchar4)(3));
|
||||
const int4 high = convert_int4((hbits >> (uchar4)((ki & 7) + j)) & (uchar4)(1));
|
||||
const half4 w = convert_half4(low - (int4)(4) + (int4)(4) * high) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline int q3_k_scale(uint sc0, uint sc1, uint sc2, uint is) {
|
||||
const uint w = is >> 2;
|
||||
const uint sh = 8u * (is & 3u);
|
||||
const uint tb = (sc2 >> sh) & 0xFFu;
|
||||
uint v;
|
||||
if (w == 0u) { v = ( (sc0 >> sh) & 0xFu) | (((tb >> 0) & 3u) << 4); }
|
||||
else if (w == 1u) { v = ( (sc1 >> sh) & 0xFu) | (((tb >> 2) & 3u) << 4); }
|
||||
else if (w == 2u) { v = (((sc0 >> sh) >> 4) & 0xFu) | (((tb >> 4) & 3u) << 4); }
|
||||
else { v = (((sc1 >> sh) >> 4) & 0xFu) | (((tb >> 6) & 3u) << 4); }
|
||||
return (int)v - 32;
|
||||
}
|
||||
|
||||
inline uint q3_k_pack(uint pk, uint hb) {
|
||||
int v0 = (int)((pk ) & 3u) - 4 + 4*(int)((hb ) & 1u);
|
||||
int v1 = (int)((pk >> 2) & 3u) - 4 + 4*(int)((hb >> 1) & 1u);
|
||||
int v2 = (int)((pk >> 4) & 3u) - 4 + 4*(int)((hb >> 2) & 1u);
|
||||
int v3 = (int)((pk >> 6) & 3u) - 4 + 4*(int)((hb >> 3) & 1u);
|
||||
return ((uint)v0 & 0xFFu) | (((uint)v1 & 0xFFu) << 8)
|
||||
| (((uint)v2 & 0xFFu) << 16) | (((uint)v3 & 0xFFu) << 24);
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
#define KQ_STAGE(p) vload4(0, (p))
|
||||
|
||||
inline int dot4_q8a_v(uint4 qw, uint4 a) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_q3_k_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uchar * src0_hm,
|
||||
__global const uint * src0_sc,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa, // q8_1 activations int8 (as uint, 4/elem) [N, K]
|
||||
__global const half * src1_da, // q8_1 per-block scale [N, K/32]
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m, // output features (rows)
|
||||
int n_no_padding, // tokens (cols)
|
||||
int k // K (== ne00)
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
const uint ib = sub >> 3; // super-block along K
|
||||
const uint sb = sub & 7u;
|
||||
|
||||
const uint scb = rrow + (3u * ib) * (uint)m;
|
||||
const uint sc0 = src0_sc[scb + 0u * (uint)m];
|
||||
const uint sc1 = src0_sc[scb + 1u * (uint)m];
|
||||
const uint sc2 = src0_sc[scb + 2u * (uint)m];
|
||||
|
||||
const float d_w = (float)src0_d[rrow + ib * (uint)m];
|
||||
const float dl0 = d_w * (float)q3_k_scale(sc0, sc1, sc2, 2u*sb + 0u);
|
||||
const float dl1 = d_w * (float)q3_k_scale(sc0, sc1, sc2, 2u*sb + 1u);
|
||||
|
||||
const uint qsb = rrow + (step >> 2) * (uint)m;
|
||||
const uint hmb = rrow + (step >> 3) * (uint)m;
|
||||
|
||||
uint4 qlo, qhi;
|
||||
{
|
||||
const uint h0 = (uint)src0_hm[hmb + 0u * (uint)m];
|
||||
const uint h1 = (uint)src0_hm[hmb + 1u * (uint)m];
|
||||
const uint h2 = (uint)src0_hm[hmb + 2u * (uint)m];
|
||||
const uint h3 = (uint)src0_hm[hmb + 3u * (uint)m];
|
||||
qlo.s0 = q3_k_pack((uint)src0_qs[qsb + 0u * (uint)m], h0 & 0xFu);
|
||||
qlo.s1 = q3_k_pack((uint)src0_qs[qsb + 1u * (uint)m], (h0 >> 4) & 0xFu);
|
||||
qlo.s2 = q3_k_pack((uint)src0_qs[qsb + 2u * (uint)m], h1 & 0xFu);
|
||||
qlo.s3 = q3_k_pack((uint)src0_qs[qsb + 3u * (uint)m], (h1 >> 4) & 0xFu);
|
||||
qhi.s0 = q3_k_pack((uint)src0_qs[qsb + 4u * (uint)m], h2 & 0xFu);
|
||||
qhi.s1 = q3_k_pack((uint)src0_qs[qsb + 5u * (uint)m], (h2 >> 4) & 0xFu);
|
||||
qhi.s2 = q3_k_pack((uint)src0_qs[qsb + 6u * (uint)m], h3 & 0xFu);
|
||||
qhi.s3 = q3_k_pack((uint)src0_qs[qsb + 7u * (uint)m], (h3 >> 4) & 0xFu);
|
||||
}
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? KQ_STAGE(src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define Q3_K_COL(T) (dl0 * (float)dot4_q8a_v(qlo, (uint4)(sh_qa4[T][0])) \
|
||||
+ dl1 * (float)dot4_q8a_v(qhi, (uint4)(sh_qa4[T][1])))
|
||||
rf.s0 = Q3_K_COL(b+0); rf.s1 = Q3_K_COL(b+1);
|
||||
rf.s2 = Q3_K_COL(b+2); rf.s3 = Q3_K_COL(b+3);
|
||||
#undef Q3_K_COL
|
||||
acc[g] += LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// DP4A GEMM for the canonical 64-row Q4_K layout.
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
#define TILE_ROWS 64
|
||||
#define NTILES 4
|
||||
#define BN_DP 8
|
||||
#define WG_THREADS (NTILES * TILE_ROWS)
|
||||
|
||||
inline void q4k_scale_min(int j, __global const uchar * q, uchar * d, uchar * m) {
|
||||
if (j < 4) {
|
||||
*d = q[j] & 0x3F;
|
||||
*m = q[j+4] & 0x3F;
|
||||
} else {
|
||||
*d = (q[j+4] & 0x0F) | ((q[j-4] & 0xC0) >> 2);
|
||||
*m = ((q[j+4] >> 4) & 0x0F) | ((q[j] & 0xC0) >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
// dp4a GEMM over the tiled q4_K layout (ne1 > 1) with a q8_1 activation; per 32-element sub-block:
|
||||
// acc += d*sd*qad*dp4a(q, qa) - dm*sm*qas
|
||||
#if defined(ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_q4_K_f32_tiled_dp4a(
|
||||
__global uint4 * src0_q,
|
||||
__global half * src0_d,
|
||||
__global half * src0_dm,
|
||||
__global uchar * src0_s,
|
||||
__global uint * qa, // q8_1 int8 activation, 4/uint [ne11][ne00/4]
|
||||
__global half * qad, // q8_1 per-32-block scale [ne11][ne00/32]
|
||||
__global half * qas, // q8_1 per-32-block sum (qad*Sum)[ne11][ne00/32]
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne11
|
||||
) {
|
||||
int rit = get_local_id(0);
|
||||
int sg = get_local_id(1);
|
||||
int lid = sg * TILE_ROWS + rit;
|
||||
int row = get_group_id(0) * WG_THREADS + lid;
|
||||
int rt = row / TILE_ROWS;
|
||||
int col0 = get_global_id(2) * BN_DP;
|
||||
int nb = ne00 / QK_K;
|
||||
int qcs = ne00 / 4;
|
||||
int dcs = ne00 / 32;
|
||||
|
||||
const bool row_ok = row < ne01;
|
||||
__local uint sh_qa[BN_DP * 64];
|
||||
__local half sh_qad[BN_DP * 8];
|
||||
__local half sh_qas[BN_DP * 8];
|
||||
|
||||
float acc[BN_DP];
|
||||
#pragma unroll
|
||||
for (int j = 0; j < BN_DP; ++j) acc[j] = 0.0f;
|
||||
|
||||
for (int sb = 0; sb < nb; ++sb) {
|
||||
for (int p = lid; p < BN_DP * 64; p += WG_THREADS) {
|
||||
int j = p >> 6, u = p & 63, c = col0 + j;
|
||||
sh_qa[p] = (c < ne11) ? qa[c * qcs + sb * 64 + u] : 0u;
|
||||
}
|
||||
for (int p = lid; p < BN_DP * 8; p += WG_THREADS) {
|
||||
int j = p >> 3, b = p & 7, c = col0 + j;
|
||||
sh_qad[p] = (c < ne11) ? qad[c * dcs + sb * 8 + b] : (half)0;
|
||||
sh_qas[p] = (c < ne11) ? qas[c * dcs + sb * 8 + b] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (row_ok) {
|
||||
int tile_blk = rt * nb + sb;
|
||||
float dval = (float)src0_d [tile_blk * TILE_ROWS + rit];
|
||||
float dmval = (float)src0_dm[tile_blk * TILE_ROWS + rit];
|
||||
__global uchar * sc = src0_s + (tile_blk * TILE_ROWS + rit) * 12;
|
||||
|
||||
uint q[32];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < 8; ++g) {
|
||||
uint4 v = src0_q[(tile_blk * 8 + g) * TILE_ROWS + rit];
|
||||
q[g*4+0] = v.x; q[g*4+1] = v.y; q[g*4+2] = v.z; q[g*4+3] = v.w;
|
||||
}
|
||||
|
||||
for (int is = 0; is < 8; ++is) {
|
||||
uchar sd, sm;
|
||||
q4k_scale_min(is, sc, &sd, &sm);
|
||||
float scl = dval * (float)sd, mnv = dmval * (float)sm;
|
||||
|
||||
uint cw[8];
|
||||
#pragma unroll
|
||||
for (int t = 0; t < 8; ++t) {
|
||||
uint pk = 0;
|
||||
#pragma unroll
|
||||
for (int u = 0; u < 4; ++u) {
|
||||
int e = is * 32 + t * 4 + u;
|
||||
uint code = (q[e >> 3] >> ((e & 7) * 4)) & 0xF;
|
||||
pk |= (code & 0xFF) << (u * 8);
|
||||
}
|
||||
cw[t] = pk;
|
||||
}
|
||||
#pragma unroll
|
||||
for (int j = 0; j < BN_DP; ++j) {
|
||||
__local uint * a = sh_qa + j * 64 + is * 8;
|
||||
int raw = 0;
|
||||
#pragma unroll
|
||||
for (int t = 0; t < 8; ++t) raw = dot_acc_sat_4x8packed_ss_int(cw[t], a[t], raw);
|
||||
acc[j] += scl * (float)sh_qad[j*8 + is] * (float)raw
|
||||
- mnv * (float)sh_qas[j*8 + is];
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (row_ok) {
|
||||
dst = (global float*)((global char*)dst + offsetd);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < BN_DP; ++j) {
|
||||
int c = col0 + j;
|
||||
if (c < ne11) dst[(ulong)c * ne01 + row] = acc[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef Q3_K_MV_NSG
|
||||
#define Q3_K_MV_NSG 8
|
||||
#endif
|
||||
|
||||
#ifndef Q3_K_MV_R
|
||||
#define Q3_K_MV_R 2
|
||||
#endif
|
||||
|
||||
inline int q3_k_scale(uint sc0, uint sc1, uint sc2, uint is) {
|
||||
const uint w = is >> 2;
|
||||
const uint sh = 8u * (is & 3u);
|
||||
const uint tb = (sc2 >> sh) & 0xFFu;
|
||||
uint v;
|
||||
if (w == 0u) { v = ( (sc0 >> sh) & 0xFu) | (((tb >> 0) & 3u) << 4); }
|
||||
else if (w == 1u) { v = ( (sc1 >> sh) & 0xFu) | (((tb >> 2) & 3u) << 4); }
|
||||
else if (w == 2u) { v = (((sc0 >> sh) >> 4) & 0xFu) | (((tb >> 4) & 3u) << 4); }
|
||||
else { v = (((sc1 >> sh) >> 4) & 0xFu) | (((tb >> 6) & 3u) << 4); }
|
||||
return (int)v - 32;
|
||||
}
|
||||
|
||||
inline float4 q3_k_vals(uint pk, uint hb) {
|
||||
float4 v;
|
||||
v.s0 = (float)((int)((pk ) & 3u) - 4 + 4*(int)((hb ) & 1u));
|
||||
v.s1 = (float)((int)((pk >> 2) & 3u) - 4 + 4*(int)((hb >> 1) & 1u));
|
||||
v.s2 = (float)((int)((pk >> 4) & 3u) - 4 + 4*(int)((hb >> 2) & 1u));
|
||||
v.s3 = (float)((int)((pk >> 6) & 3u) - 4 + 4*(int)((hb >> 3) & 1u));
|
||||
return v;
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_q3_k_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_hm,
|
||||
global const uint * src0_sc,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00, // K
|
||||
int ne01, // M
|
||||
int ne10, // activation row stride
|
||||
int ne0 // dst row stride
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
#if Q3_K_MV_R == 4
|
||||
const uint mq = m >> 2;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 2;
|
||||
|
||||
float sumf = 0.f, sumf1 = 0.f, sumf2 = 0.f, sumf3 = 0.f;
|
||||
|
||||
if (j < mq) {
|
||||
global const uint * qsw = (global const uint *)src0_qs;
|
||||
global const uint * hmw = (global const uint *)src0_hm;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const half4 dh = vload4(j + ib * mq, src0_d);
|
||||
const uint4 s0 = vload4(j + (3u * ib + 0u) * mq, src0_sc);
|
||||
const uint4 s1 = vload4(j + (3u * ib + 1u) * mq, src0_sc);
|
||||
const uint4 s2 = vload4(j + (3u * ib + 2u) * mq, src0_sc);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f, acc2 = 0.f, acc3 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mq;
|
||||
const uint hmb = j + (grp >> 1) * mq;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) { // two 16-weight halves
|
||||
const int l0 = q3_k_scale(s0.s0, s1.s0, s2.s0, 2u*sb + h);
|
||||
const int l1 = q3_k_scale(s0.s1, s1.s1, s2.s1, 2u*sb + h);
|
||||
const int l2 = q3_k_scale(s0.s2, s1.s2, s2.s2, 2u*sb + h);
|
||||
const int l3 = q3_k_scale(s0.s3, s1.s3, s2.s3, 2u*sb + h);
|
||||
|
||||
float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = qsw[qsb + gg * mq]; // four rows, one load
|
||||
const uint hmv = hmw[hmb + (gg >> 1) * mq];
|
||||
const uint hsh = 4u * (gg & 1u);
|
||||
const float4 yv = vload4(grp + gg, y); // shared by all four
|
||||
a0 += dot(yv, q3_k_vals((qsv ) & 0xFFu, ((hmv ) >> hsh) & 0xFu));
|
||||
a1 += dot(yv, q3_k_vals((qsv >> 8) & 0xFFu, ((hmv >> 8) >> hsh) & 0xFu));
|
||||
a2 += dot(yv, q3_k_vals((qsv >> 16) & 0xFFu, ((hmv >> 16) >> hsh) & 0xFu));
|
||||
a3 += dot(yv, q3_k_vals((qsv >> 24) & 0xFFu, ((hmv >> 24) >> hsh) & 0xFu));
|
||||
}
|
||||
acc0 += (float)l0 * a0;
|
||||
acc1 += (float)l1 * a1;
|
||||
acc2 += (float)l2 * a2;
|
||||
acc3 += (float)l3 * a3;
|
||||
}
|
||||
}
|
||||
sumf += (float)dh.s0 * acc0;
|
||||
sumf1 += (float)dh.s1 * acc1;
|
||||
sumf2 += (float)dh.s2 * acc2;
|
||||
sumf3 += (float)dh.s3 * acc3;
|
||||
}
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
global const ushort * hmu = (global const ushort *)src0_hm;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const half2 dh = vload2(j + ib * mh, src0_d);
|
||||
const uint2 s0 = vload2(j + (3u * ib + 0u) * mh, src0_sc);
|
||||
const uint2 s1 = vload2(j + (3u * ib + 1u) * mh, src0_sc);
|
||||
const uint2 s2 = vload2(j + (3u * ib + 2u) * mh, src0_sc);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mh;
|
||||
const uint hmb = j + (grp >> 1) * mh;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) { // two 16-weight halves
|
||||
const int l0 = q3_k_scale(s0.s0, s1.s0, s2.s0, 2u*sb + h);
|
||||
const int l1 = q3_k_scale(s0.s1, s1.s1, s2.s1, 2u*sb + h);
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = (uint)qsu[qsb + gg * mh];
|
||||
const uint hmv = (uint)hmu[hmb + (gg >> 1) * mh];
|
||||
const uint hsh = 4u * (gg & 1u);
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
a0 += dot(yv, q3_k_vals( qsv & 0xFFu, ( hmv >> hsh) & 0xFu));
|
||||
a1 += dot(yv, q3_k_vals((qsv >> 8) & 0xFFu, ((hmv >> 8) >> hsh) & 0xFu));
|
||||
}
|
||||
acc0 += (float)l0 * a0;
|
||||
acc1 += (float)l1 * a1;
|
||||
}
|
||||
}
|
||||
sumf += (float)dh.s0 * acc0;
|
||||
sumf1 += (float)dh.s1 * acc1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
const uint row = get_group_id(0) * 64u + lid;
|
||||
|
||||
float sumf = 0.f;
|
||||
|
||||
if (row < m) {
|
||||
for (uint ib = sgi; ib < nsb; ib += Q3_K_MV_NSG) {
|
||||
const float d = (float)src0_d[row + ib * m];
|
||||
const uint s0 = src0_sc[row + (3u * ib + 0u) * m];
|
||||
const uint s1 = src0_sc[row + (3u * ib + 1u) * m];
|
||||
const uint s2 = src0_sc[row + (3u * ib + 2u) * m];
|
||||
|
||||
float acc = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = row + grp * m;
|
||||
const uint hmb = row + (grp >> 1) * m;
|
||||
|
||||
for (uint h = 0; h < 2u; ++h) {
|
||||
const int l = q3_k_scale(s0, s1, s2, 2u*sb + h);
|
||||
float a = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint gg = 4u*h + u;
|
||||
const uint qsv = (uint)src0_qs[qsb + gg * m];
|
||||
const uint hmv = (uint)src0_hm[hmb + (gg >> 1) * m];
|
||||
const float4 yv = vload4(grp + gg, y);
|
||||
a += dot(yv, q3_k_vals(qsv, (hmv >> (4u * (gg & 1u))) & 0xFu));
|
||||
}
|
||||
acc += (float)l * a;
|
||||
}
|
||||
}
|
||||
sumf += d * acc;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if Q3_K_MV_NSG > 1
|
||||
#if Q3_K_MV_R == 4
|
||||
__local float4 part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float4)(sumf, sumf1, sumf2, sumf3);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
const float4 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
sumf2 += p.s2;
|
||||
sumf3 += p.s3;
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
__local float2 part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#else
|
||||
__local float part[Q3_K_MV_NSG][64];
|
||||
part[sgi][lid] = sumf;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < Q3_K_MV_NSG; ++s) {
|
||||
sumf += part[s][lid];
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if Q3_K_MV_R == 4
|
||||
if (j < mq) {
|
||||
vstore4((float4)(sumf, sumf1, sumf2, sumf3), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#elif Q3_K_MV_R == 2
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#else
|
||||
if (row < m) {
|
||||
dst[(ulong)col * (uint)ne0 + row] = sumf;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -57,13 +57,17 @@ kernel void kernel_add_id_add_id_swiglu_oai(
|
||||
|
||||
global const float * g_row = (global const float *)(src_g + i1*nb01_g + i2*nb02_g);
|
||||
global const float * u_row = (global const float *)(src_u + i1*nb01_u + i2*nb02_u);
|
||||
global const float * gb_row = (global const float *)(src_gb + i11*nb11_g);
|
||||
global const float * ub_row = (global const float *)(src_ub + i11*nb11_u);
|
||||
global float * d_row = (global float *)(dst + i1*nbd1 + i2*nbd2);
|
||||
|
||||
for (int i0 = get_local_id(0); i0 < ne0; i0 += get_local_size(0)) {
|
||||
float x0 = g_row[i0] + gb_row[i0];
|
||||
float x1 = u_row[i0] + ub_row[i0];
|
||||
float x0 = g_row[i0];
|
||||
float x1 = u_row[i0];
|
||||
if (i11 != -1) {
|
||||
global const float * gb_row = (global const float *)(src_gb + i11*nb11_g);
|
||||
global const float * ub_row = (global const float *)(src_ub + i11*nb11_u);
|
||||
x0 += gb_row[i0];
|
||||
x1 += ub_row[i0];
|
||||
}
|
||||
|
||||
x0 = min(x0, limit);
|
||||
x1 = max(min(x1, limit), -limit);
|
||||
|
||||
@@ -42,8 +42,11 @@ __kernel void kernel_moe_combine_bias_f32(
|
||||
float4 acc = (float4)(0.0f);
|
||||
for (int e = 0; e < k; ++e) {
|
||||
const int i11 = *((__global const int *)(I + (ulong)e*sizeof(int)));
|
||||
__global const float * Brow = (__global const float *)(B + (ulong)i11*nb_b1) + r4*4u;
|
||||
const float4 v = vload4(0, E + (uint)e*e1) + vload4(0, Brow);
|
||||
float4 v = vload4(0, E + (uint)e*e1);
|
||||
if (i11 != -1) {
|
||||
__global const float * Brow = (__global const float *)(B + (ulong)i11*nb_b1) + r4*4u;
|
||||
v += vload4(0, Brow);
|
||||
}
|
||||
acc = mad(v, (float4)(W[(uint)e*w1]), acc);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,32 +80,6 @@ __kernel void kernel_moe_scatter(
|
||||
emap[tile_idx] = val;
|
||||
}
|
||||
|
||||
// the gemm only writes the rows an expert owns, so the skipped rows (negative ids) are zeroed here
|
||||
__kernel void kernel_moe_zero_dst(
|
||||
__global const int * input,
|
||||
__global float * dst,
|
||||
uint N,
|
||||
uint topK,
|
||||
uint n_experts,
|
||||
uint ne0
|
||||
) {
|
||||
uint n = get_global_id(0);
|
||||
uint k = get_global_id(1);
|
||||
|
||||
if (n >= N || k >= topK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input[n * n_experts + k] != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
__global float * dst_row = dst + ((ulong)n * topK + k) * ne0;
|
||||
for (uint i = get_local_id(2); i < ne0; i += get_local_size(2)) {
|
||||
dst_row[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic replacement for kernel_moe_scatter.
|
||||
//
|
||||
// kernel_moe_scatter takes each token's slot from atomic_inc(slot_counter[expert]),
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
kernel void kernel_moe_zero_dst(
|
||||
global const char * ids,
|
||||
ulong offset_ids,
|
||||
ulong nb_ids1,
|
||||
global char * dst,
|
||||
ulong offset_dst,
|
||||
ulong nb1,
|
||||
ulong nb2,
|
||||
int ne0
|
||||
) {
|
||||
const size_t i1 = get_group_id(0);
|
||||
const size_t i2 = get_group_id(1);
|
||||
const int expert = *((global const int *)(ids + offset_ids + i1*sizeof(int) + i2*nb_ids1));
|
||||
if (expert != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
global float * row = (global float *)(dst + offset_dst + i1*nb1 + i2*nb2);
|
||||
for (size_t i0 = get_local_id(2); i0 < (size_t)ne0; i0 += get_local_size(2)) {
|
||||
row[i0] = 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_q3_K {
|
||||
uchar hmask[QK_K/8];
|
||||
uchar qs[QK_K/4];
|
||||
uchar scales[12];
|
||||
half d;
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#ifndef BK
|
||||
#define BK 32
|
||||
#endif
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#else
|
||||
#define TM 4
|
||||
#endif
|
||||
#define TN 8
|
||||
|
||||
#ifndef LM_HALF
|
||||
#define LM_HALF 0
|
||||
#endif
|
||||
#if LM_HALF
|
||||
typedef half lm_st;
|
||||
#define LM_LD4(p, i) convert_float4(vload4((i), (p)))
|
||||
#else
|
||||
typedef float lm_st;
|
||||
#define LM_LD4(p, i) vload4((i), (p))
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_q3_k_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_hm,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local lm_st buf_a[BM * BK];
|
||||
local lm_st buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one q3_K super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float4 sums4[(TM/4) * TN];
|
||||
float4 cache_a4[TM/4];
|
||||
|
||||
for (int i = 0; i < (TM/4) * TN; i++) {
|
||||
sums4[i] = (float4)(0.0f);
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_q3_K);
|
||||
int e = idx % QK_K;
|
||||
|
||||
// Only qs advances in the second half of a super-block.
|
||||
int n = e >> 7;
|
||||
int rem = e & 127;
|
||||
int j = rem >> 5;
|
||||
int s = (rem >> 4) & 1;
|
||||
int l16 = rem & 15;
|
||||
int is = 8*n + 2*j + s;
|
||||
int shift = 2*j;
|
||||
uchar m = (uchar)(1u << (4*n + j));
|
||||
|
||||
global ushort * a = (global ushort *)(src0_sc + 12*ib);
|
||||
uint a0 = (uint)a[0] | ((uint)a[1] << 16);
|
||||
uint a1 = (uint)a[2] | ((uint)a[3] << 16);
|
||||
uint tmp = (uint)a[4] | ((uint)a[5] << 16);
|
||||
|
||||
uint g = is >> 2;
|
||||
uint w = (g & 1u) ? a1 : a0;
|
||||
w = ((w >> ((g >> 1)*4)) & 0x0f0f0f0fu) | (((tmp >> (2*g)) & 0x03030303u) << 4);
|
||||
|
||||
uint sc6 = (w >> (8*(is & 3))) & 0xFFu;
|
||||
float dl = (float)src0_d[ib] * ((float)sc6 - 32.0f);
|
||||
|
||||
uchar4 q = vload4(0, src0_qs + 64*ib + 32*n + 16*s + l16);
|
||||
uchar4 hb = vload4(0, src0_hm + 32*ib + 16*s + l16);
|
||||
|
||||
float4 qv = convert_float4((uchar4)((q.s0 >> shift) & 3,
|
||||
(q.s1 >> shift) & 3,
|
||||
(q.s2 >> shift) & 3,
|
||||
(q.s3 >> shift) & 3));
|
||||
float4 hv = (float4)((hb.s0 & m) ? 0.f : 4.f,
|
||||
(hb.s1 & m) ? 0.f : 4.f,
|
||||
(hb.s2 & m) ? 0.f : 4.f,
|
||||
(hb.s3 & m) ? 0.f : 4.f);
|
||||
float4 v1 = (qv - hv) * dl;
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
cache_a4[a] = LM_LD4(buf_a + (i) * BM + th_r * TM, a);
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
const float cache_b = buf_b[(i) * BN + th_c * TN + cc];
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
const int sums_idx = cc*(TM/4) + a;
|
||||
sums4[sums_idx] = mad(cache_a4[a], (float4)cache_b, sums4[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int a = 0; a < TM/4; a++) {
|
||||
const float4 v = sums4[cc * (TM/4) + a];
|
||||
const float vs[4] = { v.s0, v.s1, v.s2, v.s3 };
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (dr + 4*a + k < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + 4*a + k] = vs[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// block_q3_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q
|
||||
typedef struct {
|
||||
uchar hmask[QK_K/8]; // quants - high bit
|
||||
uchar qs[QK_K/4]; // quants - low 2 bits
|
||||
uchar scales[12]; // scales, quantized with 6 bits
|
||||
half d; // super-block scale
|
||||
} block_q3_K;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 2 // number of rows each SIMD group works on
|
||||
#define N_SIMDGROUP 1 // number of SIMD groups in a thread group
|
||||
#define N_SIMDWIDTH 16 // SIMD group size
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 2
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 8 threads cover one super block, so a wave covers N_SIMDWIDTH/8 of them
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_q3_K_f32(
|
||||
global char * src0,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src0 = src0 + offset0;
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03;
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global block_q3_K * x = (global block_q3_K *) (src0 + offset_src0);
|
||||
global float * yy = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
|
||||
// Eight lanes cover one super-block.
|
||||
int ix = get_sub_group_local_id() % BLOCK_STRIDE;
|
||||
int tid = get_sub_group_local_id() / BLOCK_STRIDE;
|
||||
|
||||
int ip = tid/4; // 0 or 1
|
||||
int il = 2*((tid%4)/2); // 0 or 2
|
||||
int ir = tid%2;
|
||||
int l0 = 8*ir;
|
||||
|
||||
// Shift the masks instead of indexing private arrays to avoid spills.
|
||||
ushort4 hm = (ushort4)(0x0001, 0x0100, 0x0002, 0x0200) << (ushort4)(2*(2*ip + il/2));
|
||||
int4 qm = (int4) (0x0003, 0x0300, 0x000c, 0x0c00) << (int4) (4*(il/2));
|
||||
|
||||
int shift = 2*il;
|
||||
|
||||
float v1 = il == 0 ? 4.f : 64.f;
|
||||
float v2 = 4.f * v1;
|
||||
|
||||
int s_shift1 = 4*ip;
|
||||
int s_shift2 = s_shift1 + il;
|
||||
|
||||
int q_offset = 32*ip + l0;
|
||||
int y_offset = 128*ip + 32*il + l0;
|
||||
|
||||
global float * y1 = yy + ix*QK_K + y_offset;
|
||||
|
||||
float sumf1[N_DST] = {0.f};
|
||||
float sumf2[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
for (int i = ix; i < nb; i += BLOCK_STRIDE) {
|
||||
for (int l = 0; l < 8; ++l) {
|
||||
yl[l+ 0] = y1[l+ 0];
|
||||
yl[l+ 8] = y1[l+16];
|
||||
yl[l+16] = y1[l+32];
|
||||
yl[l+24] = y1[l+48];
|
||||
}
|
||||
|
||||
global ushort * q = (global ushort *)(x[i].qs + q_offset);
|
||||
global ushort * h = (global ushort *)(x[i].hmask + l0);
|
||||
global ushort * a = (global ushort *)(x[i].scales);
|
||||
global half * dh = &x[i].d;
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; ++row) {
|
||||
float d_all = dh[0];
|
||||
|
||||
uint s32 = (uint)a[4] | ((uint)a[5] << 16);
|
||||
uint aux32 = ((s32 >> s_shift2) << 4) & 0x30303030u;
|
||||
s32 = (uint)a[il+0] | ((uint)a[il+1] << 16);
|
||||
s32 = ((s32 >> s_shift1) & 0x0f0f0f0fu) | aux32;
|
||||
char4 sc = as_char4(s32);
|
||||
|
||||
float s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2];
|
||||
s1 += yl[l+0] * (qs & qm.s0);
|
||||
s2 += yl[l+1] * (qs & qm.s1);
|
||||
s3 += ((h[l/2] & hm.s0) ? 0.f : yl[l+0]) + ((h[l/2] & hm.s1) ? 0.f : yl[l+1]);
|
||||
s4 += yl[l+16] * (qs & qm.s2);
|
||||
s5 += yl[l+17] * (qs & qm.s3);
|
||||
s6 += ((h[l/2] & hm.s2) ? 0.f : yl[l+16]) + ((h[l/2] & hm.s3) ? 0.f : yl[l+17]);
|
||||
}
|
||||
float d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
float d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s0 - 32);
|
||||
sumf2[row] += d2 * (sc.s2 - 32);
|
||||
|
||||
s1 = s2 = s3 = s4 = s5 = s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2+8];
|
||||
s1 += yl[l+8] * (qs & qm.s0);
|
||||
s2 += yl[l+9] * (qs & qm.s1);
|
||||
s3 += ((h[l/2+8] & hm.s0) ? 0.f : yl[l+8]) + ((h[l/2+8] & hm.s1) ? 0.f : yl[l+9]);
|
||||
s4 += yl[l+24] * (qs & qm.s2);
|
||||
s5 += yl[l+25] * (qs & qm.s3);
|
||||
s6 += ((h[l/2+8] & hm.s2) ? 0.f : yl[l+24]) + ((h[l/2+8] & hm.s3) ? 0.f : yl[l+25]);
|
||||
}
|
||||
d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s1 - 32);
|
||||
sumf2[row] += d2 * (sc.s3 - 32);
|
||||
|
||||
q += nb01/2;
|
||||
h += nb01/2;
|
||||
a += nb01/2;
|
||||
dh += nb01/2;
|
||||
}
|
||||
|
||||
y1 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add((sumf1[row] + 0.25f * sumf2[row]) / (1 << shift));
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// block_q3_K
|
||||
//------------------------------------------------------------------------------
|
||||
#define QK_K 256
|
||||
|
||||
// 16 blocks of 16 elements each
|
||||
// weight is represented as x = a * q
|
||||
typedef struct {
|
||||
uchar hmask[QK_K/8]; // quants - high bit
|
||||
uchar qs[QK_K/4]; // quants - low 2 bits
|
||||
uchar scales[12]; // scales, quantized with 6 bits
|
||||
half d; // super-block scale
|
||||
} block_q3_K;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 2 // number of rows each SIMD group works on
|
||||
#define N_SIMDGROUP 1 // number of SIMD groups in a thread group
|
||||
#define N_SIMDWIDTH 16 // SIMD group size
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 2
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 8 threads cover one super block, so a wave covers N_SIMDWIDTH/8 of them
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_q3_K_f32_flat(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_hm,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = (offset0 + first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03)/sizeof(block_q3_K);
|
||||
ulong row_stride = nb01/sizeof(block_q3_K);
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global float * yy = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
|
||||
// Eight lanes cover one super-block.
|
||||
int ix = get_sub_group_local_id() % BLOCK_STRIDE;
|
||||
int tid = get_sub_group_local_id() / BLOCK_STRIDE;
|
||||
|
||||
int ip = tid/4; // 0 or 1
|
||||
int il = 2*((tid%4)/2); // 0 or 2
|
||||
int ir = tid%2;
|
||||
int l0 = 8*ir;
|
||||
|
||||
// Shift the masks instead of indexing private arrays to avoid spills.
|
||||
ushort4 hm = (ushort4)(0x0001, 0x0100, 0x0002, 0x0200) << (ushort4)(2*(2*ip + il/2));
|
||||
int4 qm = (int4) (0x0003, 0x0300, 0x000c, 0x0c00) << (int4) (4*(il/2));
|
||||
|
||||
int shift = 2*il;
|
||||
|
||||
float v1 = il == 0 ? 4.f : 64.f;
|
||||
float v2 = 4.f * v1;
|
||||
|
||||
int s_shift1 = 4*ip;
|
||||
int s_shift2 = s_shift1 + il;
|
||||
|
||||
int q_offset = 32*ip + l0;
|
||||
int y_offset = 128*ip + 32*il + l0;
|
||||
|
||||
global float * y1 = yy + ix*QK_K + y_offset;
|
||||
|
||||
float sumf1[N_DST] = {0.f};
|
||||
float sumf2[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
for (int i = ix; i < nb; i += BLOCK_STRIDE) {
|
||||
for (int l = 0; l < 8; ++l) {
|
||||
yl[l+ 0] = y1[l+ 0];
|
||||
yl[l+ 8] = y1[l+16];
|
||||
yl[l+16] = y1[l+32];
|
||||
yl[l+24] = y1[l+48];
|
||||
}
|
||||
|
||||
global ushort * q = (global ushort *)(src0_qs + 64*(offset_src0 + i) + q_offset);
|
||||
global ushort * h = (global ushort *)(src0_hm + 32*(offset_src0 + i) + l0);
|
||||
global ushort * a = (global ushort *)(src0_sc + 12*(offset_src0 + i));
|
||||
global half * dh = src0_d + offset_src0 + i;
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; ++row) {
|
||||
float d_all = dh[0];
|
||||
|
||||
uint s32 = (uint)a[4] | ((uint)a[5] << 16);
|
||||
uint aux32 = ((s32 >> s_shift2) << 4) & 0x30303030u;
|
||||
s32 = (uint)a[il+0] | ((uint)a[il+1] << 16);
|
||||
s32 = ((s32 >> s_shift1) & 0x0f0f0f0fu) | aux32;
|
||||
char4 sc = as_char4(s32);
|
||||
|
||||
float s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2];
|
||||
s1 += yl[l+0] * (qs & qm.s0);
|
||||
s2 += yl[l+1] * (qs & qm.s1);
|
||||
s3 += ((h[l/2] & hm.s0) ? 0.f : yl[l+0]) + ((h[l/2] & hm.s1) ? 0.f : yl[l+1]);
|
||||
s4 += yl[l+16] * (qs & qm.s2);
|
||||
s5 += yl[l+17] * (qs & qm.s3);
|
||||
s6 += ((h[l/2] & hm.s2) ? 0.f : yl[l+16]) + ((h[l/2] & hm.s3) ? 0.f : yl[l+17]);
|
||||
}
|
||||
float d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
float d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s0 - 32);
|
||||
sumf2[row] += d2 * (sc.s2 - 32);
|
||||
|
||||
s1 = s2 = s3 = s4 = s5 = s6 = 0;
|
||||
for (int l = 0; l < 8; l += 2) {
|
||||
int qs = q[l/2+8];
|
||||
s1 += yl[l+8] * (qs & qm.s0);
|
||||
s2 += yl[l+9] * (qs & qm.s1);
|
||||
s3 += ((h[l/2+8] & hm.s0) ? 0.f : yl[l+8]) + ((h[l/2+8] & hm.s1) ? 0.f : yl[l+9]);
|
||||
s4 += yl[l+24] * (qs & qm.s2);
|
||||
s5 += yl[l+25] * (qs & qm.s3);
|
||||
s6 += ((h[l/2+8] & hm.s2) ? 0.f : yl[l+24]) + ((h[l/2+8] & hm.s3) ? 0.f : yl[l+25]);
|
||||
}
|
||||
d1 = d_all * (s1 + 1.f/256.f * s2 - s3*v1);
|
||||
d2 = d_all * (s4 + 1.f/256.f * s5 - s6*v2);
|
||||
sumf1[row] += d1 * (sc.s1 - 32);
|
||||
sumf2[row] += d2 * (sc.s3 - 32);
|
||||
|
||||
q += row_stride*32;
|
||||
h += row_stride*16;
|
||||
a += row_stride*6;
|
||||
dh += row_stride*1;
|
||||
}
|
||||
|
||||
y1 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add((sumf1[row] + 0.25f * sumf2[row]) / (1 << shift));
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1446,6 +1446,10 @@ static ggml_openvino_op_support is_op_supported_case(const ggml_tensor * op) {
|
||||
if (op->type == GGML_TYPE_BF16) {
|
||||
return {false, "SCALE with BF16 type is not supported"};
|
||||
}
|
||||
// The result is copied back densely, so an in-place SCALE on a strided view would be written wrongly.
|
||||
if (op->view_src != nullptr && !ggml_is_contiguous(op)) {
|
||||
return {false, "SCALE in place on a non-contiguous view is not supported"};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GGML_OP_ADD_ID: {
|
||||
|
||||
@@ -70,7 +70,7 @@ OutputVector translate_scale(const NodeContext & context) {
|
||||
return rename_outputs_with_suffix({cleared_cache_rs}, context.get_name());
|
||||
}
|
||||
|
||||
auto scaled = std::make_shared<ov::op::v1::Multiply>(context.get_input(0), scale_node);
|
||||
auto scaled = std::make_shared<ov::op::v1::Multiply>(process_view_input_new(context, 0), scale_node);
|
||||
|
||||
std::shared_ptr<ov::Node> res;
|
||||
if (bias != 0.0f) {
|
||||
|
||||
+32
-34
@@ -25,15 +25,12 @@
|
||||
|
||||
#define UNUSED GGML_UNUSED
|
||||
|
||||
static inline int best_index_int8(int n, const int8_t * val, float x) {
|
||||
if (x <= val[0]) return 0;
|
||||
if (x >= val[n-1]) return n-1;
|
||||
int ml = 0, mu = n-1;
|
||||
while (mu-ml > 1) {
|
||||
int mav = (ml+mu)/2;
|
||||
if (x < val[mav]) mu = mav; else ml = mav;
|
||||
}
|
||||
return x - val[mu-1] < val[mu] - x ? mu-1 : mu;
|
||||
// index of the nearest kvalues_iq4nl value, with mid[k] = (values[k] + values[k+1])/2
|
||||
// for this table it matches a binary search with the tie rule x - values[k] < values[k+1] - x for every float x
|
||||
static inline int best_index_iq4nl(const float * GGML_RESTRICT mid, float x) {
|
||||
int l = 15;
|
||||
for (int k = 0; k < 15; ++k) l -= x < mid[k];
|
||||
return l;
|
||||
}
|
||||
|
||||
// reference implementation for deterministic creation of model files
|
||||
@@ -4949,6 +4946,11 @@ static void quantize_row_iq4_nl_impl(const int super_block_size, const int block
|
||||
memset(q4, 0, super_block_size/2);
|
||||
dh[0] = GGML_FP32_TO_FP16(0.f);
|
||||
|
||||
float mid[15];
|
||||
float fval[16];
|
||||
for (int k = 0; k < 15; ++k) mid[k] = 0.5f*(values[k] + values[k+1]);
|
||||
for (int k = 0; k < 16; ++k) fval[k] = values[k];
|
||||
|
||||
float max_scale = 0, amax_scale = 0;
|
||||
for (int ib = 0; ib < super_block_size/block_size; ++ib) {
|
||||
const float * xb = x + ib*block_size;
|
||||
@@ -4971,32 +4973,28 @@ static void quantize_row_iq4_nl_impl(const int super_block_size, const int block
|
||||
continue;
|
||||
}
|
||||
float d = ntry > 0 ? -max/values[0] : max/values[0];
|
||||
float id = 1/d;
|
||||
float sumqx = 0, sumq2 = 0;
|
||||
// lane 0 is the initial guess, lanes 1..nt-1 are itry = -ntry..ntry; each lane sums over j in order
|
||||
const int nt = ntry >= 0 ? 2*ntry + 2 : 1;
|
||||
float idt[16], sqx[16], sq2[16];
|
||||
idt[0] = 1/d;
|
||||
for (int t = 1; t < nt; ++t) idt[t] = (t - 1 - ntry + values[0])/max;
|
||||
for (int t = 0; t < nt; ++t) sqx[t] = sq2[t] = 0;
|
||||
for (int j = 0; j < block_size; ++j) {
|
||||
float al = id*xb[j];
|
||||
int l = best_index_int8(16, values, al);
|
||||
Lb[j] = l;
|
||||
float q = values[l];
|
||||
float w = weight[j];
|
||||
sumqx += w*q*xb[j];
|
||||
sumq2 += w*q*q;
|
||||
Lb[j] = best_index_iq4nl(mid, idt[0]*xb[j]);
|
||||
}
|
||||
d = sumq2 > 0 ? sumqx/sumq2 : 0.f;
|
||||
float best = d*sumqx;
|
||||
for (int itry = -ntry; itry <= ntry; ++itry) {
|
||||
id = (itry + values[0])/max;
|
||||
sumqx = sumq2 = 0;
|
||||
for (int j = 0; j < block_size; ++j) {
|
||||
float al = id*xb[j];
|
||||
int l = best_index_int8(16, values, al);
|
||||
float q = values[l];
|
||||
float w = weight[j];
|
||||
sumqx += w*q*xb[j];
|
||||
sumq2 += w*q*q;
|
||||
for (int j = 0; j < block_size; ++j) {
|
||||
float w = weight[j];
|
||||
for (int t = 0; t < nt; ++t) {
|
||||
float q = fval[best_index_iq4nl(mid, idt[t]*xb[j])];
|
||||
sqx[t] += w*q*xb[j];
|
||||
sq2[t] += w*q*q;
|
||||
}
|
||||
if (sumq2 > 0 && sumqx*sumqx > best*sumq2) {
|
||||
d = sumqx/sumq2; best = d * sumqx;
|
||||
}
|
||||
d = sq2[0] > 0 ? sqx[0]/sq2[0] : 0.f;
|
||||
float best = d*sqx[0];
|
||||
for (int t = 1; t < nt; ++t) {
|
||||
if (sq2[t] > 0 && sqx[t]*sqx[t] > best*sq2[t]) {
|
||||
d = sqx[t]/sq2[t]; best = d * sqx[t];
|
||||
}
|
||||
}
|
||||
scales[ib] = d;
|
||||
@@ -5020,7 +5018,7 @@ static void quantize_row_iq4_nl_impl(const int super_block_size, const int block
|
||||
uint8_t * Lb = L + ib*block_size;
|
||||
const float * xb = x + ib*block_size;
|
||||
for (int j = 0; j < block_size; ++j) {
|
||||
Lb[j] = best_index_int8(16, values, idl*xb[j]);
|
||||
Lb[j] = best_index_iq4nl(mid, idl*xb[j]);
|
||||
}
|
||||
l += 32;
|
||||
uint8_t l_l = l & 0xf;
|
||||
@@ -5034,7 +5032,7 @@ static void quantize_row_iq4_nl_impl(const int super_block_size, const int block
|
||||
if (ntry > 0) {
|
||||
float id = scales[0] ? 1/scales[0] : 0;
|
||||
for (int j = 0; j < super_block_size; ++j) {
|
||||
L[j] = best_index_int8(16, values, id*x[j]);
|
||||
L[j] = best_index_iq4nl(mid, id*x[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +348,10 @@ static bool recv_msg(socket_ptr sock, std::vector<uint8_t> & input) {
|
||||
}
|
||||
try {
|
||||
input.resize(size);
|
||||
} catch (const std::bad_alloc & e) {
|
||||
} catch (const std::length_error &) {
|
||||
LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
|
||||
return false;
|
||||
} catch (const std::bad_alloc &) {
|
||||
LOG_ERROR("Failed to allocate input buffer of size %" PRIu64 "\n", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ if (GGML_SYCL_F16)
|
||||
add_compile_definitions(GGML_SYCL_F16)
|
||||
endif()
|
||||
|
||||
if (GGML_SYCL_SUPPORT_XMX)
|
||||
add_compile_definitions(GGML_SYCL_SUPPORT_XMX)
|
||||
endif()
|
||||
|
||||
if (GGML_SYCL_TARGET STREQUAL "INTEL")
|
||||
add_compile_definitions(GGML_SYCL_WARP_SIZE=16)
|
||||
if (NOT GGML_SYCL_DEVICE_ARCH)
|
||||
|
||||
@@ -135,6 +135,7 @@ extern int g_ggml_sycl_dev2dev_memcpy;
|
||||
extern int g_ggml_sycl_fa_onednn;
|
||||
extern int g_ggml_sycl_fa_onednn_max_kv;
|
||||
extern int g_ggml_sycl_enable_mkl_fa;
|
||||
extern int g_ggml_sycl_fa_dpas;
|
||||
extern int g_ggml_sycl_memtrace;
|
||||
extern int g_ggml_sycl_memtrace_step;
|
||||
extern int g_ggml_sycl_graph_eviction_timeout;
|
||||
@@ -162,12 +163,6 @@ extern int g_ggml_sycl_graph_eviction_timeout;
|
||||
|
||||
#define GGML_SYCL_MAX_NODES 8192 // TODO: adapt to hardwares
|
||||
|
||||
// define for XMX in Intel GPU
|
||||
// TODO: currently, it's not used for XMX really.
|
||||
#if !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define SYCL_USE_XMX
|
||||
#endif
|
||||
|
||||
// max batch size to use MMQ kernels when tensor cores are available
|
||||
#define MMQ_MAX_BATCH_SIZE 32
|
||||
|
||||
|
||||
+22
-232
@@ -196,7 +196,8 @@ void concat_impl_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
template <typename block_t, int qk, ggml_type qtype>
|
||||
static void concat_impl_q_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
@@ -204,25 +205,25 @@ static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q4_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK4_0 == 0);
|
||||
GGML_ASSERT(src0->type == qtype);
|
||||
GGML_ASSERT(src1->type == qtype);
|
||||
GGML_ASSERT(dst->type == qtype);
|
||||
GGML_ASSERT(src0->ne[0] % qk == 0);
|
||||
GGML_ASSERT(src1->ne[0] % qk == 0);
|
||||
GGML_ASSERT(dst->ne[0] % qk == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK4_0;
|
||||
const int ne0_blk = dst->ne[0] / QK4_0;
|
||||
const int ne00_blk = src0->ne[0] / qk;
|
||||
const int ne0_blk = dst->ne[0] / qk;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q4_0 * src0_d = (const block_q4_0 *) src0->data;
|
||||
const block_q4_0 * src1_d = (const block_q4_0 *) src1->data;
|
||||
block_q4_0 * dst_d = (block_q4_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q4_0);
|
||||
const block_t * src0_d = (const block_t *) src0->data;
|
||||
const block_t * src1_d = (const block_t *) src1->data;
|
||||
block_t * dst_d = (block_t *) dst->data;
|
||||
const size_t type_size = sizeof(block_t);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q4_0>(
|
||||
concat_T_sycl<block_t>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
@@ -237,223 +238,12 @@ static void concat_impl_q4_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q4_0>(
|
||||
concat_T_sycl_non_cont<block_t>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK4_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q4_1_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q4_1);
|
||||
GGML_ASSERT(src0->ne[0] % QK4_1 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK4_1 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK4_1 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK4_1;
|
||||
const int ne0_blk = dst->ne[0] / QK4_1;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q4_1 * src0_d = (const block_q4_1 *) src0->data;
|
||||
const block_q4_1 * src1_d = (const block_q4_1 *) src1->data;
|
||||
block_q4_1 * dst_d = (block_q4_1 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q4_1);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q4_1>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q4_1>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK4_1, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q5_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q5_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK5_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK5_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK5_0 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK5_0;
|
||||
const int ne0_blk = dst->ne[0] / QK5_0;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q5_0 * src0_d = (const block_q5_0 *) src0->data;
|
||||
const block_q5_0 * src1_d = (const block_q5_0 *) src1->data;
|
||||
block_q5_0 * dst_d = (block_q5_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q5_0);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q5_0>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q5_0>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK5_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q5_1_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q5_1);
|
||||
GGML_ASSERT(src0->ne[0] % QK5_1 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK5_1 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK5_1 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK5_1;
|
||||
const int ne0_blk = dst->ne[0] / QK5_1;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q5_1 * src0_d = (const block_q5_1 *) src0->data;
|
||||
const block_q5_1 * src1_d = (const block_q5_1 *) src1->data;
|
||||
block_q5_1 * dst_d = (block_q5_1 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q5_1);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q5_1>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q5_1>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK5_1, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
}
|
||||
}
|
||||
|
||||
static void concat_impl_q8_0_sycl(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2);
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
queue_ptr stream = ctx.stream();
|
||||
|
||||
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(src1->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(dst->type == GGML_TYPE_Q8_0);
|
||||
GGML_ASSERT(src0->ne[0] % QK8_0 == 0);
|
||||
GGML_ASSERT(src1->ne[0] % QK8_0 == 0);
|
||||
GGML_ASSERT(dst->ne[0] % QK8_0 == 0);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK8_0;
|
||||
const int ne0_blk = dst->ne[0] / QK8_0;
|
||||
|
||||
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
||||
const block_q8_0 * src0_d = (const block_q8_0 *) src0->data;
|
||||
const block_q8_0 * src1_d = (const block_q8_0 *) src1->data;
|
||||
block_q8_0 * dst_d = (block_q8_0 *) dst->data;
|
||||
const size_t type_size = sizeof(block_q8_0);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q8_0>(
|
||||
src0_d + i3 * (src0->nb[3] / type_size),
|
||||
src1_d + i3 * (src1->nb[3] / type_size),
|
||||
dst_d + i3 * (dst->nb[3] / type_size),
|
||||
ne00_blk, src0->ne[1], src0->ne[2], ne0_blk,
|
||||
dst->ne[1], dst->ne[2], dim, stream);
|
||||
}
|
||||
} else {
|
||||
const size_t size0 = ggml_nbytes(src0);
|
||||
const size_t size1 = ggml_nbytes(src1);
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy((char *) dst_d + size0, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<block_q8_0>(
|
||||
stream, (const char *) src0->data, (const char *) src1->data,
|
||||
(char *) dst->data,
|
||||
ne00_blk, src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
||||
src1->ne[0] / QK8_0, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->ne[0] / qk, src1->ne[1], src1->ne[2], src1->ne[3],
|
||||
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
||||
ne0_blk, dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], dim);
|
||||
@@ -487,19 +277,19 @@ void ggml_sycl_op_concat(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
concat_impl_sycl<int8_t>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_0:
|
||||
concat_impl_q4_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q4_0, QK4_0, GGML_TYPE_Q4_0>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_1:
|
||||
concat_impl_q4_1_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q4_1, QK4_1, GGML_TYPE_Q4_1>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_0:
|
||||
concat_impl_q5_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q5_0, QK5_0, GGML_TYPE_Q5_0>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_1:
|
||||
concat_impl_q5_1_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q5_1, QK5_1, GGML_TYPE_Q5_1>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q8_0:
|
||||
concat_impl_q8_0_sycl(ctx, dst);
|
||||
concat_impl_q_sycl<block_q8_0, QK8_0, GGML_TYPE_Q8_0>(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unsupported types: dst: %s\n", __func__, ggml_type_name(dst->type));
|
||||
|
||||
@@ -158,15 +158,12 @@ static void dequantize_row_q4_0_sycl_reorder(const void *vx, dst_t *y, const int
|
||||
dpct::has_capability_or_fail(stream->get_device(),
|
||||
{sycl::aspect::fp16});
|
||||
|
||||
// flat range<1>, four qs bytes (8 outputs) per item: two vectorized stores.
|
||||
// k % QK4_0 == 0 gives whole 32-element blocks, so the k/8 launch covers every row exactly.
|
||||
GGML_ASSERT(k % QK4_0 == 0);
|
||||
const int64_t n_groups = ceil_div(k / 2, SYCL_DEQUANTIZE_BLOCK_SIZE);
|
||||
// decrease global range when it exceeds the max int; the kernel loops to cover the rest
|
||||
const int64_t wg_size = downsample_sycl_global_range(n_groups, SYCL_DEQUANTIZE_BLOCK_SIZE);
|
||||
stream->parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, n_groups) *
|
||||
sycl::range<3>(1, 1, wg_size),
|
||||
sycl::range<3>(1, 1, wg_size)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q4_0_reorder(vx, y, k, item_ct1);
|
||||
stream->parallel_for(sycl::range<1>((size_t) (k / 8)),
|
||||
[=](sycl::id<1> idx) {
|
||||
dequantize_block_q4_0_reorder_wide(vx, y, k, idx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -253,7 +250,7 @@ static void dequantize_row_q5_K_sycl_reorder(const void * vx, dst_t * y, const i
|
||||
sycl::local_accessor<uint8_t, 1> scale_local_acc(sycl::range<1>(K_SCALE_SIZE), cgh);
|
||||
|
||||
cgh.parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 64), sycl::range<3>(1, 1, 64)),
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, nb) * sycl::range<3>(1, 1, 32), sycl::range<3>(1, 1, 32)),
|
||||
[=](sycl::nd_item<3> item_ct1) {
|
||||
dequantize_block_q5_K_reorder(vx, y + item_ct1.get_group(2) * QK_K, item_ct1.get_group(2), get_pointer(scale_local_acc), item_ct1, nb);
|
||||
});
|
||||
@@ -531,13 +528,13 @@ static void dequantize_row_mxfp4_sycl(const void * vx, dst_t * y, const int64_t
|
||||
|
||||
template <typename dst_t>
|
||||
static void dequantize_row_mxfp4_sycl_reorder(const void * vx, dst_t * y, const int64_t k, dpct::queue_ptr stream) {
|
||||
// flat range<1>, four qs bytes (8 outputs) per item: two vectorized stores.
|
||||
// Each 32-element block holds QK_MXFP4/2 = 16 qs bytes = 4 items, so the
|
||||
// k/8 launch covers the row exactly (k % QK_MXFP4 == 0 asserted).
|
||||
GGML_ASSERT(k % QK_MXFP4 == 0);
|
||||
const int n_warp = (k / QK_MXFP4 + WARP_SIZE - 1) / WARP_SIZE;
|
||||
stream->parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(1, 1, n_warp) * sycl::range<3>(1, 1, WARP_SIZE),
|
||||
sycl::range<3>(1, 1, WARP_SIZE)),
|
||||
[=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
|
||||
dequantize_block_mxfp4_reorder(vx, y, k, item_ct1);
|
||||
stream->parallel_for(sycl::range<1>((size_t) (k / 8)),
|
||||
[=](sycl::id<1> idx) {
|
||||
dequantize_block_mxfp4_reorder_wide(vx, y, k, idx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -817,25 +817,34 @@ static void dequantize_block_q4_0(const void * __restrict__ vx, dst_t * __restri
|
||||
}
|
||||
}
|
||||
|
||||
// four qs bytes per item, all inside one 32-element block: two vec<dst_t,4> stores.
|
||||
// float math, same products as the scalar kernel; nibble order kept (lo at +0, hi at +16).
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_q4_0_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
|
||||
const sycl::nd_item<3> &item_ct1) {
|
||||
// one thread per qs byte, like dequantize_block<dequantize_q4_0>; the reorder layout makes the byte reads contiguous
|
||||
// grid-stride loop because the launcher may shrink the work-group to keep the global range below INT_MAX
|
||||
// float math, not dequantize_q4_0_reorder: that helper computes in half with GGML_SYCL_F16, which changes f32 output
|
||||
const sycl::half * d_ptr = (const sycl::half *) ((const uint8_t *) vx + k/2);
|
||||
const int64_t stride = item_ct1.get_local_range(2) * item_ct1.get_group_range(2);
|
||||
static void dequantize_block_q4_0_reorder_wide(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
|
||||
const sycl::id<1> idx) {
|
||||
const int64_t i = (int64_t) idx[0];
|
||||
const int64_t ib = i / 4;
|
||||
const int64_t iqs = 4 * (i % 4);
|
||||
|
||||
for (int64_t iq = item_ct1.get_global_id(2); iq < k/2; iq += stride) {
|
||||
const int64_t ib = iq / (QK4_0/2);
|
||||
const int64_t iqs = iq % (QK4_0/2);
|
||||
const int vq = ((const uint8_t *) vx)[iq];
|
||||
const float d = float(d_ptr[ib]);
|
||||
const uint8_t * qs = (const uint8_t *) vx;
|
||||
const sycl::half * d_ptr = (const sycl::half *) ((const uint8_t *) vx + k/2);
|
||||
|
||||
dst_t * y = yy + ib*QK4_0 + iqs;
|
||||
y[0] = d * ((vq & 0xF) - 8);
|
||||
y[QK4_0/2] = d * ((vq >> 4) - 8);
|
||||
}
|
||||
const float d = float(d_ptr[ib]);
|
||||
const uint8_t * q = qs + ib*(QK4_0/2) + iqs;
|
||||
|
||||
sycl::vec<float, 4> lo, hi;
|
||||
lo[0] = d * ((q[0] & 0xF) - 8);
|
||||
lo[1] = d * ((q[1] & 0xF) - 8);
|
||||
lo[2] = d * ((q[2] & 0xF) - 8);
|
||||
lo[3] = d * ((q[3] & 0xF) - 8);
|
||||
hi[0] = d * ((q[0] >> 4) - 8);
|
||||
hi[1] = d * ((q[1] >> 4) - 8);
|
||||
hi[2] = d * ((q[2] >> 4) - 8);
|
||||
hi[3] = d * ((q[3] >> 4) - 8);
|
||||
|
||||
dst_t * yb = yy + ib*QK4_0 + iqs;
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(yb) = lo.convert<dst_t, sycl::rounding_mode::automatic>();
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(yb + QK4_0/2) = hi.convert<dst_t, sycl::rounding_mode::automatic>();
|
||||
}
|
||||
|
||||
// Dequantize Q8_0 from reorder layout: [all qs (k bytes)][all d values]
|
||||
@@ -963,19 +972,25 @@ static void dequantize_block_q2_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const ggml_half2 * dm = reinterpret_cast<const ggml_half2 *>(base + dm_offset);
|
||||
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
const int64_t n = tid / 32;
|
||||
const int64_t l = tid - 32 * n;
|
||||
const int64_t is = 8 * n + l / 16;
|
||||
const int64_t n = tid / 32; // 128-element chunk: 0 or 1
|
||||
const int64_t eh0 = 4 * (tid - 32 * n); // base element within the chunk
|
||||
|
||||
const uint8_t q = qs[32 * n + l];
|
||||
dst_t * y = yy + 128 * n;
|
||||
const int shift = 2 * (eh0 / 32); // 2-bit lane: 0/2/4/6
|
||||
const uint8_t * q4 = qs + 32 * n + (eh0 & 31);
|
||||
const uint8_t sc0 = scales[8 * n + eh0 / 16];
|
||||
|
||||
const float dall = (*dm)[0];
|
||||
const float dmin = (*dm)[1];
|
||||
y[l+ 0] = dall * (scales[is+0] & 0xF) * ((q >> 0) & 3) - dmin * (scales[is+0] >> 4);
|
||||
y[l+32] = dall * (scales[is+2] & 0xF) * ((q >> 2) & 3) - dmin * (scales[is+2] >> 4);
|
||||
y[l+64] = dall * (scales[is+4] & 0xF) * ((q >> 4) & 3) - dmin * (scales[is+4] >> 4);
|
||||
y[l+96] = dall * (scales[is+6] & 0xF) * ((q >> 6) & 3) - dmin * (scales[is+6] >> 4);
|
||||
|
||||
// remapped decomposition: one aligned vec<dst_t,4> per thread instead of four
|
||||
// scalars strided 32 apart; a 4-run shares one scale byte and one 2-bit lane,
|
||||
// so the per-element math and output are identical to the scalar kernel.
|
||||
sycl::vec<dst_t, 4> v;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
v[j] = dall * (sc0 & 0xF) * ((q4[j] >> shift) & 3) - dmin * (sc0 >> 4);
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(yy + 128 * n + eh0) = v;
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
@@ -1085,9 +1100,12 @@ static void dequantize_block_q3_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const uint8_t * q = qs + 32 * n;
|
||||
const uint8_t * hm = hmask;
|
||||
|
||||
for (int l = l0; l < l0 + 4; ++l) {
|
||||
y[l] = dl * ((int8_t) ((q[l] >> shift) & 3) - ((hm[l] & m) ? 0 : 4));
|
||||
sycl::vec<dst_t, 4> v;
|
||||
#pragma unroll
|
||||
for (int jj = 0; jj < 4; ++jj) {
|
||||
v[jj] = dl * ((int8_t) ((q[l0 + jj] >> shift) & 3) - ((hm[l0 + jj] & m) ? 0 : 4));
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + l0) = v;
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
@@ -1173,10 +1191,14 @@ inline void dequantize_q4_K_common(dst_t * __restrict__ y, const uint8_t * __res
|
||||
const float m2 = dmin * m;
|
||||
|
||||
sycl::vec<uint8_t, n> q_vec = vec_aligned_load<uint8_t, n>(qs_ptr + 32 * il + n * ir);
|
||||
|
||||
sycl::vec<dst_t, n> lo, hi;
|
||||
for (int l = 0; l < n; ++l) {
|
||||
y[l + 0] = d1 * (q_vec[l] & 0xF) - m1;
|
||||
y[l + 32] = d2 * (q_vec[l] >> 4) - m2;
|
||||
lo[l] = d1 * (q_vec[l] & 0xF) - m1;
|
||||
hi[l] = d2 * (q_vec[l] >> 4) - m2;
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, n> *>(y + 0) = lo;
|
||||
*reinterpret_cast<sycl::vec<dst_t, n> *>(y + 32) = hi;
|
||||
}
|
||||
|
||||
template <typename dst_t>
|
||||
@@ -1260,13 +1282,15 @@ static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
|
||||
#if QK_K == 256
|
||||
// assume 64 threads
|
||||
// remapped decomposition: 32 threads, each emitting two aligned vec<dst_t,4>
|
||||
// from four consecutive ql/qh bytes; a 4-element run shares one scale pair,
|
||||
// so the per-element math and output are identical to the 64-thread kernel.
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
const int64_t il = tid / 16; // 0...3
|
||||
const int64_t ir = tid % 16; // 0...15
|
||||
const int64_t il = tid / 8; // 0...3
|
||||
const int64_t ir = tid % 8; // 0...7
|
||||
const int64_t is = 2 * il;
|
||||
|
||||
dst_t * y = yy + 64 * il + 2 * ir;
|
||||
dst_t * y = yy + 64 * il + 4 * ir;
|
||||
|
||||
const uint8_t * base = static_cast<const uint8_t *>(vx);
|
||||
|
||||
@@ -1284,8 +1308,8 @@ static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const float dall = dm_values.x();
|
||||
const float dmin = dm_values.y();
|
||||
|
||||
const uint8_t * ql = qs_ptr + 32 * il + 2 * ir;
|
||||
const uint8_t * qh = qh_ptr + 2 * ir;
|
||||
const uint8_t * ql = qs_ptr + 32 * il + 4 * ir; // four consecutive bytes
|
||||
const uint8_t * qh = qh_ptr + 4 * ir; // four consecutive bytes
|
||||
|
||||
if (tid < K_SCALE_SIZE) {
|
||||
scales_local[tid] = scales_ptr[tid];
|
||||
@@ -1299,12 +1323,18 @@ static void dequantize_block_q5_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
get_scale_min_k4(is + 1, scales_local, sc, m);
|
||||
const float d2 = dall * sc; const float m2 = dmin * m;
|
||||
|
||||
uint8_t hm = 1 << (2 * il);
|
||||
y[ 0] = d1 * ((ql[ 0] & 0xF) + (qh[ 0] & hm ? 16 : 0)) - m1;
|
||||
y[ 1] = d1 * ((ql[ 1] & 0xF) + (qh[ 1] & hm ? 16 : 0)) - m1;
|
||||
hm <<= 1;
|
||||
y[32] = d2 * ((ql[ 0] >> 4) + (qh[ 0] & hm ? 16 : 0)) - m2;
|
||||
y[33] = d2 * ((ql[ 1] >> 4) + (qh[ 1] & hm ? 16 : 0)) - m2;
|
||||
const uint8_t hm_lo = 1 << (2 * il);
|
||||
const uint8_t hm_hi = 1 << (2 * il + 1);
|
||||
|
||||
sycl::vec<dst_t, 4> lo, hi;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
lo[j] = d1 * ((ql[j] & 0xF) + (qh[j] & hm_lo ? 16 : 0)) - m1;
|
||||
hi[j] = d2 * ((ql[j] >> 4) + (qh[j] & hm_hi ? 16 : 0)) - m2;
|
||||
}
|
||||
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 0) = lo;
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 32) = hi;
|
||||
#else
|
||||
GGML_UNUSED(ib); GGML_UNUSED(tid); GGML_UNUSED(yy); GGML_UNUSED(scales_local); GGML_UNUSED(n_blocks);
|
||||
GGML_ABORT("Q5_K reorder dequantize not supported for QK_K != 256");
|
||||
@@ -1360,11 +1390,13 @@ static void dequantize_block_q6_K(const void * __restrict__ vx, dst_t * __restri
|
||||
template <typename dst_t>
|
||||
static void dequantize_block_q6_K_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, const int64_t ib,
|
||||
const sycl::nd_item<3> & item_ct1, int64_t n_blocks) {
|
||||
|
||||
// remapped decomposition: each of the 64 threads emits one aligned vec<dst_t,4>
|
||||
// instead of four scalars strided 32 apart. The four elements of a run share one
|
||||
// 16-element scale group, one ql nibble source and one qh bit pair, so the
|
||||
// per-element math and the output are identical to the scalar kernel.
|
||||
const int64_t tid = item_ct1.get_local_id(2);
|
||||
const int64_t ip = tid / 32; // ip is 0 or 1
|
||||
const int64_t il = tid - 32 * ip; // 0...32
|
||||
const int64_t is = 8 * ip + il / 16;
|
||||
const int64_t ip = tid / 32; // 128-element half: 0 or 1
|
||||
const int64_t eh0 = 4 * (tid - 32 * ip); // base element within the half
|
||||
|
||||
const uint8_t * base_ptr = static_cast<const uint8_t *>(vx);
|
||||
const auto ql_offset = ib * (QK_K / 2);
|
||||
@@ -1376,16 +1408,18 @@ static void dequantize_block_q6_K_reorder(const void * __restrict__ vx, dst_t *
|
||||
const uint8_t * scales_ptr = base_ptr + base_scales_offset;
|
||||
const ggml_half * d = (const ggml_half *) (base_ptr + base_d_offset) + ib;
|
||||
|
||||
dst_t * y = yy + 128 * ip + il;
|
||||
const int shift = eh0 >= 64 ? 4 : 0; // low or high ql nibble
|
||||
const int qb = 2 * (eh0 / 32); // qh bit pair: 0/2/4/6
|
||||
const int8_t sc0 = reinterpret_cast<const int8_t *>(scales_ptr + 8 * ip + eh0 / 16)[0];
|
||||
const uint8_t * ql4 = ql_ptr + 64 * ip + (eh0 & 63);
|
||||
const uint8_t * qh4 = qh_ptr + 32 * ip + (eh0 & 31);
|
||||
|
||||
const uint8_t * ql = ql_ptr + 64 * ip + il;
|
||||
const uint8_t qh = *(qh_ptr + 32 * ip + il);
|
||||
const int8_t * sc = reinterpret_cast<const int8_t *>(scales_ptr + is);
|
||||
|
||||
y[0] = *d * sc[0] * ((int8_t) ((ql[0] & 0xF) | (((qh >> 0) & 3) << 4)) - 32);
|
||||
y[32] = *d * sc[2] * ((int8_t) ((ql[32] & 0xF) | (((qh >> 2) & 3) << 4)) - 32);
|
||||
y[64] = *d * sc[4] * ((int8_t) ((ql[0] >> 4) | (((qh >> 4) & 3) << 4)) - 32);
|
||||
y[96] = *d * sc[6] * ((int8_t) ((ql[32] >> 4) | (((qh >> 6) & 3) << 4)) - 32);
|
||||
sycl::vec<dst_t, 4> v;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
v[j] = *d * sc0 * ((int8_t) (((ql4[j] >> shift) & 0xF) | ((((qh4[j] >> qb) & 3)) << 4)) - 32);
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(yy + 128 * ip + eh0) = v;
|
||||
}
|
||||
|
||||
template<typename dst_t>
|
||||
@@ -1521,11 +1555,14 @@ static void dequantize_block_iq3_xxs_reorder(const void * __restrict__ vx, dst_t
|
||||
const float dl = (float) d[i] * (0.5f + (aux32 >> 28)) * 0.5f;
|
||||
const uint8_t signs = ksigns_iq2xs[(aux32 >> (7 * il)) & 127];
|
||||
dst_t * y = yy + 32 * ib + 8 * il;
|
||||
sycl::vec<dst_t, 4> lo, hi;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
y[j+0] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
y[j+4] = dl * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f);
|
||||
lo[j] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
hi[j] = dl * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f);
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 0) = lo;
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 4) = hi;
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
@@ -1594,11 +1631,14 @@ dequantize_block_iq3_s_reorder(const void * __restrict__ vx, dst_t * __restrict_
|
||||
const float dl = (float) d * (1 + 2 * ((scales[ib/2] >> (4 * (ib%2))) & 0xf));
|
||||
const uint8_t signs = signs_base[4 * ib + il];
|
||||
dst_t * y = yy + 32 * ib + 8 * il;
|
||||
sycl::vec<dst_t, 4> lo, hi;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
y[j+0] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
y[j+4] = dl * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f);
|
||||
lo[j] = dl * grid1[j] * (signs & kmask_iq2xs[j+0] ? -1.f : 1.f);
|
||||
hi[j] = dl * grid2[j] * (signs & kmask_iq2xs[j+4] ? -1.f : 1.f);
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 0) = lo;
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + 4) = hi;
|
||||
#else
|
||||
GGML_UNUSED(vx);
|
||||
GGML_UNUSED(yy);
|
||||
@@ -1737,24 +1777,28 @@ static void dequantize_block_mxfp4(const void * __restrict__ vx, dst_t * __restr
|
||||
}
|
||||
}
|
||||
|
||||
// Reordered MXFP4 ([qs...][e...], see ggml_sycl_reordered::block_q_t<MXFP4>): one work-item per block.
|
||||
template <typename dst_t>
|
||||
static void dequantize_block_mxfp4_reorder(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
|
||||
const sycl::nd_item<3> & item_ct1) {
|
||||
const int64_t ib = (int64_t) item_ct1.get_group(2) * WARP_SIZE + item_ct1.get_local_id(2);
|
||||
if (ib >= k / QK_MXFP4) {
|
||||
return;
|
||||
}
|
||||
// Reordered MXFP4 ([qs...][e...], see ggml_sycl_reordered::block_q_t<MXFP4>).
|
||||
// four qs bytes per item, all inside one 32-element block: two vec<dst_t,4> stores.
|
||||
// same LUT lookups and per-block scale; bit-identical output.
|
||||
template<typename dst_t>
|
||||
static void dequantize_block_mxfp4_reorder_wide(const void * __restrict__ vx, dst_t * __restrict__ yy, int64_t k,
|
||||
const sycl::id<1> idx) {
|
||||
const int64_t i = (int64_t) idx[0];
|
||||
const int64_t ib = i / 4;
|
||||
const int64_t iq = 4 * (i % 4); // first of four qs bytes in the block
|
||||
|
||||
const uint8_t * qs = (const uint8_t *) vx + ib * (QK_MXFP4 / 2);
|
||||
const float d = ggml_sycl_e8m0_to_fp32(((const uint8_t *) vx)[k / 2 + ib]) * 0.5f;
|
||||
dst_t * y = yy + ib * QK_MXFP4;
|
||||
dst_t * y = yy + ib * QK_MXFP4 + iq;
|
||||
|
||||
sycl::vec<dst_t, 4> lo, hi;
|
||||
#pragma unroll
|
||||
for (int j = 0; j < QK_MXFP4 / 2; ++j) {
|
||||
y[j] = d * kvalues_mxfp4[qs[j] & 0xf];
|
||||
y[j + QK_MXFP4 / 2] = d * kvalues_mxfp4[qs[j] >> 4];
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
lo[j] = d * kvalues_mxfp4[qs[iq + j] & 0xf];
|
||||
hi[j] = d * kvalues_mxfp4[qs[iq + j] >> 4];
|
||||
}
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y) = lo;
|
||||
*reinterpret_cast<sycl::vec<dst_t, 4> *>(y + QK_MXFP4 / 2) = hi;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,722 @@
|
||||
#include <sycl/sycl.hpp>
|
||||
#include <sycl/ext/intel/experimental/grf_size_properties.hpp>
|
||||
#include "dpct/helper.hpp"
|
||||
#include "common.hpp"
|
||||
#include "fattn-common.hpp"
|
||||
#include "fattn-dpas.hpp"
|
||||
|
||||
// GGML_SYCL_SUPPORT_XMX=OFF builds skip the DPAS below, as do spir64_gen builds for sub-group 8
|
||||
// targets (Xe-HPG) or mixed target lists, which cannot compile it
|
||||
#if !defined(GGML_SYCL_SUPPORT_XMX) || (defined(GGML_SYCL_XMX_AOT_SG) && GGML_SYCL_XMX_AOT_SG != 16)
|
||||
#define GGML_SYCL_FA_DPAS_BUILD 0
|
||||
#else
|
||||
#define GGML_SYCL_FA_DPAS_BUILD 1
|
||||
#endif
|
||||
|
||||
#if GGML_SYCL_FA_DPAS_BUILD
|
||||
static constexpr int FA_DPAS_WG = 256; // work-group size
|
||||
static constexpr int FA_DPAS_NSG = 16; // sub-groups of 16 lanes
|
||||
static constexpr int FA_DPAS_MAX_Q = 31; // larger batches go to the prefill kernels
|
||||
static constexpr int FA_DPAS_MAX_G = 32; // query heads per KV head
|
||||
static constexpr float FA_DPAS_MINF = -1.0e30f; // finite start value of the running max
|
||||
|
||||
static bool fattn_dpas_support_arch(gpu_arch arch) {
|
||||
return arch == gpu_arch::intel_gpu_bmg_g21 || arch == gpu_arch::intel_gpu_bmg_g31 ||
|
||||
arch == gpu_arch::intel_gpu_lnl_m || arch == gpu_arch::intel_gpu_ptl_h ||
|
||||
arch == gpu_arch::intel_gpu_ptl_u || arch == gpu_arch::intel_gpu_wcl;
|
||||
}
|
||||
|
||||
// head sizes with a compiled kernel (multiples of the 32-dim q4_0 / q8_0 block)
|
||||
static bool fattn_dpas_head_ok(int64_t d) {
|
||||
return d == 64 || d == 96 || d == 128 || d == 256 || d == 512;
|
||||
}
|
||||
#endif // GGML_SYCL_FA_DPAS_BUILD
|
||||
|
||||
bool ggml_sycl_flash_attn_ext_dpas_supported(int device, const ggml_tensor * dst) {
|
||||
#if !GGML_SYCL_FA_DPAS_BUILD
|
||||
GGML_UNUSED(device);
|
||||
GGML_UNUSED(dst);
|
||||
return false;
|
||||
#else
|
||||
if (!g_ggml_sycl_fa_dpas || !fattn_dpas_support_arch(ggml_sycl_info().devices[device].hw_info.arch)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * Q = dst->src[0];
|
||||
const ggml_tensor * K = dst->src[1];
|
||||
const ggml_tensor * V = dst->src[2];
|
||||
const ggml_tensor * mask = dst->src[3];
|
||||
const ggml_tensor * sinks = dst->src[4];
|
||||
|
||||
if (Q->type != GGML_TYPE_F32 || (K->type != GGML_TYPE_Q4_0 && K->type != GGML_TYPE_Q8_0) || V->type != K->type ||
|
||||
(sinks && sinks->type != GGML_TYPE_F32)) {
|
||||
return false;
|
||||
}
|
||||
if (!fattn_dpas_head_ok(K->ne[0]) || V->ne[0] != K->ne[0] || Q->ne[0] != K->ne[0]) {
|
||||
return false;
|
||||
}
|
||||
if (Q->ne[1] < 1 || Q->ne[1] > FA_DPAS_MAX_Q || K->ne[1] < 1) {
|
||||
return false;
|
||||
}
|
||||
if (K->ne[2] < 1 || V->ne[2] != K->ne[2] || Q->ne[2] % K->ne[2] != 0 || Q->ne[2] / K->ne[2] > FA_DPAS_MAX_G) {
|
||||
return false;
|
||||
}
|
||||
if (Q->ne[3] != K->ne[3] || V->ne[3] != K->ne[3]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float max_bias = 0.0f;
|
||||
float logit_softcap = 0.0f;
|
||||
memcpy(&max_bias, (const float *) dst->op_params + 1, sizeof(float));
|
||||
memcpy(&logit_softcap, (const float *) dst->op_params + 2, sizeof(float));
|
||||
if (max_bias != 0.0f || logit_softcap != 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mask && (mask->type != GGML_TYPE_F16 || mask->ne[2] != 1 || mask->nb[0] != sizeof(sycl::half) ||
|
||||
mask->ne[0] < K->ne[1] || mask->ne[1] < Q->ne[1])) {
|
||||
return false;
|
||||
}
|
||||
if (sinks && (sinks->nb[0] != sizeof(float) || sinks->ne[0] < Q->ne[2])) {
|
||||
return false;
|
||||
}
|
||||
if (Q->nb[0] != sizeof(float)) {
|
||||
return false;
|
||||
}
|
||||
// q4_0 / q8_0 rows are read as dwords from the dword at or before the row start: rows must start on 2-byte bounds
|
||||
// (head sizes with an odd block count, e.g. 96, put every other head 2 bytes into a dword)
|
||||
// so a row's first/last dword may hold 2 bytes outside it; aligned dwords never cross the 128-byte buffer alignment
|
||||
for (const ggml_tensor * t : { K, V }) {
|
||||
if (t->nb[0] != ggml_type_size(t->type) || t->nb[1] % 2 != 0 || t->nb[2] % 2 != 0 || t->nb[3] % 2 != 0 ||
|
||||
(uintptr_t) t->data % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GGML_SYCL_FA_DPAS_BUILD
|
||||
|
||||
typedef short fa_dpas_short8 __attribute__((ext_vector_type(8)));
|
||||
typedef int fa_dpas_int4 __attribute__((ext_vector_type(4)));
|
||||
typedef int fa_dpas_int8 __attribute__((ext_vector_type(8)));
|
||||
typedef float fa_dpas_float8 __attribute__((ext_vector_type(8)));
|
||||
|
||||
// Xe2 DPAS (SIMD16, systolic depth 8, repeat count 8) as inline vISA: joint_matrix has no s4 type.
|
||||
// d = a x b. a: 8 rows x 32 s8, 2 per lane per row. b: 32 x 16 s4, 16 bytes per lane, one column per lane.
|
||||
static inline void fa_dpas_s8_s4(const fa_dpas_short8 & a, const fa_dpas_int4 & b, fa_dpas_int8 & d) {
|
||||
#ifdef __SYCL_DEVICE_ONLY__
|
||||
__asm__("{\n"
|
||||
".decl DB v_type=G type=ud num_elts=64 align=GRF alias=<%1,0>\n"
|
||||
".decl DA v_type=G type=ud num_elts=64 align=GRF alias=<%2,0>\n"
|
||||
"dpas.s4.s8.8.8 (M1, 16) %0.0 %%null.0 DB.0 DA(0,0)\n"
|
||||
"}\n"
|
||||
: "=rw"(d)
|
||||
: "rw"(b), "rw"(a));
|
||||
#else
|
||||
d = 0;
|
||||
GGML_UNUSED(a);
|
||||
GGML_UNUSED(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
// d = a x b. a: 8 rows x 32 s8, 2 per lane per row. b: 32 x 16 s8, 32 bytes per lane, one column per lane.
|
||||
static inline void fa_dpas_s8_s8(const fa_dpas_short8 & a, const fa_dpas_int8 & b, fa_dpas_int8 & d) {
|
||||
#ifdef __SYCL_DEVICE_ONLY__
|
||||
__asm__("{\n"
|
||||
".decl DB v_type=G type=ud num_elts=128 align=GRF alias=<%1,0>\n"
|
||||
".decl DA v_type=G type=ud num_elts=64 align=GRF alias=<%2,0>\n"
|
||||
"dpas.s8.s8.8.8 (M1, 16) %0.0 %%null.0 DB.0 DA(0,0)\n"
|
||||
"}\n"
|
||||
: "=rw"(d)
|
||||
: "rw"(b), "rw"(a));
|
||||
#else
|
||||
d = 0;
|
||||
GGML_UNUSED(a);
|
||||
GGML_UNUSED(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
// acc += a x b. a: 8 rows x 16 f16, one per lane per row. b: 16 x 16 f16 as pairs, 8 dwords per lane, one column per lane.
|
||||
static inline void fa_dpas_f16(const fa_dpas_short8 & a, const fa_dpas_int8 & b, fa_dpas_float8 & acc) {
|
||||
#ifdef __SYCL_DEVICE_ONLY__
|
||||
__asm__("{\n"
|
||||
".decl DB v_type=G type=ud num_elts=128 align=GRF alias=<%1,0>\n"
|
||||
".decl DA v_type=G type=ud num_elts=64 align=GRF alias=<%2,0>\n"
|
||||
"dpas.hf.hf.8.8 (M1, 16) %0.0 %0.0 DB.0 DA(0,0)\n"
|
||||
"}\n"
|
||||
: "+rw"(acc)
|
||||
: "rw"(b), "rw"(a));
|
||||
#else
|
||||
GGML_UNUSED(a);
|
||||
GGML_UNUSED(b);
|
||||
GGML_UNUSED(acc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Block b of a row = f16 scale + NQ dwords of quants (q4_0: 18 bytes, NQ 4; q8_0: 34 bytes, NQ 8), at byte (2 + 4 NQ) b.
|
||||
// w = the NQ + 1 dwords from that byte / 4. An odd block starts 2 bytes into w[0].
|
||||
template <int NQ> static inline void fa_dpas_split_block(const uint32_t * w, bool odd, uint32_t & sc, uint32_t * q) {
|
||||
if (!odd) {
|
||||
sc = w[0] & 0xFFFFu;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < NQ; ++i) {
|
||||
q[i] = (w[i] >> 16) | (w[i + 1] << 16);
|
||||
}
|
||||
} else {
|
||||
sc = w[0] >> 16;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < NQ; ++i) {
|
||||
q[i] = w[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct fa_dpas_params {
|
||||
const char * Q;
|
||||
const char * K;
|
||||
const char * V;
|
||||
const char * mask;
|
||||
float * dst;
|
||||
float * parts; // per-split partial results, nsplit > 1 only
|
||||
sycl::float2 * meta; // per-split (max, sum), nsplit > 1 only
|
||||
float scale;
|
||||
const float * sinks; // per query head softmax sink logit, or null
|
||||
int G; // query heads per KV head
|
||||
int nq; // query tokens per work-group
|
||||
int nqc; // token chunks
|
||||
int ne01;
|
||||
int ne02;
|
||||
int ne11;
|
||||
int ne33;
|
||||
int nsplit;
|
||||
int chunk; // keys per split
|
||||
int64_t nb01, nb02, nb03;
|
||||
int64_t nb11, nb12, nb13;
|
||||
int64_t nb21, nb22, nb23;
|
||||
int64_t nb31, nb33;
|
||||
};
|
||||
|
||||
// One work-group = one KV head x nq query tokens x one slice of the keys: S = Q K^T by s8 x s4 / s8 x s8 DPAS on the
|
||||
// raw K blocks, online softmax, O^T += V^T P^T by f16 DPAS. ST: stage K/V tiles in SLM (1: 16-byte, 2: dword loads).
|
||||
// LG: 256-register file (no spills at 32 rows, same occupancy). Q8: K/V are q8_0, else q4_0.
|
||||
template <int D, int NR16, int KG, int TC, int ST, bool LG, bool Q8>
|
||||
static void fattn_dpas_kernel(const fa_dpas_params p, const int nkvh, const int ne03, dpct::queue_ptr stream) {
|
||||
constexpr int RMAX = 16 * NR16;
|
||||
constexpr int RTMAX = RMAX / 8; // 8-row tiles in Q.K
|
||||
constexpr int TK = 16 * KG; // keys per tile
|
||||
static_assert(QK4_0 == QK8_0, "q4_0 and q8_0 blocks hold the same number of dims");
|
||||
constexpr int BB = Q8 ? (int) sizeof(block_q8_0) : (int) sizeof(block_q4_0); // bytes per block
|
||||
constexpr int NQ = (BB - 2) / 4; // quant dwords per block
|
||||
constexpr int NB = D / QK4_0; // blocks per row
|
||||
constexpr int DPS = D <= 128 ? 8 : D <= 256 ? 16 : 32; // head dims per sub-group in P.V, inside one 32-dim block
|
||||
constexpr int NPV = D / DPS; // sub-groups doing P.V, the rest only reach its barrier
|
||||
constexpr bool ALLPV = NPV == FA_DPAS_NSG; // every sub-group does P.V: no runtime check in the loops
|
||||
constexpr int XT = DPS / 8; // 8-dim tiles per sub-group in P.V
|
||||
constexpr int PS = TK + 16; // f16 row stride of P
|
||||
constexpr int RW = NB * BB / 4; // dwords per row
|
||||
constexpr bool ODD = (NB * BB) % 4 != 0; // odd block count: rows may start 2 bytes into a dword
|
||||
constexpr int SW = RW + 1; // odd SLM row stride: no bank conflicts for lane = key
|
||||
static_assert(D % QK4_0 == 0 && D % DPS == 0 && NPV <= FA_DPAS_NSG && QK4_0 % DPS == 0, "unsupported head size");
|
||||
static_assert(ST != 1 || RW % 4 == 0, "16-byte staging needs rows of 16-byte multiples");
|
||||
|
||||
stream->submit([&](sycl::handler & cgh) {
|
||||
sycl::local_accessor<fa_dpas_short8, 1> sQ8(sycl::range<1>(RTMAX * NB * 16), cgh); // Q s8 [tile][block][lane]
|
||||
sycl::local_accessor<fa_dpas_float8, 1> sDQ(sycl::range<1>(RTMAX * NB), cgh); // Q scales [tile][block]
|
||||
sycl::local_accessor<float, 1> sS(sycl::range<1>(RMAX * TK), cgh); // S [row][key]
|
||||
sycl::local_accessor<fa_dpas_int8, 1> sP(sycl::range<1>(RMAX * PS / 16), cgh); // P f16 [row][key]
|
||||
sycl::local_accessor<float, 1> sM(sycl::range<1>(RMAX), cgh);
|
||||
sycl::local_accessor<float, 1> sL(sycl::range<1>(RMAX), cgh);
|
||||
sycl::local_accessor<float, 1> sA(sycl::range<1>(RMAX), cgh);
|
||||
sycl::local_accessor<uint32_t, 1> sKV(sycl::range<1>(ST ? TK * SW : 1), cgh);
|
||||
const sycl::nd_range<3> ndr(sycl::range<3>((size_t) ne03 * p.nqc, nkvh, (size_t) p.nsplit * FA_DPAS_WG),
|
||||
sycl::range<3>(1, 1, FA_DPAS_WG));
|
||||
auto kern = [=](sycl::nd_item<3> it) [[sycl::reqd_sub_group_size(16)]] {
|
||||
const int tid = it.get_local_id(2);
|
||||
const int split = it.get_group(2);
|
||||
const int kvh = it.get_group(1);
|
||||
const int seq = it.get_group(0) / p.nqc;
|
||||
const int c0 = (it.get_group(0) % p.nqc) * p.nq;
|
||||
const int G = p.G;
|
||||
const int R = p.nq * G;
|
||||
const int RT = (R + 7) / 8;
|
||||
auto sg = it.get_sub_group();
|
||||
const int w = tid / 16;
|
||||
const int lane = tid % 16;
|
||||
short * sQs = (short *) &sQ8[0];
|
||||
short * sPs = (short *) &sP[0];
|
||||
|
||||
// Q -> s8, one scale per row and block. Lane l holds the 2 dims of K column elements 2 l, 2 l + 1:
|
||||
// q4_0 packs dims l and l + 16 into byte l, q8_0 stores dims in order
|
||||
for (int e = tid; e < RT * 8 * NB; e += FA_DPAS_WG) {
|
||||
const int r = e / NB;
|
||||
const int b = e % NB;
|
||||
const int c = c0 + r / G;
|
||||
const bool real = r < R && c < p.ne01;
|
||||
const float * qr = (const float *) (p.Q + seq * p.nb03 + (int64_t) (real ? c : 0) * p.nb01 +
|
||||
(int64_t) (kvh * G + (real ? r % G : 0)) * p.nb02) + b * QK4_0;
|
||||
float x[QK4_0];
|
||||
float amax = 0.0f;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < QK4_0; ++i) {
|
||||
x[i] = real ? qr[i] * p.scale : 0.0f;
|
||||
amax = sycl::fmax(amax, sycl::fabs(x[i]));
|
||||
}
|
||||
const float dq = amax / 127.0f;
|
||||
const float iq = amax > 0.0f ? 127.0f / amax : 0.0f;
|
||||
const int t = r / 8;
|
||||
const int m = r % 8;
|
||||
((float *) &sDQ[0])[(t * NB + b) * 8 + m] = dq;
|
||||
#pragma unroll
|
||||
for (int l = 0; l < 16; ++l) {
|
||||
const int lo = (int) sycl::rint(x[Q8 ? 2 * l : l] * iq);
|
||||
const int hi = (int) sycl::rint(x[Q8 ? 2 * l + 1 : l + 16] * iq);
|
||||
sQs[((t * NB + b) * 16 + l) * 8 + m] = (short) ((lo & 0xFF) | ((hi & 0xFF) << 8));
|
||||
}
|
||||
}
|
||||
// rows without a query keep P = 0
|
||||
for (int e = tid; e < RMAX * PS; e += FA_DPAS_WG) {
|
||||
sPs[e] = 0;
|
||||
}
|
||||
if (tid < RMAX) {
|
||||
// the sink is one more key with logit sinks[head] and no value; the first key slice owns it
|
||||
const bool sink = p.sinks && split == 0 && tid < R;
|
||||
sM[tid] = sink ? p.sinks[kvh * G + tid % G] : FA_DPAS_MINF;
|
||||
sL[tid] = sink ? 1.0f : 0.0f;
|
||||
sA[tid] = 1.0f;
|
||||
}
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
|
||||
const int k_begin = split * p.chunk;
|
||||
const int k_end = sycl::min(k_begin + p.chunk, p.ne11);
|
||||
const char * Kh = p.K + seq * p.nb13 + kvh * p.nb12;
|
||||
const char * Vh = p.V + seq * p.nb23 + kvh * p.nb22;
|
||||
const sycl::half * mrow = p.mask ? (const sycl::half *) (p.mask + (seq % p.ne33) * p.nb33) : nullptr;
|
||||
|
||||
// O^T: lane = query row, element m = head dim
|
||||
fa_dpas_float8 o[NR16][XT];
|
||||
#pragma unroll
|
||||
for (int t = 0; t < NR16; ++t) {
|
||||
#pragma unroll
|
||||
for (int x = 0; x < XT; ++x) {
|
||||
o[t][x] = 0.0f;
|
||||
}
|
||||
}
|
||||
// the dims of this sub-group lie in one V block
|
||||
const int vb = (DPS * w) / QK4_0;
|
||||
const int vd0 = (DPS * w) % QK4_0;
|
||||
|
||||
// keys past k_end repeat key k_begin, their scores are -inf
|
||||
auto stage = [&](const char * base, int64_t nb, int t0) {
|
||||
if constexpr (ST == 2) {
|
||||
for (int e = tid; e < TK * RW; e += FA_DPAS_WG) {
|
||||
const int key = e / RW;
|
||||
const int q = e - key * RW;
|
||||
const int j = t0 + key < k_end ? t0 + key : k_begin;
|
||||
sKV[key * SW + q] = ((const uint32_t *) (base + (int64_t) j * nb))[q];
|
||||
}
|
||||
return;
|
||||
}
|
||||
constexpr int RV = ST == 1 ? RW / 4 : 1;
|
||||
constexpr int NV = TK * RV;
|
||||
constexpr int PER = (NV + FA_DPAS_WG - 1) / FA_DPAS_WG;
|
||||
sycl::uint4 v[PER];
|
||||
#pragma unroll
|
||||
for (int i = 0; i < PER; ++i) {
|
||||
const int e = tid + i * FA_DPAS_WG;
|
||||
if (e < NV) {
|
||||
const int key = e / RV;
|
||||
const int q = e - key * RV;
|
||||
const int j = t0 + key < k_end ? t0 + key : k_begin;
|
||||
v[i] = ((const sycl::uint4 *) (base + (int64_t) j * nb))[q];
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int i = 0; i < PER; ++i) {
|
||||
const int e = tid + i * FA_DPAS_WG;
|
||||
if (e < NV) {
|
||||
const int key = e / RV;
|
||||
const int q = e - key * RV;
|
||||
const int o = key * SW + 4 * q;
|
||||
sKV[o + 0] = v[i][0];
|
||||
sKV[o + 1] = v[i][1];
|
||||
sKV[o + 2] = v[i][2];
|
||||
sKV[o + 3] = v[i][3];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// the NQ + 1 dwords of block b: of tile key kk in SLM, or of a row that starts 2 ph bytes into dword row[0]
|
||||
auto load_block = [&](const uint32_t * row, int ph, int kk, int b, uint32_t * wv) {
|
||||
const int wd = (2 * ph + BB * b) / 4;
|
||||
if constexpr (ST) {
|
||||
GGML_UNUSED(row);
|
||||
#pragma unroll
|
||||
for (int i = 0; i < NQ + 1; ++i) {
|
||||
wv[i] = sKV[kk * SW + wd + i];
|
||||
}
|
||||
} else {
|
||||
GGML_UNUSED(kk);
|
||||
#pragma unroll
|
||||
for (int i = 0; i < NQ + 1; ++i) {
|
||||
wv[i] = row[wd + i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// start loading the rows of keys t0 .. t0 + TK - 1 into L1 while the current phase computes
|
||||
auto prefetch_rows = [&](const char * base, int64_t nb, int t0) {
|
||||
constexpr int NL = RW * 4 / 64 + 1; // cache lines per row
|
||||
for (int e = tid; e < TK * NL; e += FA_DPAS_WG) {
|
||||
const int j = t0 + e / NL;
|
||||
if (j < k_end) {
|
||||
char * pa = const_cast<char *>(base) + (int64_t) j * nb + (e % NL) * 64;
|
||||
sycl::ext::oneapi::experimental::prefetch(pa, sycl::ext::oneapi::experimental::properties{ sycl::ext::oneapi::experimental::prefetch_hint_L1 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int t0 = k_begin; t0 < k_end; t0 += TK) {
|
||||
if constexpr (ST) {
|
||||
stage(Kh, p.nb11, t0);
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
}
|
||||
prefetch_rows(Vh, p.nb21, t0);
|
||||
|
||||
// S = Q K^T, job = (key group, TC row tiles)
|
||||
const int nch = (RT + TC - 1) / TC;
|
||||
for (int job = w; job < KG * nch; job += FA_DPAS_NSG) {
|
||||
const int kg = job % KG;
|
||||
const int ch = job / KG;
|
||||
const int j = t0 + kg * 16 + lane;
|
||||
const bool jv = j < k_end;
|
||||
const char * kb = Kh + (int64_t) (jv ? j : k_begin) * p.nb11;
|
||||
const int kp = (ST || !ODD) ? 0 : (int) (((uintptr_t) kb >> 1) & 1); // row starts 2 bytes into a dword
|
||||
const uint32_t * kr = (const uint32_t *) (kb - 2 * kp);
|
||||
fa_dpas_float8 s[TC];
|
||||
#pragma unroll
|
||||
for (int tt = 0; tt < TC; ++tt) {
|
||||
s[tt] = 0.0f;
|
||||
}
|
||||
#pragma unroll
|
||||
for (int b = 0; b < NB; ++b) {
|
||||
uint32_t wv[NQ + 1];
|
||||
uint32_t sc;
|
||||
uint32_t q[NQ];
|
||||
load_block(kr, kp, kg * 16 + lane, b, wv);
|
||||
fa_dpas_split_block<NQ>(wv, (b + kp) % 2 != 0, sc, q);
|
||||
const float dk = static_cast<float>(sycl::bit_cast<sycl::half>((uint16_t) sc));
|
||||
#pragma unroll
|
||||
for (int tt = 0; tt < TC; ++tt) {
|
||||
const int t = ch * TC + tt;
|
||||
if (t < RT) {
|
||||
fa_dpas_int8 ia;
|
||||
if constexpr (Q8) {
|
||||
const fa_dpas_int8 bv = { (int) q[0], (int) q[1], (int) q[2], (int) q[3],
|
||||
(int) q[4], (int) q[5], (int) q[6], (int) q[7] };
|
||||
fa_dpas_s8_s8(sQ8[(t * NB + b) * 16 + lane], bv, ia);
|
||||
} else {
|
||||
// u4 (n + 8) -> s4 n
|
||||
const fa_dpas_int4 bv = { (int) (q[0] ^ 0x88888888u), (int) (q[1] ^ 0x88888888u),
|
||||
(int) (q[2] ^ 0x88888888u), (int) (q[3] ^ 0x88888888u) };
|
||||
fa_dpas_s8_s4(sQ8[(t * NB + b) * 16 + lane], bv, ia);
|
||||
}
|
||||
const fa_dpas_float8 ds = sDQ[t * NB + b] * dk;
|
||||
s[tt] += __builtin_convertvector(ia, fa_dpas_float8) * ds;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int tt = 0; tt < TC; ++tt) {
|
||||
const int t = ch * TC + tt;
|
||||
if (t < RT) {
|
||||
#pragma unroll
|
||||
for (int m = 0; m < 8; ++m) {
|
||||
const int r = t * 8 + m;
|
||||
const int c = c0 + r / G;
|
||||
if (r < R) {
|
||||
const bool ok = jv && c < p.ne01;
|
||||
const float mv = (ok && mrow) ? static_cast<float>(mrow[((int64_t) c * p.nb31) / (int64_t) sizeof(sycl::half) + j]) : 0.0f;
|
||||
sS[r * TK + kg * 16 + lane] = ok ? s[tt][m] + mv : -INFINITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
if constexpr (ST) {
|
||||
stage(Vh, p.nb21, t0); // the softmax does not use sKV
|
||||
}
|
||||
|
||||
// online softmax per row -> P
|
||||
for (int r = w; r < R; r += FA_DPAS_NSG) {
|
||||
float sv[KG];
|
||||
float mt = -INFINITY;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < KG; ++i) {
|
||||
sv[i] = sS[r * TK + i * 16 + lane];
|
||||
mt = sycl::fmax(mt, sv[i]);
|
||||
}
|
||||
mt = sycl::reduce_over_group(sg, mt, sycl::maximum<float>());
|
||||
const float m_old = sM[r];
|
||||
const float m_new = sycl::fmax(m_old, mt);
|
||||
float lt = 0.0f;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < KG; ++i) {
|
||||
const float pv = sycl::native::exp(sv[i] - m_new);
|
||||
sPs[r * PS + i * 16 + lane] = (short) sycl::bit_cast<uint16_t>(sycl::half(pv));
|
||||
lt += pv;
|
||||
}
|
||||
lt = sycl::reduce_over_group(sg, lt, sycl::plus<float>());
|
||||
if (lane == 0) {
|
||||
const float a = sycl::native::exp(m_old - m_new);
|
||||
sA[r] = a;
|
||||
sL[r] = sL[r] * a + lt;
|
||||
sM[r] = m_new;
|
||||
}
|
||||
}
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
|
||||
prefetch_rows(Kh, p.nb11, t0 + TK);
|
||||
|
||||
// O^T = diag(alpha) O^T + V^T P^T. A = V^T (8 dims x 16 keys, lane = key), B = P^T (16 keys x 16 rows)
|
||||
#pragma unroll
|
||||
for (int t = 0; t < NR16; ++t) {
|
||||
const float a = sA[t * 16 + lane];
|
||||
#pragma unroll
|
||||
for (int x = 0; x < XT; ++x) {
|
||||
o[t][x] *= a;
|
||||
}
|
||||
}
|
||||
#pragma unroll 1
|
||||
for (int kg = 0; kg < ((ALLPV || w < NPV) ? KG : 0); ++kg) {
|
||||
const int j = t0 + kg * 16 + lane;
|
||||
const bool jv = j < k_end;
|
||||
const char * vbp = Vh + (int64_t) (jv ? j : k_begin) * p.nb21;
|
||||
const int vp = (ST || !ODD) ? 0 : (int) (((uintptr_t) vbp >> 1) & 1);
|
||||
const uint32_t * vr = (const uint32_t *) (vbp - 2 * vp);
|
||||
uint32_t wv[NQ + 1];
|
||||
uint32_t sc;
|
||||
uint32_t q[NQ];
|
||||
load_block(vr, vp, kg * 16 + lane, vb, wv);
|
||||
fa_dpas_split_block<NQ>(wv, (vb + vp) % 2 != 0, sc, q);
|
||||
const float dv = jv ? static_cast<float>(sycl::bit_cast<sycl::half>((uint16_t) sc)) : 0.0f;
|
||||
fa_dpas_short8 va[XT];
|
||||
#pragma unroll
|
||||
for (int x = 0; x < XT; ++x) {
|
||||
#pragma unroll
|
||||
for (int m = 0; m < 8; ++m) {
|
||||
const int d = vd0 + x * 8 + m;
|
||||
int v;
|
||||
if constexpr (Q8) {
|
||||
v = (int8_t) ((q[d / 4] >> (8 * (d % 4))) & 0xFF);
|
||||
} else {
|
||||
const int byte = d % 16;
|
||||
v = (int) ((q[byte / 4] >> (8 * (byte % 4) + 4 * (d / 16))) & 0xF) - 8;
|
||||
}
|
||||
va[x][m] = (short) sycl::bit_cast<uint16_t>(sycl::half((float) v * dv));
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int t = 0; t < NR16; ++t) {
|
||||
const fa_dpas_int8 pb = sP[((t * 16 + lane) * PS + kg * 16) / 16];
|
||||
#pragma unroll
|
||||
for (int x = 0; x < XT; ++x) {
|
||||
fa_dpas_f16(va[x], pb, o[t][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int t = 0; t < NR16; ++t) {
|
||||
const int r = t * 16 + lane;
|
||||
const int c = c0 + r / G;
|
||||
if ((ALLPV || w < NPV) && r < R && c < p.ne01) {
|
||||
const int64_t jdu = ((int64_t) seq * p.ne01 + c) * p.ne02 + kvh * G + r % G;
|
||||
const float l = sL[r];
|
||||
#pragma unroll
|
||||
for (int x = 0; x < XT; ++x) {
|
||||
#pragma unroll
|
||||
for (int m = 0; m < 8; ++m) {
|
||||
const int d = w * DPS + x * 8 + m;
|
||||
if (p.nsplit == 1) {
|
||||
p.dst[jdu * D + d] = l > 0.0f ? o[t][x][m] / l : 0.0f;
|
||||
} else {
|
||||
p.parts[(jdu * p.nsplit + split) * D + d] = o[t][x][m];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p.nsplit > 1 && tid < R && c0 + tid / G < p.ne01) {
|
||||
const int64_t jdu = ((int64_t) seq * p.ne01 + c0 + tid / G) * p.ne02 + kvh * G + tid % G;
|
||||
p.meta[jdu * p.nsplit + split] = sycl::float2(sM[tid], sL[tid]);
|
||||
}
|
||||
};
|
||||
if constexpr (LG) {
|
||||
cgh.parallel_for(ndr, sycl::ext::oneapi::experimental::properties{ sycl::ext::intel::experimental::grf_size<256> }, kern);
|
||||
} else {
|
||||
cgh.parallel_for(ndr, kern);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <int D, int NR16, int KG, int TC, int ST, bool Q8>
|
||||
static void fattn_dpas_case(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
constexpr int TK = 16 * KG;
|
||||
|
||||
const ggml_tensor * Q = dst->src[0];
|
||||
const ggml_tensor * K = dst->src[1];
|
||||
const ggml_tensor * V = dst->src[2];
|
||||
const ggml_tensor * mask = dst->src[3];
|
||||
|
||||
const int ne01 = Q->ne[1];
|
||||
const int ne11 = K->ne[1];
|
||||
const int nkvh = K->ne[2];
|
||||
const int ne03 = Q->ne[3];
|
||||
|
||||
fa_dpas_params p;
|
||||
p.Q = (const char *) Q->data;
|
||||
p.K = (const char *) K->data;
|
||||
p.V = (const char *) V->data;
|
||||
p.mask = mask ? (const char *) mask->data : nullptr;
|
||||
p.sinks = dst->src[4] ? (const float *) dst->src[4]->data : nullptr;
|
||||
p.dst = (float *) dst->data;
|
||||
memcpy(&p.scale, (const float *) dst->op_params + 0, sizeof(float));
|
||||
p.G = Q->ne[2] / K->ne[2];
|
||||
p.nq = std::max(1, std::min(ne01, 16 * NR16 / p.G));
|
||||
p.nqc = (ne01 + p.nq - 1) / p.nq;
|
||||
p.ne01 = ne01;
|
||||
p.ne02 = Q->ne[2];
|
||||
p.ne11 = ne11;
|
||||
p.ne33 = mask ? mask->ne[3] : 1;
|
||||
p.nb01 = Q->nb[1];
|
||||
p.nb02 = Q->nb[2];
|
||||
p.nb03 = Q->nb[3];
|
||||
p.nb11 = K->nb[1];
|
||||
p.nb12 = K->nb[2];
|
||||
p.nb13 = K->nb[3];
|
||||
p.nb21 = V->nb[1];
|
||||
p.nb22 = V->nb[2];
|
||||
p.nb23 = V->nb[3];
|
||||
p.nb31 = mask ? mask->nb[1] : 0;
|
||||
p.nb33 = mask ? mask->nb[3] : 0;
|
||||
|
||||
// split the keys so that all work-groups run in one wave
|
||||
// SLM fits about 4 work-groups per Xe2 core with 16 rows, 2 with 32. Xe2 core = 8 EUs, nsm = EUs / 16.
|
||||
const int xe_cores = std::max(1, ggml_sycl_info().devices[ctx.device].nsm * 2);
|
||||
const int wave = xe_cores * (NR16 == 1 ? 4 : 2);
|
||||
const int ntiles = (ne11 + TK - 1) / TK;
|
||||
int nsplit = std::max(1, std::min(wave / (nkvh * ne03 * p.nqc), ntiles));
|
||||
p.chunk = ((ntiles + nsplit - 1) / nsplit) * TK;
|
||||
nsplit = (ne11 + p.chunk - 1) / p.chunk;
|
||||
p.nsplit = nsplit;
|
||||
|
||||
ggml_sycl_pool_alloc<float> parts(ctx.pool());
|
||||
ggml_sycl_pool_alloc<sycl::float2> meta(ctx.pool());
|
||||
if (nsplit > 1) {
|
||||
parts.alloc((size_t) nsplit * ggml_nelements(dst));
|
||||
meta.alloc((size_t) nsplit * ggml_nrows(dst));
|
||||
}
|
||||
p.parts = parts.get();
|
||||
p.meta = meta.get();
|
||||
|
||||
dpct::queue_ptr stream = ctx.stream();
|
||||
// the large register file pays off for 32-row launches from about 4K keys
|
||||
if (NR16 == 2 && ne11 >= 4096) {
|
||||
fattn_dpas_kernel<D, NR16, KG, TC, ST, true, Q8>(p, nkvh, ne03, stream);
|
||||
} else {
|
||||
fattn_dpas_kernel<D, NR16, KG, TC, ST, false, Q8>(p, nkvh, ne03, stream);
|
||||
}
|
||||
|
||||
if (nsplit > 1) {
|
||||
float * parts_p = parts.get();
|
||||
sycl::float2 * meta_p = meta.get();
|
||||
float * dst_p = (float *) dst->data;
|
||||
stream->submit([&](sycl::handler & cgh) {
|
||||
sycl::local_accessor<uint8_t, 1> lm(sycl::range<1>(nsplit * sizeof(sycl::float2)), cgh);
|
||||
cgh.parallel_for(
|
||||
sycl::nd_range<3>(sycl::range<3>(Q->ne[3], Q->ne[2], (size_t) ne01 * D), sycl::range<3>(1, 1, D)),
|
||||
[=](sycl::nd_item<3>) [[sycl::reqd_sub_group_size(16)]] {
|
||||
flash_attn_combine_results<D>(parts_p, meta_p, dst_p, nsplit,
|
||||
lm.get_multi_ptr<sycl::access::decorated::no>().get());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Q8> static void fattn_dpas_dispatch(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * Q = dst->src[0];
|
||||
const ggml_tensor * K = dst->src[1];
|
||||
const ggml_tensor * V = dst->src[2];
|
||||
|
||||
const int D = K->ne[0];
|
||||
const int G = Q->ne[2] / K->ne[2];
|
||||
const int ne01 = Q->ne[1];
|
||||
|
||||
bool aligned16 = true;
|
||||
for (const ggml_tensor * t : { K, V }) {
|
||||
aligned16 = aligned16 && t->nb[1] % 16 == 0 && t->nb[2] % 16 == 0 && t->nb[3] % 16 == 0 &&
|
||||
(uintptr_t) t->data % 16 == 0;
|
||||
}
|
||||
|
||||
// single token decode: staged K/V tiles are faster at long context
|
||||
if (D == 256 && ne01 == 1 && G <= 16 && aligned16) {
|
||||
fattn_dpas_case<256, 1, 8, 1, 1, Q8>(ctx, dst);
|
||||
return;
|
||||
}
|
||||
const bool rows16 = ne01 * G <= 16;
|
||||
// 256-key tiles for one row tile; 128 at head size 512 so S and the Q copy stay in SLM for the planned waves
|
||||
#define FATTN_DPAS_D(D_, KG16_) \
|
||||
case D_: \
|
||||
if (rows16) { \
|
||||
fattn_dpas_case<D_, 1, KG16_, 1, 0, Q8>(ctx, dst); \
|
||||
} else { \
|
||||
fattn_dpas_case<D_, 2, 8, 2, 0, Q8>(ctx, dst); \
|
||||
} \
|
||||
break;
|
||||
// q8_0 at head size 128: its 136-byte rows gather poorly per lane, stage them in SLM by coalesced dword loads
|
||||
bool aligned4 = true;
|
||||
for (const ggml_tensor * t : { K, V }) {
|
||||
aligned4 = aligned4 && t->nb[1] % 4 == 0 && t->nb[2] % 4 == 0 && t->nb[3] % 4 == 0 && (uintptr_t) t->data % 4 == 0;
|
||||
}
|
||||
if (Q8 && D == 128 && aligned4) {
|
||||
if (rows16) {
|
||||
fattn_dpas_case<128, 1, 16, 1, 2, Q8>(ctx, dst);
|
||||
} else {
|
||||
fattn_dpas_case<128, 2, 8, 2, 2, Q8>(ctx, dst);
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (D) {
|
||||
FATTN_DPAS_D( 64, 16)
|
||||
FATTN_DPAS_D( 96, 16)
|
||||
FATTN_DPAS_D(128, 16)
|
||||
FATTN_DPAS_D(256, 16)
|
||||
FATTN_DPAS_D(512, 8)
|
||||
default:
|
||||
GGML_ABORT("fattn-dpas: unsupported head size %d", D);
|
||||
}
|
||||
#undef FATTN_DPAS_D
|
||||
}
|
||||
|
||||
void ggml_sycl_flash_attn_ext_dpas(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
if (dst->src[1]->type == GGML_TYPE_Q8_0) {
|
||||
fattn_dpas_dispatch<true>(ctx, dst);
|
||||
} else {
|
||||
fattn_dpas_dispatch<false>(ctx, dst);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ggml_sycl_flash_attn_ext_dpas(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
GGML_UNUSED(ctx);
|
||||
GGML_UNUSED(dst);
|
||||
GGML_ABORT("fattn-dpas: not built for this target");
|
||||
}
|
||||
|
||||
#endif // GGML_SYCL_FA_DPAS_BUILD
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef GGML_SYCL_FATTN_DPAS_HPP
|
||||
#define GGML_SYCL_FATTN_DPAS_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
// q4_0 / q8_0 K/V flash attention for decode and small batches on Xe2 XMX (s8 x s4 / s8 x s8 DPAS)
|
||||
bool ggml_sycl_flash_attn_ext_dpas_supported(int device, const ggml_tensor * dst);
|
||||
void ggml_sycl_flash_attn_ext_dpas(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
#endif // GGML_SYCL_FATTN_DPAS_HPP
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "fattn.hpp"
|
||||
#include "fattn-onednn.hpp"
|
||||
#include "fattn-sparse.hpp"
|
||||
#include "fattn-dpas.hpp"
|
||||
|
||||
#define FATTN_VEC_CASE(D, type_K, type_V) \
|
||||
{ \
|
||||
@@ -97,6 +98,7 @@ static void ggml_sycl_flash_attn_ext_vec(ggml_backend_sycl_context & ctx, ggml_t
|
||||
enum best_fattn_kernel {
|
||||
BEST_FATTN_KERNEL_NONE = 0,
|
||||
BEST_FATTN_KERNEL_VEC = 100,
|
||||
BEST_FATTN_KERNEL_DPAS = 120, // q4_0 / q8_0 K/V decode on Xe2 XMX
|
||||
BEST_FATTN_KERNEL_ONEDNN = 150, // oneDNN SDPA: native F16 (PR #25222)
|
||||
BEST_FATTN_KERNEL_TILE = 200,
|
||||
BEST_FATTN_KERNEL_MKL = 300,
|
||||
@@ -244,6 +246,11 @@ static best_fattn_kernel ggml_sycl_get_best_fattn_kernel(const int device, const
|
||||
return BEST_FATTN_KERNEL_NONE;
|
||||
}
|
||||
|
||||
// q4_0 / q8_0 K/V decode and small batches on Xe2: read the cache directly, no F16 copy of K and V
|
||||
if (ggml_sycl_flash_attn_ext_dpas_supported(device, dst)) {
|
||||
return BEST_FATTN_KERNEL_DPAS;
|
||||
}
|
||||
|
||||
// For small batch sizes the vector kernel may be preferable over the kernels optimized for large batch sizes.
|
||||
// BF16 is excluded: the VEC kernel has no BF16 template (it needs GGML_SYCL_FA_ALL_QUANTS for non-F16/Q4_0/Q8_0).
|
||||
const bool has_bf16 = (K->type == GGML_TYPE_BF16 || V->type == GGML_TYPE_BF16);
|
||||
@@ -318,6 +325,7 @@ void ggml_sycl_flash_attn_ext(ggml_backend_sycl_context & ctx, ggml_tensor * dst
|
||||
if (k == BEST_FATTN_KERNEL_MKL) kname = "MKL";
|
||||
if (k == BEST_FATTN_KERNEL_ONEDNN) kname = "ONEDNN";
|
||||
if (k == BEST_FATTN_KERNEL_VEC) kname = "VEC";
|
||||
if (k == BEST_FATTN_KERNEL_DPAS) kname = "DPAS";
|
||||
int64_t delta = 0;
|
||||
if (Dk == 256) {
|
||||
delta = cur_nkv - last_nkv_d256;
|
||||
@@ -350,6 +358,9 @@ void ggml_sycl_flash_attn_ext(ggml_backend_sycl_context & ctx, ggml_tensor * dst
|
||||
case BEST_FATTN_KERNEL_VEC:
|
||||
ggml_sycl_flash_attn_ext_vec(ctx, dst);
|
||||
break;
|
||||
case BEST_FATTN_KERNEL_DPAS:
|
||||
ggml_sycl_flash_attn_ext_dpas(ctx, dst);
|
||||
break;
|
||||
case BEST_FATTN_KERNEL_MKL:
|
||||
ggml_sycl_flash_attn_ext_mkl(ctx, dst);
|
||||
break;
|
||||
@@ -377,6 +388,7 @@ void ggml_sycl_flash_attn_ext(ggml_backend_sycl_context & ctx, ggml_tensor * dst
|
||||
if (kb == BEST_FATTN_KERNEL_MKL) kname = "MKL";
|
||||
if (kb == BEST_FATTN_KERNEL_TILE) kname = "TILE";
|
||||
if (kb == BEST_FATTN_KERNEL_VEC) kname = "VEC";
|
||||
if (kb == BEST_FATTN_KERNEL_DPAS) kname = "DPAS";
|
||||
GGML_LOG_INFO("[FA-DIAG] #%d %s D=%d n_kv=%lld n_q=%lld "
|
||||
"n_qh=%lld n_kvh=%lld K=%s V=%s "
|
||||
"nb1=%zu nb2=%zu first 64 floats:\n",
|
||||
|
||||
@@ -657,7 +657,7 @@ void ggml_sycl_op_get_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) {
|
||||
case GGML_TYPE_Q5_K:
|
||||
if (src0_reordered) {
|
||||
const void * vx = dst->src[0]->data;
|
||||
get_rows_sycl_reorder_k<3, 64, K_SCALE_SIZE>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
get_rows_sycl_reorder_k<3, 32, K_SCALE_SIZE>(dst->src[0], dst->src[1], dst, src1_i32, (float *)dst->data, ctx.stream(),
|
||||
[=](float * y, int64_t ib, uint8_t * scales_local, const sycl::nd_item<3> & item_ct1, int64_t nb) {
|
||||
dequantize_block_q5_K_reorder(vx, y, ib, scales_local, item_ct1, nb);
|
||||
});
|
||||
|
||||
+413
-498
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -1340,7 +1340,7 @@ mul_mat_q(const void *__restrict__ vx, const void *__restrict__ vy,
|
||||
#define MMQ_X_Q4_0_RDNA1 64
|
||||
#define MMQ_Y_Q4_0_RDNA1 64
|
||||
#define NWARPS_Q4_0_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q4_0_AMPERE 4
|
||||
#define MMQ_Y_Q4_0_AMPERE 32
|
||||
#define NWARPS_Q4_0_AMPERE 4
|
||||
@@ -1384,7 +1384,7 @@ template <bool need_check> static void
|
||||
#define MMQ_X_Q4_1_RDNA1 64
|
||||
#define MMQ_Y_Q4_1_RDNA1 64
|
||||
#define NWARPS_Q4_1_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q4_1_AMPERE 4
|
||||
#define MMQ_Y_Q4_1_AMPERE 32
|
||||
#define NWARPS_Q4_1_AMPERE 4
|
||||
@@ -1427,7 +1427,7 @@ template <bool need_check> static void
|
||||
#define MMQ_X_Q5_0_RDNA1 64
|
||||
#define MMQ_Y_Q5_0_RDNA1 64
|
||||
#define NWARPS_Q5_0_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q5_0_AMPERE 4
|
||||
#define MMQ_Y_Q5_0_AMPERE 32
|
||||
#define NWARPS_Q5_0_AMPERE 4
|
||||
@@ -1470,7 +1470,7 @@ template <bool need_check> static void
|
||||
#define MMQ_X_Q5_1_RDNA1 64
|
||||
#define MMQ_Y_Q5_1_RDNA1 64
|
||||
#define NWARPS_Q5_1_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q5_1_AMPERE 4
|
||||
#define MMQ_Y_Q5_1_AMPERE 32
|
||||
#define NWARPS_Q5_1_AMPERE 4
|
||||
@@ -1513,7 +1513,7 @@ mul_mat_q5_1(
|
||||
#define MMQ_X_Q8_0_RDNA1 64
|
||||
#define MMQ_Y_Q8_0_RDNA1 64
|
||||
#define NWARPS_Q8_0_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q8_0_AMPERE 4
|
||||
#define MMQ_Y_Q8_0_AMPERE 32
|
||||
#define NWARPS_Q8_0_AMPERE 4
|
||||
@@ -1556,7 +1556,7 @@ template <bool need_check> static void
|
||||
#define MMQ_X_Q2_K_RDNA1 128
|
||||
#define MMQ_Y_Q2_K_RDNA1 32
|
||||
#define NWARPS_Q2_K_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q2_K_AMPERE 4
|
||||
#define MMQ_Y_Q2_K_AMPERE 32
|
||||
#define NWARPS_Q2_K_AMPERE 4
|
||||
@@ -1600,7 +1600,7 @@ mul_mat_q2_K(
|
||||
#define MMQ_X_Q3_K_RDNA1 32
|
||||
#define MMQ_Y_Q3_K_RDNA1 128
|
||||
#define NWARPS_Q3_K_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q3_K_AMPERE 4
|
||||
#define MMQ_Y_Q3_K_AMPERE 32
|
||||
#define NWARPS_Q3_K_AMPERE 4
|
||||
@@ -1645,7 +1645,7 @@ mul_mat_q3_K(
|
||||
#define MMQ_X_Q4_K_RDNA1 32
|
||||
#define MMQ_Y_Q4_K_RDNA1 64
|
||||
#define NWARPS_Q4_K_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q4_K_AMPERE 4
|
||||
#define MMQ_Y_Q4_K_AMPERE 32
|
||||
#define NWARPS_Q4_K_AMPERE 4
|
||||
@@ -1689,7 +1689,7 @@ template <bool need_check> static void
|
||||
#define MMQ_X_Q5_K_RDNA1 32
|
||||
#define MMQ_Y_Q5_K_RDNA1 64
|
||||
#define NWARPS_Q5_K_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q5_K_AMPERE 4
|
||||
#define MMQ_Y_Q5_K_AMPERE 32
|
||||
#define NWARPS_Q5_K_AMPERE 4
|
||||
@@ -1733,7 +1733,7 @@ mul_mat_q5_K(
|
||||
#define MMQ_X_Q6_K_RDNA1 32
|
||||
#define MMQ_Y_Q6_K_RDNA1 64
|
||||
#define NWARPS_Q6_K_RDNA1 8
|
||||
#if defined(SYCL_USE_XMX)
|
||||
#if defined(GGML_SYCL_SUPPORT_XMX) && !defined(GGML_SYCL_FORCE_MMQ)
|
||||
#define MMQ_X_Q6_K_AMPERE 4
|
||||
#define MMQ_Y_Q6_K_AMPERE 32
|
||||
#define NWARPS_Q6_K_AMPERE 4
|
||||
|
||||
+160
-670
File diff suppressed because it is too large
Load Diff
@@ -848,18 +848,18 @@ struct vk_device_struct {
|
||||
vk_pipeline pipeline_set_rows_i64[2][GGML_TYPE_COUNT];
|
||||
vk_pipeline pipeline_norm_f32;
|
||||
vk_pipeline pipeline_group_norm_f32;
|
||||
vk_pipeline pipeline_rms_norm_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_mul_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_add_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_add_mul_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_add_mul_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_set_rows_f32_f32;
|
||||
vk_pipeline pipeline_rms_norm_set_rows_f32_f16;
|
||||
vk_pipeline pipeline_rms_norm_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_partials_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_rope_f32_f32;
|
||||
vk_pipeline pipeline_rms_norm_mul_rope_f32_f16;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_rope_f32_f32;
|
||||
std::array<vk_pipeline, 3> pipeline_rms_norm_mul_rope_f32_f16;
|
||||
vk_pipeline pipeline_rms_norm_back_f32;
|
||||
vk_pipeline pipeline_l2_norm_f32;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user