marcsun13 HF Staff commited on
Commit
d297b20
·
1 Parent(s): f79d690

Add Metal backend; vendor whole llama.cpp trees

Browse files

Metal 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
Files changed (50) hide show
  1. build.toml +18 -1
  2. flake.lock +117 -0
  3. gguf_metal/common.h +31 -0
  4. gguf_metal/ggml_dispatch.mm +282 -0
  5. gguf_metal/gguf_metal.cpp +59 -0
  6. vendor.py +26 -31
  7. vendor/include/ggml-blas.h +25 -0
  8. vendor/include/ggml-cann.h +123 -0
  9. vendor/include/ggml-cpp.h +39 -0
  10. vendor/include/ggml-cpu.h +152 -0
  11. vendor/include/ggml-et.h +28 -0
  12. vendor/include/ggml-hexagon.h +19 -0
  13. vendor/include/ggml-metal.h +61 -0
  14. vendor/include/ggml-opencl.h +26 -0
  15. vendor/include/ggml-openvino.h +37 -0
  16. vendor/include/ggml-opt.h +256 -0
  17. vendor/include/ggml-rpc.h +35 -0
  18. vendor/include/ggml-sycl.h +57 -0
  19. vendor/include/ggml-virtgpu.h +14 -0
  20. vendor/include/ggml-vulkan.h +29 -0
  21. vendor/include/ggml-webgpu.h +19 -0
  22. vendor/include/ggml-zdnn.h +17 -0
  23. vendor/include/ggml-zendnn.h +22 -0
  24. vendor/src/ggml-cuda/CMakeLists.txt +269 -0
  25. vendor/src/ggml-cuda/acc.cu +61 -0
  26. vendor/src/ggml-cuda/acc.cuh +5 -0
  27. vendor/src/ggml-cuda/add-id.cu +58 -0
  28. vendor/src/ggml-cuda/add-id.cuh +3 -0
  29. vendor/src/ggml-cuda/allreduce.cu +971 -0
  30. vendor/src/ggml-cuda/allreduce.cuh +29 -0
  31. vendor/src/ggml-cuda/arange.cu +34 -0
  32. vendor/src/ggml-cuda/arange.cuh +5 -0
  33. vendor/src/ggml-cuda/argmax.cu +91 -0
  34. vendor/src/ggml-cuda/argmax.cuh +3 -0
  35. vendor/src/ggml-cuda/argsort.cu +292 -0
  36. vendor/src/ggml-cuda/argsort.cuh +20 -0
  37. vendor/src/ggml-cuda/binbcast.cu +574 -0
  38. vendor/src/ggml-cuda/binbcast.cuh +12 -0
  39. vendor/src/ggml-cuda/clamp.cu +45 -0
  40. vendor/src/ggml-cuda/clamp.cuh +5 -0
  41. vendor/src/ggml-cuda/col2im-1d.cu +81 -0
  42. vendor/src/ggml-cuda/col2im-1d.cuh +3 -0
  43. vendor/src/ggml-cuda/concat.cu +242 -0
  44. vendor/src/ggml-cuda/concat.cuh +5 -0
  45. vendor/src/ggml-cuda/conv-transpose-1d.cu +88 -0
  46. vendor/src/ggml-cuda/conv-transpose-1d.cuh +5 -0
  47. vendor/src/ggml-cuda/conv2d-dw.cu +161 -0
  48. vendor/src/ggml-cuda/conv2d-dw.cuh +5 -0
  49. vendor/src/ggml-cuda/conv2d-transpose.cu +115 -0
  50. 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(FILES)} files from llama.cpp @ {rev[:12]} into {VENDOR}")
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);