Add Metal backend; vendor whole llama.cpp trees
Browse filesMetal backend (gguf_metal/) driving llama.cpp's ggml-metal.metal, compiled into
the embedded metallib by kernel-builder. The shader includes ggml-common.h from
vendor/src, so the metal kernel section needs an include path like the cuda one.
vendor.py now copies whole trees (include, ggml-cuda, ggml-metal) rather than a
hand-listed file set. The curated list had drifted from build.toml: it no longer
performed the mmvq.cu -> mmvq-impl.cuh rename that ggml_dispatch.cu depends on,
so re-vendoring left build.toml pointing at a file that was never written.
Vendoring everything means a pin bump cannot leave a dangling include again;
only files named in a src list are compiled.
This view is limited to 50 files because it contains too many changes. See raw diff
- build.toml +18 -1
- flake.lock +117 -0
- gguf_metal/common.h +31 -0
- gguf_metal/ggml_dispatch.mm +282 -0
- gguf_metal/gguf_metal.cpp +59 -0
- vendor.py +26 -31
- vendor/include/ggml-blas.h +25 -0
- vendor/include/ggml-cann.h +123 -0
- vendor/include/ggml-cpp.h +39 -0
- vendor/include/ggml-cpu.h +152 -0
- vendor/include/ggml-et.h +28 -0
- vendor/include/ggml-hexagon.h +19 -0
- vendor/include/ggml-metal.h +61 -0
- vendor/include/ggml-opencl.h +26 -0
- vendor/include/ggml-openvino.h +37 -0
- vendor/include/ggml-opt.h +256 -0
- vendor/include/ggml-rpc.h +35 -0
- vendor/include/ggml-sycl.h +57 -0
- vendor/include/ggml-virtgpu.h +14 -0
- vendor/include/ggml-vulkan.h +29 -0
- vendor/include/ggml-webgpu.h +19 -0
- vendor/include/ggml-zdnn.h +17 -0
- vendor/include/ggml-zendnn.h +22 -0
- vendor/src/ggml-cuda/CMakeLists.txt +269 -0
- vendor/src/ggml-cuda/acc.cu +61 -0
- vendor/src/ggml-cuda/acc.cuh +5 -0
- vendor/src/ggml-cuda/add-id.cu +58 -0
- vendor/src/ggml-cuda/add-id.cuh +3 -0
- vendor/src/ggml-cuda/allreduce.cu +971 -0
- vendor/src/ggml-cuda/allreduce.cuh +29 -0
- vendor/src/ggml-cuda/arange.cu +34 -0
- vendor/src/ggml-cuda/arange.cuh +5 -0
- vendor/src/ggml-cuda/argmax.cu +91 -0
- vendor/src/ggml-cuda/argmax.cuh +3 -0
- vendor/src/ggml-cuda/argsort.cu +292 -0
- vendor/src/ggml-cuda/argsort.cuh +20 -0
- vendor/src/ggml-cuda/binbcast.cu +574 -0
- vendor/src/ggml-cuda/binbcast.cuh +12 -0
- vendor/src/ggml-cuda/clamp.cu +45 -0
- vendor/src/ggml-cuda/clamp.cuh +5 -0
- vendor/src/ggml-cuda/col2im-1d.cu +81 -0
- vendor/src/ggml-cuda/col2im-1d.cuh +3 -0
- vendor/src/ggml-cuda/concat.cu +242 -0
- vendor/src/ggml-cuda/concat.cuh +5 -0
- vendor/src/ggml-cuda/conv-transpose-1d.cu +88 -0
- vendor/src/ggml-cuda/conv-transpose-1d.cuh +5 -0
- vendor/src/ggml-cuda/conv2d-dw.cu +161 -0
- vendor/src/ggml-cuda/conv2d-dw.cuh +5 -0
- vendor/src/ggml-cuda/conv2d-transpose.cu +115 -0
- vendor/src/ggml-cuda/conv2d-transpose.cuh +5 -0
build.toml
CHANGED
|
@@ -9,7 +9,7 @@
|
|
| 9 |
name = "gguf-kernels"
|
| 10 |
version = 1
|
| 11 |
license = "MIT"
|
| 12 |
-
backends = ["cuda"]
|
| 13 |
|
| 14 |
[torch]
|
| 15 |
src = [
|
|
@@ -73,3 +73,20 @@ cuda-flags = [
|
|
| 73 |
"--expt-extended-lambda",
|
| 74 |
]
|
| 75 |
cxx-flags = ["-DGGML_USE_CUDA", "-DNDEBUG"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
name = "gguf-kernels"
|
| 10 |
version = 1
|
| 11 |
license = "MIT"
|
| 12 |
+
backends = ["cuda", "metal"]
|
| 13 |
|
| 14 |
[torch]
|
| 15 |
src = [
|
|
|
|
| 73 |
"--expt-extended-lambda",
|
| 74 |
]
|
| 75 |
cxx-flags = ["-DGGML_USE_CUDA", "-DNDEBUG"]
|
| 76 |
+
|
| 77 |
+
[kernel.gguf_metal]
|
| 78 |
+
backend = "metal"
|
| 79 |
+
depends = ["torch"]
|
| 80 |
+
# ggml-metal.metal includes "ggml-common.h" from vendor/src, so the shader compile needs it
|
| 81 |
+
# on its include path just as the cuda sources do.
|
| 82 |
+
include = ["gguf_metal", "torch-ext", "vendor/include", "vendor/src", "vendor/src/ggml-metal"]
|
| 83 |
+
src = [
|
| 84 |
+
"gguf_metal/gguf_metal.cpp",
|
| 85 |
+
"gguf_metal/ggml_dispatch.mm",
|
| 86 |
+
"gguf_metal/common.h",
|
| 87 |
+
# ggml's shader, compiled into the embedded metallib. Its mul_mv_* kernels are what the gemv
|
| 88 |
+
# dispatches to; the K-quants have no dequantize-to-dense kernel upstream, so that one is ours.
|
| 89 |
+
"vendor/src/ggml-metal/ggml-metal.metal",
|
| 90 |
+
"vendor/src/ggml-metal/ggml-metal-impl.h",
|
| 91 |
+
"vendor/src/ggml-common.h",
|
| 92 |
+
]
|
flake.lock
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"nodes": {
|
| 3 |
+
"flake-compat": {
|
| 4 |
+
"locked": {
|
| 5 |
+
"lastModified": 1767039857,
|
| 6 |
+
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
| 7 |
+
"owner": "edolstra",
|
| 8 |
+
"repo": "flake-compat",
|
| 9 |
+
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
| 10 |
+
"type": "github"
|
| 11 |
+
},
|
| 12 |
+
"original": {
|
| 13 |
+
"owner": "edolstra",
|
| 14 |
+
"repo": "flake-compat",
|
| 15 |
+
"type": "github"
|
| 16 |
+
}
|
| 17 |
+
},
|
| 18 |
+
"flake-utils": {
|
| 19 |
+
"inputs": {
|
| 20 |
+
"systems": "systems"
|
| 21 |
+
},
|
| 22 |
+
"locked": {
|
| 23 |
+
"lastModified": 1731533236,
|
| 24 |
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
| 25 |
+
"owner": "numtide",
|
| 26 |
+
"repo": "flake-utils",
|
| 27 |
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
| 28 |
+
"type": "github"
|
| 29 |
+
},
|
| 30 |
+
"original": {
|
| 31 |
+
"owner": "numtide",
|
| 32 |
+
"repo": "flake-utils",
|
| 33 |
+
"type": "github"
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
"kernel-builder": {
|
| 37 |
+
"inputs": {
|
| 38 |
+
"flake-compat": "flake-compat",
|
| 39 |
+
"flake-utils": "flake-utils",
|
| 40 |
+
"nixpkgs": "nixpkgs",
|
| 41 |
+
"rust-overlay": "rust-overlay"
|
| 42 |
+
},
|
| 43 |
+
"locked": {
|
| 44 |
+
"lastModified": 1785942216,
|
| 45 |
+
"narHash": "sha256-OlefmZzVX8KRYFwqo5Mx2MuG9LHjhwkehaN6vXu2c1c=",
|
| 46 |
+
"owner": "huggingface",
|
| 47 |
+
"repo": "kernels",
|
| 48 |
+
"rev": "81f55ea30fd8f819dcf93a3c934dd584c895bd2f",
|
| 49 |
+
"type": "github"
|
| 50 |
+
},
|
| 51 |
+
"original": {
|
| 52 |
+
"owner": "huggingface",
|
| 53 |
+
"repo": "kernels",
|
| 54 |
+
"type": "github"
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"nixpkgs": {
|
| 58 |
+
"locked": {
|
| 59 |
+
"lastModified": 1783284758,
|
| 60 |
+
"narHash": "sha256-tiQ8/qi8I45OOaBBYlVbXoAVkeQzvvTQOv5I45rMw5o=",
|
| 61 |
+
"owner": "NixOS",
|
| 62 |
+
"repo": "nixpkgs",
|
| 63 |
+
"rev": "ec1a11210589d294f0ac99d3290a27e6c73dfa1d",
|
| 64 |
+
"type": "github"
|
| 65 |
+
},
|
| 66 |
+
"original": {
|
| 67 |
+
"owner": "NixOS",
|
| 68 |
+
"repo": "nixpkgs",
|
| 69 |
+
"rev": "ec1a11210589d294f0ac99d3290a27e6c73dfa1d",
|
| 70 |
+
"type": "github"
|
| 71 |
+
}
|
| 72 |
+
},
|
| 73 |
+
"root": {
|
| 74 |
+
"inputs": {
|
| 75 |
+
"kernel-builder": "kernel-builder"
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"rust-overlay": {
|
| 79 |
+
"inputs": {
|
| 80 |
+
"nixpkgs": [
|
| 81 |
+
"kernel-builder",
|
| 82 |
+
"nixpkgs"
|
| 83 |
+
]
|
| 84 |
+
},
|
| 85 |
+
"locked": {
|
| 86 |
+
"lastModified": 1783320166,
|
| 87 |
+
"narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=",
|
| 88 |
+
"owner": "oxalica",
|
| 89 |
+
"repo": "rust-overlay",
|
| 90 |
+
"rev": "20ee15370c9256669d66968b89ee20a4b0a4e673",
|
| 91 |
+
"type": "github"
|
| 92 |
+
},
|
| 93 |
+
"original": {
|
| 94 |
+
"owner": "oxalica",
|
| 95 |
+
"repo": "rust-overlay",
|
| 96 |
+
"type": "github"
|
| 97 |
+
}
|
| 98 |
+
},
|
| 99 |
+
"systems": {
|
| 100 |
+
"locked": {
|
| 101 |
+
"lastModified": 1681028828,
|
| 102 |
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
| 103 |
+
"owner": "nix-systems",
|
| 104 |
+
"repo": "default",
|
| 105 |
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
| 106 |
+
"type": "github"
|
| 107 |
+
},
|
| 108 |
+
"original": {
|
| 109 |
+
"owner": "nix-systems",
|
| 110 |
+
"repo": "default",
|
| 111 |
+
"type": "github"
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
},
|
| 115 |
+
"root": "root",
|
| 116 |
+
"version": 7
|
| 117 |
+
}
|
gguf_metal/common.h
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include <cstddef>
|
| 4 |
+
#include <cstdint>
|
| 5 |
+
|
| 6 |
+
/* The ggml-facing boundary, mirroring gguf_cuda/ggml_dispatch.cu.
|
| 7 |
+
*
|
| 8 |
+
* Everything that touches ggml or Metal lives behind these functions, so torch headers and ggml
|
| 9 |
+
* headers never meet in one translation unit. Buffers arrive as (MTLBuffer, byte offset) pairs
|
| 10 |
+
* because a torch tensor's storage is a whole MTLBuffer that the tensor may only be a view into.
|
| 11 |
+
*
|
| 12 |
+
* Each returns 0 on success, or non-zero when the quantization has no kernel upstream, which the
|
| 13 |
+
* caller reports rather than faulting.
|
| 14 |
+
*/
|
| 15 |
+
|
| 16 |
+
extern "C" {
|
| 17 |
+
|
| 18 |
+
// out[M, N] = x[M, K] @ dequant(blocks)[N, K]^T, using ggml's mul_mv (M small) or mul_mm kernels.
|
| 19 |
+
// `x` and `out` are f32; `blocks` is the GGUF weight exactly as stored.
|
| 20 |
+
int gguf_metal_mul_mat(void *blocks, size_t blocks_off, void *x, size_t x_off, void *out,
|
| 21 |
+
size_t out_off, int ggml_type, int64_t K, int64_t N, int64_t M);
|
| 22 |
+
|
| 23 |
+
// dequant(blocks)[rows, cols] -> out, via ggml's get_rows. `indices` is an i32 buffer holding the
|
| 24 |
+
// row numbers to unpack, which for a whole weight is simply 0..rows-1.
|
| 25 |
+
int gguf_metal_get_rows(void *blocks, size_t blocks_off, void *indices, size_t indices_off,
|
| 26 |
+
void *out, size_t out_off, int ggml_type, int64_t rows, int64_t cols,
|
| 27 |
+
int out_dtype);
|
| 28 |
+
|
| 29 |
+
// True when the type has a gemv/gemm kernel in the metallib.
|
| 30 |
+
int gguf_metal_supports(int ggml_type);
|
| 31 |
+
}
|
gguf_metal/ggml_dispatch.mm
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Metal side: load ggml's metallib, specialise its kernels, encode them on torch's own stream.
|
| 2 |
+
*
|
| 3 |
+
* The three things this has that a `torch.mps.compile_shader` caller does not, and which are the
|
| 4 |
+
* reason the backend exists at all:
|
| 5 |
+
*
|
| 6 |
+
* - MTLFunctionConstantValues, so a kernel is specialised the way ggml specialises it instead of
|
| 7 |
+
* the source being rewritten before compiling.
|
| 8 |
+
* - setThreadgroupMemoryLength, without which mul_mm cannot run at all -- and mul_mm is what
|
| 9 |
+
* keeps prefill from unpacking every weight to dense.
|
| 10 |
+
* - encoding into torch's current command buffer, so a layer's dispatches join the work already
|
| 11 |
+
* queued rather than each becoming its own submission.
|
| 12 |
+
*/
|
| 13 |
+
|
| 14 |
+
#import <Metal/Metal.h>
|
| 15 |
+
|
| 16 |
+
#include <ATen/mps/MPSDevice.h>
|
| 17 |
+
#include <ATen/mps/MPSStream.h>
|
| 18 |
+
|
| 19 |
+
#include <string>
|
| 20 |
+
#include <unordered_map>
|
| 21 |
+
|
| 22 |
+
#include "common.h"
|
| 23 |
+
#include "ggml-metal-impl.h"
|
| 24 |
+
|
| 25 |
+
#ifdef EMBEDDED_METALLIB_HEADER
|
| 26 |
+
#include EMBEDDED_METALLIB_HEADER
|
| 27 |
+
#endif
|
| 28 |
+
|
| 29 |
+
namespace {
|
| 30 |
+
|
| 31 |
+
// ggml type ids, matching ggml.h
|
| 32 |
+
enum { GGML_Q4_0 = 2, GGML_Q4_1 = 3, GGML_Q5_0 = 6, GGML_Q5_1 = 7, GGML_Q8_0 = 8,
|
| 33 |
+
GGML_Q2_K = 10, GGML_Q3_K = 11, GGML_Q4_K = 12, GGML_Q5_K = 13, GGML_Q6_K = 14 };
|
| 34 |
+
|
| 35 |
+
struct TypeInfo {
|
| 36 |
+
const char *name; // the infix in kernel_mul_mv_<name>_f32
|
| 37 |
+
int block_elems;
|
| 38 |
+
int block_bytes;
|
| 39 |
+
int mv_nsg; // N_SG_* from ggml-metal-impl.h
|
| 40 |
+
int mv_nr0; // N_R0_*
|
| 41 |
+
};
|
| 42 |
+
|
| 43 |
+
const std::unordered_map<int, TypeInfo> &type_table() {
|
| 44 |
+
static const std::unordered_map<int, TypeInfo> table = {
|
| 45 |
+
{GGML_Q8_0, {"q8_0", 32, 34, N_SG_Q8_0, N_R0_Q8_0}},
|
| 46 |
+
{GGML_Q4_K, {"q4_K", 256, 144, N_SG_Q4_K, N_R0_Q4_K}},
|
| 47 |
+
{GGML_Q5_K, {"q5_K", 256, 176, N_SG_Q5_K, N_R0_Q5_K}},
|
| 48 |
+
{GGML_Q6_K, {"q6_K", 256, 210, N_SG_Q6_K, N_R0_Q6_K}},
|
| 49 |
+
};
|
| 50 |
+
return table;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
const TypeInfo *lookup(int ggml_type) {
|
| 54 |
+
auto it = type_table().find(ggml_type);
|
| 55 |
+
return it == type_table().end() ? nullptr : &it->second;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
id<MTLDevice> device() { return at::mps::MPSDevice::getInstance()->device(); }
|
| 59 |
+
|
| 60 |
+
id<MTLLibrary> library() {
|
| 61 |
+
static id<MTLLibrary> lib = nil;
|
| 62 |
+
if (lib != nil) {
|
| 63 |
+
return lib;
|
| 64 |
+
}
|
| 65 |
+
NSError *error = nil;
|
| 66 |
+
#ifdef EMBEDDED_METALLIB_HEADER
|
| 67 |
+
lib = EMBEDDED_METALLIB_NAMESPACE::createLibrary(device(), &error);
|
| 68 |
+
#else
|
| 69 |
+
// Local builds point at a metallib on disk; the packaged build embeds it instead.
|
| 70 |
+
const char *path = getenv("GGUF_METAL_METALLIB");
|
| 71 |
+
if (path == nullptr) {
|
| 72 |
+
NSLog(@"gguf-kernels: GGUF_METAL_METALLIB is unset and no metallib is embedded");
|
| 73 |
+
return nil;
|
| 74 |
+
}
|
| 75 |
+
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path]];
|
| 76 |
+
lib = [device() newLibraryWithURL:url error:&error];
|
| 77 |
+
#endif
|
| 78 |
+
if (lib == nil) {
|
| 79 |
+
NSLog(@"gguf-kernels: could not load the metallib: %@", error);
|
| 80 |
+
} else {
|
| 81 |
+
[lib retain];
|
| 82 |
+
}
|
| 83 |
+
return lib;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// Pipelines are cached under the same key ggml uses: the kernel name plus the constants it was
|
| 87 |
+
// specialised with, since a different specialisation is a different pipeline.
|
| 88 |
+
id<MTLComputePipelineState> pipeline(const std::string &key, const char *fn_name,
|
| 89 |
+
MTLFunctionConstantValues *constants) {
|
| 90 |
+
static std::unordered_map<std::string, id<MTLComputePipelineState>> cache;
|
| 91 |
+
auto it = cache.find(key);
|
| 92 |
+
if (it != cache.end()) {
|
| 93 |
+
return it->second;
|
| 94 |
+
}
|
| 95 |
+
id<MTLLibrary> lib = library();
|
| 96 |
+
if (lib == nil) {
|
| 97 |
+
return nil;
|
| 98 |
+
}
|
| 99 |
+
NSError *error = nil;
|
| 100 |
+
NSString *name = [NSString stringWithUTF8String:fn_name];
|
| 101 |
+
id<MTLFunction> fn = constants == nil
|
| 102 |
+
? [lib newFunctionWithName:name]
|
| 103 |
+
: [lib newFunctionWithName:name constantValues:constants error:&error];
|
| 104 |
+
if (fn == nil) {
|
| 105 |
+
NSLog(@"gguf-kernels: no function %s in the metallib: %@", fn_name, error);
|
| 106 |
+
return nil;
|
| 107 |
+
}
|
| 108 |
+
id<MTLComputePipelineState> pso = [device() newComputePipelineStateWithFunction:fn error:&error];
|
| 109 |
+
[fn release];
|
| 110 |
+
if (pso == nil) {
|
| 111 |
+
NSLog(@"gguf-kernels: could not build a pipeline for %s: %@", fn_name, error);
|
| 112 |
+
return nil;
|
| 113 |
+
}
|
| 114 |
+
cache[key] = pso;
|
| 115 |
+
return pso;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
void set_short(MTLFunctionConstantValues *cv, int16_t value, NSUInteger index) {
|
| 119 |
+
[cv setConstantValue:&value type:MTLDataTypeShort atIndex:index];
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
void set_bool(MTLFunctionConstantValues *cv, bool value, NSUInteger index) {
|
| 123 |
+
[cv setConstantValue:&value type:MTLDataTypeBool atIndex:index];
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
} // namespace
|
| 127 |
+
|
| 128 |
+
extern "C" int gguf_metal_supports(int ggml_type) { return lookup(ggml_type) != nullptr; }
|
| 129 |
+
|
| 130 |
+
extern "C" int gguf_metal_mul_mat(void *blocks, size_t blocks_off, void *x, size_t x_off, void *out,
|
| 131 |
+
size_t out_off, int ggml_type, int64_t K, int64_t N, int64_t M) {
|
| 132 |
+
const TypeInfo *info = lookup(ggml_type);
|
| 133 |
+
if (info == nullptr) {
|
| 134 |
+
return 1;
|
| 135 |
+
}
|
| 136 |
+
const uint64_t nb01 = (uint64_t)(K / info->block_elems) * info->block_bytes; // bytes per row
|
| 137 |
+
// One batch, no broadcast: this backend is only ever handed a 2-D weight and a 2-D activation.
|
| 138 |
+
const int16_t r2 = 1, r3 = 1;
|
| 139 |
+
const int32_t ne12 = 1;
|
| 140 |
+
|
| 141 |
+
// torch keeps one encoder open on its command buffer and coalesces into it; opening a second is
|
| 142 |
+
// an error. Reusing it is also what puts these dispatches in the same submission as torch's own.
|
| 143 |
+
at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
|
| 144 |
+
id<MTLComputeCommandEncoder> enc = stream->commandEncoder();
|
| 145 |
+
|
| 146 |
+
id<MTLBuffer> b_blocks = (__bridge id<MTLBuffer>)blocks;
|
| 147 |
+
id<MTLBuffer> b_x = (__bridge id<MTLBuffer>)x;
|
| 148 |
+
id<MTLBuffer> b_out = (__bridge id<MTLBuffer>)out;
|
| 149 |
+
|
| 150 |
+
int rc = 0;
|
| 151 |
+
if (M <= 8) {
|
| 152 |
+
// gemv: one token (or a handful), the decode path
|
| 153 |
+
const int nsg = info->mv_nsg, nr0 = info->mv_nr0;
|
| 154 |
+
char fn[128];
|
| 155 |
+
snprintf(fn, sizeof(fn), "kernel_mul_mv_%s_f32", info->name);
|
| 156 |
+
std::string key = std::string(fn) + "_nsg=" + std::to_string(nsg);
|
| 157 |
+
|
| 158 |
+
MTLFunctionConstantValues *cv = [MTLFunctionConstantValues new];
|
| 159 |
+
set_short(cv, (int16_t)nsg, FC_MUL_MV + 0);
|
| 160 |
+
set_short(cv, (int16_t)ne12, FC_MUL_MV + 2);
|
| 161 |
+
set_short(cv, r2, FC_MUL_MV + 3);
|
| 162 |
+
set_short(cv, r3, FC_MUL_MV + 4);
|
| 163 |
+
id<MTLComputePipelineState> pso = pipeline(key, fn, cv);
|
| 164 |
+
[cv release];
|
| 165 |
+
if (pso == nil) {
|
| 166 |
+
rc = 2;
|
| 167 |
+
} else {
|
| 168 |
+
ggml_metal_kargs_mul_mv args = {
|
| 169 |
+
/*.ne00 =*/ (int32_t)K, /*.ne01 =*/ (int32_t)N, /*.ne02 =*/ 1,
|
| 170 |
+
/*.nb00 =*/ (uint64_t)info->block_bytes,
|
| 171 |
+
/*.nb01 =*/ nb01, /*.nb02 =*/ nb01 * N, /*.nb03 =*/ nb01 * N,
|
| 172 |
+
/*.ne10 =*/ (int32_t)K, /*.ne11 =*/ (int32_t)M, /*.ne12 =*/ 1,
|
| 173 |
+
/*.nb10 =*/ 4, /*.nb11 =*/ 4 * (uint64_t)K,
|
| 174 |
+
/*.nb12 =*/ 4 * (uint64_t)K * M, /*.nb13 =*/ 4 * (uint64_t)K * M,
|
| 175 |
+
/*.ne0 =*/ (int32_t)N, /*.ne1 =*/ (int32_t)M, /*.nr0 =*/ nr0,
|
| 176 |
+
/*.r2 =*/ r2, /*.r3 =*/ r3,
|
| 177 |
+
};
|
| 178 |
+
[enc setComputePipelineState:pso];
|
| 179 |
+
[enc setBytes:&args length:sizeof(args) atIndex:0];
|
| 180 |
+
[enc setBuffer:b_blocks offset:blocks_off atIndex:1];
|
| 181 |
+
[enc setBuffer:b_x offset:x_off atIndex:2];
|
| 182 |
+
[enc setBuffer:b_out offset:out_off atIndex:3];
|
| 183 |
+
const NSUInteger gx = (N + nr0 * nsg - 1) / (nr0 * nsg);
|
| 184 |
+
[enc dispatchThreadgroups:MTLSizeMake(gx, M, 1)
|
| 185 |
+
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
|
| 186 |
+
}
|
| 187 |
+
} else {
|
| 188 |
+
// gemm: prefill. This is the branch that needs threadgroup memory, and the reason a caller
|
| 189 |
+
// without it has to unpack the weight to dense instead.
|
| 190 |
+
const bool bc_inp = (K % 32) != 0;
|
| 191 |
+
const bool bc_out = false; // the tensor API path, which this backend does not use
|
| 192 |
+
const int nsg = N_MM_SIMD_GROUP_X * N_MM_SIMD_GROUP_Y;
|
| 193 |
+
const int nr0 = 64, nr1 = 32;
|
| 194 |
+
const size_t smem = bc_out ? 8192 : (4096 + 2048);
|
| 195 |
+
|
| 196 |
+
char fn[128];
|
| 197 |
+
snprintf(fn, sizeof(fn), "kernel_mul_mm_%s_f32", info->name);
|
| 198 |
+
std::string key = std::string(fn) + "_bci=" + std::to_string(bc_inp) +
|
| 199 |
+
"_bco=" + std::to_string(bc_out);
|
| 200 |
+
|
| 201 |
+
MTLFunctionConstantValues *cv = [MTLFunctionConstantValues new];
|
| 202 |
+
// All six must be set: an unspecified function constant is not defaulted, the pipeline just
|
| 203 |
+
// reads whatever is there.
|
| 204 |
+
set_bool(cv, bc_inp, FC_MUL_MM + 0);
|
| 205 |
+
set_bool(cv, bc_out, FC_MUL_MM + 1);
|
| 206 |
+
set_short(cv, (int16_t)ne12, FC_MUL_MM + 2);
|
| 207 |
+
set_short(cv, 1, FC_MUL_MM + 3); // ne13
|
| 208 |
+
set_short(cv, r2, FC_MUL_MM + 4);
|
| 209 |
+
set_short(cv, r3, FC_MUL_MM + 5);
|
| 210 |
+
id<MTLComputePipelineState> pso = pipeline(key, fn, cv);
|
| 211 |
+
[cv release];
|
| 212 |
+
if (pso == nil) {
|
| 213 |
+
rc = 2;
|
| 214 |
+
} else {
|
| 215 |
+
ggml_metal_kargs_mul_mm args = {
|
| 216 |
+
/*.ne00 =*/ (int32_t)K, /*.ne02 =*/ 1,
|
| 217 |
+
/*.nb01 =*/ nb01, /*.nb02 =*/ nb01 * N, /*.nb03 =*/ nb01 * N,
|
| 218 |
+
/*.ne12 =*/ 1,
|
| 219 |
+
/*.nb10 =*/ 4, /*.nb11 =*/ 4 * (uint64_t)K,
|
| 220 |
+
/*.nb12 =*/ 4 * (uint64_t)K * M, /*.nb13 =*/ 4 * (uint64_t)K * M,
|
| 221 |
+
/*.ne0 =*/ (int32_t)N, /*.ne1 =*/ (int32_t)M,
|
| 222 |
+
/*.r2 =*/ r2, /*.r3 =*/ r3,
|
| 223 |
+
};
|
| 224 |
+
[enc setComputePipelineState:pso];
|
| 225 |
+
[enc setBytes:&args length:sizeof(args) atIndex:0];
|
| 226 |
+
[enc setBuffer:b_blocks offset:blocks_off atIndex:1];
|
| 227 |
+
[enc setBuffer:b_x offset:x_off atIndex:2];
|
| 228 |
+
[enc setBuffer:b_out offset:out_off atIndex:3];
|
| 229 |
+
[enc setThreadgroupMemoryLength:smem atIndex:0];
|
| 230 |
+
[enc dispatchThreadgroups:MTLSizeMake((M + nr1 - 1) / nr1, (N + nr0 - 1) / nr0, 1)
|
| 231 |
+
threadsPerThreadgroup:MTLSizeMake(32, nsg, 1)];
|
| 232 |
+
}
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
return rc;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
extern "C" int gguf_metal_get_rows(void *blocks, size_t blocks_off, void *indices,
|
| 239 |
+
size_t indices_off, void *out, size_t out_off, int ggml_type,
|
| 240 |
+
int64_t rows, int64_t cols, int out_dtype) {
|
| 241 |
+
const TypeInfo *info = lookup(ggml_type);
|
| 242 |
+
if (info == nullptr) {
|
| 243 |
+
return 1;
|
| 244 |
+
}
|
| 245 |
+
(void)out_dtype; // ggml's get_rows writes f32; the caller casts
|
| 246 |
+
|
| 247 |
+
char fn[128];
|
| 248 |
+
snprintf(fn, sizeof(fn), "kernel_get_rows_%s", info->name);
|
| 249 |
+
id<MTLComputePipelineState> pso = pipeline(fn, fn, nil);
|
| 250 |
+
if (pso == nil) {
|
| 251 |
+
return 2;
|
| 252 |
+
}
|
| 253 |
+
const uint64_t nb01 = (uint64_t)(cols / info->block_elems) * info->block_bytes;
|
| 254 |
+
|
| 255 |
+
// ggml walks a quantized row 16 values at a time, so the thread count is derived from ne00/16
|
| 256 |
+
// rather than the element count.
|
| 257 |
+
const int32_t ne00t = (int32_t)(cols / 16);
|
| 258 |
+
|
| 259 |
+
ggml_metal_kargs_get_rows args = {
|
| 260 |
+
/*.ne00t =*/ ne00t, /*.ne00 =*/ (int32_t)cols,
|
| 261 |
+
/*.nb01 =*/ nb01, /*.nb02 =*/ nb01 * rows, /*.nb03 =*/ nb01 * rows,
|
| 262 |
+
/*.ne10 =*/ (int32_t)rows,
|
| 263 |
+
/*.nb10 =*/ 4, /*.nb11 =*/ 4, /*.nb12 =*/ 4 * (uint64_t)rows,
|
| 264 |
+
/*.nb1 =*/ 4 * (uint64_t)cols, /*.nb2 =*/ 4 * (uint64_t)cols * rows,
|
| 265 |
+
/*.nb3 =*/ 4 * (uint64_t)cols * rows,
|
| 266 |
+
};
|
| 267 |
+
|
| 268 |
+
const NSUInteger max_threads = pso.maxTotalThreadsPerThreadgroup;
|
| 269 |
+
const NSUInteger nth = MIN((NSUInteger)ne00t, max_threads);
|
| 270 |
+
const NSUInteger nw0 = (ne00t + nth - 1) / nth;
|
| 271 |
+
|
| 272 |
+
at::mps::MPSStream *stream = at::mps::getCurrentMPSStream();
|
| 273 |
+
id<MTLComputeCommandEncoder> enc = stream->commandEncoder();
|
| 274 |
+
[enc setComputePipelineState:pso];
|
| 275 |
+
[enc setBytes:&args length:sizeof(args) atIndex:0];
|
| 276 |
+
[enc setBuffer:(__bridge id<MTLBuffer>)blocks offset:blocks_off atIndex:1];
|
| 277 |
+
[enc setBuffer:(__bridge id<MTLBuffer>)indices offset:indices_off atIndex:2];
|
| 278 |
+
[enc setBuffer:(__bridge id<MTLBuffer>)out offset:out_off atIndex:3];
|
| 279 |
+
[enc dispatchThreadgroups:MTLSizeMake(nw0 * rows, 1, 1)
|
| 280 |
+
threadsPerThreadgroup:MTLSizeMake(nth, 1, 1)];
|
| 281 |
+
return 0;
|
| 282 |
+
}
|
gguf_metal/gguf_metal.cpp
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Metal implementation of the two entry points.
|
| 2 |
+
*
|
| 3 |
+
* Same shape as gguf_cuda.cu: no ggml header is included here, all ggml and Metal contact sits
|
| 4 |
+
* behind the extern "C" boundary in ggml_dispatch.mm, and every output comes from torch's
|
| 5 |
+
* allocator so the ops compose with the rest of a model's memory.
|
| 6 |
+
*
|
| 7 |
+
* A torch MPS tensor's storage is a whole MTLBuffer that the tensor may be a view into, so each
|
| 8 |
+
* buffer crosses the boundary as a (buffer, byte offset) pair.
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
#include <torch/torch.h>
|
| 12 |
+
|
| 13 |
+
#include "common.h"
|
| 14 |
+
#include "torch_binding.h"
|
| 15 |
+
|
| 16 |
+
namespace {
|
| 17 |
+
|
| 18 |
+
void *mtl_buffer(const at::Tensor &t) {
|
| 19 |
+
return const_cast<void *>(t.storage().data());
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
size_t byte_offset(const at::Tensor &t) {
|
| 23 |
+
return static_cast<size_t>(t.storage_offset()) * t.element_size();
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
} // namespace
|
| 27 |
+
|
| 28 |
+
at::Tensor dequantize(const at::Tensor &blocks, int64_t ggml_type, int64_t rows, int64_t cols,
|
| 29 |
+
at::ScalarType dtype) {
|
| 30 |
+
TORCH_CHECK(blocks.is_mps() && blocks.scalar_type() == at::kByte, "blocks must be mps uint8");
|
| 31 |
+
TORCH_CHECK(blocks.is_contiguous(), "blocks must be contiguous");
|
| 32 |
+
|
| 33 |
+
// ggml unpacks rows by index, so a whole weight is just every index in order.
|
| 34 |
+
auto indices = at::arange(rows, blocks.options().dtype(at::kInt));
|
| 35 |
+
auto out = at::empty({rows, cols}, blocks.options().dtype(at::kFloat));
|
| 36 |
+
|
| 37 |
+
const int rc = gguf_metal_get_rows(mtl_buffer(blocks), byte_offset(blocks), mtl_buffer(indices),
|
| 38 |
+
byte_offset(indices), mtl_buffer(out), byte_offset(out),
|
| 39 |
+
static_cast<int>(ggml_type), rows, cols, 0);
|
| 40 |
+
TORCH_CHECK(rc == 0, "gguf-kernels: dequantize has no implementation for ggml type ", ggml_type);
|
| 41 |
+
return dtype == at::kFloat ? out : out.to(dtype);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
at::Tensor mul_mat_vec(const at::Tensor &blocks, const at::Tensor &x, int64_t ggml_type,
|
| 45 |
+
int64_t out_features) {
|
| 46 |
+
TORCH_CHECK(blocks.is_mps() && blocks.scalar_type() == at::kByte, "blocks must be mps uint8");
|
| 47 |
+
TORCH_CHECK(x.is_mps() && x.dim() == 2, "x must be a 2D mps tensor");
|
| 48 |
+
|
| 49 |
+
const int64_t rows = x.size(0), in_features = x.size(1);
|
| 50 |
+
// ggml's mul_mv/mul_mm kernels for quantized weights all take an f32 activation.
|
| 51 |
+
const auto xc = x.scalar_type() == at::kFloat ? x.contiguous() : x.to(at::kFloat).contiguous();
|
| 52 |
+
auto out = at::empty({rows, out_features}, x.options().dtype(at::kFloat));
|
| 53 |
+
|
| 54 |
+
const int rc = gguf_metal_mul_mat(mtl_buffer(blocks), byte_offset(blocks), mtl_buffer(xc),
|
| 55 |
+
byte_offset(xc), mtl_buffer(out), byte_offset(out),
|
| 56 |
+
static_cast<int>(ggml_type), in_features, out_features, rows);
|
| 57 |
+
TORCH_CHECK(rc == 0, "gguf-kernels: no matmul for ggml type ", ggml_type, " at ", rows, " rows");
|
| 58 |
+
return out;
|
| 59 |
+
}
|
vendor.py
CHANGED
|
@@ -20,41 +20,30 @@ import subprocess
|
|
| 20 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 21 |
VENDOR = os.path.join(HERE, "vendor")
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
FILES = [
|
| 24 |
-
"include/ggml-alloc.h",
|
| 25 |
-
"include/ggml-backend.h",
|
| 26 |
-
"include/ggml-cuda.h",
|
| 27 |
-
"include/ggml.h",
|
| 28 |
-
"include/gguf.h",
|
| 29 |
"src/ggml-common.h",
|
| 30 |
"src/ggml-impl.h",
|
| 31 |
-
"src/ggml-cuda/common.cuh",
|
| 32 |
-
"src/ggml-cuda/convert.cu",
|
| 33 |
-
"src/ggml-cuda/convert.cuh",
|
| 34 |
-
"src/ggml-cuda/dequantize.cuh",
|
| 35 |
-
"src/ggml-cuda/mma.cuh",
|
| 36 |
-
"src/ggml-cuda/mmq-config-ampere.cuh",
|
| 37 |
-
"src/ggml-cuda/mmq-config-blackwell.cuh",
|
| 38 |
-
"src/ggml-cuda/mmq-config-cdna.cuh",
|
| 39 |
-
"src/ggml-cuda/mmq-config-pascal.cuh",
|
| 40 |
-
"src/ggml-cuda/mmq-config-rdna2.cuh",
|
| 41 |
-
"src/ggml-cuda/mmq-config-rdna3-5.cuh",
|
| 42 |
-
"src/ggml-cuda/mmq-config-rdna3.cuh",
|
| 43 |
-
"src/ggml-cuda/mmq-config-rdna4.cuh",
|
| 44 |
-
"src/ggml-cuda/mmq-load-tiles.cuh",
|
| 45 |
-
"src/ggml-cuda/mmq-vec-dot.cuh",
|
| 46 |
-
"src/ggml-cuda/mmq.cuh",
|
| 47 |
-
"src/ggml-cuda/mmvq.cu",
|
| 48 |
-
"src/ggml-cuda/mmvq.cuh",
|
| 49 |
-
"src/ggml-cuda/mmvf.cu",
|
| 50 |
-
"src/ggml-cuda/mmvf.cuh",
|
| 51 |
-
"src/ggml-cuda/quantize.cu",
|
| 52 |
-
"src/ggml-cuda/quantize.cuh",
|
| 53 |
-
"src/ggml-cuda/unary.cuh",
|
| 54 |
-
"src/ggml-cuda/vecdotq.cuh",
|
| 55 |
-
"src/ggml-cuda/vendors/cuda.h",
|
| 56 |
]
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
def main():
|
| 60 |
ap = argparse.ArgumentParser()
|
|
@@ -75,16 +64,22 @@ def main():
|
|
| 75 |
|
| 76 |
if os.path.isdir(VENDOR):
|
| 77 |
shutil.rmtree(VENDOR)
|
|
|
|
|
|
|
| 78 |
for rel in FILES:
|
| 79 |
dst = os.path.join(VENDOR, rel)
|
| 80 |
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
| 81 |
shutil.copy2(os.path.join(args.src, "ggml", rel), dst)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
with open(os.path.join(VENDOR, "UPSTREAM"), "w") as f:
|
| 84 |
f.write(f"https://github.com/ggml-org/llama.cpp\n{rev}\n")
|
| 85 |
shutil.copy2(os.path.join(args.src, "LICENSE"), os.path.join(VENDOR, "LICENSE"))
|
| 86 |
|
| 87 |
-
print(f"vendored {len(
|
| 88 |
|
| 89 |
|
| 90 |
if __name__ == "__main__":
|
|
|
|
| 20 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 21 |
VENDOR = os.path.join(HERE, "vendor")
|
| 22 |
|
| 23 |
+
# Whole trees rather than a curated list. A hand-maintained file list silently drifts from what
|
| 24 |
+
# `build.toml` and upstream's own includes need -- vendoring everything means bumping the pin can
|
| 25 |
+
# never leave a dangling include. Only the files named in a `src` list are compiled; the rest ride
|
| 26 |
+
# along so the next kernel we reach for is already here.
|
| 27 |
+
TREES = [
|
| 28 |
+
"include",
|
| 29 |
+
"src/ggml-cuda",
|
| 30 |
+
"src/ggml-metal",
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
# Top-level headers the backends include directly.
|
| 34 |
FILES = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
"src/ggml-common.h",
|
| 36 |
"src/ggml-impl.h",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
]
|
| 38 |
|
| 39 |
+
# Upstream compiles mmvq.cu as its own translation unit. Here `ggml_dispatch.cu` #includes it instead,
|
| 40 |
+
# so it is also vendored under a .cuh name -- a .cu in the tree would be picked up and compiled a
|
| 41 |
+
# second time, defining every symbol twice. Dropping this rename is what left `build.toml` pointing at
|
| 42 |
+
# a file that no longer existed.
|
| 43 |
+
RENAMES = {
|
| 44 |
+
"src/ggml-cuda/mmvq.cu": "src/ggml-cuda/mmvq-impl.cuh",
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
|
| 48 |
def main():
|
| 49 |
ap = argparse.ArgumentParser()
|
|
|
|
| 64 |
|
| 65 |
if os.path.isdir(VENDOR):
|
| 66 |
shutil.rmtree(VENDOR)
|
| 67 |
+
for rel in TREES:
|
| 68 |
+
shutil.copytree(os.path.join(args.src, "ggml", rel), os.path.join(VENDOR, rel))
|
| 69 |
for rel in FILES:
|
| 70 |
dst = os.path.join(VENDOR, rel)
|
| 71 |
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
| 72 |
shutil.copy2(os.path.join(args.src, "ggml", rel), dst)
|
| 73 |
+
for rel, renamed in RENAMES.items():
|
| 74 |
+
dst = os.path.join(VENDOR, renamed)
|
| 75 |
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
| 76 |
+
shutil.copy2(os.path.join(args.src, "ggml", rel), dst)
|
| 77 |
|
| 78 |
with open(os.path.join(VENDOR, "UPSTREAM"), "w") as f:
|
| 79 |
f.write(f"https://github.com/ggml-org/llama.cpp\n{rev}\n")
|
| 80 |
shutil.copy2(os.path.join(args.src, "LICENSE"), os.path.join(VENDOR, "LICENSE"))
|
| 81 |
|
| 82 |
+
print(f"vendored {sum(len(f) for _, _, f in os.walk(VENDOR))} files from llama.cpp @ {rev[:12]} into {VENDOR}")
|
| 83 |
|
| 84 |
|
| 85 |
if __name__ == "__main__":
|
vendor/include/ggml-blas.h
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
#ifdef __cplusplus
|
| 8 |
+
extern "C" {
|
| 9 |
+
#endif
|
| 10 |
+
|
| 11 |
+
// backend API
|
| 12 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_blas_init(void);
|
| 13 |
+
|
| 14 |
+
GGML_BACKEND_API bool ggml_backend_is_blas(ggml_backend_t backend);
|
| 15 |
+
|
| 16 |
+
// number of threads used for conversion to float
|
| 17 |
+
// for openblas and blis, this will also set the number of threads used for blas operations
|
| 18 |
+
GGML_BACKEND_API void ggml_backend_blas_set_n_threads(ggml_backend_t backend_blas, int n_threads);
|
| 19 |
+
|
| 20 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_blas_reg(void);
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
#ifdef __cplusplus
|
| 24 |
+
}
|
| 25 |
+
#endif
|
vendor/include/ggml-cann.h
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) 2023-2026 The ggml authors
|
| 3 |
+
*
|
| 4 |
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 5 |
+
* of this software and associated documentation files (the "Software"), to
|
| 6 |
+
* deal in the Software without restriction, including without limitation the
|
| 7 |
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
| 8 |
+
* sell copies of the Software, and to permit persons to whom the Software is
|
| 9 |
+
* furnished to do so, subject to the following conditions:
|
| 10 |
+
*
|
| 11 |
+
* The above copyright notice and this permission notice shall be included in
|
| 12 |
+
* all copies or substantial portions of the Software.
|
| 13 |
+
*
|
| 14 |
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 15 |
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 16 |
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 17 |
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 18 |
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
| 19 |
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
| 20 |
+
* IN THE SOFTWARE.
|
| 21 |
+
*/
|
| 22 |
+
|
| 23 |
+
#pragma once
|
| 24 |
+
|
| 25 |
+
#include "ggml-backend.h"
|
| 26 |
+
#include "ggml.h"
|
| 27 |
+
|
| 28 |
+
#ifdef __cplusplus
|
| 29 |
+
extern "C" {
|
| 30 |
+
#endif
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* @brief Maximum number of CANN devices supported.
|
| 34 |
+
*/
|
| 35 |
+
#define GGML_CANN_MAX_DEVICES 16
|
| 36 |
+
|
| 37 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_cann_reg(void);
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* @brief Initializes the CANN backend for a specified device.
|
| 41 |
+
*
|
| 42 |
+
* This function initializes the CANN backend for the given device.
|
| 43 |
+
* It verifies the device index, allocates a context, and creates a backend
|
| 44 |
+
* instance.
|
| 45 |
+
*
|
| 46 |
+
* @param device The index of the device to initialize.
|
| 47 |
+
* @return A pointer to the initialized backend instance, or nullptr on failure.
|
| 48 |
+
*/
|
| 49 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_cann_init(int32_t device);
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* @brief Checks if a given backend is a CANN backend.
|
| 53 |
+
*
|
| 54 |
+
* This function verifies if the provided backend is a CANN backend by comparing
|
| 55 |
+
* its GUID with the CANN backend's GUID.
|
| 56 |
+
*
|
| 57 |
+
* @param backend The backend instance to check.
|
| 58 |
+
* @return True if the backend is a CANN backend, false otherwise.
|
| 59 |
+
*/
|
| 60 |
+
GGML_BACKEND_API bool ggml_backend_is_cann(ggml_backend_t backend);
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* @brief Retrieves the CANN buffer type for a specified device.
|
| 64 |
+
*
|
| 65 |
+
* This function initializes and returns the buffer type interface associated
|
| 66 |
+
* with the given device. It ensures thread-safe access using a mutex.
|
| 67 |
+
*
|
| 68 |
+
* @param device The device index for which to retrieve the buffer type.
|
| 69 |
+
* @return A pointer to the buffer type interface for the specified device, or
|
| 70 |
+
* nullptr if the device index is out of range.
|
| 71 |
+
*/
|
| 72 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t
|
| 73 |
+
ggml_backend_cann_buffer_type(int32_t device);
|
| 74 |
+
|
| 75 |
+
/**
|
| 76 |
+
* @brief Retrieves the number of CANN devices available.
|
| 77 |
+
*
|
| 78 |
+
* This function returns the number of CANN devices available based on
|
| 79 |
+
* information obtained from `ggml_cann_info()`.
|
| 80 |
+
*
|
| 81 |
+
* @return The number of CANN devices available.
|
| 82 |
+
*/
|
| 83 |
+
GGML_BACKEND_API int32_t ggml_backend_cann_get_device_count(void);
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* @brief pinned host buffer for use with the CPU backend for faster copies between CPU and NPU.
|
| 87 |
+
*
|
| 88 |
+
* @return A pointer to the host buffer type interface.
|
| 89 |
+
*/
|
| 90 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_cann_host_buffer_type(void);
|
| 91 |
+
|
| 92 |
+
/**
|
| 93 |
+
* @brief Retrieves the description of a specific CANN device.
|
| 94 |
+
*
|
| 95 |
+
* This function sets the specified device, retrieves the SoC name,
|
| 96 |
+
* and writes it into the provided description buffer.
|
| 97 |
+
*
|
| 98 |
+
* @param device The device index to retrieve the description for.
|
| 99 |
+
* @param description Pointer to a buffer where the description will be written.
|
| 100 |
+
* @param description_size Size of the description buffer.
|
| 101 |
+
*/
|
| 102 |
+
GGML_BACKEND_API void ggml_backend_cann_get_device_description(
|
| 103 |
+
int32_t device, char* description, size_t description_size);
|
| 104 |
+
|
| 105 |
+
/**
|
| 106 |
+
* @brief Retrieves the memory information of a specific CANN device.
|
| 107 |
+
*
|
| 108 |
+
* This function sets the specified device, retrieves the free and total
|
| 109 |
+
* memory information of the specified type (ACL_HBM_MEM), and stores them
|
| 110 |
+
* in the provided pointers.
|
| 111 |
+
*
|
| 112 |
+
* @param device The device index to retrieve memory information for.
|
| 113 |
+
* @param free Pointer to a variable where the free memory size will be stored.
|
| 114 |
+
* @param total Pointer to a variable where the total memory size will be
|
| 115 |
+
* stored.
|
| 116 |
+
*/
|
| 117 |
+
GGML_BACKEND_API void ggml_backend_cann_get_device_memory(int32_t device,
|
| 118 |
+
size_t* free,
|
| 119 |
+
size_t* total);
|
| 120 |
+
|
| 121 |
+
#ifdef __cplusplus
|
| 122 |
+
}
|
| 123 |
+
#endif
|
vendor/include/ggml-cpp.h
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#ifndef __cplusplus
|
| 4 |
+
#error "This header is for C++ only"
|
| 5 |
+
#endif
|
| 6 |
+
|
| 7 |
+
#include "ggml.h"
|
| 8 |
+
#include "ggml-alloc.h"
|
| 9 |
+
#include "ggml-backend.h"
|
| 10 |
+
#include "gguf.h"
|
| 11 |
+
#include <memory>
|
| 12 |
+
|
| 13 |
+
// Smart pointers for ggml types
|
| 14 |
+
|
| 15 |
+
// ggml
|
| 16 |
+
|
| 17 |
+
struct ggml_context_deleter { void operator()(ggml_context * ctx) { ggml_free(ctx); } };
|
| 18 |
+
struct gguf_context_deleter { void operator()(gguf_context * ctx) { gguf_free(ctx); } };
|
| 19 |
+
|
| 20 |
+
typedef std::unique_ptr<ggml_context, ggml_context_deleter> ggml_context_ptr;
|
| 21 |
+
typedef std::unique_ptr<gguf_context, gguf_context_deleter> gguf_context_ptr;
|
| 22 |
+
|
| 23 |
+
// ggml-alloc
|
| 24 |
+
|
| 25 |
+
struct ggml_gallocr_deleter { void operator()(ggml_gallocr_t galloc) { ggml_gallocr_free(galloc); } };
|
| 26 |
+
|
| 27 |
+
typedef std::unique_ptr<ggml_gallocr, ggml_gallocr_deleter> ggml_gallocr_ptr;
|
| 28 |
+
|
| 29 |
+
// ggml-backend
|
| 30 |
+
|
| 31 |
+
struct ggml_backend_deleter { void operator()(ggml_backend_t backend) { ggml_backend_free(backend); } };
|
| 32 |
+
struct ggml_backend_buffer_deleter { void operator()(ggml_backend_buffer_t buffer) { ggml_backend_buffer_free(buffer); } };
|
| 33 |
+
struct ggml_backend_event_deleter { void operator()(ggml_backend_event_t event) { ggml_backend_event_free(event); } };
|
| 34 |
+
struct ggml_backend_sched_deleter { void operator()(ggml_backend_sched_t sched) { ggml_backend_sched_free(sched); } };
|
| 35 |
+
|
| 36 |
+
typedef std::unique_ptr<ggml_backend, ggml_backend_deleter> ggml_backend_ptr;
|
| 37 |
+
typedef std::unique_ptr<ggml_backend_buffer, ggml_backend_buffer_deleter> ggml_backend_buffer_ptr;
|
| 38 |
+
typedef std::unique_ptr<ggml_backend_event, ggml_backend_event_deleter> ggml_backend_event_ptr;
|
| 39 |
+
typedef std::unique_ptr<ggml_backend_sched, ggml_backend_sched_deleter> ggml_backend_sched_ptr;
|
vendor/include/ggml-cpu.h
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
// the compute plan that needs to be prepared for ggml_graph_compute()
|
| 11 |
+
// since https://github.com/ggml-org/ggml/issues/287
|
| 12 |
+
struct ggml_cplan {
|
| 13 |
+
size_t work_size; // size of work buffer, calculated by `ggml_graph_plan()`
|
| 14 |
+
uint8_t * work_data; // work buffer, to be allocated by caller before calling to `ggml_graph_compute()`
|
| 15 |
+
|
| 16 |
+
int n_threads;
|
| 17 |
+
struct ggml_threadpool * threadpool;
|
| 18 |
+
|
| 19 |
+
// abort ggml_graph_compute when true
|
| 20 |
+
ggml_abort_callback abort_callback;
|
| 21 |
+
void * abort_callback_data;
|
| 22 |
+
|
| 23 |
+
// use only reference implementations
|
| 24 |
+
bool use_ref;
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
// numa strategies
|
| 28 |
+
enum ggml_numa_strategy {
|
| 29 |
+
GGML_NUMA_STRATEGY_DISABLED = 0,
|
| 30 |
+
GGML_NUMA_STRATEGY_DISTRIBUTE = 1,
|
| 31 |
+
GGML_NUMA_STRATEGY_ISOLATE = 2,
|
| 32 |
+
GGML_NUMA_STRATEGY_NUMACTL = 3,
|
| 33 |
+
GGML_NUMA_STRATEGY_MIRROR = 4,
|
| 34 |
+
GGML_NUMA_STRATEGY_COUNT
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
GGML_BACKEND_API void ggml_numa_init(enum ggml_numa_strategy numa); // call once for better performance on NUMA systems
|
| 38 |
+
GGML_BACKEND_API bool ggml_is_numa(void); // true if init detected that system has >1 NUMA node
|
| 39 |
+
|
| 40 |
+
GGML_BACKEND_API struct ggml_tensor * ggml_new_i32(struct ggml_context * ctx, int32_t value);
|
| 41 |
+
GGML_BACKEND_API struct ggml_tensor * ggml_new_f32(struct ggml_context * ctx, float value);
|
| 42 |
+
|
| 43 |
+
GGML_BACKEND_API struct ggml_tensor * ggml_set_i32 (struct ggml_tensor * tensor, int32_t value);
|
| 44 |
+
GGML_BACKEND_API struct ggml_tensor * ggml_set_f32 (struct ggml_tensor * tensor, float value);
|
| 45 |
+
|
| 46 |
+
GGML_BACKEND_API int32_t ggml_get_i32_1d(const struct ggml_tensor * tensor, int i);
|
| 47 |
+
GGML_BACKEND_API void ggml_set_i32_1d(const struct ggml_tensor * tensor, int i, int32_t value);
|
| 48 |
+
|
| 49 |
+
GGML_BACKEND_API int32_t ggml_get_i32_nd(const struct ggml_tensor * tensor, int i0, int i1, int i2, int i3);
|
| 50 |
+
GGML_BACKEND_API void ggml_set_i32_nd(const struct ggml_tensor * tensor, int i0, int i1, int i2, int i3, int32_t value);
|
| 51 |
+
|
| 52 |
+
GGML_BACKEND_API float ggml_get_f32_1d(const struct ggml_tensor * tensor, int i);
|
| 53 |
+
GGML_BACKEND_API void ggml_set_f32_1d(const struct ggml_tensor * tensor, int i, float value);
|
| 54 |
+
|
| 55 |
+
GGML_BACKEND_API float ggml_get_f32_nd(const struct ggml_tensor * tensor, int i0, int i1, int i2, int i3);
|
| 56 |
+
GGML_BACKEND_API void ggml_set_f32_nd(const struct ggml_tensor * tensor, int i0, int i1, int i2, int i3, float value);
|
| 57 |
+
|
| 58 |
+
GGML_BACKEND_API struct ggml_threadpool * ggml_threadpool_new (struct ggml_threadpool_params * params);
|
| 59 |
+
GGML_BACKEND_API void ggml_threadpool_free (struct ggml_threadpool * threadpool);
|
| 60 |
+
GGML_BACKEND_API int ggml_threadpool_get_n_threads (struct ggml_threadpool * threadpool);
|
| 61 |
+
GGML_BACKEND_API void ggml_threadpool_pause (struct ggml_threadpool * threadpool);
|
| 62 |
+
GGML_BACKEND_API void ggml_threadpool_resume (struct ggml_threadpool * threadpool);
|
| 63 |
+
|
| 64 |
+
// ggml_graph_plan() has to be called before ggml_graph_compute()
|
| 65 |
+
// when plan.work_size > 0, caller must allocate memory for plan.work_data
|
| 66 |
+
GGML_BACKEND_API struct ggml_cplan ggml_graph_plan(
|
| 67 |
+
const struct ggml_cgraph * cgraph,
|
| 68 |
+
int n_threads, /* = GGML_DEFAULT_N_THREADS */
|
| 69 |
+
struct ggml_threadpool * threadpool /* = NULL */ );
|
| 70 |
+
GGML_BACKEND_API enum ggml_status ggml_graph_compute(struct ggml_cgraph * cgraph, struct ggml_cplan * cplan);
|
| 71 |
+
|
| 72 |
+
// same as ggml_graph_compute() but the work data is allocated as a part of the context
|
| 73 |
+
// note: the drawback of this API is that you must have ensured that the context has enough memory for the work data
|
| 74 |
+
GGML_BACKEND_API enum ggml_status ggml_graph_compute_with_ctx(struct ggml_context * ctx, struct ggml_cgraph * cgraph, int n_threads);
|
| 75 |
+
|
| 76 |
+
//
|
| 77 |
+
// system info
|
| 78 |
+
//
|
| 79 |
+
|
| 80 |
+
// x86
|
| 81 |
+
GGML_BACKEND_API int ggml_cpu_has_sse3 (void);
|
| 82 |
+
GGML_BACKEND_API int ggml_cpu_has_ssse3 (void);
|
| 83 |
+
GGML_BACKEND_API int ggml_cpu_has_avx (void);
|
| 84 |
+
GGML_BACKEND_API int ggml_cpu_has_avx_vnni (void);
|
| 85 |
+
GGML_BACKEND_API int ggml_cpu_has_avx2 (void);
|
| 86 |
+
GGML_BACKEND_API int ggml_cpu_has_bmi2 (void);
|
| 87 |
+
GGML_BACKEND_API int ggml_cpu_has_f16c (void);
|
| 88 |
+
GGML_BACKEND_API int ggml_cpu_has_fma (void);
|
| 89 |
+
GGML_BACKEND_API int ggml_cpu_has_avx512 (void);
|
| 90 |
+
GGML_BACKEND_API int ggml_cpu_has_avx512_vbmi(void);
|
| 91 |
+
GGML_BACKEND_API int ggml_cpu_has_avx512_vnni(void);
|
| 92 |
+
GGML_BACKEND_API int ggml_cpu_has_avx512_bf16(void);
|
| 93 |
+
GGML_BACKEND_API int ggml_cpu_has_amx_int8 (void);
|
| 94 |
+
// ARM
|
| 95 |
+
GGML_BACKEND_API int ggml_cpu_has_neon (void);
|
| 96 |
+
GGML_BACKEND_API int ggml_cpu_has_arm_fma (void);
|
| 97 |
+
GGML_BACKEND_API int ggml_cpu_has_fp16_va (void);
|
| 98 |
+
GGML_BACKEND_API int ggml_cpu_has_dotprod (void);
|
| 99 |
+
GGML_BACKEND_API int ggml_cpu_has_matmul_int8(void);
|
| 100 |
+
GGML_BACKEND_API int ggml_cpu_has_sve (void);
|
| 101 |
+
GGML_BACKEND_API int ggml_cpu_get_sve_cnt (void); // sve vector length in bytes
|
| 102 |
+
GGML_BACKEND_API int ggml_cpu_has_sme (void);
|
| 103 |
+
GGML_BACKEND_API int ggml_cpu_has_sme2 (void);
|
| 104 |
+
// other
|
| 105 |
+
GGML_BACKEND_API int ggml_cpu_has_riscv_v (void);
|
| 106 |
+
GGML_BACKEND_API int ggml_cpu_get_rvv_vlen (void); // risc-v vector length in bytes
|
| 107 |
+
GGML_BACKEND_API int ggml_cpu_has_vsx (void);
|
| 108 |
+
GGML_BACKEND_API int ggml_cpu_has_vxe (void);
|
| 109 |
+
GGML_BACKEND_API int ggml_cpu_has_wasm_simd (void);
|
| 110 |
+
GGML_BACKEND_API int ggml_cpu_has_llamafile (void);
|
| 111 |
+
|
| 112 |
+
// Internal types and functions exposed for tests and benchmarks
|
| 113 |
+
|
| 114 |
+
typedef void (*ggml_vec_dot_t) (int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT x, size_t bx,
|
| 115 |
+
const void * GGML_RESTRICT y, size_t by, int nrc);
|
| 116 |
+
|
| 117 |
+
struct ggml_type_traits_cpu {
|
| 118 |
+
ggml_from_float_t from_float;
|
| 119 |
+
ggml_vec_dot_t vec_dot;
|
| 120 |
+
enum ggml_type vec_dot_type;
|
| 121 |
+
int64_t nrows; // number of rows to process simultaneously
|
| 122 |
+
};
|
| 123 |
+
|
| 124 |
+
GGML_BACKEND_API const struct ggml_type_traits_cpu * ggml_get_type_traits_cpu(enum ggml_type type);
|
| 125 |
+
|
| 126 |
+
GGML_BACKEND_API void ggml_cpu_init(void);
|
| 127 |
+
|
| 128 |
+
//
|
| 129 |
+
// CPU backend
|
| 130 |
+
//
|
| 131 |
+
|
| 132 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_cpu_init(void);
|
| 133 |
+
|
| 134 |
+
GGML_BACKEND_API bool ggml_backend_is_cpu (ggml_backend_t backend);
|
| 135 |
+
GGML_BACKEND_API void ggml_backend_cpu_set_n_threads (ggml_backend_t backend_cpu, int n_threads);
|
| 136 |
+
GGML_BACKEND_API void ggml_backend_cpu_set_threadpool (ggml_backend_t backend_cpu, ggml_threadpool_t threadpool);
|
| 137 |
+
GGML_BACKEND_API void ggml_backend_cpu_set_abort_callback(ggml_backend_t backend_cpu, ggml_abort_callback abort_callback, void * abort_callback_data);
|
| 138 |
+
|
| 139 |
+
GGML_BACKEND_API void ggml_backend_cpu_set_use_ref(ggml_backend_t backend_cpu, bool use_ref);
|
| 140 |
+
|
| 141 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_cpu_reg(void);
|
| 142 |
+
|
| 143 |
+
GGML_BACKEND_API void ggml_cpu_fp32_to_fp32(const float *, float *, int64_t);
|
| 144 |
+
GGML_BACKEND_API void ggml_cpu_fp32_to_i32 (const float *, int32_t *, int64_t);
|
| 145 |
+
GGML_BACKEND_API void ggml_cpu_fp32_to_fp16(const float *, ggml_fp16_t *, int64_t);
|
| 146 |
+
GGML_BACKEND_API void ggml_cpu_fp16_to_fp32(const ggml_fp16_t *, float *, int64_t);
|
| 147 |
+
GGML_BACKEND_API void ggml_cpu_fp32_to_bf16(const float *, ggml_bf16_t *, int64_t);
|
| 148 |
+
GGML_BACKEND_API void ggml_cpu_bf16_to_fp32(const ggml_bf16_t *, float *, int64_t);
|
| 149 |
+
|
| 150 |
+
#ifdef __cplusplus
|
| 151 |
+
}
|
| 152 |
+
#endif
|
vendor/include/ggml-et.h
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
#define GGML_ET_NAME "ET"
|
| 11 |
+
|
| 12 |
+
// backend API
|
| 13 |
+
GGML_BACKEND_API ggml_guid_t ggml_backend_et_guid(void);
|
| 14 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_et_init(size_t devidx);
|
| 15 |
+
|
| 16 |
+
GGML_BACKEND_API bool ggml_backend_is_et(ggml_backend_t backend);
|
| 17 |
+
GGML_BACKEND_API int ggml_backend_et_get_device_count(void);
|
| 18 |
+
GGML_BACKEND_API void ggml_backend_et_get_device_description(int devidx, char * description, size_t description_size);
|
| 19 |
+
GGML_BACKEND_API void ggml_backend_et_get_device_memory(int devidx, size_t * free, size_t * total);
|
| 20 |
+
|
| 21 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_et_buffer_type(size_t dev_num);
|
| 22 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_et_host_buffer_type(void);
|
| 23 |
+
|
| 24 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_et_reg(void);
|
| 25 |
+
|
| 26 |
+
#ifdef __cplusplus
|
| 27 |
+
}
|
| 28 |
+
#endif
|
vendor/include/ggml-hexagon.h
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
// backend API
|
| 11 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_hexagon_init(void);
|
| 12 |
+
|
| 13 |
+
GGML_BACKEND_API bool ggml_backend_is_hexagon(ggml_backend_t backend);
|
| 14 |
+
|
| 15 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_hexagon_reg(void);
|
| 16 |
+
|
| 17 |
+
#ifdef __cplusplus
|
| 18 |
+
}
|
| 19 |
+
#endif
|
vendor/include/ggml-metal.h
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Note: this description is outdated
|
| 2 |
+
//
|
| 3 |
+
// An interface allowing to compute ggml_cgraph with Metal
|
| 4 |
+
//
|
| 5 |
+
// This is a fully functional interface that extends ggml with GPU support for Apple devices.
|
| 6 |
+
// A similar interface can be created for other GPU backends (e.g. Vulkan, CUDA, etc.)
|
| 7 |
+
//
|
| 8 |
+
// How it works?
|
| 9 |
+
//
|
| 10 |
+
// As long as your program can create and evaluate a ggml_cgraph on the CPU, you can use this
|
| 11 |
+
// interface to evaluate the same graph on the GPU. Instead of using ggml_graph_compute(), you
|
| 12 |
+
// use ggml_metal_graph_compute() (or ggml_vulkan_graph_compute(), etc.)
|
| 13 |
+
//
|
| 14 |
+
// You only need to make sure that all memory buffers that you used during the graph creation
|
| 15 |
+
// are mapped to the device memory with the ggml_metal_add_buffer() function. This mapping is
|
| 16 |
+
// used during the graph evaluation to determine the arguments of the compute kernels.
|
| 17 |
+
//
|
| 18 |
+
// Synchronization between device and host memory (for example for input and output tensors)
|
| 19 |
+
// is done with the ggml_metal_set_tensor() and ggml_metal_get_tensor() functions.
|
| 20 |
+
//
|
| 21 |
+
|
| 22 |
+
#pragma once
|
| 23 |
+
|
| 24 |
+
#include "ggml.h"
|
| 25 |
+
#include "ggml-backend.h"
|
| 26 |
+
|
| 27 |
+
#include <stddef.h>
|
| 28 |
+
#include <stdbool.h>
|
| 29 |
+
|
| 30 |
+
struct ggml_tensor;
|
| 31 |
+
struct ggml_cgraph;
|
| 32 |
+
|
| 33 |
+
#ifdef __cplusplus
|
| 34 |
+
extern "C" {
|
| 35 |
+
#endif
|
| 36 |
+
|
| 37 |
+
//
|
| 38 |
+
// backend API
|
| 39 |
+
// user-code should use only these functions
|
| 40 |
+
//
|
| 41 |
+
|
| 42 |
+
// TODO: remove in the future
|
| 43 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_metal_init(void);
|
| 44 |
+
|
| 45 |
+
GGML_BACKEND_API bool ggml_backend_is_metal(ggml_backend_t backend);
|
| 46 |
+
|
| 47 |
+
GGML_BACKEND_API void ggml_backend_metal_set_abort_callback(ggml_backend_t backend, ggml_abort_callback abort_callback, void * user_data);
|
| 48 |
+
|
| 49 |
+
// helper to check if the device supports a specific family
|
| 50 |
+
// ideally, the user code should be doing these checks
|
| 51 |
+
// ref: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
| 52 |
+
GGML_BACKEND_API bool ggml_backend_metal_supports_family(ggml_backend_t backend, int family);
|
| 53 |
+
|
| 54 |
+
// capture all command buffers committed the next time `ggml_backend_graph_compute` is called
|
| 55 |
+
GGML_BACKEND_API void ggml_backend_metal_capture_next_compute(ggml_backend_t backend);
|
| 56 |
+
|
| 57 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_metal_reg(void);
|
| 58 |
+
|
| 59 |
+
#ifdef __cplusplus
|
| 60 |
+
}
|
| 61 |
+
#endif
|
vendor/include/ggml-opencl.h
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef GGML_OPENCL_H
|
| 2 |
+
#define GGML_OPENCL_H
|
| 3 |
+
|
| 4 |
+
#include "ggml.h"
|
| 5 |
+
#include "ggml-backend.h"
|
| 6 |
+
|
| 7 |
+
#ifdef __cplusplus
|
| 8 |
+
extern "C" {
|
| 9 |
+
#endif
|
| 10 |
+
|
| 11 |
+
//
|
| 12 |
+
// backend API
|
| 13 |
+
//
|
| 14 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_opencl_init(void);
|
| 15 |
+
GGML_BACKEND_API bool ggml_backend_is_opencl(ggml_backend_t backend);
|
| 16 |
+
|
| 17 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_opencl_buffer_type(void);
|
| 18 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_opencl_host_buffer_type(void);
|
| 19 |
+
|
| 20 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_opencl_reg(void);
|
| 21 |
+
|
| 22 |
+
#ifdef __cplusplus
|
| 23 |
+
}
|
| 24 |
+
#endif
|
| 25 |
+
|
| 26 |
+
#endif // GGML_OPENCL_H
|
vendor/include/ggml-openvino.h
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml-backend.h"
|
| 4 |
+
|
| 5 |
+
#include <cstring>
|
| 6 |
+
|
| 7 |
+
#ifdef __cplusplus
|
| 8 |
+
extern "C" {
|
| 9 |
+
#endif
|
| 10 |
+
|
| 11 |
+
#define GGML_OPENVINO_NAME "OPENVINO"
|
| 12 |
+
|
| 13 |
+
// backend API
|
| 14 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_openvino_init(int device);
|
| 15 |
+
|
| 16 |
+
GGML_BACKEND_API bool ggml_backend_is_openvino(ggml_backend_t backend);
|
| 17 |
+
|
| 18 |
+
GGML_BACKEND_API bool ggml_backend_buffer_is_openvino(ggml_backend_buffer_t buffer);
|
| 19 |
+
|
| 20 |
+
GGML_BACKEND_API bool ggml_backend_buft_is_openvino(ggml_backend_buffer_type_t buft);
|
| 21 |
+
|
| 22 |
+
GGML_BACKEND_API bool ggml_backend_buft_is_openvino_host(ggml_backend_buffer_type_t buft);
|
| 23 |
+
|
| 24 |
+
GGML_BACKEND_API size_t ggml_backend_openvino_buffer_get_ctx_id(ggml_backend_buffer_t buffer);
|
| 25 |
+
|
| 26 |
+
// device buffer
|
| 27 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_openvino_buffer_type(int device);
|
| 28 |
+
|
| 29 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_openvino_host_buffer_type(int device);
|
| 30 |
+
|
| 31 |
+
GGML_BACKEND_API int ggml_backend_openvino_get_device_count(void);
|
| 32 |
+
|
| 33 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_openvino_reg(void);
|
| 34 |
+
|
| 35 |
+
#ifdef __cplusplus
|
| 36 |
+
}
|
| 37 |
+
#endif
|
vendor/include/ggml-opt.h
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// This file contains functionality for training models using GGML.
|
| 2 |
+
// It is not strictly needed vs. just vanilla GGML but it provides a more high-level interface for common needs such as datasets.
|
| 3 |
+
// At the bottom of this file especially there are relatively high-level functions that are suitable use or adaptation in user code.
|
| 4 |
+
//
|
| 5 |
+
// Module maintainer: Johannes Gäßler (@JohannesGaessler, johannesg@5d6.de)
|
| 6 |
+
|
| 7 |
+
#pragma once
|
| 8 |
+
|
| 9 |
+
#include "ggml.h"
|
| 10 |
+
#include "ggml-backend.h"
|
| 11 |
+
|
| 12 |
+
#include <stdint.h>
|
| 13 |
+
|
| 14 |
+
#ifdef __cplusplus
|
| 15 |
+
extern "C" {
|
| 16 |
+
#endif
|
| 17 |
+
|
| 18 |
+
struct ggml_opt_dataset;
|
| 19 |
+
struct ggml_opt_context;
|
| 20 |
+
struct ggml_opt_result;
|
| 21 |
+
|
| 22 |
+
typedef struct ggml_opt_dataset * ggml_opt_dataset_t;
|
| 23 |
+
typedef struct ggml_opt_context * ggml_opt_context_t;
|
| 24 |
+
typedef struct ggml_opt_result * ggml_opt_result_t;
|
| 25 |
+
|
| 26 |
+
// ====== Loss ======
|
| 27 |
+
|
| 28 |
+
// built-in loss types, i.e. the built-in quantities minimized by the optimizer
|
| 29 |
+
// custom loss types can be defined via mean or sum which simply reduce the outputs for all datapoints to a single value
|
| 30 |
+
enum ggml_opt_loss_type {
|
| 31 |
+
GGML_OPT_LOSS_TYPE_MEAN,
|
| 32 |
+
GGML_OPT_LOSS_TYPE_SUM,
|
| 33 |
+
GGML_OPT_LOSS_TYPE_CROSS_ENTROPY,
|
| 34 |
+
GGML_OPT_LOSS_TYPE_MEAN_SQUARED_ERROR,
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
// ====== Dataset ======
|
| 38 |
+
|
| 39 |
+
GGML_API ggml_opt_dataset_t ggml_opt_dataset_init(
|
| 40 |
+
enum ggml_type type_data, // the type for the internal data tensor
|
| 41 |
+
enum ggml_type type_label, // the type for the internal labels tensor
|
| 42 |
+
int64_t ne_datapoint, // number of elements per datapoint
|
| 43 |
+
int64_t ne_label, // number of elements per label
|
| 44 |
+
int64_t ndata, // total number of datapoints/labels
|
| 45 |
+
int64_t ndata_shard); // number of datapoints/labels per shard (unit at which the dataset is shuffled/copied)
|
| 46 |
+
GGML_API void ggml_opt_dataset_free(ggml_opt_dataset_t dataset);
|
| 47 |
+
|
| 48 |
+
// get underlying tensors that store the data
|
| 49 |
+
GGML_API int64_t ggml_opt_dataset_ndata (ggml_opt_dataset_t dataset);
|
| 50 |
+
GGML_API struct ggml_tensor * ggml_opt_dataset_data (ggml_opt_dataset_t dataset); // shape = [ne_datapoint, ndata]
|
| 51 |
+
GGML_API struct ggml_tensor * ggml_opt_dataset_labels(ggml_opt_dataset_t dataset); // shape = [nd_label, ndata]
|
| 52 |
+
|
| 53 |
+
// shuffle idata first datapoints from dataset with RNG from opt_ctx, shuffle all datapoints if idata is negative
|
| 54 |
+
GGML_API void ggml_opt_dataset_shuffle(ggml_opt_context_t opt_ctx, ggml_opt_dataset_t dataset, int64_t idata);
|
| 55 |
+
|
| 56 |
+
// get batch at position ibatch from dataset and copy the data to data_batch and labels_batch
|
| 57 |
+
GGML_API void ggml_opt_dataset_get_batch(
|
| 58 |
+
ggml_opt_dataset_t dataset,
|
| 59 |
+
struct ggml_tensor * data_batch, // shape = [ne_datapoint, ndata_batch]
|
| 60 |
+
struct ggml_tensor * labels_batch, // shape = [ne_label, ndata_batch]
|
| 61 |
+
int64_t ibatch);
|
| 62 |
+
GGML_API void ggml_opt_dataset_get_batch_host(
|
| 63 |
+
ggml_opt_dataset_t dataset,
|
| 64 |
+
void * data_batch,
|
| 65 |
+
size_t nb_data_batch,
|
| 66 |
+
void * labels_batch,
|
| 67 |
+
int64_t ibatch);
|
| 68 |
+
|
| 69 |
+
// ====== Model / Context ======
|
| 70 |
+
|
| 71 |
+
enum ggml_opt_build_type {
|
| 72 |
+
GGML_OPT_BUILD_TYPE_FORWARD = 10,
|
| 73 |
+
GGML_OPT_BUILD_TYPE_GRAD = 20,
|
| 74 |
+
GGML_OPT_BUILD_TYPE_OPT = 30,
|
| 75 |
+
};
|
| 76 |
+
|
| 77 |
+
enum ggml_opt_optimizer_type {
|
| 78 |
+
GGML_OPT_OPTIMIZER_TYPE_ADAMW,
|
| 79 |
+
GGML_OPT_OPTIMIZER_TYPE_SGD,
|
| 80 |
+
|
| 81 |
+
GGML_OPT_OPTIMIZER_TYPE_COUNT
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
+
// parameters that control which optimizer is used and how said optimizer tries to find the minimal loss
|
| 85 |
+
struct ggml_opt_optimizer_params {
|
| 86 |
+
struct {
|
| 87 |
+
float alpha; // learning rate
|
| 88 |
+
float beta1; // first AdamW momentum
|
| 89 |
+
float beta2; // second AdamW momentum
|
| 90 |
+
float eps; // epsilon for numerical stability
|
| 91 |
+
float wd; // weight decay - 0.0f to disable
|
| 92 |
+
} adamw;
|
| 93 |
+
struct {
|
| 94 |
+
float alpha; // learning rate
|
| 95 |
+
float wd; // weight decay
|
| 96 |
+
} sgd;
|
| 97 |
+
};
|
| 98 |
+
|
| 99 |
+
// callback to calculate optimizer parameters prior to a backward pass
|
| 100 |
+
// userdata can be used to pass arbitrary data
|
| 101 |
+
typedef struct ggml_opt_optimizer_params (*ggml_opt_get_optimizer_params)(void * userdata);
|
| 102 |
+
|
| 103 |
+
// returns the default optimizer params (constant, hard-coded values)
|
| 104 |
+
// userdata is not used
|
| 105 |
+
GGML_API struct ggml_opt_optimizer_params ggml_opt_get_default_optimizer_params(void * userdata);
|
| 106 |
+
|
| 107 |
+
// casts userdata to ggml_opt_optimizer_params and returns it
|
| 108 |
+
GGML_API struct ggml_opt_optimizer_params ggml_opt_get_constant_optimizer_params(void * userdata);
|
| 109 |
+
|
| 110 |
+
// parameters for initializing a new optimization context
|
| 111 |
+
struct ggml_opt_params {
|
| 112 |
+
ggml_backend_sched_t backend_sched; // defines which backends are used to construct the compute graphs
|
| 113 |
+
|
| 114 |
+
// by default the forward graph needs to be reconstructed for each eval
|
| 115 |
+
// if ctx_compute, inputs, and outputs are set the graphs are instead allocated statically
|
| 116 |
+
struct ggml_context * ctx_compute;
|
| 117 |
+
struct ggml_tensor * inputs;
|
| 118 |
+
struct ggml_tensor * outputs;
|
| 119 |
+
|
| 120 |
+
enum ggml_opt_loss_type loss_type;
|
| 121 |
+
enum ggml_opt_build_type build_type;
|
| 122 |
+
|
| 123 |
+
int32_t opt_period; // after how many gradient accumulation steps an optimizer step should be done
|
| 124 |
+
|
| 125 |
+
ggml_opt_get_optimizer_params get_opt_pars; // callback for calculating optimizer parameters
|
| 126 |
+
void * get_opt_pars_ud; // userdata for calculating optimizer parameters
|
| 127 |
+
|
| 128 |
+
// only GGML_OPT_OPTIMIZER_TYPE_ADAMW needs m, v momenta per parameter tensor
|
| 129 |
+
enum ggml_opt_optimizer_type optimizer;
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
// get parameters for an optimization context with defaults set where possible
|
| 133 |
+
// parameters for which no sensible defaults exist are supplied as arguments to this function
|
| 134 |
+
GGML_API struct ggml_opt_params ggml_opt_default_params(
|
| 135 |
+
ggml_backend_sched_t backend_sched,
|
| 136 |
+
enum ggml_opt_loss_type loss_type);
|
| 137 |
+
|
| 138 |
+
GGML_API ggml_opt_context_t ggml_opt_init(struct ggml_opt_params params);
|
| 139 |
+
GGML_API void ggml_opt_free(ggml_opt_context_t opt_ctx);
|
| 140 |
+
|
| 141 |
+
// set gradients to zero, initialize loss, and optionally reset the optimizer
|
| 142 |
+
GGML_API void ggml_opt_reset(ggml_opt_context_t opt_ctx, bool optimizer);
|
| 143 |
+
|
| 144 |
+
GGML_API bool ggml_opt_static_graphs(ggml_opt_context_t opt_ctx); // whether the graphs are allocated_statically
|
| 145 |
+
|
| 146 |
+
// get underlying tensors that store data
|
| 147 |
+
// if not using static graphs these pointers become invalid with the next call to ggml_opt_alloc
|
| 148 |
+
GGML_API struct ggml_tensor * ggml_opt_inputs( ggml_opt_context_t opt_ctx); // forward graph input tensor
|
| 149 |
+
GGML_API struct ggml_tensor * ggml_opt_outputs( ggml_opt_context_t opt_ctx); // forward graph output tensor
|
| 150 |
+
GGML_API struct ggml_tensor * ggml_opt_labels( ggml_opt_context_t opt_ctx); // labels to compare outputs against
|
| 151 |
+
GGML_API struct ggml_tensor * ggml_opt_loss( ggml_opt_context_t opt_ctx); // scalar tensor that contains the loss
|
| 152 |
+
GGML_API struct ggml_tensor * ggml_opt_pred( ggml_opt_context_t opt_ctx); // predictions made by outputs
|
| 153 |
+
GGML_API struct ggml_tensor * ggml_opt_ncorrect(ggml_opt_context_t opt_ctx); // number of matching predictions between outputs and labels
|
| 154 |
+
|
| 155 |
+
// get the gradient accumulator for a node from the forward graph
|
| 156 |
+
GGML_API struct ggml_tensor * ggml_opt_grad_acc(ggml_opt_context_t opt_ctx, struct ggml_tensor * node);
|
| 157 |
+
|
| 158 |
+
GGML_API enum ggml_opt_optimizer_type ggml_opt_context_optimizer_type(ggml_opt_context_t); //TODO consistent naming scheme
|
| 159 |
+
|
| 160 |
+
GGML_API const char * ggml_opt_optimizer_name(enum ggml_opt_optimizer_type);
|
| 161 |
+
|
| 162 |
+
// ====== Optimization Result ======
|
| 163 |
+
|
| 164 |
+
GGML_API ggml_opt_result_t ggml_opt_result_init(void);
|
| 165 |
+
GGML_API void ggml_opt_result_free(ggml_opt_result_t result);
|
| 166 |
+
GGML_API void ggml_opt_result_reset(ggml_opt_result_t result);
|
| 167 |
+
|
| 168 |
+
// get data from result, uncertainties are optional and can be ignored by passing NULL
|
| 169 |
+
GGML_API void ggml_opt_result_ndata( ggml_opt_result_t result, int64_t * ndata); // writes 1 value, number of datapoints
|
| 170 |
+
GGML_API void ggml_opt_result_loss( ggml_opt_result_t result, double * loss, double * unc); // writes 1 value
|
| 171 |
+
GGML_API void ggml_opt_result_pred( ggml_opt_result_t result, int32_t * pred); // writes ndata values
|
| 172 |
+
GGML_API void ggml_opt_result_accuracy(ggml_opt_result_t result, double * accuracy, double * unc); // writes 1 value
|
| 173 |
+
|
| 174 |
+
// ====== Computation ======
|
| 175 |
+
|
| 176 |
+
// if not using static graphs, this function must be called prior to ggml_opt_alloc
|
| 177 |
+
GGML_API void ggml_opt_prepare_alloc(
|
| 178 |
+
ggml_opt_context_t opt_ctx,
|
| 179 |
+
struct ggml_context * ctx_compute,
|
| 180 |
+
struct ggml_cgraph * gf,
|
| 181 |
+
struct ggml_tensor * inputs,
|
| 182 |
+
struct ggml_tensor * outputs);
|
| 183 |
+
|
| 184 |
+
// allocate the next graph for evaluation, either forward or forward + backward
|
| 185 |
+
// must be called exactly once prior to calling ggml_opt_eval
|
| 186 |
+
GGML_API void ggml_opt_alloc(ggml_opt_context_t opt_ctx, bool backward);
|
| 187 |
+
|
| 188 |
+
// do forward pass, increment result if not NULL, do backward pass if allocated
|
| 189 |
+
GGML_API void ggml_opt_eval(ggml_opt_context_t opt_ctx, ggml_opt_result_t result);
|
| 190 |
+
|
| 191 |
+
// ############################################################################
|
| 192 |
+
// ## The high-level functions start here. They do not depend on any private ##
|
| 193 |
+
// ## functions or structs and can be copied to and adapted for user code. ##
|
| 194 |
+
// ############################################################################
|
| 195 |
+
|
| 196 |
+
// ====== Intended Usage ======
|
| 197 |
+
//
|
| 198 |
+
// 1. Select the appropriate loss for your problem.
|
| 199 |
+
// 2. Create a dataset and set the data for the "data" tensor. Also set the "labels" tensor if your loss needs them.
|
| 200 |
+
// Setting the shard size to 1 will be fine, it's the granularity with which data is shuffled/loaded (bigger values are faster).
|
| 201 |
+
// 3. Create a GGML graph for your model with no_alloc == true. Use two separate contexts for the tensors.
|
| 202 |
+
// The first context should contain the model parameters and inputs and be allocated statically in user code.
|
| 203 |
+
// The second context should contain all other tensors and will be (re)allocated automatically.
|
| 204 |
+
// Due to this automated allocation the data of the second context is not defined when accessed in user code.
|
| 205 |
+
// Note that the second dimension of the inputs/outputs are interpreted as the number of datapoints in those tensors.
|
| 206 |
+
// 4. Call ggml_opt_fit. If you need more control you can use ggml_opt_epoch instead.
|
| 207 |
+
|
| 208 |
+
// signature for a callback while evaluating opt_ctx on dataset, called after an evaluation
|
| 209 |
+
typedef void (*ggml_opt_epoch_callback)(
|
| 210 |
+
bool train, // true after training evaluation, false after validation evaluation
|
| 211 |
+
ggml_opt_context_t opt_ctx,
|
| 212 |
+
ggml_opt_dataset_t dataset,
|
| 213 |
+
ggml_opt_result_t result, // result associated with the dataset subsection
|
| 214 |
+
int64_t ibatch, // number of batches that have been evaluated so far
|
| 215 |
+
int64_t ibatch_max, // total number of batches in this dataset subsection
|
| 216 |
+
int64_t t_start_us); // time at which the evaluation on the dataset subsection was started
|
| 217 |
+
|
| 218 |
+
// do training on front of dataset, do evaluation only on back of dataset
|
| 219 |
+
GGML_API void ggml_opt_epoch(
|
| 220 |
+
ggml_opt_context_t opt_ctx,
|
| 221 |
+
ggml_opt_dataset_t dataset,
|
| 222 |
+
ggml_opt_result_t result_train, // result to increment during training, ignored if NULL
|
| 223 |
+
ggml_opt_result_t result_eval, // result to increment during evaluation, ignored if NULL
|
| 224 |
+
int64_t idata_split, // data index at which to split training and evaluation
|
| 225 |
+
ggml_opt_epoch_callback callback_train,
|
| 226 |
+
ggml_opt_epoch_callback callback_eval);
|
| 227 |
+
|
| 228 |
+
// callback that prints a progress bar on stderr
|
| 229 |
+
GGML_API void ggml_opt_epoch_callback_progress_bar(
|
| 230 |
+
bool train,
|
| 231 |
+
ggml_opt_context_t opt_ctx,
|
| 232 |
+
ggml_opt_dataset_t dataset,
|
| 233 |
+
ggml_opt_result_t result,
|
| 234 |
+
int64_t ibatch,
|
| 235 |
+
int64_t ibatch_max,
|
| 236 |
+
int64_t t_start_us);
|
| 237 |
+
|
| 238 |
+
// fit model defined by inputs and outputs to dataset
|
| 239 |
+
GGML_API void ggml_opt_fit(
|
| 240 |
+
ggml_backend_sched_t backend_sched, // backend scheduler for constructing the compute graphs
|
| 241 |
+
struct ggml_context * ctx_compute, // context with temporarily allocated tensors to calculate the outputs
|
| 242 |
+
struct ggml_tensor * inputs, // input tensor with shape [ne_datapoint, ndata_batch]
|
| 243 |
+
struct ggml_tensor * outputs, // output tensor, must have shape [ne_label, ndata_batch] if labels are used
|
| 244 |
+
ggml_opt_dataset_t dataset, // dataset with data and optionally also labels
|
| 245 |
+
enum ggml_opt_loss_type loss_type, // loss to minimize
|
| 246 |
+
enum ggml_opt_optimizer_type optimizer, // sgd or adamw
|
| 247 |
+
ggml_opt_get_optimizer_params get_opt_pars, // callback to get optimizer params, userdata is pointer to epoch (of type int64_t)
|
| 248 |
+
int64_t nepoch, // how many times the dataset should be iterated over
|
| 249 |
+
int64_t nbatch_logical, // datapoints optimizer step, must be a multiple of ndata_batch in inputs/outputs
|
| 250 |
+
float val_split, // fraction of the dataset to use for validation, must be in [0.0f, 1.0f)
|
| 251 |
+
bool silent); // whether or not info prints to stderr should be suppressed
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
#ifdef __cplusplus
|
| 255 |
+
}
|
| 256 |
+
#endif
|
vendor/include/ggml-rpc.h
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml-backend.h"
|
| 4 |
+
|
| 5 |
+
#ifdef __cplusplus
|
| 6 |
+
extern "C" {
|
| 7 |
+
#endif
|
| 8 |
+
|
| 9 |
+
#define RPC_PROTO_MAJOR_VERSION 5
|
| 10 |
+
#define RPC_PROTO_MINOR_VERSION 0
|
| 11 |
+
#define RPC_PROTO_PATCH_VERSION 0
|
| 12 |
+
|
| 13 |
+
#ifdef __cplusplus
|
| 14 |
+
static_assert(GGML_OP_COUNT == 101, "GGML_OP_COUNT has changed - update RPC_PROTO_PATCH_VERSION");
|
| 15 |
+
#endif
|
| 16 |
+
|
| 17 |
+
#define GGML_RPC_MAX_SERVERS 16
|
| 18 |
+
|
| 19 |
+
// backend API
|
| 20 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_rpc_init(const char * endpoint, uint32_t device);
|
| 21 |
+
GGML_BACKEND_API bool ggml_backend_is_rpc(ggml_backend_t backend);
|
| 22 |
+
|
| 23 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_rpc_buffer_type(const char * endpoint, uint32_t device);
|
| 24 |
+
|
| 25 |
+
GGML_BACKEND_API void ggml_backend_rpc_get_device_memory(const char * endpoint, uint32_t device, size_t * free, size_t * total);
|
| 26 |
+
|
| 27 |
+
GGML_BACKEND_API void ggml_backend_rpc_start_server(const char * endpoint, const char * cache_dir,
|
| 28 |
+
size_t n_threads, size_t n_devices, ggml_backend_dev_t * devices);
|
| 29 |
+
|
| 30 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_rpc_reg(void);
|
| 31 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_rpc_add_server(const char * endpoint);
|
| 32 |
+
|
| 33 |
+
#ifdef __cplusplus
|
| 34 |
+
}
|
| 35 |
+
#endif
|
vendor/include/ggml-sycl.h
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// MIT license
|
| 3 |
+
// Copyright (C) 2024 Intel Corporation
|
| 4 |
+
// SPDX-License-Identifier: MIT
|
| 5 |
+
//
|
| 6 |
+
|
| 7 |
+
#pragma once
|
| 8 |
+
|
| 9 |
+
#include "ggml.h"
|
| 10 |
+
#include "ggml-backend.h"
|
| 11 |
+
|
| 12 |
+
#define GGML_SYCL_NAME "SYCL"
|
| 13 |
+
#define GGML_SYCL_MAX_DEVICES 48
|
| 14 |
+
|
| 15 |
+
#ifdef __cplusplus
|
| 16 |
+
extern "C" {
|
| 17 |
+
#endif
|
| 18 |
+
|
| 19 |
+
// backend API
|
| 20 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_sycl_init(int device);
|
| 21 |
+
|
| 22 |
+
GGML_BACKEND_API bool ggml_backend_is_sycl(ggml_backend_t backend);
|
| 23 |
+
|
| 24 |
+
// devide buffer
|
| 25 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_buffer_type(int device);
|
| 26 |
+
|
| 27 |
+
// split tensor buffer that splits matrices by rows across multiple devices
|
| 28 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_split_buffer_type(const float * tensor_split);
|
| 29 |
+
|
| 30 |
+
// Tensor parallelism (--split-mode tensor): comm_init/free/allreduce_tensor
|
| 31 |
+
// trio queried by the meta-backend via ggml_backend_reg_get_proc_address.
|
| 32 |
+
// See typedefs in ggml/include/ggml-backend.h. Mirrors the CUDA backend's
|
| 33 |
+
// pattern (ggml_backend_cuda_comm_*).
|
| 34 |
+
GGML_BACKEND_API void * ggml_backend_sycl_comm_init(ggml_backend_t * backends, size_t n_backends);
|
| 35 |
+
GGML_BACKEND_API void ggml_backend_sycl_comm_free(void * comm_ctx);
|
| 36 |
+
GGML_BACKEND_API bool ggml_backend_sycl_comm_allreduce_tensor(void * comm_ctx, struct ggml_tensor ** tensors);
|
| 37 |
+
|
| 38 |
+
// pinned host buffer for use with the CPU backend for faster copies between CPU and GPU
|
| 39 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_sycl_host_buffer_type(void);
|
| 40 |
+
|
| 41 |
+
GGML_BACKEND_API void ggml_backend_sycl_print_sycl_devices(void);
|
| 42 |
+
GGML_BACKEND_API void ggml_backend_sycl_get_gpu_list(int *id_list, int max_len);
|
| 43 |
+
GGML_BACKEND_API void ggml_backend_sycl_get_device_description(int device,
|
| 44 |
+
char *description,
|
| 45 |
+
size_t description_size);
|
| 46 |
+
GGML_BACKEND_API int ggml_backend_sycl_get_device_count();
|
| 47 |
+
GGML_BACKEND_API void ggml_backend_sycl_get_device_memory(int device, size_t *free, size_t *total);
|
| 48 |
+
|
| 49 |
+
// SYCL doesn't support registering host memory, keep here for reference
|
| 50 |
+
// GGML_BACKEND_API bool ggml_backend_sycl_register_host_buffer(void * buffer, size_t size);
|
| 51 |
+
// GGML_BACKEND_API void ggml_backend_sycl_unregister_host_buffer(void * buffer);
|
| 52 |
+
|
| 53 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_sycl_reg(void);
|
| 54 |
+
|
| 55 |
+
#ifdef __cplusplus
|
| 56 |
+
}
|
| 57 |
+
#endif
|
vendor/include/ggml-virtgpu.h
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_virtgpu_reg();
|
| 11 |
+
|
| 12 |
+
#ifdef __cplusplus
|
| 13 |
+
}
|
| 14 |
+
#endif
|
vendor/include/ggml-vulkan.h
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
#define GGML_VK_NAME "Vulkan"
|
| 11 |
+
#define GGML_VK_MAX_DEVICES 16
|
| 12 |
+
|
| 13 |
+
// backend API
|
| 14 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_vk_init(size_t dev_num);
|
| 15 |
+
|
| 16 |
+
GGML_BACKEND_API bool ggml_backend_is_vk(ggml_backend_t backend);
|
| 17 |
+
GGML_BACKEND_API int ggml_backend_vk_get_device_count(void);
|
| 18 |
+
GGML_BACKEND_API void ggml_backend_vk_get_device_description(int device, char * description, size_t description_size);
|
| 19 |
+
GGML_BACKEND_API void ggml_backend_vk_get_device_memory(int device, size_t * free, size_t * total);
|
| 20 |
+
|
| 21 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_vk_buffer_type(size_t dev_num);
|
| 22 |
+
// pinned host buffer for use with the CPU backend for faster copies between CPU and GPU
|
| 23 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_vk_host_buffer_type(void);
|
| 24 |
+
|
| 25 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_vk_reg(void);
|
| 26 |
+
|
| 27 |
+
#ifdef __cplusplus
|
| 28 |
+
}
|
| 29 |
+
#endif
|
vendor/include/ggml-webgpu.h
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
#define GGML_WEBGPU_NAME "WebGPU"
|
| 11 |
+
|
| 12 |
+
// Needed for examples in ggml
|
| 13 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_webgpu_init(void);
|
| 14 |
+
|
| 15 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_webgpu_reg(void);
|
| 16 |
+
|
| 17 |
+
#ifdef __cplusplus
|
| 18 |
+
}
|
| 19 |
+
#endif
|
vendor/include/ggml-zdnn.h
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml.h"
|
| 4 |
+
#include "ggml-backend.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
// device buffer
|
| 11 |
+
GGML_BACKEND_API ggml_backend_buffer_type_t ggml_backend_zdnn_buffer_type(void);
|
| 12 |
+
|
| 13 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_zdnn_reg(void);
|
| 14 |
+
|
| 15 |
+
#ifdef __cplusplus
|
| 16 |
+
}
|
| 17 |
+
#endif
|
vendor/include/ggml-zendnn.h
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "ggml-backend.h"
|
| 4 |
+
#include "ggml.h"
|
| 5 |
+
|
| 6 |
+
#ifdef __cplusplus
|
| 7 |
+
extern "C" {
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
// backend API
|
| 11 |
+
GGML_BACKEND_API ggml_backend_t ggml_backend_zendnn_init(void);
|
| 12 |
+
|
| 13 |
+
GGML_BACKEND_API bool ggml_backend_is_zendnn(ggml_backend_t backend);
|
| 14 |
+
|
| 15 |
+
// number of threads used for zendnn operations
|
| 16 |
+
GGML_BACKEND_API void ggml_backend_zendnn_set_n_threads(ggml_backend_t backend_zendnn, int n_threads);
|
| 17 |
+
|
| 18 |
+
GGML_BACKEND_API ggml_backend_reg_t ggml_backend_zendnn_reg(void);
|
| 19 |
+
|
| 20 |
+
#ifdef __cplusplus
|
| 21 |
+
}
|
| 22 |
+
#endif
|
vendor/src/ggml-cuda/CMakeLists.txt
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.18) # for CMAKE_CUDA_ARCHITECTURES
|
| 2 |
+
|
| 3 |
+
find_package(CUDAToolkit)
|
| 4 |
+
|
| 5 |
+
if (CUDAToolkit_FOUND)
|
| 6 |
+
message(STATUS "CUDA Toolkit found")
|
| 7 |
+
|
| 8 |
+
if (NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
|
| 9 |
+
# native == GPUs available at build time
|
| 10 |
+
# 50 == Maxwell, lowest CUDA 12 standard
|
| 11 |
+
# 60 == P100, FP16 CUDA intrinsics
|
| 12 |
+
# 61 == Pascal, __dp4a instruction (per-byte integer dot product)
|
| 13 |
+
# 70 == V100, FP16 tensor cores
|
| 14 |
+
# 75 == Turing, int8 tensor cores
|
| 15 |
+
# 80 == Ampere, asynchronous data loading, faster tensor core instructions
|
| 16 |
+
# 86 == RTX 3000, needs CUDA v11.1
|
| 17 |
+
# 89 == RTX 4000, needs CUDA v11.8
|
| 18 |
+
# 90 == Hopper H100/200, needs CUDA v11.8
|
| 19 |
+
# 120 == Blackwell, needs CUDA v12.8, FP4 tensor cores
|
| 20 |
+
#
|
| 21 |
+
# XX-virtual == compile CUDA code as PTX, do JIT compilation to binary code on first run
|
| 22 |
+
# XX-real == compile CUDA code as device code for this specific architecture
|
| 23 |
+
# no suffix == compile as both PTX and device code
|
| 24 |
+
#
|
| 25 |
+
# The default behavior for a non-native is to build virtual architectures as needed to cover all features needed
|
| 26 |
+
# for best performance and to also build real architectures for the most commonly used GPUs.
|
| 27 |
+
if (GGML_NATIVE AND CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.6" AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.24")
|
| 28 |
+
set(CMAKE_CUDA_ARCHITECTURES "native")
|
| 29 |
+
else()
|
| 30 |
+
if (CUDAToolkit_VERSION VERSION_LESS "13")
|
| 31 |
+
list(APPEND CMAKE_CUDA_ARCHITECTURES 50-virtual 61-virtual 70-virtual)
|
| 32 |
+
endif ()
|
| 33 |
+
|
| 34 |
+
list(APPEND CMAKE_CUDA_ARCHITECTURES 75-virtual 80-virtual 86-real)
|
| 35 |
+
|
| 36 |
+
if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "11.8")
|
| 37 |
+
list(APPEND CMAKE_CUDA_ARCHITECTURES 89-real 90-virtual)
|
| 38 |
+
endif()
|
| 39 |
+
|
| 40 |
+
if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.8")
|
| 41 |
+
# The CUDA architecture 120f-virtual would in principle work for Blackwell support
|
| 42 |
+
# but the newly added "f" suffix conflicted with a preexising regex for validating CUDA architectures in CMake.
|
| 43 |
+
# So either a recent CMake version or one with the backported fix is needed.
|
| 44 |
+
# The following versions should work:
|
| 45 |
+
# - CMake >= v3.31.8 && CMake < v4.0.0
|
| 46 |
+
# - CMake >= v4.0.2
|
| 47 |
+
# This is NOT documented in the CMake release notes,
|
| 48 |
+
# check Modules/Internal/CMakeCUDAArchitecturesValidate.cmake in the CMake git repository instead.
|
| 49 |
+
# However, the architectures 120a-real and 121a-real should work with basically any CMake version and
|
| 50 |
+
# until the release of e.g. Rubin there is no benefit to shipping virtual architectures for Blackwell.
|
| 51 |
+
list(APPEND CMAKE_CUDA_ARCHITECTURES 120a-real)
|
| 52 |
+
endif()
|
| 53 |
+
if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.9")
|
| 54 |
+
list(APPEND CMAKE_CUDA_ARCHITECTURES 121a-real)
|
| 55 |
+
endif()
|
| 56 |
+
endif()
|
| 57 |
+
endif()
|
| 58 |
+
|
| 59 |
+
enable_language(CUDA)
|
| 60 |
+
|
| 61 |
+
# TODO: Remove once CCCL 3.2 has been released and bundled with CUDA Toolkit
|
| 62 |
+
if (GGML_CUDA_CUB_3DOT2)
|
| 63 |
+
include(FetchContent)
|
| 64 |
+
|
| 65 |
+
FetchContent_Declare(
|
| 66 |
+
CCCL
|
| 67 |
+
GIT_REPOSITORY https://github.com/nvidia/cccl.git
|
| 68 |
+
GIT_TAG v3.2.0
|
| 69 |
+
GIT_SHALLOW TRUE
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
FetchContent_MakeAvailable(CCCL)
|
| 73 |
+
endif()
|
| 74 |
+
|
| 75 |
+
# Replace any plain 12X CUDA architectures with their "architecture-specific" equivalents 12Xa.
|
| 76 |
+
# 12X is forwards-compatible, 12Xa is not.
|
| 77 |
+
# Notably the Blackwell FP4 tensor core instructions are not forwards compatible and therefore need 12Xa.
|
| 78 |
+
# But while 12X vs. 12Xa can be checked in device code there is (to my knowledge) no easy way to do the same check in host code.
|
| 79 |
+
# So for now just replace all instances of 12X with 12Xa, this should be fine until Rubin is released.
|
| 80 |
+
foreach(ARCHS IN ITEMS CMAKE_CUDA_ARCHITECTURES CMAKE_CUDA_ARCHITECTURES_NATIVE)
|
| 81 |
+
set(FIXED_ARCHS "")
|
| 82 |
+
foreach(ARCH IN LISTS ${ARCHS})
|
| 83 |
+
if (ARCH MATCHES "^12[0-9](-real|-virtual)?$")
|
| 84 |
+
string(REGEX REPLACE "^(12[0-9])((-real|-virtual)?)$" "\\1a\\2" FIXED_ARCH ${ARCH})
|
| 85 |
+
message(STATUS "Replacing ${ARCH} in ${ARCHS} with ${FIXED_ARCH}")
|
| 86 |
+
list(APPEND FIXED_ARCHS "${FIXED_ARCH}")
|
| 87 |
+
else()
|
| 88 |
+
list(APPEND FIXED_ARCHS "${ARCH}")
|
| 89 |
+
endif()
|
| 90 |
+
endforeach()
|
| 91 |
+
set(${ARCHS} ${FIXED_ARCHS})
|
| 92 |
+
endforeach()
|
| 93 |
+
|
| 94 |
+
# If we try to compile a "native" build it will use the 12X architectures and fail.
|
| 95 |
+
# So we should instead use the native architectures as determined by CMake after replacing 12X with 12Xa.
|
| 96 |
+
# But if at the time of the build no GPUs are connected at all CMAKE_CUDA_ARCHITECTURES will contain garbage that we should not use.
|
| 97 |
+
if (CMAKE_CUDA_ARCHITECTURES STREQUAL "native" AND CMAKE_CUDA_ARCHITECTURES_NATIVE MATCHES "^[0-9]+(a|f)?(-real|-virtual)?(;[0-9]+(a|f)?(-real|-virtual)?|;)*$")
|
| 98 |
+
set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES_NATIVE})
|
| 99 |
+
endif()
|
| 100 |
+
message(STATUS "Using CMAKE_CUDA_ARCHITECTURES=${CMAKE_CUDA_ARCHITECTURES} CMAKE_CUDA_ARCHITECTURES_NATIVE=${CMAKE_CUDA_ARCHITECTURES_NATIVE}")
|
| 101 |
+
|
| 102 |
+
file(GLOB GGML_HEADERS_CUDA "*.cuh")
|
| 103 |
+
list(APPEND GGML_HEADERS_CUDA "../../include/ggml-cuda.h")
|
| 104 |
+
|
| 105 |
+
file(GLOB GGML_SOURCES_CUDA "*.cu")
|
| 106 |
+
file(GLOB SRCS "template-instances/fattn-tile*.cu")
|
| 107 |
+
list(APPEND GGML_SOURCES_CUDA ${SRCS})
|
| 108 |
+
file(GLOB SRCS "template-instances/fattn-mma*.cu")
|
| 109 |
+
list(APPEND GGML_SOURCES_CUDA ${SRCS})
|
| 110 |
+
file(GLOB SRCS "template-instances/mmq*.cu")
|
| 111 |
+
list(APPEND GGML_SOURCES_CUDA ${SRCS})
|
| 112 |
+
file(GLOB SRCS "template-instances/mmf*.cu")
|
| 113 |
+
list(APPEND GGML_SOURCES_CUDA ${SRCS})
|
| 114 |
+
|
| 115 |
+
if (GGML_CUDA_FA_ALL_QUANTS)
|
| 116 |
+
file(GLOB SRCS "template-instances/fattn-vec*.cu")
|
| 117 |
+
list(APPEND GGML_SOURCES_CUDA ${SRCS})
|
| 118 |
+
add_compile_definitions(GGML_CUDA_FA_ALL_QUANTS)
|
| 119 |
+
else()
|
| 120 |
+
list(APPEND GGML_SOURCES_CUDA
|
| 121 |
+
template-instances/fattn-vec-instance-f16-f16.cu
|
| 122 |
+
template-instances/fattn-vec-instance-q4_0-q4_0.cu
|
| 123 |
+
template-instances/fattn-vec-instance-q8_0-q8_0.cu
|
| 124 |
+
template-instances/fattn-vec-instance-bf16-bf16.cu)
|
| 125 |
+
endif()
|
| 126 |
+
|
| 127 |
+
ggml_add_backend_library(ggml-cuda
|
| 128 |
+
${GGML_HEADERS_CUDA}
|
| 129 |
+
${GGML_SOURCES_CUDA}
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
add_compile_definitions(GGML_CUDA_PEER_MAX_BATCH_SIZE=${GGML_CUDA_PEER_MAX_BATCH_SIZE})
|
| 133 |
+
|
| 134 |
+
if (GGML_CUDA_GRAPHS)
|
| 135 |
+
add_compile_definitions(GGML_CUDA_USE_GRAPHS)
|
| 136 |
+
endif()
|
| 137 |
+
|
| 138 |
+
if (GGML_CUDA_FORCE_MMQ)
|
| 139 |
+
add_compile_definitions(GGML_CUDA_FORCE_MMQ)
|
| 140 |
+
endif()
|
| 141 |
+
|
| 142 |
+
if (GGML_CUDA_FORCE_CUBLAS)
|
| 143 |
+
add_compile_definitions(GGML_CUDA_FORCE_CUBLAS)
|
| 144 |
+
endif()
|
| 145 |
+
|
| 146 |
+
if (GGML_CUDA_NO_VMM)
|
| 147 |
+
add_compile_definitions(GGML_CUDA_NO_VMM)
|
| 148 |
+
endif()
|
| 149 |
+
|
| 150 |
+
if (NOT GGML_CUDA_FA)
|
| 151 |
+
add_compile_definitions(GGML_CUDA_NO_FA)
|
| 152 |
+
endif()
|
| 153 |
+
|
| 154 |
+
if (GGML_CUDA_NO_PEER_COPY)
|
| 155 |
+
add_compile_definitions(GGML_CUDA_NO_PEER_COPY)
|
| 156 |
+
endif()
|
| 157 |
+
|
| 158 |
+
if (GGML_STATIC)
|
| 159 |
+
if (WIN32)
|
| 160 |
+
# As of 12.3.1 CUDA Toolkit for Windows does not offer a static cublas library
|
| 161 |
+
target_link_libraries(ggml-cuda PRIVATE CUDA::cudart_static CUDA::cublas)
|
| 162 |
+
else ()
|
| 163 |
+
if (GGML_CUDA_CUB_3DOT2)
|
| 164 |
+
target_link_libraries(ggml-cuda PRIVATE CCCL::CCCL)
|
| 165 |
+
endif()
|
| 166 |
+
if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "10.1")
|
| 167 |
+
target_link_libraries(ggml-cuda PRIVATE CUDA::cudart_static CUDA::cublas_static CUDA::cublasLt_static)
|
| 168 |
+
else()
|
| 169 |
+
target_link_libraries(ggml-cuda PRIVATE CUDA::cudart_static CUDA::cublas_static)
|
| 170 |
+
endif()
|
| 171 |
+
endif()
|
| 172 |
+
else()
|
| 173 |
+
if (GGML_CUDA_CUB_3DOT2)
|
| 174 |
+
target_link_libraries(ggml-cuda PRIVATE CCCL::CCCL)
|
| 175 |
+
endif()
|
| 176 |
+
target_link_libraries(ggml-cuda PRIVATE CUDA::cudart CUDA::cublas)
|
| 177 |
+
endif()
|
| 178 |
+
|
| 179 |
+
if (GGML_CUDA_NO_VMM)
|
| 180 |
+
# No VMM requested, no need to link directly with the cuda driver lib (libcuda.so)
|
| 181 |
+
else()
|
| 182 |
+
target_link_libraries(ggml-cuda PRIVATE CUDA::cuda_driver)
|
| 183 |
+
endif()
|
| 184 |
+
|
| 185 |
+
if (GGML_CUDA_NCCL)
|
| 186 |
+
find_package(NCCL)
|
| 187 |
+
if (NCCL_FOUND)
|
| 188 |
+
add_compile_definitions(GGML_USE_NCCL)
|
| 189 |
+
target_link_libraries(ggml-cuda PRIVATE NCCL::NCCL)
|
| 190 |
+
else()
|
| 191 |
+
message(STATUS "Warning: NCCL not found, performance for multiple CUDA GPUs will be suboptimal")
|
| 192 |
+
endif()
|
| 193 |
+
endif()
|
| 194 |
+
|
| 195 |
+
set(CUDA_CXX_FLAGS "")
|
| 196 |
+
|
| 197 |
+
set(CUDA_FLAGS -use_fast_math -extended-lambda)
|
| 198 |
+
|
| 199 |
+
if (GGML_CUDA_DEBUG)
|
| 200 |
+
list(APPEND CUDA_FLAGS -lineinfo)
|
| 201 |
+
add_compile_definitions(GGML_CUDA_DEBUG)
|
| 202 |
+
endif()
|
| 203 |
+
|
| 204 |
+
if (CUDAToolkit_VERSION VERSION_GREATER_EQUAL "12.8")
|
| 205 |
+
# Options are:
|
| 206 |
+
# - none (not recommended)
|
| 207 |
+
# - speed (nvcc's default)
|
| 208 |
+
# - balance
|
| 209 |
+
# - size
|
| 210 |
+
list(APPEND CUDA_FLAGS -compress-mode=${GGML_CUDA_COMPRESSION_MODE})
|
| 211 |
+
endif()
|
| 212 |
+
|
| 213 |
+
if (GGML_FATAL_WARNINGS)
|
| 214 |
+
list(APPEND CUDA_FLAGS -Werror all-warnings)
|
| 215 |
+
endif()
|
| 216 |
+
|
| 217 |
+
if (GGML_ALL_WARNINGS AND NOT MSVC)
|
| 218 |
+
set(NVCC_CMD ${CMAKE_CUDA_COMPILER} .c)
|
| 219 |
+
if (NOT CMAKE_CUDA_HOST_COMPILER STREQUAL "")
|
| 220 |
+
list(APPEND NVCC_CMD -ccbin ${CMAKE_CUDA_HOST_COMPILER})
|
| 221 |
+
endif()
|
| 222 |
+
|
| 223 |
+
execute_process(
|
| 224 |
+
COMMAND ${NVCC_CMD} -Xcompiler --version
|
| 225 |
+
OUTPUT_VARIABLE CUDA_CCFULLVER
|
| 226 |
+
ERROR_QUIET
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
if (NOT CUDA_CCFULLVER MATCHES clang)
|
| 230 |
+
set(CUDA_CCID "GNU")
|
| 231 |
+
execute_process(
|
| 232 |
+
COMMAND ${NVCC_CMD} -Xcompiler "-dumpfullversion -dumpversion"
|
| 233 |
+
OUTPUT_VARIABLE CUDA_CCVER
|
| 234 |
+
ERROR_QUIET
|
| 235 |
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
| 236 |
+
)
|
| 237 |
+
else()
|
| 238 |
+
if (CUDA_CCFULLVER MATCHES Apple)
|
| 239 |
+
set(CUDA_CCID "AppleClang")
|
| 240 |
+
else()
|
| 241 |
+
set(CUDA_CCID "Clang")
|
| 242 |
+
endif()
|
| 243 |
+
string(REGEX REPLACE "^.* version ([0-9.]*).*$" "\\1" CUDA_CCVER ${CUDA_CCFULLVER})
|
| 244 |
+
endif()
|
| 245 |
+
|
| 246 |
+
message(STATUS "CUDA host compiler is ${CUDA_CCID} ${CUDA_CCVER}")
|
| 247 |
+
|
| 248 |
+
ggml_get_flags(${CUDA_CCID} ${CUDA_CCVER})
|
| 249 |
+
list(APPEND CUDA_CXX_FLAGS ${CXX_FLAGS} ${GF_CXX_FLAGS}) # This is passed to -Xcompiler later
|
| 250 |
+
endif()
|
| 251 |
+
|
| 252 |
+
if (NOT MSVC)
|
| 253 |
+
list(APPEND CUDA_CXX_FLAGS -Wno-pedantic)
|
| 254 |
+
else()
|
| 255 |
+
# CCCL 3.2 onwards will require a cpp-standard-compliant preprocessor for MSVC
|
| 256 |
+
# https://github.com/NVIDIA/cccl/pull/6827
|
| 257 |
+
list(APPEND CUDA_CXX_FLAGS /Zc:preprocessor)
|
| 258 |
+
endif()
|
| 259 |
+
|
| 260 |
+
list(JOIN CUDA_CXX_FLAGS " " CUDA_CXX_FLAGS_JOINED) # pass host compiler flags as a single argument
|
| 261 |
+
|
| 262 |
+
if (NOT CUDA_CXX_FLAGS_JOINED STREQUAL "")
|
| 263 |
+
list(APPEND CUDA_FLAGS -Xcompiler ${CUDA_CXX_FLAGS_JOINED})
|
| 264 |
+
endif()
|
| 265 |
+
|
| 266 |
+
target_compile_options(ggml-cuda PRIVATE "$<$<COMPILE_LANGUAGE:CUDA>:${CUDA_FLAGS}>")
|
| 267 |
+
else()
|
| 268 |
+
message(FATAL_ERROR "CUDA Toolkit not found")
|
| 269 |
+
endif()
|
vendor/src/ggml-cuda/acc.cu
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "acc.cuh"
|
| 2 |
+
|
| 3 |
+
static __global__ void acc_f32(const float * x, const float * y, float * dst, const int64_t ne,
|
| 4 |
+
const int64_t ne10, const int64_t ne11, const int64_t ne12, const int64_t ne13,
|
| 5 |
+
const int64_t s11, const int64_t s12, const int64_t s13, const int64_t offset) {
|
| 6 |
+
const int64_t i = blockDim.x * blockIdx.x + threadIdx.x;
|
| 7 |
+
|
| 8 |
+
if (i >= ne) {
|
| 9 |
+
return;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
int64_t src1_idx = i - offset;
|
| 13 |
+
|
| 14 |
+
int64_t tmp = src1_idx;
|
| 15 |
+
const int64_t i13 = tmp / s13;
|
| 16 |
+
tmp -= i13 * s13;
|
| 17 |
+
const int64_t i12 = tmp / s12;
|
| 18 |
+
tmp -= i12 * s12;
|
| 19 |
+
const int64_t i11 = tmp / s11;
|
| 20 |
+
tmp -= i11 * s11;
|
| 21 |
+
const int64_t i10 = tmp;
|
| 22 |
+
|
| 23 |
+
float val = x[i];
|
| 24 |
+
if (src1_idx >= 0 && i10 < ne10 && i11 < ne11 && i12 < ne12 && i13 < ne13) {
|
| 25 |
+
val += y[((i13*ne12 + i12) * ne11 + i11) * ne10 + i10];
|
| 26 |
+
}
|
| 27 |
+
dst[i] = val;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
static void acc_f32_cuda(const float * x, const float * y, float * dst, const int64_t n_elements,
|
| 31 |
+
const int64_t ne10, const int64_t ne11, const int64_t ne12, const int64_t ne13,
|
| 32 |
+
const int64_t s1, const int64_t s2, const int64_t s3, const int64_t offset, cudaStream_t stream) {
|
| 33 |
+
const int num_blocks = (n_elements + CUDA_ACC_BLOCK_SIZE - 1) / CUDA_ACC_BLOCK_SIZE;
|
| 34 |
+
acc_f32<<<num_blocks, CUDA_ACC_BLOCK_SIZE, 0, stream>>>(x, y, dst, n_elements, ne10, ne11, ne12, ne13, s1, s2, s3, offset);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
void ggml_cuda_op_acc(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 38 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 39 |
+
const ggml_tensor * src1 = dst->src[1];
|
| 40 |
+
|
| 41 |
+
const float * src0_d = (const float *) src0->data;
|
| 42 |
+
const float * src1_d = (const float *) src1->data;
|
| 43 |
+
float * dst_d = (float *) dst->data;
|
| 44 |
+
|
| 45 |
+
cudaStream_t stream = ctx.stream();
|
| 46 |
+
|
| 47 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
| 48 |
+
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
| 49 |
+
GGML_ASSERT( dst->type == GGML_TYPE_F32);
|
| 50 |
+
|
| 51 |
+
GGML_ASSERT(ggml_is_contiguous(src1));
|
| 52 |
+
GGML_ASSERT(dst->nb[0] == ggml_element_size(dst));
|
| 53 |
+
GGML_ASSERT(ggml_is_contiguously_allocated(dst));
|
| 54 |
+
|
| 55 |
+
const int64_t s1 = dst->op_params[0] / sizeof(float);
|
| 56 |
+
const int64_t s2 = dst->op_params[1] / sizeof(float);
|
| 57 |
+
const int64_t s3 = dst->op_params[2] / sizeof(float);
|
| 58 |
+
const int64_t offset = dst->op_params[3] / sizeof(float);
|
| 59 |
+
|
| 60 |
+
acc_f32_cuda(src0_d, src1_d, dst_d, ggml_nelements(dst), src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3], s1, s2, s3, offset, stream);
|
| 61 |
+
}
|
vendor/src/ggml-cuda/acc.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_ACC_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_op_acc(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/add-id.cu
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "add-id.cuh"
|
| 2 |
+
|
| 3 |
+
static __global__ void add_id_kernel(
|
| 4 |
+
const float * src0, const float * src1, const int32_t * src2, float * dst,
|
| 5 |
+
int64_t ne0, int64_t ne1,
|
| 6 |
+
size_t nb01, size_t nb02,
|
| 7 |
+
size_t nb11,
|
| 8 |
+
size_t nb21
|
| 9 |
+
) {
|
| 10 |
+
|
| 11 |
+
const int64_t i1 = blockIdx.x;
|
| 12 |
+
const int64_t i2 = blockIdx.y;
|
| 13 |
+
|
| 14 |
+
const int i11 = *(const int32_t *) ((const char *) src2 + i1*sizeof(int32_t) + i2*nb21);
|
| 15 |
+
|
| 16 |
+
const size_t nb1 = ne0 * sizeof(float);
|
| 17 |
+
const size_t nb2 = ne1 * nb1;
|
| 18 |
+
|
| 19 |
+
float * dst_row = (float *)((char *)dst + i1*nb1 + i2*nb2);
|
| 20 |
+
const float * src0_row = (const float *)((const char *)src0 + i1*nb01 + i2*nb02);
|
| 21 |
+
const float * src1_row = (const float *)((const char *)src1 + i11*nb11);
|
| 22 |
+
|
| 23 |
+
for (int64_t i0 = threadIdx.x; i0 < ne0; i0 += blockDim.x) {
|
| 24 |
+
dst_row[i0] = src0_row[i0] + src1_row[i0];
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
void ggml_cuda_op_add_id(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 29 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 30 |
+
const ggml_tensor * src1 = dst->src[1];
|
| 31 |
+
const ggml_tensor * src2 = dst->src[2];
|
| 32 |
+
|
| 33 |
+
GGML_TENSOR_TERNARY_OP_LOCALS
|
| 34 |
+
|
| 35 |
+
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
| 36 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
| 37 |
+
GGML_ASSERT(src1->type == GGML_TYPE_F32);
|
| 38 |
+
GGML_ASSERT(src2->type == GGML_TYPE_I32);
|
| 39 |
+
|
| 40 |
+
GGML_ASSERT(nb00 == sizeof(float));
|
| 41 |
+
GGML_ASSERT(nb10 == sizeof(float));
|
| 42 |
+
GGML_ASSERT(nb20 == sizeof(int32_t));
|
| 43 |
+
|
| 44 |
+
const float * src0_d = (const float *)src0->data;
|
| 45 |
+
const float * src1_d = (const float *)src1->data;
|
| 46 |
+
const int32_t * src2_d = (const int32_t *)src2->data;
|
| 47 |
+
float * dst_d = (float *)dst->data;
|
| 48 |
+
|
| 49 |
+
int threads = std::min((int)ne00, 768); // cols
|
| 50 |
+
dim3 blocks(ne01, ne02); // n_experts_used, n_tokens
|
| 51 |
+
add_id_kernel<<<blocks, threads, 0, ctx.stream()>>>(
|
| 52 |
+
src0_d, src1_d, src2_d, dst_d,
|
| 53 |
+
ne0, ne1,
|
| 54 |
+
nb01, nb02,
|
| 55 |
+
nb11,
|
| 56 |
+
nb21
|
| 57 |
+
);
|
| 58 |
+
}
|
vendor/src/ggml-cuda/add-id.cuh
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
void ggml_cuda_op_add_id(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/allreduce.cu
ADDED
|
@@ -0,0 +1,971 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "allreduce.cuh"
|
| 2 |
+
|
| 3 |
+
#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
| 4 |
+
|
| 5 |
+
#include "convert.cuh"
|
| 6 |
+
#include "ggml-impl.h"
|
| 7 |
+
|
| 8 |
+
#include <algorithm>
|
| 9 |
+
#include <cstdlib>
|
| 10 |
+
#include <cstring>
|
| 11 |
+
#include <limits>
|
| 12 |
+
|
| 13 |
+
// ---------------------------------------------------------------------------
|
| 14 |
+
// CUDA AllReduce for tensor-parallel inference across two GPUs.
|
| 15 |
+
//
|
| 16 |
+
// Provides an in-place sum reduction over matching tensors on two CUDA
|
| 17 |
+
// devices in the same process. Used by the tensor-split path alongside
|
| 18 |
+
// NCCL; targets setups without NVLink, where data is exchanged between the
|
| 19 |
+
// GPUs by staging it through pinned host memory over PCIe.
|
| 20 |
+
//
|
| 21 |
+
// Two reduction strategies are selected per call by tensor size:
|
| 22 |
+
//
|
| 23 |
+
// * Chunked kernel path (small reductions): a single CUDA kernel both
|
| 24 |
+
// stages data through pinned host memory and performs the local sum.
|
| 25 |
+
// Cross-GPU synchronization happens *inside the kernel* (busy-wait on
|
| 26 |
+
// a host-memory flag), which keeps launch overhead low for the
|
| 27 |
+
// latency-sensitive token-generation case.
|
| 28 |
+
//
|
| 29 |
+
// * Copy-engine path (large reductions): the transfer is split into
|
| 30 |
+
// D2H + H2D cudaMemcpyAsync chunks driven by the GPU's copy engine,
|
| 31 |
+
// followed by a small device-side add kernel. Cross-GPU
|
| 32 |
+
// synchronization happens *outside the kernel*, via CUDA events
|
| 33 |
+
// between streams. This keeps the compute engine free while large
|
| 34 |
+
// transfers are in flight, which matters for prefill-sized tensors.
|
| 35 |
+
// Reductions larger than the per-call inner cap are processed by an
|
| 36 |
+
// outer chunker that issues sequential inner calls.
|
| 37 |
+
// ---------------------------------------------------------------------------
|
| 38 |
+
|
| 39 |
+
// ---------------------------------------------------------------------------
|
| 40 |
+
// Cross-GPU signal mechanism
|
| 41 |
+
//
|
| 42 |
+
// One int per (slot, rank) pair in pinned host memory. Each AR call writes a
|
| 43 |
+
// strictly increasing token (= the AR call number) into its own arrival int.
|
| 44 |
+
// The peer spins until its read of the other's arrival int equals the token
|
| 45 |
+
// it expects for this call -- a mismatch means the peer hasn't arrived yet.
|
| 46 |
+
// Tokens never repeat over realistic call rates (32-bit int wraps in tens of
|
| 47 |
+
// days at thousands of ARs/sec), so arrival ints don't need to be reset
|
| 48 |
+
// between calls; we initialize once at pipeline init and let the values
|
| 49 |
+
// accumulate.
|
| 50 |
+
//
|
| 51 |
+
// There is exactly one writer (the owning GPU) and one reader (the peer), so
|
| 52 |
+
// we don't need atomics. A volatile store paired with __threadfence_system()
|
| 53 |
+
// provides the release ordering that makes the D2H writes visible system-wide
|
| 54 |
+
// before the arrival token is observed.
|
| 55 |
+
//
|
| 56 |
+
// atomicAdd_system() requires hostNativeAtomicSupported, which is unavailable
|
| 57 |
+
// on PCIe-attached consumer GPUs without NVLink, so the volatile path is the
|
| 58 |
+
// portable choice.
|
| 59 |
+
// ---------------------------------------------------------------------------
|
| 60 |
+
|
| 61 |
+
static __device__ __forceinline__ void ggml_cuda_ar_signal_set(int * p, int token) {
|
| 62 |
+
*(volatile int *)p = token;
|
| 63 |
+
}
|
| 64 |
+
static __device__ __forceinline__ int ggml_cuda_ar_signal_get(const int * p) {
|
| 65 |
+
return *(const volatile int *)p;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// Byte spacing between adjacent arrival ints. 64 bytes (one cache line)
|
| 69 |
+
// ensures each GPU/block's arrival slot lives on its own line, preventing
|
| 70 |
+
// false-sharing stalls on the polling GPU.
|
| 71 |
+
static constexpr size_t GGML_CUDA_AR_ARRIVAL_STRIDE = 64;
|
| 72 |
+
|
| 73 |
+
// Number of blocks the chunked kernel launches with. Each block stripes a
|
| 74 |
+
// disjoint slice of the data and synchronizes through its own arrival-token
|
| 75 |
+
// slot so multiple SMs can pump PCIe stores in parallel.
|
| 76 |
+
static constexpr int GGML_CUDA_AR_KERNEL_BLOCKS = 8;
|
| 77 |
+
|
| 78 |
+
// ---------------------------------------------------------------------------
|
| 79 |
+
// Chunked kernel AllReduce -- 2 GPUs, supports float, half, and bfloat16.
|
| 80 |
+
//
|
| 81 |
+
// Both GPUs run this kernel simultaneously on independent streams. sendbuf
|
| 82 |
+
// and recvbuf live in T_dst (the caller's tensor type); host_mine / host_other
|
| 83 |
+
// carry data in T_wire (the on-wire type, possibly narrower than T_dst -- e.g.
|
| 84 |
+
// T_dst=F32 with T_wire=BF16 halves the bytes pushed across PCIe). When
|
| 85 |
+
// T_dst == T_wire the casts below are no-ops.
|
| 86 |
+
//
|
| 87 |
+
// Each GPU runs three phases:
|
| 88 |
+
//
|
| 89 |
+
// Phase 1 (all threads): cast sendbuf (T_dst) -> T_wire and store as
|
| 90 |
+
// single-instruction-width vectors into host_mine.
|
| 91 |
+
// __threadfence_system() commits these writes to host
|
| 92 |
+
// memory.
|
| 93 |
+
// Phase 2 (thread 0): write token to arrival_mine; spin until
|
| 94 |
+
// arrival_other == token.
|
| 95 |
+
// Phase 3 (all threads): read T_wire vectors from host_other, cast
|
| 96 |
+
// each element to T_dst, and sum with the local
|
| 97 |
+
// sendbuf value (also rounded through T_wire so that
|
| 98 |
+
// both GPUs truncate identically -- this guarantees
|
| 99 |
+
// bit-equivalent results across the two devices).
|
| 100 |
+
//
|
| 101 |
+
// Multi-block: blocks stripe vectors across (gridDim.x * blockDim.x) global
|
| 102 |
+
// threads to keep multiple SMs issuing PCIe stores in parallel. Each block
|
| 103 |
+
// has its own arrival-token slot (offset by blockIdx.x * ARRIVAL_STRIDE);
|
| 104 |
+
// thread 0 of each block signals/spins on that slot independently of other
|
| 105 |
+
// blocks. Tail elements (the leftover < ELEMS_PER_VEC at the end) are
|
| 106 |
+
// handled only by block 0 to avoid cross-block writes to the same slots.
|
| 107 |
+
// ---------------------------------------------------------------------------
|
| 108 |
+
template <typename T_dst, typename T_wire>
|
| 109 |
+
static __global__ void ggml_cuda_ar_kernel(
|
| 110 |
+
const T_dst * sendbuf,
|
| 111 |
+
T_dst * recvbuf,
|
| 112 |
+
T_wire * __restrict__ host_mine,
|
| 113 |
+
const T_wire * __restrict__ host_other,
|
| 114 |
+
int count,
|
| 115 |
+
int * arrival_mine,
|
| 116 |
+
int * arrival_other,
|
| 117 |
+
int token) {
|
| 118 |
+
|
| 119 |
+
// Vector unit for the wire type, sized to the arch's widest single-instruction
|
| 120 |
+
// copy (16 B on Volta+). Each phase-1 iter writes one vector to host memory;
|
| 121 |
+
// each phase-3 iter reads one and produces ELEMS_PER_VEC sums.
|
| 122 |
+
constexpr int ELEMS_PER_VEC = ggml_cuda_get_max_cpy_bytes() / sizeof(T_wire);
|
| 123 |
+
constexpr int ARRIVAL_INTS = (int)(GGML_CUDA_AR_ARRIVAL_STRIDE / sizeof(int));
|
| 124 |
+
|
| 125 |
+
const int tid = threadIdx.x;
|
| 126 |
+
const int nt = blockDim.x;
|
| 127 |
+
const int bid = blockIdx.x;
|
| 128 |
+
const int gtid = bid * nt + tid;
|
| 129 |
+
const int gnt = gridDim.x * nt;
|
| 130 |
+
const int count_vec = count / ELEMS_PER_VEC;
|
| 131 |
+
const int tail = count_vec * ELEMS_PER_VEC;
|
| 132 |
+
|
| 133 |
+
// Phase 1: cast sendbuf (T_dst) -> host_mine (T_wire) and store as vectors.
|
| 134 |
+
{
|
| 135 |
+
for (int i = gtid; i < count_vec; i += gnt) {
|
| 136 |
+
const int off = i * ELEMS_PER_VEC;
|
| 137 |
+
T_wire wire[ELEMS_PER_VEC];
|
| 138 |
+
#pragma unroll
|
| 139 |
+
for (int k = 0; k < ELEMS_PER_VEC; ++k) {
|
| 140 |
+
wire[k] = ggml_cuda_cast<T_wire>(sendbuf[off + k]);
|
| 141 |
+
}
|
| 142 |
+
ggml_cuda_memcpy_1<sizeof(wire)>(&host_mine[off], wire);
|
| 143 |
+
}
|
| 144 |
+
if (bid == 0 && tid < count - tail) {
|
| 145 |
+
host_mine[tail + tid] = ggml_cuda_cast<T_wire>(sendbuf[tail + tid]);
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
// Commit this block's host writes before signalling.
|
| 150 |
+
__threadfence_system();
|
| 151 |
+
__syncthreads();
|
| 152 |
+
|
| 153 |
+
// Phase 2: thread 0 of each block signals on its own arrival slot, then
|
| 154 |
+
// spins for the matching slot from peer. Per-block tokens mean blocks
|
| 155 |
+
// proceed independently -- no inter-block barrier needed.
|
| 156 |
+
if (tid == 0) {
|
| 157 |
+
int * my_slot = arrival_mine + bid * ARRIVAL_INTS;
|
| 158 |
+
const int * other_slot = arrival_other + bid * ARRIVAL_INTS;
|
| 159 |
+
|
| 160 |
+
ggml_cuda_ar_signal_set(my_slot, token);
|
| 161 |
+
__threadfence_system(); // make our signal visible system-wide
|
| 162 |
+
|
| 163 |
+
while (ggml_cuda_ar_signal_get(other_slot) != token) {
|
| 164 |
+
#if __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA
|
| 165 |
+
__nanosleep(100);
|
| 166 |
+
#else
|
| 167 |
+
NO_DEVICE_CODE;
|
| 168 |
+
#endif // __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
__syncthreads();
|
| 173 |
+
|
| 174 |
+
// Acquire peer's host_other writes (this block's stripe of them).
|
| 175 |
+
__threadfence_system();
|
| 176 |
+
|
| 177 |
+
// Phase 3: read peer's T_wire vector, cast both sides through T_wire for
|
| 178 |
+
// bit-equivalence, sum in T_dst precision, and write back to recvbuf.
|
| 179 |
+
{
|
| 180 |
+
for (int i = gtid; i < count_vec; i += gnt) {
|
| 181 |
+
const int off = i * ELEMS_PER_VEC;
|
| 182 |
+
T_wire wire[ELEMS_PER_VEC];
|
| 183 |
+
ggml_cuda_memcpy_1<sizeof(wire)>(wire, &host_other[off]);
|
| 184 |
+
#pragma unroll
|
| 185 |
+
for (int k = 0; k < ELEMS_PER_VEC; ++k) {
|
| 186 |
+
const T_wire d_low = ggml_cuda_cast<T_wire>(sendbuf[off + k]);
|
| 187 |
+
recvbuf[off + k] = ggml_cuda_cast<T_dst>(
|
| 188 |
+
ggml_cuda_cast<float>(d_low) + ggml_cuda_cast<float>(wire[k]));
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
if (bid == 0 && tid < count - tail) {
|
| 192 |
+
const T_wire d_low = ggml_cuda_cast<T_wire>(sendbuf[tail + tid]);
|
| 193 |
+
recvbuf[tail + tid] = ggml_cuda_cast<T_dst>(
|
| 194 |
+
ggml_cuda_cast<float>(d_low) +
|
| 195 |
+
ggml_cuda_cast<float>(host_other[tail + tid]));
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
// Combined load-convert-add kernel. The peer's contribution arrives as T_src
|
| 201 |
+
// (which may be a lower-precision type than T_dst when the BF16 round-trip is
|
| 202 |
+
// active). For bit-equivalence between the two GPUs, dst is first rounded
|
| 203 |
+
// through T_src's precision via ggml_cuda_cast -- peer already truncated its
|
| 204 |
+
// own value the same way before sending -- so both sides perform identical
|
| 205 |
+
// arithmetic. When T_dst == T_src the round-trip cast is a no-op.
|
| 206 |
+
template <typename T_dst, typename T_src>
|
| 207 |
+
static __global__ void ggml_cuda_ar_add_kernel(
|
| 208 |
+
T_dst * __restrict__ dst,
|
| 209 |
+
const T_src * __restrict__ src,
|
| 210 |
+
int count) {
|
| 211 |
+
const int tid = blockIdx.x * blockDim.x + threadIdx.x;
|
| 212 |
+
const int nt = gridDim.x * blockDim.x;
|
| 213 |
+
for (int i = tid; i < count; i += nt) {
|
| 214 |
+
const T_src d_low = ggml_cuda_cast<T_src>(dst[i]);
|
| 215 |
+
dst[i] = ggml_cuda_cast<T_dst>(
|
| 216 |
+
ggml_cuda_cast<float>(d_low) + ggml_cuda_cast<float>(src[i]));
|
| 217 |
+
}
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
// ---------------------------------------------------------------------------
|
| 221 |
+
// Pipeline structure
|
| 222 |
+
// ---------------------------------------------------------------------------
|
| 223 |
+
|
| 224 |
+
// Number of slots in the event / arrival ring. Two slots is sufficient:
|
| 225 |
+
// lockstep guarantees the two GPUs are at most one AR (or chunk) apart, so
|
| 226 |
+
// slot[N%2] is always safe to reuse -- peer has already consumed slot[N%2]
|
| 227 |
+
// from AR N-2 by the time we get to AR N. acquire_slot's
|
| 228 |
+
// cudaEventSynchronize on ev.ker for both devices makes that consumption
|
| 229 |
+
// explicit before we overwrite host_buf[slot] for the new AR.
|
| 230 |
+
static constexpr int GGML_CUDA_AR_POOL_SIZE = 2;
|
| 231 |
+
|
| 232 |
+
// Maximum chunk size (bytes per GPU) handled by one chunked kernel launch.
|
| 233 |
+
// Larger tensors are reduced by issuing multiple chunked launches.
|
| 234 |
+
static constexpr size_t GGML_CUDA_AR_MAX_BYTES = 1024 * 1024; // 1 MB
|
| 235 |
+
|
| 236 |
+
// Copy-engine path: largest tensor accepted on this path; sets host_large /
|
| 237 |
+
// dev_tmp allocation size.
|
| 238 |
+
static constexpr size_t GGML_CUDA_AR_COPY_MAX_BYTES = 32 * 1024 * 1024; // 32 MB
|
| 239 |
+
|
| 240 |
+
// AR wire size at which the copy-engine path takes over from the chunked-
|
| 241 |
+
// kernel path. Override via GGML_CUDA_AR_COPY_THRESHOLD.
|
| 242 |
+
static constexpr size_t GGML_CUDA_AR_COPY_THRESHOLD_DEFAULT = 1024 * 1024; // 1 MB
|
| 243 |
+
// Per-call CE chunk-size heuristic: chunk_bytes = clamp(nbytes / 4, MIN, MAX).
|
| 244 |
+
// The /4 keeps ~4 chunks in flight at any moment (good D2H/H2D overlap with
|
| 245 |
+
// the peer); the clamps cover the cases where nbytes/4 is too small (per-
|
| 246 |
+
// memcpy fixed cost dominates) or too large (chunk-level pipelining stalls).
|
| 247 |
+
// Env var GGML_CUDA_AR_COPY_CHUNK_BYTES can override with a fixed value.
|
| 248 |
+
static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MIN = 512 * 1024; // 512 KB
|
| 249 |
+
static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MAX = 2 * 1024 * 1024; // 2 MB
|
| 250 |
+
// Absolute floor that an env-var override is allowed to set; this caps the
|
| 251 |
+
// per-slot copy-event array. 256 KB -> up to 128 chunks per 32 MB tensor.
|
| 252 |
+
static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN = 256 * 1024;
|
| 253 |
+
static constexpr int GGML_CUDA_AR_COPY_MAX_CHUNKS =
|
| 254 |
+
static_cast<int>((GGML_CUDA_AR_COPY_MAX_BYTES + GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN - 1) /
|
| 255 |
+
GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN);
|
| 256 |
+
|
| 257 |
+
struct ggml_cuda_ar_event_slot {
|
| 258 |
+
cudaEvent_t app = nullptr; // upstream computation complete
|
| 259 |
+
cudaEvent_t cpy[GGML_CUDA_AR_COPY_MAX_CHUNKS] = {}; // copy-engine D2H chunks complete
|
| 260 |
+
cudaEvent_t h2d = nullptr; // copy-engine H2Ds complete (handoff AR stream -> compute stream)
|
| 261 |
+
cudaEvent_t ker = nullptr; // AllReduce kernel complete
|
| 262 |
+
};
|
| 263 |
+
|
| 264 |
+
// Mapped pinned host allocation: cudaHostAlloc + cudaHostGetDevicePointer
|
| 265 |
+
// in one place, with the host handle preserved for cudaFreeHost. Used where
|
| 266 |
+
// the CPU never touches the buffer -- only the device reads/writes via the
|
| 267 |
+
// mapped device pointer. Required on systems where cudaDevAttrCanUseHost-
|
| 268 |
+
// PointerForRegisteredMem is 0 and the host pointer can't be used as a
|
| 269 |
+
// device pointer.
|
| 270 |
+
struct ggml_cuda_ar_host_mapping {
|
| 271 |
+
uint8_t * host = nullptr; // cudaFreeHost handle; also the H-side ptr for cudaMemcpyAsync
|
| 272 |
+
uint8_t * dev = nullptr; // device-side pointer for kernels / cudaMemset
|
| 273 |
+
|
| 274 |
+
cudaError_t alloc(size_t bytes) {
|
| 275 |
+
cudaError_t rc = cudaHostAlloc(reinterpret_cast<void **>(&host), bytes,
|
| 276 |
+
cudaHostAllocPortable | cudaHostAllocMapped);
|
| 277 |
+
if (rc != cudaSuccess) {
|
| 278 |
+
host = nullptr;
|
| 279 |
+
return rc;
|
| 280 |
+
}
|
| 281 |
+
rc = cudaHostGetDevicePointer(reinterpret_cast<void **>(&dev), host, 0);
|
| 282 |
+
if (rc != cudaSuccess) {
|
| 283 |
+
cudaFreeHost(host);
|
| 284 |
+
host = nullptr;
|
| 285 |
+
dev = nullptr;
|
| 286 |
+
}
|
| 287 |
+
return rc;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
void free() {
|
| 291 |
+
if (host) {
|
| 292 |
+
cudaFreeHost(host);
|
| 293 |
+
host = nullptr;
|
| 294 |
+
dev = nullptr;
|
| 295 |
+
}
|
| 296 |
+
}
|
| 297 |
+
};
|
| 298 |
+
|
| 299 |
+
struct ggml_cuda_ar_pipeline {
|
| 300 |
+
int n_devices;
|
| 301 |
+
int devices[GGML_CUDA_MAX_DEVICES];
|
| 302 |
+
size_t buf_bytes; // bytes per device in host_buf[]
|
| 303 |
+
size_t copy_bytes; // bytes per device in host_large[] / dev_tmp[]
|
| 304 |
+
size_t copy_threshold;
|
| 305 |
+
size_t copy_chunk_bytes;
|
| 306 |
+
size_t bf16_threshold; // tensors >= this size (bytes) are reduced via FP32->BF16 round-trip; 0 disables
|
| 307 |
+
uint64_t call_count;
|
| 308 |
+
|
| 309 |
+
// Per-device resources.
|
| 310 |
+
ggml_cuda_ar_host_mapping host_buf[GGML_CUDA_MAX_DEVICES]; // pinned staging (chunked kernel)
|
| 311 |
+
ggml_cuda_ar_host_mapping host_large[GGML_CUDA_MAX_DEVICES]; // pinned staging (copy-engine)
|
| 312 |
+
char * dev_tmp[GGML_CUDA_MAX_DEVICES]; // device scratch for copy-engine path
|
| 313 |
+
cudaStream_t streams[GGML_CUDA_MAX_DEVICES]; // non-blocking
|
| 314 |
+
ggml_cuda_ar_event_slot ev_pool[GGML_CUDA_MAX_DEVICES][GGML_CUDA_AR_POOL_SIZE];
|
| 315 |
+
|
| 316 |
+
// Copy-engine: per-device "I finished reading my peer's host_large"
|
| 317 |
+
// event. Indexed by RECORDER device. Recorded same-device on streams[i]
|
| 318 |
+
// after stage 2's last H2D from host_large[peer]. Waited cross-device
|
| 319 |
+
// by peer's stage-1 stream before the next AR overwrites host_large[peer].
|
| 320 |
+
cudaEvent_t host_large_read_done[GGML_CUDA_MAX_DEVICES];
|
| 321 |
+
bool host_large_read_done_valid;
|
| 322 |
+
|
| 323 |
+
// Copy-engine: per-device "my add_kernel is done with dev_tmp" event.
|
| 324 |
+
// Recorded on the compute stream after each add_kernel; the AR stream
|
| 325 |
+
// waits on it before the next copy_impl's H2D overwrites dev_tmp. Lets us
|
| 326 |
+
// single-buffer dev_tmp despite add_kernel running on a separate stream.
|
| 327 |
+
cudaEvent_t dev_tmp_kernel_done[GGML_CUDA_MAX_DEVICES];
|
| 328 |
+
bool dev_tmp_kernel_done_valid;
|
| 329 |
+
|
| 330 |
+
// Arrival ring: ARRIVAL_STRIDE bytes between adjacent ints. Mapped pinned
|
| 331 |
+
// memory; CPU never reads/writes -- only the kernel and cudaMemset.
|
| 332 |
+
// Use ggml_cuda_ar_arrival_ptr() to index.
|
| 333 |
+
ggml_cuda_ar_host_mapping arrival;
|
| 334 |
+
};
|
| 335 |
+
|
| 336 |
+
// Base pointer for the (slot, rank) per-block token block. The kernel adds
|
| 337 |
+
// blockIdx.x * (ARRIVAL_STRIDE/sizeof(int)) internally to land on its own slot.
|
| 338 |
+
static int * ggml_cuda_ar_arrival_ptr(const ggml_cuda_ar_pipeline * p, int slot, int rank) {
|
| 339 |
+
const size_t offset = ((size_t)slot * p->n_devices + rank) *
|
| 340 |
+
GGML_CUDA_AR_KERNEL_BLOCKS * GGML_CUDA_AR_ARRIVAL_STRIDE;
|
| 341 |
+
return reinterpret_cast<int *>(p->arrival.dev + offset);
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
static uint64_t ggml_cuda_ar_env_u64(const char * name, uint64_t default_value) {
|
| 345 |
+
const char * value = getenv(name);
|
| 346 |
+
if (value == nullptr || value[0] == '\0') {
|
| 347 |
+
return default_value;
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
char * end = nullptr;
|
| 351 |
+
const unsigned long long parsed = strtoull(value, &end, 10);
|
| 352 |
+
return end != value ? (uint64_t) parsed : default_value;
|
| 353 |
+
}
|
| 354 |
+
|
| 355 |
+
struct ggml_cuda_ar_slot_info {
|
| 356 |
+
int slot;
|
| 357 |
+
int token;
|
| 358 |
+
};
|
| 359 |
+
|
| 360 |
+
static ggml_cuda_ar_slot_info ggml_cuda_ar_acquire_slot(ggml_cuda_ar_pipeline * p) {
|
| 361 |
+
const int slot = static_cast<int>(p->call_count % GGML_CUDA_AR_POOL_SIZE);
|
| 362 |
+
const bool pool_lapped = p->call_count >= GGML_CUDA_AR_POOL_SIZE;
|
| 363 |
+
p->call_count++;
|
| 364 |
+
|
| 365 |
+
if (pool_lapped) {
|
| 366 |
+
for (int i = 0; i < p->n_devices; ++i) {
|
| 367 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 368 |
+
CUDA_CHECK(cudaEventSynchronize(p->ev_pool[i][slot].ker));
|
| 369 |
+
}
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
return { slot, (int) p->call_count };
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
// Per-AR copy-engine chunk size: env-var override if set, else heuristic
|
| 376 |
+
// (clamp(nbytes/4, HEURISTIC_MIN, HEURISTIC_MAX)).
|
| 377 |
+
static size_t ggml_cuda_ar_chunk_bytes(const ggml_cuda_ar_pipeline * p, size_t nbytes) {
|
| 378 |
+
if (p->copy_chunk_bytes > 0) {
|
| 379 |
+
return p->copy_chunk_bytes;
|
| 380 |
+
}
|
| 381 |
+
return std::min(GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MAX,
|
| 382 |
+
std::max(GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MIN, nbytes / 4));
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
static void ggml_cuda_ar_wait_for_compute(
|
| 386 |
+
ggml_cuda_ar_pipeline * p, ggml_backend_cuda_context * cuda_ctx, int rank, int slot) {
|
| 387 |
+
ggml_cuda_ar_event_slot & ev = p->ev_pool[rank][slot];
|
| 388 |
+
CUDA_CHECK(cudaEventRecord(ev.app, cuda_ctx->stream()));
|
| 389 |
+
CUDA_CHECK(cudaStreamWaitEvent(p->streams[rank], ev.app));
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
// ---------------------------------------------------------------------------
|
| 393 |
+
// Init / free
|
| 394 |
+
// ---------------------------------------------------------------------------
|
| 395 |
+
|
| 396 |
+
ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int * devices, size_t n_devices) {
|
| 397 |
+
|
| 398 |
+
if (n_devices != 2) {
|
| 399 |
+
GGML_LOG_DEBUG("%s: internal AllReduce only supports n_devices=2 (got %zu); "
|
| 400 |
+
"falling back\n", __func__, n_devices);
|
| 401 |
+
return nullptr;
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
// The chunked kernel uses __nanosleep, which is sm70+ (Volta+).
|
| 405 |
+
for (size_t i = 0; i < n_devices; ++i) {
|
| 406 |
+
const int cc = ggml_cuda_info().devices[devices[i]].cc;
|
| 407 |
+
if (cc < GGML_CUDA_CC_VOLTA) {
|
| 408 |
+
GGML_LOG_DEBUG("%s: internal AllReduce requires compute capability >= %d "
|
| 409 |
+
"(device %d has cc=%d); falling back\n",
|
| 410 |
+
__func__, GGML_CUDA_CC_VOLTA, devices[i], cc);
|
| 411 |
+
return nullptr;
|
| 412 |
+
}
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
auto * p = new ggml_cuda_ar_pipeline{};
|
| 416 |
+
p->n_devices = n_devices;
|
| 417 |
+
p->copy_bytes = GGML_CUDA_AR_COPY_MAX_BYTES;
|
| 418 |
+
p->copy_threshold = ggml_cuda_ar_env_u64("GGML_CUDA_AR_COPY_THRESHOLD", GGML_CUDA_AR_COPY_THRESHOLD_DEFAULT);
|
| 419 |
+
// 0 = use the per-call heuristic (default). Non-zero env value forces a
|
| 420 |
+
// fixed chunk size for diagnostics, with a floor at COPY_CHUNK_BYTES_MIN.
|
| 421 |
+
p->copy_chunk_bytes = ggml_cuda_ar_env_u64("GGML_CUDA_AR_COPY_CHUNK_BYTES", 0);
|
| 422 |
+
if (p->copy_chunk_bytes > 0 && p->copy_chunk_bytes < GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN) {
|
| 423 |
+
GGML_LOG_WARN("%s: GGML_CUDA_AR_COPY_CHUNK_BYTES=%zu below minimum %zu; clamping\n",
|
| 424 |
+
__func__, p->copy_chunk_bytes, GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN);
|
| 425 |
+
p->copy_chunk_bytes = GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN;
|
| 426 |
+
}
|
| 427 |
+
// Default 1: BF16 round-trip is always on for F32 inputs (any non-zero
|
| 428 |
+
// ne). Set GGML_CUDA_AR_BF16_THRESHOLD=0 to disable, or to a larger
|
| 429 |
+
// byte threshold to opt out for small tensors.
|
| 430 |
+
p->bf16_threshold = ggml_cuda_ar_env_u64("GGML_CUDA_AR_BF16_THRESHOLD", 1);
|
| 431 |
+
for (size_t i = 0; i < n_devices; ++i) {
|
| 432 |
+
p->devices[i] = devices[i];
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
// Per-device streams and event pools.
|
| 436 |
+
for (size_t i = 0; i < n_devices; ++i) {
|
| 437 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 438 |
+
|
| 439 |
+
cudaStream_t stream = nullptr;
|
| 440 |
+
if (cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking) != cudaSuccess) {
|
| 441 |
+
GGML_LOG_ERROR("%s: cudaStreamCreateWithFlags failed for device %d\n",
|
| 442 |
+
__func__, p->devices[i]);
|
| 443 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 444 |
+
return nullptr;
|
| 445 |
+
}
|
| 446 |
+
p->streams[i] = stream;
|
| 447 |
+
|
| 448 |
+
for (int s = 0; s < GGML_CUDA_AR_POOL_SIZE; ++s) {
|
| 449 |
+
bool ok =
|
| 450 |
+
cudaEventCreateWithFlags(&p->ev_pool[i][s].app, cudaEventDisableTiming) == cudaSuccess &&
|
| 451 |
+
cudaEventCreateWithFlags(&p->ev_pool[i][s].h2d, cudaEventDisableTiming) == cudaSuccess &&
|
| 452 |
+
cudaEventCreateWithFlags(&p->ev_pool[i][s].ker, cudaEventDisableTiming) == cudaSuccess;
|
| 453 |
+
for (int c = 0; ok && c < GGML_CUDA_AR_COPY_MAX_CHUNKS; ++c) {
|
| 454 |
+
ok = cudaEventCreateWithFlags(&p->ev_pool[i][s].cpy[c], cudaEventDisableTiming) == cudaSuccess;
|
| 455 |
+
}
|
| 456 |
+
if (!ok) {
|
| 457 |
+
GGML_LOG_ERROR("%s: cudaEventCreate failed for device %d slot %d\n",
|
| 458 |
+
__func__, p->devices[i], s);
|
| 459 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 460 |
+
return nullptr;
|
| 461 |
+
}
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
if (cudaEventCreateWithFlags(&p->host_large_read_done[i], cudaEventDisableTiming) != cudaSuccess) {
|
| 465 |
+
GGML_LOG_ERROR("%s: cudaEventCreate for host_large_read_done failed for device %d\n",
|
| 466 |
+
__func__, p->devices[i]);
|
| 467 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 468 |
+
return nullptr;
|
| 469 |
+
}
|
| 470 |
+
if (cudaEventCreateWithFlags(&p->dev_tmp_kernel_done[i], cudaEventDisableTiming) != cudaSuccess) {
|
| 471 |
+
GGML_LOG_ERROR("%s: cudaEventCreate for dev_tmp_kernel_done failed for device %d\n",
|
| 472 |
+
__func__, p->devices[i]);
|
| 473 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 474 |
+
return nullptr;
|
| 475 |
+
}
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
// Arrival ring: cache-line padded so each GPU's int is on its own line.
|
| 479 |
+
const size_t arrival_bytes =
|
| 480 |
+
(size_t)GGML_CUDA_AR_POOL_SIZE * n_devices *
|
| 481 |
+
GGML_CUDA_AR_KERNEL_BLOCKS * GGML_CUDA_AR_ARRIVAL_STRIDE;
|
| 482 |
+
if (p->arrival.alloc(arrival_bytes) != cudaSuccess) {
|
| 483 |
+
GGML_LOG_ERROR("%s: alloc for arrival ring failed (%zu bytes)\n",
|
| 484 |
+
__func__, arrival_bytes);
|
| 485 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 486 |
+
return nullptr;
|
| 487 |
+
}
|
| 488 |
+
ggml_cuda_set_device(p->devices[0]);
|
| 489 |
+
if (cudaMemset(p->arrival.dev, 0, arrival_bytes) != cudaSuccess) {
|
| 490 |
+
GGML_LOG_ERROR("%s: cudaMemset for arrival ring failed (%zu bytes)\n",
|
| 491 |
+
__func__, arrival_bytes);
|
| 492 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 493 |
+
return nullptr;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
// Per-device pinned staging buffers -- POOL_SIZE-deep ring so the chunked-
|
| 497 |
+
// kernel can write the next slot's data while the peer is still reading
|
| 498 |
+
// the previous slot's. Indexed by (slot * buf_bytes) at the call site.
|
| 499 |
+
p->buf_bytes = GGML_CUDA_AR_MAX_BYTES;
|
| 500 |
+
const size_t host_buf_total = (size_t) GGML_CUDA_AR_POOL_SIZE * p->buf_bytes;
|
| 501 |
+
for (size_t i = 0; i < n_devices; ++i) {
|
| 502 |
+
if (p->host_buf[i].alloc(host_buf_total) != cudaSuccess) {
|
| 503 |
+
GGML_LOG_ERROR("%s: alloc for staging failed (%zu bytes)\n",
|
| 504 |
+
__func__, host_buf_total);
|
| 505 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 506 |
+
return nullptr;
|
| 507 |
+
}
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
// Copy-engine path: pinned host staging + device scratch, sized for the
|
| 511 |
+
// largest tensor we accept on this path (GGML_CUDA_AR_COPY_MAX_BYTES).
|
| 512 |
+
// dev_tmp is single-buffered; cross-AR safety is enforced by an explicit
|
| 513 |
+
// cross-stream wait in copy_impl on the prior AR's add_kernel-done event.
|
| 514 |
+
for (size_t i = 0; i < n_devices; ++i) {
|
| 515 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 516 |
+
if (p->host_large[i].alloc(p->copy_bytes) != cudaSuccess) {
|
| 517 |
+
GGML_LOG_ERROR("%s: alloc for large staging failed (%zu bytes)\n",
|
| 518 |
+
__func__, p->copy_bytes);
|
| 519 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 520 |
+
return nullptr;
|
| 521 |
+
}
|
| 522 |
+
if (cudaMalloc(reinterpret_cast<void **>(&p->dev_tmp[i]), p->copy_bytes) != cudaSuccess) {
|
| 523 |
+
GGML_LOG_ERROR("%s: cudaMalloc for copy scratch failed (%zu bytes) on device %d\n",
|
| 524 |
+
__func__, p->copy_bytes, p->devices[i]);
|
| 525 |
+
ggml_cuda_ar_pipeline_free(p);
|
| 526 |
+
return nullptr;
|
| 527 |
+
}
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
GGML_LOG_INFO("%s: initialized AllReduce pipeline: %zu GPUs, "
|
| 531 |
+
"%zu KB chunked kernel staging + %zu MB copy-engine staging per GPU\n",
|
| 532 |
+
__func__, n_devices, p->buf_bytes >> 10, p->copy_bytes >> 20);
|
| 533 |
+
|
| 534 |
+
return p;
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline * p) {
|
| 538 |
+
if (!p) {
|
| 539 |
+
return;
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
// Drain all in-flight kernels before tearing down resources.
|
| 543 |
+
for (int i = 0; i < p->n_devices; ++i) {
|
| 544 |
+
if (p->streams[i]) {
|
| 545 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 546 |
+
cudaStreamSynchronize(p->streams[i]);
|
| 547 |
+
}
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
for (int i = 0; i < p->n_devices; ++i) {
|
| 551 |
+
p->host_buf[i].free();
|
| 552 |
+
p->host_large[i].free();
|
| 553 |
+
if (p->dev_tmp[i]) {
|
| 554 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 555 |
+
cudaFree(p->dev_tmp[i]);
|
| 556 |
+
}
|
| 557 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 558 |
+
for (int s = 0; s < GGML_CUDA_AR_POOL_SIZE; ++s) {
|
| 559 |
+
if (p->ev_pool[i][s].app) { cudaEventDestroy(p->ev_pool[i][s].app); }
|
| 560 |
+
for (int c = 0; c < GGML_CUDA_AR_COPY_MAX_CHUNKS; ++c) {
|
| 561 |
+
if (p->ev_pool[i][s].cpy[c]) { cudaEventDestroy(p->ev_pool[i][s].cpy[c]); }
|
| 562 |
+
}
|
| 563 |
+
if (p->ev_pool[i][s].h2d) { cudaEventDestroy(p->ev_pool[i][s].h2d); }
|
| 564 |
+
if (p->ev_pool[i][s].ker) { cudaEventDestroy(p->ev_pool[i][s].ker); }
|
| 565 |
+
}
|
| 566 |
+
if (p->host_large_read_done[i]) {
|
| 567 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 568 |
+
cudaEventDestroy(p->host_large_read_done[i]);
|
| 569 |
+
}
|
| 570 |
+
if (p->dev_tmp_kernel_done[i]) {
|
| 571 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 572 |
+
cudaEventDestroy(p->dev_tmp_kernel_done[i]);
|
| 573 |
+
}
|
| 574 |
+
if (p->streams[i]) {
|
| 575 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 576 |
+
cudaStreamDestroy(p->streams[i]);
|
| 577 |
+
}
|
| 578 |
+
}
|
| 579 |
+
p->arrival.free();
|
| 580 |
+
delete p;
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
// ---------------------------------------------------------------------------
|
| 584 |
+
// Dispatch
|
| 585 |
+
// ---------------------------------------------------------------------------
|
| 586 |
+
|
| 587 |
+
// Asymmetric copy_impl: data sent over PCIe in T_src precision (one element of
|
| 588 |
+
// nbytes per ne element); accumulated locally into a T_dst buffer. When
|
| 589 |
+
// T_src == T_dst this is the original homogeneous reduction. When they differ
|
| 590 |
+
// (e.g. BF16 wire / F32 accumulator) the add kernel rounds dst through T_src
|
| 591 |
+
// for bit-equivalence between GPUs and we skip the otherwise-needed
|
| 592 |
+
// post-conversion entirely.
|
| 593 |
+
template <typename T_src, typename T_dst>
|
| 594 |
+
static bool ggml_cuda_ar_allreduce_copy_impl(
|
| 595 |
+
ggml_cuda_ar_pipeline * p,
|
| 596 |
+
ggml_backend_t * backends,
|
| 597 |
+
T_src * const src_buf[GGML_CUDA_MAX_DEVICES],
|
| 598 |
+
T_dst * const dst_buf[GGML_CUDA_MAX_DEVICES],
|
| 599 |
+
const bool compute[GGML_CUDA_MAX_DEVICES],
|
| 600 |
+
int64_t ne,
|
| 601 |
+
size_t nbytes) {
|
| 602 |
+
GGML_ASSERT(p->n_devices == 2);
|
| 603 |
+
GGML_ASSERT(nbytes <= p->copy_bytes);
|
| 604 |
+
GGML_ASSERT(ne <= std::numeric_limits<int>::max());
|
| 605 |
+
|
| 606 |
+
const size_t chunk_bytes = ggml_cuda_ar_chunk_bytes(p, nbytes);
|
| 607 |
+
GGML_ASSERT(chunk_bytes > 0);
|
| 608 |
+
|
| 609 |
+
const int slot = ggml_cuda_ar_acquire_slot(p).slot;
|
| 610 |
+
const size_t copy_chunks = (nbytes + chunk_bytes - 1) / chunk_bytes;
|
| 611 |
+
GGML_ASSERT(copy_chunks <= GGML_CUDA_AR_COPY_MAX_CHUNKS);
|
| 612 |
+
|
| 613 |
+
ggml_backend_cuda_context * cuda_ctx[2] = {};
|
| 614 |
+
|
| 615 |
+
// Stage 1: both GPUs copy their local contribution to pinned host memory.
|
| 616 |
+
for (int i = 0; i < 2; ++i) {
|
| 617 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 618 |
+
cuda_ctx[i] = static_cast<ggml_backend_cuda_context *>(backends[i]->context);
|
| 619 |
+
GGML_ASSERT(cuda_ctx[i]->device == p->devices[i]);
|
| 620 |
+
|
| 621 |
+
ggml_cuda_ar_wait_for_compute(p, cuda_ctx[i], i, slot);
|
| 622 |
+
|
| 623 |
+
// Wait for peer's H2D from our host_large[i] (recorded in the
|
| 624 |
+
// previous AR's stage 2) to complete before we overwrite host_large[i].
|
| 625 |
+
// host_large_read_done[peer] = peer finished reading host_large[i].
|
| 626 |
+
// No-op on the first AR -- no prior record exists.
|
| 627 |
+
if (p->host_large_read_done_valid) {
|
| 628 |
+
const int peer = 1 - i;
|
| 629 |
+
CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->host_large_read_done[peer]));
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
if (!compute[i]) {
|
| 633 |
+
CUDA_CHECK(cudaMemsetAsync(src_buf[i], 0, nbytes, p->streams[i]));
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
for (size_t c = 0; c < copy_chunks; ++c) {
|
| 637 |
+
const size_t offset = c * chunk_bytes;
|
| 638 |
+
const size_t this_bytes = (nbytes - offset) < chunk_bytes ?
|
| 639 |
+
(nbytes - offset) : chunk_bytes;
|
| 640 |
+
|
| 641 |
+
CUDA_CHECK(cudaMemcpyAsync(
|
| 642 |
+
p->host_large[i].host + offset, reinterpret_cast<char *>(src_buf[i]) + offset, this_bytes,
|
| 643 |
+
cudaMemcpyDeviceToHost, p->streams[i]));
|
| 644 |
+
CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].cpy[c], p->streams[i]));
|
| 645 |
+
}
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
// Stage 2: each GPU waits for each peer D2H chunk, pulls that chunk back to
|
| 649 |
+
// local device scratch (dev_tmp), then performs one device-local add over
|
| 650 |
+
// the assembled peer tensor. The H2Ds run on the AR stream (copy engine)
|
| 651 |
+
// and the add_kernel runs on the caller's compute stream, so the AR stream
|
| 652 |
+
// stays pure-copy and avoids an in-stream copy->compute engine switch every
|
| 653 |
+
// AR. dev_tmp is single-buffered: the AR stream waits cross-stream on the
|
| 654 |
+
// prior AR's add_kernel-done event before overwriting it.
|
| 655 |
+
for (int i = 0; i < 2; ++i) {
|
| 656 |
+
const int peer = 1 - i;
|
| 657 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 658 |
+
|
| 659 |
+
// Wait for the previous AR's add_kernel (on the compute stream) to
|
| 660 |
+
// finish reading dev_tmp before our H2D overwrites it. No-op on the
|
| 661 |
+
// first copy_impl call.
|
| 662 |
+
if (p->dev_tmp_kernel_done_valid) {
|
| 663 |
+
CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->dev_tmp_kernel_done[i]));
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
for (size_t c = 0; c < copy_chunks; ++c) {
|
| 667 |
+
const size_t offset = c * chunk_bytes;
|
| 668 |
+
const size_t this_bytes = (nbytes - offset) < chunk_bytes ?
|
| 669 |
+
(nbytes - offset) : chunk_bytes;
|
| 670 |
+
|
| 671 |
+
CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->ev_pool[peer][slot].cpy[c]));
|
| 672 |
+
CUDA_CHECK(cudaMemcpyAsync(
|
| 673 |
+
p->dev_tmp[i] + offset, p->host_large[peer].host + offset, this_bytes,
|
| 674 |
+
cudaMemcpyHostToDevice, p->streams[i]));
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
// Mark our reads of host_large[peer] complete so peer's next AR can
|
| 678 |
+
// safely overwrite it.
|
| 679 |
+
CUDA_CHECK(cudaEventRecord(p->host_large_read_done[i], p->streams[i]));
|
| 680 |
+
|
| 681 |
+
// Hand off from AR stream (copy engine) to compute stream: compute
|
| 682 |
+
// stream waits for all H2Ds to finish, then runs the add_kernel.
|
| 683 |
+
CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].h2d, p->streams[i]));
|
| 684 |
+
CUDA_CHECK(cudaStreamWaitEvent(cuda_ctx[i]->stream(), p->ev_pool[i][slot].h2d));
|
| 685 |
+
|
| 686 |
+
const int block_size = 256;
|
| 687 |
+
int n_blocks = (int) ((ne + block_size - 1) / block_size);
|
| 688 |
+
if (n_blocks > 1024) {
|
| 689 |
+
n_blocks = 1024;
|
| 690 |
+
}
|
| 691 |
+
ggml_cuda_ar_add_kernel<T_dst, T_src><<<n_blocks, block_size, 0, cuda_ctx[i]->stream()>>>(
|
| 692 |
+
dst_buf[i],
|
| 693 |
+
reinterpret_cast<const T_src *>(p->dev_tmp[i]),
|
| 694 |
+
(int) ne);
|
| 695 |
+
CUDA_CHECK(cudaGetLastError());
|
| 696 |
+
|
| 697 |
+
// Record dev_tmp-released on the compute stream so the next copy_impl
|
| 698 |
+
// can wait for the kernel to finish before overwriting dev_tmp. Also
|
| 699 |
+
// record AR-done as ev.ker for acquire_slot's pool-wraparound sync.
|
| 700 |
+
CUDA_CHECK(cudaEventRecord(p->dev_tmp_kernel_done[i], cuda_ctx[i]->stream()));
|
| 701 |
+
CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].ker, cuda_ctx[i]->stream()));
|
| 702 |
+
}
|
| 703 |
+
p->host_large_read_done_valid = true;
|
| 704 |
+
p->dev_tmp_kernel_done_valid = true;
|
| 705 |
+
|
| 706 |
+
return true;
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
+
// Outer-level chunker: copy_impl handles up to copy_bytes per call (limited by
|
| 710 |
+
// the host_large / dev_tmp allocation size). When the full AR exceeds that,
|
| 711 |
+
// slice the tensor into copy_bytes-sized pieces and call copy_impl repeatedly.
|
| 712 |
+
// Each slice goes through its own stage 1 -> stage 2 cycle and acquires its own
|
| 713 |
+
// slot, so cross-AR fences and pool wraparound work the same way as for any
|
| 714 |
+
// other sequence of small ARs.
|
| 715 |
+
template <typename T_src, typename T_dst>
|
| 716 |
+
static bool ggml_cuda_ar_allreduce_copy_outer(
|
| 717 |
+
ggml_cuda_ar_pipeline * p,
|
| 718 |
+
ggml_backend_t * backends,
|
| 719 |
+
T_src * const src_buf[GGML_CUDA_MAX_DEVICES],
|
| 720 |
+
T_dst * const dst_buf[GGML_CUDA_MAX_DEVICES],
|
| 721 |
+
const bool compute[GGML_CUDA_MAX_DEVICES],
|
| 722 |
+
int64_t ne) {
|
| 723 |
+
const int64_t outer_max_elems = (int64_t) (p->copy_bytes / sizeof(T_src));
|
| 724 |
+
GGML_ASSERT(outer_max_elems > 0);
|
| 725 |
+
|
| 726 |
+
bool ok = true;
|
| 727 |
+
for (int64_t outer_start = 0; outer_start < ne && ok; outer_start += outer_max_elems) {
|
| 728 |
+
const int64_t outer_ne = std::min(outer_max_elems, ne - outer_start);
|
| 729 |
+
const size_t outer_nbytes = (size_t) outer_ne * sizeof(T_src);
|
| 730 |
+
|
| 731 |
+
T_src * src[GGML_CUDA_MAX_DEVICES] = {};
|
| 732 |
+
T_dst * dst[GGML_CUDA_MAX_DEVICES] = {};
|
| 733 |
+
for (int i = 0; i < p->n_devices; ++i) {
|
| 734 |
+
src[i] = src_buf[i] + outer_start;
|
| 735 |
+
dst[i] = dst_buf[i] + outer_start;
|
| 736 |
+
}
|
| 737 |
+
ok = ggml_cuda_ar_allreduce_copy_impl<T_src, T_dst>(
|
| 738 |
+
p, backends, src, dst, compute, outer_ne, outer_nbytes);
|
| 739 |
+
}
|
| 740 |
+
return ok;
|
| 741 |
+
}
|
| 742 |
+
|
| 743 |
+
bool ggml_cuda_ar_allreduce(
|
| 744 |
+
ggml_cuda_ar_pipeline * p,
|
| 745 |
+
ggml_backend_t * backends,
|
| 746 |
+
ggml_tensor ** tensors) {
|
| 747 |
+
GGML_ASSERT(p != nullptr);
|
| 748 |
+
|
| 749 |
+
const int n = p->n_devices;
|
| 750 |
+
GGML_ASSERT(n == 2);
|
| 751 |
+
|
| 752 |
+
const ggml_type input_type = tensors[0]->type;
|
| 753 |
+
GGML_ASSERT(input_type == GGML_TYPE_F32 || input_type == GGML_TYPE_F16 || input_type == GGML_TYPE_BF16);
|
| 754 |
+
|
| 755 |
+
const int64_t ne = ggml_nelements(tensors[0]);
|
| 756 |
+
GGML_ASSERT(ne > 0);
|
| 757 |
+
|
| 758 |
+
const size_t input_nbytes = ggml_nbytes(tensors[0]);
|
| 759 |
+
|
| 760 |
+
// BF16 round-trip: F32 inputs >= bf16_threshold are converted to BF16 for
|
| 761 |
+
// the reduction (chunked or copy-engine), halving on-wire bytes. Matches
|
| 762 |
+
// NCCL's behaviour. The pre-conversion zeroes inactive shards so the
|
| 763 |
+
// inner paths see them as already-prepared compute tensors.
|
| 764 |
+
const bool use_bf16 =
|
| 765 |
+
input_type == GGML_TYPE_F32 &&
|
| 766 |
+
p->bf16_threshold > 0 &&
|
| 767 |
+
input_nbytes >= p->bf16_threshold;
|
| 768 |
+
|
| 769 |
+
const ggml_type kernel_type = use_bf16 ? GGML_TYPE_BF16 : input_type;
|
| 770 |
+
const size_t type_size = ggml_type_size(kernel_type);
|
| 771 |
+
GGML_ASSERT(p->buf_bytes >= type_size);
|
| 772 |
+
const size_t nbytes = (size_t) ne * type_size;
|
| 773 |
+
|
| 774 |
+
bool compute_flag[GGML_CUDA_MAX_DEVICES] = {};
|
| 775 |
+
for (int i = 0; i < n; ++i) {
|
| 776 |
+
compute_flag[i] = (tensors[i]->flags & GGML_TENSOR_FLAG_COMPUTE) != 0;
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
// Decide between copy-engine and chunked kernel paths based on the working
|
| 780 |
+
// type's actual byte count. No upper bound: copy_outer slices reductions
|
| 781 |
+
// larger than copy_bytes into copy_bytes-sized pieces.
|
| 782 |
+
const bool use_copy_engine =
|
| 783 |
+
p->copy_threshold > 0 &&
|
| 784 |
+
nbytes >= p->copy_threshold;
|
| 785 |
+
|
| 786 |
+
// BF16 inactive-shard zeroing: when use_bf16 is on, the combined kernel
|
| 787 |
+
// (chunked kernel path) and the combined add kernel (copy_engine path)
|
| 788 |
+
// both accumulate into the F32 tensor data directly, so an inactive
|
| 789 |
+
// shard's accumulator must start at zero.
|
| 790 |
+
if (use_bf16) {
|
| 791 |
+
for (int i = 0; i < n; ++i) {
|
| 792 |
+
if (!compute_flag[i]) {
|
| 793 |
+
auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context);
|
| 794 |
+
GGML_ASSERT(cuda_ctx->device == p->devices[i]);
|
| 795 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 796 |
+
CUDA_CHECK(cudaMemsetAsync(tensors[i]->data, 0, (size_t) ne * sizeof(float), cuda_ctx->stream()));
|
| 797 |
+
}
|
| 798 |
+
}
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
// Pre-convert F32 -> BF16 into bf16_tmp ONLY for the copy_engine + use_bf16
|
| 802 |
+
// path; the chunked kernel path's combined kernel does the conversion
|
| 803 |
+
// inline as it writes to host_buf.
|
| 804 |
+
ggml_cuda_pool_alloc<nv_bfloat16> bf16_tmp[GGML_CUDA_MAX_DEVICES];
|
| 805 |
+
void * copy_src_ptr[GGML_CUDA_MAX_DEVICES] = {};
|
| 806 |
+
|
| 807 |
+
if (use_copy_engine && use_bf16) {
|
| 808 |
+
to_bf16_cuda_t to_bf16 = ggml_get_to_bf16_cuda(GGML_TYPE_F32);
|
| 809 |
+
for (int i = 0; i < n; ++i) {
|
| 810 |
+
auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context);
|
| 811 |
+
GGML_ASSERT(cuda_ctx->device == p->devices[i]);
|
| 812 |
+
bf16_tmp[i].pool = &cuda_ctx->pool();
|
| 813 |
+
bf16_tmp[i].alloc(ne);
|
| 814 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 815 |
+
if (compute_flag[i]) {
|
| 816 |
+
to_bf16(tensors[i]->data, bf16_tmp[i].get(), ne, cuda_ctx->stream());
|
| 817 |
+
CUDA_CHECK(cudaGetLastError());
|
| 818 |
+
} else {
|
| 819 |
+
CUDA_CHECK(cudaMemsetAsync(bf16_tmp[i].get(), 0, nbytes, cuda_ctx->stream()));
|
| 820 |
+
}
|
| 821 |
+
copy_src_ptr[i] = bf16_tmp[i].get();
|
| 822 |
+
}
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
bool ok = true;
|
| 826 |
+
if (use_copy_engine) {
|
| 827 |
+
// After up-front BF16 conversion, the tmp buffers already hold the
|
| 828 |
+
// (possibly zeroed-for-inactive) data, so the inner path can treat
|
| 829 |
+
// every shard as compute.
|
| 830 |
+
bool inner_compute[GGML_CUDA_MAX_DEVICES];
|
| 831 |
+
for (int i = 0; i < n; ++i) {
|
| 832 |
+
inner_compute[i] = use_bf16 ? true : compute_flag[i];
|
| 833 |
+
}
|
| 834 |
+
|
| 835 |
+
// Dispatch into copy_impl with explicit src/dst types. When use_bf16
|
| 836 |
+
// is on, the wire type is BF16 (src = bf16_tmp) and the accumulator
|
| 837 |
+
// is F32 (dst = tensors[i]->data); the combined add kernel rounds dst
|
| 838 |
+
// through BF16 for bit-equivalence and writes F32 directly, so no
|
| 839 |
+
// post-conversion is needed. Otherwise src == dst (same native type).
|
| 840 |
+
if (use_bf16) {
|
| 841 |
+
GGML_ASSERT(kernel_type == GGML_TYPE_BF16);
|
| 842 |
+
nv_bfloat16 * src[GGML_CUDA_MAX_DEVICES] = {};
|
| 843 |
+
float * dst[GGML_CUDA_MAX_DEVICES] = {};
|
| 844 |
+
for (int i = 0; i < n; ++i) {
|
| 845 |
+
src[i] = static_cast<nv_bfloat16 *>(copy_src_ptr[i]);
|
| 846 |
+
dst[i] = static_cast<float *>(tensors[i]->data);
|
| 847 |
+
}
|
| 848 |
+
ok = ggml_cuda_ar_allreduce_copy_outer<nv_bfloat16, float>(
|
| 849 |
+
p, backends, src, dst, inner_compute, ne);
|
| 850 |
+
} else {
|
| 851 |
+
switch (kernel_type) {
|
| 852 |
+
case GGML_TYPE_F32: {
|
| 853 |
+
float * buf[GGML_CUDA_MAX_DEVICES] = {};
|
| 854 |
+
for (int i = 0; i < n; ++i) {
|
| 855 |
+
buf[i] = static_cast<float *>(tensors[i]->data);
|
| 856 |
+
}
|
| 857 |
+
ok = ggml_cuda_ar_allreduce_copy_outer<float, float>(
|
| 858 |
+
p, backends, buf, buf, inner_compute, ne);
|
| 859 |
+
break;
|
| 860 |
+
}
|
| 861 |
+
case GGML_TYPE_BF16: {
|
| 862 |
+
nv_bfloat16 * buf[GGML_CUDA_MAX_DEVICES] = {};
|
| 863 |
+
for (int i = 0; i < n; ++i) {
|
| 864 |
+
buf[i] = static_cast<nv_bfloat16 *>(tensors[i]->data);
|
| 865 |
+
}
|
| 866 |
+
ok = ggml_cuda_ar_allreduce_copy_outer<nv_bfloat16, nv_bfloat16>(
|
| 867 |
+
p, backends, buf, buf, inner_compute, ne);
|
| 868 |
+
break;
|
| 869 |
+
}
|
| 870 |
+
case GGML_TYPE_F16: {
|
| 871 |
+
half * buf[GGML_CUDA_MAX_DEVICES] = {};
|
| 872 |
+
for (int i = 0; i < n; ++i) {
|
| 873 |
+
buf[i] = static_cast<half *>(tensors[i]->data);
|
| 874 |
+
}
|
| 875 |
+
ok = ggml_cuda_ar_allreduce_copy_outer<half, half>(
|
| 876 |
+
p, backends, buf, buf, inner_compute, ne);
|
| 877 |
+
break;
|
| 878 |
+
}
|
| 879 |
+
default:
|
| 880 |
+
GGML_ASSERT(false);
|
| 881 |
+
}
|
| 882 |
+
}
|
| 883 |
+
} else {
|
| 884 |
+
// host_buf carries T_wire-typed data; max_chunk_elems is the count that
|
| 885 |
+
// fits in one host_buf at the wire size.
|
| 886 |
+
const size_t max_chunk_elems = p->buf_bytes / type_size;
|
| 887 |
+
const size_t input_type_size = ggml_type_size(input_type);
|
| 888 |
+
|
| 889 |
+
// Chunked kernel path runs entirely on the caller's compute stream:
|
| 890 |
+
// since AR is a barrier here, same-stream ordering subsumes any
|
| 891 |
+
// cross-stream event handshake that the copy-engine path needs, and
|
| 892 |
+
// skips the cross-stream scheduling overhead that was hurting the
|
| 893 |
+
// small-tensor (tg) latency on the AR-stream variant. Only ev.ker is
|
| 894 |
+
// still recorded at end-of-AR for acquire_slot's pool-wraparound check.
|
| 895 |
+
for (int64_t chunk_start = 0; chunk_start < ne; chunk_start += (int64_t) max_chunk_elems) {
|
| 896 |
+
const size_t remaining_elems = (size_t) (ne - chunk_start);
|
| 897 |
+
const size_t chunk_elems = remaining_elems < max_chunk_elems ? remaining_elems : max_chunk_elems;
|
| 898 |
+
const size_t chunk_dst_bytes = chunk_elems * input_type_size;
|
| 899 |
+
|
| 900 |
+
const auto [slot, token] = ggml_cuda_ar_acquire_slot(p);
|
| 901 |
+
const bool last_chunk = chunk_start + (int64_t) chunk_elems == ne;
|
| 902 |
+
|
| 903 |
+
for (int i = 0; i < n; ++i) {
|
| 904 |
+
const int peer = 1 - i; // valid for n == 2 only
|
| 905 |
+
ggml_cuda_set_device(p->devices[i]);
|
| 906 |
+
auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context);
|
| 907 |
+
GGML_ASSERT(cuda_ctx->device == p->devices[i]);
|
| 908 |
+
cudaStream_t stream = cuda_ctx->stream();
|
| 909 |
+
|
| 910 |
+
char * data = static_cast<char *>(tensors[i]->data) + chunk_start * (int64_t) input_type_size;
|
| 911 |
+
|
| 912 |
+
// Match NCCL/meta-backend semantics: inactive shards contribute
|
| 913 |
+
// zeros. On the BF16 path the F32 tensor data was already
|
| 914 |
+
// zeroed up-front (above), so per-chunk zeroing isn't needed.
|
| 915 |
+
if (!compute_flag[i] && !use_bf16) {
|
| 916 |
+
CUDA_CHECK(cudaMemsetAsync(data, 0, chunk_dst_bytes, stream));
|
| 917 |
+
}
|
| 918 |
+
|
| 919 |
+
#define LAUNCH_AR_KERNEL(T_dst, T_wire) \
|
| 920 |
+
ggml_cuda_ar_kernel<T_dst, T_wire><<<dim3(GGML_CUDA_AR_KERNEL_BLOCKS), dim3(256), 0, stream>>>( \
|
| 921 |
+
reinterpret_cast<const T_dst *>(data), \
|
| 922 |
+
reinterpret_cast<T_dst *>(data), \
|
| 923 |
+
reinterpret_cast<T_wire *>(p->host_buf[i].dev + (size_t) slot * p->buf_bytes), \
|
| 924 |
+
reinterpret_cast<const T_wire *>(p->host_buf[peer].dev + (size_t) slot * p->buf_bytes), \
|
| 925 |
+
static_cast<int>(chunk_elems), \
|
| 926 |
+
ggml_cuda_ar_arrival_ptr(p, slot, i), \
|
| 927 |
+
ggml_cuda_ar_arrival_ptr(p, slot, peer), \
|
| 928 |
+
token)
|
| 929 |
+
|
| 930 |
+
if (use_bf16) {
|
| 931 |
+
GGML_ASSERT(input_type == GGML_TYPE_F32);
|
| 932 |
+
LAUNCH_AR_KERNEL(float, nv_bfloat16);
|
| 933 |
+
} else {
|
| 934 |
+
switch (input_type) {
|
| 935 |
+
case GGML_TYPE_F32: LAUNCH_AR_KERNEL(float, float); break;
|
| 936 |
+
case GGML_TYPE_F16: LAUNCH_AR_KERNEL(half, half); break;
|
| 937 |
+
case GGML_TYPE_BF16: LAUNCH_AR_KERNEL(nv_bfloat16, nv_bfloat16); break;
|
| 938 |
+
default: GGML_ASSERT(false);
|
| 939 |
+
}
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
#undef LAUNCH_AR_KERNEL
|
| 943 |
+
CUDA_CHECK(cudaGetLastError());
|
| 944 |
+
|
| 945 |
+
if (last_chunk) {
|
| 946 |
+
CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].ker, stream));
|
| 947 |
+
}
|
| 948 |
+
}
|
| 949 |
+
}
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
return ok;
|
| 953 |
+
}
|
| 954 |
+
|
| 955 |
+
#else // defined(GGML_USE_HIP) || defined(GGML_USE_MUSA)
|
| 956 |
+
|
| 957 |
+
// HIP and MUSA lack the host-mapped pinned-memory APIs (cudaHostAllocPortable
|
| 958 |
+
// / cudaHostAllocMapped / cudaHostGetDevicePointer) and __nanosleep that this
|
| 959 |
+
// implementation relies on, so the internal AllReduce is a CUDA-only feature.
|
| 960 |
+
// The dispatcher in ggml-cuda.cu treats a nullptr pipeline as "init failed"
|
| 961 |
+
// and silently falls back to the meta backend's generic AllReduce.
|
| 962 |
+
ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int *, size_t) {
|
| 963 |
+
return nullptr;
|
| 964 |
+
}
|
| 965 |
+
void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline *) {
|
| 966 |
+
}
|
| 967 |
+
bool ggml_cuda_ar_allreduce(ggml_cuda_ar_pipeline *, ggml_backend_t *, ggml_tensor **) {
|
| 968 |
+
return false;
|
| 969 |
+
}
|
| 970 |
+
|
| 971 |
+
#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)
|
vendor/src/ggml-cuda/allreduce.cuh
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
|
| 3 |
+
#include "common.cuh"
|
| 4 |
+
#include "ggml-backend-impl.h"
|
| 5 |
+
|
| 6 |
+
#include <cstddef>
|
| 7 |
+
|
| 8 |
+
// Opaque pipeline context -- owns all pinned buffers, streams, and events.
|
| 9 |
+
struct ggml_cuda_ar_pipeline;
|
| 10 |
+
|
| 11 |
+
// Allocate a pipeline for n_devices GPUs.
|
| 12 |
+
// devices[] holds the CUDA device IDs in rank order.
|
| 13 |
+
// Returns nullptr on allocation failure.
|
| 14 |
+
ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(
|
| 15 |
+
const int * devices, size_t n_devices);
|
| 16 |
+
|
| 17 |
+
// Release all resources owned by the pipeline.
|
| 18 |
+
void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline * pipeline);
|
| 19 |
+
|
| 20 |
+
// Execute an in-place AllReduce (sum) across tensors[0..n_devices-1].
|
| 21 |
+
// tensors[i] must live on the device managed by backends[i] and be
|
| 22 |
+
// contiguous F32, F16, or BF16.
|
| 23 |
+
// Preconditions are checked by the CUDA comm dispatcher before calling this.
|
| 24 |
+
// Returns true once the reduction work has been enqueued successfully.
|
| 25 |
+
bool ggml_cuda_ar_allreduce(
|
| 26 |
+
ggml_cuda_ar_pipeline * pipeline,
|
| 27 |
+
ggml_backend_t * backends,
|
| 28 |
+
ggml_tensor ** tensors);
|
| 29 |
+
|
vendor/src/ggml-cuda/arange.cu
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "arange.cuh"
|
| 2 |
+
|
| 3 |
+
static __global__ void arange_f32(float * dst, const int ne0, const float start, const float step) {
|
| 4 |
+
// blockIDx.x: idx of ne0 / BLOCK_SIZE
|
| 5 |
+
int nidx = threadIdx.x + blockIdx.x * blockDim.x;
|
| 6 |
+
if (nidx >= ne0) {
|
| 7 |
+
return;
|
| 8 |
+
}
|
| 9 |
+
dst[nidx] = start + step * nidx;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
static void arange_f32_cuda(float * dst, const int ne0, const float start, const float step, cudaStream_t stream) {
|
| 13 |
+
int num_blocks = (ne0 + CUDA_ARANGE_BLOCK_SIZE - 1) / CUDA_ARANGE_BLOCK_SIZE;
|
| 14 |
+
arange_f32<<<num_blocks, CUDA_ARANGE_BLOCK_SIZE, 0, stream>>>(dst, ne0, start, step);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
void ggml_cuda_op_arange(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 18 |
+
float * dst_d = (float *)dst->data;
|
| 19 |
+
cudaStream_t stream = ctx.stream();
|
| 20 |
+
|
| 21 |
+
GGML_ASSERT(dst->type == GGML_TYPE_F32);
|
| 22 |
+
|
| 23 |
+
float start;
|
| 24 |
+
float stop;
|
| 25 |
+
float step;
|
| 26 |
+
memcpy(&start, (float *)dst->op_params + 0, sizeof(float));
|
| 27 |
+
memcpy(&stop, (float *)dst->op_params + 1, sizeof(float));
|
| 28 |
+
memcpy(&step, (float *)dst->op_params + 2, sizeof(float));
|
| 29 |
+
|
| 30 |
+
int64_t steps = (int64_t)ceil((stop - start) / step);
|
| 31 |
+
GGML_ASSERT(ggml_nelements(dst) == steps);
|
| 32 |
+
|
| 33 |
+
arange_f32_cuda(dst_d, dst->ne[0], start, step, stream);
|
| 34 |
+
}
|
vendor/src/ggml-cuda/arange.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_ARANGE_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_op_arange(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/argmax.cu
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <algorithm>
|
| 2 |
+
#include <cstdint>
|
| 3 |
+
|
| 4 |
+
#include "argmax.cuh"
|
| 5 |
+
#include "common.cuh"
|
| 6 |
+
#include "sum.cuh"
|
| 7 |
+
|
| 8 |
+
static __global__ void argmax_f32(const float * __restrict__ x, int32_t * __restrict__ dst, const int64_t ncols) {
|
| 9 |
+
const int64_t row = blockIdx.x;
|
| 10 |
+
|
| 11 |
+
float maxval = -FLT_MAX;
|
| 12 |
+
int argmax = -1;
|
| 13 |
+
const float * rowx = x + row * ncols;
|
| 14 |
+
|
| 15 |
+
for (int32_t col = threadIdx.x; col < ncols; col += blockDim.x) {
|
| 16 |
+
const float val = rowx[col];
|
| 17 |
+
if (val > maxval) {
|
| 18 |
+
maxval = val;
|
| 19 |
+
argmax = col;
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
#pragma unroll
|
| 24 |
+
for (int offset = WARP_SIZE/2; offset > 0; offset >>= 1) {
|
| 25 |
+
const float val = __shfl_xor_sync(0xFFFFFFFF, maxval, offset, WARP_SIZE);
|
| 26 |
+
const int col = __shfl_xor_sync(0xFFFFFFFF, argmax, offset, WARP_SIZE);
|
| 27 |
+
if (val > maxval) {
|
| 28 |
+
maxval = val;
|
| 29 |
+
argmax = col;
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
const int n_warps = blockDim.x / WARP_SIZE;
|
| 34 |
+
const int lane_id = threadIdx.x % WARP_SIZE;
|
| 35 |
+
const int warp_id = threadIdx.x / WARP_SIZE;
|
| 36 |
+
if (n_warps > 1) {
|
| 37 |
+
constexpr int max_warps = 1024 / WARP_SIZE;
|
| 38 |
+
__shared__ float shared_maxval[max_warps];
|
| 39 |
+
__shared__ int shared_argmax[max_warps];
|
| 40 |
+
if (lane_id == 0) {
|
| 41 |
+
shared_maxval[warp_id] = maxval;
|
| 42 |
+
shared_argmax[warp_id] = argmax;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
__syncthreads();
|
| 46 |
+
|
| 47 |
+
if (warp_id == 0) {
|
| 48 |
+
if (lane_id < n_warps) {
|
| 49 |
+
maxval = shared_maxval[lane_id];
|
| 50 |
+
argmax = shared_argmax[lane_id];
|
| 51 |
+
}
|
| 52 |
+
#pragma unroll
|
| 53 |
+
for (int offset = WARP_SIZE/2; offset > 0; offset >>= 1) {
|
| 54 |
+
const float val = __shfl_xor_sync(0xFFFFFFFF, maxval, offset, WARP_SIZE);
|
| 55 |
+
const int col = __shfl_xor_sync(0xFFFFFFFF, argmax, offset, WARP_SIZE);
|
| 56 |
+
if (val > maxval) {
|
| 57 |
+
maxval = val;
|
| 58 |
+
argmax = col;
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
if (warp_id == 0 && lane_id == 0) {
|
| 65 |
+
dst[row] = argmax;
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
void ggml_cuda_argmax(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 70 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 71 |
+
|
| 72 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
| 73 |
+
GGML_ASSERT( dst->type == GGML_TYPE_I32);
|
| 74 |
+
|
| 75 |
+
GGML_ASSERT(ggml_is_contiguous(src0));
|
| 76 |
+
|
| 77 |
+
const int64_t ne00 = src0->ne[0];
|
| 78 |
+
const int64_t nrows = ggml_nrows(src0);
|
| 79 |
+
|
| 80 |
+
const float * src0_d = (const float *) src0->data;
|
| 81 |
+
int32_t * dst_d = (int32_t *) dst->data;
|
| 82 |
+
|
| 83 |
+
cudaStream_t stream = ctx.stream();
|
| 84 |
+
|
| 85 |
+
const int64_t num_blocks = nrows;
|
| 86 |
+
const int64_t num_threads = std::min<int64_t>(1024, (ne00 + WARP_SIZE - 1) / WARP_SIZE * WARP_SIZE);
|
| 87 |
+
const dim3 blocks_dim(num_threads, 1, 1);
|
| 88 |
+
const dim3 blocks_num(num_blocks, 1, 1);
|
| 89 |
+
|
| 90 |
+
argmax_f32<<<blocks_num, blocks_dim, 0, stream>>>(src0_d, dst_d, ne00);
|
| 91 |
+
}
|
vendor/src/ggml-cuda/argmax.cuh
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
void ggml_cuda_argmax(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/argsort.cu
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "argsort.cuh"
|
| 2 |
+
|
| 3 |
+
#ifdef GGML_CUDA_USE_CUB
|
| 4 |
+
# include <cub/cub.cuh>
|
| 5 |
+
# if (CCCL_MAJOR_VERSION >= 3 && CCCL_MINOR_VERSION >= 1)
|
| 6 |
+
# define STRIDED_ITERATOR_AVAILABLE
|
| 7 |
+
# include <cuda/iterator>
|
| 8 |
+
# endif
|
| 9 |
+
using namespace cub;
|
| 10 |
+
#endif // GGML_CUDA_USE_CUB
|
| 11 |
+
|
| 12 |
+
static __global__ void init_indices(int * indices, const int ncols, const int nrows) {
|
| 13 |
+
const int col = blockIdx.x * blockDim.x + threadIdx.x;
|
| 14 |
+
const int row = blockIdx.y;
|
| 15 |
+
|
| 16 |
+
if (col < ncols && row < nrows) {
|
| 17 |
+
indices[row * ncols + col] = col;
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
#ifndef STRIDED_ITERATOR_AVAILABLE
|
| 22 |
+
static __global__ void init_offsets(int * offsets, const int ncols, const int nrows) {
|
| 23 |
+
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 24 |
+
if (idx <= nrows) {
|
| 25 |
+
offsets[idx] = idx * ncols;
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
#endif // STRIDED_ITERATOR_AVAILABLE
|
| 29 |
+
|
| 30 |
+
#ifdef GGML_CUDA_USE_CUB
|
| 31 |
+
|
| 32 |
+
// returns the suggested maximum number of rows to process during one argsort_f32_i32_cuda_cub() call
|
| 33 |
+
int argsort_f32_i32_cuda_cub_chunk_nrows(const size_t nb01, const int64_t nrows) {
|
| 34 |
+
// perform argsort in chunks up to approximately this size (currently 64MB)
|
| 35 |
+
// to avoid excessive temporary buffers memory usage
|
| 36 |
+
const int chunk_bytes = 1 << 26;
|
| 37 |
+
|
| 38 |
+
// calculate how many rows will fit in one chunk (must be at least one)
|
| 39 |
+
const int chunk_nrows = std::max((int) (chunk_bytes / nb01), 1);
|
| 40 |
+
|
| 41 |
+
// limit the resulting amount to total nrows
|
| 42 |
+
return std::min((int64_t) chunk_nrows, nrows);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
|
| 46 |
+
const float * x,
|
| 47 |
+
int * dst,
|
| 48 |
+
const int ncols,
|
| 49 |
+
const int nrows,
|
| 50 |
+
ggml_sort_order order,
|
| 51 |
+
cudaStream_t stream) {
|
| 52 |
+
ggml_cuda_pool_alloc<int> temp_indices_alloc(pool, ncols * nrows);
|
| 53 |
+
ggml_cuda_pool_alloc<float> temp_keys_alloc(pool, ncols * nrows);
|
| 54 |
+
|
| 55 |
+
int * temp_indices = temp_indices_alloc.get();
|
| 56 |
+
float * temp_keys = temp_keys_alloc.get();
|
| 57 |
+
|
| 58 |
+
static const int block_size = 256;
|
| 59 |
+
const dim3 grid_size((ncols + block_size - 1) / block_size, nrows);
|
| 60 |
+
init_indices<<<grid_size, block_size, 0, stream>>>(temp_indices, ncols, nrows);
|
| 61 |
+
|
| 62 |
+
#ifdef STRIDED_ITERATOR_AVAILABLE
|
| 63 |
+
auto offset_iterator = cuda::make_strided_iterator(cuda::make_counting_iterator(0), ncols);
|
| 64 |
+
#else
|
| 65 |
+
// offset_iterator needs to populate nrows + 1 elements, so we also have to ceildiv nrows + 1 by block_size
|
| 66 |
+
const int nrows_offset = nrows + 1;
|
| 67 |
+
ggml_cuda_pool_alloc<int> offsets_alloc(pool, nrows_offset);
|
| 68 |
+
int * offset_iterator = offsets_alloc.get();
|
| 69 |
+
const dim3 offset_grid((nrows_offset + block_size - 1) / block_size);
|
| 70 |
+
init_offsets<<<offset_grid, block_size, 0, stream>>>(offset_iterator, ncols, nrows);
|
| 71 |
+
#endif
|
| 72 |
+
CUDA_CHECK(cudaMemcpyAsync(temp_keys, x, ncols * nrows * sizeof(float), cudaMemcpyDeviceToDevice, stream));
|
| 73 |
+
|
| 74 |
+
size_t temp_storage_bytes = 0;
|
| 75 |
+
|
| 76 |
+
bool is_capturing = false;
|
| 77 |
+
#ifdef USE_CUDA_GRAPH
|
| 78 |
+
// Currently (confirmed for CCCL <= 3.2) DeviceSegmentedSort does not support stream capture, while DeviceSegmentedRadixSort does.
|
| 79 |
+
// See https://github.com/NVIDIA/cccl/issues/5661#issuecomment-3229037149
|
| 80 |
+
// TODO: constrain this to the CCCL versions that have this issue once it's resolved in a future CCCL release.
|
| 81 |
+
cudaStreamCaptureStatus capture_status;
|
| 82 |
+
CUDA_CHECK(cudaStreamIsCapturing(stream, &capture_status));
|
| 83 |
+
is_capturing = (capture_status != cudaStreamCaptureStatusNone);
|
| 84 |
+
#endif // USE_CUDA_GRAPH
|
| 85 |
+
|
| 86 |
+
if (order == GGML_SORT_ORDER_ASC) {
|
| 87 |
+
if (nrows == 1) {
|
| 88 |
+
CUDA_CHECK(DeviceRadixSort::SortPairs(nullptr, temp_storage_bytes, temp_keys, temp_keys, // keys (in-place)
|
| 89 |
+
temp_indices, dst, // values (indices)
|
| 90 |
+
ncols, 0, sizeof(float) * 8, stream));
|
| 91 |
+
} else if (is_capturing) {
|
| 92 |
+
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairs(
|
| 93 |
+
nullptr, temp_storage_bytes, temp_keys, temp_keys, // keys (in-place)
|
| 94 |
+
temp_indices, dst, // values (indices)
|
| 95 |
+
ncols * nrows, nrows, // num items, num segments
|
| 96 |
+
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
|
| 97 |
+
} else {
|
| 98 |
+
CUDA_CHECK(DeviceSegmentedSort::SortPairs(nullptr, temp_storage_bytes, temp_keys,
|
| 99 |
+
temp_keys, // keys (in-place)
|
| 100 |
+
temp_indices, dst, // values (indices)
|
| 101 |
+
ncols * nrows, nrows, // num items, num segments
|
| 102 |
+
offset_iterator, offset_iterator + 1, stream));
|
| 103 |
+
}
|
| 104 |
+
} else {
|
| 105 |
+
if (nrows == 1) {
|
| 106 |
+
CUDA_CHECK(DeviceRadixSort::SortPairsDescending(nullptr, temp_storage_bytes, temp_keys,
|
| 107 |
+
temp_keys, // keys (in-place)
|
| 108 |
+
temp_indices, dst, // values (indices)
|
| 109 |
+
ncols, 0, sizeof(float) * 8, stream));
|
| 110 |
+
} else if (is_capturing) {
|
| 111 |
+
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairsDescending(
|
| 112 |
+
nullptr, temp_storage_bytes, temp_keys, temp_keys, temp_indices, dst, ncols * nrows, nrows,
|
| 113 |
+
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
|
| 114 |
+
} else {
|
| 115 |
+
CUDA_CHECK(DeviceSegmentedSort::SortPairsDescending(nullptr, temp_storage_bytes, temp_keys, temp_keys,
|
| 116 |
+
temp_indices, dst, ncols * nrows, nrows,
|
| 117 |
+
offset_iterator, offset_iterator + 1, stream));
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
ggml_cuda_pool_alloc<uint8_t> temp_storage_alloc(pool, temp_storage_bytes);
|
| 122 |
+
void * d_temp_storage = temp_storage_alloc.get();
|
| 123 |
+
|
| 124 |
+
if (order == GGML_SORT_ORDER_ASC) {
|
| 125 |
+
if (nrows == 1) {
|
| 126 |
+
CUDA_CHECK(DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys,
|
| 127 |
+
temp_keys, // keys (in-place)
|
| 128 |
+
temp_indices, dst, // values (indices)
|
| 129 |
+
ncols, 0, sizeof(float) * 8, stream));
|
| 130 |
+
} else if (is_capturing) {
|
| 131 |
+
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys,
|
| 132 |
+
temp_indices, dst, ncols * nrows, nrows, offset_iterator,
|
| 133 |
+
offset_iterator + 1, 0, sizeof(float) * 8, stream));
|
| 134 |
+
} else {
|
| 135 |
+
CUDA_CHECK(DeviceSegmentedSort::SortPairs(d_temp_storage, temp_storage_bytes, temp_keys, temp_keys,
|
| 136 |
+
temp_indices, dst, ncols * nrows, nrows, offset_iterator,
|
| 137 |
+
offset_iterator + 1, stream));
|
| 138 |
+
}
|
| 139 |
+
} else {
|
| 140 |
+
if (nrows == 1) {
|
| 141 |
+
CUDA_CHECK(DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, temp_keys,
|
| 142 |
+
temp_keys, // keys (in-place)
|
| 143 |
+
temp_indices, dst, // values (indices)
|
| 144 |
+
ncols, 0, sizeof(float) * 8, stream));
|
| 145 |
+
} else if (is_capturing) {
|
| 146 |
+
CUDA_CHECK(DeviceSegmentedRadixSort::SortPairsDescending(
|
| 147 |
+
d_temp_storage, temp_storage_bytes, temp_keys, temp_keys, temp_indices, dst, ncols * nrows, nrows,
|
| 148 |
+
offset_iterator, offset_iterator + 1, 0, sizeof(float) * 8, stream));
|
| 149 |
+
} else {
|
| 150 |
+
CUDA_CHECK(DeviceSegmentedSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, temp_keys,
|
| 151 |
+
temp_keys, temp_indices, dst, ncols * nrows, nrows,
|
| 152 |
+
offset_iterator, offset_iterator + 1, stream));
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
#endif // GGML_CUDA_USE_CUB
|
| 157 |
+
|
| 158 |
+
// Bitonic sort implementation
|
| 159 |
+
template<typename T>
|
| 160 |
+
static inline __device__ void ggml_cuda_swap(T & a, T & b) {
|
| 161 |
+
T tmp = a;
|
| 162 |
+
a = b;
|
| 163 |
+
b = tmp;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
template<ggml_sort_order order>
|
| 167 |
+
static __global__ void k_argsort_f32_i32(const float * x, int * dst, const int ncols, int ncols_pad) {
|
| 168 |
+
// bitonic sort
|
| 169 |
+
int col = threadIdx.x;
|
| 170 |
+
int row = blockIdx.x;
|
| 171 |
+
|
| 172 |
+
if (col >= ncols_pad) {
|
| 173 |
+
return;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
const float * x_row = x + row * ncols;
|
| 177 |
+
extern __shared__ int dst_row[];
|
| 178 |
+
|
| 179 |
+
// initialize indices
|
| 180 |
+
dst_row[col] = col;
|
| 181 |
+
|
| 182 |
+
__syncthreads();
|
| 183 |
+
|
| 184 |
+
for (int k = 2; k <= ncols_pad; k *= 2) {
|
| 185 |
+
for (int j = k / 2; j > 0; j /= 2) {
|
| 186 |
+
int ixj = col ^ j;
|
| 187 |
+
if (ixj > col) {
|
| 188 |
+
if ((col & k) == 0) {
|
| 189 |
+
if (dst_row[col] >= ncols ||
|
| 190 |
+
(dst_row[ixj] < ncols && (order == GGML_SORT_ORDER_ASC ?
|
| 191 |
+
x_row[dst_row[col]] > x_row[dst_row[ixj]] :
|
| 192 |
+
x_row[dst_row[col]] < x_row[dst_row[ixj]]))
|
| 193 |
+
) {
|
| 194 |
+
ggml_cuda_swap(dst_row[col], dst_row[ixj]);
|
| 195 |
+
}
|
| 196 |
+
} else {
|
| 197 |
+
if (dst_row[ixj] >= ncols ||
|
| 198 |
+
(dst_row[col] < ncols && (order == GGML_SORT_ORDER_ASC ?
|
| 199 |
+
x_row[dst_row[col]] < x_row[dst_row[ixj]] :
|
| 200 |
+
x_row[dst_row[col]] > x_row[dst_row[ixj]]))
|
| 201 |
+
) {
|
| 202 |
+
ggml_cuda_swap(dst_row[col], dst_row[ixj]);
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
__syncthreads();
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
// copy the result to dst without the padding
|
| 211 |
+
if (col < ncols) {
|
| 212 |
+
dst[row * ncols + col] = dst_row[col];
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
static int next_power_of_2(int x) {
|
| 217 |
+
int n = 1;
|
| 218 |
+
while (n < x) {
|
| 219 |
+
n *= 2;
|
| 220 |
+
}
|
| 221 |
+
return n;
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
void argsort_f32_i32_cuda_bitonic(const float * x,
|
| 225 |
+
int * dst,
|
| 226 |
+
const int ncols,
|
| 227 |
+
const int nrows,
|
| 228 |
+
ggml_sort_order order,
|
| 229 |
+
cudaStream_t stream) {
|
| 230 |
+
// bitonic sort requires ncols to be power of 2
|
| 231 |
+
const int ncols_pad = next_power_of_2(ncols);
|
| 232 |
+
|
| 233 |
+
const dim3 block_dims(ncols_pad, 1, 1);
|
| 234 |
+
const dim3 block_nums(nrows, 1, 1);
|
| 235 |
+
const size_t shared_mem = ncols_pad * sizeof(int);
|
| 236 |
+
|
| 237 |
+
// FIXME: this limit could be raised by ~2-4x on Ampere or newer
|
| 238 |
+
GGML_ASSERT(shared_mem <= ggml_cuda_info().devices[ggml_cuda_get_device()].smpb);
|
| 239 |
+
|
| 240 |
+
if (order == GGML_SORT_ORDER_ASC) {
|
| 241 |
+
k_argsort_f32_i32<GGML_SORT_ORDER_ASC>
|
| 242 |
+
<<<block_nums, block_dims, shared_mem, stream>>>(x, dst, ncols, ncols_pad);
|
| 243 |
+
} else if (order == GGML_SORT_ORDER_DESC) {
|
| 244 |
+
k_argsort_f32_i32<GGML_SORT_ORDER_DESC>
|
| 245 |
+
<<<block_nums, block_dims, shared_mem, stream>>>(x, dst, ncols, ncols_pad);
|
| 246 |
+
} else {
|
| 247 |
+
GGML_ABORT("fatal error");
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
void ggml_cuda_op_argsort(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 252 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 253 |
+
const float * src0_d = (const float *)src0->data;
|
| 254 |
+
float * dst_d = (float *)dst->data;
|
| 255 |
+
cudaStream_t stream = ctx.stream();
|
| 256 |
+
|
| 257 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
| 258 |
+
GGML_ASSERT( dst->type == GGML_TYPE_I32);
|
| 259 |
+
GGML_ASSERT(ggml_is_contiguous(src0));
|
| 260 |
+
|
| 261 |
+
const int64_t ncols = src0->ne[0];
|
| 262 |
+
const int64_t nrows = ggml_nrows(src0);
|
| 263 |
+
|
| 264 |
+
enum ggml_sort_order order = (enum ggml_sort_order) dst->op_params[0];
|
| 265 |
+
|
| 266 |
+
#ifdef GGML_CUDA_USE_CUB
|
| 267 |
+
const int ncols_pad = next_power_of_2(ncols);
|
| 268 |
+
const size_t shared_mem = ncols_pad * sizeof(int);
|
| 269 |
+
const size_t max_shared_mem = ggml_cuda_info().devices[ggml_cuda_get_device()].smpb;
|
| 270 |
+
|
| 271 |
+
// early return if we can use bitonic argsort
|
| 272 |
+
if (shared_mem <= max_shared_mem && ncols <= 1024) {
|
| 273 |
+
argsort_f32_i32_cuda_bitonic(src0_d, (int *) dst_d, ncols, nrows, order, stream);
|
| 274 |
+
return;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
const int chunk_nrows = argsort_f32_i32_cuda_cub_chunk_nrows(src0->nb[1], nrows);
|
| 278 |
+
|
| 279 |
+
ggml_cuda_pool & pool = ctx.pool();
|
| 280 |
+
|
| 281 |
+
for (int64_t i = 0; i < nrows; i += chunk_nrows) {
|
| 282 |
+
int iter_nrows = std::min((int64_t) chunk_nrows, nrows - i);
|
| 283 |
+
|
| 284 |
+
argsort_f32_i32_cuda_cub(pool, src0_d, (int *) dst_d, ncols, iter_nrows, order, stream);
|
| 285 |
+
|
| 286 |
+
src0_d += ncols * iter_nrows;
|
| 287 |
+
dst_d += ncols * iter_nrows;
|
| 288 |
+
}
|
| 289 |
+
#else
|
| 290 |
+
argsort_f32_i32_cuda_bitonic(src0_d, (int *) dst_d, ncols, nrows, order, stream);
|
| 291 |
+
#endif
|
| 292 |
+
}
|
vendor/src/ggml-cuda/argsort.cuh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
void ggml_cuda_op_argsort(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 4 |
+
|
| 5 |
+
#ifdef GGML_CUDA_USE_CUB
|
| 6 |
+
int argsort_f32_i32_cuda_cub_chunk_nrows(const size_t nb01, const int64_t nrows);
|
| 7 |
+
void argsort_f32_i32_cuda_cub(ggml_cuda_pool & pool,
|
| 8 |
+
const float * x,
|
| 9 |
+
int * dst,
|
| 10 |
+
const int ncols,
|
| 11 |
+
const int nrows,
|
| 12 |
+
ggml_sort_order order,
|
| 13 |
+
cudaStream_t stream);
|
| 14 |
+
#endif // GGML_CUDA_USE_CUB
|
| 15 |
+
void argsort_f32_i32_cuda_bitonic(const float * x,
|
| 16 |
+
int * dst,
|
| 17 |
+
const int ncols,
|
| 18 |
+
const int nrows,
|
| 19 |
+
ggml_sort_order order,
|
| 20 |
+
cudaStream_t stream);
|
vendor/src/ggml-cuda/binbcast.cu
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "binbcast.cuh"
|
| 2 |
+
#include <cstdint>
|
| 3 |
+
#include <utility>
|
| 4 |
+
|
| 5 |
+
template<typename T, size_t>
|
| 6 |
+
using type_for_index = T;
|
| 7 |
+
|
| 8 |
+
static __device__ __forceinline__ float op_repeat(const float a, const float b) {
|
| 9 |
+
return b;
|
| 10 |
+
GGML_UNUSED(a);
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
static __device__ __forceinline__ float op_add(const float a, const float b) {
|
| 14 |
+
return a + b;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
static __device__ __forceinline__ float op_sub(const float a, const float b) {
|
| 18 |
+
return a - b;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
static __device__ __forceinline__ float op_mul(const float a, const float b) {
|
| 22 |
+
return a * b;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
static __device__ __forceinline__ float op_div(const float a, const float b) {
|
| 26 |
+
return a / b;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
template <float (*bin_op)(const float, const float),
|
| 30 |
+
typename src0_t,
|
| 31 |
+
typename src1_t,
|
| 32 |
+
typename dst_t,
|
| 33 |
+
typename... src1_ptrs>
|
| 34 |
+
static __global__ void k_bin_bcast(const src0_t * src0,
|
| 35 |
+
const src1_t * src1,
|
| 36 |
+
dst_t * dst,
|
| 37 |
+
const uint32_t ne0,
|
| 38 |
+
const uint32_t ne1,
|
| 39 |
+
const uint32_t ne2,
|
| 40 |
+
const uint3 ne3,
|
| 41 |
+
const uint3 ne10,
|
| 42 |
+
const uint3 ne11,
|
| 43 |
+
const uint3 ne12,
|
| 44 |
+
const uint3 ne13,
|
| 45 |
+
/*const uint32_t s0,*/
|
| 46 |
+
const uint32_t s1,
|
| 47 |
+
const uint32_t s2,
|
| 48 |
+
const uint32_t s3,
|
| 49 |
+
const uint32_t s00,
|
| 50 |
+
const uint32_t s01,
|
| 51 |
+
const uint32_t s02,
|
| 52 |
+
const uint32_t s03,
|
| 53 |
+
const uint32_t s10,
|
| 54 |
+
const uint32_t s11,
|
| 55 |
+
const uint32_t s12,
|
| 56 |
+
const uint32_t s13,
|
| 57 |
+
src1_ptrs... src1s) {
|
| 58 |
+
ggml_cuda_pdl_lc();
|
| 59 |
+
const uint32_t i0s = blockDim.x * blockIdx.x + threadIdx.x;
|
| 60 |
+
const uint32_t i1 = (blockDim.y * blockIdx.y + threadIdx.y);
|
| 61 |
+
const uint32_t i2 = fastdiv((blockDim.z * blockIdx.z + threadIdx.z), ne3);
|
| 62 |
+
const uint32_t i3 = (blockDim.z * blockIdx.z + threadIdx.z) - (i2 * ne3.z);
|
| 63 |
+
|
| 64 |
+
if (i0s >= ne0 || i1 >= ne1 || i2 >= ne2 || i3 >= ne3.z) {
|
| 65 |
+
return;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
const uint32_t i11 = fastmodulo(i1, ne11);
|
| 69 |
+
const uint32_t i12 = fastmodulo(i2, ne12);
|
| 70 |
+
const uint32_t i13 = fastmodulo(i3, ne13);
|
| 71 |
+
|
| 72 |
+
const size_t i_src0 = size_t( i3)*s03 + size_t( i2)*s02 + size_t( i1)*s01;
|
| 73 |
+
const size_t i_src1 = size_t(i13)*s13 + size_t(i12)*s12 + size_t(i11)*s11;
|
| 74 |
+
const size_t i_dst = size_t( i3)*s3 + size_t( i2)*s2 + size_t( i1)*s1;
|
| 75 |
+
|
| 76 |
+
const src0_t * src0_row = src0 ? (src0 + i_src0) : nullptr;
|
| 77 |
+
dst_t * dst_row = dst + i_dst;
|
| 78 |
+
|
| 79 |
+
const uint32_t s0 = blockDim.x * gridDim.x;
|
| 80 |
+
|
| 81 |
+
ggml_cuda_pdl_sync();
|
| 82 |
+
for (uint32_t i0 = i0s; i0 < ne0; i0 += s0) {
|
| 83 |
+
const uint32_t i10 = fastmodulo(i0, ne10);
|
| 84 |
+
|
| 85 |
+
float result = src0_row ? (float) src0_row[size_t(i0)*s00] : 0.0f;
|
| 86 |
+
if constexpr (sizeof...(src1_ptrs) > 0) {
|
| 87 |
+
result = (..., (result = bin_op(result, (float)src1s[i_src1 + size_t(i10)*s10])));
|
| 88 |
+
} else {
|
| 89 |
+
result = bin_op(result, (float)src1[i_src1 + size_t(i10)*s10]);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
dst_row[i0] = (dst_t) result;
|
| 93 |
+
|
| 94 |
+
// protect i0 from overflow
|
| 95 |
+
if (ne0 - i0 <= s0) {
|
| 96 |
+
break;
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
template <float (*bin_op)(const float, const float),
|
| 102 |
+
typename src0_t,
|
| 103 |
+
typename src1_t,
|
| 104 |
+
typename dst_t,
|
| 105 |
+
typename... src1_ptrs>
|
| 106 |
+
static __global__ void k_bin_bcast_unravel(const src0_t * src0,
|
| 107 |
+
const src1_t * src1,
|
| 108 |
+
dst_t * dst,
|
| 109 |
+
const uint3 ne0,
|
| 110 |
+
const uint3 ne1,
|
| 111 |
+
const uint3 ne2,
|
| 112 |
+
const uint32_t ne3,
|
| 113 |
+
const uint3 prod_012,
|
| 114 |
+
const uint3 prod_01,
|
| 115 |
+
const uint3 ne10,
|
| 116 |
+
const uint3 ne11,
|
| 117 |
+
const uint3 ne12,
|
| 118 |
+
const uint3 ne13,
|
| 119 |
+
/*const int s0,*/
|
| 120 |
+
const uint32_t s1,
|
| 121 |
+
const uint32_t s2,
|
| 122 |
+
const uint32_t s3,
|
| 123 |
+
const uint32_t s00,
|
| 124 |
+
const uint32_t s01,
|
| 125 |
+
const uint32_t s02,
|
| 126 |
+
const uint32_t s03,
|
| 127 |
+
const uint32_t s10,
|
| 128 |
+
const uint32_t s11,
|
| 129 |
+
const uint32_t s12,
|
| 130 |
+
const uint32_t s13,
|
| 131 |
+
src1_ptrs... src1s) {
|
| 132 |
+
const uint32_t i = blockDim.x*blockIdx.x + threadIdx.x;
|
| 133 |
+
|
| 134 |
+
const uint32_t i3 = fastdiv(i, prod_012);
|
| 135 |
+
const uint32_t i2 = fastdiv(i - i3 * prod_012.z, prod_01);
|
| 136 |
+
const uint32_t i1 = fastdiv(i - i3 * prod_012.z - i2 * prod_01.z, ne0);
|
| 137 |
+
const uint32_t i0 = i - i3 * prod_012.z - i2 * prod_01.z - i1 * ne0.z;
|
| 138 |
+
|
| 139 |
+
if (i0 >= ne0.z || i1 >= ne1.z || i2 >= ne2.z || i3 >= ne3) {
|
| 140 |
+
return;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
const uint32_t i11 = fastmodulo(i1, ne11);
|
| 144 |
+
const uint32_t i12 = fastmodulo(i2, ne12);
|
| 145 |
+
const uint32_t i13 = fastmodulo(i3, ne13);
|
| 146 |
+
|
| 147 |
+
const size_t i_src0 = size_t( i3)*s03 + size_t( i2)*s02 + size_t( i1)*s01;
|
| 148 |
+
const size_t i_src1 = size_t(i13)*s13 + size_t(i12)*s12 + size_t(i11)*s11;
|
| 149 |
+
const size_t i_dst = size_t( i3)*s3 + size_t( i2)*s2 + size_t( i1)*s1;
|
| 150 |
+
|
| 151 |
+
const src0_t * src0_row = src0 ? (src0 + i_src0) : nullptr;
|
| 152 |
+
dst_t * dst_row = dst + i_dst;
|
| 153 |
+
|
| 154 |
+
const uint32_t i10 = fastmodulo(i0, ne10);
|
| 155 |
+
|
| 156 |
+
ggml_cuda_pdl_sync();
|
| 157 |
+
float result = src0_row ? (float) src0_row[size_t(i0)*s00] : 0.0f;
|
| 158 |
+
if constexpr (sizeof...(src1_ptrs) > 0) {
|
| 159 |
+
result = (..., (result = bin_op(result, (float)src1s[i_src1 + size_t(i10)*s10])));
|
| 160 |
+
} else {
|
| 161 |
+
result = bin_op(result, (float)src1[i_src1 + size_t(i10)*s10]);
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
dst_row[i0] = (dst_t) result;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
template <float (*bin_op)(const float, const float), typename src0_t, typename src1_t, typename dst_t, size_t... I>
|
| 168 |
+
static void launch_bin_bcast_pack(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst,
|
| 169 |
+
const src0_t * src0_dd, const src1_t * src1_dd, dst_t * dst_dd,
|
| 170 |
+
cudaStream_t stream, std::index_sequence<I...>) {
|
| 171 |
+
GGML_TENSOR_BINARY_OP_LOCALS
|
| 172 |
+
|
| 173 |
+
int nr0 = ne10 / ne0;
|
| 174 |
+
int nr1 = ne11 / ne1;
|
| 175 |
+
int nr2 = ne12 / ne2;
|
| 176 |
+
int nr3 = ne13 / ne3;
|
| 177 |
+
|
| 178 |
+
int nr[4] = { nr0, nr1, nr2, nr3 };
|
| 179 |
+
|
| 180 |
+
int64_t cne[] = { ne0, ne1, ne2, ne3 };
|
| 181 |
+
int64_t cne0[] = { ne00, ne01, ne02, ne03 };
|
| 182 |
+
int64_t cne1[] = { ne10, ne11, ne12, ne13 };
|
| 183 |
+
|
| 184 |
+
size_t cnb[] = { nb0, nb1, nb2, nb3 };
|
| 185 |
+
size_t cnb0[] = { nb00, nb01, nb02, nb03 };
|
| 186 |
+
size_t cnb1[] = { nb10, nb11, nb12, nb13 };
|
| 187 |
+
|
| 188 |
+
auto collapse = [](int64_t cne[]) {
|
| 189 |
+
cne[0] *= cne[1];
|
| 190 |
+
cne[1] = cne[2];
|
| 191 |
+
cne[2] = cne[3];
|
| 192 |
+
cne[3] = 1;
|
| 193 |
+
};
|
| 194 |
+
|
| 195 |
+
auto collapse_nb = [](size_t cnb[], const int64_t cne[]) {
|
| 196 |
+
cnb[1] *= cne[1];
|
| 197 |
+
cnb[2] *= cne[2];
|
| 198 |
+
cnb[3] *= cne[3];
|
| 199 |
+
};
|
| 200 |
+
|
| 201 |
+
if (ggml_is_contiguous(src0) && ggml_is_contiguous(src1) && !ggml_is_permuted(src0) && !ggml_is_permuted(src1)) {
|
| 202 |
+
for (int i = 0; i < 4; i++) {
|
| 203 |
+
if (nr[i] != 1) {
|
| 204 |
+
break;
|
| 205 |
+
}
|
| 206 |
+
if (i > 0) {
|
| 207 |
+
collapse_nb(cnb, cne);
|
| 208 |
+
collapse_nb(cnb0, cne0);
|
| 209 |
+
collapse_nb(cnb1, cne1);
|
| 210 |
+
collapse(cne);
|
| 211 |
+
collapse(cne0);
|
| 212 |
+
collapse(cne1);
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
{
|
| 218 |
+
int64_t ne0 = cne[0];
|
| 219 |
+
int64_t ne1 = cne[1];
|
| 220 |
+
int64_t ne2 = cne[2];
|
| 221 |
+
int64_t ne3 = cne[3];
|
| 222 |
+
|
| 223 |
+
//int64_t ne00 = cne0[0]; GGML_UNUSED(ne00);
|
| 224 |
+
//int64_t ne01 = cne0[1]; GGML_UNUSED(ne01);
|
| 225 |
+
//int64_t ne02 = cne0[2]; GGML_UNUSED(ne02);
|
| 226 |
+
//int64_t ne03 = cne0[3]; GGML_UNUSED(ne03);
|
| 227 |
+
|
| 228 |
+
size_t nb0 = cnb[0];
|
| 229 |
+
size_t nb1 = cnb[1];
|
| 230 |
+
size_t nb2 = cnb[2];
|
| 231 |
+
size_t nb3 = cnb[3];
|
| 232 |
+
|
| 233 |
+
size_t nb00 = cnb0[0];
|
| 234 |
+
size_t nb01 = cnb0[1];
|
| 235 |
+
size_t nb02 = cnb0[2];
|
| 236 |
+
size_t nb03 = cnb0[3];
|
| 237 |
+
|
| 238 |
+
size_t nb10 = cnb1[0];
|
| 239 |
+
size_t nb11 = cnb1[1];
|
| 240 |
+
size_t nb12 = cnb1[2];
|
| 241 |
+
size_t nb13 = cnb1[3];
|
| 242 |
+
|
| 243 |
+
//size_t s0 = nb0 / sizeof(dst_t);
|
| 244 |
+
size_t s1 = nb1 / sizeof(dst_t);
|
| 245 |
+
size_t s2 = nb2 / sizeof(dst_t);
|
| 246 |
+
size_t s3 = nb3 / sizeof(dst_t);
|
| 247 |
+
|
| 248 |
+
size_t s10 = nb10 / sizeof(src1_t);
|
| 249 |
+
size_t s11 = nb11 / sizeof(src1_t);
|
| 250 |
+
size_t s12 = nb12 / sizeof(src1_t);
|
| 251 |
+
size_t s13 = nb13 / sizeof(src1_t);
|
| 252 |
+
|
| 253 |
+
size_t s00 = nb00 / sizeof(src0_t);
|
| 254 |
+
size_t s01 = nb01 / sizeof(src0_t);
|
| 255 |
+
size_t s02 = nb02 / sizeof(src0_t);
|
| 256 |
+
size_t s03 = nb03 / sizeof(src0_t);
|
| 257 |
+
|
| 258 |
+
GGML_ASSERT(ne0 <= std::numeric_limits<uint32_t>::max());
|
| 259 |
+
GGML_ASSERT(ne1 <= std::numeric_limits<uint32_t>::max());
|
| 260 |
+
GGML_ASSERT(ne2 <= std::numeric_limits<uint32_t>::max());
|
| 261 |
+
GGML_ASSERT(ne3 <= std::numeric_limits<uint32_t>::max());
|
| 262 |
+
|
| 263 |
+
//GGML_ASSERT(s0 <= std::numeric_limits<uint32_t>::max());
|
| 264 |
+
GGML_ASSERT(s1 <= std::numeric_limits<uint32_t>::max());
|
| 265 |
+
GGML_ASSERT(s2 <= std::numeric_limits<uint32_t>::max());
|
| 266 |
+
GGML_ASSERT(s3 <= std::numeric_limits<uint32_t>::max());
|
| 267 |
+
|
| 268 |
+
GGML_ASSERT(s00 <= std::numeric_limits<uint32_t>::max());
|
| 269 |
+
GGML_ASSERT(s01 <= std::numeric_limits<uint32_t>::max());
|
| 270 |
+
GGML_ASSERT(s02 <= std::numeric_limits<uint32_t>::max());
|
| 271 |
+
GGML_ASSERT(s03 <= std::numeric_limits<uint32_t>::max());
|
| 272 |
+
|
| 273 |
+
GGML_ASSERT(s10 <= std::numeric_limits<uint32_t>::max());
|
| 274 |
+
GGML_ASSERT(s11 <= std::numeric_limits<uint32_t>::max());
|
| 275 |
+
GGML_ASSERT(s12 <= std::numeric_limits<uint32_t>::max());
|
| 276 |
+
GGML_ASSERT(s13 <= std::numeric_limits<uint32_t>::max());
|
| 277 |
+
|
| 278 |
+
GGML_ASSERT(cne1[0] <= std::numeric_limits<uint32_t>::max());
|
| 279 |
+
GGML_ASSERT(cne1[1] <= std::numeric_limits<uint32_t>::max());
|
| 280 |
+
GGML_ASSERT(cne1[2] <= std::numeric_limits<uint32_t>::max());
|
| 281 |
+
GGML_ASSERT(cne1[3] <= std::numeric_limits<uint32_t>::max());
|
| 282 |
+
|
| 283 |
+
GGML_ASSERT(nb0 % sizeof(dst_t) == 0);
|
| 284 |
+
GGML_ASSERT(nb1 % sizeof(dst_t) == 0);
|
| 285 |
+
GGML_ASSERT(nb2 % sizeof(dst_t) == 0);
|
| 286 |
+
GGML_ASSERT(nb3 % sizeof(dst_t) == 0);
|
| 287 |
+
|
| 288 |
+
GGML_ASSERT(nb00 % sizeof(src0_t) == 0);
|
| 289 |
+
GGML_ASSERT(nb01 % sizeof(src0_t) == 0);
|
| 290 |
+
GGML_ASSERT(nb02 % sizeof(src0_t) == 0);
|
| 291 |
+
GGML_ASSERT(nb03 % sizeof(src0_t) == 0);
|
| 292 |
+
|
| 293 |
+
GGML_ASSERT(nb10 % sizeof(src1_t) == 0);
|
| 294 |
+
GGML_ASSERT(nb11 % sizeof(src1_t) == 0);
|
| 295 |
+
GGML_ASSERT(nb12 % sizeof(src1_t) == 0);
|
| 296 |
+
GGML_ASSERT(nb13 % sizeof(src1_t) == 0);
|
| 297 |
+
|
| 298 |
+
GGML_ASSERT(ne2 * ne3 <= std::numeric_limits<unsigned int>::max());
|
| 299 |
+
|
| 300 |
+
const int block_size = 128;
|
| 301 |
+
|
| 302 |
+
int64_t hne0 = std::max(ne0 / 2LL, 1LL);
|
| 303 |
+
|
| 304 |
+
dim3 block_dims;
|
| 305 |
+
block_dims.x = std::min<unsigned int>(hne0, block_size);
|
| 306 |
+
block_dims.y = std::min<unsigned int>(ne1, block_size / block_dims.x);
|
| 307 |
+
block_dims.z = std::min(std::min<unsigned int>(ne2 * ne3, block_size / block_dims.x / block_dims.y), 64U);
|
| 308 |
+
|
| 309 |
+
dim3 block_nums((hne0 + block_dims.x - 1) / block_dims.x, (ne1 + block_dims.y - 1) / block_dims.y,
|
| 310 |
+
(ne2 * ne3 + block_dims.z - 1) / block_dims.z);
|
| 311 |
+
|
| 312 |
+
const uint3 ne10 = init_fastdiv_values((uint32_t) cne1[0]);
|
| 313 |
+
const uint3 ne11 = init_fastdiv_values((uint32_t) cne1[1]);
|
| 314 |
+
const uint3 ne12 = init_fastdiv_values((uint32_t) cne1[2]);
|
| 315 |
+
const uint3 ne13 = init_fastdiv_values((uint32_t) cne1[3]);
|
| 316 |
+
|
| 317 |
+
if (block_nums.z > 65535 || block_nums.y > 65535) {
|
| 318 |
+
int64_t block_num = (ne0 * ne1 * ne2 * ne3 + block_size - 1) / block_size;
|
| 319 |
+
|
| 320 |
+
GGML_ASSERT(block_num <= std::numeric_limits<uint32_t>::max());
|
| 321 |
+
GGML_ASSERT(block_num * block_size <= std::numeric_limits<uint32_t>::max());
|
| 322 |
+
GGML_ASSERT(ne0 * ne1 <= std::numeric_limits<uint32_t>::max());
|
| 323 |
+
GGML_ASSERT(ne0 * ne1 * ne2 <= std::numeric_limits<uint32_t>::max());
|
| 324 |
+
|
| 325 |
+
const uint3 prod_012 = init_fastdiv_values((uint32_t) (ne0 * ne1 * ne2));
|
| 326 |
+
const uint3 prod_01 = init_fastdiv_values((uint32_t) (ne0 * ne1));
|
| 327 |
+
const uint3 ne0_fastdiv = init_fastdiv_values((uint32_t) ne0);
|
| 328 |
+
const uint3 ne1_fastdiv = init_fastdiv_values((uint32_t) ne1);
|
| 329 |
+
const uint3 ne2_fastdiv = init_fastdiv_values((uint32_t) ne2);
|
| 330 |
+
|
| 331 |
+
{
|
| 332 |
+
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params((dim3)block_num, block_size, 0, stream);
|
| 333 |
+
ggml_cuda_kernel_launch(k_bin_bcast_unravel<bin_op, src0_t, src1_t, dst_t, type_for_index<const src1_t *, I>...>, launch_params,
|
| 334 |
+
src0_dd, src1_dd, dst_dd, ne0_fastdiv, ne1_fastdiv, ne2_fastdiv, ne3, prod_012, prod_01, ne10, ne11,
|
| 335 |
+
ne12, ne13,
|
| 336 |
+
/*s0,*/ s1, s2, s3,
|
| 337 |
+
s00, s01, s02, s03,
|
| 338 |
+
s10, s11, s12, s13, (const src1_t *) dst->src[I + 1]->data...);
|
| 339 |
+
}
|
| 340 |
+
} else {
|
| 341 |
+
GGML_ASSERT(int64_t(block_nums.x) * block_dims.x <= std::numeric_limits<uint32_t>::max());
|
| 342 |
+
GGML_ASSERT(int64_t(block_nums.y) * block_dims.y <= std::numeric_limits<uint32_t>::max());
|
| 343 |
+
GGML_ASSERT(int64_t(block_nums.z) * block_dims.z <= std::numeric_limits<uint32_t>::max());
|
| 344 |
+
|
| 345 |
+
const uint3 ne3_fastdiv = init_fastdiv_values((uint32_t) ne3);
|
| 346 |
+
{
|
| 347 |
+
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(block_nums, block_dims, 0, stream);
|
| 348 |
+
ggml_cuda_kernel_launch(k_bin_bcast<bin_op, src0_t, src1_t, dst_t, type_for_index<const src1_t *, I>...>, launch_params,
|
| 349 |
+
src0_dd, src1_dd, dst_dd, ne0, ne1, ne2, ne3_fastdiv, ne10, ne11, ne12, ne13,
|
| 350 |
+
/*s0,*/ s1, s2, s3,
|
| 351 |
+
s00, s01, s02, s03,
|
| 352 |
+
s10, s11, s12, s13, (const src1_t *) dst->src[I + 1]->data...);
|
| 353 |
+
}
|
| 354 |
+
}
|
| 355 |
+
}
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
template <typename T>
|
| 359 |
+
static __global__ void k_repeat_back(
|
| 360 |
+
const T * __restrict__ src, T * __restrict__ dst, const int64_t ne00, const int64_t ne01, const int64_t ne02, const int64_t ne03,
|
| 361 |
+
const size_t s00, const size_t s01, const size_t s02, const size_t s03,
|
| 362 |
+
const int64_t ne0, const int64_t ne1, const int64_t ne2, const int64_t ne3) {
|
| 363 |
+
|
| 364 |
+
const int64_t tid0 = int64_t(blockIdx.x)*blockDim.x + threadIdx.x;
|
| 365 |
+
const int64_t tid1 = int64_t(blockIdx.y)*blockDim.y + threadIdx.y;
|
| 366 |
+
const int64_t tid23 = int64_t(blockIdx.z)*blockDim.z + threadIdx.z;
|
| 367 |
+
const int64_t tid2 = tid23 % ne2;
|
| 368 |
+
const int64_t tid3 = tid23 / ne2;
|
| 369 |
+
|
| 370 |
+
if (tid0 >= ne0) {
|
| 371 |
+
return;
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
T sum = 0;
|
| 375 |
+
ggml_cuda_pdl_sync();
|
| 376 |
+
for (int64_t i3 = tid3; i3 < ne03; i3 += ne3) {
|
| 377 |
+
for (int64_t i2 = tid2; i2 < ne02; i2 += ne2) {
|
| 378 |
+
for (int64_t i1 = tid1; i1 < ne01; i1 += ne1) {
|
| 379 |
+
for (int64_t i0 = tid0; i0 < ne00; i0 += ne0) {
|
| 380 |
+
sum += src[i3*s03 + i2*s02 + i1*s01 + i0*s00];
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
+
dst[tid3*ne2*ne1*ne0 + tid2*ne1*ne0 + tid1*ne0 + tid0] = sum;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
template <float (*bin_op)(const float, const float), int n_fuse = 1>
|
| 389 |
+
struct bin_bcast_cuda {
|
| 390 |
+
template<typename src0_t, typename src1_t, typename dst_t>
|
| 391 |
+
void operator()(const struct ggml_tensor * src0, const struct ggml_tensor * src1, struct ggml_tensor * dst,
|
| 392 |
+
const src0_t * src0_dd, const src1_t * src1_dd, dst_t * dst_dd,
|
| 393 |
+
cudaStream_t stream) {
|
| 394 |
+
launch_bin_bcast_pack<bin_op, src0_t, src1_t, dst_t>(
|
| 395 |
+
src0, src1, dst, src0_dd, src1_dd, dst_dd, stream, std::make_index_sequence<n_fuse>{});
|
| 396 |
+
}
|
| 397 |
+
};
|
| 398 |
+
|
| 399 |
+
template <typename T>
|
| 400 |
+
static void repeat_back_cuda(
|
| 401 |
+
const T * src, T * dst, const int64_t ne00, const int64_t ne01, const int64_t ne02, const int64_t ne03,
|
| 402 |
+
const size_t s00, const size_t s01, const size_t s02, const size_t s03,
|
| 403 |
+
const int64_t ne0, const int64_t ne1, const int64_t ne2, const int64_t ne3, cudaStream_t stream) {
|
| 404 |
+
|
| 405 |
+
const dim3 block_dims(WARP_SIZE, 1, 1);
|
| 406 |
+
const dim3 block_nums((ne0 + WARP_SIZE - 1) / WARP_SIZE, ne1, ne2*ne3);
|
| 407 |
+
k_repeat_back<T><<<block_nums, block_dims, 0, stream>>>
|
| 408 |
+
(src, dst, ne00, ne01, ne02, ne03, s00, s01, s02, s03, ne0, ne1, ne2, ne3);
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
template<class op>
|
| 412 |
+
static void ggml_cuda_op_bin_bcast(
|
| 413 |
+
const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst,
|
| 414 |
+
const void * src0_dd, const void * src1_dd, void * dst_dd, cudaStream_t stream) {
|
| 415 |
+
|
| 416 |
+
GGML_ASSERT(src1->type == GGML_TYPE_F32 || src1->type == GGML_TYPE_F16);
|
| 417 |
+
|
| 418 |
+
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
| 419 |
+
op()(src0, src1, dst, (const float *)src0_dd, (const float *)src1_dd, (float *)dst_dd, stream);
|
| 420 |
+
} else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) {
|
| 421 |
+
op()(src0, src1, dst, (const half *) src0_dd, (const half *)src1_dd, (half *) dst_dd, stream);
|
| 422 |
+
} else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) {
|
| 423 |
+
op()(src0, src1, dst, (const half *) src0_dd, (const float *)src1_dd, (half *) dst_dd, stream);
|
| 424 |
+
} else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F32) {
|
| 425 |
+
op()(src0, src1, dst, (const half *) src0_dd, (const float *)src1_dd, (float *)dst_dd, stream);
|
| 426 |
+
} else {
|
| 427 |
+
fprintf(stderr, "%s: unsupported types: dst: %s, src0: %s, src1: %s\n", __func__,
|
| 428 |
+
ggml_type_name(dst->type), ggml_type_name(src0->type), ggml_type_name(src1->type));
|
| 429 |
+
GGML_ABORT("fatal error");
|
| 430 |
+
}
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
void ggml_cuda_op_repeat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 434 |
+
ggml_cuda_op_bin_bcast<bin_bcast_cuda<op_repeat, 0>>(dst, dst->src[0], dst, nullptr, dst->src[0]->data, dst->data, ctx.stream());
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
void ggml_cuda_op_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 438 |
+
ggml_cuda_op_bin_bcast<bin_bcast_cuda<op_add>>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream());
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
void ggml_cuda_op_sub(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 442 |
+
ggml_cuda_op_bin_bcast<bin_bcast_cuda<op_sub>>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream());
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
void ggml_cuda_op_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 446 |
+
ggml_cuda_op_bin_bcast<bin_bcast_cuda<op_mul>>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream());
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
void ggml_cuda_op_div(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 450 |
+
ggml_cuda_op_bin_bcast<bin_bcast_cuda<op_div>>(dst->src[0], dst->src[1], dst, dst->src[0]->data, dst->src[1]->data, dst->data, ctx.stream());
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
template <float (*op)(const float, const float), int n_fuse>
|
| 454 |
+
static void ggml_cuda_op_fused_binbcast_impl(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 455 |
+
cudaStream_t stream = ctx.stream();
|
| 456 |
+
|
| 457 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 458 |
+
const ggml_tensor * src1 = dst->src[1];
|
| 459 |
+
|
| 460 |
+
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
| 461 |
+
launch_bin_bcast_pack<op, float, float, float>(src0, src1, dst,
|
| 462 |
+
(const float *) src0->data, (const float *) src1->data, (float *) dst->data,
|
| 463 |
+
stream, std::make_index_sequence<n_fuse>{});
|
| 464 |
+
} else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) {
|
| 465 |
+
launch_bin_bcast_pack<op, half, half, half>(src0, src1, dst,
|
| 466 |
+
(const half *) src0->data, (const half *) src1->data, (half *) dst->data,
|
| 467 |
+
stream, std::make_index_sequence<n_fuse>{});
|
| 468 |
+
} else if (src0->type == GGML_TYPE_F16 && src1->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) {
|
| 469 |
+
launch_bin_bcast_pack<op, half, float, half>(src0, src1, dst,
|
| 470 |
+
(const half *) src0->data, (const float *) src1->data, (half *) dst->data,
|
| 471 |
+
stream, std::make_index_sequence<n_fuse>{});
|
| 472 |
+
} else if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F32) {
|
| 473 |
+
launch_bin_bcast_pack<op, half, float, float>(src0, src1, dst,
|
| 474 |
+
(const half *) src0->data, (const float *) src1->data, (float *) dst->data,
|
| 475 |
+
stream, std::make_index_sequence<n_fuse>{});
|
| 476 |
+
} else {
|
| 477 |
+
fprintf(stderr,
|
| 478 |
+
"%s: unsupported types for fusion: dst: %s, src0: %s, src1: %s\n",
|
| 479 |
+
__func__, ggml_type_name(dst->type), ggml_type_name(src0->type), ggml_type_name(src1->type));
|
| 480 |
+
GGML_ABORT("fatal error");
|
| 481 |
+
}
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
void ggml_cuda_op_fused_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst, int n_fuse) {
|
| 486 |
+
GGML_ASSERT(2 <= n_fuse && n_fuse <= 8);
|
| 487 |
+
|
| 488 |
+
switch (n_fuse) {
|
| 489 |
+
case 2:
|
| 490 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 2>(ctx, dst);
|
| 491 |
+
break;
|
| 492 |
+
case 3:
|
| 493 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 3>(ctx, dst);
|
| 494 |
+
break;
|
| 495 |
+
case 4:
|
| 496 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 4>(ctx, dst);
|
| 497 |
+
break;
|
| 498 |
+
case 5:
|
| 499 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 5>(ctx, dst);
|
| 500 |
+
break;
|
| 501 |
+
case 6:
|
| 502 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 6>(ctx, dst);
|
| 503 |
+
break;
|
| 504 |
+
case 7:
|
| 505 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 7>(ctx, dst);
|
| 506 |
+
break;
|
| 507 |
+
case 8:
|
| 508 |
+
ggml_cuda_op_fused_binbcast_impl<op_add, 8>(ctx, dst);
|
| 509 |
+
break;
|
| 510 |
+
default:
|
| 511 |
+
GGML_ASSERT(false && "Unsupported n_fuse value");
|
| 512 |
+
}
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
void ggml_cuda_op_fused_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst, int n_fuse) {
|
| 516 |
+
GGML_ASSERT(2 <= n_fuse && n_fuse <= 8);
|
| 517 |
+
|
| 518 |
+
switch (n_fuse) {
|
| 519 |
+
case 2:
|
| 520 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 2>(ctx, dst);
|
| 521 |
+
break;
|
| 522 |
+
case 3:
|
| 523 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 3>(ctx, dst);
|
| 524 |
+
break;
|
| 525 |
+
case 4:
|
| 526 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 4>(ctx, dst);
|
| 527 |
+
break;
|
| 528 |
+
case 5:
|
| 529 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 5>(ctx, dst);
|
| 530 |
+
break;
|
| 531 |
+
case 6:
|
| 532 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 6>(ctx, dst);
|
| 533 |
+
break;
|
| 534 |
+
case 7:
|
| 535 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 7>(ctx, dst);
|
| 536 |
+
break;
|
| 537 |
+
case 8:
|
| 538 |
+
ggml_cuda_op_fused_binbcast_impl<op_mul, 8>(ctx, dst);
|
| 539 |
+
break;
|
| 540 |
+
default:
|
| 541 |
+
GGML_ASSERT(false && "Unsupported n_fuse value");
|
| 542 |
+
}
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
+
void ggml_cuda_op_repeat_back(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 546 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 547 |
+
|
| 548 |
+
GGML_ASSERT(src0->type == dst->type);
|
| 549 |
+
GGML_ASSERT(ggml_is_contiguous(dst));
|
| 550 |
+
GGML_ASSERT(ggml_can_repeat(dst, src0));
|
| 551 |
+
|
| 552 |
+
cudaStream_t stream = ctx.stream();
|
| 553 |
+
|
| 554 |
+
GGML_TENSOR_UNARY_OP_LOCALS;
|
| 555 |
+
|
| 556 |
+
GGML_ASSERT(ne2*ne3 <= (1 << 15));
|
| 557 |
+
|
| 558 |
+
const size_t ts = ggml_type_size(src0->type);
|
| 559 |
+
const size_t s00 = nb00 / ts;
|
| 560 |
+
const size_t s01 = nb01 / ts;
|
| 561 |
+
const size_t s02 = nb02 / ts;
|
| 562 |
+
const size_t s03 = nb03 / ts;
|
| 563 |
+
|
| 564 |
+
switch (dst->type) {
|
| 565 |
+
case GGML_TYPE_F32: {
|
| 566 |
+
const float * src0_d = (const float *) src0->data;
|
| 567 |
+
float * dst_d = (float *) dst->data;
|
| 568 |
+
repeat_back_cuda(src0_d, dst_d, ne00, ne01, ne02, ne03, s00, s01, s02, s03, ne0, ne1, ne2, ne3, stream);
|
| 569 |
+
} break;
|
| 570 |
+
default: {
|
| 571 |
+
GGML_ASSERT(false);
|
| 572 |
+
} break;
|
| 573 |
+
}
|
| 574 |
+
}
|
vendor/src/ggml-cuda/binbcast.cuh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
void ggml_cuda_op_repeat(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 4 |
+
void ggml_cuda_op_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 5 |
+
void ggml_cuda_op_sub(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 6 |
+
void ggml_cuda_op_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 7 |
+
void ggml_cuda_op_div(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 8 |
+
|
| 9 |
+
void ggml_cuda_op_repeat_back(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
| 10 |
+
|
| 11 |
+
void ggml_cuda_op_fused_add(ggml_backend_cuda_context & ctx, ggml_tensor * dst, int n_fuse);
|
| 12 |
+
void ggml_cuda_op_fused_mul(ggml_backend_cuda_context & ctx, ggml_tensor * dst, int n_fuse);
|
vendor/src/ggml-cuda/clamp.cu
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "clamp.cuh"
|
| 2 |
+
|
| 3 |
+
static __device__ __forceinline__ float op_clamp(float x, float min, float max) {
|
| 4 |
+
return fminf(fmaxf(x, min), max);
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
template <class T>
|
| 8 |
+
static __global__ void op_clamp_kernel(const T * x, T * dst, const T min, const T max, const int k) {
|
| 9 |
+
const int i = blockDim.x*blockIdx.x + threadIdx.x;
|
| 10 |
+
|
| 11 |
+
if (i >= k) {
|
| 12 |
+
return;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
dst[i] = (T)op_clamp((float)x[i], (float)min, (float)max);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
template <class T>
|
| 19 |
+
static void clamp_cuda(const T * x, T * dst, const T min, const T max, const int k, cudaStream_t stream) {
|
| 20 |
+
const int num_blocks = (k + CUDA_CLAMP_BLOCK_SIZE - 1) / CUDA_CLAMP_BLOCK_SIZE;
|
| 21 |
+
op_clamp_kernel<<<num_blocks, CUDA_CLAMP_BLOCK_SIZE, 0, stream>>>(x, dst, min, max, k);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
void ggml_cuda_op_clamp(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 26 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 27 |
+
const void * src0_d = src0->data;
|
| 28 |
+
void * dst_d = dst->data;
|
| 29 |
+
cudaStream_t stream = ctx.stream();
|
| 30 |
+
|
| 31 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16);
|
| 32 |
+
GGML_ASSERT( dst->type == GGML_TYPE_F32 || dst->type == GGML_TYPE_F16);
|
| 33 |
+
GGML_ASSERT(src0->type == dst->type);
|
| 34 |
+
|
| 35 |
+
float min;
|
| 36 |
+
float max;
|
| 37 |
+
memcpy(&min, dst->op_params, sizeof(float));
|
| 38 |
+
memcpy(&max, (float *) dst->op_params + 1, sizeof(float));
|
| 39 |
+
|
| 40 |
+
if (src0->type == GGML_TYPE_F16) {
|
| 41 |
+
clamp_cuda((const half *)src0_d, (half *)dst_d, (half)min, (half)max, ggml_nelements(src0), stream);
|
| 42 |
+
} else {
|
| 43 |
+
clamp_cuda((const float *)src0_d, (float *)dst_d, (float)min, (float)max, ggml_nelements(src0), stream);
|
| 44 |
+
}
|
| 45 |
+
}
|
vendor/src/ggml-cuda/clamp.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_CLAMP_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_op_clamp(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/col2im-1d.cu
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "col2im-1d.cuh"
|
| 2 |
+
#include "convert.cuh"
|
| 3 |
+
|
| 4 |
+
// col2im_1d: scatter-add GEMM columns to 1D signal (gather approach)
|
| 5 |
+
// columns: [K*OC, T_in] -> output: [T_out, OC]
|
| 6 |
+
// Supports F32, F16, BF16 data with F32 accumulator.
|
| 7 |
+
|
| 8 |
+
template <typename T>
|
| 9 |
+
static __global__ void col2im_1d_kernel(
|
| 10 |
+
const T * __restrict__ col,
|
| 11 |
+
T * __restrict__ dst,
|
| 12 |
+
const int T_in, const uint3 T_out_fd,
|
| 13 |
+
const int OC, const int K, const int K_OC,
|
| 14 |
+
const int s0, const int p0, const int total) {
|
| 15 |
+
|
| 16 |
+
const int idx = threadIdx.x + blockIdx.x * blockDim.x;
|
| 17 |
+
if (idx >= total) return;
|
| 18 |
+
|
| 19 |
+
// dst layout: [T_out, OC], ne[0]=T_out fastest
|
| 20 |
+
const uint2 qr = fast_div_modulo((uint32_t)idx, T_out_fd); // qr.x = idx / T_out, qr.y = idx % T_out
|
| 21 |
+
const int oc = (int)qr.x;
|
| 22 |
+
const int t_out = (int)qr.y;
|
| 23 |
+
const int t_abs = t_out + p0; // absolute position in uncropped signal
|
| 24 |
+
|
| 25 |
+
// Gather: find all (t_in, k) where t_in*s + k == t_abs, 0 <= k < K
|
| 26 |
+
int t_in_min = (t_abs - K + s0) / s0; // ceil((t_abs - K + 1) / s)
|
| 27 |
+
if (t_in_min < 0) t_in_min = 0;
|
| 28 |
+
int t_in_max = t_abs / s0;
|
| 29 |
+
if (t_in_max >= T_in) t_in_max = T_in - 1;
|
| 30 |
+
|
| 31 |
+
float sum = 0.0f;
|
| 32 |
+
for (int t_in = t_in_min; t_in <= t_in_max; t_in++) {
|
| 33 |
+
const int k = t_abs - t_in * s0;
|
| 34 |
+
// col layout: [K*OC, T_in], column index = oc * K + k
|
| 35 |
+
sum += ggml_cuda_cast<float>(col[(oc * K + k) + t_in * K_OC]);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
dst[idx] = ggml_cuda_cast<T>(sum);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
void ggml_cuda_op_col2im_1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 42 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 43 |
+
cudaStream_t stream = ctx.stream();
|
| 44 |
+
|
| 45 |
+
GGML_ASSERT(ggml_is_contiguous(src0));
|
| 46 |
+
|
| 47 |
+
const int32_t s0 = ((const int32_t *)(dst->op_params))[0];
|
| 48 |
+
const int32_t OC = ((const int32_t *)(dst->op_params))[1];
|
| 49 |
+
const int32_t p0 = ((const int32_t *)(dst->op_params))[2];
|
| 50 |
+
|
| 51 |
+
const int K_OC = (int) src0->ne[0];
|
| 52 |
+
const int T_in = (int) src0->ne[1];
|
| 53 |
+
const int K = K_OC / OC;
|
| 54 |
+
const int T_out = (int) dst->ne[0];
|
| 55 |
+
|
| 56 |
+
const uint3 T_out_fd = init_fastdiv_values((uint32_t)T_out);
|
| 57 |
+
|
| 58 |
+
const int total = T_out * OC;
|
| 59 |
+
const int block_size = 256;
|
| 60 |
+
const int num_blocks = (total + block_size - 1) / block_size;
|
| 61 |
+
|
| 62 |
+
switch (src0->type) {
|
| 63 |
+
case GGML_TYPE_F32: {
|
| 64 |
+
col2im_1d_kernel<<<num_blocks, block_size, 0, stream>>>(
|
| 65 |
+
(const float *)src0->data, (float *)dst->data,
|
| 66 |
+
T_in, T_out_fd, OC, K, K_OC, s0, p0, total);
|
| 67 |
+
} break;
|
| 68 |
+
case GGML_TYPE_F16: {
|
| 69 |
+
col2im_1d_kernel<<<num_blocks, block_size, 0, stream>>>(
|
| 70 |
+
(const half *)src0->data, (half *)dst->data,
|
| 71 |
+
T_in, T_out_fd, OC, K, K_OC, s0, p0, total);
|
| 72 |
+
} break;
|
| 73 |
+
case GGML_TYPE_BF16: {
|
| 74 |
+
col2im_1d_kernel<<<num_blocks, block_size, 0, stream>>>(
|
| 75 |
+
(const nv_bfloat16 *)src0->data, (nv_bfloat16 *)dst->data,
|
| 76 |
+
T_in, T_out_fd, OC, K, K_OC, s0, p0, total);
|
| 77 |
+
} break;
|
| 78 |
+
default:
|
| 79 |
+
GGML_ABORT("col2im_1d: unsupported type");
|
| 80 |
+
}
|
| 81 |
+
}
|
vendor/src/ggml-cuda/col2im-1d.cuh
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
void ggml_cuda_op_col2im_1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/concat.cu
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "concat.cuh"
|
| 2 |
+
|
| 3 |
+
#include <stdint.h>
|
| 4 |
+
|
| 5 |
+
// contiguous kernels
|
| 6 |
+
template <typename T, int dim>
|
| 7 |
+
static __global__ void __launch_bounds__(CUDA_CONCAT_BLOCK_SIZE) concat_cont(const T * x,
|
| 8 |
+
const T * y,
|
| 9 |
+
T * dst,
|
| 10 |
+
int64_t ne00,
|
| 11 |
+
int64_t ne01,
|
| 12 |
+
int64_t ne02,
|
| 13 |
+
int64_t ne0,
|
| 14 |
+
int64_t ne1,
|
| 15 |
+
int64_t ne2) {
|
| 16 |
+
static_assert(dim >= 0 && dim <= 2, "dim must be in [0, 2]");
|
| 17 |
+
|
| 18 |
+
const int64_t n = ne0 * ne1 * ne2;
|
| 19 |
+
|
| 20 |
+
ggml_cuda_pdl_sync();
|
| 21 |
+
for (int64_t i = (int64_t) blockIdx.x * blockDim.x + threadIdx.x; i < n; i += (int64_t) blockDim.x * gridDim.x) {
|
| 22 |
+
if constexpr (dim == 0) {
|
| 23 |
+
const int64_t row = i / ne0;
|
| 24 |
+
const int64_t i0 = i - row * ne0;
|
| 25 |
+
|
| 26 |
+
if (i0 < ne00) {
|
| 27 |
+
dst[i] = x[row * ne00 + i0];
|
| 28 |
+
} else {
|
| 29 |
+
dst[i] = y[row * (ne0 - ne00) + (i0 - ne00)];
|
| 30 |
+
}
|
| 31 |
+
} else if constexpr (dim == 1) {
|
| 32 |
+
const int64_t dst_plane = ne0 * ne1;
|
| 33 |
+
const int64_t src0_plane = ne0 * ne01;
|
| 34 |
+
const int64_t src1_plane = dst_plane - src0_plane;
|
| 35 |
+
const int64_t i2 = i / dst_plane;
|
| 36 |
+
const int64_t i01 = i - i2 * dst_plane;
|
| 37 |
+
|
| 38 |
+
if (i01 < src0_plane) {
|
| 39 |
+
dst[i] = x[i2 * src0_plane + i01];
|
| 40 |
+
} else {
|
| 41 |
+
dst[i] = y[i2 * src1_plane + (i01 - src0_plane)];
|
| 42 |
+
}
|
| 43 |
+
} else {
|
| 44 |
+
const int64_t src0_size = ne0 * ne1 * ne02;
|
| 45 |
+
|
| 46 |
+
if (i < src0_size) {
|
| 47 |
+
dst[i] = x[i];
|
| 48 |
+
} else {
|
| 49 |
+
dst[i] = y[i - src0_size];
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
template <typename T>
|
| 56 |
+
static void concat_cont_cuda(const T * x,
|
| 57 |
+
const T * y,
|
| 58 |
+
T * dst,
|
| 59 |
+
int64_t ne00,
|
| 60 |
+
int64_t ne01,
|
| 61 |
+
int64_t ne02,
|
| 62 |
+
int64_t ne0,
|
| 63 |
+
int64_t ne1,
|
| 64 |
+
int64_t ne2,
|
| 65 |
+
int dim,
|
| 66 |
+
cudaStream_t stream) {
|
| 67 |
+
const int64_t n = ne0 * ne1 * ne2;
|
| 68 |
+
const int num_blocks = (n + CUDA_CONCAT_BLOCK_SIZE - 1) / CUDA_CONCAT_BLOCK_SIZE;
|
| 69 |
+
|
| 70 |
+
if (dim == 0) {
|
| 71 |
+
const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params(num_blocks, CUDA_CONCAT_BLOCK_SIZE, 0, stream);
|
| 72 |
+
ggml_cuda_kernel_launch(concat_cont<T, 0>, launch_params, x, y, dst, ne00, ne01, ne02, ne0, ne1, ne2);
|
| 73 |
+
return;
|
| 74 |
+
}
|
| 75 |
+
if (dim == 1) {
|
| 76 |
+
concat_cont<T, 1><<<num_blocks, CUDA_CONCAT_BLOCK_SIZE, 0, stream>>>(x, y, dst, ne00, ne01, ne02, ne0, ne1, ne2);
|
| 77 |
+
return;
|
| 78 |
+
}
|
| 79 |
+
concat_cont<T, 2><<<num_blocks, CUDA_CONCAT_BLOCK_SIZE, 0, stream>>>(x, y, dst, ne00, ne01, ne02, ne0, ne1, ne2);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
// non-contiguous kernel (slow)
|
| 83 |
+
template <typename T, int dim>
|
| 84 |
+
static __global__ void __launch_bounds__(CUDA_CONCAT_BLOCK_SIZE)
|
| 85 |
+
concat_non_cont(
|
| 86 |
+
const char * src0,
|
| 87 |
+
const char * src1,
|
| 88 |
+
char * dst,
|
| 89 |
+
int64_t ne00,
|
| 90 |
+
int64_t ne01,
|
| 91 |
+
int64_t ne02,
|
| 92 |
+
int64_t ne03,
|
| 93 |
+
uint64_t nb00,
|
| 94 |
+
uint64_t nb01,
|
| 95 |
+
uint64_t nb02,
|
| 96 |
+
uint64_t nb03,
|
| 97 |
+
int64_t /*ne10*/,
|
| 98 |
+
int64_t /*ne11*/,
|
| 99 |
+
int64_t /*ne12*/,
|
| 100 |
+
int64_t /*ne13*/,
|
| 101 |
+
uint64_t nb10,
|
| 102 |
+
uint64_t nb11,
|
| 103 |
+
uint64_t nb12,
|
| 104 |
+
uint64_t nb13,
|
| 105 |
+
int64_t ne0,
|
| 106 |
+
int64_t /*ne1*/,
|
| 107 |
+
int64_t /*ne2*/,
|
| 108 |
+
int64_t /*ne3*/,
|
| 109 |
+
uint64_t nb0,
|
| 110 |
+
uint64_t nb1,
|
| 111 |
+
uint64_t nb2,
|
| 112 |
+
uint64_t nb3) {
|
| 113 |
+
static_assert(dim >= 0 && dim <= 3, "dim must be in [0, 3]");
|
| 114 |
+
|
| 115 |
+
const int64_t i3 = blockIdx.z;
|
| 116 |
+
const int64_t i2 = blockIdx.y;
|
| 117 |
+
const int64_t i1 = blockIdx.x;
|
| 118 |
+
|
| 119 |
+
const T * x;
|
| 120 |
+
|
| 121 |
+
for (int64_t i0 = threadIdx.x; i0 < ne0; i0 += blockDim.x) {
|
| 122 |
+
if (i0 < ne00 && i1 < ne01 && i2 < ne02 && i3 < ne03) {
|
| 123 |
+
x = (const T *)(src0 + i3*nb03 + i2*nb02 + i1*nb01 + i0*nb00);
|
| 124 |
+
} else {
|
| 125 |
+
if constexpr (dim == 0) {
|
| 126 |
+
x = (const T *)(src1 + i3*nb13 + i2*nb12 + i1*nb11 + (i0 - ne00)*nb10);
|
| 127 |
+
} else if constexpr (dim == 1) {
|
| 128 |
+
x = (const T *)(src1 + i3*nb13 + i2*nb12 + (i1 - ne01)*nb11 + i0*nb10);
|
| 129 |
+
} else if constexpr (dim == 2) {
|
| 130 |
+
x = (const T *)(src1 + i3*nb13 + (i2 - ne02)*nb12 + i1*nb11 + i0*nb10);
|
| 131 |
+
} else if constexpr (dim == 3) {
|
| 132 |
+
x = (const T *)(src1 + (i3 - ne03)*nb13 + i2*nb12 + i1*nb11 + i0*nb10);
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
T * y = (T *)(dst + i3*nb3 + i2*nb2 + i1*nb1 + i0*nb0);
|
| 137 |
+
|
| 138 |
+
*y = *x;
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
template <typename T>
|
| 143 |
+
static void concat_cuda(const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, int dim, cudaStream_t stream) {
|
| 144 |
+
if (dim != 3 && ggml_is_contiguous_to_3(src0) && ggml_is_contiguous_to_3(src1)) {
|
| 145 |
+
const T * src0_d = (const T *) src0->data;
|
| 146 |
+
const T * src1_d = (const T *) src1->data;
|
| 147 |
+
T * dst_d = (T *) dst->data;
|
| 148 |
+
|
| 149 |
+
for (int64_t i3 = 0; i3 < dst->ne[3]; i3++) {
|
| 150 |
+
concat_cont_cuda(
|
| 151 |
+
src0_d + i3*(src0->nb[3] / sizeof(T)),
|
| 152 |
+
src1_d + i3*(src1->nb[3] / sizeof(T)),
|
| 153 |
+
dst_d + i3*( dst->nb[3] / sizeof(T)),
|
| 154 |
+
ggml_row_size(src0->type, src0->ne[0])/sizeof(T), src0->ne[1], src0->ne[2],
|
| 155 |
+
ggml_row_size(dst->type, dst->ne[0])/sizeof(T), dst->ne[1], dst->ne[2], dim, stream);
|
| 156 |
+
}
|
| 157 |
+
} else if (dim == 3 && ggml_is_contiguous(src0) && ggml_is_contiguous(src1)) {
|
| 158 |
+
const size_t size0 = ggml_nbytes(src0);
|
| 159 |
+
const size_t size1 = ggml_nbytes(src1);
|
| 160 |
+
|
| 161 |
+
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data, src0->data, size0, cudaMemcpyDeviceToDevice, stream));
|
| 162 |
+
CUDA_CHECK(cudaMemcpyAsync((char *) dst->data + size0, src1->data, size1, cudaMemcpyDeviceToDevice, stream));
|
| 163 |
+
} else {
|
| 164 |
+
GGML_ASSERT(!ggml_is_quantized(src0->type));
|
| 165 |
+
|
| 166 |
+
dim3 grid_dim(dst->ne[1], dst->ne[2], dst->ne[3]);
|
| 167 |
+
auto launch_kernel = [&](auto dim) {
|
| 168 |
+
concat_non_cont<T, dim><<<grid_dim, CUDA_CONCAT_BLOCK_SIZE, 0, stream>>>(
|
| 169 |
+
(const char *) src0->data, (const char *) src1->data, (char *) dst->data,
|
| 170 |
+
src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
| 171 |
+
src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3],
|
| 172 |
+
src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3],
|
| 173 |
+
src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3],
|
| 174 |
+
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
| 175 |
+
dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3]);
|
| 176 |
+
};
|
| 177 |
+
switch (dim) {
|
| 178 |
+
case 0:
|
| 179 |
+
launch_kernel(std::integral_constant<int, 0>{});
|
| 180 |
+
break;
|
| 181 |
+
case 1:
|
| 182 |
+
launch_kernel(std::integral_constant<int, 1>{});
|
| 183 |
+
break;
|
| 184 |
+
case 2:
|
| 185 |
+
launch_kernel(std::integral_constant<int, 2>{});
|
| 186 |
+
break;
|
| 187 |
+
case 3:
|
| 188 |
+
launch_kernel(std::integral_constant<int, 3>{});
|
| 189 |
+
break;
|
| 190 |
+
default:
|
| 191 |
+
GGML_ABORT("Invalid dim: %d", dim);
|
| 192 |
+
break;
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 198 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 199 |
+
const ggml_tensor * src1 = dst->src[1];
|
| 200 |
+
|
| 201 |
+
cudaStream_t stream = ctx.stream();
|
| 202 |
+
|
| 203 |
+
const int32_t dim = ((int32_t *) dst->op_params)[0];
|
| 204 |
+
|
| 205 |
+
GGML_ASSERT(src0->type == src1->type);
|
| 206 |
+
GGML_ASSERT(dst->type == src0->type);
|
| 207 |
+
|
| 208 |
+
if (ggml_is_quantized(src0->type)) {
|
| 209 |
+
if (dim == 3) {
|
| 210 |
+
GGML_ASSERT(ggml_is_contiguous(src0));
|
| 211 |
+
GGML_ASSERT(ggml_is_contiguous(src1));
|
| 212 |
+
} else {
|
| 213 |
+
GGML_ASSERT(ggml_is_contiguous_to_3(src0));
|
| 214 |
+
GGML_ASSERT(ggml_is_contiguous_to_3(src1));
|
| 215 |
+
}
|
| 216 |
+
GGML_ASSERT(src0->ne[0] % ggml_blck_size(src0->type) == 0);
|
| 217 |
+
GGML_ASSERT(src1->ne[0] % ggml_blck_size(src1->type) == 0);
|
| 218 |
+
|
| 219 |
+
// if first 3 dimensions are contiguous and ne[0] is multiple of the block size we can concat both tensors as byte tensors
|
| 220 |
+
concat_cuda<uint8_t>(src0, src1, dst, dim, stream);
|
| 221 |
+
} else {
|
| 222 |
+
GGML_ASSERT(ggml_blck_size(src0->type) == 1);
|
| 223 |
+
|
| 224 |
+
switch (ggml_type_size(src0->type)) {
|
| 225 |
+
case 1:
|
| 226 |
+
concat_cuda<uint8_t>(src0, src1, dst, dim, stream);
|
| 227 |
+
break;
|
| 228 |
+
case 2:
|
| 229 |
+
concat_cuda<uint16_t>(src0, src1, dst, dim, stream);
|
| 230 |
+
break;
|
| 231 |
+
case 4:
|
| 232 |
+
concat_cuda<uint32_t>(src0, src1, dst, dim, stream);
|
| 233 |
+
break;
|
| 234 |
+
case 8:
|
| 235 |
+
concat_cuda<uint64_t>(src0, src1, dst, dim, stream);
|
| 236 |
+
break;
|
| 237 |
+
default:
|
| 238 |
+
GGML_ABORT("Unsupported type size: %zu", ggml_type_size(src0->type));
|
| 239 |
+
break;
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
}
|
vendor/src/ggml-cuda/concat.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_CONCAT_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_op_concat(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/conv-transpose-1d.cu
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "conv-transpose-1d.cuh"
|
| 2 |
+
|
| 3 |
+
static __global__ void conv_transpose_1d_kernel(
|
| 4 |
+
const int s0, const int p0, const int d0, const int output_size,
|
| 5 |
+
const int src0_ne0, const int src0_ne1, const int src0_ne2, const int src0_ne3,
|
| 6 |
+
const int src1_ne0, const int src1_ne1, const int src1_ne2, const int src1_ne3,
|
| 7 |
+
const int dst_ne0, const int dst_ne1, const int dst_ne2, const int dst_ne3,
|
| 8 |
+
const float * src0, const float * src1, float * dst) {
|
| 9 |
+
int global_index = threadIdx.x + blockIdx.x * blockDim.x;
|
| 10 |
+
if (global_index >= output_size) {
|
| 11 |
+
return;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
int out_t = global_index % dst_ne0;
|
| 15 |
+
int out_ch = (global_index / dst_ne0) % dst_ne1;
|
| 16 |
+
int plane = global_index / (dst_ne0 * dst_ne1);
|
| 17 |
+
|
| 18 |
+
float accumulator = 0;
|
| 19 |
+
|
| 20 |
+
for (int c = 0; c < src0_ne2; c++) {
|
| 21 |
+
int kernel_offset = src0_ne0 * (out_ch + src0_ne1 * c);
|
| 22 |
+
int input_offset = src1_ne0 * (c + src1_ne1 * plane);
|
| 23 |
+
|
| 24 |
+
for (int k = 0; k < src0_ne0; k++) {
|
| 25 |
+
int input_numer = out_t + p0 - k*d0;
|
| 26 |
+
if (input_numer < 0 || input_numer % s0 != 0) {
|
| 27 |
+
continue;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
int input_t = input_numer / s0;
|
| 31 |
+
if (input_t >= src1_ne0) {
|
| 32 |
+
continue;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
accumulator += src0[kernel_offset + k] * src1[input_offset + input_t];
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
dst[global_index] = accumulator;
|
| 39 |
+
GGML_UNUSED_VARS(src0_ne3, src1_ne2, src1_ne3, dst_ne2, dst_ne3);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
static void conv_transpose_1d_f32_f32_cuda(
|
| 43 |
+
const int s0, const int p0, const int d0, const int output_size,
|
| 44 |
+
const int src0_ne0, const int src0_ne1, const int src0_ne2, const int src0_ne3,
|
| 45 |
+
const int src1_ne0, const int src1_ne1, const int src1_ne2, const int src1_ne3,
|
| 46 |
+
const int dst_ne0, const int dst_ne1, const int dst_ne2, const int dst_ne3,
|
| 47 |
+
const float * src0, const float * src1, float * dst,
|
| 48 |
+
cudaStream_t stream) {
|
| 49 |
+
|
| 50 |
+
const int num_blocks = (output_size + CUDA_CONV_TRANPOSE_1D_BLOCK_SIZE - 1) / CUDA_CONV_TRANPOSE_1D_BLOCK_SIZE;
|
| 51 |
+
conv_transpose_1d_kernel<<<num_blocks,CUDA_CONV_TRANPOSE_1D_BLOCK_SIZE, 0, stream>>>(
|
| 52 |
+
s0,p0,d0,output_size,
|
| 53 |
+
src0_ne0, src0_ne1, src0_ne2, src0_ne3,
|
| 54 |
+
src1_ne0, src1_ne1, src1_ne2, src1_ne3,
|
| 55 |
+
dst_ne0, dst_ne1, dst_ne2, dst_ne3,
|
| 56 |
+
src0,src1, dst);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
void ggml_cuda_op_conv_transpose_1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 60 |
+
const ggml_tensor * src0 = dst->src[0];
|
| 61 |
+
const float * src0_d = (const float *)src0->data;
|
| 62 |
+
|
| 63 |
+
const ggml_tensor * src1 = dst->src[1];
|
| 64 |
+
const float * src1_d = (const float *)src1->data;
|
| 65 |
+
|
| 66 |
+
float * dst_d = (float *)dst->data;
|
| 67 |
+
cudaStream_t stream = ctx.stream();
|
| 68 |
+
|
| 69 |
+
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
| 70 |
+
GGML_ASSERT( dst->type == GGML_TYPE_F32);
|
| 71 |
+
|
| 72 |
+
GGML_ASSERT(ggml_is_contiguous(src0));
|
| 73 |
+
GGML_ASSERT(ggml_is_contiguous(src1));
|
| 74 |
+
|
| 75 |
+
const int32_t * opts = (const int32_t *)dst->op_params;
|
| 76 |
+
|
| 77 |
+
const int s0 = opts[0];
|
| 78 |
+
const int p0 = 0;//opts[3];
|
| 79 |
+
const int d0 = 1;//opts[4];
|
| 80 |
+
|
| 81 |
+
const int64_t output_size = ggml_nelements(dst);
|
| 82 |
+
|
| 83 |
+
conv_transpose_1d_f32_f32_cuda(s0, p0, d0, output_size,
|
| 84 |
+
src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
| 85 |
+
src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3],
|
| 86 |
+
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
| 87 |
+
src0_d, src1_d, dst_d, stream);
|
| 88 |
+
}
|
vendor/src/ggml-cuda/conv-transpose-1d.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_CONV_TRANPOSE_1D_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_op_conv_transpose_1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/conv2d-dw.cu
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "conv2d-dw.cuh"
|
| 2 |
+
|
| 3 |
+
struct conv_params {
|
| 4 |
+
int in_w, in_h;
|
| 5 |
+
int out_w, out_h;
|
| 6 |
+
int kernel_w, kernel_h;
|
| 7 |
+
int stride_x, stride_y;
|
| 8 |
+
int padding_x, padding_y;
|
| 9 |
+
int dilation_x, dilation_y;
|
| 10 |
+
int channels, batches;
|
| 11 |
+
};
|
| 12 |
+
|
| 13 |
+
struct kernel_bounds {
|
| 14 |
+
int y_min, y_max;
|
| 15 |
+
int x_min, x_max;
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
__device__ __forceinline__ kernel_bounds calculate_kernel_bounds(int out_x, int out_y, const conv_params & params) {
|
| 19 |
+
kernel_bounds bounds;
|
| 20 |
+
bounds.y_min = max(0, (params.padding_y - out_y * params.stride_y + params.dilation_y - 1) / params.dilation_y);
|
| 21 |
+
bounds.y_max =
|
| 22 |
+
min(params.kernel_h,
|
| 23 |
+
(params.in_h + params.padding_y - out_y * params.stride_y + params.dilation_y - 1) / params.dilation_y);
|
| 24 |
+
bounds.x_min = max(0, (params.padding_x - out_x * params.stride_x + params.dilation_x - 1) / params.dilation_x);
|
| 25 |
+
bounds.x_max =
|
| 26 |
+
min(params.kernel_w,
|
| 27 |
+
(params.in_w + params.padding_x - out_x * params.stride_x + params.dilation_x - 1) / params.dilation_x);
|
| 28 |
+
return bounds;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
__device__ __forceinline__ int calculate_input_coord(int out_coord, int kern_coord, int stride, int dilation, int padding) {
|
| 32 |
+
return out_coord * stride + kern_coord * dilation - padding;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
struct whcn_layout {
|
| 36 |
+
__device__ static int input_index(int n, int c, int y, int x, const conv_params & params) {
|
| 37 |
+
return n * (params.channels * params.in_w * params.in_h) + c * params.in_w * params.in_h + y * params.in_w + x;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
__device__ static int kernel_index(int c, int ky, int kx, const conv_params & params) {
|
| 41 |
+
return c * params.kernel_h * params.kernel_w + ky * params.kernel_w + kx;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
__device__ static int output_index(int n, int c, int y, int x, const conv_params & params) {
|
| 45 |
+
return n * (params.channels * params.out_w * params.out_h) + c * params.out_w * params.out_h +
|
| 46 |
+
y * params.out_w + x;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
__device__ static void unpack_indices(int global_idx, const conv_params & params, int & n, int & c, int & out_y,
|
| 50 |
+
int & out_x) {
|
| 51 |
+
out_x = global_idx % params.out_w;
|
| 52 |
+
out_y = (global_idx / params.out_w) % params.out_h;
|
| 53 |
+
c = (global_idx / (params.out_w * params.out_h)) % params.channels;
|
| 54 |
+
n = global_idx / (params.out_w * params.out_h * params.channels);
|
| 55 |
+
}
|
| 56 |
+
};
|
| 57 |
+
|
| 58 |
+
struct cwhn_layout {
|
| 59 |
+
__device__ static int input_index(int n, int c, int y, int x, const conv_params & params) {
|
| 60 |
+
return n * (params.channels * params.in_w * params.in_h) + (y * params.in_w + x) * params.channels + c;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
__device__ static int kernel_index(int c, int ky, int kx, const conv_params & params) {
|
| 64 |
+
return (ky * params.kernel_w + kx) * params.channels + c;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
__device__ static int output_index(int n, int c, int y, int x, const conv_params & params) {
|
| 68 |
+
return n * (params.channels * params.out_w * params.out_h) + y * (params.out_w * params.channels) +
|
| 69 |
+
x * params.channels + c;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
__device__ static void unpack_indices(int global_idx, const conv_params & params, int & n, int & c, int & out_y,
|
| 73 |
+
int & out_x) {
|
| 74 |
+
c = global_idx % params.channels;
|
| 75 |
+
out_x = (global_idx / params.channels) % params.out_w;
|
| 76 |
+
out_y = (global_idx / (params.channels * params.out_w)) % params.out_h;
|
| 77 |
+
n = global_idx / (params.channels * params.out_w * params.out_h);
|
| 78 |
+
}
|
| 79 |
+
};
|
| 80 |
+
|
| 81 |
+
template <typename T, typename Layout>
|
| 82 |
+
__global__ void conv2d_dw_kernel(const T * __restrict__ input, const T * __restrict__ kernel, T * __restrict__ output,
|
| 83 |
+
const int in_w, const int in_h, const int out_w, const int out_h,
|
| 84 |
+
const int kernel_w, const int kernel_h, const int stride_x, const int stride_y,
|
| 85 |
+
const int padding_x, const int padding_y, const int dilation_x, const int dilation_y,
|
| 86 |
+
const int channels, const int batches) {
|
| 87 |
+
const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 88 |
+
const int total_elements = batches * channels * out_h * out_w;
|
| 89 |
+
|
| 90 |
+
if (global_idx >= total_elements) {
|
| 91 |
+
return;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
conv_params params = { in_w, in_h, out_w, out_h, kernel_w, kernel_h, stride_x,
|
| 95 |
+
stride_y, padding_x, padding_y, dilation_x, dilation_y, channels, batches };
|
| 96 |
+
|
| 97 |
+
int batch_idx, channel_idx, out_y_idx, out_x_idx;
|
| 98 |
+
Layout::unpack_indices(global_idx, params, batch_idx, channel_idx, out_y_idx, out_x_idx);
|
| 99 |
+
|
| 100 |
+
T accumulator = 0;
|
| 101 |
+
kernel_bounds bounds = calculate_kernel_bounds(out_x_idx, out_y_idx, params);
|
| 102 |
+
|
| 103 |
+
for (int kern_y = bounds.y_min; kern_y < bounds.y_max; ++kern_y) {
|
| 104 |
+
int in_y_idx = calculate_input_coord(out_y_idx, kern_y, params.stride_y, params.dilation_y, params.padding_y);
|
| 105 |
+
|
| 106 |
+
for (int kern_x = bounds.x_min; kern_x < bounds.x_max; ++kern_x) {
|
| 107 |
+
int in_x_idx = calculate_input_coord(out_x_idx, kern_x, params.stride_x, params.dilation_x, params.padding_x);
|
| 108 |
+
|
| 109 |
+
const T input_val = input[Layout::input_index(batch_idx, channel_idx, in_y_idx, in_x_idx, params)];
|
| 110 |
+
const T kernel_val = kernel[Layout::kernel_index(channel_idx, kern_y, kern_x, params)];
|
| 111 |
+
|
| 112 |
+
accumulator += input_val * kernel_val;
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
output[Layout::output_index(batch_idx, channel_idx, out_y_idx, out_x_idx, params)] = accumulator;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
void ggml_cuda_op_conv2d_dw(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 120 |
+
const ggml_tensor * kernel = dst->src[0];
|
| 121 |
+
const ggml_tensor * input = dst->src[1];
|
| 122 |
+
|
| 123 |
+
GGML_ASSERT(kernel->type == GGML_TYPE_F32 && input->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32);
|
| 124 |
+
const float * w_d = (const float *) kernel->data;
|
| 125 |
+
const float * x_d = (const float *) input->data;
|
| 126 |
+
float * y_d = (float *) dst->data;
|
| 127 |
+
|
| 128 |
+
const int32_t * p = (const int32_t *) dst->op_params;
|
| 129 |
+
const int stride_x = p[0];
|
| 130 |
+
const int stride_y = p[1];
|
| 131 |
+
const int padding_x = p[2];
|
| 132 |
+
const int padding_y = p[3];
|
| 133 |
+
const int dilation_x = p[4];
|
| 134 |
+
const int dilation_y = p[5];
|
| 135 |
+
|
| 136 |
+
const int in_w = input->ne[0];
|
| 137 |
+
const int in_h = input->ne[1];
|
| 138 |
+
const int kernel_w = kernel->ne[0];
|
| 139 |
+
const int kernel_h = kernel->ne[1];
|
| 140 |
+
const int out_w = dst->ne[0];
|
| 141 |
+
const int out_h = dst->ne[1];
|
| 142 |
+
const int channels = dst->ne[2];
|
| 143 |
+
const int batches = dst->ne[3];
|
| 144 |
+
|
| 145 |
+
cudaStream_t st = ctx.stream();
|
| 146 |
+
|
| 147 |
+
const int total = batches * channels * out_h * out_w;
|
| 148 |
+
const int blocks = (total + CUDA_CONV2D_DW_BLOCK_SIZE - 1) / CUDA_CONV2D_DW_BLOCK_SIZE;
|
| 149 |
+
|
| 150 |
+
if (ggml_is_contiguous(input)) {
|
| 151 |
+
conv2d_dw_kernel<float, whcn_layout><<<blocks, CUDA_CONV2D_DW_BLOCK_SIZE, 0, st>>>(
|
| 152 |
+
x_d, w_d, y_d, in_w, in_h, out_w, out_h, kernel_w, kernel_h, stride_x, stride_y, padding_x, padding_y,
|
| 153 |
+
dilation_x, dilation_y, channels, batches);
|
| 154 |
+
} else if (ggml_is_contiguous_channels(input)) {
|
| 155 |
+
conv2d_dw_kernel<float, cwhn_layout><<<blocks, CUDA_CONV2D_DW_BLOCK_SIZE, 0, st>>>(
|
| 156 |
+
x_d, w_d, y_d, in_w, in_h, out_w, out_h, kernel_w, kernel_h, stride_x, stride_y, padding_x, padding_y,
|
| 157 |
+
dilation_x, dilation_y, channels, batches);
|
| 158 |
+
} else {
|
| 159 |
+
GGML_ABORT("Unsupported memory layout for conv_2d_dw");
|
| 160 |
+
}
|
| 161 |
+
}
|
vendor/src/ggml-cuda/conv2d-dw.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
#include "common.cuh"
|
| 3 |
+
|
| 4 |
+
#define CUDA_CONV2D_DW_BLOCK_SIZE 256
|
| 5 |
+
void ggml_cuda_op_conv2d_dw(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
vendor/src/ggml-cuda/conv2d-transpose.cu
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "conv2d-transpose.cuh"
|
| 2 |
+
#include "convert.cuh"
|
| 3 |
+
|
| 4 |
+
template <typename kernel_t>
|
| 5 |
+
static __global__ void conv2d_transpose_kernel(const float * __restrict__ input,
|
| 6 |
+
const kernel_t * __restrict__ kernel,
|
| 7 |
+
float * __restrict__ output,
|
| 8 |
+
const int in_w,
|
| 9 |
+
const int in_h,
|
| 10 |
+
const int out_w,
|
| 11 |
+
const int out_h,
|
| 12 |
+
const int kernel_w,
|
| 13 |
+
const int kernel_h,
|
| 14 |
+
const int stride,
|
| 15 |
+
const int c_in,
|
| 16 |
+
const int c_out,
|
| 17 |
+
const int batches) {
|
| 18 |
+
const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 19 |
+
|
| 20 |
+
const int total_elements = out_w * out_h * c_out * batches;
|
| 21 |
+
|
| 22 |
+
if (global_idx >= total_elements) {
|
| 23 |
+
return;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
const int out_x_idx = global_idx % out_w;
|
| 27 |
+
const int out_y_idx = (global_idx / out_w) % out_h;
|
| 28 |
+
const int c_idx = (global_idx / (out_w * out_h)) % c_out;
|
| 29 |
+
const int n_idx = global_idx / (out_w * out_h * c_out);
|
| 30 |
+
|
| 31 |
+
float accumulator = 0;
|
| 32 |
+
// For each output idx, find the inputs that contribute to it by checking stride alignment and bounds
|
| 33 |
+
|
| 34 |
+
for (int c_in_idx = 0; c_in_idx < c_in; c_in_idx++) {
|
| 35 |
+
for (int kh = 0; kh < kernel_h; ++kh) {
|
| 36 |
+
int in_y = out_y_idx - kh;
|
| 37 |
+
if (in_y < 0 || in_y % stride) {
|
| 38 |
+
continue;
|
| 39 |
+
}
|
| 40 |
+
in_y /= stride;
|
| 41 |
+
if (in_y >= in_h) {
|
| 42 |
+
continue;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
for (int kw = 0; kw < kernel_w; ++kw) {
|
| 46 |
+
int in_x = out_x_idx - kw;
|
| 47 |
+
if (in_x < 0 || in_x % stride) {
|
| 48 |
+
continue;
|
| 49 |
+
}
|
| 50 |
+
in_x /= stride;
|
| 51 |
+
if (in_x >= in_w) {
|
| 52 |
+
continue;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const int input_idx = (in_w * in_h * c_in) * n_idx + (in_w * in_h) * c_in_idx + (in_w) *in_y + in_x;
|
| 56 |
+
const int kernel_idx =
|
| 57 |
+
(kernel_h * kernel_w * c_out) * c_in_idx + (kernel_h * kernel_w) * c_idx + (kernel_w) *kh + kw;
|
| 58 |
+
|
| 59 |
+
float input_val = input[input_idx];
|
| 60 |
+
kernel_t kern_val = kernel[kernel_idx];
|
| 61 |
+
|
| 62 |
+
accumulator += input_val * ggml_cuda_cast<float>(kern_val);
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
output[(out_w * out_h * c_out) * n_idx + (out_w * out_h) * c_idx + (out_w) *out_y_idx + out_x_idx] = accumulator;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
//input is (W, H, C_in, N), Kernel is (W, H, C_out, C_in)
|
| 71 |
+
void ggml_cuda_conv_2d_transpose_p0(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
| 72 |
+
const ggml_tensor * kernel = dst->src[0];
|
| 73 |
+
const ggml_tensor * input = dst->src[1];
|
| 74 |
+
|
| 75 |
+
GGML_ASSERT(kernel->type == GGML_TYPE_F16 || kernel->type == GGML_TYPE_F32);
|
| 76 |
+
GGML_ASSERT(input->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32);
|
| 77 |
+
|
| 78 |
+
const float * input_data = (const float *) input->data;
|
| 79 |
+
float * output_data = (float *) dst->data;
|
| 80 |
+
const void * kernel_data = kernel->data;
|
| 81 |
+
|
| 82 |
+
const int input_w = input->ne[0];
|
| 83 |
+
const int input_h = input->ne[1];
|
| 84 |
+
const int output_w = dst->ne[0];
|
| 85 |
+
const int output_h = dst->ne[1];
|
| 86 |
+
const int channels_in = input->ne[2];
|
| 87 |
+
const int channels_out = kernel->ne[2];
|
| 88 |
+
const int kernel_w = kernel->ne[0];
|
| 89 |
+
const int kernel_h = kernel->ne[1];
|
| 90 |
+
const int stride = dst->op_params[0];
|
| 91 |
+
const int batches = input->ne[3];
|
| 92 |
+
|
| 93 |
+
GGML_ASSERT(channels_in == kernel->ne[3]);
|
| 94 |
+
GGML_ASSERT(stride > 0);
|
| 95 |
+
|
| 96 |
+
cudaStream_t st = ctx.stream();
|
| 97 |
+
|
| 98 |
+
GGML_ASSERT(ggml_is_contiguous(input));
|
| 99 |
+
GGML_ASSERT(ggml_is_contiguous(kernel));
|
| 100 |
+
GGML_ASSERT(ggml_is_contiguous(dst));
|
| 101 |
+
|
| 102 |
+
const int total = output_w * output_h * channels_out * batches;
|
| 103 |
+
const int blocks = (total + CUDA_CONV2D_TRANSPOSE_BLOCK_SIZE - 1) / CUDA_CONV2D_TRANSPOSE_BLOCK_SIZE;
|
| 104 |
+
|
| 105 |
+
if (kernel->type == GGML_TYPE_F16) {
|
| 106 |
+
conv2d_transpose_kernel<half><<<blocks, CUDA_CONV2D_TRANSPOSE_BLOCK_SIZE, 0, st>>>(
|
| 107 |
+
input_data, (const half *) kernel_data, output_data, input_w, input_h, output_w, output_h, kernel_w,
|
| 108 |
+
kernel_h, stride, channels_in, channels_out, batches);
|
| 109 |
+
|
| 110 |
+
} else {
|
| 111 |
+
conv2d_transpose_kernel<float><<<blocks, CUDA_CONV2D_TRANSPOSE_BLOCK_SIZE, 0, st>>>(
|
| 112 |
+
input_data, (const float *) kernel_data, output_data, input_w, input_h, output_w, output_h, kernel_w,
|
| 113 |
+
kernel_h, stride, channels_in, channels_out, batches);
|
| 114 |
+
}
|
| 115 |
+
}
|
vendor/src/ggml-cuda/conv2d-transpose.cuh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "common.cuh"
|
| 2 |
+
|
| 3 |
+
#define CUDA_CONV2D_TRANSPOSE_BLOCK_SIZE 256
|
| 4 |
+
|
| 5 |
+
void ggml_cuda_conv_2d_transpose_p0(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|