mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-11-04 01:47:36 -06:00
Compare commits
94
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb02c93e9 | ||
|
|
960b1d51b7 | ||
|
|
4b29d02629 | ||
|
|
bee23322f1 | ||
|
|
149f5641b9 | ||
|
|
b97627967c | ||
|
|
7071ba378a | ||
|
|
6cb3e672e8 | ||
|
|
6b9c0cfcba | ||
|
|
d1c4e5bec1 | ||
|
|
20a51f24a8 | ||
|
|
0f35d6d7b0 | ||
|
|
a464960ed3 | ||
|
|
9309bf1bcf | ||
|
|
d1044bc827 | ||
|
|
28b812d6cb | ||
|
|
749db7f034 | ||
|
|
0943ca5472 | ||
|
|
f9448cddc4 | ||
|
|
0978a6e902 | ||
|
|
0ef27309b7 | ||
|
|
8fc0376721 | ||
|
|
740251a920 | ||
|
|
71efc559a0 | ||
|
|
19e8a86fe8 | ||
|
|
259d91709d | ||
|
|
36ee3caf7a | ||
|
|
fac3a894fc | ||
|
|
b74298c109 | ||
|
|
37b0f9c36a | ||
|
|
ca8daad199 | ||
|
|
0f663d2471 | ||
|
|
00f113eceb | ||
|
|
e6192de0a6 | ||
|
|
4402e65401 | ||
|
|
74101b3cda | ||
|
|
713319e284 | ||
|
|
ee17f293b6 | ||
|
|
51fe42c5d1 | ||
|
|
2cbca389f5 | ||
|
|
29c720eaf7 | ||
|
|
d02b4c0d66 | ||
|
|
e4b6c8ca6a | ||
|
|
1b6935b227 | ||
|
|
3f8af15a76 | ||
|
|
cd8427d018 | ||
|
|
6b00d5627b | ||
|
|
458fcfc340 | ||
|
|
6ed812057c | ||
|
|
ea24b952cd | ||
|
|
84d9ce0fad | ||
|
|
dcbb091677 | ||
|
|
07ec7c8af0 | ||
|
|
cb02250b5b | ||
|
|
2c93f0b6e1 | ||
|
|
5e18eb021b | ||
|
|
5c2ea1ab11 | ||
|
|
23426d797e | ||
|
|
90080ce094 | ||
|
|
64be95b163 | ||
|
|
b12b405c17 | ||
|
|
aac6da8012 | ||
|
|
00ef136649 | ||
|
|
59695669f1 | ||
|
|
a7a490a490 | ||
|
|
f72dc30928 | ||
|
|
fcfe4b59df | ||
|
|
4e910a86e8 | ||
|
|
1c5ed702c9 | ||
|
|
785322d17f | ||
|
|
dde879bc4f | ||
|
|
060b2fea4e | ||
|
|
a91eab67d8 | ||
|
|
d5ef408e20 | ||
|
|
79bce0c881 | ||
|
|
bc428e407a | ||
|
|
f6312ad75e | ||
|
|
0138937e3f | ||
|
|
ddf48f85c9 | ||
|
|
f3586e66bc | ||
|
|
2871678a1d | ||
|
|
7bd1808308 | ||
|
|
0ec4027a2a | ||
|
|
1052f7e1d6 | ||
|
|
8956c769e8 | ||
|
|
5047748f11 | ||
|
|
792fcf4e75 | ||
|
|
a34de8618b | ||
|
|
aa588a3463 | ||
|
|
889f2ec96d | ||
|
|
1ab23c33b7 | ||
|
|
27b8464e82 | ||
|
|
d63fbfe794 | ||
|
|
e216f4ec87 |
@@ -160,3 +160,13 @@ jobs:
|
||||
source .venv/Scripts/activate
|
||||
cd build
|
||||
ctest -C Release -L behavior -LE 'model|backend' --verbose --timeout 900
|
||||
|
||||
- name: ccache-clear
|
||||
uses: ./.github/actions/ccache-clear
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
key: behavior-check-windows-2025
|
||||
older: 5m
|
||||
min: 1
|
||||
dry-run: ${{ github.event_name != 'push' || github.ref != 'refs/heads/master' }}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
name: CI (apple - daily)
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allows manual triggering
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
cache-mode: none
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GGML_NLOOP: 3
|
||||
GGML_N_THREADS: 1
|
||||
LLAMA_ARG_LOG_COLORS: 1
|
||||
LLAMA_ARG_LOG_PREFIX: 1
|
||||
LLAMA_ARG_LOG_TIMESTAMPS: 1
|
||||
|
||||
jobs:
|
||||
macos-latest-ios-xcode:
|
||||
runs-on: macos-latest
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: ggml-org/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
- name: xcodebuild for swift package
|
||||
id: xcodebuild
|
||||
run: |
|
||||
./build-xcframework.sh
|
||||
|
||||
- name: Upload xcframework artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: llama-xcframework
|
||||
path: build-apple/llama.xcframework/
|
||||
retention-days: 1
|
||||
|
||||
- name: Build Xcode project
|
||||
run: |
|
||||
xcodebuild -downloadPlatform iOS
|
||||
xcodebuild -project examples/llama.swiftui/llama.swiftui.xcodeproj -scheme llama.swiftui -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' FRAMEWORK_FOLDER_PATH=./build-ios build
|
||||
|
||||
macos-latest-tvos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_BUILD_COMMON=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
macos-latest-visionos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_BUILD_COMMON=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=1.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
macos-latest-swift:
|
||||
runs-on: macos-latest
|
||||
needs: macos-latest-ios-xcode
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
destination: ['generic/platform=macOS', 'generic/platform=iOS', 'generic/platform=tvOS']
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download xcframework artifact
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: llama-xcframework
|
||||
path: build-apple/llama.xcframework/
|
||||
|
||||
- name: Build llama.cpp with CMake
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu)
|
||||
@@ -107,140 +107,3 @@ jobs:
|
||||
# ref: https://github.com/ggml-org/llama.cpp/pull/19802#issuecomment-4013704023
|
||||
ctest -L main -E "test-llama-archs|test-llama-context|test-speculative-rejection-sampling" --verbose --timeout 900
|
||||
|
||||
macos-latest-ios-xcode:
|
||||
runs-on: macos-latest
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: ggml-org/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
- name: xcodebuild for swift package
|
||||
id: xcodebuild
|
||||
run: |
|
||||
./build-xcframework.sh
|
||||
|
||||
- name: Upload xcframework artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: llama-xcframework
|
||||
path: build-apple/llama.xcframework/
|
||||
retention-days: 1
|
||||
|
||||
- name: Build Xcode project
|
||||
run: |
|
||||
xcodebuild -downloadPlatform iOS
|
||||
xcodebuild -project examples/llama.swiftui/llama.swiftui.xcodeproj -scheme llama.swiftui -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' FRAMEWORK_FOLDER_PATH=./build-ios build
|
||||
|
||||
macos-latest-tvos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_BUILD_COMMON=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=tvOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
macos-latest-visionos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Build
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_BUILD_COMMON=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=visionOS \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=1.0 \
|
||||
-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
macos-latest-swift:
|
||||
runs-on: macos-latest
|
||||
needs: macos-latest-ios-xcode
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
destination: ['generic/platform=macOS', 'generic/platform=iOS', 'generic/platform=tvOS']
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
id: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download xcframework artifact
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: llama-xcframework
|
||||
path: build-apple/llama.xcframework/
|
||||
|
||||
- name: Build llama.cpp with CMake
|
||||
id: cmake_build
|
||||
run: |
|
||||
sysctl -a
|
||||
cmake -B build -G Xcode \
|
||||
-DGGML_METAL_EMBED_LIBRARY=ON \
|
||||
-DLLAMA_OPENSSL=OFF \
|
||||
-DLLAMA_BUILD_APP=OFF \
|
||||
-DLLAMA_BUILD_EXAMPLES=OFF \
|
||||
-DLLAMA_BUILD_TOOLS=OFF \
|
||||
-DLLAMA_BUILD_TESTS=OFF \
|
||||
-DLLAMA_BUILD_SERVER=OFF \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
cmake --build build --config Release -j $(sysctl -n hw.logicalcpu)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: "PR video demo label"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
cache-mode: none
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
video-demo-label:
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Add or remove the video demo label
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
# passed through env, so the body is only matched, never run, and is safe to read from a fork
|
||||
BODY: ${{ github.event.pull_request.body }}
|
||||
HAS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'video demo') }}
|
||||
run: |
|
||||
# sections written by demo:pr (see tools/ui/demo/cli.ts): the managed comment
|
||||
# markers, or a preview linking to a demo video uploaded for this PR
|
||||
has_demo=false
|
||||
if { grep -qF '<!-- demo:start -->' <<< "$BODY" && grep -qF '<!-- demo:end -->' <<< "$BODY"; } ||
|
||||
grep -qE "/resolve/videos/pr/$PR_NUMBER/[0-9a-f]{10}(-[0-9]+)?\.mp4" <<< "$BODY"; then
|
||||
has_demo=true
|
||||
fi
|
||||
|
||||
if [ "$has_demo" = true ] && [ "$HAS_LABEL" = false ]; then
|
||||
gh pr edit "$PR_URL" --add-label 'video demo'
|
||||
elif [ "$has_demo" = false ] && [ "$HAS_LABEL" = true ]; then
|
||||
gh pr edit "$PR_URL" --remove-label 'video demo'
|
||||
fi
|
||||
@@ -150,6 +150,7 @@ option(LLAMA_LLGUIDANCE "llama-common: include LLGuidance library for structured
|
||||
# Required for relocatable CMake package
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/build-info.cmake)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/common.cmake)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/win-app.cmake)
|
||||
|
||||
if (NOT DEFINED LLAMA_BUILD_NUMBER)
|
||||
set(LLAMA_BUILD_NUMBER ${BUILD_NUMBER})
|
||||
@@ -269,6 +270,8 @@ if (LLAMA_BUILD_MTMD AND NOT (LLAMA_BUILD_COMMON AND LLAMA_BUILD_TOOLS))
|
||||
add_subdirectory(tools/mtmd)
|
||||
endif()
|
||||
|
||||
llama_apply_app_manifests()
|
||||
|
||||
#
|
||||
# install
|
||||
#
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[](https://github.com/ggml-org/llama.cpp/actions/workflows/docker.yml)
|
||||
[](https://github.com/ggml-org/llama.cpp/actions/workflows/winget.yml)
|
||||
|
||||
[ggml](https://github.com/ggml-org/ggml) / [ops](https://github.com/ggml-org/llama.cpp/blob/master/docs/ops.md) / [maintainer PRs](https://github.com/ggml-org/llama.cpp/issues?q=is%3Apr%20is%3Aopen%20draft%3AFalse%20(author%3Asanmai%20OR%20author%3Argerganov%20OR%20author%3AKitaitiMakoto%20OR%20author%3Adanbev%20OR%20author%3Aaldehir%20OR%20author%3Amax-krasnyansky%20OR%20author%3ACISC%20OR%20author%3Aggerganov%20OR%20author%3Aam17an%20OR%20author%3Ajhen0409%20OR%20author%3Abartowski1182%20OR%20author%3Anikwen%20OR%20author%3Ahipudding%20OR%20author%3Aravi9%20OR%20author%3AServeurpersoCom%20OR%20author%3Athelittlefireman%20OR%20author%3Apwilkin%20OR%20author%3Areeselevine%20OR%20author%3Angxson%20OR%20author%3Ajeffbolznv%20OR%20author%3Amarty1885%20OR%20author%3A0cc4m%20OR%20author%3ATitaniumtown%20OR%20author%3Aangt%20OR%20author%3AIMbackK%20OR%20author%3Aarthw%20OR%20author%3AJohannesGaessler%20OR%20author%3AORippler%20OR%20author%3Aruixiang63%20OR%20author%3Axctan%20OR%20author%3AmasterFoad%20OR%20author%3Aallozaur%20OR%20author%3AAndreas-Krebbel%20OR%20author%3Ayomaytk%20OR%20author%3Aaendk%20OR%20author%3Awine99%20OR%20author%3Agaugarg-nv%20OR%20author%3Ataronaeo%20OR%20author%3Aforforever73%20OR%20author%3Achaxu01%20OR%20author%3Alhez%20OR%20author%3AAlekseiNikiforovIBM%20OR%20author%3Anetrunnereve%20OR%20author%3Afairydreaming%20OR%20author%3Aynankani)%20sort%3Aupdated-desc) / [dev stats](https://github.com/ggml-org/llama.cpp-dev) / [lib llama API](https://github.com/ggml-org/llama.cpp/issues/9289) / [llama-server REST API](https://github.com/ggml-org/llama.cpp/issues/9291)
|
||||
[ggml](https://github.com/ggml-org/ggml) / [ops](https://github.com/ggml-org/llama.cpp/blob/master/docs/ops.md) / [maintainer PRs](https://github.com/ggml-org/llama.cpp/pulls?q=is%3Apr%20is%3Aopen%20draft%3AFalse%20(author%3Asanmai%20OR%20author%3Argerganov%20OR%20author%3AKitaitiMakoto%20OR%20author%3Adanbev%20OR%20author%3Aaldehir%20OR%20author%3Amax-krasnyansky%20OR%20author%3ACISC%20OR%20author%3Aggerganov%20OR%20author%3Aam17an%20OR%20author%3Ajhen0409%20OR%20author%3Abartowski1182%20OR%20author%3Anikwen%20OR%20author%3Ahipudding%20OR%20author%3Aravi9%20OR%20author%3AServeurpersoCom%20OR%20author%3Athelittlefireman%20OR%20author%3Apwilkin%20OR%20author%3Areeselevine%20OR%20author%3Angxson%20OR%20author%3Ajeffbolznv%20OR%20author%3Amarty1885%20OR%20author%3A0cc4m%20OR%20author%3ATitaniumtown%20OR%20author%3Aangt%20OR%20author%3AIMbackK%20OR%20author%3Aarthw%20OR%20author%3AJohannesGaessler%20OR%20author%3AORippler%20OR%20author%3Aruixiang63%20OR%20author%3Axctan%20OR%20author%3AmasterFoad%20OR%20author%3Aallozaur%20OR%20author%3AAndreas-Krebbel%20OR%20author%3Ayomaytk%20OR%20author%3Aaendk%20OR%20author%3Awine99%20OR%20author%3Agaugarg-nv%20OR%20author%3Ataronaeo%20OR%20author%3Aforforever73%20OR%20author%3Achaxu01%20OR%20author%3Alhez%20OR%20author%3AAlekseiNikiforovIBM%20OR%20author%3Anetrunnereve%20OR%20author%3Afairydreaming%20OR%20author%3Aynankani)%20sort%3Aupdated-desc) / [dev stats](https://github.com/ggml-org/llama.cpp-dev) / [lib llama API](https://github.com/ggml-org/llama.cpp/issues/9289) / [llama-server REST API](https://github.com/ggml-org/llama.cpp/issues/9291)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -79,7 +79,7 @@ The `llama.cpp` project is build on top of the [ggml](https://github.com/ggml-or
|
||||
| --- | --- |
|
||||
| [BLAS](docs/build.md#blas-build) | All |
|
||||
| [BLIS](docs/backend/BLIS.md) | All |
|
||||
| [CANN](docs/build.md#cann) | Ascend NPU |
|
||||
| [CANN](docs/build.md#cann) | Huawei Ascend NPU |
|
||||
| [CUDA](docs/build.md#cuda) | Nvidia GPU |
|
||||
| [HIP](docs/build.md#hip) | AMD GPU |
|
||||
| [Hexagon](docs/backend/snapdragon/README.md) | Snapdragon |
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
set(LLAMA_WIN_APP_MANIFEST "${CMAKE_CURRENT_LIST_DIR}/../common/win-app.manifest")
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||
set(LLAMA_WIN_APP_MSVC_ABI TRUE)
|
||||
else()
|
||||
set(LLAMA_WIN_APP_MSVC_ABI FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT LLAMA_WIN_APP_MSVC_ABI)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.21)
|
||||
message(FATAL_ERROR "CMake 3.21 or newer is required to build for Windows with the GNU ABI")
|
||||
endif()
|
||||
enable_language(RC)
|
||||
set(LLAMA_WIN_APP_MANIFEST_RC "${CMAKE_CURRENT_BINARY_DIR}/win-app-manifest.rc")
|
||||
configure_file("${CMAKE_CURRENT_LIST_DIR}/win-app.rc.in" "${LLAMA_WIN_APP_MANIFEST_RC}" @ONLY)
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${LLAMA_WIN_APP_MANIFEST}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(llama_add_app_manifest target)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
get_target_property(_aliased ${target} ALIASED_TARGET)
|
||||
if(_aliased)
|
||||
return()
|
||||
endif()
|
||||
get_target_property(_type ${target} TYPE)
|
||||
if(NOT _type STREQUAL "EXECUTABLE")
|
||||
return()
|
||||
endif()
|
||||
if(LLAMA_WIN_APP_MSVC_ABI)
|
||||
target_sources(${target} PRIVATE "${LLAMA_WIN_APP_MANIFEST}")
|
||||
else()
|
||||
if(NOT TARGET llama-win-app-manifest-res)
|
||||
add_library(llama-win-app-manifest-res OBJECT ${LLAMA_WIN_APP_MANIFEST_RC})
|
||||
set_source_files_properties(${LLAMA_WIN_APP_MANIFEST_RC} PROPERTIES OBJECT_DEPENDS "${LLAMA_WIN_APP_MANIFEST}")
|
||||
endif()
|
||||
target_link_libraries(${target} PRIVATE "$<TARGET_OBJECTS:llama-win-app-manifest-res>")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(llama_apply_app_manifests_dir dir)
|
||||
get_property(_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
|
||||
foreach(_target IN LISTS _targets)
|
||||
llama_add_app_manifest(${_target})
|
||||
endforeach()
|
||||
get_property(_subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
|
||||
foreach(_subdir IN LISTS _subdirs)
|
||||
llama_apply_app_manifests_dir("${_subdir}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(llama_apply_app_manifests)
|
||||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
get_property(_subdirs DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY SUBDIRECTORIES)
|
||||
foreach(_dir IN LISTS _subdirs)
|
||||
get_filename_component(_name "${_dir}" NAME)
|
||||
if(_name STREQUAL "app" OR _name STREQUAL "examples" OR _name STREQUAL "pocs" OR _name STREQUAL "tests" OR _name STREQUAL "tools")
|
||||
llama_apply_app_manifests_dir("${_dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,3 @@
|
||||
// generated from cmake/win-app.rc.in - do not edit
|
||||
// RT_MANIFEST (24), ID 1: the application manifest shared by all llama.cpp executables
|
||||
1 24 "@LLAMA_WIN_APP_MANIFEST@"
|
||||
+2
-63
@@ -12,16 +12,6 @@
|
||||
#include "speculative.h"
|
||||
#include "preset.h"
|
||||
|
||||
// fix problem with std::min and std::max
|
||||
#if defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <climits>
|
||||
@@ -38,20 +28,6 @@
|
||||
#include <thread> // for hardware_concurrency
|
||||
#include <vector>
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef __linux__
|
||||
#include <linux/limits.h>
|
||||
#elif defined(_WIN32)
|
||||
# if !defined(PATH_MAX)
|
||||
# define PATH_MAX MAX_PATH
|
||||
# endif
|
||||
#elif defined(_AIX)
|
||||
#include <sys/limits.h>
|
||||
#else
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define LLAMA_MAX_URL_LENGTH 2084 // Maximum URL Length in Chrome: 2083
|
||||
|
||||
using json = common_json;
|
||||
@@ -351,7 +327,7 @@ static bool parse_bool_value(const std::string & value) {
|
||||
static std::string get_default_local_path(const std::string & url) {
|
||||
auto f = string_split<std::string>(url, '#').front();
|
||||
f = string_split<std::string>(f, '?').front();
|
||||
return fs_path_to_utf8(fs_get_cache_file(string_split<std::string>(f, '/').back()));
|
||||
return fs_get_cache_file(string_split<std::string>(f, '/').back()).string();
|
||||
}
|
||||
|
||||
static bool spec_types_is_default(const common_params & params) {
|
||||
@@ -762,7 +738,7 @@ static void common_params_apply_system_config(common_params & params, llama_exam
|
||||
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", fs_path_to_utf8(path).c_str());
|
||||
LOG_INF("using config file: %s\n", path.string().c_str());
|
||||
common_preset global;
|
||||
common_presets presets = ctx.load_from_ini(path, global);
|
||||
global.apply_to_params(params);
|
||||
@@ -1259,44 +1235,7 @@ bool common_params_to_map(int argc, char ** argv, llama_example ex, std::map<com
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
struct utf8_argv {
|
||||
std::vector<std::string> buf;
|
||||
std::vector<char*> ptrs;
|
||||
};
|
||||
|
||||
static utf8_argv make_utf8_argv() {
|
||||
utf8_argv out;
|
||||
int wargc = 0;
|
||||
LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
|
||||
if (!wargv) return out;
|
||||
|
||||
out.buf.reserve(wargc);
|
||||
for (int i = 0; i < wargc; ++i) {
|
||||
int n = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wargv[i], -1, nullptr, 0, nullptr, nullptr);
|
||||
if (n <= 0) { out.buf.emplace_back(); continue; }
|
||||
auto& s = out.buf.emplace_back();
|
||||
s.resize(static_cast<size_t>(n - 1));
|
||||
(void)WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, s.data(), n, nullptr, nullptr);
|
||||
}
|
||||
LocalFree(wargv);
|
||||
|
||||
out.ptrs.reserve(out.buf.size() + 1);
|
||||
for (auto& s : out.buf) out.ptrs.push_back(s.data());
|
||||
out.ptrs.push_back(nullptr);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool common_params_parse(int argc, char ** argv, common_params & params, llama_example ex, void(*print_usage)(int, char **)) {
|
||||
#ifdef _WIN32
|
||||
auto utf8 = make_utf8_argv();
|
||||
// repair argv only when it matches the process command line
|
||||
if (static_cast<int>(utf8.buf.size()) == argc) {
|
||||
argv = utf8.ptrs.data();
|
||||
}
|
||||
#endif
|
||||
|
||||
auto ctx_arg = common_params_parser_init(params, ex, print_usage);
|
||||
const common_params params_org = ctx_arg.params; // the example can modify the default params
|
||||
|
||||
|
||||
+18
-8
@@ -934,12 +934,6 @@ std::string wstring_to_utf8(const std::wstring & str) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns the path as a UTF-8 string, preserving its separators
|
||||
std::string fs_path_to_utf8(const std::filesystem::path & path) {
|
||||
const auto value = path.u8string();
|
||||
return std::string(value.begin(), value.end());
|
||||
}
|
||||
|
||||
void fs_write_atomic(const std::filesystem::path & path, const std::string & data) {
|
||||
// a random suffix gives every writer its own temporary file, across processes and containers
|
||||
std::random_device rd;
|
||||
@@ -963,7 +957,7 @@ void fs_write_atomic(const std::filesystem::path & path, const std::string & dat
|
||||
|
||||
if (file.fail() || ec) {
|
||||
std::filesystem::remove(path_tmp, ec);
|
||||
throw std::runtime_error("failed to write file: " + fs_path_to_utf8(path));
|
||||
throw std::runtime_error("failed to write file: " + path.string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1057,7 +1051,7 @@ std::filesystem::path fs_get_cache_file(const std::string & filename) {
|
||||
std::error_code ec;
|
||||
common_create_directories(cache_directory, ec);
|
||||
if (ec) {
|
||||
throw std::runtime_error("failed to create cache directory: " + fs_path_to_utf8(cache_directory));
|
||||
throw std::runtime_error("failed to create cache directory: " + cache_directory.string());
|
||||
}
|
||||
return cache_directory / std::filesystem::u8path(filename);
|
||||
}
|
||||
@@ -2281,6 +2275,16 @@ int32_t common_batch::add_embd(llama_embd embd, const llama_pos * pos, llama_seq
|
||||
return size() - 1;
|
||||
}
|
||||
|
||||
int32_t common_batch::add_embd_row(int32_t row, const llama_pos * pos, llama_seq_id seq_id, bool output) {
|
||||
token t = { LLAMA_TOKEN_NULL, { 0, 0, 0, 0 }, seq_id, output, { nullptr, 0, 0 }, { nullptr, 0, 0 }, {} };
|
||||
t.embd_row = row;
|
||||
for (int32_t j = 0; j < n_pos; ++j) {
|
||||
t.pos[j] = pos[j];
|
||||
}
|
||||
tokens.push_back(t);
|
||||
return size() - 1;
|
||||
}
|
||||
|
||||
llama_batch_ext * common_batch::get_sub_batch(int32_t off, int32_t n) {
|
||||
GGML_ASSERT(batch && "common_batch was not initialized with a context");
|
||||
GGML_ASSERT(off >= 0 && n >= 0 && off + n <= size());
|
||||
@@ -2301,6 +2305,12 @@ llama_batch_ext * common_batch::get_sub_batch(int32_t off, int32_t n) {
|
||||
if (t.embd.data && !llama_batch_ext_set_embd_token(res, idx, t.embd)) {
|
||||
GGML_ABORT("%s: failed to set the embedding of token %d at index %d\n", __func__, t.id, i);
|
||||
}
|
||||
} else if (t.embd_row >= 0) {
|
||||
idx = llama_batch_ext_add_embd_row(res, t.seq_id, t.embd_row);
|
||||
if (idx < 0) {
|
||||
GGML_ABORT("%s: failed to add embd row %d at index %d (error %d, n = %d)\n", __func__, t.embd_row, i, idx, n);
|
||||
}
|
||||
llama_batch_ext_set_pos(res, idx, t.pos.data());
|
||||
} else {
|
||||
idx = llama_batch_ext_add_embd(res, t.seq_id, t.embd);
|
||||
if (idx < 0) {
|
||||
|
||||
+5
-3
@@ -910,9 +910,6 @@ std::wstring utf8_to_wstring(const std::string & str);
|
||||
std::string wstring_to_utf8(const std::wstring & str);
|
||||
#endif
|
||||
|
||||
// returns the path as a UTF-8 string, preserving its separators
|
||||
std::string fs_path_to_utf8(const std::filesystem::path & path);
|
||||
|
||||
//
|
||||
// Environment utils
|
||||
//
|
||||
@@ -1099,6 +1096,7 @@ struct common_batch {
|
||||
llama_embd state; // non-owning view of the data passed to set_embd_state(), data == NULL if none
|
||||
std::vector<llama_seq_id> seq_ids_extra; // see add_seq()
|
||||
int32_t decision_order = 0; // see llama_batch_ext_set_decision_order()
|
||||
int32_t embd_row = -1; // see add_embd_row(), -1 if none
|
||||
};
|
||||
|
||||
std::vector<token> tokens; // mirror of the entries, tokens[i] describes batch index i
|
||||
@@ -1141,6 +1139,10 @@ struct common_batch {
|
||||
// pos points to n_pos positions
|
||||
int32_t add_embd(llama_embd embd, const llama_pos * pos, llama_seq_id seq_id, bool output);
|
||||
|
||||
// add an entry whose embedding is row `row` of the layer input device buffer of the context's ctx_other
|
||||
// pos points to n_pos positions
|
||||
int32_t add_embd_row(int32_t row, const llama_pos * pos, llama_seq_id seq_id, bool output);
|
||||
|
||||
int32_t size() const { return (int32_t) tokens.size(); }
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -748,7 +748,8 @@ static hf_cache::hf_file find_best_model(const hf_cache::hf_files & files,
|
||||
}
|
||||
|
||||
for (const auto & t : tags) {
|
||||
std::regex pattern(t + "[.-]", std::regex::icase);
|
||||
// match the tag as a full token, so ":F16" does not match a BF16 file
|
||||
std::regex pattern("(^|[-./_])" + t + "[.-]", std::regex::icase);
|
||||
for (const auto & f : files) {
|
||||
if (gguf_filename_is_model(f.path) &&
|
||||
std::regex_search(f.path, pattern)) {
|
||||
@@ -1005,7 +1006,7 @@ std::string common_docker_resolve_model(const std::string & docker) {
|
||||
std::string model_filename = repo;
|
||||
std::replace(model_filename.begin(), model_filename.end(), '/', '_');
|
||||
model_filename += "_" + tag + ".gguf";
|
||||
std::string local_path = fs_path_to_utf8(fs_get_cache_file(model_filename));
|
||||
std::string local_path = fs_get_cache_file(model_filename).string();
|
||||
|
||||
const std::string blob_url = url_prefix + "/blobs/" + gguf_digest;
|
||||
common_download_opts opts;
|
||||
|
||||
+6
-6
@@ -63,7 +63,7 @@ static fs::path get_cache_directory() {
|
||||
}
|
||||
|
||||
std::string get_cache_path() {
|
||||
return fs_path_to_utf8(get_cache_directory());
|
||||
return get_cache_directory().string();
|
||||
}
|
||||
|
||||
static std::string folder_name_to_repo(const std::string & folder) {
|
||||
@@ -340,11 +340,11 @@ hf_files get_repo_files(const std::string & repo_id,
|
||||
file.url = endpoint + repo_id + "/resolve/" + commit + "/" + file.path;
|
||||
|
||||
fs::path final_path = commit_path / subpath;
|
||||
file.final_path = fs_path_to_utf8(final_path);
|
||||
file.final_path = final_path.string();
|
||||
|
||||
if (!file.oid.empty() && !fs::exists(final_path)) {
|
||||
fs::path local_path = blobs_path / file.oid;
|
||||
file.local_path = fs_path_to_utf8(local_path);
|
||||
file.local_path = local_path.string();
|
||||
} else {
|
||||
file.local_path = file.final_path;
|
||||
}
|
||||
@@ -411,7 +411,7 @@ hf_files get_cached_files(const std::string & repo_id) {
|
||||
if (!fs::exists(snapshots_path)) {
|
||||
continue;
|
||||
}
|
||||
std::string _repo_id = folder_name_to_repo(fs_path_to_utf8(repo.path().filename()));
|
||||
std::string _repo_id = folder_name_to_repo(repo.path().filename().string());
|
||||
|
||||
if (!is_valid_repo_id(_repo_id)) {
|
||||
continue;
|
||||
@@ -436,7 +436,7 @@ hf_files get_cached_files(const std::string & repo_id) {
|
||||
file.repo_id = _repo_id;
|
||||
const auto generic_path = path.generic_u8string();
|
||||
file.path = std::string(generic_path.begin(), generic_path.end());
|
||||
file.local_path = fs_path_to_utf8(entry.path());
|
||||
file.local_path = entry.path().string();
|
||||
file.final_path = file.local_path;
|
||||
files.push_back(std::move(file));
|
||||
}
|
||||
@@ -516,7 +516,7 @@ bool remove_cached_repo(const std::string & repo_id) {
|
||||
std::error_code ec;
|
||||
auto removed = fs::remove_all(repo_path, ec);
|
||||
if (ec) {
|
||||
LOG_ERR("%s: failed to remove repo cache %s: %s\n", __func__, fs_path_to_utf8(repo_path).c_str(), ec.message().c_str());
|
||||
LOG_ERR("%s: failed to remove repo cache %s: %s\n", __func__, repo_path.string().c_str(), ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
return removed > 0;
|
||||
|
||||
@@ -307,58 +307,13 @@ value binary_expression::execute_impl(context & ctx) const {
|
||||
throw std::runtime_error("Unknown operator \"" + op.value + "\" between " + left_val->type() + " and " + right_val->type());
|
||||
}
|
||||
|
||||
static value try_builtin_func(context & ctx, const std::string & name, value & input, bool undef_on_missing = false) {
|
||||
JJ_DEBUG("Trying built-in function '%s' for type %s", name.c_str(), input->type().c_str());
|
||||
if (ctx.is_get_stats) {
|
||||
value_t::stats_t::mark_used(input);
|
||||
input->stats.ops.insert(name);
|
||||
}
|
||||
auto builtins = input->get_builtins();
|
||||
auto it = builtins.find(name);
|
||||
if (it != builtins.end()) {
|
||||
JJ_DEBUG("Binding built-in '%s'", name.c_str());
|
||||
return mk_val<value_func>(name, it->second, input);
|
||||
}
|
||||
if (undef_on_missing) {
|
||||
return mk_val<value_undefined>(name);
|
||||
}
|
||||
throw std::runtime_error("Unknown (built-in) filter '" + name + "' for type " + input->type());
|
||||
}
|
||||
|
||||
static value apply_filter(context & ctx, const statement_ptr & filter, value input) {
|
||||
JJ_DEBUG("Applying filter to %s", input->type().c_str());
|
||||
|
||||
auto set_filter_alias = [](auto & filter_id) {
|
||||
if (filter_id == "count") {
|
||||
filter_id = "length";
|
||||
} else if (filter_id == "d") {
|
||||
filter_id = "default";
|
||||
} else if (filter_id == "e") {
|
||||
filter_id = "escape";
|
||||
} else if (filter_id == "trim") {
|
||||
filter_id = "strip";
|
||||
}
|
||||
};
|
||||
|
||||
if (is_stmt<identifier>(filter)) {
|
||||
auto filter_id = cast_stmt<identifier>(filter)->val;
|
||||
|
||||
set_filter_alias(filter_id);
|
||||
JJ_DEBUG("Applying filter '%s' to %s", filter_id.c_str(), input->type().c_str());
|
||||
// TODO: Refactor filters so this coercion can be done automatically
|
||||
if (!input->is_undefined() && !is_val<value_string>(input) && (
|
||||
filter_id == "capitalize" ||
|
||||
filter_id == "lower" ||
|
||||
filter_id == "replace" ||
|
||||
filter_id == "strip" ||
|
||||
filter_id == "title" ||
|
||||
filter_id == "upper" ||
|
||||
filter_id == "wordcount"
|
||||
)) {
|
||||
JJ_DEBUG("Coercing %s to String for '%s' filter", input->type().c_str(), filter_id.c_str());
|
||||
input = mk_val<value_string>(input->as_string());
|
||||
}
|
||||
return try_builtin_func(ctx, filter_id, input)->invoke(func_args(ctx));
|
||||
return apply_filter_by_name(ctx, filter_id, input, func_args(ctx));
|
||||
|
||||
} else if (is_stmt<call_expression>(filter)) {
|
||||
auto call = cast_stmt<call_expression>(filter);
|
||||
@@ -367,14 +322,13 @@ static value apply_filter(context & ctx, const statement_ptr & filter, value inp
|
||||
}
|
||||
auto filter_id = cast_stmt<identifier>(call->callee)->val;
|
||||
|
||||
set_filter_alias(filter_id);
|
||||
JJ_DEBUG("Applying filter '%s' with arguments to %s", filter_id.c_str(), input->type().c_str());
|
||||
func_args args(ctx);
|
||||
for (const auto & arg_expr : call->args) {
|
||||
args.push_back(arg_expr->execute(ctx));
|
||||
}
|
||||
|
||||
return try_builtin_func(ctx, filter_id, input)->invoke(args);
|
||||
return apply_filter_by_name(ctx, filter_id, input, args);
|
||||
|
||||
} else {
|
||||
throw std::runtime_error("Invalid filter expression");
|
||||
|
||||
+114
-10
@@ -72,6 +72,17 @@ const std::vector<value> & func_args::get_args() const {
|
||||
return args;
|
||||
}
|
||||
|
||||
void func_args::ensure_kwargs(std::initializer_list<const char *> names) const {
|
||||
for (const auto & arg : args) {
|
||||
if (is_val<value_kwarg>(arg)) {
|
||||
auto * kwarg = cast_val<value_kwarg>(arg);
|
||||
if (std::find(names.begin(), names.end(), kwarg->key) == names.end()) {
|
||||
throw raised_exception("Function '" + func_name + "' got an unexpected keyword argument '" + kwarg->key + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that mimics Python's array slicing.
|
||||
*/
|
||||
@@ -134,6 +145,14 @@ static value empty_value_fn(const func_args &) {
|
||||
return mk_val<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static value empty_value_no_args_fn(const func_args & args) {
|
||||
args.ensure_kwargs();
|
||||
args.ensure_count(1, 1);
|
||||
return empty_value_fn<T>(args);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static value test_type_fn(const func_args & args) {
|
||||
args.ensure_count(1);
|
||||
@@ -594,6 +613,52 @@ const func_builtins & global_builtins() {
|
||||
return builtins;
|
||||
}
|
||||
|
||||
value try_builtin_func(context & ctx, const std::string & name, value & input, bool undef_on_missing) {
|
||||
JJ_DEBUG("Trying built-in function '%s' for type %s", name.c_str(), input->type().c_str());
|
||||
if (ctx.is_get_stats) {
|
||||
value_t::stats_t::mark_used(input);
|
||||
input->stats.ops.insert(name);
|
||||
}
|
||||
auto builtins = input->get_builtins();
|
||||
auto it = builtins.find(name);
|
||||
if (it != builtins.end()) {
|
||||
JJ_DEBUG("Binding built-in '%s'", name.c_str());
|
||||
return mk_val<value_func>(name, it->second, input);
|
||||
}
|
||||
if (undef_on_missing) {
|
||||
return mk_val<value_undefined>(name);
|
||||
}
|
||||
throw std::runtime_error("Unknown (built-in) filter '" + name + "' for type " + input->type());
|
||||
}
|
||||
|
||||
value apply_filter_by_name(context & ctx, std::string filter_id, value input, const func_args & args) {
|
||||
if (filter_id == "count") {
|
||||
filter_id = "length";
|
||||
} else if (filter_id == "d") {
|
||||
filter_id = "default";
|
||||
} else if (filter_id == "e") {
|
||||
filter_id = "escape";
|
||||
} else if (filter_id == "trim") {
|
||||
filter_id = "strip";
|
||||
}
|
||||
|
||||
// TODO: Refactor filters so this coercion can be done automatically
|
||||
if (!input->is_undefined() && !is_val<value_string>(input) && (
|
||||
filter_id == "capitalize" ||
|
||||
filter_id == "lower" ||
|
||||
filter_id == "replace" ||
|
||||
filter_id == "strip" ||
|
||||
filter_id == "title" ||
|
||||
filter_id == "upper" ||
|
||||
filter_id == "wordcount"
|
||||
)) {
|
||||
JJ_DEBUG("Coercing %s to String for '%s' filter", input->type().c_str(), filter_id.c_str());
|
||||
input = mk_val<value_string>(input->as_string());
|
||||
}
|
||||
|
||||
return try_builtin_func(ctx, filter_id, input)->invoke(args);
|
||||
}
|
||||
|
||||
|
||||
const func_builtins & value_int_t::get_builtins() const {
|
||||
static const func_builtins builtins = {
|
||||
@@ -667,16 +732,22 @@ const func_builtins & value_string_t::get_builtins() const {
|
||||
static const func_builtins builtins = {
|
||||
{"default", default_value},
|
||||
{"upper", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs();
|
||||
args.ensure_count(1, 1);
|
||||
args.ensure_vals<value_string>();
|
||||
jinja::string str = args.get_pos(0)->as_string().uppercase();
|
||||
return mk_val<value_string>(str);
|
||||
}},
|
||||
{"lower", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs();
|
||||
args.ensure_count(1, 1);
|
||||
args.ensure_vals<value_string>();
|
||||
jinja::string str = args.get_pos(0)->as_string().lowercase();
|
||||
return mk_val<value_string>(str);
|
||||
}},
|
||||
{"strip", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs({"chars"});
|
||||
args.ensure_count(1, 2);
|
||||
value val_input = args.get_pos(0);
|
||||
if (!is_val<value_string>(val_input)) {
|
||||
throw raised_exception("strip() first argument must be a string");
|
||||
@@ -707,11 +778,15 @@ const func_builtins & value_string_t::get_builtins() const {
|
||||
}
|
||||
}},
|
||||
{"title", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs();
|
||||
args.ensure_count(1, 1);
|
||||
args.ensure_vals<value_string>();
|
||||
jinja::string str = args.get_pos(0)->as_string().titlecase();
|
||||
return mk_val<value_string>(str);
|
||||
}},
|
||||
{"capitalize", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs();
|
||||
args.ensure_count(1, 1);
|
||||
args.ensure_vals<value_string>();
|
||||
jinja::string str = args.get_pos(0)->as_string().capitalize();
|
||||
return mk_val<value_string>(str);
|
||||
@@ -1013,6 +1088,10 @@ const func_builtins & value_bool_t::get_builtins() const {
|
||||
return builtins;
|
||||
}
|
||||
|
||||
[[noreturn]] static value array_sum_not_implemented(const func_args &) {
|
||||
throw not_implemented_exception("Array sum builtin not implemented");
|
||||
}
|
||||
|
||||
const func_builtins & value_array_t::get_builtins() const {
|
||||
static const func_builtins builtins = {
|
||||
{"default", default_value},
|
||||
@@ -1079,7 +1158,9 @@ const func_builtins & value_array_t::get_builtins() const {
|
||||
{"select", select_or_reject<false, false>},
|
||||
{"rejectattr", select_or_reject<true, true>},
|
||||
{"reject", select_or_reject<true, false>},
|
||||
{"sum", array_sum_not_implemented},
|
||||
{"join", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs({"d", "attribute"});
|
||||
args.ensure_count(1, 3);
|
||||
if (!is_val<value_array>(args.get_pos(0))) {
|
||||
throw raised_exception("join() first argument must be an array");
|
||||
@@ -1117,13 +1198,28 @@ const func_builtins & value_array_t::get_builtins() const {
|
||||
{"tojson", tojson},
|
||||
{"map", [](const func_args & args) -> value {
|
||||
args.ensure_count(2);
|
||||
if (!is_val<value_array>(args.get_pos(0))) {
|
||||
value val = args.get_pos(0);
|
||||
if (!is_val<value_array>(val)) {
|
||||
throw raised_exception("map: first argument must be an array");
|
||||
}
|
||||
if (!is_val<value_kwarg>(args.get_args().at(1))) {
|
||||
throw not_implemented_exception("map: filter-mapping not implemented");
|
||||
value filter = args.get_pos(1);
|
||||
if (!is_val<value_string>(filter)) {
|
||||
throw raised_exception("map: filter name must be a string");
|
||||
}
|
||||
auto out = mk_val<value_array>();
|
||||
func_args filter_args(args.ctx);
|
||||
const auto & all_args = args.get_args();
|
||||
for (size_t i = 2; i < all_args.size(); ++i) {
|
||||
filter_args.push_back(all_args[i]);
|
||||
}
|
||||
const std::string filter_id = filter->as_string().str();
|
||||
auto arr = val->as_array();
|
||||
for (const auto & item : arr) {
|
||||
out->push_back(apply_filter_by_name(args.ctx, filter_id, item, filter_args));
|
||||
}
|
||||
return is_val<value_tuple>(val) ? mk_val<value_tuple>(out->as_array()) : out;
|
||||
}
|
||||
value val = args.get_pos(0);
|
||||
value attribute = args.get_kwarg_or_pos("attribute", 1);
|
||||
value default_val = args.get_kwarg("default", mk_val<value_undefined>());
|
||||
auto out = mk_val<value_array>();
|
||||
@@ -1132,7 +1228,7 @@ const func_builtins & value_array_t::get_builtins() const {
|
||||
value attr_val = get_attribute(item, attribute, default_val);
|
||||
out->push_back(attr_val);
|
||||
}
|
||||
return is_val<value_tuple>(val) ? mk_val<value_tuple>(std::move(out->as_array())) : out;
|
||||
return is_val<value_tuple>(val) ? mk_val<value_tuple>(out->as_array()) : out;
|
||||
}},
|
||||
{"append", [](const func_args & args) -> value {
|
||||
args.ensure_count(2);
|
||||
@@ -1405,14 +1501,18 @@ const func_builtins & value_none_t::get_builtins() const {
|
||||
const func_builtins & value_undefined_t::get_builtins() const {
|
||||
static const func_builtins builtins = {
|
||||
{"default", default_value},
|
||||
{"capitalize", empty_value_fn<value_string>},
|
||||
{"capitalize", empty_value_no_args_fn<value_string>},
|
||||
{"first", empty_value_fn<value_undefined>},
|
||||
{"items", empty_value_fn<value_array>},
|
||||
{"join", empty_value_fn<value_string>},
|
||||
{"join", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs({"d", "attribute"});
|
||||
args.ensure_count(1, 3);
|
||||
return empty_value_fn<value_string>(args);
|
||||
}},
|
||||
{"last", empty_value_fn<value_undefined>},
|
||||
{"length", empty_value_fn<value_int>},
|
||||
{"list", empty_value_fn<value_array>},
|
||||
{"lower", empty_value_fn<value_string>},
|
||||
{"lower", empty_value_no_args_fn<value_string>},
|
||||
{"map", empty_value_fn<value_array>},
|
||||
{"max", empty_value_fn<value_undefined>},
|
||||
{"min", empty_value_fn<value_undefined>},
|
||||
@@ -1425,12 +1525,16 @@ const func_builtins & value_undefined_t::get_builtins() const {
|
||||
{"selectattr", empty_value_fn<value_array>},
|
||||
{"sort", empty_value_fn<value_array>},
|
||||
{"string", empty_value_fn<value_string>},
|
||||
{"strip", empty_value_fn<value_string>},
|
||||
{"strip", [](const func_args & args) -> value {
|
||||
args.ensure_kwargs({"chars"});
|
||||
args.ensure_count(1, 2);
|
||||
return empty_value_fn<value_string>(args);
|
||||
}},
|
||||
{"sum", empty_value_fn<value_int>},
|
||||
{"title", empty_value_fn<value_string>},
|
||||
{"title", empty_value_no_args_fn<value_string>},
|
||||
{"truncate", empty_value_fn<value_string>},
|
||||
{"unique", empty_value_fn<value_array>},
|
||||
{"upper", empty_value_fn<value_string>},
|
||||
{"upper", empty_value_no_args_fn<value_string>},
|
||||
{"wordcount", empty_value_fn<value_int>},
|
||||
};
|
||||
return builtins;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -176,6 +177,8 @@ protected:
|
||||
//
|
||||
|
||||
const func_builtins & global_builtins();
|
||||
value try_builtin_func(context & ctx, const std::string & name, value & input, bool undef_on_missing = false);
|
||||
value apply_filter_by_name(context & ctx, std::string filter_id, value input, const func_args & args);
|
||||
|
||||
std::string value_to_json(const value & val, int indent = -1, const std::string_view item_sep = ", ", const std::string_view key_sep = ": ");
|
||||
|
||||
@@ -658,6 +661,7 @@ public:
|
||||
size_t count() const { return args.size(); }
|
||||
void push_back(const value & val);
|
||||
void push_front(const value & val);
|
||||
void ensure_kwargs(std::initializer_list<const char *> names = {}) const;
|
||||
void ensure_count(size_t min, size_t max = 999) const {
|
||||
size_t n = args.size();
|
||||
if (n < min || n > max) {
|
||||
|
||||
@@ -71,6 +71,7 @@ common_chat_params common_chat_params_init_gpt_oss(const common_chat_template &
|
||||
auto content = p.rule("message-content", p.until("<|end|>"));
|
||||
auto channel = p.literal("<|channel|>") + (p.literal("commentary") | p.literal("analysis"));
|
||||
auto constrain_type = p.chars("[A-Za-z0-9_-]", 1, -1);
|
||||
auto constraint = p.optional(p.space() + p.optional(p.literal("<|constrain|>")) + constrain_type);
|
||||
|
||||
// Occasionally, gpt-oss-20b will prefix channels with this commentary
|
||||
auto stray_commentary = p.optional(p.literal("<|channel|>commentary") + p.optional(p.literal(" to=assistant")));
|
||||
@@ -84,7 +85,7 @@ common_chat_params common_chat_params_init_gpt_oss(const common_chat_template &
|
||||
|
||||
auto analysis = p.ref("analysis");
|
||||
auto preamble = p.rule("preamble", p.literal("<|channel|>commentary<|message|>") + p.content(content) + end);
|
||||
auto final_msg = p.rule("final", stray_commentary + p.literal("<|channel|>final<|message|>") + p.content(content));
|
||||
auto final_msg = p.rule("final", stray_commentary + p.literal("<|channel|>final") + constraint + p.literal("<|message|>") + p.content(content));
|
||||
|
||||
// Consume any unsolicited tool calls, e.g. builtin functions
|
||||
auto unsolicited = p.rule("unsolicited", p.atomic(p.optional(channel) + p.literal(" to=") + content + end));
|
||||
@@ -92,7 +93,6 @@ common_chat_params common_chat_params_init_gpt_oss(const common_chat_template &
|
||||
auto any = p.rule("any", preamble | analysis);
|
||||
|
||||
if (has_response_format) {
|
||||
auto constraint = p.optional(p.space() + p.optional(p.literal("<|constrain|>")) + constrain_type);
|
||||
auto response_format = p.rule("response-format",
|
||||
p.literal("<|channel|>final") + constraint + p.literal("<|message|>") +
|
||||
p.content(p.schema(p.json(), "response-format-schema", inputs.json_schema)));
|
||||
@@ -109,7 +109,6 @@ common_chat_params common_chat_params_init_gpt_oss(const common_chat_template &
|
||||
const auto params = common_chat_tool_parameters(function);
|
||||
|
||||
auto func_name = p.literal(" to=functions.") + p.tool_name(p.literal(name));
|
||||
auto constraint = p.optional(p.space() + p.optional(p.literal("<|constrain|>")) + constrain_type);
|
||||
auto args = p.tool_args(p.schema(p.json(), "tool-" + std::to_string(tool_index) + "-schema", params));
|
||||
|
||||
// recipient in role header
|
||||
|
||||
@@ -74,16 +74,24 @@ common_chat_params common_chat_params_init_qwen3_coder(const common_chat_templat
|
||||
data.parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
|
||||
auto generation_prompt = p.literal(GEN_PREFIX);
|
||||
|
||||
// The template strips the newline before </think>, so leave it out of the reasoning
|
||||
auto think_body = p.until_one_of({ "\n</think>", "</think>", "<tool_call>" });
|
||||
auto think_close = p.literal("\n</think>") | p.literal("</think>") | p.peek(p.literal("<tool_call>"));
|
||||
|
||||
auto reasoning_extracted = p.optional("<think>" + p.space() + p.reasoning(think_body) + think_close);
|
||||
auto reasoning_skipped = p.optional("<think>" + p.space() + think_body + think_close);
|
||||
|
||||
auto reasoning = p.eps();
|
||||
if (supports_reasoning && extract_reasoning) {
|
||||
// The template strips the newline before </think>, so leave it out of the reasoning
|
||||
reasoning = p.optional("<think>" + p.space() +
|
||||
p.reasoning(p.until_one_of({ "\n</think>", "</think>", "<tool_call>" })) +
|
||||
(p.literal("\n</think>") | p.literal("</think>") | p.peek(p.literal("<tool_call>"))));
|
||||
reasoning = reasoning_extracted;
|
||||
}
|
||||
|
||||
// Response format parser
|
||||
if (has_response_format) {
|
||||
// The answer must be bare JSON, so the think block is consumed even when it is not extracted
|
||||
if (supports_reasoning && !extract_reasoning) {
|
||||
reasoning = reasoning_skipped;
|
||||
}
|
||||
return generation_prompt + (reasoning << p.content(p.schema(p.json(), "response-format", inputs.json_schema)));
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -171,12 +171,12 @@ static std::map<std::string, std::map<std::string, std::string>> parse_ini_from_
|
||||
std::map<std::string, std::map<std::string, std::string>> parsed;
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
throw std::runtime_error("preset file does not exist: " + fs_path_to_utf8(path));
|
||||
throw std::runtime_error("preset file does not exist: " + path.string());
|
||||
}
|
||||
|
||||
std::ifstream file(path);
|
||||
if (!file.good()) {
|
||||
throw std::runtime_error("failed to open server preset file: " + fs_path_to_utf8(path));
|
||||
throw std::runtime_error("failed to open server preset file: " + path.string());
|
||||
}
|
||||
|
||||
std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
@@ -225,7 +225,7 @@ static std::map<std::string, std::map<std::string, std::string>> parse_ini_from_
|
||||
common_peg_parse_context ctx(contents);
|
||||
const auto result = parser.parse(ctx);
|
||||
if (!result.success()) {
|
||||
throw std::runtime_error("failed to parse server config file: " + fs_path_to_utf8(path));
|
||||
throw std::runtime_error("failed to parse server config file: " + path.string());
|
||||
}
|
||||
|
||||
std::string current_section = COMMON_PRESET_DEFAULT_NAME;
|
||||
@@ -323,7 +323,7 @@ common_presets common_preset_context::load_from_ini(const std::filesystem::path
|
||||
}
|
||||
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(), fs_path_to_utf8(path).c_str());
|
||||
LOG_WRN("ignoring option '%s' from %s: not supported by this program\n", key.c_str(), path.string().c_str());
|
||||
} else {
|
||||
throw std::runtime_error(string_format(
|
||||
"option '%s' not recognized in preset '%s'",
|
||||
@@ -401,7 +401,7 @@ common_presets common_preset_context::load_from_models_dir(const std::string & m
|
||||
if (!entry.is_regular_file(ec)) {
|
||||
continue;
|
||||
}
|
||||
const std::string fname = fs_path_to_utf8(entry.path().filename());
|
||||
const std::string fname = entry.path().filename().string();
|
||||
if (!string_ends_with(fname, ".gguf")) {
|
||||
continue;
|
||||
}
|
||||
@@ -422,9 +422,9 @@ common_presets common_preset_context::load_from_models_dir(const std::string & m
|
||||
if (!path.empty()) {
|
||||
models.push_back({
|
||||
/* name */ name,
|
||||
/* path */ fs_path_to_utf8(path),
|
||||
/* path_mmproj */ fs_path_to_utf8(mmproj_file), // can be empty
|
||||
/* path_draft */ fs_path_to_utf8(draft_file) // can be empty
|
||||
/* path */ path.string(),
|
||||
/* path_mmproj */ mmproj_file.string(), // can be empty
|
||||
/* path_draft */ draft_file.string() // can be empty
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -432,13 +432,13 @@ common_presets common_preset_context::load_from_models_dir(const std::string & m
|
||||
for (const auto & entry : std::filesystem::directory_iterator(dir)) {
|
||||
std::error_code ec;
|
||||
if (entry.is_directory(ec)) {
|
||||
scan_subdir(entry.path(), fs_path_to_utf8(entry.path().filename()));
|
||||
scan_subdir(entry.path(), entry.path().filename().string());
|
||||
continue;
|
||||
}
|
||||
if (!entry.is_regular_file(ec)) {
|
||||
continue;
|
||||
}
|
||||
const std::string fname = fs_path_to_utf8(entry.path().filename());
|
||||
const std::string fname = entry.path().filename().string();
|
||||
if (!string_ends_with(fname, ".gguf")) {
|
||||
continue;
|
||||
}
|
||||
@@ -450,7 +450,7 @@ common_presets common_preset_context::load_from_models_dir(const std::string & m
|
||||
string_replace_all(name, ".gguf", "");
|
||||
models.push_back({
|
||||
/* name */ name,
|
||||
/* path */ fs_path_to_utf8(entry.path()),
|
||||
/* path */ entry.path().string(),
|
||||
/* path_mmproj */ "",
|
||||
/* path_draft */ ""
|
||||
});
|
||||
|
||||
+82
-8
@@ -1027,6 +1027,9 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
const int32_t * target_layer_ids = nullptr; // model_dft's extract layer indices
|
||||
uint32_t target_layer_ids_n = 0;
|
||||
|
||||
// inject target features from the target's device buffer instead of host copies (env LLAMA_DFLASH_DEV=0 disables)
|
||||
bool use_dev_features = false;
|
||||
|
||||
common_speculative_impl_draft_dflash(const common_params_speculative & params, uint32_t n_seq,
|
||||
common_speculative_type type = COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH)
|
||||
: common_speculative_impl(type, n_seq, params.draft.n_max)
|
||||
@@ -1141,6 +1144,23 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
llama_set_embeddings_layer_inp(ctx_tgt, (uint32_t) target_layer_ids[k], true);
|
||||
}
|
||||
|
||||
// the device buffer keeps layers in ascending id order, the fc input expects target_layer_ids order
|
||||
{
|
||||
const char * env = getenv("LLAMA_DFLASH_DEV");
|
||||
const bool ascending = std::adjacent_find(target_layer_ids, target_layer_ids + target_layer_ids_n,
|
||||
[](int32_t a, int32_t b) { return a >= b; }) == target_layer_ids + target_layer_ids_n;
|
||||
|
||||
use_dev_features = !is_lilicorr && ascending && (env == nullptr || atoi(env) != 0);
|
||||
if (use_dev_features) {
|
||||
llama_set_embeddings_layer_inp_dev(ctx_tgt, true);
|
||||
use_dev_features = llama_supports_embd_row(ctx_dft);
|
||||
if (!use_dev_features) {
|
||||
llama_set_embeddings_layer_inp_dev(ctx_tgt, false);
|
||||
}
|
||||
}
|
||||
LOG_INF("%s: DFlash target features via %s\n", __func__, use_dev_features ? "device buffer" : "host copies");
|
||||
}
|
||||
|
||||
// DFlash2 reads its selector lattice from h_nextn and never consumes raw logits.
|
||||
llama_set_embeddings_nextn(ctx_dft, true, /*masked*/ !is_dflash2 && !is_lilicorr);
|
||||
llama_set_causal_attn(ctx_dft, causal_attn); // DFlash needs non-causal attention unless the model says otherwise
|
||||
@@ -1233,6 +1253,25 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
|
||||
for (int32_t offset = 0; offset < n_rows; offset += n_ubatch) {
|
||||
const int32_t n_chunk = std::min(n_ubatch, n_rows - offset);
|
||||
|
||||
if (use_dev_features) {
|
||||
// the features are the target batch rows in the target's device buffer
|
||||
batch_inject.clear();
|
||||
for (int32_t i = 0; i < n_chunk; ++i) {
|
||||
const int32_t row = i_batch_beg[seq_id] + offset + i;
|
||||
const llama_pos p = batch_in.tokens[row].pos[0];
|
||||
const llama_pos pos_arr[4] = { p, p, p, 0 };
|
||||
batch_inject.add_embd_row(row, pos_arr, seq_id, false);
|
||||
}
|
||||
|
||||
const int32_t rc = llama_process(ctx_dft, LLAMA_PROCESS_TYPE_DECODE, batch_inject.get());
|
||||
if (rc != 0) {
|
||||
LOG_ERR("%s: llama_process(ctx_dft) failed rc=%d (n_tokens=%d, offset=%d)\n",
|
||||
__func__, rc, (int) n_chunk, (int) offset);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// gather target features per extract layer; the fused decode encodes and
|
||||
// injects them into the K/V cache at the target positions
|
||||
features_buf.resize((size_t) n_chunk * n_embd_enc);
|
||||
@@ -1574,12 +1613,12 @@ struct common_speculative_impl_draft_mtp : public common_speculative_impl {
|
||||
}
|
||||
}
|
||||
|
||||
llama_set_embeddings_nextn(ctx_tgt, true, /*masked*/ false);
|
||||
llama_set_embeddings_nextn(ctx_dft, true, /*masked*/ true);
|
||||
|
||||
is_mem_shared = llama_get_ctx_other(ctx_dft) == ctx_tgt;
|
||||
chain_heads = n_mtp_layers > 1 && !is_mem_shared;
|
||||
|
||||
llama_set_embeddings_nextn(ctx_tgt, true, /*masked*/ is_mem_shared);
|
||||
llama_set_embeddings_nextn(ctx_dft, true, /*masked*/ true);
|
||||
|
||||
if (chain_heads) {
|
||||
this->params.n_max = std::min(this->params.n_max, n_mtp_layers);
|
||||
|
||||
@@ -1733,16 +1772,28 @@ struct common_speculative_impl_draft_mtp : public common_speculative_impl {
|
||||
}
|
||||
|
||||
const int32_t n_rows = i_batch_end[seq_id] - i_batch_beg[seq_id] + 1;
|
||||
verify_h_rows[seq_id] = n_rows;
|
||||
verify_h[seq_id].resize((size_t) n_rows * n_embd);
|
||||
if (is_mem_shared && !batch_in.tokens[i_batch_end[seq_id]].output) {
|
||||
verify_h_rows[seq_id] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < n_rows; ++i) {
|
||||
const float * h = llama_get_embeddings_nextn_ith(ctx_tgt, i_batch_beg[seq_id] + i);
|
||||
const bool all_rows_output = std::all_of(
|
||||
batch_in.tokens.begin() + i_batch_beg[seq_id],
|
||||
batch_in.tokens.begin() + i_batch_end[seq_id] + 1,
|
||||
[](const common_batch::token & token) { return token.output; });
|
||||
const int32_t h_rows = is_mem_shared && !all_rows_output ? 1 : n_rows;
|
||||
const int32_t h_offset = is_mem_shared && !all_rows_output ? i_batch_end[seq_id] : i_batch_beg[seq_id];
|
||||
|
||||
verify_h_rows[seq_id] = h_rows;
|
||||
verify_h[seq_id].resize((size_t) h_rows * n_embd);
|
||||
|
||||
for (int32_t i = 0; i < h_rows; ++i) {
|
||||
const float * h = llama_get_embeddings_nextn_ith(ctx_tgt, h_offset + i);
|
||||
std::memcpy(verify_h[seq_id].data() + (size_t) i * n_embd, h, row_bytes);
|
||||
}
|
||||
|
||||
std::memcpy(pending_h[seq_id].data(),
|
||||
verify_h[seq_id].data() + (size_t) (n_rows - 1) * n_embd, row_bytes);
|
||||
verify_h[seq_id].data() + (size_t) (h_rows - 1) * n_embd, row_bytes);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1928,6 +1979,29 @@ 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 get_state(llama_seq_id seq_id, std::vector<uint8_t> & data) const override {
|
||||
if (seq_id < 0 || seq_id >= (llama_seq_id) n_seq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t row_bytes = (size_t) n_embd * sizeof(float);
|
||||
data.resize(row_bytes);
|
||||
std::memcpy(data.data(), pending_h[seq_id].data(), row_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_state(llama_seq_id seq_id, const std::vector<uint8_t> & data) override {
|
||||
if (seq_id < 0 || seq_id >= (llama_seq_id) n_seq) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t row_bytes = (size_t) n_embd * sizeof(float);
|
||||
if (data.size() != row_bytes) {
|
||||
return;
|
||||
}
|
||||
std::memcpy(pending_h[seq_id].data(), data.data(), row_bytes);
|
||||
}
|
||||
};
|
||||
|
||||
// state of self-speculation (simple implementation, not ngram-map)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -45,7 +45,9 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"ChameleonForConditionalGeneration": "chameleon",
|
||||
"ChatGLMForConditionalGeneration": "chatglm",
|
||||
"ChatGLMModel": "chatglm",
|
||||
"ChatterboxModel": "chatterbox",
|
||||
"ClefModel": "clef",
|
||||
"CloverMoeModelLM": "clover",
|
||||
"CodeShellForCausalLM": "codeshell",
|
||||
"CogVLMForCausalLM": "cogvlm",
|
||||
"Cohere2MoeForCausalLM": "command_r",
|
||||
@@ -196,6 +198,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"MambaLMHeadModel": "mamba",
|
||||
"MapleForCausalLM": "maple",
|
||||
"MellumForCausalLM": "mellum",
|
||||
"Metis16ForCausalLM": "metis",
|
||||
"MiMoV2FlashForCausalLM": "mimo",
|
||||
"MiMoV2ForCausalLM": "mimo",
|
||||
"MiniCPM3ForCausalLM": "minicpm",
|
||||
@@ -222,6 +225,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
"ModernBertForSequenceClassification": "bert",
|
||||
"ModernBertModel": "bert",
|
||||
"NanbeigeForCausalLM": "nanbeige",
|
||||
"Nemotron3_5TranscribeForConditionalGeneration": "nemotron",
|
||||
"NemotronForCausalLM": "nemotron",
|
||||
"NemotronHForCausalLM": "nemotron",
|
||||
"NemotronHPuzzleForCausalLM": "nemotron",
|
||||
@@ -327,6 +331,7 @@ TEXT_MODEL_MAP: dict[str, str] = {
|
||||
|
||||
MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"AudioFlamingo3ForConditionalGeneration": "ultravox",
|
||||
"ChatterboxModel": "chatterbox",
|
||||
"ClefModel": "clef",
|
||||
"CogVLMForCausalLM": "cogvlm",
|
||||
"Cohere2VisionForConditionalGeneration": "command_r",
|
||||
@@ -379,6 +384,7 @@ MMPROJ_MODEL_MAP: dict[str, str] = {
|
||||
"MiniCPMOcrForConditionalGeneration": "minicpm",
|
||||
"Mistral3ForConditionalGeneration": "llava",
|
||||
"Mistral3Model": "llava",
|
||||
"Nemotron3_5TranscribeForConditionalGeneration": "nemotron",
|
||||
"NemotronH_Omni_Reasoning_V3": "nemotron",
|
||||
"NemotronH_Nano_VL_V2": "nemotron",
|
||||
"MuseGlimmerForConditionalGeneration": "muse_glimmer",
|
||||
|
||||
+17
-10
@@ -225,8 +225,6 @@ class ModelBase:
|
||||
tensors: dict[str, Callable[[], Tensor]] = {}
|
||||
|
||||
if remote_hf_model_id is not None:
|
||||
is_safetensors = True
|
||||
|
||||
logger.info(f"Using remote model with HuggingFace id: {remote_hf_model_id}")
|
||||
remote_tensors = gguf.utility.SafetensorRemote.get_list_tensors_hf_model(remote_hf_model_id)
|
||||
for name, remote_tensor in remote_tensors.items():
|
||||
@@ -237,9 +235,13 @@ class ModelBase:
|
||||
|
||||
return tensors
|
||||
|
||||
prefix = "model" if not self.is_mistral_format else "consolidated"
|
||||
return self.index_local_tensors("model" if not self.is_mistral_format else "consolidated")
|
||||
|
||||
# index the local "<prefix>*.safetensors" files (or pytorch_model*.bin), through filter_tensors
|
||||
def index_local_tensors(self, prefix: str) -> dict[str, Callable[[], Tensor]]:
|
||||
tensors: dict[str, Callable[[], Tensor]] = {}
|
||||
part_names: list[str] = ModelBase.get_model_part_names(self.dir_model, prefix, ".safetensors")
|
||||
is_safetensors: bool = len(part_names) > 0 or (not self.is_mistral_format and (self.dir_model / "model.safetensors.index.json").is_file())
|
||||
is_safetensors: bool = len(part_names) > 0 or (not self.is_mistral_format and (self.dir_model / f"{prefix}.safetensors.index.json").is_file())
|
||||
if not is_safetensors:
|
||||
part_names = ModelBase.get_model_part_names(self.dir_model, "pytorch_model", ".bin")
|
||||
|
||||
@@ -247,7 +249,7 @@ class ModelBase:
|
||||
tensor_names_from_parts: set[str] = set()
|
||||
|
||||
if not self.is_mistral_format:
|
||||
index_name = "model.safetensors" if is_safetensors else "pytorch_model.bin"
|
||||
index_name = f"{prefix}.safetensors" if is_safetensors else "pytorch_model.bin"
|
||||
index_name += ".index.json"
|
||||
index_file = self.dir_model / index_name
|
||||
|
||||
@@ -1277,6 +1279,7 @@ class ModelBase:
|
||||
expert_scales: dict[tuple[int, str], list[tuple[int, float]]] = {}
|
||||
expert_input_scales: dict[tuple[int, str], list[tuple[int, float]]] = {}
|
||||
expert_shapes: dict[tuple[int, str], list[int]] = {}
|
||||
expert_names: dict[tuple[int, str], str] = {}
|
||||
n_experts = self.find_hparam(["num_local_experts", "num_experts", "n_routed_experts"], optional=True) or 0
|
||||
consumed: list[str] = []
|
||||
|
||||
@@ -1325,6 +1328,7 @@ class ModelBase:
|
||||
expert_scales[key] = []
|
||||
expert_input_scales[key] = []
|
||||
expert_shapes[key] = shape
|
||||
expert_names[key] = name
|
||||
expert_blocks[key].append((expert_id, raw.copy()))
|
||||
# Collect per-expert scale2 (scalar per expert)
|
||||
expert_scales[key].append((expert_id, float(scale2.float().sum())))
|
||||
@@ -1333,13 +1337,13 @@ class ModelBase:
|
||||
|
||||
# Flush when all experts for this (layer, proj) are collected
|
||||
if n_experts > 0 and len(expert_blocks[key]) >= n_experts:
|
||||
self._flush_nvfp4_experts(key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type)
|
||||
self._flush_nvfp4_experts(key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, expert_names, bid, proj_type)
|
||||
else:
|
||||
self._repack_nvfp4(name, weight, scale, scale2, input_scale)
|
||||
|
||||
# Flush any remaining experts (fallback if n_experts was unknown)
|
||||
for bid, proj_type in list(expert_blocks.keys()):
|
||||
self._flush_nvfp4_experts((bid, proj_type), expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type)
|
||||
self._flush_nvfp4_experts((bid, proj_type), expert_blocks, expert_scales, expert_input_scales, expert_shapes, expert_names, bid, proj_type)
|
||||
|
||||
# Remove consumed tensors so get_tensors/modify_tensors won't see them
|
||||
for name in consumed:
|
||||
@@ -1350,7 +1354,7 @@ class ModelBase:
|
||||
if name.endswith((".k_scale", ".v_scale")):
|
||||
del self.model_tensors[name]
|
||||
|
||||
def _flush_nvfp4_experts(self, key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, bid, proj_type):
|
||||
def _flush_nvfp4_experts(self, key, expert_blocks, expert_scales, expert_input_scales, expert_shapes, expert_names, bid, proj_type):
|
||||
experts = expert_blocks.pop(key)
|
||||
scales = expert_scales.pop(key)
|
||||
input_scales = expert_input_scales.pop(key)
|
||||
@@ -1362,7 +1366,7 @@ class ModelBase:
|
||||
new_name = self.map_tensor_name(merged_name)
|
||||
logger.info(f"Repacked {new_name} with shape [{len(experts)}, {shape[0]}, {shape[1]}] and quantization NVFP4")
|
||||
self.gguf_writer.add_tensor(new_name, merged, raw_dtype=gguf.GGMLQuantizationType.NVFP4)
|
||||
self._tag_prec_a4(merged_name, new_name)
|
||||
self._tag_prec_a4(expert_names.pop(key), new_name)
|
||||
|
||||
scales.sort(key=lambda x: x[0])
|
||||
self._write_scales_tensor(new_name.replace(".weight", ".scale"), [s[1] for s in scales])
|
||||
@@ -2114,6 +2118,9 @@ class TextModel(ModelBase):
|
||||
if chkhsh == "0fe1cf6eda062318a1af7270f3331a85c539a01778ff948e24388e949c5282f4":
|
||||
# ref: https://huggingface.co/evilfreelancer/ruGPT3XL
|
||||
res = "gpt-2"
|
||||
if chkhsh == "df1bc34a6366466bcb36a9a1b1cc0afdb08cfe21c9250602c930f159085390a9":
|
||||
# ref: https://huggingface.co/Lernex/Metis-1.6-Logos
|
||||
res = "gpt-2"
|
||||
if chkhsh == "9e454714343b69b99b71795c1d27a68c2a1d15dab111f4d353109f966af29da7":
|
||||
# ref: https://huggingface.co/LiquidAI/LFM2.5-8B-A1B
|
||||
res = "lfm2"
|
||||
@@ -3384,7 +3391,7 @@ def get_model_architecture(hparams: dict[str, Any], model_type: ModelType) -> st
|
||||
# Kimi-K3's text_config reports "KimiLinearForCausalLM", which is the older
|
||||
# Kimi-Linear-48B architecture and cannot load K3 (no attention residuals,
|
||||
# latent MoE, situ, ...). Route on the top-level architecture instead.
|
||||
if model_type == ModelType.TEXT and arch in ("NEOChatModel", "StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration"):
|
||||
if model_type == ModelType.TEXT and arch in ("NEOChatModel", "StepVLForConditionalGeneration", "Sarashina2VisionForCausalLM", "Exaone4_5_ForConditionalGeneration", "Step3p7ForConditionalGeneration", "KimiK3ForConditionalGeneration", "Nemotron3_5TranscribeForConditionalGeneration"):
|
||||
return arch
|
||||
|
||||
# if "architectures" is found in the sub-config, use that instead
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
# Chatterbox (ResembleAI) turbo and multilingual v3, from the raw checkpoints of the official repos
|
||||
# the checkpoints ship no config.json, the variant is detected from the talker file and the hparams come from the reference code
|
||||
# talker: text_emb and speech_emb are fused into one [text | speech] vocab, speech token i is <|speech_i|>
|
||||
# mmproj: the s3gen sidecar, the voice and conditioning encoders, the position and speech tables, and the conds.pt voice
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterable
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import ModelBase, TextModel, MmprojModel, LazyTorchTensor, gguf, logger
|
||||
from .llama import LlamaModel
|
||||
|
||||
# checkpoint file prefixes, the files are "<prefix>.safetensors"
|
||||
TURBO_TALKER = "t3_turbo_v1"
|
||||
MTL_TALKER = "t3_mtl23ls_v3"
|
||||
TURBO_S3GEN = "s3gen_meanflow"
|
||||
MTL_S3GEN = "s3gen_v3"
|
||||
VE = "ve"
|
||||
|
||||
# relative speech ids shared by both variants (start/stop_speech_token in the reference)
|
||||
SPEECH_BOS = 6561
|
||||
SPEECH_EOS = 6562
|
||||
|
||||
# sampling defaults of the reference, samplers it does not use are disabled so the common defaults do not apply
|
||||
TURBO_TOP_K = 1000
|
||||
MTL_TOP_K = 0
|
||||
TURBO_SAMPLING = {"min_p": 0.0, "top_p": 0.95, "temp": 0.8, "penalty_repeat": 1.2}
|
||||
MTL_SAMPLING = {"min_p": 0.05, "top_p": 1.0, "temp": 0.8, "penalty_repeat": 1.2}
|
||||
# the reference repetition penalty covers the whole generated history
|
||||
PENALTY_LAST_N = -1
|
||||
|
||||
|
||||
def _has_file(dir_model: Path, prefix: str) -> bool:
|
||||
return (dir_model / f"{prefix}.safetensors").is_file()
|
||||
|
||||
|
||||
def _is_turbo(dir_model: Path) -> bool:
|
||||
if _has_file(dir_model, TURBO_TALKER):
|
||||
return True
|
||||
if _has_file(dir_model, MTL_TALKER):
|
||||
return False
|
||||
raise FileNotFoundError(f"no chatterbox talker checkpoint in {dir_model}")
|
||||
|
||||
|
||||
# talker hparams of the reference (GPT2_MEDIUM_CONFIG / LLAMA_520M_CONFIG_DICT, T3Config)
|
||||
TURBO_TEXT_CONFIG = {
|
||||
"n_layer": 24, "n_embd": 1024, "n_inner": 4096, "n_head": 16, "n_ctx": 8196, "layer_norm_epsilon": 1e-05,
|
||||
"vocab_size": 50276, "speech_vocab_size": 6563,
|
||||
}
|
||||
MTL_TEXT_CONFIG = {
|
||||
"num_hidden_layers": 30, "hidden_size": 1024, "intermediate_size": 4096, "num_attention_heads": 16,
|
||||
"num_key_value_heads": 16, "head_dim": 64, "max_position_embeddings": 131072, "rms_norm_eps": 1e-05,
|
||||
"rope_theta": 500000.0,
|
||||
"rope_scaling": {"factor": 8.0, "high_freq_factor": 4.0, "low_freq_factor": 1.0, "original_max_position_embeddings": 8192},
|
||||
"vocab_size": 2454, "speech_vocab_size": 8194,
|
||||
}
|
||||
|
||||
|
||||
def _is_chatterbox_checkpoint(dir_model: Path) -> bool:
|
||||
return _has_file(dir_model, VE) and (_has_file(dir_model, TURBO_TALKER) or _has_file(dir_model, MTL_TALKER))
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(_is_chatterbox_checkpoint)
|
||||
def _load_chatterbox_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
turbo = _is_turbo(dir_model)
|
||||
logger.info(f"gguf: detected Chatterbox {'turbo' if turbo else 'multilingual v3'} checkpoint")
|
||||
text_config = dict(TURBO_TEXT_CONFIG if turbo else MTL_TEXT_CONFIG)
|
||||
# the mmproj tensors are named explicitly, they do not go through the block tensor map
|
||||
return {"architectures": ["ChatterboxModel"], "text_config": text_config, "audio_config": {"num_hidden_layers": 0}}
|
||||
|
||||
|
||||
@ModelBase.register("ChatterboxModel")
|
||||
class ChatterboxTalkerModel(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.LLAMA # multilingual; the turbo constructor switches to GPT2
|
||||
|
||||
def __init__(self, dir_model: Path, *args, **kwargs):
|
||||
self.is_turbo = _is_turbo(dir_model)
|
||||
if self.is_turbo:
|
||||
self.model_arch = gguf.MODEL_ARCH.GPT2
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
self._text_embd: Tensor | None = None
|
||||
self._speech_embd: Tensor | None = None
|
||||
self._text_head: Tensor | None = None
|
||||
self._speech_head: Tensor | None = None
|
||||
|
||||
def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]:
|
||||
return self.index_local_tensors(TURBO_TALKER if self.is_turbo else MTL_TALKER)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, _ = item
|
||||
# the reference feeds inputs_embeds, the transformer input embeddings are unused
|
||||
if name in ("tfmr.wte.weight", "tfmr.embed_tokens.weight"):
|
||||
return None
|
||||
if name.startswith(("cond_enc.", "text_pos_emb.", "speech_pos_emb.")):
|
||||
return None
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def set_vocab(self):
|
||||
if self.is_turbo:
|
||||
self._set_vocab_turbo()
|
||||
else:
|
||||
self._set_vocab_mtl()
|
||||
|
||||
def _speech_token_names(self, n_speech: int) -> list[str]:
|
||||
return [f"<|speech_{i}|>" for i in range(n_speech)]
|
||||
|
||||
def _set_vocab_turbo(self):
|
||||
# stock GPT-2 BPE from the checkpoint dir, extended with the speech tokens
|
||||
tokens, toktypes, tokpre = self.get_vocab_base()
|
||||
n_text = len(tokens)
|
||||
n_speech = self.hparams["speech_vocab_size"]
|
||||
speech = self._speech_token_names(n_speech)
|
||||
tokens += speech
|
||||
toktypes += [gguf.TokenType.CONTROL] * n_speech
|
||||
|
||||
with open(self.dir_model / "merges.txt", "r", encoding="utf-8") as f:
|
||||
merges = [line.rstrip("\n") for line in f if line.strip() and not line.startswith("#version")]
|
||||
|
||||
self._write_vocab(tokens, toktypes, merges, tokpre, n_text)
|
||||
|
||||
def _write_vocab(self, tokens: list[str], toktypes: list[int], merges: list[str], tokpre: str, n_text: int):
|
||||
self.gguf_writer.add_tokenizer_model("gpt2")
|
||||
self.gguf_writer.add_tokenizer_pre(tokpre)
|
||||
self.gguf_writer.add_token_list(tokens)
|
||||
self.gguf_writer.add_token_types(toktypes)
|
||||
self.gguf_writer.add_token_merges(merges)
|
||||
|
||||
self.gguf_writer.add_bos_token_id(n_text + SPEECH_BOS)
|
||||
self.gguf_writer.add_eos_token_id(n_text + SPEECH_EOS)
|
||||
self.gguf_writer.add_add_bos_token(False)
|
||||
self.gguf_writer.add_add_eos_token(False)
|
||||
|
||||
# the reference samples the speech head only
|
||||
self.gguf_writer.add_suppress_tokens(list(range(n_text)))
|
||||
|
||||
def _set_vocab_mtl(self):
|
||||
# the reference BPE is char-level, it is re-encoded to byte-level with extra merges that rebuild each multi-byte char
|
||||
# its lowercasing is folded into the embedding table, so the prompt needs no text preprocessing
|
||||
with open(self.dir_model / "mtl_tokenizer.json", "r", encoding="utf-8") as f:
|
||||
tok = json.load(f)
|
||||
|
||||
byte_map = gguf.vocab.bytes_to_unicode()
|
||||
|
||||
def enc(s: str) -> str:
|
||||
return "".join(byte_map[b] for b in s.encode("utf-8"))
|
||||
|
||||
n_text = self.hparams["vocab_size"]
|
||||
tokens: list[str] = [f"[unused_{i}]" for i in range(n_text)]
|
||||
toktypes = [int(gguf.TokenType.UNUSED)] * n_text
|
||||
char_merges: list[str] = []
|
||||
for t, i in tok["model"]["vocab"].items():
|
||||
if t == " ":
|
||||
# the reference substitutes [SPACE] before encoding, the raw space token is never used
|
||||
continue
|
||||
tokens[i] = enc(t)
|
||||
toktypes[i] = int(gguf.TokenType.NORMAL)
|
||||
if len(t) == 1 and len(t.encode("utf-8")) > 1:
|
||||
parts = [byte_map[b] for b in t.encode("utf-8")]
|
||||
for k in range(1, len(parts)):
|
||||
char_merges.append("".join(parts[:k]) + " " + parts[k])
|
||||
for entry in tok.get("added_tokens", []):
|
||||
tokens[entry["id"]] = entry["content"]
|
||||
toktypes[entry["id"]] = int(gguf.TokenType.CONTROL)
|
||||
|
||||
# raw spaces tokenize to [SPACE] directly
|
||||
space_id = tok["model"]["vocab"]["[SPACE]"]
|
||||
tokens[space_id] = enc(" ")
|
||||
toktypes[space_id] = int(gguf.TokenType.NORMAL)
|
||||
|
||||
n_speech = self.hparams["speech_vocab_size"]
|
||||
tokens += self._speech_token_names(n_speech)
|
||||
toktypes += [int(gguf.TokenType.CONTROL)] * n_speech
|
||||
|
||||
# chars are atomic in the reference, they must form before any of its merges
|
||||
merges = char_merges
|
||||
for m in tok["model"].get("merges", []):
|
||||
a, b = m if isinstance(m, list) else m.split(" ")
|
||||
merges.append(enc(a) + " " + enc(b))
|
||||
|
||||
self._write_vocab(tokens, toktypes, merges, "default", n_text)
|
||||
|
||||
def _mtl_case_fold_pairs(self) -> list[tuple[int, int]]:
|
||||
# (upper_id, lower_id) of the single-char vocab entries, for every script in the vocab
|
||||
with open(self.dir_model / "mtl_tokenizer.json", "r", encoding="utf-8") as f:
|
||||
vocab = json.load(f)["model"]["vocab"]
|
||||
pairs: list[tuple[int, int]] = []
|
||||
for t, i in vocab.items():
|
||||
if len(t) != 1:
|
||||
continue
|
||||
low = t.lower()
|
||||
if low != t and len(low) == 1 and low in vocab:
|
||||
pairs.append((i, vocab[low]))
|
||||
return pairs
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
|
||||
sampling = TURBO_SAMPLING if self.is_turbo else MTL_SAMPLING
|
||||
self.gguf_writer.add_sampling_top_k(TURBO_TOP_K if self.is_turbo else MTL_TOP_K)
|
||||
self.gguf_writer.add_sampling_min_p(sampling["min_p"])
|
||||
self.gguf_writer.add_sampling_top_p(sampling["top_p"])
|
||||
self.gguf_writer.add_sampling_temp(sampling["temp"])
|
||||
self.gguf_writer.add_sampling_penalty_repeat(sampling["penalty_repeat"])
|
||||
self.gguf_writer.add_sampling_penalty_last_n(PENALTY_LAST_N)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
if self.is_turbo:
|
||||
return
|
||||
# llama3 rope scaling baked into the rope_freqs factors tensor
|
||||
rp = self.hparams["rope_scaling"]
|
||||
dim = self.hparams["head_dim"]
|
||||
base = self.hparams["rope_theta"]
|
||||
freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim))
|
||||
factor = rp["factor"]
|
||||
low_freq_wavelen = rp["original_max_position_embeddings"] / rp["low_freq_factor"]
|
||||
high_freq_wavelen = rp["original_max_position_embeddings"] / rp["high_freq_factor"]
|
||||
rope_factors = []
|
||||
for freq in freqs:
|
||||
wavelen = 2 * math.pi / freq
|
||||
if wavelen < high_freq_wavelen:
|
||||
rope_factors.append(1)
|
||||
elif wavelen > low_freq_wavelen:
|
||||
rope_factors.append(factor)
|
||||
else:
|
||||
smooth = (rp["original_max_position_embeddings"] / wavelen - rp["low_freq_factor"]) / (rp["high_freq_factor"] - rp["low_freq_factor"])
|
||||
rope_factors.append(1 / ((1 - smooth) / factor + smooth))
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.ROPE_FREQS), torch.tensor(rope_factors, dtype=torch.float32))
|
||||
|
||||
def _maybe_emit_fused(self) -> Iterable[tuple[str, Tensor]]:
|
||||
if self._text_embd is not None and self._speech_embd is not None:
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.TOKEN_EMBD),
|
||||
torch.cat([self._text_embd, self._speech_embd], dim=0))
|
||||
self._text_embd = None
|
||||
self._speech_embd = None
|
||||
if self._text_head is not None and self._speech_head is not None:
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT),
|
||||
torch.cat([self._text_head, self._speech_head], dim=0))
|
||||
self._text_head = None
|
||||
self._speech_head = None
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# generate_extra_tensors output comes back through here with its final name
|
||||
if name.startswith("rope_freqs"):
|
||||
yield (name, data_torch)
|
||||
return
|
||||
# fused [text | speech] vocab: embeddings and output head
|
||||
if name == "text_emb.weight":
|
||||
if not self.is_turbo:
|
||||
# the reference lowercases its input, the uppercase rows take the lowercase ones
|
||||
fold = list(range(data_torch.shape[0]))
|
||||
for upper, lower in self._mtl_case_fold_pairs():
|
||||
fold[upper] = lower
|
||||
data_torch = data_torch[torch.tensor(fold)]
|
||||
self._text_embd = data_torch
|
||||
yield from self._maybe_emit_fused()
|
||||
return
|
||||
if name == "speech_emb.weight":
|
||||
self._speech_embd = data_torch
|
||||
yield from self._maybe_emit_fused()
|
||||
return
|
||||
if name == "text_head.weight":
|
||||
self._text_head = data_torch
|
||||
yield from self._maybe_emit_fused()
|
||||
return
|
||||
if name == "speech_head.weight":
|
||||
self._speech_head = data_torch
|
||||
yield from self._maybe_emit_fused()
|
||||
return
|
||||
if name == "speech_head.bias":
|
||||
# the text head has no bias, its zone is suppressed at sampling anyway
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.OUTPUT, suffix=".bias"), F.pad(data_torch, (self.hparams["vocab_size"], 0)))
|
||||
return
|
||||
|
||||
assert name.startswith("tfmr.")
|
||||
name = name[len("tfmr."):]
|
||||
|
||||
if self.is_turbo:
|
||||
# HF GPT-2 layout: Conv1D style weights are stored transposed
|
||||
if name.endswith((".c_attn.weight", ".c_proj.weight", ".c_fc.weight")):
|
||||
data_torch = data_torch.transpose(1, 0)
|
||||
yield (self.map_tensor_name(name), data_torch)
|
||||
return
|
||||
|
||||
if name.endswith("q_proj.weight"):
|
||||
data_torch = LlamaModel.permute(data_torch, self.hparams["num_attention_heads"], None)
|
||||
if name.endswith("k_proj.weight"):
|
||||
data_torch = LlamaModel.permute(data_torch, self.hparams["num_attention_heads"], self.hparams["num_key_value_heads"])
|
||||
yield (self.map_tensor_name("model." + name), data_torch)
|
||||
|
||||
|
||||
@ModelBase.register("ChatterboxModel")
|
||||
class ChatterboxMmprojModel(MmprojModel):
|
||||
has_vision_encoder = False
|
||||
has_audio_encoder = True
|
||||
|
||||
def __init__(self, dir_model: Path, *args, **kwargs):
|
||||
self.is_turbo = _is_turbo(dir_model)
|
||||
super().__init__(dir_model, *args, **kwargs)
|
||||
self._wnorm_g: dict[str, Tensor] = {}
|
||||
self._wnorm_v: dict[str, Tensor] = {}
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
return self.global_config.get("audio_config")
|
||||
|
||||
def index_tensors(self, remote_hf_model_id: str | None = None) -> dict[str, Callable[[], Tensor]]:
|
||||
tensors: dict[str, Callable[[], Tensor]] = {}
|
||||
for prefix in (TURBO_S3GEN if self.is_turbo else MTL_S3GEN, VE, TURBO_TALKER if self.is_turbo else MTL_TALKER):
|
||||
tensors.update(self.index_local_tensors(prefix))
|
||||
return tensors
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
# the s3gen, ve and talker checkpoints have disjoint top-level names
|
||||
name, gen = item
|
||||
new_name: str | None = None
|
||||
# batchnorm bookkeeping (over the gguf name length cap) and the tokenizer dsp buffers are unused
|
||||
if name.endswith("num_batches_tracked") or name in ("tokenizer.window", "tokenizer._mel_filters"):
|
||||
return None
|
||||
for src, dst in (
|
||||
("flow.encoder.", "a.gen.fenc."),
|
||||
("flow.decoder.estimator.", "a.gen.est."),
|
||||
("mel2wav.", "a.gen.hift."),
|
||||
("speaker_encoder.", "a.spk."),
|
||||
("tokenizer.", "a.s3tok."),
|
||||
("flow.", "a.gen.flow."), # input_embedding, encoder_proj, spk_embed_affine_layer
|
||||
("lstm.", "a.ve.lstm."),
|
||||
("proj.", "a.ve.proj."),
|
||||
("cond_enc.", "a.cenc."),
|
||||
):
|
||||
if name.startswith(src):
|
||||
new_name = dst + name[len(src):]
|
||||
break
|
||||
if name == "text_pos_emb.emb.weight":
|
||||
new_name = "a.gen.t3.text_pos_emb"
|
||||
elif name == "speech_pos_emb.emb.weight":
|
||||
new_name = "a.gen.t3.speech_pos_emb"
|
||||
elif name == "speech_emb.weight":
|
||||
new_name = gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.A_GEN_CODE_OUT_EMBD] + ".weight"
|
||||
if new_name is None:
|
||||
return None
|
||||
return super().filter_tensors((new_name, gen))
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
self.gguf_writer.add_file_type(self.ftype)
|
||||
|
||||
# speaker encoder, the front-end hparams are hard-coded in clip.cpp
|
||||
self.gguf_writer.add_clip_has_audio_encoder(True)
|
||||
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.CHATTERBOX_SPKENC)
|
||||
self.gguf_writer.add_audio_projection_dim(80)
|
||||
self.gguf_writer.add_audio_num_mel_bins(80)
|
||||
self.gguf_writer.add_audio_block_count(0)
|
||||
self.gguf_writer.add_audio_embedding_length(192)
|
||||
self.gguf_writer.add_audio_head_count(1)
|
||||
self.gguf_writer.add_audio_feed_forward_length(192)
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(1e-5)
|
||||
|
||||
# audio generator (s3gen, chatterbox projector); the flow encoder shape
|
||||
self.gguf_writer.add_clip_has_gen_audio_encoder(True)
|
||||
self.gguf_writer.add_clip_gen_audio_projector_type(gguf.VisionProjectorType.CHATTERBOX)
|
||||
self.gguf_writer.add_gen_audio_projection_dim(80)
|
||||
self.gguf_writer.add_gen_audio_embedding_length(512)
|
||||
self.gguf_writer.add_gen_audio_feed_forward_length(2048)
|
||||
self.gguf_writer.add_gen_audio_block_count(6)
|
||||
self.gguf_writer.add_gen_audio_head_count(8)
|
||||
self.gguf_writer.add_gen_audio_attention_layernorm_eps(1e-5)
|
||||
|
||||
def _fuse_weight_norm(self, name: str, data_torch: Tensor) -> tuple[str, Tensor] | None:
|
||||
# torch weight_norm parametrization: weight = g * v / |v| over dims 1..n
|
||||
base = name.split(".parametrizations.weight.original")[0]
|
||||
if name.endswith("original0"):
|
||||
self._wnorm_g[base] = data_torch
|
||||
else:
|
||||
self._wnorm_v[base] = data_torch
|
||||
if base in self._wnorm_g and base in self._wnorm_v:
|
||||
g = self._wnorm_g.pop(base)
|
||||
v = self._wnorm_v.pop(base)
|
||||
norm = v.float().norm(dim=tuple(range(1, v.dim())), keepdim=True)
|
||||
return (base + ".weight", g.float() * v.float() / norm)
|
||||
return None
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if ".parametrizations.weight.original" in name:
|
||||
fused = self._fuse_weight_norm(name, data_torch)
|
||||
if fused is not None:
|
||||
yield fused
|
||||
return
|
||||
yield (name, data_torch)
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
del name, bid
|
||||
# tensors read on the host stay F32
|
||||
if new_name.startswith(("a.ve.", "a.cenc.", "voice.", "a.gen.t3.", "a.gen.hift.m_source.")):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
# the conv ops have no BF16 kernels
|
||||
if n_dims >= 2 and new_name.endswith((".weight", ".weight_v")):
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
conds = torch.load(self.dir_model / "conds.pt", map_location="cpu", weights_only=True)
|
||||
t3c = conds["t3"] if isinstance(conds, dict) else conds.t3
|
||||
genc = conds["gen"] if isinstance(conds, dict) else conds.gen
|
||||
t3c = vars(t3c) if not isinstance(t3c, dict) else t3c
|
||||
genc = vars(genc) if not isinstance(genc, dict) else genc
|
||||
|
||||
def talker_tensor(name: str) -> Tensor:
|
||||
return LazyTorchTensor.to_eager(self.model_tensors[name]())
|
||||
|
||||
# the conds.pt voice: voice.default holds the talker conditioning rows, voice.default.ref_* the flow decoder reference
|
||||
# 1D tensors are written as F32, the speech ids stay exact
|
||||
yield ("voice.default.ref_tokens", genc["prompt_token"][0].float())
|
||||
yield ("voice.default.ref_feat", genc["prompt_feat"][0].float())
|
||||
yield ("voice.default.ref_spk", genc["embedding"][0].float())
|
||||
|
||||
spkr_w = talker_tensor("a.cenc.spkr_enc.weight").float()
|
||||
spkr_b = talker_tensor("a.cenc.spkr_enc.bias").float()
|
||||
spkr_row = spkr_w @ t3c["speaker_emb"][0].float() + spkr_b
|
||||
speech_emb = talker_tensor(self.format_tensor_name(gguf.MODEL_TENSOR.A_GEN_CODE_OUT_EMBD)).float()
|
||||
cond_tokens = t3c["cond_prompt_speech_tokens"][0]
|
||||
|
||||
if self.is_turbo:
|
||||
# turbo talker conditioning: [spkr, speech rows of the cond tokens]
|
||||
yield ("voice.default", torch.cat([spkr_row[None], speech_emb[cond_tokens]], dim=0))
|
||||
return
|
||||
|
||||
# multilingual talker conditioning [spkr, perceiver x32, emotion], from the reference perceiver over the cond speech tokens
|
||||
with torch.no_grad():
|
||||
speech_pos = talker_tensor("a.gen.t3.speech_pos_emb").float()
|
||||
pse = speech_emb[cond_tokens] + speech_pos[: cond_tokens.shape[0]]
|
||||
|
||||
ln_w = talker_tensor("a.cenc.perceiver.attn.norm.weight").float()
|
||||
ln_b = talker_tensor("a.cenc.perceiver.attn.norm.bias").float()
|
||||
wq = talker_tensor("a.cenc.perceiver.attn.to_q.weight").float()
|
||||
bq = talker_tensor("a.cenc.perceiver.attn.to_q.bias").float()
|
||||
wk = talker_tensor("a.cenc.perceiver.attn.to_k.weight").float()
|
||||
bk = talker_tensor("a.cenc.perceiver.attn.to_k.bias").float()
|
||||
wv = talker_tensor("a.cenc.perceiver.attn.to_v.weight").float()
|
||||
bv = talker_tensor("a.cenc.perceiver.attn.to_v.bias").float()
|
||||
wo = talker_tensor("a.cenc.perceiver.attn.proj_out.weight").float()
|
||||
bo = talker_tensor("a.cenc.perceiver.attn.proj_out.bias").float()
|
||||
query = talker_tensor("a.cenc.perceiver.pre_attention_query")[0].float()
|
||||
|
||||
n_head = 4
|
||||
n_e = query.shape[1]
|
||||
|
||||
def attn_block(x1: Tensor, x2: Tensor) -> Tensor:
|
||||
nx1 = F.layer_norm(x1, (n_e,), ln_w, ln_b)
|
||||
nx2 = F.layer_norm(x2, (n_e,), ln_w, ln_b)
|
||||
q = (nx1 @ wq.T + bq).view(-1, n_head, n_e // n_head).transpose(0, 1)
|
||||
k = (nx2 @ wk.T + bk).view(-1, n_head, n_e // n_head).transpose(0, 1)
|
||||
v = (nx2 @ wv.T + bv).view(-1, n_head, n_e // n_head).transpose(0, 1)
|
||||
ctx = F.scaled_dot_product_attention(q, k, v)
|
||||
ctx = ctx.transpose(0, 1).reshape(-1, n_e)
|
||||
return ctx @ wo.T + bo + x1
|
||||
|
||||
pre = attn_block(query, pse)
|
||||
p32 = attn_block(pre, pre)
|
||||
|
||||
emo = talker_tensor("a.cenc.emotion_adv_fc.weight").float()
|
||||
emo_row = emo[:, 0] * t3c["emotion_adv"].reshape(-1)[0]
|
||||
yield ("voice.default", torch.cat([spkr_row[None], p32, emo_row[None]], dim=0))
|
||||
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
from .base import ModelBase, gguf
|
||||
from .qwen import _LinearAttentionVReorderBase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
@ModelBase.register("CloverMoeModelLM")
|
||||
@ModelBase.example("Diffuspace/Clover-35B-A3B-Preview")
|
||||
class CloverMoeModel(_LinearAttentionVReorderBase):
|
||||
model_arch = gguf.MODEL_ARCH.CLOVER_MOE
|
||||
supports_mtp_export = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.no_mtp = True
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def set_vocab(self):
|
||||
self._set_vocab_gpt2()
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
self.gguf_writer.add_expert_shared_count(self.hparams.get("num_shared_experts", 1))
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, _ = item
|
||||
if name.endswith((".conv_right_weight", ".bd_softmax_logit_scale", ".bd_softmax_history_bias", ".bd_softmax_history_c")):
|
||||
return None
|
||||
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name.endswith(".A_log") or (name.endswith("norm.weight") and not name.endswith("linear_attn.norm.weight")):
|
||||
data_torch = data_torch.float()
|
||||
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
@@ -453,9 +453,6 @@ class LFM3AudioMmprojModel(MmprojModel):
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if new_name.startswith("a.conv1d.") or "conv_dw" in new_name or new_name.startswith("a.gen.wav.quant."):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
# the codec decoder is small and its log-magnitude/phase output does not survive quantization
|
||||
if new_name.startswith("a.gen.wav."):
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Callable, Iterable, TYPE_CHECKING
|
||||
|
||||
import torch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
|
||||
from .base import LazyTorchTensor, ModelBase, TextModel, gguf
|
||||
|
||||
|
||||
@ModelBase.register("Metis16ForCausalLM")
|
||||
class Metis16Model(TextModel):
|
||||
model_arch = gguf.MODEL_ARCH.METIS16
|
||||
|
||||
# read by generate_extra_tensors() or not used at inference: dropped in modify_tensors()
|
||||
_folded = {
|
||||
"pass_embeddings", "lm_head.weight", "depth_budget.multiplier",
|
||||
"depth_memory.pass_embeddings", "depth_memory.anchor_embeddings",
|
||||
"depth_memory.state_write.bias", "depth_memory.metadata_write.bias",
|
||||
}
|
||||
_folded_layer = {
|
||||
"mixer_connection.controller.weight", "mixer_connection.controller.bias", "mixer_connection.mix_logits",
|
||||
"mixer_connection.read_logits", "mixer_connection.write_logits",
|
||||
"moe_connection.controller.weight", "moe_connection.controller.bias", "moe_connection.mix_logits",
|
||||
"moe_connection.read_logits", "moe_connection.write_logits",
|
||||
"moe.k_router.weight", "moe.k_router.bias", "moe.k_budget.multiplier",
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
hp = self.hparams
|
||||
self.n_pass = hp["num_passes"]
|
||||
self.attn_layers = list(hp["attention_layer_indices"])
|
||||
self._generated: set[str] = set()
|
||||
# one cache slot per pass and layer, while the tensors exist once per physical layer
|
||||
self.block_count = self.n_pass * hp["num_hidden_layers"]
|
||||
|
||||
def set_vocab(self):
|
||||
self._set_vocab_gpt2()
|
||||
# turns end on the first eos id, so llama.cpp must stop on it too
|
||||
eos = self.hparams.get("eos_token_id")
|
||||
if isinstance(eos, list) and eos:
|
||||
self.gguf_writer.add_eot_token_id(eos[0])
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
hp = self.hparams
|
||||
super().set_gguf_parameters()
|
||||
|
||||
n_layer = hp["num_hidden_layers"]
|
||||
self.gguf_writer.add_head_count_kv([hp["num_key_value_heads"] if il % n_layer in self.attn_layers else 0 for il in range(self.block_count)])
|
||||
|
||||
self.gguf_writer.add_expert_feed_forward_length(hp["expert_intermediate_size"])
|
||||
self.gguf_writer.add_expert_shared_feed_forward_length(hp["shared_expert_intermediate_size"])
|
||||
self.gguf_writer.add_expert_shared_count(1)
|
||||
self.gguf_writer.add_moe_latent_size(hp["latent_dim"])
|
||||
|
||||
d_inner = hp["mamba_expand"] * hp["hidden_size"]
|
||||
self.gguf_writer.add_ssm_conv_kernel(hp["mamba_d_conv"])
|
||||
self.gguf_writer.add_ssm_inner_size(d_inner)
|
||||
self.gguf_writer.add_ssm_state_size(hp["mamba_d_state"])
|
||||
self.gguf_writer.add_ssm_time_step_rank(d_inner // hp["mamba_head_dim"])
|
||||
self.gguf_writer.add_ssm_group_count(hp["mamba_n_groups"])
|
||||
self.gguf_writer.add_ssm_norm_epsilon(hp["mamba_norm_eps"])
|
||||
|
||||
self.gguf_writer.add_hyper_connection_count(hp["n_streams"])
|
||||
self.gguf_writer.add_metis_pass_count(self.n_pass)
|
||||
self.gguf_writer.add_metis_memory_length(hp["memory_dim"])
|
||||
self.gguf_writer.add_metis_route_length(hp["route_feature_dim"])
|
||||
self.gguf_writer.add_metis_continuation_logit_limit(hp["continuation_logit_limit"])
|
||||
self.gguf_writer.add_attention_pass_lora_rank(hp["attention_pass_lora_rank"])
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
# the checkpoint leaves these names suffix-less; add the suffix of the GGUF tensor so the mapper keeps it
|
||||
if name.endswith((".mixer.impl.mixer.dt_bias", ".moe.selection_bias")):
|
||||
name += ".bias"
|
||||
elif (name.endswith((".moe.expert_embeddings", ".mixer.pass_gates"))
|
||||
or name in ("depth_memory.stream_gate", "stream_embeddings")):
|
||||
name += ".weight"
|
||||
elif name == "depth_memory.stream_gate_bias":
|
||||
name = "depth_memory.stream_gate.bias"
|
||||
return super().filter_tensors((name, gen))
|
||||
|
||||
def _load(self, name: str) -> Tensor:
|
||||
return LazyTorchTensor.to_eager(self.model_tensors[name]())
|
||||
|
||||
def _emit(self, name: str, data: Tensor) -> tuple[str, Tensor]:
|
||||
self._generated.add(name)
|
||||
return name, data
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
hp = self.hparams
|
||||
r = hp["route_feature_dim"]
|
||||
n_stream = hp["n_streams"]
|
||||
T = gguf.MODEL_TENSOR
|
||||
passes = self._load("pass_embeddings")[: self.n_pass]
|
||||
|
||||
# the mixes depend only on the weights and the pass, so Sinkhorn runs once here
|
||||
# the controller runs in the checkpoint dtype, as the reference does
|
||||
for il in range(hp["num_hidden_layers"]):
|
||||
for src, (mix, read, write) in (
|
||||
("mixer_connection", (T.HC_ATTN_MIX, T.HC_ATTN_READ, T.HC_ATTN_WRITE)),
|
||||
("moe_connection", (T.HC_FFN_MIX, T.HC_FFN_READ, T.HC_FFN_WRITE)),
|
||||
):
|
||||
prefix = f"layers.{il}.{src}."
|
||||
ctrl_w, ctrl_b = self._load(prefix + "controller.weight"), self._load(prefix + "controller.bias")
|
||||
mixes, reads, writes = [], [], []
|
||||
for p in range(self.n_pass):
|
||||
delta = torch.nn.functional.linear(passes[p].to(ctrl_w.dtype), ctrl_w, ctrl_b).float()
|
||||
m_delta, r_delta, w_delta = torch.split(delta, [n_stream * n_stream, n_stream, n_stream])
|
||||
log_m = self._load(prefix + "mix_logits").float() + m_delta.view(n_stream, n_stream)
|
||||
for _ in range(hp["mhc_sinkhorn_iterations"]):
|
||||
log_m = log_m - torch.logsumexp(log_m, dim=-1, keepdim=True)
|
||||
log_m = log_m - torch.logsumexp(log_m, dim=-2, keepdim=True)
|
||||
mixes.append(log_m.exp())
|
||||
reads.append(torch.softmax(self._load(prefix + "read_logits").float() + r_delta, dim=-1))
|
||||
writes.append(torch.softmax(self._load(prefix + "write_logits").float() + w_delta, dim=-1))
|
||||
act = ctrl_w.dtype
|
||||
yield self._emit(self.format_tensor_name(mix, il), torch.stack(mixes).to(act).float())
|
||||
yield self._emit(self.format_tensor_name(read, il), torch.stack(reads).to(act).float())
|
||||
yield self._emit(self.format_tensor_name(write, il), torch.stack(writes).to(act).float())
|
||||
|
||||
# one bias per memory entry: write biases, pass and anchor embeddings, and the constant routed width
|
||||
meta_w = self._load("depth_memory.metadata_write.weight").float()
|
||||
mean_k = hp["num_experts_per_tok"] / hp["max_routed_k"]
|
||||
base = (self._load("depth_memory.state_write.bias").float() + self._load("depth_memory.metadata_write.bias").float()
|
||||
+ mean_k * meta_w[:, r])
|
||||
pass_e = self._load("depth_memory.pass_embeddings").float()
|
||||
anchor_e = self._load("depth_memory.anchor_embeddings").float()
|
||||
n_anchor = len(self.attn_layers) + 1
|
||||
entry_bias = torch.stack([base + pass_e[p] + anchor_e[a] for p in range(self.n_pass) for a in range(n_anchor)])
|
||||
yield self._emit(self.format_tensor_name(T.DEPTH_MEM_ENTRY_BIAS), entry_bias)
|
||||
|
||||
def _split_features(self, data_torch: Tensor, template: str, parts: tuple[str, ...], d: int, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# the graph reads the concatenated features one block at a time; each block is its own tensor
|
||||
for i, part in enumerate(parts):
|
||||
piece = data_torch[:, i * d:(i + 1) * d] if i < 3 else data_torch[:, 3 * d:]
|
||||
yield from super().modify_tensors(piece, template.format(part), bid)
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name in self._generated:
|
||||
yield name, data_torch
|
||||
return
|
||||
if name in self._folded:
|
||||
return
|
||||
hp = self.hparams
|
||||
d = hp["hidden_size"]
|
||||
|
||||
if bid is None:
|
||||
if name == "stream_embeddings.weight":
|
||||
# the graph keeps the streams innermost: [n_stream, n_embd, n_tokens]
|
||||
yield from super().modify_tensors(data_torch.T.contiguous(), name, bid)
|
||||
return
|
||||
if name == "depth_memory.metadata_write.weight":
|
||||
# metadata input becomes [summary, entropy, confidence, continuation]; entropy arrives unnormalized
|
||||
r = hp["route_feature_dim"]
|
||||
meta_w = data_torch.float()
|
||||
entropy_col = meta_w[:, r + 1:r + 2] / math.log(hp["n_routed_experts"])
|
||||
data_torch = torch.cat((meta_w[:, :r], entropy_col, meta_w[:, r + 2:]), dim=1)
|
||||
elif name == "depth_memory.stream_gate.weight":
|
||||
data_torch = data_torch.float() / math.sqrt(d)
|
||||
elif name == "depth_memory.route_projection.weight":
|
||||
yield from self._split_features(data_torch, "depth_memory.route_{}.weight", ("state", "retr", "diff", "hist"), d, bid)
|
||||
return
|
||||
elif name == "continuation.hidden.weight":
|
||||
yield from self._split_features(data_torch, "continuation.hidden_{}.weight", ("state", "retr", "diff", "route"), d, bid)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
return
|
||||
|
||||
local = name.removeprefix(f"layers.{bid}.")
|
||||
if local in self._folded_layer:
|
||||
return
|
||||
|
||||
if local == "mixer.impl.mixer.A_log":
|
||||
data_torch = -torch.exp(data_torch).unsqueeze(-1)
|
||||
elif local == "mixer.impl.mixer.D":
|
||||
data_torch = data_torch.unsqueeze(-1)
|
||||
elif local == "mixer.impl.mixer.conv1d.weight":
|
||||
data_torch = data_torch.squeeze(1)
|
||||
elif local == "mixer.impl.mixer.norm.weight":
|
||||
data_torch = data_torch.reshape(hp["mamba_n_groups"], -1)
|
||||
elif local == "mixer.pass_gates.weight":
|
||||
# the reference applies tanh(gate) in the checkpoint dtype
|
||||
data_torch = torch.tanh(data_torch[: self.n_pass]).to(torch.bfloat16).float()
|
||||
elif local == "moe.shared_expert.gate_up.weight":
|
||||
gate, up = data_torch.chunk(2, dim=0)
|
||||
yield from super().modify_tensors(gate, name.replace("gate_up", "gate"), bid)
|
||||
yield from super().modify_tensors(up, name.replace("gate_up", "up"), bid)
|
||||
return
|
||||
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
def tensor_force_quant(self, name: str, new_name: str, bid: int | None, n_dims: int) -> gguf.GGMLQuantizationType | bool:
|
||||
# FP32 in the checkpoint, tiny and read as rows, or computed here at full precision: keep FP32
|
||||
if (new_name.startswith(("cont_", "depth_mem_entry_bias", "depth_mem_stream_gate"))
|
||||
or new_name.endswith(("ffn_exp_embd.weight", "ffn_gate_inp.weight", "hc_attn_mix.weight", "hc_attn_read.weight",
|
||||
"hc_attn_write.weight", "hc_ffn_mix.weight", "hc_ffn_read.weight", "hc_ffn_write.weight"))):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
+147
-2
@@ -428,6 +428,9 @@ class NemotronHModel(GraniteHybridModel):
|
||||
is_moe: bool = False
|
||||
supports_mtp_export = True
|
||||
_experts: list[dict[str, Tensor]] | None = None
|
||||
# tokens past the embedding rows (left out of the vocab), and special tokens that are printed as text
|
||||
_vocab_skip: tuple[str, ...] = ()
|
||||
_user_defined_tokens: re.Pattern[str] | None = None
|
||||
|
||||
_SSM_LAYER_TYPES = {"mamba", "linear_attention"}
|
||||
_ATTN_LAYER_TYPES = {"attention", "full_attention"}
|
||||
@@ -616,7 +619,11 @@ class NemotronHModel(GraniteHybridModel):
|
||||
vocab_size = -(vocab_size // -pad_vocab) * pad_vocab
|
||||
self.hparams["vocab_size"] = vocab_size
|
||||
|
||||
assert max(tokenizer.vocab.values()) < vocab_size # ty: ignore[unresolved-attribute]
|
||||
# draft-model converters (DFlash, LiLiCorr) call this with their own instance, which has neither hook
|
||||
vocab_skip = getattr(self, "_vocab_skip", ())
|
||||
user_defined_tokens = getattr(self, "_user_defined_tokens", None)
|
||||
|
||||
assert max(id_ for tok, id_ in tokenizer.vocab.items() if tok not in vocab_skip) < vocab_size # ty: ignore[unresolved-attribute]
|
||||
|
||||
tokpre = self.get_vocab_base_pre(tokenizer)
|
||||
|
||||
@@ -638,7 +645,8 @@ class NemotronHModel(GraniteHybridModel):
|
||||
if previous_token != token:
|
||||
logger.info(f"{repr(previous_token)} is encoded and decoded back to {repr(token)} using AutoTokenizer")
|
||||
|
||||
if added_tokens_decoder[i].special or self.does_token_look_special(token):
|
||||
printed = user_defined_tokens is not None and user_defined_tokens.fullmatch(token)
|
||||
if (added_tokens_decoder[i].special or self.does_token_look_special(token)) and not printed:
|
||||
toktypes.append(gguf.TokenType.CONTROL)
|
||||
else:
|
||||
token = token.replace(b"\xe2\x96\x81".decode("utf-8"), " ") # pre-normalize user-defined spaces
|
||||
@@ -834,3 +842,140 @@ class NemotronHPuzzleModel(NemotronHModel):
|
||||
if item[0].startswith("mtp."):
|
||||
return None
|
||||
return super().filter_tensors(item)
|
||||
|
||||
|
||||
# NeMo speechlm2 SALM checkpoint: a Nemotron-H decoder under llm.*, the speech encoder under perception.*
|
||||
@ModelBase.register("Nemotron3_5TranscribeForConditionalGeneration")
|
||||
@ModelBase.example("nvidia/NVIDIA-Nemotron-3.5-Transcribe-30B-A4B")
|
||||
class NemotronTranscribeModel(NemotronHModel):
|
||||
model_arch = gguf.MODEL_ARCH.NEMOTRON_H
|
||||
# the audio placeholder (id vocab_size) has no embedding row; speaker, event and style tags print with the transcript
|
||||
_vocab_skip = ("<|audio|>",)
|
||||
_user_defined_tokens = re.compile(r"<spk:\d+>|<ev:[a-z_]+>|<sty:(?:stt|end):[a-z_]+>")
|
||||
_renames = {
|
||||
"llm.model.embed_tokens.weight": "backbone.embeddings.weight",
|
||||
"llm.model.norm.weight": "backbone.norm_f.weight",
|
||||
"llm.lm_head.weight": "lm_head.weight",
|
||||
}
|
||||
# grouped experts: [n_expert, n_in, n_out] -> [n_expert, n_out, n_in]
|
||||
_grouped_experts = {"gate_and_up_projs": "up_proj", "down_projs": "down_proj"}
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
if name.startswith(("perception.", "ctc_timestamp.")) or "_extra_state" in name:
|
||||
return None
|
||||
if not name.startswith("llm."):
|
||||
# the FP8 and NVFP4 releases store the decoder as backbone.*
|
||||
raise ValueError(f"unexpected tensor {name}: only the BF16 checkpoint layout (llm.*) is supported")
|
||||
if name in cls._renames:
|
||||
name = cls._renames[name]
|
||||
elif name.startswith("llm.model.layers."):
|
||||
name = "backbone.layers." + name[len("llm.model.layers."):]
|
||||
elif name.startswith("llm.mtp.layers."):
|
||||
name = "mtp.layers." + name[len("llm.mtp.layers."):]
|
||||
else:
|
||||
raise ValueError(f"unexpected decoder tensor {name}")
|
||||
return super().filter_tensors((name.replace("._fp32_params.", "."), gen))
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
leaf = name.rsplit(".", 1)[-1]
|
||||
if ".mixer.experts." in name and leaf in self._grouped_experts:
|
||||
if name.startswith("mtp."):
|
||||
assert self._mtp_bid is not None, f"{name}: MTP head is not exported"
|
||||
bid = self._mtp_bid
|
||||
assert bid is not None
|
||||
data_torch = data_torch.permute(0, 2, 1).contiguous()
|
||||
name = f"model.layers.{bid}.mlp.experts.{self._grouped_experts[leaf]}.weight"
|
||||
yield from ModelBase.modify_tensors(self, data_torch, name, bid)
|
||||
return
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
|
||||
@ModelBase.register("Nemotron3_5TranscribeForConditionalGeneration")
|
||||
class NemotronTranscribeAudioModel(MmprojModel):
|
||||
# NeMo ParallelExpertEncoder: ASR and speaker transformers, speaker activity fused into the ASR states
|
||||
has_vision_encoder = False
|
||||
has_audio_encoder = True
|
||||
|
||||
def get_audio_config(self) -> dict[str, Any] | None:
|
||||
asr = self.global_config["pe_encoder_config"]["asr_encoder_cfg"]
|
||||
return {
|
||||
"hidden_size": asr["d_model"],
|
||||
"intermediate_size": int(float(asr.get("ff_expansion", 4)) * asr["d_model"]),
|
||||
"num_hidden_layers": asr["n_layers"],
|
||||
"num_attention_heads": asr["n_heads"],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _rope_dims(cfg: dict[str, Any]) -> int:
|
||||
assert cfg.get("self_attention_model") == "rope" and cfg.get("subsampling", "feature_stacking") == "feature_stacking"
|
||||
assert cfg.get("attn_mode", "full") == "full" and not cfg.get("xscaling", False) and cfg.get("pre_block_norm", True)
|
||||
assert float(cfg.get("rope_base", 10000.0)) == 10000.0
|
||||
n_rot = int(cfg["d_model"] // cfg["n_heads"] * float(cfg.get("rotary_fraction", 1.0)))
|
||||
assert n_rot >= 2 and n_rot % 2 == 0
|
||||
return n_rot
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
super().set_gguf_parameters()
|
||||
pe = self.global_config["pe_encoder_config"]
|
||||
asr = pe["asr_encoder_cfg"]
|
||||
diar = pe["diarization_model_cfg"]
|
||||
spk = diar["encoder"]
|
||||
assert pe.get("asr_encoder_type") == "transformer" and pe.get("speaker_feature_mode", "thresholded") == "thresholded"
|
||||
assert pe.get("asr_normalize_type") == "per_feature" and pe.get("diar_normalize_type") in (None, "NA")
|
||||
assert asr["subsampling_factor"] == spk["subsampling_factor"] == 8 and asr["feat_in"] == spk["feat_in"]
|
||||
assert self._rope_dims(spk) == spk["d_model"] // spk["n_heads"] and not spk.get("qkv_bias", False)
|
||||
assert diar.get("high_resolution", False)
|
||||
# constants in clip: the ParallelExpertEncoder online settings and the Sortformer streaming config
|
||||
assert (pe.get("online_inference_length", 500), pe.get("chunk_left_context", 50), pe.get("chunk_right_context", 50),
|
||||
pe.get("diar_spkcache_len", 188), pe.get("diar_fifo_len", 40), pe.get("diar_spkcache_update_period", 300),
|
||||
pe.get("speaker_activity_threshold", 0.5)) == (500, 50, 50, 188, 40, 300, 0.5), \
|
||||
"streaming settings differ from the constants in tools/mtmd/models/nemotron-transcribe.cpp"
|
||||
sm = diar["sortformer_modules"]
|
||||
assert (sm["spkcache_sil_frames_per_spk"], sm["pred_score_threshold"], sm["scores_boost_latest"], sm["strong_boost_rate"],
|
||||
sm["weak_boost_rate"], sm["min_pos_scores_rate"], sm["use_learnable_sil_emb"]) == (1, 0.25, 0.05, 0.75, 1.5, 0.5, True), \
|
||||
"speaker cache settings differ from the constants in tools/mtmd/models/nemotron-transcribe.cpp"
|
||||
|
||||
self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.NEMOTRON_TRANSCRIBE)
|
||||
self.gguf_writer.add_audio_num_mel_bins(asr["feat_in"])
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(1e-5)
|
||||
self.gguf_writer.add_audio_subsampling_factor(asr["subsampling_factor"])
|
||||
self.gguf_writer.add_audio_rope_dims(self._rope_dims(asr))
|
||||
self.gguf_writer.add_audio_spk_block_count(spk["n_layers"])
|
||||
self.gguf_writer.add_audio_spk_head_count(spk["n_heads"])
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
# ParallelExpertEncoder._build_sinusoid_position_encoding
|
||||
pe = self.global_config["pe_encoder_config"]
|
||||
n_spk = int(pe["diarization_model_cfg"]["sortformer_modules"]["num_spks"])
|
||||
d = int(pe["asr_encoder_cfg"]["d_model"])
|
||||
scale = float(pe.get("spk_kernel_scale", 1.0))
|
||||
pos = torch.arange(n_spk, dtype=torch.float32)[:, None]
|
||||
div = torch.exp(torch.arange(0, d, 2, dtype=torch.float32) * -(np.log(10000.0) / d))
|
||||
kernel = torch.zeros(n_spk, d, dtype=torch.float32)
|
||||
kernel[:, 0::2] = torch.sin(pos * div)
|
||||
kernel[:, 1::2] = torch.cos(pos * div)
|
||||
# stored as [d, n_spk]: the graph multiplies it with the speaker activity directly
|
||||
yield self.format_tensor_name(gguf.MODEL_TENSOR.A_FUSION_SPK_KERNEL, suffix=""), (kernel * scale).T.contiguous()
|
||||
|
||||
def tensor_force_quant(self, name, new_name, bid, n_dims):
|
||||
if self.match_model_tensor_name(new_name, gguf.MODEL_TENSOR.A_SPK_HEAD_UPSAMPLE, None):
|
||||
return gguf.GGMLQuantizationType.F16 # ggml_conv_1d kernel
|
||||
return super().tensor_force_quant(name, new_name, bid, n_dims)
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item: tuple[str, Callable[[], Tensor]]) -> tuple[str, Callable[[], Tensor]] | None:
|
||||
name, gen = item
|
||||
if not name.startswith("perception.") or "_extra_state" in name:
|
||||
return None
|
||||
# training-only head; the speaker model's unused front end
|
||||
if name.startswith(("perception.encoder.diarization_model.sortformer_modules.hidden_to_spks.",
|
||||
"perception.encoder.diarization_model.preprocessor.")):
|
||||
return None
|
||||
return name, gen
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
if name == "perception.preprocessor.featurizer.fb":
|
||||
data_torch = data_torch.squeeze(0)
|
||||
yield from super().modify_tensors(data_torch, name, bid)
|
||||
|
||||
+158
-11
@@ -5,6 +5,7 @@ from pathlib import Path
|
||||
from typing import Any, Iterable, TYPE_CHECKING
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from torch import Tensor
|
||||
@@ -45,6 +46,102 @@ def _tensor_shapes(dir_model: Path) -> dict[str, tuple[int, ...]]:
|
||||
return {name: tuple(part[name].shape) for name in part.keys()}
|
||||
|
||||
|
||||
def _has_voice_encoder(dir_model: Path) -> bool:
|
||||
# without voice cloning, the mimi encoder weights are all zero
|
||||
part_names = ModelBase.get_model_part_names(dir_model, "model", ".safetensors")
|
||||
with gguf.utility.SafetensorsLocal(dir_model / part_names[0]) as part:
|
||||
nonzero = {bool(part[name].mmap_bytes().any()) for name in part.keys() if name.startswith("mimi.encoder")}
|
||||
if len(nonzero) != 1:
|
||||
raise ValueError(f"{dir_model}: the mimi encoder is partly zeroed out")
|
||||
return nonzero.pop()
|
||||
|
||||
|
||||
def _load_safetensors(path: Path, prefixes: tuple[str, ...]) -> dict[str, Tensor]:
|
||||
dtypes = {"F32": torch.float32, "BF16": torch.bfloat16, "I64": torch.int64}
|
||||
with gguf.utility.SafetensorsLocal(path) as part:
|
||||
return {name: torch.from_numpy(part[name].mmap_bytes()).view(dtypes[part[name].dtype]).reshape(part[name].shape)
|
||||
for name in part.keys() if name.startswith(prefixes)}
|
||||
|
||||
|
||||
def _rope_pairs(x: Tensor, n_head: int, rope_theta: float) -> Tensor:
|
||||
# ggml NORM mode rope, x: [n_pos, n_embd]
|
||||
n_pos, n_embd = x.shape
|
||||
head_dim = n_embd // n_head
|
||||
angle = torch.arange(n_pos, dtype=x.dtype)[:, None, None] * \
|
||||
torch.pow(rope_theta, -torch.arange(0, head_dim, 2, dtype=x.dtype) / head_dim)
|
||||
x = x.view(n_pos, n_head, head_dim // 2, 2)
|
||||
return torch.stack([x[..., 0] * angle.cos() - x[..., 1] * angle.sin(),
|
||||
x[..., 0] * angle.sin() + x[..., 1] * angle.cos()], -1).reshape(n_pos, n_embd)
|
||||
|
||||
|
||||
def _voice_prompt_embd(weights: dict[str, Tensor], kv: list[Tensor], n_head: int, rope_theta: float) -> tuple[Tensor, float]:
|
||||
# the voice state only has K/V of each layer (kv[il] is [k/v, pos, n_embd]): layer 0 V gives each frame up to its scale, layer 1 V gives the scale
|
||||
# returns the frames without bos_before_voice, and the max relative K/V error when the frames are prompted again
|
||||
w = {name: t.double() for name, t in weights.items()}
|
||||
kv = [t.double() for t in kv]
|
||||
k0, v0, v1 = kv[0][0], kv[0][1], kv[1][1]
|
||||
n_pos, n_embd = v0.shape
|
||||
head_dim = n_embd // n_head
|
||||
|
||||
def p(il: int, name: str) -> Tensor:
|
||||
return w[f"flow_lm.transformer.layers.{il}.{name}"]
|
||||
|
||||
def norm(x: Tensor, il: int, name: str) -> Tensor:
|
||||
return F.layer_norm(x, (n_embd,), p(il, f"{name}.weight"), p(il, f"{name}.bias"), 1e-5)
|
||||
|
||||
def heads(t: Tensor) -> Tensor:
|
||||
return t.reshape(n_pos, n_head, head_dim).transpose(0, 1)
|
||||
|
||||
def rows(t: Tensor) -> Tensor:
|
||||
return t.transpose(0, 1).reshape(n_pos, n_embd)
|
||||
|
||||
def attn(il: int, h: Tensor, k: Tensor, v: Tensor) -> Tensor:
|
||||
q = _rope_pairs(h @ p(il, "self_attn.in_proj.weight").chunk(3)[0].T, n_head, rope_theta)
|
||||
return rows(F.scaled_dot_product_attention(heads(q), heads(k), heads(v), is_causal=True)) @ p(il, "self_attn.out_proj.weight").T
|
||||
|
||||
def ffn(il: int, x: Tensor) -> Tensor:
|
||||
return F.gelu(norm(x, il, "norm2") @ p(il, "linear1.weight").T) @ p(il, "linear2.weight").T
|
||||
|
||||
wv = p(0, "self_attn.in_proj.weight").chunk(3)[2]
|
||||
h = torch.linalg.solve(wv, v0.T).T # norm1(x)
|
||||
z = (h - p(0, "norm1.bias")) / p(0, "norm1.weight") # (x - mean(x)) / std(x)
|
||||
|
||||
# fit z on [speaker_proj, 1] to get the latent direction back
|
||||
proj = w["flow_lm.speaker_proj_weight"]
|
||||
fit = torch.linalg.lstsq(torch.cat([proj, torch.ones(n_embd, 1, dtype=proj.dtype)], 1), z[1:].T).solution
|
||||
frames = (proj @ fit[:-1]).T
|
||||
|
||||
a0 = attn(0, h, k0, v0)[1:]
|
||||
wv1 = p(1, "self_attn.in_proj.weight").chunk(3)[2]
|
||||
|
||||
def err(scale: Tensor) -> Tensor: # per frame relative error of layer 1 V
|
||||
x = scale[:, None] * frames + a0
|
||||
x = x + ffn(0, x)
|
||||
return (norm(x, 1, "norm1") @ wv1.T - v1[1:]).norm(dim=1) / v1[1:].norm(dim=1)
|
||||
|
||||
grid = torch.logspace(-3, 3, 49, dtype=torch.float64)
|
||||
best = torch.stack([err(s.expand(n_pos - 1)) for s in grid]).argmin(0)
|
||||
lo, hi = grid[(best - 1).clamp(min=0)], grid[(best + 1).clamp(max=len(grid) - 1)]
|
||||
for _ in range(40): # golden section search, all frames at once
|
||||
m1, m2 = hi - 0.618 * (hi - lo), lo + 0.618 * (hi - lo)
|
||||
left = err(m1) < err(m2)
|
||||
lo, hi = torch.where(left, lo, m1), torch.where(left, m2, hi)
|
||||
frames = (lo + hi)[:, None] / 2 * frames
|
||||
|
||||
# prompt the frames again and compare K/V with the shipped state
|
||||
x = torch.cat([w["flow_lm.bos_before_voice"].reshape(1, n_embd), frames])
|
||||
drift = 0.0
|
||||
for il, (k_ref, v_ref) in enumerate(kv):
|
||||
h = norm(x, il, "norm1")
|
||||
_, wk, wv = p(il, "self_attn.in_proj.weight").chunk(3)
|
||||
k, v = _rope_pairs(h @ wk.T, n_head, rope_theta), h @ wv.T
|
||||
for a, b in ((k, k_ref), (v, v_ref)):
|
||||
drift = max(drift, ((a - b).norm(dim=1) / b.norm(dim=1)).median().item())
|
||||
x = x + attn(il, h, k, v)
|
||||
x = x + ffn(il, x)
|
||||
return frames, drift
|
||||
|
||||
|
||||
@ModelBase.register_hparams_loader(lambda dir_model: "flow_lm.bos_emb" in _tensor_shapes(dir_model))
|
||||
def _load_hparams(dir_model: Path) -> dict[str, Any]:
|
||||
logger.info("gguf: detected pocket-tts checkpoint, deriving hparams from tensor shapes")
|
||||
@@ -194,22 +291,27 @@ class PocketTTSMmprojModel(MmprojModel):
|
||||
(gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_Q, gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_K, gguf.MODEL_TENSOR.A_GEN_WAV_TFM_ATTN_V),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.has_audio_encoder = _has_voice_encoder(self.dir_model)
|
||||
|
||||
def set_gguf_parameters(self):
|
||||
self.gguf_writer.add_file_type(self.ftype)
|
||||
assert self.hparams_audio is not None
|
||||
|
||||
# voice-prompt encoder: mimi encoder + speaker_proj
|
||||
self.gguf_writer.add_clip_has_audio_encoder(True)
|
||||
# note: the 24kHz sample rate is hardcoded on the clip.cpp side, like the other audio models
|
||||
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.POCKETTTS_SPKENC)
|
||||
self.gguf_writer.add_audio_projection_dim(self.n_embd_text)
|
||||
self.gguf_writer.add_audio_block_count(self.hparams_audio["num_hidden_layers"])
|
||||
self.gguf_writer.add_audio_embedding_length(self.hparams_audio["hidden_size"])
|
||||
self.gguf_writer.add_audio_feed_forward_length(self.hparams_audio["intermediate_size"])
|
||||
self.gguf_writer.add_audio_head_count(self.hparams_audio["num_attention_heads"])
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(1e-5)
|
||||
# mimi convolves the waveform directly, it is passed around as a 1-row "mel"
|
||||
self.gguf_writer.add_audio_num_mel_bins(1)
|
||||
if self.has_audio_encoder:
|
||||
self.gguf_writer.add_clip_has_audio_encoder(True)
|
||||
# note: the 24kHz sample rate is hardcoded on the clip.cpp side, like the other audio models
|
||||
self.gguf_writer.add_clip_audio_projector_type(gguf.VisionProjectorType.POCKETTTS_SPKENC)
|
||||
self.gguf_writer.add_audio_projection_dim(self.n_embd_text)
|
||||
self.gguf_writer.add_audio_block_count(self.hparams_audio["num_hidden_layers"])
|
||||
self.gguf_writer.add_audio_embedding_length(self.hparams_audio["hidden_size"])
|
||||
self.gguf_writer.add_audio_feed_forward_length(self.hparams_audio["intermediate_size"])
|
||||
self.gguf_writer.add_audio_head_count(self.hparams_audio["num_attention_heads"])
|
||||
self.gguf_writer.add_audio_attention_layernorm_eps(1e-5)
|
||||
# mimi convolves the waveform directly, it is passed around as a 1-row "mel"
|
||||
self.gguf_writer.add_audio_num_mel_bins(1)
|
||||
|
||||
# generation: flow-matching decoder + mimi decoder
|
||||
# the SEANet and flow net hparams are constant across the family, clip.cpp holds them
|
||||
@@ -229,12 +331,57 @@ class PocketTTSMmprojModel(MmprojModel):
|
||||
# conv1d/conv1d_dw kernels must be F16, ggml_conv_1d(_dw) has no BF16 path
|
||||
if ".seanet." in new_name or new_name in ("a.downsample.conv.weight", "a.gen.wav.upsample.weight"):
|
||||
return gguf.GGMLQuantizationType.F16
|
||||
# voices are read on the host
|
||||
if new_name.startswith("voice."):
|
||||
return gguf.GGMLQuantizationType.F32
|
||||
return False
|
||||
|
||||
def generate_extra_tensors(self) -> Iterable[tuple[str, Tensor]]:
|
||||
# embeddings/<name>.safetensors hold the K/V state after the voice prompt (see get_state_for_audio_prompt() in the reference), turn it back into prompt embeddings
|
||||
paths = sorted((self.dir_model / "embeddings").glob("*.safetensors"))
|
||||
if not paths:
|
||||
return
|
||||
weights = _load_safetensors(self.dir_model / "model.safetensors",
|
||||
("flow_lm.transformer.layers.", "flow_lm.speaker_proj_weight", "flow_lm.bos_before_voice"))
|
||||
n_layer = self.global_config["num_hidden_layers"]
|
||||
for path in paths:
|
||||
state = _load_safetensors(path, ("transformer.layers.",))
|
||||
kv = []
|
||||
for il in range(n_layer):
|
||||
prefix = f"transformer.layers.{il}.self_attn/"
|
||||
if any(prefix + k not in state for k in ("cache", "offset")):
|
||||
break
|
||||
n_pos = int(state[prefix + "offset"].item())
|
||||
# older packs have no pad entry
|
||||
if prefix + "pad" in state and int(state[prefix + "pad"].item()) != 0:
|
||||
break
|
||||
kv.append(state[prefix + "cache"][:, 0, :n_pos].flatten(2)) # [k/v, pos, n_embd]
|
||||
if len(kv) != n_layer or any(t.shape != kv[0].shape for t in kv) or kv[0].shape[1] < 2 or \
|
||||
kv[0].shape[2] != self.global_config["hidden_size"]:
|
||||
logger.warning(f"voice {path.stem}: skipped, not an unpadded voice state of this backbone")
|
||||
continue
|
||||
embd, drift = _voice_prompt_embd(weights, kv, self.global_config["num_attention_heads"],
|
||||
self.global_config["rope_theta"])
|
||||
# some packs ship a state made with other weights, the reference still uses it: keep up to 50% error, warn above 5%
|
||||
if drift > 0.5:
|
||||
logger.warning(f"voice {path.stem}: skipped, its state does not belong to these weights (error {drift:.1e})")
|
||||
continue
|
||||
log = logger.warning if drift > 0.05 else logger.info
|
||||
log(f"voice {path.stem}: {embd.shape[0]} frames, prompting them again gives the shipped state within {drift:.1e}")
|
||||
yield (f"voice.{path.stem}", embd.float())
|
||||
|
||||
def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
del bid # the block index of the mimi transformers is parsed here, not by the base class
|
||||
T = gguf.MODEL_TENSOR
|
||||
|
||||
if name.startswith("voice."):
|
||||
yield (name, data_torch)
|
||||
return
|
||||
|
||||
if not self.has_audio_encoder and (name.startswith(("mimi.encoder", "mimi.downsample."))
|
||||
or name == "flow_lm.speaker_proj_weight"):
|
||||
return # voice-prompt encoder
|
||||
|
||||
if name in ("flow_lm.bos_emb", "flow_lm.bos_before_voice", "flow_lm.conditioner.embed.weight"):
|
||||
return # folded into the backbone embedding table
|
||||
if name.startswith("flow_lm.transformer.") or name.startswith("flow_lm.out_norm."):
|
||||
|
||||
@@ -198,6 +198,7 @@ pre_computed_hashes = [
|
||||
# jina-v2-de variants
|
||||
{"name": "jina-v2-de", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/aari1995/German_Semantic_V3", "chkhsh": "b3d1dd861f1d4c5c0d2569ce36baf3f90fe8a102db3de50dd71ff860d91be3df"},
|
||||
{"name": "gpt-2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/evilfreelancer/ruGPT3XL", "chkhsh": "0fe1cf6eda062318a1af7270f3331a85c539a01778ff948e24388e949c5282f4"},
|
||||
{"name": "gpt-2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/Lernex/Metis-1.6-Logos", "chkhsh": "df1bc34a6366466bcb36a9a1b1cc0afdb08cfe21c9250602c930f159085390a9"},
|
||||
# lfm2 variants
|
||||
{"name": "lfm2", "tokt": TOKENIZER_TYPE.BPE, "repo": "https://huggingface.co/LiquidAI/LFM2.5-8B-A1B", "chkhsh": "9e454714343b69b99b71795c1d27a68c2a1d15dab111f4d353109f966af29da7"},
|
||||
# hrm-text (DFM Mimir) is SPM-style BPE: normalizer maps ' ' -> '▁', merges
|
||||
|
||||
@@ -161,7 +161,7 @@ lspci -n | grep -Eo '19e5:d[0-9a-f]{3}' | cut -d: -f2
|
||||
| FP16 | Support | Support |
|
||||
| Q8_0 | Support | Partial |
|
||||
| Q4_0 | Support | Partial |
|
||||
| BF16 | Support | |
|
||||
| BF16 | Support | No |
|
||||
|
||||
> **310P note**
|
||||
> - `Q8_0`: data transform / buffer path is implemented, and `GET_ROWS` is supported, but quantized `MUL_MAT` / `MUL_MAT_ID` are not supported.
|
||||
@@ -274,7 +274,9 @@ cmake --build build --config release
|
||||
|
||||
**Notes**:
|
||||
|
||||
- CANN backend only supports FP16/Q4_0/Q8_0 models currently.
|
||||
- On 910B the CANN backend supports FP16, BF16 and Q4_0/Q8_0 models. On 310P, BF16 is not
|
||||
supported and quantized `MUL_MAT` / `MUL_MAT_ID` are not implemented, so only FP16 models
|
||||
are supported (see [DataType Supports](#datatype-supports)).
|
||||
|
||||
2. **Launch inference**
|
||||
|
||||
@@ -355,3 +357,7 @@ Enable ACL graph execution during the prefill stage, default is false. This opti
|
||||
### GGML_CANN_OPERATOR_FUSION
|
||||
|
||||
Enable operator fusion during computation, default is false. This option fuses compatible operators (e.g., ADD + RMS_NORM) to reduce overhead and improve performance.
|
||||
|
||||
### GGML_CANN_NO_PINNED
|
||||
|
||||
Disable pinned (host-locked) host buffers. When it is set (any value), `aclrtMallocHost` is not called: the CANN host buffer type allocates regular CPU memory instead and the backend reports no `host_buffer` capability, so host-side staging uses pageable memory. Use it when pinned host allocation fails, or when the pinned memory is needed by other processes on the host.
|
||||
|
||||
+3
-3
@@ -249,7 +249,7 @@ IBM VXE/VXE2 SIMD acceleration depends on the BLAS implementation. It is strongl
|
||||
| MXFP4 | ✅ | ❓ | ❓ |
|
||||
| Q5_0 | ✅ | ❓ | ❓ |
|
||||
| Q5_1 | ✅ | ❓ | ❓ |
|
||||
| Q8_0 | ✅ | ❓ | ❓ |
|
||||
| Q8_0 | ✅ | ✅ | ❓ |
|
||||
| Q2_K | 🚫 | ❓ | ❓ |
|
||||
| Q3_K | ✅ | ❓ | ❓ |
|
||||
| Q4_K | ✅ | ❓ | ❓ |
|
||||
@@ -266,11 +266,11 @@ IBM VXE/VXE2 SIMD acceleration depends on the BLAS implementation. It is strongl
|
||||
| IQ1_M | 🚫 | ❓ | ❓ |
|
||||
| IQ4_NL | ✅ | ❓ | ❓ |
|
||||
| IQ4_XS | ✅ | ❓ | ❓ |
|
||||
| FP32->FP16 | 🚫 | ❓ | ❓ |
|
||||
| FP32->FP16 | ✅ | ❓ | ❓ |
|
||||
| FP16->FP32 | 🚫 | ❓ | ❓ |
|
||||
|
||||
- ✅ - acceleration available
|
||||
- 🚫 - acceleration unavailable, will still run using scalar implementation
|
||||
- ❓ - acceleration unknown, please contribute if you can test it yourself
|
||||
|
||||
Last Updated by **Aaron Teo (aaron.teo1@ibm.com)** on Sep 8, 2026.
|
||||
Last Updated by **Aaron Teo (aaron.teo1@ibm.com)** on Nov 3, 2026.
|
||||
|
||||
+1
-1
@@ -481,7 +481,7 @@ cmake --build build --config Release
|
||||
Now you can load the model in conversation mode using `Vulkan`
|
||||
|
||||
```sh
|
||||
build/bin/Release/llama-cli -m "[PATH TO MODEL]" -ngl 100 -c 16384 -t 10 -n -2 -cnv
|
||||
build/bin/Release/llama-cli -m "[PATH TO MODEL]" -ngl 100 -c 16384 -t 10 -n -2
|
||||
```
|
||||
|
||||
**MSYS2**
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ Legend:
|
||||
| ADD | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| ADD1 | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| ADD_ID | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| ARANGE | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| ARANGE | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| ARGMAX | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| ARGSORT | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| CEIL | ❌ | ❌ | ✅ | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
@@ -74,7 +74,7 @@ Legend:
|
||||
| MEAN | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | 🟡 | ✅ | ❌ | ❌ | ❌ |
|
||||
| MUL | ❌ | ✅ | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| MUL_MAT | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |
|
||||
| MUL_MAT_HADAMARD | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | 🟡 | ✅ | ✅ | ✅ | ❌ | 🟡 |
|
||||
| MUL_MAT_HADAMARD | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | 🟡 |
|
||||
| MUL_MAT_ID | ❌ | 🟡 | ✅ | ✅ | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | ✅ | 🟡 | 🟡 | ❌ |
|
||||
| MUL_MAT_ID_W4A4 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | 🟡 | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| MUL_MAT_ID_W4A8 | ❌ | ❌ | ✅ | ❌ | ❌ | 🟡 | ❌ | 🟡 | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
+18
-18
@@ -9242,31 +9242,31 @@
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=128,n=32,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=128,n=4,k=128,bs=[2,3],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=256,n=512,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=64,n=1,k=64,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=1,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=512,n=1,k=512,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=32,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=512,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=64,n=1,k=64,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=1,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=512,n=1,k=512,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=32,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=512,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=32,n=1,k=32,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=16384,n=1,k=16384,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=64,n=1,k=64,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=1,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=512,n=1,k=512,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=32,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=4,k=128,bs=[2,3],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=512,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=64,n=1,k=64,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=1,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=512,n=1,k=512,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=32,k=128,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=128,n=4,k=128,bs=[2,3],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=256,n=512,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=1024,n=1,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=2048,n=1,k=2048,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=4096,n=1,k=4096,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=8192,n=1,k=8192,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f32,m=1024,n=7,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=1024,n=1,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=2048,n=1,k=2048,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=4096,n=1,k=4096,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=8192,n=1,k=8192,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=1024,n=7,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=1024,n=1,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=2048,n=1,k=2048,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=4096,n=1,k=4096,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=8192,n=1,k=8192,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_HADAMARD","type_a=f32,type_b=f16,m=1024,n=7,k=1024,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","1","yes","OpenCL"
|
||||
"OpenCL","MUL_MAT_W4A8","type_a=nvfp4,type_b=f32,m=32,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_W4A8","type_a=nvfp4,type_b=f32,m=32,n=32,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
"OpenCL","MUL_MAT_W4A8","type_a=nvfp4,type_b=f32,m=64,n=16,k=512,bs=[1,1],nr=[1,1],per=[0,1,2,3],k_v=0,o=1,src_overlap=0,m_v=0,pad=0","support","0","no","OpenCL"
|
||||
|
||||
|
Can't render this file because it is too large.
|
+10
-2
@@ -12326,8 +12326,16 @@
|
||||
"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[512,34,2,1],pad_0=10,pad_1=9","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","PAD_REFLECT_1D","type=f32,ne_a=[3000,384,4,1],pad_0=10,pad_1=9","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","ROLL","shift0=3,shift1=-2,shift3=1,shift4=-1","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=0.000000,stop=10.000000,step=1.000000","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=0.000000,stop=1048576.000000,step=1.000000","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=0.000000,stop=10.000000,step=1.000000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=3.000000,stop=3.500000,step=1.000000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-10.000000,stop=10.000000,step=0.300000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=62.750000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=63.000000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=63.250000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=254.750000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=255.000000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=-1.000000,stop=255.250000,step=0.250000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","ARANGE","type=f32,start=0.000000,stop=1048576.000000,step=1.000000","support","1","yes","WebGPU"
|
||||
"WebGPU: WebGPU","TIMESTEP_EMBEDDING","type=f32,ne_a=[2,1,1,1],dim=320,max_period=10000","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","LEAKY_RELU","type=f32,ne_a=[10,5,4,3],negative_slope=0.100000","support","0","no","WebGPU"
|
||||
"WebGPU: WebGPU","CUMSUM","type=f32,ne=[10,5,4,3]","support","1","yes","WebGPU"
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -211,6 +211,18 @@ extern "C" {
|
||||
|
||||
// Context management and operations for faster communication between backends, used for tensor parallelism (meta backend)
|
||||
typedef void * (*ggml_backend_comm_init_t)(ggml_backend_t * backends, size_t n_backends);
|
||||
|
||||
// Builds the communicator for the case where each rank is a separate process, so a process
|
||||
// owns one backend of the communicator rather than all of them.
|
||||
//
|
||||
// The caller provides bootstrap_callback, which moves bytes between the ranks before the
|
||||
// communicator exists. Each rank passes size bytes in send_buf, and recv_buf receives
|
||||
// world_size * size bytes in rank order. The backend decides what to exchange and how many
|
||||
// times to call it, and every rank must make the same calls in the same order.
|
||||
// comm_init_rank blocks until all world_size ranks have joined.
|
||||
typedef bool (*ggml_backend_comm_bootstrap_callback)(void * user_data, const void * send_buf, void * recv_buf, size_t size);
|
||||
typedef void * (*ggml_backend_comm_init_rank_t)(ggml_backend_t backend, int rank, int world_size, ggml_backend_comm_bootstrap_callback bootstrap_callback, void * bootstrap_callback_data);
|
||||
|
||||
typedef void (*ggml_backend_comm_free_t)(void * comm_ctx);
|
||||
typedef bool (*ggml_backend_comm_allreduce_tensor_t)(void * comm_ctx, struct ggml_tensor ** tensors);
|
||||
|
||||
|
||||
@@ -489,6 +489,32 @@ static struct ggml_tensor * ggml_backend_meta_buffer_simple_tensor(const struct
|
||||
return it->second[index];
|
||||
}
|
||||
|
||||
// bytes spanned by all dims except skip_dim
|
||||
static size_t ggml_backend_meta_inner_extent(const struct ggml_tensor * tensor, int skip_dim) {
|
||||
size_t extent = 0;
|
||||
for (int i = 0; i < GGML_MAX_DIMS; i++) {
|
||||
if (i != skip_dim && tensor->ne[i] > 1) {
|
||||
extent += (size_t) (tensor->ne[i] - 1) * tensor->nb[i];
|
||||
}
|
||||
}
|
||||
return extent;
|
||||
}
|
||||
|
||||
// the view dim that holds the split axis of src, or -1
|
||||
// all other dims of src and view must stay inside one slab of that axis
|
||||
static int ggml_backend_meta_view_slab_dim(const struct ggml_tensor * view, const struct ggml_tensor * src, int axis) {
|
||||
const size_t slab = src->nb[axis];
|
||||
if (ggml_backend_meta_inner_extent(src, axis) >= slab) {
|
||||
return -1;
|
||||
}
|
||||
for (int dim = 0; dim < GGML_MAX_DIMS; dim++) {
|
||||
if (view->ne[dim] == src->ne[axis] && view->nb[dim] == slab) {
|
||||
return ggml_backend_meta_inner_extent(view, dim) < slab ? dim : -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(const struct ggml_tensor * tensor, bool assume_sync);
|
||||
|
||||
static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
@@ -700,9 +726,39 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
}
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
// permuted view: split the view dim that spans the src split axis, other dims must be inside one element of it or outside the span
|
||||
if (ggml_is_permuted(tensor) && !ggml_is_permuted(tensor->src[0]) && axis >= 0 && axis < GGML_MAX_DIMS-1) {
|
||||
const size_t nb_axis = tensor->src[0]->nb[axis];
|
||||
const size_t nb_next = tensor->src[0]->nb[axis+1];
|
||||
for (int dim = 0; dim < GGML_MAX_DIMS; dim++) {
|
||||
if (tensor->ne[dim] == 1 || tensor->nb[dim] < nb_axis || tensor->nb[dim]*tensor->ne[dim] != nb_next) {
|
||||
continue;
|
||||
}
|
||||
bool others_ok = true;
|
||||
for (int i = 0; i < GGML_MAX_DIMS; i++) {
|
||||
if (i == dim || tensor->ne[i] == 1) {
|
||||
continue;
|
||||
}
|
||||
others_ok = others_ok && (tensor->nb[i]*tensor->ne[i] <= tensor->nb[dim] || tensor->nb[i] % nb_next == 0);
|
||||
}
|
||||
if (others_ok) {
|
||||
return {ggml_backend_meta_split_axis(dim), {0}, {1}, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_MIRRORED || src_ss[0].axis == GGML_BACKEND_SPLIT_AXIS_PARTIAL) {
|
||||
return src_ss[0];
|
||||
}
|
||||
// a view that only regroups dims inside one slab of the split axis keeps the split,
|
||||
// the per-device strides are only rescaled above nb[axis] (e.g. the KV streams of a permuted q)
|
||||
if (axis >= 0 && axis < GGML_MAX_DIMS && tensor->view_offs == tensor->src[0]->view_offs) {
|
||||
const int dim = ggml_backend_meta_view_slab_dim(tensor, tensor->src[0], axis);
|
||||
if (dim >= 0) {
|
||||
ggml_backend_meta_split_state ret = src_ss[0];
|
||||
ret.axis = ggml_backend_meta_split_axis(dim);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
GGML_ABORT("view of permuted tensor not implemented");
|
||||
//return {GGML_BACKEND_SPLIT_AXIS_UNKNOWN, {0}, {1}, 1};
|
||||
};
|
||||
@@ -1000,8 +1056,13 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(
|
||||
split_state = handle_pad(src_ss);
|
||||
} break;
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
case GGML_OP_ROLL:
|
||||
case GGML_OP_ARANGE:
|
||||
case GGML_OP_ROLL: {
|
||||
split_state = handle_generic(src_ss, /*scalar_only =*/ true);
|
||||
} break;
|
||||
case GGML_OP_ARANGE: {
|
||||
// no sources, every device computes the full range
|
||||
split_state = {GGML_BACKEND_SPLIT_AXIS_MIRRORED, {0}, {1}, 1};
|
||||
} break;
|
||||
case GGML_OP_TIMESTEP_EMBEDDING: {
|
||||
split_state = handle_generic(src_ss, /*scalar_only =*/ true);
|
||||
} break;
|
||||
|
||||
@@ -30,7 +30,7 @@ struct ggml_compute_params {
|
||||
};
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
|
||||
#define m512bh(p) p
|
||||
#define m512i(p) p
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
#include "batch-get.cuh"
|
||||
#include "common.cuh"
|
||||
#include "ggml-cpp.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
||||
struct ggml_cuda_batch_get_descriptor {
|
||||
const unsigned char * src;
|
||||
unsigned char * dst;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static __global__ void ggml_cuda_batch_get_kernel(const ggml_cuda_batch_get_descriptor * copies, size_t count) {
|
||||
// Blocks along y cooperate on each descriptor.
|
||||
const size_t index = blockIdx.x;
|
||||
if (index >= count) {
|
||||
return;
|
||||
}
|
||||
const size_t first = (size_t) blockIdx.y * blockDim.x + threadIdx.x;
|
||||
const size_t stride = (size_t) gridDim.y * blockDim.x;
|
||||
const auto copy = copies[index];
|
||||
// Use 16-, 8-, 4-, or 2-byte copies when both pointers meet that alignment; threads copy separate chunks.
|
||||
const uintptr_t alignment = (uintptr_t) copy.src | (uintptr_t) copy.dst;
|
||||
size_t tail = 0;
|
||||
if (alignment % 16 == 0) {
|
||||
for (size_t i = first; i < copy.size / 16; i += stride) {
|
||||
ggml_cuda_memcpy_1<16>(copy.dst + 16*i, copy.src + 16*i);
|
||||
}
|
||||
tail = copy.size / 16 * 16;
|
||||
} else if (alignment % 8 == 0) {
|
||||
for (size_t i = first; i < copy.size / 8; i += stride) {
|
||||
ggml_cuda_memcpy_1<8>(copy.dst + 8*i, copy.src + 8*i);
|
||||
}
|
||||
tail = copy.size / 8 * 8;
|
||||
} else if (alignment % 4 == 0) {
|
||||
for (size_t i = first; i < copy.size / 4; i += stride) {
|
||||
ggml_cuda_memcpy_1<4>(copy.dst + 4*i, copy.src + 4*i);
|
||||
}
|
||||
tail = copy.size / 4 * 4;
|
||||
} else if (alignment % 2 == 0) {
|
||||
for (size_t i = first; i < copy.size / 2; i += stride) {
|
||||
ggml_cuda_memcpy_1<2>(copy.dst + 2*i, copy.src + 2*i);
|
||||
}
|
||||
tail = copy.size / 2 * 2;
|
||||
}
|
||||
// Copy trailing bytes, or the entire range when no wider alignment is available.
|
||||
for (size_t i = tail + first; i < copy.size; i += stride) {
|
||||
ggml_cuda_memcpy_1<1>(copy.dst + i, copy.src + i);
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_cuda_batch_get_mapping {
|
||||
void * host = nullptr;
|
||||
unsigned char * device = nullptr;
|
||||
|
||||
ggml_cuda_batch_get_mapping(void * host, size_t size) {
|
||||
const cudaError_t err = cudaHostRegister(host, size, cudaHostRegisterMapped | cudaHostRegisterPortable);
|
||||
if (err != cudaSuccess) {
|
||||
(void) cudaGetLastError();
|
||||
GGML_LOG_DEBUG("%s: failed to register %.2f MiB of pinned memory: %s\n", __func__,
|
||||
size / 1024.0 / 1024.0, cudaGetErrorString(err));
|
||||
return;
|
||||
}
|
||||
this->host = host;
|
||||
if (cudaHostGetDevicePointer((void **) &device, host, 0) != cudaSuccess) {
|
||||
(void) cudaGetLastError();
|
||||
device = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ggml_cuda_batch_get_mapping(const ggml_cuda_batch_get_mapping &) = delete;
|
||||
ggml_cuda_batch_get_mapping & operator=(const ggml_cuda_batch_get_mapping &) = delete;
|
||||
|
||||
~ggml_cuda_batch_get_mapping() {
|
||||
if (host) {
|
||||
// Release only the registration acquired by this object.
|
||||
CUDA_CHECK(cudaHostUnregister(host));
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
bool ggml_cuda_get_tensor_n(int device,
|
||||
const ggml_tensor * const * tensors, void * const * data, const size_t * offsets,
|
||||
const size_t * sizes, size_t n_tensors) {
|
||||
#if defined(GGML_USE_HIP) || defined(GGML_USE_MUSA)
|
||||
GGML_UNUSED(device);
|
||||
GGML_UNUSED(tensors);
|
||||
GGML_UNUSED(data);
|
||||
GGML_UNUSED(offsets);
|
||||
GGML_UNUSED(sizes);
|
||||
GGML_UNUSED(n_tensors);
|
||||
return false;
|
||||
#else
|
||||
// Provisional dispatch values based on experiments.
|
||||
constexpr size_t min_batch_reads = 128;
|
||||
constexpr size_t max_gather_read_bytes = 128 * 1024;
|
||||
// Collect non-empty CPU destination ranges.
|
||||
std::vector<std::pair<uintptr_t, uintptr_t>> ranges;
|
||||
ranges.reserve(n_tensors);
|
||||
size_t payload_bytes = 0;
|
||||
// Number of non-empty reads whose size is at most max_gather_read_bytes.
|
||||
size_t n_reads_small = 0;
|
||||
for (size_t i = 0; i < n_tensors; ++i) {
|
||||
if (sizes[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
const uintptr_t address = (uintptr_t) data[i];
|
||||
GGML_ASSERT(address <= UINTPTR_MAX - sizes[i]);
|
||||
GGML_ASSERT(payload_bytes <= SIZE_MAX - sizes[i]);
|
||||
payload_bytes += sizes[i];
|
||||
n_reads_small += sizes[i] <= max_gather_read_bytes;
|
||||
ranges.emplace_back(address, address + sizes[i]);
|
||||
}
|
||||
if (ranges.empty()) {
|
||||
return true;
|
||||
}
|
||||
// Total number of non-empty reads.
|
||||
const size_t n_reads = ranges.size();
|
||||
if (n_reads > SIZE_MAX / sizeof(ggml_cuda_batch_get_descriptor) || n_reads > INT_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (n_reads < 2 || (n_reads < min_batch_reads && n_reads_small == n_reads)) {
|
||||
return false;
|
||||
}
|
||||
std::sort(ranges.begin(), ranges.end());
|
||||
for (size_t i = 1; i < ranges.size(); ++i) {
|
||||
// Preserve the ordered copy path when CPU destinations overlap.
|
||||
if (ranges[i].first < ranges[i - 1].second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Find the CPU span to register, including gaps between destinations.
|
||||
const uintptr_t begin = ranges.front().first;
|
||||
const uintptr_t end = ranges.back().second;
|
||||
if (end - begin - payload_bytes > payload_bytes) {
|
||||
return false;
|
||||
}
|
||||
// Register and map the existing CPU destination so the kernel can write to it directly.
|
||||
ggml_cuda_batch_get_mapping mapping((void *) begin, end - begin);
|
||||
if (!mapping.host) {
|
||||
return false;
|
||||
}
|
||||
// Reads selected for gather; zero means all reads use DMA.
|
||||
size_t n_reads_gather = mapping.device && n_reads_small >= min_batch_reads ? n_reads_small : 0;
|
||||
// Keep descriptor storage alive until the final stream synchronization.
|
||||
constexpr size_t max_batch_descriptors = 128 * 1024;
|
||||
const size_t bytes = std::min(n_reads_gather, max_batch_descriptors) * sizeof(ggml_cuda_batch_get_descriptor);
|
||||
ggml_backend_buffer_ptr descriptors(n_reads_gather ? ggml_backend_buft_alloc_buffer(ggml_backend_cuda_buffer_type(device), bytes) : nullptr);
|
||||
if (!descriptors) {
|
||||
n_reads_gather = 0;
|
||||
}
|
||||
const auto host_type = ggml_backend_cuda_host_buffer_type();
|
||||
ggml_backend_buffer_ptr host_descriptors(n_reads_gather ? ggml_backend_buft_alloc_buffer(host_type, bytes) : nullptr);
|
||||
if (!host_descriptors || ggml_backend_buffer_get_type(host_descriptors.get()) != host_type) {
|
||||
n_reads_gather = 0;
|
||||
}
|
||||
if (n_reads_gather) {
|
||||
auto * copies = static_cast<ggml_cuda_batch_get_descriptor *>(ggml_backend_buffer_get_base(host_descriptors.get()));
|
||||
auto * device_copies = static_cast<ggml_cuda_batch_get_descriptor *>(ggml_backend_buffer_get_base(descriptors.get()));
|
||||
const int threads = 256;
|
||||
const size_t nsm = ggml_cuda_info().devices[device].nsm;
|
||||
size_t i = 0;
|
||||
for (size_t first = 0; first < n_reads_gather; first += max_batch_descriptors) {
|
||||
const size_t n_batch = std::min(n_reads_gather - first, max_batch_descriptors);
|
||||
size_t count = 0;
|
||||
for (; i < n_tensors && count < n_batch; ++i) {
|
||||
if (sizes[i] == 0 || sizes[i] > max_gather_read_bytes) {
|
||||
continue;
|
||||
}
|
||||
copies[count++] = {(const unsigned char *) tensors[i]->data + offsets[i],
|
||||
mapping.device + ((uintptr_t) data[i] - begin), sizes[i]};
|
||||
}
|
||||
const size_t blocks_per_copy = std::min<size_t>(UINT16_MAX, std::max<size_t>(1, (nsm + n_batch - 1) / n_batch));
|
||||
const dim3 blocks((unsigned int) n_batch, (unsigned int) blocks_per_copy);
|
||||
// Stream ordering keeps each kernel ahead of the next descriptor overwrite.
|
||||
CUDA_CHECK(cudaMemcpyAsync(device_copies, copies, n_batch * sizeof(copies[0]), cudaMemcpyHostToDevice, cudaStreamPerThread));
|
||||
ggml_cuda_batch_get_kernel<<<blocks, threads, 0, cudaStreamPerThread>>>(device_copies, n_batch);
|
||||
CUDA_CHECK(cudaGetLastError());
|
||||
if (first + n_batch < n_reads_gather) {
|
||||
// Wait before refilling the pinned host descriptor buffer.
|
||||
CUDA_CHECK(cudaStreamSynchronize(cudaStreamPerThread));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep the destination pinned for DMA too; overlapping destinations already declined above.
|
||||
for (size_t i = 0; i < n_tensors; ++i) {
|
||||
if (sizes[i] && (!n_reads_gather || sizes[i] > max_gather_read_bytes)) {
|
||||
CUDA_CHECK(cudaMemcpyAsync(data[i], (const char *) tensors[i]->data + offsets[i], sizes[i], cudaMemcpyDeviceToHost, cudaStreamPerThread));
|
||||
}
|
||||
}
|
||||
// Wait for GPU writes before releasing the CPU mapping and descriptor buffers.
|
||||
CUDA_CHECK(cudaStreamSynchronize(cudaStreamPerThread));
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ggml-backend.h"
|
||||
|
||||
bool ggml_cuda_get_tensor_n(int device,
|
||||
const ggml_tensor * const * tensors, void * const * data, const size_t * offsets,
|
||||
const size_t * sizes, size_t n_tensors);
|
||||
@@ -1469,16 +1469,21 @@ struct ggml_backend_cuda_context {
|
||||
// when the computation is split across CPU/GPU (e.g., with --n-cpu-moe)
|
||||
std::unordered_map<const void *, std::unique_ptr<ggml_cuda_graph>> cuda_graphs;
|
||||
|
||||
int64_t last_graph_eviction_sweep = 0;
|
||||
int64_t next_graph_eviction_time = 0;
|
||||
int64_t last_graph_use_time = 0;
|
||||
|
||||
ggml_cuda_graph * cuda_graph(const void * first_node_ptr) {
|
||||
const int64_t time_now = ggml_time_us();
|
||||
ggml_cuda_graph * cuda_graph(const void * first_node_ptr, int64_t time_now = ggml_time_us()) {
|
||||
// Give all cached graphs 10s to be reused after an idle period, without updating each graph.
|
||||
if (!cuda_graphs.empty() && time_now - last_graph_use_time >= 10'000'000) {
|
||||
next_graph_eviction_time = time_now + 10'000'000;
|
||||
}
|
||||
last_graph_use_time = time_now;
|
||||
|
||||
// sweep every 5s, evicting cuda graphs unused for >=10s
|
||||
if (time_now - last_graph_eviction_sweep >= 5'000'000) {
|
||||
last_graph_eviction_sweep = time_now;
|
||||
// Sweep every 5s during active use, evicting graphs unused for at least 10s.
|
||||
if (time_now >= next_graph_eviction_time) {
|
||||
next_graph_eviction_time = time_now + 5'000'000;
|
||||
for (auto it = cuda_graphs.begin(); it != cuda_graphs.end(); ) {
|
||||
if (time_now - it->second->last_used_time >= 10'000'000) {
|
||||
if (it->first != first_node_ptr && time_now - it->second->last_used_time >= 10'000'000) {
|
||||
it = cuda_graphs.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "ggml-cuda/arange.cuh"
|
||||
#include "ggml-cuda/argmax.cuh"
|
||||
#include "ggml-cuda/argsort.cuh"
|
||||
#include "ggml-cuda/batch-get.cuh"
|
||||
#include "ggml-cuda/binbcast.cuh"
|
||||
#include "ggml-cuda/clamp.cuh"
|
||||
#include "ggml-cuda/col2im-1d.cuh"
|
||||
@@ -886,6 +887,11 @@ static void ggml_backend_cuda_buffer_get_tensor_n(ggml_backend_buffer_t buffer,
|
||||
|
||||
ggml_cuda_set_device(ctx->device);
|
||||
|
||||
if (!ggml_cuda_pinned_memory_disabled() &&
|
||||
ggml_cuda_get_tensor_n(ctx->device, tensors, data, offsets, sizes, n_tensors)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n_tensors; i++) {
|
||||
if (sizes[i] == 0) {
|
||||
continue;
|
||||
@@ -1117,6 +1123,18 @@ struct ggml_backend_cuda_comm_context {
|
||||
|
||||
ggml_cuda_ar_pipeline * ar_pipeline = nullptr;
|
||||
|
||||
// Number of ranks in the communicator when each rank is a separate process. Zero unless
|
||||
// comm_init_rank created this context.
|
||||
int world_size = 0;
|
||||
|
||||
// Number of ranks taking part in the reduction. For a communicator from comm_init_rank this is
|
||||
// world_size. Otherwise it is the number of backends this process holds. The NCCL reduction
|
||||
// uses it to choose the tensor size below which it reduces in F32 rather than converting to
|
||||
// BF16 first.
|
||||
size_t n_ranks() const {
|
||||
return world_size > 0 ? (size_t) world_size : backends.size();
|
||||
}
|
||||
|
||||
#ifdef GGML_USE_NCCL
|
||||
std::vector<ncclComm_t> comms;
|
||||
#endif // GGML_USE_NCCL
|
||||
@@ -1144,6 +1162,7 @@ static bool ggml_backend_cuda_comm_allreduce_nccl(
|
||||
}
|
||||
|
||||
const size_t n_backends = comm_ctx->backends.size();
|
||||
const size_t n_ranks = comm_ctx->n_ranks();
|
||||
|
||||
for (size_t i = 0; i < n_backends; ++i) {
|
||||
GGML_ASSERT(tensors[i] != nullptr);
|
||||
@@ -1153,7 +1172,7 @@ static bool ggml_backend_cuda_comm_allreduce_nccl(
|
||||
|
||||
// For small tensors, simply reduce them as FP32.
|
||||
// The following heuristic for how "small" a tensor should be is based on RTX 4090s connected via 16x PCIe 4.0.
|
||||
if ((n_backends <= 2 && ne < 32768) || (n_backends == 3 && ne < 131072) || (n_backends >= 4 && ne < 262144)) {
|
||||
if ((n_ranks <= 2 && ne < 32768) || (n_ranks == 3 && ne < 131072) || (n_ranks >= 4 && ne < 262144)) {
|
||||
for (size_t i = 0; i < n_backends; ++i) {
|
||||
if ((tensors[i]->flags & GGML_TENSOR_FLAG_COMPUTE) == 0) {
|
||||
ggml_backend_cuda_context * cuda_ctx = (ggml_backend_cuda_context *) comm_ctx->backends[i]->context;
|
||||
@@ -1384,6 +1403,90 @@ static void * ggml_backend_cuda_comm_init(ggml_backend_t * backends, size_t n_ba
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef GGML_USE_NCCL
|
||||
// Builds the communicator with ncclCommInitRank, for the case where each rank is its own
|
||||
// process. Blocks until all world_size ranks have joined.
|
||||
static void * ggml_backend_cuda_comm_init_rank(ggml_backend_t backend, int rank, int world_size,
|
||||
ggml_backend_comm_bootstrap_callback bootstrap_callback, void * bootstrap_callback_data) {
|
||||
if (world_size < 1 || rank < 0 || rank >= world_size) {
|
||||
GGML_LOG_ERROR("%s: invalid rank %d for world size %d\n", __func__, rank, world_size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Every rank sends whether its local checks passed, and rank 0 also sends the NCCL unique id.
|
||||
// So all ranks either join together or fail together, and no rank waits in ncclCommInitRank alone.
|
||||
struct bootstrap_msg {
|
||||
uint8_t ok;
|
||||
ncclUniqueId id;
|
||||
};
|
||||
bootstrap_msg local;
|
||||
memset(&local, 0, sizeof(local));
|
||||
local.ok = 1;
|
||||
|
||||
if (!ggml_backend_is_cuda(backend)) {
|
||||
GGML_LOG_ERROR("%s: backend is not a CUDA backend\n", __func__);
|
||||
local.ok = 0;
|
||||
} else {
|
||||
// NCCL requires one distinct physical GPU per rank, see ggml_backend_cuda_comm_init_nccl.
|
||||
const ggml_cuda_device_info & info = ggml_cuda_info();
|
||||
if (info.device_count > info.physical_device_count) {
|
||||
GGML_LOG_ERROR("%s: CUDA virtual devices are in use, which NCCL does not support\n", __func__);
|
||||
local.ok = 0;
|
||||
}
|
||||
}
|
||||
if (local.ok && rank == 0) {
|
||||
const ncclResult_t rc = ncclGetUniqueId(&local.id);
|
||||
if (rc != ncclSuccess) {
|
||||
GGML_LOG_ERROR("%s: ncclGetUniqueId failed (%s)\n", __func__, ncclGetErrorString(rc));
|
||||
local.ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bootstrap_msg> all(world_size);
|
||||
if (!bootstrap_callback(bootstrap_callback_data, &local, all.data(), sizeof(local))) {
|
||||
GGML_LOG_ERROR("%s: bootstrap exchange failed on rank %d\n", __func__, rank);
|
||||
return nullptr;
|
||||
}
|
||||
for (int i = 0; i < world_size; i++) {
|
||||
if (!all[i].ok) {
|
||||
GGML_LOG_ERROR("%s: rank %d failed its setup checks\n", __func__, i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ggml_backend_cuda_context * cuda_ctx = (ggml_backend_cuda_context *) backend->context;
|
||||
|
||||
auto * ret = new ggml_backend_cuda_comm_context;
|
||||
ret->backends.push_back(backend);
|
||||
ret->dev_ids.push_back(cuda_ctx->device);
|
||||
ret->world_size = world_size;
|
||||
ret->comms.resize(1);
|
||||
ret->try_allreduce = ggml_backend_cuda_comm_try_allreduce_nccl;
|
||||
|
||||
// On DGX Spark, NCCL picks 64 channels by default, which is slower and uses more memory than 8.
|
||||
// NCCL_MAX_CTAS still overrides this.
|
||||
ncclConfig_t config = NCCL_CONFIG_INITIALIZER;
|
||||
const int cc = ggml_cuda_info().devices[cuda_ctx->device].cc;
|
||||
if (GGML_CUDA_CC_IS_NVIDIA(cc) && cc == GGML_CUDA_CC_DGX_SPARK) {
|
||||
config.maxCTAs = 8;
|
||||
}
|
||||
|
||||
ggml_cuda_set_device(cuda_ctx->device);
|
||||
const ncclResult_t rc = ncclCommInitRankConfig(&ret->comms[0], world_size, all[0].id, rank, &config);
|
||||
if (rc != ncclSuccess) {
|
||||
GGML_LOG_ERROR("%s: ncclCommInitRankConfig failed for rank %d/%d (%s)\n",
|
||||
__func__, rank, world_size, ncclGetErrorString(rc));
|
||||
ret->comms.clear();
|
||||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GGML_LOG_INFO("%s: joined NCCL communicator as rank %d/%d on device %d\n",
|
||||
__func__, rank, world_size, cuda_ctx->device);
|
||||
return ret;
|
||||
}
|
||||
#endif // GGML_USE_NCCL
|
||||
|
||||
// Top-level dispatch -- calls the function pointer chosen by comm_init.
|
||||
// Returns false to let the meta-backend's butterfly run.
|
||||
static bool ggml_backend_cuda_comm_allreduce_tensor(void * comm_ctx_v, struct ggml_tensor ** tensors) {
|
||||
@@ -6384,6 +6487,11 @@ static void * ggml_backend_cuda_reg_get_proc_address(ggml_backend_reg_t reg, con
|
||||
if (strcmp(name, "ggml_backend_comm_allreduce_tensor") == 0) {
|
||||
return (void *)ggml_backend_cuda_comm_allreduce_tensor;
|
||||
}
|
||||
#ifdef GGML_USE_NCCL
|
||||
if (strcmp(name, "ggml_backend_comm_init_rank") == 0) {
|
||||
return (void *)ggml_backend_cuda_comm_init_rank;
|
||||
}
|
||||
#endif // GGML_USE_NCCL
|
||||
if (strcmp(name, "ggml_backend_register_host_buffer") == 0) {
|
||||
return (void *)ggml_backend_cuda_register_host_buffer;
|
||||
}
|
||||
|
||||
@@ -83,21 +83,21 @@ static __global__ void quantize_q8_1(
|
||||
ggml_cuda_pdl_sync();
|
||||
const float xi = i0 < ne00 ? x[i03*s03 + i02*s02 + i01*s01 + i00] : 0.0f;
|
||||
float amax = fabsf(xi);
|
||||
float sum = xi;
|
||||
|
||||
amax = warp_reduce_max<QK8_1>(amax);
|
||||
sum = warp_reduce_sum<QK8_1>(sum);
|
||||
|
||||
const float d = amax / 127.0f;
|
||||
const int8_t q = amax == 0.0f ? 0 : roundf(xi / d);
|
||||
|
||||
const int sum = warp_reduce_sum<QK8_1>((int) q);
|
||||
|
||||
y[ib].qs[iqs] = q;
|
||||
|
||||
if (iqs > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
y[ib].ds = make_half2(d, sum);
|
||||
y[ib].ds = make_half2(d, d * sum);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ uint8_t compute_e8m0_scale(float amax) {
|
||||
@@ -500,17 +500,6 @@ static __global__ void quantize_mmq_q8_1(
|
||||
amax = fmaxf(amax, __shfl_xor_sync(0xFFFFFFFF, amax, offset, WARP_SIZE));
|
||||
}
|
||||
|
||||
float sum;
|
||||
if (ds_layout != MMQ_Q8_1_DS_LAYOUT_D4) {
|
||||
sum = xi.x + xi.y + xi.z + xi.w;
|
||||
|
||||
// Calculate sums across vals_per_sum/4 threads.
|
||||
#pragma unroll
|
||||
for (int offset = vals_per_sum/8; offset > 0; offset >>= 1) {
|
||||
sum += __shfl_xor_sync(0xFFFFFFFF, sum, offset, WARP_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
const float d_inv = 127.0f / amax;
|
||||
char4 q;
|
||||
q.x = roundf(xi.x*d_inv);
|
||||
@@ -519,6 +508,19 @@ static __global__ void quantize_mmq_q8_1(
|
||||
q.w = roundf(xi.w*d_inv);
|
||||
const float d = 1.0f / d_inv;
|
||||
|
||||
float sum = 0.0f;
|
||||
if (ds_layout != MMQ_Q8_1_DS_LAYOUT_D4) {
|
||||
int sum_q = int(q.x) + int(q.y) + int(q.z) + int(q.w);
|
||||
|
||||
// Calculate sums across vals_per_sum/4 threads.
|
||||
#pragma unroll
|
||||
for (int offset = vals_per_sum/8; offset > 0; offset >>= 1) {
|
||||
sum_q += __shfl_xor_sync(0xFFFFFFFF, sum_q, offset, WARP_SIZE);
|
||||
}
|
||||
|
||||
sum = d * float(sum_q);
|
||||
}
|
||||
|
||||
// write the block once (normal) or to each of the token's compact rows (scatter)
|
||||
const int nwrite = scatter ? n_expert_used : 1;
|
||||
#pragma unroll
|
||||
|
||||
@@ -135,6 +135,7 @@ size_t opt_mbuf_total = 0; // total buffer space limit (
|
||||
int opt_mm_select = 2; // 2 = HMX -> HVX -> CPU, 1 = HVX -> CPU, 0 = CPU (unsupported)
|
||||
int opt_fa_select = 2; // 2 = HMX -> HVX -> CPU, 1 = HVX -> CPU, 0 = CPU (unsupported)
|
||||
int opt_fa_split = 1; // 1 = head-split + team-split when n_kv_heads < N_cores (default), 0 = off
|
||||
int opt_li_select = 2; // 2 = HMX -> HVX, 1 = HVX, 0 = CPU (unsupported)
|
||||
int opt_gdn_select = 2; // 2 = HMX -> HVX, 1 = HVX, 0 = CPU (unsupported)
|
||||
int opt_ar_select = 2; // 2 = fused ALLREDUCE+ADD (default), 1 = unfused ALLREDUCE, 0 = fallback to CPY+FENCE
|
||||
int opt_ar_scatter = 1; // 1 = reduce-scatter the fused ALLREDUCE+ADD (default), 0 = full reduction
|
||||
@@ -3409,6 +3410,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
const char * str_mm_select = getenv("GGML_HEXAGON_MM_SELECT");
|
||||
const char * str_fa_select = getenv("GGML_HEXAGON_FA_SELECT");
|
||||
const char * str_fa_split = getenv("GGML_HEXAGON_FA_SPLIT");
|
||||
const char * str_li_select = getenv("GGML_HEXAGON_LI_SELECT");
|
||||
const char * str_gdn_select = getenv("GGML_HEXAGON_GDN_SELECT");
|
||||
const char * str_ar_select = getenv("GGML_HEXAGON_AR_SELECT");
|
||||
const char * str_ar_scatter = getenv("GGML_HEXAGON_AR_SCATTER");
|
||||
@@ -3475,6 +3477,7 @@ static void ggml_hexagon_init(ggml_backend_reg * reg) {
|
||||
opt_mm_select = str_mm_select ? atoi(str_mm_select) : opt_mm_select;
|
||||
opt_fa_select = str_fa_select ? atoi(str_fa_select) : opt_fa_select;
|
||||
opt_fa_split = str_fa_split ? atoi(str_fa_split) : opt_fa_split;
|
||||
opt_li_select = str_li_select ? atoi(str_li_select) : opt_li_select;
|
||||
opt_gdn_select = str_gdn_select ? atoi(str_gdn_select) : opt_gdn_select;
|
||||
opt_ar_select = str_ar_select ? atoi(str_ar_select) : opt_ar_select;
|
||||
opt_ar_scatter = str_ar_scatter ? atoi(str_ar_scatter) : opt_ar_scatter;
|
||||
|
||||
@@ -29,6 +29,7 @@ extern size_t opt_mbuf_total;
|
||||
extern int opt_mm_select;
|
||||
extern int opt_fa_select;
|
||||
extern int opt_fa_split;
|
||||
extern int opt_li_select;
|
||||
extern int opt_gdn_select;
|
||||
extern int opt_ar_select;
|
||||
extern int opt_ar_scatter;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,12 +11,14 @@
|
||||
#include "htp/cpy-ops.h"
|
||||
#include "htp/cumsum-ops.h"
|
||||
#include "htp/diag-ops.h"
|
||||
#include "htp/dsv4-hc-ops.h"
|
||||
#include "htp/fill-ops.h"
|
||||
#include "htp/flash-attn-ops.h"
|
||||
#include "htp/gated-delta-net-ops.h"
|
||||
#include "htp/get-rows-ops.h"
|
||||
#include "htp/glu-ops.h"
|
||||
#include "htp/im2col-ops.h"
|
||||
#include "htp/lightning-indexer-ops.h"
|
||||
#include "htp/matmul-ops.h"
|
||||
#include "htp/pad-ops.h"
|
||||
#include "htp/pool-ops.h"
|
||||
@@ -28,6 +30,7 @@
|
||||
#include "htp/solve-tri-ops.h"
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/top-k-mask-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
#include "htp/upscale-ops.h"
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
#include "htp/binary-ops.h"
|
||||
#include "htp/cumsum-ops.h"
|
||||
#include "htp/diag-ops.h"
|
||||
#include "htp/dsv4-hc-ops.h"
|
||||
#include "htp/fill-ops.h"
|
||||
#include "htp/flash-attn-ops.h"
|
||||
#include "htp/gated-delta-net-ops.h"
|
||||
#include "htp/get-rows-ops.h"
|
||||
#include "htp/glu-ops.h"
|
||||
#include "htp/lightning-indexer-ops.h"
|
||||
#include "htp/matmul-ops.h"
|
||||
#include "htp/pad-ops.h"
|
||||
#include "htp/repeat-ops.h"
|
||||
@@ -18,6 +20,7 @@
|
||||
#include "htp/solve-tri-ops.h"
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/top-k-mask-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
#include "htp/upscale-ops.h"
|
||||
|
||||
@@ -185,6 +188,22 @@ static std::string format_kernel_params(const htp_opnode & node) {
|
||||
const char * path = (kparams->kernel_type == HTP_GDN_KERNEL_HMX_CHUNKED) ? "hmx-chunked" : "hvx-recurrent";
|
||||
snprintf(str, max_size, "%s-%s vtcm %u", path, kparams->kda ? "kda" : "scalar",
|
||||
(unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_DSV4_HC_PRE) {
|
||||
const auto * kparams = node.kparams<htp_dsv4_hc_pre_kernel_params>();
|
||||
const char * ktype_str = kparams->kernel_type == HTP_DSV4_HC_PRE_KERNEL_GATED ? "gated" : "plain";
|
||||
snprintf(str, max_size, "%s nth %u vtcm %u", ktype_str,
|
||||
(unsigned int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_DSV4_HC_POST) {
|
||||
const auto * kparams = node.kparams<htp_dsv4_hc_post_kernel_params>();
|
||||
const char * ktype_str = kparams->kernel_type == HTP_DSV4_HC_POST_KERNEL_COMB ? "comb" : "identity";
|
||||
snprintf(str, max_size, "%s nth %u vtcm %u", ktype_str,
|
||||
(unsigned int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_LIGHTNING_INDEXER) {
|
||||
const auto * kparams = node.kparams<htp_lid_kernel_params>();
|
||||
snprintf(str, max_size, "%s tok %u bc %u nth %u vtcm %u",
|
||||
kparams->kernel_type == HTP_LID_KERNEL_HMX ? "hmx" : "hvx",
|
||||
(unsigned int) kparams->n_tokens_tile, (unsigned int) kparams->kv_block,
|
||||
(unsigned int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_MUL || node.opcode == HTP_OP_ADD || node.opcode == HTP_OP_ADD_ID ||
|
||||
node.opcode == HTP_OP_SUB || node.opcode == HTP_OP_DIV) {
|
||||
const auto * kparams = node.kparams<htp_binary_kernel_params>();
|
||||
@@ -213,6 +232,14 @@ static std::string format_kernel_params(const htp_opnode & node) {
|
||||
} else if (node.opcode == HTP_OP_CUMSUM) {
|
||||
const auto * kparams = node.kparams<htp_cumsum_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d vtcm %u", kparams->use_dma ? "dma" : "hvx", (int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_TOP_K_MASK) {
|
||||
const auto * kparams = node.kparams<htp_top_k_mask_kernel_params>();
|
||||
const char * ktype_str = "?";
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_TOP_K_MASK_KERNEL_ROWS: ktype_str = "rows"; break;
|
||||
case HTP_TOP_K_MASK_KERNEL_PICKS: ktype_str = "picks"; break;
|
||||
}
|
||||
snprintf(str, max_size, "%s nth %d tile %u x%u vtcm %u", ktype_str, (int) kparams->n_threads, (unsigned int) kparams->tile_cols, (unsigned int) kparams->n_col_tiles, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_FILL) {
|
||||
const auto * kparams = node.kparams<htp_fill_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d", kparams->kernel_type == HTP_FILL_KERNEL_CONTIGUOUS ? "contig" : "strided", (int) kparams->n_threads);
|
||||
|
||||
@@ -37,6 +37,7 @@ add_library(${HTP_LIB} SHARED
|
||||
repeat-ops.c
|
||||
ssm-conv.c
|
||||
cumsum-ops.c
|
||||
top-k-mask-ops.c
|
||||
fill-ops.c
|
||||
concat-ops.c
|
||||
diag-ops.c
|
||||
@@ -49,6 +50,8 @@ add_library(${HTP_LIB} SHARED
|
||||
pool-ops.c
|
||||
upscale-ops.c
|
||||
allreduce-ops.c
|
||||
dsv4-hc-ops.c
|
||||
lightning-indexer-ops.c
|
||||
)
|
||||
|
||||
target_compile_definitions(${HTP_LIB} PRIVATE
|
||||
|
||||
@@ -315,55 +315,61 @@ static void bitonic_sort_vtcm_desc(uint8_t * values, uint8_t * indices, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
static void top_k_select_tiled(dma_queue * dma_q, struct htp_thread_trace * tr,
|
||||
dma_addr_t src_addr, uint32_t n, uint32_t k,
|
||||
float * values_buf, int32_t * indices_buf,
|
||||
float * out_values, int32_t * out_indices,
|
||||
uint32_t chunk_base) {
|
||||
const uint32_t tile_elems = 1024;
|
||||
uint32_t n_tiles = (n + tile_elems - 1) / tile_elems;
|
||||
uint32_t candidate_count = n_tiles * k;
|
||||
uint32_t merge_n_vec = hmx_ceil_div(candidate_count, 32);
|
||||
uint32_t merge_n_vec_pow2 = 1;
|
||||
while (merge_n_vec_pow2 < merge_n_vec) merge_n_vec_pow2 <<= 1;
|
||||
uint32_t merge_elems = merge_n_vec_pow2 * 32;
|
||||
float * candidate_values = values_buf + tile_elems;
|
||||
int32_t * candidate_indices = indices_buf + tile_elems;
|
||||
uint32_t candidate_pos = 0;
|
||||
// top-k of one row held in VTCM, k <= 64. Keeps a sorted list of the k best, rejects 256-float blocks with one max-reduce.
|
||||
// Writes k values and k indices (ties: first seen wins). Slots with no finite value get -INF and index 0.
|
||||
static void top_k_scan(const float * vals, uint32_t n, uint32_t k, float * out_v, int32_t * out_i) {
|
||||
float best_v[64];
|
||||
int32_t best_i[64];
|
||||
uint32_t cnt = 0;
|
||||
float thr = -INFINITY;
|
||||
|
||||
for (uint32_t offset = 0; offset < n; offset += tile_elems) {
|
||||
uint32_t tile_count = MIN(tile_elems, n - offset);
|
||||
dma_sync_read(dma_q, values_buf, src_addr + (size_t) offset * sizeof(float), tile_count * sizeof(float));
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) offset);
|
||||
if (tile_count < tile_elems) {
|
||||
hvx_splat_f32_u((uint8_t *) (values_buf + tile_count), -INFINITY, tile_elems - tile_count);
|
||||
const uint32_t blk = 256;
|
||||
for (uint32_t off = 0; off < n; off += blk) {
|
||||
const uint32_t bn = MIN(blk, n - off);
|
||||
if (hvx_reduce_max_f32((const uint8_t *) (vals + off), bn) <= thr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bitonic_sort_vtcm_desc((uint8_t *) values_buf, (uint8_t *) indices_buf, tile_elems / 32, true);
|
||||
uint32_t tile_k = MIN(k, tile_count);
|
||||
for (uint32_t j = 0; j < tile_k; j++) {
|
||||
candidate_values[candidate_pos] = values_buf[j];
|
||||
candidate_indices[candidate_pos] = indices_buf[j] + (int32_t) offset;
|
||||
candidate_pos++;
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) offset);
|
||||
}
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, 0);
|
||||
if (merge_elems > candidate_pos) {
|
||||
hvx_splat_f32_u((uint8_t *) (candidate_values + candidate_pos), -INFINITY, merge_elems - candidate_pos);
|
||||
for (uint32_t j = candidate_pos; j < merge_elems; j++) {
|
||||
candidate_indices[j] = 0;
|
||||
for (uint32_t vi = 0; vi < bn; vi += 32) {
|
||||
const uint32_t vn = MIN(32, bn - vi);
|
||||
HVX_VectorAlias v_local;
|
||||
v_local.v = hvx_vmem(vals + off + vi);
|
||||
for (uint32_t i = 0; i < vn; i++) {
|
||||
const float v = v_local.fp32[i];
|
||||
if (!(v > thr)) continue;
|
||||
uint32_t pos = cnt < k ? cnt++ : k - 1;
|
||||
while (pos > 0 && best_v[pos - 1] < v) {
|
||||
best_v[pos] = best_v[pos - 1];
|
||||
best_i[pos] = best_i[pos - 1];
|
||||
pos--;
|
||||
}
|
||||
best_v[pos] = v;
|
||||
best_i[pos] = (int32_t) (off + vi + i);
|
||||
if (cnt == k) thr = best_v[k - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitonic_sort_vtcm_desc((uint8_t *) candidate_values, (uint8_t *) candidate_indices, merge_n_vec_pow2, false);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, 0);
|
||||
// fewer than k values above -INF: take -INF or NaN elements, then pad with index 0
|
||||
for (uint32_t off = 0; off < n && cnt < k; off += 32) {
|
||||
const uint32_t vn = MIN(32, n - off);
|
||||
HVX_VectorAlias v_local;
|
||||
v_local.v = hvx_vmem(vals + off);
|
||||
for (uint32_t j = 0; j < vn && cnt < k; j++) {
|
||||
if (!(v_local.fp32[j] > -INFINITY)) {
|
||||
best_v[cnt] = -INFINITY;
|
||||
best_i[cnt] = (int32_t) (off + j);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (; cnt < k; cnt++) {
|
||||
best_v[cnt] = -INFINITY;
|
||||
best_i[cnt] = 0;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < k; j++) {
|
||||
out_values[j] = candidate_values[j];
|
||||
out_indices[j] = candidate_indices[j] + (int32_t) chunk_base;
|
||||
out_v[j] = best_v[j];
|
||||
out_i[j] = best_i[j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,10 +515,14 @@ static void htp_sort_chunk_job(unsigned int n, unsigned int i, void * data) {
|
||||
}
|
||||
|
||||
if (local_k > 1 && local_k <= 64 && chunk_elems > 1024) {
|
||||
top_k_select_tiled(octx->ctx->dma[i], tr, src_addr, real_count, local_k,
|
||||
values_buf, indices_buf,
|
||||
&merge_values[i * local_k], &merge_indices[i * local_k],
|
||||
chunk_base);
|
||||
dma_sync_read(octx->ctx->dma[i], values_buf, src_addr, real_count * sizeof(float));
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
top_k_scan(values_buf, real_count, local_k, &merge_values[i * local_k], &merge_indices[i * local_k]);
|
||||
for (uint32_t j = 0; j < local_k; j++) {
|
||||
merge_indices[i * local_k + j] += (int32_t) chunk_base;
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -668,10 +678,14 @@ static inline void compute_row_sort(
|
||||
uint32_t ne00,
|
||||
uint32_t chunk_elems,
|
||||
bool is_top_k,
|
||||
enum ggml_sort_order order
|
||||
enum ggml_sort_order order,
|
||||
uint32_t k
|
||||
) {
|
||||
if (is_top_k) {
|
||||
if (ne00 == 32 || ne00 == 64 || ne00 == 128 || ne00 == 256 || ne00 == 512 || ne00 == 1024) {
|
||||
if (k > 1 && k <= 64 && ne00 > 1024) {
|
||||
float out_v[64];
|
||||
top_k_scan(cur_values, ne00, k, out_v, cur_indices);
|
||||
} else if (ne00 == 32 || ne00 == 64 || ne00 == 128 || ne00 == 256 || ne00 == 512 || ne00 == 1024) {
|
||||
switch (ne00) {
|
||||
case 32: sort32_f32_hvx((uint8_t *) cur_values, (uint8_t *) cur_indices, GGML_SORT_ORDER_DESC); break;
|
||||
case 64: sort64_f32_hvx((uint8_t *) cur_values, (uint8_t *) cur_indices, GGML_SORT_ORDER_DESC); break;
|
||||
@@ -788,7 +802,7 @@ static void htp_sort_multi_row_job(unsigned int n, unsigned int ith, void * data
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
compute_row_sort(cur_values, cur_indices, ne00, chunk_elems, is_top_k, order);
|
||||
compute_row_sort(cur_values, cur_indices, ne00, chunk_elems, is_top_k, order, k);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
|
||||
// Push writeback of current slot
|
||||
@@ -817,7 +831,7 @@ static void htp_sort_multi_row_job(unsigned int n, unsigned int ith, void * data
|
||||
dma_sync_read(dma_q, values_buf, src_addr, src_bytes);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
compute_row_sort(values_buf, indices_buf, ne00, chunk_elems, is_top_k, order);
|
||||
compute_row_sort(values_buf, indices_buf, ne00, chunk_elems, is_top_k, order, k);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
|
||||
dma_sync_write(dma_q, dst_addr, indices_buf, dst_bytes);
|
||||
|
||||
@@ -35,6 +35,10 @@ struct htp_copy_context {
|
||||
struct htp_copy_convert_vtcm_layout layout;
|
||||
uint8_t * vtcm_src0;
|
||||
uint8_t * vtcm_dst;
|
||||
|
||||
uint32_t tile_start;
|
||||
uint32_t ntiles;
|
||||
uint32_t tiles_per_thread;
|
||||
};
|
||||
|
||||
#define cpy_preamble \
|
||||
@@ -162,6 +166,171 @@ static inline void cpy_cursor_advance(struct cpy_row_cursor * c, uint32_t n, uin
|
||||
}
|
||||
}
|
||||
|
||||
struct cpy_tile_cursor {
|
||||
uint32_t ibk;
|
||||
uint32_t ib0;
|
||||
uint32_t i2;
|
||||
uint32_t i3;
|
||||
};
|
||||
|
||||
static inline void cpy_tile_cursor_advance(struct cpy_tile_cursor * c, const struct htp_copy_transpose_params * tp) {
|
||||
if (++c->ibk == tp->nkb) {
|
||||
c->ibk = 0;
|
||||
if (++c->ib0 == tp->n0b) {
|
||||
c->ib0 = 0;
|
||||
if (++c->i2 == tp->ne[2]) {
|
||||
c->i2 = 0;
|
||||
c->i3++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gather offsets for element e = l >> log2_m at (e % r, e / r)
|
||||
static inline HVX_Vector cpy_transpose_offsets(const struct htp_copy_transpose_params * tp) {
|
||||
const uint32_t r_mask = (1u << tp->log2_r) - 1;
|
||||
if (tp->elem_size != 2) {
|
||||
const uint32_t m_mask = (1u << tp->log2_m) - 1;
|
||||
int32_t offsets[32] __attribute__((aligned(128)));
|
||||
for (uint32_t l = 0; l < 32; l++) {
|
||||
const uint32_t e = l >> tp->log2_m;
|
||||
offsets[l] = (e & r_mask) * tp->in_stride + (e >> tp->log2_r) * tp->elem_size + (l & m_mask) * 4;
|
||||
}
|
||||
return *(HVX_Vector *) offsets;
|
||||
}
|
||||
int16_t offsets[64] __attribute__((aligned(128)));
|
||||
for (uint32_t l = 0; l < 64; l++) {
|
||||
offsets[l] = (l & r_mask) * tp->in_stride + (l >> tp->log2_r) * 2;
|
||||
}
|
||||
return *(HVX_Vector *) offsets;
|
||||
}
|
||||
|
||||
static inline void cpy_transpose_push_in(dma_queue * q, const struct htp_copy_transpose_params * tp, dma_addr_t src,
|
||||
const struct cpy_tile_cursor * c, uint8_t * buf) {
|
||||
const uint32_t a0 = c->ib0 * tp->blk0;
|
||||
const uint32_t ak = c->ibk * tp->blkk;
|
||||
const uint32_t off = a0 * tp->src_nb[0] + ak * tp->elem_size + c->i2 * tp->src_nb[1] + c->i3 * tp->src_nb[2];
|
||||
dma_queue_push(q, dma_make_data(buf, src + off), tp->in_stride, tp->src_nb[0],
|
||||
MIN(tp->blkk, tp->ne[1] - ak) * tp->elem_size, MIN(tp->blk0, tp->ne[0] - a0));
|
||||
}
|
||||
|
||||
// Pipelined tile worker: DMA in -> vgather transpose -> DMA out
|
||||
static void cpy_thread_transpose(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct htp_copy_context * ct = (struct htp_copy_context *) data;
|
||||
struct htp_ops_context * octx = ct->octx;
|
||||
const struct htp_copy_transpose_params * tp = &ct->kparams->u.transpose;
|
||||
|
||||
const uint32_t t0 = ct->tile_start + ith * ct->tiles_per_thread;
|
||||
const uint32_t t1 = MIN(t0 + ct->tiles_per_thread, ct->tile_start + ct->ntiles);
|
||||
if (t0 >= t1) {
|
||||
return;
|
||||
}
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const dma_addr_t src = octx->src[0]->data;
|
||||
const dma_addr_t dst = octx->dst->data;
|
||||
|
||||
const uint32_t es = tp->elem_size;
|
||||
const uint32_t ne0 = tp->ne[0];
|
||||
const uint32_t nek = tp->ne[1];
|
||||
const uint32_t in_stride = tp->in_stride;
|
||||
const uint32_t out_stride = tp->out_stride;
|
||||
|
||||
const HVX_Vector offsets = cpy_transpose_offsets(tp);
|
||||
const uint32_t packed_mu = (ne0 - 1) * in_stride + 127;
|
||||
const uint32_t packed_step = 128 >> tp->log2_r;
|
||||
const uint32_t epv = 1u << tp->log2_r;
|
||||
|
||||
uint8_t * in_buf[2];
|
||||
uint8_t * out_buf[2];
|
||||
in_buf[0] = ct->vtcm_src0 + ith * 2 * tp->in_buf_size;
|
||||
in_buf[1] = in_buf[0] + tp->in_buf_size;
|
||||
out_buf[0] = ct->vtcm_dst + ith * 2 * tp->out_buf_size;
|
||||
out_buf[1] = out_buf[0] + tp->out_buf_size;
|
||||
|
||||
struct cpy_tile_cursor cur;
|
||||
{
|
||||
const uint32_t q0 = fastdiv(t0, &tp->div_nkb);
|
||||
const uint32_t q1 = fastdiv(q0, &tp->div_n0b);
|
||||
cur.ibk = t0 - q0 * tp->nkb;
|
||||
cur.ib0 = q0 - q1 * tp->n0b;
|
||||
cur.i3 = fastdiv(q1, &tp->div_ne2);
|
||||
cur.i2 = q1 - cur.i3 * tp->ne[2];
|
||||
}
|
||||
|
||||
struct cpy_tile_cursor nxt = cur;
|
||||
|
||||
cpy_transpose_push_in(dma_q, tp, src, &cur, in_buf[0]);
|
||||
|
||||
for (uint32_t t = t0, i = 0; t < t1; t++, i++) {
|
||||
while (dma_queue_depth(dma_q) > (i > 0 ? 1u : 0u)) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
|
||||
uint8_t * in = in_buf[i & 1];
|
||||
uint8_t * out = out_buf[i & 1];
|
||||
|
||||
const uint32_t a0 = cur.ib0 * tp->blk0;
|
||||
const uint32_t ak = cur.ibk * tp->blkk;
|
||||
const uint32_t n0 = MIN(tp->blk0, ne0 - a0);
|
||||
const uint32_t nk = MIN(tp->blkk, nek - ak);
|
||||
|
||||
if (t + 1 < t1) {
|
||||
cpy_tile_cursor_advance(&nxt, tp);
|
||||
cpy_transpose_push_in(dma_q, tp, src, &nxt, in_buf[(i + 1) & 1]);
|
||||
}
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) t);
|
||||
if (tp->packed) {
|
||||
const uint32_t nvec = (nk * ne0 * es + 127) >> 7;
|
||||
if (es != 2) {
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (out + v * 128), (size_t) (in + v * packed_step), packed_mu, offsets);
|
||||
}
|
||||
} else {
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVh((HVX_Vector *) (out + v * 128), (size_t) (in + v * packed_step), packed_mu, offsets);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t rb = 0; rb < n0; rb += epv) {
|
||||
const uint32_t mu = (MIN(epv, n0 - rb) - 1) * in_stride + es - 1;
|
||||
uint8_t * d = out + rb * es;
|
||||
const uint8_t * s = in + rb * in_stride;
|
||||
if (es != 2) {
|
||||
#pragma unroll(4)
|
||||
for (uint32_t ik = 0; ik < nk; ik++) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (d + ik * out_stride), (size_t) (s + ik * es), mu, offsets);
|
||||
}
|
||||
} else {
|
||||
#pragma unroll(4)
|
||||
for (uint32_t ik = 0; ik < nk; ik++) {
|
||||
Q6_vgather_ARMVh((HVX_Vector *) (d + ik * out_stride), (size_t) (s + ik * 2), mu, offsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hvx_gather_sync(out);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) t);
|
||||
|
||||
const uint32_t doff = a0 * es + ak * tp->dst_nb[0] + cur.i2 * tp->dst_nb[1] + cur.i3 * tp->dst_nb[2];
|
||||
if (tp->packed) {
|
||||
const uint32_t n = nk * ne0 * es;
|
||||
dma_queue_push(dma_q, dma_make_data(dst + doff, out), n, n, n, 1);
|
||||
} else {
|
||||
dma_queue_push(dma_q, dma_make_data(dst + doff, out), tp->dst_nb[0], out_stride, n0 * es, nk);
|
||||
}
|
||||
|
||||
cur = nxt;
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
#define DEFINE_CPY_CONVERT_SAMESHAPE(NAME, CONV_FUNC) \
|
||||
static void cpy_thread_##NAME##_sameshape(unsigned int nth, unsigned int ith, void * data) { \
|
||||
struct htp_copy_context * ct = (struct htp_copy_context *) data; \
|
||||
@@ -439,6 +608,48 @@ static int cpy_reshape(struct htp_ops_context * octx, const struct htp_copy_kern
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int cpy_transpose(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const struct htp_copy_transpose_params * tp = &kparams->u.transpose;
|
||||
|
||||
uint32_t tile_start = 0;
|
||||
uint32_t ntiles = tp->total_tiles;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
tp->total_tiles, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
tile_start = range.start;
|
||||
ntiles = range.count;
|
||||
}
|
||||
|
||||
if (ntiles == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (kparams->vtcm_size > octx->ctx->vtcm_size) {
|
||||
return HTP_STATUS_VTCM_TOO_SMALL;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
struct htp_copy_context ct;
|
||||
ct.octx = octx;
|
||||
ct.kparams = kparams;
|
||||
ct.tile_start = tile_start;
|
||||
ct.ntiles = ntiles;
|
||||
ct.tiles_per_thread = fastdiv(ntiles + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
uint8_t * vtcm_base = (uint8_t *) octx->ctx->vtcm_base;
|
||||
ct.vtcm_src0 = vtcm_base;
|
||||
ct.vtcm_dst = vtcm_base + (size_t) n_threads * 2 * tp->in_buf_size;
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, cpy_thread_transpose, &ct, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_cpy(struct htp_ops_context * octx) {
|
||||
const struct htp_copy_kernel_params * kparams = (const struct htp_copy_kernel_params *) octx->kernel_params;
|
||||
int status = HTP_STATUS_OK;
|
||||
@@ -459,6 +670,9 @@ int op_cpy(struct htp_ops_context * octx) {
|
||||
case HTP_COPY_KERNEL_RESHAPE:
|
||||
status = cpy_reshape(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_TRANSPOSE:
|
||||
status = cpy_transpose(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_GATHER_ROWS:
|
||||
status = htp_gather_rows(octx, octx->src[0], NULL, &kparams->u.gather, kparams->n_threads);
|
||||
break;
|
||||
|
||||
@@ -15,6 +15,7 @@ enum htp_copy_kernel_type {
|
||||
HTP_COPY_KERNEL_RESHAPE = 4,
|
||||
HTP_COPY_KERNEL_SCALAR = 5,
|
||||
HTP_COPY_KERNEL_GATHER_ROWS = 6,
|
||||
HTP_COPY_KERNEL_TRANSPOSE = 7,
|
||||
};
|
||||
|
||||
struct htp_copy_convert_params {
|
||||
@@ -32,6 +33,29 @@ struct htp_copy_reshape_params {
|
||||
struct fastdiv_values div_ne02_ne01_ne00;
|
||||
};
|
||||
|
||||
// Batched 2D transpose of elem_size elements (dim 0 dense in dst, dim 1 dense in src)
|
||||
struct htp_copy_transpose_params {
|
||||
uint32_t ne[3]; // dims 0..2
|
||||
uint32_t src_nb[3]; // src strides for dims 0, 2, 3
|
||||
uint32_t dst_nb[3]; // dst strides for dims 1, 2, 3
|
||||
struct fastdiv_values div_nkb;
|
||||
struct fastdiv_values div_n0b;
|
||||
struct fastdiv_values div_ne2;
|
||||
uint32_t blk0; // dim-0 elements per tile
|
||||
uint32_t blkk; // dim-1 elements per tile
|
||||
uint32_t n0b; // tiles along dim 0
|
||||
uint32_t nkb; // tiles along dim 1
|
||||
uint32_t in_stride; // VTCM row pitch of staged src tile
|
||||
uint32_t out_stride; // VTCM row pitch of transposed tile
|
||||
uint32_t in_buf_size;
|
||||
uint32_t out_buf_size;
|
||||
uint32_t total_tiles;
|
||||
uint8_t elem_size;
|
||||
uint8_t packed; // gather straight to dst order
|
||||
uint8_t log2_m; // log2(gather lanes per element)
|
||||
uint8_t log2_r; // log2(dim-0 elements per vector)
|
||||
};
|
||||
|
||||
struct htp_copy_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t src0_type_size;
|
||||
@@ -46,6 +70,7 @@ struct htp_copy_kernel_params {
|
||||
struct htp_copy_convert_params convert;
|
||||
struct htp_copy_reshape_params reshape;
|
||||
struct htp_gather_rows_params gather;
|
||||
struct htp_copy_transpose_params transpose;
|
||||
} u;
|
||||
};
|
||||
|
||||
@@ -116,6 +141,76 @@ static inline void htp_copy_convert_solve_layout(
|
||||
htp_copy_convert_vtcm_layout_build(layout, ne00, src_type_size, dst_type_size, blk_rows, n_threads);
|
||||
}
|
||||
|
||||
#define HTP_COPY_TRANSPOSE_BLK_BYTES (64 * 1024)
|
||||
|
||||
static inline bool htp_copy_transpose_layout_build(struct htp_copy_transpose_params * tp, uint32_t blk_bytes) {
|
||||
const uint32_t ne0 = tp->ne[0];
|
||||
const uint32_t nek = tp->ne[1];
|
||||
const uint32_t es = tp->elem_size;
|
||||
const uint32_t epv = 128 / es;
|
||||
|
||||
uint32_t blk0;
|
||||
uint32_t blkk;
|
||||
if (tp->packed) {
|
||||
// Packed: vector holds epv / ne0 whole dst rows
|
||||
blk0 = ne0;
|
||||
blkk = blk_bytes / (ne0 * es);
|
||||
if (es == 2 && blkk > 16384 / ne0 - 64) {
|
||||
blkk = 16384 / ne0 - 64;
|
||||
}
|
||||
if (blkk > nek) {
|
||||
blkk = nek;
|
||||
}
|
||||
if (blkk == 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (nek <= epv) {
|
||||
// Stage short contiguous dim and expand dim 0
|
||||
blkk = nek;
|
||||
blk0 = blk_bytes / (nek * es);
|
||||
blk0 -= blk0 % epv;
|
||||
if (blk0 > hex_round_up(ne0, epv)) {
|
||||
blk0 = hex_round_up(ne0, epv);
|
||||
}
|
||||
if (blk0 < epv) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
blk0 = hex_round_up(ne0, epv);
|
||||
if (blk0 > 256) {
|
||||
blk0 = 256;
|
||||
}
|
||||
blkk = blk_bytes / (blk0 * es);
|
||||
blkk -= blkk % epv;
|
||||
// Cap f16 blkk to 256 for 16-bit gather offsets
|
||||
const uint32_t max_blkk = (es == 2 ? 512 : 4096) / es;
|
||||
if (blkk > max_blkk) {
|
||||
blkk = max_blkk;
|
||||
}
|
||||
if (blkk > hex_round_up(nek, epv)) {
|
||||
blkk = hex_round_up(nek, epv);
|
||||
}
|
||||
if (blkk < epv) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tp->blk0 = blk0;
|
||||
tp->blkk = blkk;
|
||||
tp->n0b = (ne0 + blk0 - 1) / blk0;
|
||||
tp->nkb = (nek + blkk - 1) / blkk;
|
||||
// Pad row pitch by 1 element to avoid VTCM bank conflicts
|
||||
const uint32_t pad = (es == 2) ? 2 : 4;
|
||||
tp->in_stride = blkk * es + pad;
|
||||
tp->out_stride = blk0 * es;
|
||||
// Extra vector for packed gather overhang
|
||||
tp->in_buf_size = hex_round_up(blk0 * tp->in_stride + (tp->packed ? 128 : 0), 128);
|
||||
tp->out_buf_size = hex_round_up(blkk * tp->out_stride, 128);
|
||||
tp->log2_m = es == 2 ? 0 : __builtin_ctz(es) - 2;
|
||||
tp->log2_r = __builtin_ctz(tp->packed ? ne0 : epv);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_copy_kernel_params) <= 128, "htp_copy_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
|
||||
@@ -141,43 +141,9 @@ bool dma_queue_push_fallback_2d(dma_queue * q, dma_data ddata, size_t dst_stride
|
||||
dma_ring_flush(r1);
|
||||
r0->tail = r1->tail;
|
||||
|
||||
return dma_ring_push_single_2d(r0, ddata, 0, 0, 0, /*nrows=*/ 0);
|
||||
}
|
||||
|
||||
bool dma_queue_push_fallback_contig(dma_queue * q, dma_data ddata, size_t total) {
|
||||
dma_ring * r0 = q->ring0;
|
||||
dma_ring * r1 = q->ring1;
|
||||
|
||||
if (((r0->push_idx + 1) & r0->idx_mask) == r0->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r1->tail = r0->tail;
|
||||
|
||||
size_t rem_bytes = total;
|
||||
dma_addr_t cur_dst = ddata.dst;
|
||||
dma_addr_t cur_src = ddata.src;
|
||||
|
||||
while (rem_bytes > 0) {
|
||||
const uint32_t cur_bytes = MIN(rem_bytes, DMA_SAFE_CHUNK_SIZE);
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_contig(r1, cur_data, cur_bytes)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_contig(r1, cur_data, cur_bytes);
|
||||
}
|
||||
cur_dst += cur_bytes;
|
||||
cur_src += cur_bytes;
|
||||
rem_bytes -= cur_bytes;
|
||||
}
|
||||
|
||||
dma_ring_flush(r1);
|
||||
r0->tail = r1->tail;
|
||||
|
||||
return dma_ring_push_single_1d(r0, ddata, /*size=*/ 0);
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
|
||||
bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
dma_ring * r0 = q->ring0;
|
||||
dma_ring * r1 = q->ring1;
|
||||
@@ -188,19 +154,28 @@ bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride
|
||||
|
||||
r1->tail = r0->tail;
|
||||
|
||||
size_t rem_rows = nrows;
|
||||
dma_addr_t cur_dst = ddata.dst;
|
||||
dma_addr_t cur_src = ddata.src;
|
||||
dma_addr_t row_dst = ddata.dst;
|
||||
dma_addr_t row_src = ddata.src;
|
||||
|
||||
while (rem_rows > 0) {
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_1d(r1, cur_data, row_size)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_1d(r1, cur_data, row_size);
|
||||
for (size_t i = 0; i < nrows; i++) {
|
||||
size_t rem_bytes = row_size;
|
||||
dma_addr_t cur_dst = row_dst;
|
||||
dma_addr_t cur_src = row_src;
|
||||
|
||||
while (rem_bytes > 0) {
|
||||
const uint32_t cur_bytes = MIN(rem_bytes, DMA_SAFE_CHUNK_SIZE);
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_contig(r1, cur_data, cur_bytes)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_contig(r1, cur_data, cur_bytes);
|
||||
}
|
||||
cur_dst += cur_bytes;
|
||||
cur_src += cur_bytes;
|
||||
rem_bytes -= cur_bytes;
|
||||
}
|
||||
cur_dst += dst_stride;
|
||||
cur_src += src_stride;
|
||||
rem_rows -= 1;
|
||||
|
||||
row_dst += dst_stride;
|
||||
row_src += src_stride;
|
||||
}
|
||||
|
||||
dma_ring_flush(r1);
|
||||
@@ -208,5 +183,3 @@ bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride
|
||||
|
||||
return dma_ring_push_single_1d(r0, ddata, /*size=*/ 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -148,10 +148,7 @@ dma_queue_t dma_queue_alias_init(void * ptr, dma_queue_t main_q);
|
||||
void dma_queue_alias_free(dma_queue_t q);
|
||||
|
||||
bool dma_queue_push_fallback_2d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows);
|
||||
bool dma_queue_push_fallback_contig(dma_queue * q, dma_data ddata, size_t total);
|
||||
#if __HVX_ARCH__ < 75
|
||||
bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows);
|
||||
#endif
|
||||
|
||||
// TODO: technically we don't need these and could use Q6_dmstart/wait/etc instead
|
||||
// but those do not seem to always compiler properly.
|
||||
@@ -400,7 +397,7 @@ static inline bool dma_queue_push(dma_queue * q, dma_data ddata, size_t dst_stri
|
||||
if (__builtin_expect(total <= DMA_MAX_SIZE_24B, 1)) {
|
||||
return dma_ring_push_single_contig(q->ring0, ddata, total);
|
||||
}
|
||||
return dma_queue_push_fallback_contig(q, ddata, total);
|
||||
return dma_queue_push_fallback_1d(q, ddata, 0, 0, total, 1);
|
||||
}
|
||||
|
||||
// 2. Hot path: Standard strided 2D (10-20% of calls)
|
||||
@@ -410,14 +407,10 @@ static inline bool dma_queue_push(dma_queue * q, dma_data ddata, size_t dst_stri
|
||||
}
|
||||
|
||||
// 3. Cold path: Descriptor chunking fallbacks (< 0.1%)
|
||||
#if __HVX_ARCH__ < 75
|
||||
if (row_size <= DMA_MAX_SIZE_16B && (src_stride | dst_stride) <= DMA_MAX_STRIDE_16B) {
|
||||
if ((row_size | src_stride | dst_stride) <= DMA_MAX_2D_ROW_SIZE) {
|
||||
return dma_queue_push_fallback_2d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
return dma_queue_push_fallback_1d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
#else
|
||||
return dma_queue_push_fallback_2d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void dma_sync_read(dma_queue * dma_q, void * dst, dma_addr_t src, size_t bytes) {
|
||||
|
||||
@@ -0,0 +1,675 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dma-queue.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "dsv4-hc-ops.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DSV4_HC_PRE
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// hvx_vec_fast_sigmoid_f32_guard_2it with fewer live constants and predicates
|
||||
static inline HVX_Vector dsv4_hc_sigmoid_f32(HVX_Vector v, HVX_Vector max_exp, HVX_Vector min_exp) {
|
||||
v = Q6_Vsf_vmin_VsfVsf(Q6_Vsf_vmax_VsfVsf(v, min_exp), max_exp);
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(v, hvx_vec_splat_f32(0.5f * 1.442695022f));
|
||||
|
||||
HVX_Vector in_int = Q6_Vw_equals_Vsf(Q6_Vsf_equals_Vqf32(v));
|
||||
HVX_Vector x = Q6_Vqf32_vsub_Vqf32Vsf(v, Q6_Vsf_equals_Vw(in_int));
|
||||
HVX_Vector xx = Q6_Vqf32_vmpy_Vqf32Vqf32(x, x);
|
||||
|
||||
HVX_Vector v1 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(xx), Q6_V_vsplat_R(FAST_SIGMOID_C2));
|
||||
v1 = Q6_Vqf32_vadd_Vqf32Vsf(v1, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(x), Q6_V_vsplat_R(FAST_SIGMOID_C1));
|
||||
v2 = Q6_Vqf32_vmpy_Vqf32Vqf32(v2, xx);
|
||||
v2 = Q6_Vqf32_vadd_Vqf32Vqf32(v2, x);
|
||||
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vqf32(v2, v1));
|
||||
v3 = Q6_Vw_vaslacc_VwVwR(v3, in_int, 24);
|
||||
|
||||
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(v2, v1));
|
||||
HVX_Vector v5 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(v3, v4));
|
||||
|
||||
// Newton-Raphson with 2 iterations
|
||||
HVX_Vector r = Q6_Vw_vsub_VwVw(Q6_V_vsplat_R(0x7EEEEBB3), v5);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const HVX_Vector rv = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(r, v5));
|
||||
r = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(Q6_Vqf32_vadd_VsfVsf(r, r), Q6_Vqf32_vmpy_VsfVsf(r, rv)));
|
||||
}
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(v3, r));
|
||||
}
|
||||
|
||||
struct dsv4_hc_pre_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_dsv4_hc_pre_kernel_params * kparams;
|
||||
uint32_t token_start;
|
||||
uint32_t n_chunks;
|
||||
uint32_t total_jobs;
|
||||
uint32_t jobs_per_thread;
|
||||
float scale;
|
||||
uint8_t * vtcm;
|
||||
uint32_t dma_block_chunks;
|
||||
uint32_t dma_row_stride;
|
||||
uint32_t dma_buffer_size;
|
||||
};
|
||||
|
||||
struct dsv4_hc_block {
|
||||
uint32_t it;
|
||||
uint32_t chunk;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
static inline struct dsv4_hc_block dsv4_hc_pre_block_make(
|
||||
const struct dsv4_hc_pre_context * dctx,
|
||||
uint32_t job,
|
||||
uint32_t end) {
|
||||
const uint32_t local_token = fastdiv(job, &dctx->kparams->div_n_chunks);
|
||||
const uint32_t chunk = job - local_token*dctx->n_chunks;
|
||||
const uint32_t count = hex_smin(dctx->dma_block_chunks, hex_smin(dctx->n_chunks - chunk, end - job));
|
||||
return (struct dsv4_hc_block) {
|
||||
.it = dctx->token_start + local_token,
|
||||
.chunk = chunk,
|
||||
.count = count,
|
||||
};
|
||||
}
|
||||
|
||||
static inline void dsv4_hc_pre_push_plain(
|
||||
dma_queue * dma_q,
|
||||
const struct dsv4_hc_pre_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
uint8_t * spad) {
|
||||
const struct htp_tensor * x = dctx->octx->src[0];
|
||||
const struct htp_tensor * weights = dctx->octx->src[1];
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const uint32_t row_size = block->count * VLEN;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(spad, x->data + (block->it*x->nb[2] + block->chunk*VLEN)), row_stride, x->nb[1], row_size, 4);
|
||||
dma_queue_push(dma_q, dma_make_data(spad + 5*row_stride, weights->data + (block->it*weights->nb[1])), 16, 16, 16, 1);
|
||||
}
|
||||
|
||||
static inline void dsv4_hc_pre_push_gated(
|
||||
dma_queue * dma_q,
|
||||
const struct dsv4_hc_pre_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
uint8_t * spad) {
|
||||
const struct htp_tensor * x = dctx->octx->src[0];
|
||||
const struct htp_tensor * g = dctx->octx->src[1];
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const uint32_t row_size = block->count * VLEN;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(spad, x->data + (block->it*x->nb[2] + block->chunk*VLEN)), row_stride, x->nb[1], row_size, 4);
|
||||
dma_queue_push(dma_q, dma_make_data(spad + 4*row_stride, g->data + (block->it*g->nb[2] + block->chunk*VLEN)), row_stride, g->nb[1], row_size, 4);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void dsv4_hc_pre_plain_block_f32(
|
||||
const struct dsv4_hc_pre_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
const uint8_t * x_spad,
|
||||
uint8_t * d_spad,
|
||||
const float * wb) {
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const HVX_Vector scale_v = hvx_vec_splat_f32(dctx->scale);
|
||||
const HVX_Vector w0 = hvx_vec_splat_f32(wb[0]);
|
||||
const HVX_Vector w1 = hvx_vec_splat_f32(wb[1]);
|
||||
const HVX_Vector w2 = hvx_vec_splat_f32(wb[2]);
|
||||
const HVX_Vector w3 = hvx_vec_splat_f32(wb[3]);
|
||||
|
||||
for (uint32_t c = 0; c < block->count; ++c) {
|
||||
const uint8_t * xb = x_spad + c*VLEN;
|
||||
|
||||
HVX_Vector x0 = *(const HVX_Vector *) xb;
|
||||
HVX_Vector x1 = *(const HVX_Vector *) (xb + row_stride);
|
||||
HVX_Vector x2 = *(const HVX_Vector *) (xb + 2*row_stride);
|
||||
HVX_Vector x3 = *(const HVX_Vector *) (xb + 3*row_stride);
|
||||
|
||||
HVX_Vector acc = hvx_vec_mul_f32_f32(x0, w0);
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(x1, w1));
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(x2, w2));
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(x3, w3));
|
||||
|
||||
*(HVX_Vector *) (d_spad + c*VLEN) = hvx_vec_mul_f32_f32(acc, scale_v);
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void dsv4_hc_pre_gated_block_f32(
|
||||
const struct dsv4_hc_pre_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
const uint8_t * x_spad,
|
||||
const uint8_t * g_spad,
|
||||
uint8_t * d_spad) {
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const HVX_Vector scale_v = hvx_vec_splat_f32(dctx->scale);
|
||||
const HVX_Vector max_exp = hvx_vec_splat_f32(87.0f);
|
||||
const HVX_Vector min_exp = hvx_vec_splat_f32(-87.0f);
|
||||
|
||||
for (uint32_t c = 0; c < block->count; ++c) {
|
||||
const uint8_t * xb = x_spad + c*VLEN;
|
||||
const uint8_t * gb = g_spad + c*VLEN;
|
||||
|
||||
HVX_Vector xv = *(const HVX_Vector *) xb;
|
||||
HVX_Vector gv = dsv4_hc_sigmoid_f32(*(const HVX_Vector *) gb, max_exp, min_exp);
|
||||
HVX_Vector acc = hvx_vec_mul_f32_f32(xv, gv);
|
||||
|
||||
xv = *(const HVX_Vector *) (xb + row_stride);
|
||||
gv = dsv4_hc_sigmoid_f32(*(const HVX_Vector *) (gb + row_stride), max_exp, min_exp);
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(xv, gv));
|
||||
|
||||
xv = *(const HVX_Vector *) (xb + 2*row_stride);
|
||||
gv = dsv4_hc_sigmoid_f32(*(const HVX_Vector *) (gb + 2*row_stride), max_exp, min_exp);
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(xv, gv));
|
||||
|
||||
xv = *(const HVX_Vector *) (xb + 3*row_stride);
|
||||
gv = dsv4_hc_sigmoid_f32(*(const HVX_Vector *) (gb + 3*row_stride), max_exp, min_exp);
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(xv, gv));
|
||||
|
||||
*(HVX_Vector *) (d_spad + c*VLEN) = hvx_vec_mul_f32_f32(acc, scale_v);
|
||||
}
|
||||
}
|
||||
|
||||
static void dsv4_hc_pre_thread_plain_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct dsv4_hc_pre_context * dctx = (struct dsv4_hc_pre_context *) data;
|
||||
struct htp_ops_context * octx = dctx->octx;
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint64_t d_data = dst->data;
|
||||
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const uint32_t buffer_size = dctx->dma_buffer_size;
|
||||
const uint32_t start = dctx->jobs_per_thread * ith;
|
||||
const uint32_t end = hex_smin(start + dctx->jobs_per_thread, dctx->total_jobs);
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t * spad[2] = {
|
||||
dctx->vtcm + 2*buffer_size*ith,
|
||||
dctx->vtcm + 2*buffer_size*ith + buffer_size,
|
||||
};
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint32_t prefetch_job = start;
|
||||
for (uint32_t i = 0; i < 2 && prefetch_job < end; ++i) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_pre_block_make(dctx, prefetch_job, end);
|
||||
// empty dst writeback keeps pop order dst, x, weights in loop
|
||||
dma_queue_push(dma_q, dma_make_data(d_data, spad[i] + 4*row_stride), block.count*VLEN, block.count*VLEN, 0, 0);
|
||||
dsv4_hc_pre_push_plain(dma_q, dctx, &block, spad[i]);
|
||||
prefetch_job += block.count;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
uint32_t job = start;
|
||||
uint32_t curr = 0;
|
||||
while (job < end) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_pre_block_make(dctx, job, end);
|
||||
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
const float * wb = (const float *) (spad[curr] + 5*row_stride);
|
||||
uint8_t * d_spad = spad[curr] + 4*row_stride;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
dsv4_hc_pre_plain_block_f32(dctx, &block, spad[curr], d_spad, wb);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
|
||||
const uint64_t d_dst = d_data + (block.it*dst->nb[1] + block.chunk*VLEN);
|
||||
dma_queue_push(dma_q, dma_make_data(d_dst, d_spad), block.count*VLEN, block.count*VLEN, block.count*VLEN, 1);
|
||||
|
||||
if (prefetch_job < end) {
|
||||
const struct dsv4_hc_block next = dsv4_hc_pre_block_make(dctx, prefetch_job, end);
|
||||
dsv4_hc_pre_push_plain(dma_q, dctx, &next, spad[curr]);
|
||||
prefetch_job += next.count;
|
||||
}
|
||||
|
||||
job += block.count;
|
||||
curr ^= 1;
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
static void dsv4_hc_pre_thread_gated_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct dsv4_hc_pre_context * dctx = (struct dsv4_hc_pre_context *) data;
|
||||
struct htp_ops_context * octx = dctx->octx;
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint64_t d_data = dst->data;
|
||||
|
||||
const uint32_t row_stride = dctx->dma_row_stride;
|
||||
const uint32_t buffer_size = dctx->dma_buffer_size;
|
||||
const uint32_t start = dctx->jobs_per_thread * ith;
|
||||
const uint32_t end = hex_smin(start + dctx->jobs_per_thread, dctx->total_jobs);
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t * spad[2] = {
|
||||
dctx->vtcm + 2*buffer_size*ith,
|
||||
dctx->vtcm + 2*buffer_size*ith + buffer_size,
|
||||
};
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint32_t prefetch_job = start;
|
||||
for (uint32_t i = 0; i < 2 && prefetch_job < end; ++i) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_pre_block_make(dctx, prefetch_job, end);
|
||||
// empty dst writeback keeps pop order dst, x, g in loop
|
||||
dma_queue_push(dma_q, dma_make_data(d_data, spad[i] + 8*row_stride), block.count*VLEN, block.count*VLEN, 0, 0);
|
||||
dsv4_hc_pre_push_gated(dma_q, dctx, &block, spad[i]);
|
||||
prefetch_job += block.count;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
uint32_t job = start;
|
||||
uint32_t curr = 0;
|
||||
while (job < end) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_pre_block_make(dctx, job, end);
|
||||
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
uint8_t * d_spad = spad[curr] + 8*row_stride;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
dsv4_hc_pre_gated_block_f32(dctx, &block, spad[curr], spad[curr] + 4*row_stride, d_spad);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
|
||||
const uint64_t d_dst = d_data + (block.it*dst->nb[1] + block.chunk*VLEN);
|
||||
dma_queue_push(dma_q, dma_make_data(d_dst, d_spad), block.count*VLEN, block.count*VLEN, block.count*VLEN, 1);
|
||||
|
||||
if (prefetch_job < end) {
|
||||
const struct dsv4_hc_block next = dsv4_hc_pre_block_make(dctx, prefetch_job, end);
|
||||
dsv4_hc_pre_push_gated(dma_q, dctx, &next, spad[curr]);
|
||||
prefetch_job += next.count;
|
||||
}
|
||||
|
||||
job += block.count;
|
||||
curr ^= 1;
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
int op_dsv4_hc_pre(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * x = octx->src[0];
|
||||
const struct htp_tensor * weights = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const struct htp_dsv4_hc_pre_kernel_params * kparams =
|
||||
(const struct htp_dsv4_hc_pre_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t n_threads = kparams->n_threads;
|
||||
if (!htp_ops_context_set_n_threads_div(octx, n_threads, kparams->div_n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
uint32_t token_start = 0;
|
||||
uint32_t n_tokens = x->ne[2];
|
||||
uint32_t total_jobs = kparams->total_jobs;
|
||||
uint32_t jobs_per_thread = kparams->jobs_per_thread;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const bool can_split = htp_tensor_mdev_data_aligned(dst) && ((dst->nb[1] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(n_tokens, can_split ? 1 : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
token_start = range.start;
|
||||
n_tokens = range.count;
|
||||
total_jobs = n_tokens * kparams->n_chunks;
|
||||
jobs_per_thread = fastdiv(total_jobs + n_threads - 1, &kparams->div_n_threads);
|
||||
}
|
||||
|
||||
if (n_tokens == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
float scale;
|
||||
memcpy(&scale, octx->op_params, sizeof(scale));
|
||||
|
||||
struct dsv4_hc_pre_context dctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.token_start = token_start,
|
||||
.n_chunks = kparams->n_chunks,
|
||||
.total_jobs = total_jobs,
|
||||
.jobs_per_thread = jobs_per_thread,
|
||||
.scale = scale,
|
||||
.vtcm = (uint8_t *) octx->ctx->vtcm_base,
|
||||
.dma_block_chunks = kparams->dma_block_chunks,
|
||||
.dma_row_stride = kparams->dma_row_stride,
|
||||
.dma_buffer_size = kparams->dma_buffer_size,
|
||||
};
|
||||
|
||||
work_queue_func_t worker = NULL;
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_DSV4_HC_PRE_KERNEL_PLAIN: worker = dsv4_hc_pre_thread_plain_f32; break;
|
||||
case HTP_DSV4_HC_PRE_KERNEL_GATED: worker = dsv4_hc_pre_thread_gated_f32; break;
|
||||
default: return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
FARF(HIGH, "dsv4-hc-pre: (%ux%ux%u) -> (%ux%u) : ktype %u tokens %u:%u nth %u\n",
|
||||
x->ne[0], x->ne[1], x->ne[2], dst->ne[0], dst->ne[1], kparams->kernel_type,
|
||||
token_start, token_start + n_tokens, n_threads);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, worker, &dctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DSV4_HC_POST
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct dsv4_hc_post_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_dsv4_hc_post_kernel_params * kparams;
|
||||
uint32_t token_start;
|
||||
uint32_t total_jobs;
|
||||
uint32_t jobs_per_thread;
|
||||
uint8_t * vtcm;
|
||||
};
|
||||
|
||||
static inline struct dsv4_hc_block dsv4_hc_post_block_make(
|
||||
const struct dsv4_hc_post_context * dctx,
|
||||
uint32_t job,
|
||||
uint32_t end,
|
||||
uint32_t block_chunks) {
|
||||
const uint32_t local_token = fastdiv(job, &dctx->kparams->div_n_chunks);
|
||||
const uint32_t chunk = job - local_token*dctx->kparams->n_chunks;
|
||||
const uint32_t count = hex_smin(block_chunks, hex_smin(dctx->kparams->n_chunks - chunk, end - job));
|
||||
return (struct dsv4_hc_block) {
|
||||
.it = dctx->token_start + local_token,
|
||||
.chunk = chunk,
|
||||
.count = count,
|
||||
};
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void dsv4_hc_post_identity_block_f32(
|
||||
const uint8_t * xb,
|
||||
const uint8_t * rb,
|
||||
size_t r_stride,
|
||||
uint8_t * db,
|
||||
size_t d_stride,
|
||||
uint32_t count,
|
||||
const float * p) {
|
||||
for (uint32_t id = 0; id < 4; ++id) {
|
||||
const HVX_Vector pv = hvx_vec_splat_f32(p[id]);
|
||||
const uint8_t * rr = rb + id*r_stride;
|
||||
uint8_t * dd = db + id*d_stride;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const HVX_Vector xv = *(const HVX_Vector *) (xb + i*VLEN);
|
||||
const HVX_Vector rv = *(const HVX_Vector *) (rr + i*VLEN);
|
||||
*(HVX_Vector *) (dd + i*VLEN) = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(xv, pv), rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void dsv4_hc_post_comb_block_f32(
|
||||
const uint8_t * xb,
|
||||
const uint8_t * rb,
|
||||
size_t r_stride,
|
||||
uint8_t * db,
|
||||
size_t d_stride,
|
||||
uint32_t count,
|
||||
const float * p,
|
||||
const float * c) {
|
||||
for (uint32_t id = 0; id < 4; ++id) {
|
||||
const HVX_Vector pv = hvx_vec_splat_f32(p[id]);
|
||||
const HVX_Vector c0 = hvx_vec_splat_f32(c[id]);
|
||||
const HVX_Vector c1 = hvx_vec_splat_f32(c[4 + id]);
|
||||
const HVX_Vector c2 = hvx_vec_splat_f32(c[8 + id]);
|
||||
const HVX_Vector c3 = hvx_vec_splat_f32(c[12 + id]);
|
||||
uint8_t * dd = db + id*d_stride;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const uint8_t * r = rb + i*VLEN;
|
||||
HVX_Vector acc = hvx_vec_mul_f32_f32(*(const HVX_Vector *) (xb + i*VLEN), pv);
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(*(const HVX_Vector *) r, c0));
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(*(const HVX_Vector *) (r + r_stride), c1));
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(*(const HVX_Vector *) (r + 2*r_stride), c2));
|
||||
acc = hvx_vec_add_f32_f32(acc, hvx_vec_mul_f32_f32(*(const HVX_Vector *) (r + 3*r_stride), c3));
|
||||
*(HVX_Vector *) (dd + i*VLEN) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dsv4_hc_post_push_identity(
|
||||
dma_queue * dma_q,
|
||||
const struct dsv4_hc_post_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
uint8_t * spad) {
|
||||
const struct htp_tensor * x = dctx->octx->src[0];
|
||||
const struct htp_tensor * residual = dctx->octx->src[1];
|
||||
const struct htp_tensor * post = dctx->octx->src[2];
|
||||
const uint32_t row_stride = dctx->kparams->dma_row_stride;
|
||||
const uint32_t row_size = block->count * VLEN;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(spad, residual->data + (block->it*residual->nb[2] + block->chunk*VLEN)),
|
||||
row_stride, residual->nb[1], row_size, 4);
|
||||
dma_queue_push(dma_q, dma_make_data(spad + 4*row_stride, x->data + (block->it*x->nb[1] + block->chunk*VLEN)),
|
||||
row_stride, row_size, row_size, 1);
|
||||
dma_queue_push(dma_q, dma_make_data(spad + 9*row_stride, post->data + (block->it*post->nb[1])),
|
||||
16, 16, 16, 1);
|
||||
}
|
||||
|
||||
static inline void dsv4_hc_post_push_comb(
|
||||
dma_queue * dma_q,
|
||||
const struct dsv4_hc_post_context * dctx,
|
||||
const struct dsv4_hc_block * block,
|
||||
uint8_t * spad) {
|
||||
const struct htp_tensor * comb = dctx->octx->src[3];
|
||||
const uint32_t row_stride = dctx->kparams->dma_row_stride;
|
||||
|
||||
dsv4_hc_post_push_identity(dma_q, dctx, block, spad);
|
||||
dma_queue_push(dma_q, dma_make_data(spad + 9*row_stride + 64, comb->data + (block->it*comb->nb[2])),
|
||||
16, comb->nb[1], 16, 4);
|
||||
}
|
||||
|
||||
static void dsv4_hc_post_thread_identity_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct dsv4_hc_post_context * dctx = (struct dsv4_hc_post_context *) data;
|
||||
struct htp_ops_context * octx = dctx->octx;
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint64_t d_data = dst->data;
|
||||
|
||||
const uint32_t block_chunks = dctx->kparams->dma_block_chunks;
|
||||
const uint32_t row_stride = dctx->kparams->dma_row_stride;
|
||||
const uint32_t buffer_size = dctx->kparams->dma_buffer_size;
|
||||
const uint32_t start = dctx->jobs_per_thread * ith;
|
||||
const uint32_t end = hex_smin(start + dctx->jobs_per_thread, dctx->total_jobs);
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t * spad[2] = {
|
||||
dctx->vtcm + 2*buffer_size*ith,
|
||||
dctx->vtcm + 2*buffer_size*ith + buffer_size,
|
||||
};
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint32_t prefetch_job = start;
|
||||
for (uint32_t i = 0; i < 2 && prefetch_job < end; ++i) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_post_block_make(dctx, prefetch_job, end, block_chunks);
|
||||
// empty dst writeback keeps pop order dst, residual, x, post in loop
|
||||
dma_queue_push(dma_q, dma_make_data(d_data, spad[i] + 5*row_stride), dst->nb[1], row_stride, 0, 0);
|
||||
dsv4_hc_post_push_identity(dma_q, dctx, &block, spad[i]);
|
||||
prefetch_job += block.count;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
uint32_t job = start;
|
||||
uint32_t curr = 0;
|
||||
while (job < end) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_post_block_make(dctx, job, end, block_chunks);
|
||||
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
const float * p = (const float *) (spad[curr] + 9*row_stride);
|
||||
uint8_t * d_spad = spad[curr] + 5*row_stride;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
dsv4_hc_post_identity_block_f32(spad[curr] + 4*row_stride, spad[curr], row_stride, d_spad, row_stride, block.count, p);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
|
||||
const uint64_t d_dst = d_data + (block.it*dst->nb[2] + block.chunk*VLEN);
|
||||
dma_queue_push(dma_q, dma_make_data(d_dst, d_spad), dst->nb[1], row_stride, block.count*VLEN, 4);
|
||||
|
||||
if (prefetch_job < end) {
|
||||
const struct dsv4_hc_block next = dsv4_hc_post_block_make(dctx, prefetch_job, end, block_chunks);
|
||||
dsv4_hc_post_push_identity(dma_q, dctx, &next, spad[curr]);
|
||||
prefetch_job += next.count;
|
||||
}
|
||||
|
||||
job += block.count;
|
||||
curr ^= 1;
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
static void dsv4_hc_post_thread_comb_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct dsv4_hc_post_context * dctx = (struct dsv4_hc_post_context *) data;
|
||||
struct htp_ops_context * octx = dctx->octx;
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint64_t d_data = dst->data;
|
||||
|
||||
const uint32_t block_chunks = dctx->kparams->dma_block_chunks;
|
||||
const uint32_t row_stride = dctx->kparams->dma_row_stride;
|
||||
const uint32_t buffer_size = dctx->kparams->dma_buffer_size;
|
||||
const uint32_t start = dctx->jobs_per_thread * ith;
|
||||
const uint32_t end = hex_smin(start + dctx->jobs_per_thread, dctx->total_jobs);
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t * spad[2] = {
|
||||
dctx->vtcm + 2*buffer_size*ith,
|
||||
dctx->vtcm + 2*buffer_size*ith + buffer_size,
|
||||
};
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint32_t prefetch_job = start;
|
||||
for (uint32_t i = 0; i < 2 && prefetch_job < end; ++i) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_post_block_make(dctx, prefetch_job, end, block_chunks);
|
||||
// empty dst writeback keeps pop order dst, residual, x, post, comb in loop
|
||||
dma_queue_push(dma_q, dma_make_data(d_data, spad[i] + 5*row_stride), dst->nb[1], row_stride, 0, 0);
|
||||
dsv4_hc_post_push_comb(dma_q, dctx, &block, spad[i]);
|
||||
prefetch_job += block.count;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
uint32_t job = start;
|
||||
uint32_t curr = 0;
|
||||
while (job < end) {
|
||||
const struct dsv4_hc_block block = dsv4_hc_post_block_make(dctx, job, end, block_chunks);
|
||||
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
const float * p = (const float *) (spad[curr] + 9*row_stride);
|
||||
const float * c = (const float *) (spad[curr] + 9*row_stride + 64);
|
||||
uint8_t * d_spad = spad[curr] + 5*row_stride;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
dsv4_hc_post_comb_block_f32(spad[curr] + 4*row_stride, spad[curr], row_stride, d_spad, row_stride, block.count, p, c);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) block.it);
|
||||
|
||||
const uint64_t d_dst = d_data + (block.it*dst->nb[2] + block.chunk*VLEN);
|
||||
dma_queue_push(dma_q, dma_make_data(d_dst, d_spad), dst->nb[1], row_stride, block.count*VLEN, 4);
|
||||
|
||||
if (prefetch_job < end) {
|
||||
const struct dsv4_hc_block next = dsv4_hc_post_block_make(dctx, prefetch_job, end, block_chunks);
|
||||
dsv4_hc_post_push_comb(dma_q, dctx, &next, spad[curr]);
|
||||
prefetch_job += next.count;
|
||||
}
|
||||
|
||||
job += block.count;
|
||||
curr ^= 1;
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
int op_dsv4_hc_post(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * x = octx->src[0];
|
||||
const struct htp_tensor * residual = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const struct htp_dsv4_hc_post_kernel_params * kparams =
|
||||
(const struct htp_dsv4_hc_post_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t n_threads = kparams->n_threads;
|
||||
if (!htp_ops_context_set_n_threads_div(octx, n_threads, kparams->div_n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
uint32_t token_start = 0;
|
||||
uint32_t n_tokens = x->ne[1];
|
||||
uint32_t total_jobs = kparams->total_jobs;
|
||||
uint32_t jobs_per_thread = kparams->jobs_per_thread;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const bool can_split = htp_tensor_mdev_data_aligned(dst) && ((dst->nb[2] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(n_tokens, can_split ? 1 : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
token_start = range.start;
|
||||
n_tokens = range.count;
|
||||
total_jobs = n_tokens * kparams->n_chunks;
|
||||
jobs_per_thread = fastdiv(total_jobs + n_threads - 1, &kparams->div_n_threads);
|
||||
}
|
||||
|
||||
if (n_tokens == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
struct dsv4_hc_post_context dctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.token_start = token_start,
|
||||
.total_jobs = total_jobs,
|
||||
.jobs_per_thread = jobs_per_thread,
|
||||
.vtcm = (uint8_t *) octx->ctx->vtcm_base,
|
||||
};
|
||||
|
||||
work_queue_func_t worker = NULL;
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_DSV4_HC_POST_KERNEL_IDENTITY: worker = dsv4_hc_post_thread_identity_f32; break;
|
||||
case HTP_DSV4_HC_POST_KERNEL_COMB: worker = dsv4_hc_post_thread_comb_f32; break;
|
||||
default: return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
FARF(HIGH, "dsv4-hc-post: (%ux%u) (%ux%ux%u) -> (%ux%ux%u) : ktype %u tokens %u:%u nth %u\n",
|
||||
x->ne[0], x->ne[1], residual->ne[0], residual->ne[1], residual->ne[2],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], kparams->kernel_type,
|
||||
token_start, token_start + n_tokens, n_threads);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, worker, &dctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef HTP_DSV4_HC_OPS_H
|
||||
#define HTP_DSV4_HC_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-ops.h"
|
||||
|
||||
#define HTP_DSV4_HC_CHUNK_ELEMS 32
|
||||
#define HTP_DSV4_HC_MAX_CHUNKS 128
|
||||
#define HTP_DSV4_HC_PRE_GATED_ROWS 9
|
||||
#define HTP_DSV4_HC_PRE_PLAIN_ROWS 6
|
||||
|
||||
enum htp_dsv4_hc_pre_kernel_type {
|
||||
HTP_DSV4_HC_PRE_KERNEL_PLAIN = 0,
|
||||
HTP_DSV4_HC_PRE_KERNEL_GATED = 1,
|
||||
};
|
||||
|
||||
struct htp_dsv4_hc_pre_kernel_params {
|
||||
uint32_t kernel_type;
|
||||
uint32_t n_threads;
|
||||
uint32_t n_chunks;
|
||||
uint32_t total_jobs;
|
||||
uint32_t jobs_per_thread;
|
||||
|
||||
uint32_t dma_block_chunks;
|
||||
uint32_t dma_row_stride;
|
||||
uint32_t dma_buffer_size;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
struct fastdiv_values div_n_chunks;
|
||||
struct fastdiv_values div_n_threads;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_dsv4_hc_pre_kernel_params) <= 128, "htp_dsv4_hc_pre_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_dsv4_hc_pre_kernel_params) <= 128, "htp_dsv4_hc_pre_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
// dma buffer holds residual rows 0..3, x at row 4, dst rows 5..8, post/comb at row 9
|
||||
#define HTP_DSV4_HC_POST_ROWS 10
|
||||
|
||||
enum htp_dsv4_hc_post_kernel_type {
|
||||
HTP_DSV4_HC_POST_KERNEL_IDENTITY = 0,
|
||||
HTP_DSV4_HC_POST_KERNEL_COMB = 1,
|
||||
};
|
||||
|
||||
struct htp_dsv4_hc_post_kernel_params {
|
||||
uint32_t kernel_type;
|
||||
uint32_t n_threads;
|
||||
uint32_t n_chunks;
|
||||
uint32_t total_jobs;
|
||||
uint32_t jobs_per_thread;
|
||||
uint32_t has_comb;
|
||||
|
||||
uint32_t dma_block_chunks;
|
||||
uint32_t dma_row_stride;
|
||||
uint32_t dma_buffer_size;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
struct fastdiv_values div_n_chunks;
|
||||
struct fastdiv_values div_n_threads;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_dsv4_hc_post_kernel_params) <= 128, "htp_dsv4_hc_post_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_dsv4_hc_post_kernel_params) <= 128, "htp_dsv4_hc_post_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct htp_ops_context;
|
||||
|
||||
int op_dsv4_hc_pre(struct htp_ops_context * octx);
|
||||
int op_dsv4_hc_post(struct htp_ops_context * octx);
|
||||
|
||||
#endif // HTP_DSV4_HC_OPS_H
|
||||
@@ -186,53 +186,6 @@ static void swiglu_clamp_f32(const float * restrict src0,
|
||||
static const float GELU_COEF_A = 0.044715f;
|
||||
static const float SQRT_2_OVER_PI = 0.79788456080286535587989211986876f;
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32_2it(HVX_Vector v) {
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(v, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(v), Q6_V_vsplat_R(FAST_SIGMOID_C3));
|
||||
|
||||
HVX_Vector in_int = hvx_vec_truncate_f32(Q6_Vsf_equals_Vqf32(v));
|
||||
HVX_Vector x = Q6_Vqf32_vsub_Vqf32Vsf(v, Q6_Vsf_equals_Vw(in_int));
|
||||
HVX_Vector xx = Q6_Vqf32_vmpy_Vqf32Vqf32(x, x);
|
||||
|
||||
HVX_Vector v1 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(xx), Q6_V_vsplat_R(FAST_SIGMOID_C2));
|
||||
v1 = Q6_Vqf32_vadd_Vqf32Vsf(v1, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(x), Q6_V_vsplat_R(FAST_SIGMOID_C1));
|
||||
v2 = Q6_Vqf32_vmpy_Vqf32Vqf32(v2, xx);
|
||||
v2 = Q6_Vqf32_vadd_Vqf32Vqf32(v2, x);
|
||||
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vqf32(v2, v1));
|
||||
v3 = Q6_Vw_vaslacc_VwVwR(v3, in_int, 24);
|
||||
|
||||
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(v2, v1));
|
||||
HVX_Vector v5 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(v3, v4));
|
||||
|
||||
// Newton-Raphson with 2 iterations
|
||||
HVX_Vector two_sf = hvx_vec_splat_f32(2.0f);
|
||||
HVX_Vector i_sf = Q6_Vw_vsub_VwVw(Q6_V_vsplat_R(0x7EEEEBB3), v5);
|
||||
HVX_Vector r_qf = Q6_Vqf32_vmpy_VsfVsf(
|
||||
i_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(i_sf, v5)))));
|
||||
r_qf = Q6_Vqf32_vmpy_Vqf32Vqf32(
|
||||
r_qf, Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(r_qf), v5))));
|
||||
HVX_Vector res = Q6_Vsf_equals_Vqf32(r_qf);
|
||||
|
||||
res = Q6_Vqf32_vmpy_VsfVsf(v3, res);
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(res);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32_guard_2it(HVX_Vector v,
|
||||
HVX_Vector one,
|
||||
HVX_Vector max_exp,
|
||||
HVX_Vector min_exp) {
|
||||
const HVX_VectorPred pred_max = Q6_Q_vcmp_gt_VsfVsf(max_exp, v);
|
||||
const HVX_VectorPred pred_min = Q6_Q_vcmp_gt_VsfVsf(v, min_exp);
|
||||
|
||||
HVX_Vector out = hvx_vec_fast_sigmoid_f32_2it(v);
|
||||
out = Q6_V_vmux_QVV(pred_max, out, one);
|
||||
return Q6_V_vmux_QVV(pred_min, out, Q6_V_vzero());
|
||||
}
|
||||
|
||||
static inline void hvx_geglu_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src0 % 128 == 0);
|
||||
|
||||
@@ -131,21 +131,34 @@ static inline void htp_ops_context_reset(struct htp_ops_context * octx, struct h
|
||||
octx->n_threads_div = ctx->n_threads_div;
|
||||
}
|
||||
|
||||
static inline bool htp_ops_context_set_n_threads(struct htp_ops_context * octx, uint32_t n_threads) {
|
||||
static inline bool htp_ops_context_set_n_threads_div(
|
||||
struct htp_ops_context * octx,
|
||||
uint32_t n_threads,
|
||||
struct fastdiv_values n_threads_div) {
|
||||
if (n_threads == 0 || n_threads > octx->ctx->n_threads) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n_threads != octx->n_threads) {
|
||||
octx->n_threads = n_threads;
|
||||
octx->n_threads_div = n_threads == octx->ctx->n_threads
|
||||
? octx->ctx->n_threads_div
|
||||
: init_fastdiv_values(n_threads);
|
||||
}
|
||||
octx->n_threads = n_threads;
|
||||
octx->n_threads_div = n_threads_div;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool htp_ops_context_set_n_threads(struct htp_ops_context * octx, uint32_t n_threads) {
|
||||
if (n_threads == 0 || n_threads > octx->ctx->n_threads) {
|
||||
return false;
|
||||
}
|
||||
if (n_threads == octx->n_threads) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct fastdiv_values n_threads_div = n_threads == octx->ctx->n_threads
|
||||
? octx->ctx->n_threads_div
|
||||
: init_fastdiv_values(n_threads);
|
||||
return htp_ops_context_set_n_threads_div(octx, n_threads, n_threads_div);
|
||||
}
|
||||
|
||||
static inline void htp_ops_context_set_status(struct htp_ops_context * octx, int status) {
|
||||
if (status > HTP_STATUS_OK && octx->status == HTP_STATUS_OK) {
|
||||
octx->status = status;
|
||||
@@ -174,6 +187,7 @@ int op_argsort(struct htp_ops_context * octx);
|
||||
int op_top_k(struct htp_ops_context * octx);
|
||||
int op_ssm_conv(struct htp_ops_context * octx);
|
||||
int op_cumsum(struct htp_ops_context * octx);
|
||||
int op_top_k_mask(struct htp_ops_context * octx);
|
||||
int op_fill(struct htp_ops_context * octx);
|
||||
int op_concat(struct htp_ops_context * octx);
|
||||
int op_diag(struct htp_ops_context * octx);
|
||||
@@ -189,5 +203,6 @@ int op_pool_1d(struct htp_ops_context * octx);
|
||||
int op_allreduce(struct htp_ops_context * octx);
|
||||
int op_roll(struct htp_ops_context * octx);
|
||||
int op_upscale(struct htp_ops_context * octx);
|
||||
int op_lightning_indexer(struct htp_ops_context * octx);
|
||||
|
||||
#endif /* HTP_CTX_H */
|
||||
|
||||
@@ -117,6 +117,10 @@ enum htp_op_code {
|
||||
HTP_OP_UNARY_CEIL,
|
||||
HTP_OP_UNARY_SGN,
|
||||
HTP_OP_UPSCALE,
|
||||
HTP_OP_DSV4_HC_PRE,
|
||||
HTP_OP_DSV4_HC_POST,
|
||||
HTP_OP_TOP_K_MASK,
|
||||
HTP_OP_LIGHTNING_INDEXER,
|
||||
|
||||
HTP_OP_INVALID
|
||||
};
|
||||
|
||||
@@ -52,6 +52,53 @@ static inline HVX_Vector hvx_vec_fast_sigmoid_f32_guard(HVX_Vector v,
|
||||
return Q6_V_vmux_QVV(pred_min, out, Q6_V_vzero());
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32_2it(HVX_Vector v) {
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(v, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(v), Q6_V_vsplat_R(FAST_SIGMOID_C3));
|
||||
|
||||
HVX_Vector in_int = hvx_vec_truncate_f32(Q6_Vsf_equals_Vqf32(v));
|
||||
HVX_Vector x = Q6_Vqf32_vsub_Vqf32Vsf(v, Q6_Vsf_equals_Vw(in_int));
|
||||
HVX_Vector xx = Q6_Vqf32_vmpy_Vqf32Vqf32(x, x);
|
||||
|
||||
HVX_Vector v1 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(xx), Q6_V_vsplat_R(FAST_SIGMOID_C2));
|
||||
v1 = Q6_Vqf32_vadd_Vqf32Vsf(v1, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(x), Q6_V_vsplat_R(FAST_SIGMOID_C1));
|
||||
v2 = Q6_Vqf32_vmpy_Vqf32Vqf32(v2, xx);
|
||||
v2 = Q6_Vqf32_vadd_Vqf32Vqf32(v2, x);
|
||||
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vqf32(v2, v1));
|
||||
v3 = Q6_Vw_vaslacc_VwVwR(v3, in_int, 24);
|
||||
|
||||
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(v2, v1));
|
||||
HVX_Vector v5 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(v3, v4));
|
||||
|
||||
// Newton-Raphson with 2 iterations
|
||||
HVX_Vector two_sf = hvx_vec_splat_f32(2.0f);
|
||||
HVX_Vector i_sf = Q6_Vw_vsub_VwVw(Q6_V_vsplat_R(0x7EEEEBB3), v5);
|
||||
HVX_Vector r_qf = Q6_Vqf32_vmpy_VsfVsf(
|
||||
i_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(i_sf, v5)))));
|
||||
r_qf = Q6_Vqf32_vmpy_Vqf32Vqf32(
|
||||
r_qf, Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(r_qf), v5))));
|
||||
HVX_Vector res = Q6_Vsf_equals_Vqf32(r_qf);
|
||||
|
||||
res = Q6_Vqf32_vmpy_VsfVsf(v3, res);
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(res);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32_guard_2it(HVX_Vector v,
|
||||
HVX_Vector one,
|
||||
HVX_Vector max_exp,
|
||||
HVX_Vector min_exp) {
|
||||
const HVX_VectorPred pred_max = Q6_Q_vcmp_gt_VsfVsf(max_exp, v);
|
||||
const HVX_VectorPred pred_min = Q6_Q_vcmp_gt_VsfVsf(v, min_exp);
|
||||
|
||||
HVX_Vector out = hvx_vec_fast_sigmoid_f32_2it(v);
|
||||
out = Q6_V_vmux_QVV(pred_max, out, one);
|
||||
return Q6_V_vmux_QVV(pred_min, out, Q6_V_vzero());
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_tanh_f32(HVX_Vector x) {
|
||||
// tanh(x) = 2 * sigmoid(2x) - 1
|
||||
HVX_Vector two = hvx_vec_splat_f32(2.0f);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
||||
#ifndef HTP_LIGHTNING_INDEXER_OPS_H
|
||||
#define HTP_LIGHTNING_INDEXER_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HTP_LID_KV_BLOCK 64 // kv rows per block (HVX and HMX Bc)
|
||||
#define HTP_LID_TOKENS_MAX 32 // max tokens per Q tile (HVX splat path)
|
||||
#define HTP_LID_HMX_TOKENS_MAX 512 // HMX can keep more tokens; VTCM decides
|
||||
#define HTP_LID_HMX_KV_BLOCK_MAX 2048
|
||||
#define HTP_LID_HMX_MAX_QSETS 8 // streams in one HMX pipeline run
|
||||
#define HTP_LID_N_BUF 4 // K / mask / output buffers per thread (two blocks computed, two in flight)
|
||||
|
||||
#ifndef HMX_FP16_TILE_N_COLS
|
||||
#define HMX_FP16_TILE_N_COLS 32
|
||||
#endif
|
||||
#ifndef HMX_FP16_TILE_SIZE
|
||||
#define HMX_FP16_TILE_SIZE 2048
|
||||
#endif
|
||||
|
||||
enum htp_lid_kernel_type {
|
||||
HTP_LID_KERNEL_HVX = 0,
|
||||
HTP_LID_KERNEL_HMX = 1,
|
||||
};
|
||||
|
||||
struct htp_lid_kernel_params {
|
||||
uint8_t kernel_type; // enum htp_lid_kernel_type
|
||||
uint32_t n_threads;
|
||||
uint32_t n_tokens_tile; // tokens per Q tile
|
||||
uint32_t n_kv_blocks; // HVX: per stream, HMX: per run of n_qsets sets
|
||||
uint32_t blocks_per_thread;
|
||||
uint32_t vtcm_size;
|
||||
uint32_t k_row_size; // K row bytes
|
||||
uint32_t kv_block; // HMX: kv rows per block (multiple of 64)
|
||||
uint32_t n_qsets; // HMX: Q sets kept in VTCM (streams run in one pipeline)
|
||||
uint32_t n_qbuf; // HMX: Q buffers, 2: the next run's Q is prepped during the current run
|
||||
uint32_t kv_seg; // HMX: rows per set, n_kv padded to 64
|
||||
uint32_t n_tt; // HMX: token tiles per stream
|
||||
|
||||
struct fastdiv_values div_n_head;
|
||||
struct fastdiv_values div_m_ne3; // mask streams (broadcast)
|
||||
struct fastdiv_values div_kv_seg; // HMX
|
||||
struct fastdiv_values div_n_tt; // HMX
|
||||
struct fastdiv_values div_n_ch; // HMX: channel tiles per Q set
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_lid_kernel_params) <= 128, "htp_lid_kernel_params is too large");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_lid_kernel_params) <= 128, "htp_lid_kernel_params is too large");
|
||||
#endif
|
||||
|
||||
// VTCM layout shared by host (fit check) and device (buffer offsets).
|
||||
// Shared regions: Q splats (one f16 vector per q element) and head weight splats for the current token tile.
|
||||
// Per-thread regions: Q prep staging, K / mask / output buffers, two transposed K blocks and transpose scratch.
|
||||
struct htp_lid_vtcm_layout {
|
||||
size_t off_q;
|
||||
size_t off_w;
|
||||
size_t off_qs;
|
||||
size_t off_k;
|
||||
size_t off_kt;
|
||||
size_t off_m;
|
||||
size_t off_o;
|
||||
|
||||
size_t size_q_row; // f16 Q row, padded
|
||||
size_t size_qs_row; // f32 Q staging row, padded
|
||||
size_t size_k_row; // f16 K row, padded
|
||||
size_t size_k_raw_row;
|
||||
size_t size_m_row; // f16 mask row of one kv block, padded
|
||||
size_t size_o_row; // f32 output row of one kv block, padded
|
||||
|
||||
size_t size_qs; // per thread
|
||||
size_t size_k_buf; // one K buffer
|
||||
size_t size_kt; // per thread: two transposed K blocks, two 64x64 scratch tiles, Q8_0 block scales
|
||||
size_t size_m_buf; // one mask buffer
|
||||
size_t size_o_buf; // one output buffer
|
||||
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_lid_vtcm_layout_build(struct htp_lid_vtcm_layout * L, size_t D, size_t n_head, size_t n_tok, size_t n_threads, size_t k_row_size) {
|
||||
L->size_q_row = hex_round_up(D * sizeof(uint16_t), 128);
|
||||
L->size_qs_row = hex_round_up(D * sizeof(float), 128);
|
||||
L->size_k_row = hex_round_up(D * sizeof(uint16_t), 128);
|
||||
L->size_k_raw_row = L->size_k_row > hex_round_up(k_row_size, 128) ? L->size_k_row : hex_round_up(k_row_size, 128);
|
||||
L->size_m_row = hex_round_up(HTP_LID_KV_BLOCK * sizeof(uint16_t), 128);
|
||||
L->size_o_row = hex_round_up(HTP_LID_KV_BLOCK * sizeof(float), 128);
|
||||
|
||||
L->size_qs = 2 * (n_head * L->size_qs_row + hex_round_up(n_head * sizeof(float), 128)) + L->size_q_row;
|
||||
L->size_k_buf = HTP_LID_KV_BLOCK * L->size_k_raw_row;
|
||||
L->size_kt = (2 * hex_round_up(D, HTP_LID_KV_BLOCK) + 2 * HTP_LID_KV_BLOCK + D / 32) * 128;
|
||||
L->size_m_buf = n_tok * L->size_m_row;
|
||||
L->size_o_buf = n_tok * L->size_o_row;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_q, n_tok * n_head * D * 128);
|
||||
VTCM_LAYOUT_ALLOC(off, off_w, n_tok * n_head * 128);
|
||||
VTCM_LAYOUT_ALLOC(off, off_qs, n_threads * L->size_qs);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k, n_threads * HTP_LID_N_BUF * L->size_k_buf);
|
||||
VTCM_LAYOUT_ALLOC(off, off_kt, n_threads * L->size_kt);
|
||||
VTCM_LAYOUT_ALLOC(off, off_m, n_threads * HTP_LID_N_BUF * L->size_m_buf);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o, n_threads * HTP_LID_N_BUF * L->size_o_buf);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline size_t htp_lid_compute_vtcm_usage(size_t D, size_t n_head, size_t n_tok, size_t n_threads, size_t k_row_size) {
|
||||
struct htp_lid_vtcm_layout L;
|
||||
htp_lid_vtcm_layout_build(&L, D, n_head, n_tok, n_threads, k_row_size);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
// HMX path. K is the activation (kv rows), Q the weights (one output channel per (token, head)).
|
||||
// mm1: S = relu(K Q^T) * |w| per channel (HMX convert scale + pos clamp)
|
||||
// mm2: O = S G, G[(t,h), t] = sign(w[t,h]). O is [kv, token], f16.
|
||||
// Q weights, G, scales and w are per Q set (one per stream when all tokens fit one tile).
|
||||
struct htp_lid_hmx_vtcm_layout {
|
||||
size_t off_qw; // Q weight tiles [n_qsets][n_ch][n_dot]
|
||||
size_t off_g; // G weight tiles [n_qsets][n_ch]
|
||||
size_t off_sc; // per channel scales, 256 bytes per channel tile, [n_qsets][n_ch]
|
||||
size_t off_sc_id; // identity scales
|
||||
size_t off_s; // S tiles of one block and token tile, [n_rt][n_sg]
|
||||
size_t off_w; // w of the token tile, [n_qsets][n_tok][n_head] f32
|
||||
size_t off_qs; // per thread: f32 and f16 staging for 32 Q rows, gather scratch
|
||||
size_t off_k_raw[2]; // K rows, [Bc][size_k_row]
|
||||
size_t off_k_t[2]; // K tiles [n_rt][n_dot]
|
||||
size_t off_o[2]; // O tiles [n_rt][n_u]
|
||||
size_t off_m[4]; // mask rows [n_tok][size_m_row]
|
||||
size_t off_out[2]; // f32 output rows [n_tok][size_o_row]
|
||||
|
||||
size_t size_qs_row; // f32 Q row, padded
|
||||
size_t size_qh_row; // f16 Q row, padded
|
||||
size_t size_k_row; // f16 K row, padded
|
||||
size_t size_k_raw_row;
|
||||
size_t size_m_row;
|
||||
size_t size_o_row;
|
||||
size_t size_qs; // per thread
|
||||
size_t size_w; // per Q set
|
||||
|
||||
uint32_t n_ch; // channel tiles: ceil(n_tok * n_head / 32)
|
||||
uint32_t n_u; // token tiles: ceil(n_tok / 32)
|
||||
uint32_t n_dot; // D / 32
|
||||
uint32_t n_rt; // kv row tiles per block: Bc / 32
|
||||
uint32_t n_sg; // S tiles per (row tile, token tile): min(n_head, n_ch)
|
||||
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_lid_hmx_vtcm_layout_build(struct htp_lid_hmx_vtcm_layout * L,
|
||||
size_t D, size_t n_head, size_t n_tok, size_t Bc, size_t n_qsets, size_t n_threads, size_t k_row_size) {
|
||||
L->n_ch = (uint32_t) ((n_tok * n_head + 31) / 32);
|
||||
L->n_u = (uint32_t) ((n_tok + 31) / 32);
|
||||
L->n_dot = (uint32_t) (D / 32);
|
||||
L->n_rt = (uint32_t) (Bc / 32);
|
||||
L->n_sg = (uint32_t) (n_head < L->n_ch ? n_head : L->n_ch);
|
||||
|
||||
L->size_qs_row = hex_round_up(D * sizeof(float), 128);
|
||||
L->size_qh_row = hex_round_up(D * sizeof(uint16_t), 128);
|
||||
L->size_k_row = hex_round_up(D * sizeof(uint16_t), 128);
|
||||
L->size_k_raw_row = L->size_k_row > hex_round_up(k_row_size, 128) ? L->size_k_row : hex_round_up(k_row_size, 128);
|
||||
L->size_m_row = hex_round_up(Bc * sizeof(uint16_t), 128);
|
||||
L->size_o_row = hex_round_up(Bc * sizeof(float), 128);
|
||||
L->size_qs = 2 * 32 * L->size_qs_row + 32 * L->size_qh_row + hex_round_up(Bc / 64 * 128, 128);
|
||||
L->size_w = hex_round_up(n_tok * n_head * sizeof(float), 128);
|
||||
|
||||
const size_t tile = HMX_FP16_TILE_SIZE;
|
||||
|
||||
// tile regions first: they need 2 KB alignment
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_qw, n_qsets * L->n_ch * L->n_dot * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_g, n_qsets * L->n_ch * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_s, (size_t) L->n_rt * L->n_sg * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_t[0], (size_t) L->n_rt * L->n_dot * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_t[1], (size_t) L->n_rt * L->n_dot * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o[0], (size_t) L->n_rt * L->n_u * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o[1], (size_t) L->n_rt * L->n_u * tile);
|
||||
VTCM_LAYOUT_ALLOC(off, off_sc, n_qsets * L->n_ch * 256);
|
||||
VTCM_LAYOUT_ALLOC(off, off_sc_id, 256);
|
||||
VTCM_LAYOUT_ALLOC(off, off_w, n_qsets * L->size_w);
|
||||
VTCM_LAYOUT_ALLOC(off, off_qs, n_threads * L->size_qs);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_raw[i], Bc * L->size_k_raw_row);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
VTCM_LAYOUT_ALLOC(off, off_m[i], n_tok * L->size_m_row);
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
VTCM_LAYOUT_ALLOC(off, off_out[i], n_tok * L->size_o_row);
|
||||
}
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline size_t htp_lid_hmx_compute_vtcm_usage(size_t D, size_t n_head, size_t n_tok, size_t Bc, size_t n_qsets, size_t n_threads, size_t k_row_size) {
|
||||
struct htp_lid_hmx_vtcm_layout L;
|
||||
htp_lid_hmx_vtcm_layout_build(&L, D, n_head, n_tok, Bc, n_qsets, n_threads, k_row_size);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HTP_LIGHTNING_INDEXER_OPS_H */
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "work-queue.h"
|
||||
#include "hex-profile.h"
|
||||
#include "allreduce-ops.h"
|
||||
#include "dsv4-hc-ops.h"
|
||||
#include "htp-fence.h"
|
||||
|
||||
#define HMX_QUEUE_CAPACITY 128
|
||||
@@ -1007,6 +1008,9 @@ static int execute_op(struct htp_ops_context * octx) {
|
||||
case HTP_OP_CUMSUM:
|
||||
return op_cumsum(octx);
|
||||
|
||||
case HTP_OP_TOP_K_MASK:
|
||||
return op_top_k_mask(octx);
|
||||
|
||||
case HTP_OP_FILL:
|
||||
return op_fill(octx);
|
||||
|
||||
@@ -1040,12 +1044,21 @@ static int execute_op(struct htp_ops_context * octx) {
|
||||
case HTP_OP_POOL_1D:
|
||||
return op_pool_1d(octx);
|
||||
|
||||
case HTP_OP_DSV4_HC_PRE:
|
||||
return op_dsv4_hc_pre(octx);
|
||||
|
||||
case HTP_OP_DSV4_HC_POST:
|
||||
return op_dsv4_hc_post(octx);
|
||||
|
||||
case HTP_OP_CONCAT:
|
||||
return op_concat(octx);
|
||||
|
||||
case HTP_OP_GATED_DELTA_NET:
|
||||
return op_gated_delta_net(octx);
|
||||
|
||||
case HTP_OP_LIGHTNING_INDEXER:
|
||||
return op_lightning_indexer(octx);
|
||||
|
||||
case HTP_OP_TRI:
|
||||
return op_unary(octx);
|
||||
|
||||
|
||||
@@ -0,0 +1,665 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "hvx-types.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "dma-queue.h"
|
||||
#include "top-k-mask-ops.h"
|
||||
|
||||
#define TOP_K_MASK_NINF_F32 0xff800000u // f32 score compare
|
||||
|
||||
struct htp_top_k_mask_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_top_k_mask_kernel_params * kparams;
|
||||
|
||||
uint32_t rows_per_thread;
|
||||
uint32_t row0, row1; // rows this device handles
|
||||
uint32_t off_extra, off_score; // parts inside one row input slot
|
||||
uint32_t col0, ncols_out; // PICKS: columns this device writes back
|
||||
atomic_uint barrier; // PICKS: arrivals, barrier k waits for k * n_threads
|
||||
|
||||
uint8_t * blocks;
|
||||
uint8_t * mask;
|
||||
uint8_t * rowin;
|
||||
uint8_t * tiles;
|
||||
uint8_t * mtiles;
|
||||
uint8_t * scratch;
|
||||
};
|
||||
|
||||
// DMA queue pop with a trace event around the wait
|
||||
static inline void top_k_mask_pop(dma_queue * dma_q, struct htp_thread_trace * tr, uint16_t info) {
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_DMA, info);
|
||||
dma_queue_pop(dma_q);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_DMA, info);
|
||||
}
|
||||
|
||||
// Lanes 0..31 as words, in registers: inclusive scan of ones, minus one
|
||||
static inline HVX_Vector top_k_mask_lane_iota(void) {
|
||||
const HVX_Vector zero = Q6_V_vzero();
|
||||
const HVX_Vector one = Q6_V_vsplat_R(1);
|
||||
HVX_Vector v = one;
|
||||
v = Q6_Vw_vadd_VwVw(v, Q6_V_vlalign_VVR(v, zero, 4));
|
||||
v = Q6_Vw_vadd_VwVw(v, Q6_V_vlalign_VVR(v, zero, 8));
|
||||
v = Q6_Vw_vadd_VwVw(v, Q6_V_vlalign_VVR(v, zero, 16));
|
||||
v = Q6_Vw_vadd_VwVw(v, Q6_V_vlalign_VVR(v, zero, 32));
|
||||
v = Q6_Vw_vadd_VwVw(v, Q6_V_vlalign_VVR(v, zero, 64));
|
||||
return Q6_Vw_vsub_VwVw(v, one);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void top_k_mask_fill(uint8_t * restrict dst, uint32_t nbytes, uint32_t value) {
|
||||
const HVX_Vector v = Q6_V_vsplat_R(value);
|
||||
HVX_Vector * vdst = (HVX_Vector *) dst;
|
||||
const uint32_t nvec = nbytes / VLEN;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (uint32_t i = 0; i < nvec; i++) {
|
||||
vdst[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
#define TOP_K_MASK_DEAD 0x7fffffffu // cell value of a dead lane: c - c0 >=u ncols for every tile, signed max
|
||||
|
||||
// Byte offsets of the cells in [c0, c0 + ncols) relative to c0, 0 for the others
|
||||
static inline HVX_Vector top_k_mask_keep_off(HVX_Vector c, HVX_Vector v_c0, HVX_Vector v_ncols, const bool is_f16) {
|
||||
const HVX_Vector cr = Q6_Vw_vsub_VwVw(c, v_c0);
|
||||
return Q6_Vw_vasl_VwR(Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VuwVuw(v_ncols, cr), cr, Q6_V_vzero()), is_f16 ? 1 : 2);
|
||||
}
|
||||
|
||||
// Mask value of each cell lane in [c0, c0 + ncols) into tmp (predicated gather), returns the scatter offsets
|
||||
static inline HVX_Vector top_k_mask_keep_gather(HVX_Vector c, const uint8_t * restrict mrow, HVX_Vector v_c0, HVX_Vector v_ncols,
|
||||
uint32_t mu, HVX_Vector * restrict tmp, const bool is_f16) {
|
||||
const HVX_VectorPred q = Q6_Q_vcmp_gt_VuwVuw(v_ncols, Q6_Vw_vsub_VwVw(c, v_c0));
|
||||
const HVX_Vector off = top_k_mask_keep_off(c, v_c0, v_ncols, is_f16);
|
||||
|
||||
// Halfword data with word offsets: both halves of the pair hold the same offsets, the word predicate covers both halfwords of a lane
|
||||
if (is_f16) {
|
||||
Q6_vgather_AQRMWw(tmp, q, (size_t) mrow, mu, Q6_W_vcombine_VV(off, off));
|
||||
} else {
|
||||
Q6_vgather_AQRMVw(tmp, q, (size_t) mrow, mu, off);
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline void top_k_mask_keep_scatter(HVX_Vector c, HVX_Vector off, uint8_t * restrict tile, HVX_Vector v_c0, HVX_Vector v_ncols,
|
||||
uint32_t mu, HVX_Vector val, const bool is_f16) {
|
||||
const HVX_VectorPred q = Q6_Q_vcmp_gt_VuwVuw(v_ncols, Q6_Vw_vsub_VwVw(c, v_c0));
|
||||
if (is_f16) {
|
||||
Q6_vscatter_QRMWwV(q, (size_t) tile, mu, Q6_W_vcombine_VV(off, off), val);
|
||||
} else {
|
||||
Q6_vscatter_QRMVwV(q, (size_t) tile, mu, off, val);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep 4 cell vectors (dead lanes = TOP_K_MASK_DEAD): tile[c - c0] = mrow[c - c0] for c in [c0, c0 + ncols). One gather sync.
|
||||
// flag: no mask, tile[c - c0] = all ones (keep flag)
|
||||
static inline void top_k_mask_keep4(HVX_Vector c0v, HVX_Vector c1v, HVX_Vector c2v, HVX_Vector c3v, uint8_t * restrict tile,
|
||||
const uint8_t * restrict mrow, HVX_Vector v_c0, HVX_Vector v_ncols, uint32_t mu,
|
||||
HVX_Vector * restrict tmp, const bool is_f16, const bool flag) {
|
||||
if (flag) {
|
||||
const HVX_Vector ones = Q6_V_vsplat_R(0xffffffffu);
|
||||
top_k_mask_keep_scatter(c0v, top_k_mask_keep_off(c0v, v_c0, v_ncols, is_f16), tile, v_c0, v_ncols, mu, ones, is_f16);
|
||||
top_k_mask_keep_scatter(c1v, top_k_mask_keep_off(c1v, v_c0, v_ncols, is_f16), tile, v_c0, v_ncols, mu, ones, is_f16);
|
||||
top_k_mask_keep_scatter(c2v, top_k_mask_keep_off(c2v, v_c0, v_ncols, is_f16), tile, v_c0, v_ncols, mu, ones, is_f16);
|
||||
top_k_mask_keep_scatter(c3v, top_k_mask_keep_off(c3v, v_c0, v_ncols, is_f16), tile, v_c0, v_ncols, mu, ones, is_f16);
|
||||
return;
|
||||
}
|
||||
const HVX_Vector o0 = top_k_mask_keep_gather(c0v, mrow, v_c0, v_ncols, mu, tmp + 0, is_f16);
|
||||
const HVX_Vector o1 = top_k_mask_keep_gather(c1v, mrow, v_c0, v_ncols, mu, tmp + 1, is_f16);
|
||||
const HVX_Vector o2 = top_k_mask_keep_gather(c2v, mrow, v_c0, v_ncols, mu, tmp + 2, is_f16);
|
||||
const HVX_Vector o3 = top_k_mask_keep_gather(c3v, mrow, v_c0, v_ncols, mu, tmp + 3, is_f16);
|
||||
hvx_gather_sync(tmp + 3);
|
||||
top_k_mask_keep_scatter(c0v, o0, tile, v_c0, v_ncols, mu, tmp[0], is_f16);
|
||||
top_k_mask_keep_scatter(c1v, o1, tile, v_c0, v_ncols, mu, tmp[1], is_f16);
|
||||
top_k_mask_keep_scatter(c2v, o2, tile, v_c0, v_ncols, mu, tmp[2], is_f16);
|
||||
top_k_mask_keep_scatter(c3v, o3, tile, v_c0, v_ncols, mu, tmp[3], is_f16);
|
||||
}
|
||||
|
||||
// Pick vector j: picks below j1 and n_pick are valid, returns them with dead lanes marked. v_rem = j1 - lane index.
|
||||
static inline HVX_Vector top_k_mask_load_picks(const int32_t * restrict tk, uint32_t j, HVX_Vector v_rem, HVX_Vector v_npick) {
|
||||
const HVX_Vector p_raw = hvx_vmem(tk + j);
|
||||
// lane + j <u j1 is the tail, p <u n_pick drops p < 0 and p >= n_pick
|
||||
const HVX_VectorPred valid = Q6_Q_vcmp_gtand_QVuwVuw(Q6_Q_vcmp_gt_VwVw(v_rem, Q6_V_vsplat_R(j)), v_npick, p_raw);
|
||||
return Q6_V_vmux_QVV(valid, p_raw, Q6_V_vsplat_R(TOP_K_MASK_DEAD));
|
||||
}
|
||||
|
||||
// Scatter the kept cells of picks [j0, j1) (and the extras) into the tile of columns [c0, c0 + ncols). slot holds the row inputs, mrow the mask columns.
|
||||
// Gathers are issued in batches with one sync per batch: tmp[0..3] scores/blocks, tmp[4] score with blocks, tmp[5..8] mask values.
|
||||
static inline __attribute__((always_inline)) void top_k_mask_scatter_tile(
|
||||
const struct htp_top_k_mask_context * tctx, const uint8_t * restrict slot, const uint8_t * restrict mrow, uint8_t * restrict tile,
|
||||
HVX_Vector * restrict tmp, uint32_t c0, uint32_t ncols, uint32_t j0, uint32_t j1, bool do_extra,
|
||||
const bool is_f16, const bool flag) {
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
|
||||
const int32_t * restrict tk = (const int32_t *) slot;
|
||||
const int32_t * restrict ex = (const int32_t *) (slot + tctx->off_extra);
|
||||
const uint8_t * restrict sc = slot + tctx->off_score;
|
||||
const uint8_t * restrict blk = tctx->blocks;
|
||||
|
||||
const bool has_score = kp->flags & HTP_TOP_K_MASK_HAS_SCORE;
|
||||
const bool has_blocks = kp->flags & HTP_TOP_K_MASK_HAS_BLOCKS;
|
||||
const uint32_t n_blk = kp->n_blk;
|
||||
const uint32_t n_extra = do_extra ? kp->n_extra : 0;
|
||||
const uint32_t mu = ncols * kp->elem_size - 1;
|
||||
const uint32_t sc_mu = kp->score_bytes - 1;
|
||||
const uint32_t blk_mu = kp->blocks_bytes - 1;
|
||||
const uint32_t nb1_h = ((uint32_t) kp->blocks_nb1 << 16) | kp->blocks_nb1;
|
||||
|
||||
const HVX_Vector v_lane = top_k_mask_lane_iota(); // in the HVX_COMP scope of the caller
|
||||
const HVX_Vector v_c0 = Q6_V_vsplat_R(c0);
|
||||
const HVX_Vector v_ncols = Q6_V_vsplat_R(ncols);
|
||||
const HVX_Vector v_npick = Q6_V_vsplat_R(kp->n_pick);
|
||||
const HVX_Vector v_pmax = Q6_V_vsplat_R(kp->n_pick - 1); // dead picks gather the last entry, every gather stays in its region
|
||||
const HVX_Vector v_rem = Q6_Vw_vsub_VwVw(Q6_V_vsplat_R(j1), v_lane);
|
||||
const HVX_Vector v_ninf = Q6_V_vsplat_R(TOP_K_MASK_NINF_F32);
|
||||
const HVX_Vector v_dead = Q6_V_vsplat_R(TOP_K_MASK_DEAD);
|
||||
|
||||
HVX_Vector * restrict mtmp = tmp + 5;
|
||||
|
||||
if (has_blocks) {
|
||||
for (uint32_t j = j0; j < j1; j += VLEN_FP32) {
|
||||
HVX_Vector cell = top_k_mask_load_picks(tk, j, v_rem, v_npick);
|
||||
const HVX_Vector p = Q6_Vw_vmin_VwVw(cell, v_pmax);
|
||||
if (has_score) {
|
||||
Q6_vgather_ARMVw(tmp + 4, (size_t) sc, sc_mu, Q6_Vw_vasl_VwR(p, 2));
|
||||
}
|
||||
const HVX_Vector boff = Q6_Vw_vmpyi_VwRh(p, nb1_h);
|
||||
for (uint32_t k = 0; k < n_blk; k += 4) {
|
||||
// blocks k..k+3 of every pick; the missing ones (k + i >= n_blk) are dead
|
||||
const uint32_t nk = MIN(n_blk - k, 4);
|
||||
const HVX_Vector bo = Q6_Vw_vadd_VwVw(boff, Q6_V_vsplat_R(k * 4));
|
||||
for (uint32_t i = 0; i < nk; i++) {
|
||||
Q6_vgather_ARMVw(tmp + i, (size_t) blk, blk_mu, Q6_Vw_vadd_VwVw(bo, Q6_V_vsplat_R(i * 4)));
|
||||
}
|
||||
hvx_gather_sync(tmp + nk - 1);
|
||||
if (has_score && k == 0) {
|
||||
// IEEE compare: NaN and -inf scores are dead
|
||||
cell = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VsfVsf(tmp[4], v_ninf), cell, v_dead);
|
||||
}
|
||||
const HVX_VectorPred dead = Q6_Q_vcmp_eq_VwVw(cell, v_dead);
|
||||
top_k_mask_keep4(Q6_V_vmux_QVV(dead, v_dead, tmp[0]),
|
||||
nk > 1 ? Q6_V_vmux_QVV(dead, v_dead, tmp[1]) : v_dead,
|
||||
nk > 2 ? Q6_V_vmux_QVV(dead, v_dead, tmp[2]) : v_dead,
|
||||
nk > 3 ? Q6_V_vmux_QVV(dead, v_dead, tmp[3]) : v_dead,
|
||||
tile, mrow, v_c0, v_ncols, mu, mtmp, is_f16, flag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 4 pick vectors per batch, the vectors at or past j1 are dead and not loaded
|
||||
for (uint32_t j = j0; j < j1; j += 4 * VLEN_FP32) {
|
||||
HVX_Vector c0v = top_k_mask_load_picks(tk, j, v_rem, v_npick);
|
||||
HVX_Vector c1v = j + 1 * VLEN_FP32 < j1 ? top_k_mask_load_picks(tk, j + 1 * VLEN_FP32, v_rem, v_npick) : v_dead;
|
||||
HVX_Vector c2v = j + 2 * VLEN_FP32 < j1 ? top_k_mask_load_picks(tk, j + 2 * VLEN_FP32, v_rem, v_npick) : v_dead;
|
||||
HVX_Vector c3v = j + 3 * VLEN_FP32 < j1 ? top_k_mask_load_picks(tk, j + 3 * VLEN_FP32, v_rem, v_npick) : v_dead;
|
||||
if (has_score) {
|
||||
Q6_vgather_ARMVw(tmp + 0, (size_t) sc, sc_mu, Q6_Vw_vasl_VwR(Q6_Vw_vmin_VwVw(c0v, v_pmax), 2));
|
||||
Q6_vgather_ARMVw(tmp + 1, (size_t) sc, sc_mu, Q6_Vw_vasl_VwR(Q6_Vw_vmin_VwVw(c1v, v_pmax), 2));
|
||||
Q6_vgather_ARMVw(tmp + 2, (size_t) sc, sc_mu, Q6_Vw_vasl_VwR(Q6_Vw_vmin_VwVw(c2v, v_pmax), 2));
|
||||
Q6_vgather_ARMVw(tmp + 3, (size_t) sc, sc_mu, Q6_Vw_vasl_VwR(Q6_Vw_vmin_VwVw(c3v, v_pmax), 2));
|
||||
hvx_gather_sync(tmp + 3);
|
||||
c0v = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VsfVsf(tmp[0], v_ninf), c0v, v_dead);
|
||||
c1v = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VsfVsf(tmp[1], v_ninf), c1v, v_dead);
|
||||
c2v = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VsfVsf(tmp[2], v_ninf), c2v, v_dead);
|
||||
c3v = Q6_V_vmux_QVV(Q6_Q_vcmp_gt_VsfVsf(tmp[3], v_ninf), c3v, v_dead);
|
||||
}
|
||||
top_k_mask_keep4(c0v, c1v, c2v, c3v, tile, mrow, v_c0, v_ncols, mu, mtmp, is_f16, flag);
|
||||
}
|
||||
}
|
||||
|
||||
const HVX_Vector v_rem_ex = Q6_Vw_vsub_VwVw(Q6_V_vsplat_R(n_extra), v_lane);
|
||||
for (uint32_t j = 0; j < n_extra; j += VLEN_FP32) {
|
||||
const HVX_VectorPred tail = Q6_Q_vcmp_gt_VwVw(v_rem_ex, Q6_V_vsplat_R(j));
|
||||
top_k_mask_keep4(Q6_V_vmux_QVV(tail, hvx_vmem(ex + j), v_dead), v_dead, v_dead, v_dead, tile, mrow, v_c0, v_ncols, mu, mtmp, is_f16, flag);
|
||||
}
|
||||
|
||||
// release the scatters before DMA reads the tile
|
||||
hvx_gather_sync(tile);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void top_k_mask_scatter_tile_f32(const struct htp_top_k_mask_context * tctx, const uint8_t * restrict slot,
|
||||
const uint8_t * restrict mrow, uint8_t * restrict tile, HVX_Vector * restrict tmp,
|
||||
uint32_t c0, uint32_t ncols, uint32_t j0, uint32_t j1, bool do_extra) {
|
||||
top_k_mask_scatter_tile(tctx, slot, mrow, tile, tmp, c0, ncols, j0, j1, do_extra, false, false);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void top_k_mask_scatter_tile_f16(const struct htp_top_k_mask_context * tctx, const uint8_t * restrict slot,
|
||||
const uint8_t * restrict mrow, uint8_t * restrict tile, HVX_Vector * restrict tmp,
|
||||
uint32_t c0, uint32_t ncols, uint32_t j0, uint32_t j1, bool do_extra) {
|
||||
top_k_mask_scatter_tile(tctx, slot, mrow, tile, tmp, c0, ncols, j0, j1, do_extra, true, false);
|
||||
}
|
||||
|
||||
// PICKS: keep flags (all ones) of the picks [j0, j1) into the flag tile of the whole row, the element size picks the flag width
|
||||
static __attribute__((noinline)) void top_k_mask_flag_tile_f32(const struct htp_top_k_mask_context * tctx, const uint8_t * restrict slot,
|
||||
uint8_t * restrict flags, HVX_Vector * restrict tmp, uint32_t j0, uint32_t j1, bool do_extra) {
|
||||
top_k_mask_scatter_tile(tctx, slot, NULL, flags, tmp, 0, tctx->kparams->n_kv, j0, j1, do_extra, false, true);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void top_k_mask_flag_tile_f16(const struct htp_top_k_mask_context * tctx, const uint8_t * restrict slot,
|
||||
uint8_t * restrict flags, HVX_Vector * restrict tmp, uint32_t j0, uint32_t j1, bool do_extra) {
|
||||
top_k_mask_scatter_tile(tctx, slot, NULL, flags, tmp, 0, tctx->kparams->n_kv, j0, j1, do_extra, true, true);
|
||||
}
|
||||
|
||||
// out = flag ? out : -inf over nbytes (a multiple of 128) of the row tile
|
||||
static __attribute__((noinline)) void top_k_mask_select(uint8_t * restrict out, const uint8_t * restrict flags, uint32_t nbytes, uint32_t ninf) {
|
||||
const HVX_Vector v_ninf = Q6_V_vsplat_R(ninf);
|
||||
HVX_Vector * vout = (HVX_Vector *) out;
|
||||
const HVX_Vector * vflag = (const HVX_Vector *) flags;
|
||||
const uint32_t nvec = nbytes / VLEN;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (uint32_t i = 0; i < nvec; i++) {
|
||||
vout[i] = Q6_V_vxor_VV(v_ninf, Q6_V_vand_VV(Q6_V_vxor_VV(vout[i], v_ninf), vflag[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Push the row inputs of flat row r into slot: top_k, extra, score. Returns the push count.
|
||||
static inline uint32_t top_k_mask_push_rowin(const struct htp_top_k_mask_context * tctx, dma_queue * dma_q, uint32_t r, uint8_t * slot) {
|
||||
const struct htp_ops_context * octx = tctx->octx;
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
const struct htp_tensor * top_k = octx->src[1];
|
||||
const struct htp_tensor * score = octx->src[2];
|
||||
const struct htp_tensor * extra = octx->src[4];
|
||||
|
||||
const uint32_t tk_bytes = kp->n_top * sizeof(int32_t);
|
||||
dma_queue_push(dma_q, dma_make_data(slot, top_k->data + r * kp->u.rows.rs_top), tk_bytes, tk_bytes, tk_bytes, 1);
|
||||
uint32_t n = 1;
|
||||
|
||||
if (kp->flags & HTP_TOP_K_MASK_HAS_EXTRA) {
|
||||
const uint32_t ex_bytes = kp->n_extra * sizeof(int32_t);
|
||||
dma_queue_push(dma_q, dma_make_data(slot + tctx->off_extra, extra->data + r * kp->u.rows.rs_extra), ex_bytes, ex_bytes, ex_bytes, 1);
|
||||
n++;
|
||||
}
|
||||
|
||||
if (kp->flags & HTP_TOP_K_MASK_HAS_SCORE) {
|
||||
dma_queue_push(dma_q, dma_make_data(slot + tctx->off_score, score->data + r * kp->u.rows.rs_score), kp->score_bytes, kp->score_bytes, kp->score_bytes, 1);
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline struct fastdiv_values top_k_mask_fd(const struct htp_top_k_mask_kernel_params * kp, uint32_t k) {
|
||||
const struct fastdiv_values fd = { kp->u.rows.m.dec.mp[k], kp->u.rows.m.dec.l[k] };
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Mask row of flat row r: (i1, i2, i3) of dst, each taken modulo the mask dims, rows dense above the mask ne1
|
||||
static inline uint32_t top_k_mask_mask_row(const struct htp_top_k_mask_kernel_params * kp, uint32_t r) {
|
||||
const uint16_t * ne = kp->u.rows.m.dec.ne;
|
||||
const struct fastdiv_values fd1 = top_k_mask_fd(kp, 0);
|
||||
const struct fastdiv_values fd2 = top_k_mask_fd(kp, 1);
|
||||
const struct fastdiv_values fm1 = top_k_mask_fd(kp, 2);
|
||||
const struct fastdiv_values fm2 = top_k_mask_fd(kp, 3);
|
||||
const struct fastdiv_values fm3 = top_k_mask_fd(kp, 4);
|
||||
|
||||
const uint32_t q1 = fastdiv(r, &fd1);
|
||||
const uint32_t i1 = r - q1 * ne[0];
|
||||
const uint32_t i3 = fastdiv(q1, &fd2);
|
||||
const uint32_t i2 = q1 - i3 * ne[1];
|
||||
|
||||
const uint32_t m1 = fastmodulo(i1, ne[2], &fm1);
|
||||
const uint32_t m2 = fastmodulo(i2, ne[3], &fm2);
|
||||
const uint32_t m3 = fastmodulo(i3, ne[4], &fm3);
|
||||
return (m3 * ne[3] + m2) * ne[2] + m1;
|
||||
}
|
||||
|
||||
// Push the inputs of item (row r, col tile ct): row inputs on the first tile of a row, then the mask tile. Returns the push count.
|
||||
static inline uint32_t top_k_mask_push_item(const struct htp_top_k_mask_context * tctx, dma_queue * dma_q, uint32_t r, uint32_t ct,
|
||||
uint8_t * slot, uint8_t * mslot) {
|
||||
const struct htp_ops_context * octx = tctx->octx;
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
const struct htp_tensor * mask = octx->src[0];
|
||||
|
||||
const uint32_t c0 = ct * kp->tile_cols;
|
||||
|
||||
uint32_t n = 0;
|
||||
if (ct == 0) {
|
||||
n += top_k_mask_push_rowin(tctx, dma_q, r, slot);
|
||||
}
|
||||
|
||||
if (mslot) {
|
||||
const uint32_t m_off = top_k_mask_mask_row(kp, r) * kp->u.rows.mask_nb1 + c0 * kp->elem_size;
|
||||
const uint32_t nb = MIN(kp->tile_cols, kp->n_kv - c0) * kp->elem_size;
|
||||
dma_queue_push(dma_q, dma_make_data(mslot, mask->data + m_off), nb, nb, nb, 1);
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void top_k_mask_next(uint32_t * r, uint32_t * ct, uint32_t n_col_tiles) {
|
||||
if (++(*ct) == n_col_tiles) {
|
||||
*ct = 0;
|
||||
++(*r);
|
||||
}
|
||||
}
|
||||
|
||||
// ROWS: the thread count follows the device rows (mdev), so the slice of an n byte copy is sized here
|
||||
static inline uint32_t top_k_mask_slice(const struct htp_ops_context * octx, unsigned int nth, uint32_t n) {
|
||||
return hex_round_up(fastdiv(n + nth - 1, &octx->n_threads_div), HTP_TOP_K_MASK_UNIT);
|
||||
}
|
||||
|
||||
// Push this thread's slice (128B aligned) of a contiguous n byte copy. Returns the push count.
|
||||
static inline uint32_t top_k_mask_push_slice(dma_queue * dma_q, unsigned int ith, dma_addr_t dst, dma_addr_t src, uint32_t n, uint32_t slice) {
|
||||
const uint32_t b0 = ith * slice;
|
||||
if (b0 >= n) {
|
||||
return 0;
|
||||
}
|
||||
const uint32_t nb = MIN(slice, n - b0);
|
||||
dma_queue_push(dma_q, dma_make_data(dst + b0, src + b0), nb, nb, nb, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Pass 1 (ROWS): load the blocks table and the resident mask row, split across threads. Fill the tiles of this thread with -inf meanwhile.
|
||||
static void top_k_mask_preload(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct htp_top_k_mask_context * tctx = (struct htp_top_k_mask_context *) data;
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
struct htp_ops_context * octx = tctx->octx;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
uint32_t n_pushed = 0;
|
||||
|
||||
if (kp->blocks_bytes) {
|
||||
n_pushed += top_k_mask_push_slice(dma_q, ith, (dma_addr_t) tctx->blocks, octx->src[3]->data, kp->blocks_bytes, top_k_mask_slice(octx, nth, kp->blocks_bytes));
|
||||
}
|
||||
if (kp->flags & HTP_TOP_K_MASK_MASK_RESIDENT) {
|
||||
const uint32_t mask_bytes = kp->u.rows.m.mask_bytes;
|
||||
n_pushed += top_k_mask_push_slice(dma_q, ith, (dma_addr_t) tctx->mask, octx->src[0]->data, mask_bytes, top_k_mask_slice(octx, nth, mask_bytes));
|
||||
}
|
||||
|
||||
const uint32_t tile_bytes = kp->tile_units * HTP_TOP_K_MASK_UNIT;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ith);
|
||||
top_k_mask_fill(tctx->tiles + ith * 2 * tile_bytes, 2 * tile_bytes, kp->ninf);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ith);
|
||||
|
||||
for (uint32_t i = 0; i < n_pushed; i++) {
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2 (ROWS): depth-2 DMA pipeline over the items of rows [row_first, row_last), item = (row, col tile).
|
||||
// The row inputs alternate between 2 slots by row.
|
||||
static void top_k_mask_run_items(const struct htp_top_k_mask_context * tctx, unsigned int ith, uint32_t row_first, uint32_t row_last) {
|
||||
struct htp_ops_context * octx = tctx->octx;
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const bool is_f16 = kp->flags & HTP_TOP_K_MASK_F16;
|
||||
const uint32_t elem_size = kp->elem_size;
|
||||
|
||||
const uint32_t rowin_bytes = kp->rowin_units * HTP_TOP_K_MASK_UNIT;
|
||||
const uint32_t tile_bytes = kp->tile_units * HTP_TOP_K_MASK_UNIT;
|
||||
const uint32_t tile_cols = kp->tile_cols;
|
||||
const uint32_t n_col_tiles = kp->n_col_tiles;
|
||||
const uint32_t n_items = (row_last - row_first) * n_col_tiles;
|
||||
|
||||
// 2 row input slots per thread, alternating by row
|
||||
uint8_t * rowin = tctx->rowin + ith * 2 * rowin_bytes;
|
||||
uint8_t * tiles = tctx->tiles + ith * 2 * tile_bytes;
|
||||
uint8_t * mtiles = (kp->flags & HTP_TOP_K_MASK_MASK_RESIDENT) ? NULL : tctx->mtiles + ith * 2 * tile_bytes;
|
||||
|
||||
HVX_Vector * tmp = (HVX_Vector *) (tctx->scratch + ith * HTP_TOP_K_MASK_SCRATCH_BYTES);
|
||||
|
||||
const dma_addr_t dst_data = dst->data;
|
||||
|
||||
uint32_t n_in[2] = { 0, 0 };
|
||||
|
||||
// prefetch cursor
|
||||
uint32_t pr = row_first, pct = 0;
|
||||
|
||||
for (uint32_t it = 0; it < MIN(n_items, 2); it++) {
|
||||
// Dummy dst writeback to establish queue ordering
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, tiles + it * tile_bytes), 0, 0, 0, 0);
|
||||
n_in[it] = top_k_mask_push_item(tctx, dma_q, pr, pct, rowin + ((pr - row_first) & 1) * rowin_bytes, mtiles ? mtiles + it * tile_bytes : NULL);
|
||||
top_k_mask_next(&pr, &pct, n_col_tiles);
|
||||
}
|
||||
|
||||
uint32_t r = row_first, ct = 0;
|
||||
|
||||
for (uint32_t it = 0; it < n_items; it++) {
|
||||
const uint32_t s = it & 1;
|
||||
uint8_t * tile = tiles + s * tile_bytes;
|
||||
|
||||
// writeback of item it - 2 (or the dummy), then the tile is free again
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) it);
|
||||
if (it >= 2) {
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) it);
|
||||
top_k_mask_fill(tile, tile_bytes, kp->ninf);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) it);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_in[s]; i++) {
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) it);
|
||||
}
|
||||
|
||||
const uint32_t c0 = ct * tile_cols;
|
||||
const uint32_t ncols = MIN(tile_cols, kp->n_kv - c0);
|
||||
|
||||
const uint8_t * slot = rowin + ((r - row_first) & 1) * rowin_bytes;
|
||||
const uint8_t * mrow = mtiles ? mtiles + s * tile_bytes : tctx->mask + c0 * elem_size;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) it);
|
||||
if (is_f16) {
|
||||
top_k_mask_scatter_tile_f16(tctx, slot, mrow, tile, tmp, c0, ncols, 0, kp->n_top, true);
|
||||
} else {
|
||||
top_k_mask_scatter_tile_f32(tctx, slot, mrow, tile, tmp, c0, ncols, 0, kp->n_top, true);
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) it);
|
||||
|
||||
const uint32_t d_off = r * kp->u.rows.rs_dst + c0 * elem_size;
|
||||
const uint32_t nb = ncols * elem_size;
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data + d_off, tile), nb, nb, nb, 1);
|
||||
|
||||
if (it + 2 < n_items) {
|
||||
n_in[s] = top_k_mask_push_item(tctx, dma_q, pr, pct, rowin + ((pr - row_first) & 1) * rowin_bytes, mtiles ? mtiles + s * tile_bytes : NULL);
|
||||
top_k_mask_next(&pr, &pct, n_col_tiles);
|
||||
}
|
||||
top_k_mask_next(&r, &ct, n_col_tiles);
|
||||
}
|
||||
|
||||
const uint32_t n_wb = MIN(n_items, 2);
|
||||
for (uint32_t i = 0; i < n_wb; i++) {
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2 (ROWS): whole rows split across threads, the col tiles of a row in order
|
||||
static void top_k_mask_thread_rows(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_top_k_mask_context * tctx = (const struct htp_top_k_mask_context *) data;
|
||||
|
||||
const uint32_t ir0 = tctx->row0 + ith * tctx->rows_per_thread;
|
||||
const uint32_t ir1 = MIN(ir0 + tctx->rows_per_thread, tctx->row1);
|
||||
if (ir0 >= ir1) {
|
||||
return;
|
||||
}
|
||||
|
||||
top_k_mask_run_items(tctx, ith, ir0, ir1);
|
||||
}
|
||||
|
||||
static inline void top_k_mask_barrier(atomic_uint * b, uint32_t target, struct htp_thread_trace * tr, uint16_t info) {
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_FENCE, info);
|
||||
atomic_fetch_add_explicit(b, 1, memory_order_release);
|
||||
while (atomic_load_explicit(b, memory_order_acquire) < target) {
|
||||
hex_pause();
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_FENCE, info);
|
||||
}
|
||||
|
||||
// PICKS, one pass over n_virt = kp->n_threads virtual workers (host slices); real worker ith runs the virtual workers ith, ith + nth, ...
|
||||
// A: shared preload and a slice of the flag clear per virtual worker, the mask slice of this worker into the row tile;
|
||||
// barrier; B: scatter keep flags of the picks of each virtual worker; barrier; C: select mask or -inf in this worker's slice and write it back
|
||||
static void top_k_mask_thread_picks(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct htp_top_k_mask_context * tctx = (struct htp_top_k_mask_context *) data;
|
||||
const struct htp_top_k_mask_kernel_params * kp = tctx->kparams;
|
||||
struct htp_ops_context * octx = tctx->octx;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const struct htp_tensor * top_k = octx->src[1];
|
||||
const struct htp_tensor * score = octx->src[2];
|
||||
const struct htp_tensor * extra = octx->src[4];
|
||||
|
||||
uint8_t * slot = tctx->rowin;
|
||||
const uint32_t tile_bytes = kp->tile_units * HTP_TOP_K_MASK_UNIT;
|
||||
uint8_t * out = tctx->tiles;
|
||||
uint8_t * flags = tctx->tiles + tile_bytes;
|
||||
|
||||
const uint32_t n_virt = kp->n_threads;
|
||||
|
||||
// this worker's slice of the device columns: the mask goes straight into the row tile, it is popped after the scan.
|
||||
// The device share of the columns depends on mdev, so this slice is sized here.
|
||||
const uint32_t out_b0 = tctx->col0 * kp->elem_size;
|
||||
const uint32_t out_nb = tctx->ncols_out * kp->elem_size;
|
||||
const uint32_t wslice = hex_round_up(fastdiv(out_nb + nth - 1, &octx->n_threads_div), 128);
|
||||
const uint32_t wb0 = out_b0 + ith * wslice;
|
||||
const uint32_t wnb = ith * wslice < out_nb ? MIN(wslice, out_nb - ith * wslice) : 0;
|
||||
|
||||
const uint32_t fslice = kp->u.picks.flags_slice_u * HTP_TOP_K_MASK_UNIT;
|
||||
|
||||
for (uint32_t v = ith; v < n_virt; v += nth) {
|
||||
uint32_t n_pushed = 0;
|
||||
if (kp->blocks_bytes) {
|
||||
n_pushed += top_k_mask_push_slice(dma_q, v, (dma_addr_t) tctx->blocks, octx->src[3]->data, kp->blocks_bytes, kp->u.picks.blocks_slice_u * HTP_TOP_K_MASK_UNIT);
|
||||
}
|
||||
n_pushed += top_k_mask_push_slice(dma_q, v, (dma_addr_t) slot, top_k->data, kp->n_top * sizeof(int32_t), kp->u.picks.top_slice_u * HTP_TOP_K_MASK_UNIT);
|
||||
if (kp->flags & HTP_TOP_K_MASK_HAS_EXTRA) {
|
||||
n_pushed += top_k_mask_push_slice(dma_q, v, (dma_addr_t) (slot + tctx->off_extra), extra->data, kp->n_extra * sizeof(int32_t), kp->u.picks.extra_slice_u * HTP_TOP_K_MASK_UNIT);
|
||||
}
|
||||
if (kp->flags & HTP_TOP_K_MASK_HAS_SCORE) {
|
||||
n_pushed += top_k_mask_push_slice(dma_q, v, (dma_addr_t) (slot + tctx->off_score), score->data, kp->score_bytes, kp->u.picks.score_slice_u * HTP_TOP_K_MASK_UNIT);
|
||||
}
|
||||
|
||||
// the mask slice follows the last preload in the queue, so the pops below leave it in flight
|
||||
if (wnb && v + nth >= n_virt) {
|
||||
dma_queue_push(dma_q, dma_make_data(out + wb0, octx->src[0]->data + wb0), wnb, wnb, wnb, 1);
|
||||
}
|
||||
|
||||
const uint32_t fb0 = v * fslice;
|
||||
if (fb0 < tile_bytes) {
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) v);
|
||||
top_k_mask_fill(flags + fb0, MIN(fslice, tile_bytes - fb0), 0); // clear the flags
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) v);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_pushed; i++) {
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
top_k_mask_barrier(&tctx->barrier, nth, tr, 0);
|
||||
|
||||
HVX_Vector * tmp = (HVX_Vector *) (tctx->scratch + ith * HTP_TOP_K_MASK_SCRATCH_BYTES);
|
||||
|
||||
for (uint32_t v = ith; v < n_virt; v += nth) {
|
||||
const uint32_t j0 = MIN(v * kp->u.picks.picks_per_thread, kp->n_top);
|
||||
const uint32_t j1 = MIN(j0 + kp->u.picks.picks_per_thread, kp->n_top);
|
||||
const bool do_extra = v == n_virt - 1;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) v);
|
||||
if (kp->flags & HTP_TOP_K_MASK_F16) {
|
||||
top_k_mask_flag_tile_f16(tctx, slot, flags, tmp, j0, j1, do_extra);
|
||||
} else {
|
||||
top_k_mask_flag_tile_f32(tctx, slot, flags, tmp, j0, j1, do_extra);
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) v);
|
||||
}
|
||||
|
||||
top_k_mask_barrier(&tctx->barrier, 2 * nth, tr, 1);
|
||||
|
||||
if (wnb) {
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) ith);
|
||||
// wb0 is 128B aligned (col0 is a multiple of 64 columns), the select may run past the row inside the tile
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ith);
|
||||
top_k_mask_select(out + wb0, flags + wb0, hex_round_up(wnb, 128), kp->ninf);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ith);
|
||||
dma_queue_push(dma_q, dma_make_data(octx->dst->data + wb0, out + wb0), wnb, wnb, wnb, 1);
|
||||
top_k_mask_pop(dma_q, tr, (uint16_t) ith);
|
||||
}
|
||||
}
|
||||
|
||||
int op_top_k_mask(struct htp_ops_context * octx) {
|
||||
const struct htp_top_k_mask_kernel_params * kp = (const struct htp_top_k_mask_kernel_params *) octx->kernel_params;
|
||||
|
||||
if (kp->vtcm_size > octx->ctx->vtcm_size) {
|
||||
return HTP_STATUS_VTCM_TOO_SMALL;
|
||||
}
|
||||
|
||||
const bool picks = kp->kernel_type == HTP_TOP_K_MASK_KERNEL_PICKS;
|
||||
|
||||
// mdev: ROWS gives this device a contiguous share of the rows, DMA writes are exempt from the 128B rule.
|
||||
// PICKS: every device scans the row, the write back is split by 64 column chunks.
|
||||
uint32_t dev_row0 = 0;
|
||||
uint32_t dev_rows = picks ? 1 : kp->u.rows.total_rows;
|
||||
uint32_t dev_c0 = 0;
|
||||
uint32_t dev_cols = kp->n_kv;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = picks ?
|
||||
htp_tensor_mdev_partition(kp->n_kv, 64, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div) :
|
||||
htp_tensor_mdev_partition(kp->u.rows.total_rows, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
if (range.count == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
if (picks) {
|
||||
dev_c0 = range.start;
|
||||
dev_cols = range.count;
|
||||
} else {
|
||||
dev_row0 = range.start;
|
||||
dev_rows = range.count;
|
||||
}
|
||||
}
|
||||
|
||||
// fewer workers than kparams->n_threads always fit the VTCM layout: ROWS runs one row range per worker, PICKS loops over the host slices
|
||||
const uint32_t max_threads = MIN((uint32_t) kp->n_threads, octx->ctx->n_threads);
|
||||
if (!htp_ops_context_set_n_threads(octx, picks ? max_threads : MIN(max_threads, dev_rows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
uint8_t * vtcm = octx->ctx->vtcm_base;
|
||||
|
||||
struct htp_top_k_mask_context tctx = {
|
||||
.octx = octx,
|
||||
.kparams = kp,
|
||||
.off_extra = kp->off_extra_units * HTP_TOP_K_MASK_UNIT,
|
||||
.off_score = kp->off_score_units * HTP_TOP_K_MASK_UNIT,
|
||||
.row0 = dev_row0,
|
||||
.row1 = dev_row0 + dev_rows,
|
||||
.col0 = dev_c0,
|
||||
.ncols_out = dev_cols,
|
||||
.blocks = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_blocks_u * HTP_TOP_K_MASK_UNIT),
|
||||
.mask = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_mask_u * HTP_TOP_K_MASK_UNIT),
|
||||
.rowin = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_rowin_u * HTP_TOP_K_MASK_UNIT),
|
||||
.tiles = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_tiles_u * HTP_TOP_K_MASK_UNIT),
|
||||
.mtiles = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_mtiles_u * HTP_TOP_K_MASK_UNIT),
|
||||
.scratch = VTCM_LAYOUT_PTR(uint8_t, vtcm, kp->off_scratch_u * HTP_TOP_K_MASK_UNIT),
|
||||
};
|
||||
|
||||
if (picks) {
|
||||
atomic_init(&tctx.barrier, 0);
|
||||
work_queue_run(octx->ctx->work_queue, top_k_mask_thread_picks, &tctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
tctx.rows_per_thread = fastdiv(dev_rows + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, top_k_mask_preload, &tctx, n_threads);
|
||||
work_queue_run(octx->ctx->work_queue, top_k_mask_thread_rows, &tctx, n_threads);
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#ifndef HTP_TOP_K_MASK_OPS_H
|
||||
#define HTP_TOP_K_MASK_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// HVX gather results land here (gathers write to VTCM only): 10 vectors per thread
|
||||
#define HTP_TOP_K_MASK_SCRATCH_BYTES 1280
|
||||
|
||||
enum htp_top_k_mask_kernel {
|
||||
HTP_TOP_K_MASK_KERNEL_ROWS = 0, // threads split rows, row inputs streamed per row
|
||||
HTP_TOP_K_MASK_KERNEL_PICKS = 1, // one row with one resident mask row: threads split the picks, keep flags select mask or -inf
|
||||
};
|
||||
|
||||
#define HTP_TOP_K_MASK_F16 (1u << 0)
|
||||
#define HTP_TOP_K_MASK_HAS_SCORE (1u << 1)
|
||||
#define HTP_TOP_K_MASK_HAS_BLOCKS (1u << 2)
|
||||
#define HTP_TOP_K_MASK_HAS_EXTRA (1u << 3)
|
||||
#define HTP_TOP_K_MASK_MASK_RESIDENT (1u << 4) // mask has exactly 1 row
|
||||
|
||||
#define HTP_TOP_K_MASK_UNIT 128 // unit of the compact u16 sizes in kernel_params
|
||||
|
||||
struct htp_top_k_mask_shape {
|
||||
uint32_t n_kv, elem_size, n_top, n_pick, n_blk, n_extra;
|
||||
uint32_t has_score, has_blocks, mask_resident;
|
||||
};
|
||||
|
||||
struct htp_top_k_mask_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_blocks, off_mask, off_rowin, off_tiles, off_mtiles; // 0 when unused
|
||||
size_t off_scratch; // HTP_TOP_K_MASK_SCRATCH_BYTES per thread
|
||||
size_t rowin_bytes; // one slot of top_k + extra + score row, parts at htp_top_k_mask_rowin_off_*
|
||||
size_t tile_bytes; // one output tile slot, 128B aligned
|
||||
size_t blocks_bytes, mask_bytes; // exact DMA sizes, 0 when unused
|
||||
};
|
||||
|
||||
static inline uint64_t htp_top_k_mask_align128(uint64_t n) {
|
||||
return (n + 127) & ~(uint64_t) 127;
|
||||
}
|
||||
|
||||
// Parts of one row input slot: top_k at 0, then extra, then score; used by the host layout
|
||||
static inline uint64_t htp_top_k_mask_rowin_off_extra(uint32_t n_top) {
|
||||
return htp_top_k_mask_align128((uint64_t) n_top * 4);
|
||||
}
|
||||
|
||||
static inline uint64_t htp_top_k_mask_rowin_off_score(uint32_t n_top, uint32_t n_extra) {
|
||||
return htp_top_k_mask_rowin_off_extra(n_top) + htp_top_k_mask_align128((uint64_t) n_extra * 4);
|
||||
}
|
||||
|
||||
// the caller compares total_bytes against the VTCM size
|
||||
static inline void htp_top_k_mask_vtcm_layout_build(struct htp_top_k_mask_vtcm_layout * L,
|
||||
const struct htp_top_k_mask_shape * S, uint32_t kernel_type, uint32_t n_threads, uint32_t tile_cols) {
|
||||
memset(L, 0, sizeof(*L));
|
||||
|
||||
// PICKS streams the mask straight into its row tile
|
||||
const bool picks = kernel_type == HTP_TOP_K_MASK_KERNEL_PICKS;
|
||||
const uint64_t blocks_exact = S->has_blocks ? (uint64_t) S->n_blk * S->n_pick * 4 : 0;
|
||||
const uint64_t blocks_bytes = htp_top_k_mask_align128(blocks_exact);
|
||||
const uint64_t mask_exact = S->mask_resident && !picks ? (uint64_t) S->n_kv * S->elem_size : 0;
|
||||
const uint64_t mask_bytes = htp_top_k_mask_align128(mask_exact);
|
||||
const uint64_t rowin_bytes = htp_top_k_mask_rowin_off_score(S->n_top, S->n_extra) +
|
||||
(S->has_score ? htp_top_k_mask_align128((uint64_t) S->n_pick * 4) : 0);
|
||||
const uint64_t tile_bytes = htp_top_k_mask_align128((uint64_t) tile_cols * S->elem_size);
|
||||
|
||||
// ROWS streams row inputs into 2 slots per thread. PICKS: one row, a row tile and a flag tile.
|
||||
const uint64_t rowin_total = (picks ? 1 : 2 * (uint64_t) n_threads) * rowin_bytes;
|
||||
const uint64_t tiles_total = picks ? 2 * tile_bytes : 2 * (uint64_t) n_threads * tile_bytes;
|
||||
const uint64_t mtiles_total = S->mask_resident ? 0 : tiles_total;
|
||||
const uint64_t scratch = (uint64_t) n_threads * HTP_TOP_K_MASK_SCRATCH_BYTES;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_blocks, (size_t) blocks_bytes, blocks_bytes > 0);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_mask, (size_t) mask_bytes, mask_bytes > 0);
|
||||
VTCM_LAYOUT_ALLOC(off, off_rowin, (size_t) rowin_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_tiles, (size_t) tiles_total);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_mtiles, (size_t) mtiles_total, !S->mask_resident);
|
||||
VTCM_LAYOUT_ALLOC(off, off_scratch, (size_t) scratch);
|
||||
|
||||
L->rowin_bytes = (size_t) rowin_bytes;
|
||||
L->tile_bytes = (size_t) tile_bytes;
|
||||
L->blocks_bytes = (size_t) blocks_exact;
|
||||
L->mask_bytes = (size_t) mask_exact;
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
// Set by the host, used as is by the NPU: no tensor shape, stride or layout is derived on the NPU.
|
||||
// Sizes of one slot or tile, the slot parts and the VTCM offsets are in HTP_TOP_K_MASK_UNIT bytes as u16.
|
||||
// Row strides: byte offset of flat row r of top_k, extra, score and dst is r * stride (the host requires row flattenable inputs).
|
||||
// PICKS splits the work in n_threads virtual workers; fewer real workers loop over them.
|
||||
struct htp_top_k_mask_kernel_params {
|
||||
uint8_t n_threads, kernel_type, flags, elem_size;
|
||||
uint32_t vtcm_size;
|
||||
uint32_t n_kv, n_top, n_pick, n_extra;
|
||||
uint32_t ninf; // -inf cell pattern of elem_size
|
||||
uint32_t blocks_bytes, score_bytes; // exact DMA sizes, gather regions are [0, bytes)
|
||||
uint32_t tile_cols, n_col_tiles;
|
||||
uint16_t n_blk, blocks_nb1; // blocks_nb1 = n_blk * 4, fits a signed 16-bit multiply
|
||||
uint16_t off_blocks_u, off_mask_u, off_rowin_u, off_tiles_u, off_mtiles_u, off_scratch_u;
|
||||
uint16_t rowin_units, tile_units, off_extra_units, off_score_units;
|
||||
union {
|
||||
struct {
|
||||
uint32_t total_rows;
|
||||
uint32_t rs_top, rs_extra, rs_score, rs_dst; // flat row strides (bytes of row 1)
|
||||
uint32_t mask_nb1; // mask row stride, rows of the mask are dense above it
|
||||
union {
|
||||
uint32_t mask_bytes; // resident mask (1 row)
|
||||
struct { // broadcast mask: row r -> (i1, i2, i3) -> mask row (i mod mne)
|
||||
uint32_t mp[5]; // fastdiv multipliers of dst ne1, ne2 and mask ne1, ne2, ne3
|
||||
uint16_t ne[5]; // the divisors
|
||||
uint8_t l[5]; // fastdiv shifts
|
||||
} dec;
|
||||
} m;
|
||||
} rows;
|
||||
struct {
|
||||
uint32_t picks_per_thread; // per virtual worker, multiple of the lanes of one vector
|
||||
uint16_t blocks_slice_u, top_slice_u, extra_slice_u, score_slice_u, flags_slice_u; // per virtual worker slices
|
||||
} picks;
|
||||
} u;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_top_k_mask_kernel_params) <= 128, "htp_top_k_mask_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_top_k_mask_kernel_params) <= 128, "htp_top_k_mask_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_TOP_K_MASK_OPS_H
|
||||
@@ -2135,19 +2135,11 @@ static void ggml_metal_buffer_rset_free(ggml_metal_buffer_t buf) {
|
||||
static void * ggml_metal_host_malloc(size_t n) {
|
||||
void * data = NULL;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
kern_return_t err = vm_allocate((vm_map_t) mach_task_self(), (void *) &data, n, VM_FLAGS_ANYWHERE);
|
||||
if (err != KERN_SUCCESS) {
|
||||
GGML_LOG_ERROR("%s: error: vm_allocate failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
const int result = posix_memalign((void **) &data, sysconf(_SC_PAGESIZE), n);
|
||||
if (result != 0) {
|
||||
GGML_LOG_ERROR("%s: error: posix_memalign failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -2329,11 +2321,7 @@ void ggml_metal_buffer_free(ggml_metal_buffer_t buf) {
|
||||
}
|
||||
|
||||
if (buf->is_shared && buf->owned) {
|
||||
#if TARGET_OS_OSX
|
||||
vm_deallocate((vm_map_t)mach_task_self(), (vm_address_t)buf->all_data, buf->all_size);
|
||||
#else
|
||||
free(buf->all_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
@@ -45,10 +45,9 @@ size_t ggml_metal_op_flash_attn_ext_extra_blk(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_tmp(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_kv_f16(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_idx(const struct ggml_tensor * op);
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_rows(const struct ggml_tensor * op);
|
||||
|
||||
size_t ggml_metal_op_argmax_extra(const struct ggml_tensor * op);
|
||||
// size of the contiguous K/V buffers holding the rows referenced by the kv_rows lists
|
||||
size_t ggml_metal_op_flash_attn_ext_extra_rows(const struct ggml_tensor * op);
|
||||
|
||||
int ggml_metal_op_concat (ggml_metal_op_t ctx, int idx);
|
||||
int ggml_metal_op_repeat (ggml_metal_op_t ctx, int idx);
|
||||
|
||||
@@ -20,6 +20,7 @@ void quantize_q1_0(device const float * src, device block_q1_0 & dst) {
|
||||
}
|
||||
|
||||
void quantize_q2_0(device const float * src, device block_q2_0 & dst) {
|
||||
#pragma METAL fp math_mode(safe)
|
||||
float amax = 0.0f;
|
||||
for (int j = 0; j < QK2_0; j++) {
|
||||
float a = fabs(src[j]);
|
||||
|
||||
@@ -26,11 +26,11 @@ kernel void kernel_cpy_t_t(
|
||||
const int32_t i1 = (n - i3*args.ne2*args.ne1*args.ne0 - i2*args.ne1*args.ne0)/args.ne0;
|
||||
const int32_t i0 = (n - i3*args.ne2*args.ne1*args.ne0 - i2*args.ne1*args.ne0 - i1*args.ne0);
|
||||
|
||||
device T1 * dst_data = (device T1 *) (dst + i3*args.nb3 + i2*args.nb2 + i1*args.nb1 + i0*args.nb0);
|
||||
device char * dst_data = dst + i3*args.nb3 + i2*args.nb2 + i1*args.nb1 + i0*args.nb0;
|
||||
|
||||
for (int32_t i00 = iw0*ntg[0] + tpitg.x; i00 < args.ne00;) {
|
||||
device const T0 * src = (device T0 *)(src0 + i03*args.nb03 + i02*args.nb02 + i01*args.nb01 + i00*args.nb00);
|
||||
dst_data[i00] = (T1) src[0];
|
||||
*(device T1 *)(dst_data + i00*args.nb0) = (T1) src[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,11 @@ set(GGML_OPENCL_KERNELS
|
||||
argmax
|
||||
argsort
|
||||
top_k
|
||||
top_k_mask
|
||||
lightning_indexer
|
||||
tri
|
||||
fill
|
||||
fwht
|
||||
clamp
|
||||
cpy
|
||||
cvt
|
||||
@@ -117,6 +119,21 @@ set(GGML_OPENCL_KERNELS
|
||||
gemm_noshuffle_q2_0_f32
|
||||
gemm_noshuffle_q2_0_q8_1_dp4a
|
||||
gemv_noshuffle_q2_0_f32
|
||||
gemm_noshuffle_iq1_m_f32
|
||||
gemm_noshuffle_iq1_m_q8_1_dp4a
|
||||
gemv_noshuffle_iq1_m_f32
|
||||
gemm_noshuffle_iq1_s_f32
|
||||
gemm_noshuffle_iq1_s_q8_1_dp4a
|
||||
gemv_noshuffle_iq1_s_f32
|
||||
gemm_noshuffle_iq3_s_f32
|
||||
gemm_noshuffle_iq3_s_q8_1_dp4a
|
||||
gemv_noshuffle_iq3_s_f32
|
||||
gemm_noshuffle_iq3_xxs_f32
|
||||
gemm_noshuffle_iq3_xxs_q8_1_dp4a
|
||||
gemv_noshuffle_iq3_xxs_f32
|
||||
gemm_noshuffle_iq4_xs_f32
|
||||
gemm_noshuffle_iq4_xs_q8_1_dp4a
|
||||
gemv_noshuffle_iq4_xs_f32
|
||||
gemm_noshuffle_q3_k_q8_1_dp4a
|
||||
gemv_noshuffle_q3_k_f32
|
||||
mul_mv_q2_k_f32
|
||||
@@ -136,6 +153,25 @@ set(GGML_OPENCL_KERNELS
|
||||
mul_mv_q2_0_f32
|
||||
mul_mv_q2_0_f32_flat
|
||||
mul_mm_q2_0_f32_l4_lm
|
||||
mul_mv_iq1_m_f32
|
||||
mul_mv_iq1_m_f32_flat
|
||||
mul_mm_iq1_m_f32_l4_lm
|
||||
iq1_s_grid
|
||||
mul_mv_iq1_s_f32
|
||||
mul_mv_iq1_s_f32_flat
|
||||
mul_mm_iq1_s_f32_l4_lm
|
||||
iq3_xxs_grid
|
||||
mul_mv_iq3_xxs_f32
|
||||
mul_mv_iq3_xxs_f32_flat
|
||||
mul_mm_iq3_xxs_f32_l4_lm
|
||||
iq3_s_grid
|
||||
mul_mv_iq3_s_f32
|
||||
mul_mv_iq3_s_f32_flat
|
||||
mul_mm_iq3_s_f32_l4_lm
|
||||
iq4_xs_grid
|
||||
mul_mv_iq4_xs_f32
|
||||
mul_mv_iq4_xs_f32_flat
|
||||
mul_mm_iq4_xs_f32_l4_lm
|
||||
mul_mv_q3_k_f32_flat
|
||||
mul_mm_q3_k_f32_l4_lm
|
||||
mul_mv_q2_k_f32_flat
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3088,3 +3088,293 @@ kernel void kernel_restore_block_q2_0(
|
||||
dst[i].qs[j] = src_qs[16*i + j];
|
||||
}
|
||||
}
|
||||
|
||||
struct block_iq1_s {
|
||||
half d;
|
||||
uchar qs[QK_K/8];
|
||||
ushort qh[QK_K/32];
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_iq1_s(
|
||||
global struct block_iq1_s * src0,
|
||||
global uchar * dst_qs,
|
||||
global ushort * dst_qh,
|
||||
global half * dst_d,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst_d[i] = src0[i].d;
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst_qs[32*i + j] = src0[i].qs[j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
dst_qh[8*i + j] = src0[i].qh[j];
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq1_s(
|
||||
global uchar * src_qs,
|
||||
global ushort * src_qh,
|
||||
global half * src_d,
|
||||
global struct block_iq1_s * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst[i].d = src_d[i];
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst[i].qs[j] = src_qs[32*i + j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
dst[i].qh[j] = src_qh[8*i + j];
|
||||
}
|
||||
}
|
||||
|
||||
struct block_iq1_m {
|
||||
uchar qs[QK_K/8];
|
||||
uchar qh[QK_K/16];
|
||||
uchar scales[QK_K/32];
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_iq1_m(
|
||||
global struct block_iq1_m * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_qh,
|
||||
global ushort * dst_sc,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst_qs[32*i + j] = src0[i].qs[j];
|
||||
}
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
dst_qh[16*i + j] = src0[i].qh[j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst_sc[4*i + j] = (ushort)src0[i].scales[2*j] | ((ushort)src0[i].scales[2*j + 1] << 8);
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq1_m(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_qh,
|
||||
global ushort * src_sc,
|
||||
global struct block_iq1_m * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst[i].qs[j] = src_qs[32*i + j];
|
||||
}
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
dst[i].qh[j] = src_qh[16*i + j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
ushort sc = src_sc[4*i + j];
|
||||
dst[i].scales[2*j] = (uchar)sc;
|
||||
dst[i].scales[2*j + 1] = (uchar)(sc >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
struct block_iq3_s {
|
||||
half d;
|
||||
uchar qs[QK_K/4];
|
||||
uchar qh[QK_K/32];
|
||||
uchar signs[QK_K/8];
|
||||
uchar scales[QK_K/64];
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_iq3_s(
|
||||
global struct block_iq3_s * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_sg,
|
||||
global uchar * dst_qh,
|
||||
global uchar * dst_sc,
|
||||
global half * dst_d,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst_d[i] = src0[i].d;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst_qs[64*i + j] = src0[i].qs[j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst_sg[32*i + j] = src0[i].signs[j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
dst_qh[8*i + j] = src0[i].qh[j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst_sc[4*i + j] = src0[i].scales[j];
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq3_s(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_sg,
|
||||
global uchar * src_qh,
|
||||
global uchar * src_sc,
|
||||
global half * src_d,
|
||||
global struct block_iq3_s * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst[i].d = src_d[i];
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst[i].qs[j] = src_qs[64*i + j];
|
||||
}
|
||||
for (int j = 0; j < 32; ++j) {
|
||||
dst[i].signs[j] = src_sg[32*i + j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
dst[i].qh[j] = src_qh[8*i + j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i].scales[j] = src_sc[4*i + j];
|
||||
}
|
||||
}
|
||||
|
||||
struct block_iq3_xxs {
|
||||
half d;
|
||||
uchar qs[3*QK_K/8];
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_iq3_xxs(
|
||||
global struct block_iq3_xxs * src0,
|
||||
global uchar * dst_qs,
|
||||
global uint * dst_sas,
|
||||
global half * dst_d,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst_d[i] = src0[i].d;
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst_qs[64*i + j] = src0[i].qs[j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
global ushort * q = (global ushort *)(src0[i].qs + 64 + 4*j);
|
||||
dst_sas[8*i + j] = (uint)q[0] | ((uint)q[1] << 16);
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq3_xxs(
|
||||
global uchar * src_qs,
|
||||
global uint * src_sas,
|
||||
global half * src_d,
|
||||
global struct block_iq3_xxs * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst[i].d = src_d[i];
|
||||
for (int j = 0; j < 64; ++j) {
|
||||
dst[i].qs[j] = src_qs[64*i + j];
|
||||
}
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
global ushort * q = (global ushort *)(dst[i].qs + 64 + 4*j);
|
||||
uint sas = src_sas[8*i + j];
|
||||
q[0] = (ushort)sas;
|
||||
q[1] = (ushort)(sas >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
struct block_iq4_xs {
|
||||
half d;
|
||||
ushort scales_h;
|
||||
uchar scales_l[QK_K/64];
|
||||
uchar qs[QK_K/2];
|
||||
};
|
||||
|
||||
kernel void kernel_convert_block_iq4_xs(
|
||||
global struct block_iq4_xs * src0,
|
||||
global uchar * dst_qs,
|
||||
global uchar * dst_sl,
|
||||
global ushort * dst_sh,
|
||||
global half * dst_d,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst_d[i] = src0[i].d;
|
||||
dst_sh[i] = src0[i].scales_h;
|
||||
for (int j = 0; j < 128; ++j) {
|
||||
dst_qs[128*i + j] = src0[i].qs[j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst_sl[4*i + j] = src0[i].scales_l[j];
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq4_xs(
|
||||
global uchar * src_qs,
|
||||
global uchar * src_sl,
|
||||
global ushort * src_sh,
|
||||
global half * src_d,
|
||||
global struct block_iq4_xs * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst[i].d = src_d[i];
|
||||
dst[i].scales_h = src_sh[i];
|
||||
for (int j = 0; j < 128; ++j) {
|
||||
dst[i].qs[j] = src_qs[128*i + j];
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i].scales_l[j] = src_sl[4*i + j];
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_convert_block_iq4_xs_noshuffle(
|
||||
global struct block_iq4_xs * src0,
|
||||
global ushort * dst_qs,
|
||||
global uint * dst_sl,
|
||||
global ushort * dst_sh,
|
||||
global half * dst_d,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst_d[i] = src0[i].d;
|
||||
dst_sh[i] = src0[i].scales_h;
|
||||
dst_sl[i] = as_uint(vload4(0, src0[i].scales_l));
|
||||
for (int sb = 0; sb < 8; ++sb) {
|
||||
for (int u = 0; u < 8; ++u) {
|
||||
ushort q = 0;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
uchar v = src0[i].qs[16*sb + 4*(u & 3) + j];
|
||||
q |= (ushort)(((v >> (4*(u >> 2))) & 15) << (4*j));
|
||||
}
|
||||
dst_qs[64*i + 8*sb + u] = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_restore_block_iq4_xs_noshuffle(
|
||||
global ushort * src_qs,
|
||||
global uint * src_sl,
|
||||
global ushort * src_sh,
|
||||
global half * src_d,
|
||||
global struct block_iq4_xs * dst,
|
||||
ulong n_blocks
|
||||
) {
|
||||
const ulong i = get_global_id(0);
|
||||
if (i >= n_blocks) { return; }
|
||||
dst[i].d = src_d[i];
|
||||
dst[i].scales_h = src_sh[i];
|
||||
vstore4(as_uchar4(src_sl[i]), 0, dst[i].scales_l);
|
||||
for (int sb = 0; sb < 8; ++sb) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
const int shift = 4*(j & 3);
|
||||
const ushort lo = src_qs[64*i + 8*sb + j/4];
|
||||
const ushort hi = src_qs[64*i + 8*sb + j/4 + 4];
|
||||
dst[i].qs[16*sb + j] = (uchar)(((lo >> shift) & 15) | (((hi >> shift) & 15) << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
kernel void kernel_fwht(
|
||||
global const char * src,
|
||||
ulong offset_src,
|
||||
global char * dst,
|
||||
ulong offset_dst,
|
||||
int n,
|
||||
ulong ne1,
|
||||
ulong ne2,
|
||||
ulong nb1,
|
||||
ulong nb2,
|
||||
ulong nb3,
|
||||
float scale,
|
||||
local float * work) {
|
||||
const ulong row = get_group_id(1);
|
||||
const ulong i1 = row % ne1;
|
||||
const ulong i2 = row / ne1 % ne2;
|
||||
const ulong i3 = row / ne1 / ne2;
|
||||
global const SRC_TYPE * x = (global const SRC_TYPE *)(src + offset_src + i1*nb1 + i2*nb2 + i3*nb3);
|
||||
global float * y = (global float *)(dst + offset_dst) + row*n;
|
||||
const int tid = get_local_id(0);
|
||||
const int nth = get_local_size(0);
|
||||
|
||||
for (int i = tid; i < n; i += nth) {
|
||||
work[i] = (float)x[i] * scale;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
for (int h = 1; h < n; h *= 2) {
|
||||
for (int i = tid; i < n/2; i += nth) {
|
||||
const int j = 2*i - (i & (h - 1));
|
||||
const float a = work[j];
|
||||
const float b = work[j + h];
|
||||
work[j] = a + b;
|
||||
work[j + h] = a - b;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
for (int i = tid; i < n; i += nth) {
|
||||
y[i] = work[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_iq1_m_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_qh,
|
||||
global const ushort * src0_sc,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const uint4 w0 = convert_uint4(vload4(0, src0_sc + row + (4*(i / 256) + 0) * m));
|
||||
const uint4 w1 = convert_uint4(vload4(0, src0_sc + row + (4*(i / 256) + 1) * m));
|
||||
const uint4 w2 = convert_uint4(vload4(0, src0_sc + row + (4*(i / 256) + 2) * m));
|
||||
const uint4 w3 = convert_uint4(vload4(0, src0_sc + row + (4*(i / 256) + 3) * m));
|
||||
const ushort4 bits = convert_ushort4((w0 >> (uint4)(12)) | ((w1 >> (uint4)(8)) & (uint4)(0x00f0))
|
||||
| ((w2 >> (uint4)(4)) & (uint4)(0x0f00)) | (w3 & (uint4)(0xf000)));
|
||||
const half4 d = as_half4(bits);
|
||||
const uint4 sc = convert_uint4(vload4(0, src0_sc + row + (i / 64) * m));
|
||||
const half4 scale = d * convert_half4(2*((sc >> (uint4)(3*((i / 16) & 3))) & (uint4)(7)) + 1);
|
||||
const uint4 qh = convert_uint4(vload4(0, src0_qh + row + (i / 16) * m));
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uint4 gi = convert_uint4(vload4(0, src0_qs + row + (ki / 8) * m))
|
||||
| (((qh >> (uint4)(4*((ki / 8) & 1))) & (uint4)(7)) << (uint4)(8));
|
||||
const uint4 grid = (uint4)(iq1_s_grid[gi.s0], iq1_s_grid[gi.s1], iq1_s_grid[gi.s2], iq1_s_grid[gi.s3]);
|
||||
const float4 delta = select((float4)(-0.875f), (float4)(-1.125f), (qh & (uint4)(0x08 << (4*((ki / 8) & 1)))) != 0);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const float4 magnitude = convert_float4((grid >> (uint4)(8*j + (ki & 4))) & (uint4)(15));
|
||||
const half4 w = convert_half4(magnitude + delta) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline uint iq1_m_pack(uint g, uint shift, uint negative_delta) {
|
||||
// 8*(grid + delta) is an exact signed integer in [-9, 9].
|
||||
const int bias = negative_delta ? -9 : -7;
|
||||
const int4 values = 8*convert_int4(as_uchar4((g >> shift) & 0x0F0F0F0Fu)) + bias;
|
||||
return as_uint(convert_char4(values));
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
inline int dot4_q8a_v(uint4 qw, uint4 a) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_iq1_m_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uchar * src0_qh,
|
||||
__global const ushort * src0_sc,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
|
||||
#define IQ1_M_GRID(i) iq1_s_grid[(i)]
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
float dsuper = 0.f;
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
const uint ib = sub >> 3;
|
||||
|
||||
if ((sub & 7u) == 0u) {
|
||||
const uint sb = rrow + 4u*ib*(uint)m;
|
||||
const uint w0 = src0_sc[sb + 0u*(uint)m];
|
||||
const uint w1 = src0_sc[sb + 1u*(uint)m];
|
||||
const uint w2 = src0_sc[sb + 2u*(uint)m];
|
||||
const uint w3 = src0_sc[sb + 3u*(uint)m];
|
||||
const ushort bits = (ushort)((w0 >> 12) | ((w1 >> 8) & 0x00f0u)
|
||||
| ((w2 >> 4) & 0x0f00u) | (w3 & 0xf000u));
|
||||
dsuper = (float)as_half(bits);
|
||||
}
|
||||
const uint sc = src0_sc[rrow + (4u*ib + (sub & 7u)/2u)*(uint)m];
|
||||
const uint sh = 6u*(sub & 1u);
|
||||
const float dl0 = dsuper * (float)(2u*((sc >> sh) & 7u) + 1u) * 0.125f;
|
||||
const float dl1 = dsuper * (float)(2u*((sc >> (sh + 3u)) & 7u) + 1u) * 0.125f;
|
||||
const uint h0 = src0_qh[rrow + (2u*sub + 0u)*(uint)m];
|
||||
const uint h1 = src0_qh[rrow + (2u*sub + 1u)*(uint)m];
|
||||
const uint gb = rrow + 4u*sub*(uint)m;
|
||||
const uint g0 = IQ1_M_GRID((uint)src0_qs[gb + 0u*(uint)m] | (( h0 & 7u) << 8));
|
||||
const uint g1 = IQ1_M_GRID((uint)src0_qs[gb + 1u*(uint)m] | (((h0 >> 4) & 7u) << 8));
|
||||
const uint g2 = IQ1_M_GRID((uint)src0_qs[gb + 2u*(uint)m] | (( h1 & 7u) << 8));
|
||||
const uint g3 = IQ1_M_GRID((uint)src0_qs[gb + 3u*(uint)m] | (((h1 >> 4) & 7u) << 8));
|
||||
const uint4 qlo = (uint4)(iq1_m_pack(g0, 0, h0 & 0x08u), iq1_m_pack(g0, 4, h0 & 0x08u),
|
||||
iq1_m_pack(g1, 0, h0 & 0x80u), iq1_m_pack(g1, 4, h0 & 0x80u));
|
||||
const uint4 qhi = (uint4)(iq1_m_pack(g2, 0, h1 & 0x08u), iq1_m_pack(g2, 4, h1 & 0x08u),
|
||||
iq1_m_pack(g3, 0, h1 & 0x80u), iq1_m_pack(g3, 4, h1 & 0x80u));
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? vload4(0, src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#define IQ1_M_COL(b) (dl0 * (float)dot4_q8a_v(qlo, (uint4)(sh_qa4[b][0])) \
|
||||
+ dl1 * (float)dot4_q8a_v(qhi, (uint4)(sh_qa4[b][1])))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
rf.s0 = IQ1_M_COL(b+0); rf.s1 = IQ1_M_COL(b+1);
|
||||
rf.s2 = IQ1_M_COL(b+2); rf.s3 = IQ1_M_COL(b+3);
|
||||
acc[g] += LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef IQ1_M_COL
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
#undef IQ1_M_GRID
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_iq1_s_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const ushort * src0_qh,
|
||||
global const half * src0_d,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const uint4 qh = convert_uint4(vload4(0, src0_qh + row + (i / 32) * m));
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const half4 scale = d * convert_half4(2*((qh >> (uint4)(12)) & (uint4)(7)) + 1);
|
||||
const float4 delta = select((float4)(-0.875f), (float4)(-1.125f), (qh & (uint4)(0x8000)) != 0);
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uint4 gi = convert_uint4(vload4(0, src0_qs + row + (ki / 8) * m))
|
||||
| (((qh >> (uint4)(3*((ki / 8) & 3))) & (uint4)(7)) << (uint4)(8));
|
||||
const uint4 grid = (uint4)(iq1_s_grid[gi.s0], iq1_s_grid[gi.s1], iq1_s_grid[gi.s2], iq1_s_grid[gi.s3]);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const float4 magnitude = convert_float4((grid >> (uint4)(8*j + (ki & 4))) & (uint4)(15));
|
||||
const half4 w = convert_half4(magnitude + delta) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_iq1_s_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const ushort * src0_qh,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global const half * src1_sa,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_da[TILESIZE_N];
|
||||
__local half sh_sa[TILESIZE_N];
|
||||
|
||||
#define IQ1_S_GRID(i) iq1_s_grid[(i)]
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
const uint ib = sub >> 3;
|
||||
|
||||
const uint qhv = (uint)src0_qh[rrow + sub * (uint)m];
|
||||
const float dl = (float)src0_d[rrow + ib * (uint)m]
|
||||
* (float)(2u * ((qhv >> 12) & 7u) + 1u);
|
||||
|
||||
// The codebook stores grid + 1; sa includes the Q8_1 activation scale.
|
||||
const float dlt = ((qhv & 0x8000u) ? -0.125f : 0.125f) - 1.0f;
|
||||
|
||||
const uint gb = rrow + (step >> 3) * (uint)m;
|
||||
|
||||
uint8 qw;
|
||||
{
|
||||
const uint g0 = IQ1_S_GRID((uint)src0_qs[gb + 0u * (uint)m] | ((( qhv & 7u) << 8)));
|
||||
const uint g1 = IQ1_S_GRID((uint)src0_qs[gb + 1u * (uint)m] | ((((qhv >> 3) & 7u) << 8)));
|
||||
const uint g2 = IQ1_S_GRID((uint)src0_qs[gb + 2u * (uint)m] | ((((qhv >> 6) & 7u) << 8)));
|
||||
const uint g3 = IQ1_S_GRID((uint)src0_qs[gb + 3u * (uint)m] | ((((qhv >> 9) & 7u) << 8)));
|
||||
qw.s0 = g0 & 0x0F0F0F0Fu; qw.s1 = (g0 >> 4) & 0x0F0F0F0Fu;
|
||||
qw.s2 = g1 & 0x0F0F0F0Fu; qw.s3 = (g1 >> 4) & 0x0F0F0F0Fu;
|
||||
qw.s4 = g2 & 0x0F0F0F0Fu; qw.s5 = (g2 >> 4) & 0x0F0F0F0Fu;
|
||||
qw.s6 = g3 & 0x0F0F0F0Fu; qw.s7 = (g3 >> 4) & 0x0F0F0F0Fu;
|
||||
}
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? vload4(0, src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
const bool ok = c < (uint)n_no_padding;
|
||||
sh_da[lid] = ok ? src1_da[c * k_b + sub] : (half)0;
|
||||
sh_sa[lid] = ok ? src1_sa[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define TDOT4(T) dot8_q8a_v(qw, (uint4)(sh_qa4[T][0]), (uint4)(sh_qa4[T][1]))
|
||||
rf.s0 = (float)TDOT4(b+0); rf.s1 = (float)TDOT4(b+1);
|
||||
rf.s2 = (float)TDOT4(b+2); rf.s3 = (float)TDOT4(b+3);
|
||||
acc[g] += dl * (LD4(sh_da, b) * rf) + (dl * dlt) * LD4(sh_sa, b);
|
||||
}
|
||||
#undef TDOT4
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
#undef IQ1_S_GRID
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_iq3_s_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_sg,
|
||||
global const uchar * src0_qh,
|
||||
global const uchar * src0_sc,
|
||||
global const half * src0_d,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const uchar4 sc = vload4(0, src0_sc + row + (i / 64) * m);
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const float4 scale_f = convert_float4(d) * (1.0f + 2.0f*convert_float4((sc >> (uchar4)(4*((i / 32) & 1))) & (uchar4)(15)));
|
||||
const half4 scale = convert_half4(scale_f);
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uint4 qs = convert_uint4(vload4(0, src0_qs + row + (ki / 4) * m));
|
||||
const uint4 qh = convert_uint4(vload4(0, src0_qh + row + (ki / 32) * m));
|
||||
const uchar4 sg = vload4(0, src0_sg + row + (ki / 8) * m);
|
||||
const uint4 gi = qs | (((qh >> (uint4)(((ki / 4) & 7))) & (uint4)(1)) << (uint4)(8));
|
||||
const uint4 grid = (uint4)(iq3_s_grid[gi.s0], iq3_s_grid[gi.s1],
|
||||
iq3_s_grid[gi.s2], iq3_s_grid[gi.s3]);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const int4 magnitude = convert_int4((grid >> (uint4)(8*j)) & (uint4)(255));
|
||||
const int4 signs = convert_int4((sg >> (uchar4)((ki & 7) + j)) & (uchar4)(1));
|
||||
const half4 w = convert_half4(magnitude * ((int4)(1) - 2*signs)) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline uint iq3_s_pack(uint gv, uint sg, uint base) {
|
||||
int v0 = (int)((gv >> 0) & 0xFF); if (sg & (1u << (base + 0))) { v0 = -v0; }
|
||||
int v1 = (int)((gv >> 8) & 0xFF); if (sg & (1u << (base + 1))) { v1 = -v1; }
|
||||
int v2 = (int)((gv >> 16) & 0xFF); if (sg & (1u << (base + 2))) { v2 = -v2; }
|
||||
int v3 = (int)((gv >> 24) & 0xFF); if (sg & (1u << (base + 3))) { v3 = -v3; }
|
||||
return ((uint)v0 & 0xFFu) | (((uint)v1 & 0xFFu) << 8)
|
||||
| (((uint)v2 & 0xFFu) << 16) | (((uint)v3 & 0xFFu) << 24);
|
||||
}
|
||||
|
||||
#define IQ3_S_GEMM_GRID(i) iq3_s_grid[(i)]
|
||||
|
||||
inline uint iq3_s_load(__global const uchar * qs, uint qsb, uint qhb, uint m,
|
||||
uint u, uint sgv, uint base) {
|
||||
const uint gidx = (uint)qs[qsb + u * m] | (((qhb >> u) & 1u) << 8);
|
||||
return iq3_s_pack(IQ3_S_GEMM_GRID(gidx), sgv, base);
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_iq3_s_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uchar * src0_sg,
|
||||
__global const uchar * src0_qh,
|
||||
__global const uchar * src0_sc,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
|
||||
const uint ib = sub >> 3;
|
||||
const uint sb = sub & 7u;
|
||||
|
||||
const uint scb = (uint)src0_sc[rrow + (ib*(QK_K/64) + (sb >> 1)) * (uint)m];
|
||||
const uint nib = (sb & 1u) ? (scb >> 4) : (scb & 0xFu);
|
||||
const float d_w = (float)src0_d[rrow + ib * (uint)m] * (float)(1 + 2*nib);
|
||||
|
||||
const uint qhb = (uint)src0_qh[rrow + (ib*(QK_K/32) + sb) * (uint)m];
|
||||
const uint qsb = rrow + (ib*(QK_K/4) + 8*sb) * (uint)m;
|
||||
const uint sgb = rrow + (ib*(QK_K/8) + 4*sb) * (uint)m;
|
||||
|
||||
const uint sg0 = (uint)src0_sg[sgb + 0 * (uint)m];
|
||||
const uint sg1 = (uint)src0_sg[sgb + 1 * (uint)m];
|
||||
const uint sg2 = (uint)src0_sg[sgb + 2 * (uint)m];
|
||||
const uint sg3 = (uint)src0_sg[sgb + 3 * (uint)m];
|
||||
|
||||
uint8 qw;
|
||||
qw.s0 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 0u, sg0, 0u);
|
||||
qw.s1 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 1u, sg0, 4u);
|
||||
qw.s2 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 2u, sg1, 0u);
|
||||
qw.s3 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 3u, sg1, 4u);
|
||||
qw.s4 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 4u, sg2, 0u);
|
||||
qw.s5 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 5u, sg2, 4u);
|
||||
qw.s6 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 6u, sg3, 0u);
|
||||
qw.s7 = iq3_s_load(src0_qs, qsb, qhb, (uint)m, 7u, sg3, 4u);
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? vload4(0, src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define TDOT4(T) dot8_q8a_v(qw, (uint4)(sh_qa4[T][0]), (uint4)(sh_qa4[T][1]))
|
||||
rf.s0 = (float)TDOT4(b+0); rf.s1 = (float)TDOT4(b+1);
|
||||
rf.s2 = (float)TDOT4(b+2); rf.s3 = (float)TDOT4(b+3);
|
||||
acc[g] += d_w * LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef TDOT4
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_iq3_xxs_f32(
|
||||
global const uchar * src0_qs,
|
||||
global const uint * src0_sas,
|
||||
global const half * src0_d,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const uint4 sas = vload4(0, src0_sas + row + (i / 32) * m);
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const float4 scale_f = convert_float4(d) * 0.5f * (0.5f + convert_float4(sas >> (uint4)(28)));
|
||||
const half4 scale = convert_half4(scale_f);
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const uint4 gi = convert_uint4(vload4(0, src0_qs + row + (ki / 4) * m));
|
||||
const uint4 code = (sas >> (uint4)(7*((ki / 8) & 3))) & (uint4)(127);
|
||||
const uint4 sg = code | ((popcount(code) & (uint4)(1)) << (uint4)(7));
|
||||
const uint4 grid = (uint4)(iq3_xxs_grid[gi.s0], iq3_xxs_grid[gi.s1],
|
||||
iq3_xxs_grid[gi.s2], iq3_xxs_grid[gi.s3]);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const int4 magnitude = convert_int4((grid >> (uint4)(8*j)) & (uint4)(255));
|
||||
const int4 signs = convert_int4((sg >> (uint4)((ki & 7) + j)) & (uint4)(1));
|
||||
const half4 w = convert_half4(magnitude * ((int4)(1) - 2*signs)) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline uint iq3_xxs_pack(uint gv, uint sg, uint base) {
|
||||
int v0 = (int)((gv >> 0) & 0xFF); if (sg & (1u << (base + 0))) { v0 = -v0; }
|
||||
int v1 = (int)((gv >> 8) & 0xFF); if (sg & (1u << (base + 1))) { v1 = -v1; }
|
||||
int v2 = (int)((gv >> 16) & 0xFF); if (sg & (1u << (base + 2))) { v2 = -v2; }
|
||||
int v3 = (int)((gv >> 24) & 0xFF); if (sg & (1u << (base + 3))) { v3 = -v3; }
|
||||
return ((uint)v0 & 0xFFu) | (((uint)v1 & 0xFFu) << 8)
|
||||
| (((uint)v2 & 0xFFu) << 16) | (((uint)v3 & 0xFFu) << 24);
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_iq3_xxs_q8_1_dp4a(
|
||||
__global const uchar * src0_qs,
|
||||
__global const uint * src0_sas,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
|
||||
const uint ib = sub >> 3;
|
||||
const uint aux = src0_sas[rrow + sub*(uint)m];
|
||||
const float d_w = (float)src0_d[rrow + ib*(uint)m] * (0.5f + (float)(aux >> 28)) * 0.5f;
|
||||
const uint sg0 = iq3_xxs_signs( aux & 127u);
|
||||
const uint sg1 = iq3_xxs_signs((aux >> 7) & 127u);
|
||||
const uint sg2 = iq3_xxs_signs((aux >> 14) & 127u);
|
||||
const uint sg3 = iq3_xxs_signs((aux >> 21) & 127u);
|
||||
const uint qsb = rrow + sub*8u*(uint)m;
|
||||
|
||||
uint8 qw;
|
||||
qw.s0 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 0u*(uint)m]], sg0, 0u);
|
||||
qw.s1 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 1u*(uint)m]], sg0, 4u);
|
||||
qw.s2 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 2u*(uint)m]], sg1, 0u);
|
||||
qw.s3 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 3u*(uint)m]], sg1, 4u);
|
||||
qw.s4 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 4u*(uint)m]], sg2, 0u);
|
||||
qw.s5 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 5u*(uint)m]], sg2, 4u);
|
||||
qw.s6 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 6u*(uint)m]], sg3, 0u);
|
||||
qw.s7 = iq3_xxs_pack(iq3_xxs_grid[src0_qs[qsb + 7u*(uint)m]], sg3, 4u);
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? vload4(0, src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define TDOT4(T) dot8_q8a_v(qw, (uint4)(sh_qa4[T][0]), (uint4)(sh_qa4[T][1]))
|
||||
rf.s0 = (float)TDOT4(b+0); rf.s1 = (float)TDOT4(b+1);
|
||||
rf.s2 = (float)TDOT4(b+2); rf.s3 = (float)TDOT4(b+3);
|
||||
acc[g] += d_w * LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef TDOT4
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_qcom_reqd_sub_group_size
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
__attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
kernel void kernel_gemm_noshuffle_iq4_xs_f32(
|
||||
global const ushort * src0_qs,
|
||||
global const uint * src0_sl,
|
||||
global const ushort * src0_sh,
|
||||
global const half * src0_d,
|
||||
read_only image1d_buffer_t src1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n,
|
||||
int k,
|
||||
int n_no_padding
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
const int gy = get_global_id(0);
|
||||
const int row = get_global_id(1) * 4;
|
||||
const int n_4 = n / 4;
|
||||
if (row >= m) {
|
||||
return;
|
||||
}
|
||||
|
||||
half8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
for (int i = 0; i < k; i += 16) {
|
||||
const uint4 sl = vload4(0, src0_sl + row + (i / 256) * m);
|
||||
const uint4 sh = convert_uint4(vload4(0, src0_sh + row + (i / 256) * m));
|
||||
const half4 d = vload4(0, src0_d + row + (i / 256) * m);
|
||||
const int sb = (i / 32) & 7;
|
||||
const int4 ls = convert_int4(((sl >> (uint4)(4*sb)) & (uint4)(15)) | (((sh >> (uint4)(2*sb)) & (uint4)(3)) << (uint4)(4))) - (int4)(32);
|
||||
const half4 scale = convert_half4(convert_float4(d) * convert_float4(ls));
|
||||
|
||||
for (int l = 0; l < 16; l += 4) {
|
||||
const int ki = i + l;
|
||||
const ushort4 q = vload4(0, src0_qs + row + (ki / 4) * m);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
half8 b;
|
||||
b.s0123 = read_imageh(src1, gy * 2 + (ki + j) * n_4);
|
||||
b.s4567 = read_imageh(src1, gy * 2 + 1 + (ki + j) * n_4);
|
||||
const ushort4 indices = (q >> (ushort4)(4*j)) & (ushort4)(15);
|
||||
const half4 w = (half4)(iq4_xs_values[indices.s0], iq4_xs_values[indices.s1],
|
||||
iq4_xs_values[indices.s2], iq4_xs_values[indices.s3]) * scale;
|
||||
c0 += b * w.s0;
|
||||
c1 += b * w.s1;
|
||||
c2 += b * w.s2;
|
||||
c3 += b * w.s3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idx = gy * 8 * m + row;
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s0, c1.s0, c2.s0, c3.s0), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s1, c1.s1, c2.s1, c3.s1), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s2, c1.s2, c2.s2, c3.s2), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s3, c1.s3, c2.s3, c3.s3), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s4, c1.s4, c2.s4, c3.s4), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s5, c1.s5, c2.s5, c3.s5), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s6, c1.s6, c2.s6, c3.s6), 0, dst + idx);
|
||||
idx += m;
|
||||
}
|
||||
if (idx + 3 < m * n_no_padding) {
|
||||
vstore4((float4)(c0.s7, c1.s7, c2.s7, c3.s7), 0, dst + idx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
|
||||
#ifdef cl_khr_integer_dot_product
|
||||
#pragma OPENCL EXTENSION cl_khr_integer_dot_product : enable
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef TILESIZE_N
|
||||
#define TILESIZE_N 32
|
||||
#endif
|
||||
|
||||
inline uint iq4_xs_pack(ushort q) {
|
||||
return iq4_xs_code(q & 15)
|
||||
| (iq4_xs_code((q >> 4) & 15) << 8)
|
||||
| (iq4_xs_code((q >> 8) & 15) << 16)
|
||||
| (iq4_xs_code(q >> 12) << 24);
|
||||
}
|
||||
|
||||
// Pass staged operands by value; local dp4a operands miscompile on X2.
|
||||
inline int dot8_q8a_v(uint8 qw, uint4 a0, uint4 a1) {
|
||||
int r = 0;
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s0, a0.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s1, a0.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s2, a0.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s3, a0.w, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s4, a1.x, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s5, a1.y, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s6, a1.z, r);
|
||||
r = dot_acc_sat_4x8packed_ss_int(qw.s7, a1.w, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((qcom_wave_pair_mode(1)))
|
||||
kernel void kernel_gemm_noshuffle_iq4_xs_q8_1_dp4a(
|
||||
__global const ushort * src0_qs,
|
||||
__global const uint * src0_sl,
|
||||
__global const ushort * src0_sh,
|
||||
__global const half * src0_d,
|
||||
__global const uint * src1_qa,
|
||||
__global const half * src1_da,
|
||||
__global float * dst,
|
||||
ulong offsetd,
|
||||
int m,
|
||||
int n_no_padding,
|
||||
int k
|
||||
) {
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint block_id_m = get_global_id(1);
|
||||
const uint block_id_n = get_global_id(2);
|
||||
|
||||
const uint row = block_id_m * 64 + lid;
|
||||
const uint col_base = block_id_n * TILESIZE_N;
|
||||
const bool row_valid = row < (uint)m;
|
||||
const uint rrow = row_valid ? row : 0;
|
||||
|
||||
const uint k_u = (uint)k >> 2;
|
||||
const uint k_b = (uint)k >> 5;
|
||||
|
||||
__local uint4 sh_qa4[TILESIZE_N][2];
|
||||
__local half sh_d[TILESIZE_N];
|
||||
|
||||
#define NGROUPS (TILESIZE_N / 4)
|
||||
float4 acc[NGROUPS];
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) acc[g] = (float4)(0.0f);
|
||||
|
||||
for (uint step = 0; step < (uint)k; step += 32) {
|
||||
const uint sub = step >> 5;
|
||||
|
||||
const uint ib = sub >> 3;
|
||||
const uint sb = sub & 7u;
|
||||
const uint sl = src0_sl[rrow + ib*(uint)m];
|
||||
const uint sh = src0_sh[rrow + ib*(uint)m];
|
||||
const int ls = (int)(((sl >> (4*sb)) & 15u) | (((sh >> (2*sb)) & 3u) << 4)) - 32;
|
||||
const float d_w = (float)src0_d[rrow + ib*(uint)m] * (float)ls;
|
||||
const uint qsb = rrow + sub*8u*(uint)m;
|
||||
|
||||
uint8 qw;
|
||||
qw.s0 = iq4_xs_pack(src0_qs[qsb + 0u*(uint)m]);
|
||||
qw.s1 = iq4_xs_pack(src0_qs[qsb + 1u*(uint)m]);
|
||||
qw.s2 = iq4_xs_pack(src0_qs[qsb + 2u*(uint)m]);
|
||||
qw.s3 = iq4_xs_pack(src0_qs[qsb + 3u*(uint)m]);
|
||||
qw.s4 = iq4_xs_pack(src0_qs[qsb + 4u*(uint)m]);
|
||||
qw.s5 = iq4_xs_pack(src0_qs[qsb + 5u*(uint)m]);
|
||||
qw.s6 = iq4_xs_pack(src0_qs[qsb + 6u*(uint)m]);
|
||||
qw.s7 = iq4_xs_pack(src0_qs[qsb + 7u*(uint)m]);
|
||||
|
||||
for (uint idx = lid; idx < TILESIZE_N * 2; idx += 64) {
|
||||
const uint t = idx >> 1;
|
||||
const uint v = idx & 1;
|
||||
const uint c = col_base + t;
|
||||
sh_qa4[t][v] = (c < (uint)n_no_padding)
|
||||
? vload4(0, src1_qa + c * k_u + (step >> 2) + (v << 2))
|
||||
: (uint4)(0u);
|
||||
}
|
||||
if (lid < TILESIZE_N) {
|
||||
const uint c = col_base + lid;
|
||||
sh_d[lid] = (c < (uint)n_no_padding) ? src1_da[c * k_b + sub] : (half)0;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
#define LD4(arr, b) ((float4)((float)arr[(b)+0], (float)arr[(b)+1], (float)arr[(b)+2], (float)arr[(b)+3]))
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const int b = g * 4;
|
||||
float4 rf;
|
||||
#define TDOT4(T) dot8_q8a_v(qw, (uint4)(sh_qa4[T][0]), (uint4)(sh_qa4[T][1]))
|
||||
rf.s0 = (float)TDOT4(b+0); rf.s1 = (float)TDOT4(b+1);
|
||||
rf.s2 = (float)TDOT4(b+2); rf.s3 = (float)TDOT4(b+3);
|
||||
acc[g] += d_w * LD4(sh_d, b) * rf;
|
||||
}
|
||||
#undef TDOT4
|
||||
#undef LD4
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
if (!row_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int g = 0; g < NGROUPS; ++g) {
|
||||
const uint b = (uint)(g * 4);
|
||||
const float4 a = acc[g];
|
||||
const uint c0 = col_base + b;
|
||||
if (c0 + 0 < (uint)n_no_padding) dst[(c0 + 0) * (uint)m + row] = a.s0;
|
||||
if (c0 + 1 < (uint)n_no_padding) dst[(c0 + 1) * (uint)m + row] = a.s1;
|
||||
if (c0 + 2 < (uint)n_no_padding) dst[(c0 + 2) * (uint)m + row] = a.s2;
|
||||
if (c0 + 3 < (uint)n_no_padding) dst[(c0 + 3) * (uint)m + row] = a.s3;
|
||||
}
|
||||
#undef NGROUPS
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
#define IQ1_M_DELTA 0.125f
|
||||
|
||||
#ifndef IQ1_M_MV_NSG
|
||||
#define IQ1_M_MV_NSG 4
|
||||
#endif
|
||||
|
||||
inline float4 iq1_m_vals(uint g) {
|
||||
return (float4)((float)( g & 0xFu), (float)((g >> 8) & 0xFu),
|
||||
(float)((g >> 16) & 0xFu), (float)((g >> 24) & 0xFu));
|
||||
}
|
||||
|
||||
inline float iq1_m_super(uint w0, uint w1, uint w2, uint w3) {
|
||||
const ushort u16 = (ushort)(((w0 >> 12)) | ((w1 >> 8) & 0x00f0u)
|
||||
| ((w2 >> 4) & 0x0f00u) | (w3 & 0xf000u));
|
||||
return (float)as_half(u16);
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_iq1_m_f32(
|
||||
__read_only image1d_buffer_t grid_img,
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_qh,
|
||||
global const ushort * src0_sc,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne10,
|
||||
int ne0
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
#define IQ1_M_GRID(i) (read_imageui(grid_img, (int)(i)).x)
|
||||
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f, sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
global const ushort * qhu = (global const ushort *)src0_qh;
|
||||
global const uint * scu = (global const uint *)src0_sc;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += IQ1_M_MV_NSG) {
|
||||
const uint sb = j + (ib * 4u) * mh;
|
||||
const uint w0 = scu[sb + 0u * mh];
|
||||
const uint w1 = scu[sb + 1u * mh];
|
||||
const uint w2 = scu[sb + 2u * mh];
|
||||
const uint w3 = scu[sb + 3u * mh];
|
||||
const float d0 = iq1_m_super( w0 & 0xFFFFu, w1 & 0xFFFFu,
|
||||
w2 & 0xFFFFu, w3 & 0xFFFFu);
|
||||
const float d1 = iq1_m_super((w0 >> 16) & 0xFFFFu, (w1 >> 16) & 0xFFFFu,
|
||||
(w2 >> 16) & 0xFFFFu, (w3 >> 16) & 0xFFFFu);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
|
||||
for (uint sub_l = 0; sub_l < 8u; ++sub_l) {
|
||||
const uint sub = ib * 8u + sub_l;
|
||||
const uint scw = scu[j + (ib * 4u + (sub_l >> 1)) * mh];
|
||||
const uint grp = sub * 8u;
|
||||
const uint shb = 3u * (2u * (sub_l & 1u));
|
||||
|
||||
for (uint l = 0; l < 4u; ++l) {
|
||||
const uint sh = shb + 3u * (l >> 1);
|
||||
const float s0 = (float)(2u * (((scw ) >> sh) & 7u) + 1u);
|
||||
const float s1 = (float)(2u * (((scw >> 16) >> sh) & 7u) + 1u);
|
||||
|
||||
const uint qhv = (uint)qhu[j + (sub * 2u + (l >> 1)) * mh];
|
||||
const uint h0 = qhv & 0xFFu;
|
||||
const uint h1 = (qhv >> 8) & 0xFFu;
|
||||
const uint qsv = (uint)qsu[j + (sub * 4u + l) * mh];
|
||||
|
||||
const uint msk = 0x08u << (4u * (l & 1u));
|
||||
const float t0 = (h0 & msk) ? (-1.f - IQ1_M_DELTA) : (-1.f + IQ1_M_DELTA);
|
||||
const float t1 = (h1 & msk) ? (-1.f - IQ1_M_DELTA) : (-1.f + IQ1_M_DELTA);
|
||||
|
||||
const uint g0 = IQ1_M_GRID(( qsv & 0xFFu)
|
||||
| ((((h0 >> (4u*(l & 1u))) & 7u) << 8)));
|
||||
const uint g1 = IQ1_M_GRID(((qsv >> 8) & 0xFFu)
|
||||
| ((((h1 >> (4u*(l & 1u))) & 7u) << 8)));
|
||||
|
||||
const float4 y0 = vload4(grp + 2u*l + 0u, y);
|
||||
const float4 y1 = vload4(grp + 2u*l + 1u, y);
|
||||
|
||||
const float4 ys = y0 + y1;
|
||||
const float as = ys.s0 + ys.s1 + ys.s2 + ys.s3;
|
||||
|
||||
const float a0 = dot(y0, iq1_m_vals(g0)) + dot(y1, iq1_m_vals(g0 >> 4));
|
||||
const float a1 = dot(y0, iq1_m_vals(g1)) + dot(y1, iq1_m_vals(g1 >> 4));
|
||||
acc0 += s0 * (a0 + t0 * as);
|
||||
acc1 += s1 * (a1 + t1 * as);
|
||||
}
|
||||
}
|
||||
sumf += d0 * acc0;
|
||||
sumf1 += d1 * acc1;
|
||||
}
|
||||
}
|
||||
|
||||
#if IQ1_M_MV_NSG > 1
|
||||
__local float2 part[IQ1_M_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < IQ1_M_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#undef IQ1_M_GRID
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef IQ1_S_MV_NSG
|
||||
#define IQ1_S_MV_NSG 4
|
||||
#endif
|
||||
|
||||
inline float4 iq1_s_vals(uint g) {
|
||||
return (float4)((float)( g & 0xFu), (float)((g >> 8) & 0xFu),
|
||||
(float)((g >> 16) & 0xFu), (float)((g >> 24) & 0xFu));
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_iq1_s_f32(
|
||||
__read_only image1d_buffer_t grid_img,
|
||||
global const uchar * src0_qs,
|
||||
global const ushort * src0_qh,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne10,
|
||||
int ne0
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
#define IQ1_S_GRID(i) (read_imageui(grid_img, (int)(i)).x)
|
||||
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f, sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += IQ1_S_MV_NSG) {
|
||||
const half2 dh = vload2(j + ib * mh, src0_d);
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint sub = ib * 8u + sb;
|
||||
const ushort2 qhv = vload2(j + sub * mh, src0_qh);
|
||||
const uint h0 = (uint)qhv.s0, h1 = (uint)qhv.s1;
|
||||
|
||||
const float s0 = (float)(2u * ((h0 >> 12) & 7u) + 1u);
|
||||
const float s1 = (float)(2u * ((h1 >> 12) & 7u) + 1u);
|
||||
const float t0 = ((h0 & 0x8000u) ? -0.125f : 0.125f) - 1.0f;
|
||||
const float t1 = ((h1 & 0x8000u) ? -0.125f : 0.125f) - 1.0f;
|
||||
|
||||
const uint gb = j + (sub * 4u) * mh;
|
||||
const uint grp = sub * 8u;
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
float4 as = (float4)(0.f);
|
||||
for (uint l = 0; l < 4u; ++l) {
|
||||
const uint qsv = (uint)qsu[gb + l * mh];
|
||||
const uint g0 = IQ1_S_GRID(( qsv & 0xFFu) | ((((h0 >> (3u*l)) & 7u) << 8)));
|
||||
const uint g1 = IQ1_S_GRID(((qsv >> 8) & 0xFFu) | ((((h1 >> (3u*l)) & 7u) << 8)));
|
||||
|
||||
const float4 y0 = vload4(grp + 2u*l + 0u, y);
|
||||
const float4 y1 = vload4(grp + 2u*l + 1u, y);
|
||||
as += y0 + y1;
|
||||
a0 += dot(y0, iq1_s_vals(g0)) + dot(y1, iq1_s_vals(g0 >> 4));
|
||||
a1 += dot(y0, iq1_s_vals(g1)) + dot(y1, iq1_s_vals(g1 >> 4));
|
||||
}
|
||||
|
||||
const float asum = as.s0 + as.s1 + as.s2 + as.s3;
|
||||
acc0 += s0 * (a0 + t0 * asum);
|
||||
acc1 += s1 * (a1 + t1 * asum);
|
||||
}
|
||||
sumf += (float)dh.s0 * acc0;
|
||||
sumf1 += (float)dh.s1 * acc1;
|
||||
}
|
||||
}
|
||||
|
||||
#if IQ1_S_MV_NSG > 1
|
||||
__local float2 part[IQ1_S_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < IQ1_S_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#undef IQ1_S_GRID
|
||||
}
|
||||
|
||||
kernel void kernel_iq1_s_grid_export(global uint * out) {
|
||||
const uint i = get_global_id(0);
|
||||
if (i < 2048u) {
|
||||
out[i] = iq1_s_grid[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef IQ3_S_MV_NSG
|
||||
#define IQ3_S_MV_NSG 4
|
||||
#endif
|
||||
|
||||
inline float4 iq3_s_vals(uint gv, uint sg, uint base) {
|
||||
|
||||
const uint s = sg >> base;
|
||||
const uint4 sgn = (uint4)(s << 31, s << 30, s << 29, s << 28) & 0x80000000u;
|
||||
return as_float4(as_uint4(convert_float4(as_uchar4(gv))) ^ sgn);
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_iq3_s_f32(
|
||||
__read_only image1d_buffer_t grid_img,
|
||||
global const uchar * src0_qs,
|
||||
global const uchar * src0_sg,
|
||||
global const uchar * src0_qh,
|
||||
global const uchar * src0_sc,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne10,
|
||||
int ne0
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
#define IQ3_S_GRID(i) (read_imageui(grid_img, (int)(i)).x)
|
||||
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
global const ushort * qhu = (global const ushort *)src0_qh;
|
||||
global const ushort * sgu = (global const ushort *)src0_sg;
|
||||
global const ushort * scu = (global const ushort *)src0_sc;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += IQ3_S_MV_NSG) {
|
||||
const half2 dh = vload2(j + ib * mh, src0_d);
|
||||
const float d0 = (float)dh.s0;
|
||||
const float d1 = (float)dh.s1;
|
||||
|
||||
const uint scbase = j + ib * 4u * mh;
|
||||
ushort sc4[4];
|
||||
sc4[0] = scu[scbase + 0u * mh];
|
||||
sc4[1] = scu[scbase + 1u * mh];
|
||||
sc4[2] = scu[scbase + 2u * mh];
|
||||
sc4[3] = scu[scbase + 3u * mh];
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint scv = (uint)sc4[sb >> 1];
|
||||
const uint nib0 = (sb & 1u) ? ((scv >> 4) & 0xFu) : ( scv & 0xFu);
|
||||
const uint nib1 = (sb & 1u) ? ((scv >> 12) & 0xFu) : ((scv >> 8) & 0xFu);
|
||||
|
||||
const uint qhv = (uint)qhu[j + (ib * 8u + sb) * mh];
|
||||
const uint qh0 = qhv & 0xFFu;
|
||||
const uint qh1 = qhv >> 8;
|
||||
|
||||
const uint grp = ib * 64u + sb * 8u;
|
||||
const uint qsb = j + grp * mh;
|
||||
const uint sgb = j + (ib * 32u + sb * 4u) * mh;
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
for (uint u = 0; u < 8u; ++u) {
|
||||
const uint qsv = (uint)qsu[qsb + u * mh];
|
||||
const uint sgv = (uint)sgu[sgb + (u >> 1) * mh];
|
||||
const uint g0 = ( qsv & 0xFFu) | (((qh0 >> u) & 1u) << 8);
|
||||
const uint g1 = ((qsv >> 8) & 0xFFu) | (((qh1 >> u) & 1u) << 8);
|
||||
const uint base = (u & 1u) * 4u;
|
||||
const float4 yv = vload4(grp + u, y);
|
||||
const uint gw0 = IQ3_S_GRID(g0);
|
||||
const uint gw1 = IQ3_S_GRID(g1);
|
||||
a0 += dot(yv, iq3_s_vals(gw0, sgv & 0xFFu, base));
|
||||
a1 += dot(yv, iq3_s_vals(gw1, (sgv >> 8) & 0xFFu, base));
|
||||
}
|
||||
acc0 += (float)(1u + 2u * nib0) * a0;
|
||||
acc1 += (float)(1u + 2u * nib1) * a1;
|
||||
}
|
||||
sumf += d0 * acc0;
|
||||
sumf1 += d1 * acc1;
|
||||
}
|
||||
}
|
||||
|
||||
#if IQ3_S_MV_NSG > 1
|
||||
__local float2 part[IQ3_S_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < IQ3_S_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
}
|
||||
|
||||
kernel void kernel_iq3_s_grid_export(global uint * out) {
|
||||
out[get_global_id(0)] = iq3_s_grid[get_global_id(0)];
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef IQ3_XXS_MV_NSG
|
||||
#define IQ3_XXS_MV_NSG 4
|
||||
#endif
|
||||
|
||||
inline float4 iq3_xxs_vals(uint gv, uint sgv, uint base) {
|
||||
const uint s = sgv >> base;
|
||||
const uint4 sgn = (uint4)(s << 31, s << 30, s << 29, s << 28) & 0x80000000u;
|
||||
return as_float4(as_uint4(convert_float4(as_uchar4(gv))) ^ sgn);
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_iq3_xxs_f32(
|
||||
__read_only image1d_buffer_t grid_img,
|
||||
global const uchar * src0_qs,
|
||||
global const uint * src0_sas,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne10,
|
||||
int ne0
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint K = (uint)ne00;
|
||||
const uint nsb = K / QK_K;
|
||||
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
#define IQ3_XXS_GRID(i) (read_imageui(grid_img, (int)(i)).x)
|
||||
|
||||
const uint mh = m >> 1;
|
||||
const uint j = get_group_id(0) * 64u + lid;
|
||||
const uint row = j << 1;
|
||||
|
||||
float sumf = 0.f;
|
||||
float sumf1 = 0.f;
|
||||
|
||||
if (j < mh) {
|
||||
global const ushort * qsu = (global const ushort *)src0_qs;
|
||||
|
||||
for (uint ib = sgi; ib < nsb; ib += IQ3_XXS_MV_NSG) {
|
||||
const half2 dh = vload2(j + ib * mh, src0_d);
|
||||
const float d0 = (float)dh.s0;
|
||||
const float d1 = (float)dh.s1;
|
||||
|
||||
float acc0 = 0.f, acc1 = 0.f;
|
||||
for (uint sb = 0; sb < 8u; ++sb) {
|
||||
const uint sub = ib * 8u + sb;
|
||||
const uint2 aux = vload2(j + sub * mh, src0_sas);
|
||||
|
||||
const uint grp = sub * 8u;
|
||||
const uint qsb = j + grp * mh;
|
||||
|
||||
float a0 = 0.f, a1 = 0.f;
|
||||
for (uint u = 0; u < 4u; ++u) {
|
||||
const uint qsv0 = (uint)qsu[qsb + (2u*u)*mh];
|
||||
const uint qsv1 = (uint)qsu[qsb + (2u*u + 1u)*mh];
|
||||
const uint sg0 = iq3_xxs_signs((aux.s0 >> (7u * u)) & 127u);
|
||||
const uint sg1 = iq3_xxs_signs((aux.s1 >> (7u * u)) & 127u);
|
||||
|
||||
const uint g00 = qsv0 & 0xFFu;
|
||||
const uint g01 = qsv1 & 0xFFu;
|
||||
const uint g10 = (qsv0 >> 8) & 0xFFu;
|
||||
const uint g11 = (qsv1 >> 8) & 0xFFu;
|
||||
|
||||
const float4 yl = vload4(sub * 8u + u * 2u + 0u, y);
|
||||
const float4 yh = vload4(sub * 8u + u * 2u + 1u, y);
|
||||
|
||||
a0 += dot(yl, iq3_xxs_vals(IQ3_XXS_GRID(g00), sg0, 0u))
|
||||
+ dot(yh, iq3_xxs_vals(IQ3_XXS_GRID(g01), sg0, 4u));
|
||||
a1 += dot(yl, iq3_xxs_vals(IQ3_XXS_GRID(g10), sg1, 0u))
|
||||
+ dot(yh, iq3_xxs_vals(IQ3_XXS_GRID(g11), sg1, 4u));
|
||||
}
|
||||
acc0 += (0.5f + (float)(aux.s0 >> 28)) * a0;
|
||||
acc1 += (0.5f + (float)(aux.s1 >> 28)) * a1;
|
||||
}
|
||||
sumf += d0 * 0.5f * acc0;
|
||||
sumf1 += d1 * 0.5f * acc1;
|
||||
}
|
||||
}
|
||||
|
||||
#if IQ3_XXS_MV_NSG > 1
|
||||
__local float2 part[IQ3_XXS_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sumf, sumf1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < IQ3_XXS_MV_NSG; ++s) {
|
||||
const float2 p = part[s][lid];
|
||||
sumf += p.s0;
|
||||
sumf1 += p.s1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sumf, sumf1), 0, dst + (ulong)col * (uint)ne0 + row);
|
||||
}
|
||||
#undef IQ3_XXS_GRID
|
||||
}
|
||||
|
||||
kernel void kernel_iq3_xxs_grid_export(global uint * out) {
|
||||
const uint i = get_global_id(0);
|
||||
if (i < 256u) {
|
||||
out[i] = iq3_xxs_grid[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
#ifndef IQ4_XS_MV_NSG
|
||||
#define IQ4_XS_MV_NSG 8
|
||||
#endif
|
||||
|
||||
inline float4 iq4_xs_vals(ushort q) {
|
||||
return (float4)((char)iq4_xs_code(q & 15), (char)iq4_xs_code((q >> 4) & 15),
|
||||
(char)iq4_xs_code((q >> 8) & 15), (char)iq4_xs_code(q >> 12));
|
||||
}
|
||||
|
||||
kernel void kernel_gemv_noshuffle_iq4_xs_f32(
|
||||
global const ushort * src0_qs,
|
||||
global const uint * src0_sl,
|
||||
global const ushort * src0_sh,
|
||||
global const half * src0_d,
|
||||
global const float * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne10,
|
||||
int ne0
|
||||
) {
|
||||
src1 = (global const float *)((global const char *)src1 + offset1);
|
||||
dst = (global float *)((global char *)dst + offsetd);
|
||||
|
||||
const uint m = (uint)ne01;
|
||||
const uint mh = m >> 1;
|
||||
const uint lid = get_local_id(0);
|
||||
const uint sgi = get_local_id(1);
|
||||
const uint col = get_group_id(1);
|
||||
const uint j = get_group_id(0)*64u + lid;
|
||||
const uint row = j << 1;
|
||||
global const float * y = src1 + (ulong)col * (uint)ne10;
|
||||
|
||||
float sum0 = 0.0f, sum1 = 0.0f;
|
||||
if (j < mh) {
|
||||
global const uint * qs = (global const uint *)src0_qs;
|
||||
global const uint * sh = (global const uint *)src0_sh;
|
||||
for (uint ib = sgi; ib < (uint)ne00/QK_K; ib += IQ4_XS_MV_NSG) {
|
||||
const uint base = j + ib*mh;
|
||||
const uint2 sl = vload2(base, src0_sl);
|
||||
const uint shp = sh[base];
|
||||
const half2 d = vload2(base, src0_d);
|
||||
float acc0 = 0.0f, acc1 = 0.0f;
|
||||
for (uint sb = 0; sb < 8; ++sb) {
|
||||
const int ls0 = (int)(((sl.s0 >> (4*sb)) & 15u) | (((shp >> (2*sb)) & 3u) << 4)) - 32;
|
||||
const int ls1 = (int)(((sl.s1 >> (4*sb)) & 15u) | (((shp >> (16 + 2*sb)) & 3u) << 4)) - 32;
|
||||
float a0 = 0.0f, a1 = 0.0f;
|
||||
for (uint u = 0; u < 8; ++u) {
|
||||
const uint group = ib*64 + sb*8 + u;
|
||||
const uint q = qs[j + group*mh];
|
||||
const float4 yv = vload4(group, y);
|
||||
a0 += dot(yv, iq4_xs_vals((ushort)q));
|
||||
a1 += dot(yv, iq4_xs_vals((ushort)(q >> 16)));
|
||||
}
|
||||
acc0 += (float)ls0*a0;
|
||||
acc1 += (float)ls1*a1;
|
||||
}
|
||||
sum0 += (float)d.s0*acc0;
|
||||
sum1 += (float)d.s1*acc1;
|
||||
}
|
||||
}
|
||||
|
||||
#if IQ4_XS_MV_NSG > 1
|
||||
local float2 part[IQ4_XS_MV_NSG][64];
|
||||
part[sgi][lid] = (float2)(sum0, sum1);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (sgi != 0) {
|
||||
return;
|
||||
}
|
||||
for (uint s = 1; s < IQ4_XS_MV_NSG; ++s) {
|
||||
const float2 v = part[s][lid];
|
||||
sum0 += v.s0;
|
||||
sum1 += v.s1;
|
||||
}
|
||||
#endif
|
||||
if (j < mh) {
|
||||
vstore2((float2)(sum0, sum1), 0, dst + (ulong)col*(uint)ne0 + row);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// IQ1 codebook: each byte packs grid[j] + 1 and grid[j + 4] + 1.
|
||||
constant uint iq1_s_grid[2048] = {
|
||||
0x00000000, 0x00000002, 0x00000101, 0x00000200, 0x00000202, 0x00010001, 0x00010101, 0x00020000,
|
||||
0x00020002, 0x00020200, 0x00020202, 0x01000101, 0x01010001, 0x01010100, 0x01010102, 0x01020101,
|
||||
0x02000000, 0x02000002, 0x02000200, 0x02000202, 0x02010101, 0x02020000, 0x02020002, 0x02020200,
|
||||
0x02020202, 0x00000110, 0x00000111, 0x00010011, 0x00010110, 0x00010112, 0x00010211, 0x00010212,
|
||||
0x00020111, 0x01000011, 0x01000112, 0x01000211, 0x01010012, 0x01010111, 0x01010212, 0x01020011,
|
||||
0x01020110, 0x01020112, 0x01020210, 0x02000111, 0x02010011, 0x02010110, 0x02010112, 0x02020111,
|
||||
0x00000020, 0x00000022, 0x00000220, 0x00000222, 0x00010121, 0x00020020, 0x00020022, 0x00020220,
|
||||
0x00020222, 0x01000121, 0x01010021, 0x01010221, 0x01020120, 0x01020221, 0x02000020, 0x02000022,
|
||||
0x02000220, 0x02000222, 0x02010021, 0x02010121, 0x02010221, 0x02020020, 0x02020022, 0x02020220,
|
||||
0x02020222, 0x00011001, 0x00011100, 0x00011102, 0x00021101, 0x01001001, 0x01001201, 0x01011101,
|
||||
0x01011202, 0x01021100, 0x01021101, 0x02011001, 0x02011201, 0x02021101, 0x00001011, 0x00001110,
|
||||
0x00001111, 0x00001112, 0x00011111, 0x00011210, 0x00011212, 0x00021211, 0x01001010, 0x01001111,
|
||||
0x01001212, 0x01011010, 0x01011011, 0x01011110, 0x01011111, 0x01011112, 0x01011211, 0x01021010,
|
||||
0x01021012, 0x01021111, 0x01021210, 0x01021212, 0x02001011, 0x02011011, 0x02011111, 0x02011210,
|
||||
0x02011212, 0x02021011, 0x02021110, 0x02021111, 0x02021112, 0x02021211, 0x00011120, 0x00011221,
|
||||
0x01001021, 0x01001120, 0x01011020, 0x01011022, 0x01011121, 0x01011220, 0x01021020, 0x01021021,
|
||||
0x01021122, 0x01021221, 0x02001121, 0x02011021, 0x02011120, 0x02011221, 0x00002000, 0x00002002,
|
||||
0x00002200, 0x00002202, 0x00012101, 0x00022000, 0x00022002, 0x00022200, 0x00022202, 0x01002101,
|
||||
0x01012001, 0x01012102, 0x01022101, 0x02002000, 0x02002002, 0x02002200, 0x02002202, 0x02012101,
|
||||
0x02022000, 0x02022002, 0x02022200, 0x02022202, 0x00002111, 0x00012011, 0x00012110, 0x00012211,
|
||||
0x00022110, 0x00022111, 0x01002011, 0x01012010, 0x01012011, 0x01012111, 0x01022011, 0x01022110,
|
||||
0x01022211, 0x02012011, 0x02012110, 0x02012112, 0x02012211, 0x02022111, 0x00002020, 0x00002022,
|
||||
0x00002220, 0x00002222, 0x00012121, 0x00022020, 0x00022022, 0x00022220, 0x00022222, 0x01002121,
|
||||
0x01012021, 0x01012221, 0x01022021, 0x01022121, 0x02002020, 0x02002022, 0x02002121, 0x02002220,
|
||||
0x02002222, 0x02012121, 0x02022020, 0x02022022, 0x02022220, 0x02022222, 0x00110000, 0x00110001,
|
||||
0x00110100, 0x00110201, 0x00120100, 0x00120101, 0x01100001, 0x01100100, 0x01110000, 0x01110101,
|
||||
0x01110200, 0x01120001, 0x01120100, 0x01120101, 0x01120201, 0x02110001, 0x02110100, 0x02110102,
|
||||
0x02120001, 0x02120101, 0x00100011, 0x00100110, 0x00100112, 0x00100211, 0x00110010, 0x00110012,
|
||||
0x00110111, 0x00110210, 0x00120011, 0x00120110, 0x00120211, 0x01100111, 0x01100212, 0x01110010,
|
||||
0x01110011, 0x01110012, 0x01110110, 0x01110111, 0x01110112, 0x01110211, 0x01120010, 0x01120111,
|
||||
0x02100110, 0x02110012, 0x02110111, 0x02120011, 0x02120110, 0x00110021, 0x00110120, 0x00110122,
|
||||
0x00120121, 0x01100020, 0x01100122, 0x01100221, 0x01110022, 0x01110121, 0x01110220, 0x01110222,
|
||||
0x01120120, 0x01120122, 0x02100121, 0x02110021, 0x02110120, 0x02110122, 0x02120121, 0x00101001,
|
||||
0x00101102, 0x00101201, 0x00111100, 0x00111101, 0x00111200, 0x00111201, 0x00121001, 0x00121102,
|
||||
0x01101001, 0x01101101, 0x01101102, 0x01101200, 0x01101202, 0x01111001, 0x01111100, 0x01111101,
|
||||
0x01111102, 0x01111201, 0x01121002, 0x01121101, 0x01121200, 0x02101100, 0x02101201, 0x02111000,
|
||||
0x02111100, 0x02111101, 0x02111200, 0x02111201, 0x02111202, 0x02121001, 0x02121100, 0x02121101,
|
||||
0x02121201, 0x00101012, 0x00101111, 0x00101212, 0x00111011, 0x00111110, 0x00111111, 0x00111112,
|
||||
0x00111211, 0x00121010, 0x00121012, 0x00121111, 0x00121210, 0x00121212, 0x01101011, 0x01101110,
|
||||
0x01101111, 0x01101112, 0x01111011, 0x01111012, 0x01111110, 0x01111111, 0x01111112, 0x01111211,
|
||||
0x01111212, 0x01121011, 0x01121110, 0x01121111, 0x01121112, 0x01121211, 0x02101010, 0x02101012,
|
||||
0x02101110, 0x02101111, 0x02101210, 0x02101212, 0x02111010, 0x02111011, 0x02111110, 0x02111111,
|
||||
0x02111112, 0x02111211, 0x02111212, 0x02121010, 0x02121012, 0x02121111, 0x00101021, 0x00101120,
|
||||
0x00101121, 0x00101122, 0x00111121, 0x00111122, 0x00111220, 0x00111222, 0x00121021, 0x00121122,
|
||||
0x01101020, 0x01101022, 0x01101120, 0x01101121, 0x01101220, 0x01101222, 0x01111021, 0x01111121,
|
||||
0x01111122, 0x01111220, 0x01111221, 0x01121021, 0x01121120, 0x01121121, 0x01121220, 0x01121221,
|
||||
0x01121222, 0x02101122, 0x02101222, 0x02111022, 0x02111121, 0x02121120, 0x02121221, 0x00112001,
|
||||
0x00112102, 0x00122101, 0x01102001, 0x01102100, 0x01102102, 0x01102201, 0x01112000, 0x01112101,
|
||||
0x01112200, 0x01112202, 0x01122000, 0x01122001, 0x01122100, 0x01122102, 0x01122201, 0x02102101,
|
||||
0x02112001, 0x02112100, 0x02122101, 0x00112010, 0x00112012, 0x00112111, 0x00112212, 0x00122011,
|
||||
0x00122111, 0x01102012, 0x01102110, 0x01102111, 0x01102210, 0x01112011, 0x01112110, 0x01112111,
|
||||
0x01112112, 0x01112211, 0x01112212, 0x01122010, 0x01122111, 0x01122212, 0x02102211, 0x02112011,
|
||||
0x02112012, 0x02112111, 0x02112210, 0x02122011, 0x02122112, 0x02122211, 0x00102221, 0x00112122,
|
||||
0x00122120, 0x00122122, 0x01102120, 0x01102122, 0x01102221, 0x01112020, 0x01112022, 0x01112121,
|
||||
0x01112220, 0x01122021, 0x01122122, 0x01122221, 0x02102121, 0x02112021, 0x02112122, 0x02112222,
|
||||
0x00200000, 0x00200002, 0x00200200, 0x00200202, 0x00210101, 0x00220000, 0x00220002, 0x00220101,
|
||||
0x00220200, 0x00220202, 0x01200101, 0x01210001, 0x01210201, 0x01220001, 0x01220101, 0x02200000,
|
||||
0x02200002, 0x02200200, 0x02200202, 0x02210101, 0x02220000, 0x02220002, 0x02220101, 0x02220200,
|
||||
0x02220202, 0x00200111, 0x00210011, 0x00210110, 0x00210211, 0x00220111, 0x01200012, 0x01200110,
|
||||
0x01200211, 0x01210111, 0x01210210, 0x01210212, 0x01220011, 0x01220110, 0x01220111, 0x01220112,
|
||||
0x02200111, 0x02210010, 0x02210112, 0x02210211, 0x02220111, 0x00200021, 0x00200220, 0x00200222,
|
||||
0x00210021, 0x00210121, 0x00220020, 0x00220022, 0x00220220, 0x00220222, 0x01200121, 0x01210021,
|
||||
0x01210122, 0x01210221, 0x01220121, 0x02200021, 0x02200220, 0x02200222, 0x02210021, 0x02210121,
|
||||
0x02220020, 0x02220022, 0x02220220, 0x02220222, 0x00201101, 0x00211100, 0x00211102, 0x00211201,
|
||||
0x00221101, 0x01201100, 0x01201101, 0x01201102, 0x01201201, 0x01211002, 0x01211101, 0x01211200,
|
||||
0x01211202, 0x01221102, 0x02201101, 0x02211001, 0x02211100, 0x02211201, 0x02221001, 0x02221101,
|
||||
0x00201211, 0x00211111, 0x00221011, 0x00221211, 0x01201010, 0x01201111, 0x01201210, 0x01211011,
|
||||
0x01211110, 0x01211111, 0x01211211, 0x01221012, 0x01221111, 0x01221210, 0x02201211, 0x02211010,
|
||||
0x02211110, 0x02211111, 0x02211210, 0x02211212, 0x02221011, 0x02221110, 0x02221112, 0x02221211,
|
||||
0x00201121, 0x00211020, 0x00211022, 0x00211221, 0x00221121, 0x01201021, 0x01201221, 0x01211121,
|
||||
0x01221020, 0x01221021, 0x01221221, 0x02201120, 0x02201122, 0x02211020, 0x02211222, 0x00202000,
|
||||
0x00202002, 0x00202200, 0x00202202, 0x00212101, 0x00222000, 0x00222002, 0x00222200, 0x00222202,
|
||||
0x01202101, 0x01212001, 0x01212100, 0x01222101, 0x02202000, 0x02202002, 0x02202200, 0x02202202,
|
||||
0x02222000, 0x02222002, 0x02222200, 0x02222202, 0x00202211, 0x00212011, 0x00212110, 0x00212211,
|
||||
0x00222111, 0x01202112, 0x01202211, 0x01212012, 0x01212111, 0x01222011, 0x01222110, 0x01222112,
|
||||
0x01222211, 0x02202111, 0x02212010, 0x02212112, 0x02212211, 0x02222110, 0x02222111, 0x00202020,
|
||||
0x00202022, 0x00202220, 0x00202222, 0x00222020, 0x00222022, 0x00222220, 0x00222222, 0x01202121,
|
||||
0x01212021, 0x01212122, 0x01212221, 0x01222121, 0x02202020, 0x02202022, 0x02202220, 0x02202222,
|
||||
0x02212121, 0x02222020, 0x02222022, 0x02222220, 0x02222222, 0x10000101, 0x10010001, 0x10010102,
|
||||
0x10020101, 0x11000201, 0x11010002, 0x11010101, 0x11010200, 0x11010202, 0x11020001, 0x11020100,
|
||||
0x11020102, 0x12010100, 0x12010201, 0x12020001, 0x12020102, 0x10000010, 0x10000011, 0x10000110,
|
||||
0x10000112, 0x10000211, 0x10010012, 0x10010111, 0x10010112, 0x10010210, 0x10010212, 0x10020011,
|
||||
0x10020112, 0x10020211, 0x11000111, 0x11000210, 0x11000212, 0x11010011, 0x11010110, 0x11010111,
|
||||
0x11010112, 0x11010211, 0x11010212, 0x11020111, 0x11020210, 0x11020212, 0x12000011, 0x12000110,
|
||||
0x12000112, 0x12010010, 0x12010012, 0x12010111, 0x12020010, 0x12020011, 0x12020012, 0x10000121,
|
||||
0x10010021, 0x10010120, 0x10010122, 0x10020121, 0x11000021, 0x11010022, 0x11010121, 0x11010222,
|
||||
0x11020120, 0x11020221, 0x12000221, 0x12010120, 0x12020121, 0x10001001, 0x10011101, 0x10011201,
|
||||
0x10021201, 0x11001101, 0x11001200, 0x11001202, 0x11011001, 0x11011100, 0x11011101, 0x11011102,
|
||||
0x11021001, 0x11021002, 0x11021101, 0x11021200, 0x11021202, 0x12001001, 0x12001102, 0x12001201,
|
||||
0x12011000, 0x12011002, 0x12011101, 0x12021000, 0x12021001, 0x12021201, 0x10001011, 0x10001012,
|
||||
0x10001111, 0x10001212, 0x10011011, 0x10011110, 0x10011111, 0x10011112, 0x10011211, 0x10021010,
|
||||
0x10021111, 0x10021212, 0x11001011, 0x11001110, 0x11001111, 0x11001112, 0x11001211, 0x11011010,
|
||||
0x11011011, 0x11011110, 0x11011111, 0x11011112, 0x11011210, 0x11011211, 0x11021011, 0x11021110,
|
||||
0x11021111, 0x11021112, 0x11021211, 0x12001012, 0x12001110, 0x12001111, 0x12001210, 0x12011011,
|
||||
0x12011110, 0x12011111, 0x12011112, 0x12011211, 0x12011212, 0x12021111, 0x12021210, 0x12021212,
|
||||
0x10001021, 0x10001121, 0x10001221, 0x10011120, 0x10011121, 0x10011220, 0x10011222, 0x10021021,
|
||||
0x10021120, 0x10021221, 0x11001020, 0x11001022, 0x11001121, 0x11001220, 0x11011020, 0x11011021,
|
||||
0x11011022, 0x11011121, 0x11011122, 0x11011221, 0x11021022, 0x11021121, 0x11021220, 0x12001021,
|
||||
0x12001121, 0x12001222, 0x12011120, 0x12011121, 0x12021021, 0x12021120, 0x12021122, 0x10002101,
|
||||
0x10012001, 0x10012101, 0x10012202, 0x10022101, 0x11002002, 0x11002201, 0x11012000, 0x11012101,
|
||||
0x11012200, 0x11022001, 0x11022100, 0x11022102, 0x11022201, 0x12002101, 0x12012001, 0x12012100,
|
||||
0x12012102, 0x12012201, 0x12022101, 0x10002011, 0x10002111, 0x10002112, 0x10002212, 0x10012010,
|
||||
0x10012110, 0x10012111, 0x10012210, 0x10022011, 0x10022110, 0x10022112, 0x11002010, 0x11002111,
|
||||
0x11002212, 0x11012011, 0x11012012, 0x11012110, 0x11012111, 0x11012112, 0x11012211, 0x11022010,
|
||||
0x11022012, 0x11022111, 0x11022112, 0x11022212, 0x12002112, 0x12002211, 0x12012012, 0x12012111,
|
||||
0x12012112, 0x12012210, 0x12022011, 0x12022110, 0x12022112, 0x12022211, 0x10012122, 0x11002120,
|
||||
0x11002122, 0x11002221, 0x11012121, 0x11012220, 0x11012222, 0x11022120, 0x11022221, 0x12012120,
|
||||
0x12022121, 0x10100001, 0x10100100, 0x10100101, 0x10100102, 0x10100201, 0x10110002, 0x10110101,
|
||||
0x10110202, 0x10120001, 0x10120100, 0x10120201, 0x11100000, 0x11100101, 0x11100200, 0x11110001,
|
||||
0x11110100, 0x11110101, 0x11110102, 0x11110201, 0x11120101, 0x11120200, 0x12100102, 0x12100201,
|
||||
0x12110101, 0x12110200, 0x12120000, 0x12120001, 0x12120102, 0x12120201, 0x10100111, 0x10100210,
|
||||
0x10100211, 0x10100212, 0x10110011, 0x10110110, 0x10110111, 0x10110112, 0x10110210, 0x10110211,
|
||||
0x10120010, 0x10120111, 0x10120112, 0x10120210, 0x10120212, 0x11100011, 0x11100110, 0x11100111,
|
||||
0x11100112, 0x11100211, 0x11110010, 0x11110011, 0x11110012, 0x11110110, 0x11110111, 0x11110112,
|
||||
0x11110210, 0x11110211, 0x11110212, 0x11120011, 0x11120110, 0x11120111, 0x11120112, 0x11120211,
|
||||
0x12100012, 0x12100111, 0x12110011, 0x12110110, 0x12110111, 0x12110112, 0x12110211, 0x12120010,
|
||||
0x12120111, 0x12120212, 0x10100021, 0x10100122, 0x10110022, 0x10110121, 0x10110222, 0x10120021,
|
||||
0x10120120, 0x11100022, 0x11100121, 0x11100222, 0x11110021, 0x11110120, 0x11110121, 0x11110122,
|
||||
0x11110221, 0x11120022, 0x11120121, 0x12100121, 0x12110020, 0x12110022, 0x12110121, 0x12110221,
|
||||
0x12110222, 0x12120120, 0x10101100, 0x10101101, 0x10111001, 0x10111100, 0x10111101, 0x10111102,
|
||||
0x10111200, 0x10111201, 0x10121001, 0x10121101, 0x10121200, 0x10121202, 0x11101001, 0x11101100,
|
||||
0x11101101, 0x11101102, 0x11101201, 0x11101202, 0x11111000, 0x11111001, 0x11111100, 0x11111101,
|
||||
0x11111102, 0x11111200, 0x11111201, 0x11111202, 0x11121001, 0x11121002, 0x11121100, 0x11121101,
|
||||
0x11121102, 0x11121201, 0x12101000, 0x12101200, 0x12101202, 0x12111001, 0x12111100, 0x12111101,
|
||||
0x12111102, 0x12111201, 0x12121001, 0x12121100, 0x12121101, 0x12121202, 0x10101011, 0x10101012,
|
||||
0x10101110, 0x10101111, 0x10101112, 0x10101211, 0x10111010, 0x10111011, 0x10111012, 0x10111110,
|
||||
0x10111111, 0x10111112, 0x10111211, 0x10111212, 0x10121011, 0x10121110, 0x10121111, 0x10121112,
|
||||
0x10121211, 0x11101010, 0x11101011, 0x11101012, 0x11101110, 0x11101111, 0x11101112, 0x11101210,
|
||||
0x11101211, 0x11111010, 0x11111011, 0x11111012, 0x11111110, 0x11111111, 0x11111112, 0x11111210,
|
||||
0x11111211, 0x11111212, 0x11121010, 0x11121011, 0x11121110, 0x11121111, 0x11121112, 0x11121210,
|
||||
0x11121211, 0x11121212, 0x12101011, 0x12101110, 0x12101111, 0x12101211, 0x12101212, 0x12111010,
|
||||
0x12111011, 0x12111110, 0x12111111, 0x12111112, 0x12111210, 0x12111211, 0x12121011, 0x12121110,
|
||||
0x12121111, 0x12121112, 0x12121211, 0x10101020, 0x10101021, 0x10101022, 0x10101120, 0x10101122,
|
||||
0x10101220, 0x10101221, 0x10111021, 0x10111120, 0x10111121, 0x10111220, 0x10111221, 0x10121020,
|
||||
0x10121021, 0x10121022, 0x10121120, 0x10121121, 0x10121122, 0x10121220, 0x10121221, 0x11101021,
|
||||
0x11101121, 0x11101122, 0x11101220, 0x11101221, 0x11101222, 0x11111020, 0x11111021, 0x11111022,
|
||||
0x11111120, 0x11111121, 0x11111122, 0x11111220, 0x11111221, 0x11111222, 0x11121021, 0x11121120,
|
||||
0x11121121, 0x11121221, 0x12101022, 0x12101121, 0x12101122, 0x12101220, 0x12101221, 0x12101222,
|
||||
0x12111021, 0x12111121, 0x12111222, 0x12121022, 0x12121121, 0x12121122, 0x12121220, 0x12121221,
|
||||
0x10102100, 0x10102101, 0x10102102, 0x10102201, 0x10112000, 0x10112101, 0x10112200, 0x10122001,
|
||||
0x10122202, 0x11102101, 0x11102200, 0x11102202, 0x11112001, 0x11112100, 0x11112101, 0x11112102,
|
||||
0x11112200, 0x11112201, 0x11122000, 0x11122002, 0x11122100, 0x11122101, 0x12102002, 0x12102201,
|
||||
0x12112000, 0x12112002, 0x12112101, 0x12112200, 0x12122001, 0x12122201, 0x10102011, 0x10102012,
|
||||
0x10102111, 0x10102212, 0x10112011, 0x10112110, 0x10112111, 0x10112112, 0x10112211, 0x10122111,
|
||||
0x11102011, 0x11102110, 0x11102111, 0x11102112, 0x11102211, 0x11112010, 0x11112011, 0x11112012,
|
||||
0x11112110, 0x11112111, 0x11112112, 0x11112210, 0x11112211, 0x11112212, 0x11122011, 0x11122110,
|
||||
0x11122111, 0x11122112, 0x11122211, 0x12102011, 0x12102111, 0x12102211, 0x12112011, 0x12112110,
|
||||
0x12112111, 0x12112112, 0x12112210, 0x12112211, 0x12122111, 0x10102120, 0x10102220, 0x10112121,
|
||||
0x10112222, 0x10122020, 0x10122121, 0x10122122, 0x10122221, 0x11102121, 0x11102220, 0x11102221,
|
||||
0x11112021, 0x11112121, 0x11112122, 0x11112220, 0x11112221, 0x11122022, 0x11122121, 0x11122220,
|
||||
0x11122222, 0x12102021, 0x12102222, 0x12112022, 0x12112121, 0x12112122, 0x12112220, 0x12112222,
|
||||
0x12122021, 0x10200101, 0x10210100, 0x10210102, 0x10210201, 0x10220101, 0x11200100, 0x11210000,
|
||||
0x11210101, 0x11210102, 0x11210200, 0x11210202, 0x11220001, 0x11220100, 0x11220102, 0x11220201,
|
||||
0x12200001, 0x12210102, 0x12220101, 0x10200011, 0x10200110, 0x10200112, 0x10200211, 0x10210012,
|
||||
0x10210111, 0x10220011, 0x10220012, 0x10220112, 0x10220211, 0x11200111, 0x11200211, 0x11210011,
|
||||
0x11210111, 0x11210112, 0x11210211, 0x11220111, 0x11220112, 0x11220212, 0x12200110, 0x12200212,
|
||||
0x12210012, 0x12210111, 0x12220011, 0x12220112, 0x12220211, 0x10210021, 0x10210122, 0x10210221,
|
||||
0x11200020, 0x11200021, 0x11200122, 0x11210121, 0x11210122, 0x11210220, 0x11220020, 0x12200121,
|
||||
0x12210021, 0x12210122, 0x12220121, 0x10211001, 0x10211002, 0x10211101, 0x10211102, 0x10211202,
|
||||
0x10221001, 0x10221102, 0x10221201, 0x11201000, 0x11201002, 0x11201101, 0x11201200, 0x11201202,
|
||||
0x11211001, 0x11211100, 0x11211101, 0x11211102, 0x11211201, 0x11211202, 0x11221000, 0x11221002,
|
||||
0x11221101, 0x12201100, 0x12201101, 0x12201201, 0x12211000, 0x12211002, 0x12211100, 0x12211101,
|
||||
0x12211102, 0x12211200, 0x12211202, 0x12221001, 0x12221100, 0x12221201, 0x10201111, 0x10201210,
|
||||
0x10201212, 0x10211011, 0x10211111, 0x10211112, 0x10211211, 0x11201110, 0x11201111, 0x11201112,
|
||||
0x11201211, 0x11211010, 0x11211011, 0x11211110, 0x11211111, 0x11211112, 0x11211211, 0x11221011,
|
||||
0x11221110, 0x11221111, 0x11221112, 0x11221211, 0x12201112, 0x12201211, 0x12201212, 0x12211011,
|
||||
0x12211111, 0x12211112, 0x12211211, 0x12211212, 0x12221012, 0x12221111, 0x12221112, 0x12221210,
|
||||
0x10201022, 0x10201221, 0x10211121, 0x10221020, 0x10221122, 0x10221220, 0x10221221, 0x11201020,
|
||||
0x11201121, 0x11201220, 0x11201222, 0x11211021, 0x11211120, 0x11211121, 0x11211122, 0x11211220,
|
||||
0x11211222, 0x11221020, 0x11221121, 0x11221220, 0x12201020, 0x12201022, 0x12201121, 0x12201222,
|
||||
0x12211120, 0x12211122, 0x12211220, 0x12211221, 0x12221020, 0x12221120, 0x12221122, 0x12221222,
|
||||
0x10212102, 0x10212201, 0x10222101, 0x11202001, 0x11212002, 0x11212101, 0x11212202, 0x11222001,
|
||||
0x11222201, 0x12202101, 0x12212001, 0x12212200, 0x12222102, 0x10202011, 0x10202110, 0x10212010,
|
||||
0x10212111, 0x10222011, 0x10222110, 0x10222112, 0x10222211, 0x11202010, 0x11202011, 0x11202111,
|
||||
0x11202112, 0x11202210, 0x11212011, 0x11212110, 0x11212111, 0x11212112, 0x11212211, 0x11222010,
|
||||
0x11222111, 0x11222212, 0x12202012, 0x12202110, 0x12202212, 0x12212111, 0x12222011, 0x12222110,
|
||||
0x12222111, 0x12222211, 0x10212021, 0x10212122, 0x10212220, 0x11202021, 0x11202120, 0x11202221,
|
||||
0x11212020, 0x11212121, 0x11212220, 0x11212222, 0x11222120, 0x11222121, 0x11222221, 0x12202122,
|
||||
0x12212120, 0x12212220, 0x12212222, 0x12222122, 0x20000000, 0x20000002, 0x20000200, 0x20000202,
|
||||
0x20020000, 0x20020002, 0x20020200, 0x20020202, 0x21000101, 0x21010000, 0x21010001, 0x21010100,
|
||||
0x21010102, 0x21010201, 0x21020101, 0x22000000, 0x22000002, 0x22000200, 0x22000202, 0x22010101,
|
||||
0x22020000, 0x22020002, 0x22020200, 0x22020202, 0x20000111, 0x20010011, 0x20010110, 0x20010112,
|
||||
0x20010211, 0x20020111, 0x21000011, 0x21000110, 0x21000211, 0x21010010, 0x21010012, 0x21010111,
|
||||
0x21010112, 0x21010210, 0x21010211, 0x21020110, 0x21020112, 0x21020211, 0x22000111, 0x22000211,
|
||||
0x22010110, 0x22010112, 0x22010211, 0x22020111, 0x20000020, 0x20000022, 0x20000220, 0x20000222,
|
||||
0x20010121, 0x20020020, 0x20020022, 0x20020220, 0x20020222, 0x21010021, 0x21010120, 0x21010221,
|
||||
0x21020121, 0x22000020, 0x22000022, 0x22000220, 0x22000222, 0x22010121, 0x22020020, 0x22020022,
|
||||
0x22020220, 0x22020222, 0x20011100, 0x20011201, 0x21001001, 0x21001100, 0x21011001, 0x21011101,
|
||||
0x21011202, 0x21021001, 0x21021100, 0x21021201, 0x22011100, 0x22011201, 0x20001011, 0x20001211,
|
||||
0x20011012, 0x20011111, 0x20011212, 0x20021112, 0x20021211, 0x21001010, 0x21001011, 0x21001111,
|
||||
0x21001210, 0x21011011, 0x21011110, 0x21011111, 0x21011112, 0x21011211, 0x21011212, 0x21021111,
|
||||
0x21021112, 0x21021210, 0x21021212, 0x22001011, 0x22001110, 0x22001112, 0x22001211, 0x22011010,
|
||||
0x22011012, 0x22011111, 0x22011210, 0x22021112, 0x20011021, 0x20011122, 0x20011221, 0x20021121,
|
||||
0x21001021, 0x21001120, 0x21001221, 0x21001222, 0x21011020, 0x21011121, 0x21011221, 0x21011222,
|
||||
0x21021021, 0x21021122, 0x21021222, 0x22001121, 0x22011021, 0x22011222, 0x22021120, 0x20002000,
|
||||
0x20002002, 0x20002200, 0x20002202, 0x20012101, 0x20022000, 0x20022002, 0x20022200, 0x20022202,
|
||||
0x21002001, 0x21002101, 0x21012001, 0x21012100, 0x21012201, 0x21022101, 0x21022201, 0x22002000,
|
||||
0x22002002, 0x22002200, 0x22002202, 0x22012101, 0x22022000, 0x22022002, 0x22022200, 0x22022202,
|
||||
0x20002111, 0x20002112, 0x20012011, 0x20012110, 0x20012112, 0x20022111, 0x21002011, 0x21002110,
|
||||
0x21002112, 0x21002211, 0x21012010, 0x21012012, 0x21012111, 0x21012212, 0x21022011, 0x21022110,
|
||||
0x22002111, 0x22012112, 0x22012211, 0x22022111, 0x20002020, 0x20002022, 0x20002220, 0x20002222,
|
||||
0x20012121, 0x20022020, 0x20022022, 0x20022220, 0x20022222, 0x21002121, 0x21012021, 0x21012120,
|
||||
0x21012122, 0x22002020, 0x22002022, 0x22002220, 0x22002222, 0x22012121, 0x22022020, 0x22022022,
|
||||
0x22022220, 0x22022222, 0x20100101, 0x20110001, 0x20110102, 0x20110200, 0x20110201, 0x20120101,
|
||||
0x21100001, 0x21100102, 0x21100201, 0x21110101, 0x21110200, 0x21110202, 0x21120201, 0x21120202,
|
||||
0x22100101, 0x22110001, 0x22110100, 0x22110102, 0x22110201, 0x22120101, 0x20100011, 0x20100110,
|
||||
0x20100112, 0x20100211, 0x20110010, 0x20110111, 0x20110210, 0x20110212, 0x20120011, 0x20120110,
|
||||
0x20120112, 0x20120211, 0x21100010, 0x21100111, 0x21110010, 0x21110011, 0x21110110, 0x21110111,
|
||||
0x21110112, 0x21110211, 0x21120012, 0x21120111, 0x22100110, 0x22100112, 0x22110012, 0x22110111,
|
||||
0x22110210, 0x22120011, 0x22120110, 0x22120112, 0x22120211, 0x20100121, 0x20110021, 0x20110120,
|
||||
0x20110221, 0x20120121, 0x21100120, 0x21100122, 0x21100221, 0x21110020, 0x21110022, 0x21110121,
|
||||
0x21110220, 0x21120122, 0x21120221, 0x22100121, 0x22110120, 0x22110122, 0x22120221, 0x20101001,
|
||||
0x20101100, 0x20101102, 0x20111000, 0x20111101, 0x20111200, 0x20121102, 0x21101000, 0x21101202,
|
||||
0x21111001, 0x21111100, 0x21111101, 0x21111102, 0x21111200, 0x21111201, 0x21121000, 0x21121001,
|
||||
0x21121002, 0x21121101, 0x22101100, 0x22101102, 0x22111002, 0x22111100, 0x22111101, 0x22111200,
|
||||
0x22121001, 0x22121201, 0x20101010, 0x20101111, 0x20101210, 0x20101212, 0x20111010, 0x20111011,
|
||||
0x20111110, 0x20111111, 0x20111112, 0x20111211, 0x20121011, 0x20121111, 0x20121211, 0x20121212,
|
||||
0x21101011, 0x21101110, 0x21101111, 0x21101112, 0x21101211, 0x21111010, 0x21111011, 0x21111012,
|
||||
0x21111110, 0x21111111, 0x21111112, 0x21111210, 0x21111211, 0x21111212, 0x21121011, 0x21121110,
|
||||
0x21121111, 0x21121112, 0x21121211, 0x22101011, 0x22101111, 0x22101210, 0x22111011, 0x22111012,
|
||||
0x22111110, 0x22111111, 0x22111112, 0x22111211, 0x22111212, 0x22121010, 0x22121012, 0x22121111,
|
||||
0x22121210, 0x22121212, 0x20101021, 0x20101120, 0x20111020, 0x20111121, 0x20111221, 0x20121020,
|
||||
0x20121122, 0x20121221, 0x21101121, 0x21101220, 0x21101221, 0x21111021, 0x21111022, 0x21111121,
|
||||
0x21111122, 0x21111221, 0x21121121, 0x21121220, 0x22101022, 0x22101120, 0x22101221, 0x22101222,
|
||||
0x22111022, 0x22111120, 0x22111121, 0x22121120, 0x22121122, 0x22121221, 0x20102101, 0x20112102,
|
||||
0x20112201, 0x20122101, 0x21102001, 0x21102102, 0x21112000, 0x21112002, 0x21112101, 0x21112102,
|
||||
0x21112202, 0x21122100, 0x21122101, 0x22102101, 0x22112001, 0x22112102, 0x22112201, 0x22122101,
|
||||
0x20102110, 0x20102112, 0x20102211, 0x20112010, 0x20112012, 0x20112111, 0x20112210, 0x20112212,
|
||||
0x20122010, 0x20122011, 0x20122110, 0x20122112, 0x21102010, 0x21102012, 0x21102111, 0x21102210,
|
||||
0x21102212, 0x21112011, 0x21112110, 0x21112111, 0x21112112, 0x21112211, 0x21122012, 0x21122111,
|
||||
0x21122112, 0x21122212, 0x22102011, 0x22102110, 0x22112010, 0x22112012, 0x22112111, 0x22112212,
|
||||
0x22122011, 0x22122112, 0x20102121, 0x20112121, 0x20122121, 0x21102120, 0x21102122, 0x21102221,
|
||||
0x21112020, 0x21112121, 0x21112220, 0x21122021, 0x22102121, 0x22112021, 0x22112120, 0x22112121,
|
||||
0x22112122, 0x20200000, 0x20200002, 0x20200200, 0x20200202, 0x20210101, 0x20220000, 0x20220002,
|
||||
0x20220200, 0x20220202, 0x21200101, 0x21210001, 0x21210100, 0x21210102, 0x21210201, 0x22200000,
|
||||
0x22200002, 0x22200200, 0x22200202, 0x22210101, 0x22220000, 0x22220002, 0x22220200, 0x22220202,
|
||||
0x20200111, 0x20200211, 0x20210011, 0x20210110, 0x20210112, 0x20210211, 0x20210212, 0x21200112,
|
||||
0x21200211, 0x21210011, 0x21210111, 0x21210210, 0x21210212, 0x21220011, 0x21220110, 0x22200111,
|
||||
0x22210010, 0x22210012, 0x22210112, 0x22210211, 0x20200022, 0x20200220, 0x20200222, 0x20210020,
|
||||
0x20210221, 0x20220022, 0x20220220, 0x20220222, 0x21200121, 0x21210021, 0x21210122, 0x21210221,
|
||||
0x21220121, 0x22200020, 0x22200022, 0x22200220, 0x22200222, 0x22210121, 0x22220020, 0x22220022,
|
||||
0x22220220, 0x22220222, 0x20211201, 0x20221101, 0x21201001, 0x21201100, 0x21211000, 0x21211100,
|
||||
0x21211101, 0x21211200, 0x21211202, 0x21221001, 0x21221101, 0x21221102, 0x21221200, 0x21221201,
|
||||
0x22201101, 0x20201112, 0x20201211, 0x20211010, 0x20211012, 0x20211111, 0x20211210, 0x20221112,
|
||||
0x20221211, 0x21201012, 0x21201111, 0x21211011, 0x21211110, 0x21211111, 0x21211112, 0x21211211,
|
||||
0x21221111, 0x21221212, 0x22201011, 0x22201110, 0x22201111, 0x22201112, 0x22201211, 0x22211012,
|
||||
0x22211111, 0x22211210, 0x20201121, 0x20211021, 0x20211122, 0x20211222, 0x20221021, 0x20221121,
|
||||
0x21201120, 0x21201122, 0x21201222, 0x21211022, 0x21211121, 0x21211122, 0x21211220, 0x21221020,
|
||||
0x21221022, 0x22201122, 0x22211020, 0x22211121, 0x22211122, 0x22211221, 0x22221021, 0x22221120,
|
||||
0x22221122, 0x20202000, 0x20202002, 0x20202200, 0x20202202, 0x20222000, 0x20222002, 0x20222200,
|
||||
0x20222202, 0x21212001, 0x21212100, 0x21212102, 0x21212201, 0x22202000, 0x22202002, 0x22202200,
|
||||
0x22202202, 0x22212101, 0x22222000, 0x22222002, 0x22222200, 0x22222202, 0x20202111, 0x20212110,
|
||||
0x20212211, 0x20222011, 0x20222111, 0x21202011, 0x21212010, 0x21212111, 0x21212212, 0x21222011,
|
||||
0x21222112, 0x21222211, 0x22212010, 0x22212112, 0x20202020, 0x20202022, 0x20202220, 0x20202222,
|
||||
0x20222020, 0x20222022, 0x20222220, 0x20222222, 0x21212021, 0x21212120, 0x21212122, 0x22202020,
|
||||
0x22202022, 0x22202220, 0x22202222, 0x22212121, 0x22222020, 0x22222022, 0x22222220, 0x22222222
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
constant uint iq3_s_grid[512] = {
|
||||
0x01010101, 0x01010103, 0x01010105, 0x0101010b, 0x0101010f, 0x01010301, 0x01010303, 0x01010305,
|
||||
0x01010309, 0x0101030d, 0x01010501, 0x01010503, 0x0101050b, 0x01010707, 0x01010901, 0x01010905,
|
||||
0x0101090b, 0x0101090f, 0x01010b03, 0x01010b07, 0x01010d01, 0x01010d05, 0x01010f03, 0x01010f09,
|
||||
0x01010f0f, 0x01030101, 0x01030103, 0x01030105, 0x01030109, 0x01030301, 0x01030303, 0x0103030b,
|
||||
0x01030501, 0x01030507, 0x0103050f, 0x01030703, 0x0103070b, 0x01030909, 0x01030d03, 0x01030d0b,
|
||||
0x01030f05, 0x01050101, 0x01050103, 0x0105010b, 0x0105010f, 0x01050301, 0x01050307, 0x0105030d,
|
||||
0x01050503, 0x0105050b, 0x01050701, 0x01050709, 0x01050905, 0x0105090b, 0x0105090f, 0x01050b03,
|
||||
0x01050b07, 0x01050f01, 0x01050f07, 0x01070107, 0x01070303, 0x0107030b, 0x01070501, 0x01070505,
|
||||
0x01070703, 0x01070707, 0x0107070d, 0x01070909, 0x01070b01, 0x01070b05, 0x01070d0f, 0x01070f03,
|
||||
0x01070f0b, 0x01090101, 0x01090307, 0x0109030f, 0x01090503, 0x01090509, 0x01090705, 0x01090901,
|
||||
0x01090907, 0x01090b03, 0x01090f01, 0x010b0105, 0x010b0109, 0x010b0501, 0x010b0505, 0x010b050d,
|
||||
0x010b0707, 0x010b0903, 0x010b090b, 0x010b090f, 0x010b0d0d, 0x010b0f07, 0x010d010d, 0x010d0303,
|
||||
0x010d0307, 0x010d0703, 0x010d0b05, 0x010d0f03, 0x010f0101, 0x010f0105, 0x010f0109, 0x010f0501,
|
||||
0x010f0505, 0x010f050d, 0x010f0707, 0x010f0b01, 0x010f0b09, 0x03010101, 0x03010103, 0x03010105,
|
||||
0x03010109, 0x03010301, 0x03010303, 0x03010307, 0x0301030b, 0x0301030f, 0x03010501, 0x03010505,
|
||||
0x03010703, 0x03010709, 0x0301070d, 0x03010b09, 0x03010b0d, 0x03010d03, 0x03010f05, 0x03030101,
|
||||
0x03030103, 0x03030107, 0x0303010d, 0x03030301, 0x03030309, 0x03030503, 0x03030701, 0x03030707,
|
||||
0x03030903, 0x03030b01, 0x03030b05, 0x03030f01, 0x03030f0d, 0x03050101, 0x03050305, 0x0305030b,
|
||||
0x0305030f, 0x03050501, 0x03050509, 0x03050705, 0x03050901, 0x03050907, 0x03050b0b, 0x03050d01,
|
||||
0x03050f05, 0x03070103, 0x03070109, 0x0307010f, 0x03070301, 0x03070307, 0x03070503, 0x0307050f,
|
||||
0x03070701, 0x03070709, 0x03070903, 0x03070d05, 0x03070f01, 0x03090107, 0x0309010b, 0x03090305,
|
||||
0x03090309, 0x03090703, 0x03090707, 0x03090905, 0x0309090d, 0x03090b01, 0x03090b09, 0x030b0103,
|
||||
0x030b0301, 0x030b0307, 0x030b0503, 0x030b0701, 0x030b0705, 0x030b0b03, 0x030d0501, 0x030d0509,
|
||||
0x030d050f, 0x030d0909, 0x030d090d, 0x030f0103, 0x030f0107, 0x030f0301, 0x030f0305, 0x030f0503,
|
||||
0x030f070b, 0x030f0903, 0x030f0d05, 0x030f0f01, 0x05010101, 0x05010103, 0x05010107, 0x0501010b,
|
||||
0x0501010f, 0x05010301, 0x05010305, 0x05010309, 0x0501030d, 0x05010503, 0x05010507, 0x0501050f,
|
||||
0x05010701, 0x05010705, 0x05010903, 0x05010907, 0x0501090b, 0x05010b01, 0x05010b05, 0x05010d0f,
|
||||
0x05010f01, 0x05010f07, 0x05010f0b, 0x05030101, 0x05030105, 0x05030301, 0x05030307, 0x0503030f,
|
||||
0x05030505, 0x0503050b, 0x05030703, 0x05030709, 0x05030905, 0x05030b03, 0x05050103, 0x05050109,
|
||||
0x0505010f, 0x05050503, 0x05050507, 0x05050701, 0x0505070f, 0x05050903, 0x05050b07, 0x05050b0f,
|
||||
0x05050f03, 0x05050f09, 0x05070101, 0x05070105, 0x0507010b, 0x05070303, 0x05070505, 0x05070509,
|
||||
0x05070703, 0x05070707, 0x05070905, 0x05070b01, 0x05070d0d, 0x05090103, 0x0509010f, 0x05090501,
|
||||
0x05090507, 0x05090705, 0x0509070b, 0x05090903, 0x05090f05, 0x05090f0b, 0x050b0109, 0x050b0303,
|
||||
0x050b0505, 0x050b070f, 0x050b0901, 0x050b0b07, 0x050b0f01, 0x050d0101, 0x050d0105, 0x050d010f,
|
||||
0x050d0503, 0x050d0b0b, 0x050d0d03, 0x050f010b, 0x050f0303, 0x050f050d, 0x050f0701, 0x050f0907,
|
||||
0x050f0b01, 0x07010105, 0x07010303, 0x07010307, 0x0701030b, 0x0701030f, 0x07010505, 0x07010703,
|
||||
0x07010707, 0x0701070b, 0x07010905, 0x07010909, 0x0701090f, 0x07010b03, 0x07010d07, 0x07010f03,
|
||||
0x07030103, 0x07030107, 0x0703010b, 0x07030309, 0x07030503, 0x07030507, 0x07030901, 0x07030d01,
|
||||
0x07030f05, 0x07030f0d, 0x07050101, 0x07050305, 0x07050501, 0x07050705, 0x07050709, 0x07050b01,
|
||||
0x07070103, 0x07070301, 0x07070309, 0x07070503, 0x07070507, 0x0707050f, 0x07070701, 0x07070903,
|
||||
0x07070907, 0x0707090f, 0x07070b0b, 0x07070f07, 0x07090107, 0x07090303, 0x0709030d, 0x07090505,
|
||||
0x07090703, 0x07090b05, 0x07090d01, 0x07090d09, 0x070b0103, 0x070b0301, 0x070b0305, 0x070b050b,
|
||||
0x070b0705, 0x070b0909, 0x070b0b0d, 0x070b0f07, 0x070d030d, 0x070d0903, 0x070f0103, 0x070f0107,
|
||||
0x070f0501, 0x070f0505, 0x070f070b, 0x09010101, 0x09010109, 0x09010305, 0x09010501, 0x09010509,
|
||||
0x0901050f, 0x09010705, 0x09010903, 0x09010b01, 0x09010f01, 0x09030105, 0x0903010f, 0x09030303,
|
||||
0x09030307, 0x09030505, 0x09030701, 0x0903070b, 0x09030907, 0x09030b03, 0x09030b0b, 0x09050103,
|
||||
0x09050107, 0x09050301, 0x0905030b, 0x09050503, 0x09050707, 0x09050901, 0x09050b0f, 0x09050d05,
|
||||
0x09050f01, 0x09070109, 0x09070303, 0x09070307, 0x09070501, 0x09070505, 0x09070703, 0x0907070b,
|
||||
0x09090101, 0x09090105, 0x09090509, 0x0909070f, 0x09090901, 0x09090f03, 0x090b010b, 0x090b010f,
|
||||
0x090b0503, 0x090b0d05, 0x090d0307, 0x090d0709, 0x090d0d01, 0x090f0301, 0x090f030b, 0x090f0701,
|
||||
0x090f0907, 0x090f0b03, 0x0b010105, 0x0b010301, 0x0b010309, 0x0b010505, 0x0b010901, 0x0b010909,
|
||||
0x0b01090f, 0x0b010b05, 0x0b010d0d, 0x0b010f09, 0x0b030103, 0x0b030107, 0x0b03010b, 0x0b030305,
|
||||
0x0b030503, 0x0b030705, 0x0b030f05, 0x0b050101, 0x0b050303, 0x0b050507, 0x0b050701, 0x0b05070d,
|
||||
0x0b050b07, 0x0b070105, 0x0b07010f, 0x0b070301, 0x0b07050f, 0x0b070909, 0x0b070b03, 0x0b070d0b,
|
||||
0x0b070f07, 0x0b090103, 0x0b090109, 0x0b090501, 0x0b090705, 0x0b09090d, 0x0b0b0305, 0x0b0b050d,
|
||||
0x0b0b0b03, 0x0b0b0b07, 0x0b0d0905, 0x0b0f0105, 0x0b0f0109, 0x0b0f0505, 0x0d010303, 0x0d010307,
|
||||
0x0d01030b, 0x0d010703, 0x0d010707, 0x0d010d01, 0x0d030101, 0x0d030501, 0x0d03050f, 0x0d030d09,
|
||||
0x0d050305, 0x0d050709, 0x0d050905, 0x0d050b0b, 0x0d050d05, 0x0d050f01, 0x0d070101, 0x0d070309,
|
||||
0x0d070503, 0x0d070901, 0x0d09050b, 0x0d090907, 0x0d090d05, 0x0d0b0101, 0x0d0b0107, 0x0d0b0709,
|
||||
0x0d0b0d01, 0x0d0d010b, 0x0d0d0901, 0x0d0f0303, 0x0d0f0307, 0x0f010101, 0x0f010109, 0x0f01010f,
|
||||
0x0f010501, 0x0f010505, 0x0f01070d, 0x0f010901, 0x0f010b09, 0x0f010d05, 0x0f030105, 0x0f030303,
|
||||
0x0f030509, 0x0f030907, 0x0f03090b, 0x0f050103, 0x0f050109, 0x0f050301, 0x0f05030d, 0x0f050503,
|
||||
0x0f050701, 0x0f050b03, 0x0f070105, 0x0f070705, 0x0f07070b, 0x0f070b07, 0x0f090103, 0x0f09010b,
|
||||
0x0f090307, 0x0f090501, 0x0f090b01, 0x0f0b0505, 0x0f0b0905, 0x0f0d0105, 0x0f0d0703, 0x0f0f0101
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
constant uint iq3_xxs_grid[256] = {
|
||||
0x04040404, 0x04040414, 0x04040424, 0x04040c0c, 0x04040c1c, 0x04040c3e, 0x04041404, 0x04041414,
|
||||
0x04041c0c, 0x04042414, 0x04043e1c, 0x04043e2c, 0x040c040c, 0x040c041c, 0x040c0c04, 0x040c0c14,
|
||||
0x040c140c, 0x040c142c, 0x040c1c04, 0x040c1c14, 0x040c240c, 0x040c2c24, 0x040c3e04, 0x04140404,
|
||||
0x04140414, 0x04140424, 0x04140c0c, 0x04141404, 0x04141414, 0x04141c0c, 0x04141c1c, 0x04141c3e,
|
||||
0x04142c0c, 0x04142c3e, 0x04143e2c, 0x041c040c, 0x041c043e, 0x041c0c04, 0x041c0c14, 0x041c142c,
|
||||
0x041c3e04, 0x04240c1c, 0x04241c3e, 0x04242424, 0x04242c3e, 0x04243e1c, 0x04243e2c, 0x042c040c,
|
||||
0x042c043e, 0x042c1c14, 0x042c2c14, 0x04341c2c, 0x04343424, 0x043e0c04, 0x043e0c24, 0x043e0c34,
|
||||
0x043e241c, 0x043e340c, 0x0c04040c, 0x0c04041c, 0x0c040c04, 0x0c040c14, 0x0c04140c, 0x0c04141c,
|
||||
0x0c041c04, 0x0c041c14, 0x0c041c24, 0x0c04243e, 0x0c042c04, 0x0c0c0404, 0x0c0c0414, 0x0c0c0c0c,
|
||||
0x0c0c1404, 0x0c0c1414, 0x0c14040c, 0x0c14041c, 0x0c140c04, 0x0c140c14, 0x0c14140c, 0x0c141c04,
|
||||
0x0c143e14, 0x0c1c0404, 0x0c1c0414, 0x0c1c1404, 0x0c1c1c0c, 0x0c1c2434, 0x0c1c3434, 0x0c24040c,
|
||||
0x0c24042c, 0x0c242c04, 0x0c2c1404, 0x0c2c1424, 0x0c2c2434, 0x0c2c3e0c, 0x0c34042c, 0x0c3e1414,
|
||||
0x0c3e2404, 0x14040404, 0x14040414, 0x14040c0c, 0x14040c1c, 0x14041404, 0x14041414, 0x14041434,
|
||||
0x14041c0c, 0x14042414, 0x140c040c, 0x140c041c, 0x140c042c, 0x140c0c04, 0x140c0c14, 0x140c140c,
|
||||
0x140c1c04, 0x140c341c, 0x140c343e, 0x140c3e04, 0x14140404, 0x14140414, 0x14140c0c, 0x14140c3e,
|
||||
0x14141404, 0x14141414, 0x14141c3e, 0x14142404, 0x14142c2c, 0x141c040c, 0x141c0c04, 0x141c0c24,
|
||||
0x141c3e04, 0x141c3e24, 0x14241c2c, 0x14242c1c, 0x142c041c, 0x142c143e, 0x142c240c, 0x142c3e24,
|
||||
0x143e040c, 0x143e041c, 0x143e0c34, 0x143e242c, 0x1c04040c, 0x1c040c04, 0x1c040c14, 0x1c04140c,
|
||||
0x1c04141c, 0x1c042c04, 0x1c04342c, 0x1c043e14, 0x1c0c0404, 0x1c0c0414, 0x1c0c1404, 0x1c0c1c0c,
|
||||
0x1c0c2424, 0x1c0c2434, 0x1c14040c, 0x1c14041c, 0x1c140c04, 0x1c14142c, 0x1c142c14, 0x1c143e14,
|
||||
0x1c1c0c0c, 0x1c1c1c1c, 0x1c241c04, 0x1c24243e, 0x1c243e14, 0x1c2c0404, 0x1c2c0434, 0x1c2c1414,
|
||||
0x1c2c2c2c, 0x1c340c24, 0x1c341c34, 0x1c34341c, 0x1c3e1c1c, 0x1c3e3404, 0x24040424, 0x24040c3e,
|
||||
0x24041c2c, 0x24041c3e, 0x24042c1c, 0x24042c3e, 0x240c3e24, 0x24141404, 0x24141c3e, 0x24142404,
|
||||
0x24143404, 0x24143434, 0x241c043e, 0x241c242c, 0x24240424, 0x24242c0c, 0x24243424, 0x242c142c,
|
||||
0x242c241c, 0x242c3e04, 0x243e042c, 0x243e0c04, 0x243e0c14, 0x243e1c04, 0x2c040c14, 0x2c04240c,
|
||||
0x2c043e04, 0x2c0c0404, 0x2c0c0434, 0x2c0c1434, 0x2c0c2c2c, 0x2c140c24, 0x2c141c14, 0x2c143e14,
|
||||
0x2c1c0414, 0x2c1c2c1c, 0x2c240c04, 0x2c24141c, 0x2c24143e, 0x2c243e14, 0x2c2c0414, 0x2c2c1c0c,
|
||||
0x2c342c04, 0x2c3e1424, 0x2c3e2414, 0x34041424, 0x34042424, 0x34042434, 0x34043424, 0x340c140c,
|
||||
0x340c340c, 0x34140c3e, 0x34143424, 0x341c1c04, 0x341c1c34, 0x34242424, 0x342c042c, 0x342c2c14,
|
||||
0x34341c1c, 0x343e041c, 0x343e140c, 0x3e04041c, 0x3e04042c, 0x3e04043e, 0x3e040c04, 0x3e041c14,
|
||||
0x3e042c14, 0x3e0c1434, 0x3e0c2404, 0x3e140c14, 0x3e14242c, 0x3e142c14, 0x3e1c0404, 0x3e1c0c2c,
|
||||
0x3e1c1c1c, 0x3e1c3404, 0x3e24140c, 0x3e24240c, 0x3e2c0404, 0x3e2c0414, 0x3e2c1424, 0x3e341c04
|
||||
};
|
||||
|
||||
inline uint iq3_xxs_signs(uint code) {
|
||||
return code | ((popcount(code) & 1u) << 7);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
constant char iq4_xs_values[16] = {
|
||||
-127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113,
|
||||
};
|
||||
|
||||
inline uint iq4_xs_code(uint n) {
|
||||
const uint lo = (n & 4u) ? 0xF6EADDCFu : 0xBFAD9881u;
|
||||
const uint hi = (n & 4u) ? 0x71594535u : 0x26190D01u;
|
||||
const uint t = (n & 8u) ? hi : lo;
|
||||
return (t >> (8*(n & 3u))) & 255u;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_iq1_m {
|
||||
uchar qs[QK_K/8];
|
||||
uchar qh[QK_K/16];
|
||||
uchar scales[QK_K/32];
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#define BK 32
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#define TN 8
|
||||
#else
|
||||
#define TM 4
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_iq1_m_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_qh,
|
||||
global ushort * src0_sc,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local float buf_a[BM * BK];
|
||||
local float buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one iq1_m super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float sums[TM * TN];
|
||||
float cache_a[TM];
|
||||
float cache_b[TN];
|
||||
|
||||
for (int i = 0; i < TM * TN; i++) {
|
||||
sums[i] = 0.0f;
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_iq1_m);
|
||||
int e = idx % QK_K;
|
||||
|
||||
int ib32 = e >> 5;
|
||||
int rem = e & 31;
|
||||
int lg = rem >> 3;
|
||||
int j0 = rem & 7;
|
||||
|
||||
global ushort * sc = src0_sc + 4*ib;
|
||||
ushort u16 = (ushort)((sc[0] >> 12) | ((sc[1] >> 8) & 0x00f0)
|
||||
| ((sc[2] >> 4) & 0x0f00) | (sc[3] & 0xf000));
|
||||
float dl = (float)as_half(u16) * (float)(2*((sc[ib32/2] >> (6*(ib32%2) + 3*(lg/2))) & 7) + 1);
|
||||
uchar qhb = src0_qh[16*ib + 2*ib32 + lg/2];
|
||||
float dlt = (qhb & (0x08 << (4*(lg%2)))) ? -1.125f : -0.875f;
|
||||
uint gi = (uint)src0_qs[32*ib + 4*ib32 + lg] | ((((uint)qhb >> (4*(lg%2))) & 7) << 8);
|
||||
uint g = iq1_s_grid[gi];
|
||||
int sh = (j0 >> 2) * 4;
|
||||
|
||||
float4 v1;
|
||||
v1.s0 = dl * ((float)((g >> ( 0 + sh)) & 0xF) + dlt);
|
||||
v1.s1 = dl * ((float)((g >> ( 8 + sh)) & 0xF) + dlt);
|
||||
v1.s2 = dl * ((float)((g >> (16 + sh)) & 0xF) + dlt);
|
||||
v1.s3 = dl * ((float)((g >> (24 + sh)) & 0xF) + dlt);
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int j = 0; j < TM; j++) {
|
||||
cache_a[j] = buf_a[(i) * BM + th_r * TM + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < TN; j++) {
|
||||
cache_b[j] = buf_b[(i) * BN + th_c * TN + j];
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
const int sums_idx = cc*TM + cr;
|
||||
sums[sums_idx] = mad(cache_a[cr], cache_b[cc], sums[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
if (dr + cr < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + cr] = sums[cc * TM + cr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_iq1_s {
|
||||
half d;
|
||||
uchar qs[QK_K/8];
|
||||
ushort qh[QK_K/32];
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#define BK 32
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#define TN 8
|
||||
#else
|
||||
#define TM 4
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_iq1_s_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global ushort * src0_qh,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local float buf_a[BM * BK];
|
||||
local float buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one iq1_s super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float sums[TM * TN];
|
||||
float cache_a[TM];
|
||||
float cache_b[TN];
|
||||
|
||||
for (int i = 0; i < TM * TN; i++) {
|
||||
sums[i] = 0.0f;
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_iq1_s);
|
||||
int e = idx % QK_K;
|
||||
|
||||
int ib32 = e >> 5;
|
||||
int rem = e & 31;
|
||||
int lg = rem >> 3;
|
||||
int j0 = rem & 7;
|
||||
|
||||
ushort qhb = src0_qh[8*ib + ib32];
|
||||
float dl = (float)src0_d[ib] * (float)(2*((qhb >> 12) & 7) + 1);
|
||||
float dlt = (qhb & 0x8000) ? -1.125f : -0.875f;
|
||||
uint gi = (uint)src0_qs[32*ib + 4*ib32 + lg] | ((((uint)qhb >> (3*lg)) & 7) << 8);
|
||||
uint g = iq1_s_grid[gi];
|
||||
int sh = (j0 >> 2) * 4;
|
||||
|
||||
float4 v1;
|
||||
v1.s0 = dl * ((float)((g >> ( 0 + sh)) & 0xF) + dlt);
|
||||
v1.s1 = dl * ((float)((g >> ( 8 + sh)) & 0xF) + dlt);
|
||||
v1.s2 = dl * ((float)((g >> (16 + sh)) & 0xF) + dlt);
|
||||
v1.s3 = dl * ((float)((g >> (24 + sh)) & 0xF) + dlt);
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int j = 0; j < TM; j++) {
|
||||
cache_a[j] = buf_a[(i) * BM + th_r * TM + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < TN; j++) {
|
||||
cache_b[j] = buf_b[(i) * BN + th_c * TN + j];
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
const int sums_idx = cc*TM + cr;
|
||||
sums[sums_idx] = mad(cache_a[cr], cache_b[cc], sums[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
if (dr + cr < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + cr] = sums[cc * TM + cr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_iq3_s {
|
||||
half d;
|
||||
uchar qs[QK_K/4];
|
||||
uchar qh[QK_K/32];
|
||||
uchar signs[QK_K/8];
|
||||
uchar scales[QK_K/64];
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#define BK 32
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#define TN 8
|
||||
#else
|
||||
#define TM 4
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_iq3_s_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_sg,
|
||||
global uchar * src0_qh,
|
||||
global uchar * src0_sc,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local float buf_a[BM * BK];
|
||||
local float buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one iq3_s super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float sums[TM * TN];
|
||||
float cache_a[TM];
|
||||
float cache_b[TN];
|
||||
|
||||
for (int i = 0; i < TM * TN; i++) {
|
||||
sums[i] = 0.0f;
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_iq3_s);
|
||||
int e = idx % QK_K;
|
||||
|
||||
int ib32 = e >> 5;
|
||||
int rem = e & 31;
|
||||
int lg = rem >> 3;
|
||||
int j0 = rem & 7;
|
||||
|
||||
uchar scb = src0_sc[4*ib + ib32/2];
|
||||
float db = (float)src0_d[ib] * (float)(1 + 2*((scb >> (4*(ib32 & 1))) & 15));
|
||||
int which = j0 >> 2;
|
||||
uint gi = (uint)src0_qs[64*ib + 8*ib32 + 2*lg + which]
|
||||
| (((uint)src0_qh[8*ib + ib32] << (8 - 2*lg - which)) & 256);
|
||||
uchar sg = src0_sg[32*ib + 4*ib32 + lg];
|
||||
uint g = iq3_s_grid[gi];
|
||||
|
||||
float4 v1;
|
||||
v1.s0 = db * (float)((g >> 0) & 0xFF) * ((sg & (1 << (j0+0))) ? -1.f : 1.f);
|
||||
v1.s1 = db * (float)((g >> 8) & 0xFF) * ((sg & (1 << (j0+1))) ? -1.f : 1.f);
|
||||
v1.s2 = db * (float)((g >> 16) & 0xFF) * ((sg & (1 << (j0+2))) ? -1.f : 1.f);
|
||||
v1.s3 = db * (float)((g >> 24) & 0xFF) * ((sg & (1 << (j0+3))) ? -1.f : 1.f);
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int j = 0; j < TM; j++) {
|
||||
cache_a[j] = buf_a[(i) * BM + th_r * TM + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < TN; j++) {
|
||||
cache_b[j] = buf_b[(i) * BN + th_c * TN + j];
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
const int sums_idx = cc*TM + cr;
|
||||
sums[sums_idx] = mad(cache_a[cr], cache_b[cc], sums[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
if (dr + cr < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + cr] = sums[cc * TM + cr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_iq3_xxs {
|
||||
half d;
|
||||
uchar qs[3*QK_K/8];
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#define BK 32
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#define TN 8
|
||||
#else
|
||||
#define TM 4
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_iq3_xxs_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uint * src0_sas,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local float buf_a[BM * BK];
|
||||
local float buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one iq3_xxs super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float sums[TM * TN];
|
||||
float cache_a[TM];
|
||||
float cache_b[TN];
|
||||
|
||||
for (int i = 0; i < TM * TN; i++) {
|
||||
sums[i] = 0.0f;
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_iq3_xxs);
|
||||
int e = idx % QK_K;
|
||||
|
||||
int ib32 = e >> 5;
|
||||
int rem = e & 31;
|
||||
int lg = rem >> 3;
|
||||
int j0 = rem & 7;
|
||||
|
||||
uint aux = src0_sas[8*ib + ib32];
|
||||
float db = (float)src0_d[ib] * (0.5f + (float)(aux >> 28)) * 0.5f;
|
||||
|
||||
uint gi = src0_qs[64*ib + 8*ib32 + 2*lg + (j0 >> 2)];
|
||||
uint sg = iq3_xxs_signs((aux >> (7*lg)) & 127u);
|
||||
uint g = iq3_xxs_grid[gi];
|
||||
|
||||
float4 v1;
|
||||
v1.s0 = db * (float)((g >> 0) & 0xFF) * ((sg & (1 << (j0+0))) ? -1.f : 1.f);
|
||||
v1.s1 = db * (float)((g >> 8) & 0xFF) * ((sg & (1 << (j0+1))) ? -1.f : 1.f);
|
||||
v1.s2 = db * (float)((g >> 16) & 0xFF) * ((sg & (1 << (j0+2))) ? -1.f : 1.f);
|
||||
v1.s3 = db * (float)((g >> 24) & 0xFF) * ((sg & (1 << (j0+3))) ? -1.f : 1.f);
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int j = 0; j < TM; j++) {
|
||||
cache_a[j] = buf_a[(i) * BM + th_r * TM + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < TN; j++) {
|
||||
cache_b[j] = buf_b[(i) * BN + th_c * TN + j];
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
const int sums_idx = cc*TM + cr;
|
||||
sums[sums_idx] = mad(cache_a[cr], cache_b[cc], sums[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
if (dr + cr < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + cr] = sums[cc * TM + cr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#define INTEL_GPU 1
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
|
||||
struct block_iq4_xs {
|
||||
half d;
|
||||
ushort scales_h;
|
||||
uchar scales_l[QK_K/64];
|
||||
uchar qs[QK_K/2];
|
||||
};
|
||||
|
||||
#define LOAD_VEC_A 4
|
||||
#define LOAD_VEC_B 4
|
||||
|
||||
#define BM 64
|
||||
#define BN 64
|
||||
#define BK 32
|
||||
#ifdef INTEL_GPU
|
||||
#define TM 8
|
||||
#define TN 8
|
||||
#else
|
||||
#define TM 4
|
||||
#define TN 8
|
||||
#endif
|
||||
|
||||
kernel void kernel_mul_mm_iq4_xs_f32_l4_lm(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_sl,
|
||||
global ushort * src0_sh,
|
||||
global half * src0_d,
|
||||
ulong offset0,
|
||||
global float4 * src1,
|
||||
ulong offset1,
|
||||
global float * dst,
|
||||
ulong offsetd,
|
||||
|
||||
int ne00,
|
||||
int ne01,
|
||||
int ne02,
|
||||
int ne11,
|
||||
int ne12,
|
||||
|
||||
int stride_a,
|
||||
int stride_b,
|
||||
int stride_d,
|
||||
|
||||
int batch_stride_a,
|
||||
int batch_stride_b,
|
||||
int batch_stride_d,
|
||||
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = (global float4*)((global char*)src1 + offset1);
|
||||
dst = (global float *)((global char*)dst + offsetd);
|
||||
|
||||
local float buf_a[BM * BK];
|
||||
local float buf_b[BN * BK];
|
||||
|
||||
const int batch_idx = get_global_id(2);
|
||||
|
||||
const int i13 = batch_idx / ne12;
|
||||
const int i12 = batch_idx % ne12;
|
||||
|
||||
const int i03 = i13 / r3;
|
||||
const int i02 = i12 / r2;
|
||||
|
||||
const int batch_idx_a = i03 * ne02 + i02;
|
||||
|
||||
const int ir = get_group_id(0);
|
||||
const int ic = get_group_id(1);
|
||||
|
||||
const int tid = get_local_id(0);
|
||||
const int th_r = tid % (BM / TM);
|
||||
const int th_c = tid / (BM / TM);
|
||||
|
||||
const int loadr_a = get_local_id(0) % (BK / LOAD_VEC_A);
|
||||
const int loadc_a = get_local_id(0) / (BK / LOAD_VEC_A);
|
||||
const int loadr_b = get_local_id(0) % (BK / LOAD_VEC_B);
|
||||
const int loadc_b = get_local_id(0) / (BK / LOAD_VEC_B);
|
||||
|
||||
const int loadstride_a = get_local_size(0) * LOAD_VEC_A / BK;
|
||||
const int loadstride_b = get_local_size(0) * LOAD_VEC_B / BK;
|
||||
|
||||
// pos_a counts elements, not blocks - one iq4_xs super block holds QK_K of them
|
||||
int pos_a = batch_idx_a * batch_stride_a + ir * BM * stride_a;
|
||||
int pos_b = (batch_idx * batch_stride_b + ic * BN * stride_b) / LOAD_VEC_B;
|
||||
|
||||
float sums[TM * TN];
|
||||
float cache_a[TM];
|
||||
float cache_b[TN];
|
||||
|
||||
for (int i = 0; i < TM * TN; i++) {
|
||||
sums[i] = 0.0f;
|
||||
}
|
||||
|
||||
for (int block = 0; block < ne00; block += BK) {
|
||||
for (int l = 0; l < BM; l += loadstride_a) {
|
||||
if (ir*BM + loadc_a + l < ne01) {
|
||||
int idx = pos_a + (loadc_a + l) * stride_a + loadr_a * LOAD_VEC_A;
|
||||
int ib = idx / QK_K;
|
||||
ib += offset0/sizeof(struct block_iq4_xs);
|
||||
int e = idx % QK_K;
|
||||
|
||||
int ib32 = e >> 5;
|
||||
int j = e & 15;
|
||||
int shift = (e & 16) >> 2;
|
||||
int ls = ((src0_sl[4*ib + ib32/2] >> (4*(ib32 & 1))) & 15) | (((src0_sh[ib] >> (2*ib32)) & 3) << 4);
|
||||
float db = (float)src0_d[ib] * (float)(ls - 32);
|
||||
uchar4 q = vload4(0, src0_qs + 128*ib + 16*ib32 + j);
|
||||
q = (q >> (uchar4)(shift)) & (uchar4)(15);
|
||||
|
||||
float4 v1;
|
||||
v1.s0 = db * (float)iq4_xs_values[q.s0];
|
||||
v1.s1 = db * (float)iq4_xs_values[q.s1];
|
||||
v1.s2 = db * (float)iq4_xs_values[q.s2];
|
||||
v1.s3 = db * (float)iq4_xs_values[q.s3];
|
||||
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = v1.s0;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = v1.s1;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = v1.s2;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = v1.s3;
|
||||
} else {
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 0) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 1) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 2) * BM + loadc_a + l] = 0.0f;
|
||||
buf_a[(loadr_a * LOAD_VEC_A + 3) * BM + loadc_a + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < BN; l += loadstride_b) {
|
||||
if (ic*BN + loadc_b + l < ne11) {
|
||||
int idx = pos_b + (loadc_b + l) * stride_b / LOAD_VEC_B + loadr_b;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = src1[idx].s0;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = src1[idx].s1;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = src1[idx].s2;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = src1[idx].s3;
|
||||
} else {
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 0) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 1) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 2) * BN + loadc_b + l] = 0.0f;
|
||||
buf_b[(loadr_b * LOAD_VEC_B + 3) * BN + loadc_b + l] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
pos_a += BK;
|
||||
pos_b += BK / LOAD_VEC_B;
|
||||
|
||||
for (int i = 0; i < BK; i++) {
|
||||
for (int j = 0; j < TM; j++) {
|
||||
cache_a[j] = buf_a[(i) * BM + th_r * TM + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < TN; j++) {
|
||||
cache_b[j] = buf_b[(i) * BN + th_c * TN + j];
|
||||
}
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
const int sums_idx = cc*TM + cr;
|
||||
sums[sums_idx] = mad(cache_a[cr], cache_b[cc], sums[sums_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
const int dr = ir * BM + th_r * TM;
|
||||
const int dc = ic * BN + th_c * TN;
|
||||
|
||||
const ulong offsets = batch_idx * batch_stride_d;
|
||||
|
||||
for (int cc = 0; cc < TN; cc++) {
|
||||
for (int cr = 0; cr < TM; cr++) {
|
||||
if (dr + cr < ne01 && dc + cc < ne11) {
|
||||
dst[offsets + (dc + cc) * stride_d + dr + cr] = sums[cc * TM + cr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
#define IQ1_M_DELTA 0.125f
|
||||
|
||||
// no d field: the super block scale is 4 nibbles spread over scales[]
|
||||
typedef struct {
|
||||
uchar qs[QK_K/8];
|
||||
uchar qh[QK_K/16];
|
||||
uchar scales[QK_K/32];
|
||||
} block_iq1_m;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 16
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 8 threads cover one super block, one 32 element sub block each
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_iq1_m_f32(
|
||||
global char * src0,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src0 = src0 + offset0;
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int ix = get_sub_group_local_id()/8; // super block index
|
||||
int it = get_sub_group_local_id()%8; // sub block inside the super block
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03;
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global block_iq1_m * x = (global block_iq1_m *) (src0 + offset_src0);
|
||||
global float * y = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
float sumf[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
global float * y4 = y + ix * QK_K + 32 * it;
|
||||
|
||||
for (int ib = ix; ib < nb; ib += BLOCK_STRIDE) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
yl[i] = y4[i];
|
||||
}
|
||||
|
||||
global char * xrow = (global char *)(x + ib);
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; row++) {
|
||||
global block_iq1_m * xb = (global block_iq1_m *)(xrow + row*nb01);
|
||||
|
||||
global ushort * sc = (global ushort *)xb->scales;
|
||||
ushort u16 = (ushort)(((uint)sc[0] >> 12) | (((uint)sc[1] >> 8) & 0x00f0)
|
||||
| (((uint)sc[2] >> 4) & 0x0f00) | ((uint)sc[3] & 0xf000));
|
||||
float dsuper = (float)as_half(u16);
|
||||
|
||||
float acc = 0.f;
|
||||
for (int il = 0; il < 4; ++il) {
|
||||
int ib16 = 2*it + il/2;
|
||||
float dl = dsuper * (float)(2*(((uint)sc[ib16/4] >> (3*(ib16%4))) & 7) + 1);
|
||||
|
||||
uchar qhb = xb->qh[2*it + il/2];
|
||||
float dlt = (qhb & (0x08 << (4*(il%2)))) ? (-1.f - IQ1_M_DELTA) : (-1.f + IQ1_M_DELTA);
|
||||
|
||||
uint gi = (uint)xb->qs[4*it+il] | ((((uint)qhb >> (4*(il%2))) & 7) << 8);
|
||||
uint g = iq1_s_grid[gi];
|
||||
|
||||
float a = 0.f;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
a += yl[8*il+j+0] * ((float)((g >> (8*j+0)) & 0xF) + dlt);
|
||||
a += yl[8*il+j+4] * ((float)((g >> (8*j+4)) & 0xF) + dlt);
|
||||
}
|
||||
acc += dl * a;
|
||||
}
|
||||
|
||||
sumf[row] += acc;
|
||||
}
|
||||
|
||||
y4 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add(sumf[row]);
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||||
|
||||
#ifdef cl_intel_required_subgroup_size
|
||||
#pragma OPENCL EXTENSION cl_intel_required_subgroup_size : enable
|
||||
#define INTEL_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_16 __attribute__((intel_reqd_sub_group_size(16)))
|
||||
#define REQD_SUBGROUP_SIZE_32 __attribute__((intel_reqd_sub_group_size(32)))
|
||||
#elif defined(cl_qcom_reqd_sub_group_size)
|
||||
#pragma OPENCL EXTENSION cl_qcom_reqd_sub_group_size : enable
|
||||
#define ADRENO_GPU 1
|
||||
#define REQD_SUBGROUP_SIZE_64 __attribute__((qcom_reqd_sub_group_size("half")))
|
||||
#define REQD_SUBGROUP_SIZE_128 __attribute__((qcom_reqd_sub_group_size("full")))
|
||||
#endif
|
||||
|
||||
#define QK_K 256
|
||||
#define IQ1_M_DELTA 0.125f
|
||||
|
||||
// no d field: the super block scale is 4 nibbles spread over scales[]
|
||||
typedef struct {
|
||||
uchar qs[QK_K/8];
|
||||
uchar qh[QK_K/16];
|
||||
uchar scales[QK_K/32];
|
||||
} block_iq1_m;
|
||||
|
||||
#undef N_DST
|
||||
#undef N_SIMDGROUP
|
||||
#undef N_SIMDWIDTH
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 16
|
||||
#elif defined (ADRENO_GPU)
|
||||
#define N_DST 4
|
||||
#define N_SIMDGROUP 1
|
||||
#define N_SIMDWIDTH 64
|
||||
#endif
|
||||
|
||||
#undef BLOCK_STRIDE
|
||||
// 8 threads cover one super block, one 32 element sub block each
|
||||
#define BLOCK_STRIDE (N_SIMDWIDTH/8)
|
||||
|
||||
#ifdef INTEL_GPU
|
||||
REQD_SUBGROUP_SIZE_16
|
||||
#elif defined (ADRENO_GPU)
|
||||
REQD_SUBGROUP_SIZE_64
|
||||
#endif
|
||||
kernel void kernel_mul_mv_iq1_m_f32_flat(
|
||||
global uchar * src0_qs,
|
||||
global uchar * src0_qh,
|
||||
global ushort * src0_sc,
|
||||
ulong offset0,
|
||||
global char * src1,
|
||||
ulong offset1,
|
||||
global char * dst,
|
||||
ulong offsetd,
|
||||
int ne00,
|
||||
int ne01,
|
||||
ulong nb01,
|
||||
ulong nb02,
|
||||
ulong nb03,
|
||||
int ne12,
|
||||
ulong nb11,
|
||||
ulong nb12,
|
||||
ulong nb13,
|
||||
int ne0,
|
||||
int ne1,
|
||||
int r2,
|
||||
int r3
|
||||
) {
|
||||
src1 = src1 + offset1;
|
||||
dst = dst + offsetd;
|
||||
|
||||
int ix = get_sub_group_local_id()/8; // super block index
|
||||
int it = get_sub_group_local_id()%8; // sub block inside the super block
|
||||
|
||||
int nb = ne00/QK_K;
|
||||
|
||||
int r0 = get_group_id(0);
|
||||
int r1 = get_group_id(1);
|
||||
int im = get_group_id(2);
|
||||
int first_row = (r0 * N_SIMDGROUP + get_sub_group_id()) * N_DST;
|
||||
|
||||
int i12 = im%ne12;
|
||||
int i13 = im/ne12;
|
||||
|
||||
ulong offset_src0 = (offset0 + first_row*nb01 + (i12/r2)*nb02 + (i13/r3)*nb03)/sizeof(block_iq1_m);
|
||||
ulong row_stride = nb01/sizeof(block_iq1_m);
|
||||
ulong offset_src1 = r1*nb11 + (i12 )*nb12 + (i13 )*nb13;
|
||||
|
||||
global float * y = (global float *) (src1 + offset_src1);
|
||||
|
||||
float yl[32];
|
||||
float sumf[N_DST] = {0.f};
|
||||
float all_sum;
|
||||
|
||||
global float * y4 = y + ix * QK_K + 32 * it;
|
||||
|
||||
for (int ib = ix; ib < nb; ib += BLOCK_STRIDE) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
yl[i] = y4[i];
|
||||
}
|
||||
|
||||
// only ne01 output rows exist; reading past them can pull in an inf scale
|
||||
for (int row = 0; row < N_DST && first_row + row < ne01; row++) {
|
||||
const ulong b = offset_src0 + ib + row*row_stride;
|
||||
|
||||
global ushort * sc = src0_sc + 4*b;
|
||||
ushort u16 = (ushort)(((uint)sc[0] >> 12) | (((uint)sc[1] >> 8) & 0x00f0)
|
||||
| (((uint)sc[2] >> 4) & 0x0f00) | ((uint)sc[3] & 0xf000));
|
||||
float dsuper = (float)as_half(u16);
|
||||
|
||||
float acc = 0.f;
|
||||
for (int il = 0; il < 4; ++il) {
|
||||
int ib16 = 2*it + il/2;
|
||||
float dl = dsuper * (float)(2*(((uint)sc[ib16/4] >> (3*(ib16%4))) & 7) + 1);
|
||||
|
||||
uchar qhb = src0_qh[16*b + 2*it + il/2];
|
||||
float dlt = (qhb & (0x08 << (4*(il%2)))) ? (-1.f - IQ1_M_DELTA) : (-1.f + IQ1_M_DELTA);
|
||||
|
||||
uint gi = (uint)src0_qs[32*b + 4*it+il] | ((((uint)qhb >> (4*(il%2))) & 7) << 8);
|
||||
uint g = iq1_s_grid[gi];
|
||||
|
||||
float a = 0.f;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
a += yl[8*il+j+0] * ((float)((g >> (8*j+0)) & 0xF) + dlt);
|
||||
a += yl[8*il+j+4] * ((float)((g >> (8*j+4)) & 0xF) + dlt);
|
||||
}
|
||||
acc += dl * a;
|
||||
}
|
||||
|
||||
sumf[row] += acc;
|
||||
}
|
||||
|
||||
y4 += BLOCK_STRIDE * QK_K;
|
||||
}
|
||||
|
||||
global float * dst_f32 = (global float *) dst + im*ne0*ne1 + r1*ne0;
|
||||
|
||||
for (int row = 0; row < N_DST; ++row) {
|
||||
all_sum = sub_group_reduce_add(sumf[row]);
|
||||
if (first_row + row < ne01) {
|
||||
if (get_sub_group_local_id() == 0) {
|
||||
dst_f32[first_row + row] = all_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user