static struct ggml_backend_metal_device_context {
id<MTLDevice> mtl_device;
int mtl_device_ref_count;
+ id<MTLLibrary> mtl_library;
bool has_simdgroup_reduction;
bool has_simdgroup_mm;
} g_ggml_ctx_dev_main = {
/*.mtl_device =*/ nil,
/*.mtl_device_ref_count =*/ 0,
+ /*.mtl_library =*/ nil,
/*.has_simdgroup_reduction =*/ false,
/*.has_simdgroup_mm =*/ false,
/*.has_residency_sets =*/ false,
ctx->mtl_device_ref_count--;
if (ctx->mtl_device_ref_count == 0) {
+ if (ctx->mtl_library) {
+ [ctx->mtl_library release];
+ ctx->mtl_library = nil;
+ }
+
if (ctx->mtl_device) {
[ctx->mtl_device release];
ctx->mtl_device = nil;
return data;
}
-static struct ggml_backend_metal_context * ggml_metal_init(ggml_backend_dev_t dev) {
- GGML_LOG_INFO("%s: allocating\n", __func__);
-
-#if TARGET_OS_OSX && !GGML_METAL_NDEBUG
- // Show all the Metal device instances in the system
- NSArray * devices = MTLCopyAllDevices();
- for (id<MTLDevice> device in devices) {
- GGML_LOG_INFO("%s: found device: %s\n", __func__, [[device name] UTF8String]);
- }
- [devices release]; // since it was created by a *Copy* C method
-#endif
-
- // init context
- struct ggml_backend_metal_context * ctx = calloc(1, sizeof(struct ggml_backend_metal_context));
- struct ggml_backend_metal_device_context * ctx_dev = dev->context;
-
- id<MTLDevice> device = ggml_backend_metal_device_acq(ctx_dev);
- GGML_LOG_INFO("%s: picking default device: %s\n", __func__, [[device name] UTF8String]);
-
- ctx->queue = [device newCommandQueue];
- if (ctx->queue == nil) {
- GGML_LOG_ERROR("%s: error: failed to create command queue\n", __func__);
- return NULL;
- }
-
- ctx->d_queue = dispatch_queue_create("ggml-metal", DISPATCH_QUEUE_CONCURRENT);
-
+// load library
+//
+// - first check if the library is embedded
+// - then check if the library is in the bundle
+// - if not found, load the source and compile it
+// - if that fails, return NULL
+static id<MTLLibrary> ggml_metal_load_library(id<MTLDevice> device, bool use_bfloat) {
id<MTLLibrary> metal_library = nil;
-
- // load library
- //
- // - first check if the library is embedded
- // - then check if the library is in the bundle
- // - if not found, load the source and compile it
- // - if that fails, return NULL
- {
- NSError * error = nil;
- NSString * src = nil;
+ NSError * error = nil;
+ NSString * src = nil;
#if GGML_METAL_EMBED_LIBRARY
- GGML_LOG_INFO("%s: using embedded metal library\n", __func__);
+ GGML_LOG_INFO("%s: using embedded metal library\n", __func__);
- extern const char ggml_metallib_start[];
- extern const char ggml_metallib_end[];
+ extern const char ggml_metallib_start[];
+ extern const char ggml_metallib_end[];
- src = [[NSString alloc] initWithBytes:ggml_metallib_start length:(ggml_metallib_end-ggml_metallib_start) encoding:NSUTF8StringEncoding];
+ src = [[NSString alloc] initWithBytes:ggml_metallib_start length:(ggml_metallib_end-ggml_metallib_start) encoding:NSUTF8StringEncoding];
#else
#ifdef SWIFT_PACKAGE
- NSBundle * bundle = SWIFTPM_MODULE_BUNDLE;
+ NSBundle * bundle = SWIFTPM_MODULE_BUNDLE;
#else
- NSBundle * bundle = [NSBundle bundleForClass:[GGMLMetalClass class]];
+ NSBundle * bundle = [NSBundle bundleForClass:[GGMLMetalClass class]];
#endif
- NSString * path_lib = [bundle pathForResource:@"default" ofType:@"metallib"];
- if (path_lib == nil) {
- // Try to find the resource in the directory where the current binary located.
- NSString * current_binary = [[NSProcessInfo processInfo] arguments][0];
- NSString * bin_dir = [current_binary stringByDeletingLastPathComponent];
- NSString * default_metallib_path = [NSString pathWithComponents:@[bin_dir, @"default.metallib"]];
- if ([[NSFileManager defaultManager] isReadableFileAtPath:default_metallib_path]) {
- GGML_LOG_INFO("%s: found '%s'\n", __func__, [default_metallib_path UTF8String]);
- NSDictionary * atts = [[NSFileManager defaultManager] attributesOfItemAtPath:default_metallib_path error:&error];
- if (atts && atts[NSFileType] == NSFileTypeSymbolicLink) {
- // Optionally, if this is a symlink, try to resolve it.
- default_metallib_path = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:default_metallib_path error:&error];
- if (default_metallib_path && [default_metallib_path length] > 0 && ![[default_metallib_path substringToIndex:1] isEqualToString:@"/"]) {
- // It is a relative path, adding the binary directory as directory prefix.
- default_metallib_path = [NSString pathWithComponents:@[bin_dir, default_metallib_path]];
- }
- if (!default_metallib_path || ![[NSFileManager defaultManager] isReadableFileAtPath:default_metallib_path]) {
- // Link to the resource could not be resolved.
- default_metallib_path = nil;
- } else {
- GGML_LOG_INFO("%s: symlink resolved '%s'\n", __func__, [default_metallib_path UTF8String]);
- }
+ NSString * path_lib = [bundle pathForResource:@"default" ofType:@"metallib"];
+ if (path_lib == nil) {
+ // Try to find the resource in the directory where the current binary located.
+ NSString * current_binary = [[NSProcessInfo processInfo] arguments][0];
+ NSString * bin_dir = [current_binary stringByDeletingLastPathComponent];
+ NSString * default_metallib_path = [NSString pathWithComponents:@[bin_dir, @"default.metallib"]];
+ if ([[NSFileManager defaultManager] isReadableFileAtPath:default_metallib_path]) {
+ GGML_LOG_INFO("%s: found '%s'\n", __func__, [default_metallib_path UTF8String]);
+ NSDictionary * atts = [[NSFileManager defaultManager] attributesOfItemAtPath:default_metallib_path error:&error];
+ if (atts && atts[NSFileType] == NSFileTypeSymbolicLink) {
+ // Optionally, if this is a symlink, try to resolve it.
+ default_metallib_path = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:default_metallib_path error:&error];
+ if (default_metallib_path && [default_metallib_path length] > 0 && ![[default_metallib_path substringToIndex:1] isEqualToString:@"/"]) {
+ // It is a relative path, adding the binary directory as directory prefix.
+ default_metallib_path = [NSString pathWithComponents:@[bin_dir, default_metallib_path]];
+ }
+ if (!default_metallib_path || ![[NSFileManager defaultManager] isReadableFileAtPath:default_metallib_path]) {
+ // Link to the resource could not be resolved.
+ default_metallib_path = nil;
+ } else {
+ GGML_LOG_INFO("%s: symlink resolved '%s'\n", __func__, [default_metallib_path UTF8String]);
}
- } else {
- // The resource couldn't be found in the binary's directory.
- default_metallib_path = nil;
}
- path_lib = default_metallib_path;
+ } else {
+ // The resource couldn't be found in the binary's directory.
+ default_metallib_path = nil;
}
+ path_lib = default_metallib_path;
+ }
- if (path_lib != nil) {
- // pre-compiled library found
- NSURL * libURL = [NSURL fileURLWithPath:path_lib];
- GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_lib UTF8String]);
+ if (path_lib != nil) {
+ // pre-compiled library found
+ NSURL * libURL = [NSURL fileURLWithPath:path_lib];
+ GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_lib UTF8String]);
- metal_library = [device newLibraryWithURL:libURL error:&error];
- if (error) {
- GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
- return NULL;
- }
- } else {
- GGML_LOG_INFO("%s: default.metallib not found, loading from source\n", __func__);
+ metal_library = [device newLibraryWithURL:libURL error:&error];
+ if (error) {
+ GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
+ return NULL;
+ }
+ } else {
+ GGML_LOG_INFO("%s: default.metallib not found, loading from source\n", __func__);
- NSString * path_source;
- NSString * path_resource = [[NSProcessInfo processInfo].environment objectForKey:@"GGML_METAL_PATH_RESOURCES"];
+ NSString * path_source;
+ NSString * path_resource = [[NSProcessInfo processInfo].environment objectForKey:@"GGML_METAL_PATH_RESOURCES"];
- GGML_LOG_INFO("%s: GGML_METAL_PATH_RESOURCES = %s\n", __func__, path_resource ? [path_resource UTF8String] : "nil");
+ GGML_LOG_INFO("%s: GGML_METAL_PATH_RESOURCES = %s\n", __func__, path_resource ? [path_resource UTF8String] : "nil");
- if (path_resource) {
- path_source = [path_resource stringByAppendingPathComponent:@"ggml-metal.metal"];
- } else {
- path_source = [bundle pathForResource:@"ggml-metal" ofType:@"metal"];
- }
+ if (path_resource) {
+ path_source = [path_resource stringByAppendingPathComponent:@"ggml-metal.metal"];
+ } else {
+ path_source = [bundle pathForResource:@"ggml-metal" ofType:@"metal"];
+ }
- if (path_source == nil) {
- GGML_LOG_WARN("%s: error: could not use bundle path to find ggml-metal.metal, falling back to trying cwd\n", __func__);
- path_source = @"ggml-metal.metal";
- }
+ if (path_source == nil) {
+ GGML_LOG_WARN("%s: error: could not use bundle path to find ggml-metal.metal, falling back to trying cwd\n", __func__);
+ path_source = @"ggml-metal.metal";
+ }
- GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_source UTF8String]);
+ GGML_LOG_INFO("%s: loading '%s'\n", __func__, [path_source UTF8String]);
- src = [NSString stringWithContentsOfFile:path_source encoding:NSUTF8StringEncoding error:&error];
- if (error) {
- GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
- return NULL;
- }
+ src = [NSString stringWithContentsOfFile:path_source encoding:NSUTF8StringEncoding error:&error];
+ if (error) {
+ GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
+ return NULL;
}
+ }
#endif
- if (!metal_library) {
- @autoreleasepool {
- // dictionary of preprocessor macros
- NSMutableDictionary * prep = [NSMutableDictionary dictionary];
+ if (!metal_library) {
+ @autoreleasepool {
+ // dictionary of preprocessor macros
+ NSMutableDictionary * prep = [NSMutableDictionary dictionary];
- if (ctx_dev->use_bfloat) {
- [prep setObject:@"1" forKey:@"GGML_METAL_USE_BF16"];
- }
+ if (use_bfloat) {
+ [prep setObject:@"1" forKey:@"GGML_METAL_USE_BF16"];
+ }
#if GGML_METAL_EMBED_LIBRARY
- [prep setObject:@"1" forKey:@"GGML_METAL_EMBED_LIBRARY"];
+ [prep setObject:@"1" forKey:@"GGML_METAL_EMBED_LIBRARY"];
#endif
- MTLCompileOptions * options = [MTLCompileOptions new];
- options.preprocessorMacros = prep;
+ MTLCompileOptions * options = [MTLCompileOptions new];
+ options.preprocessorMacros = prep;
- //[options setFastMathEnabled:false];
+ //[options setFastMathEnabled:false];
- metal_library = [device newLibraryWithSource:src options:options error:&error];
- if (error) {
- GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
- return NULL;
- }
+ metal_library = [device newLibraryWithSource:src options:options error:&error];
+ if (error) {
+ GGML_LOG_ERROR("%s: error: %s\n", __func__, [[error description] UTF8String]);
+ return NULL;
+ }
#if !__has_feature(objc_arc)
- [options release];
+ [options release];
#endif
- }
}
+ }
#if GGML_METAL_EMBED_LIBRARY
- [src release];
+ [src release];
#endif // GGML_METAL_EMBED_LIBRARY
+
+ return metal_library;
+}
+
+static struct ggml_backend_metal_context * ggml_metal_init(ggml_backend_dev_t dev) {
+ GGML_LOG_INFO("%s: allocating\n", __func__);
+
+#if TARGET_OS_OSX && !GGML_METAL_NDEBUG
+ // Show all the Metal device instances in the system
+ NSArray * devices = MTLCopyAllDevices();
+ for (id<MTLDevice> device in devices) {
+ GGML_LOG_INFO("%s: found device: %s\n", __func__, [[device name] UTF8String]);
+ }
+ [devices release]; // since it was created by a *Copy* C method
+#endif
+
+ // init context
+ struct ggml_backend_metal_context * ctx = calloc(1, sizeof(struct ggml_backend_metal_context));
+ struct ggml_backend_metal_device_context * ctx_dev = dev->context;
+
+ id<MTLDevice> device = ggml_backend_metal_device_acq(ctx_dev);
+ GGML_LOG_INFO("%s: picking default device: %s\n", __func__, [[device name] UTF8String]);
+
+ ctx->queue = [device newCommandQueue];
+ if (ctx->queue == nil) {
+ GGML_LOG_ERROR("%s: error: failed to create command queue\n", __func__);
+ return NULL;
+ }
+
+ ctx->d_queue = dispatch_queue_create("ggml-metal", DISPATCH_QUEUE_CONCURRENT);
+
+ // load library
+ if (ctx_dev->mtl_library == nil) {
+ ctx_dev->mtl_library = ggml_metal_load_library(device, ctx_dev->use_bfloat);
+ }
+ id<MTLLibrary> metal_library = ctx_dev->mtl_library;
+ if (metal_library == nil) {
+ GGML_LOG_ERROR("%s: error: metal library is nil\n", __func__);
+ return NULL;
}
// print MTL GPU family:
[metal_function release]; \
if (error) { \
GGML_LOG_ERROR("%s: error: load pipeline error: %s\n", __func__, [[error description] UTF8String]); \
- [metal_library release]; \
return NULL; \
} \
} else { \
GGML_METAL_ADD_KERNEL(GGML_METAL_KERNEL_TYPE_POOL_2D_MAX_F32, pool_2d_max_f32, true);
}
- [metal_library release];
-
return ctx;
}