whisper.rn 0.5.0-rc.9 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +2 -1
- package/android/gradle.properties +1 -1
- package/cpp/ggml-alloc.c +265 -141
- package/cpp/ggml-backend-impl.h +4 -1
- package/cpp/ggml-backend-reg.cpp +30 -13
- package/cpp/ggml-backend.cpp +221 -38
- package/cpp/ggml-backend.h +17 -1
- package/cpp/ggml-common.h +17 -0
- package/cpp/ggml-cpu/amx/amx.cpp +4 -2
- package/cpp/ggml-cpu/arch/arm/quants.c +132 -596
- package/cpp/ggml-cpu/arch/arm/repack.cpp +14 -286
- package/cpp/ggml-cpu/arch/x86/quants.c +184 -675
- package/cpp/ggml-cpu/arch/x86/repack.cpp +4679 -1657
- package/cpp/ggml-cpu/arch-fallback.h +32 -2
- package/cpp/ggml-cpu/common.h +14 -0
- package/cpp/ggml-cpu/ggml-cpu-impl.h +13 -6
- package/cpp/ggml-cpu/ggml-cpu.c +70 -42
- package/cpp/ggml-cpu/ggml-cpu.cpp +35 -28
- package/cpp/ggml-cpu/ops.cpp +1587 -1177
- package/cpp/ggml-cpu/ops.h +5 -8
- package/cpp/ggml-cpu/quants.c +35 -0
- package/cpp/ggml-cpu/quants.h +8 -0
- package/cpp/ggml-cpu/repack.cpp +458 -47
- package/cpp/ggml-cpu/repack.h +22 -0
- package/cpp/ggml-cpu/simd-mappings.h +89 -60
- package/cpp/ggml-cpu/traits.cpp +2 -2
- package/cpp/ggml-cpu/traits.h +1 -1
- package/cpp/ggml-cpu/vec.cpp +170 -26
- package/cpp/ggml-cpu/vec.h +506 -63
- package/cpp/ggml-cpu.h +1 -1
- package/cpp/ggml-impl.h +119 -9
- package/cpp/ggml-metal/ggml-metal-common.cpp +446 -0
- package/cpp/ggml-metal/ggml-metal-common.h +52 -0
- package/cpp/ggml-metal/ggml-metal-context.h +33 -0
- package/cpp/ggml-metal/ggml-metal-context.m +600 -0
- package/cpp/ggml-metal/ggml-metal-device.cpp +1376 -0
- package/cpp/ggml-metal/ggml-metal-device.h +226 -0
- package/cpp/ggml-metal/ggml-metal-device.m +1312 -0
- package/cpp/ggml-metal/ggml-metal-impl.h +722 -0
- package/cpp/ggml-metal/ggml-metal-ops.cpp +3158 -0
- package/cpp/ggml-metal/ggml-metal-ops.h +82 -0
- package/cpp/ggml-metal/ggml-metal.cpp +718 -0
- package/cpp/ggml-metal/ggml-whisper-sim.metallib +0 -0
- package/cpp/ggml-metal/ggml-whisper.metallib +0 -0
- package/cpp/ggml-metal-impl.h +90 -51
- package/cpp/ggml-metal.h +1 -6
- package/cpp/ggml-opt.cpp +97 -41
- package/cpp/ggml-opt.h +25 -6
- package/cpp/ggml-quants.c +111 -16
- package/cpp/ggml-quants.h +6 -0
- package/cpp/ggml.c +486 -98
- package/cpp/ggml.h +221 -16
- package/cpp/gguf.cpp +8 -1
- package/cpp/jsi/RNWhisperJSI.cpp +25 -6
- package/cpp/jsi/ThreadPool.h +3 -3
- package/cpp/whisper.cpp +100 -76
- package/cpp/whisper.h +1 -0
- package/ios/CMakeLists.txt +6 -1
- package/ios/RNWhisper.mm +6 -6
- package/ios/RNWhisperContext.mm +2 -0
- package/ios/RNWhisperVadContext.mm +16 -13
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-backend-impl.h +4 -1
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-backend.h +17 -1
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-common.h +17 -0
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-cpu.h +1 -1
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-impl.h +119 -9
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-metal-impl.h +90 -51
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-metal.h +1 -6
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-opt.h +25 -6
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml-quants.h +6 -0
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/ggml.h +221 -16
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Headers/whisper.h +1 -0
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/Info.plist +0 -0
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/ggml-whisper.metallib +0 -0
- package/ios/rnwhisper.xcframework/ios-arm64/rnwhisper.framework/rnwhisper +0 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-backend-impl.h +4 -1
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-backend.h +17 -1
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-common.h +17 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-cpu.h +1 -1
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-impl.h +119 -9
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-metal-impl.h +90 -51
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-metal.h +1 -6
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-opt.h +25 -6
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-quants.h +6 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml.h +221 -16
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Headers/whisper.h +1 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/Info.plist +0 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/_CodeSignature/CodeResources +1 -1
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/ggml-whisper-sim.metallib +0 -0
- package/ios/rnwhisper.xcframework/ios-arm64_x86_64-simulator/rnwhisper.framework/rnwhisper +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-backend-impl.h +4 -1
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-backend.h +17 -1
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-common.h +17 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-cpu.h +1 -1
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-impl.h +119 -9
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-metal-impl.h +90 -51
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-metal.h +1 -6
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-opt.h +25 -6
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml-quants.h +6 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/ggml.h +221 -16
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Headers/whisper.h +1 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/Info.plist +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/ggml-whisper.metallib +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64/rnwhisper.framework/rnwhisper +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-backend-impl.h +4 -1
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-backend.h +17 -1
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-common.h +17 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-cpu.h +1 -1
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-impl.h +119 -9
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-metal-impl.h +90 -51
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-metal.h +1 -6
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-opt.h +25 -6
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml-quants.h +6 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/ggml.h +221 -16
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Headers/whisper.h +1 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/Info.plist +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/_CodeSignature/CodeResources +1 -1
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/ggml-whisper-sim.metallib +0 -0
- package/ios/rnwhisper.xcframework/tvos-arm64_x86_64-simulator/rnwhisper.framework/rnwhisper +0 -0
- package/lib/commonjs/realtime-transcription/RealtimeTranscriber.js +13 -0
- package/lib/commonjs/realtime-transcription/RealtimeTranscriber.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/realtime-transcription/RealtimeTranscriber.js +13 -0
- package/lib/module/realtime-transcription/RealtimeTranscriber.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/realtime-transcription/RealtimeTranscriber.d.ts.map +1 -1
- package/lib/typescript/realtime-transcription/types.d.ts +6 -0
- package/lib/typescript/realtime-transcription/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/realtime-transcription/RealtimeTranscriber.ts +17 -0
- package/src/realtime-transcription/types.ts +6 -0
- package/src/version.json +1 -1
- package/whisper-rn.podspec +8 -9
- package/cpp/ggml-metal.m +0 -6284
- package/cpp/ggml-whisper-sim.metallib +0 -0
- package/cpp/ggml-whisper.metallib +0 -0
|
@@ -0,0 +1,1312 @@
|
|
|
1
|
+
#import "ggml-metal-device.h"
|
|
2
|
+
|
|
3
|
+
#import "ggml-impl.h"
|
|
4
|
+
#import "ggml-threading.h"
|
|
5
|
+
|
|
6
|
+
#include <Foundation/Foundation.h>
|
|
7
|
+
|
|
8
|
+
#include <Metal/Metal.h>
|
|
9
|
+
|
|
10
|
+
#ifndef TARGET_OS_VISION
|
|
11
|
+
#define TARGET_OS_VISION 0
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
// create residency sets only on macOS >= 15.0
|
|
15
|
+
#if !TARGET_CPU_X86_64 && TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || \
|
|
16
|
+
TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || \
|
|
17
|
+
TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 180000 || \
|
|
18
|
+
TARGET_OS_VISION && __VISION_OS_VERSION_MAX_ALLOWED >= 200000
|
|
19
|
+
#define WSP_GGML_METAL_HAS_RESIDENCY_SETS 1
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
// overload of MTLGPUFamilyMetal3 (not available in some environments)
|
|
23
|
+
static const NSInteger MTLGPUFamilyMetal3_GGML = 5001;
|
|
24
|
+
|
|
25
|
+
#if !WSP_GGML_METAL_EMBED_LIBRARY
|
|
26
|
+
// Here to assist with NSBundle Path Hack
|
|
27
|
+
@interface WSPGGMLMetalClass : NSObject
|
|
28
|
+
@end
|
|
29
|
+
@implementation WSPGGMLMetalClass
|
|
30
|
+
@end
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
//
|
|
34
|
+
// MTLFunctionConstantValues wrapper
|
|
35
|
+
//
|
|
36
|
+
|
|
37
|
+
struct wsp_ggml_metal_cv {
|
|
38
|
+
MTLFunctionConstantValues * obj;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
wsp_ggml_metal_cv_t wsp_ggml_metal_cv_init(void) {
|
|
42
|
+
wsp_ggml_metal_cv_t res = calloc(1, sizeof(struct wsp_ggml_metal_cv));
|
|
43
|
+
|
|
44
|
+
res->obj = [[MTLFunctionConstantValues alloc] init];
|
|
45
|
+
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void wsp_ggml_metal_cv_free(wsp_ggml_metal_cv_t cv) {
|
|
50
|
+
[cv->obj release];
|
|
51
|
+
free(cv);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void wsp_ggml_metal_cv_set_int16(wsp_ggml_metal_cv_t cv, int16_t value, int32_t idx) {
|
|
55
|
+
[cv->obj setConstantValue:&value type:MTLDataTypeShort atIndex:idx];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void wsp_ggml_metal_cv_set_int32(wsp_ggml_metal_cv_t cv, int32_t value, int32_t idx) {
|
|
59
|
+
[cv->obj setConstantValue:&value type:MTLDataTypeInt atIndex:idx];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void wsp_ggml_metal_cv_set_bool(wsp_ggml_metal_cv_t cv, bool value, int32_t idx) {
|
|
63
|
+
[cv->obj setConstantValue:&value type:MTLDataTypeBool atIndex:idx];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//
|
|
67
|
+
// MTLComputePipelineState wrapper
|
|
68
|
+
//
|
|
69
|
+
|
|
70
|
+
struct wsp_ggml_metal_pipeline {
|
|
71
|
+
id<MTLComputePipelineState> obj;
|
|
72
|
+
|
|
73
|
+
// suggested dispatch sizes
|
|
74
|
+
int nsg;
|
|
75
|
+
|
|
76
|
+
int nr0;
|
|
77
|
+
int nr1;
|
|
78
|
+
|
|
79
|
+
size_t smem;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
wsp_ggml_metal_pipeline_t wsp_ggml_metal_pipeline_init(void) {
|
|
83
|
+
wsp_ggml_metal_pipeline_t res = calloc(1, sizeof(struct wsp_ggml_metal_pipeline));
|
|
84
|
+
|
|
85
|
+
*res = (struct wsp_ggml_metal_pipeline) {
|
|
86
|
+
/*.obj =*/ nil,
|
|
87
|
+
/*.nsg =*/ 0,
|
|
88
|
+
/*.nr0 =*/ 0,
|
|
89
|
+
/*.nr1 =*/ 0,
|
|
90
|
+
/*.smem =*/ 0,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void wsp_ggml_metal_pipeline_free(wsp_ggml_metal_pipeline_t pipeline) {
|
|
97
|
+
[pipeline->obj release];
|
|
98
|
+
|
|
99
|
+
free(pipeline);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void wsp_ggml_metal_pipeline_set_nsg(wsp_ggml_metal_pipeline_t pipeline, int nsg) {
|
|
103
|
+
pipeline->nsg = nsg;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
int wsp_ggml_metal_pipeline_get_nsg(wsp_ggml_metal_pipeline_t pipeline) {
|
|
107
|
+
return pipeline->nsg;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void wsp_ggml_metal_pipeline_set_nr0(wsp_ggml_metal_pipeline_t pipeline, int nr0) {
|
|
111
|
+
pipeline->nr0 = nr0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
int wsp_ggml_metal_pipeline_get_nr0(wsp_ggml_metal_pipeline_t pipeline) {
|
|
115
|
+
return pipeline->nr0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void wsp_ggml_metal_pipeline_set_nr1(wsp_ggml_metal_pipeline_t pipeline, int nr1) {
|
|
119
|
+
pipeline->nr1 = nr1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
int wsp_ggml_metal_pipeline_get_nr1(wsp_ggml_metal_pipeline_t pipeline) {
|
|
123
|
+
return pipeline->nr1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void wsp_ggml_metal_pipeline_set_smem(wsp_ggml_metal_pipeline_t pipeline, size_t smem) {
|
|
127
|
+
pipeline->smem = smem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
size_t wsp_ggml_metal_pipeline_get_smem(wsp_ggml_metal_pipeline_t pipeline) {
|
|
131
|
+
return pipeline->smem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
int wsp_ggml_metal_pipeline_max_theads_per_threadgroup(wsp_ggml_metal_pipeline_t pipeline) {
|
|
135
|
+
return pipeline->obj.maxTotalThreadsPerThreadgroup;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
struct wsp_ggml_metal_library {
|
|
139
|
+
id<MTLLibrary> obj;
|
|
140
|
+
id<MTLDevice> device;
|
|
141
|
+
|
|
142
|
+
wsp_ggml_metal_pipelines_t pipelines; // cache of compiled pipelines
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
wsp_ggml_metal_library_t wsp_ggml_metal_library_init(wsp_ggml_metal_device_t dev) {
|
|
146
|
+
id<MTLLibrary> library = nil;
|
|
147
|
+
id<MTLDevice> device = wsp_ggml_metal_device_get_obj(dev);
|
|
148
|
+
|
|
149
|
+
// load library
|
|
150
|
+
//
|
|
151
|
+
// - first check if the library is embedded
|
|
152
|
+
// - then check if the library is in the bundle
|
|
153
|
+
// - if not found, load the source and compile it
|
|
154
|
+
// - if that fails, return NULL
|
|
155
|
+
//
|
|
156
|
+
// TODO: move to a function
|
|
157
|
+
{
|
|
158
|
+
const int64_t t_start = wsp_ggml_time_us();
|
|
159
|
+
|
|
160
|
+
NSError * error = nil;
|
|
161
|
+
NSString * src = nil;
|
|
162
|
+
|
|
163
|
+
#if WSP_GGML_METAL_EMBED_LIBRARY
|
|
164
|
+
WSP_GGML_LOG_INFO("%s: using embedded metal library\n", __func__);
|
|
165
|
+
|
|
166
|
+
extern const char wsp_ggml_metallib_start[];
|
|
167
|
+
extern const char wsp_ggml_metallib_end[];
|
|
168
|
+
|
|
169
|
+
src = [[NSString alloc] initWithBytes:wsp_ggml_metallib_start length:(wsp_ggml_metallib_end-wsp_ggml_metallib_start) encoding:NSUTF8StringEncoding];
|
|
170
|
+
#else
|
|
171
|
+
|
|
172
|
+
#ifdef SWIFT_PACKAGE
|
|
173
|
+
NSBundle * bundle = SWIFTPM_MODULE_BUNDLE;
|
|
174
|
+
#else
|
|
175
|
+
NSBundle * bundle = [NSBundle bundleForClass:[WSPGGMLMetalClass class]];
|
|
176
|
+
#endif
|
|
177
|
+
|
|
178
|
+
#if TARGET_OS_SIMULATOR
|
|
179
|
+
NSString * path_lib = [bundle pathForResource:@"ggml-whisper-sim" ofType:@"metallib"];
|
|
180
|
+
#else
|
|
181
|
+
NSString * path_lib = [bundle pathForResource:@"ggml-whisper" ofType:@"metallib"];
|
|
182
|
+
#endif
|
|
183
|
+
if (path_lib == nil) {
|
|
184
|
+
// Try to find the resource in the directory where the current binary located.
|
|
185
|
+
NSString * bin_cur = [[NSProcessInfo processInfo] arguments][0];
|
|
186
|
+
NSString * bin_dir = [bin_cur stringByDeletingLastPathComponent];
|
|
187
|
+
|
|
188
|
+
NSString * path_lib_default = [NSString pathWithComponents:@[bin_dir, @"default.metallib"]];
|
|
189
|
+
if ([[NSFileManager defaultManager] isReadableFileAtPath:path_lib_default]) {
|
|
190
|
+
WSP_GGML_LOG_INFO("%s: found '%s'\n", __func__, [path_lib_default UTF8String]);
|
|
191
|
+
|
|
192
|
+
NSDictionary * atts = [[NSFileManager defaultManager] attributesOfItemAtPath:path_lib_default error:&error];
|
|
193
|
+
if (atts && atts[NSFileType] == NSFileTypeSymbolicLink) {
|
|
194
|
+
// Optionally, if this is a symlink, try to resolve it.
|
|
195
|
+
path_lib_default = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:path_lib_default error:&error];
|
|
196
|
+
if (path_lib_default && [path_lib_default length] > 0 && ![[path_lib_default substringToIndex:1] isEqualToString:@"/"]) {
|
|
197
|
+
// It is a relative path, adding the binary directory as directory prefix.
|
|
198
|
+
path_lib_default = [NSString pathWithComponents:@[bin_dir, path_lib_default]];
|
|
199
|
+
}
|
|
200
|
+
if (!path_lib_default || ![[NSFileManager defaultManager] isReadableFileAtPath:path_lib_default]) {
|
|
201
|
+
// Link to the resource could not be resolved.
|
|
202
|
+
path_lib_default = nil;
|
|
203
|
+
} else {
|
|
204
|
+
WSP_GGML_LOG_INFO("%s: symlink resolved '%s'\n", __func__, [path_lib_default UTF8String]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
// The resource couldn't be found in the binary's directory.
|
|
209
|
+
path_lib_default = nil;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
path_lib = path_lib_default;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (path_lib != nil) {
|
|
216
|
+
// pre-compiled library found
|
|
217
|
+
NSURL * libURL = [NSURL fileURLWithPath:path_lib];
|
|
218
|
+
WSP_GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_lib UTF8String]);
|
|
219
|
+
|
|
220
|
+
library = [device newLibraryWithURL:libURL error:&error];
|
|
221
|
+
if (error) {
|
|
222
|
+
WSP_GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
|
|
223
|
+
return nil;
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
WSP_GGML_LOG_INFO("%s: default.metallib not found, loading from source\n", __func__);
|
|
227
|
+
|
|
228
|
+
NSString * path_source;
|
|
229
|
+
NSString * path_resource = [[NSProcessInfo processInfo].environment objectForKey:@"WSP_GGML_METAL_PATH_RESOURCES"];
|
|
230
|
+
|
|
231
|
+
WSP_GGML_LOG_INFO("%s: WSP_GGML_METAL_PATH_RESOURCES = %s\n", __func__, path_resource ? [path_resource UTF8String] : "nil");
|
|
232
|
+
|
|
233
|
+
if (path_resource) {
|
|
234
|
+
path_source = [path_resource stringByAppendingPathComponent:@"ggml-metal.metal"];
|
|
235
|
+
} else {
|
|
236
|
+
path_source = [bundle pathForResource:@"ggml-metal" ofType:@"metal"];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (path_source == nil) {
|
|
240
|
+
WSP_GGML_LOG_WARN("%s: error: could not use bundle path to find ggml-metal.metal, falling back to trying cwd\n", __func__);
|
|
241
|
+
path_source = @"ggml-metal.metal";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
WSP_GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_source UTF8String]);
|
|
245
|
+
|
|
246
|
+
src = [NSString stringWithContentsOfFile:path_source encoding:NSUTF8StringEncoding error:&error];
|
|
247
|
+
if (error) {
|
|
248
|
+
WSP_GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
|
|
249
|
+
return nil;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
#endif
|
|
253
|
+
|
|
254
|
+
if (!library) {
|
|
255
|
+
@autoreleasepool {
|
|
256
|
+
// dictionary of preprocessor macros
|
|
257
|
+
NSMutableDictionary * prep = [NSMutableDictionary dictionary];
|
|
258
|
+
|
|
259
|
+
if (wsp_ggml_metal_device_get_props(dev)->has_bfloat) {
|
|
260
|
+
[prep setObject:@"1" forKey:@"WSP_GGML_METAL_HAS_BF16"];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#if WSP_GGML_METAL_EMBED_LIBRARY
|
|
264
|
+
[prep setObject:@"1" forKey:@"WSP_GGML_METAL_EMBED_LIBRARY"];
|
|
265
|
+
#endif
|
|
266
|
+
|
|
267
|
+
MTLCompileOptions * options = [MTLCompileOptions new];
|
|
268
|
+
options.preprocessorMacros = prep;
|
|
269
|
+
|
|
270
|
+
//[options setFastMathEnabled:false];
|
|
271
|
+
|
|
272
|
+
library = [device newLibraryWithSource:src options:options error:&error];
|
|
273
|
+
if (error) {
|
|
274
|
+
WSP_GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
|
|
275
|
+
return nil;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
#if !__has_feature(objc_arc)
|
|
279
|
+
[options release];
|
|
280
|
+
#endif
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#if WSP_GGML_METAL_EMBED_LIBRARY
|
|
285
|
+
[src release];
|
|
286
|
+
#endif // WSP_GGML_METAL_EMBED_LIBRARY
|
|
287
|
+
|
|
288
|
+
WSP_GGML_LOG_INFO("%s: loaded in %.3f sec\n", __func__, (wsp_ggml_time_us() - t_start) / 1e6);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
wsp_ggml_metal_library_t res = calloc(1, sizeof(struct wsp_ggml_metal_library));
|
|
292
|
+
|
|
293
|
+
res->obj = library;
|
|
294
|
+
res->device = device;
|
|
295
|
+
res->pipelines = wsp_ggml_metal_pipelines_init();
|
|
296
|
+
|
|
297
|
+
return res;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
void wsp_ggml_metal_library_free(wsp_ggml_metal_library_t lib) {
|
|
301
|
+
if (!lib) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (lib->obj) {
|
|
306
|
+
[lib->obj release];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
wsp_ggml_metal_pipelines_free(lib->pipelines);
|
|
310
|
+
|
|
311
|
+
free(lib);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
wsp_ggml_metal_pipeline_t wsp_ggml_metal_library_get_pipeline(wsp_ggml_metal_library_t lib, const char * name) {
|
|
315
|
+
return wsp_ggml_metal_pipelines_get(lib->pipelines, name);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
wsp_ggml_metal_pipeline_t wsp_ggml_metal_library_compile_pipeline(wsp_ggml_metal_library_t lib, const char * base, const char * name, wsp_ggml_metal_cv_t cv) {
|
|
319
|
+
// note: the pipelines are cached in the library per device, so they are shared across all metal contexts
|
|
320
|
+
wsp_ggml_critical_section_start();
|
|
321
|
+
|
|
322
|
+
wsp_ggml_metal_pipeline_t res = wsp_ggml_metal_library_get_pipeline(lib, name);
|
|
323
|
+
if (res) {
|
|
324
|
+
wsp_ggml_critical_section_end();
|
|
325
|
+
|
|
326
|
+
return res;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
res = wsp_ggml_metal_pipeline_init();
|
|
330
|
+
|
|
331
|
+
@autoreleasepool {
|
|
332
|
+
NSError * error = nil;
|
|
333
|
+
|
|
334
|
+
NSString * base_func = [NSString stringWithUTF8String:base];
|
|
335
|
+
|
|
336
|
+
WSP_GGML_LOG_DEBUG("%s: compiling pipeline: base = '%s', name = '%s'\n", __func__, base, name);
|
|
337
|
+
|
|
338
|
+
id<MTLFunction> mtl_function;
|
|
339
|
+
if (!cv) {
|
|
340
|
+
mtl_function = [lib->obj newFunctionWithName:base_func];
|
|
341
|
+
} else {
|
|
342
|
+
mtl_function = [lib->obj newFunctionWithName:base_func constantValues:cv->obj error:&error];
|
|
343
|
+
}
|
|
344
|
+
if (!mtl_function) {
|
|
345
|
+
wsp_ggml_critical_section_end();
|
|
346
|
+
|
|
347
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to compile pipeline: base = '%s', name = '%s'\n", __func__, base, name);
|
|
348
|
+
if (error) {
|
|
349
|
+
WSP_GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return nil;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
res->obj = [lib->device newComputePipelineStateWithFunction:mtl_function error:&error];
|
|
356
|
+
|
|
357
|
+
wsp_ggml_metal_pipelines_add(lib->pipelines, name, res);
|
|
358
|
+
|
|
359
|
+
[mtl_function release];
|
|
360
|
+
|
|
361
|
+
WSP_GGML_LOG_DEBUG("%s: loaded %-40s %16p | th_max = %4d | th_width = %4d\n", __func__, name, (void *) res->obj,
|
|
362
|
+
(int) res->obj.maxTotalThreadsPerThreadgroup,
|
|
363
|
+
(int) res->obj.threadExecutionWidth);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
wsp_ggml_critical_section_end();
|
|
367
|
+
|
|
368
|
+
return res;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
//
|
|
372
|
+
// MTLComputeCommandEncoder wrapper
|
|
373
|
+
//
|
|
374
|
+
|
|
375
|
+
struct wsp_ggml_metal_encoder {
|
|
376
|
+
id<MTLComputeCommandEncoder> obj;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
wsp_ggml_metal_encoder_t wsp_ggml_metal_encoder_init(wsp_ggml_metal_cmd_buf_t cmd_buf_raw, bool concurrent) {
|
|
380
|
+
wsp_ggml_metal_encoder_t res = calloc(1, sizeof(struct wsp_ggml_metal_encoder));
|
|
381
|
+
|
|
382
|
+
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>) cmd_buf_raw;
|
|
383
|
+
|
|
384
|
+
if (concurrent) {
|
|
385
|
+
res->obj = [cmd_buf computeCommandEncoderWithDispatchType: MTLDispatchTypeConcurrent];
|
|
386
|
+
} else {
|
|
387
|
+
res->obj = [cmd_buf computeCommandEncoder];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
[res->obj retain];
|
|
391
|
+
|
|
392
|
+
return res;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
void wsp_ggml_metal_encoder_free(wsp_ggml_metal_encoder_t encoder) {
|
|
396
|
+
[encoder->obj release];
|
|
397
|
+
free(encoder);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
void wsp_ggml_metal_encoder_debug_group_push(wsp_ggml_metal_encoder_t encoder, const char * name) {
|
|
401
|
+
[encoder->obj pushDebugGroup:[NSString stringWithCString:name encoding:NSUTF8StringEncoding]];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
void wsp_ggml_metal_encoder_debug_group_pop (wsp_ggml_metal_encoder_t encoder) {
|
|
405
|
+
[encoder->obj popDebugGroup];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
void wsp_ggml_metal_encoder_set_pipeline(wsp_ggml_metal_encoder_t encoder, wsp_ggml_metal_pipeline_t pipeline) {
|
|
409
|
+
[encoder->obj setComputePipelineState:pipeline->obj];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
void wsp_ggml_metal_encoder_set_bytes(wsp_ggml_metal_encoder_t encoder, void * data, size_t size, int idx) {
|
|
413
|
+
[encoder->obj setBytes:data length:size atIndex:idx];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
void wsp_ggml_metal_encoder_set_buffer(wsp_ggml_metal_encoder_t encoder, struct wsp_ggml_metal_buffer_id buffer, int idx) {
|
|
417
|
+
[encoder->obj setBuffer:buffer.metal offset:buffer.offs atIndex:idx];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
void wsp_ggml_metal_encoder_set_threadgroup_memory_size(wsp_ggml_metal_encoder_t encoder, size_t size, int idx) {
|
|
421
|
+
[encoder->obj setThreadgroupMemoryLength:size atIndex:idx];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
void wsp_ggml_metal_encoder_dispatch_threadgroups(wsp_ggml_metal_encoder_t encoder, int tg0, int tg1, int tg2, int tptg0, int tptg1, int tptg2) {
|
|
425
|
+
[encoder->obj dispatchThreadgroups:MTLSizeMake(tg0, tg1, tg2) threadsPerThreadgroup:MTLSizeMake(tptg0, tptg1, tptg2)];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
void wsp_ggml_metal_encoder_memory_barrier(wsp_ggml_metal_encoder_t encoder) {
|
|
429
|
+
[encoder->obj memoryBarrierWithScope:MTLBarrierScopeBuffers];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
void wsp_ggml_metal_encoder_end_encoding(wsp_ggml_metal_encoder_t encoder) {
|
|
433
|
+
[encoder->obj endEncoding];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
struct wsp_ggml_metal_device {
|
|
437
|
+
id<MTLDevice> mtl_device;
|
|
438
|
+
|
|
439
|
+
// a single global queue shared by all Metal backends
|
|
440
|
+
// technically not needed for devices with unified memory, but enables discrete GPUs support
|
|
441
|
+
// ref: https://github.com/ggml-org/llama.cpp/pull/15906
|
|
442
|
+
id<MTLCommandQueue> mtl_queue;
|
|
443
|
+
|
|
444
|
+
wsp_ggml_metal_library_t library;
|
|
445
|
+
|
|
446
|
+
struct wsp_ggml_metal_device_props props;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
wsp_ggml_metal_device_t wsp_ggml_metal_device_init(void) {
|
|
450
|
+
wsp_ggml_metal_device_t dev = calloc(1, sizeof(struct wsp_ggml_metal_device));
|
|
451
|
+
|
|
452
|
+
assert(dev != NULL);
|
|
453
|
+
|
|
454
|
+
if (dev->mtl_device == nil) {
|
|
455
|
+
dev->mtl_device = MTLCreateSystemDefaultDevice();
|
|
456
|
+
|
|
457
|
+
if (dev->mtl_device) {
|
|
458
|
+
dev->mtl_queue = [dev->mtl_device newCommandQueue];
|
|
459
|
+
if (dev->mtl_queue == nil) {
|
|
460
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to create command queue\n", __func__);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
dev->props.has_simdgroup_reduction = [dev->mtl_device supportsFamily:MTLGPUFamilyApple7];
|
|
464
|
+
dev->props.has_simdgroup_reduction |= [dev->mtl_device supportsFamily:MTLGPUFamilyMetal3_GGML];
|
|
465
|
+
|
|
466
|
+
dev->props.has_simdgroup_mm = [dev->mtl_device supportsFamily:MTLGPUFamilyApple7];
|
|
467
|
+
dev->props.has_unified_memory = dev->mtl_device.hasUnifiedMemory;
|
|
468
|
+
|
|
469
|
+
dev->props.has_bfloat = [dev->mtl_device supportsFamily:MTLGPUFamilyMetal3_GGML];
|
|
470
|
+
dev->props.has_bfloat |= [dev->mtl_device supportsFamily:MTLGPUFamilyApple6];
|
|
471
|
+
|
|
472
|
+
dev->props.use_residency_sets = true;
|
|
473
|
+
#if defined(WSP_GGML_METAL_HAS_RESIDENCY_SETS)
|
|
474
|
+
dev->props.use_residency_sets = getenv("WSP_GGML_METAL_NO_RESIDENCY") == nil;
|
|
475
|
+
#endif
|
|
476
|
+
|
|
477
|
+
dev->props.use_shared_buffers = dev->props.has_unified_memory;
|
|
478
|
+
|
|
479
|
+
if (getenv("WSP_GGML_METAL_SHARED_BUFFERS_DISABLE") != NULL) {
|
|
480
|
+
dev->props.use_shared_buffers = false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
dev->props.supports_gpu_family_apple7 = [dev->mtl_device supportsFamily:MTLGPUFamilyApple7];
|
|
484
|
+
|
|
485
|
+
dev->props.max_buffer_size = dev->mtl_device.maxBufferLength;
|
|
486
|
+
dev->props.max_working_set_size = dev->mtl_device.recommendedMaxWorkingSetSize;
|
|
487
|
+
dev->props.max_theadgroup_memory_size = dev->mtl_device.maxThreadgroupMemoryLength;
|
|
488
|
+
|
|
489
|
+
strncpy(dev->props.name, [[dev->mtl_device name] UTF8String], sizeof(dev->props.name) - 1);
|
|
490
|
+
|
|
491
|
+
dev->library = wsp_ggml_metal_library_init(dev);
|
|
492
|
+
if (!dev->library) {
|
|
493
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to create library\n", __func__);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// --------------------------------------------------
|
|
497
|
+
|
|
498
|
+
// print MTL GPU family:
|
|
499
|
+
WSP_GGML_LOG_INFO("%s: GPU name: %s\n", __func__, dev->props.name);
|
|
500
|
+
|
|
501
|
+
// determine max supported GPU family
|
|
502
|
+
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
|
|
503
|
+
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
|
504
|
+
{
|
|
505
|
+
for (int i = MTLGPUFamilyApple1 + 20; i >= MTLGPUFamilyApple1; --i) {
|
|
506
|
+
if ([dev->mtl_device supportsFamily:i]) {
|
|
507
|
+
WSP_GGML_LOG_INFO("%s: GPU family: MTLGPUFamilyApple%d (%d)\n", __func__, i - (int) MTLGPUFamilyApple1 + 1, i);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
for (int i = MTLGPUFamilyCommon1 + 5; i >= MTLGPUFamilyCommon1; --i) {
|
|
513
|
+
if ([dev->mtl_device supportsFamily:i]) {
|
|
514
|
+
WSP_GGML_LOG_INFO("%s: GPU family: MTLGPUFamilyCommon%d (%d)\n", __func__, i - (int) MTLGPUFamilyCommon1 + 1, i);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
for (int i = MTLGPUFamilyMetal3_GGML + 5; i >= MTLGPUFamilyMetal3_GGML; --i) {
|
|
520
|
+
if ([dev->mtl_device supportsFamily:i]) {
|
|
521
|
+
WSP_GGML_LOG_INFO("%s: GPU family: MTLGPUFamilyMetal%d (%d)\n", __func__, i - (int) MTLGPUFamilyMetal3_GGML + 3, i);
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
WSP_GGML_LOG_INFO("%s: simdgroup reduction = %s\n", __func__, dev->props.has_simdgroup_reduction ? "true" : "false");
|
|
528
|
+
WSP_GGML_LOG_INFO("%s: simdgroup matrix mul. = %s\n", __func__, dev->props.has_simdgroup_mm ? "true" : "false");
|
|
529
|
+
WSP_GGML_LOG_INFO("%s: has unified memory = %s\n", __func__, dev->props.has_unified_memory ? "true" : "false");
|
|
530
|
+
WSP_GGML_LOG_INFO("%s: has bfloat = %s\n", __func__, dev->props.has_bfloat ? "true" : "false");
|
|
531
|
+
WSP_GGML_LOG_INFO("%s: use residency sets = %s\n", __func__, dev->props.use_residency_sets ? "true" : "false");
|
|
532
|
+
WSP_GGML_LOG_INFO("%s: use shared buffers = %s\n", __func__, dev->props.use_shared_buffers ? "true" : "false");
|
|
533
|
+
|
|
534
|
+
#if TARGET_OS_OSX || (TARGET_OS_IOS && __clang_major__ >= 15)
|
|
535
|
+
if (@available(macOS 10.12, iOS 16.0, *)) {
|
|
536
|
+
WSP_GGML_LOG_INFO("%s: recommendedMaxWorkingSetSize = %8.2f MB\n", __func__, dev->props.max_working_set_size / 1e6);
|
|
537
|
+
}
|
|
538
|
+
#endif
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return dev;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
void wsp_ggml_metal_device_free(wsp_ggml_metal_device_t dev) {
|
|
546
|
+
assert(dev != NULL);
|
|
547
|
+
|
|
548
|
+
wsp_ggml_metal_library_free(dev->library);
|
|
549
|
+
dev->library = NULL;
|
|
550
|
+
|
|
551
|
+
if (dev->mtl_queue) {
|
|
552
|
+
[dev->mtl_queue release];
|
|
553
|
+
dev->mtl_queue = nil;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (dev->mtl_device) {
|
|
557
|
+
[dev->mtl_device release];
|
|
558
|
+
dev->mtl_device = nil;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
free(dev);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
void * wsp_ggml_metal_device_get_obj(wsp_ggml_metal_device_t dev) {
|
|
565
|
+
return dev->mtl_device;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
void * wsp_ggml_metal_device_get_queue(wsp_ggml_metal_device_t dev) {
|
|
569
|
+
return dev->mtl_queue;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
wsp_ggml_metal_library_t wsp_ggml_metal_device_get_library(wsp_ggml_metal_device_t dev) {
|
|
573
|
+
return dev->library;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
void wsp_ggml_metal_device_get_memory(wsp_ggml_metal_device_t dev, size_t * free, size_t * total) {
|
|
577
|
+
if (@available(macOS 10.12, iOS 16.0, *)) {
|
|
578
|
+
*total = dev->mtl_device.recommendedMaxWorkingSetSize;
|
|
579
|
+
*free = *total - dev->mtl_device.currentAllocatedSize;
|
|
580
|
+
} else {
|
|
581
|
+
*free = 0;
|
|
582
|
+
*total = 0;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
bool wsp_ggml_metal_device_supports_op(wsp_ggml_metal_device_t dev, const struct wsp_ggml_tensor * op) {
|
|
587
|
+
const bool has_simdgroup_mm = dev->props.has_simdgroup_mm;
|
|
588
|
+
const bool has_simdgroup_reduction = dev->props.has_simdgroup_reduction;
|
|
589
|
+
const bool has_bfloat = dev->props.has_bfloat;
|
|
590
|
+
|
|
591
|
+
if (!has_bfloat) {
|
|
592
|
+
if (op->type == WSP_GGML_TYPE_BF16) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
for (size_t i = 0, n = 3; i < n; ++i) {
|
|
597
|
+
if (op->src[i] != NULL && op->src[i]->type == WSP_GGML_TYPE_BF16) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
switch (op->op) {
|
|
604
|
+
case WSP_GGML_OP_UNARY:
|
|
605
|
+
switch (wsp_ggml_get_unary_op(op)) {
|
|
606
|
+
case WSP_GGML_UNARY_OP_TANH:
|
|
607
|
+
case WSP_GGML_UNARY_OP_RELU:
|
|
608
|
+
case WSP_GGML_UNARY_OP_SIGMOID:
|
|
609
|
+
case WSP_GGML_UNARY_OP_GELU:
|
|
610
|
+
case WSP_GGML_UNARY_OP_GELU_ERF:
|
|
611
|
+
case WSP_GGML_UNARY_OP_GELU_QUICK:
|
|
612
|
+
case WSP_GGML_UNARY_OP_SILU:
|
|
613
|
+
case WSP_GGML_UNARY_OP_ELU:
|
|
614
|
+
case WSP_GGML_UNARY_OP_NEG:
|
|
615
|
+
case WSP_GGML_UNARY_OP_ABS:
|
|
616
|
+
case WSP_GGML_UNARY_OP_SGN:
|
|
617
|
+
case WSP_GGML_UNARY_OP_STEP:
|
|
618
|
+
case WSP_GGML_UNARY_OP_HARDSWISH:
|
|
619
|
+
case WSP_GGML_UNARY_OP_HARDSIGMOID:
|
|
620
|
+
case WSP_GGML_UNARY_OP_EXP:
|
|
621
|
+
return wsp_ggml_is_contiguous(op->src[0]) && op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
622
|
+
default:
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
case WSP_GGML_OP_GLU:
|
|
626
|
+
switch (wsp_ggml_get_glu_op(op)) {
|
|
627
|
+
case WSP_GGML_GLU_OP_REGLU:
|
|
628
|
+
case WSP_GGML_GLU_OP_GEGLU:
|
|
629
|
+
case WSP_GGML_GLU_OP_SWIGLU:
|
|
630
|
+
case WSP_GGML_GLU_OP_SWIGLU_OAI:
|
|
631
|
+
case WSP_GGML_GLU_OP_GEGLU_ERF:
|
|
632
|
+
case WSP_GGML_GLU_OP_GEGLU_QUICK:
|
|
633
|
+
return wsp_ggml_is_contiguous_1(op->src[0]) && op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
634
|
+
default:
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
case WSP_GGML_OP_NONE:
|
|
638
|
+
case WSP_GGML_OP_RESHAPE:
|
|
639
|
+
case WSP_GGML_OP_VIEW:
|
|
640
|
+
case WSP_GGML_OP_TRANSPOSE:
|
|
641
|
+
case WSP_GGML_OP_PERMUTE:
|
|
642
|
+
case WSP_GGML_OP_CONCAT:
|
|
643
|
+
return true;
|
|
644
|
+
case WSP_GGML_OP_ADD:
|
|
645
|
+
case WSP_GGML_OP_SUB:
|
|
646
|
+
case WSP_GGML_OP_MUL:
|
|
647
|
+
case WSP_GGML_OP_DIV:
|
|
648
|
+
case WSP_GGML_OP_ADD_ID:
|
|
649
|
+
return op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
650
|
+
case WSP_GGML_OP_ACC:
|
|
651
|
+
case WSP_GGML_OP_REPEAT:
|
|
652
|
+
case WSP_GGML_OP_SCALE:
|
|
653
|
+
case WSP_GGML_OP_CONV_TRANSPOSE_1D:
|
|
654
|
+
return true;
|
|
655
|
+
case WSP_GGML_OP_CLAMP:
|
|
656
|
+
return op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
657
|
+
case WSP_GGML_OP_SQR:
|
|
658
|
+
case WSP_GGML_OP_SQRT:
|
|
659
|
+
case WSP_GGML_OP_SIN:
|
|
660
|
+
case WSP_GGML_OP_COS:
|
|
661
|
+
case WSP_GGML_OP_LOG:
|
|
662
|
+
return wsp_ggml_is_contiguous(op->src[0]) && op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
663
|
+
case WSP_GGML_OP_SUM_ROWS:
|
|
664
|
+
case WSP_GGML_OP_MEAN:
|
|
665
|
+
case WSP_GGML_OP_SOFT_MAX:
|
|
666
|
+
case WSP_GGML_OP_GROUP_NORM:
|
|
667
|
+
return has_simdgroup_reduction && wsp_ggml_is_contiguous_rows(op->src[0]);
|
|
668
|
+
case WSP_GGML_OP_L2_NORM:
|
|
669
|
+
return has_simdgroup_reduction && (op->ne[0] % 4 == 0 && wsp_ggml_is_contiguous_1(op->src[0]));
|
|
670
|
+
case WSP_GGML_OP_ARGMAX:
|
|
671
|
+
return has_simdgroup_reduction;
|
|
672
|
+
case WSP_GGML_OP_NORM:
|
|
673
|
+
case WSP_GGML_OP_RMS_NORM:
|
|
674
|
+
return has_simdgroup_reduction && (wsp_ggml_is_contiguous_rows(op->src[0]));
|
|
675
|
+
case WSP_GGML_OP_ROPE:
|
|
676
|
+
return true;
|
|
677
|
+
case WSP_GGML_OP_IM2COL:
|
|
678
|
+
return wsp_ggml_is_contiguous(op->src[1]) && op->src[1]->type == WSP_GGML_TYPE_F32 && (op->type == WSP_GGML_TYPE_F16 || op->type == WSP_GGML_TYPE_F32);
|
|
679
|
+
case WSP_GGML_OP_POOL_1D:
|
|
680
|
+
return false;
|
|
681
|
+
case WSP_GGML_OP_UPSCALE:
|
|
682
|
+
return op->src[0]->type == WSP_GGML_TYPE_F32 && op->op_params[0] == WSP_GGML_SCALE_MODE_NEAREST;
|
|
683
|
+
case WSP_GGML_OP_POOL_2D:
|
|
684
|
+
return op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
685
|
+
case WSP_GGML_OP_PAD:
|
|
686
|
+
return (wsp_ggml_get_op_params_i32(op, 0) == 0) && (wsp_ggml_get_op_params_i32(op, 2) == 0) &&
|
|
687
|
+
(wsp_ggml_get_op_params_i32(op, 4) == 0) && (wsp_ggml_get_op_params_i32(op, 6) == 0);
|
|
688
|
+
case WSP_GGML_OP_PAD_REFLECT_1D:
|
|
689
|
+
case WSP_GGML_OP_TIMESTEP_EMBEDDING:
|
|
690
|
+
case WSP_GGML_OP_LEAKY_RELU:
|
|
691
|
+
return op->src[0]->type == WSP_GGML_TYPE_F32;
|
|
692
|
+
case WSP_GGML_OP_ARGSORT:
|
|
693
|
+
// TODO: Support arbitrary column width
|
|
694
|
+
return op->src[0]->ne[0] <= 1024;
|
|
695
|
+
case WSP_GGML_OP_ARANGE:
|
|
696
|
+
return true;
|
|
697
|
+
case WSP_GGML_OP_FLASH_ATTN_EXT:
|
|
698
|
+
// for new head sizes, add checks here
|
|
699
|
+
if (op->src[0]->ne[0] != 40 &&
|
|
700
|
+
op->src[0]->ne[0] != 64 &&
|
|
701
|
+
op->src[0]->ne[0] != 80 &&
|
|
702
|
+
op->src[0]->ne[0] != 96 &&
|
|
703
|
+
op->src[0]->ne[0] != 112 &&
|
|
704
|
+
op->src[0]->ne[0] != 128 &&
|
|
705
|
+
op->src[0]->ne[0] != 192 &&
|
|
706
|
+
op->src[0]->ne[0] != 256) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
if (op->src[0]->ne[0] == 576) {
|
|
710
|
+
// DeepSeek sizes
|
|
711
|
+
// TODO: disabled for now, until optmized
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
if (op->src[1]->type != op->src[2]->type) {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
return has_simdgroup_mm; // TODO: over-restricted for vec-kernels
|
|
718
|
+
case WSP_GGML_OP_SSM_CONV:
|
|
719
|
+
case WSP_GGML_OP_SSM_SCAN:
|
|
720
|
+
return has_simdgroup_reduction;
|
|
721
|
+
case WSP_GGML_OP_RWKV_WKV6:
|
|
722
|
+
case WSP_GGML_OP_RWKV_WKV7:
|
|
723
|
+
return true;
|
|
724
|
+
case WSP_GGML_OP_MUL_MAT:
|
|
725
|
+
case WSP_GGML_OP_MUL_MAT_ID:
|
|
726
|
+
return has_simdgroup_reduction;
|
|
727
|
+
case WSP_GGML_OP_CPY:
|
|
728
|
+
case WSP_GGML_OP_DUP:
|
|
729
|
+
case WSP_GGML_OP_CONT:
|
|
730
|
+
{
|
|
731
|
+
switch (op->src[0]->type) {
|
|
732
|
+
case WSP_GGML_TYPE_F32:
|
|
733
|
+
switch (op->type) {
|
|
734
|
+
case WSP_GGML_TYPE_F32:
|
|
735
|
+
case WSP_GGML_TYPE_F16:
|
|
736
|
+
case WSP_GGML_TYPE_BF16:
|
|
737
|
+
case WSP_GGML_TYPE_Q8_0:
|
|
738
|
+
case WSP_GGML_TYPE_Q4_0:
|
|
739
|
+
case WSP_GGML_TYPE_Q4_1:
|
|
740
|
+
case WSP_GGML_TYPE_Q5_0:
|
|
741
|
+
case WSP_GGML_TYPE_Q5_1:
|
|
742
|
+
case WSP_GGML_TYPE_IQ4_NL:
|
|
743
|
+
case WSP_GGML_TYPE_I32:
|
|
744
|
+
return true;
|
|
745
|
+
default:
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
case WSP_GGML_TYPE_F16:
|
|
749
|
+
switch (op->type) {
|
|
750
|
+
case WSP_GGML_TYPE_F32:
|
|
751
|
+
case WSP_GGML_TYPE_F16:
|
|
752
|
+
return true;
|
|
753
|
+
default:
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
case WSP_GGML_TYPE_BF16:
|
|
757
|
+
switch (op->type) {
|
|
758
|
+
case WSP_GGML_TYPE_F32:
|
|
759
|
+
case WSP_GGML_TYPE_BF16:
|
|
760
|
+
return true;
|
|
761
|
+
default:
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
case WSP_GGML_TYPE_Q4_0:
|
|
765
|
+
case WSP_GGML_TYPE_Q4_1:
|
|
766
|
+
case WSP_GGML_TYPE_Q5_0:
|
|
767
|
+
case WSP_GGML_TYPE_Q5_1:
|
|
768
|
+
case WSP_GGML_TYPE_Q8_0:
|
|
769
|
+
switch (op->type) {
|
|
770
|
+
case WSP_GGML_TYPE_F32:
|
|
771
|
+
case WSP_GGML_TYPE_F16:
|
|
772
|
+
return true;
|
|
773
|
+
default:
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
case WSP_GGML_TYPE_I32:
|
|
777
|
+
return op->type == WSP_GGML_TYPE_F32;
|
|
778
|
+
default:
|
|
779
|
+
return false;
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
case WSP_GGML_OP_GET_ROWS:
|
|
783
|
+
{
|
|
784
|
+
return op->ne[3] == 1;
|
|
785
|
+
}
|
|
786
|
+
case WSP_GGML_OP_SET_ROWS:
|
|
787
|
+
{
|
|
788
|
+
if (op->src[0]->type != WSP_GGML_TYPE_F32) {
|
|
789
|
+
return false;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
switch (op->type) {
|
|
793
|
+
case WSP_GGML_TYPE_F32:
|
|
794
|
+
case WSP_GGML_TYPE_F16:
|
|
795
|
+
case WSP_GGML_TYPE_BF16:
|
|
796
|
+
case WSP_GGML_TYPE_Q8_0:
|
|
797
|
+
case WSP_GGML_TYPE_Q4_0:
|
|
798
|
+
case WSP_GGML_TYPE_Q4_1:
|
|
799
|
+
case WSP_GGML_TYPE_Q5_0:
|
|
800
|
+
case WSP_GGML_TYPE_Q5_1:
|
|
801
|
+
case WSP_GGML_TYPE_IQ4_NL:
|
|
802
|
+
return true;
|
|
803
|
+
default:
|
|
804
|
+
return false;
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
default:
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const struct wsp_ggml_metal_device_props * wsp_ggml_metal_device_get_props(wsp_ggml_metal_device_t dev) {
|
|
813
|
+
return &dev->props;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
//
|
|
817
|
+
// device buffers
|
|
818
|
+
//
|
|
819
|
+
|
|
820
|
+
// max memory buffers that can be mapped to the device
|
|
821
|
+
#define WSP_GGML_METAL_MAX_BUFFERS 64
|
|
822
|
+
|
|
823
|
+
struct wsp_ggml_metal_buffer_wrapper {
|
|
824
|
+
void * data;
|
|
825
|
+
size_t size;
|
|
826
|
+
|
|
827
|
+
id<MTLBuffer> metal;
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
struct wsp_ggml_metal_buffer {
|
|
831
|
+
void * all_data; // TODO: https://github.com/ggml-org/llama.cpp/pull/15985
|
|
832
|
+
size_t all_size;
|
|
833
|
+
|
|
834
|
+
// if false, the Metal buffer data is allocated in private GPU memory and is not shared with the host
|
|
835
|
+
bool is_shared;
|
|
836
|
+
bool owned;
|
|
837
|
+
|
|
838
|
+
// multiple buffers are used only to avoid the maximum buffer size limitation when using mmap
|
|
839
|
+
int n_buffers;
|
|
840
|
+
struct wsp_ggml_metal_buffer_wrapper buffers[WSP_GGML_METAL_MAX_BUFFERS];
|
|
841
|
+
|
|
842
|
+
bool use_residency_sets;
|
|
843
|
+
|
|
844
|
+
// optional MTLResidencySet
|
|
845
|
+
// note: cannot use explicity "id<MTLResidencySet>" here because it is not available on certain OSes
|
|
846
|
+
id rset;
|
|
847
|
+
|
|
848
|
+
// pointers to global device objects
|
|
849
|
+
id<MTLDevice> device;
|
|
850
|
+
id<MTLCommandQueue> queue;
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
static void wsp_ggml_metal_log_allocated_size(id<MTLDevice> device, size_t size_aligned) {
|
|
854
|
+
#ifndef WSP_GGML_METAL_NDEBUG
|
|
855
|
+
#if TARGET_OS_OSX || (TARGET_OS_IOS && __clang_major__ >= 15)
|
|
856
|
+
if (@available(macOS 10.12, iOS 16.0, *)) {
|
|
857
|
+
WSP_GGML_LOG_DEBUG("%s: allocated buffer, size = %8.2f MiB, (%8.2f / %8.2f)\n",
|
|
858
|
+
__func__,
|
|
859
|
+
size_aligned / 1024.0 / 1024.0,
|
|
860
|
+
device.currentAllocatedSize / 1024.0 / 1024.0,
|
|
861
|
+
device.recommendedMaxWorkingSetSize / 1024.0 / 1024.0);
|
|
862
|
+
|
|
863
|
+
if (device.currentAllocatedSize > device.recommendedMaxWorkingSetSize) {
|
|
864
|
+
WSP_GGML_LOG_WARN("%s: warning: current allocated size is greater than the recommended max working set size\n", __func__);
|
|
865
|
+
}
|
|
866
|
+
} else {
|
|
867
|
+
WSP_GGML_LOG_INFO("%s: allocated buffer, size = %8.2f MiB, (%8.2f)\n",
|
|
868
|
+
__func__,
|
|
869
|
+
size_aligned / 1024.0 / 1024.0,
|
|
870
|
+
device.currentAllocatedSize / 1024.0 / 1024.0);
|
|
871
|
+
}
|
|
872
|
+
#endif
|
|
873
|
+
#endif
|
|
874
|
+
WSP_GGML_UNUSED(device);
|
|
875
|
+
WSP_GGML_UNUSED(size_aligned);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// rset init
|
|
879
|
+
static bool wsp_ggml_metal_buffer_rset_init(wsp_ggml_metal_buffer_t buf) {
|
|
880
|
+
buf->rset = nil;
|
|
881
|
+
|
|
882
|
+
if (!buf->use_residency_sets) {
|
|
883
|
+
return true;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
#if defined(WSP_GGML_METAL_HAS_RESIDENCY_SETS)
|
|
887
|
+
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, *)) {
|
|
888
|
+
MTLResidencySetDescriptor * desc = [[MTLResidencySetDescriptor alloc] init];
|
|
889
|
+
desc.label = @"wsp_ggml_metal";
|
|
890
|
+
desc.initialCapacity = buf->n_buffers;
|
|
891
|
+
|
|
892
|
+
NSError * error;
|
|
893
|
+
buf->rset = [buf->device newResidencySetWithDescriptor:desc error:&error];
|
|
894
|
+
if (error) {
|
|
895
|
+
WSP_GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
|
|
896
|
+
[desc release];
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
[desc release];
|
|
901
|
+
|
|
902
|
+
for (int i = 0; i < buf->n_buffers; i++) {
|
|
903
|
+
[buf->rset addAllocation:buf->buffers[i].metal];
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
[buf->rset commit];
|
|
907
|
+
[buf->rset requestResidency];
|
|
908
|
+
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
#endif
|
|
912
|
+
|
|
913
|
+
return true;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// rset free
|
|
917
|
+
static void wsp_ggml_metal_buffer_rset_free(wsp_ggml_metal_buffer_t buf) {
|
|
918
|
+
#if defined(WSP_GGML_METAL_HAS_RESIDENCY_SETS)
|
|
919
|
+
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, *)) {
|
|
920
|
+
if (buf->rset) {
|
|
921
|
+
[buf->rset endResidency];
|
|
922
|
+
[buf->rset removeAllAllocations];
|
|
923
|
+
[buf->rset release];
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
#else
|
|
927
|
+
WSP_GGML_UNUSED(buf);
|
|
928
|
+
#endif
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
static void * wsp_ggml_metal_host_malloc(size_t n) {
|
|
932
|
+
void * data = NULL;
|
|
933
|
+
|
|
934
|
+
#if TARGET_OS_OSX
|
|
935
|
+
kern_return_t err = vm_allocate((vm_map_t) mach_task_self(), (void *) &data, n, VM_FLAGS_ANYWHERE);
|
|
936
|
+
if (err != KERN_SUCCESS) {
|
|
937
|
+
WSP_GGML_LOG_ERROR("%s: error: vm_allocate failed\n", __func__);
|
|
938
|
+
return NULL;
|
|
939
|
+
}
|
|
940
|
+
#else
|
|
941
|
+
const int result = posix_memalign((void **) &data, sysconf(_SC_PAGESIZE), n);
|
|
942
|
+
if (result != 0) {
|
|
943
|
+
WSP_GGML_LOG_ERROR("%s: error: posix_memalign failed\n", __func__);
|
|
944
|
+
return NULL;
|
|
945
|
+
}
|
|
946
|
+
#endif
|
|
947
|
+
|
|
948
|
+
return data;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
wsp_ggml_metal_buffer_t wsp_ggml_metal_buffer_init(wsp_ggml_metal_device_t dev, size_t size, bool shared) {
|
|
952
|
+
wsp_ggml_metal_buffer_t res = calloc(1, sizeof(struct wsp_ggml_metal_buffer));
|
|
953
|
+
|
|
954
|
+
const size_t size_page = sysconf(_SC_PAGESIZE);
|
|
955
|
+
|
|
956
|
+
size_t size_aligned = size;
|
|
957
|
+
if ((size_aligned % size_page) != 0) {
|
|
958
|
+
size_aligned += (size_page - (size_aligned % size_page));
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const struct wsp_ggml_metal_device_props * props_dev = wsp_ggml_metal_device_get_props(dev);
|
|
962
|
+
|
|
963
|
+
shared = shared && props_dev->use_shared_buffers;
|
|
964
|
+
|
|
965
|
+
// allocate shared buffer if the device supports it and it is required by the buffer type
|
|
966
|
+
if (shared) {
|
|
967
|
+
res->all_data = wsp_ggml_metal_host_malloc(size_aligned);
|
|
968
|
+
res->is_shared = true;
|
|
969
|
+
res->owned = true;
|
|
970
|
+
} else {
|
|
971
|
+
// dummy, non-NULL value - we'll populate this after creating the Metal buffer below
|
|
972
|
+
res->all_data = (void *) 0x000000400ULL;
|
|
973
|
+
res->is_shared = false;
|
|
974
|
+
}
|
|
975
|
+
res->all_size = size_aligned;
|
|
976
|
+
|
|
977
|
+
res->device = wsp_ggml_metal_device_get_obj(dev);
|
|
978
|
+
res->queue = wsp_ggml_metal_device_get_queue(dev);
|
|
979
|
+
|
|
980
|
+
res->n_buffers = 1;
|
|
981
|
+
|
|
982
|
+
if (res->all_data != NULL) {
|
|
983
|
+
res->buffers[0].size = size;
|
|
984
|
+
res->buffers[0].metal = nil;
|
|
985
|
+
|
|
986
|
+
if (size_aligned > 0) {
|
|
987
|
+
if (props_dev->use_shared_buffers &&shared) {
|
|
988
|
+
res->buffers[0].metal = [res->device newBufferWithBytesNoCopy:res->all_data
|
|
989
|
+
length:size_aligned
|
|
990
|
+
options:MTLResourceStorageModeShared
|
|
991
|
+
deallocator:nil];
|
|
992
|
+
} else {
|
|
993
|
+
res->buffers[0].metal = [res->device newBufferWithLength:size_aligned options:MTLResourceStorageModePrivate];
|
|
994
|
+
|
|
995
|
+
res->all_data = (void *) (res->buffers[0].metal.gpuAddress);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
res->buffers[0].data = res->all_data;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if (size_aligned > 0 && (res->all_data == NULL || res->buffers[0].metal == nil)) {
|
|
1003
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to allocate buffer, size = %8.2f MiB\n", __func__, size_aligned / 1024.0 / 1024.0);
|
|
1004
|
+
free(res);
|
|
1005
|
+
return NULL;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
res->use_residency_sets = props_dev->use_residency_sets;
|
|
1009
|
+
|
|
1010
|
+
if (!wsp_ggml_metal_buffer_rset_init(res)) {
|
|
1011
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to initialize residency set\n", __func__);
|
|
1012
|
+
free(res);
|
|
1013
|
+
return NULL;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
//wsp_ggml_metal_log_allocated_size(device, size_aligned);
|
|
1017
|
+
|
|
1018
|
+
return res;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
wsp_ggml_metal_buffer_t wsp_ggml_metal_buffer_map(wsp_ggml_metal_device_t dev, void * ptr, size_t size, size_t max_tensor_size) {
|
|
1022
|
+
wsp_ggml_metal_buffer_t res = calloc(1, sizeof(struct wsp_ggml_metal_buffer));
|
|
1023
|
+
|
|
1024
|
+
res->all_data = ptr;
|
|
1025
|
+
res->all_size = size;
|
|
1026
|
+
|
|
1027
|
+
res->is_shared = true;
|
|
1028
|
+
res->owned = false;
|
|
1029
|
+
|
|
1030
|
+
res->n_buffers = 0;
|
|
1031
|
+
|
|
1032
|
+
const size_t size_page = sysconf(_SC_PAGESIZE);
|
|
1033
|
+
|
|
1034
|
+
// page-align the data ptr
|
|
1035
|
+
{
|
|
1036
|
+
const uintptr_t offs = (uintptr_t) ptr % size_page;
|
|
1037
|
+
ptr = (void *) ((char *) ptr - offs);
|
|
1038
|
+
size += offs;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
size_t size_aligned = size;
|
|
1042
|
+
if ((size_aligned % size_page) != 0) {
|
|
1043
|
+
size_aligned += (size_page - (size_aligned % size_page));
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
res->device = wsp_ggml_metal_device_get_obj(dev);
|
|
1047
|
+
res->queue = wsp_ggml_metal_device_get_queue(dev);
|
|
1048
|
+
|
|
1049
|
+
const struct wsp_ggml_metal_device_props * props_dev = wsp_ggml_metal_device_get_props(dev);
|
|
1050
|
+
|
|
1051
|
+
// the buffer fits into the max buffer size allowed by the device
|
|
1052
|
+
if (size_aligned <= props_dev->max_buffer_size) {
|
|
1053
|
+
res->buffers[res->n_buffers].data = ptr;
|
|
1054
|
+
res->buffers[res->n_buffers].size = size;
|
|
1055
|
+
res->buffers[res->n_buffers].metal = nil;
|
|
1056
|
+
|
|
1057
|
+
if (size_aligned > 0) {
|
|
1058
|
+
res->buffers[res->n_buffers].metal = [res->device newBufferWithBytesNoCopy:ptr length:size_aligned options:MTLResourceStorageModeShared deallocator:nil];
|
|
1059
|
+
|
|
1060
|
+
if (res->buffers[res->n_buffers].metal == nil) {
|
|
1061
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to allocate buffer, size = %8.2f MiB\n", __func__, size_aligned / 1024.0 / 1024.0);
|
|
1062
|
+
free(res);
|
|
1063
|
+
return NULL;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
wsp_ggml_metal_log_allocated_size(res->device, size_aligned);
|
|
1068
|
+
|
|
1069
|
+
++res->n_buffers;
|
|
1070
|
+
} else {
|
|
1071
|
+
// this overlap between the views will guarantee that the tensor with the maximum size will fully fit into
|
|
1072
|
+
// one of the views
|
|
1073
|
+
const size_t size_ovlp = ((max_tensor_size + size_page - 1) / size_page + 1) * size_page; // round-up 2 pages just in case
|
|
1074
|
+
const size_t size_step = props_dev->max_buffer_size - size_ovlp;
|
|
1075
|
+
const size_t size_view = props_dev->max_buffer_size;
|
|
1076
|
+
|
|
1077
|
+
for (size_t i = 0; i < size; i += size_step) {
|
|
1078
|
+
const size_t size_step_aligned = (i + size_view <= size) ? size_view : (size_aligned - i);
|
|
1079
|
+
|
|
1080
|
+
res->buffers[res->n_buffers].data = (void *) ((uint8_t *) ptr + i);
|
|
1081
|
+
res->buffers[res->n_buffers].size = size_step_aligned;
|
|
1082
|
+
res->buffers[res->n_buffers].metal = nil;
|
|
1083
|
+
|
|
1084
|
+
if (size_step_aligned > 0) {
|
|
1085
|
+
res->buffers[res->n_buffers].metal = [res->device newBufferWithBytesNoCopy:(void *) ((uint8_t *) ptr + i) length:size_step_aligned options:MTLResourceStorageModeShared deallocator:nil];
|
|
1086
|
+
|
|
1087
|
+
if (res->buffers[res->n_buffers].metal == nil) {
|
|
1088
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to allocate buffer, size = %8.2f MiB\n", __func__, size_step_aligned / 1024.0 / 1024.0);
|
|
1089
|
+
free(res);
|
|
1090
|
+
return NULL;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
wsp_ggml_metal_log_allocated_size(res->device, size_step_aligned);
|
|
1095
|
+
|
|
1096
|
+
if (i + size_step < size) {
|
|
1097
|
+
WSP_GGML_LOG_INFO("\n");
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
++res->n_buffers;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
res->use_residency_sets = props_dev->use_residency_sets;
|
|
1105
|
+
|
|
1106
|
+
if (!wsp_ggml_metal_buffer_rset_init(res)) {
|
|
1107
|
+
WSP_GGML_LOG_ERROR("%s: error: failed to initialize residency set\n", __func__);
|
|
1108
|
+
free(res);
|
|
1109
|
+
return NULL;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return res;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
void wsp_ggml_metal_buffer_free(wsp_ggml_metal_buffer_t buf) {
|
|
1116
|
+
for (int i = 0; i < buf->n_buffers; i++) {
|
|
1117
|
+
[buf->buffers[i].metal release];
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
wsp_ggml_metal_buffer_rset_free(buf);
|
|
1121
|
+
|
|
1122
|
+
if (buf->is_shared && buf->owned) {
|
|
1123
|
+
#if TARGET_OS_OSX
|
|
1124
|
+
vm_deallocate((vm_map_t)mach_task_self(), (vm_address_t)buf->all_data, buf->all_size);
|
|
1125
|
+
#else
|
|
1126
|
+
free(buf->all_data);
|
|
1127
|
+
#endif
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
free(buf);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
void * wsp_ggml_metal_buffer_get_base(wsp_ggml_metal_buffer_t buf) {
|
|
1134
|
+
return buf->all_data;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
bool wsp_ggml_metal_buffer_is_shared(wsp_ggml_metal_buffer_t buf) {
|
|
1138
|
+
return buf->is_shared;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
void wsp_ggml_metal_buffer_memset_tensor(wsp_ggml_metal_buffer_t buf, struct wsp_ggml_tensor * tensor, uint8_t value, size_t offset, size_t size) {
|
|
1142
|
+
if (buf->is_shared) {
|
|
1143
|
+
memset((char *)tensor->data + offset, value, size);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
@autoreleasepool {
|
|
1148
|
+
// dst
|
|
1149
|
+
struct wsp_ggml_metal_buffer_id bid_dst = wsp_ggml_metal_buffer_get_id(buf, tensor);
|
|
1150
|
+
bid_dst.offs += offset;
|
|
1151
|
+
|
|
1152
|
+
id<MTLCommandQueue> queue = buf->queue;
|
|
1153
|
+
id<MTLCommandBuffer> cmd_buf = [queue commandBufferWithUnretainedReferences];
|
|
1154
|
+
|
|
1155
|
+
{
|
|
1156
|
+
id<MTLBlitCommandEncoder> encoder = [cmd_buf blitCommandEncoder];
|
|
1157
|
+
|
|
1158
|
+
[encoder fillBuffer:bid_dst.metal
|
|
1159
|
+
range:NSMakeRange(bid_dst.offs, bid_dst.offs + size)
|
|
1160
|
+
value:value];
|
|
1161
|
+
|
|
1162
|
+
[encoder endEncoding];
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
[cmd_buf commit];
|
|
1166
|
+
[cmd_buf waitUntilCompleted];
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
void wsp_ggml_metal_buffer_set_tensor(wsp_ggml_metal_buffer_t buf, struct wsp_ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
|
|
1171
|
+
if (buf->is_shared) {
|
|
1172
|
+
memcpy((char *)tensor->data + offset, data, size);
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
@autoreleasepool {
|
|
1177
|
+
// src
|
|
1178
|
+
void * data_ptr = (void *)(uintptr_t) data; // "const cast" the src data
|
|
1179
|
+
id<MTLBuffer> buf_src = [buf->device newBufferWithBytesNoCopy:data_ptr
|
|
1180
|
+
length:size
|
|
1181
|
+
options:MTLResourceStorageModeShared
|
|
1182
|
+
deallocator:nil];
|
|
1183
|
+
|
|
1184
|
+
WSP_GGML_ASSERT(buf_src);
|
|
1185
|
+
|
|
1186
|
+
// dst
|
|
1187
|
+
struct wsp_ggml_metal_buffer_id bid_dst = wsp_ggml_metal_buffer_get_id(buf, tensor);
|
|
1188
|
+
bid_dst.offs += offset;
|
|
1189
|
+
|
|
1190
|
+
// note: for experimentation purposes, here we use a semaphore to wait for the copy to complete
|
|
1191
|
+
// this is alternative to waitUntilCompleted, which should be faster, but don't seem to make much difference
|
|
1192
|
+
dispatch_semaphore_t completion_semaphore = dispatch_semaphore_create(0);
|
|
1193
|
+
|
|
1194
|
+
id<MTLCommandQueue> queue = buf->queue;
|
|
1195
|
+
id<MTLCommandBuffer> cmd_buf = [queue commandBufferWithUnretainedReferences];
|
|
1196
|
+
|
|
1197
|
+
{
|
|
1198
|
+
id<MTLBlitCommandEncoder> encoder = [cmd_buf blitCommandEncoder];
|
|
1199
|
+
|
|
1200
|
+
[encoder copyFromBuffer:buf_src
|
|
1201
|
+
sourceOffset:0
|
|
1202
|
+
toBuffer:bid_dst.metal
|
|
1203
|
+
destinationOffset:bid_dst.offs
|
|
1204
|
+
size:size];
|
|
1205
|
+
|
|
1206
|
+
[encoder endEncoding];
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
[cmd_buf addCompletedHandler:^(id<MTLCommandBuffer> cb) {
|
|
1210
|
+
// TODO: can check for errors here
|
|
1211
|
+
WSP_GGML_UNUSED(cb);
|
|
1212
|
+
|
|
1213
|
+
dispatch_semaphore_signal(completion_semaphore);
|
|
1214
|
+
}];
|
|
1215
|
+
|
|
1216
|
+
[cmd_buf commit];
|
|
1217
|
+
|
|
1218
|
+
dispatch_semaphore_wait(completion_semaphore, DISPATCH_TIME_FOREVER);
|
|
1219
|
+
dispatch_release(completion_semaphore);
|
|
1220
|
+
|
|
1221
|
+
//[cmd_buf waitUntilCompleted];
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
void wsp_ggml_metal_buffer_get_tensor(wsp_ggml_metal_buffer_t buf, const struct wsp_ggml_tensor * tensor, void * data, size_t offset, size_t size) {
|
|
1226
|
+
if (buf->is_shared) {
|
|
1227
|
+
memcpy(data, (const char *)tensor->data + offset, size);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
@autoreleasepool {
|
|
1232
|
+
// src
|
|
1233
|
+
struct wsp_ggml_metal_buffer_id bid_src = wsp_ggml_metal_buffer_get_id(buf, tensor);
|
|
1234
|
+
bid_src.offs += offset;
|
|
1235
|
+
|
|
1236
|
+
// dst
|
|
1237
|
+
id<MTLBuffer> buf_dst = [buf->device newBufferWithBytesNoCopy:data
|
|
1238
|
+
length:size
|
|
1239
|
+
options:MTLResourceStorageModeShared
|
|
1240
|
+
deallocator:nil];
|
|
1241
|
+
|
|
1242
|
+
WSP_GGML_ASSERT(buf_dst);
|
|
1243
|
+
|
|
1244
|
+
id<MTLCommandQueue> queue = buf->queue;
|
|
1245
|
+
id<MTLCommandBuffer> cmd_buf = [queue commandBufferWithUnretainedReferences];
|
|
1246
|
+
|
|
1247
|
+
{
|
|
1248
|
+
id<MTLBlitCommandEncoder> encoder = [cmd_buf blitCommandEncoder];
|
|
1249
|
+
|
|
1250
|
+
[encoder copyFromBuffer:bid_src.metal
|
|
1251
|
+
sourceOffset:bid_src.offs
|
|
1252
|
+
toBuffer:buf_dst
|
|
1253
|
+
destinationOffset:0
|
|
1254
|
+
size:size];
|
|
1255
|
+
|
|
1256
|
+
[encoder endEncoding];
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
[cmd_buf commit];
|
|
1260
|
+
[cmd_buf waitUntilCompleted];
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
void wsp_ggml_metal_buffer_clear(wsp_ggml_metal_buffer_t buf, uint8_t value) {
|
|
1265
|
+
if (buf->is_shared) {
|
|
1266
|
+
memset(buf->all_data, value, buf->all_size);
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
@autoreleasepool {
|
|
1271
|
+
id<MTLCommandQueue> queue = buf->queue;
|
|
1272
|
+
id<MTLCommandBuffer> cmd_buf = [queue commandBufferWithUnretainedReferences];
|
|
1273
|
+
|
|
1274
|
+
{
|
|
1275
|
+
id<MTLBlitCommandEncoder> encoder = [cmd_buf blitCommandEncoder];
|
|
1276
|
+
|
|
1277
|
+
[encoder fillBuffer:buf->buffers[0].metal
|
|
1278
|
+
range:NSMakeRange(0, buf->buffers[0].size)
|
|
1279
|
+
value:value];
|
|
1280
|
+
|
|
1281
|
+
[encoder endEncoding];
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
[cmd_buf commit];
|
|
1285
|
+
[cmd_buf waitUntilCompleted];
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
struct wsp_ggml_metal_buffer_id wsp_ggml_metal_buffer_get_id(wsp_ggml_metal_buffer_t buf, const struct wsp_ggml_tensor * t) {
|
|
1290
|
+
struct wsp_ggml_metal_buffer_id res = { nil, 0 };
|
|
1291
|
+
|
|
1292
|
+
const int64_t tsize = wsp_ggml_nbytes(t);
|
|
1293
|
+
|
|
1294
|
+
// find the view that contains the tensor fully
|
|
1295
|
+
for (int i = 0; i < buf->n_buffers; ++i) {
|
|
1296
|
+
const int64_t ioffs = (int64_t) t->data - (int64_t) buf->buffers[i].data;
|
|
1297
|
+
|
|
1298
|
+
//WSP_GGML_LOG_INFO("ioffs = %10ld, tsize = %10ld, sum = %10ld, buf->buffers[%d].size = %10ld\n", ioffs, tsize, ioffs + tsize, i, buf->buffers[i].size);
|
|
1299
|
+
if (ioffs >= 0 && ioffs + tsize <= (int64_t) buf->buffers[i].size) {
|
|
1300
|
+
res.metal = buf->buffers[i].metal;
|
|
1301
|
+
res.offs = (size_t) ioffs;
|
|
1302
|
+
|
|
1303
|
+
//WSP_GGML_LOG_INFO("%s: tensor '%16s', offs = %8ld\n", __func__, t->name, *offs);
|
|
1304
|
+
|
|
1305
|
+
return res;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
WSP_GGML_LOG_ERROR("%s: error: tensor '%s' buffer is nil\n", __func__, t->name);
|
|
1310
|
+
|
|
1311
|
+
return res;
|
|
1312
|
+
}
|