mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-08-13 16:26:55 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d2869c6e5 | |||
| 4a84b0ad10 | |||
| f65e568fd8 | |||
| 0d0bfcd4fd | |||
| eeae28b67e | |||
| 154d57af3e | |||
| 1ee1cd9bc6 | |||
| 8efbf65dbd | |||
| d415e65a57 | |||
| decaf508bb | |||
| e79e4bf660 | |||
| d86c7d62df | |||
| f2efd64141 | |||
| 094e53db1c | |||
| a6040c925c | |||
| 1f368f354d | |||
| e21152dc96 | |||
| 8e7f22b67e | |||
| 84e908c625 | |||
| 9558fa44c9 | |||
| 7a9ff95979 | |||
| 680a9ae63d | |||
| d8a8beac22 | |||
| 132753bf4e | |||
| ece98b87f7 | |||
| af05a42a7c | |||
| 13fd0bb55e | |||
| 5d9e5ac30e | |||
| 4dd127584b | |||
| 89e0aa6fd3 | |||
| a4a4c51f3d | |||
| a7cd2f0e98 | |||
| 55f453b924 | |||
| 6eff593262 | |||
| cb27fe9c35 | |||
| 0b1bad14ff | |||
| 7b13a8404d | |||
| ebb546b7e9 | |||
| 5988633170 | |||
| f785fc9ea4 |
@@ -119,27 +119,27 @@ jobs:
|
||||
version_major: ${{ env.OPENVINO_VERSION_MAJOR }}
|
||||
version_full: ${{ env.OPENVINO_VERSION_FULL }}
|
||||
|
||||
windows-2022-rocm-cache:
|
||||
runs-on: windows-2022
|
||||
# windows-2022-rocm-cache:
|
||||
# runs-on: windows-2022
|
||||
|
||||
env:
|
||||
# Make sure this is in sync with release.yml and build-cuda-windows.yml
|
||||
ROCM_VERSION: "7.14.0"
|
||||
# env:
|
||||
# # Make sure this is in sync with release.yml and build-cuda-windows.yml
|
||||
# ROCM_VERSION: "7.14.0"
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
# steps:
|
||||
# - name: Clone
|
||||
# id: checkout
|
||||
# uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Cache
|
||||
uses: actions/cache@v5
|
||||
id: cache-rocm
|
||||
with:
|
||||
path: C:\TheRock\build
|
||||
key: rocm-wheels-${{ env.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
# - name: Setup Cache
|
||||
# uses: actions/cache@v5
|
||||
# id: cache-rocm
|
||||
# with:
|
||||
# path: C:\TheRock\build
|
||||
# key: rocm-wheels-${{ env.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
|
||||
- name: Setup ROCm
|
||||
if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/windows-setup-rocm
|
||||
with:
|
||||
version: ${{ env.ROCM_VERSION }}
|
||||
# - name: Setup ROCm
|
||||
# if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
# uses: ./.github/actions/windows-setup-rocm
|
||||
# with:
|
||||
# version: ${{ env.ROCM_VERSION }}
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: [self-hosted, Linux, CPU]
|
||||
runs-on: [self-hosted, Linux]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
@@ -21,15 +21,21 @@ jobs:
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_IS_DEV=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release
|
||||
cmake --build build --config Release -j $(nproc)
|
||||
cmake --install build --prefix "$PREFIX" --config Release
|
||||
|
||||
export LLAMA_CONFIG="$PREFIX"/lib/cmake/llama/llama-config.cmake
|
||||
tclsh <<'EOF'
|
||||
set build(commit) [string trim [exec git rev-parse --short HEAD]]
|
||||
set build(number) [string trim [exec git rev-list --count HEAD]]
|
||||
set build(version) "0.0.$build(number)"
|
||||
|
||||
set cmakelists [read [open "CMakeLists.txt" r]]
|
||||
regexp {set\(LLAMA_VERSION_MAJOR\s+(\d+)\)} $cmakelists -> major
|
||||
regexp {set\(LLAMA_VERSION_MINOR\s+(\d+)\)} $cmakelists -> minor
|
||||
regexp {set\(LLAMA_VERSION_PATCH\s+(\d+)\)} $cmakelists -> patch
|
||||
set build(version) "$major.$minor.$patch"
|
||||
|
||||
set llamaconfig [read [open "$env(LLAMA_CONFIG)" r]]
|
||||
set checks [list "set\\(LLAMA_VERSION \\s+$build(version)\\)" \
|
||||
@@ -48,4 +54,4 @@ jobs:
|
||||
|
||||
cd examples/simple-cmake-pkg
|
||||
cmake -S . -B build -DCMAKE_PREFIX_PATH="$PREFIX"/lib/cmake
|
||||
cmake --build build
|
||||
cmake --build build -j $(nproc)
|
||||
|
||||
@@ -94,8 +94,10 @@ jobs:
|
||||
id: cmake_build
|
||||
run: |
|
||||
cmake -B build \
|
||||
-DGGML_NATIVE=OFF \
|
||||
-DLLAMA_FATAL_WARNINGS=ON \
|
||||
-DGGML_RPC=ON
|
||||
-DGGML_RPC=ON \
|
||||
-DGGML_NATIVE=OFF
|
||||
time cmake --build build --config Release -j $(nproc)
|
||||
|
||||
- name: Test
|
||||
|
||||
@@ -97,15 +97,15 @@ jobs:
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Cache ROCm Installation
|
||||
uses: actions/cache@v5
|
||||
id: cache-rocm
|
||||
with:
|
||||
path: C:\TheRock\build
|
||||
key: rocm-wheels-${{ env.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
# - name: Cache ROCm Installation
|
||||
# uses: actions/cache@v5
|
||||
# id: cache-rocm
|
||||
# with:
|
||||
# path: C:\TheRock\build
|
||||
# key: rocm-wheels-${{ env.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
|
||||
- name: Setup ROCm
|
||||
if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
# if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/windows-setup-rocm
|
||||
with:
|
||||
version: ${{ env.ROCM_VERSION }}
|
||||
|
||||
@@ -39,9 +39,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# thread and address doesn't run properly on some self hosted machines, so run it on Github instead
|
||||
- sanitizer: ADDRESS
|
||||
machine: [self-hosted, X64, Linux]
|
||||
# thread doesn't run properly on some self hosted machines, so run it on Github instead
|
||||
machine: ubuntu-24.04
|
||||
- sanitizer: THREAD
|
||||
machine: ubuntu-24.04
|
||||
- sanitizer: UNDEFINED
|
||||
@@ -54,14 +54,14 @@ jobs:
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.21
|
||||
if: ${{ matrix.sanitizer == 'THREAD' }}
|
||||
with:
|
||||
key: ctest-thread-ubuntu-24.04
|
||||
variant: ccache
|
||||
evict-old-files: 1d
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
# - name: ccache
|
||||
# uses: ggml-org/ccache-action@v1.2.21
|
||||
# if: ${{ matrix.sanitizer != 'UNDEFINED' }}
|
||||
# with:
|
||||
# key: ctest-${{ matrix.sanitizer }}-ubuntu-24.04
|
||||
# variant: ccache
|
||||
# evict-old-files: 1d
|
||||
# save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
|
||||
# with UNDEFINED sanitizer, we have to build in Debug to avoid GCC 13 false-positive warnings
|
||||
- name: Build (undefined)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Make Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Dry run - validate without creating the tag'
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
make-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run release checks
|
||||
id: checks
|
||||
run: bash scripts/make-release-checks.sh ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
|
||||
- name: Create release tag
|
||||
if: ${{ github.event.inputs.dry_run == 'false' }}
|
||||
run: |
|
||||
VERSION="${{ steps.checks.outputs.version }}"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -a "${VERSION}" -m "Release ${VERSION}"
|
||||
git push origin "${VERSION}"
|
||||
echo "Created and pushed tag ${VERSION}"
|
||||
|
||||
- name: Dry run summary
|
||||
if: ${{ github.event.inputs.dry_run == 'true' }}
|
||||
run: |
|
||||
echo "Dry run complete - all checks passed."
|
||||
echo "Would have created tag: ${{ steps.checks.outputs.version }}"
|
||||
+109
-107
@@ -750,6 +750,8 @@ jobs:
|
||||
|
||||
windows-rocm:
|
||||
needs: [check-release]
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
strategy:
|
||||
@@ -772,15 +774,15 @@ jobs:
|
||||
key: windows-rocm-${{ matrix.ROCM_VERSION }}-${{ matrix.build }}
|
||||
evict-old-files: 1d
|
||||
|
||||
- name: Cache ROCm Installation
|
||||
id: cache-rocm
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: C:\TheRock\build
|
||||
key: rocm-wheels-${{ matrix.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
# - name: Cache ROCm Installation
|
||||
# id: cache-rocm
|
||||
# uses: actions/cache@v5
|
||||
# with:
|
||||
# path: C:\TheRock\build
|
||||
# key: rocm-wheels-${{ matrix.ROCM_VERSION }}-multi-arch-${{ runner.os }}
|
||||
|
||||
- name: Setup ROCm
|
||||
if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
# if: steps.cache-rocm.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/windows-setup-rocm
|
||||
with:
|
||||
version: ${{ matrix.ROCM_VERSION }}
|
||||
@@ -1283,123 +1285,123 @@ jobs:
|
||||
path: llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz
|
||||
name: llama-bin-ubuntu-sycl-${{ matrix.build }}-x64.tar.gz
|
||||
|
||||
ubuntu-22-rocm:
|
||||
needs: [check-release, get-version]
|
||||
if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
# ubuntu-22-rocm:
|
||||
# needs: [check-release, get-version]
|
||||
# if: ${{ needs.check-release.outputs.should_release == 'true' }}
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
# runs-on: ubuntu-22.04
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
# permissions:
|
||||
# actions: write
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- ROCM_VERSION: "7.14.0"
|
||||
gpu_targets: "gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
|
||||
build: 'x64'
|
||||
# strategy:
|
||||
# matrix:
|
||||
# include:
|
||||
# - ROCM_VERSION: "7.14.0"
|
||||
# gpu_targets: "gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
|
||||
# build: 'x64'
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# steps:
|
||||
# - name: Clone
|
||||
# id: checkout
|
||||
# uses: actions/checkout@v6
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "tools/ui/package-lock.json"
|
||||
# - name: Setup Node.js
|
||||
# uses: actions/setup-node@v6
|
||||
# with:
|
||||
# node-version: "24"
|
||||
# cache: "npm"
|
||||
# cache-dependency-path: "tools/ui/package-lock.json"
|
||||
|
||||
- name: Free up disk space
|
||||
uses: ggml-org/free-disk-space@v1.3.1
|
||||
with:
|
||||
tool-cache: true
|
||||
# - name: Free up disk space
|
||||
# uses: ggml-org/free-disk-space@v1.3.1
|
||||
# with:
|
||||
# tool-cache: true
|
||||
|
||||
- name: ccache
|
||||
uses: ggml-org/ccache-action@v1.2.21
|
||||
with:
|
||||
key: release-ubuntu-22.04-rocm-${{ matrix.ROCM_VERSION }}
|
||||
# # - name: ccache
|
||||
# # uses: ggml-org/ccache-action@v1.2.21
|
||||
# # with:
|
||||
# # key: release-ubuntu-22.04-rocm-${{ matrix.ROCM_VERSION }}
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
sudo apt install -y build-essential git cmake wget
|
||||
# - name: Dependencies
|
||||
# id: depends
|
||||
# run: |
|
||||
# sudo apt install -y build-essential git cmake wget
|
||||
|
||||
- name: Setup TheRock with Wheels
|
||||
id: therock_env
|
||||
run: |
|
||||
# Create Python virtual environment
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
# - name: Setup TheRock with Wheels
|
||||
# id: therock_env
|
||||
# run: |
|
||||
# # Create Python virtual environment
|
||||
# python3 -m venv .venv
|
||||
# source .venv/bin/activate
|
||||
|
||||
# Install ROCm wheels for build
|
||||
# libraries = HIP runtime and CMake configs needed for linking
|
||||
# devel = compilers, headers, static libs
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --index-url https://repo.amd.com/rocm/whl-multi-arch/ "rocm[libraries,devel]==${{ matrix.ROCM_VERSION }}"
|
||||
# # Install ROCm wheels for build
|
||||
# # libraries = HIP runtime and CMake configs needed for linking
|
||||
# # devel = compilers, headers, static libs
|
||||
# python -m pip install --upgrade pip
|
||||
# python -m pip install --index-url https://repo.amd.com/rocm/whl-multi-arch/ "rocm[libraries,devel]==${{ matrix.ROCM_VERSION }}"
|
||||
|
||||
# Get ROCm installation paths using the rocm-sdk CLI tool
|
||||
ROCM_PATH=$(rocm-sdk path --root)
|
||||
CMAKE_PATH=$(rocm-sdk path --cmake)
|
||||
BIN_PATH=$(rocm-sdk path --bin)
|
||||
echo "ROCM_PATH=$ROCM_PATH"
|
||||
echo "CMAKE_PATH=$CMAKE_PATH"
|
||||
echo "BIN_PATH=$BIN_PATH"
|
||||
# # Get ROCm installation paths using the rocm-sdk CLI tool
|
||||
# ROCM_PATH=$(rocm-sdk path --root)
|
||||
# CMAKE_PATH=$(rocm-sdk path --cmake)
|
||||
# BIN_PATH=$(rocm-sdk path --bin)
|
||||
# echo "ROCM_PATH=$ROCM_PATH"
|
||||
# echo "CMAKE_PATH=$CMAKE_PATH"
|
||||
# echo "BIN_PATH=$BIN_PATH"
|
||||
|
||||
# Set environment variables
|
||||
echo "ROCM_PATH=$ROCM_PATH" >> $GITHUB_ENV
|
||||
echo "CMAKE_PREFIX_PATH=$CMAKE_PATH" >> $GITHUB_ENV
|
||||
echo "HIP_PATH=$ROCM_PATH" >> $GITHUB_ENV
|
||||
echo "PATH=$BIN_PATH:${PATH}" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=$ROCM_PATH/lib:${LD_LIBRARY_PATH:-}" >> $GITHUB_ENV
|
||||
# # Set environment variables
|
||||
# echo "ROCM_PATH=$ROCM_PATH" >> $GITHUB_ENV
|
||||
# echo "CMAKE_PREFIX_PATH=$CMAKE_PATH" >> $GITHUB_ENV
|
||||
# echo "HIP_PATH=$ROCM_PATH" >> $GITHUB_ENV
|
||||
# echo "PATH=$BIN_PATH:${PATH}" >> $GITHUB_ENV
|
||||
# echo "LD_LIBRARY_PATH=$ROCM_PATH/lib:${LD_LIBRARY_PATH:-}" >> $GITHUB_ENV
|
||||
|
||||
# Keep venv activated for subsequent steps
|
||||
echo "$(pwd)/.venv/bin" >> $GITHUB_PATH
|
||||
# # Keep venv activated for subsequent steps
|
||||
# echo "$(pwd)/.venv/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build with native CMake HIP support
|
||||
id: cmake_build
|
||||
run: |
|
||||
cmake -B build -S . \
|
||||
-DCMAKE_HIP_COMPILER="$(hipconfig -l)/clang" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DGGML_BACKEND_DL=ON \
|
||||
-DGGML_NATIVE=OFF \
|
||||
-DCMAKE_INSTALL_RPATH='$ORIGIN' \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||
-DGGML_CPU_ALL_VARIANTS=ON \
|
||||
-DGPU_TARGETS="${{ matrix.gpu_targets }}" \
|
||||
-DGGML_HIP=ON \
|
||||
-DHIP_PLATFORM=amd \
|
||||
-DHF_UI_VERSION=${{ needs.get-version.outputs.ui_version }} \
|
||||
${{ env.CMAKE_ARGS }}
|
||||
cmake --build build --config Release -j $(nproc)
|
||||
# - name: Build with native CMake HIP support
|
||||
# id: cmake_build
|
||||
# run: |
|
||||
# cmake -B build -S . \
|
||||
# -DCMAKE_HIP_COMPILER="$(hipconfig -l)/clang" \
|
||||
# -DCMAKE_BUILD_TYPE=Release \
|
||||
# -DGGML_BACKEND_DL=ON \
|
||||
# -DGGML_NATIVE=OFF \
|
||||
# -DCMAKE_INSTALL_RPATH='$ORIGIN' \
|
||||
# -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||
# -DGGML_CPU_ALL_VARIANTS=ON \
|
||||
# -DGPU_TARGETS="${{ matrix.gpu_targets }}" \
|
||||
# -DGGML_HIP=ON \
|
||||
# -DHIP_PLATFORM=amd \
|
||||
# -DHF_UI_VERSION=${{ needs.get-version.outputs.ui_version }} \
|
||||
# ${{ env.CMAKE_ARGS }}
|
||||
# cmake --build build --config Release -j $(nproc)
|
||||
|
||||
- name: ccache-clear
|
||||
uses: ./.github/actions/ccache-clear
|
||||
with:
|
||||
key: release-ubuntu-22.04-rocm-${{ matrix.ROCM_VERSION }}
|
||||
# # - name: ccache-clear
|
||||
# # uses: ./.github/actions/ccache-clear
|
||||
# # with:
|
||||
# # key: release-ubuntu-22.04-rocm-${{ matrix.ROCM_VERSION }}
|
||||
|
||||
- name: Determine tag name
|
||||
id: tag
|
||||
uses: ./.github/actions/get-tag-name
|
||||
# - name: Determine tag name
|
||||
# id: tag
|
||||
# uses: ./.github/actions/get-tag-name
|
||||
|
||||
- name: Get ROCm short version
|
||||
run: echo "ROCM_VERSION_SHORT=$(echo '${{ matrix.ROCM_VERSION }}' | cut -d '.' -f 1,2)" >> $GITHUB_ENV
|
||||
# - name: Get ROCm short version
|
||||
# run: echo "ROCM_VERSION_SHORT=$(echo '${{ matrix.ROCM_VERSION }}' | cut -d '.' -f 1,2)" >> $GITHUB_ENV
|
||||
|
||||
- name: Pack artifacts
|
||||
id: pack_artifacts
|
||||
run: |
|
||||
cp LICENSE ./build/bin/
|
||||
tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin .
|
||||
# - name: Pack artifacts
|
||||
# id: pack_artifacts
|
||||
# run: |
|
||||
# cp LICENSE ./build/bin/
|
||||
# tar -czvf llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz --transform "s,^\.,llama-${{ steps.tag.outputs.name }}," -C ./build/bin .
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
path: llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz
|
||||
name: llama-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz
|
||||
# - name: Upload artifacts
|
||||
# uses: actions/upload-artifact@v6
|
||||
# with:
|
||||
# path: llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz
|
||||
# name: llama-bin-ubuntu-rocm-${{ env.ROCM_VERSION_SHORT }}-${{ matrix.build }}.tar.gz
|
||||
|
||||
ios-xcode:
|
||||
needs: [check-release, get-version]
|
||||
@@ -1576,7 +1578,7 @@ jobs:
|
||||
#- windows-sycl
|
||||
- windows-rocm
|
||||
- windows-openvino
|
||||
- ubuntu-22-rocm
|
||||
#- ubuntu-22-rocm
|
||||
- ubuntu-cpu
|
||||
- ubuntu-vulkan
|
||||
- ubuntu-24-openvino
|
||||
@@ -1686,7 +1688,7 @@ jobs:
|
||||
- [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 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 (ROCm 7.14)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-rocm-7.14-x64.tar.gz)
|
||||
- Ubuntu x64 (ROCm 7.14)[DISABLED](https://github.com/ggml-org/llama.cpp/pull/26969)
|
||||
- [Ubuntu x64 (OpenVINO)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-openvino-${{ needs.ubuntu-24-openvino.outputs.openvino_version }}-x64.tar.gz)
|
||||
- [Ubuntu x64 (SYCL FP32)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-fp32-x64.tar.gz)
|
||||
- [Ubuntu x64 (SYCL FP16)](https://github.com/ggml-org/llama.cpp/releases/download/${{ steps.tag.outputs.name }}/llama-${{ steps.tag.outputs.name }}-bin-ubuntu-sycl-fp16-x64.tar.gz)
|
||||
|
||||
@@ -19,6 +19,8 @@ jobs:
|
||||
run: |
|
||||
cargo binstall komac@2.16.0 -y
|
||||
|
||||
# TODO: This should later be updated to publish releases instead of
|
||||
# development release builds.
|
||||
- name: Find latest release
|
||||
id: find_latest_release
|
||||
uses: actions/github-script@v8
|
||||
|
||||
+23
-7
@@ -2,6 +2,26 @@ cmake_minimum_required(VERSION 3.14...3.28) # for add_link_options and implicit
|
||||
project("llama.cpp" C CXX)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
### llama.cpp version
|
||||
set(LLAMA_VERSION_MAJOR 0)
|
||||
set(LLAMA_VERSION_MINOR 1)
|
||||
set(LLAMA_VERSION_PATCH 0)
|
||||
set(LLAMA_VERSION_BASE "${LLAMA_VERSION_MAJOR}.${LLAMA_VERSION_MINOR}.${LLAMA_VERSION_PATCH}")
|
||||
|
||||
# whether this is a development/nightly build
|
||||
# set this to OFF when making a release from a release tag (vX.Y.Z)
|
||||
# ref: https://github.com/ggml-org/ggml/discussions/1579
|
||||
option(LLAMA_BUILD_IS_DEV "llama: dev build" ON)
|
||||
|
||||
if (LLAMA_BUILD_IS_DEV)
|
||||
set(LLAMA_VERSION "${LLAMA_VERSION_BASE}-dev")
|
||||
else()
|
||||
# TODO: check that the current commit is tagged correctly according to the version specified above
|
||||
set(LLAMA_VERSION "${LLAMA_VERSION_BASE}")
|
||||
endif()
|
||||
|
||||
message(STATUS "llama.cpp version: ${LLAMA_VERSION}")
|
||||
|
||||
#set(CMAKE_WARN_DEPRECATED YES)
|
||||
set(CMAKE_WARN_UNUSED_CLI YES)
|
||||
|
||||
@@ -24,9 +44,6 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
set(LLAMA_STANDALONE ON)
|
||||
|
||||
include(git-vars)
|
||||
|
||||
# configure project version
|
||||
# TODO
|
||||
else()
|
||||
set(LLAMA_STANDALONE OFF)
|
||||
endif()
|
||||
@@ -139,7 +156,6 @@ endif()
|
||||
if (NOT DEFINED LLAMA_BUILD_COMMIT)
|
||||
set(LLAMA_BUILD_COMMIT ${BUILD_COMMIT})
|
||||
endif()
|
||||
set(LLAMA_INSTALL_VERSION 0.0.${LLAMA_BUILD_NUMBER})
|
||||
|
||||
# override ggml options
|
||||
set(GGML_ALL_WARNINGS ${LLAMA_ALL_WARNINGS})
|
||||
@@ -275,12 +291,12 @@ configure_package_config_file(
|
||||
LLAMA_BIN_INSTALL_DIR )
|
||||
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/llama-version.cmake
|
||||
VERSION ${LLAMA_INSTALL_VERSION}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/llama-config-version.cmake
|
||||
VERSION ${LLAMA_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/llama-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/llama-version.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/llama-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/llama)
|
||||
|
||||
configure_file(cmake/llama.pc.in
|
||||
|
||||
@@ -106,6 +106,7 @@ The `llama.cpp` project is build on top of the [ggml](https://github.com/ggml-or
|
||||
- [XCFramework](docs/xcframework.md)
|
||||
- [Completions](docs/completions.md)
|
||||
- [Models](docs/models.md)
|
||||
- [Release process](docs/release.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
#include "build-info.h"
|
||||
|
||||
#include "llama.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -77,12 +79,12 @@ static const command cmds[] = {
|
||||
|
||||
#undef UPDATE_HIDDEN
|
||||
|
||||
static int version(int argc, char ** argv) {
|
||||
printf("%s\n", llama_build_info());
|
||||
static int version(int /*argc*/, char ** /*argv*/) {
|
||||
llama_print_build_info(llama_version());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int licenses(int argc, char ** argv) {
|
||||
static int licenses(int /*argc*/, char ** /*argv*/) {
|
||||
for (int i = 0; LICENSES[i]; ++i) {
|
||||
printf("%s\n", LICENSES[i]);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set(LLAMA_VERSION @LLAMA_INSTALL_VERSION@)
|
||||
set(LLAMA_VERSION @LLAMA_VERSION@)
|
||||
set(LLAMA_BUILD_COMMIT @LLAMA_BUILD_COMMIT@)
|
||||
set(LLAMA_BUILD_NUMBER @LLAMA_BUILD_NUMBER@)
|
||||
set(LLAMA_SHARED_LIB @BUILD_SHARED_LIBS@)
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: llama
|
||||
Description: Port of Facebook's LLaMA model in C/C++
|
||||
Version: @LLAMA_INSTALL_VERSION@
|
||||
Version: @LLAMA_VERSION@
|
||||
Libs: -L${libdir} -lggml -lggml-base -lllama
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@@ -121,8 +121,8 @@ add_library(${TARGET}
|
||||
)
|
||||
|
||||
set_target_properties(${TARGET} PROPERTIES
|
||||
VERSION ${LLAMA_INSTALL_VERSION}
|
||||
SOVERSION 0
|
||||
VERSION ${LLAMA_VERSION_BASE}
|
||||
SOVERSION ${LLAMA_VERSION_MAJOR}
|
||||
MACHO_CURRENT_VERSION 0 # keep macOS linker from seeing oversized version number
|
||||
)
|
||||
|
||||
|
||||
+60
-2
@@ -35,6 +35,7 @@
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <thread> // for hardware_concurrency
|
||||
#include <vector>
|
||||
|
||||
@@ -560,6 +561,15 @@ void common_models_handler_apply(common_models_handler & handler, common_params
|
||||
}
|
||||
}
|
||||
|
||||
// infer the speculative type from the draft GGUF metadata when none is requested
|
||||
// note: reads only the first split - sharded drafts need an explicit --spec-type
|
||||
if (spec_types_is_default(params) && !params.speculative.draft.mparams.path.empty()) {
|
||||
const auto types_gguf = common_speculative_types_from_gguf(params.speculative.draft.mparams.path);
|
||||
if (!types_gguf.empty()) {
|
||||
params.speculative.types = types_gguf;
|
||||
}
|
||||
}
|
||||
|
||||
// when a sidecar type is requested, the draft repo resolves to its sidecar instead of a full model
|
||||
const bool spec_sidecar_found = !plan_spec.mtp.local_path.empty() ||
|
||||
!plan_spec.dflash.local_path.empty() ||
|
||||
@@ -704,12 +714,61 @@ void common_models_handler_apply(common_models_handler & handler, common_params
|
||||
// CLI argument parsing functions
|
||||
//
|
||||
|
||||
// apply config files (if present), a later file overrides an earlier one:
|
||||
// 1. system-wide: /etc/llama.cpp/config.ini (%PROGRAMDATA%\llama.cpp\config.ini on windows)
|
||||
// 2. user-level: ${XDG_CONFIG_HOME:-~/.config}/llama.cpp/config.ini (%APPDATA%\llama.cpp\config.ini on windows)
|
||||
static void common_params_apply_system_config(common_params & params, llama_example ex) {
|
||||
std::vector<std::string> paths;
|
||||
|
||||
#if defined(_WIN32)
|
||||
const std::string program_data = common_get_env("PROGRAMDATA");
|
||||
if (!program_data.empty()) {
|
||||
paths.push_back(program_data + "\\llama.cpp\\config.ini");
|
||||
}
|
||||
#else
|
||||
paths.push_back("/etc/llama.cpp/config.ini");
|
||||
#endif
|
||||
|
||||
try {
|
||||
paths.push_back(fs_get_config_directory() + "config.ini");
|
||||
} catch (const std::exception & e) {
|
||||
LOG_DBG("cannot read user-level config file, skipping: %s\n", e.what());
|
||||
}
|
||||
|
||||
std::vector<std::string> found;
|
||||
for (const auto & path : paths) {
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(path, ec)) {
|
||||
found.push_back(path);
|
||||
}
|
||||
}
|
||||
if (found.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
common_preset_context ctx(ex);
|
||||
ctx.ignore_unknown_keys = true; // the same config file is shared by all programs
|
||||
for (const auto & path : found) {
|
||||
LOG_INF("using config file: %s\n", path.c_str());
|
||||
common_preset global;
|
||||
common_presets presets = ctx.load_from_ini(path, global);
|
||||
global.apply_to_params(params);
|
||||
auto it = presets.find(COMMON_PRESET_DEFAULT_NAME);
|
||||
if (it != presets.end()) {
|
||||
it->second.apply_to_params(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool common_params_parse_ex(int argc, char ** argv, common_params_context & ctx_arg) {
|
||||
common_params & params = ctx_arg.params;
|
||||
|
||||
// setup log directly from params.verbosity: see tools/cli/cli.cpp
|
||||
common_log_set_verbosity_thold(params.verbosity);
|
||||
|
||||
// config file applies first, so env variables and CLI arguments override it
|
||||
common_params_apply_system_config(params, ctx_arg.ex);
|
||||
|
||||
std::unordered_map<std::string, std::pair<common_arg *, bool>> arg_to_options;
|
||||
for (auto & opt : ctx_arg.options) {
|
||||
for (const auto & arg : opt.args) {
|
||||
@@ -1390,8 +1449,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
{"--version"},
|
||||
"show version and build info",
|
||||
[](common_params &) {
|
||||
fprintf(stderr, "version: %d (%s)\n", llama_build_number(), llama_commit());
|
||||
fprintf(stderr, "built with %s for %s\n", llama_compiler(), llama_build_target());
|
||||
llama_print_build_info(llama_version());
|
||||
exit(0);
|
||||
}
|
||||
));
|
||||
|
||||
@@ -29,7 +29,7 @@ const char * llama_build_info(void) {
|
||||
return s.c_str();
|
||||
}
|
||||
|
||||
void llama_print_build_info(void) {
|
||||
fprintf(stderr, "%s: build = %d (%s)\n", __func__, llama_build_number(), llama_commit());
|
||||
fprintf(stderr, "%s: built with %s for %s\n", __func__, llama_compiler(), llama_build_target());
|
||||
void llama_print_build_info(const char * llama_version) {
|
||||
fprintf(stderr, "version: %s (build %d, commit %s)\n", llama_version, llama_build_number(), llama_commit());
|
||||
fprintf(stderr, "built with %s for %s\n", llama_compiler(), llama_build_target());
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ const char * llama_compiler(void);
|
||||
const char * llama_build_target(void);
|
||||
const char * llama_build_info(void);
|
||||
|
||||
void llama_print_build_info(void);
|
||||
void llama_print_build_info(const char *);
|
||||
|
||||
+4
-2
@@ -3155,7 +3155,8 @@ static common_chat_params common_chat_params_init_muse_glimmer(const common_chat
|
||||
auto analysis = p.ref("analysis");
|
||||
|
||||
auto recipient = p.optional(p.literal(" to=user"));
|
||||
auto final_msg = p.rule("final", recipient + p.literal("<|message|>") + p.content(p.until("<|eot|>")));
|
||||
auto final_msg = p.rule("final", recipient + p.literal("<|message|>") +
|
||||
p.content(p.until_one_of({ "<|eot|>", "<|eom|>" })));
|
||||
|
||||
if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE) {
|
||||
auto string_value = p.ac(
|
||||
@@ -3211,7 +3212,8 @@ static common_chat_params common_chat_params_init_muse_glimmer(const common_chat
|
||||
if (inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED) {
|
||||
return p.zero_or_more(start + analysis) + start + tool_calls;
|
||||
}
|
||||
return p.zero_or_more(start + analysis) + start + (tool_calls | final_msg);
|
||||
auto trailing_calls = p.optional(p.literal("<|eom|>") + start + tool_calls);
|
||||
return p.zero_or_more(start + analysis) + start + (tool_calls | (final_msg + trailing_calls));
|
||||
}
|
||||
|
||||
return p.zero_or_more(start + analysis) + start + final_msg;
|
||||
|
||||
+63
-10
@@ -1019,20 +1019,21 @@ std::string fs_get_cache_directory() {
|
||||
std::string cache_directory = "";
|
||||
auto ensure_trailing_slash = [](std::string p) {
|
||||
// Make sure to add trailing slash
|
||||
if (p.back() != DIRECTORY_SEPARATOR) {
|
||||
if (p.empty() || p.back() != DIRECTORY_SEPARATOR) {
|
||||
p += DIRECTORY_SEPARATOR;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
if (getenv("LLAMA_CACHE")) {
|
||||
cache_directory = std::getenv("LLAMA_CACHE");
|
||||
} else {
|
||||
cache_directory = common_get_env("LLAMA_CACHE");
|
||||
if (cache_directory.empty()) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX) || \
|
||||
defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
if (std::getenv("XDG_CACHE_HOME")) {
|
||||
cache_directory = std::getenv("XDG_CACHE_HOME");
|
||||
} else if (std::getenv("HOME")) {
|
||||
cache_directory = std::getenv("HOME") + std::string("/.cache/");
|
||||
const std::string xdg_cache_home = common_get_env("XDG_CACHE_HOME");
|
||||
const std::string home = common_get_env("HOME");
|
||||
if (!xdg_cache_home.empty()) {
|
||||
cache_directory = xdg_cache_home;
|
||||
} else if (!home.empty()) {
|
||||
cache_directory = home + "/.cache/";
|
||||
} else {
|
||||
#if defined(__linux__)
|
||||
/* no $HOME is defined, fallback to getpwuid */
|
||||
@@ -1047,9 +1048,16 @@ std::string fs_get_cache_directory() {
|
||||
#endif /* defined(__linux__) */
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
cache_directory = std::getenv("HOME") + std::string("/Library/Caches/");
|
||||
cache_directory = common_get_env("HOME");
|
||||
if (cache_directory.empty()) {
|
||||
throw std::runtime_error("Failed to find $HOME directory");
|
||||
}
|
||||
cache_directory += "/Library/Caches/";
|
||||
#elif defined(_WIN32)
|
||||
cache_directory = std::getenv("LOCALAPPDATA");
|
||||
cache_directory = common_get_env("LOCALAPPDATA");
|
||||
if (cache_directory.empty()) {
|
||||
throw std::runtime_error("Failed to find %LOCALAPPDATA% directory");
|
||||
}
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
GGML_ABORT("not implemented on this platform");
|
||||
#else
|
||||
@@ -1061,6 +1069,51 @@ std::string fs_get_cache_directory() {
|
||||
return ensure_trailing_slash(cache_directory);
|
||||
}
|
||||
|
||||
std::string fs_get_config_directory() {
|
||||
std::string config_directory = "";
|
||||
auto ensure_trailing_slash = [](std::string p) {
|
||||
if (p.empty() || p.back() != DIRECTORY_SEPARATOR) {
|
||||
p += DIRECTORY_SEPARATOR;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(_AIX) || \
|
||||
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
|
||||
const std::string xdg_config_home = common_get_env("XDG_CONFIG_HOME");
|
||||
const std::string home = common_get_env("HOME");
|
||||
if (!xdg_config_home.empty()) {
|
||||
config_directory = xdg_config_home;
|
||||
} else if (!home.empty()) {
|
||||
config_directory = home + "/.config/";
|
||||
} else {
|
||||
#if defined(__linux__)
|
||||
/* no $HOME is defined, fallback to getpwuid */
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if ((!pw) || (!pw->pw_dir)) {
|
||||
throw std::runtime_error("Failed to find $HOME directory");
|
||||
}
|
||||
|
||||
config_directory = std::string(pw->pw_dir) + std::string("/.config/");
|
||||
#else
|
||||
throw std::runtime_error("Failed to find $HOME directory");
|
||||
#endif
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
config_directory = common_get_env("APPDATA");
|
||||
if (config_directory.empty()) {
|
||||
throw std::runtime_error("Failed to find %APPDATA% directory");
|
||||
}
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// caller decides what to do when there is no config directory
|
||||
throw std::runtime_error("not implemented on this platform");
|
||||
#else
|
||||
# error Unknown architecture
|
||||
#endif
|
||||
config_directory = ensure_trailing_slash(config_directory);
|
||||
config_directory += "llama.cpp";
|
||||
return ensure_trailing_slash(config_directory);
|
||||
}
|
||||
|
||||
std::string fs_get_cache_file(const std::string & filename) {
|
||||
GGML_ASSERT(filename.find(DIRECTORY_SEPARATOR) == std::string::npos);
|
||||
std::string cache_directory = fs_get_cache_directory();
|
||||
|
||||
@@ -881,6 +881,7 @@ bool fs_is_directory(const std::string & path);
|
||||
|
||||
std::string fs_get_cache_directory();
|
||||
std::string fs_get_cache_file(const std::string & filename);
|
||||
std::string fs_get_config_directory();
|
||||
|
||||
struct common_file_info {
|
||||
std::string path;
|
||||
|
||||
@@ -322,6 +322,8 @@ common_presets common_preset_context::load_from_ini(const std::string & path, co
|
||||
preset.options[opt] = value;
|
||||
}
|
||||
LOG_DBG("accepted option: %s = %s\n", key.c_str(), preset.options[opt].c_str());
|
||||
} else if (ignore_unknown_keys) {
|
||||
LOG_WRN("ignoring option '%s' from %s: not supported by this program\n", key.c_str(), path.c_str());
|
||||
} else {
|
||||
throw std::runtime_error(string_format(
|
||||
"option '%s' not recognized in preset '%s'",
|
||||
|
||||
@@ -59,6 +59,10 @@ struct common_preset_context {
|
||||
bool filter_allowed_keys = false;
|
||||
std::set<std::string> allowed_keys;
|
||||
|
||||
// if true, options unknown to the current example are skipped instead of being an error
|
||||
// used for config files shared by all binaries, where each binary only knows a subset of options
|
||||
bool ignore_unknown_keys = false;
|
||||
|
||||
// if only_remote_allowed is true, only accept whitelisted keys
|
||||
common_preset_context(llama_example ex);
|
||||
|
||||
|
||||
+94
-72
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "ggml.h"
|
||||
#include "ggml-cpp.h"
|
||||
#include "llama.h"
|
||||
#include "log.h"
|
||||
#include "ngram-cache.h"
|
||||
@@ -171,12 +172,6 @@ struct common_speculative_impl {
|
||||
// (optional) serialize/restore per-seq internal state (e.g. eagle3's deferred boundary).
|
||||
virtual bool get_state(llama_seq_id /*seq_id*/, std::vector<uint8_t> & /*data*/) const { return false; }
|
||||
virtual void set_state(llama_seq_id /*seq_id*/, const std::vector<uint8_t> & /*data*/) {}
|
||||
|
||||
// true if this implementation requires the target context to extract post-norm embeddings
|
||||
virtual bool need_embd() const = 0;
|
||||
|
||||
// true if this implementation requires the target context to extract pre-norm embeddings
|
||||
virtual bool need_embd_nextn() const { return false; }
|
||||
};
|
||||
|
||||
struct common_speculative_impl_draft_simple : public common_speculative_impl {
|
||||
@@ -193,6 +188,10 @@ struct common_speculative_impl_draft_simple : public common_speculative_impl {
|
||||
auto * ctx_dft = this->params.ctx_dft;
|
||||
auto * ctx_tgt = this->params.ctx_tgt;
|
||||
|
||||
if (!ctx_dft) {
|
||||
throw std::runtime_error("draft-simple requires a draft context");
|
||||
}
|
||||
|
||||
SPC_TRC("%s", "adding speculative implementation 'draft-simple'\n");
|
||||
SPC_TRC("- n_max=%d, n_min=%d, p_min=%f\n", this->params.n_max, this->params.n_min, this->params.p_min);
|
||||
SPC_TRC("- gpu_layers=%d, cache_k=%s, cache_v=%s, ctx_tgt=%s, ctx_dft=%s, devices=[%s]\n",
|
||||
@@ -385,10 +384,6 @@ struct common_speculative_impl_draft_simple : public common_speculative_impl {
|
||||
void accept(llama_seq_id /*seq_id*/, uint16_t /*n_accepted*/, bool /*is_other*/) override {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -907,10 +902,6 @@ struct common_speculative_impl_draft_eagle3 : public common_speculative_impl {
|
||||
pending_g_last[seq_id].resize(n_embd_dec);
|
||||
std::memcpy(pending_g_last[seq_id].data(), data.data() + sizeof(llama_pos), (size_t) n_embd_dec * sizeof(float));
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// DFlash: block-diffusion drafting with a draft-side KV cache injection
|
||||
@@ -922,6 +913,9 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
|
||||
std::vector<common_sampler_ptr> smpls;
|
||||
|
||||
// backend sampler chain per seq, attached to ctx_dft
|
||||
std::vector<llama_sampler *> backend_chains;
|
||||
|
||||
int32_t n_embd_dec = 0; // draft hidden size
|
||||
int32_t n_embd_enc = 0; // target_layer_ids_n * target_hidden_size
|
||||
int32_t n_embd_tgt = 0; // target model hidden size
|
||||
@@ -995,6 +989,22 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
s.reset(common_sampler_init(model_dft, sparams));
|
||||
}
|
||||
|
||||
// offload draft sampling to the backend
|
||||
backend_chains.assign(n_seq, nullptr);
|
||||
if (this->params.backend_sampling) {
|
||||
for (llama_seq_id seq_id = 0; seq_id < (llama_seq_id) n_seq; ++seq_id) {
|
||||
llama_sampler * chain = llama_sampler_chain_init(llama_sampler_chain_default_params());
|
||||
llama_sampler_chain_add(chain, llama_sampler_init_top_k(10));
|
||||
|
||||
if (!llama_set_sampler(ctx_dft, seq_id, chain)) {
|
||||
SPC_WRN("backend offload failed for seq_id=%d; using CPU sampler\n", (int) seq_id);
|
||||
llama_sampler_free(chain);
|
||||
chain = nullptr;
|
||||
}
|
||||
backend_chains[seq_id] = chain;
|
||||
}
|
||||
}
|
||||
|
||||
// turn on extraction of the target layers' input embeddings
|
||||
for (uint32_t k = 0; k < target_layer_ids_n; ++k) {
|
||||
llama_set_embeddings_layer_inp(ctx_tgt, (uint32_t) target_layer_ids[k], true);
|
||||
@@ -1005,6 +1015,18 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
}
|
||||
|
||||
~common_speculative_impl_draft_dflash() override {
|
||||
auto * ctx_dft = this->params.ctx_dft;
|
||||
for (llama_seq_id seq_id = 0; seq_id < (llama_seq_id) backend_chains.size(); ++seq_id) {
|
||||
if (backend_chains[seq_id] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (ctx_dft) {
|
||||
llama_set_sampler(ctx_dft, seq_id, nullptr);
|
||||
}
|
||||
llama_sampler_free(backend_chains[seq_id]);
|
||||
}
|
||||
backend_chains.clear();
|
||||
|
||||
llama_batch_free(batch);
|
||||
llama_batch_free(batch_inject);
|
||||
}
|
||||
@@ -1247,10 +1269,6 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
void accept(llama_seq_id /*seq_id*/, uint16_t /*n_accepted*/, bool /*is_other*/) override {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct common_speculative_impl_draft_mtp : public common_speculative_impl {
|
||||
@@ -1689,14 +1707,6 @@ struct common_speculative_impl_draft_mtp : public common_speculative_impl {
|
||||
const size_t row_bytes = (size_t) n_embd * sizeof(float);
|
||||
std::memcpy(pending_h[seq_id].data(), verify_h[seq_id].data() + (size_t) i_h * n_embd, row_bytes);
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool need_embd_nextn() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// state of self-speculation (simple implementation, not ngram-map)
|
||||
@@ -1743,10 +1753,6 @@ struct common_speculative_impl_ngram_simple : public common_speculative_impl {
|
||||
void accept(llama_seq_id /*seq_id*/, uint16_t /*n_accepted*/, bool /*is_other*/) override {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct common_speculative_impl_ngram_map_k : public common_speculative_impl {
|
||||
@@ -1801,10 +1807,6 @@ struct common_speculative_impl_ngram_map_k : public common_speculative_impl {
|
||||
|
||||
common_ngram_map_accept(config[seq_id], n_accepted);
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct common_speculative_impl_ngram_mod : public common_speculative_impl {
|
||||
@@ -1980,10 +1982,6 @@ struct common_speculative_impl_ngram_mod : public common_speculative_impl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
@@ -2123,10 +2121,6 @@ struct common_speculative_impl_ngram_cache : public common_speculative_impl {
|
||||
void accept(llama_seq_id /*seq_id*/, uint16_t /*n_accepted*/, bool /*is_other*/) override {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool need_embd() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct common_speculative {
|
||||
@@ -2234,6 +2228,43 @@ common_speculative_type common_speculative_type_from_name(const std::string & na
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::vector<common_speculative_type> common_speculative_types_from_gguf(const std::string & path) {
|
||||
struct gguf_init_params gguf_params = {
|
||||
/* .no_alloc = */ true,
|
||||
/* .ctx = */ nullptr,
|
||||
};
|
||||
|
||||
gguf_context_ptr gguf_ctx(gguf_init_from_file(path.c_str(), gguf_params));
|
||||
if (!gguf_ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int64_t arch_id = gguf_find_key(gguf_ctx.get(), "general.architecture");
|
||||
if (arch_id < 0 || gguf_get_kv_type(gguf_ctx.get(), arch_id) != GGUF_TYPE_STRING) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string arch = gguf_get_val_str(gguf_ctx.get(), arch_id);
|
||||
if (arch != "dflash") {
|
||||
const uint32_t block_count = gguf_get_val_u32(gguf_ctx.get(), gguf_find_key(gguf_ctx.get(), (arch + ".block_count").c_str()));
|
||||
|
||||
if (gguf_find_tensor(gguf_ctx.get(), ("blk." + std::to_string(block_count - 1) + ".nextn.eh_proj.weight").c_str()) >= 0) {
|
||||
return { COMMON_SPECULATIVE_TYPE_DRAFT_MTP };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// the Markov head distinguishes draft-dspark from draft-dflash
|
||||
const auto type = gguf_find_tensor(gguf_ctx.get(), "markov_w1.weight") >= 0
|
||||
? COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK
|
||||
: COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH;
|
||||
|
||||
SPC_INF("auto-detected speculative type '%s' from the draft model metadata\n", common_speculative_type_to_str(type).c_str());
|
||||
|
||||
return { type };
|
||||
}
|
||||
|
||||
static uint32_t common_get_enabled_speculative_configs(const std::vector<common_speculative_type> & configs) {
|
||||
uint32_t result = 0;
|
||||
for (size_t i = 0; i < configs.size(); i++) {
|
||||
@@ -2301,6 +2332,23 @@ common_params common_base_params_to_speculative(const common_params & params) {
|
||||
result.n_outputs_max = params.n_parallel;
|
||||
result.n_outputs_max_per_seq = 1;
|
||||
|
||||
// dflash/dspark decode the whole noise block in a single pass and sample every block position on the backend
|
||||
// TODO: refactor such properties to be announced by the speculative types
|
||||
// something like `struct common_speculative_type_props common_speculative_type_get_props(...);`
|
||||
const bool has_block_draft = std::any_of(
|
||||
params.speculative.types.begin(), params.speculative.types.end(),
|
||||
[](common_speculative_type t) {
|
||||
return t == COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH || t == COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK;
|
||||
});
|
||||
if (has_block_draft) {
|
||||
// per-seq output positions: DFlash decodes anchor + n_max masks (n_max + 1); DSpark n_max -> +1 covers both
|
||||
const int32_t per_seq = std::max(1, params_spec.n_max + 1);
|
||||
result.n_outputs_max = params.n_parallel * per_seq;
|
||||
if (params_spec.backend_sampling) {
|
||||
result.n_outputs_max_per_seq = per_seq;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2322,7 +2370,6 @@ common_speculative_init_result::common_speculative_init_result(
|
||||
const bool spec_mtp = std::find(params.speculative.types.begin(),
|
||||
params.speculative.types.end(),
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
|
||||
GGML_ASSERT(has_draft || spec_mtp);
|
||||
|
||||
auto mparams = common_model_params_to_llama(params);
|
||||
auto cparams = common_context_params_to_llama(params);
|
||||
@@ -2560,34 +2607,6 @@ bool common_speculative_process(common_speculative * spec, const llama_batch & b
|
||||
return result;
|
||||
}
|
||||
|
||||
bool common_speculative_need_embd(common_speculative * spec) {
|
||||
if (spec == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & impl : spec->impls) {
|
||||
if (impl->need_embd()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool common_speculative_need_embd_nextn(common_speculative * spec) {
|
||||
if (spec == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & impl : spec->impls) {
|
||||
if (impl->need_embd_nextn()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void common_speculative_draft(common_speculative * spec) {
|
||||
if (spec == nullptr) {
|
||||
return;
|
||||
@@ -2672,7 +2691,10 @@ void common_speculative_draft(common_speculative * spec) {
|
||||
void common_speculative_accept(common_speculative * spec, llama_seq_id seq_id, uint16_t n_accepted) {
|
||||
common_speculative_impl * impl = spec->impl_last[seq_id];
|
||||
|
||||
GGML_ASSERT(impl);
|
||||
if (impl == nullptr) {
|
||||
GGML_ASSERT(n_accepted == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
common_time_meas tm(impl->t_accept_us, !impl->gen_perf);
|
||||
|
||||
@@ -14,6 +14,9 @@ const char * common_speculative_all_types_str();
|
||||
// parse user provided types
|
||||
std::vector<enum common_speculative_type> common_speculative_types_from_names(const std::vector<std::string> & names);
|
||||
|
||||
// infer the spec types from the GGUF metadata of a draft model; empty if unknown
|
||||
std::vector<enum common_speculative_type> common_speculative_types_from_gguf(const std::string & path);
|
||||
|
||||
// convert string to type
|
||||
enum common_speculative_type common_speculative_type_from_name(const std::string & name);
|
||||
|
||||
@@ -67,12 +70,6 @@ void common_speculative_begin(common_speculative * spec, llama_seq_id seq_id, co
|
||||
// process the batch and update the internal state of the speculative context
|
||||
bool common_speculative_process(common_speculative * spec, const llama_batch & batch);
|
||||
|
||||
// true if any implementation requires target post-norm embeddings to be extracted
|
||||
bool common_speculative_need_embd(common_speculative * spec);
|
||||
|
||||
// true if any implementation requires target nextn embeddings to be extracted
|
||||
bool common_speculative_need_embd_nextn(common_speculative * spec);
|
||||
|
||||
// generate drafts for the sequences specified with `common_speculative_get_draft_params`
|
||||
void common_speculative_draft(common_speculative * spec);
|
||||
|
||||
|
||||
+33
-4
@@ -665,7 +665,18 @@ class Gemma4Model(Gemma3Model):
|
||||
swa_layers = [t == "sliding_attention" for t in self.hparams["layer_types"]]
|
||||
self.gguf_writer.add_sliding_window_pattern(swa_layers)
|
||||
|
||||
head_dim_full = self.hparams["global_head_dim"]
|
||||
per_layer_config = self.hparams.get("per_layer_config")
|
||||
layer_types = self.hparams.get("layer_types", [])
|
||||
if (head_dim_full := self.hparams.get("global_head_dim")) is None and per_layer_config is not None:
|
||||
for layer_idx, layer_config in per_layer_config.items():
|
||||
layer_idx = int(layer_idx)
|
||||
if layer_idx < len(layer_types):
|
||||
if layer_types[layer_idx] == "full_attention" and "head_dim" in layer_config:
|
||||
head_dim_full = layer_config["head_dim"]
|
||||
break
|
||||
|
||||
assert head_dim_full is not None
|
||||
|
||||
head_dim_swa = self.hparams["head_dim"]
|
||||
# correct the head dim for global/swa layers
|
||||
self.gguf_writer.add_key_length(head_dim_full)
|
||||
@@ -685,8 +696,14 @@ class Gemma4Model(Gemma3Model):
|
||||
n_ff_arr = [n_ff if il < first_kv_shared_layer_idx else n_ff * 2 for il in range(self.block_count)]
|
||||
self.gguf_writer.add_feed_forward_length(n_ff_arr)
|
||||
|
||||
# handle num_global_key_value_heads
|
||||
num_key_value_heads_full = self.hparams.get("num_global_key_value_heads")
|
||||
if (num_key_value_heads_full := self.hparams.get("num_global_key_value_heads")) is None and per_layer_config is not None:
|
||||
for layer_idx, layer_config in per_layer_config.items():
|
||||
layer_idx = int(layer_idx)
|
||||
if layer_idx < len(layer_types):
|
||||
if layer_types[layer_idx] == "full_attention" and "num_key_value_heads" in layer_config:
|
||||
num_key_value_heads_full = layer_config["num_key_value_heads"]
|
||||
break
|
||||
|
||||
num_key_value_heads_swa = self.hparams.get("num_key_value_heads")
|
||||
if num_key_value_heads_full is not None and num_key_value_heads_swa is not None:
|
||||
value_arr = [num_key_value_heads_swa if is_swa else num_key_value_heads_full for is_swa in swa_layers]
|
||||
@@ -708,7 +725,19 @@ class Gemma4Model(Gemma3Model):
|
||||
# IMPORTANT: this ROPE_FREQS tensor is ONLY used by the full_attention layers
|
||||
rope_params_full = self.hparams["rope_parameters"]["full_attention"]
|
||||
assert rope_params_full["rope_type"] == "proportional"
|
||||
head_dim_full = (self.hparams["global_head_dim"])
|
||||
|
||||
per_layer_config = self.hparams.get("per_layer_config")
|
||||
if (head_dim_full := self.hparams.get("global_head_dim")) is None and per_layer_config is not None:
|
||||
layer_types = self.hparams.get("layer_types", [])
|
||||
for layer_idx, layer_config in per_layer_config.items():
|
||||
layer_idx = int(layer_idx)
|
||||
if layer_idx < len(layer_types):
|
||||
if layer_types[layer_idx] == "full_attention" and "head_dim" in layer_config:
|
||||
head_dim_full = layer_config["head_dim"]
|
||||
break
|
||||
|
||||
assert head_dim_full is not None
|
||||
|
||||
partial_rotary_factor_full = rope_params_full["partial_rotary_factor"]
|
||||
n_rot_full = int(head_dim_full * partial_rotary_factor_full / 2)
|
||||
n_unrot_full = int(head_dim_full / 2) - n_rot_full
|
||||
|
||||
@@ -804,6 +804,7 @@ User can use the device management in [docs/multi-gpu.md](https://github.com/ggm
|
||||
| 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. |
|
||||
| GGML_SYCL_MKL_FA_DIAG | 0 (default) or 1 | Enable output fingerprinting for MKL flash attention. Dumps the first 64 float output values for the first 6 FA calls with n_kv ≥ 1024, labeled with kernel type (MKL/TILE/VEC) for cross-kernel comparison. |
|
||||
| GGML_SYCL_ENABLE_FUSION | 0 or 1 (default) | Enable fused-kernel dispatch in graph compute (currently top-k MoE gating). |
|
||||
| GGML_SYCL_ENABLE_ESIMD | 0 or 1 (default)| Enable ESIMD kernels when available. |
|
||||
| ZES_ENABLE_SYSMAN | 0 (default) or 1 | Support to get free memory of GPU by sycl::aspect::ext_intel_free_memory.<br>Recommended to use when --split-mode = layer |
|
||||
| UR_L0_ENABLE_RELAXED_ALLOCATION_LIMITS | 0 (default) or 1 | Allow SYCL/Unified Runtime Level Zero device allocations larger than 4 GiB. llama.cpp's direct Level Zero allocation path requests the relaxed maximum-size limit itself when GGML_SYCL_ENABLE_LEVEL_ZERO=1. |
|
||||
| GGML_SYCL_USM_SYSTEM | 0 (default) or 1 | Enable experimental support for [USM system allocations](https://github.khronos.org/SYCL_Reference/iface/usm_basic_concept.html#system-allocations) for large GPU buffers. This requires enough host memory for model weights and caches, an Intel Xe2+ GPU such as BMG or newer and supported on Linux only, with CONFIG_DRM_XE_GPUSVM enabled. |
|
||||
|
||||
+16
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
The INI preset feature, introduced in [PR#17859](https://github.com/ggml-org/llama.cpp/pull/17859), allows users to create reusable and shareable parameter configurations for llama.cpp.
|
||||
|
||||
### Using Presets with the Server
|
||||
## Using Presets with the Server
|
||||
|
||||
When running multiple models on the server (router mode), INI preset files can be used to configure model-specific parameters. Please refer to the [server documentation](../tools/server/README.md) for more details.
|
||||
|
||||
@@ -93,3 +93,18 @@ llama-server -hf user/repo:gpt-oss-120b-hf
|
||||
```
|
||||
|
||||
Please make sure to provide the correct `hf-repo` for each child preset. Otherwise, you may get error: `The specified tag is not a valid quantization scheme.`
|
||||
|
||||
## System-level config
|
||||
|
||||
The system-level config, added in PR [#26118](https://github.com/ggml-org/llama.cpp/pull/26118), allows sharing the same set of options among multiple tools and examples. Unlike the sections above, it is not limited to the server.
|
||||
|
||||
These files are loaded on startup if present. A later file overrides an earlier one:
|
||||
1. System-wide: `/etc/llama.cpp/config.ini` (or `%PROGRAMDATA%\llama.cpp\config.ini` on Windows)
|
||||
2. User-level: `$XDG_CONFIG_HOME/llama.cpp/config.ini`, `~/.config/llama.cpp/config.ini` by default (or `%APPDATA%\llama.cpp\config.ini` on Windows)
|
||||
|
||||
The config file is applied first, then its options are overridden by ENV variables, CLI arguments and model presets (in router mode).
|
||||
|
||||
Note:
|
||||
- Only the `[*]` and default sections are used; options written before any section header belong to "default. Named sections are ignored
|
||||
- Tool-specific options can be specified, but will be ignored (with a warning) if the example doesn't support it<br/>Example: if you specify `port = 1234`, only `llama-server` will use it, other examples will ignore it
|
||||
- `model` or `hf-repo` are not recommended to be configured system-level, because it may introduce conflicts<br/>Example: a `hf-repo` in the config file still takes effect when you pass `-m` on the command line, so you may load a different model than expected
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Release process
|
||||
|
||||
llama.cpp uses [semantic versioning](https://semver.org) (`MAJOR.MINOR.PATCH`).
|
||||
|
||||
## Version bump guidelines
|
||||
|
||||
| Change type | Version component |
|
||||
|---|---|
|
||||
| Breaking change to the public C API (`include/llama.h`) | `MAJOR` |
|
||||
| Backward-compatible features, model support, or API addition | `MINOR` |
|
||||
| Bug fix with no API change | `PATCH` |
|
||||
|
||||
The version is set in the three variables at the top of the root `CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
set(LLAMA_VERSION_MAJOR 0)
|
||||
set(LLAMA_VERSION_MINOR 1)
|
||||
set(LLAMA_VERSION_PATCH 0)
|
||||
```
|
||||
|
||||
_A version bump should be included in the PR that introduces the change, or in a
|
||||
dedicated bump commit merged before the release is cut._
|
||||
|
||||
_TODO: add PR labels (`semver: patch`, `semver: minor`, `semver: major`) to help
|
||||
identify which PRs require a version bump before cutting a release._
|
||||
|
||||
## Making a release
|
||||
|
||||
Releases are created by running the [make-release](.github/workflows/make-release.yml)
|
||||
which is a manual workflow.
|
||||
|
||||
The workflow creates an annotated git tag (e.g. `v0.1.0`) and pushes it to the
|
||||
remote. No GitHub Release object is created, the tag is the release artifact.
|
||||
|
||||
## Building a release
|
||||
|
||||
By default, `LLAMA_BUILD_IS_DEV=ON` which appends a `-dev` suffix to `LLAMA_VERSION`,
|
||||
marking the build as a nightly/development build. Distributors building from a
|
||||
release tag must pass `-DLLAMA_BUILD_IS_DEV=OFF` to produce a clean version string
|
||||
(e.g. `0.1.0` instead of `0.1.0-dev`).
|
||||
|
||||
## How releases reach users
|
||||
Currently releases are not published to github releases, only nightly/development
|
||||
builds are available there. The way users can access releases are using the following
|
||||
channels:
|
||||
|
||||
- **llama-install.sh** — downloads pre-built binaries built from the release tag.
|
||||
- **Package managers** — consume the git tag directly.
|
||||
- **Build from source** — users clone the repo and check out the tag.
|
||||
@@ -3,10 +3,47 @@
|
||||
Demonstration of basic greedy speculative decoding
|
||||
|
||||
```bash
|
||||
# spec-type draft-simple
|
||||
./bin/llama-speculative-simple \
|
||||
-m ../models/qwen2.5-32b-coder-instruct/ggml-model-q8_0.gguf \
|
||||
-md ../models/qwen2.5-1.5b-coder-instruct/ggml-model-q4_0.gguf \
|
||||
-f test.txt -c 0 -ngl 99 --color on \
|
||||
--sampling-seq k --top-k 1 -fa on --temp 0.0 \
|
||||
-ngld 99 --spec-draft-n-max 16 --spec-draft-n-draft-min 5 --draft-p-min 0.9
|
||||
-hf ggml-org/Qwen3-8B-Base-GGUF:Q8_0 \
|
||||
-hfd ggml-org/Qwen3-0.6B-Base-GGUF \
|
||||
-p "Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-simple --spec-draft-n-max 7 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
|
||||
# spec-type draft-mtp
|
||||
./bin/llama-speculative-simple \
|
||||
-hf ggml-org/Qwen3.6-27B-GGUF:Q8_0 \
|
||||
-p "Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-mtp --spec-draft-n-max 3 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
|
||||
# spec-type draft-mtp (with shared KV cache)
|
||||
# note: this model needs a <s> token at the start to somewhat work without the chat template
|
||||
./bin/llama-speculative-simple \
|
||||
-hf ggml-org/Gemma-4-31B-it-GGUF:Q8_0 \
|
||||
-p "<s>Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-mtp --spec-draft-n-max 3 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
|
||||
# spec-type draft-eagle3
|
||||
./bin/llama-speculative-simple \
|
||||
-hf ggml-org/gpt-oss-20b-GGUF \
|
||||
-p "Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-eagle3 --spec-draft-n-max 3 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
|
||||
# spec-type draft-dflash
|
||||
./bin/llama-speculative-simple \
|
||||
-hf ggml-org/Qwen3-8B-GGUF \
|
||||
-p "Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-dflash --spec-draft-n-max 7 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
|
||||
# spec-type draft-dspark
|
||||
./bin/llama-speculative-simple \
|
||||
-hf ggml-org/Qwen3-8B-GGUF \
|
||||
-p "Here is a quick sort implementation in C++. Just code, no comments:\n\n#include" \
|
||||
--spec-type draft-dspark --spec-draft-n-max 7 -ngld 99 --color on \
|
||||
-n 256 --temp 0 --top-k 1 --seed 42 -ngl 99 -lv 4
|
||||
```
|
||||
|
||||
@@ -51,48 +51,23 @@ int main(int argc, char ** argv) {
|
||||
|
||||
const llama_vocab * vocab = llama_model_get_vocab(model_tgt);
|
||||
|
||||
// load the draft model
|
||||
llama_model_ptr model_dft;
|
||||
llama_context_ptr ctx_dft;
|
||||
// load the draft model (if any) - this also creates the MTP draft context when MTP speculation is enabled
|
||||
common_speculative_init_result_ptr spec_init;
|
||||
|
||||
// TODO: simplify this logic
|
||||
{
|
||||
const auto & params_spec = params.speculative.draft;
|
||||
common_params params_dft = common_base_params_to_speculative(params);
|
||||
|
||||
auto params_dft = params;
|
||||
|
||||
params_dft.n_outputs_max = params.n_parallel;
|
||||
params_dft.n_outputs_max_per_seq = 1;
|
||||
|
||||
params_dft.devices = params_spec.devices;
|
||||
params_dft.model = params_spec.mparams;
|
||||
params_dft.n_gpu_layers = params_spec.n_gpu_layers;
|
||||
|
||||
if (params_spec.cpuparams.n_threads > 0) {
|
||||
params_dft.cpuparams.n_threads = params.speculative.draft.cpuparams.n_threads;
|
||||
params_dft.cpuparams_batch.n_threads = params.speculative.draft.cpuparams_batch.n_threads;
|
||||
}
|
||||
|
||||
params_dft.tensor_buft_overrides = params.speculative.draft.tensor_buft_overrides;
|
||||
|
||||
auto mparams_dft = common_model_params_to_llama(params_dft);
|
||||
|
||||
model_dft.reset(llama_model_load_from_file(params_dft.model.path.c_str(), mparams_dft));
|
||||
if (model_dft == nullptr) {
|
||||
LOG_ERR("failed to load draft model, '%s'\n", params_dft.model.path.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto cparams = common_context_params_to_llama(params_dft);
|
||||
ctx_dft.reset(llama_init_from_model(model_dft.get(), cparams));
|
||||
spec_init = common_speculative_init_from_params(params_dft, model_tgt, ctx_tgt);
|
||||
|
||||
params.speculative.draft.ctx_tgt = ctx_tgt;
|
||||
params.speculative.draft.ctx_dft = ctx_dft.get();
|
||||
params.speculative.draft.ctx_dft = spec_init->context();
|
||||
}
|
||||
|
||||
llama_context * ctx_dft = params.speculative.draft.ctx_dft;
|
||||
|
||||
// check if the context supports partial sequence removal
|
||||
const bool use_ckpt_tgt = (common_context_can_seq_rm(ctx_tgt) == COMMON_CONTEXT_SEQ_RM_TYPE_FULL);
|
||||
const bool use_ckpt_dft = (common_context_can_seq_rm(ctx_dft.get()) == COMMON_CONTEXT_SEQ_RM_TYPE_FULL);
|
||||
const bool use_ckpt_tgt = common_context_can_seq_rm(ctx_tgt) == COMMON_CONTEXT_SEQ_RM_TYPE_FULL;
|
||||
const bool use_ckpt_dft = common_context_can_seq_rm(ctx_dft) == COMMON_CONTEXT_SEQ_RM_TYPE_FULL;
|
||||
|
||||
if (use_ckpt_tgt) {
|
||||
LOG_INF("speculative decoding will use checkpoints (context does not support partial sequence removal)\n");
|
||||
@@ -138,9 +113,30 @@ int main(int argc, char ** argv) {
|
||||
// target model sampling context
|
||||
common_sampler_ptr smpl(common_sampler_init(model_tgt, params.sampling));
|
||||
|
||||
// eval the prompt
|
||||
llama_decode(ctx_tgt, llama_batch_get_one(inp.data(), inp.size() - 1));
|
||||
llama_decode(ctx_dft.get(), llama_batch_get_one(inp.data(), inp.size() - 1));
|
||||
// init the speculator
|
||||
const auto & params_spec = params.speculative;
|
||||
|
||||
struct common_speculative * spec = common_speculative_init(params.speculative, 1);
|
||||
|
||||
if (spec == nullptr) {
|
||||
LOG_ERR("%s", "failed to initialize speculative decoding\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// eval the prompt on the target and feed it to the speculative implementation(s)
|
||||
{
|
||||
llama_batch batch_prompt = llama_batch_init(inp.size(), 0, 1);
|
||||
for (size_t i = 0; i < inp.size() - 1; ++i) {
|
||||
common_batch_add(batch_prompt, inp[i], i, { seq_id }, false);
|
||||
}
|
||||
|
||||
llama_decode(ctx_tgt, batch_prompt);
|
||||
|
||||
if (!common_speculative_process(spec, batch_prompt)) {
|
||||
LOG_ERR("%s", "failed to process speculative prompt\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// note: keep the last token separate!
|
||||
llama_token id_last = inp.back();
|
||||
@@ -151,18 +147,12 @@ int main(int argc, char ** argv) {
|
||||
|
||||
int n_past = inp.size() - 1;
|
||||
|
||||
// init the speculator
|
||||
const auto & params_spec = params.speculative;
|
||||
|
||||
struct common_speculative * spec = common_speculative_init(params.speculative, 1);
|
||||
|
||||
common_speculative_begin(spec, seq_id, prompt_tgt);
|
||||
|
||||
llama_batch batch_tgt = llama_batch_init(llama_n_batch(ctx_tgt), 0, 1);
|
||||
|
||||
size_t n_draft = 0;
|
||||
|
||||
llama_tokens draft;
|
||||
|
||||
common_prompt_checkpoint ckpt;
|
||||
|
||||
const auto t_enc_end = ggml_time_us();
|
||||
@@ -184,13 +174,20 @@ int main(int argc, char ** argv) {
|
||||
llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), seq_id));
|
||||
|
||||
if (use_ckpt_dft) {
|
||||
ckpt.update_dft(ctx_dft.get(), seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
ckpt.update_dft(ctx_dft, seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
}
|
||||
|
||||
// determine the max draft that fits the remaining context and generation budget
|
||||
int n_draft_max = (int) llama_n_ctx(ctx_tgt) - n_past - 2;
|
||||
if (params.n_predict >= 0) {
|
||||
n_draft_max = std::min(n_draft_max, params.n_predict - n_predict - 1);
|
||||
}
|
||||
n_draft_max = std::max(n_draft_max, 0);
|
||||
|
||||
// generate a new draft
|
||||
common_speculative_get_draft_params(spec, seq_id) = {
|
||||
/* .drafting = */ true,
|
||||
/* .n_max = */ -1,
|
||||
/* .n_max = */ n_draft_max,
|
||||
/* .n_past = */ n_past,
|
||||
/* .id_last = */ id_last,
|
||||
/* .prompt = */ &prompt_tgt,
|
||||
@@ -198,9 +195,6 @@ int main(int argc, char ** argv) {
|
||||
};
|
||||
common_speculative_draft(spec);
|
||||
|
||||
// save the original draft size
|
||||
n_draft = draft.size();
|
||||
|
||||
// save a checkpoint of the target context before evaluating the draft
|
||||
// this allows us to restore the state if partial draft acceptance occurs
|
||||
if (!draft.empty()) {
|
||||
@@ -209,10 +203,13 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ckpt.load_dft(ctx_dft.get(), seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
// reset the draft context to the checkpoint before verification
|
||||
if (ctx_dft) {
|
||||
if (use_ckpt_dft) {
|
||||
ckpt.load_dft(ctx_dft, seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
}
|
||||
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft.get()), seq_id, ckpt.pos_max + 1, -1);
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft), seq_id, ckpt.pos_max + 1, -1);
|
||||
}
|
||||
} else {
|
||||
// we have a previous (partial) draft to reuse from checkpoint restoration
|
||||
@@ -236,10 +233,10 @@ int main(int argc, char ** argv) {
|
||||
llama_decode(ctx_tgt, batch_tgt);
|
||||
}
|
||||
|
||||
// evaluate the same batch with the draft model
|
||||
{
|
||||
// TODO: extend to support MTP, Eagle, etc. See server code for reference
|
||||
llama_decode(ctx_dft.get(), batch_tgt);
|
||||
// feed the batch to the speculative implementation(s) - this drives the draft model, MTP, Eagle3, etc.
|
||||
if (!common_speculative_process(spec, batch_tgt)) {
|
||||
LOG_ERR("%s", "failed to process speculative batch\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// only save the sampler sampler state if we use checkpoints
|
||||
@@ -248,6 +245,9 @@ int main(int argc, char ** argv) {
|
||||
smpl_save.reset(common_sampler_clone(smpl.get()));
|
||||
}
|
||||
|
||||
// save the size of the draft being verified
|
||||
const size_t n_draft = draft.size();
|
||||
|
||||
// sample from the full target batch and return the accepted tokens based on the target sampler
|
||||
//
|
||||
// for each token to be accepted, the sampler would have to sample that same token
|
||||
@@ -264,8 +264,8 @@ int main(int argc, char ** argv) {
|
||||
// check for partial draft acceptance:
|
||||
// if the context doesn't support partial sequence removal, restore the checkpoint
|
||||
// and make the accepted tokens the new partial draft for the next iteration
|
||||
if (use_ckpt_tgt && ids.size() - 1 < draft.size()) {
|
||||
LOG_DBG("partial acceptance: %zu < %zu, restoring checkpoint\n", ids.size() - 1, draft.size());
|
||||
if (use_ckpt_tgt && ids.size() - 1 < n_draft) {
|
||||
LOG_DBG("partial acceptance: %zu < %zu, restoring checkpoint\n", ids.size() - 1, n_draft);
|
||||
|
||||
draft = std::move(ids);
|
||||
|
||||
@@ -275,10 +275,10 @@ int main(int argc, char ** argv) {
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_tgt), seq_id, ckpt.pos_max + 1, -1);
|
||||
}
|
||||
|
||||
{
|
||||
ckpt.load_dft(ctx_dft.get(), seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
if (ctx_dft) {
|
||||
ckpt.load_dft(ctx_dft, seq_id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
|
||||
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft.get()), seq_id, ckpt.pos_max + 1, -1);
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft), seq_id, ckpt.pos_max + 1, -1);
|
||||
}
|
||||
|
||||
prompt_tgt.resize(ckpt.n_tokens);
|
||||
@@ -329,8 +329,11 @@ int main(int argc, char ** argv) {
|
||||
{
|
||||
LOG_DBG("clear kv cache from any extra tokens, n_past = %d\n", n_past);
|
||||
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_tgt), seq_id, n_past, -1);
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft.get()), seq_id, n_past, -1);
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_tgt), seq_id, n_past, -1);
|
||||
|
||||
if (ctx_dft) {
|
||||
llama_memory_seq_rm(llama_get_memory(ctx_dft), seq_id, n_past, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((params.n_predict >= 0 && n_predict > params.n_predict) || has_eos) {
|
||||
@@ -356,6 +359,7 @@ int main(int argc, char ** argv) {
|
||||
|
||||
LOG_INF("\n");
|
||||
LOG_INF("draft:\n\n");
|
||||
common_speculative_print_stats(spec);
|
||||
|
||||
LOG_INF("\n");
|
||||
LOG_INF("target:\n\n");
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
llama-build-install
|
||||
install
|
||||
build
|
||||
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(llama-simple)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(llama 0.1.0 REQUIRED)
|
||||
|
||||
add_executable(test-cmake test-cmake.cpp)
|
||||
target_link_libraries(test-cmake PRIVATE llama)
|
||||
target_compile_definitions(test-cmake PRIVATE
|
||||
LLAMA_BUILD_NUMBER=${LLAMA_BUILD_NUMBER}
|
||||
LLAMA_BUILD_COMMIT="${LLAMA_BUILD_COMMIT}"
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
## cmake-test
|
||||
|
||||
This is just for manually testing/developing of a llama.cpp installation to
|
||||
enable troubleshooting issues and exploration. The idea is that this can be used
|
||||
after making changes to llama.cpp installation cmake configuration and then
|
||||
verify it locally.
|
||||
|
||||
### Usage
|
||||
The following will configure, build, and install llama.cpp
|
||||
|
||||
Configuring/build/install:
|
||||
```console
|
||||
./build-install.sh
|
||||
```
|
||||
The above command will create a directory named `install` in the current directory
|
||||
which will have the follwing files in its lib directory:
|
||||
```console
|
||||
(venv) $ ls install/lib/
|
||||
cmake libggml.so libllama-common.so.0 libllama.so.0.1.0 llama.cpp
|
||||
libggml-base.so libggml.so.0 libllama-common.so.0.1.0 libmtmd.so pkgconfig
|
||||
libggml-base.so.0 libggml.so.0.19.0 libllama.so libmtmd.so.0
|
||||
libggml-base.so.0.19.0 libllama-common.so libllama.so.0 libmtmd.so.0.1.0
|
||||
```
|
||||
|
||||
Build/run this project using the installation created above:
|
||||
```console
|
||||
(venv) $ ./build.sh
|
||||
-- Configuring done (0.0s)
|
||||
-- Generating done (0.0s)
|
||||
-- Build files have been written to: /home/danbev/work/ai/llama.cpp/examples/test-cmake/build
|
||||
[100%] Built target test-cmake
|
||||
[test-cmake] Using llama.cpp version 0.1.0-dev-b10335
|
||||
[test-cmake] Initializing backend...
|
||||
load_backend: loaded CPU backend from /home/danbev/work/ai/llama.cpp/examples/test-cmake/install/lib/llama.cpp/libggml-cpu-alderlake.so
|
||||
[test-cmake] Backend initialized.
|
||||
```
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf llama-build-install install
|
||||
|
||||
cmake --fresh -S ../../. -B llama-build-install -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DGGML_BACKEND_DL=ON \
|
||||
-DGGML_CPU_ALL_VARIANTS=ON \
|
||||
-DLLAMA_TESTS_INSTALL=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="${PWD}/install" \
|
||||
-DGGML_BACKEND_DIR="${PWD}/install/lib/llama.cpp" \
|
||||
-DGGML_LIB_INSTALL_DIR="${PWD}/install/lib/llama.cpp" \
|
||||
-DLLAMA_LIB_INSTALL_DIR="${PWD}/install/lib/llama.cpp" \
|
||||
-DLLAMA_TOOLS_INSTALL=OFF
|
||||
|
||||
cmake --build llama-build-install --parallel 12
|
||||
cmake --install llama-build-install
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cmake -S . -B build -DCMAKE_PREFIX_PATH="${PWD}/install"
|
||||
cmake --build build
|
||||
LD_LIBRARY_PATH="${PWD}/install/lib/llama.cpp:${PWD}/install/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" ./build/test-cmake
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "llama.h"
|
||||
#include <cstdio>
|
||||
|
||||
int main(void) {
|
||||
printf("[test-cmake] version: %s, build: %d (%s)\n",
|
||||
llama_version(), LLAMA_BUILD_NUMBER, LLAMA_BUILD_COMMIT);
|
||||
printf("[test-cmake] Initializing backend...\n");
|
||||
llama_backend_init();
|
||||
printf("[test-cmake] Backend initialized.\n");
|
||||
llama_backend_free();
|
||||
return 0;
|
||||
}
|
||||
+2
-2
@@ -402,7 +402,7 @@ configure_package_config_file(
|
||||
GGML_BIN_INSTALL_DIR)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ggml-version.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ggml-config-version.cmake
|
||||
VERSION ${GGML_INSTALL_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
@@ -414,7 +414,7 @@ message(STATUS "ggml version: ${GGML_INSTALL_VERSION}")
|
||||
message(STATUS "ggml commit: ${GGML_BUILD_COMMIT}")
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ggml-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ggml-version.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ggml-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ggml)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -113,6 +113,7 @@ set_and_check(GGML_LIB_DIR "@PACKAGE_GGML_LIB_INSTALL_DIR@")
|
||||
if(NOT TARGET ggml::ggml)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
unset(GGML_LIBRARY CACHE)
|
||||
find_library(GGML_LIBRARY ggml
|
||||
REQUIRED
|
||||
HINTS ${GGML_LIB_DIR}
|
||||
@@ -121,8 +122,10 @@ if(NOT TARGET ggml::ggml)
|
||||
add_library(ggml::ggml UNKNOWN IMPORTED)
|
||||
set_target_properties(ggml::ggml
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${GGML_LIBRARY}")
|
||||
IMPORTED_LOCATION "${GGML_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GGML_INCLUDE_DIR}")
|
||||
|
||||
unset(GGML_BASE_LIBRARY CACHE)
|
||||
find_library(GGML_BASE_LIBRARY ggml-base
|
||||
REQUIRED
|
||||
HINTS ${GGML_LIB_DIR}
|
||||
@@ -132,6 +135,7 @@ if(NOT TARGET ggml::ggml)
|
||||
set_target_properties(ggml::ggml-base
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION "${GGML_BASE_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GGML_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${GGML_BASE_INTERFACE_LINK_LIBRARIES}")
|
||||
|
||||
set(_ggml_all_targets "")
|
||||
@@ -140,6 +144,7 @@ if(NOT TARGET ggml::ggml)
|
||||
string(REPLACE "-" "_" _ggml_backend_pfx "${_ggml_backend}")
|
||||
string(TOUPPER "${_ggml_backend_pfx}" _ggml_backend_pfx)
|
||||
|
||||
unset(${_ggml_backend_pfx}_LIBRARY CACHE)
|
||||
find_library(${_ggml_backend_pfx}_LIBRARY ${_ggml_backend}
|
||||
REQUIRED
|
||||
HINTS ${GGML_LIB_DIR}
|
||||
|
||||
@@ -1,90 +1,12 @@
|
||||
#include "ggml-backend-impl.h"
|
||||
#include "ggml-feats.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/auxv.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_FPHP)
|
||||
#define HWCAP_FPHP (1 << 9)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_ASIMDHP)
|
||||
#define HWCAP_ASIMDHP (1 << 10)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_ASIMDDP)
|
||||
#define HWCAP_ASIMDDP (1 << 20)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_SVE)
|
||||
#define HWCAP_SVE (1 << 22)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_SVE2)
|
||||
#define HWCAP2_SVE2 (1 << 1)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_I8MM)
|
||||
#define HWCAP2_I8MM (1 << 13)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_SME)
|
||||
#define HWCAP2_SME (1 << 23)
|
||||
#endif
|
||||
|
||||
struct aarch64_features {
|
||||
// has_neon not needed, aarch64 has NEON guaranteed
|
||||
bool has_dotprod = false;
|
||||
bool has_fp16 = false;
|
||||
bool has_sve = false;
|
||||
bool has_sve2 = false;
|
||||
bool has_i8mm = false;
|
||||
bool has_sme = false;
|
||||
bool has_sme2 = false;
|
||||
|
||||
aarch64_features() {
|
||||
#if defined(__linux__)
|
||||
uint32_t hwcap = getauxval(AT_HWCAP);
|
||||
uint32_t hwcap2 = getauxval(AT_HWCAP2);
|
||||
|
||||
has_dotprod = !!(hwcap & HWCAP_ASIMDDP);
|
||||
has_fp16 = !!(hwcap & HWCAP_FPHP) && !!(hwcap & HWCAP_ASIMDHP);
|
||||
has_sve = !!(hwcap & HWCAP_SVE);
|
||||
has_sve2 = !!(hwcap2 & HWCAP2_SVE2);
|
||||
has_i8mm = !!(hwcap2 & HWCAP2_I8MM);
|
||||
has_sme = !!(hwcap2 & HWCAP2_SME);
|
||||
#elif defined(__APPLE__)
|
||||
int oldp = 0;
|
||||
size_t size = sizeof(oldp);
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_DotProd", &oldp, &size, NULL, 0) == 0) {
|
||||
has_dotprod = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_I8MM", &oldp, &size, NULL, 0) == 0) {
|
||||
has_i8mm = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SME", &oldp, &size, NULL, 0) == 0) {
|
||||
has_sme = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SME2", &oldp, &size, NULL, 0) == 0) {
|
||||
has_sme2 = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
// Apple apparently does not implement SVE yet
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
|
||||
static int ggml_backend_cpu_aarch64_score() {
|
||||
int score = 1;
|
||||
aarch64_features af;
|
||||
const ggml_feats_arch64_runtime_t af = ggml_feats_get_arch64_runtime();
|
||||
GGML_UNUSED(af);
|
||||
|
||||
#ifdef GGML_USE_DOTPROD
|
||||
if (!af.has_dotprod) { return 0; }
|
||||
@@ -116,4 +38,4 @@ static int ggml_backend_cpu_aarch64_score() {
|
||||
|
||||
GGML_BACKEND_DL_SCORE_IMPL(ggml_backend_cpu_aarch64_score)
|
||||
|
||||
# endif // defined(__aarch64__)
|
||||
# endif // defined(__aarch64__) || defined(_M_ARM64)
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
#include <arm_neon.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <atomic>
|
||||
#include <cfloat>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
@@ -17,25 +19,21 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <climits>
|
||||
#include <charconv>
|
||||
#include <system_error>
|
||||
#if defined(__linux__)
|
||||
#include <asm/hwcap.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#ifndef HWCAP2_SME2
|
||||
#define HWCAP2_SME2 (1UL << 37)
|
||||
#endif
|
||||
#elif defined(__APPLE__)
|
||||
#include <string_view>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <excpt.h>
|
||||
#endif
|
||||
|
||||
#include "kleidiai.h"
|
||||
@@ -43,6 +41,7 @@
|
||||
#include "ggml-cpu.h"
|
||||
#include "ggml-cpu-impl.h"
|
||||
#include "ggml-impl.h"
|
||||
#include "ggml-feats.h"
|
||||
#include "ggml-backend-impl.h"
|
||||
#include "ggml-threading.h"
|
||||
#include "traits.h"
|
||||
@@ -64,8 +63,8 @@ struct ggml_kleidiai_context {
|
||||
ggml_kleidiai_kernels * kernels_q4;
|
||||
ggml_kleidiai_kernels * kernels_q8;
|
||||
ggml_kleidiai_kernels * kernels_f32;
|
||||
int sme_thread_cap; // <= 0 means “SME disabled/unknown”;
|
||||
int thread_hint; // <= 0 means “no hint”
|
||||
int sme_thread_cap; // <= 0 means "SME disabled/unknown"
|
||||
int thread_hint; // <= 0 means "no hint"
|
||||
int chunk_multiplier;
|
||||
} static ctx = { CPU_FEATURE_NONE, nullptr, nullptr, nullptr, 0, -1, 4 };
|
||||
|
||||
@@ -93,24 +92,117 @@ static const char* cpu_feature_to_string(cpu_feature f) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__linux__) && defined(__aarch64__)
|
||||
static bool parse_cpu_dir_name(const char* name, size_t* cpu) {
|
||||
if (strncmp(name, "cpu", 3) != 0 ||
|
||||
name[3] < '0' || name[3] > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* first = name + 3;
|
||||
const char* last = name + strlen(name);
|
||||
|
||||
size_t value = 0;
|
||||
const auto [end, ec] = std::from_chars(first, last, value, 10);
|
||||
|
||||
if (ec != std::errc{} || end != last) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*cpu = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<size_t> detect_cpu_ids() {
|
||||
std::vector<size_t> cpus;
|
||||
|
||||
DIR * dir = opendir("/sys/devices/system/cpu");
|
||||
if (dir == nullptr) {
|
||||
return cpus;
|
||||
}
|
||||
|
||||
while (dirent * entry = readdir(dir)) {
|
||||
size_t cpu = 0;
|
||||
if (parse_cpu_dir_name(entry->d_name, &cpu)) {
|
||||
cpus.push_back(cpu);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
std::sort(cpus.begin(), cpus.end());
|
||||
cpus.erase(std::unique(cpus.begin(), cpus.end()), cpus.end());
|
||||
return cpus;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
static bool apple_sme_counted_perf_level(std::string name) {
|
||||
for (std::string::size_type i = 0; i < name.size(); ++i) {
|
||||
name[i] = (char) std::tolower((unsigned char) name[i]);
|
||||
}
|
||||
|
||||
// Conservative ceiling: only count perf-level names observed to provide full SME throughput.
|
||||
// Future names should be calibrated here before they raise the automatic SME thread cap.
|
||||
return name.find("super") != std::string::npos ||
|
||||
name.find("performance") != std::string::npos;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void add_smcus_from_smidr(uint64_t smidr, size_t & num_private, std::map<uint32_t, size_t> & shared_counts) {
|
||||
// Arm ARM: SMIDR_EL1. SH==0 is implementation-defined; keep the existing
|
||||
// conservative policy and only treat zero affinity as private.
|
||||
const uint32_t sh = (uint32_t)((smidr >> 13) & 0x3);
|
||||
const uint32_t nsmc = (uint32_t)((smidr >> 56) & 0xF);
|
||||
const size_t shared_count = nsmc == 0xF ? 1 : (size_t)nsmc + 1;
|
||||
const uint32_t affinity = (uint32_t)(smidr & 0xFFFu);
|
||||
const uint32_t affinity2 = (uint32_t)((smidr >> 32) & 0xFFFFFu);
|
||||
const uint32_t id = (affinity2 << 12) | affinity;
|
||||
|
||||
if (nsmc == 0xF) {
|
||||
GGML_LOG_WARN("kleidiai: NSMC detected as 0xF indicating reseved value, setting min safe shared SMCU count to 1");
|
||||
}
|
||||
|
||||
switch (sh) {
|
||||
case 2: // private SMCU
|
||||
++num_private;
|
||||
break;
|
||||
case 3: // shared SMCU
|
||||
if (shared_counts[id] < shared_count) {
|
||||
shared_counts[id] = shared_count;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
if (id == 0) {
|
||||
++num_private;
|
||||
} else if (shared_counts[id] < shared_count) {
|
||||
shared_counts[id] = shared_count;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t detect_num_smcus() {
|
||||
if (!ggml_cpu_has_sme()) {
|
||||
const auto runtime_feat = ggml_feats_get_arch64_runtime();
|
||||
if (!runtime_feat.has_sme) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__) && defined(__aarch64__)
|
||||
// Linux/aarch64: Best-effort count of Streaming Mode Compute Units (SMCUs) via SMIDR_EL1 sysfs.
|
||||
size_t num_private = 0;
|
||||
std::set<uint32_t> shared_ids;
|
||||
std::map<uint32_t, size_t> shared_counts;
|
||||
|
||||
for (size_t cpu = 0;; ++cpu) {
|
||||
const std::vector<size_t> cpus = detect_cpu_ids();
|
||||
for (const size_t cpu : cpus) {
|
||||
const std::string path =
|
||||
"/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
|
||||
"/regs/identification/smidr_el1";
|
||||
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) {
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t smidr = 0;
|
||||
@@ -118,54 +210,69 @@ static size_t detect_num_smcus() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Arm ARM: SMIDR_EL1
|
||||
const uint32_t sh = (uint32_t)((smidr >> 13) & 0x3);
|
||||
// Build an "affinity-like" identifier for shared SMCUs.
|
||||
// Keep the original packing logic, but isolate it here.
|
||||
const uint32_t id = (uint32_t)((smidr & 0xFFFu) | ((smidr >> 20) & 0xFFFFF000u));
|
||||
|
||||
switch (sh) {
|
||||
case 0b10: // private SMCU
|
||||
++num_private;
|
||||
break;
|
||||
case 0b11: // shared SMCU
|
||||
shared_ids.emplace(id);
|
||||
break;
|
||||
case 0b00:
|
||||
// Ambiguous / implementation-defined. Be conservative:
|
||||
// treat id==0 as private, otherwise as shared.
|
||||
if (id == 0) ++num_private;
|
||||
else shared_ids.emplace(id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
add_smcus_from_smidr(smidr, num_private, shared_counts);
|
||||
}
|
||||
|
||||
return num_private + shared_ids.size();
|
||||
size_t total = num_private;
|
||||
for (const auto & entry : shared_counts) {
|
||||
total += entry.second;
|
||||
}
|
||||
return total;
|
||||
|
||||
#elif defined(__APPLE__) && defined(__aarch64__)
|
||||
// table for known M4 variants. Users can override via GGML_KLEIDIAI_SME=<n>.
|
||||
char chip_name[256] = {};
|
||||
size_t size = sizeof(chip_name);
|
||||
int perf_levels = 0;
|
||||
size_t size = sizeof(perf_levels);
|
||||
if (sysctlbyname("hw.nperflevels", &perf_levels, &size, nullptr, 0) != 0 ||
|
||||
size != sizeof(perf_levels) || perf_levels <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sysctlbyname("machdep.cpu.brand_string", chip_name, &size, nullptr, 0) == 0) {
|
||||
const std::string brand(chip_name);
|
||||
size_t units = 0;
|
||||
for (int i = 0; i < perf_levels; ++i) {
|
||||
char key[64] = {};
|
||||
int physical_cpus = 0;
|
||||
int cpus_per_l2 = 0;
|
||||
|
||||
struct ModelSMCU { const char *match; size_t smcus; };
|
||||
static const ModelSMCU table[] = {
|
||||
{ "M4 Ultra", 2 },
|
||||
{ "M4 Max", 2 },
|
||||
{ "M4 Pro", 2 },
|
||||
{ "M4", 1 },
|
||||
};
|
||||
snprintf(key, sizeof(key), "hw.perflevel%d.physicalcpu", i);
|
||||
size = sizeof(physical_cpus);
|
||||
if (sysctlbyname(key, &physical_cpus, &size, nullptr, 0) != 0 ||
|
||||
size != sizeof(physical_cpus) || physical_cpus <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &e : table) {
|
||||
if (brand.find(e.match) != std::string::npos) {
|
||||
return e.smcus;
|
||||
}
|
||||
snprintf(key, sizeof(key), "hw.perflevel%d.cpusperl2", i);
|
||||
size = sizeof(cpus_per_l2);
|
||||
if (sysctlbyname(key, &cpus_per_l2, &size, nullptr, 0) != 0 ||
|
||||
size != sizeof(cpus_per_l2) || cpus_per_l2 <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(key, sizeof(key), "hw.perflevel%d.name", i);
|
||||
size = 0;
|
||||
if (sysctlbyname(key, nullptr, &size, nullptr, 0) != 0 || size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name(size, '\0');
|
||||
if (sysctlbyname(key, &name[0], &size, nullptr, 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
name.resize(size);
|
||||
while (!name.empty() && name.back() == '\0') {
|
||||
name.pop_back();
|
||||
}
|
||||
|
||||
if (apple_sme_counted_perf_level(name)) {
|
||||
units += (size_t) ((physical_cpus + cpus_per_l2 - 1) / cpus_per_l2);
|
||||
}
|
||||
}
|
||||
|
||||
return units;
|
||||
|
||||
#elif defined(_WIN32) && (defined(_M_ARM64) || defined(__aarch64__))
|
||||
// No verified Windows arm64 SMCU detection path yet. Return unknown and use
|
||||
// GGML_KLEIDIAI_SME=N as a diagnostics/debug override for SME thread cap
|
||||
// calibration until a detection mechanism is verified on real hardware.
|
||||
return 0;
|
||||
|
||||
#else
|
||||
@@ -198,15 +305,18 @@ static void init_kleidiai_context(void) {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
// Optional diagnostics/debug overrides; production defaults come from runtime detection.
|
||||
const char *env_sme = getenv("GGML_KLEIDIAI_SME");
|
||||
const char *env_threads = getenv("GGML_TOTAL_THREADS");
|
||||
const char *env_chunk_mult = getenv("GGML_KLEIDIAI_CHUNK_MULTIPLIER");
|
||||
|
||||
const auto runtime_feat = ggml_feats_get_arch64_runtime();
|
||||
|
||||
size_t detected_smcus = 0;
|
||||
|
||||
ctx.features = (ggml_cpu_has_dotprod() ? CPU_FEATURE_DOTPROD : CPU_FEATURE_NONE) |
|
||||
(ggml_cpu_has_matmul_int8() ? CPU_FEATURE_I8MM : CPU_FEATURE_NONE) |
|
||||
((ggml_cpu_has_sve() && ggml_cpu_get_sve_cnt() == QK8_0) ? CPU_FEATURE_SVE : CPU_FEATURE_NONE);
|
||||
ctx.features = (runtime_feat.has_dotprod ? CPU_FEATURE_DOTPROD : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.has_i8mm ? CPU_FEATURE_I8MM : CPU_FEATURE_NONE) |
|
||||
(runtime_feat.sve_cnt == QK8_0 ? CPU_FEATURE_SVE : CPU_FEATURE_NONE);
|
||||
|
||||
if (env_threads) {
|
||||
bool ok = false;
|
||||
@@ -224,54 +334,54 @@ static void init_kleidiai_context(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// SME policy:
|
||||
// - env unset => auto-detect SMCUs; enable SME only if detected > 0.
|
||||
// - env=0 => force off.
|
||||
// - env>0 => force N cores, if the binary was built with SME.
|
||||
int sme_cores = 0;
|
||||
bool sme_env_ok = false;
|
||||
bool sme_env_set = (env_sme != nullptr);
|
||||
|
||||
const bool has_supported_sme_family = runtime_feat.has_sme;
|
||||
bool sme_cap_detected = false;
|
||||
|
||||
if (has_supported_sme_family) {
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cap_detected = detected_smcus > 0;
|
||||
// Some platforms expose SME without exposing a calibrated SMCU count.
|
||||
// Use one SME thread as the conservative default; add platform SMCU detection to raise it.
|
||||
sme_cores = sme_cap_detected ? (int)detected_smcus : 1;
|
||||
|
||||
if (!sme_env_set && !sme_cap_detected) {
|
||||
GGML_LOG_INFO("kleidiai: SME detected; SMCU count unavailable, using conservative SME thread cap=1\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime-detect SME support and available SMCUs first. The detected SMCU
|
||||
// count is used as the SME thread cap, and GGML_KLEIDIAI_SME can debug-override that:
|
||||
// - unset: use runtime detection.
|
||||
// - 0: disable SME-family kernels.
|
||||
// - N > 0: use N as the SME thread cap, if an SME-family kernel is selectable.
|
||||
if (sme_env_set) {
|
||||
bool ok = false;
|
||||
int v = parse_uint_env(env_sme, "GGML_KLEIDIAI_SME", &ok);
|
||||
sme_env_ok = ok;
|
||||
|
||||
if (!ok) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME set but parsing failed; falling back to runtime SME-core detection\n");
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cores = detected_smcus > 0 ? (int)detected_smcus : 0;
|
||||
} else if (v == 0) {
|
||||
sme_cores = 0;
|
||||
} else if (!ggml_cpu_has_sme()) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME=%d but the binary was not built with SME; disabling SME\n", v);
|
||||
sme_cores = 0;
|
||||
if (ok) {
|
||||
if (has_supported_sme_family) {
|
||||
sme_cores = v;
|
||||
} else {
|
||||
if (v > 0) {
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME=%d but SME is not supported on this CPU; disabling SME-family kernels\n", v);
|
||||
}
|
||||
sme_cores = 0;
|
||||
}
|
||||
} else {
|
||||
sme_cores = v;
|
||||
GGML_LOG_WARN("kleidiai: GGML_KLEIDIAI_SME set but parsing failed; using automatic SME thread cap\n");
|
||||
}
|
||||
} else {
|
||||
detected_smcus = detect_num_smcus();
|
||||
sme_cores = detected_smcus > 0 ? (int)detected_smcus : 0;
|
||||
}
|
||||
|
||||
if (!sme_env_set && ggml_cpu_has_sme() && sme_cores == 0) {
|
||||
GGML_LOG_WARN("kleidiai: runtime SME-core detection returned 0; falling back to NEON\n");
|
||||
}
|
||||
|
||||
if (sme_cores > 0) {
|
||||
if (sme_cores > 0 && has_supported_sme_family) {
|
||||
ctx.features |= CPU_FEATURE_SME;
|
||||
#if defined(__aarch64__) && defined(__linux__)
|
||||
// ARM guarantees SME2 implies SME, so only check SME2 when SME is enabled.
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_SME2) {
|
||||
if (runtime_feat.has_sme2) {
|
||||
ctx.features |= CPU_FEATURE_SME2;
|
||||
}
|
||||
#elif defined(__aarch64__) && defined(__APPLE__)
|
||||
int feat_sme2 = 0;
|
||||
size_t size = sizeof(feat_sme2);
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SME2", &feat_sme2, &size, NULL, 0) == 0 && feat_sme2) {
|
||||
ctx.features |= CPU_FEATURE_SME2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Kernel selection
|
||||
@@ -297,16 +407,19 @@ static void init_kleidiai_context(void) {
|
||||
GGML_LOG_INFO("kleidiai: primary f32 kernel feature %s\n", cpu_feature_to_string(ctx.kernels_f32->required_cpu));
|
||||
}
|
||||
|
||||
ctx.sme_thread_cap = (ctx.features & CPU_FEATURE_SME) ? sme_cores : 0;
|
||||
const bool has_selected_sme_family_kernel =
|
||||
(ctx.kernels_q4 && is_sme_family(ctx.kernels_q4->required_cpu)) ||
|
||||
(ctx.kernels_q8 && is_sme_family(ctx.kernels_q8->required_cpu)) ||
|
||||
(ctx.kernels_f32 && is_sme_family(ctx.kernels_f32->required_cpu));
|
||||
ctx.sme_thread_cap = has_selected_sme_family_kernel ? sme_cores : 0;
|
||||
|
||||
if (ctx.features & CPU_FEATURE_SME) {
|
||||
const bool has_sme2 = (ctx.features & CPU_FEATURE_SME2) != CPU_FEATURE_NONE;
|
||||
if (has_selected_sme_family_kernel) {
|
||||
if (sme_env_set && sme_env_ok && sme_cores > 0) {
|
||||
GGML_LOG_INFO("kleidiai: SME%s enabled (GGML_KLEIDIAI_SME=%d override)\n",
|
||||
has_sme2 ? "2" : "", sme_cores);
|
||||
GGML_LOG_INFO("kleidiai: SME enabled (GGML_KLEIDIAI_SME=%d debug override)\n", sme_cores);
|
||||
} else if (sme_cap_detected) {
|
||||
GGML_LOG_INFO("kleidiai: SME enabled (runtime-detected SME thread cap=%d)\n", sme_cores);
|
||||
} else {
|
||||
GGML_LOG_INFO("kleidiai: SME%s enabled (runtime-detected SME cores=%d)\n",
|
||||
has_sme2 ? "2" : "", sme_cores);
|
||||
GGML_LOG_INFO("kleidiai: SME enabled (runtime SME detected, conservative thread cap=%d)\n", sme_cores);
|
||||
}
|
||||
} else {
|
||||
GGML_LOG_INFO("kleidiai: SME disabled\n");
|
||||
@@ -467,7 +580,7 @@ static int kleidiai_collect_kernel_chain_common(
|
||||
}
|
||||
|
||||
if (is_sme_family(primary->required_cpu)) {
|
||||
const cpu_feature fallback_mask = static_cast<cpu_feature>(features & ~CPU_FEATURE_SME & ~CPU_FEATURE_SME2);
|
||||
const cpu_feature fallback_mask = static_cast<cpu_feature>(features & ~(CPU_FEATURE_SME | CPU_FEATURE_SME2));
|
||||
if (fallback_mask != CPU_FEATURE_NONE) {
|
||||
ggml_kleidiai_kernels * fallback = select_fallback(fallback_mask);
|
||||
if (fallback && fallback != primary &&
|
||||
@@ -1077,13 +1190,14 @@ class tensor_traits : public ggml::cpu::tensor_traits {
|
||||
const int ith_total = params->ith;
|
||||
|
||||
int sme_slot = -1;
|
||||
int non_sme_slot = -1;
|
||||
for (int i = 0; i < runtime_count; ++i) {
|
||||
if (is_sme_family(runtime[i].kernels->required_cpu)) {
|
||||
sme_slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int non_sme_slot = -1;
|
||||
|
||||
for (int i = 0; i < runtime_count; ++i) {
|
||||
if (!is_sme_family(runtime[i].kernels->required_cpu)) {
|
||||
non_sme_slot = i;
|
||||
|
||||
@@ -8941,7 +8941,7 @@ static void ggml_compute_forward_flash_attn_ext_tiled(
|
||||
for (int tk = 0; tk < kv_tile; tk++) {
|
||||
const char * v_data = (const char *)v->data + (ic + tk)*nbv1 + iv2*nbv2 + iv3*nbv3;
|
||||
if (kv_type == GGML_TYPE_F16) {
|
||||
ggml_fp16_to_fp32_row((const ggml_fp16_t *)v_data, V32 + tk * DV, DV);
|
||||
ggml_cpu_fp16_to_fp32((const ggml_fp16_t *)v_data, V32 + tk * DV, DV);
|
||||
} else {
|
||||
memcpy(V32 + tk * DV, v_data, DV * sizeof(float));
|
||||
}
|
||||
|
||||
@@ -1865,6 +1865,37 @@ static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor
|
||||
ggml_cuda_mul_mat_cublas(ctx, src0, src1, dst);
|
||||
}
|
||||
|
||||
// returns true when ggml_cuda_mul_mat_id takes the fallback path that requires stream synchronization
|
||||
// [TAG_MUL_MAT_ID_CUDA_GRAPHS]
|
||||
static bool ggml_cuda_mul_mat_id_needs_sync(const ggml_tensor * dst, const int cc) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
|
||||
if (src1->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dst->ne[2] <= MMVQ_MAX_BATCH_SIZE) {
|
||||
if (ggml_is_quantized(src0->type)) {
|
||||
if (dst->ne[2] <= get_mmvq_mmid_max_batch(src0->type, cc)) {
|
||||
return false;
|
||||
}
|
||||
} else if (GGML_CUDA_CC_IS_AMD(cc)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmq(src0->type, cc, src1->ne[2], /*n_experts=*/src0->ne[2])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ggml_cuda_should_use_mmf(src0->type, cc, WARP_SIZE, src0->ne, src0->nb, src1->ne[2], /*mul_mat_id=*/true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const ggml_tensor * src1 = dst->src[1];
|
||||
@@ -1907,7 +1938,7 @@ static void ggml_cuda_mul_mat_id(ggml_backend_cuda_context & ctx, ggml_tensor *
|
||||
}
|
||||
|
||||
// note: this path should not be reached when recording CUDA graphs, because it requires stream synchronization
|
||||
// TODO: add asserts to verify this. should work with CUDA, HIP, etc.
|
||||
GGML_ASSERT(ggml_cuda_mul_mat_id_needs_sync(dst, cc));
|
||||
cudaStream_t stream = ctx.stream();
|
||||
|
||||
GGML_ASSERT(nb12 % nb11 == 0);
|
||||
@@ -2522,10 +2553,8 @@ static bool ggml_cuda_graph_check_compability(ggml_cgraph * cgraph) {
|
||||
// [TAG_MUL_MAT_ID_CUDA_GRAPHS]
|
||||
if (node->op == GGML_OP_MUL_MAT_ID) {
|
||||
const int cc = ggml_cuda_info().devices[ggml_cuda_get_device()].cc;
|
||||
const int mmvq_mmid_max = get_mmvq_mmid_max_batch(node->src[0]->type, cc);
|
||||
if (!ggml_is_quantized(node->src[0]->type) || node->ne[2] > mmvq_mmid_max) {
|
||||
// under these conditions, the mul_mat_id operation will need to synchronize the stream, so we cannot use CUDA graphs
|
||||
// TODO: figure out a way to enable for larger batch sizes, without hurting performance
|
||||
if (ggml_cuda_mul_mat_id_needs_sync(node, cc)) {
|
||||
// the mul_mat_id fallback path synchronizes the stream, so we cannot use CUDA graphs
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/18958
|
||||
use_cuda_graph = false;
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -141,6 +141,57 @@ static __global__ void rwkv_wkv7_f32(const int B, const int T, const int C, cons
|
||||
}
|
||||
}
|
||||
|
||||
template <int rows_per_block>
|
||||
static __global__ void __launch_bounds__(WARP_SIZE * rows_per_block, 2)
|
||||
rwkv_wkv7_f32_t1_warp_row(const int T, const int C, const int H, const float * r, const float * w, const float * k, const float * v, const float * a, const float * b, const float * s, float * dst) {
|
||||
constexpr int head_size = CUDA_WKV_BLOCK_SIZE;
|
||||
constexpr int half_head = head_size / 2;
|
||||
|
||||
const int lane = threadIdx.x;
|
||||
const int row = blockIdx.y * rows_per_block + threadIdx.y;
|
||||
const int bid = blockIdx.x;
|
||||
|
||||
const int batch_i = bid / H;
|
||||
const int head_i = bid % H;
|
||||
const int state_size = C * head_size;
|
||||
const int head_off = head_i * head_size;
|
||||
const int t = batch_i * C + head_off + row;
|
||||
|
||||
__shared__ float _r[head_size], _w[head_size], _k[head_size], _a[head_size], _b[head_size];
|
||||
|
||||
if (threadIdx.y == 0) {
|
||||
_r[lane] = r[batch_i * C + head_off + lane];
|
||||
_w[lane] = w[batch_i * C + head_off + lane];
|
||||
_k[lane] = k[batch_i * C + head_off + lane];
|
||||
_a[lane] = a[batch_i * C + head_off + lane];
|
||||
_b[lane] = b[batch_i * C + head_off + lane];
|
||||
|
||||
_r[lane + half_head] = r[batch_i * C + head_off + lane + half_head];
|
||||
_w[lane + half_head] = w[batch_i * C + head_off + lane + half_head];
|
||||
_k[lane + half_head] = k[batch_i * C + head_off + lane + half_head];
|
||||
_a[lane + half_head] = a[batch_i * C + head_off + lane + half_head];
|
||||
_b[lane + half_head] = b[batch_i * C + head_off + lane + half_head];
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const int64_t state_base = batch_i * state_size + head_i * head_size * head_size + row * head_size;
|
||||
const float s0 = s[state_base + lane];
|
||||
const float s1 = s[state_base + lane + half_head];
|
||||
const float sa = warp_reduce_sum(_a[lane] * s0 + _a[lane + half_head] * s1);
|
||||
|
||||
const float vt = v[t];
|
||||
const float st0 = s0 * _w[lane] + _k[lane] * vt + sa * _b[lane];
|
||||
const float st1 = s1 * _w[lane + half_head] + _k[lane + half_head] * vt + sa * _b[lane + half_head];
|
||||
const float y = warp_reduce_sum(st0 * _r[lane] + st1 * _r[lane + half_head]);
|
||||
|
||||
dst[T * C + state_base + lane] = st0;
|
||||
dst[T * C + state_base + lane + half_head] = st1;
|
||||
|
||||
if (lane == 0) {
|
||||
dst[t] = y;
|
||||
}
|
||||
}
|
||||
|
||||
void ggml_cuda_op_rwkv_wkv6(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
const float * k_d = (const float *)dst->src[0]->data;
|
||||
const float * v_d = (const float *)dst->src[1]->data;
|
||||
@@ -191,7 +242,10 @@ void ggml_cuda_op_rwkv_wkv7(ggml_backend_cuda_context & ctx, ggml_tensor * dst)
|
||||
GGML_ASSERT(C % H == 0);
|
||||
GGML_ASSERT(C / H == CUDA_WKV_BLOCK_SIZE || C / H == CUDA_WKV_BLOCK_SIZE * 2);
|
||||
|
||||
if (C / H == CUDA_WKV_BLOCK_SIZE) {
|
||||
if (T / B == 1 && C / H == CUDA_WKV_BLOCK_SIZE) {
|
||||
constexpr int rows_per_block = 4;
|
||||
rwkv_wkv7_f32_t1_warp_row<rows_per_block><<<dim3(B * H, CUDA_WKV_BLOCK_SIZE / rows_per_block), dim3(WARP_SIZE, rows_per_block), 0, stream>>>(T, C, H, r_d, w_d, k_d, v_d, a_d, b_d, s_d, dst_d);
|
||||
} else if (C / H == CUDA_WKV_BLOCK_SIZE) {
|
||||
rwkv_wkv7_f32<CUDA_WKV_BLOCK_SIZE><<<B * H, C / H, 0, stream>>>(B, T, C, H, r_d, w_d, k_d, v_d, a_d, b_d, s_d, dst_d);
|
||||
} else {
|
||||
rwkv_wkv7_f32<CUDA_WKV_BLOCK_SIZE * 2><<<B * H, C / H, 0, stream>>>(B, T, C, H, r_d, w_d, k_d, v_d, a_d, b_d, s_d, dst_d);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#if !defined(HWCAP2_SVE2)
|
||||
#define HWCAP2_SVE2 (1ULL << 1)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_FPHP)
|
||||
#define HWCAP_FPHP (1 << 9)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_ASIMDHP)
|
||||
#define HWCAP_ASIMDHP (1 << 10)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_I8MM)
|
||||
#define HWCAP2_I8MM (1ULL << 13)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_ASIMDDP)
|
||||
#define HWCAP_ASIMDDP (1 << 20)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP_SVE)
|
||||
#define HWCAP_SVE (1 << 22)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_SME)
|
||||
#define HWCAP2_SME (1ULL << 23)
|
||||
#endif
|
||||
|
||||
#if !defined(HWCAP2_SME2)
|
||||
#define HWCAP2_SME2 (1ULL << 37)
|
||||
#endif
|
||||
|
||||
#if !defined(PR_SVE_GET_VL)
|
||||
#define PR_SVE_GET_VL 51
|
||||
#endif
|
||||
|
||||
#if !defined(PR_SVE_VL_LEN_MASK)
|
||||
#define PR_SVE_VL_LEN_MASK 0xffff
|
||||
#endif
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
#if !defined(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE 43
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_SVE_INSTRUCTIONS_AVAILABLE 46
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE 47
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 66
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE 67
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_SME_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_SME_INSTRUCTIONS_AVAILABLE 70
|
||||
#endif
|
||||
|
||||
#if !defined(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE)
|
||||
#define PF_ARM_SME2_INSTRUCTIONS_AVAILABLE 71
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct ggml_feats_arch64_runtime {
|
||||
bool has_dotprod;
|
||||
bool has_fp16;
|
||||
bool has_sve;
|
||||
bool has_sve2;
|
||||
bool has_i8mm;
|
||||
bool has_sme;
|
||||
bool has_sme2;
|
||||
int sve_cnt;
|
||||
} ggml_feats_arch64_runtime_t;
|
||||
|
||||
static inline ggml_feats_arch64_runtime_t ggml_feats_get_arch64_runtime(void) {
|
||||
ggml_feats_arch64_runtime_t runtime_feat = {};
|
||||
|
||||
#if defined(__linux__)
|
||||
const unsigned long hwcap = getauxval(AT_HWCAP);
|
||||
const unsigned long hwcap2 = getauxval(AT_HWCAP2);
|
||||
|
||||
runtime_feat.has_dotprod = !!(hwcap & HWCAP_ASIMDDP);
|
||||
runtime_feat.has_fp16 = !!(hwcap & HWCAP_FPHP) && !!(hwcap & HWCAP_ASIMDHP);;
|
||||
runtime_feat.has_sve = !!(hwcap & HWCAP_SVE);
|
||||
runtime_feat.has_sve2 = !!(hwcap2 & HWCAP2_SVE2);
|
||||
runtime_feat.has_i8mm = !!(hwcap2 & HWCAP2_I8MM);
|
||||
runtime_feat.has_sme = !!(hwcap2 & HWCAP2_SME);
|
||||
runtime_feat.has_sme2 = !!(hwcap2 & HWCAP2_SME2);
|
||||
|
||||
if (runtime_feat.has_sve) {
|
||||
const int vl = prctl(PR_SVE_GET_VL);
|
||||
if (vl >= 0) {
|
||||
runtime_feat.sve_cnt = vl & PR_SVE_VL_LEN_MASK;
|
||||
}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int oldp = 0;
|
||||
size_t size = sizeof(oldp);
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_DotProd", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_dotprod = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_FP16", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_fp16 = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SVE", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_sve = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SVE2", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_sve2 = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_I8MM", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_i8mm = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SME", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_sme = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
if (sysctlbyname("hw.optional.arm.FEAT_SME2", &oldp, &size, nullptr, 0) == 0) {
|
||||
runtime_feat.has_sme2 = static_cast<bool>(oldp);
|
||||
}
|
||||
|
||||
// Apple does not support userspace non-streaming SVE; keep SVE vector length unknown.
|
||||
runtime_feat.sve_cnt = 0;
|
||||
#elif defined (_WIN32)
|
||||
runtime_feat.has_dotprod = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_fp16 = IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sve = IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sve2 = IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_i8mm = IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sme = IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
runtime_feat.has_sme2 = IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
|
||||
// Windows exposes SVE feature presence, but not the runtime SVE vector length here.
|
||||
runtime_feat.sve_cnt = 0;
|
||||
#endif
|
||||
|
||||
return runtime_feat;
|
||||
}
|
||||
|
||||
#endif // defined(__aarch64__) || defined(_M_ARM64)
|
||||
@@ -126,9 +126,6 @@ if (GGML_HIP_EXPORT_METRICS)
|
||||
set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -Rpass-analysis=kernel-resource-usage --save-temps")
|
||||
endif()
|
||||
|
||||
# Fast math for HIP, like CUDA's -use_fast_math. Not -ffast-math: that implies -ffinite-math-only, which breaks ggml's INFINITY masking and produces NaNs.
|
||||
set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -funsafe-math-optimizations")
|
||||
|
||||
if (NOT GGML_CUDA_FA)
|
||||
add_compile_definitions(GGML_CUDA_NO_FA)
|
||||
endif()
|
||||
|
||||
@@ -953,6 +953,11 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mv(ggml_meta
|
||||
nr0 = N_R0_IQ4_XS;
|
||||
smem = 32*sizeof(float);
|
||||
} break;
|
||||
case GGML_TYPE_TQ2_0:
|
||||
{
|
||||
nsg = N_SG_TQ2_0;
|
||||
nr0 = N_R0_TQ2_0;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
GGML_LOG_ERROR("Asserting on type %d\n", (int) tsrc0);
|
||||
@@ -1182,6 +1187,11 @@ ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline_mul_mv_id(ggml_m
|
||||
nr0 = N_R0_IQ4_XS;
|
||||
smem = 32*sizeof(float);
|
||||
} break;
|
||||
case GGML_TYPE_TQ2_0:
|
||||
{
|
||||
nsg = N_SG_TQ2_0;
|
||||
nr0 = N_R0_TQ2_0;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
GGML_LOG_ERROR("Asserting on type %d\n", (int)op->src[2]->type);
|
||||
|
||||
@@ -1407,6 +1407,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
case GGML_TYPE_I32:
|
||||
return true;
|
||||
default:
|
||||
@@ -1435,6 +1436,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_Q8_0:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
switch (op->type) {
|
||||
case GGML_TYPE_F32:
|
||||
case GGML_TYPE_F16:
|
||||
@@ -1470,6 +1472,7 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
#define N_R0_IQ4_XS 2
|
||||
#define N_SG_IQ4_XS 2
|
||||
|
||||
#define N_R0_TQ2_0 4
|
||||
#define N_SG_TQ2_0 2
|
||||
|
||||
// function constants offsets
|
||||
#define FC_FLASH_ATTN_EXT_PAD 100
|
||||
#define FC_FLASH_ATTN_EXT_BLK 200
|
||||
|
||||
@@ -468,6 +468,34 @@ void quantize_iq4_nl(device const float * src, device block_iq4_nl & dst) {
|
||||
dst.d = sumq2 > 0 ? sumqx/sumq2 : d;
|
||||
}
|
||||
|
||||
void quantize_tq2_0(device const float * src, device block_tq2_0 & dst) {
|
||||
#pragma METAL fp math_mode(safe)
|
||||
float amax = 0.0f; // absolute max
|
||||
|
||||
for (int j = 0; j < QK_K; j++) {
|
||||
const float v = src[j];
|
||||
amax = MAX(amax, fabs(v));
|
||||
}
|
||||
|
||||
const float d = amax;
|
||||
const float id = d ? 1.0f/d : 0.0f;
|
||||
|
||||
dst.d = (half) d;
|
||||
|
||||
for (int j = 0; j < QK_K/4; j += 32) {
|
||||
for (int m = 0; m < 32; ++m) {
|
||||
uint8_t q = 0;
|
||||
for (int n = 0; n < 4; ++n) {
|
||||
// -1, 0, 1 -> 0, 1, 2
|
||||
int xi = (int)round(src[m + n*32] * id) + 1;
|
||||
q += (uint8_t)((xi & 3) << (2*n));
|
||||
}
|
||||
dst.qs[j + m] = q;
|
||||
}
|
||||
src += 4*32;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename type4x4>
|
||||
void dequantize_q4_1(device const block_q4_1 * xb, short il, thread type4x4 & reg) {
|
||||
device const uint16_t * qs = ((device const uint16_t *)xb + 2);
|
||||
@@ -1021,6 +1049,25 @@ void dequantize_iq4_xs(device const block_iq4_xs * xb, short il, thread type4x4
|
||||
}
|
||||
}
|
||||
|
||||
template <typename type4x4>
|
||||
void dequantize_tq2_0(device const block_tq2_0 * xb, short il, thread type4x4 & reg) {
|
||||
device const uint8_t * qs = xb->qs;
|
||||
const float d = xb->d;
|
||||
|
||||
float4x4 reg_f;
|
||||
|
||||
// 2 bits per element, 4 elements per byte, 128 elements per 32-byte group
|
||||
const short base = il * 16;
|
||||
for (int k = 0; k < 16; k++) {
|
||||
const int i = base + k;
|
||||
const int byte = ((i >> 7) & 1) * 32 + (i & 31);
|
||||
const int l = (i >> 5) & 3;
|
||||
reg_f[k/4][k%4] = d * (float)(((qs[byte] >> (2*l)) & 3) - 1);
|
||||
}
|
||||
|
||||
reg = (type4x4) reg_f;
|
||||
}
|
||||
|
||||
enum ggml_sort_order {
|
||||
GGML_SORT_ORDER_ASC,
|
||||
GGML_SORT_ORDER_DESC,
|
||||
@@ -8001,6 +8048,7 @@ template [[host_name("kernel_cpy_f32_q4_1")]] kernel cpy_f_q_t kernel_cpy_f32_
|
||||
template [[host_name("kernel_cpy_f32_q5_0")]] kernel cpy_f_q_t kernel_cpy_f32_q<QK5_0, block_q5_0, quantize_q5_0>;
|
||||
template [[host_name("kernel_cpy_f32_q5_1")]] kernel cpy_f_q_t kernel_cpy_f32_q<QK5_1, block_q5_1, quantize_q5_1>;
|
||||
template [[host_name("kernel_cpy_f32_iq4_nl")]] kernel cpy_f_q_t kernel_cpy_f32_q<QK4_NL, block_iq4_nl, quantize_iq4_nl>;
|
||||
template [[host_name("kernel_cpy_f32_tq2_0")]] kernel cpy_f_q_t kernel_cpy_f32_q<QK_K, block_tq2_0, quantize_tq2_0>;
|
||||
|
||||
template<typename T4x4, typename block_q, short nl, void (*dequantize_func)(device const block_q *, short, thread T4x4 &)>
|
||||
kernel void kernel_cpy_q_f32(
|
||||
@@ -8048,6 +8096,8 @@ template [[host_name("kernel_cpy_q5_0_f32")]] kernel cpy_q_f_t kernel_cpy_q_f32<
|
||||
template [[host_name("kernel_cpy_q5_1_f32")]] kernel cpy_q_f_t kernel_cpy_q_f32<float4x4, block_q5_1, 2, dequantize_q5_1>;
|
||||
template [[host_name("kernel_cpy_q8_0_f32")]] kernel cpy_q_f_t kernel_cpy_q_f32<float4x4, block_q8_0, 2, dequantize_q8_0>;
|
||||
|
||||
template [[host_name("kernel_cpy_tq2_0_f32")]] kernel cpy_q_f_t kernel_cpy_q_f32<float4x4, block_tq2_0, QK_NL, dequantize_tq2_0>;
|
||||
|
||||
template [[host_name("kernel_cpy_q1_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_q1_0, 8, dequantize_q1_0>;
|
||||
template [[host_name("kernel_cpy_q2_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_q2_0, 4, dequantize_q2_0>;
|
||||
template [[host_name("kernel_cpy_q4_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_q4_0, 2, dequantize_q4_0>;
|
||||
@@ -8056,6 +8106,8 @@ template [[host_name("kernel_cpy_q5_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<
|
||||
template [[host_name("kernel_cpy_q5_1_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_q5_1, 2, dequantize_q5_1>;
|
||||
template [[host_name("kernel_cpy_q8_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_q8_0, 2, dequantize_q8_0>;
|
||||
|
||||
template [[host_name("kernel_cpy_tq2_0_f16")]] kernel cpy_q_f_t kernel_cpy_q_f32<half4x4, block_tq2_0, QK_NL, dequantize_tq2_0>;
|
||||
|
||||
template<typename T>
|
||||
kernel void kernel_concat(
|
||||
constant ggml_metal_kargs_concat & args,
|
||||
@@ -9822,6 +9874,121 @@ kernel void kernel_mul_mv_mxfp4_f32(
|
||||
kernel_mul_mv_mxfp4_f32_impl<N_R0_MXFP4, constant ggml_metal_kargs_mul_mv &>(args, src0, src1, dst, shmem, tgpig, tiisg, sgitg);
|
||||
}
|
||||
|
||||
template<int nr0, typename args_t>
|
||||
void kernel_mul_mv_tq2_0_f32_impl(
|
||||
args_t args,
|
||||
device const char * src0,
|
||||
device const char * src1,
|
||||
device char * dst,
|
||||
threadgroup char * shmem,
|
||||
uint3 tgpig,
|
||||
ushort tiisg,
|
||||
ushort sgitg) {
|
||||
const short NSG = FC_mul_mv_nsg;
|
||||
|
||||
const int nb = args.ne00/QK_K;
|
||||
|
||||
const int r0 = tgpig.x;
|
||||
const int r1 = tgpig.y;
|
||||
const int im = tgpig.z;
|
||||
|
||||
const int first_row = (r0 * NSG + sgitg) * nr0;
|
||||
|
||||
const uint i12 = im%FC_mul_mv_ne12;
|
||||
const uint i13 = im/FC_mul_mv_ne12;
|
||||
|
||||
const uint64_t offset1 = r1*args.nb11 + (i12 )*args.nb12 + (i13 )*args.nb13;
|
||||
|
||||
device const float * y = (device const float *) (src1 + offset1);
|
||||
|
||||
device const block_tq2_0 * ax[nr0];
|
||||
for (int row = 0; row < nr0; ++row) {
|
||||
const uint64_t offset0 = (first_row + row)*args.nb01 + (i12/FC_mul_mv_r2)*args.nb02 + (i13/FC_mul_mv_r3)*args.nb03;
|
||||
ax[row] = (device const block_tq2_0 *) ((device char *) src0 + offset0);
|
||||
}
|
||||
|
||||
float sumf[nr0] = {0.f};
|
||||
|
||||
// 8 threads per block, NBLOCK blocks per pass, 2 halves per block per pass
|
||||
constexpr short NBLOCK = 4;
|
||||
|
||||
constexpr short NB = N_SIMDWIDTH/NBLOCK; // threads per block
|
||||
|
||||
const short blk = tiisg / NB; // 0..NBLOCK-1, block handled by this thread
|
||||
const short htg = tiisg % NB; // 0..NB-1, thread within block (0..7)
|
||||
|
||||
// byte and y base offsets within the block (32 elements per thread, 4 per byte)
|
||||
device const float4 * yb4 = (device const float4 *)(y + 4*htg + blk*QK_K);
|
||||
|
||||
// hoisted per-byte coefficients (from y) and total y-sum, shared across rows
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/26980
|
||||
float4 coef[4];
|
||||
|
||||
for (int ib = blk; ib < nb; ib += NBLOCK) {
|
||||
FOR_UNROLL (short h0 = 0; h0 < 2; ++h0) {
|
||||
const float4 y0 = yb4[ 0 + 32*h0];
|
||||
const float4 y1 = yb4[ 8 + 32*h0];
|
||||
const float4 y2 = yb4[16 + 32*h0];
|
||||
const float4 y3 = yb4[24 + 32*h0];
|
||||
|
||||
float sumy = 0.f;
|
||||
FOR_UNROLL (short j = 0; j < 4; ++j) {
|
||||
coef[j] = float4(
|
||||
y0[j],
|
||||
y1[j] - 4.0f*y0[j],
|
||||
y2[j] - 4.0f*y1[j],
|
||||
y3[j] - 4.0f*y2[j]);
|
||||
|
||||
sumy += (y0[j] + y1[j]) + (y2[j] + y3[j]);
|
||||
}
|
||||
|
||||
FOR_UNROLL (short row = 0; row < nr0; ++row) {
|
||||
device const block_tq2_0 & xb = ax[row][ib];
|
||||
device const uchar * qs = xb.qs + 4*htg + 32*h0;
|
||||
|
||||
float sum = -sumy;
|
||||
FOR_UNROLL (short j = 0; j < 4; ++j) {
|
||||
// express the 2-bit field shifts (v>>2, v>>4, v>>6) as float floor ops
|
||||
const float v = (float)qs[j];
|
||||
|
||||
const float f0 = v;
|
||||
const float f1 = floor(v*0.25f); // v>>2
|
||||
const float f2 = floor(v*0.0625); // v>>4
|
||||
const float f3 = floor(v*0.015625); // v>>6
|
||||
|
||||
sum += coef[j][0]*f0 + coef[j][1]*f1 + coef[j][2]*f2 + coef[j][3]*f3;
|
||||
}
|
||||
|
||||
sumf[row] += xb.d * sum;
|
||||
}
|
||||
}
|
||||
|
||||
yb4 += QK_K * NBLOCK / 4;
|
||||
}
|
||||
|
||||
device float * dst_f32 = (device float *) dst + (uint64_t)im*args.ne0*args.ne1 + (uint64_t)r1*args.ne0;
|
||||
|
||||
for (int row = 0; row < nr0; ++row) {
|
||||
const float tot = simd_sum(sumf[row]);
|
||||
if (tiisg == 0 && first_row + row < args.ne01) {
|
||||
dst_f32[first_row + row] = tot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[host_name("kernel_mul_mv_tq2_0_f32")]]
|
||||
kernel void kernel_mul_mv_tq2_0_f32(
|
||||
constant ggml_metal_kargs_mul_mv & args,
|
||||
device const char * src0,
|
||||
device const char * src1,
|
||||
device char * dst,
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
ushort tiisg[[thread_index_in_simdgroup]],
|
||||
ushort sgitg[[simdgroup_index_in_threadgroup]]) {
|
||||
|
||||
kernel_mul_mv_tq2_0_f32_impl<N_R0_TQ2_0, constant ggml_metal_kargs_mul_mv &>(args, src0, src1, dst, nullptr, tgpig, tiisg, sgitg);
|
||||
}
|
||||
|
||||
template<typename block_q, short nl, void (*dequantize_func)(device const block_q *, short, thread float4x4 &)>
|
||||
kernel void kernel_get_rows_q(
|
||||
constant ggml_metal_kargs_get_rows & args,
|
||||
@@ -9915,6 +10082,38 @@ template [[host_name("kernel_get_rows_iq1_s")]] kernel get_rows_q_t kernel_get
|
||||
template [[host_name("kernel_get_rows_iq1_m")]] kernel get_rows_q_t kernel_get_rows_q<block_iq1_m, QK_NL, dequantize_iq1_m>;
|
||||
template [[host_name("kernel_get_rows_iq4_nl")]] kernel get_rows_q_t kernel_get_rows_q<block_iq4_nl, 2, dequantize_iq4_nl>;
|
||||
template [[host_name("kernel_get_rows_iq4_xs")]] kernel get_rows_q_t kernel_get_rows_q<block_iq4_xs, QK_NL, dequantize_iq4_xs>;
|
||||
template [[host_name("kernel_get_rows_tq2_0")]] kernel get_rows_q_t kernel_get_rows_q<block_tq2_0, QK_NL, dequantize_tq2_0>;
|
||||
|
||||
template<typename TS, typename TI, short QK, typename block_q, void (*quantize_func)(device const float *, device block_q &)>
|
||||
kernel void kernel_set_rows_q(
|
||||
constant ggml_metal_kargs_set_rows & args,
|
||||
device const void * src0,
|
||||
device const void * src1,
|
||||
device float * dst,
|
||||
uint3 tgpig[[threadgroup_position_in_grid]],
|
||||
uint tiitg[[thread_index_in_threadgroup]],
|
||||
uint3 tptg [[threads_per_threadgroup]]) {
|
||||
const int32_t i03 = tgpig.z;
|
||||
const int32_t i02 = tgpig.y;
|
||||
|
||||
const int32_t i12 = i03%args.ne12;
|
||||
const int32_t i11 = i02%args.ne11;
|
||||
|
||||
const int32_t i01 = tgpig.x*tptg.y + tiitg/tptg.x;
|
||||
if (i01 >= args.ne01) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t i10 = i01;
|
||||
const TI i1 = ((const device TI *) ((const device char *) src1 + i10*args.nb10 + i11*args.nb11 + i12*args.nb12))[0];
|
||||
|
||||
device block_q * dst_row = ( device block_q *) (( device char *) dst + i1*args.nb1 + i02*args.nb2 + i03*args.nb3);
|
||||
const device TS * src_row = (const device TS *) ((const device char *) src0 + i01*args.nb01 + i02*args.nb02 + i03*args.nb03);
|
||||
|
||||
for (int ind = tiitg%tptg.x; ind < args.nk0; ind += tptg.x) {
|
||||
quantize_func(src_row + QK*ind, dst_row[ind]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TS, typename TI, typename block_q, void (*quantize_func)(device const float *, device block_q &)>
|
||||
kernel void kernel_set_rows_q32(
|
||||
@@ -10011,6 +10210,11 @@ template [[host_name("kernel_set_rows_f32_i32_q5_1")]] kernel set_rows_q32_t k
|
||||
template [[host_name("kernel_set_rows_f32_i64_iq4_nl")]] kernel set_rows_q32_t kernel_set_rows_q32<float, int64_t, block_iq4_nl, quantize_iq4_nl>;
|
||||
template [[host_name("kernel_set_rows_f32_i32_iq4_nl")]] kernel set_rows_q32_t kernel_set_rows_q32<float, int32_t, block_iq4_nl, quantize_iq4_nl>;
|
||||
|
||||
typedef decltype(kernel_set_rows_q<float, int64_t, QK_K, block_tq2_0, quantize_tq2_0>) set_rows_qK_t;
|
||||
|
||||
template [[host_name("kernel_set_rows_f32_i64_tq2_0")]] kernel set_rows_qK_t kernel_set_rows_q<float, int64_t, QK_K, block_tq2_0, quantize_tq2_0>;
|
||||
template [[host_name("kernel_set_rows_f32_i32_tq2_0")]] kernel set_rows_qK_t kernel_set_rows_q<float, int32_t, QK_K, block_tq2_0, quantize_tq2_0>;
|
||||
|
||||
kernel void kernel_diag_f32(
|
||||
constant ggml_metal_kargs_diag & args,
|
||||
device const char * src0,
|
||||
@@ -10786,6 +10990,7 @@ template [[host_name("kernel_mul_mm_iq1_s_f32")]] kernel mul_mm_t kernel_mul_m
|
||||
template [[host_name("kernel_mul_mm_iq1_m_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq1_m, QK_NL, dequantize_iq1_m, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_iq4_nl_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_nl, 2, dequantize_iq4_nl, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_iq4_xs_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_xs, QK_NL, dequantize_iq4_xs, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_tq2_0_f32")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_tq2_0, QK_NL, dequantize_tq2_0, float, float4x4, float, float2x4>;
|
||||
|
||||
template [[host_name("kernel_mul_mm_f32_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, float4x4, 1, dequantize_f32, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_f16_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, half4x4, 1, dequantize_f16, half, half4x4, half, half2x4>;
|
||||
@@ -10811,6 +11016,7 @@ template [[host_name("kernel_mul_mm_iq1_s_f16")]] kernel mul_mm_t kernel_mul_m
|
||||
template [[host_name("kernel_mul_mm_iq1_m_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq1_m, QK_NL, dequantize_iq1_m, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_iq4_nl_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_nl, 2, dequantize_iq4_nl, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_iq4_xs_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_xs, QK_NL, dequantize_iq4_xs, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_tq2_0_f16")]] kernel mul_mm_t kernel_mul_mm<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_tq2_0, QK_NL, dequantize_tq2_0, float, float4x4, half, half2x4>;
|
||||
|
||||
//
|
||||
// indirect matrix-matrix multiplication
|
||||
@@ -10845,6 +11051,7 @@ template [[host_name("kernel_mul_mm_id_iq1_s_f32")]] kernel mul_mm_id kernel_m
|
||||
template [[host_name("kernel_mul_mm_id_iq1_m_f32")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq1_m, QK_NL, dequantize_iq1_m, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_iq4_nl_f32")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_nl, 2, dequantize_iq4_nl, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_iq4_xs_f32")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_xs, QK_NL, dequantize_iq4_xs, float, float4x4, float, float2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_tq2_0_f32")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_tq2_0, QK_NL, dequantize_tq2_0, float, float4x4, float, float2x4>;
|
||||
|
||||
template [[host_name("kernel_mul_mm_id_f32_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, float4x4, 1, dequantize_f32, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_f16_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, half4x4, 1, dequantize_f16, half, half4x4, half, half2x4>;
|
||||
@@ -10870,6 +11077,7 @@ template [[host_name("kernel_mul_mm_id_iq1_s_f16")]] kernel mul_mm_id kernel_m
|
||||
template [[host_name("kernel_mul_mm_id_iq1_m_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq1_m, QK_NL, dequantize_iq1_m, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_iq4_nl_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_nl, 2, dequantize_iq4_nl, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_iq4_xs_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_iq4_xs, QK_NL, dequantize_iq4_xs, float, float4x4, half, half2x4>;
|
||||
template [[host_name("kernel_mul_mm_id_tq2_0_f16")]] kernel mul_mm_id kernel_mul_mm_id<half, half4x4, simdgroup_half8x8, half, half2x4, simdgroup_half8x8, block_tq2_0, QK_NL, dequantize_tq2_0, float, float4x4, half, half2x4>;
|
||||
|
||||
//
|
||||
// matrix-vector multiplication
|
||||
@@ -11027,6 +11235,7 @@ template [[host_name("kernel_mul_mv_id_iq3_s_f32")]] kernel kernel_mul_mv_id_t
|
||||
template [[host_name("kernel_mul_mv_id_iq2_s_f32")]] kernel kernel_mul_mv_id_t kernel_mul_mv_id<mmv_fn<kernel_mul_mv_iq2_s_f32_impl <N_R0_IQ2_S>>>;
|
||||
template [[host_name("kernel_mul_mv_id_iq4_nl_f32")]] kernel kernel_mul_mv_id_t kernel_mul_mv_id<mmv_fn<kernel_mul_mv_iq4_nl_f32_impl <N_R0_IQ4_NL>>>;
|
||||
template [[host_name("kernel_mul_mv_id_iq4_xs_f32")]] kernel kernel_mul_mv_id_t kernel_mul_mv_id<mmv_fn<kernel_mul_mv_iq4_xs_f32_impl <N_R0_IQ4_XS>>>;
|
||||
template [[host_name("kernel_mul_mv_id_tq2_0_f32")]] kernel kernel_mul_mv_id_t kernel_mul_mv_id<mmv_fn<kernel_mul_mv_tq2_0_f32_impl <N_R0_TQ2_0>>>;
|
||||
|
||||
kernel void kernel_pool_2d_max_f32(
|
||||
constant ggml_metal_kargs_pool_2d & args,
|
||||
|
||||
@@ -4929,8 +4929,13 @@ static bool ggml_opencl_ensure_fa_variant(ggml_backend_opencl_context * backend_
|
||||
const int x = (e && e[0]) ? atoi(e) : 0;
|
||||
return (x == 8 || x == 16 || x == 32) ? x : 0; // 0 = per-gen default
|
||||
}();
|
||||
// X2E needs 16 to keep per-lane o_acc at 128B (the compiler spills the
|
||||
// kernel-default width); X1E does not spill, but C=16 is still a measured
|
||||
// +28-30% DK128-GQA4 decode win there (X1-85, kv 4096/8192), neutral on
|
||||
// DK64 / GQA1 / quant-KV.
|
||||
const int fa_cl_c_gqa4 = fa_cl_c_env ? fa_cl_c_env
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E ? 16 : 0);
|
||||
: (backend_ctx->adreno_gen == ADRENO_GPU_GEN::X2E ||
|
||||
backend_ctx->adreno_gen == ADRENO_GPU_GEN::X1E ? 16 : 0);
|
||||
const std::string opts_cl_c_gqa4 = fa_cl_c_gqa4
|
||||
? " -D FA_CL_C=" + std::to_string(fa_cl_c_gqa4) : std::string();
|
||||
const std::string fa_cl_c_g8_val = std::to_string(fa_cl_c_gqa4 ? fa_cl_c_gqa4 * 2 : 16);
|
||||
@@ -7076,6 +7081,19 @@ inline bool enable_adreno_trans_weight(const ggml_backend_opencl_context *backen
|
||||
return ((elem_num < 128 * 1024 * 1024) && adreno_kernel && shape_ok); // max element num: 2**27
|
||||
}
|
||||
|
||||
inline bool enable_adreno_trans_weight_q5_K(const ggml_backend_opencl_context *backend_ctx, const ggml_tensor *tensor) {
|
||||
if (!use_adreno_kernels(backend_ctx, tensor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t elem_num = ggml_nelements(tensor);
|
||||
const size_t q_img_width = elem_num / 8;
|
||||
const size_t qh_img_width = elem_num / 16;
|
||||
|
||||
return q_img_width <= backend_ctx->image_max_buffer_size &&
|
||||
qh_img_width <= backend_ctx->image_max_buffer_size;
|
||||
}
|
||||
|
||||
static inline bool use_flat_gemv_for_large_m_q4_K(const ggml_tensor *tensor) {
|
||||
// gemv_noshuffle variant perf drops for large M, use flat variant for large M.
|
||||
// threshold is well above typical hidden/FFN dims, but below typical vocab sizes.
|
||||
@@ -9255,7 +9273,7 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
cl_kernel kernel = backend_ctx->kernel_convert_block_q5_K;
|
||||
if (use_adreno_kernels(backend_ctx, tensor)) {
|
||||
if (enable_adreno_trans_weight_q5_K(backend_ctx, tensor)) {
|
||||
kernel = backend_ctx->kernel_convert_block_q5_K_noshuffle;
|
||||
}
|
||||
#else
|
||||
@@ -9290,7 +9308,7 @@ static void ggml_backend_opencl_buffer_set_tensor(ggml_backend_buffer_t buffer,
|
||||
|
||||
tensor->extra = extra;
|
||||
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
|
||||
if (use_adreno_kernels(backend_ctx, tensor)) {
|
||||
if (enable_adreno_trans_weight_q5_K(backend_ctx, tensor)) {
|
||||
|
||||
int M = tensor->ne[1];
|
||||
int K = tensor->ne[0];
|
||||
@@ -10388,7 +10406,7 @@ static void ggml_backend_opencl_buffer_get_tensor(ggml_backend_buffer_t buffer,
|
||||
CL_CHECK(clReleaseMemObject(data_device));
|
||||
return;
|
||||
}
|
||||
if (use_adreno_kernels(backend_ctx, tensor)) {
|
||||
if (enable_adreno_trans_weight_q5_K(backend_ctx, tensor)) {
|
||||
int M = tensor->ne[1];
|
||||
int K = tensor->ne[0];
|
||||
|
||||
@@ -18928,7 +18946,8 @@ static void ggml_cl_mul_mat(ggml_backend_t backend, const ggml_tensor * src0, co
|
||||
}
|
||||
|
||||
// q5_K x fp32
|
||||
if (src0t == GGML_TYPE_Q5_K && src1t == GGML_TYPE_F32) {
|
||||
if (src0t == GGML_TYPE_Q5_K && src1t == GGML_TYPE_F32 &&
|
||||
enable_adreno_trans_weight_q5_K(backend_ctx, src0)) {
|
||||
ggml_cl_mul_mat_q5_K_f32_adreno(backend, src0, src1, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ void ggml_sycl_host_free(void* ptr);
|
||||
extern int g_ggml_sycl_debug;
|
||||
extern int g_ggml_sycl_enable_optimize;
|
||||
extern int g_ggml_sycl_enable_fusion;
|
||||
extern int g_ggml_sycl_enable_esimd;
|
||||
extern int g_ggml_sycl_prioritize_dmmv;
|
||||
extern int g_ggml_sycl_enable_flash_attention;
|
||||
extern int g_ggml_sycl_dev2dev_memcpy;
|
||||
|
||||
@@ -184,8 +184,8 @@ void concat_impl_sycl(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
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).wait()));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d + size0 / type_size, src1_d, size1).wait()));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d, src0_d, size0)));
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(stream->memcpy(dst_d + size0 / type_size, src1_d, size1)));
|
||||
}
|
||||
} else {
|
||||
concat_T_sycl_non_cont<T>(stream, (const char *) src0->data, (const char *) src1->data, (char *) dst->data,
|
||||
@@ -196,6 +196,270 @@ 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) {
|
||||
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_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);
|
||||
|
||||
const int ne00_blk = src0->ne[0] / QK4_0;
|
||||
const int ne0_blk = dst->ne[0] / QK4_0;
|
||||
|
||||
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);
|
||||
|
||||
if (dim != 3) {
|
||||
for (int i3 = 0; i3 < dst->ne[3]; i3++) {
|
||||
concat_T_sycl<block_q4_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_q4_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] / 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->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);
|
||||
}
|
||||
}
|
||||
|
||||
void ggml_sycl_op_concat(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
|
||||
switch (dst->type) {
|
||||
@@ -222,6 +486,21 @@ void ggml_sycl_op_concat(ggml_backend_sycl_context & ctx, ggml_tensor *dst) {
|
||||
case GGML_TYPE_I8:
|
||||
concat_impl_sycl<int8_t>(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_0:
|
||||
concat_impl_q4_0_sycl(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q4_1:
|
||||
concat_impl_q4_1_sycl(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_0:
|
||||
concat_impl_q5_0_sycl(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q5_1:
|
||||
concat_impl_q5_1_sycl(ctx, dst);
|
||||
break;
|
||||
case GGML_TYPE_Q8_0:
|
||||
concat_impl_q8_0_sycl(ctx, dst);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unsupported types: dst: %s\n", __func__, ggml_type_name(dst->type));
|
||||
GGML_ASSERT(false);
|
||||
|
||||
+137
-3
@@ -8,6 +8,9 @@
|
||||
#include <sycl/ext/oneapi/bfloat16.hpp>
|
||||
#define GGML_SYCL_DMMV_HAS_BF16
|
||||
#endif
|
||||
#include <sycl/ext/intel/esimd.hpp>
|
||||
#include "esimd.hpp"
|
||||
#define GGML_SYCL_DMMV_HAS_ESIMD
|
||||
#endif
|
||||
|
||||
static void convert_f16(const void * vx, const int64_t ib, const int iqs, dfloat2 & v){
|
||||
@@ -1864,6 +1867,113 @@ static void dequantize_mul_mat_vec_q6_K_sycl(const void *vx, const float *y,
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef GGML_SYCL_DMMV_HAS_ESIMD
|
||||
using ggml_sycl_esimd::GGML_SYCL_DMMV_ESIMD_WG_SIZE;
|
||||
|
||||
// generic reordered dequantize-matvec: each work-group owns a pair of
|
||||
// consecutive output rows and updates one 32-wide accumulator per row
|
||||
template <ggml_type T>
|
||||
ESIMD_INLINE void dequantize_mul_mat_vec_reorder_esimd(
|
||||
const void * vx, const float * y, float * dst,
|
||||
const int ncols, const int nrows,
|
||||
sycl::local_accessor<float, 1> lmem,
|
||||
const sycl::nd_item<1> & it) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
using traits = ggml_sycl_esimd::esimd_reorder_q_traits<T>;
|
||||
|
||||
const int num_blocks_per_row = ncols / QK_K;
|
||||
const size_t nb = (size_t) nrows * num_blocks_per_row;
|
||||
const auto ps = traits::make_ptrs(vx, nb);
|
||||
|
||||
const int tid = it.get_local_id(0);
|
||||
const int row_pair = it.get_group(0);
|
||||
const int row0 = row_pair * 2; // two consecutive output rows
|
||||
const bool has_row1 = row0 + 1 < nrows;
|
||||
|
||||
// one 32-wide accumulator per output row (small footprint, no spill)
|
||||
simd<float, 32> acc0 = 0.0f;
|
||||
simd<float, 32> acc1 = 0.0f;
|
||||
|
||||
for (int ib = tid; ib < num_blocks_per_row; ib += GGML_SYCL_DMMV_ESIMD_WG_SIZE) {
|
||||
simd<float, 256> y_vec = block_load<float, 256>(y + (size_t) ib * QK_K);
|
||||
|
||||
const size_t bi0 = (size_t) (row0 + 0) * num_blocks_per_row + ib;
|
||||
const size_t bi1 = (size_t) (row0 + 1) * num_blocks_per_row + ib;
|
||||
|
||||
traits::mac_pair(ps, bi0, ps, bi1, has_row1, y_vec, acc0, acc1);
|
||||
}
|
||||
|
||||
lmem[tid * 2 + 0] = reduce<float>(acc0, std::plus<>{});
|
||||
lmem[tid * 2 + 1] = reduce<float>(acc1, std::plus<>{});
|
||||
it.barrier(sycl::access::fence_space::local_space);
|
||||
|
||||
if (tid == 0) {
|
||||
float sum0 = 0.0f;
|
||||
float sum1 = 0.0f;
|
||||
for (int p = 0; p < GGML_SYCL_DMMV_ESIMD_WG_SIZE; ++p) {
|
||||
sum0 += lmem[p * 2 + 0];
|
||||
sum1 += lmem[p * 2 + 1];
|
||||
}
|
||||
dst[row0 + 0] = sum0;
|
||||
if (has_row1) {
|
||||
dst[row0 + 1] = sum1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dequantize_mul_mat_vec_q3_K_sycl_reorder_esimd(const void *vx, const float *y,
|
||||
float *dst, const int ncols,
|
||||
const int nrows,
|
||||
dpct::queue_ptr stream) {
|
||||
GGML_ASSERT(ncols % QK_K == 0);
|
||||
const int workgroups = (nrows + 1) / 2;
|
||||
stream->submit([&](sycl::handler &h) {
|
||||
sycl::local_accessor<float, 1> lmem(sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE * 2), h);
|
||||
h.parallel_for(
|
||||
sycl::nd_range<1>(sycl::range<1>((size_t)workgroups * GGML_SYCL_DMMV_ESIMD_WG_SIZE), sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE)),
|
||||
[=](sycl::nd_item<1> it) [[intel::sycl_explicit_simd]] {
|
||||
dequantize_mul_mat_vec_reorder_esimd<GGML_TYPE_Q3_K>(
|
||||
vx, y, dst, ncols, nrows, lmem, it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void dequantize_mul_mat_vec_q4_K_sycl_reorder_esimd(const void *vx, const float *y,
|
||||
float *dst, const int ncols,
|
||||
const int nrows,
|
||||
dpct::queue_ptr stream) {
|
||||
GGML_ASSERT(ncols % QK_K == 0);
|
||||
const int workgroups = (nrows + 1) / 2;
|
||||
stream->submit([&](sycl::handler &h) {
|
||||
sycl::local_accessor<float, 1> lmem(sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE * 2), h);
|
||||
h.parallel_for(
|
||||
sycl::nd_range<1>(sycl::range<1>((size_t)workgroups * GGML_SYCL_DMMV_ESIMD_WG_SIZE), sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE)),
|
||||
[=](sycl::nd_item<1> it) [[intel::sycl_explicit_simd]] {
|
||||
dequantize_mul_mat_vec_reorder_esimd<GGML_TYPE_Q4_K>(
|
||||
vx, y, dst, ncols, nrows, lmem, it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void dequantize_mul_mat_vec_q6_K_sycl_reorder_esimd(const void *vx, const float *y,
|
||||
float *dst, const int ncols,
|
||||
const int nrows,
|
||||
dpct::queue_ptr stream) {
|
||||
GGML_ASSERT(ncols % QK_K == 0);
|
||||
const int workgroups = (nrows + 1) / 2;
|
||||
stream->submit([&](sycl::handler &h) {
|
||||
sycl::local_accessor<float, 1> lmem(sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE * 2), h);
|
||||
h.parallel_for(
|
||||
sycl::nd_range<1>(sycl::range<1>((size_t)workgroups * GGML_SYCL_DMMV_ESIMD_WG_SIZE), sycl::range<1>(GGML_SYCL_DMMV_ESIMD_WG_SIZE)),
|
||||
[=](sycl::nd_item<1> it) [[intel::sycl_explicit_simd]] {
|
||||
dequantize_mul_mat_vec_reorder_esimd<GGML_TYPE_Q6_K>(
|
||||
vx, y, dst, ncols, nrows, lmem, it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#endif // GGML_SYCL_DMMV_HAS_ESIMD
|
||||
|
||||
static void dequantize_mul_mat_vec_q4_K_sycl_reorder(const void *vx, const float *y,
|
||||
float *dst, const int ncols,
|
||||
const int nrows,
|
||||
@@ -1992,7 +2102,15 @@ void ggml_sycl_op_dequantize_mul_mat_vec(
|
||||
case GGML_TYPE_Q3_K:
|
||||
if ((ggml_tensor_extra_gpu *) dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
dequantize_mul_mat_vec_q3_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
#ifdef GGML_SYCL_DMMV_HAS_ESIMD
|
||||
if (g_ggml_sycl_enable_esimd) {
|
||||
dequantize_mul_mat_vec_q3_K_sycl_reorder_esimd(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
dequantize_mul_mat_vec_q3_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
} else {
|
||||
dequantize_mul_mat_vec_q3_K_sycl(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
@@ -2000,7 +2118,15 @@ void ggml_sycl_op_dequantize_mul_mat_vec(
|
||||
case GGML_TYPE_Q4_K:
|
||||
if ((ggml_tensor_extra_gpu *) dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
dequantize_mul_mat_vec_q4_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
#ifdef GGML_SYCL_DMMV_HAS_ESIMD
|
||||
if (g_ggml_sycl_enable_esimd) {
|
||||
dequantize_mul_mat_vec_q4_K_sycl_reorder_esimd(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
dequantize_mul_mat_vec_q4_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
} else {
|
||||
dequantize_mul_mat_vec_q4_K_sycl(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
@@ -2016,7 +2142,15 @@ void ggml_sycl_op_dequantize_mul_mat_vec(
|
||||
case GGML_TYPE_Q6_K:
|
||||
if ((ggml_tensor_extra_gpu *) dst->src[0]->extra &&
|
||||
((ggml_tensor_extra_gpu *) dst->src[0]->extra)->optimized_feature.reorder) {
|
||||
dequantize_mul_mat_vec_q6_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
#ifdef GGML_SYCL_DMMV_HAS_ESIMD
|
||||
if (g_ggml_sycl_enable_esimd) {
|
||||
dequantize_mul_mat_vec_q6_K_sycl_reorder_esimd(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
dequantize_mul_mat_vec_q6_K_sycl_reorder(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
} else {
|
||||
dequantize_mul_mat_vec_q6_K_sycl(src0_dd_i, src1_ddf_i, dst_dd_i, ne00, row_diff, stream);
|
||||
}
|
||||
|
||||
@@ -448,6 +448,47 @@ static void unary_gated_op_generic_kernel(
|
||||
}
|
||||
}
|
||||
|
||||
// Fused UNARY + MUL. Unlike the gated ops above, `x` and `g` are separate tensors of the
|
||||
// same shape; `o0`/`o1` are their row strides in elements, so a half-view needs no repack.
|
||||
// `dst` is contiguous and indexed flat. Math is done in f32, as the CPU and CUDA references do.
|
||||
template<typename T, typename F>
|
||||
static void unary_mul_flat_kernel(const T * x, const T * g, T * dst, const int64_t k, const sycl::nd_item<1> &item_ct1, F op) {
|
||||
SYCL_GLOBAL_ID_LOOP(k, item_ct1) {
|
||||
dst[i] = (T) (op((float) x[i]) * (float) g[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
static void unary_mul_strided_kernel(const T * x, const T * g, T * dst, const int64_t k, const sycl::uint3 n_fd, const int64_t o0, const int64_t o1, const sycl::nd_item<1> &item_ct1, F op) {
|
||||
SYCL_GLOBAL_ID_LOOP(k, item_ct1) {
|
||||
const sycl::uint2 rc = fast_div_modulo((uint32_t) i, n_fd);
|
||||
const int64_t j0 = rc.x() * o0 + rc.y();
|
||||
const int64_t j1 = o0 == o1 ? j0 : rc.x() * o1 + rc.y();
|
||||
dst[i] = (T) (op((float) x[j0]) * (float) g[j1]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
static void unary_mul_sycl(const T * x, const T * g, T * dst, const int64_t k, const int64_t n, const int64_t o0, const int64_t o1, queue_ptr main_stream, F op) {
|
||||
const size_t num_blocks = ceil_div((size_t) k, (size_t) SYCL_GLU_BLOCK_SIZE);
|
||||
const sycl::nd_range<1> range(num_blocks * sycl::range<1>(SYCL_GLU_BLOCK_SIZE), sycl::range<1>(SYCL_GLU_BLOCK_SIZE));
|
||||
|
||||
// o0 == o1 == n makes (i/n)*o0 + (i%n) == i, so the strided kernel degenerates to the flat one
|
||||
if (o0 == n && o1 == n) {
|
||||
main_stream->parallel_for(range, [=](sycl::nd_item<1> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
|
||||
unary_mul_flat_kernel(x, g, dst, k, item_ct1, op);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 32-bit fastdiv, exact only below 2^31; ggml_sycl_can_fuse() already declined past that
|
||||
GGML_ASSERT(k < ((int64_t) 1 << 31));
|
||||
const sycl::uint3 n_fd = init_fastdiv_values((uint32_t) n);
|
||||
main_stream->parallel_for(range, [=](sycl::nd_item<1> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] {
|
||||
unary_mul_strided_kernel(x, g, dst, k, n_fd, o0, o1, item_ct1, op);
|
||||
});
|
||||
}
|
||||
|
||||
namespace ggml_sycl_detail {
|
||||
static void acc_f32_sycl(const char *x, const char *y, float *dst,
|
||||
const int64_t n_elements,
|
||||
@@ -991,6 +1032,52 @@ static inline void ggml_sycl_op_swiglu(ggml_backend_sycl_context & ctx, ggml_ten
|
||||
});
|
||||
}
|
||||
|
||||
// dst = op(unary_node->src[0]) * other, written straight to the MUL output, saving the
|
||||
// standalone unary launch. Preconditions come from ggml_sycl_can_fuse(); re-asserted here.
|
||||
void ggml_sycl_op_unary_mul_fused(ggml_backend_sycl_context & ctx, ggml_tensor * unary_node, ggml_tensor * mul_node) {
|
||||
scope_op_debug_print scope_dbg_print(__func__, mul_node, /*num_src=*/2);
|
||||
|
||||
const ggml_tensor * x = unary_node->src[0];
|
||||
const ggml_tensor * g = (mul_node->src[0] == unary_node) ? mul_node->src[1] : mul_node->src[0];
|
||||
|
||||
// g is picked by elimination; ggml_can_fuse()'s single-use rule rules out MUL(unary, unary)
|
||||
GGML_ASSERT(g != unary_node);
|
||||
GGML_ASSERT(x->type == g->type && x->type == mul_node->type);
|
||||
GGML_ASSERT(ggml_are_same_shape(x, g) && ggml_are_same_shape(x, mul_node));
|
||||
GGML_ASSERT(ggml_is_contiguous_1(x) && ggml_is_contiguous_1(g));
|
||||
// dst is indexed flat
|
||||
GGML_ASSERT(ggml_is_contiguous(mul_node));
|
||||
|
||||
queue_ptr main_stream = ctx.stream();
|
||||
SYCL_CHECK(ggml_sycl_set_device(ctx.device));
|
||||
|
||||
const int64_t k = ggml_nelements(mul_node);
|
||||
const int64_t n = mul_node->ne[0];
|
||||
|
||||
const auto dispatch_type = [&](auto op) {
|
||||
switch (mul_node->type) {
|
||||
case GGML_TYPE_F32:
|
||||
unary_mul_sycl((const float *) x->data, (const float *) g->data, (float *) mul_node->data,
|
||||
k, n, x->nb[1] / sizeof(float), g->nb[1] / sizeof(float), main_stream, op);
|
||||
break;
|
||||
case GGML_TYPE_F16:
|
||||
unary_mul_sycl((const sycl::half *) x->data, (const sycl::half *) g->data, (sycl::half *) mul_node->data,
|
||||
k, n, x->nb[1] / sizeof(sycl::half), g->nb[1] / sizeof(sycl::half), main_stream, op);
|
||||
break;
|
||||
default:
|
||||
GGML_ABORT("fused unary+mul: unsupported type %s", ggml_type_name(mul_node->type));
|
||||
}
|
||||
};
|
||||
|
||||
switch (ggml_get_unary_op(unary_node)) {
|
||||
case GGML_UNARY_OP_SILU: dispatch_type([](float v) { return op_silu(v); }); break;
|
||||
case GGML_UNARY_OP_SIGMOID: dispatch_type([](float v) { return op_sigmoid(v); }); break;
|
||||
case GGML_UNARY_OP_SOFTPLUS: dispatch_type([](float v) { return op_softplus(v); }); break;
|
||||
default:
|
||||
GGML_ABORT("fused unary+mul: unsupported unary op %s", ggml_unary_op_name(ggml_get_unary_op(unary_node)));
|
||||
}
|
||||
}
|
||||
|
||||
__dpct_inline__ float ggml_sycl_op_swiglu_oai_single(float x, float g, float alpha = 1.702f, float limit = 7.0f) {
|
||||
x = sycl::fmin(x, limit);
|
||||
g = sycl::fmax(sycl::fmin(g, limit), -limit);
|
||||
|
||||
@@ -95,4 +95,7 @@ void ggml_sycl_trunc(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
void ggml_sycl_arange(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
|
||||
|
||||
// fused UNARY(silu|sigmoid|softplus) + MUL; see ggml_sycl_can_fuse() for the accepted shapes
|
||||
void ggml_sycl_op_unary_mul_fused(ggml_backend_sycl_context & ctx, ggml_tensor * unary_node, ggml_tensor * mul_node);
|
||||
|
||||
#endif // GGML_SYCL_ELEMENTWISE_HPP
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
//
|
||||
// MIT license
|
||||
// Copyright (C) 2026 Intel Corporation
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
|
||||
#ifndef GGML_SYCL_ESIMD_HPP
|
||||
#define GGML_SYCL_ESIMD_HPP
|
||||
|
||||
#include <sycl/ext/intel/esimd.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace ggml_sycl_esimd {
|
||||
|
||||
constexpr int GGML_SYCL_DMMV_ESIMD_WG_SIZE = 4;
|
||||
|
||||
//
|
||||
// Shared ESIMD building blocks for the reordered K-quant dequantize-matvec
|
||||
// kernels.
|
||||
//
|
||||
// The reordered K-quant ESIMD matvec kernels share one skeleton: per super-block,
|
||||
// load a 256-float activation slice, load one weight block, dequantize it into 8
|
||||
// chunks of 32 and MAC each chunk against the matching activation slice, then
|
||||
// reduce and run a lane-0 epilogue.
|
||||
//
|
||||
// Each K-quant kernel emits exactly 8 chunks of 32 mapping to activation slices
|
||||
// 0..7, so the per-block work is captured by esimd_reorder_q_traits<T>::mac_pair,
|
||||
// which dequantizes two weight blocks and MACs both against a shared activation
|
||||
// vector with the two FMA chains interleaved (co-scheduled to hide FMA latency).
|
||||
// The "pair" is the (row0,row1) row pair owned by one work-group, so the
|
||||
// layout+dequant is written once per quant type here.
|
||||
//
|
||||
|
||||
template <ggml_type T> struct esimd_reorder_q_traits;
|
||||
|
||||
// build a 32-lane vector whose low 16 lanes are `lo` and high 16 are `hi`
|
||||
// (a super-chunk splits into two 16-wide halves with distinct scale/min codes).
|
||||
static ESIMD_INLINE sycl::ext::intel::esimd::simd<float, 32> splat_lo_hi(float lo, float hi) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
simd<float, 32> v;
|
||||
v.select<16, 1>(0) = lo;
|
||||
v.select<16, 1>(16) = hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
// unpack one block of Q4_K/Q5_K scale/min codes (get_scale_min_k4 layout) into 8
|
||||
// float scales (dall * sc) and 8 float mins (-dmin * m); the min carries the
|
||||
// negation so the dequant epilogue adds.
|
||||
static ESIMD_INLINE void unpack_scale_min_k4(
|
||||
sycl::ext::intel::esimd::simd<uint8_t, 12> scales, float dall, float dmin,
|
||||
sycl::ext::intel::esimd::simd<float, 8> & scale_f,
|
||||
sycl::ext::intel::esimd::simd<float, 8> & min_f) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
simd<uint8_t, 8> sc = 0;
|
||||
simd<uint8_t, 8> m = 0;
|
||||
simd<uint8_t, 4> scale_lo = scales.select<4, 1>(0);
|
||||
simd<uint8_t, 4> min_lo = scales.select<4, 1>(4);
|
||||
simd<uint8_t, 4> hi_bits = scales.select<4, 1>(8);
|
||||
sc.select<4, 1>(0) = scale_lo & simd<uint8_t, 4>(0x3F);
|
||||
sc.select<4, 1>(4) = (hi_bits & simd<uint8_t, 4>(0x0F)) |
|
||||
((scale_lo >> simd<uint8_t, 4>(6)) << simd<uint8_t, 4>(4));
|
||||
m.select<4, 1>(0) = min_lo & simd<uint8_t, 4>(0x3F);
|
||||
m.select<4, 1>(4) = (hi_bits >> simd<uint8_t, 4>(4)) |
|
||||
((min_lo >> simd<uint8_t, 4>(6)) << simd<uint8_t, 4>(4));
|
||||
scale_f = convert<float>(sc) * dall;
|
||||
min_f = convert<float>(m) * (-dmin);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Q3_K, SOA reorder layout produced by reorder_qw_q3_k:
|
||||
// [qs: nb*(QK_K/4)] [hmask: nb*(QK_K/8)] [scales: nb*12] [d: nb*sizeof(half)]
|
||||
// with nb = nrows*num_blocks_per_row. Single super-block scale d, no dmin.
|
||||
//
|
||||
// 3 bits per weight: 2 low bits in qs, 1 high bit in hmask. The 8 output chunks
|
||||
// of 32 (matching dequantize_row_q3_K) map to super-chunk s (0..7): byte base
|
||||
// 32*(s/4) into the 64-byte qs array, bit shift 2*(s%4); the low 16 lanes use
|
||||
// scale code 2s, the high 16 use 2s+1. hmask is a 32-byte array (like Q5_K's
|
||||
// qh) where chunk s uses bit s of the same 32 bytes, but INVERTED: the value is
|
||||
// (q & 3) - (hmask_bit_set ? 0 : 4), i.e. (q & 3) + 4*bit - 4.
|
||||
//
|
||||
// The 16 6-bit scale codes are packed into 12 bytes (get_scale_min layout for
|
||||
// Q3_K): low nibbles from bytes 0..7, high 2 bits from bytes 8..11 shifted by
|
||||
// 0/2/4/6; the dequant scale is d * (code - 32).
|
||||
// ---------------------------------------------------------------------------
|
||||
template <> struct esimd_reorder_q_traits<GGML_TYPE_Q3_K> {
|
||||
struct ptrs {
|
||||
const uint8_t * qs;
|
||||
const uint8_t * hmask;
|
||||
const uint8_t * scales;
|
||||
const sycl::half * d;
|
||||
};
|
||||
|
||||
static ESIMD_INLINE ptrs make_ptrs(const void * vx, size_t nb) {
|
||||
const uint8_t * qs = (const uint8_t *) vx;
|
||||
const uint8_t * hmask = qs + nb * (QK_K / 4);
|
||||
const uint8_t * scales = hmask + nb * (QK_K / 8);
|
||||
const sycl::half * d = (const sycl::half *) (scales + nb * 12);
|
||||
return { qs, hmask, scales, d };
|
||||
}
|
||||
|
||||
// unpack the 12 packed bytes into 16 6-bit scale codes (dequantize_row_q3_K
|
||||
// aux layout), returned as float scale = d * (code - 32).
|
||||
// done with wide (8/16-lane) ops rather than four 4-lane groups.
|
||||
static ESIMD_INLINE sycl::ext::intel::esimd::simd<float, 16> unpack_scales(
|
||||
sycl::ext::intel::esimd::simd<uint8_t, 12> in, float d) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
|
||||
// low 6-bit part: codes 0..7 = low nibble of bytes 0..7,
|
||||
// codes 8..15 = high nibble of bytes 0..7
|
||||
simd<uint8_t, 8> lo8 = in.select<8, 1>(0);
|
||||
simd<uint8_t, 16> code;
|
||||
code.select<8, 1>(0) = lo8 & simd<uint8_t, 8>(0x0F);
|
||||
code.select<8, 1>(8) = lo8 >> simd<uint8_t, 8>(4);
|
||||
|
||||
// high 2-bit part: bytes 8..11 replicated 4x, group g (0..3) shifted 2*g
|
||||
simd<uint8_t, 16> hib;
|
||||
hib.select<4, 1>(0) = in.select<4, 1>(8);
|
||||
hib.select<4, 1>(4) = in.select<4, 1>(8);
|
||||
hib.select<4, 1>(8) = in.select<4, 1>(8);
|
||||
hib.select<4, 1>(12) = in.select<4, 1>(8);
|
||||
simd<uint8_t, 16> hshift;
|
||||
hshift.select<4, 1>(0) = 0;
|
||||
hshift.select<4, 1>(4) = 2;
|
||||
hshift.select<4, 1>(8) = 4;
|
||||
hshift.select<4, 1>(12) = 6;
|
||||
hib = (hib >> hshift) & simd<uint8_t, 16>(0x03);
|
||||
|
||||
code = code | (hib << simd<uint8_t, 16>(4));
|
||||
return (convert<float>(code) - 32.0f) * d;
|
||||
}
|
||||
|
||||
static ESIMD_INLINE void mac_pair(
|
||||
const ptrs & pa, size_t bia,
|
||||
const ptrs & pb, size_t bib, bool has_b,
|
||||
sycl::ext::intel::esimd::simd<float, 256> & y_vec,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_a,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_b) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
|
||||
simd<uint8_t, 64> qs_a = block_load<uint8_t, 64>(pa.qs + bia * (QK_K / 4));
|
||||
simd<uint8_t, 64> qs_b = 0;
|
||||
simd<uint8_t, 32> hmask_a = block_load<uint8_t, 32>(pa.hmask + bia * (QK_K / 8));
|
||||
simd<uint8_t, 32> hmask_b = 0;
|
||||
simd<uint8_t, 12> scales_a = block_load<uint8_t, 12>(pa.scales + bia * 12);
|
||||
simd<uint8_t, 12> scales_b = 0;
|
||||
|
||||
const float d_a = (float) pa.d[bia];
|
||||
float d_b = 0.0f;
|
||||
if (has_b) {
|
||||
qs_b = block_load<uint8_t, 64>(pb.qs + bib * (QK_K / 4));
|
||||
hmask_b = block_load<uint8_t, 32>(pb.hmask + bib * (QK_K / 8));
|
||||
scales_b = block_load<uint8_t, 12>(pb.scales + bib * 12);
|
||||
d_b = (float) pb.d[bib];
|
||||
}
|
||||
|
||||
simd<float, 16> scale_f_a = unpack_scales(scales_a, d_a);
|
||||
simd<float, 16> scale_f_b = unpack_scales(scales_b, d_b);
|
||||
|
||||
#pragma unroll
|
||||
for (int s = 0; s < 8; ++s) {
|
||||
const int byte_base = 32 * (s / 4);
|
||||
const uint8_t shift = (uint8_t) (2 * (s % 4));
|
||||
simd<float, 32> y_s = y_vec.select<32, 1>(s * 32);
|
||||
|
||||
// 2 low bits from qs, high bit from hmask (bit s of the same 32 bytes);
|
||||
// value = (q & 3) + 4*bit - 4 (inverted hmask: subtract 4 when bit clear).
|
||||
// merge in the integer domain: q3 = (q & 3) | (bit << 2) in {0..7},
|
||||
// then a single convert + subtract yields q3 - 4 (one convert, not two)
|
||||
simd<uint16_t, 32> q3_a = convert<uint16_t>(
|
||||
(qs_a.select<32, 1>(byte_base) >> shift) & simd<uint8_t, 32>(3));
|
||||
q3_a |= convert<uint16_t>(
|
||||
((hmask_a >> simd<uint8_t, 32>((uint8_t) s)) & simd<uint8_t, 32>(1)) << simd<uint8_t, 32>(2));
|
||||
simd<uint16_t, 32> q3_b = convert<uint16_t>(
|
||||
(qs_b.select<32, 1>(byte_base) >> shift) & simd<uint8_t, 32>(3));
|
||||
q3_b |= convert<uint16_t>(
|
||||
((hmask_b >> simd<uint8_t, 32>((uint8_t) s)) & simd<uint8_t, 32>(1)) << simd<uint8_t, 32>(2));
|
||||
|
||||
simd<float, 32> qf_a = convert<float>(q3_a) - 4.0f;
|
||||
simd<float, 32> qf_b = convert<float>(q3_b) - 4.0f;
|
||||
|
||||
const float scale_a_lo = scale_f_a[2 * s + 0];
|
||||
const float scale_a_hi = scale_f_a[2 * s + 1];
|
||||
const float scale_b_lo = scale_f_b[2 * s + 0];
|
||||
const float scale_b_hi = scale_f_b[2 * s + 1];
|
||||
|
||||
simd<float, 32> scale_vec_a = splat_lo_hi(scale_a_lo, scale_a_hi);
|
||||
simd<float, 32> scale_vec_b = splat_lo_hi(scale_b_lo, scale_b_hi);
|
||||
|
||||
simd<float, 32> deq_a = qf_a * scale_vec_a;
|
||||
simd<float, 32> deq_b = qf_b * scale_vec_b;
|
||||
|
||||
acc_a += y_s * deq_a;
|
||||
acc_b += y_s * deq_b;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Q4_K, SOA reorder layout produced by reorder_qw_q4_k:
|
||||
// [qs: nb*(QK_K/2)] [scales: nb*K_SCALE_SIZE] [dm: nb*sizeof(half2)]
|
||||
// with nb = nrows*num_blocks_per_row.
|
||||
// ---------------------------------------------------------------------------
|
||||
template <> struct esimd_reorder_q_traits<GGML_TYPE_Q4_K> {
|
||||
struct ptrs {
|
||||
const uint8_t * qs;
|
||||
const uint8_t * scales;
|
||||
const sycl::half * dm;
|
||||
};
|
||||
|
||||
static ESIMD_INLINE ptrs make_ptrs(const void * vx, size_t nb) {
|
||||
const uint8_t * qs = (const uint8_t *) vx;
|
||||
const uint8_t * scales = qs + nb * (QK_K / 2);
|
||||
const sycl::half * dm = (const sycl::half *) (scales + nb * K_SCALE_SIZE);
|
||||
return { qs, scales, dm };
|
||||
}
|
||||
|
||||
static ESIMD_INLINE void mac_pair(
|
||||
const ptrs & pa, size_t bia,
|
||||
const ptrs & pb, size_t bib, bool has_b,
|
||||
sycl::ext::intel::esimd::simd<float, 256> & y_vec,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_a,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_b) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
|
||||
simd<uint8_t, 128> qs_a = block_load<uint8_t, 128>(pa.qs + bia * (QK_K / 2));
|
||||
simd<uint8_t, 128> qs_b = 0;
|
||||
simd<uint8_t, 12> scales_a = block_load<uint8_t, 12>(pa.scales + bia * K_SCALE_SIZE);
|
||||
simd<uint8_t, 12> scales_b = 0;
|
||||
|
||||
const float dall_a = (float) pa.dm[bia * 2 + 0];
|
||||
const float dmin_a = (float) pa.dm[bia * 2 + 1];
|
||||
float dall_b = 0.0f;
|
||||
float dmin_b = 0.0f;
|
||||
if (has_b) {
|
||||
qs_b = block_load<uint8_t, 128>(pb.qs + bib * (QK_K / 2));
|
||||
scales_b = block_load<uint8_t, 12>(pb.scales + bib * K_SCALE_SIZE);
|
||||
dall_b = (float) pb.dm[bib * 2 + 0];
|
||||
dmin_b = (float) pb.dm[bib * 2 + 1];
|
||||
}
|
||||
|
||||
simd<float, 8> scale_f_a, min_f_a, scale_f_b, min_f_b;
|
||||
unpack_scale_min_k4(scales_a, dall_a, dmin_a, scale_f_a, min_f_a);
|
||||
unpack_scale_min_k4(scales_b, dall_b, dmin_b, scale_f_b, min_f_b);
|
||||
|
||||
simd<uint8_t, 128> qs_lo_a = qs_a & simd<uint8_t, 128>(0x0F);
|
||||
simd<uint8_t, 128> qs_hi_a = qs_a >> simd<uint8_t, 128>(4);
|
||||
simd<uint8_t, 128> qs_lo_b = qs_b & simd<uint8_t, 128>(0x0F);
|
||||
simd<uint8_t, 128> qs_hi_b = qs_b >> simd<uint8_t, 128>(4);
|
||||
|
||||
#pragma unroll
|
||||
for (int sb = 0; sb < 8; sb += 2) {
|
||||
const int q_offset = sb * 16;
|
||||
simd<float, 32> y_lo = y_vec.select<32, 1>(sb * 32);
|
||||
simd<float, 32> y_hi = y_vec.select<32, 1>((sb + 1) * 32);
|
||||
|
||||
const float scale_a_lo = scale_f_a[sb];
|
||||
const float scale_a_hi = scale_f_a[sb + 1];
|
||||
const float min_a_lo = min_f_a[sb];
|
||||
const float min_a_hi = min_f_a[sb + 1];
|
||||
const float scale_b_lo = scale_f_b[sb];
|
||||
const float scale_b_hi = scale_f_b[sb + 1];
|
||||
const float min_b_lo = min_f_b[sb];
|
||||
const float min_b_hi = min_f_b[sb + 1];
|
||||
|
||||
simd<uint8_t, 32> qa_lo = qs_lo_a.select<32, 1>(q_offset);
|
||||
simd<uint8_t, 32> qa_hi = qs_hi_a.select<32, 1>(q_offset);
|
||||
simd<uint8_t, 32> qb_lo = qs_lo_b.select<32, 1>(q_offset);
|
||||
simd<uint8_t, 32> qb_hi = qs_hi_b.select<32, 1>(q_offset);
|
||||
|
||||
simd<float, 32> deq_a_lo = convert<float>(qa_lo) * scale_a_lo + min_a_lo;
|
||||
simd<float, 32> deq_a_hi = convert<float>(qa_hi) * scale_a_hi + min_a_hi;
|
||||
simd<float, 32> deq_b_lo = convert<float>(qb_lo) * scale_b_lo + min_b_lo;
|
||||
simd<float, 32> deq_b_hi = convert<float>(qb_hi) * scale_b_hi + min_b_hi;
|
||||
|
||||
acc_a += y_lo * deq_a_lo;
|
||||
acc_b += y_lo * deq_b_lo;
|
||||
acc_a += y_hi * deq_a_hi;
|
||||
acc_b += y_hi * deq_b_hi;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Q6_K, SOA reorder layout:
|
||||
// [ql: nb*(QK_K/2)] [qh: nb*(QK_K/4)] [scales(int8): nb*(QK_K/16)] [d: nb*half]
|
||||
// ---------------------------------------------------------------------------
|
||||
template <> struct esimd_reorder_q_traits<GGML_TYPE_Q6_K> {
|
||||
struct ptrs {
|
||||
const uint8_t * ql;
|
||||
const uint8_t * qh;
|
||||
const int8_t * scales;
|
||||
const sycl::half * d;
|
||||
};
|
||||
|
||||
static ESIMD_INLINE ptrs make_ptrs(const void * vx, size_t nb) {
|
||||
const uint8_t * ql = (const uint8_t *) vx;
|
||||
const uint8_t * qh = ql + nb * (QK_K / 2);
|
||||
const int8_t * scales = (const int8_t *) (qh + nb * (QK_K / 4));
|
||||
const sycl::half * d = (const sycl::half *) (scales + nb * (QK_K / 16));
|
||||
return { ql, qh, scales, d };
|
||||
}
|
||||
|
||||
static ESIMD_INLINE void mac_pair(
|
||||
const ptrs & pa, size_t bia,
|
||||
const ptrs & pb, size_t bib, bool has_b,
|
||||
sycl::ext::intel::esimd::simd<float, 256> & y_vec,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_a,
|
||||
sycl::ext::intel::esimd::simd<float, 32> & acc_b) {
|
||||
using namespace sycl::ext::intel::esimd;
|
||||
|
||||
simd<uint8_t, 128> ql_a = block_load<uint8_t, 128>(pa.ql + bia * (QK_K / 2));
|
||||
simd<uint8_t, 128> ql_b = 0;
|
||||
simd<uint8_t, 64> qh_a = block_load<uint8_t, 64>(pa.qh + bia * (QK_K / 4));
|
||||
simd<uint8_t, 64> qh_b = 0;
|
||||
simd<int8_t, 16> scales_a = block_load<int8_t, 16>(pa.scales + bia * (QK_K / 16));
|
||||
simd<int8_t, 16> scales_b = 0;
|
||||
|
||||
const float d_a = (float) pa.d[bia];
|
||||
float d_b = 0.0f;
|
||||
if (has_b) {
|
||||
ql_b = block_load<uint8_t, 128>(pb.ql + bib * (QK_K / 2));
|
||||
qh_b = block_load<uint8_t, 64>(pb.qh + bib * (QK_K / 4));
|
||||
scales_b = block_load<int8_t, 16>(pb.scales + bib * (QK_K / 16));
|
||||
d_b = (float) pb.d[bib];
|
||||
}
|
||||
|
||||
simd<float, 16> sc_a = convert<float>(scales_a);
|
||||
simd<float, 16> sc_b = convert<float>(scales_b);
|
||||
|
||||
#pragma unroll
|
||||
for (int im = 0; im < 2; ++im) {
|
||||
simd<uint8_t, 32> ql_lo_a = ql_a.select<32, 1>(64 * im);
|
||||
simd<uint8_t, 32> ql_hi_a = ql_a.select<32, 1>(64 * im + 32);
|
||||
simd<uint8_t, 32> qh_bits_a = qh_a.select<32, 1>(32 * im);
|
||||
simd<uint8_t, 32> ql_lo_b = ql_b.select<32, 1>(64 * im);
|
||||
simd<uint8_t, 32> ql_hi_b = ql_b.select<32, 1>(64 * im + 32);
|
||||
simd<uint8_t, 32> qh_bits_b = qh_b.select<32, 1>(32 * im);
|
||||
|
||||
// reconstruct each 32-wide 6-bit group (matches dequantize_row_q6_K)
|
||||
#pragma unroll
|
||||
for (int g = 0; g < 4; ++g) {
|
||||
simd<float, 32> y_g = y_vec.select<32, 1>(32 * (4 * im + g));
|
||||
|
||||
const float scale_a_lo = sc_a[8 * im + 2 * g + 0] * d_a;
|
||||
const float scale_a_hi = sc_a[8 * im + 2 * g + 1] * d_a;
|
||||
const float scale_b_lo = sc_b[8 * im + 2 * g + 0] * d_b;
|
||||
const float scale_b_hi = sc_b[8 * im + 2 * g + 1] * d_b;
|
||||
|
||||
simd<float, 32> scale_vec_a = splat_lo_hi(scale_a_lo, scale_a_hi);
|
||||
simd<float, 32> scale_vec_b = splat_lo_hi(scale_b_lo, scale_b_hi);
|
||||
|
||||
simd<uint8_t, 32> qa;
|
||||
simd<uint8_t, 32> qb;
|
||||
switch (g) {
|
||||
case 0:
|
||||
qa = (ql_lo_a & simd<uint8_t, 32>(0x0F)) | ((qh_bits_a & simd<uint8_t, 32>(0x03)) << simd<uint8_t, 32>(4));
|
||||
qb = (ql_lo_b & simd<uint8_t, 32>(0x0F)) | ((qh_bits_b & simd<uint8_t, 32>(0x03)) << simd<uint8_t, 32>(4));
|
||||
break;
|
||||
case 1:
|
||||
qa = (ql_hi_a & simd<uint8_t, 32>(0x0F)) | ((qh_bits_a & simd<uint8_t, 32>(0x0C)) << simd<uint8_t, 32>(2));
|
||||
qb = (ql_hi_b & simd<uint8_t, 32>(0x0F)) | ((qh_bits_b & simd<uint8_t, 32>(0x0C)) << simd<uint8_t, 32>(2));
|
||||
break;
|
||||
case 2:
|
||||
qa = (ql_lo_a >> simd<uint8_t, 32>(4)) | (qh_bits_a & simd<uint8_t, 32>(0x30));
|
||||
qb = (ql_lo_b >> simd<uint8_t, 32>(4)) | (qh_bits_b & simd<uint8_t, 32>(0x30));
|
||||
break;
|
||||
default:
|
||||
qa = (ql_hi_a >> simd<uint8_t, 32>(4)) | ((qh_bits_a & simd<uint8_t, 32>(0xC0)) >> simd<uint8_t, 32>(2));
|
||||
qb = (ql_hi_b >> simd<uint8_t, 32>(4)) | ((qh_bits_b & simd<uint8_t, 32>(0xC0)) >> simd<uint8_t, 32>(2));
|
||||
break;
|
||||
}
|
||||
|
||||
simd<float, 32> deq_a = (convert<float>(qa) - 32.0f) * scale_vec_a;
|
||||
simd<float, 32> deq_b = (convert<float>(qb) - 32.0f) * scale_vec_b;
|
||||
|
||||
acc_a += y_g * deq_a;
|
||||
acc_b += y_g * deq_b;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ggml_sycl_esimd
|
||||
|
||||
#endif // GGML_SYCL_ESIMD_HPP
|
||||
@@ -1,6 +1,14 @@
|
||||
#include "fusion.hpp"
|
||||
|
||||
bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<enum ggml_op> ops) {
|
||||
#include <algorithm>
|
||||
|
||||
bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<enum ggml_op> ops,
|
||||
std::initializer_list<enum ggml_unary_op> unary_ops) {
|
||||
#ifndef NDEBUG
|
||||
const size_t num_unary = std::count(ops.begin(), ops.end(), GGML_OP_UNARY);
|
||||
GGML_ASSERT(unary_ops.size() == num_unary);
|
||||
#endif
|
||||
|
||||
if (!g_ggml_sycl_enable_fusion) {
|
||||
return false;
|
||||
}
|
||||
@@ -40,5 +48,45 @@ bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializ
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ops.size() == 2 && ops.begin()[0] == GGML_OP_UNARY && ops.begin()[1] == GGML_OP_MUL &&
|
||||
unary_ops.size() == 1) {
|
||||
const ggml_tensor * unary = cgraph->nodes[node_idx];
|
||||
const ggml_tensor * mul = cgraph->nodes[node_idx + 1];
|
||||
|
||||
const ggml_unary_op unary_op = ggml_get_unary_op(unary);
|
||||
if (unary_op != unary_ops.begin()[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the ops ggml_sycl_op_unary_mul_fused() has a kernel for
|
||||
if (unary_op != GGML_UNARY_OP_SILU && unary_op != GGML_UNARY_OP_SIGMOID &&
|
||||
unary_op != GGML_UNARY_OP_SOFTPLUS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unary->type != GGML_TYPE_F32 && unary->type != GGML_TYPE_F16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * other = (mul->src[0] == unary) ? mul->src[1] : mul->src[0];
|
||||
if (other->type != unary->type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// one row stride per source comes from nb[1], so rows must be contiguous and equally
|
||||
// shaped; the destination is written flat, so it must be fully contiguous
|
||||
if (!ggml_is_contiguous_1(unary->src[0]) || !ggml_is_contiguous_1(other) ||
|
||||
!ggml_are_same_shape(other, unary) || !ggml_is_contiguous(mul)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the 32-bit fastdiv is inexact past 2^31; decline, the unfused path handles it
|
||||
if (ggml_nelements(mul) >= ((int64_t) 1 << 31)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include "common.hpp"
|
||||
|
||||
// Backend-side fusability test. `ops` names a candidate op sequence starting at cgraph node
|
||||
// `node_idx`; the result is true only if ggml considers that subgraph fusable *and* the SYCL
|
||||
// `node_idx`, and `unary_ops` the GGML_UNARY_OP each GGML_OP_UNARY in `ops` must carry, in
|
||||
// order; the result is true only if ggml considers that subgraph fusable *and* the SYCL
|
||||
// kernel which would service it accepts the tensors involved (types, shapes, contiguity).
|
||||
//
|
||||
// Lives in its own translation unit because it grows a branch per supported op sequence.
|
||||
bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<enum ggml_op> ops);
|
||||
bool ggml_sycl_can_fuse(const ggml_cgraph * cgraph, int node_idx, std::initializer_list<enum ggml_op> ops,
|
||||
std::initializer_list<enum ggml_unary_op> unary_ops);
|
||||
|
||||
#endif // GGML_SYCL_FUSION_HPP
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
# include <sycl/ext/oneapi/virtual_mem/virtual_mem.hpp>
|
||||
# define GGML_SYCL_SUPPORT_VMM
|
||||
#endif
|
||||
#if defined(__INTEL_LLVM_COMPILER)
|
||||
#define GGML_SYCL_DMMV_HAS_ESIMD
|
||||
#endif
|
||||
#include <sycl/half_type.hpp>
|
||||
|
||||
#include "ggml.h"
|
||||
@@ -90,6 +93,7 @@ int g_ggml_sycl_fa_onednn = 1;
|
||||
int g_ggml_sycl_fa_onednn_max_kv = 0;
|
||||
int g_ggml_sycl_enable_vmm = 1;
|
||||
int g_ggml_sycl_enable_fusion = 1;
|
||||
int g_ggml_sycl_enable_esimd = 1;
|
||||
int g_ggml_sycl_prioritize_dmmv = 0;
|
||||
int g_ggml_sycl_use_async_mem_op = 0;
|
||||
int g_ggml_sycl_use_async_mem_op_requested = 1;
|
||||
@@ -298,6 +302,7 @@ static void ggml_check_sycl() try {
|
||||
g_ggml_sycl_fa_onednn_max_kv = ggml_sycl_get_env("GGML_SYCL_FA_ONEDNN_MAX_KV", 0);
|
||||
g_ggml_sycl_enable_vmm = ggml_sycl_get_env("GGML_SYCL_ENABLE_VMM", 1);
|
||||
g_ggml_sycl_enable_fusion = ggml_sycl_get_env("GGML_SYCL_ENABLE_FUSION", 1);
|
||||
g_ggml_sycl_enable_esimd = ggml_sycl_get_env("GGML_SYCL_ENABLE_ESIMD", 1);
|
||||
g_ggml_sycl_prioritize_dmmv = ggml_sycl_get_env("GGML_SYCL_PRIORITIZE_DMMV", 0);
|
||||
|
||||
g_ggml_sycl_dev2dev_memcpy = ggml_sycl_get_env("GGML_SYCL_DEV2DEV_MEMCPY", DEV2DEV_MEMCPY_SYCL);
|
||||
@@ -392,6 +397,12 @@ static void ggml_check_sycl() try {
|
||||
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_FUSION: %d\n", g_ggml_sycl_enable_fusion);
|
||||
|
||||
#if defined(__INTEL_LLVM_COMPILER)
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_ESIMD: %d\n", g_ggml_sycl_enable_esimd);
|
||||
#else
|
||||
GGML_LOG_INFO(" GGML_SYCL_ENABLE_ESIMD: %d disabled by compile flag\n", g_ggml_sycl_enable_esimd);
|
||||
#endif
|
||||
|
||||
GGML_LOG_INFO(" GGML_SYCL_PRIORITIZE_DMMV: %d\n", g_ggml_sycl_prioritize_dmmv);
|
||||
|
||||
g_ggml_sycl_use_async_mem_op_requested = ggml_sycl_get_env("GGML_SYCL_USE_ASYNC_MEM_OP", 1);
|
||||
@@ -2676,21 +2687,15 @@ inline void ggml_sycl_op_mul_mat_sycl(
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ggml_sycl_pool_alloc<sycl::half> dst_f16(ctx.pool(), row_diff * src1_ncols);
|
||||
|
||||
const sycl::half alpha_f16 = 1.0f;
|
||||
const sycl::half beta_f16 = 0.0f;
|
||||
const float alpha = 1.0f;
|
||||
const float beta = 0.0f;
|
||||
SYCL_CHECK(CHECK_TRY_ERROR(dpct::gemm(
|
||||
*stream, oneapi::mkl::transpose::trans,
|
||||
oneapi::mkl::transpose::nontrans, row_diff, src1_ncols, ne10,
|
||||
&alpha_f16, src0_ptr, dpct::library_data_t::real_half, ne00,
|
||||
src1_ptr, dpct::library_data_t::real_half, ne10, &beta_f16,
|
||||
dst_f16.get(), dpct::library_data_t::real_half, ldc,
|
||||
dpct::library_data_t::real_half)));
|
||||
scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2,
|
||||
" : converting dst to fp32");
|
||||
const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(GGML_TYPE_F16, dst);
|
||||
to_fp32_sycl(dst_f16.get(), dst_dd_i, row_diff*src1_ncols, stream);
|
||||
&alpha, src0_ptr, dpct::library_data_t::real_half, ne00,
|
||||
src1_ptr, dpct::library_data_t::real_half, ne10, &beta,
|
||||
dst_dd_i, dpct::library_data_t::real_float, ldc,
|
||||
dpct::library_data_t::real_float)));
|
||||
}
|
||||
} else {
|
||||
ggml_sycl_pool_alloc<float> src0_ddq_as_f32(ctx.pool());
|
||||
@@ -3740,6 +3745,22 @@ inline bool ggml_sycl_supports_reorder_mmvq(enum ggml_type type) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool ggml_sycl_supports_reorder_esimd(enum ggml_type type) {
|
||||
#ifdef GGML_SYCL_DMMV_HAS_ESIMD
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q3_K:
|
||||
case GGML_TYPE_Q4_K:
|
||||
case GGML_TYPE_Q6_K:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
GGML_UNUSED(type);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ggml_sycl_supports_dmmv(enum ggml_type type) {
|
||||
switch (type) {
|
||||
case GGML_TYPE_Q1_0:
|
||||
@@ -4443,19 +4464,22 @@ static void ggml_sycl_mul_mat(ggml_backend_sycl_context & ctx, const ggml_tensor
|
||||
use_mul_mat_q = use_mul_mat_q && (src1->ne[1] <= MMQ_MAX_BATCH_SIZE);
|
||||
#endif // SYCL_USE_XMX
|
||||
|
||||
// Dispatch becomes obscure with the reorder, MMVQ when the reorder optimization
|
||||
// is enabled takes precedence over DMMV, the current if-else implementation
|
||||
// requires disabling DMMV if both conditions are met
|
||||
// When reorder is enabled, both ESIMD, MMVQ and DMMV kernels may be used. For
|
||||
// best performance use ESIMD when supported, followed by MMVQ, and finally DMMV.
|
||||
// But the reordered ESIMD path cannot be used without reordered MMVQ. A later
|
||||
// multi-token call (ne[1] in 2..8) will take the MMVQ path and it would read the
|
||||
// reordered bytes as if they were still the unreordered layout.
|
||||
|
||||
if (!g_ggml_sycl_prioritize_dmmv && ((should_reorder_tensor(ctx, dst) &&
|
||||
ggml_sycl_supports_reorder_mmvq(src0->type)))) {
|
||||
// Arc770 get benefit with Q4_0 by skipping it.
|
||||
if (!(ggml_sycl_info().devices[ctx.device].hw_info.arch ==
|
||||
gpu_arch::intel_gpu_acm_g10 &&
|
||||
src0->type == GGML_TYPE_Q4_0)) {
|
||||
use_dequantize_mul_mat_vec =
|
||||
use_dequantize_mul_mat_vec && !use_mul_mat_vec_q;
|
||||
}
|
||||
bool use = g_ggml_sycl_enable_esimd && ggml_sycl_supports_reorder_esimd(src0->type);
|
||||
// Arc770 get benefit with Q4_0 by skipping MMVQ path
|
||||
if (!(ggml_sycl_info().devices[ctx.device].hw_info.arch ==
|
||||
gpu_arch::intel_gpu_acm_g10 &&
|
||||
src0->type == GGML_TYPE_Q4_0)) {
|
||||
use = use || !use_mul_mat_vec_q;
|
||||
}
|
||||
use_dequantize_mul_mat_vec = use_dequantize_mul_mat_vec && use;
|
||||
}
|
||||
|
||||
if (!split && src0->type == GGML_TYPE_F16 && ggml_is_permuted(src0) && ggml_is_permuted(src1) && src1->ne[1] == 1) {
|
||||
@@ -5422,11 +5446,17 @@ static void ggml_backend_sycl_graph_compute_impl(ggml_backend_sycl_context * syc
|
||||
}
|
||||
#endif
|
||||
if (node->op == GGML_OP_RMS_NORM &&
|
||||
ggml_sycl_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) {
|
||||
ggml_sycl_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL }, {})) {
|
||||
ggml_sycl_op_rms_norm_fused(*sycl_ctx, node, cgraph->nodes[i + 1]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (node->op == GGML_OP_UNARY &&
|
||||
ggml_sycl_can_fuse(cgraph, i, { GGML_OP_UNARY, GGML_OP_MUL }, { ggml_get_unary_op(node) })) {
|
||||
ggml_sycl_op_unary_mul_fused(*sycl_ctx, node, cgraph->nodes[i + 1]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = ggml_sycl_compute_forward(*sycl_ctx, node);
|
||||
if (!ok) {
|
||||
|
||||
@@ -4627,6 +4627,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q5_1], matmul_q5_1_f16, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q8_0], matmul_q8_0_f16, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q2_K], matmul_q2_k_f16, mmq_wg_denoms_k, warptile_mmq_k, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_TQ2_0], matmul_tq2_0_f16, mmq_wg_denoms_k, warptile_mmq_k, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q3_K], matmul_q3_k_f16, mmq_wg_denoms_k, warptile_mmq_k, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q4_K], matmul_q4_k_f16, mmq_wg_denoms_k, warptile_mmq_k, vk_mat_mat_push_constants, 3)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_f16[GGML_TYPE_Q5_K], matmul_q5_k_f16, mmq_wg_denoms_k, warptile_mmq_k, vk_mat_mat_push_constants, 3)
|
||||
@@ -4667,6 +4668,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1], matmul_id_subgroup_q5_1_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0], matmul_id_subgroup_q8_0_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K], matmul_id_subgroup_q2_k_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0], matmul_id_subgroup_tq2_0_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K], matmul_id_subgroup_q3_k_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K], matmul_id_subgroup_q4_k_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
CREATE_MM2(pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K], matmul_id_subgroup_q5_k_f16, mmqid_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 5)
|
||||
@@ -4739,6 +4741,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q8_0], matmul_q8_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
|
||||
CREATE_MM2(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q2_K], matmul_q2_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
CREATE_MM2(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_TQ2_0], matmul_tq2_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
CREATE_MM2(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q3_K], matmul_q3_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
CREATE_MM2(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q4_K], matmul_q4_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
CREATE_MM2(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q5_K], matmul_q5_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, );
|
||||
@@ -4783,6 +4786,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1], matmul_id_subgroup_q5_1_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0], matmul_id_subgroup_q8_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K], matmul_id_subgroup_q2_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0], matmul_id_subgroup_tq2_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K], matmul_id_subgroup_q3_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K], matmul_id_subgroup_q4_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
CREATE_MM2(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K], matmul_id_subgroup_q5_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id);
|
||||
@@ -4873,6 +4877,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q5_1], matmul_q5_1_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q8_0], matmul_q8_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q2_K], matmul_q2_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_TQ2_0], matmul_tq2_0_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q3_K], matmul_q3_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q4_K], matmul_q4_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM2(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q5_K], matmul_q5_k_f32, mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
@@ -4921,6 +4926,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1], matmul_id_subgroup_q5_1_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0], matmul_id_subgroup_q8_0_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K], matmul_id_subgroup_q2_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0], matmul_id_subgroup_tq2_0_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K], matmul_id_subgroup_q3_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K], matmul_id_subgroup_q4_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM2(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K], matmul_id_subgroup_q5_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
@@ -4968,6 +4974,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM2(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1], matmul_id_q5_1_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0], matmul_id_q8_0_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K], matmul_id_q2_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0], matmul_id_tq2_0_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K], matmul_id_q3_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K], matmul_id_q4_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM2(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K], matmul_id_q5_k_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
@@ -5047,6 +5054,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q8_0].f32acc, matmul_q8_0_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
|
||||
CREATE_MM(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q2_K].f32acc, matmul_q2_k_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat[GGML_TYPE_TQ2_0].f32acc, matmul_tq2_0_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q3_K].f32acc, matmul_q3_k_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q4_K].f32acc, matmul_q4_k_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
CREATE_MM(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat[GGML_TYPE_Q5_K].f32acc, matmul_q5_k_f32, , mmq_wg_denoms, warptile_mmq, vk_mat_mat_push_constants, 3, , 0);
|
||||
@@ -5094,6 +5102,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1].f32acc, matmul_id_subgroup_q5_1_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0].f32acc, matmul_id_subgroup_q8_0_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K].f32acc, matmul_id_subgroup_q2_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0].f32acc, matmul_id_subgroup_tq2_0_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K].f32acc, matmul_id_subgroup_q3_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K].f32acc, matmul_id_subgroup_q4_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
CREATE_MM(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K].f32acc, matmul_id_subgroup_q5_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, mul_mat_subgroup_size);
|
||||
@@ -5123,6 +5132,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
CREATE_MM(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_1].f32acc, matmul_id_q5_1_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q8_0].f32acc, matmul_id_q8_0_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q2_K].f32acc, matmul_id_q2_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_TQ2_0, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_TQ2_0].f32acc, matmul_id_tq2_0_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q3_K].f32acc, matmul_id_q3_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q4_K].f32acc, matmul_id_q4_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
CREATE_MM(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_Q5_K].f32acc, matmul_id_q5_k_f32, , mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, mul_mat_id_param_count, _id, 0);
|
||||
@@ -5226,6 +5236,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q5_1][i], "mul_mat_vec_q5_1_f32_f32", arr_dmmv_q5_1_f32_f32_len[reduc], arr_dmmv_q5_1_f32_f32_data[reduc], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {2*rm_stdq, 1, 1}, {wg_size_subgroup, 2*rm_stdq, i+1}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q8_0][i], "mul_mat_vec_q8_0_f32_f32", arr_dmmv_q8_0_f32_f32_len[reduc], arr_dmmv_q8_0_f32_f32_data[reduc], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {1*rm_stdq, 1, 1}, {wg_size_subgroup, 1*rm_stdq, i+1}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q2_K][i], "mul_mat_vec_q2_k_f32_f32", arr_dmmv_q2_k_f32_f32_len[reduc16], arr_dmmv_q2_k_f32_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_TQ2_0][i], "mul_mat_vec_tq2_0_f32_f32", arr_dmmv_tq2_0_f32_f32_len[reduc16], arr_dmmv_tq2_0_f32_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q3_K][i], "mul_mat_vec_q3_k_f32_f32", arr_dmmv_q3_k_f32_f32_len[reduc16], arr_dmmv_q3_k_f32_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q4_K][i], "mul_mat_vec_q4_k_f32_f32", arr_dmmv_q4_k_f32_f32_len[reduc16], arr_dmmv_q4_k_f32_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f32_f32[w][GGML_TYPE_Q5_K][i], "mul_mat_vec_q5_k_f32_f32", arr_dmmv_q5_k_f32_f32_len[reduc16], arr_dmmv_q5_k_f32_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
@@ -5253,6 +5264,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q5_1][i], "mul_mat_vec_q5_1_f16_f32", arr_dmmv_q5_1_f16_f32_len[reduc], arr_dmmv_q5_1_f16_f32_data[reduc], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {2*rm_stdq, 1, 1}, {wg_size_subgroup, 2*rm_stdq, i+1}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q8_0][i], "mul_mat_vec_q8_0_f16_f32", arr_dmmv_q8_0_f16_f32_len[reduc], arr_dmmv_q8_0_f16_f32_data[reduc], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {1*rm_stdq, 1, 1}, {wg_size_subgroup, 1*rm_stdq, i+1}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q2_K][i], "mul_mat_vec_q2_k_f16_f32", arr_dmmv_q2_k_f16_f32_len[reduc16], arr_dmmv_q2_k_f16_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_TQ2_0][i], "mul_mat_vec_tq2_0_f16_f32", arr_dmmv_tq2_0_f16_f32_len[reduc16], arr_dmmv_tq2_0_f16_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q3_K][i], "mul_mat_vec_q3_k_f16_f32", arr_dmmv_q3_k_f16_f32_len[reduc16], arr_dmmv_q3_k_f16_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q4_K][i], "mul_mat_vec_q4_k_f16_f32", arr_dmmv_q4_k_f16_f32_len[reduc16], arr_dmmv_q4_k_f16_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_f16_f32[w][GGML_TYPE_Q5_K][i], "mul_mat_vec_q5_k_f16_f32", arr_dmmv_q5_k_f16_f32_len[reduc16], arr_dmmv_q5_k_f16_f32_data[reduc16], "main", mul_mat_vec_num_bindings, sizeof(vk_mat_vec_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq, i+1}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
@@ -5307,6 +5319,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q5_1], "mul_mat_vec_id_q5_1_f32", arr_dmmv_id_q5_1_f32_f32_len[reduc], arr_dmmv_id_q5_1_f32_f32_data[reduc], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {2*rm_stdq, 1, 1}, {wg_size_subgroup, 2*rm_stdq}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q8_0], "mul_mat_vec_id_q8_0_f32", arr_dmmv_id_q8_0_f32_f32_len[reduc], arr_dmmv_id_q8_0_f32_f32_data[reduc], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {1*rm_stdq, 1, 1}, {wg_size_subgroup, 1*rm_stdq}, 1, true, use_subgroups, force_subgroup_size);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q2_K], "mul_mat_vec_id_q2_k_f32", arr_dmmv_id_q2_k_f32_f32_len[reduc16], arr_dmmv_id_q2_k_f32_f32_data[reduc16], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_TQ2_0], "mul_mat_vec_id_tq2_0_f32", arr_dmmv_id_tq2_0_f32_f32_len[reduc16], arr_dmmv_id_tq2_0_f32_f32_data[reduc16], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q3_K], "mul_mat_vec_id_q3_k_f32", arr_dmmv_id_q3_k_f32_f32_len[reduc16], arr_dmmv_id_q3_k_f32_f32_data[reduc16], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q4_K], "mul_mat_vec_id_q4_k_f32", arr_dmmv_id_q4_k_f32_f32_len[reduc16], arr_dmmv_id_q4_k_f32_f32_data[reduc16], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant_mul_mat_vec_id_f32[w][GGML_TYPE_Q5_K], "mul_mat_vec_id_q5_k_f32", arr_dmmv_id_q5_k_f32_f32_len[reduc16], arr_dmmv_id_q5_k_f32_f32_data[reduc16], "main", mul_mat_vec_id_num_bindings, sizeof(vk_mat_vec_id_push_constants), {rm_kq, 1, 1}, {wg_size_subgroup16, rm_kq}, 1, true, use_subgroups16, force_subgroup_size16);
|
||||
@@ -5368,6 +5381,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q5_1], "dequant_q5_1", dequant_q5_1_len, dequant_q5_1_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q8_0], "dequant_q8_0", dequant_q8_0_len, dequant_q8_0_data, "main", 2, 5 * sizeof(uint32_t), {256 * 16, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q2_K], "dequant_q2_k", dequant_q2_k_len, dequant_q2_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_TQ2_0], "dequant_tq2_0", dequant_tq2_0_len, dequant_tq2_0_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q3_K], "dequant_q3_k", dequant_q3_k_len, dequant_q3_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q4_K], "dequant_q4_k", dequant_q4_k_len, dequant_q4_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 32, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_dequant[GGML_TYPE_Q5_K], "dequant_q5_k", dequant_q5_k_len, dequant_q5_k_data, "main", 2, 5 * sizeof(uint32_t), {256 * 64, 1, 1}, {}, 1);
|
||||
@@ -5396,6 +5410,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q5_1], "get_rows_q5_1", get_rows_q5_1_len, get_rows_q5_1_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q8_0], "get_rows_q8_0", get_rows_q8_0_len, get_rows_q8_0_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q2_K], "get_rows_q2_k", get_rows_q2_k_len, get_rows_q2_k_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_TQ2_0], "get_rows_tq2_0", get_rows_tq2_0_len, get_rows_tq2_0_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q3_K], "get_rows_q3_k", get_rows_q3_k_len, get_rows_q3_k_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q4_K], "get_rows_q4_k", get_rows_q4_k_len, get_rows_q4_k_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows[GGML_TYPE_Q5_K], "get_rows_q5_k", get_rows_q5_k_len, get_rows_q5_k_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
@@ -5424,6 +5439,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q5_1], "get_rows_q5_1_f32", get_rows_q5_1_f32_len, get_rows_q5_1_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q8_0], "get_rows_q8_0_f32", get_rows_q8_0_f32_len, get_rows_q8_0_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q2_K], "get_rows_q2_k_f32", get_rows_q2_k_f32_len, get_rows_q2_k_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_TQ2_0], "get_rows_tq2_0_f32", get_rows_tq2_0_f32_len, get_rows_tq2_0_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q3_K], "get_rows_q3_k_f32", get_rows_q3_k_f32_len, get_rows_q3_k_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q4_K], "get_rows_q4_k_f32", get_rows_q4_k_f32_len, get_rows_q4_k_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_get_rows_f32[GGML_TYPE_Q5_K], "get_rows_q5_k_f32", get_rows_q5_k_f32_len, get_rows_q5_k_f32_data, "main", 3, sizeof(vk_op_binary_push_constants), {1024, 1, 1}, {}, 1);
|
||||
@@ -7638,6 +7654,7 @@ static vk_pipeline ggml_vk_get_to_fp16(ggml_backend_vk_context * ctx, ggml_type
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -7712,6 +7729,7 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_pipeline(ggml_backend_vk_conte
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -7781,6 +7799,7 @@ static vk_pipeline ggml_vk_get_dequantize_mul_mat_vec(ggml_backend_vk_context *
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -7874,6 +7893,7 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -7946,6 +7966,7 @@ static vk_pipeline ggml_vk_get_dequantize_mul_mat_vec_id(ggml_backend_vk_context
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -18014,6 +18035,7 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -18119,6 +18141,7 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
case GGML_TYPE_TQ2_0:
|
||||
case GGML_TYPE_I32:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -608,6 +608,20 @@ vec2 get_dm(uint ib, uint a_offset) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DATA_A_TQ2_0)
|
||||
vec2 dequantize(uint ib, uint iqs, uint a_offset) {
|
||||
// elem e -> byte qs[(e/128)*32 + e%32], bits 2*((e%128)/32); w = q - 1 (d applied via get_dm)
|
||||
const uint qsi = (iqs / 128) * 32 + (iqs % 32); // iqs even -> qsi, qsi+1 in same group/level
|
||||
const uint shift = 2 * ((iqs % 128) / 32);
|
||||
|
||||
const uvec2 qs = uvec2(data_a[a_offset + ib].qs[qsi], data_a[a_offset + ib].qs[qsi + 1]);
|
||||
return vec2((qs >> shift) & 3) - 1.0;
|
||||
}
|
||||
vec2 get_dm(uint ib, uint a_offset) {
|
||||
return vec2(float(data_a[a_offset + ib].d), 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DATA_A_Q3_K)
|
||||
vec2 dequantize(uint ib, uint iqs, uint a_offset) {
|
||||
iqs /= 2;
|
||||
|
||||
@@ -247,6 +247,44 @@ f16vec4 dequantFuncQ8_0_v(const in decodeBufQ8_0 bl, const in uint blockCoords[2
|
||||
return f16vec4(vec4(qi) * vec4(float(d)));
|
||||
}
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 2) buffer decodeBufTQ2_0 {
|
||||
block_tq2_0 block;
|
||||
};
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 2) buffer decodeBufTQ2_0_packed16 {
|
||||
block_tq2_0_packed16 block;
|
||||
};
|
||||
|
||||
float16_t dequantFuncTQ2_0(const in decodeBufTQ2_0 bl, const in uint blockCoords[2], const in uint coordInBlock[2])
|
||||
{
|
||||
decodeBufTQ2_0_packed16 bl16 = decodeBufTQ2_0_packed16(bl);
|
||||
const uint idx = coordInBlock[1];
|
||||
|
||||
const uint qsshift = (idx & 0x60) >> 4; // 0,2,4,6
|
||||
|
||||
uint qs = uint32_t(bl16.block.qs[((idx & 0x80) >> 3) + ((idx & 0x1E) >> 1)]);
|
||||
qs = (qs >> qsshift) & 0x0303;
|
||||
qs = unpack8(qs)[idx & 1];
|
||||
|
||||
return bl.block.d * (float16_t(int(qs)) - float16_t(1.0));
|
||||
}
|
||||
|
||||
f16vec4 dequantFuncTQ2_0_v(const in decodeBufTQ2_0 bl, const in uint blockCoords[2], const in uint coordInBlock[2])
|
||||
{
|
||||
const uint idx = coordInBlock[1];
|
||||
|
||||
const uint qsshift = (idx & 0x60) >> 4; // 0,2,4,6
|
||||
const uint qsi = ((idx & 0x80) >> 2) + (idx & 0x1C); // byte index of 4-aligned group
|
||||
|
||||
const uint qsw = (uint(bl.block.qs[qsi]))
|
||||
| (uint(bl.block.qs[qsi + 1]) << 8)
|
||||
| (uint(bl.block.qs[qsi + 2]) << 16)
|
||||
| (uint(bl.block.qs[qsi + 3]) << 24);
|
||||
const u8vec4 q = unpack8((qsw >> qsshift) & 0x03030303);
|
||||
|
||||
return bl.block.d * (f16vec4(q) - f16vec4(1.0));
|
||||
}
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 4) buffer decodeBufQ2_K {
|
||||
block_q2_K block;
|
||||
};
|
||||
@@ -1368,6 +1406,9 @@ f16vec4 dequantFuncNVFP4_v(const in decodeBufNVFP4 bl, const in uint blockCoords
|
||||
#elif defined(DATA_A_Q8_0)
|
||||
#define dequantFuncA dequantFuncQ8_0
|
||||
#define dequantFuncA_v dequantFuncQ8_0_v
|
||||
#elif defined(DATA_A_TQ2_0)
|
||||
#define dequantFuncA dequantFuncTQ2_0
|
||||
#define dequantFuncA_v dequantFuncTQ2_0_v
|
||||
#elif defined(DATA_A_Q2_K)
|
||||
#define dequantFuncA dequantFuncQ2_K
|
||||
#define dequantFuncA_v dequantFuncQ2_K_v
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#version 450
|
||||
|
||||
#include "dequant_head.glsl"
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout (binding = 0) readonly buffer A {A_TYPE data_a[];};
|
||||
layout (binding = 1) writeonly buffer D {D_TYPE data_b[];};
|
||||
|
||||
void main() {
|
||||
[[unroll]] for (uint wgy = 0; wgy < 256; wgy++) {
|
||||
const uint i = gl_WorkGroupID.x * 256 + wgy;
|
||||
if (i >= p.nel / QUANT_K) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint tid = gl_LocalInvocationID.x;
|
||||
const uint ip = tid / 32; // group 0,1 (128 elems each)
|
||||
const uint il = tid - 32 * ip; // byte in group 0..31
|
||||
|
||||
const uint y_idx = i * QUANT_K + 128 * ip + il;
|
||||
|
||||
const uint8_t qs = data_a[i].qs[32 * ip + il];
|
||||
|
||||
const FLOAT_TYPE d = FLOAT_TYPE(data_a[i].d);
|
||||
data_b[y_idx + 0] = D_TYPE(d * FLOAT_TYPE(int((qs >> 0) & 3) - 1));
|
||||
data_b[y_idx + 32] = D_TYPE(d * FLOAT_TYPE(int((qs >> 2) & 3) - 1));
|
||||
data_b[y_idx + 64] = D_TYPE(d * FLOAT_TYPE(int((qs >> 4) & 3) - 1));
|
||||
data_b[y_idx + 96] = D_TYPE(d * FLOAT_TYPE(int((qs >> 6) & 3) - 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#version 450
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int32 : require
|
||||
|
||||
#include "mul_mat_vec_base.glsl"
|
||||
|
||||
layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
FLOAT_TYPE temp[NUM_COLS][NUM_ROWS];
|
||||
|
||||
// ternary TQ2_0: w = (q - 1) * d. Same qs group/level layout as q2_K, but a
|
||||
// single f16 scale per 256-block and no mins:
|
||||
// sum_e b_e * (q_e - 1) * d = d * (sum_e b_e * q_e - sum_e b_e)
|
||||
void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, const uint q_offset, const uint y_offset, const uint i, const uint num_blocks_per_row, const uint first_row, const uint num_rows) {
|
||||
const uint y_idx = i * QUANT_K + y_offset;
|
||||
|
||||
[[unroll]] for (uint n = 0; n < num_rows; ++n) {
|
||||
const uint ib0 = a_offset + (first_row+n)*num_blocks_per_row;
|
||||
if (i >= num_blocks_per_row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t qs_u32 = uint32_t(data_a_packed16[ib0 + i].qs[q_offset / 2]) | (uint32_t(data_a_packed16[ib0 + i].qs[q_offset / 2 + 8]) << 16);
|
||||
const vec4 qs_u32_0 = vec4(unpack8(qs_u32 & 0x03030303));
|
||||
const vec4 qs_u32_2 = vec4(unpack8((qs_u32 >> 2) & 0x03030303));
|
||||
const vec4 qs_u32_4 = vec4(unpack8((qs_u32 >> 4) & 0x03030303));
|
||||
const vec4 qs_u32_6 = vec4(unpack8((qs_u32 >> 6) & 0x03030303));
|
||||
|
||||
const FLOAT_TYPE d = FLOAT_TYPE(data_a[ib0 + i].d);
|
||||
|
||||
[[unroll]] for (uint j = 0; j < NUM_COLS; ++j) {
|
||||
vec2 b0 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 0]);
|
||||
vec2 b16 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 8]);
|
||||
vec2 b32 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 16]);
|
||||
vec2 b48 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 24]);
|
||||
vec2 b64 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 32]);
|
||||
vec2 b80 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 40]);
|
||||
vec2 b96 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 48]);
|
||||
vec2 b112 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 56]);
|
||||
|
||||
FLOAT_TYPE sumq = FLOAT_TYPE(0.0);
|
||||
FLOAT_TYPE sumb = FLOAT_TYPE(0.0);
|
||||
[[unroll]] for (int l = 0; l < 2; ++l) {
|
||||
sumq = fma(FLOAT_TYPE(b0[l]), FLOAT_TYPE(qs_u32_0[l ]),
|
||||
fma(FLOAT_TYPE(b16[l]), FLOAT_TYPE(qs_u32_0[l+2]),
|
||||
fma(FLOAT_TYPE(b32[l]), FLOAT_TYPE(qs_u32_2[l ]),
|
||||
fma(FLOAT_TYPE(b48[l]), FLOAT_TYPE(qs_u32_2[l+2]),
|
||||
fma(FLOAT_TYPE(b64[l]), FLOAT_TYPE(qs_u32_4[l ]),
|
||||
fma(FLOAT_TYPE(b80[l]), FLOAT_TYPE(qs_u32_4[l+2]),
|
||||
fma(FLOAT_TYPE(b96[l]), FLOAT_TYPE(qs_u32_6[l ]),
|
||||
fma(FLOAT_TYPE(b112[l]), FLOAT_TYPE(qs_u32_6[l+2]), sumq))))))));
|
||||
sumb += FLOAT_TYPE(b0[l]) + FLOAT_TYPE(b16[l]) + FLOAT_TYPE(b32[l]) + FLOAT_TYPE(b48[l])
|
||||
+ FLOAT_TYPE(b64[l]) + FLOAT_TYPE(b80[l]) + FLOAT_TYPE(b96[l]) + FLOAT_TYPE(b112[l]);
|
||||
}
|
||||
temp[j][n] = fma(d, sumq - sumb, temp[j][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void compute_outputs(const uint32_t first_row, const uint32_t num_rows) {
|
||||
uint a_offset, b_offset, d_offset;
|
||||
get_offsets(a_offset, b_offset, d_offset);
|
||||
|
||||
const uint num_blocks_per_row = p.ncols / QUANT_K;
|
||||
|
||||
// 16 threads are used to process each block
|
||||
const uint it_size = gl_WorkGroupSize.x/16;
|
||||
const uint tid = gl_LocalInvocationID.x;
|
||||
const uint itid = tid%16; // 0...15
|
||||
const uint ix = tid/16;
|
||||
|
||||
const uint v_im = itid/8; // 0 or 1. 0 computes 0..., 1 computes 128...
|
||||
const uint v_in = itid - 8*v_im; // 0...7
|
||||
|
||||
const uint l0 = 2*v_in; // 0...15
|
||||
const uint q_offset = 32*v_im + l0;
|
||||
const uint y_offset = 128*v_im + l0;
|
||||
|
||||
[[unroll]] for (uint j = 0; j < NUM_COLS; ++j) {
|
||||
[[unroll]] for (uint i = 0; i < NUM_ROWS; ++i) {
|
||||
temp[j][i] = FLOAT_TYPE(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i0 = 0; i0 < num_blocks_per_row; i0 += it_size)
|
||||
calc_superblock(a_offset, b_offset, v_im, q_offset, y_offset, i0 + ix, num_blocks_per_row, first_row, num_rows);
|
||||
|
||||
reduce_result(temp, d_offset, first_row, num_rows, tid);
|
||||
}
|
||||
|
||||
void main() {
|
||||
const uint first_row = NUM_ROWS * (gl_WorkGroupID.x + gl_NumWorkGroups.x * gl_WorkGroupID.z);
|
||||
|
||||
// do NUM_ROWS at a time, unless there aren't enough remaining rows
|
||||
if (first_row + NUM_ROWS <= p.stride_d) {
|
||||
compute_outputs(first_row, NUM_ROWS);
|
||||
} else {
|
||||
if (first_row >= p.stride_d) {
|
||||
return;
|
||||
}
|
||||
compute_outputs(first_row, p.stride_d - first_row);
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,22 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin
|
||||
|
||||
buf_a[buf_idx ] = FLOAT_TYPEV2(v.xy);
|
||||
buf_a[buf_idx + 1] = FLOAT_TYPEV2(v.zw);
|
||||
#elif defined(DATA_A_TQ2_0)
|
||||
const uint idx = pos_a + col * p.stride_a / LOAD_VEC_A + row;
|
||||
const uint buf_idx = col * SHMEM_STRIDE + row * LOAD_VEC_A / 2;
|
||||
|
||||
const uint ib = idx / 128; // 2 values per idx
|
||||
const uint iqs = (idx % 128) * 2; // elem 0,2,4..254
|
||||
|
||||
const uint qsi = (iqs / 128) * 32 + (iqs % 32); // byte pair start
|
||||
const uint shift = 2 * ((iqs % 128) / 32); // 0,2,4,6
|
||||
|
||||
const uvec2 qs = uvec2(data_a[ib].qs[qsi], data_a[ib].qs[qsi + 1]);
|
||||
const float d = float(data_a[ib].d);
|
||||
|
||||
const vec2 v = d * (vec2((qs >> shift) & 3) - 1.0);
|
||||
|
||||
buf_a[buf_idx] = FLOAT_TYPEV2(v.xy);
|
||||
#elif defined(DATA_A_Q3_K)
|
||||
const uint idx = pos_a + col * p.stride_a / LOAD_VEC_A + row;
|
||||
const uint buf_idx = col * SHMEM_STRIDE + row * LOAD_VEC_A / 2;
|
||||
|
||||
@@ -303,6 +303,30 @@ struct block_q2_K_packed32
|
||||
#define DATA_A_QUANT_K
|
||||
#endif
|
||||
|
||||
#define QUANT_K_TQ2_0 256
|
||||
|
||||
// ternary (BitNet): 2-bit codes, w = (q - 1) * d; qs layout matches q2_K's
|
||||
// two 32-byte groups with four bit-levels per byte
|
||||
struct block_tq2_0
|
||||
{
|
||||
uint8_t qs[QUANT_K_TQ2_0/4];
|
||||
float16_t d;
|
||||
};
|
||||
|
||||
struct block_tq2_0_packed16
|
||||
{
|
||||
uint16_t qs[QUANT_K_TQ2_0/4/2];
|
||||
float16_t d;
|
||||
};
|
||||
|
||||
#if defined(DATA_A_TQ2_0)
|
||||
#define QUANT_K QUANT_K_TQ2_0
|
||||
#define QUANT_R 1
|
||||
#define A_TYPE block_tq2_0
|
||||
#define A_TYPE_PACKED16 block_tq2_0_packed16
|
||||
#define DATA_A_QUANT_K
|
||||
#endif
|
||||
|
||||
#define QUANT_K_Q3_K 256
|
||||
|
||||
struct block_q3_K
|
||||
|
||||
@@ -72,6 +72,7 @@ const std::vector<std::string> type_names = {
|
||||
"iq4_nl",
|
||||
"mxfp4",
|
||||
"nvfp4",
|
||||
"tq2_0",
|
||||
"bf16",
|
||||
};
|
||||
|
||||
@@ -733,7 +734,7 @@ void process_shaders() {
|
||||
for (const auto& tname : type_names) {
|
||||
// mul mat vec
|
||||
std::string data_a_key = "DATA_A_" + to_uppercase(tname);
|
||||
std::string shader = (string_ends_with(tname, "_k") || string_starts_with(tname, "iq1_") || string_starts_with(tname, "iq2_") || string_starts_with(tname, "iq3_")) ? "mul_mat_vec_" + tname + ".comp" : "mul_mat_vec.comp";
|
||||
std::string shader = (string_ends_with(tname, "_k") || string_starts_with(tname, "iq1_") || string_starts_with(tname, "iq2_") || string_starts_with(tname, "iq3_") || tname == "tq2_0") ? "mul_mat_vec_" + tname + ".comp" : "mul_mat_vec.comp";
|
||||
|
||||
string_to_spv("mul_mat_vec_" + tname + "_f32_f32", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}));
|
||||
string_to_spv("mul_mat_vec_" + tname + "_f16_f32", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}));
|
||||
|
||||
+12
-3
@@ -611,6 +611,13 @@ static struct gguf_context * gguf_init_from_reader(const struct gguf_reader & gr
|
||||
GGML_ASSERT(int64_t(ctx->kv.size()) == n_kv);
|
||||
|
||||
const int alignment_idx = gguf_find_key(ctx, GGUF_KEY_GENERAL_ALIGNMENT);
|
||||
if (alignment_idx != -1 && gguf_get_kv_type(ctx, alignment_idx) != GGUF_TYPE_UINT32) {
|
||||
GGML_LOG_ERROR("%s: key '%s' must be of type %s but is %s\n",
|
||||
__func__, GGUF_KEY_GENERAL_ALIGNMENT, gguf_type_name(GGUF_TYPE_UINT32),
|
||||
gguf_type_name(gguf_get_kv_type(ctx, alignment_idx)));
|
||||
gguf_free(ctx);
|
||||
return nullptr;
|
||||
}
|
||||
ctx->alignment = alignment_idx == -1 ? GGUF_DEFAULT_ALIGNMENT : gguf_get_val_u32(ctx, alignment_idx);
|
||||
|
||||
if (ctx->alignment == 0 || (ctx->alignment & (ctx->alignment - 1)) != 0) {
|
||||
@@ -682,9 +689,11 @@ static struct gguf_context * gguf_init_from_reader(const struct gguf_reader & gr
|
||||
}
|
||||
|
||||
// check that the total number of elements is representable
|
||||
if (ok && ((INT64_MAX/info.t.ne[1] <= info.t.ne[0]) ||
|
||||
(INT64_MAX/info.t.ne[2] <= info.t.ne[0]*info.t.ne[1]) ||
|
||||
(INT64_MAX/info.t.ne[3] <= info.t.ne[0]*info.t.ne[1]*info.t.ne[2]))) {
|
||||
// (a zero-element tensor is trivially representable; the guard also avoids a division by zero below)
|
||||
if (ok && ggml_nelements(&info.t) > 0 &&
|
||||
((INT64_MAX/info.t.ne[1] <= info.t.ne[0]) ||
|
||||
(INT64_MAX/info.t.ne[2] <= info.t.ne[0]*info.t.ne[1]) ||
|
||||
(INT64_MAX/info.t.ne[3] <= info.t.ne[0]*info.t.ne[1]*info.t.ne[2]))) {
|
||||
|
||||
GGML_LOG_ERROR("%s: total number of elements in tensor '%s' with shape "
|
||||
"(%" PRIi64 ", %" PRIi64 ", %" PRIi64 ", %" PRIi64 ") is >= %" PRIi64 "\n",
|
||||
|
||||
@@ -457,6 +457,8 @@ extern "C" {
|
||||
// lora adapter
|
||||
struct llama_adapter_lora;
|
||||
|
||||
LLAMA_API const char * llama_version(void);
|
||||
|
||||
// Helpers for getting default parameters
|
||||
// TODO: update API to start accepting pointers to params structs (https://github.com/ggml-org/llama.cpp/discussions/9172)
|
||||
LLAMA_API struct llama_model_params llama_model_default_params(void);
|
||||
@@ -883,6 +885,7 @@ extern "C" {
|
||||
const llama_token * tokens,
|
||||
size_t n_token_count);
|
||||
|
||||
// If tokens_out is NULL, only the token count is reported through n_token_count_out and no state is loaded
|
||||
LLAMA_API size_t llama_state_seq_load_file(
|
||||
struct llama_context * ctx,
|
||||
const char * filepath,
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
{#
|
||||
Template: Muse Glimmer ATEM Chat Template
|
||||
Renders the ATEM tool-calling protocol: reasoning channel (to=self), tool
|
||||
channels (to=<tool>), and the user channel, plus tool definitions and the
|
||||
valid-recipient list in the system block.
|
||||
|
||||
Whitespace note: every tag uses the {%- -%} / {{- -}} stripping markers, so
|
||||
the indentation below is purely for readability and contributes nothing to
|
||||
the rendered output.
|
||||
#}
|
||||
{%- macro render_content(content) -%}
|
||||
{%- if content is string -%}
|
||||
{{- content -}}
|
||||
{%- elif content is not none -%}
|
||||
{%- for part in content -%}
|
||||
{%- if part['type'] == 'image' -%}
|
||||
{{- '<|patch|>' -}}
|
||||
{%- elif part['type'] == 'video' -%}
|
||||
{{- '<|video|>' -}}
|
||||
{%- elif part['type'] == 'text' -%}
|
||||
{{- part['text'] -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
{%- macro render_atem(tc) -%}
|
||||
{%- set args = tc.function.arguments -%}
|
||||
{%- if args is not mapping -%}
|
||||
{{- raise_exception('Muse Glimmer ATEM chat template requires tool_call.function.arguments to be a dict (mapping); a JSON string cannot be parsed in the HF jinja sandbox.') -}}
|
||||
{%- endif -%}
|
||||
{{- '<atem:function_calls>\n<atem:invoke name="' + tc.function.name + '">\n' -}}
|
||||
{%- for k, v in args.items() -%}
|
||||
{{- '<atem:parameter name="' + k + '">' -}}
|
||||
{%- if v is boolean -%}
|
||||
{%- if v -%}
|
||||
true
|
||||
{%- else -%}
|
||||
false
|
||||
{%- endif -%}
|
||||
{%- elif v is none -%}
|
||||
null
|
||||
{%- elif v is mapping or (v is iterable and v is not string) -%}
|
||||
{{- v | tojson -}}
|
||||
{%- else -%}
|
||||
{{- v -}}
|
||||
{%- endif -%}
|
||||
{{- '</atem:parameter>\n' -}}
|
||||
{%- endfor -%}
|
||||
{{- '</atem:invoke>\n</atem:function_calls>' -}}
|
||||
{%- endmacro -%}
|
||||
{%- macro render_tool_defs(tools) -%}
|
||||
{{- 'In this environment you have access to a set of tools you can use to answer the user\'s question.\n\n' -}}
|
||||
{{- 'You can invoke a function by writing a "<atem:function_calls>" block like the following:\n' -}}
|
||||
{{- '<atem:function_calls>\n<atem:invoke name="$FUNCTION_NAME">\n<atem:parameter name="$PARAMETER_NAME">$PARAMETER_VALUE</atem:parameter>\n...\n</atem:invoke>\n</atem:function_calls>\n\n' -}}
|
||||
{{- 'String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular expressions.\n' -}}
|
||||
{{- 'Here are the functions available in JSONSchema format:\n' -}}
|
||||
{{- '// Tool metadata\n' -}}
|
||||
{%- set nsns = namespace(seen=[]) -%}
|
||||
{%- for tool in tools -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{%- set tns = fn.name.split('.')[0] -%}
|
||||
{%- if tns not in nsns.seen -%}
|
||||
{%- set nsns.seen = nsns.seen + [tns] -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- set nd = tool_namespace_descriptions if tool_namespace_descriptions is defined else {} -%}
|
||||
{%- for tns in nsns.seen -%}
|
||||
{{- '{"name": ' + (tns | tojson) + ', "description": ' + ((nd[tns] if tns in nd else '') | tojson) + '}\n' -}}
|
||||
{%- endfor -%}
|
||||
{{- '// Function schemas' -}}
|
||||
{%- for tool in tools -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{{- '\n{"name": ' + (fn.name | tojson) + ', "description": ' + (fn.description | tojson) + ', "parameters": ' + (fn.parameters | tojson) + '}' -}}
|
||||
{%- endfor -%}
|
||||
{{- '\n\nHere\'s an example of how to call a function in the tool set:\n' -}}
|
||||
{{- '(If the tool namespace is not specified, invoke the function directly as `example_function_name` rather than `example_tool_name.example_function_name`)\n\n' -}}
|
||||
{{- 'to=example_tool_name.example_function_name\n\n' -}}
|
||||
{{- '<atem:function_calls>\n<atem:invoke name="example_tool_name.example_function_name">\n' -}}
|
||||
{{- '<atem:parameter name="example_parameter_1">value_1</atem:parameter>\n' -}}
|
||||
{{- '<atem:parameter name="example_parameter_2">This is the value for the second parameter\nthat can span\n"multiple" lines\n</atem:parameter>\n' -}}
|
||||
{{- '</atem:invoke>\n</atem:function_calls>' -}}
|
||||
{%- endmacro -%}
|
||||
{%- macro render_reasoning() -%}
|
||||
{%- set rs = reasoning_strength if reasoning_strength is defined and reasoning_strength else 'high' -%}
|
||||
{{- 'Reasoning strength: ' + rs + '.' -}}
|
||||
{%- endmacro -%}
|
||||
{%- macro render_system_meta(tools) -%}
|
||||
{%- set rns = namespace(recipients=['"self"'], nslist=[]) -%}
|
||||
{%- if tools -%}
|
||||
{%- for tool in tools -%}
|
||||
{%- set fn = tool.function if tool.function is defined else tool -%}
|
||||
{%- set tns = fn.name.split('.')[0] -%}
|
||||
{%- if tns not in rns.nslist -%}
|
||||
{%- set rns.nslist = rns.nslist + [tns] -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- for tns in rns.nslist -%}
|
||||
{%- set rns.recipients = rns.recipients + ['"' + tns + '.*"'] -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- set rns.recipients = rns.recipients + ['"user"'] -%}
|
||||
{{- '# Valid recipients: ' + rns.recipients | join(', ') + '.' -}}
|
||||
{%- endmacro -%}
|
||||
{{- bos_token -}}
|
||||
{%- set ns = namespace(has_system=false) -%}
|
||||
{%- for m in messages -%}
|
||||
{%- if m['role'] == 'system' -%}
|
||||
{%- set ns.has_system = true -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if not ns.has_system -%}
|
||||
{{- '<|start|>system<|message|>You are a helpful AI assistant.' -}}
|
||||
{%- set kc = knowledge_cutoff if knowledge_cutoff is defined and knowledge_cutoff else '2026-01-04' -%}
|
||||
{{- '\nKnowledge cutoff: ' + kc + '.' -}}
|
||||
{%- if current_date is defined and current_date -%}
|
||||
{{- '\nCurrent date: ' + current_date + '.' -}}
|
||||
{%- elif strftime_now is defined -%}
|
||||
{{- '\nCurrent date: ' + strftime_now('%Y-%m-%d') + '.' -}}
|
||||
{%- endif -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_reasoning() -}}
|
||||
{%- if tools -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_tool_defs(tools) -}}
|
||||
{%- endif -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_system_meta(tools) -}}
|
||||
{{- '<|eot|>' -}}
|
||||
{%- endif -%}
|
||||
{%- for message in messages -%}
|
||||
{%- set role = message['role'] -%}
|
||||
{%- set end_token = '<|eom|>' if (not loop.last and messages[loop.index0 + 1]['role'] == role) else '<|eot|>' -%}
|
||||
{%- if role == 'system' -%}
|
||||
{#- Callers sometimes write the directive into the system prompt themselves.
|
||||
Normalise "Reasoning effort" to "Reasoning strength" (jinja has no
|
||||
case-insensitive replace, hence the four realistic casings), then skip
|
||||
the kwarg-driven line below if the prompt already carries one. -#}
|
||||
{%- set sys_text = render_content(message['content'])
|
||||
| replace('Reasoning effort', 'Reasoning strength')
|
||||
| replace('Reasoning Effort', 'Reasoning Strength')
|
||||
| replace('reasoning effort', 'reasoning strength')
|
||||
| replace('REASONING EFFORT', 'REASONING STRENGTH') -%}
|
||||
{{- '<|start|>system<|message|>' -}}
|
||||
{{- sys_text -}}
|
||||
{%- if 'reasoning strength' not in (sys_text | lower) -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_reasoning() -}}
|
||||
{%- endif -%}
|
||||
{%- if tools -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_tool_defs(tools) -}}
|
||||
{%- endif -%}
|
||||
{{- '\n\n' -}}
|
||||
{{- render_system_meta(tools) -}}
|
||||
{{- '<|eot|>' -}}
|
||||
{%- elif role == 'user' -%}
|
||||
{{- '<|start|>user<|message|>' -}}
|
||||
{{- render_content(message['content']) -}}
|
||||
{{- '<|eot|>' -}}
|
||||
{%- elif role == 'tool' -%}
|
||||
{%- set tname = message.get('name') -%}
|
||||
{%- if not tname -%}
|
||||
{%- set tcid = message.get('tool_call_id') -%}
|
||||
{%- set rns = namespace(name=tcid if tcid else '') -%}
|
||||
{%- for m in messages -%}
|
||||
{%- if m.get('tool_calls') -%}
|
||||
{%- for tc in m['tool_calls'] -%}
|
||||
{%- if tcid is not none and tc.id is defined and tc.id == tcid -%}
|
||||
{%- set rns.name = tc.function.name -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- set tname = rns.name -%}
|
||||
{%- endif -%}
|
||||
{{- '<|start|>tool ' + tname + '<|message|><tool_output name="' + tname + '">\n' -}}
|
||||
{{- render_content(message['content']) -}}
|
||||
{{- '\n</tool_output><|eot|>' -}}
|
||||
{%- elif role == 'assistant' -%}
|
||||
{%- if message.get('reasoning_content') -%}
|
||||
{{- '<|start|>assistant to=self<|message|>' + message['reasoning_content'] + '<|eom|>' -}}
|
||||
{%- endif -%}
|
||||
{%- if message.get('tool_calls') -%}
|
||||
{%- for tc in message['tool_calls'] -%}
|
||||
{{- '<|start|>assistant to=' + tc.function.name + '<|message|>' -}}
|
||||
{{- render_atem(tc) -}}
|
||||
{%- if loop.last -%}
|
||||
{{- end_token -}}
|
||||
{%- else -%}
|
||||
{{- '<|eom|>' -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- else -%}
|
||||
{%- set recipient = message.get('recipient') or 'user' -%}
|
||||
{%- set end_turn = message.get('end_turn') -%}
|
||||
{%- if end_turn is none -%}
|
||||
{%- set end_turn = not (recipient and recipient != 'user') -%}
|
||||
{%- endif -%}
|
||||
{{- '<|start|>assistant' -}}
|
||||
{%- if recipient -%}
|
||||
{{- ' to=' + recipient -}}
|
||||
{%- endif -%}
|
||||
{{- '<|message|>' -}}
|
||||
{{- render_content(message['content']) -}}
|
||||
{{- ('<|eot|>' if end_turn else '<|eom|>') -}}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- if add_generation_prompt -%}
|
||||
{{- '<|start|>assistant' -}}
|
||||
{%- endif -%}
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
# Run all pre-release checks and determine the release version.
|
||||
#
|
||||
# Usage: make-release-checks.sh [--dry-run]
|
||||
# --dry-run: warn on failures instead of aborting
|
||||
#
|
||||
# Env (when running in GitHub Actions): GH_TOKEN, GITHUB_REPOSITORY, GITHUB_OUTPUT
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
DRY_RUN=false
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
*) echo "Unknown argument: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
MAJOR=$(grep "set(LLAMA_VERSION_MAJOR" "$REPO_ROOT/CMakeLists.txt" | grep -oP '\d+')
|
||||
MINOR=$(grep "set(LLAMA_VERSION_MINOR" "$REPO_ROOT/CMakeLists.txt" | grep -oP '\d+')
|
||||
PATCH=$(grep "set(LLAMA_VERSION_PATCH" "$REPO_ROOT/CMakeLists.txt" | grep -oP '\d+')
|
||||
VERSION="v${MAJOR}.${MINOR}.${PATCH}"
|
||||
echo "Determined version: ${VERSION}"
|
||||
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
echo "Checking that tag ${VERSION} does not already exist..."
|
||||
if git ls-remote --tags origin "${VERSION}" | grep -q "${VERSION}"; then
|
||||
echo "Error: tag ${VERSION} already exists on remote"
|
||||
exit 1
|
||||
fi
|
||||
echo "Tag ${VERSION} does not exist on remote - OK"
|
||||
|
||||
SHA=$(git rev-parse HEAD)
|
||||
echo "Checking release.yml status for commit ${SHA}..."
|
||||
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||
echo "Warning: GITHUB_REPOSITORY not set - skipping CI check (local run)"
|
||||
else
|
||||
RUNS=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/release.yml/runs" \
|
||||
--jq "[.workflow_runs[] | select(.head_sha == \"${SHA}\" and .conclusion == \"success\")] | length")
|
||||
if [[ "$RUNS" -eq 0 ]]; then
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "Warning: no successful release.yml run found for HEAD (${SHA}) (dry run, continuing)."
|
||||
else
|
||||
echo "Error: no successful release.yml run found for HEAD (${SHA})"
|
||||
echo "The nightly build must complete successfully before making a release."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Found successful release.yml run for HEAD."
|
||||
fi
|
||||
fi
|
||||
|
||||
MAJOR=$(grep "set(GGML_VERSION_MAJOR" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+')
|
||||
MINOR=$(grep "set(GGML_VERSION_MINOR" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+')
|
||||
PATCH=$(grep "set(GGML_VERSION_PATCH" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+')
|
||||
GGML_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
|
||||
echo "Local ggml version: ${GGML_VERSION}"
|
||||
|
||||
if ! git clone --depth 1 --branch "${GGML_VERSION}" https://github.com/ggml-org/ggml.git upstream-ggml 2>/dev/null; then
|
||||
echo "Warning: tag ${GGML_VERSION} not found in upstream ggml - skipping comparison"
|
||||
else
|
||||
echo "Comparing local ggml/ src and include with upstream ${GGML_VERSION}..."
|
||||
DIFF=$(diff -rq "$REPO_ROOT/ggml/src" upstream-ggml/src 2>&1 || true)
|
||||
DIFF+=$(diff -rq "$REPO_ROOT/ggml/include" upstream-ggml/include 2>&1 || true)
|
||||
DIFF+=$(diff "$REPO_ROOT/ggml/CMakeLists.txt" upstream-ggml/CMakeLists.txt 2>&1 || true)
|
||||
rm -rf upstream-ggml
|
||||
if [[ -n "$DIFF" ]]; then
|
||||
echo "local ggml/ differs from upstream ${GGML_VERSION}:"
|
||||
echo "$DIFF"
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "Warning: would abort release due to ggml mismatch (dry run, continuing)."
|
||||
else
|
||||
echo "Error: ggml must match upstream before making a release."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "local ggml/ matches upstream ${GGML_VERSION}"
|
||||
fi
|
||||
fi
|
||||
@@ -1 +1 @@
|
||||
30bf8685ed4eb0a47f2b06229543327749904150
|
||||
8846b79e66747bb9f68597420e95114c177315ce
|
||||
|
||||
+7
-2
@@ -45,11 +45,16 @@ add_library(llama
|
||||
)
|
||||
|
||||
set_target_properties(llama PROPERTIES
|
||||
VERSION ${LLAMA_INSTALL_VERSION}
|
||||
SOVERSION 0
|
||||
VERSION ${LLAMA_VERSION_BASE}
|
||||
SOVERSION ${LLAMA_VERSION_MAJOR}
|
||||
MACHO_CURRENT_VERSION 0 # keep macOS linker from seeing oversized version number
|
||||
)
|
||||
|
||||
target_compile_definitions(llama PRIVATE
|
||||
LLAMA_VERSION="${LLAMA_VERSION}"
|
||||
LLAMA_COMMIT="${LLAMA_BUILD_COMMIT}"
|
||||
)
|
||||
|
||||
target_include_directories(llama PRIVATE .)
|
||||
target_include_directories(llama PUBLIC ../include)
|
||||
target_compile_features (llama PRIVATE cxx_std_17) # don't bump
|
||||
|
||||
@@ -3110,6 +3110,17 @@ size_t llama_context::state_seq_load_file(llama_seq_id seq_id, const char * file
|
||||
{
|
||||
const uint32_t n_token_count = file.read_u32();
|
||||
|
||||
if (tokens_out == nullptr) {
|
||||
const size_t n_token_max = (file.size() - file.tell()) / sizeof(llama_token);
|
||||
if (n_token_count > n_token_max) {
|
||||
LLAMA_LOG_ERROR("%s: token count in sequence state file exceeds the file size! %u > %zu\n", __func__, n_token_count, n_token_max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*n_token_count_out = n_token_count;
|
||||
return file.tell();
|
||||
}
|
||||
|
||||
if (n_token_count > n_token_capacity) {
|
||||
LLAMA_LOG_ERROR("%s: token count in sequence state file exceeded capacity! %u > %zu\n", __func__, n_token_count, n_token_capacity);
|
||||
return 0;
|
||||
|
||||
@@ -1122,6 +1122,9 @@ void llama_model_base::load_hparams(llama_model_loader & ml) {
|
||||
|
||||
ml.get_key(LLM_KV_CONVNEXT_EMBEDDING_LENGTH, hparams.convnext.n_embd);
|
||||
ml.get_key(LLM_KV_CONVNEXT_BLOCK_COUNT, hparams.convnext.n_layer);
|
||||
|
||||
GGML_ASSERT(hparams.posnet.n_layer <= hparams.n_layer_all);
|
||||
GGML_ASSERT(hparams.convnext.n_layer <= hparams.n_layer_all);
|
||||
}
|
||||
|
||||
GGML_ASSERT(hparams.n_expert <= LLAMA_MAX_EXPERTS);
|
||||
|
||||
@@ -114,6 +114,10 @@ bool llama_supports_rpc(void) {
|
||||
return ggml_backend_reg_by_name("RPC") != nullptr;
|
||||
}
|
||||
|
||||
const char * llama_version(void) {
|
||||
return LLAMA_VERSION;
|
||||
}
|
||||
|
||||
void llama_backend_init(void) {
|
||||
ggml_time_init();
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ void llama_model_dflash::load_arch_hparams(llama_model_loader & ml) {
|
||||
// DFlash has a single rope, so the SWA rope == main rope.
|
||||
if (ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa, false) && hparams.n_swa > 0) {
|
||||
hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
|
||||
ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.is_swa_impl, hparams.n_layer());
|
||||
ml.get_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.is_swa_impl);
|
||||
hparams.rope_freq_base_train_swa = hparams.rope_freq_base_train;
|
||||
hparams.rope_freq_scale_train_swa = hparams.rope_freq_scale_train;
|
||||
}
|
||||
|
||||
+146
-3
@@ -3695,6 +3695,117 @@ struct test_relu_sqr : public test_case {
|
||||
}
|
||||
};
|
||||
|
||||
// GGML_OP_UNARY(SILU|SIGMOID|SOFTPLUS) + GGML_OP_MUL (fused operation).
|
||||
// `layout` and `tail` are used for fallback cases where fusion must be skipped
|
||||
struct test_unary_mul : public test_case {
|
||||
const ggml_unary_op op;
|
||||
const ggml_type type;
|
||||
const std::array<int64_t, 4> ne;
|
||||
const bool swap; // unary result is the second MUL operand
|
||||
const std::string layout; // operand layout, see build_graph()
|
||||
const std::string tail; // extra consumer past the MUL, see build_graph()
|
||||
|
||||
std::string op_desc(ggml_tensor * t) override {
|
||||
GGML_UNUSED(t);
|
||||
return std::string(ggml_unary_op_name(op)) + "_MUL";
|
||||
}
|
||||
|
||||
bool run_whole_graph() override { return true; }
|
||||
|
||||
double max_nmse_err() override {
|
||||
// the fused kernel elides the rounding of the unary result that the CPU chain
|
||||
// performs; relax the tolerance to match that drift
|
||||
switch (type) {
|
||||
case GGML_TYPE_F16: return 5e-5;
|
||||
default: return 1e-7;
|
||||
}
|
||||
}
|
||||
|
||||
std::string vars() override {
|
||||
return VARS_TO_STR5(type, ne, swap, layout, tail);
|
||||
}
|
||||
|
||||
test_unary_mul(ggml_unary_op op,
|
||||
ggml_type type = GGML_TYPE_F32,
|
||||
std::array<int64_t, 4> ne = {128, 2, 2, 2},
|
||||
bool swap = false,
|
||||
std::string layout = "packed",
|
||||
std::string tail = "")
|
||||
: op(op), type(type), ne(ne), swap(swap), layout(std::move(layout)), tail(std::move(tail)) {}
|
||||
|
||||
// `ne` viewed out of a wider tensor: rows stay contiguous, but the stride exceeds the width
|
||||
ggml_tensor * padded(ggml_context * ctx, const char * name, int64_t mul0, int64_t off0) {
|
||||
std::array<int64_t, 4> ne_w = ne;
|
||||
ne_w[0] *= mul0;
|
||||
ggml_tensor * base = ggml_new_tensor(ctx, type, 4, ne_w.data());
|
||||
ggml_set_name(base, name);
|
||||
return ggml_view_4d(ctx, base, ne[0], ne[1], ne[2], ne[3],
|
||||
base->nb[1], base->nb[2], base->nb[3], off0 * base->nb[0]);
|
||||
}
|
||||
|
||||
ggml_tensor * build_graph(ggml_context * ctx) override {
|
||||
ggml_tensor * a = nullptr; // unary source
|
||||
ggml_tensor * b = nullptr; // other MUL operand
|
||||
|
||||
if (layout == "packed") {
|
||||
a = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
b = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
} else if (layout == "pad_unary") {
|
||||
a = padded(ctx, "a", 3, 0);
|
||||
b = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
} else if (layout == "pad_other") {
|
||||
a = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
b = padded(ctx, "b", 3, 0);
|
||||
} else if (layout == "halves") {
|
||||
// the shape the Conformer audio encoders build: one tensor split in two
|
||||
std::array<int64_t, 4> ne_w = ne;
|
||||
ne_w[0] *= 2;
|
||||
ggml_tensor * base = ggml_new_tensor(ctx, type, 4, ne_w.data());
|
||||
ggml_set_name(base, "base");
|
||||
b = ggml_view_4d(ctx, base, ne[0], ne[1], ne[2], ne[3], base->nb[1], base->nb[2], base->nb[3], 0);
|
||||
a = ggml_view_4d(ctx, base, ne[0], ne[1], ne[2], ne[3], base->nb[1], base->nb[2], base->nb[3],
|
||||
ne[0] * base->nb[0]);
|
||||
} else if (layout == "strided_dim1") {
|
||||
// contiguous rows but a strided dim 1: not ggml_is_contiguous_1, must not fuse
|
||||
std::array<int64_t, 4> ne_w = ne;
|
||||
ne_w[1] *= 3;
|
||||
ggml_tensor * base = ggml_new_tensor(ctx, type, 4, ne_w.data());
|
||||
ggml_set_name(base, "a");
|
||||
a = ggml_view_4d(ctx, base, ne[0], ne[1], ne[2], ne[3], base->nb[1], base->nb[2], base->nb[3], 0);
|
||||
b = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
} else if (layout == "bcast") {
|
||||
a = ggml_new_tensor(ctx, type, 4, ne.data());
|
||||
b = ggml_new_tensor_4d(ctx, type, ne[0], 1, 1, 1);
|
||||
} else {
|
||||
GGML_ABORT("unknown layout %s", layout.c_str());
|
||||
}
|
||||
ggml_set_name(a, "a");
|
||||
ggml_set_name(b, "b");
|
||||
|
||||
ggml_tensor * u = ggml_unary(ctx, a, op);
|
||||
ggml_set_name(u, "unary");
|
||||
|
||||
// a broadcasting operand can only be the second one
|
||||
const bool second = swap && layout != "bcast";
|
||||
ggml_tensor * out = second ? ggml_mul(ctx, b, u) : ggml_mul(ctx, u, b);
|
||||
|
||||
if (tail == "reuse") {
|
||||
// a second read of the unary result must block the fusion
|
||||
ggml_set_name(out, "mul");
|
||||
out = ggml_add(ctx, out, u);
|
||||
} else if (tail == "consumer") {
|
||||
// fusion still applies; catches a dispatcher that skips one node too many
|
||||
ggml_set_name(out, "mul");
|
||||
out = ggml_add(ctx, out, b);
|
||||
} else if (!tail.empty()) {
|
||||
GGML_ABORT("unknown tail %s", tail.c_str());
|
||||
}
|
||||
ggml_set_name(out, "out");
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// SNAKE activation fusion: y = x + sin(a*x)^2 * inv_b
|
||||
// CUDA backend matches the naive 5-op chain (mul, sin, sqr, mul, add)
|
||||
// and dispatches a single fused kernel.
|
||||
@@ -8000,7 +8111,8 @@ static const ggml_type all_types[] = {
|
||||
GGML_TYPE_Q2_K, GGML_TYPE_Q3_K,
|
||||
GGML_TYPE_Q4_K, GGML_TYPE_Q5_K,
|
||||
GGML_TYPE_Q6_K,
|
||||
// GGML_TYPE_TQ1_0, GGML_TYPE_TQ2_0, // TODO: implement for all backends
|
||||
GGML_TYPE_TQ2_0,
|
||||
// GGML_TYPE_TQ1_0, // TODO: implement for all backends
|
||||
GGML_TYPE_IQ2_XXS, GGML_TYPE_IQ2_XS, GGML_TYPE_IQ2_S,
|
||||
GGML_TYPE_IQ3_XXS, GGML_TYPE_IQ1_S, GGML_TYPE_IQ1_M,
|
||||
GGML_TYPE_IQ4_NL, GGML_TYPE_IQ3_S, GGML_TYPE_IQ4_XS,
|
||||
@@ -8027,7 +8139,8 @@ static const ggml_type other_types[] = {
|
||||
GGML_TYPE_Q2_K, GGML_TYPE_Q3_K,
|
||||
GGML_TYPE_Q5_K,
|
||||
GGML_TYPE_Q6_K,
|
||||
// GGML_TYPE_TQ1_0, GGML_TYPE_TQ2_0, // TODO: implement for all backends
|
||||
GGML_TYPE_TQ2_0,
|
||||
// GGML_TYPE_TQ1_0, // TODO: implement for all backends
|
||||
GGML_TYPE_IQ2_XS, GGML_TYPE_IQ2_S,
|
||||
GGML_TYPE_IQ3_XXS, GGML_TYPE_IQ1_S, GGML_TYPE_IQ1_M,
|
||||
GGML_TYPE_IQ4_NL, GGML_TYPE_IQ3_S, GGML_TYPE_IQ4_XS,
|
||||
@@ -8063,6 +8176,25 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
test_cases.emplace_back(new test_relu_sqr(type, { 5, 7, 11, 13 }));
|
||||
}
|
||||
|
||||
// fused unary + mul (gated activations that are not expressed as GGML_OP_GLU)
|
||||
for (ggml_unary_op op : { GGML_UNARY_OP_SILU, GGML_UNARY_OP_SIGMOID, GGML_UNARY_OP_SOFTPLUS }) {
|
||||
for (ggml_type type : { GGML_TYPE_F16, GGML_TYPE_F32 }) {
|
||||
for (bool swap : { false, true }) {
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, swap));
|
||||
}
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 5, 7, 11, 13 }));
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, false, "pad_unary"));
|
||||
// a view only stays out from between the two ops when the unary result is second
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, true, "pad_other"));
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, true, "halves"));
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, false, "packed", "consumer"));
|
||||
// must not fuse
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, false, "strided_dim1"));
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, false, "bcast"));
|
||||
test_cases.emplace_back(new test_unary_mul(op, type, { 128, 2, 2, 2 }, false, "packed", "reuse"));
|
||||
}
|
||||
}
|
||||
|
||||
// SNAKE activation fusion: x + sin(a*x)^2 * inv_b
|
||||
for (ggml_type type : { GGML_TYPE_F32, GGML_TYPE_F16, GGML_TYPE_BF16 }) {
|
||||
test_cases.emplace_back(new test_snake_fuse(type, { 5, 7, 1, 1})); // primes sub-block
|
||||
@@ -8827,6 +8959,7 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
test_cases.emplace_back(new test_rwkv_wkv6(GGML_TYPE_F32, 32, 64, 128, 4));
|
||||
|
||||
test_cases.emplace_back(new test_rwkv_wkv7(GGML_TYPE_F32, 32, 64, 1, 1));
|
||||
test_cases.emplace_back(new test_rwkv_wkv7(GGML_TYPE_F32, 32, 64, 1, 4));
|
||||
test_cases.emplace_back(new test_rwkv_wkv7(GGML_TYPE_F32, 32, 64, 32, 1));
|
||||
test_cases.emplace_back(new test_rwkv_wkv7(GGML_TYPE_F32, 32, 64, 32, 4));
|
||||
test_cases.emplace_back(new test_rwkv_wkv7(GGML_TYPE_F32, 32, 64, 128, 4));
|
||||
@@ -8862,7 +8995,13 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
|
||||
for (ggml_type type_a : all_types) {
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 16, i, 256, { 1, 1}, {1, 1}));
|
||||
test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 16, i, 1*256, { 1, 1}, {1, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 12, i, 2*256, { 2, 1}, {1, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 11, i, 3*256, { 1, 3}, {5, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 13, i, 4*256, { 2, 3}, {1, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 17, i, 31*256, { 4, 1}, {1, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 18, i, 32*256, { 1, 1}, {8, 1}));
|
||||
//test_cases.emplace_back(new test_mul_mat(type_a, GGML_TYPE_F32, 19, i, 33*256, { 1, 1}, {1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9011,6 +9150,8 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
test_cases.emplace_back(new test_mul_mat(GGML_TYPE_F32, GGML_TYPE_F32, 64, 128, k, {12,1}, {1,1}));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 16, 16, false, 50, 200, k));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 16, 16, true, 50, 200, k));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, false, 50, 200, k));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, true, 50, 200, k));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F32, GGML_TYPE_F32, 16, 16, false, 50, 200, k));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F32, GGML_TYPE_F32, 16, 16, true, 50, 200, k));
|
||||
}
|
||||
@@ -9042,6 +9183,8 @@ static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() {
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 16, 16, b, 32, 1024, 16));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 2, 2, b, 32, 8192, 64));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 16, 16, b, 50, 200, 64));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, b, 32, 1024, 16));
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_BF16, GGML_TYPE_F32, 16, 16, b, 50, 200, 64));
|
||||
}
|
||||
|
||||
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 1, 1, false, 8, 16, 1));
|
||||
|
||||
@@ -5843,6 +5843,52 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
|
||||
.run();
|
||||
}
|
||||
|
||||
// Muse Glimmer format tests
|
||||
{
|
||||
auto tst = peg_tester("models/templates/muse-glimmer.jinja", detailed_debug);
|
||||
|
||||
const std::string call_markup =
|
||||
"<atem:function_calls>\n"
|
||||
"<atem:invoke name=\"special_function\">\n"
|
||||
"<atem:parameter name=\"arg1\">1</atem:parameter>\n"
|
||||
"</atem:invoke>\n"
|
||||
"</atem:function_calls>";
|
||||
|
||||
// A plain answer is unaffected
|
||||
tst.test(" to=user<|message|>Hello, world!\nWhat's up?<|eot|>")
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
|
||||
.expect(message_assist)
|
||||
.run();
|
||||
|
||||
// "Inform then act": the model answers the user and calls a tool in ONE generation,
|
||||
// closing the answer with <|eom|>. The answer must stop there rather than swallow it.
|
||||
tst.test(" to=user<|message|>Hello, world!\nWhat's up?<|eom|>"
|
||||
"<|start|>assistant to=special_function<|message|>" +
|
||||
call_markup)
|
||||
.tools({ special_function_tool })
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
|
||||
.expect(message_with_content_and_tool_call("Hello, world!\nWhat's up?", "special_function",
|
||||
"{\"arg1\":1}"))
|
||||
.run();
|
||||
|
||||
// Markup quoted in an answer has no preceding <|eom|>, so it stays content instead of
|
||||
// becoming an invocation the user never asked for
|
||||
tst.test(" to=user<|message|>You invoke it like this:\n" + call_markup + "<|eot|>")
|
||||
.tools({ special_function_tool })
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
|
||||
.expect_content("You invoke it like this:\n" + call_markup)
|
||||
.run();
|
||||
|
||||
// Tool markup inside the analysis channel is reasoning, not a call
|
||||
tst.test(" to=self<|message|>I could use " + call_markup + " here<|eom|>"
|
||||
"<|start|>assistant to=user<|message|>Hello!<|eot|>")
|
||||
.tools({ special_function_tool })
|
||||
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
|
||||
.expect_reasoning("I could use " + call_markup + " here")
|
||||
.expect_content("Hello!")
|
||||
.run();
|
||||
}
|
||||
|
||||
// GPT-OSS format tests
|
||||
{
|
||||
auto tst = peg_tester("models/templates/openai-gpt-oss-120b.jinja", detailed_debug);
|
||||
|
||||
+30
-8
@@ -31,11 +31,13 @@ enum handcrafted_file_type {
|
||||
// HANDCRAFTED_KV_BAD_VALUE_SIZE = 30 + offset_has_kv, // removed because it can result in allocations > 1 TB (default sanitizer limit)
|
||||
HANDCRAFTED_KV_DUPLICATE_KEY = 40 + offset_has_kv,
|
||||
HANDCRAFTED_KV_BAD_ALIGN = 50 + offset_has_kv,
|
||||
HANDCRAFTED_KV_WRONG_TYPE_ALIGN = 55 + offset_has_kv,
|
||||
HANDCRAFTED_KV_SUCCESS = 800 + offset_has_kv,
|
||||
|
||||
HANDCRAFTED_TENSORS_BAD_NAME_SIZE = 10 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_BAD_N_DIMS = 20 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_BAD_SHAPE = 30 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_ZERO_DIM = 35 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_NE_TOO_BIG = 40 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_NBYTES_TOO_BIG = 45 + offset_has_tensors,
|
||||
HANDCRAFTED_TENSORS_BAD_TYPE = 50 + offset_has_tensors,
|
||||
@@ -69,11 +71,13 @@ static std::string handcrafted_file_type_name(const enum handcrafted_file_type h
|
||||
case HANDCRAFTED_KV_BAD_TYPE: return "KV_BAD_TYPE";
|
||||
case HANDCRAFTED_KV_DUPLICATE_KEY: return "KV_DUPLICATE_KEY";
|
||||
case HANDCRAFTED_KV_BAD_ALIGN: return "KV_BAD_ALIGN";
|
||||
case HANDCRAFTED_KV_WRONG_TYPE_ALIGN: return "KV_WRONG_TYPE_ALIGN";
|
||||
case HANDCRAFTED_KV_SUCCESS: return "KV_RANDOM_KV";
|
||||
|
||||
case HANDCRAFTED_TENSORS_BAD_NAME_SIZE: return "TENSORS_BAD_NAME_SIZE";
|
||||
case HANDCRAFTED_TENSORS_BAD_N_DIMS: return "TENSORS_BAD_N_DIMS";
|
||||
case HANDCRAFTED_TENSORS_BAD_SHAPE: return "TENSORS_BAD_SHAPE";
|
||||
case HANDCRAFTED_TENSORS_ZERO_DIM: return "TENSORS_ZERO_DIM";
|
||||
case HANDCRAFTED_TENSORS_NE_TOO_BIG: return "TENSORS_NE_TOO_BIG";
|
||||
case HANDCRAFTED_TENSORS_NBYTES_TOO_BIG: return "TENSORS_NBYTES_TOO_BIG";
|
||||
case HANDCRAFTED_TENSORS_BAD_TYPE: return "TENSORS_BAD_TYPE";
|
||||
@@ -95,6 +99,9 @@ static std::string handcrafted_file_type_name(const enum handcrafted_file_type h
|
||||
}
|
||||
|
||||
static bool expect_context_not_null(const enum handcrafted_file_type hft) {
|
||||
if (hft == HANDCRAFTED_TENSORS_ZERO_DIM) {
|
||||
return true;
|
||||
}
|
||||
if (hft < offset_has_kv) {
|
||||
return hft >= HANDCRAFTED_HEADER_EMPTY;
|
||||
}
|
||||
@@ -257,9 +264,9 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
|
||||
}
|
||||
{
|
||||
uint64_t n_kv = kv_types.size();
|
||||
if (hft == HANDCRAFTED_KV_BAD_ALIGN ||
|
||||
hft == HANDCRAFTED_TENSORS_BAD_ALIGN || hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN ||
|
||||
hft == HANDCRAFTED_DATA_BAD_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN) {
|
||||
if (hft == HANDCRAFTED_KV_BAD_ALIGN || hft == HANDCRAFTED_KV_WRONG_TYPE_ALIGN ||
|
||||
hft == HANDCRAFTED_TENSORS_BAD_ALIGN || hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN ||
|
||||
hft == HANDCRAFTED_DATA_BAD_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN) {
|
||||
|
||||
n_kv += 1;
|
||||
} else if (hft == HANDCRAFTED_HEADER_BAD_N_KV) {
|
||||
@@ -344,15 +351,17 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
|
||||
helper_write(file, data, hft == HANDCRAFTED_KV_BAD_TYPE ? 1 : gguf_type_size(type));
|
||||
}
|
||||
|
||||
if (hft == HANDCRAFTED_KV_BAD_ALIGN ||
|
||||
hft == HANDCRAFTED_TENSORS_BAD_ALIGN || hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN ||
|
||||
hft == HANDCRAFTED_DATA_BAD_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN) {
|
||||
if (hft == HANDCRAFTED_KV_BAD_ALIGN || hft == HANDCRAFTED_KV_WRONG_TYPE_ALIGN ||
|
||||
hft == HANDCRAFTED_TENSORS_BAD_ALIGN || hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN ||
|
||||
hft == HANDCRAFTED_DATA_BAD_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN) {
|
||||
|
||||
const uint64_t n = strlen(GGUF_KEY_GENERAL_ALIGNMENT);
|
||||
helper_write(file, n);
|
||||
helper_write(file, GGUF_KEY_GENERAL_ALIGNMENT, n);
|
||||
|
||||
const int32_t type = gguf_type(GGUF_TYPE_UINT32);
|
||||
// HANDCRAFTED_KV_WRONG_TYPE_ALIGN declares general.alignment with a non-UINT32 type,
|
||||
// which the loader must reject cleanly instead of aborting on an assertion
|
||||
const int32_t type = hft == HANDCRAFTED_KV_WRONG_TYPE_ALIGN ? int32_t(GGUF_TYPE_INT32) : int32_t(GGUF_TYPE_UINT32);
|
||||
helper_write(file, type);
|
||||
|
||||
alignment = expect_context_not_null(hft) ? 1 : 13;
|
||||
@@ -403,6 +412,9 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hft == HANDCRAFTED_TENSORS_ZERO_DIM) {
|
||||
n_dims = 2;
|
||||
}
|
||||
if (hft == HANDCRAFTED_TENSORS_BAD_N_DIMS) {
|
||||
const uint32_t n_dims_bad = GGML_MAX_DIMS + 1;
|
||||
helper_write(file, n_dims_bad);
|
||||
@@ -415,6 +427,9 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
|
||||
for (uint32_t j = 0; j < n_dims; ++j) {
|
||||
helper_write(file, bad_dim);
|
||||
}
|
||||
} else if (hft == HANDCRAFTED_TENSORS_ZERO_DIM) {
|
||||
const int64_t zero_shape[2] = { shape[0], 0 };
|
||||
helper_write(file, zero_shape, 2*sizeof(int64_t));
|
||||
} else if (hft == HANDCRAFTED_TENSORS_NE_TOO_BIG){
|
||||
const int64_t big_dim = 4*int64_t(INT32_MAX);
|
||||
for (uint32_t j = 0; j < n_dims; ++j) {
|
||||
@@ -446,6 +461,9 @@ static FILE * get_handcrafted_file(const unsigned int seed, const enum handcraft
|
||||
for (uint32_t i = 1; i < n_dims; ++i) {
|
||||
ne *= shape[i];
|
||||
}
|
||||
if (hft == HANDCRAFTED_TENSORS_ZERO_DIM) {
|
||||
ne = 0;
|
||||
}
|
||||
|
||||
offset += GGML_PAD(ggml_row_size(type, ne), (uint64_t) alignment);
|
||||
}
|
||||
@@ -747,11 +765,13 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
|
||||
HANDCRAFTED_KV_BAD_TYPE,
|
||||
HANDCRAFTED_KV_DUPLICATE_KEY,
|
||||
HANDCRAFTED_KV_BAD_ALIGN,
|
||||
HANDCRAFTED_KV_WRONG_TYPE_ALIGN,
|
||||
HANDCRAFTED_KV_SUCCESS,
|
||||
|
||||
HANDCRAFTED_TENSORS_BAD_NAME_SIZE,
|
||||
HANDCRAFTED_TENSORS_BAD_N_DIMS,
|
||||
HANDCRAFTED_TENSORS_BAD_SHAPE,
|
||||
HANDCRAFTED_TENSORS_ZERO_DIM,
|
||||
HANDCRAFTED_TENSORS_NE_TOO_BIG,
|
||||
HANDCRAFTED_TENSORS_NBYTES_TOO_BIG,
|
||||
HANDCRAFTED_TENSORS_BAD_TYPE,
|
||||
@@ -840,7 +860,9 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
|
||||
ntest++;
|
||||
}
|
||||
|
||||
if (expect_context_not_null(hft) && hft >= offset_has_tensors) {
|
||||
// HANDCRAFTED_TENSORS_ZERO_DIM deliberately mangles the tensor shapes to 0 elements,
|
||||
// so only assert that it loads without crashing; skip the exact-geometry comparison.
|
||||
if (expect_context_not_null(hft) && hft >= offset_has_tensors && hft != HANDCRAFTED_TENSORS_ZERO_DIM) {
|
||||
printf("%s: - check_tensors: ", __func__);
|
||||
if (handcrafted_check_tensors(gguf_ctx, seed)) {
|
||||
printf("\033[1;32mOK\033[0m\n");
|
||||
|
||||
@@ -301,7 +301,7 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
llama_print_build_info();
|
||||
llama_print_build_info(llama_version());
|
||||
|
||||
// load the model
|
||||
fprintf(stderr, "Loading model\n");
|
||||
|
||||
@@ -421,7 +421,7 @@ int main(int argc, char ** argv) {
|
||||
params.cb_eval_user_data = &cb_data;
|
||||
params.warmup = false;
|
||||
|
||||
llama_print_build_info();
|
||||
llama_print_build_info(llama_version());
|
||||
llama_backend_init();
|
||||
llama_numa_init(params.numa);
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ static void split_params_parse_ex(int argc, const char ** argv, split_params & p
|
||||
split_print_usage(argv[0]);
|
||||
exit(0);
|
||||
} else if (arg == "--version") {
|
||||
fprintf(stderr, "version: %d (%s)\n", llama_build_number(), llama_commit());
|
||||
fprintf(stderr, "version: %s (build %d, commit %s)\n", llama_version(), llama_build_number(), llama_commit());
|
||||
fprintf(stderr, "built with %s for %s\n", llama_compiler(), llama_build_target());
|
||||
exit(0);
|
||||
} else if (arg == "--dry-run") {
|
||||
|
||||
@@ -72,8 +72,8 @@ add_library(mtmd
|
||||
)
|
||||
|
||||
set_target_properties(mtmd PROPERTIES
|
||||
VERSION ${LLAMA_INSTALL_VERSION}
|
||||
SOVERSION 0
|
||||
VERSION ${LLAMA_VERSION_BASE}
|
||||
SOVERSION ${LLAMA_VERSION_MAJOR}
|
||||
MACHO_CURRENT_VERSION 0 # keep macOS linker from seeing oversized version number
|
||||
)
|
||||
|
||||
|
||||
@@ -611,7 +611,7 @@ int llama_quantize(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
llama_print_build_info();
|
||||
llama_print_build_info(llama_version());
|
||||
|
||||
if (params.dry_run) {
|
||||
fprintf(stderr, "%s: calculating quantization size for '%s' as %s", __func__, fname_inp.c_str(), ftype_str.c_str());
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
json format_error_response(const std::string & message, const enum error_type type) {
|
||||
std::string type_str;
|
||||
@@ -58,6 +60,33 @@ json format_error_response(const std::string & message, const enum error_type ty
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// server_slot_stats
|
||||
//
|
||||
|
||||
json server_slot_stats::to_json() const {
|
||||
json base = {
|
||||
{"cache_n", n_prompt_cached},
|
||||
|
||||
{"prompt_n", n_prompt_processed},
|
||||
{"prompt_ms", t_prompt_ms()},
|
||||
{"prompt_per_token_ms", t_prompt_per_token_ms()},
|
||||
{"prompt_per_second", n_prompt_tps()},
|
||||
|
||||
{"predicted_n", n_gen},
|
||||
{"predicted_ms", t_gen_ms()},
|
||||
{"predicted_per_token_ms", t_gen_per_token_ms()},
|
||||
{"predicted_per_second", n_gen_tps()},
|
||||
};
|
||||
|
||||
if (n_draft_tokens > 0) {
|
||||
base["draft_n"] = n_draft_tokens;
|
||||
base["draft_n_accepted"] = n_draft_accepted;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
//
|
||||
// random string / id
|
||||
//
|
||||
@@ -235,6 +264,102 @@ static inline raw_buffer base64_decode(const std::string & encoded_string) {
|
||||
// server_tokens implementation
|
||||
//
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t SERVER_TOKENS_STATE_VERSION = 1;
|
||||
|
||||
uint32_t server_tokens_state_u32(size_t value) {
|
||||
if (value > std::numeric_limits<uint32_t>::max()) {
|
||||
throw std::runtime_error("Server tokens state is too large");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
class server_tokens_state_writer {
|
||||
public:
|
||||
template <typename T>
|
||||
void write(T value) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
|
||||
const auto * ptr = reinterpret_cast<const char *>(&value);
|
||||
data.insert(data.end(), ptr, ptr + sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const std::vector<T> & values) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
|
||||
write(server_tokens_state_u32(values.size()));
|
||||
if (values.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto * ptr = reinterpret_cast<const char *>(values.data());
|
||||
data.insert(data.end(), ptr, ptr + values.size() * sizeof(T));
|
||||
}
|
||||
|
||||
void write_media_chunk(const mtmd_input_chunk * chunk) {
|
||||
size_t chunk_size = 0;
|
||||
if (mtmd_input_chunk_save(chunk, nullptr, 0, &chunk_size) != 0 || chunk_size == 0) {
|
||||
throw std::runtime_error("Cannot serialize media chunk in server tokens");
|
||||
}
|
||||
std::vector<char> chunk_data(server_tokens_state_u32(chunk_size));
|
||||
if (mtmd_input_chunk_save(chunk, chunk_data.data(), chunk_data.size(), nullptr) != 0) {
|
||||
throw std::runtime_error("Cannot serialize media chunk in server tokens");
|
||||
}
|
||||
write(chunk_data);
|
||||
}
|
||||
|
||||
std::vector<char> take() {
|
||||
data.resize((data.size() + sizeof(llama_token) - 1) / sizeof(llama_token) * sizeof(llama_token), 0);
|
||||
return std::move(data);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
class server_tokens_state_reader {
|
||||
public:
|
||||
server_tokens_state_reader(const char * data, size_t size) : data(data), size(size) {}
|
||||
|
||||
template <typename T>
|
||||
T read() {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
|
||||
if (size - pos < sizeof(T)) {
|
||||
throw std::runtime_error("Unexpected end of server tokens state");
|
||||
}
|
||||
T value;
|
||||
std::memcpy(&value, data + pos, sizeof(value));
|
||||
pos += sizeof(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector() {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
|
||||
const uint32_t n_values = read<uint32_t>();
|
||||
// reject before resizing, so that a small corrupted payload cannot request a huge allocation
|
||||
if (n_values > remaining() / sizeof(T)) {
|
||||
throw std::runtime_error("Unexpected end of server tokens state");
|
||||
}
|
||||
std::vector<T> values(n_values);
|
||||
if (n_values > 0) {
|
||||
std::memcpy(values.data(), data + pos, values.size() * sizeof(T));
|
||||
pos += values.size() * sizeof(T);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
size_t remaining() const {
|
||||
return size - pos;
|
||||
}
|
||||
|
||||
private:
|
||||
const char * data;
|
||||
size_t size;
|
||||
size_t pos = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
server_tokens::server_tokens(mtmd::input_chunks & mtmd_chunks, bool has_mtmd) : has_mtmd(has_mtmd) {
|
||||
for (size_t i = 0; i < mtmd_chunks.size(); ++i) {
|
||||
push_back(mtmd_chunks[i]);
|
||||
@@ -408,6 +533,73 @@ const llama_tokens & server_tokens::get_tokens() const {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::vector<char> server_tokens::serialize() const {
|
||||
static_assert(sizeof(llama_token) == sizeof(uint32_t), "unexpected llama_token size");
|
||||
|
||||
server_tokens_state_writer writer;
|
||||
writer.write((llama_token) LLAMA_TOKEN_NULL);
|
||||
writer.write(SERVER_TOKENS_STATE_VERSION);
|
||||
writer.write(tokens);
|
||||
|
||||
std::vector<uint32_t> media_keys;
|
||||
media_keys.reserve(map_idx_to_media.size());
|
||||
for (const auto & item : map_idx_to_media) {
|
||||
media_keys.push_back(server_tokens_state_u32(item.first));
|
||||
}
|
||||
writer.write(media_keys);
|
||||
|
||||
for (const auto & item : map_idx_to_media) {
|
||||
writer.write_media_chunk(item.second.get());
|
||||
}
|
||||
|
||||
return writer.take();
|
||||
}
|
||||
|
||||
server_tokens server_tokens::deserialize(const llama_tokens & packed, bool has_mtmd) {
|
||||
static_assert(sizeof(llama_token) == sizeof(uint32_t), "unexpected llama_token size");
|
||||
|
||||
if (packed.empty() || packed[0] != LLAMA_TOKEN_NULL) {
|
||||
// plain token list, as written by older versions
|
||||
return server_tokens(packed, has_mtmd);
|
||||
}
|
||||
|
||||
server_tokens_state_reader reader(reinterpret_cast<const char *>(packed.data()), packed.size() * sizeof(llama_token));
|
||||
reader.read<llama_token>(); // format marker
|
||||
if (reader.read<uint32_t>() != SERVER_TOKENS_STATE_VERSION) {
|
||||
throw std::runtime_error("Unsupported server tokens state version");
|
||||
}
|
||||
|
||||
const llama_tokens tokens = reader.read_vector<llama_token>();
|
||||
|
||||
// the media start indices, followed by the media chunks in the same order
|
||||
const std::vector<uint32_t> media_keys = reader.read_vector<uint32_t>();
|
||||
if (!media_keys.empty() && !has_mtmd) {
|
||||
throw std::runtime_error("Cannot restore media tokens without an mmproj");
|
||||
}
|
||||
|
||||
server_tokens result(tokens, has_mtmd);
|
||||
|
||||
for (const uint32_t key : media_keys) {
|
||||
const size_t start_idx = key;
|
||||
const std::vector<char> chunk_data = reader.read_vector<char>();
|
||||
if (chunk_data.empty()) {
|
||||
throw std::runtime_error("Cannot load media chunk from server tokens state");
|
||||
}
|
||||
|
||||
mtmd::input_chunk_ptr chunk(mtmd_input_chunk_load(chunk_data.data(), chunk_data.size()));
|
||||
if (!chunk) {
|
||||
throw std::runtime_error("Cannot load media chunk from server tokens state");
|
||||
}
|
||||
result.map_idx_to_media[start_idx] = std::move(chunk);
|
||||
}
|
||||
|
||||
if (reader.remaining() >= sizeof(llama_token)) {
|
||||
throw std::runtime_error("Trailing data in server tokens state");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
llama_tokens server_tokens::get_text_tokens() const {
|
||||
llama_tokens res;
|
||||
res.reserve(tokens.size());
|
||||
@@ -530,14 +722,28 @@ bool server_tokens::validate(const struct llama_context * ctx) const {
|
||||
const llama_model * model = llama_get_model(ctx);
|
||||
const llama_vocab * vocab = llama_model_get_vocab(model);
|
||||
const int32_t n_vocab = llama_vocab_n_tokens(vocab);
|
||||
size_t n_media = 0;
|
||||
|
||||
for (size_t i = 0; i < tokens.size(); ++i) {
|
||||
const auto & t = tokens[i];
|
||||
if (t == LLAMA_TOKEN_NULL) {
|
||||
try {
|
||||
const auto & chunk = find_chunk(i);
|
||||
size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk.get());
|
||||
i += n_tokens - 1; // will be +1 by the for loop
|
||||
if (mtmd_input_chunk_get_type(chunk.get()) == MTMD_INPUT_CHUNK_TYPE_TEXT) {
|
||||
return false;
|
||||
}
|
||||
const size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk.get());
|
||||
const llama_pos n_pos = mtmd_input_chunk_get_n_pos(chunk.get());
|
||||
if (n_tokens == 0 || n_pos <= 0 || n_tokens > tokens.size() - i) {
|
||||
return false;
|
||||
}
|
||||
for (size_t j = i; j < i + n_tokens; ++j) {
|
||||
if (tokens[j] != LLAMA_TOKEN_NULL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++n_media;
|
||||
i += n_tokens - 1;
|
||||
} catch (const std::exception & e) {
|
||||
return false;
|
||||
}
|
||||
@@ -545,7 +751,7 @@ bool server_tokens::validate(const struct llama_context * ctx) const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return n_media == map_idx_to_media.size();
|
||||
}
|
||||
|
||||
server_tokens server_tokens::clone() const {
|
||||
|
||||
@@ -201,11 +201,14 @@ public:
|
||||
// for compatibility with context shift and prompt truncation
|
||||
void insert(const llama_tokens & inp_tokens);
|
||||
|
||||
// for compatibility with speculative decoding, ctx shift, slot save/load
|
||||
// for compatibility with speculative decoding, ctx shift
|
||||
const llama_tokens & get_tokens() const;
|
||||
|
||||
llama_tokens get_text_tokens() const;
|
||||
|
||||
std::vector<char> serialize() const;
|
||||
static server_tokens deserialize(const llama_tokens & packed, bool has_mtmd);
|
||||
|
||||
// for compatibility with speculative decoding
|
||||
void set_token(llama_pos pos, llama_token id);
|
||||
|
||||
@@ -213,9 +216,6 @@ public:
|
||||
|
||||
bool empty() const { return tokens.empty(); }
|
||||
|
||||
// true if the sequence actually contains image/audio chunks.
|
||||
bool has_media() const { return !map_idx_to_media.empty(); }
|
||||
|
||||
void clear() {
|
||||
map_idx_to_media.clear();
|
||||
tokens.clear();
|
||||
@@ -230,7 +230,7 @@ public:
|
||||
// split the tokens into message spans, skipping over media chunks
|
||||
common_chat_msg_spans find_message_spans(const common_chat_msg_delimiters & delims) const;
|
||||
|
||||
// make sure all text tokens are within the vocab range
|
||||
// check text token IDs and the mapping between media chunks and token ranges
|
||||
bool validate(const struct llama_context * ctx) const;
|
||||
|
||||
server_tokens clone() const;
|
||||
@@ -334,6 +334,160 @@ json format_response_rerank(
|
||||
std::vector<std::string> & texts,
|
||||
int top_n);
|
||||
|
||||
//
|
||||
// stats and metrics
|
||||
//
|
||||
|
||||
// shared between server_slot and server_task_result_*
|
||||
struct server_slot_stats {
|
||||
uint64_t n_prompt_cached = 0;
|
||||
uint64_t n_prompt_processed = 0;
|
||||
uint64_t n_gen = 0;
|
||||
|
||||
// speculative decoding stats
|
||||
// note: the per-position breakdown lives in server_slot, it is not needed in a task result
|
||||
uint64_t n_draft_tokens = 0;
|
||||
uint64_t n_draft_accepted = 0;
|
||||
uint64_t n_draft_verif_steps = 0;
|
||||
|
||||
// these are absolute timestamps (in us)
|
||||
// note: must be signed - they are subtracted before the later ones are set
|
||||
int64_t t_start = 0;
|
||||
int64_t t_prompt_last = 0;
|
||||
int64_t t_gen_last = 0;
|
||||
|
||||
// can only move one direction: start -> prompt -> gen
|
||||
void update_prompt_start() {
|
||||
GGML_ASSERT(t_start == 0);
|
||||
t_start = ggml_time_us();
|
||||
}
|
||||
void set_prompt_last(int64_t t_us) {
|
||||
GGML_ASSERT(t_start > 0);
|
||||
t_prompt_last = t_us;
|
||||
}
|
||||
void update_prompt_last() {
|
||||
set_prompt_last(ggml_time_us());
|
||||
}
|
||||
void update_gen_last() {
|
||||
GGML_ASSERT(t_prompt_last > 0);
|
||||
t_gen_last = ggml_time_us();
|
||||
}
|
||||
|
||||
// these are time durations
|
||||
int64_t t_elapsed_us() const {
|
||||
return ggml_time_us() - t_start;
|
||||
}
|
||||
double t_prompt_ms() const {
|
||||
if (t_prompt_last == 0) {
|
||||
return 0.0; // the prompt is not processed yet
|
||||
}
|
||||
return (t_prompt_last - t_start) / 1000.0;
|
||||
}
|
||||
int64_t t_gen_us() const {
|
||||
if (t_gen_last == 0) {
|
||||
return 0; // the generation is not started yet
|
||||
}
|
||||
// clamp to 1 us, the first token can land in the same us as t_prompt_last
|
||||
return std::max<int64_t>(1, t_gen_last - t_prompt_last);
|
||||
}
|
||||
double t_gen_ms() const {
|
||||
return t_gen_us() / 1000.0;
|
||||
}
|
||||
|
||||
// number of decode steps spent on generation
|
||||
// the first token is free, it comes from the logits of the last prompt batch
|
||||
uint64_t n_gen_steps() const {
|
||||
return n_gen > 0 ? n_gen - 1 : 0;
|
||||
}
|
||||
|
||||
// other derived metrics
|
||||
// note: all of them return 0.0 if the divisor is not known yet
|
||||
double t_prompt_per_token_ms() const {
|
||||
return n_prompt_processed > 0 ? t_prompt_ms() / n_prompt_processed : 0.0;
|
||||
}
|
||||
double t_gen_per_token_ms() const {
|
||||
return n_gen_steps() > 0 ? t_gen_ms() / n_gen_steps() : 0.0;
|
||||
}
|
||||
double n_prompt_tps() const {
|
||||
const double t_ms = t_prompt_ms();
|
||||
return t_ms > 0.0 ? 1e3 / t_ms * n_prompt_processed : 0.0;
|
||||
}
|
||||
double n_gen_tps() const {
|
||||
const double t_ms = t_gen_ms();
|
||||
return t_ms > 0.0 ? 1e3 / t_ms * n_gen_steps() : 0.0;
|
||||
}
|
||||
|
||||
// false if the slot never started, i.e. the task result carries no stats
|
||||
bool is_set() const {
|
||||
return t_start > 0;
|
||||
}
|
||||
|
||||
json to_json() const;
|
||||
};
|
||||
|
||||
// shared between server_context_impl and server_task_result_*
|
||||
// unlike server_slot_stats, server_metrics is server-global and cumulative, not tied to a slot
|
||||
struct server_metrics {
|
||||
int64_t t_start = 0;
|
||||
|
||||
struct bucket {
|
||||
uint64_t count = 0; // number of tokens
|
||||
uint64_t steps = 0; // number of decode steps,
|
||||
// this excludes first generated token (logits from prompt batch)
|
||||
uint64_t time = 0; // in microseconds
|
||||
|
||||
// the rate uses the decode steps, so that "free" tokens do not inflate it
|
||||
double n_per_second() const {
|
||||
return time > 0 ? (double) steps / (double) time * 1e6 : 0.0;
|
||||
}
|
||||
|
||||
void add(uint64_t n, uint64_t n_steps, uint64_t t_us) {
|
||||
count += n;
|
||||
steps += n_steps;
|
||||
time += t_us;
|
||||
}
|
||||
};
|
||||
|
||||
// these are reset by reset_bucket(), only the rate is read from them
|
||||
bucket prompt_bucket;
|
||||
bucket predict_bucket;
|
||||
|
||||
// metrics below are cumulative since the server started
|
||||
bucket prompt; // only processed tokens, cached ones are counted separately below
|
||||
bucket predict;
|
||||
|
||||
// tokens reused from the cache need no decode, so they only have a count
|
||||
uint64_t n_prompt_cached = 0;
|
||||
|
||||
uint64_t n_tokens_max = 0;
|
||||
|
||||
uint64_t n_decode = 0;
|
||||
uint64_t n_busy_slots = 0;
|
||||
|
||||
uint64_t n_draft_tokens = 0; // Total draft tokens generated
|
||||
uint64_t n_draft_accepted = 0; // Draft tokens actually accepted
|
||||
uint64_t n_draft_verif_steps = 0; // Total draft token verification steps by the target model
|
||||
std::vector<uint64_t> n_accepted_per_pos; // Accepted tokens per draft position
|
||||
|
||||
void init() {
|
||||
t_start = ggml_time_us();
|
||||
}
|
||||
|
||||
void reset_bucket() {
|
||||
prompt_bucket = {};
|
||||
predict_bucket = {};
|
||||
}
|
||||
|
||||
void add_prompt(uint64_t n_tokens, uint64_t t_us) {
|
||||
prompt .add(n_tokens, n_tokens, t_us);
|
||||
prompt_bucket.add(n_tokens, n_tokens, t_us);
|
||||
}
|
||||
|
||||
void add_prompt_cached(uint64_t n_tokens) {
|
||||
n_prompt_cached += n_tokens;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// other utils
|
||||
//
|
||||
|
||||
+325
-400
File diff suppressed because it is too large
Load Diff
+115
-71
@@ -10,6 +10,8 @@
|
||||
#include "speculative.h"
|
||||
#include "server-common.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using json = nlohmann::ordered_json;
|
||||
|
||||
//
|
||||
@@ -236,34 +238,6 @@ common_chat_msg task_result_state::update_chat_msg(
|
||||
return chat_msg;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// result_timings
|
||||
//
|
||||
|
||||
json result_timings::to_json() const {
|
||||
json base = {
|
||||
{"cache_n", cache_n},
|
||||
|
||||
{"prompt_n", prompt_n},
|
||||
{"prompt_ms", prompt_ms},
|
||||
{"prompt_per_token_ms", prompt_per_token_ms},
|
||||
{"prompt_per_second", prompt_per_second},
|
||||
|
||||
{"predicted_n", predicted_n},
|
||||
{"predicted_ms", predicted_ms},
|
||||
{"predicted_per_token_ms", predicted_per_token_ms},
|
||||
{"predicted_per_second", predicted_per_second},
|
||||
};
|
||||
|
||||
if (draft_n > 0) {
|
||||
base["draft_n"] = draft_n;
|
||||
base["draft_n_accepted"] = draft_n_accepted;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
//
|
||||
// result_prompt_progress
|
||||
//
|
||||
@@ -382,7 +356,7 @@ json server_task_result_cmpl_final::to_json_non_oaicompat() {
|
||||
{"stop_type", stop_type_to_str(stop)},
|
||||
{"stopping_word", stopping_word},
|
||||
{"tokens_cached", n_tokens_cached},
|
||||
{"timings", timings.to_json()},
|
||||
{"timings", stats.to_json()},
|
||||
};
|
||||
if (!stream && !probs_output.empty()) {
|
||||
res["completion_probabilities"] = completion_token_output::probs_vector_to_json(probs_output, post_sampling_probs);
|
||||
@@ -432,8 +406,8 @@ json server_task_result_cmpl_final::to_json_oaicompat() {
|
||||
if (verbose) {
|
||||
res["__verbose"] = to_json_non_oaicompat();
|
||||
}
|
||||
if (timings.prompt_n >= 0) {
|
||||
res.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
res.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -480,8 +454,8 @@ json server_task_result_cmpl_final::to_json_oaicompat_chat() {
|
||||
if (verbose) {
|
||||
res["__verbose"] = to_json_non_oaicompat();
|
||||
}
|
||||
if (timings.prompt_n >= 0) {
|
||||
res.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
res.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -541,8 +515,8 @@ json server_task_result_cmpl_final::to_json_oaicompat_chat_stream() {
|
||||
});
|
||||
}
|
||||
|
||||
if (timings.prompt_n >= 0) {
|
||||
deltas.back().push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
deltas.back().push_back({"timings", stats.to_json()});
|
||||
}
|
||||
|
||||
// extra fields for debugging purposes
|
||||
@@ -734,8 +708,8 @@ json server_task_result_cmpl_final::to_json_oaicompat_resp_stream() {
|
||||
}}
|
||||
});
|
||||
|
||||
if (timings.prompt_n >= 0) {
|
||||
server_sent_events.back().at("data").push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
server_sent_events.back().at("data").push_back({"timings", stats.to_json()});
|
||||
}
|
||||
|
||||
return server_sent_events;
|
||||
@@ -1086,8 +1060,8 @@ json server_task_result_cmpl_partial::to_json_non_oaicompat() {
|
||||
{"tokens_evaluated", n_prompt_tokens},
|
||||
};
|
||||
// populate the timings object when needed (usually for the last response or with timings_per_token enabled)
|
||||
if (timings.prompt_n > 0) {
|
||||
res.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
res.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
if (is_progress) {
|
||||
res.push_back({"prompt_progress", progress.to_json()});
|
||||
@@ -1126,8 +1100,8 @@ json server_task_result_cmpl_partial::to_json_oaicompat() {
|
||||
if (verbose) {
|
||||
res["__verbose"] = to_json_non_oaicompat();
|
||||
}
|
||||
if (timings.prompt_n >= 0) {
|
||||
res.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
res.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
if (is_progress) {
|
||||
res.push_back({"prompt_progress", progress.to_json()});
|
||||
@@ -1180,8 +1154,8 @@ json server_task_result_cmpl_partial::to_json_oaicompat_chat() {
|
||||
};
|
||||
}
|
||||
|
||||
if (timings.prompt_n >= 0) {
|
||||
last_json.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
last_json.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
if (is_progress) {
|
||||
last_json.push_back({"prompt_progress", progress.to_json()});
|
||||
@@ -1330,8 +1304,8 @@ json server_task_result_cmpl_partial::to_json_oaicompat_resp() {
|
||||
|
||||
if (!events.empty()) {
|
||||
json & data = events.back().at("data");
|
||||
if (timings.prompt_n >= 0) {
|
||||
data.push_back({"timings", timings.to_json()});
|
||||
if (stats.is_set()) {
|
||||
data.push_back({"timings", stats.to_json()});
|
||||
}
|
||||
if (is_progress) {
|
||||
data.push_back({"prompt_progress", progress.to_json()});
|
||||
@@ -1539,34 +1513,104 @@ json server_task_result_error::to_json() {
|
||||
// server_task_result_metrics
|
||||
//
|
||||
json server_task_result_metrics::to_json() {
|
||||
return json {
|
||||
{ "idle", n_idle_slots },
|
||||
{ "processing", n_processing_slots },
|
||||
{ "deferred", n_tasks_deferred },
|
||||
{ "t_start", t_start },
|
||||
return slots_data;
|
||||
}
|
||||
|
||||
{ "n_prompt_tokens_processed_total", n_prompt_tokens_processed_total },
|
||||
{ "t_tokens_generation_total", t_tokens_generation_total },
|
||||
{ "n_tokens_predicted_total", n_tokens_predicted_total },
|
||||
{ "t_prompt_processing_total", t_prompt_processing_total },
|
||||
|
||||
{ "n_tokens_max", n_tokens_max },
|
||||
|
||||
{ "n_prompt_tokens_processed", n_prompt_tokens_processed },
|
||||
{ "t_prompt_processing", t_prompt_processing },
|
||||
{ "n_tokens_predicted", n_tokens_predicted },
|
||||
{ "t_tokens_generation", t_tokens_generation },
|
||||
|
||||
{ "n_decode_total", n_decode_total },
|
||||
{ "n_busy_slots_total", n_busy_slots_total },
|
||||
|
||||
{ "n_draft_tokens_total", n_draft_tokens_total },
|
||||
{ "n_draft_accepted_total", n_draft_accepted_total },
|
||||
{ "n_draft_verif_steps_total", n_draft_verif_steps_total },
|
||||
{ "n_accepted_per_pos_total", n_accepted_per_pos_total },
|
||||
|
||||
{ "slots", slots_data },
|
||||
// metrics definition: https://prometheus.io/docs/practices/naming/#metric-names
|
||||
std::string server_task_result_metrics::to_metrics() {
|
||||
const std::vector<metric_item> counters = {
|
||||
{
|
||||
"prompt_tokens_total",
|
||||
"Number of prompt tokens processed, excluding cached tokens",
|
||||
(double) metrics.prompt.count
|
||||
}, {
|
||||
"prompt_tokens_cached_total",
|
||||
"Number of prompt tokens reused from the cache",
|
||||
(double) metrics.n_prompt_cached
|
||||
}, {
|
||||
"prompt_seconds_total",
|
||||
"Total time spent processing prompts",
|
||||
metrics.prompt.time / 1.e6
|
||||
}, {
|
||||
"tokens_predicted_total",
|
||||
"Number of generation tokens processed",
|
||||
(double) metrics.predict.count
|
||||
}, {
|
||||
"tokens_predicted_seconds_total",
|
||||
"Total time spent generating tokens",
|
||||
metrics.predict.time / 1.e6
|
||||
}, {
|
||||
"n_decode_total",
|
||||
"Total number of llama_decode() calls, excluding speculative decoding and multimodal decoding",
|
||||
(double) metrics.n_decode
|
||||
}, {
|
||||
"n_tokens_max",
|
||||
"Largest observed sequence length (prompt + generation)",
|
||||
(double) metrics.n_tokens_max
|
||||
}, {
|
||||
"spec_decode_num_draft_tokens_total",
|
||||
"Speculative: Total draft tokens generated",
|
||||
(double) metrics.n_draft_tokens
|
||||
}, {
|
||||
"spec_decode_num_accepted_tokens_total",
|
||||
"Speculative: Total draft tokens accepted by the target model",
|
||||
(double) metrics.n_draft_accepted
|
||||
}, {
|
||||
"spec_decode_num_drafts_total",
|
||||
"Speculative: Total speculative decoding verification steps",
|
||||
(double) metrics.n_draft_verif_steps
|
||||
},
|
||||
};
|
||||
|
||||
const std::vector<metric_item> gauges = {
|
||||
{
|
||||
"prompt_tokens_seconds",
|
||||
"Average prompt throughput in tokens/s",
|
||||
metrics.prompt_bucket.n_per_second()
|
||||
}, {
|
||||
"predicted_tokens_seconds",
|
||||
"Average generation throughput in tokens/s",
|
||||
metrics.predict_bucket.n_per_second()
|
||||
}, {
|
||||
"requests_processing",
|
||||
"Number of requests processing",
|
||||
(double) n_processing_slots
|
||||
}, {
|
||||
"requests_deferred",
|
||||
"Number of requests deferred",
|
||||
(double) n_tasks_deferred
|
||||
}, {
|
||||
"n_busy_slots_per_decode",
|
||||
"Average number of busy slots per llama_decode() call",
|
||||
(double) metrics.n_busy_slots / std::max((double) metrics.n_decode, 1.0)
|
||||
},
|
||||
};
|
||||
|
||||
std::stringstream prometheus;
|
||||
|
||||
auto add_items = [&prometheus](const char * type, const std::vector<metric_item> & items) {
|
||||
for (const auto & item : items) {
|
||||
prometheus << "# HELP llamacpp:" << item.name << " " << item.description << "\n"
|
||||
<< "# TYPE llamacpp:" << item.name << " " << type << "\n"
|
||||
<< "llamacpp:" << item.name << " " << item.value << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
add_items("counter", counters);
|
||||
add_items("gauge", gauges);
|
||||
|
||||
// labeled counter: one time series per draft position
|
||||
if (!metrics.n_accepted_per_pos.empty()) {
|
||||
prometheus << "# HELP llamacpp:spec_decode_num_accepted_tokens_per_pos_total"
|
||||
" Accepted tokens per draft position\n"
|
||||
<< "# TYPE llamacpp:spec_decode_num_accepted_tokens_per_pos_total counter\n";
|
||||
for (size_t i = 0; i < metrics.n_accepted_per_pos.size(); i++) {
|
||||
prometheus << "llamacpp:spec_decode_num_accepted_tokens_per_pos_total{position=\""
|
||||
<< i << "\"} " << metrics.n_accepted_per_pos[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return prometheus.str();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+13
-44
@@ -259,26 +259,6 @@ struct server_task {
|
||||
}
|
||||
};
|
||||
|
||||
struct result_timings {
|
||||
int32_t cache_n = -1;
|
||||
|
||||
int32_t prompt_n = -1;
|
||||
double prompt_ms = 0.0;
|
||||
double prompt_per_token_ms = 0.0;
|
||||
double prompt_per_second = 0.0;
|
||||
|
||||
int32_t predicted_n = -1;
|
||||
double predicted_ms = 0.0;
|
||||
double predicted_per_token_ms = 0.0;
|
||||
double predicted_per_second = 0.0;
|
||||
|
||||
// Optional speculative metrics - only included when > 0
|
||||
int32_t draft_n = 0;
|
||||
int32_t draft_n_accepted = 0;
|
||||
|
||||
json to_json() const;
|
||||
};
|
||||
|
||||
struct result_prompt_progress {
|
||||
int32_t total = 0;
|
||||
int32_t cache = 0;
|
||||
@@ -343,7 +323,7 @@ struct server_task_result_cmpl_final : server_task_result {
|
||||
|
||||
bool stream;
|
||||
bool include_usage;
|
||||
result_timings timings;
|
||||
server_slot_stats stats;
|
||||
std::string prompt;
|
||||
|
||||
bool truncated;
|
||||
@@ -425,7 +405,7 @@ struct server_task_result_cmpl_partial : server_task_result {
|
||||
bool is_begin = false; // whether to send 200 status to HTTP client (begin of SSE stream)
|
||||
// ref: https://github.com/ggml-org/llama.cpp/pull/23884
|
||||
completion_token_output prob_output;
|
||||
result_timings timings;
|
||||
server_slot_stats stats;
|
||||
result_prompt_progress progress;
|
||||
|
||||
// response formatting
|
||||
@@ -510,38 +490,27 @@ struct server_task_result_error : server_task_result {
|
||||
};
|
||||
|
||||
struct server_task_result_metrics : server_task_result {
|
||||
// these are immediate stats, not accumulated (server_metrics is cumulative)
|
||||
int n_idle_slots;
|
||||
int n_processing_slots;
|
||||
int n_tasks_deferred;
|
||||
int64_t t_start;
|
||||
|
||||
// TODO: somehow reuse server_metrics in the future, instead of duplicating the fields
|
||||
uint64_t n_prompt_tokens_processed_total = 0;
|
||||
uint64_t t_prompt_processing_total = 0;
|
||||
uint64_t n_tokens_predicted_total = 0;
|
||||
uint64_t t_tokens_generation_total = 0;
|
||||
|
||||
uint64_t n_tokens_max = 0;
|
||||
|
||||
uint64_t n_prompt_tokens_processed = 0;
|
||||
uint64_t t_prompt_processing = 0;
|
||||
|
||||
uint64_t n_tokens_predicted = 0;
|
||||
uint64_t t_tokens_generation = 0;
|
||||
|
||||
uint64_t n_decode_total = 0;
|
||||
uint64_t n_busy_slots_total = 0;
|
||||
|
||||
uint64_t n_draft_tokens_total = 0;
|
||||
uint64_t n_draft_accepted_total = 0;
|
||||
uint64_t n_draft_verif_steps_total = 0;
|
||||
std::vector<uint64_t> n_accepted_per_pos_total;
|
||||
server_metrics metrics;
|
||||
|
||||
// while we can also use std::vector<server_slot> this requires copying the slot object which can be quite messy
|
||||
// therefore, we use json to temporarily store the slot.to_json() result
|
||||
json slots_data = json::array();
|
||||
|
||||
// used by /slots API
|
||||
virtual json to_json() override;
|
||||
|
||||
// used by /metrics API
|
||||
struct metric_item {
|
||||
std::string name;
|
||||
std::string description;
|
||||
double value; // prometheus values are always float64
|
||||
};
|
||||
std::string to_metrics();
|
||||
};
|
||||
|
||||
struct server_task_result_slot_save_load : server_task_result {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "server-tools.h"
|
||||
|
||||
#include "subproc.h"
|
||||
#include "base64.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -864,6 +865,7 @@ static bool path_glob_match(const std::string & pattern, const std::string & rel
|
||||
//
|
||||
|
||||
static constexpr size_t SERVER_TOOL_READ_FILE_MAX_SIZE = 16 * 1024; // 16 KB
|
||||
static constexpr size_t SERVER_TOOL_READ_FILE_MAX_SIZE_BASE64 = 32 * 1024 * 1024; // 32 MB
|
||||
|
||||
struct server_tool_read_file : server_tool {
|
||||
server_tool_read_file() {
|
||||
@@ -899,6 +901,8 @@ struct server_tool_read_file : server_tool {
|
||||
int start_line = json_value(params, "start_line", 1);
|
||||
int end_line = json_value(params, "end_line", -1); // -1 = no limit
|
||||
bool append_loc = json_value(params, "append_loc", false);
|
||||
// comes from the x-resp-type header, the model cannot ask for it
|
||||
bool as_base64 = json_value(params, "resp_type", std::string()) == "base64";
|
||||
|
||||
auto io = make_tools_io(params);
|
||||
|
||||
@@ -906,6 +910,23 @@ struct server_tool_read_file : server_tool {
|
||||
if (!io->file_size(path, file_size)) {
|
||||
return {{"error", "cannot stat file: " + path}};
|
||||
}
|
||||
|
||||
if (as_base64) {
|
||||
if (file_size > SERVER_TOOL_READ_FILE_MAX_SIZE_BASE64) {
|
||||
return {{"error", string_format(
|
||||
"file too large (%zu bytes, max %zu)",
|
||||
(size_t)file_size, SERVER_TOOL_READ_FILE_MAX_SIZE_BASE64)}};
|
||||
}
|
||||
std::string content;
|
||||
if (!io->read_file(path, content)) {
|
||||
return {{"error", "failed to open file: " + path}};
|
||||
}
|
||||
return {
|
||||
{"base64", base64::encode(content.data(), content.size())},
|
||||
{"size_bytes", (size_t) content.size()},
|
||||
};
|
||||
}
|
||||
|
||||
if (file_size > SERVER_TOOL_READ_FILE_MAX_SIZE && end_line == -1) {
|
||||
return {{"error", string_format(
|
||||
"file too large (%zu bytes, max %zu). Use start_line/end_line to read a portion.",
|
||||
@@ -2135,6 +2156,15 @@ void server_tools::setup(const std::vector<std::string> & enabled_tools,
|
||||
params["runtime"] = runtime->spec();
|
||||
}
|
||||
|
||||
// x-resp-type header is only used by read_file for now
|
||||
if (params.contains("resp_type")) {
|
||||
params.erase("resp_type");
|
||||
}
|
||||
auto resp_type = get_header(req.headers, "x-resp-type");
|
||||
if (!resp_type.empty()) {
|
||||
params["resp_type"] = resp_type;
|
||||
}
|
||||
|
||||
server_tool & tool = find_tool(tools, tool_name, stream);
|
||||
|
||||
if (stream) {
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
import pytest
|
||||
from utils import *
|
||||
|
||||
server = ServerPreset.tinyllama2()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def create_server():
|
||||
global server
|
||||
server = ServerPreset.tinyllama2()
|
||||
server.server_metrics = True
|
||||
|
||||
|
||||
def fetch_metrics(server: ServerProcess) -> str:
|
||||
"""get /metrics as raw prometheus text"""
|
||||
res = server.make_request("GET", "/metrics")
|
||||
assert res.status_code == 200
|
||||
assert "Process-Start-Time-Unix" in res.headers
|
||||
assert isinstance(res.body, str)
|
||||
return res.body
|
||||
|
||||
|
||||
def parse_metrics(text: str) -> dict:
|
||||
"""parse the prometheus text format into {name: (type, value)}"""
|
||||
out = {}
|
||||
types = {}
|
||||
for line in text.splitlines():
|
||||
if line.startswith("# TYPE "):
|
||||
_, _, name, kind = line.split(" ", 3)
|
||||
types[name] = kind
|
||||
elif line.startswith("llamacpp:") and "{" not in line:
|
||||
name, value = line.split(" ", 1)
|
||||
assert name in types, f"{name} has no # TYPE line"
|
||||
out[name] = (types[name], float(value))
|
||||
return out
|
||||
|
||||
|
||||
def test_metrics_disabled():
|
||||
global server
|
||||
server.server_metrics = False
|
||||
server.start()
|
||||
res = server.make_request("GET", "/metrics")
|
||||
assert res.status_code == 501 # ERROR_TYPE_NOT_SUPPORTED
|
||||
|
||||
|
||||
def test_metrics_prometheus_format():
|
||||
global server
|
||||
server.start()
|
||||
server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": 8})
|
||||
|
||||
text = fetch_metrics(server)
|
||||
metrics = parse_metrics(text)
|
||||
|
||||
expected_counters = [
|
||||
"llamacpp:prompt_tokens_total",
|
||||
"llamacpp:prompt_tokens_cached_total",
|
||||
"llamacpp:prompt_seconds_total",
|
||||
"llamacpp:tokens_predicted_total",
|
||||
"llamacpp:tokens_predicted_seconds_total",
|
||||
"llamacpp:n_decode_total",
|
||||
"llamacpp:n_tokens_max",
|
||||
"llamacpp:spec_decode_num_draft_tokens_total",
|
||||
"llamacpp:spec_decode_num_accepted_tokens_total",
|
||||
"llamacpp:spec_decode_num_drafts_total",
|
||||
]
|
||||
expected_gauges = [
|
||||
"llamacpp:prompt_tokens_seconds",
|
||||
"llamacpp:predicted_tokens_seconds",
|
||||
"llamacpp:requests_processing",
|
||||
"llamacpp:requests_deferred",
|
||||
"llamacpp:n_busy_slots_per_decode",
|
||||
]
|
||||
|
||||
for name in expected_counters:
|
||||
assert metrics[name][0] == "counter"
|
||||
for name in expected_gauges:
|
||||
assert metrics[name][0] == "gauge"
|
||||
|
||||
# every metric must carry a help line
|
||||
for name in expected_counters + expected_gauges:
|
||||
assert f"# HELP {name} " in text
|
||||
|
||||
assert metrics["llamacpp:n_decode_total"][1] > 0
|
||||
assert metrics["llamacpp:requests_processing"][1] == 0
|
||||
|
||||
|
||||
def test_metrics_prompt_processed_and_cached():
|
||||
global server
|
||||
server.n_slots = 1 # keep the prompt cache on a single slot
|
||||
server.start()
|
||||
|
||||
prompt = "the quick brown fox jumps over the lazy dog"
|
||||
|
||||
n_processed = 0
|
||||
n_cached = 0
|
||||
for _ in range(2):
|
||||
res = server.make_request("POST", "/completion", data={"prompt": prompt, "n_predict": 4})
|
||||
assert res.status_code == 200
|
||||
n_processed += res.body["timings"]["prompt_n"]
|
||||
n_cached += res.body["timings"]["cache_n"]
|
||||
|
||||
# the second request must reuse the prompt of the first one
|
||||
assert n_cached > 0
|
||||
|
||||
metrics = parse_metrics(fetch_metrics(server))
|
||||
|
||||
# cached tokens are counted apart, they cost no decode
|
||||
assert metrics["llamacpp:prompt_tokens_total"][1] == n_processed
|
||||
assert metrics["llamacpp:prompt_tokens_cached_total"][1] == n_cached
|
||||
|
||||
|
||||
def test_metrics_predicted_total_matches_requests():
|
||||
global server
|
||||
server.start()
|
||||
|
||||
n_predicted = 0
|
||||
for n_predict in [1, 4, 16]:
|
||||
res = server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": n_predict})
|
||||
assert res.status_code == 200
|
||||
n_predicted += res.body["timings"]["predicted_n"]
|
||||
|
||||
metrics = parse_metrics(fetch_metrics(server))
|
||||
assert metrics["llamacpp:tokens_predicted_total"][1] == n_predicted
|
||||
|
||||
|
||||
def test_metrics_generation_rate_excludes_first_token():
|
||||
global server
|
||||
server.start()
|
||||
|
||||
# the first token comes from the logits of the last prompt batch, so it costs no decode step
|
||||
res = server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": 1})
|
||||
timings = res.body["timings"]
|
||||
assert timings["predicted_n"] == 1
|
||||
assert timings["predicted_per_second"] == 0.0
|
||||
assert timings["predicted_per_token_ms"] == 0.0
|
||||
|
||||
res = server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": 16})
|
||||
timings = res.body["timings"]
|
||||
assert timings["predicted_n"] == 16
|
||||
# the rate is over 15 decode steps, not 16 tokens
|
||||
expected = 1e3 / timings["predicted_ms"] * 15
|
||||
assert abs(timings["predicted_per_second"] - expected) < 1e-6
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n_predict", [1, 8])
|
||||
def test_metrics_timings_are_finite(n_predict: int):
|
||||
global server
|
||||
server.start()
|
||||
res = server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": n_predict})
|
||||
timings = res.body["timings"]
|
||||
|
||||
# a null here means the server produced inf or nan
|
||||
for key, value in timings.items():
|
||||
assert value is not None, f"{key} is null"
|
||||
assert value >= 0, f"{key} is negative"
|
||||
|
||||
assert timings["prompt_ms"] > 0
|
||||
assert timings["prompt_per_token_ms"] > 0
|
||||
|
||||
|
||||
def test_metrics_timings_on_prompt_progress():
|
||||
global server
|
||||
server.start()
|
||||
|
||||
# a long prompt so that it is split over several batches (n_batch = 32)
|
||||
prompt = "the quick brown fox jumps over the lazy dog " * 8
|
||||
chunks = list(server.make_stream_request("POST", "/completion", data={
|
||||
"prompt": prompt,
|
||||
"n_predict": 4,
|
||||
"stream": True,
|
||||
"timings_per_token": True,
|
||||
"return_progress": True,
|
||||
}))
|
||||
|
||||
progress = [c for c in chunks if "prompt_progress" in c]
|
||||
assert len(progress) > 1 # the prompt did not fit in a single batch
|
||||
|
||||
# the very first update is sent before any prompt token is decoded
|
||||
first = progress[0]["timings"]
|
||||
assert first["prompt_n"] == 0
|
||||
assert first["prompt_ms"] == 0.0
|
||||
assert first["predicted_n"] == 0
|
||||
assert first["predicted_ms"] == 0.0
|
||||
|
||||
# timings must never go backwards, nor report bogus values
|
||||
prompt_ms = 0.0
|
||||
for chunk in progress:
|
||||
timings = chunk["timings"]
|
||||
for key, value in timings.items():
|
||||
assert value is not None, f"{key} is null"
|
||||
assert value >= 0, f"{key} is negative"
|
||||
assert timings["prompt_ms"] >= prompt_ms
|
||||
prompt_ms = timings["prompt_ms"]
|
||||
|
||||
assert prompt_ms > 0
|
||||
|
||||
|
||||
def test_metrics_slots_idle_after_completion():
|
||||
global server
|
||||
server.server_slots = True
|
||||
server.start()
|
||||
server.make_request("POST", "/completion", data={"prompt": "I believe", "n_predict": 8})
|
||||
|
||||
res = server.make_request("GET", "/slots")
|
||||
assert res.status_code == 200
|
||||
for slot in res.body:
|
||||
assert slot["is_processing"] is False
|
||||
if "next_token" in slot:
|
||||
# the budget of the finished task must not leak into the idle slot
|
||||
assert slot["next_token"][0]["n_remain"] == -1
|
||||
assert slot["next_token"][0]["n_decoded"] == 0
|
||||
|
||||
|
||||
def test_metrics_embedding_prompt_is_counted():
|
||||
global server
|
||||
server = ServerPreset.bert_bge_small()
|
||||
server.server_metrics = True
|
||||
server.start()
|
||||
|
||||
res = server.make_request("POST", "/v1/embeddings", data={"input": ["hello world", "goodbye world"]})
|
||||
assert res.status_code == 200
|
||||
|
||||
# embedding tasks never sample a token, but their prompt still costs a decode
|
||||
metrics = parse_metrics(fetch_metrics(server))
|
||||
assert metrics["llamacpp:prompt_tokens_total"][1] > 0
|
||||
assert metrics["llamacpp:n_decode_total"][1] > 0
|
||||
assert metrics["llamacpp:tokens_predicted_total"][1] == 0
|
||||
@@ -2,6 +2,10 @@ import pytest
|
||||
from utils import *
|
||||
import base64
|
||||
import requests
|
||||
import struct
|
||||
|
||||
# sequence state file: magic(4) version(4) payload_size(4), then payload_size llama_token words
|
||||
STATE_FILE_HEADER_SIZE = 12
|
||||
|
||||
server = ServerPreset.tinyllama2()
|
||||
|
||||
@@ -72,6 +76,60 @@ def test_slot_save_restore():
|
||||
assert res.body["timings"]["prompt_n"] == 1
|
||||
|
||||
|
||||
def test_slot_restore_legacy_token_list():
|
||||
global server
|
||||
server.start()
|
||||
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "What is the capital of France?",
|
||||
"id_slot": 1,
|
||||
"cache_prompt": True,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
|
||||
res = server.make_request("POST", "/slots/1?action=save", data={
|
||||
"filename": "slot_legacy.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_saved"] == 84
|
||||
|
||||
# rewrite the token payload into a plain token list, as written by servers that predate the packed server_tokens format
|
||||
path = os.path.join("tmp", "slot_legacy.bin")
|
||||
with open(path, "rb") as f:
|
||||
data = bytearray(f.read())
|
||||
|
||||
# the payload written by this server starts with a packed header: LLAMA_TOKEN_NULL(4) version(4) n_tokens(4)
|
||||
packed_header_size = 12
|
||||
|
||||
payload_size = struct.unpack_from("=I", data, STATE_FILE_HEADER_SIZE - 4)[0]
|
||||
payload_end = STATE_FILE_HEADER_SIZE + payload_size * 4
|
||||
n_tokens = struct.unpack_from("=I", data, STATE_FILE_HEADER_SIZE + 8)[0]
|
||||
assert n_tokens == 84
|
||||
|
||||
tokens_start = STATE_FILE_HEADER_SIZE + packed_header_size
|
||||
data = data[:STATE_FILE_HEADER_SIZE] + data[tokens_start:tokens_start + n_tokens * 4] + data[payload_end:]
|
||||
struct.pack_into("=I", data, STATE_FILE_HEADER_SIZE - 4, n_tokens)
|
||||
|
||||
with open(path, "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
# the plain token list must restore, and the restored KV must be reusable
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "slot_legacy.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == 84
|
||||
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "What is the capital of Germany?",
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["prompt_n"] == 6 # only the different part is processed
|
||||
|
||||
|
||||
|
||||
def test_slot_erase():
|
||||
global server
|
||||
server.start()
|
||||
@@ -103,14 +161,12 @@ def test_slot_erase():
|
||||
#
|
||||
# Multimodal server (mmproj loaded) slot save/restore.
|
||||
#
|
||||
# Regression coverage for issue #21133: slot save/restore/erase must be gated on
|
||||
# the slot's CONTENT (does it actually hold image/audio tokens) rather than the
|
||||
# model's CAPABILITY (is an mmproj loaded). A pure-text slot on a multimodal
|
||||
# server must save/restore/erase normally; a slot that actually holds an image
|
||||
# must be rejected with ERROR_TYPE_NOT_SUPPORTED (HTTP 501).
|
||||
# A pure-text slot on a multimodal server and a slot containing images must both support save/restore.
|
||||
# Erase remains gated on the slot's content.
|
||||
#
|
||||
|
||||
IMG_URL_CAT = "https://huggingface.co/ggml-org/tinygemma3-GGUF/resolve/main/test/91_cat.png"
|
||||
IMG_URL_TRUCK = "https://huggingface.co/ggml-org/tinygemma3-GGUF/resolve/main/test/11_truck.png"
|
||||
|
||||
|
||||
def _get_img_base64(url: str) -> str:
|
||||
@@ -121,8 +177,7 @@ def _get_img_base64(url: str) -> str:
|
||||
|
||||
@pytest.fixture
|
||||
def mmproj_server():
|
||||
# tinygemma3 is a small multimodal model: the mmproj is provided by the HF
|
||||
# registry API and auto-downloaded on first run.
|
||||
# tinygemma3 is a small multimodal model: the mmproj is provided by the HF registry API and auto-downloaded on first run.
|
||||
os.environ['LLAMA_MEDIA_MARKER'] = '<__media__>'
|
||||
mm_server = ServerPreset.tinygemma3()
|
||||
mm_server.slot_save_path = "./tmp"
|
||||
@@ -159,10 +214,7 @@ def test_slot_save_restore_text_only_on_multimodal(mmproj_server):
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == n_saved
|
||||
|
||||
# The restored slot is usable for a follow-up completion. We do NOT assert
|
||||
# prefix reuse here: tinygemma3 is a SWA model, which forces full prompt
|
||||
# re-processing after a restore (a model property, not the save/restore gate
|
||||
# under test).
|
||||
# Prefix reuse is not checked with the default SWA cache.
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "The quick brown fox jumps over the lazy dog.",
|
||||
"id_slot": 0,
|
||||
@@ -171,54 +223,307 @@ def test_slot_save_restore_text_only_on_multimodal(mmproj_server):
|
||||
assert res.status_code == 200
|
||||
|
||||
|
||||
def test_slot_save_rejected_when_slot_holds_image(mmproj_server):
|
||||
def test_slot_save_restore_with_image(mmproj_server):
|
||||
server = mmproj_server
|
||||
# Use the full SWA cache so the restored image prefix can be reused.
|
||||
server.swa_full = True
|
||||
server.start()
|
||||
|
||||
# Process a prompt that actually contains an image on slot 1.
|
||||
prompt_cat = {
|
||||
"prompt_string": "What is this: <__media__>\n",
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT)],
|
||||
}
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 1,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
content_cat = res.body["content"]
|
||||
prompt_n_full = res.body["timings"]["prompt_n"]
|
||||
assert res.body["timings"]["cache_n"] == 0
|
||||
assert prompt_n_full > 32 # text plus image tokens are all processed
|
||||
|
||||
res = server.make_request("POST", "/slots/1?action=save", data={
|
||||
"filename": "mm_slot_image.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
n_saved = res.body["n_saved"]
|
||||
n_written = res.body["n_written"]
|
||||
assert n_saved > 0
|
||||
assert n_written > 0
|
||||
|
||||
res = server.make_request("POST", "/slots/1?action=erase")
|
||||
assert res.status_code == 200
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_image.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == n_saved
|
||||
assert res.body["n_read"] == n_written
|
||||
|
||||
# a different image must not reuse the restored image tokens; only the text prefix before the image is common
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": {
|
||||
"prompt_string": "What is this: <__media__>\n",
|
||||
"multimodal_data": [ _get_img_base64(IMG_URL_CAT) ],
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_TRUCK)],
|
||||
},
|
||||
})
|
||||
assert res.status_code == 200
|
||||
cache_n = res.body["timings"]["cache_n"]
|
||||
assert cache_n < 16
|
||||
assert res.body["timings"]["prompt_n"] == prompt_n_full - cache_n
|
||||
|
||||
# restore again and resend the same image: the image tokens must be reused and greedy sampling must reproduce the original content
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_image.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == n_saved
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["cache_n"] == prompt_n_full - 1
|
||||
assert res.body["timings"]["prompt_n"] == 1
|
||||
assert res.body["content"] == content_cat
|
||||
|
||||
|
||||
def test_slot_save_restore_with_two_images(mmproj_server):
|
||||
server = mmproj_server
|
||||
server.swa_full = True
|
||||
server.n_ctx = 2048 # two images need more than the default 512 per slot
|
||||
server.start()
|
||||
|
||||
prompt = {
|
||||
"prompt_string": "A: <__media__> B: <__media__>\n",
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT), _get_img_base64(IMG_URL_TRUCK)],
|
||||
}
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 1,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
content = res.body["content"]
|
||||
prompt_n_full = res.body["timings"]["prompt_n"]
|
||||
assert prompt_n_full > 64
|
||||
|
||||
res = server.make_request("POST", "/slots/1?action=save", data={
|
||||
"filename": "mm_slot_two_images.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
n_saved = res.body["n_saved"]
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_two_images.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == n_saved
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["cache_n"] == prompt_n_full - 1
|
||||
assert res.body["timings"]["prompt_n"] == 1
|
||||
assert res.body["content"] == content
|
||||
|
||||
|
||||
def test_slot_save_restore_with_image_across_restart(mmproj_server):
|
||||
server = mmproj_server
|
||||
server.swa_full = True
|
||||
server.start()
|
||||
|
||||
prompt_cat = {
|
||||
"prompt_string": "What is this: <__media__>\n",
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT)],
|
||||
}
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
content = res.body["content"]
|
||||
prompt_n_full = res.body["timings"]["prompt_n"]
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=save", data={
|
||||
"filename": "mm_slot_restart.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
n_saved = res.body["n_saved"]
|
||||
|
||||
# restart the server with the same model and mmproj: the saved file must restore in the new process and the image KV must be reused
|
||||
server.stop()
|
||||
server.start()
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_restart.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["n_restored"] == n_saved
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["cache_n"] == prompt_n_full - 1
|
||||
assert res.body["timings"]["prompt_n"] == 1
|
||||
assert res.body["content"] == content
|
||||
|
||||
|
||||
def test_slot_save_restore_image_payload_larger_than_context(mmproj_server):
|
||||
server = mmproj_server
|
||||
server.swa_full = True
|
||||
server.start()
|
||||
|
||||
# the slot context, as the server computed it (n_ctx split across the slots)
|
||||
res = server.make_request("GET", "/props")
|
||||
assert res.status_code == 200
|
||||
n_ctx_slot = res.body["default_generation_settings"]["n_ctx"]
|
||||
|
||||
# a filler token, used to grow the prompt up to the slot context
|
||||
res = server.make_request("POST", "/tokenize", data={"content": " hello" * 8})
|
||||
assert res.status_code == 200
|
||||
assert len(res.body["tokens"]) == 8
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": {
|
||||
"prompt_string": "What is this: <__media__>\n",
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT)],
|
||||
},
|
||||
})
|
||||
assert res.status_code == 200
|
||||
|
||||
# Saving a slot that holds image tokens must be rejected (HTTP 501,
|
||||
# not_supported_error).
|
||||
res = server.make_request("POST", "/slots/1?action=save", data={
|
||||
"filename": "mm_slot_image.bin",
|
||||
prompt_cat = {
|
||||
"prompt_string": "What is this: <__media__>\n" + " hello" * (n_ctx_slot - res.body["timings"]["prompt_n"] - 8),
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT)],
|
||||
}
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code != 200
|
||||
assert res.body["error"]["type"] == "not_supported_error"
|
||||
assert res.status_code == 200
|
||||
prompt_n_full = res.body["timings"]["cache_n"] + res.body["timings"]["prompt_n"]
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=save", data={
|
||||
"filename": "mm_slot_large_payload.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
|
||||
path = os.path.join("tmp", "mm_slot_large_payload.bin")
|
||||
with open(path, "rb") as f:
|
||||
data = bytearray(f.read())
|
||||
payload_size = struct.unpack_from("=I", data, STATE_FILE_HEADER_SIZE - 4)[0]
|
||||
assert payload_size > n_ctx_slot # the scenario under test: the payload does not fit in n_ctx
|
||||
|
||||
# drop the image from the slot, then restore it from the file
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "The quick brown fox",
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_large_payload.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": prompt_cat,
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["cache_n"] == prompt_n_full - 1
|
||||
assert res.body["timings"]["prompt_n"] == 1
|
||||
|
||||
|
||||
def test_slot_erase_text_only_on_multimodal(mmproj_server):
|
||||
def test_slot_restore_media_file_without_mmproj(mmproj_server):
|
||||
server = mmproj_server
|
||||
server.start()
|
||||
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "The quick brown fox jumps over the lazy dog.",
|
||||
"id_slot": 1,
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": {
|
||||
"prompt_string": "What is this: <__media__>\n",
|
||||
"multimodal_data": [_get_img_base64(IMG_URL_CAT)],
|
||||
},
|
||||
})
|
||||
assert res.status_code == 200
|
||||
prompt_n = res.body["timings"]["prompt_n"]
|
||||
assert prompt_n > 0 # all tokens are processed
|
||||
|
||||
# Erasing a pure-text slot must succeed even though an mmproj is loaded.
|
||||
res = server.make_request("POST", "/slots/1?action=erase")
|
||||
assert res.status_code == 200
|
||||
|
||||
# Re-running the same prompt should process all tokens again.
|
||||
res = server.make_request("POST", "/completion", data={
|
||||
"prompt": "The quick brown fox jumps over the lazy dog.",
|
||||
"id_slot": 1,
|
||||
"cache_prompt": True,
|
||||
res = server.make_request("POST", "/slots/0?action=save", data={
|
||||
"filename": "mm_slot_no_mmproj.bin",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["prompt_n"] == prompt_n # all tokens are processed again
|
||||
|
||||
# restart the same model without the mmproj: restoring the media file must fail gracefully and leave the slot usable
|
||||
server.stop()
|
||||
server.no_mmproj = True
|
||||
server.start()
|
||||
|
||||
res = server.make_request("POST", "/slots/0?action=restore", data={
|
||||
"filename": "mm_slot_no_mmproj.bin",
|
||||
})
|
||||
assert res.status_code == 400
|
||||
assert "Cannot restore media tokens without an mmproj" in res.body["error"]["message"]
|
||||
|
||||
# A failed restore must leave the slot empty and usable.
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 1,
|
||||
"cache_prompt": True,
|
||||
"prompt": "The quick brown fox",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
content = res.body["content"]
|
||||
|
||||
res = server.make_request("POST", "/completions", data={
|
||||
"temperature": 0.0,
|
||||
"top_k": 1,
|
||||
"id_slot": 0,
|
||||
"cache_prompt": True,
|
||||
"prompt": "The quick brown fox",
|
||||
})
|
||||
assert res.status_code == 200
|
||||
assert res.body["timings"]["cache_n"] == 0
|
||||
assert res.body["content"] == content
|
||||
|
||||
@@ -27,8 +27,8 @@ def test_with_and_without_draft():
|
||||
global server
|
||||
request = {
|
||||
"prompt": "I believe the meaning of life is",
|
||||
"temperature": 0.8,
|
||||
"top_k": 40,
|
||||
"temperature": 0.2,
|
||||
"top_k": 5,
|
||||
"seed": 4242,
|
||||
"n_predict": 16,
|
||||
"return_tokens": True,
|
||||
@@ -36,7 +36,6 @@ def test_with_and_without_draft():
|
||||
|
||||
server.model_draft = None # disable draft model
|
||||
server.spec_type = None
|
||||
server.backend_sampling = True
|
||||
server.start()
|
||||
res = server.make_request("POST", "/completion", data=request)
|
||||
assert res.status_code == 200
|
||||
@@ -45,7 +44,6 @@ def test_with_and_without_draft():
|
||||
|
||||
# create new server with draft model
|
||||
create_server()
|
||||
server.backend_sampling = True
|
||||
server.start()
|
||||
res = server.make_request("POST", "/completion", data=request)
|
||||
assert res.status_code == 200
|
||||
|
||||
@@ -86,6 +86,7 @@ class ServerProcess:
|
||||
server_reranking: bool | None = False
|
||||
server_metrics: bool | None = False
|
||||
kv_unified: bool | None = False
|
||||
swa_full: bool | None = False
|
||||
server_slots: bool | None = False
|
||||
pooling: str | None = None
|
||||
api_key: str | None = None
|
||||
@@ -106,6 +107,7 @@ class ServerProcess:
|
||||
chat_template_file: str | None = None
|
||||
server_path: str | None = None
|
||||
mmproj_url: str | None = None
|
||||
no_mmproj: bool | None = None
|
||||
media_path: str | None = None
|
||||
sleep_idle_seconds: int | None = None
|
||||
cache_ram: int | None = None
|
||||
@@ -198,6 +200,8 @@ class ServerProcess:
|
||||
server_args.append("--metrics")
|
||||
if self.kv_unified:
|
||||
server_args.append("--kv-unified")
|
||||
if self.swa_full:
|
||||
server_args.append("--swa-full")
|
||||
if self.server_slots:
|
||||
server_args.append("--slots")
|
||||
else:
|
||||
@@ -259,6 +263,8 @@ class ServerProcess:
|
||||
server_args.extend(["--chat-template-file", self.chat_template_file])
|
||||
if self.mmproj_url:
|
||||
server_args.extend(["--mmproj-url", self.mmproj_url])
|
||||
if self.no_mmproj:
|
||||
server_args.append("--no-mmproj")
|
||||
if self.media_path:
|
||||
server_args.extend(["--media-path", self.media_path])
|
||||
if self.sleep_idle_seconds is not None:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { writeThemeFavicons } from './scripts/favicon-colorize';
|
||||
import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa';
|
||||
import { FAVICON_COLORS, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa.constants';
|
||||
import { defineConfig } from '@vite-pwa/assets-generator/config';
|
||||
|
||||
writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK, {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
PWA_ASSET_GENERATOR,
|
||||
PWA_GENERATOR_DEVICES,
|
||||
THEME_COLORS
|
||||
} from './src/lib/constants/pwa';
|
||||
} from './src/lib/constants/pwa.constants';
|
||||
import { SplashOrientation } from './src/lib/enums/splash.enums';
|
||||
import {
|
||||
combinePresetAndAppleSplashScreens,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BUILD_CONFIG } from '../src/lib/constants/pwa';
|
||||
import { BUILD_CONFIG } from '../src/lib/constants/pwa.constants';
|
||||
import { existsSync, writeFileSync } from 'node:fs';
|
||||
import { resolve } from 'path';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BUILD_CONFIG } from '../src/lib/constants/pwa';
|
||||
import { BUILD_CONFIG } from '../src/lib/constants/pwa.constants';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { resolve } from 'path';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { NEWLINE, TAB } from '../src/lib/constants/code';
|
||||
import { APPLE_DEVICES, BUILD_CONFIG, REGEX_PATTERNS, SPLASH_LINK } from '../src/lib/constants/pwa';
|
||||
import {
|
||||
APPLE_DEVICES,
|
||||
BUILD_CONFIG,
|
||||
REGEX_PATTERNS,
|
||||
SPLASH_LINK
|
||||
} from '../src/lib/constants/pwa.constants';
|
||||
import { NEWLINE, TAB } from '../src/lib/constants/special-characters.constants';
|
||||
import { SplashOrientation } from '../src/lib/enums/splash.enums';
|
||||
import type { SplashDimensions } from '../src/lib/types';
|
||||
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user