2025-02-14 08:31:21 +08:00
|
|
|
package ggml
|
|
|
|
|
|
2025-10-02 06:12:32 +08:00
|
|
|
// #cgo linux LDFLAGS: -lrt -lpthread -ldl -lstdc++ -lm
|
|
|
|
|
// #cgo windows LDFLAGS: -lpthread
|
2025-03-01 10:06:47 +08:00
|
|
|
// #cgo CPPFLAGS: -I${SRCDIR}/ggml/include
|
|
|
|
|
// #include <stdlib.h>
|
|
|
|
|
// #include <stdint.h>
|
|
|
|
|
// #include "ggml.h"
|
|
|
|
|
// #include "ggml-cpu.h"
|
|
|
|
|
// #include "ggml-backend.h"
|
2025-02-14 08:31:21 +08:00
|
|
|
import "C"
|
|
|
|
|
|
|
|
|
|
import (
|
2025-03-20 04:03:16 +08:00
|
|
|
"context"
|
2025-10-29 03:08:49 +08:00
|
|
|
"encoding/binary"
|
2025-05-30 03:21:48 +08:00
|
|
|
"errors"
|
2025-02-14 08:31:21 +08:00
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"log/slog"
|
2025-02-20 06:26:40 +08:00
|
|
|
"maps"
|
2025-02-14 08:31:21 +08:00
|
|
|
"os"
|
2025-03-20 04:03:16 +08:00
|
|
|
"runtime"
|
2025-02-20 06:26:40 +08:00
|
|
|
"slices"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2025-04-18 08:12:01 +08:00
|
|
|
"sync"
|
2025-03-20 04:03:16 +08:00
|
|
|
"sync/atomic"
|
2025-02-20 06:26:40 +08:00
|
|
|
"unicode"
|
2025-02-14 08:31:21 +08:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"github.com/ollama/ollama/format"
|
2025-03-19 05:38:44 +08:00
|
|
|
"github.com/ollama/ollama/fs"
|
|
|
|
|
fsggml "github.com/ollama/ollama/fs/ggml"
|
2025-05-13 02:43:00 +08:00
|
|
|
"github.com/ollama/ollama/logutil"
|
2025-02-14 08:31:21 +08:00
|
|
|
"github.com/ollama/ollama/ml"
|
2025-02-25 07:48:42 +08:00
|
|
|
ggml "github.com/ollama/ollama/ml/backend/ggml/ggml/src"
|
2025-05-21 06:51:08 +08:00
|
|
|
"github.com/ollama/ollama/ml/nn/rope"
|
2025-02-14 08:31:21 +08:00
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
|
)
|
|
|
|
|
|
2025-04-18 08:12:01 +08:00
|
|
|
var (
|
|
|
|
|
cpus, accels, gpus []C.ggml_backend_dev_t
|
|
|
|
|
backends map[C.ggml_backend_dev_t]C.ggml_backend_t
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var initDevices = sync.OnceFunc(func() {
|
2025-03-05 05:06:56 +08:00
|
|
|
ggml.OnceLoad()
|
|
|
|
|
|
2025-04-18 08:12:01 +08:00
|
|
|
backends = make(map[C.ggml_backend_dev_t]C.ggml_backend_t)
|
|
|
|
|
for i := range C.ggml_backend_dev_count() {
|
|
|
|
|
d := C.ggml_backend_dev_get(i)
|
|
|
|
|
|
|
|
|
|
switch C.ggml_backend_dev_type(d) {
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_CPU:
|
|
|
|
|
if len(cpus) == 0 {
|
|
|
|
|
// only the first cpu device should be used
|
|
|
|
|
cpus = append(cpus, d)
|
|
|
|
|
}
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_ACCEL:
|
|
|
|
|
accels = append(accels, d)
|
Vulkan based on #9650 (#11835)
* implement the vulkan C backend
* add support in gpu.go
* add support in gen_linux.sh
* it builds
* fix segfault
* fix compilation
* fix free memory monitor
* fix total memory monitor
* update gpu.go
* fix build
* fix check_perfmon len
* remove cap_get_bound check
* fix vulkan handle releasing
* fix build on federa 40
* fix vulkan on windows
* making amdgpu work on arm achitecutre with vulkan
* add x86_64 lines in VulkanGlobs and capLinuxGlobs
* add aarch64 lines in vulkanGlobs and capLinuxGlobs
* Fix variable name
* Add vulkan build patch from @jmorganca
* Sync vendored ggml to add Vulkan support
* Updated dockerfile
https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Installing rocm library
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* This version works well
built based on this: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 00-fix-vulkan-building.patch
Work done by McBane87 here: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixed the "detached head" issues
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged in the right direction
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merging the latest stable (#2)
* Applied 00-fix-vulkan-building.patch
* Implemented vulkan backend based on the work done by whyvl, Dts0, McBane87 and others
Tested on AMD Ryzen 7 8845HS w/ Radeon 780M Graphics with ROCm disabled
```
[GIN-debug] POST /v1/chat/completions --> github.com/ollama/ollama/server.(*Server).ChatHandler-fm (6 handlers)
[GIN-debug] POST /v1/completions --> github.com/ollama/ollama/server.(*Server).GenerateHandler-fm (6 handlers)
[GIN-debug] POST /v1/embeddings --> github.com/ollama/ollama/server.(*Server).EmbedHandler-fm (6 handlers)
[GIN-debug] GET /v1/models --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (6 handlers)
[GIN-debug] GET /v1/models/:model --> github.com/ollama/ollama/server.(*Server).ShowHandler-fm (6 handlers)
time=2025-03-11T13:00:40.793Z level=INFO source=gpu.go:199 msg="vulkan: load libvulkan and libcap ok"
time=2025-03-11T13:00:40.877Z level=INFO source=gpu.go:421 msg="error looking up vulkan GPU memory" error="device is a CPU"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:443 msg="amdgpu detected, but no compatible rocm library found. Either install rocm v6, or follow manual install instructions at https://github.com/ollama/ollama/blob/main/docs/linux.md#manual-install"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:348 msg="unable to verify rocm library: no suitable rocm found, falling back to CPU"
time=2025-03-11T13:00:40.879Z level=INFO source=types.go:137 msg="inference compute" id=0 library=vulkan variant="" compute=1.3 driver=1.3 name="AMD Radeon Graphics (RADV GFX1103_R1)" total="15.6 GiB" available="15.6 GiB"
```
```
# ollama run phi4:14b
>>> /set verbose
Set 'verbose' mode.
>>> how's it going?
Hello! I'm here to help you with any questions or tasks you have. How can I assist you today? 😊
total duration: 3.341959745s
load duration: 18.165612ms
prompt eval count: 15 token(s)
prompt eval duration: 475ms
prompt eval rate: 31.58 tokens/s
eval count: 26 token(s)
eval duration: 2.846s
eval rate: 9.14 tokens/s
>>>
```
* This is no longer needed
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixes SIGSEGV: segmentation violation running gemma3 models on ollama 0.6.0 #21
Patch provided by McBane87 on https://github.com/whyvl/ollama-vulkan/issues/21
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 04-disable-mmap-vulkan.patch
From: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Pulled new upstream code for ggml-bulkan backend
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged latest ollama 0.6.2 and nasrally's Flash Attention patches (#5)
* readme: add Ellama to list of community integrations (#9800)
* readme: add screenpipe to community integrations (#9786)
* Add support for ROCm gfx1151 (#9773)
* conditionally enable parallel pipelines
* sample: make mutations in transforms explicit (#9743)
* updated minP to use early exit making use of sorted tokens
* ml/backend/ggml: allocate memory with malloc when loading model (#9822)
* runner: remove cache prompt flag from ollama runner (#9826)
We do not need to bypass the prompt caching in the ollama runner yet, as
only embedding models needed to bypass the prompt caching. When embedding
models are implemented they can skip initializing this cache completely.
* ollamarunner: Check for minBatch of context space when shifting
Models can specify that a group of inputs need to be handled a single
batch. However, context shifting didn't respect this and could trigger
a break anyways. In this case, we should instead trigger a context
shift earlier so that it occurs before the grouped batch.
Note that there still some corner cases:
- A long prompt that exceeds the context window can get truncated
in the middle of an image. With the current models, this will
result in the model not recognizing the image at all, which is
pretty much the expected result with truncation.
- The context window is set less than the minimum batch size. The
only solution to this is to refuse to load the model with these
settings. However, this can never occur with current models and
default settings.
Since users are unlikely to run into these scenarios, fixing them is
left as a follow up.
* Applied latest patches from McBane87
See this for details: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2708820861
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Add ability to enable flash attention on vulkan (#4)
* discover: add flash attention handling for vulkan
* envconfig: fix typo in config.go
As part of the process some code was refactored and I added a new field
FlashAttention to GpuInfo since the previous solution didn't allow for a
granular check via vulkan extensions. As a side effect, this now allows
for granular per-device FA support checking in other places
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
* Revert Readme changes
* Revert
* Revert changes in amd_linux.go
* Revert changes in amd_linux.go
* Remove flashattention setting gpu.go
* Revert whitespace changes in gpu.go
* Revert changes in transforms_test.go
* Revert changes in runner.go
* Revert changes in Makefile.sync
* Revert some unintented changes in Dockerfile
* Revert vulkan copy changes in Dockerfile
* Update Vulkan Code to de4c07f93783a1a96456a44dc16b9db538ee1618
* Fixed duplicate sync in ggml.go
* Revert changes in ggml.go
* Revert chnages in ggml.go
* enable falsh attention on vulkan
* revert remove parenthesis
* fixed flash attention logic enabling
* vk_check_flash_attention 0 means supported
* Update gpu.go
* Add vulkan to Windows Build script
* Remove commented out code
* Enable Vulkan Flash attention in FlashAttentionSupported
* Fix logging
* Update Vulkan backend to e54d41befcc1575f4c898c5ff4ef43970cead75f
* Removed libcap related code
libcap is not directly related to Vulkan and should be added by its own PR. It adds additional library dependencies for building and also requires users to run setcap or run ollama as root, which is not ideal for easy use
* Fix Unit Test (Add Vulkan Library)
* Add vulkan to TestHomogeneousGPUs
Test
* vulkan: get GPU ID (ollama v0.11.5)
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
* disable mmap for vulkan
* Reduce Changes remove TestHomogeneousGPUs (doesn't exist on master)
* Update vulkan version to the version used in llama.cpp
* rename gpu patch to correct number
* added Vulkan API to get correct Device UUID
current UUID from pipelineCacheUUID does not match CUDA
* Fix GPU ID Patch
* Remove Code not in llama.cpp
* modified UUID code inside ggml
* Fix Patch
* Copied minimal definition from vulkan header
* Fix compile error in Mac
Metal is preferred so we're disabling Vulkan for now
* Removed unused code
Fix linter error in CI
* Fix patches apply
* fixing lint error
* Removed unneeded function call
Somehow removing this call fixed the crashing when Vulkan header was removed
* added missing NL
* Fixed missing members in Vulkan header
also added zero clear for some structs
* Fixed wrong structure ID
* Fixed Vulkan header
More aligned with official header definition now
* buildvulkanAsSeperateFunction
* Vulkan on Windows Test
* temporarly comment out gate to run windows task
* use temporarly windows-latest for build
* Commenting out other presets to build vulkan
* reenable cpu
* commenting out error action stop
* temporarly commenting out rocm
* set vulkan path
* comment out cude for faster turnaround
* correct vulkan install
* correct vulkan silent install
* fixed install command
* revert debugging changes (vulkan builds on windows)
* revert windows-latest
* trying to build vulkan for linux
* temporarly disable cuda and rocm
* try again linux build
* fix version
* trying to fix
* trying again
* trying again
* fix version
* fixed vulkan-sdk name
* try again
* trying again
* try without version number
* try again
* add some more extra
* trying to use version 1.4.313
* revert debugging changes
* Filter out already supported gpus
* revert debug code
* Use runners for GPU discovery
This revamps how we discover GPUs in the system by leveraging the Ollama
runner. This should eliminate inconsistency between our GPU discovery and the
runners capabilities at runtime, particularly for cases where we try to filter
out unsupported GPUs. Now the runner does that implicitly based on the actual
device list. In some cases free VRAM reporting can be unreliable which can
leaad to scheduling mistakes, so this also includes a patch to leverage more
reliable VRAM reporting libraries if available.
Automatic workarounds have been removed as only one GPU leveraged this, which
is now documented. This GPU will soon fall off the support matrix with the next
ROCm bump.
Additional cleanup of the scheduler and discovery packages can be done in the
future once we have switched on the new memory management code, and removed
support for the llama runner.
* timing info for runner
* WIP - wire up Vulkan with the new engine based discovery
Not a complete implementation - free VRAM is better, but not accurate on
windows
* fix - trust the library paths from discovery when starting runner
* fix index bug
* fix vulkan ids to be underlying
* fix - give bootstrapping more time on slow systems
* Test if Vulkan device is supported
* vk_check_flash_attention is not needed (coompat2 coopmapt and scalar implementation exist)
* Handle GGML_VK_VISIBLE_DEVICES
* ask for supported first
* win: fix CPU query buffer handling
Try in a short loop until we get the size right.
* test: harden integration tests for slow start
If the server takes a while to start up, block
tests from starting until it's online to avoid
setting large timeouts in individual test cases.
* gofumpt fix
* fix build
* merge fixes
* merge fixes
* fixed build
* merge fixes
* fixing build
* fixed build
* fixed formatting
* fixed build
* fix vulkan gpu id patch
* sync llama.cpp vulkan code
* update build windows script
* merge fixes
* fix format
* fixed vulkan casing
* handle igpu as gpu
* improve case
* print out unknown library
* rturn Vulkan for vulkan library
* Revert "rturn Vulkan for vulkan library"
This reverts commit 690461a12fd5e93295d174c97edefb2bc33285b1.
* fixed patch number
* return Library Name
* remvoe debug code
* return integrated in vulkan backend
* Return pci Properties
* update patch
* directly get pci proeprties without parsing
* workaround for filtering devices. Correct way is to have a LibraryPosition Parameter in the deviceInfo
* Revert "directly get pci proeprties without parsing"
This reverts commit 8e0624851f5ed7d9f74518f574dfb422e4dd4dc2.
* Set FilteredID for Environment Filtering
* ROCm Library is named ROCm
* revert changes in patch
* Create 0028-vulkan-pci-and-memory.patch
* vulkan memory patch
* casing fix
* Add more pci properties
* Added better memory management
* Added better memory managament
* fixed patch
* Fixed patch
* FilterID creation group by library
* filter out vulkan supported by other gpu
* fixing deviceid compare
* Vulkan Fix FA coopmat1 invalid array indexing
* Use everywhere the same Vulkan Version 1.4.321.1
* Remove unneeded patch
* vulkan update
* sync vulkan glsl files
* only use for vulkan the filteredid (numeric device number)
* simplify code
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: pufferffish <github@bandersnatch.anonaddy.com>
Co-authored-by: KOISHI KOMEIJI FROM TOUHOU 11 <fuck>
Co-authored-by: DSLstandard <qgeneral35@gmail.com>
Co-authored-by: pufferffish <me@windtfw.com>
Co-authored-by: yeongbba <yeongmo.lee@logpresso.com>
Co-authored-by: tomaThomas <tomathomas@mailbox.org>
Co-authored-by: Antoine Viallon <antoine@lesviallon.fr>
Co-authored-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
Co-authored-by: Masato Nakasaka <masato.nakasaka@intel.com>
Co-authored-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: Daniel Hiltgen <daniel@ollama.com>
2025-10-15 01:59:58 +08:00
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_GPU,
|
|
|
|
|
C.GGML_BACKEND_DEVICE_TYPE_IGPU:
|
2025-04-18 08:12:01 +08:00
|
|
|
gpus = append(gpus, d)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
backends[d] = C.ggml_backend_dev_init(d, nil)
|
|
|
|
|
}
|
|
|
|
|
})
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
type layerDevice struct {
|
|
|
|
|
d C.ggml_backend_dev_t
|
|
|
|
|
bt C.ggml_backend_buffer_type_t
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
type Backend struct {
|
2025-04-18 04:42:40 +08:00
|
|
|
// modelPath is the location of the model data
|
|
|
|
|
modelPath string
|
|
|
|
|
|
2025-04-04 03:50:20 +08:00
|
|
|
meta *fsggml.GGML
|
|
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
// allocMemory means that memory should be allocated for tensors and not
|
|
|
|
|
// just a dry run
|
|
|
|
|
allocMemory bool
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
// tensorLoadTargets maps from the name of the tensor in the file
|
|
|
|
|
// to the name that is used by the model definition
|
|
|
|
|
tensorLoadTargets map[string][]string
|
|
|
|
|
|
2025-08-30 05:20:28 +08:00
|
|
|
schedMu sync.Mutex // Only one Compute can run at a time
|
2025-08-07 02:39:08 +08:00
|
|
|
sched C.ggml_backend_sched_t
|
|
|
|
|
schedBackends []C.ggml_backend_t
|
|
|
|
|
schedBufts []C.ggml_backend_buffer_type_t
|
2025-04-04 03:50:20 +08:00
|
|
|
|
2025-02-26 08:06:32 +08:00
|
|
|
tensors map[string]*C.struct_ggml_tensor
|
2025-03-05 05:06:56 +08:00
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
// input is the backend buffer type used for inputs
|
2025-08-07 02:39:08 +08:00
|
|
|
input C.ggml_backend_buffer_type_t
|
2025-03-05 05:06:56 +08:00
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
// output is the backend device used for outputs
|
|
|
|
|
output C.ggml_backend_dev_t
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// layers is the backend used for repeating layers
|
2025-05-30 03:21:48 +08:00
|
|
|
layers map[int]layerDevice
|
2025-02-26 09:24:36 +08:00
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
// requiredMemory is the cumulative memory allocations needed by the backend
|
|
|
|
|
requiredMemory *ml.BackendMemory
|
|
|
|
|
|
|
|
|
|
// btDeviceMemory maps from a buffer type to the memory allocations associated with that device
|
2025-08-07 02:39:08 +08:00
|
|
|
btDeviceMemory map[C.ggml_backend_buffer_type_t]*ml.DeviceMemory
|
2025-04-18 02:00:25 +08:00
|
|
|
|
2025-02-20 06:26:40 +08:00
|
|
|
flashAttention bool
|
2025-03-05 05:06:56 +08:00
|
|
|
|
|
|
|
|
// maxGraphNodes is the maximum allowed number of graph nodes in this scheduler
|
|
|
|
|
maxGraphNodes int
|
2025-04-18 08:12:01 +08:00
|
|
|
|
|
|
|
|
// weightBuffers are the GGML contexts and buffers for allocating weights
|
|
|
|
|
weightBuffers map[*C.struct_ggml_context]C.ggml_backend_buffer_t
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
var once sync.Once
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
func New(modelPath string, params ml.BackendParams) (ml.Backend, error) {
|
|
|
|
|
r, err := os.Open(modelPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer r.Close()
|
|
|
|
|
|
|
|
|
|
meta, err := fsggml.Decode(r, -1)
|
2025-02-14 08:31:21 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
once.Do(func() {
|
|
|
|
|
slog.Info(
|
|
|
|
|
"",
|
|
|
|
|
"architecture", meta.KV().Architecture(),
|
|
|
|
|
"file_type", meta.KV().FileType(),
|
|
|
|
|
"name", meta.KV().String("general.name"),
|
|
|
|
|
"description", meta.KV().String("general.description"),
|
|
|
|
|
"num_tensors", len(meta.Tensors().Items()),
|
|
|
|
|
"num_key_values", len(meta.KV()),
|
|
|
|
|
)
|
|
|
|
|
})
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-04-18 08:12:01 +08:00
|
|
|
initDevices()
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
var requiredMemory ml.BackendMemory
|
2025-08-07 02:39:08 +08:00
|
|
|
btDeviceMemory := make(map[C.ggml_backend_buffer_type_t]*ml.DeviceMemory)
|
2025-04-18 02:00:25 +08:00
|
|
|
|
2025-02-27 06:17:08 +08:00
|
|
|
type deviceBufferType struct {
|
2025-08-07 02:39:08 +08:00
|
|
|
d C.ggml_backend_dev_t
|
|
|
|
|
bts []C.ggml_backend_buffer_type_t
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
blocks := int(meta.KV().BlockCount())
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// create list of buffer types for the cpu
|
2025-03-01 07:42:52 +08:00
|
|
|
cpuDeviceBufferType := deviceBufferType{d: C.ggml_backend_dev_by_type(C.GGML_BACKEND_DEVICE_TYPE_CPU)}
|
2025-02-20 06:26:40 +08:00
|
|
|
for _, d := range append(accels, append(gpus, cpus...)...) {
|
|
|
|
|
switch C.ggml_backend_dev_type(d) {
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_CPU,
|
|
|
|
|
C.GGML_BACKEND_DEVICE_TYPE_ACCEL:
|
2025-05-30 03:21:48 +08:00
|
|
|
bt := C.ggml_backend_dev_buffer_type(d)
|
|
|
|
|
cpuDeviceBufferType.bts = append(cpuDeviceBufferType.bts, bt)
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
btDeviceMemory[C.ggml_backend_dev_buffer_type(d)] = &requiredMemory.CPU
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
requiredMemory.CPU.Name = C.GoString(C.ggml_backend_dev_name(cpuDeviceBufferType.d))
|
2025-06-18 22:30:49 +08:00
|
|
|
var props C.struct_ggml_backend_dev_props
|
|
|
|
|
C.ggml_backend_dev_get_props(cpuDeviceBufferType.d, &props)
|
2025-06-26 08:13:32 +08:00
|
|
|
requiredMemory.CPU.ID = C.GoString(props.id)
|
2025-10-02 06:12:32 +08:00
|
|
|
requiredMemory.CPU.Library = C.GoString(props.library)
|
2025-09-23 08:27:03 +08:00
|
|
|
requiredMemory.CPU.Weights = make([]uint64, blocks+1)
|
|
|
|
|
requiredMemory.CPU.Cache = make([]uint64, blocks+1)
|
2025-04-18 02:00:25 +08:00
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// create list of buffer types for each gpu
|
2025-02-27 06:17:08 +08:00
|
|
|
var gpuDeviceBufferTypes []deviceBufferType
|
2025-04-18 02:00:25 +08:00
|
|
|
requiredMemory.GPUs = make([]ml.DeviceMemory, len(gpus))
|
|
|
|
|
for i, d := range gpus {
|
2025-02-20 06:26:40 +08:00
|
|
|
bt := C.ggml_backend_dev_buffer_type(d)
|
2025-02-27 06:17:08 +08:00
|
|
|
gpuDeviceBufferTypes = append(gpuDeviceBufferTypes, deviceBufferType{
|
2025-02-20 06:26:40 +08:00
|
|
|
d: d,
|
2025-08-07 02:39:08 +08:00
|
|
|
bts: append([]C.ggml_backend_buffer_type_t{bt}, cpuDeviceBufferType.bts...),
|
2025-02-20 06:26:40 +08:00
|
|
|
})
|
2025-05-30 03:21:48 +08:00
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
btDeviceMemory[bt] = &requiredMemory.GPUs[i]
|
|
|
|
|
requiredMemory.GPUs[i].Name = C.GoString(C.ggml_backend_dev_name(d))
|
2025-06-18 22:30:49 +08:00
|
|
|
var props C.struct_ggml_backend_dev_props
|
|
|
|
|
C.ggml_backend_dev_get_props(d, &props)
|
2025-06-26 08:13:32 +08:00
|
|
|
requiredMemory.GPUs[i].ID = C.GoString(props.id)
|
2025-10-02 06:12:32 +08:00
|
|
|
requiredMemory.GPUs[i].Library = C.GoString(props.library)
|
2025-09-23 08:27:03 +08:00
|
|
|
requiredMemory.GPUs[i].Weights = make([]uint64, blocks+1)
|
|
|
|
|
requiredMemory.GPUs[i].Cache = make([]uint64, blocks+1)
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// inputs always use cpu
|
2025-03-01 07:42:52 +08:00
|
|
|
input := cpuDeviceBufferType
|
2025-02-20 06:26:40 +08:00
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
assignLayer := func(layer int) deviceBufferType {
|
|
|
|
|
for _, p := range params.GPULayers {
|
|
|
|
|
for _, l := range p.Layers {
|
|
|
|
|
if l == layer {
|
|
|
|
|
for i := range requiredMemory.GPUs {
|
2025-10-02 06:12:32 +08:00
|
|
|
if requiredMemory.GPUs[i].DeviceID == p.DeviceID {
|
2025-05-30 03:21:48 +08:00
|
|
|
return gpuDeviceBufferTypes[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-27 06:17:08 +08:00
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
return cpuDeviceBufferType
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-27 07:14:16 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
return cpuDeviceBufferType
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// repeating layers are assigned based on their index in reverse order, e.g. i / (block_count + 1)
|
2025-02-27 06:17:08 +08:00
|
|
|
layers := make([]deviceBufferType, blocks)
|
2025-02-20 06:26:40 +08:00
|
|
|
for i := range layers {
|
2025-02-27 06:17:08 +08:00
|
|
|
layers[i] = assignLayer(i)
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// outputs are assigned iff allowed by splits and configured number of gpu layers
|
2025-02-27 06:17:08 +08:00
|
|
|
output := assignLayer(blocks)
|
2025-02-20 06:26:40 +08:00
|
|
|
|
|
|
|
|
maxTensors := len(meta.Tensors().Items())
|
|
|
|
|
maxTensors += 1
|
2025-03-05 05:06:56 +08:00
|
|
|
// each layer has at most 2 extra tensors for rope operations
|
2025-02-20 06:26:40 +08:00
|
|
|
maxTensors += blocks * 2
|
|
|
|
|
|
2025-02-25 07:48:42 +08:00
|
|
|
type tensor struct {
|
2025-03-19 05:38:44 +08:00
|
|
|
source *fsggml.Tensor
|
2025-02-25 07:48:42 +08:00
|
|
|
target string
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// some tensors are mapped to different names so keep a list
|
2025-02-25 07:48:42 +08:00
|
|
|
targets := make(map[string][]string)
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// contexts are shared by tensors of the same buffer type
|
2025-08-07 02:39:08 +08:00
|
|
|
ctxs := make(map[C.ggml_backend_buffer_type_t]*C.struct_ggml_context)
|
|
|
|
|
createTensor := func(t tensor, bts []C.ggml_backend_buffer_type_t, layer int) *C.struct_ggml_tensor {
|
2025-02-20 06:26:40 +08:00
|
|
|
for _, bt := range bts {
|
|
|
|
|
if _, ok := ctxs[bt]; !ok {
|
|
|
|
|
ctxs[bt] = C.ggml_init(C.struct_ggml_init_params{
|
|
|
|
|
mem_size: C.ggml_tensor_overhead() * C.size_t(maxTensors),
|
|
|
|
|
no_alloc: true,
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-02-25 07:48:42 +08:00
|
|
|
targets[t.source.Name] = append(targets[t.source.Name], t.target)
|
|
|
|
|
|
|
|
|
|
name := t.source.Name
|
|
|
|
|
if t.target != "" {
|
|
|
|
|
name = t.target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cname := C.CString(name)
|
2025-02-14 08:31:21 +08:00
|
|
|
defer C.free(unsafe.Pointer(cname))
|
2025-02-20 06:26:40 +08:00
|
|
|
if tt := C.ggml_get_tensor(ctxs[bt], cname); tt != nil {
|
|
|
|
|
return tt
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 05:42:58 +08:00
|
|
|
kind := t.source.Kind
|
|
|
|
|
if t.source.Kind == 4 {
|
|
|
|
|
// transform raw mxfp4 stream to ggml mxfp4 format
|
|
|
|
|
kind = 39
|
|
|
|
|
} else if t.source.Kind == uint32(fsggml.TensorTypeBF16) && strings.HasSuffix(t.source.Name, "_exps.bias") {
|
|
|
|
|
// transform "_exps.bias" from bf16 to fp32; add_ids only supports fp32 tensors
|
|
|
|
|
kind = uint32(fsggml.TensorTypeF32)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tt := C.ggml_new_tensor(ctxs[bt], kind, C.int(len(t.source.Shape)), (*C.int64_t)(unsafe.Pointer(&t.source.Shape[0])))
|
2025-02-14 08:31:21 +08:00
|
|
|
C.ggml_set_name(tt, cname)
|
|
|
|
|
|
2025-09-03 04:09:12 +08:00
|
|
|
logutil.Trace("created tensor", "name", name, "shape", t.source.Shape, "dtype", t.source.Kind, "buffer_type", C.GoString(C.ggml_backend_buft_name(bt)))
|
2025-04-18 02:00:25 +08:00
|
|
|
|
|
|
|
|
size := pad(C.ggml_backend_buft_get_alloc_size(bt, tt), C.ggml_backend_buft_get_alignment(bt))
|
|
|
|
|
if layer == -1 {
|
2025-09-23 08:27:03 +08:00
|
|
|
requiredMemory.InputWeights += uint64(size)
|
2025-04-18 02:00:25 +08:00
|
|
|
} else {
|
2025-09-23 08:27:03 +08:00
|
|
|
btDeviceMemory[bt].Weights[layer] += uint64(size)
|
2025-04-18 02:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 06:26:40 +08:00
|
|
|
//nolint:staticcheck // TODO: check if buffer type supports this tensor
|
|
|
|
|
return tt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-28 08:46:01 +08:00
|
|
|
contains := func(s string, parts ...string) bool {
|
2025-02-20 06:26:40 +08:00
|
|
|
split := strings.Split(s, ".")
|
|
|
|
|
for _, part := range parts {
|
|
|
|
|
if slices.Contains(split, part) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 06:26:40 +08:00
|
|
|
for _, t := range meta.Tensors().Items() {
|
|
|
|
|
switch {
|
2025-02-28 08:46:01 +08:00
|
|
|
case contains(t.Name, "position_embd", "token_embd", "token_norm_embd", "token_types"):
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t}, input.bts, -1)
|
2025-03-10 02:29:08 +08:00
|
|
|
if _, ok := meta.Tensors().GroupLayers()["output"]; !ok && t.Name == "token_embd.weight" {
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t, target: "output.weight"}, output.bts, blocks)
|
2025-03-10 02:29:08 +08:00
|
|
|
}
|
2025-06-26 12:47:09 +08:00
|
|
|
case contains(t.Name, "cls", "output", "output_norm",
|
|
|
|
|
"altup_proj", "altup_unembd_proj",
|
|
|
|
|
"per_layer_token_embd", "per_layer_model_proj", "per_layer_proj_norm"):
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t}, output.bts, blocks)
|
2025-02-28 08:46:01 +08:00
|
|
|
case strings.HasPrefix(t.Name, "v.") || strings.HasPrefix(t.Name, "mm."):
|
2025-03-05 05:06:56 +08:00
|
|
|
// TODO: assign vision tensors to the gpu if possible
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t}, output.bts, blocks)
|
2025-03-11 01:46:17 +08:00
|
|
|
case contains(t.Name, "rope_freqs", "rope_factors_long", "rope_factors_short"):
|
|
|
|
|
// these tensors should be repeated per layer
|
|
|
|
|
for i, layer := range layers {
|
|
|
|
|
createTensor(tensor{
|
|
|
|
|
source: t,
|
|
|
|
|
target: "blk." + strconv.Itoa(i) + "." + t.Name,
|
2025-04-18 02:00:25 +08:00
|
|
|
}, layer.bts, i)
|
2025-03-11 01:46:17 +08:00
|
|
|
}
|
2025-02-20 06:26:40 +08:00
|
|
|
default:
|
2025-03-05 05:06:56 +08:00
|
|
|
layerIndex := -1
|
|
|
|
|
if fields := strings.FieldsFunc(t.Name, func(r rune) bool { return !unicode.IsNumber(r) }); len(fields) > 0 {
|
|
|
|
|
if i, err := strconv.Atoi(fields[0]); err == nil {
|
|
|
|
|
layerIndex = i
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
2025-03-05 05:06:56 +08:00
|
|
|
}
|
2025-02-20 06:26:40 +08:00
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
if layerIndex >= 0 {
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t}, layers[layerIndex].bts, layerIndex)
|
2025-02-20 06:26:40 +08:00
|
|
|
} else {
|
2025-03-11 01:46:17 +08:00
|
|
|
// load all other tensors on the cpu
|
2025-04-18 02:00:25 +08:00
|
|
|
createTensor(tensor{source: t}, input.bts, -1)
|
2025-02-20 06:26:40 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// map tensor names to tensors for easy lookup later
|
2025-02-20 06:26:40 +08:00
|
|
|
tensors := make(map[string]*C.struct_ggml_tensor)
|
|
|
|
|
for _, c := range ctxs {
|
|
|
|
|
for t := C.ggml_get_first_tensor(c); t != nil; t = C.ggml_get_next_tensor(c, t) {
|
|
|
|
|
tensors[C.GoString(C.ggml_get_name(t))] = t
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
// map devices to backend buffer types so new tensors can be assigned to the correct device
|
2025-08-07 02:39:08 +08:00
|
|
|
deviceBufferTypes := make(map[C.ggml_backend_dev_t]C.ggml_backend_buffer_type_t)
|
2025-04-18 04:42:40 +08:00
|
|
|
|
|
|
|
|
// create backends and buffer types used for the compute graph scheduler
|
2025-08-07 02:39:08 +08:00
|
|
|
var schedBackends []C.ggml_backend_t
|
|
|
|
|
var schedBufts []C.ggml_backend_buffer_type_t
|
2025-04-18 04:42:40 +08:00
|
|
|
for _, d := range append(gpus, append(accels, cpus...)...) {
|
2025-04-18 08:12:01 +08:00
|
|
|
b := backends[d]
|
2025-04-18 04:42:40 +08:00
|
|
|
bt := C.ggml_backend_get_default_buffer_type(b)
|
|
|
|
|
|
2025-05-30 03:21:48 +08:00
|
|
|
// Always include CPU as a fallback but otherwise, just use the devices where we assigned layers
|
|
|
|
|
if !slices.Contains(cpuDeviceBufferType.bts, bt) {
|
|
|
|
|
if c, ok := ctxs[bt]; !ok || C.ggml_get_first_tensor(c) == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
deviceBufferTypes[d] = bt
|
|
|
|
|
|
|
|
|
|
schedBackends = append(schedBackends, b)
|
|
|
|
|
schedBufts = append(schedBufts, bt)
|
|
|
|
|
|
|
|
|
|
if C.ggml_backend_is_cpu(b) {
|
|
|
|
|
// set number of threads for cpu backend
|
|
|
|
|
C.ggml_backend_cpu_set_n_threads(b, C.int(Threads(params.NumThreads)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxGraphNodes := max(8192, len(meta.Tensors().Items())*5)
|
2025-09-10 07:17:31 +08:00
|
|
|
|
|
|
|
|
sched := C.ggml_backend_sched_new_ext(
|
|
|
|
|
(*C.ggml_backend_t)(unsafe.Pointer(&schedBackends[0])),
|
|
|
|
|
(*C.ggml_backend_buffer_type_t)(unsafe.Pointer(&schedBufts[0])),
|
|
|
|
|
C.int(len(schedBackends)),
|
|
|
|
|
C.size_t(maxGraphNodes),
|
|
|
|
|
C._Bool(false),
|
|
|
|
|
C._Bool(false),
|
|
|
|
|
C._Bool(params.AllocMemory),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// allocate buffers for each context
|
|
|
|
|
bbs := make(map[*C.struct_ggml_context]C.ggml_backend_buffer_t, len(ctxs))
|
|
|
|
|
for bt, c := range ctxs {
|
|
|
|
|
if C.ggml_get_first_tensor(c) == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b := C.ggml_backend_alloc_ctx_tensors_from_buft(c, bt)
|
|
|
|
|
if b == nil {
|
|
|
|
|
for _, b := range bbs {
|
|
|
|
|
C.ggml_backend_buffer_free(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ctx := range ctxs {
|
|
|
|
|
C.ggml_free(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
panic(ml.ErrNoMem{BackendMemory: requiredMemory})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C.ggml_backend_buffer_set_usage(b, C.GGML_BACKEND_BUFFER_USAGE_WEIGHTS)
|
|
|
|
|
bbs[c] = b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for bs := range maps.Values(bbs) {
|
|
|
|
|
logutil.Trace("model weights", "buffer", C.GoString(C.ggml_backend_buffer_name(bs)),
|
|
|
|
|
"size", format.HumanBytes2(uint64(C.ggml_backend_buffer_get_size(bs))))
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
return &Backend{
|
|
|
|
|
modelPath: modelPath,
|
2025-05-30 03:21:48 +08:00
|
|
|
allocMemory: params.AllocMemory,
|
2025-04-18 04:42:40 +08:00
|
|
|
flashAttention: params.FlashAttention,
|
|
|
|
|
meta: meta,
|
|
|
|
|
tensorLoadTargets: targets,
|
|
|
|
|
tensors: tensors,
|
2025-09-10 07:17:31 +08:00
|
|
|
sched: sched,
|
|
|
|
|
schedBackends: schedBackends,
|
|
|
|
|
schedBufts: schedBufts,
|
|
|
|
|
input: deviceBufferTypes[input.d],
|
|
|
|
|
output: output.d,
|
2025-05-30 03:21:48 +08:00
|
|
|
layers: func() map[int]layerDevice {
|
|
|
|
|
m := make(map[int]layerDevice)
|
2025-04-18 04:42:40 +08:00
|
|
|
for i, layer := range layers {
|
2025-05-30 03:21:48 +08:00
|
|
|
m[i] = layerDevice{
|
|
|
|
|
d: layer.d,
|
|
|
|
|
bt: deviceBufferTypes[layer.d],
|
|
|
|
|
}
|
2025-04-18 04:42:40 +08:00
|
|
|
}
|
|
|
|
|
return m
|
|
|
|
|
}(),
|
2025-04-18 02:00:25 +08:00
|
|
|
requiredMemory: &requiredMemory,
|
|
|
|
|
btDeviceMemory: btDeviceMemory,
|
|
|
|
|
maxGraphNodes: maxGraphNodes,
|
2025-04-18 08:12:01 +08:00
|
|
|
weightBuffers: bbs,
|
2025-04-18 04:42:40 +08:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
ml.RegisterBackend("ggml", New)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 08:12:01 +08:00
|
|
|
func (b *Backend) Close() {
|
|
|
|
|
if b == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ctx, b := range b.weightBuffers {
|
|
|
|
|
C.ggml_backend_buffer_free(b)
|
|
|
|
|
C.ggml_free(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C.ggml_backend_sched_free(b.sched)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
func (b *Backend) Load(ctx context.Context, progress func(float32)) error {
|
2025-05-30 03:21:48 +08:00
|
|
|
if !b.allocMemory {
|
|
|
|
|
return errors.New("cannot load model without memory allocation")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mimic llama runner logs summarizing layers and memory
|
|
|
|
|
gpuLayers := 0
|
|
|
|
|
for layer := range maps.Values(b.layers) {
|
Vulkan based on #9650 (#11835)
* implement the vulkan C backend
* add support in gpu.go
* add support in gen_linux.sh
* it builds
* fix segfault
* fix compilation
* fix free memory monitor
* fix total memory monitor
* update gpu.go
* fix build
* fix check_perfmon len
* remove cap_get_bound check
* fix vulkan handle releasing
* fix build on federa 40
* fix vulkan on windows
* making amdgpu work on arm achitecutre with vulkan
* add x86_64 lines in VulkanGlobs and capLinuxGlobs
* add aarch64 lines in vulkanGlobs and capLinuxGlobs
* Fix variable name
* Add vulkan build patch from @jmorganca
* Sync vendored ggml to add Vulkan support
* Updated dockerfile
https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Installing rocm library
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* This version works well
built based on this: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 00-fix-vulkan-building.patch
Work done by McBane87 here: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixed the "detached head" issues
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged in the right direction
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merging the latest stable (#2)
* Applied 00-fix-vulkan-building.patch
* Implemented vulkan backend based on the work done by whyvl, Dts0, McBane87 and others
Tested on AMD Ryzen 7 8845HS w/ Radeon 780M Graphics with ROCm disabled
```
[GIN-debug] POST /v1/chat/completions --> github.com/ollama/ollama/server.(*Server).ChatHandler-fm (6 handlers)
[GIN-debug] POST /v1/completions --> github.com/ollama/ollama/server.(*Server).GenerateHandler-fm (6 handlers)
[GIN-debug] POST /v1/embeddings --> github.com/ollama/ollama/server.(*Server).EmbedHandler-fm (6 handlers)
[GIN-debug] GET /v1/models --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (6 handlers)
[GIN-debug] GET /v1/models/:model --> github.com/ollama/ollama/server.(*Server).ShowHandler-fm (6 handlers)
time=2025-03-11T13:00:40.793Z level=INFO source=gpu.go:199 msg="vulkan: load libvulkan and libcap ok"
time=2025-03-11T13:00:40.877Z level=INFO source=gpu.go:421 msg="error looking up vulkan GPU memory" error="device is a CPU"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:443 msg="amdgpu detected, but no compatible rocm library found. Either install rocm v6, or follow manual install instructions at https://github.com/ollama/ollama/blob/main/docs/linux.md#manual-install"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:348 msg="unable to verify rocm library: no suitable rocm found, falling back to CPU"
time=2025-03-11T13:00:40.879Z level=INFO source=types.go:137 msg="inference compute" id=0 library=vulkan variant="" compute=1.3 driver=1.3 name="AMD Radeon Graphics (RADV GFX1103_R1)" total="15.6 GiB" available="15.6 GiB"
```
```
# ollama run phi4:14b
>>> /set verbose
Set 'verbose' mode.
>>> how's it going?
Hello! I'm here to help you with any questions or tasks you have. How can I assist you today? 😊
total duration: 3.341959745s
load duration: 18.165612ms
prompt eval count: 15 token(s)
prompt eval duration: 475ms
prompt eval rate: 31.58 tokens/s
eval count: 26 token(s)
eval duration: 2.846s
eval rate: 9.14 tokens/s
>>>
```
* This is no longer needed
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixes SIGSEGV: segmentation violation running gemma3 models on ollama 0.6.0 #21
Patch provided by McBane87 on https://github.com/whyvl/ollama-vulkan/issues/21
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 04-disable-mmap-vulkan.patch
From: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Pulled new upstream code for ggml-bulkan backend
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged latest ollama 0.6.2 and nasrally's Flash Attention patches (#5)
* readme: add Ellama to list of community integrations (#9800)
* readme: add screenpipe to community integrations (#9786)
* Add support for ROCm gfx1151 (#9773)
* conditionally enable parallel pipelines
* sample: make mutations in transforms explicit (#9743)
* updated minP to use early exit making use of sorted tokens
* ml/backend/ggml: allocate memory with malloc when loading model (#9822)
* runner: remove cache prompt flag from ollama runner (#9826)
We do not need to bypass the prompt caching in the ollama runner yet, as
only embedding models needed to bypass the prompt caching. When embedding
models are implemented they can skip initializing this cache completely.
* ollamarunner: Check for minBatch of context space when shifting
Models can specify that a group of inputs need to be handled a single
batch. However, context shifting didn't respect this and could trigger
a break anyways. In this case, we should instead trigger a context
shift earlier so that it occurs before the grouped batch.
Note that there still some corner cases:
- A long prompt that exceeds the context window can get truncated
in the middle of an image. With the current models, this will
result in the model not recognizing the image at all, which is
pretty much the expected result with truncation.
- The context window is set less than the minimum batch size. The
only solution to this is to refuse to load the model with these
settings. However, this can never occur with current models and
default settings.
Since users are unlikely to run into these scenarios, fixing them is
left as a follow up.
* Applied latest patches from McBane87
See this for details: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2708820861
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Add ability to enable flash attention on vulkan (#4)
* discover: add flash attention handling for vulkan
* envconfig: fix typo in config.go
As part of the process some code was refactored and I added a new field
FlashAttention to GpuInfo since the previous solution didn't allow for a
granular check via vulkan extensions. As a side effect, this now allows
for granular per-device FA support checking in other places
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
* Revert Readme changes
* Revert
* Revert changes in amd_linux.go
* Revert changes in amd_linux.go
* Remove flashattention setting gpu.go
* Revert whitespace changes in gpu.go
* Revert changes in transforms_test.go
* Revert changes in runner.go
* Revert changes in Makefile.sync
* Revert some unintented changes in Dockerfile
* Revert vulkan copy changes in Dockerfile
* Update Vulkan Code to de4c07f93783a1a96456a44dc16b9db538ee1618
* Fixed duplicate sync in ggml.go
* Revert changes in ggml.go
* Revert chnages in ggml.go
* enable falsh attention on vulkan
* revert remove parenthesis
* fixed flash attention logic enabling
* vk_check_flash_attention 0 means supported
* Update gpu.go
* Add vulkan to Windows Build script
* Remove commented out code
* Enable Vulkan Flash attention in FlashAttentionSupported
* Fix logging
* Update Vulkan backend to e54d41befcc1575f4c898c5ff4ef43970cead75f
* Removed libcap related code
libcap is not directly related to Vulkan and should be added by its own PR. It adds additional library dependencies for building and also requires users to run setcap or run ollama as root, which is not ideal for easy use
* Fix Unit Test (Add Vulkan Library)
* Add vulkan to TestHomogeneousGPUs
Test
* vulkan: get GPU ID (ollama v0.11.5)
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
* disable mmap for vulkan
* Reduce Changes remove TestHomogeneousGPUs (doesn't exist on master)
* Update vulkan version to the version used in llama.cpp
* rename gpu patch to correct number
* added Vulkan API to get correct Device UUID
current UUID from pipelineCacheUUID does not match CUDA
* Fix GPU ID Patch
* Remove Code not in llama.cpp
* modified UUID code inside ggml
* Fix Patch
* Copied minimal definition from vulkan header
* Fix compile error in Mac
Metal is preferred so we're disabling Vulkan for now
* Removed unused code
Fix linter error in CI
* Fix patches apply
* fixing lint error
* Removed unneeded function call
Somehow removing this call fixed the crashing when Vulkan header was removed
* added missing NL
* Fixed missing members in Vulkan header
also added zero clear for some structs
* Fixed wrong structure ID
* Fixed Vulkan header
More aligned with official header definition now
* buildvulkanAsSeperateFunction
* Vulkan on Windows Test
* temporarly comment out gate to run windows task
* use temporarly windows-latest for build
* Commenting out other presets to build vulkan
* reenable cpu
* commenting out error action stop
* temporarly commenting out rocm
* set vulkan path
* comment out cude for faster turnaround
* correct vulkan install
* correct vulkan silent install
* fixed install command
* revert debugging changes (vulkan builds on windows)
* revert windows-latest
* trying to build vulkan for linux
* temporarly disable cuda and rocm
* try again linux build
* fix version
* trying to fix
* trying again
* trying again
* fix version
* fixed vulkan-sdk name
* try again
* trying again
* try without version number
* try again
* add some more extra
* trying to use version 1.4.313
* revert debugging changes
* Filter out already supported gpus
* revert debug code
* Use runners for GPU discovery
This revamps how we discover GPUs in the system by leveraging the Ollama
runner. This should eliminate inconsistency between our GPU discovery and the
runners capabilities at runtime, particularly for cases where we try to filter
out unsupported GPUs. Now the runner does that implicitly based on the actual
device list. In some cases free VRAM reporting can be unreliable which can
leaad to scheduling mistakes, so this also includes a patch to leverage more
reliable VRAM reporting libraries if available.
Automatic workarounds have been removed as only one GPU leveraged this, which
is now documented. This GPU will soon fall off the support matrix with the next
ROCm bump.
Additional cleanup of the scheduler and discovery packages can be done in the
future once we have switched on the new memory management code, and removed
support for the llama runner.
* timing info for runner
* WIP - wire up Vulkan with the new engine based discovery
Not a complete implementation - free VRAM is better, but not accurate on
windows
* fix - trust the library paths from discovery when starting runner
* fix index bug
* fix vulkan ids to be underlying
* fix - give bootstrapping more time on slow systems
* Test if Vulkan device is supported
* vk_check_flash_attention is not needed (coompat2 coopmapt and scalar implementation exist)
* Handle GGML_VK_VISIBLE_DEVICES
* ask for supported first
* win: fix CPU query buffer handling
Try in a short loop until we get the size right.
* test: harden integration tests for slow start
If the server takes a while to start up, block
tests from starting until it's online to avoid
setting large timeouts in individual test cases.
* gofumpt fix
* fix build
* merge fixes
* merge fixes
* fixed build
* merge fixes
* fixing build
* fixed build
* fixed formatting
* fixed build
* fix vulkan gpu id patch
* sync llama.cpp vulkan code
* update build windows script
* merge fixes
* fix format
* fixed vulkan casing
* handle igpu as gpu
* improve case
* print out unknown library
* rturn Vulkan for vulkan library
* Revert "rturn Vulkan for vulkan library"
This reverts commit 690461a12fd5e93295d174c97edefb2bc33285b1.
* fixed patch number
* return Library Name
* remvoe debug code
* return integrated in vulkan backend
* Return pci Properties
* update patch
* directly get pci proeprties without parsing
* workaround for filtering devices. Correct way is to have a LibraryPosition Parameter in the deviceInfo
* Revert "directly get pci proeprties without parsing"
This reverts commit 8e0624851f5ed7d9f74518f574dfb422e4dd4dc2.
* Set FilteredID for Environment Filtering
* ROCm Library is named ROCm
* revert changes in patch
* Create 0028-vulkan-pci-and-memory.patch
* vulkan memory patch
* casing fix
* Add more pci properties
* Added better memory management
* Added better memory managament
* fixed patch
* Fixed patch
* FilterID creation group by library
* filter out vulkan supported by other gpu
* fixing deviceid compare
* Vulkan Fix FA coopmat1 invalid array indexing
* Use everywhere the same Vulkan Version 1.4.321.1
* Remove unneeded patch
* vulkan update
* sync vulkan glsl files
* only use for vulkan the filteredid (numeric device number)
* simplify code
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: pufferffish <github@bandersnatch.anonaddy.com>
Co-authored-by: KOISHI KOMEIJI FROM TOUHOU 11 <fuck>
Co-authored-by: DSLstandard <qgeneral35@gmail.com>
Co-authored-by: pufferffish <me@windtfw.com>
Co-authored-by: yeongbba <yeongmo.lee@logpresso.com>
Co-authored-by: tomaThomas <tomathomas@mailbox.org>
Co-authored-by: Antoine Viallon <antoine@lesviallon.fr>
Co-authored-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
Co-authored-by: Masato Nakasaka <masato.nakasaka@intel.com>
Co-authored-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: Daniel Hiltgen <daniel@ollama.com>
2025-10-15 01:59:58 +08:00
|
|
|
switch C.ggml_backend_dev_type(layer.d) {
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_GPU,
|
|
|
|
|
C.GGML_BACKEND_DEVICE_TYPE_IGPU:
|
2025-05-30 03:21:48 +08:00
|
|
|
gpuLayers++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
slog.Info(fmt.Sprintf("offloading %d repeating layers to GPU", gpuLayers))
|
|
|
|
|
|
|
|
|
|
switch C.ggml_backend_dev_type(b.output) {
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_CPU:
|
|
|
|
|
slog.Info("offloading output layer to CPU")
|
Vulkan based on #9650 (#11835)
* implement the vulkan C backend
* add support in gpu.go
* add support in gen_linux.sh
* it builds
* fix segfault
* fix compilation
* fix free memory monitor
* fix total memory monitor
* update gpu.go
* fix build
* fix check_perfmon len
* remove cap_get_bound check
* fix vulkan handle releasing
* fix build on federa 40
* fix vulkan on windows
* making amdgpu work on arm achitecutre with vulkan
* add x86_64 lines in VulkanGlobs and capLinuxGlobs
* add aarch64 lines in vulkanGlobs and capLinuxGlobs
* Fix variable name
* Add vulkan build patch from @jmorganca
* Sync vendored ggml to add Vulkan support
* Updated dockerfile
https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Installing rocm library
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* This version works well
built based on this: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 00-fix-vulkan-building.patch
Work done by McBane87 here: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixed the "detached head" issues
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged in the right direction
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merging the latest stable (#2)
* Applied 00-fix-vulkan-building.patch
* Implemented vulkan backend based on the work done by whyvl, Dts0, McBane87 and others
Tested on AMD Ryzen 7 8845HS w/ Radeon 780M Graphics with ROCm disabled
```
[GIN-debug] POST /v1/chat/completions --> github.com/ollama/ollama/server.(*Server).ChatHandler-fm (6 handlers)
[GIN-debug] POST /v1/completions --> github.com/ollama/ollama/server.(*Server).GenerateHandler-fm (6 handlers)
[GIN-debug] POST /v1/embeddings --> github.com/ollama/ollama/server.(*Server).EmbedHandler-fm (6 handlers)
[GIN-debug] GET /v1/models --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (6 handlers)
[GIN-debug] GET /v1/models/:model --> github.com/ollama/ollama/server.(*Server).ShowHandler-fm (6 handlers)
time=2025-03-11T13:00:40.793Z level=INFO source=gpu.go:199 msg="vulkan: load libvulkan and libcap ok"
time=2025-03-11T13:00:40.877Z level=INFO source=gpu.go:421 msg="error looking up vulkan GPU memory" error="device is a CPU"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:443 msg="amdgpu detected, but no compatible rocm library found. Either install rocm v6, or follow manual install instructions at https://github.com/ollama/ollama/blob/main/docs/linux.md#manual-install"
time=2025-03-11T13:00:40.878Z level=WARN source=amd_linux.go:348 msg="unable to verify rocm library: no suitable rocm found, falling back to CPU"
time=2025-03-11T13:00:40.879Z level=INFO source=types.go:137 msg="inference compute" id=0 library=vulkan variant="" compute=1.3 driver=1.3 name="AMD Radeon Graphics (RADV GFX1103_R1)" total="15.6 GiB" available="15.6 GiB"
```
```
# ollama run phi4:14b
>>> /set verbose
Set 'verbose' mode.
>>> how's it going?
Hello! I'm here to help you with any questions or tasks you have. How can I assist you today? 😊
total duration: 3.341959745s
load duration: 18.165612ms
prompt eval count: 15 token(s)
prompt eval duration: 475ms
prompt eval rate: 31.58 tokens/s
eval count: 26 token(s)
eval duration: 2.846s
eval rate: 9.14 tokens/s
>>>
```
* This is no longer needed
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Fixes SIGSEGV: segmentation violation running gemma3 models on ollama 0.6.0 #21
Patch provided by McBane87 on https://github.com/whyvl/ollama-vulkan/issues/21
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Applied 04-disable-mmap-vulkan.patch
From: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2660836871
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Pulled new upstream code for ggml-bulkan backend
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Merged latest ollama 0.6.2 and nasrally's Flash Attention patches (#5)
* readme: add Ellama to list of community integrations (#9800)
* readme: add screenpipe to community integrations (#9786)
* Add support for ROCm gfx1151 (#9773)
* conditionally enable parallel pipelines
* sample: make mutations in transforms explicit (#9743)
* updated minP to use early exit making use of sorted tokens
* ml/backend/ggml: allocate memory with malloc when loading model (#9822)
* runner: remove cache prompt flag from ollama runner (#9826)
We do not need to bypass the prompt caching in the ollama runner yet, as
only embedding models needed to bypass the prompt caching. When embedding
models are implemented they can skip initializing this cache completely.
* ollamarunner: Check for minBatch of context space when shifting
Models can specify that a group of inputs need to be handled a single
batch. However, context shifting didn't respect this and could trigger
a break anyways. In this case, we should instead trigger a context
shift earlier so that it occurs before the grouped batch.
Note that there still some corner cases:
- A long prompt that exceeds the context window can get truncated
in the middle of an image. With the current models, this will
result in the model not recognizing the image at all, which is
pretty much the expected result with truncation.
- The context window is set less than the minimum batch size. The
only solution to this is to refuse to load the model with these
settings. However, this can never occur with current models and
default settings.
Since users are unlikely to run into these scenarios, fixing them is
left as a follow up.
* Applied latest patches from McBane87
See this for details: https://github.com/whyvl/ollama-vulkan/issues/7#issuecomment-2708820861
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
* Add ability to enable flash attention on vulkan (#4)
* discover: add flash attention handling for vulkan
* envconfig: fix typo in config.go
As part of the process some code was refactored and I added a new field
FlashAttention to GpuInfo since the previous solution didn't allow for a
granular check via vulkan extensions. As a side effect, this now allows
for granular per-device FA support checking in other places
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
* Revert Readme changes
* Revert
* Revert changes in amd_linux.go
* Revert changes in amd_linux.go
* Remove flashattention setting gpu.go
* Revert whitespace changes in gpu.go
* Revert changes in transforms_test.go
* Revert changes in runner.go
* Revert changes in Makefile.sync
* Revert some unintented changes in Dockerfile
* Revert vulkan copy changes in Dockerfile
* Update Vulkan Code to de4c07f93783a1a96456a44dc16b9db538ee1618
* Fixed duplicate sync in ggml.go
* Revert changes in ggml.go
* Revert chnages in ggml.go
* enable falsh attention on vulkan
* revert remove parenthesis
* fixed flash attention logic enabling
* vk_check_flash_attention 0 means supported
* Update gpu.go
* Add vulkan to Windows Build script
* Remove commented out code
* Enable Vulkan Flash attention in FlashAttentionSupported
* Fix logging
* Update Vulkan backend to e54d41befcc1575f4c898c5ff4ef43970cead75f
* Removed libcap related code
libcap is not directly related to Vulkan and should be added by its own PR. It adds additional library dependencies for building and also requires users to run setcap or run ollama as root, which is not ideal for easy use
* Fix Unit Test (Add Vulkan Library)
* Add vulkan to TestHomogeneousGPUs
Test
* vulkan: get GPU ID (ollama v0.11.5)
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
* disable mmap for vulkan
* Reduce Changes remove TestHomogeneousGPUs (doesn't exist on master)
* Update vulkan version to the version used in llama.cpp
* rename gpu patch to correct number
* added Vulkan API to get correct Device UUID
current UUID from pipelineCacheUUID does not match CUDA
* Fix GPU ID Patch
* Remove Code not in llama.cpp
* modified UUID code inside ggml
* Fix Patch
* Copied minimal definition from vulkan header
* Fix compile error in Mac
Metal is preferred so we're disabling Vulkan for now
* Removed unused code
Fix linter error in CI
* Fix patches apply
* fixing lint error
* Removed unneeded function call
Somehow removing this call fixed the crashing when Vulkan header was removed
* added missing NL
* Fixed missing members in Vulkan header
also added zero clear for some structs
* Fixed wrong structure ID
* Fixed Vulkan header
More aligned with official header definition now
* buildvulkanAsSeperateFunction
* Vulkan on Windows Test
* temporarly comment out gate to run windows task
* use temporarly windows-latest for build
* Commenting out other presets to build vulkan
* reenable cpu
* commenting out error action stop
* temporarly commenting out rocm
* set vulkan path
* comment out cude for faster turnaround
* correct vulkan install
* correct vulkan silent install
* fixed install command
* revert debugging changes (vulkan builds on windows)
* revert windows-latest
* trying to build vulkan for linux
* temporarly disable cuda and rocm
* try again linux build
* fix version
* trying to fix
* trying again
* trying again
* fix version
* fixed vulkan-sdk name
* try again
* trying again
* try without version number
* try again
* add some more extra
* trying to use version 1.4.313
* revert debugging changes
* Filter out already supported gpus
* revert debug code
* Use runners for GPU discovery
This revamps how we discover GPUs in the system by leveraging the Ollama
runner. This should eliminate inconsistency between our GPU discovery and the
runners capabilities at runtime, particularly for cases where we try to filter
out unsupported GPUs. Now the runner does that implicitly based on the actual
device list. In some cases free VRAM reporting can be unreliable which can
leaad to scheduling mistakes, so this also includes a patch to leverage more
reliable VRAM reporting libraries if available.
Automatic workarounds have been removed as only one GPU leveraged this, which
is now documented. This GPU will soon fall off the support matrix with the next
ROCm bump.
Additional cleanup of the scheduler and discovery packages can be done in the
future once we have switched on the new memory management code, and removed
support for the llama runner.
* timing info for runner
* WIP - wire up Vulkan with the new engine based discovery
Not a complete implementation - free VRAM is better, but not accurate on
windows
* fix - trust the library paths from discovery when starting runner
* fix index bug
* fix vulkan ids to be underlying
* fix - give bootstrapping more time on slow systems
* Test if Vulkan device is supported
* vk_check_flash_attention is not needed (coompat2 coopmapt and scalar implementation exist)
* Handle GGML_VK_VISIBLE_DEVICES
* ask for supported first
* win: fix CPU query buffer handling
Try in a short loop until we get the size right.
* test: harden integration tests for slow start
If the server takes a while to start up, block
tests from starting until it's online to avoid
setting large timeouts in individual test cases.
* gofumpt fix
* fix build
* merge fixes
* merge fixes
* fixed build
* merge fixes
* fixing build
* fixed build
* fixed formatting
* fixed build
* fix vulkan gpu id patch
* sync llama.cpp vulkan code
* update build windows script
* merge fixes
* fix format
* fixed vulkan casing
* handle igpu as gpu
* improve case
* print out unknown library
* rturn Vulkan for vulkan library
* Revert "rturn Vulkan for vulkan library"
This reverts commit 690461a12fd5e93295d174c97edefb2bc33285b1.
* fixed patch number
* return Library Name
* remvoe debug code
* return integrated in vulkan backend
* Return pci Properties
* update patch
* directly get pci proeprties without parsing
* workaround for filtering devices. Correct way is to have a LibraryPosition Parameter in the deviceInfo
* Revert "directly get pci proeprties without parsing"
This reverts commit 8e0624851f5ed7d9f74518f574dfb422e4dd4dc2.
* Set FilteredID for Environment Filtering
* ROCm Library is named ROCm
* revert changes in patch
* Create 0028-vulkan-pci-and-memory.patch
* vulkan memory patch
* casing fix
* Add more pci properties
* Added better memory management
* Added better memory managament
* fixed patch
* Fixed patch
* FilterID creation group by library
* filter out vulkan supported by other gpu
* fixing deviceid compare
* Vulkan Fix FA coopmat1 invalid array indexing
* Use everywhere the same Vulkan Version 1.4.321.1
* Remove unneeded patch
* vulkan update
* sync vulkan glsl files
* only use for vulkan the filteredid (numeric device number)
* simplify code
---------
Signed-off-by: Vadim Grinco <vadim@grinco.eu>
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: pufferffish <github@bandersnatch.anonaddy.com>
Co-authored-by: KOISHI KOMEIJI FROM TOUHOU 11 <fuck>
Co-authored-by: DSLstandard <qgeneral35@gmail.com>
Co-authored-by: pufferffish <me@windtfw.com>
Co-authored-by: yeongbba <yeongmo.lee@logpresso.com>
Co-authored-by: tomaThomas <tomathomas@mailbox.org>
Co-authored-by: Antoine Viallon <antoine@lesviallon.fr>
Co-authored-by: Vadim Grinco <vadim@grinco.eu>
Co-authored-by: zeo <108888572+zeozeozeo@users.noreply.github.com>
Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: Daniel Hiltgen <dhiltgen@users.noreply.github.com>
Co-authored-by: Michael Yang <mxyng@pm.me>
Co-authored-by: Parth Sareen <parth.sareen@ollama.com>
Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
Co-authored-by: Jesse Gross <jesse@ollama.com>
Co-authored-by: Nikita <50599445+nasrally@users.noreply.github.com>
Co-authored-by: Masato Nakasaka <masato.nakasaka@intel.com>
Co-authored-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Co-authored-by: Daniel Hiltgen <daniel@ollama.com>
2025-10-15 01:59:58 +08:00
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_GPU,
|
|
|
|
|
C.GGML_BACKEND_DEVICE_TYPE_IGPU:
|
2025-05-30 03:21:48 +08:00
|
|
|
slog.Info("offloading output layer to GPU")
|
|
|
|
|
gpuLayers++
|
|
|
|
|
case C.GGML_BACKEND_DEVICE_TYPE_ACCEL:
|
|
|
|
|
slog.Info("offloading output layer to ACCEL")
|
|
|
|
|
}
|
|
|
|
|
slog.Info(fmt.Sprintf("offloaded %d/%d layers to GPU", gpuLayers, len(b.layers)+1))
|
|
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
var doneBytes atomic.Uint64
|
2025-04-18 04:42:40 +08:00
|
|
|
totalBytes := uint64(b.meta.Length) - b.meta.Tensors().Offset
|
2025-03-20 04:03:16 +08:00
|
|
|
|
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
|
g.SetLimit(runtime.GOMAXPROCS(0))
|
2025-04-18 04:42:40 +08:00
|
|
|
for _, t := range b.meta.Tensors().Items() {
|
2025-05-07 02:20:48 +08:00
|
|
|
t := t
|
2025-03-20 04:03:16 +08:00
|
|
|
g.Go(func() error {
|
2025-04-18 04:42:40 +08:00
|
|
|
tts := make([]*C.struct_ggml_tensor, max(1, len(b.tensorLoadTargets[t.Name])))
|
2025-03-20 04:03:16 +08:00
|
|
|
for i := range tts {
|
2025-04-18 04:42:40 +08:00
|
|
|
target := b.tensorLoadTargets[t.Name][i]
|
2025-02-25 07:48:42 +08:00
|
|
|
if target == "" {
|
|
|
|
|
target = t.Name
|
|
|
|
|
}
|
2025-02-20 06:26:40 +08:00
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
tt, ok := b.tensors[target]
|
2025-02-25 07:48:42 +08:00
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("unassigned tensor: %s", t.Name)
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
tts[i] = tt
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-05 08:04:24 +08:00
|
|
|
// Create a new FD for each goroutine so that each FD is read sequentially, rather than
|
|
|
|
|
// seeking around within an FD shared between all goroutines.
|
2025-04-18 04:42:40 +08:00
|
|
|
file, err := os.Open(b.modelPath)
|
2025-04-05 08:04:24 +08:00
|
|
|
if err != nil {
|
2025-04-18 04:42:40 +08:00
|
|
|
slog.Warn("file open error", "file", b.modelPath, "error", err)
|
2025-04-05 08:04:24 +08:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
2025-04-18 04:42:40 +08:00
|
|
|
sr := io.NewSectionReader(file, int64(b.meta.Tensors().Offset+t.Offset), int64(t.Size()))
|
2025-08-15 05:42:58 +08:00
|
|
|
|
|
|
|
|
if t.Kind == 4 && tts[0]._type == 39 {
|
|
|
|
|
// source is mxfp4, target is ggml mxfp4
|
|
|
|
|
|
|
|
|
|
const BS = 17 // MXFP4 block size
|
|
|
|
|
bts := make([]byte, 8*BS*format.KibiByte) // ~128k block aligned
|
|
|
|
|
var s uint64
|
2025-08-27 07:41:02 +08:00
|
|
|
var tmp [16]byte
|
2025-08-15 05:42:58 +08:00
|
|
|
for s < t.Size() {
|
|
|
|
|
// Stop if either the parent context has been canceled or if any of the other tensors returned an error
|
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
n, err := io.ReadFull(sr, bts[:min(len(bts), int(t.Size()-s))])
|
|
|
|
|
if err != nil {
|
|
|
|
|
slog.Warn("file read error", "file", b.modelPath, "error", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for j := range n / BS {
|
|
|
|
|
for i := 1; i < 9; i++ {
|
2025-08-27 07:41:02 +08:00
|
|
|
// transform a1b2c3 ... x7y8z9 -> 71xa82yb93zc
|
|
|
|
|
a, b := bts[j*BS+i], bts[j*BS+i+8]
|
|
|
|
|
tmp[2*(i-1)] = (a & 0x0F) | (b << 4)
|
|
|
|
|
tmp[2*(i-1)+1] = (a >> 4) | (b & 0xF0)
|
2025-08-15 05:42:58 +08:00
|
|
|
}
|
2025-08-27 07:41:02 +08:00
|
|
|
copy(bts[j*BS+1:j*BS+17], tmp[:])
|
2025-08-15 05:42:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tts {
|
|
|
|
|
C.ggml_backend_tensor_set(tt, unsafe.Pointer(&bts[0]), C.size_t(s), C.size_t(n))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s += uint64(n)
|
|
|
|
|
|
|
|
|
|
if progress != nil {
|
|
|
|
|
done := doneBytes.Add(uint64(n))
|
|
|
|
|
progress(float32(done) / float32(totalBytes))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
} else if strings.HasSuffix(t.Name, "_exps.bias") && t.Kind == 30 && tts[0]._type == 0 {
|
|
|
|
|
// source is bf16, target is ggml fp32
|
|
|
|
|
|
|
|
|
|
// data is bf16 but we need to convert to fp32
|
|
|
|
|
bts := make([]byte, 128*format.KibiByte)
|
|
|
|
|
var e uint64
|
|
|
|
|
for e < t.Elements() {
|
|
|
|
|
// Stop if either the parent context has been canceled or if any of the other tensors returned an error
|
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
n, err := io.ReadFull(sr, bts[:min(len(bts), int(t.Elements()-e)*2)])
|
|
|
|
|
if err != nil {
|
|
|
|
|
slog.Warn("file read error", "file", b.modelPath, "error", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
fp32 := ConvertToF32(bts, uint32(fsggml.TensorTypeBF16), uint64(n/2))
|
|
|
|
|
|
|
|
|
|
for _, tt := range tts {
|
|
|
|
|
C.ggml_backend_tensor_set(tt, unsafe.Pointer(&fp32[0]), C.size_t(e*4), C.size_t(n*2))
|
|
|
|
|
}
|
|
|
|
|
e += uint64(n / 2)
|
|
|
|
|
if progress != nil {
|
|
|
|
|
done := doneBytes.Add(uint64(n))
|
|
|
|
|
progress(float32(done) / float32(totalBytes))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
bts := make([]byte, 128*format.KibiByte)
|
|
|
|
|
|
|
|
|
|
var s uint64
|
|
|
|
|
for s < t.Size() {
|
2025-05-02 08:06:53 +08:00
|
|
|
// Stop if either the parent context has been canceled or if any of the other tensors returned an error
|
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
n, err := io.ReadFull(sr, bts[:min(len(bts), int(t.Size()-s))])
|
|
|
|
|
if err != nil {
|
2025-04-18 04:42:40 +08:00
|
|
|
slog.Warn("file read error", "file", b.modelPath, "error", err)
|
2025-03-20 04:03:16 +08:00
|
|
|
return err
|
2025-02-25 07:48:42 +08:00
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
for _, tt := range tts {
|
|
|
|
|
C.ggml_backend_tensor_set(tt, unsafe.Pointer(&bts[0]), C.size_t(s), C.size_t(n))
|
2025-02-25 07:48:42 +08:00
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-03-20 04:03:16 +08:00
|
|
|
s += uint64(n)
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
if progress != nil {
|
2025-03-20 04:03:16 +08:00
|
|
|
done := doneBytes.Add(uint64(n))
|
2025-04-18 04:42:40 +08:00
|
|
|
progress(float32(done) / float32(totalBytes))
|
2025-03-20 04:03:16 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
})
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-27 05:17:43 +08:00
|
|
|
// Cleanup any backend state from devices that we didn't end up using
|
|
|
|
|
nextDevice:
|
|
|
|
|
for _, d := range append(gpus, append(accels, cpus...)...) {
|
|
|
|
|
for _, backend := range b.schedBackends {
|
|
|
|
|
if d == C.ggml_backend_get_device(backend) {
|
|
|
|
|
continue nextDevice
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C.ggml_backend_dev_reset(d)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 07:51:33 +08:00
|
|
|
if err := g.Wait(); err != nil {
|
2025-04-18 04:42:40 +08:00
|
|
|
return err
|
2025-02-19 08:52:29 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 04:42:40 +08:00
|
|
|
return nil
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
func (b *Backend) BackendMemory() ml.BackendMemory {
|
|
|
|
|
return *b.requiredMemory
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 05:38:44 +08:00
|
|
|
func (b *Backend) Config() fs.Config {
|
2025-02-14 08:31:21 +08:00
|
|
|
return b.meta.KV()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Backend) Get(name string) ml.Tensor {
|
2025-02-20 06:26:40 +08:00
|
|
|
if t, ok := b.tensors[name]; ok {
|
|
|
|
|
return &Tensor{b: b, t: t}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Backend) NewContext() ml.Context {
|
2025-03-05 05:06:56 +08:00
|
|
|
return b.NewContextSize(b.maxGraphNodes)
|
2025-02-26 08:06:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Backend) NewContextSize(n int) ml.Context {
|
2025-03-08 03:19:03 +08:00
|
|
|
if n > b.maxGraphNodes {
|
|
|
|
|
panic(fmt.Errorf("requested number of graph nodes (%v) for new context exceeds maximum (%v)", n, b.maxGraphNodes))
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 02:39:08 +08:00
|
|
|
var allocatedBuffers []C.ggml_backend_buffer_t
|
2025-04-09 03:11:55 +08:00
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
return &Context{
|
2025-03-06 06:48:27 +08:00
|
|
|
b: b,
|
|
|
|
|
maxGraphNodes: n,
|
2025-02-20 06:26:40 +08:00
|
|
|
ctx: C.ggml_init(C.struct_ggml_init_params{
|
2025-02-26 08:06:32 +08:00
|
|
|
mem_size: C.size_t(n)*C.ggml_tensor_overhead() + C.ggml_graph_overhead_custom(C.size_t(n), false),
|
2025-02-20 06:26:40 +08:00
|
|
|
no_alloc: true,
|
|
|
|
|
}),
|
2025-04-09 03:11:55 +08:00
|
|
|
allocatedBuffers: &allocatedBuffers,
|
2025-04-18 02:00:25 +08:00
|
|
|
layer: -1,
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 13:34:10 +08:00
|
|
|
func (b *Backend) CacheConfig() ml.CacheConfig {
|
2025-02-26 09:24:36 +08:00
|
|
|
if b.flashAttention {
|
|
|
|
|
return ml.CacheConfig{CachePadding: 256, MaskDType: ml.DTypeF16, MaskBatchPadding: C.GGML_KQ_MASK_PAD}
|
|
|
|
|
} else {
|
|
|
|
|
return ml.CacheConfig{CachePadding: 32, PermutedV: true}
|
|
|
|
|
}
|
2025-02-23 13:34:10 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-02 06:12:32 +08:00
|
|
|
func (b *Backend) BackendDevices() []ml.DeviceInfo {
|
|
|
|
|
deviceInfos := []ml.DeviceInfo{}
|
|
|
|
|
for _, dev := range gpus {
|
|
|
|
|
// If we have a model loaded, and it's only loaded on a subset of the devices
|
|
|
|
|
// skip idle/unused devices to avoid initializing them and causing VRAM allocations
|
|
|
|
|
if b.allocMemory {
|
|
|
|
|
idleDev := true
|
|
|
|
|
for _, backend := range b.schedBackends {
|
|
|
|
|
if dev == C.ggml_backend_get_device(backend) {
|
|
|
|
|
idleDev = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if idleDev {
|
|
|
|
|
slog.Debug("skipping unused backend device", "description", C.GoString(C.ggml_backend_dev_description(dev)))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info := ml.DeviceInfo{}
|
|
|
|
|
props := C.struct_ggml_backend_dev_props{}
|
|
|
|
|
C.ggml_backend_dev_get_props(dev, &props)
|
|
|
|
|
info.Name = C.GoString(props.name)
|
|
|
|
|
info.Description = C.GoString(props.description)
|
|
|
|
|
info.ID = C.GoString(props.id)
|
|
|
|
|
info.Library = C.GoString(props.library)
|
|
|
|
|
info.ComputeMajor = (int)(props.compute_major)
|
|
|
|
|
info.ComputeMinor = (int)(props.compute_minor)
|
|
|
|
|
info.DriverMajor = (int)(props.driver_major)
|
|
|
|
|
info.DriverMinor = (int)(props.driver_minor)
|
|
|
|
|
info.Integrated = props.integrated != 0
|
|
|
|
|
if props.library != nil {
|
|
|
|
|
info.Library = C.GoString(props.library)
|
|
|
|
|
}
|
|
|
|
|
info.PCIID = fmt.Sprintf("%02x:%02x.%x", props.pci_bus_id, props.pci_device_id, props.pci_domain_id)
|
|
|
|
|
info.LibraryPath = ggml.LibPaths()
|
2025-10-17 00:07:35 +08:00
|
|
|
if props.numeric_id != nil {
|
|
|
|
|
info.FilteredID = C.GoString(props.numeric_id)
|
|
|
|
|
}
|
2025-10-02 06:12:32 +08:00
|
|
|
|
|
|
|
|
C.ggml_backend_dev_memory(dev, &props.memory_free, &props.memory_total)
|
|
|
|
|
info.TotalMemory = (uint64)(props.memory_total)
|
|
|
|
|
info.FreeMemory = (uint64)(props.memory_free)
|
|
|
|
|
|
|
|
|
|
deviceInfos = append(deviceInfos, info)
|
|
|
|
|
}
|
|
|
|
|
return deviceInfos
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
type Context struct {
|
2025-02-20 06:26:40 +08:00
|
|
|
b *Backend
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-02-20 06:26:40 +08:00
|
|
|
ctx *C.struct_ggml_context
|
2025-02-14 08:31:21 +08:00
|
|
|
graph *C.struct_ggml_cgraph
|
2025-02-26 08:06:32 +08:00
|
|
|
|
2025-03-06 06:48:27 +08:00
|
|
|
// buft is the buffer type used for new tensors
|
2025-08-07 02:39:08 +08:00
|
|
|
buft C.ggml_backend_buffer_type_t
|
2025-02-20 06:26:40 +08:00
|
|
|
|
2025-04-09 03:11:55 +08:00
|
|
|
// allocatedBuffers are buffers for tensors that we have allocated in this context
|
|
|
|
|
// so that we can free them when we close the context
|
2025-08-07 02:39:08 +08:00
|
|
|
allocatedBuffers *[]C.ggml_backend_buffer_t
|
2025-04-09 03:11:55 +08:00
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
// maxGraphNodes is the maximum allowed number of graph nodes in this context
|
2025-02-26 04:57:49 +08:00
|
|
|
maxGraphNodes int
|
2025-04-18 02:00:25 +08:00
|
|
|
|
|
|
|
|
// layer is the graph layer that this context is allocating for - assumed to be cache
|
|
|
|
|
layer int
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) Input() ml.Context {
|
2025-03-05 05:06:56 +08:00
|
|
|
if c.b.input != nil {
|
2025-02-26 08:06:32 +08:00
|
|
|
return &Context{
|
2025-04-09 03:11:55 +08:00
|
|
|
b: c.b,
|
|
|
|
|
ctx: c.ctx,
|
|
|
|
|
buft: c.b.input,
|
|
|
|
|
allocatedBuffers: c.allocatedBuffers,
|
|
|
|
|
maxGraphNodes: c.maxGraphNodes,
|
2025-04-18 02:00:25 +08:00
|
|
|
layer: -1,
|
2025-02-26 08:06:32 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
return c
|
2025-02-26 08:06:32 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) Layer(i int) ml.Context {
|
2025-05-30 03:21:48 +08:00
|
|
|
if layer, ok := c.b.layers[i]; ok {
|
2025-02-26 08:06:32 +08:00
|
|
|
return &Context{
|
2025-04-09 03:11:55 +08:00
|
|
|
b: c.b,
|
|
|
|
|
ctx: c.ctx,
|
2025-05-30 03:21:48 +08:00
|
|
|
buft: layer.bt,
|
2025-04-09 03:11:55 +08:00
|
|
|
allocatedBuffers: c.allocatedBuffers,
|
|
|
|
|
maxGraphNodes: c.maxGraphNodes,
|
2025-04-18 02:00:25 +08:00
|
|
|
layer: i,
|
2025-02-26 08:06:32 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
return c
|
2025-02-26 08:06:32 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-22 03:57:08 +08:00
|
|
|
func (c *Context) Forward(tensors ...ml.Tensor) ml.Context {
|
2025-02-14 08:31:21 +08:00
|
|
|
if c.graph == nil {
|
2025-02-26 04:57:49 +08:00
|
|
|
c.graph = C.ggml_new_graph_custom(c.ctx, C.size_t(c.maxGraphNodes), false)
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-22 03:57:08 +08:00
|
|
|
for _, tensor := range tensors {
|
|
|
|
|
C.ggml_build_forward_expand(c.graph, tensor.(*Tensor).t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) Compute(tensors ...ml.Tensor) {
|
2025-08-30 05:20:28 +08:00
|
|
|
c.ComputeWithNotify(nil, tensors...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Context) ComputeWithNotify(cb func(), tensors ...ml.Tensor) {
|
|
|
|
|
c.b.schedMu.Lock()
|
|
|
|
|
defer c.b.schedMu.Unlock()
|
|
|
|
|
if cb != nil {
|
|
|
|
|
go cb()
|
|
|
|
|
}
|
2025-06-20 05:39:20 +08:00
|
|
|
if status := C.ggml_backend_sched_graph_compute_async(c.b.sched, c.graph); status != C.GGML_STATUS_SUCCESS {
|
|
|
|
|
panic(fmt.Errorf("error computing ggml graph: %v", status))
|
|
|
|
|
}
|
2025-03-05 05:06:56 +08:00
|
|
|
C.ggml_backend_sched_reset(c.b.sched)
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-02-06 05:18:36 +08:00
|
|
|
needSync := true
|
|
|
|
|
sync := func() {
|
|
|
|
|
if needSync {
|
2025-02-19 08:52:29 +08:00
|
|
|
C.ggml_backend_sched_synchronize(c.b.sched)
|
2025-02-06 05:18:36 +08:00
|
|
|
needSync = false
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
|
2025-02-06 05:18:36 +08:00
|
|
|
for _, t := range tensors {
|
|
|
|
|
if C.ggml_nbytes(t.(*Tensor).t) > 0 {
|
|
|
|
|
t.(*Tensor).sync = sync
|
2025-02-04 11:35:12 +08:00
|
|
|
}
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
func (c *Context) Reserve() {
|
|
|
|
|
reserved := C.ggml_backend_sched_reserve(c.b.sched, c.graph)
|
2025-04-04 03:50:20 +08:00
|
|
|
|
|
|
|
|
slog.Debug("compute graph", "nodes", C.ggml_graph_n_nodes(c.graph), "splits", C.ggml_backend_sched_get_n_splits(c.b.sched))
|
2025-04-18 02:00:25 +08:00
|
|
|
|
|
|
|
|
// Reserve may get called multiple times for different graphs - we just want the last run, which will contain the max allocations
|
|
|
|
|
for _, bt := range c.b.schedBufts {
|
2025-09-23 08:27:03 +08:00
|
|
|
c.b.btDeviceMemory[bt].Graph = 0
|
2025-04-18 02:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-04 03:50:20 +08:00
|
|
|
for i := range c.b.schedBackends {
|
2025-09-23 08:27:03 +08:00
|
|
|
bufferSize := C.ggml_backend_sched_get_attempted_buffer_size(c.b.sched, c.b.schedBackends[i])
|
|
|
|
|
c.b.btDeviceMemory[c.b.schedBufts[i]].Graph += uint64(bufferSize)
|
2025-04-18 02:00:25 +08:00
|
|
|
|
2025-09-03 04:09:12 +08:00
|
|
|
logutil.Trace("compute graph", "backend", C.GoString(C.ggml_backend_name(c.b.schedBackends[i])),
|
2025-09-23 08:27:03 +08:00
|
|
|
"buffer_type", C.GoString(C.ggml_backend_buft_name(c.b.schedBufts[i])), "size", format.HumanBytes2(uint64(bufferSize)))
|
2025-04-04 03:50:20 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
if !reserved {
|
|
|
|
|
panic(ml.ErrNoMem{BackendMemory: *c.b.requiredMemory})
|
|
|
|
|
}
|
2025-04-04 03:50:20 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) MaxGraphNodes() int {
|
2025-02-26 04:57:49 +08:00
|
|
|
return c.maxGraphNodes
|
2024-12-18 11:59:41 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func shapeToGGML(shape []int) *C.int64_t {
|
|
|
|
|
sh := make([]C.int64_t, len(shape))
|
|
|
|
|
for i, s := range shape {
|
2025-02-20 06:26:40 +08:00
|
|
|
sh[i] = C.int64_t(s)
|
2025-02-04 09:21:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &sh[0]
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 09:20:54 +08:00
|
|
|
func pad(length, pad C.size_t) C.size_t {
|
|
|
|
|
return ((length + pad - 1) / pad) * pad
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
func (c *Context) newTensor(dtype ml.DType, shape []int) *Tensor {
|
2025-03-06 06:48:27 +08:00
|
|
|
if c.buft == nil {
|
2025-04-05 06:04:25 +08:00
|
|
|
panic("set Input or Layer before creating tensors")
|
2025-03-06 06:48:27 +08:00
|
|
|
}
|
|
|
|
|
|
2025-08-20 00:52:18 +08:00
|
|
|
cdtype := ggmlDType(dtype)
|
2025-03-05 05:06:56 +08:00
|
|
|
|
2025-03-08 03:19:03 +08:00
|
|
|
if len(shape) < 1 || shape[0] == 0 {
|
2025-03-05 05:06:56 +08:00
|
|
|
var shape C.int64_t = 0
|
2025-04-18 02:00:25 +08:00
|
|
|
return &Tensor{b: c.b, t: C.ggml_new_tensor(c.ctx, cdtype, 1, &shape)}
|
2025-03-05 05:06:56 +08:00
|
|
|
} else if len(shape) > 4 {
|
2025-02-14 08:31:21 +08:00
|
|
|
panic("unsupported number of dimensions")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, dim := range shape {
|
|
|
|
|
if dim < 1 {
|
|
|
|
|
panic("invalid shape")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
t := C.ggml_new_tensor(c.ctx, cdtype, C.int(len(shape)), shapeToGGML(shape))
|
2025-03-08 09:20:54 +08:00
|
|
|
size := pad(C.ggml_backend_buft_get_alloc_size(c.buft, t), C.ggml_backend_buft_get_alignment(c.buft))
|
2025-04-18 02:00:25 +08:00
|
|
|
|
2025-03-08 09:20:54 +08:00
|
|
|
b := C.ggml_backend_buft_alloc_buffer(c.buft, size)
|
2025-04-18 02:00:25 +08:00
|
|
|
if c.layer >= 0 {
|
2025-09-23 08:27:03 +08:00
|
|
|
c.b.btDeviceMemory[c.buft].Cache[c.layer] += uint64(size)
|
2025-04-18 02:00:25 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-05 06:04:25 +08:00
|
|
|
if b == nil {
|
2025-04-18 02:00:25 +08:00
|
|
|
panic(ml.ErrNoMem{BackendMemory: *c.b.requiredMemory})
|
2025-04-05 06:04:25 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
*c.allocatedBuffers = append(*c.allocatedBuffers, b)
|
2025-02-14 08:31:21 +08:00
|
|
|
C.ggml_backend_tensor_alloc(b, t, C.ggml_backend_buffer_get_base(b))
|
2025-04-18 02:00:25 +08:00
|
|
|
return &Tensor{b: c.b, t: t}
|
2025-03-01 09:48:07 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) Empty(dtype ml.DType, shape ...int) ml.Tensor {
|
2025-04-18 02:00:25 +08:00
|
|
|
return c.newTensor(dtype, shape)
|
2025-03-01 09:48:07 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 07:06:06 +08:00
|
|
|
func (c *Context) Zeros(dtype ml.DType, shape ...int) ml.Tensor {
|
2025-04-18 02:00:25 +08:00
|
|
|
t := c.newTensor(dtype, shape)
|
2025-05-30 03:21:48 +08:00
|
|
|
if c.b.allocMemory {
|
2025-10-29 03:08:49 +08:00
|
|
|
C.ggml_set_zero(t.t)
|
2025-05-30 03:21:48 +08:00
|
|
|
}
|
2025-02-20 06:26:40 +08:00
|
|
|
return t
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 01:43:56 +08:00
|
|
|
func checkShape[S ~[]E, E any](s S, shape ...int) {
|
2025-02-14 08:31:21 +08:00
|
|
|
n := len(s)
|
2025-03-08 03:19:03 +08:00
|
|
|
|
|
|
|
|
if n == 0 {
|
2025-05-20 01:43:56 +08:00
|
|
|
return
|
2025-03-08 03:19:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
for _, v := range shape {
|
|
|
|
|
n /= v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n != 1 {
|
2025-05-20 01:43:56 +08:00
|
|
|
panic(fmt.Errorf("invalid shape: %v", shape))
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
func (c Context) FromBytes(dtype ml.DType, s []uint8, shape ...int) ml.Tensor {
|
|
|
|
|
// Unchecked to handle quantized types
|
|
|
|
|
t := c.newTensor(dtype, shape)
|
|
|
|
|
if c.b.allocMemory {
|
|
|
|
|
t.FromBytes(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Context) FromFloats(s []float32, shape ...int) ml.Tensor {
|
2025-05-20 01:43:56 +08:00
|
|
|
checkShape(s, shape...)
|
2025-02-26 08:06:32 +08:00
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
t := c.newTensor(ml.DTypeF32, shape)
|
2025-04-05 06:04:25 +08:00
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
if c.b.allocMemory {
|
|
|
|
|
t.FromFloats(s)
|
2025-03-08 03:19:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 01:43:56 +08:00
|
|
|
return t
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
func (c *Context) FromInts(s []int32, shape ...int) ml.Tensor {
|
2025-05-20 01:43:56 +08:00
|
|
|
checkShape(s, shape...)
|
2025-02-26 08:06:32 +08:00
|
|
|
|
2025-04-18 02:00:25 +08:00
|
|
|
t := c.newTensor(ml.DTypeI32, shape)
|
2025-10-29 03:08:49 +08:00
|
|
|
if c.b.allocMemory {
|
|
|
|
|
t.FromInts(s)
|
2025-03-08 03:19:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 01:43:56 +08:00
|
|
|
return t
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-04 01:25:23 +08:00
|
|
|
func (c Context) Arange(start, stop, step float32, dtype ml.DType) ml.Tensor {
|
|
|
|
|
switch dtype {
|
|
|
|
|
case ml.DTypeF32:
|
|
|
|
|
// ggml_arange creates a float32 tensor
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: c.b,
|
|
|
|
|
t: C.ggml_arange(c.ctx, C.float(start), C.float(stop), C.float(step)),
|
|
|
|
|
}
|
|
|
|
|
case ml.DTypeI32:
|
|
|
|
|
// ggml_cast does not support float32 to int32 conversion
|
|
|
|
|
arange := make([]int32, 0, int((stop-start)/step))
|
|
|
|
|
for i := start; i < stop; i += step {
|
|
|
|
|
arange = append(arange, int32(i))
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
return c.Input().FromInts(arange, len(arange))
|
2025-04-04 01:25:23 +08:00
|
|
|
default:
|
|
|
|
|
panic("unsupported dtype for arange")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-05 05:06:56 +08:00
|
|
|
func (c *Context) Close() {
|
|
|
|
|
if c != nil {
|
2025-04-09 03:11:55 +08:00
|
|
|
for _, b := range *c.allocatedBuffers {
|
|
|
|
|
C.ggml_backend_buffer_free(b)
|
|
|
|
|
}
|
|
|
|
|
*c.allocatedBuffers = nil
|
|
|
|
|
|
2025-02-11 02:36:00 +08:00
|
|
|
C.ggml_free(c.ctx)
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Tensor struct {
|
2025-02-28 06:52:39 +08:00
|
|
|
b *Backend
|
2025-02-14 08:31:21 +08:00
|
|
|
t *C.struct_ggml_tensor
|
2025-02-06 05:18:36 +08:00
|
|
|
sync func()
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) LogValue() slog.Value {
|
|
|
|
|
return slog.GroupValue(
|
|
|
|
|
slog.String("name", C.GoString(C.ggml_get_name(t.t))),
|
|
|
|
|
slog.String("type", C.GoString(C.ggml_type_name(t.t._type))),
|
|
|
|
|
slog.Any("shape", t.Shape()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func (t *Tensor) Dim(n int) int {
|
|
|
|
|
return int(t.t.ne[n])
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func (t *Tensor) Stride(n int) int {
|
|
|
|
|
return int(t.t.nb[n])
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func (t *Tensor) Shape() []int {
|
|
|
|
|
shape := make([]int, C.ggml_n_dims(t.t))
|
2025-02-14 08:31:21 +08:00
|
|
|
for i := range shape {
|
|
|
|
|
shape[i] = t.Dim(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return shape
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 05:18:36 +08:00
|
|
|
func (t *Tensor) Bytes() (data []byte) {
|
|
|
|
|
if t.sync != nil {
|
|
|
|
|
data = make([]byte, C.ggml_nbytes(t.t))
|
|
|
|
|
|
|
|
|
|
t.sync()
|
|
|
|
|
C.ggml_backend_tensor_get(t.t, unsafe.Pointer(&data[0]), 0, C.ggml_nbytes(t.t))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-06 05:18:36 +08:00
|
|
|
func (t *Tensor) Floats() (data []float32) {
|
|
|
|
|
if t.sync != nil {
|
|
|
|
|
data = make([]float32, C.ggml_nelements(t.t))
|
|
|
|
|
|
|
|
|
|
t.sync()
|
|
|
|
|
C.ggml_backend_tensor_get(t.t, unsafe.Pointer(&data[0]), 0, C.ggml_nbytes(t.t))
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 03:08:49 +08:00
|
|
|
func tensorSet[S ~[]E, E byte | float32 | int32](t *Tensor, s S) {
|
|
|
|
|
if len(s) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if int(C.ggml_nbytes(t.t)) != len(s)*binary.Size(s[0]) {
|
|
|
|
|
panic("data size does not match tensor size")
|
2025-08-30 05:20:28 +08:00
|
|
|
}
|
2025-10-29 03:08:49 +08:00
|
|
|
C.ggml_backend_tensor_set(t.t, unsafe.Pointer(&s[0]), 0, C.ggml_nbytes(t.t))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) FromBytes(s []byte) {
|
|
|
|
|
tensorSet(t, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) FromFloats(s []float32) {
|
|
|
|
|
tensorSet(t, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) FromInts(s []int32) {
|
|
|
|
|
tensorSet(t, s)
|
2025-08-30 05:20:28 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) DType() ml.DType {
|
|
|
|
|
switch t.t._type {
|
|
|
|
|
case C.GGML_TYPE_F32:
|
|
|
|
|
return ml.DTypeF32
|
2024-12-18 11:59:41 +08:00
|
|
|
case C.GGML_TYPE_F16:
|
|
|
|
|
return ml.DTypeF16
|
2025-02-22 12:54:14 +08:00
|
|
|
case C.GGML_TYPE_Q8_0:
|
|
|
|
|
return ml.DTypeQ80
|
|
|
|
|
case C.GGML_TYPE_Q4_0:
|
|
|
|
|
return ml.DTypeQ40
|
2025-02-14 08:31:21 +08:00
|
|
|
case C.GGML_TYPE_I32:
|
|
|
|
|
return ml.DTypeI32
|
2025-08-06 03:21:16 +08:00
|
|
|
case C.GGML_TYPE_MXFP4:
|
|
|
|
|
return ml.DTypeMXFP4
|
2025-02-14 08:31:21 +08:00
|
|
|
default:
|
|
|
|
|
return ml.DTypeOther
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 00:52:18 +08:00
|
|
|
func ggmlDType(dtype ml.DType) uint32 {
|
|
|
|
|
switch dtype {
|
|
|
|
|
case ml.DTypeF32:
|
|
|
|
|
return C.GGML_TYPE_F32
|
|
|
|
|
case ml.DTypeF16:
|
|
|
|
|
return C.GGML_TYPE_F16
|
|
|
|
|
case ml.DTypeQ80:
|
|
|
|
|
return C.GGML_TYPE_Q8_0
|
|
|
|
|
case ml.DTypeQ40:
|
|
|
|
|
return C.GGML_TYPE_Q4_0
|
|
|
|
|
case ml.DTypeI32:
|
|
|
|
|
return C.GGML_TYPE_I32
|
|
|
|
|
case ml.DTypeMXFP4:
|
|
|
|
|
return C.GGML_TYPE_MXFP4
|
|
|
|
|
default:
|
|
|
|
|
panic("unsupported dtype")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Cast(ctx ml.Context, dtype ml.DType) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cast(ctx.(*Context).ctx, t.t, ggmlDType(dtype)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 07:56:32 +08:00
|
|
|
func (t *Tensor) Neg(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_neg(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Add(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_add(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 12:47:09 +08:00
|
|
|
func (t *Tensor) Sub(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sub(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 07:56:32 +08:00
|
|
|
func (t *Tensor) Repeat(ctx ml.Context, dim, n int) ml.Tensor {
|
|
|
|
|
if dim < 0 || dim >= C.GGML_MAX_DIMS {
|
|
|
|
|
panic("invalid dimension")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape := make([]C.int64_t, C.GGML_MAX_DIMS)
|
|
|
|
|
for i := range C.GGML_MAX_DIMS {
|
|
|
|
|
if i == dim {
|
|
|
|
|
shape[i] = C.int64_t(t.Dim(i) * n)
|
|
|
|
|
} else {
|
|
|
|
|
shape[i] = C.int64_t(t.Dim(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmpl := C.ggml_new_tensor(ctx.(*Context).ctx, t.t._type, C.int(len(shape)), unsafe.SliceData(shape))
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_repeat(ctx.(*Context).ctx, t.t, tmpl),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Stack(ctx ml.Context, dim int, s ...ml.Tensor) ml.Tensor {
|
|
|
|
|
if len(s) > 0 {
|
|
|
|
|
return t.Concat(ctx, s[0].Stack(ctx, dim, s[1:]...), dim)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Concat(ctx ml.Context, t2 ml.Tensor, dim int) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_concat(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.int(dim)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 03:21:16 +08:00
|
|
|
func (t *Tensor) Contiguous(ctx ml.Context, shape ...int) ml.Tensor {
|
|
|
|
|
switch len(shape) {
|
|
|
|
|
case 0:
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cont(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
case 1:
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cont_1d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0])),
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cont_2d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1])),
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cont_3d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1]), C.int64_t(shape[2])),
|
|
|
|
|
}
|
|
|
|
|
case 4:
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cont_4d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1]), C.int64_t(shape[2]), C.int64_t(shape[3])),
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic("unsupported number of dimensions")
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Mul(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_mul(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 01:21:07 +08:00
|
|
|
func (t *Tensor) Div(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_div(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Mulmat(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_mul_mat(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 02:01:14 +08:00
|
|
|
func (t *Tensor) MulmatFullPrec(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
mul := C.ggml_mul_mat(ctx.(*Context).ctx, t.t, t2.(*Tensor).t)
|
|
|
|
|
C.ggml_mul_mat_set_prec(mul, C.GGML_PREC_F32)
|
|
|
|
|
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 02:01:14 +08:00
|
|
|
t: mul,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-04 06:18:29 +08:00
|
|
|
func (t *Tensor) MulmatID(ctx ml.Context, t2, ids ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_mul_mat_id(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, ids.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 05:42:58 +08:00
|
|
|
func (t *Tensor) AddID(ctx ml.Context, t2, ids ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_add_id(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, ids.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-16 06:35:59 +08:00
|
|
|
func (t *Tensor) L2Norm(ctx ml.Context, eps float32) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_l2_norm(ctx.(*Context).ctx, t.t, C.float(eps)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) LayerNorm(ctx ml.Context, w, b ml.Tensor, eps float32) ml.Tensor {
|
2025-04-04 06:18:29 +08:00
|
|
|
tt := C.ggml_norm(ctx.(*Context).ctx, t.t, C.float(eps))
|
|
|
|
|
if w != nil {
|
|
|
|
|
tt = C.ggml_mul(ctx.(*Context).ctx, tt, w.(*Tensor).t)
|
|
|
|
|
if b != nil {
|
|
|
|
|
tt = C.ggml_add(ctx.(*Context).ctx, tt, b.(*Tensor).t)
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-04 06:18:29 +08:00
|
|
|
return &Tensor{b: t.b, t: tt}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) RMSNorm(ctx ml.Context, w ml.Tensor, eps float32) ml.Tensor {
|
2025-04-04 06:18:29 +08:00
|
|
|
tt := C.ggml_rms_norm(ctx.(*Context).ctx, t.t, C.float(eps))
|
|
|
|
|
if w != nil {
|
|
|
|
|
tt = C.ggml_mul(ctx.(*Context).ctx, tt, w.(*Tensor).t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Tensor{b: t.b, t: tt}
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func (t *Tensor) Pad(ctx ml.Context, shape ...int) ml.Tensor {
|
2025-02-14 08:31:21 +08:00
|
|
|
if len(shape) != 4 {
|
|
|
|
|
panic("expected 4 dimensions")
|
2025-05-16 04:44:44 +08:00
|
|
|
} else if shape[3] != 0 {
|
|
|
|
|
panic("cuda does not support 4d tensors")
|
2025-02-14 08:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_pad(ctx.(*Context).ctx, t.t, C.int(shape[0]), C.int(shape[1]), C.int(shape[2]), C.int(shape[3])),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Permute(ctx ml.Context, shape ...int) ml.Tensor {
|
|
|
|
|
if len(shape) != 4 {
|
|
|
|
|
panic("expected 4 dimensions")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_permute(ctx.(*Context).ctx, t.t, C.int(shape[0]), C.int(shape[1]), C.int(shape[2]), C.int(shape[3])),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Rows(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_get_rows(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Copy(ctx ml.Context, t2 ml.Tensor) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_cpy(ctx.(*Context).ctx, t.t, t2.(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-04 09:21:57 +08:00
|
|
|
func (t *Tensor) Reshape(ctx ml.Context, shape ...int) ml.Tensor {
|
2025-02-14 08:31:21 +08:00
|
|
|
switch len(shape) {
|
|
|
|
|
case 1:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_reshape_1d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0])),
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_reshape_2d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1])),
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_reshape_3d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1]), C.int64_t(shape[2])),
|
|
|
|
|
}
|
|
|
|
|
case 4:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_reshape_4d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.int64_t(shape[1]), C.int64_t(shape[2]), C.int64_t(shape[3])),
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic("unsupported number of dimensions")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Scale(ctx ml.Context, s float64) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_scale(ctx.(*Context).ctx, t.t, (C.float)(s)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 01:21:07 +08:00
|
|
|
func (t *Tensor) SumRows(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sum_rows(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Softmax(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_soft_max(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 07:56:32 +08:00
|
|
|
func (t *Tensor) Sin(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sin(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Cos(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_cos(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Tanh(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_tanh_inplace(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-04 06:18:29 +08:00
|
|
|
func (t *Tensor) Sigmoid(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sigmoid_inplace(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) View(ctx ml.Context, offset int, shape ...int) ml.Tensor {
|
|
|
|
|
switch len(shape) {
|
|
|
|
|
case 1:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_view_1d(ctx.(*Context).ctx, t.t, C.int64_t(shape[0]), C.size_t(offset)),
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_view_2d(ctx.(*Context).ctx, t.t,
|
|
|
|
|
C.int64_t(shape[0]), C.int64_t(shape[2]),
|
|
|
|
|
C.size_t(shape[1]),
|
|
|
|
|
C.size_t(offset)),
|
|
|
|
|
}
|
|
|
|
|
case 5:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_view_3d(ctx.(*Context).ctx, t.t,
|
|
|
|
|
C.int64_t(shape[0]), C.int64_t(shape[2]), C.int64_t(shape[4]),
|
|
|
|
|
C.size_t(shape[1]), C.size_t(shape[3]),
|
|
|
|
|
C.size_t(offset)),
|
|
|
|
|
}
|
|
|
|
|
case 7:
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_view_4d(ctx.(*Context).ctx, t.t,
|
|
|
|
|
C.int64_t(shape[0]), C.int64_t(shape[2]), C.int64_t(shape[4]), C.int64_t(shape[6]),
|
|
|
|
|
C.size_t(shape[1]), C.size_t(shape[3]), C.size_t(shape[5]),
|
|
|
|
|
C.size_t(offset)),
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic("unsupported number of dimensions")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 06:51:08 +08:00
|
|
|
func (t *Tensor) RoPE(ctx ml.Context, positions ml.Tensor, ropeDim int, ropeBase, ropeScale float32, options ...func(*rope.Options)) ml.Tensor {
|
2025-05-14 11:58:02 +08:00
|
|
|
// Default options
|
2025-08-06 03:21:16 +08:00
|
|
|
opts := rope.Options{
|
|
|
|
|
Factors: &Tensor{},
|
|
|
|
|
OriginalContextLength: 131072,
|
|
|
|
|
ExtrapolationFactor: 0.,
|
|
|
|
|
AttentionFactor: 1.,
|
|
|
|
|
BetaFast: 32.,
|
|
|
|
|
BetaSlow: 1.,
|
|
|
|
|
}
|
2025-05-14 11:58:02 +08:00
|
|
|
|
|
|
|
|
// Apply any provided options
|
|
|
|
|
for _, option := range options {
|
2025-08-06 03:21:16 +08:00
|
|
|
option(&opts)
|
2025-05-14 11:58:02 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-18 11:59:41 +08:00
|
|
|
dequant := t.t
|
|
|
|
|
if C.ggml_is_quantized(t.t._type) {
|
|
|
|
|
dequant = C.ggml_cast(ctx.(*Context).ctx, t.t, C.GGML_TYPE_F32)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_rope_ext(
|
2025-05-14 11:58:02 +08:00
|
|
|
ctx.(*Context).ctx,
|
|
|
|
|
dequant,
|
2025-05-21 06:51:08 +08:00
|
|
|
positions.(*Tensor).t,
|
|
|
|
|
opts.Factors.(*Tensor).t,
|
2025-02-14 08:31:21 +08:00
|
|
|
C.int(ropeDim),
|
2025-05-21 06:51:08 +08:00
|
|
|
C.int(opts.Type),
|
|
|
|
|
C.int(opts.OriginalContextLength),
|
2025-02-14 08:31:21 +08:00
|
|
|
C.float(ropeBase),
|
|
|
|
|
C.float(ropeScale),
|
2025-08-06 03:21:16 +08:00
|
|
|
C.float(opts.ExtrapolationFactor),
|
|
|
|
|
C.float(opts.AttentionFactor),
|
|
|
|
|
C.float(opts.BetaFast),
|
|
|
|
|
C.float(opts.BetaSlow),
|
2025-02-14 08:31:21 +08:00
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 07:56:32 +08:00
|
|
|
func (t *Tensor) IM2Col(ctx ml.Context, t2 ml.Tensor, s0, s1, p0, p1, d0, d1 int) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_im2col(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.int(s0), C.int(s1), C.int(p0), C.int(p1), C.int(d0), C.int(d1), true, C.GGML_TYPE_F32),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 00:51:19 +08:00
|
|
|
func (t *Tensor) GELU(ctx ml.Context, t2 ...ml.Tensor) ml.Tensor {
|
|
|
|
|
if len(t2) > 0 {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_geglu_split(ctx.(*Context).ctx, t.t, t2[0].(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_gelu_inplace(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 00:51:19 +08:00
|
|
|
func (t *Tensor) SILU(ctx ml.Context, t2 ...ml.Tensor) ml.Tensor {
|
|
|
|
|
if len(t2) > 0 {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_swiglu_split(ctx.(*Context).ctx, t.t, t2[0].(*Tensor).t),
|
|
|
|
|
}
|
2025-08-06 03:21:16 +08:00
|
|
|
}
|
2025-02-14 08:31:21 +08:00
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_silu_inplace(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 00:51:19 +08:00
|
|
|
func (t *Tensor) RELU(ctx ml.Context, t2 ...ml.Tensor) ml.Tensor {
|
|
|
|
|
if len(t2) > 0 {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_reglu_split(ctx.(*Context).ctx, t.t, t2[0].(*Tensor).t),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-26 12:47:09 +08:00
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_relu_inplace(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 00:51:19 +08:00
|
|
|
func (t *Tensor) SILUAlphaLimit(ctx ml.Context, up ml.Tensor, alpha, limit float32) ml.Tensor {
|
2025-08-15 05:42:58 +08:00
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_swiglu_oai(ctx.(*Context).ctx, t.t, up.(*Tensor).t, C.float(alpha), C.float(limit)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 08:31:21 +08:00
|
|
|
func (t *Tensor) Conv2D(ctx ml.Context, t2 ml.Tensor, s0, s1, p0, p1, d0, d1 int) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
2025-02-28 06:52:39 +08:00
|
|
|
b: t.b,
|
2025-02-14 08:31:21 +08:00
|
|
|
t: C.ggml_conv_2d(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.int(s0), C.int(s1), C.int(p0), C.int(p1), C.int(d0), C.int(d1)),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-15 07:55:33 +08:00
|
|
|
|
2025-03-12 00:00:10 +08:00
|
|
|
func (t *Tensor) AvgPool2D(ctx ml.Context, k, s int, p float32) ml.Tensor {
|
2025-03-07 04:16:54 +08:00
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
2025-03-12 00:00:10 +08:00
|
|
|
t: C.ggml_pool_2d(ctx.(*Context).ctx, t.t, C.GGML_OP_POOL_AVG, C.int(k), C.int(k), C.int(s), C.int(s), C.float(p), C.float(p)),
|
2025-03-07 04:16:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 05:52:45 +08:00
|
|
|
func (t *Tensor) Set(ctx ml.Context, t2 ml.Tensor, offset int, strides ...int) ml.Tensor {
|
|
|
|
|
var tt *C.struct_ggml_tensor
|
|
|
|
|
switch len(strides) {
|
|
|
|
|
case 0:
|
2025-03-08 09:38:36 +08:00
|
|
|
tt = C.ggml_set_1d(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.size_t(offset))
|
2025-03-08 05:52:45 +08:00
|
|
|
case 1:
|
2025-03-08 09:38:36 +08:00
|
|
|
tt = C.ggml_set_2d(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.size_t(offset), C.size_t(strides[0]))
|
2025-03-08 05:52:45 +08:00
|
|
|
default:
|
|
|
|
|
panic("unsupported number of dimensions")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Tensor{b: t.b, t: tt}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-15 05:42:58 +08:00
|
|
|
func (t *Tensor) ScaledDotProductAttention(ctx ml.Context, key, value, mask, sinks ml.Tensor, scale float64) ml.Tensor {
|
2025-02-15 12:51:44 +08:00
|
|
|
var kqMask *C.struct_ggml_tensor
|
|
|
|
|
if mask != nil {
|
|
|
|
|
kqMask = mask.(*Tensor).t
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 13:34:10 +08:00
|
|
|
query := t.Permute(ctx, 0, 2, 1, 3)
|
|
|
|
|
key = key.Permute(ctx, 0, 2, 1, 3)
|
|
|
|
|
|
2025-02-26 09:24:36 +08:00
|
|
|
if t.b.flashAttention {
|
|
|
|
|
value = value.Permute(ctx, 0, 2, 1, 3)
|
2025-02-15 12:51:44 +08:00
|
|
|
|
2025-02-26 09:24:36 +08:00
|
|
|
kqv := C.ggml_flash_attn_ext(ctx.(*Context).ctx, query.(*Tensor).t, key.(*Tensor).t, value.(*Tensor).t, kqMask, C.float(scale), 0, 0)
|
2025-08-15 05:42:58 +08:00
|
|
|
if sinks != nil {
|
|
|
|
|
C.ggml_flash_attn_ext_add_sinks(kqv, sinks.(*Tensor).t)
|
|
|
|
|
}
|
2025-02-26 09:24:36 +08:00
|
|
|
C.ggml_flash_attn_ext_set_prec(kqv, C.GGML_PREC_F32)
|
|
|
|
|
return &Tensor{b: t.b, t: kqv}
|
|
|
|
|
} else {
|
|
|
|
|
kq := key.MulmatFullPrec(ctx, query)
|
|
|
|
|
kq = &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_soft_max_ext(ctx.(*Context).ctx, kq.(*Tensor).t, kqMask, C.float(scale), 0),
|
|
|
|
|
}
|
2025-08-15 05:42:58 +08:00
|
|
|
if sinks != nil {
|
|
|
|
|
C.ggml_soft_max_add_sinks(kq.(*Tensor).t, sinks.(*Tensor).t)
|
|
|
|
|
}
|
2025-02-26 09:24:36 +08:00
|
|
|
|
|
|
|
|
kqv := value.Mulmat(ctx, kq)
|
|
|
|
|
return kqv.Permute(ctx, 0, 2, 1, 3).Contiguous(ctx)
|
|
|
|
|
}
|
2025-02-15 12:51:44 +08:00
|
|
|
}
|
2025-03-15 07:56:32 +08:00
|
|
|
|
|
|
|
|
func (t *Tensor) Duplicate(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_dup(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-04 06:18:29 +08:00
|
|
|
|
|
|
|
|
func (t *Tensor) TopK(ctx ml.Context, k int) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_top_k(ctx.(*Context).ctx, t.t, C.int(k)),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-14 11:58:02 +08:00
|
|
|
|
|
|
|
|
func (t *Tensor) Argsort(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_argsort(ctx.(*Context).ctx, t.t, C.GGML_SORT_ORDER_ASC),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-26 12:47:09 +08:00
|
|
|
|
|
|
|
|
func (t *Tensor) Mean(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_mean(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Variance(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return t.Add(ctx, t.Mean(ctx).Scale(ctx, -1)).
|
|
|
|
|
Sqr(ctx).
|
|
|
|
|
SumRows(ctx).
|
|
|
|
|
Scale(ctx, 1/float64(t.Dim(0)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Stddev(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return t.Variance(ctx).Sqrt(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Sqr(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sqr(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Sqrt(ctx ml.Context) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_sqrt(ctx.(*Context).ctx, t.t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *Tensor) Clamp(ctx ml.Context, min, max float32) ml.Tensor {
|
|
|
|
|
return &Tensor{
|
|
|
|
|
b: t.b,
|
|
|
|
|
t: C.ggml_clamp(ctx.(*Context).ctx, t.t, C.float(min), C.float(max)),
|
|
|
|
|
}
|
|
|
|
|
}
|