char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
a2878d30d
char/aliceai-t5-moe
1#import "ggml-metal-device.h" 2#import "ggml-metal-fusion.h" 3 4#import "ggml-impl.h" 5#import "ggml-backend-impl.h" 6#import "ggml-metal-impl.h" 7#import "ggml-metal-common.h" 8 9#include <Foundation/Foundation.h> 10 11#include <Metal/Metal.h> 12 13#include <stdatomic.h> 14 15#ifndef TARGET_OS_VISION 16#define TARGET_OS_VISION 0 17#endif 18 19// create residency sets only on macOS >= 15.0 20# if ! TARGET_CPU_X86_64 && TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || \ 21TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || \ 22TARGET_OS_TV && __TV_OS_VERSION_MAX_ALLOWED >= 180000 || \ 23TARGET_OS_VISION && __VISION_OS_VERSION_MAX_ALLOWED >= 200000 24# define GGML_METAL_HAS_RESIDENCY_SETS 1 25# endif 26 27// overload of MTLGPUFamilyMetalX (not available in some environments) 28static const NSInteger MTLGPUFamilyMetal3_GGML = 5001; 29static const NSInteger MTLGPUFamilyMetal4_GGML = 5002; 30 31// MTLLanguageVersion4_0 is not present in older SDKs 32static const NSUInteger MTLLanguageVersion4_0_GGML = 4 << 16; 33 34# if ! GGML_METAL_EMBED_LIBRARY 35// Here to assist with NSBundle Path Hack 36@ interface GGMLMetalClass : NSObject 37@ end 38@ implementation GGMLMetalClass 39@ end 40# endif 41 42// 43// MTLFunctionConstantValues wrapper 44// 45 46s truct ggml_metal_cv { 47MTLFunctionConstantValues * obj ; 48}; 49 50ggml_metal_cv_t ggml_metal_cv_init(void) { 51ggml_metal_cv_t res = calloc(1, sizeof(struct ggml_metal_cv)); 52 53res - > obj = [[ MTLFunctionConstantValues alloc] init]; 54 55return res; 56} 57 58v oid ggml_metal_cv_free(ggml_metal_cv_t cv) { 59[ cv - > obj release]; 60free ( cv ); 61} 62 63v oid ggml_metal_cv_set_int16(ggml_metal_cv_t cv, int16_t value, int32_t idx) { 64[ cv - > obj setConstantValue : & value type : MTLDataTypeShort atIndex : idx ]; 65} 66 67v oid ggml_metal_cv_set_int32(ggml_metal_cv_t cv, int32_t value, int32_t idx) { 68[ cv - > obj setConstantValue : & value type : MTLDataTypeInt atIndex : idx ]; 69} 70 71v oid ggml_metal_cv_set_bool(ggml_metal_cv_t cv, bool value, int32_t idx) { 72[ cv - > obj setConstantValue : & value type : MTLDataTypeBool atIndex : idx ]; 73} 74 75// 76// MTLComputePipelineState wrapper 77// 78 79s truct ggml_metal_pipeline { 80id < MTLComputePipelineState > obj ; 81}; 82 83ggml_metal_pipeline_t ggml_metal_pipeline_init(void) { 84ggml_metal_pipeline_t res = calloc(1, sizeof(struct ggml_metal_pipeline)); 85 86* res = ( struct ggml_metal_pipeline) { 87/* . obj =*/ nil , 88}; 89 90return res; 91} 92 93v oid ggml_metal_pipeline_free(ggml_metal_pipeline_t pipeline) { 94[ pipeline - > obj release]; 95 96free ( pipeline ); 97} 98 99i nt ggml_metal_pipeline_max_theads_per_threadgroup(struct ggml_metal_pipeline_with_params pipeline) { 100return pipeline. pipeline - > obj . maxTotalThreadsPerThreadgroup ; 101} 102 103// 104// MTLLibrary collection (one library per op-source, compiled separately) 105// 106 107// Single source of truth for the per-kind metal libraries. The order here 108// defines the enum values and every per-kind table below, so adding a library 109// is a one-line change here (plus adding its source to CMakeLists.txt). 110// X( suffix , name ) : name is both the kernels / < name >. metal basename and the 111// ggml_metallib_< name > _ { start , end } embed - symbol stem. 112# define GGML_METAL_LIBS \ 113X( FA , fa ) \ 114X( MUL_MV , mul_mv ) \ 115X( MUL_MM , mul_mm ) \ 116X( QUANTIZE , quantize ) \ 117X( SOFTMAX , softmax ) \ 118X( NORM , norm ) \ 119X( UNARY , unary ) \ 120X( BINBCAST , binbcast ) \ 121X( REDUCE , reduce ) \ 122X( TRI , tri ) \ 123X( SSM , ssm ) \ 124X( WKV , wkv ) \ 125X( GATED_DELTA_NET , gated_delta_net )\ 126X( SOLVE_TRI , solve_tri ) \ 127X( ROPE , rope ) \ 128X( CONV , conv ) \ 129X( UPSCALE , upscale ) \ 130X( ARGSORT , argsort ) \ 131X( POOL , pool ) \ 132X( MISC , misc ) 133 134enum ggml_metal_lib_kind { 135# define X(e, s) GGML_METAL_LIB_##e, 136GGML_METAL_LIBS 137# undef X 138GGML_METAL_LIB_COUNT , 139}; 140 141static const char * const k_lib_names[GGML_METAL_LIB_COUNT] = { 142# define X(e, s) [GGML_METAL_LIB_##e] = #s, 143GGML_METAL_LIBS 144# undef X 145}; 146 147struct ggml_metal_library { 148// Per - kind compiled libraries. When single_library is true, the whole library 149// ( e . g . a pre - compiled default. metallib or a from - source build) lives at 150// objs[ 0 ] and the remaining slots are nil. 151id < MTLLibrary > objs [ GGML_METAL_LIB_COUNT ]; 152bool single_library; // true : combined library at objs[ 0 ]; false : per - kind libs in objs[ * ] 153 154// Routing table : kernel function name -> objs[] index, populated from each 155// compiled library's -[MTLLibrary functionNames]. The actual compiled 156// libraries are the single source of truth for which library owns a kernel, 157// so adding kernels later requires no manual routing maintenance. 158// nil in single_library mode (everything resolves to objs[0]). 159NSMutableDictionary < NSString * , NSNumber *> * fn_to_lib ; 160 161// kernels from a second metallib, resolved ahead of the combined library 162NSSet < NSString * > * override_fns ; 163 164ggml_metal_device_t dev; 165ggml_metal_pipelines_t pipelines; // cache of compiled pipelines 166 167NSLock * lock ; 168}; 169 170// Build the fn_to_lib routing table by querying each compiled library's public 171// function names . Call once after all per - kind libraries have been compiled. 172s tatic void ggml_metal_library_build_index( ggml_metal_library_t lib) { 173@ autoreleasepool { 174NSMutableDictionary < NSString * , NSNumber * > * index = [[ NSMutableDictionary alloc] init]; 175for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 176for ( NSString * fname in [ lib - > objs [ kind ] functionNames]) { 177index [ fname ] = @( kind ); 178} 179} 180l ib - > fn_to_lib = index; 181} 182} 183 184// note : defined below, after struct ggml_metal_device 185static void ggml_metal_device_disable_tensor(ggml_metal_device_t dev); 186 187// the tensor API headers are exposed to the shader compiler only at Metal language version 4.0 188static void ggml_metal_compile_options_set_lang(MTLCompileOptions * options, bool has_tensor) { 189if (! has_tensor ) { 190return; 191} 192 193o ptions . languageVersion = ( MTLLanguageVersion ) MTLLanguageVersion4_0_GGML; 194} 195 196// Parse a `#include "name"` line. Returns the quoted name in *include_name on 197// success. Whitespace - tolerant ; ignores `#include <...>` (system headers). 198static bool ggml_metal_library_parse_quoted_include(NSString * line, NSString ** include_name) { 199NSScanner * scanner = [ NSScanner scannerWithString : line ]; 200scanner . charactersToBeSkipped = [ NSCharacterSet whitespaceCharacterSet]; 201 202if (![ scanner scanString :@ "#" intoString : NULL ] || 203![ scanner scanString :@ "include" intoString : NULL ] || 204![ scanner scanString :@ "\"" intoString:NULL]) { 205return false; 206} 207 208NSString * name = nil; 209if (![scanner scanUpToString:@" \ "" intoString : & name ]) { 210return false; 211} 212 213if ( include_name ) { 214* include_name = name; 215} 216return true; 217} 218 219// Recursively inline `#include "name"` directives. System includes (<...>), 220// `#if/#else/#endif`, and other preprocessor lines are passed through to the 221// Metal compiler unchanged. `#pragma once` is dropped since `seen` already 222// guards against double-inclusion. 223static bool ggml_metal_library_flatten_file(NSMutableString * dst, NSString * path, 224NSArray < NSString * > * search_paths , 225NSMutableSet < NSString * > * seen , NSError ** error) { 226NSString * key = [ path stringByStandardizingPath]; 227if ([ seen containsObject : key ]) { 228return true; 229} 230[ seen addObject : key ]; 231 232NSString * src = [ NSString stringWithContentsOfFile : path encoding : NSUTF8StringEncoding error : error ]; 233if (! src ) { 234return false; 235} 236 237NSFileManager * fm = [ NSFileManager defaultManager]; 238for ( NSString * line in [ src componentsSeparatedByString :@ "\n" ]) { 239NSString * trimmed = [ line stringByTrimmingCharactersInSet : [ NSCharacterSet whitespaceCharacterSet]]; 240if ([ trimmed isEqualToString :@ "#pragma once" ]) { 241continue; 242} 243 244N SString * include_name = nil; 245if ( ggml_metal_library_parse_quoted_include ( line , & include_name )) { 246NSString * resolved = nil; 247for ( NSString * dir in search_paths) { 248NSString * candidate = [ dir stringByAppendingPathComponent : include_name ]; 249if ([ fm isReadableFileAtPath : candidate ]) { 250resolved = candidate; 251break; 252} 253} 254if (! resolved ) { 255if ( error ) { 256NSString * msg = [ NSString stringWithFormat :@ "could not resolve include \" %@\" from '%@'", include_name, path]; 257* error = [ NSError errorWithDomain :@ "ggml-metal-source-flatten" code : 1 258userInfo :@ { NSLocalizedDescriptionKey : msg } ]; 259} 260return false; 261} 262if (! ggml_metal_library_flatten_file ( dst , resolved , search_paths , seen , error )) { 263return false; 264} 265continue ; 266} 267 268[ dst appendString : line ]; 269[ dst appendString :@ "\n" ]; 270} 271 272return true; 273} 274 275s tatic NSString * ggml_metal_library_flatten_source(NSString * path_source, NSError ** error) { 276// Search paths cover both runtime layout (build/bin/kernels + build/bin) 277// and source-tree layout (ggml/src/ggml-metal/kernels + ggml/src/ggml-metal + ggml/src). 278NSString * path_kernels = [ path_source stringByDeletingLastPathComponent]; 279NSString * path_base = [ path_kernels stringByDeletingLastPathComponent]; 280NSArray < NSString * > * search_paths = @[ 281path_kernels , 282path_base , 283[ path_base stringByDeletingLastPathComponent], 284]; 285 286NSMutableString * src = [[ NSMutableString alloc] init]; 287NSMutableSet < NSString * > * seen = [ NSMutableSet set]; 288 289if (! ggml_metal_library_flatten_file ( src , path_source , search_paths , seen , error )) { 290[ src release]; 291return nil; 292} 293return src; 294} 295 296// Compile all per-kind libraries in parallel. `source_for_kind` returns the MSL 297// source for a kind (the helper takes ownership and releases it), or nil with 298// * err set on failure. On success the objs[] slots are populated and the routing 299// index is built; on any failure every error is logged and false is returned 300// ( the caller is responsible for freeing ` res `). 301s tatic bool ggml_metal_library_compile_all( 302g gml_metal_library_t res, 303id < MTLDevice > device , 304NSDictionary * prep , 305NSString * ( ^ source_for_kind )(int kind, NSError ** err), 306const char * origin) { 307const int64_t t_start = ggml_time_us(); 308 309int64_t * t_per_lib = calloc( GGML_METAL_LIB_COUNT , sizeof ( int64_t )); 310NSError ** err_per_lib = calloc(GGML_METAL_LIB_COUNT, sizeof(NSError *)); 311__block atomic_bool any_failure = false; 312 313dispatch_group_t group = dispatch_group_create(); 314dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0); 315 316for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 317dispatch_group_async ( group , queue , ^ { 318 319const int64_t t0 = ggml_time_us(); 320 321NSError * error = nil; 322 323NSString * src = source_for_kind( kind , & error ); 324if (! src ) { 325err_per_lib [ kind ] = [ error retain]; 326atomic_store (& any_failure , true ); 327return; 328} 329 330id < MTLLibrary > lib = nil; 331 332@ autoreleasepool { 333MTLCompileOptions * options = [ MTLCompileOptions new]; 334options . preprocessorMacros = prep; 335ggml_metal_compile_options_set_lang ( options , ggml_metal_device_get_props ( res - > dev ) - > has_tensor ); 336 337lib = [ device newLibraryWithSource : src options : options error : & error ]; 338 339[ options release]; 340 341// retain the error before the autorelease pool drains it 342if (! lib ) { 343err_per_lib [ kind ] = [ error retain]; 344} 345} 346 347[ src release]; 348 349t_per_lib [ kind ] = ggml_time_us() - t0 ; 350 351if (! lib ) { 352atomic_store (& any_failure , true ); 353return; 354} 355 356res - > objs [ kind ] = lib; 357}); 358} 359dispatch_group_wait ( group , DISPATCH_TIME_FOREVER ); 360dispatch_release ( group ); 361 362const bool ok = !atomic_load(&any_failure); 363 364if ( ok ) { 365const int64_t t_total = ggml_time_us() - t_start ; 366int64_t t_max = 0; 367for ( int kind = 0; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 368GGML_LOG_DEBUG ( "%s: compiled '%s' library in %.3f sec\n" , 369_ _func__ , k_lib_names [ kind ], t_per_lib [ kind ] / 1e6 ); 370if ( t_per_lib [ kind ] > t_max ) t_max = t_per_lib[ kind ]; 371} 372GGML_LOG_INFO ( "%s: loaded %d libraries from %s in %.3f sec (max single = %.3f sec)\n" , 373_ _func__ , GGML_METAL_LIB_COUNT , origin , t_total / 1e6 , t_max / 1e6 ); 374 375ggml_metal_library_build_index ( res ); 376} else { 377for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 378if ( err_per_lib [ kind ]) { 379GGML_LOG_ERROR ( "%s: failed to build '%s' library: %s\n" , __func__ , 380k _lib_names [ kind ], [[ err_per_lib [ kind ] description] UTF8String]); 381[ err_per_lib [ kind ] release]; 382} 383} 384} 385 386f ree ( err_per_lib ); 387free ( t_per_lib ); 388 389return ok; 390} 391 392// look for <name>.metallib as a bundle resource, then next to the running binary 393static NSString * ggml_metal_find_metallib(NSBundle * bundle, NSString * name) { 394NSError * error = nil; 395 396NSString * path_lib = [ bundle pathForResource : name ofType :@ "metallib" ]; 397if ( path_lib == nil ) { 398// Try to find the resource in the directory where the current binary located. 399N SString * bin_cur = [[ NSProcessInfo processInfo] arguments][ 0 ]; 400NSString * bin_dir = [ bin_cur stringByDeletingLastPathComponent]; 401 402NSString * path_lib_default = [NSString pathWithComponents :@ [ bin_dir , [ name stringByAppendingPathExtension :@ "metallib" ]]]; 403if ([[ NSFileManager defaultManager] isReadableFileAtPath : path_lib_default ]) { 404GGML_LOG_INFO ( "%s: found '%s'\n" , __func__ , [ path_lib_default UTF8String]); 405 406NSDictionary * atts = [[ NSFileManager defaultManager] attributesOfItemAtPath : path_lib_default error : & error ]; 407if ( atts && atts [ NSFileType ] == NSFileTypeSymbolicLink ) { 408// Optionally, if this is a symlink, try to resolve it. 409pa th_lib_default = [[ NSFileManager defaultManager] destinationOfSymbolicLinkAtPath : path_lib_default error : & error ]; 410if ( path_lib_default && [ path_lib_default length] > 0 && ![[ path_lib_default substringToIndex : 1 ] isEqualToString :@ "/" ]) { 411// It is a relative path, adding the binary directory as directory prefix. 412pa th_lib_default = [ NSString pathWithComponents :@ [ bin_dir , path_lib_default ]]; 413} 414if (! path_lib_default || ![[ NSFileManager defaultManager] isReadableFileAtPath : path_lib_default ]) { 415// Link to the resource could not be resolved. 416pa th_lib_default = nil; 417} else { 418GGML_LOG_INFO ( "%s: symlink resolved '%s'\n" , __func__ , [ path_lib_default UTF8String]); 419} 420} 421} else { 422// The resource couldn ' t be found in the binary ' s directory. 423pa th_lib_default = nil; 424} 425 426path_lib = path_lib_default ; 427} 428 429return path_lib; 430} 431 432g gml_metal_library_t ggml_metal_library_init(ggml_metal_device_t dev) { 433id < MTLDevice > device = ggml_metal_device_get_obj( dev ); 434 435ggml_metal_library_t res = calloc(1, sizeof(struct ggml_metal_library)); 436res - > dev = dev; 437res - > pipelines = ggml_metal_pipelines_init(); 438res - > lock = [ NSLock new]; 439 440// shared MTLCompileOptions preprocessor macros (matches the build-time defines) 441NSMutableDictionary * prep = [ NSMutableDictionary dictionary]; 442if ( ggml_metal_device_get_props ( dev ) - > has_bfloat ) { 443[ prep setObject :@ "1" forKey :@ "GGML_METAL_HAS_BF16" ]; 444} 445if ( ggml_metal_device_get_props ( dev ) - > has_tensor ) { 446[ prep setObject :@ "1" forKey :@ "GGML_METAL_HAS_TENSOR" ]; 447} 448# if GGML_METAL_EMBED_LIBRARY 449[ prep setObject :@ "1" forKey :@ "GGML_METAL_EMBED_LIBRARY" ]; 450# endif 451 452# if GGML_METAL_EMBED_LIBRARY 453GGML_LOG_INFO ( "%s: using embedded metal library\n" , __func__ ); 454 455// start / end symbols emitted by CMake ( see CMakeLists. txt ), one pair per kind 456# define X(e, s) extern const char ggml_metallib_##s##_start[]; extern const char ggml_metallib_##s##_end[]; 457GGML_METAL_LIBS 458# undef X 459 460static const char * const lib_start[GGML_METAL_LIB_COUNT] = { 461# define X(e, s) [GGML_METAL_LIB_##e] = ggml_metallib_##s##_start, 462GGML_METAL_LIBS 463# undef X 464}; 465static const char * const lib_end[GGML_METAL_LIB_COUNT] = { 466# define X(e, s) [GGML_METAL_LIB_##e] = ggml_metallib_##s##_end, 467GGML_METAL_LIBS 468# undef X 469}; 470 471const bool ok = ggml_metal_library_compile_all(res, device, prep, 472^ NSString * ( int kind, NSError * * err ) { 473( void ) err; 474return [[ NSString alloc] initWithBytes : lib_start [ kind ] 475length : ( lib_end [ kind ] - lib_start [ kind ]) 476encoding : NSUTF8StringEncoding ]; 477}, "embedded data" ); 478 479if (! ok ) { 480ggml_metal_library_free ( res ); 481return NULL; 482} 483 484return res; 485# else 486# ifdef SWIFT_PACKAGE 487NSBundle * bundle = SWIFTPM_MODULE_BUNDLE; 488# else 489N SBundle * bundle = [ NSBundle bundleForClass : [ GGMLMetalClass class]]; 490# endif 491 492const int64_t t_start = ggml_time_us(); 493 494NSError * error = nil; 495NSString * path_lib = ggml_metal_find_metallib( bundle , @ "default" ); 496 497if ( path_lib ! = nil) { 498// pre - compiled library found : a single combined default. metallib 499NSURL * libURL = [ NSURL fileURLWithPath : path_lib ]; 500GGML_LOG_INFO ( "%s: loading '%s'\n" , __func__ , [ path_lib UTF8String]); 501 502res - > objs [ 0 ] = [device newLibraryWithURL : libURL error : & error ]; 503res - > single_library = true ; 504if ( ! res -> objs [ 0 ]) { 505GGML_LOG_ERROR ( "%s: error: %s\n" , __func__ , [[ error description] UTF8String]); 506ggml_metal_library_free ( res ); 507return NULL; 508} 509 510// the tensor API kernels are built into a separate metallib 511if ( ggml_metal_device_get_props ( dev ) - > has_tensor ) { 512NSString * path_mm = ggml_metal_find_metallib( bundle , @ "ggml-tensor" ); 513 514id < MTLLibrary > lib_mm = nil; 515if ( path_mm ! = nil) { 516lib_mm = [ device newLibraryWithURL : [ NSURL fileURLWithPath : path_mm ] error : & error ]; 517if (! lib_mm && error ) { 518GGML_LOG_ERROR ( "%s: %s\n" , __func__ , [[ error description] UTF8String]); 519} 520} 521 522if ( lib_mm ) { 523GGML_LOG_INFO ( "%s: loaded '%s'\n" , __func__ , [ path_mm UTF8String]); 524 525res - > objs [ GGML_METAL_LIB_MUL_MM ] = [ lib_mm retain]; 526res - > override_fns = [[ NSSet setWithArray : [ lib_mm functionNames]] retain]; 527} else { 528GGML_LOG_INFO ( "%s: ggml-tensor.metallib not found - disabling the tensor API\n" , __func__ ); 529 530ggml_metal_device_disable_tensor ( dev ); 531} 532} 533 534G GML_LOG_INFO ( "%s: loaded in %.3f sec\n" , __func__ , ( ggml_time_us () - t_start ) / 1e6 ); 535return res; 536} 537 538// no pre-compiled metallib: fall back to compiling each kernel source separately 539GGML_LOG_INFO ( "%s: default.metallib not found, loading kernel sources\n" , __func__ ); 540 541NSString * path_resource = [[ NSProcessInfo processInfo].environment objectForKey :@ "GGML_METAL_PATH_RESOURCES" ]; 542if ( path_resource ) { 543GGML_LOG_INFO ( "%s: GGML_METAL_PATH_RESOURCES = %s\n" , __func__ , [ path_resource UTF8String]); 544} 545 546// resolve each kind's source path up front (file lookup/logging stays on the calling thread) 547NSString ** path_per_kind = calloc(GGML_METAL_LIB_COUNT, sizeof(NSString *)); 548for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 549NSString * rel = [ NSString stringWithFormat :@ "kernels/%s.metal" , k_lib_names [ kind ]]; 550 551NSString * path_source = nil; 552if ( path_resource ) { 553path_source = [ path_resource stringByAppendingPathComponent : rel ]; 554} else { 555NSString * stem = [ NSString stringWithFormat :@ "kernels/%s" , k_lib_names [ kind ]]; 556path_source = [bundle pathForResource : stem ofType :@ "metal" ]; 557} 558 559if ( path_source == nil || ![[ NSFileManager defaultManager] isReadableFileAtPath : path_source ]) { 560GGML_LOG_WARN ( "%s: could not locate %s in bundle, falling back to cwd\n" , __func__ , [ rel UTF8String]); 561path_source = rel; 562} 563 564GGML_LOG_DEBUG ( "%s: loading '%s'\n" , __func__ , [ path_source UTF8String]); 565 566path_per_kind [ kind ] = [ path_source retain]; 567} 568 569const bool ok = ggml_metal_library_compile_all(res, device, prep, 570^ NSString * ( int kind, NSError * * err ) { 571return ggml_metal_library_flatten_source( path_per_kind [ kind ], err ); 572}, "source" ); 573 574for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 575[ path_per_kind [ kind ] release]; 576} 577free ( path_per_kind ); 578 579if (! ok ) { 580ggml_metal_library_free ( res ); 581return NULL; 582} 583 584return res; 585# endif 586} 587 588g gml_metal_library_t ggml_metal_library_init_from_source(ggml_metal_device_t dev, const char * source, bool verbose) { 589if ( source == NULL ) { 590GGML_LOG_ERROR ( "%s: source is NULL\n" , __func__ ); 591return NULL; 592} 593 594id < MTLDevice > device = ggml_metal_device_get_obj( dev ); 595id < MTLLibrary > library = nil; 596NSError * error = nil; 597 598const int64_t t_start = ggml_time_us(); 599 600NSString * src = [[ NSString alloc] initWithBytes : source 601length : strlen ( source ) 602encoding : NSUTF8StringEncoding ]; 603if (! src ) { 604GGML_LOG_ERROR ( "%s: failed to create NSString from source\n" , __func__ ); 605return NULL; 606} 607 608@ autoreleasepool { 609NSMutableDictionary * prep = [ NSMutableDictionary dictionary]; 610 611MTLCompileOptions * options = [ MTLCompileOptions new]; 612options . preprocessorMacros = prep; 613ggml_metal_compile_options_set_lang ( options , ggml_metal_device_get_props ( dev ) - > has_tensor ); 614 615library = [ device newLibraryWithSource : src options : options error : & error ]; 616if ( error ) { 617if ( verbose ) { 618GGML_LOG_ERROR ( "%s: error compiling source: %s\n" , __func__ , [[ error description] UTF8String]); 619} else { 620GGML_LOG_ERROR ( "%s: error compiling source\n" , __func__ ); 621} 622library = nil; 623} 624 625[ options release]; 626} 627 628[ src release]; 629 630if (! library ) { 631if ( verbose ) { 632GGML_LOG_ERROR ( "%s: failed to create Metal library from source\n" , __func__ ); 633} 634 635return NULL; 636} 637 638if ( verbose ) { 639GGML_LOG_INFO ( "%s: compiled in %.3f sec\n" , __func__ , ( ggml_time_us () - t_start ) / 1e6 ); 640} 641 642ggml_metal_library_t res = calloc(1, sizeof(struct ggml_metal_library)); 643if (! res ) { 644GGML_LOG_ERROR ( "%s: calloc failed\n" , __func__ ); 645return NULL; 646} 647 648res - > objs [ 0 ] = library; 649res - > single_library = true; 650res - > dev = dev; 651res - > pipelines = ggml_metal_pipelines_init(); 652res - > lock = [ NSLock new]; 653 654return res; 655} 656 657v oid ggml_metal_library_free(ggml_metal_library_t lib) { 658if (! lib ) { 659return; 660} 661 662for ( int kind = 0 ; kind < GGML_METAL_LIB_COUNT ; ++ kind ) { 663if ( lib - > objs [ kind ]) { 664[ lib - > objs [ kind ] release]; 665} 666} 667 668if ( lib - > fn_to_lib ) { 669[ lib - > fn_to_lib release]; 670} 671 672if ( lib - > override_fns ) { 673[ lib - > override_fns release]; 674} 675 676ggml_metal_pipelines_free ( lib - > pipelines ); 677 678[ lib - > lock release]; 679 680free ( lib ); 681} 682 683g gml_metal_device_t ggml_metal_library_get_device(ggml_metal_library_t lib) { 684return lib - >dev; 685} 686 687s truct ggml_metal_pipeline_with_params ggml_metal_library_get_pipeline(ggml_metal_library_t lib, const char * name) { 688[ lib - > lock lock]; 689 690struct ggml_metal_pipeline_with_params res = { 691/* . pipeline =*/ nil , 692/* . nsg =*/ 0 , 693/* . nr0 =*/ 0 , 694/* . nr1 =*/ 0 , 695/* . smem =*/ 0 , 696/* . c4 =*/ false , 697/* . cnt =*/ false , 698}; 699 700res . pipeline = ggml_metal_pipelines_get ( lib - > pipelines , name ); 701 702[ lib - > lock unlock]; 703 704return res; 705} 706 707s truct ggml_metal_pipeline_with_params ggml_metal_library_compile_pipeline(ggml_metal_library_t lib, const char * base, const char * name, ggml_metal_cv_t cv) { 708struct ggml_metal_pipeline_with_params res = { 709/* . pipeline =*/ nil , 710/* . nsg =*/ 0 , 711/* . nr0 =*/ 0 , 712/* . nr1 =*/ 0 , 713/* . smem =*/ 0 , 714/* . c4 =*/ false , 715/* . cnt =*/ false , 716}; 717 718[ lib - > lock lock]; 719 720res . pipeline = ggml_metal_pipelines_get ( lib - > pipelines , name ); 721if ( res . pipeline ) { 722[ lib - > lock unlock]; 723 724return res; 725} 726 727@ autoreleasepool { 728NSError * error = nil; 729 730NSString * base_func = [ NSString stringWithUTF8String : base ]; 731 732GGML_LOG_DEBUG ( "%s: compiling pipeline: base = '%s', name = '%s'\n" , __func__ , base , name ); 733 734// route to the library that actually defines this kernel; fn_to_lib is 735// built from -[ MTLLibrary functionNames] so it ' s always in sync 736int lib_idx = 0; 737if ( lib - > override_fns && [ lib - > override_fns containsObject : base_func ]) { 738lib_idx = GGML_METAL_LIB_MUL_MM; 739} else if (! lib - > single_library ) { 740NSNumber * idx = lib - > fn_to_lib [ base_func ]; 741if (! idx ) { 742[ lib - > lock unlock]; 743 744GGML_LOG_ERROR ( "%s: kernel not found in any metal library: base = '%s', name = '%s'\n" , __func__ , base , name ); 745 746return res; 747} 748lib_idx = [ idx intValue]; 749} 750 751id < MTLLibrary > mtl_lib = lib - > objs [ lib_idx ]; 752 753id < MTLFunction > mtl_function ; 754if (! cv ) { 755mtl_function = [ mtl_lib newFunctionWithName : base_func ]; 756} else { 757mtl_function = [mtl_lib newFunctionWithName : base_func constantValues : cv - > obj error : & error ]; 758} 759if (! mtl_function ) { 760[ lib - > lock unlock]; 761 762GGML_LOG_ERROR ( "%s: failed to compile pipeline: base = '%s', name = '%s'\n" , __func__ , base , name ); 763if ( error ) { 764GGML_LOG_ERROR ( "%s: %s\n" , __func__ , [[ error description] UTF8String]); 765} 766 767return res; 768} 769 770id < MTLDevice > device = ggml_metal_device_get_obj( lib - > dev ); 771id < MTLComputePipelineState > obj = [ device newComputePipelineStateWithFunction : mtl_function error : & error ]; 772 773[ mtl_function release]; 774 775if (! obj ) { 776[ lib - > lock unlock]; 777 778GGML_LOG_ERROR ( "%s: failed to create pipeline state: base = '%s', name = '%s'\n" , __func__ , base , name ); 779if ( error ) { 780GGML_LOG_ERROR ( "%s: %s\n" , __func__ , [[ error description] UTF8String]); 781} 782 783return res; 784} 785 786GGML_LOG_DEBUG ( "%s: loaded %-40s %16p | th_max = %4d | th_width = %4d\n" , __func__ , name , 787( void * ) obj, 788( int ) obj. maxTotalThreadsPerThreadgroup , 789( int ) obj. threadExecutionWidth ); 790 791if ( obj . maxTotalThreadsPerThreadgroup == 0 || obj . threadExecutionWidth == 0 ) { 792[ obj release]; 793 794[ lib - > lock unlock]; 795 796GGML_LOG_ERROR ( "%s: incompatible pipeline %s\n" , __func__ , name ); 797 798return res; 799} 800 801res . pipeline = ggml_metal_pipeline_init(); 802res . pipeline - > obj = obj; 803 804ggml_metal_pipelines_add ( lib - > pipelines , name , res . pipeline ); 805} 806 807[ lib - > lock unlock]; 808 809return res; 810} 811 812// 813// MTLComputeCommandEncoder wrapper 814// 815 816s truct ggml_metal_encoder { 817id < MTLComputeCommandEncoder > obj ; 818}; 819 820ggml_metal_encoder_t ggml_metal_encoder_init(ggml_metal_cmd_buf_t cmd_buf_raw, bool concurrent) { 821ggml_metal_encoder_t res = calloc(1, sizeof(struct ggml_metal_encoder)); 822 823id < MTLCommandBuffer > cmd_buf = ( id < MTLCommandBuffer >) cmd_buf_raw; 824 825if ( concurrent ) { 826res - > obj = [ cmd_buf computeCommandEncoderWithDispatchType : MTLDispatchTypeConcurrent ]; 827} else { 828res - > obj = [ cmd_buf computeCommandEncoder]; 829} 830 831[ res - > obj retain]; 832 833return res; 834} 835 836v oid ggml_metal_encoder_free(ggml_metal_encoder_t encoder) { 837[ encoder - > obj release]; 838free ( encoder ); 839} 840 841v oid ggml_metal_encoder_debug_group_push(ggml_metal_encoder_t encoder, const char * name) { 842@ autoreleasepool { 843[ encoder - > obj pushDebugGroup : [ NSString stringWithCString : name encoding : NSUTF8StringEncoding ]]; 844} 845} 846 847v oid ggml_metal_encoder_debug_group_pop (ggml_metal_encoder_t encoder) { 848[ encoder - > obj popDebugGroup]; 849} 850 851v oid ggml_metal_encoder_set_pipeline(ggml_metal_encoder_t encoder, struct ggml_metal_pipeline_with_params pipeline) { 852if (! pipeline . pipeline ) { 853GGML_ABORT ( "%s: nil Metal pipeline (missing kernel; see compile_pipeline log above)\n" , __func__ ); 854} 855 856[ encoder - > obj setComputePipelineState : pipeline . pipeline - > obj ]; 857} 858 859v oid ggml_metal_encoder_set_bytes(ggml_metal_encoder_t encoder, void * data, size_t size, int idx) { 860[ encoder - > obj setBytes : data length : size atIndex : idx ]; 861} 862 863v oid ggml_metal_encoder_set_buffer(ggml_metal_encoder_t encoder, struct ggml_metal_buffer_id buffer, int idx) { 864[ encoder - > obj setBuffer : buffer . metal offset : buffer . offs atIndex : idx ]; 865} 866 867v oid ggml_metal_encoder_set_threadgroup_memory_size(ggml_metal_encoder_t encoder, size_t size, int idx) { 868// ref : https :// developer . apple . com / documentation / metal / mtlcomputecommandencoder / setthreadgroupmemorylength ( _ : index : ) 869GGML_ASSERT ( size % 16 == 0); 870 871[ encoder - > obj setThreadgroupMemoryLength : size atIndex : idx ]; 872} 873 874v oid ggml_metal_encoder_dispatch_threadgroups( ggml_metal_encoder_t encoder, int tg0, int tg1, int tg2, int tptg0, int tptg1, int tptg2) { 875[ encoder - > obj dispatchThreadgroups : MTLSizeMake ( tg0 , tg1 , tg2 ) threadsPerThreadgroup : MTLSizeMake ( tptg0 , tptg1 , tptg2 )]; 876} 877 878v oid ggml_metal_encoder_memory_barrier( ggml_metal_encoder_t encoder) { 879[ encoder - > obj memoryBarrierWithScope : MTLBarrierScopeBuffers ]; 880} 881 882v oid ggml_metal_encoder_end_encoding( ggml_metal_encoder_t encoder) { 883[ encoder - > obj endEncoding]; 884} 885 886s truct ggml_metal_device { 887i d < MTLDevice > mtl_device ; 888 889/ / a single global queue shared by all Metal backends 890/ / technically not needed for devices with unified memory, but enables discrete GPUs support 891/ / ref : https :// github . com / ggml - org / llama . cpp / pull / 15906 892id< MTLCommandQueue > mtl_queue ; 893 894ggml_metal_rsets_t rsets ; 895 896ggml_metal_library_t library ; 897 898struct ggml_metal_device_props props ; 899 900// shared fusion debugging context 901struct ggml_metal_fusion_info * finfo; 902 903// virtual address for GPU memory allocations 904atomic_uintptr_t addr_virt; 905}; 906 907// 908// MTLResidenceSet wrapper 909// 910 911s truct ggml_metal_rsets { 912NSLock * lock ; 913 914NSMutableArray * data ; 915 916// number of seconds since the last graph computation 917// keep the residency sets wired for that amount of time to avoid being collected by the OS 918int keep_alive_s; 919int loops_per_s; 920int time_per_loop_ms; 921 922// background heartbeat thread to keep the residency sets alive 923atomic_bool d_stop; 924atomic_int d_loop; 925 926dispatch_group_t d_group; 927}; 928 929# if defined ( GGML_METAL_HAS_RESIDENCY_SETS ) 930static void ggml_metal_dummy_work(ggml_metal_device_t dev) { 931if ( dev - > mtl_queue == nil ) { 932return; 933} 934 935@ autoreleasepool { 936// perform a minimal dummy operation on the GPU 937id < MTLBuffer > buf = [ dev - > mtl_device newBufferWithLength : 1 options : MTLResourceStorageModePrivate ]; 938id < MTLCommandBuffer > cmd_buf = [dev - > mtl_queue commandBuffer]; 939 940{ 941id < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 942 943[ encoder fillBuffer : buf range : NSMakeRange ( 0 , 1 ) value : 0 ]; 944 945[ encoder endEncoding]; 946} 947 948[ cmd_buf commit]; 949[ buf release]; 950} 951} 952# endif 953 954ggml_metal_rsets_t ggml_metal_rsets_init(ggml_metal_device_t dev) { 955ggml_metal_rsets_t res = calloc(1, sizeof(struct ggml_metal_rsets)); 956 957res - > lock = [[ NSLock alloc] init]; 958res - > data = [[ NSMutableArray alloc] init]; 959 960// by default keep the memory wired for 3 minutes 961res - > keep_alive_s = 3 * 60 ; 962 963const char * GGML_METAL_RESIDENCY_KEEP_ALIVE_S = getenv("GGML_METAL_RESIDENCY_KEEP_ALIVE_S"); 964if ( GGML_METAL_RESIDENCY_KEEP_ALIVE_S ) { 965res - > keep_alive_s = atoi( GGML_METAL_RESIDENCY_KEEP_ALIVE_S ); 966} 967 968if ( res - > keep_alive_s <= 0 ) { 969res - > keep_alive_s = 3 * 60 ; 970} 971 972res - > time_per_loop_ms = 5; 973res - > loops_per_s = 1000 / res - > time_per_loop_ms ; 974 975GGML_LOG_INFO ( "%s: creating a residency set collection (keep_alive = %d s)\n" , __func__ , res - > keep_alive_s ); 976 977atomic_store_explicit (& res - > d_stop , false , memory_order_relaxed ); 978atomic_store_explicit (& res - > d_loop , res - > loops_per_s * res - > keep_alive_s , memory_order_relaxed ); 979 980res - > d_group = dispatch_group_create(); 981 982// start a background thread that periodically requests residency for all the currently active sets in the collection 983// the requests stop after a certain amount of time (keep_alive_s) of inactivity 984dispatch_queue_t d_queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0); 985dispatch_group_async ( res - > d_group , d_queue , ^ { 986# if defined( GGML_METAL_HAS_RESIDENCY_SETS ) 987if ( @ available (macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, * )) { 988while (! atomic_load_explicit (& res - > d_stop , memory_order_relaxed)) { 989if ( atomic_load_explicit (& res - > d_loop , memory_order_relaxed) > 0 ) { 990[ res - > lock lock]; 991 992for ( int i = 0; i < ( int ) res - > data . count ; ++ i ) { 993[ res - > data [ i ] requestResidency]; 994} 995 996atomic_fetch_sub_explicit (& res - > d_loop , 1 , memory_order_relaxed ); 997 998[ res - > lock unlock]; 999} 1000 1001u sleep ( res - > time_per_loop_ms * 1000 ); 1002} 1003} 1004# endif 1005}); 1006 1007# if defined ( GGML_METAL_HAS_RESIDENCY_SETS ) 1008if ( @ available (macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, * )) { 1009// workaround for residency set memory not being released if no GPU operation occurs 1010// https :// developer . apple . com / forums / thread / 839089 1011// https :// github . com / ggml - org / llama . cpp / issues / 25937 1012ggml_metal_dummy_work ( dev ); 1013} 1014# endif 1015 1016return res; 1017} 1018 1019v oid ggml_metal_rsets_free(ggml_metal_rsets_t rsets) { 1020if ( rsets == NULL ) { 1021return; 1022} 1023 1024// note : if you hit this assert, most likely you haven't deallocated all Metal resources before exiting 1025GGML_ASSERT ([ rsets - > data count] == 0 ); 1026 1027atomic_store_explicit (& rsets - > d_stop , true , memory_order_relaxed ); 1028 1029dispatch_group_wait ( rsets - > d_group , DISPATCH_TIME_FOREVER ); 1030dispatch_release ( rsets - > d_group ); 1031 1032[ rsets - > data release]; 1033[ rsets - > lock release]; 1034 1035free ( rsets ); 1036} 1037 1038s tatic const struct { 1039const char * name; 1040const char * token; 1041enum ggml_metal_device_id id; 1042} k_metal_devices[] = { 1043# define DEV( name , id ) { name , # id , id } 1044DEV ( "M1" , GGML_METAL_DEVICE_M1 ), 1045DEV ( "M1 Pro" , GGML_METAL_DEVICE_M1_PRO ), 1046DEV ( "M1 Max" , GGML_METAL_DEVICE_M1_MAX ), 1047DEV ( "M1 Ultra" , GGML_METAL_DEVICE_M1_ULTRA ), 1048DEV ( "M2" , GGML_METAL_DEVICE_M2 ), 1049DEV ( "M2 Pro" , GGML_METAL_DEVICE_M2_PRO ), 1050DEV ( "M2 Max" , GGML_METAL_DEVICE_M2_MAX ), 1051DEV ( "M2 Ultra" , GGML_METAL_DEVICE_M2_ULTRA ), 1052DEV ( "M3" , GGML_METAL_DEVICE_M3 ), 1053DEV ( "M3 Pro" , GGML_METAL_DEVICE_M3_PRO ), 1054DEV ( "M3 Max" , GGML_METAL_DEVICE_M3_MAX ), 1055DEV ( "M3 Ultra" , GGML_METAL_DEVICE_M3_ULTRA ), 1056DEV ( "M4" , GGML_METAL_DEVICE_M4 ), 1057DEV ( "M4 Pro" , GGML_METAL_DEVICE_M4_PRO ), 1058DEV ( "M4 Max" , GGML_METAL_DEVICE_M4_MAX ), 1059DEV ( "M5" , GGML_METAL_DEVICE_M5 ), 1060DEV ( "M5 Pro" , GGML_METAL_DEVICE_M5_PRO ), 1061DEV ( "M5 Max" , GGML_METAL_DEVICE_M5_MAX ), 1062DEV ( "M5 Ultra" , GGML_METAL_DEVICE_M5_ULTRA ), 1063DEV ( "A18 Pro" , GGML_METAL_DEVICE_A18_PRO ), 1064# undef DEV 1065}; 1066 1067static enum ggml_metal_device_id ggml_metal_device_id_parse(const char * name) { 1068if (! name ) { 1069return GGML_METAL_DEVICE_GENERIC; 1070} 1071 1072static const char prefix[] = "Apple "; 1073if ( strncmp ( name , prefix , sizeof ( prefix ) - 1 ) ! = 0) { 1074return GGML_METAL_DEVICE_GENERIC; 1075} 1076const char * suffix = name + sizeof(prefix) - 1; 1077 1078for ( size_t i = 0 ; i < sizeof ( k_metal_devices ) / sizeof ( k_metal_devices [ 0 ]); ++ i ) { 1079if ( strcmp ( suffix , k_metal_devices [ i ]. name ) == 0 ) { 1080return k_metal_devices[ i ]. id ; 1081} 1082} 1083return GGML_METAL_DEVICE_GENERIC; 1084} 1085 1086con st char * ggml_metal_device_id_token(enum ggml_metal_device_id id) { 1087for ( size_t i = 0 ; i < sizeof ( k_metal_devices ) / sizeof ( k_metal_devices [ 0 ]); ++ i ) { 1088if ( k_metal_devices [ i ].id == id ) { 1089return k_metal_devices[ i ]. token ; 1090} 1091} 1092return "GGML_METAL_DEVICE_GENERIC" ; 1093} 1094 1095g gml_metal_device_t ggml_metal_device_init(int device, int n_devices) { 1096ggml_metal_device_t dev = calloc(1, sizeof(struct ggml_metal_device)); 1097 1098assert ( dev ! = NULL ); 1099 1100@ autoreleasepool { 1101if ( dev - > mtl_device == nil ) { 1102dev - > mtl_device = MTLCreateSystemDefaultDevice(); 1103 1104if ( dev - > mtl_device ) { 1105dev - > mtl_queue = [ dev - > mtl_device newCommandQueue]; 1106if ( dev - > mtl_queue == nil ) { 1107GGML_LOG_ERROR ( "%s: error: failed to create command queue\n" , __func__ ); 1108} 1109 1110dev - > addr_virt = 0x000000400 ULL ; 1111 1112dev - > props . device = device; 1113 1114// the Metal backend uses the system default device as the single physical device; 1115// additional ( virtual ) devices are emulated on top of it via GGML_METAL_DEVICES 1116dev - > props . device_phys = 0; 1117dev - > props . device_virt = device; 1118 1119dev - > props . has_simdgroup_reduction = [ dev - > mtl_device supportsFamily : MTLGPUFamilyApple7 ]; 1120dev - > props . has_simdgroup_reduction | = [ dev - > mtl_device supportsFamily : MTLGPUFamilyMetal3_GGML ]; 1121 1122dev - > props . has_simdgroup_mm = [ dev - > mtl_device supportsFamily : MTLGPUFamilyApple7 ]; 1123dev - > props . has_unified_memory = dev - > mtl_device . hasUnifiedMemory ; 1124 1125dev - > props . has_bfloat = [ dev - > mtl_device supportsFamily : MTLGPUFamilyMetal3_GGML ]; 1126dev - > props . has_bfloat | = [ dev - > mtl_device supportsFamily : MTLGPUFamilyApple6 ]; 1127if ( getenv ( "GGML_METAL_BF16_DISABLE" ) ! = NULL) { 1128dev - > props . has_bfloat = false; 1129} 1130 1131dev - > props . has_tensor = [ dev - > mtl_device supportsFamily : MTLGPUFamilyMetal4_GGML ]; 1132if ( getenv ( "GGML_METAL_TENSOR_DISABLE" ) ! = NULL) { 1133dev - > props . has_tensor = false; 1134} 1135 1136// note : disable the tensor API by default for old chips because with the current implementation it is not useful 1137// - M2 Ultra : ~ 5 % slower 1138// - M4 , M4 Max : no significant difference 1139// 1140// TODO : try to update the tensor API kernels to at least match the simdgroup performance 1141if ( getenv ( "GGML_METAL_TENSOR_ENABLE" ) == NULL && 1142![[ dev - > mtl_device name] containsString :@ "M5" ] && 1143![[ dev - > mtl_device name] containsString :@ "M6" ] && 1144![[ dev - > mtl_device name] containsString :@ "A19" ] && 1145![[ dev - > mtl_device name] containsString :@ "A20" ]) { 1146GGML_LOG_INFO ( "%s: tensor API disabled for pre-M5 and pre-A19 devices\n" , __func__ ); 1147dev - > props . has_tensor = false; 1148} 1149 1150// double - check that the tensor API compiles 1151if ( dev - > props . has_tensor ) { 1152const char * src_tensor_f16 = "\n" 1153"#include <metal_stdlib> \n" 1154"#include <metal_tensor> \n" 1155"#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h> \n" 1156" \n" 1157"using namespace metal; \n" 1158"using namespace mpp::tensor_ops; \n" 1159" \n" 1160"kernel void dummy_kernel( \n" 1161" tensor<device half, dextents<int32_t, 2>> A [[buffer(0)]], \n" 1162" tensor<device half, dextents<int32_t, 2>> B [[buffer(1)]], \n" 1163" device float * C [[buffer(2)]], \n" 1164" uint2 tgid [[threadgroup_position_in_grid]]) \n" 1165"{ \n" 1166" auto tA = A.slice(0, (int)tgid.y); \n" 1167" auto tB = B.slice((int)tgid.x, 0); \n" 1168" \n" 1169" matmul2d< \n" 1170" matmul2d_descriptor(16, 16, dynamic_extent), \n" 1171" execution_simdgroups<4>> mm; \n" 1172" \n" 1173" auto cT = mm.get_destination_cooperative_tensor<decltype(tA), decltype(tB), float>(); \n" 1174" \n" 1175" auto sA = tA.slice(0, 0); \n" 1176" auto sB = tB.slice(0, 0); \n" 1177" mm.run(sB, sA, cT); \n" 1178" \n" 1179" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(16, 16)); \n" 1180" \n" 1181" cT.store(tC); \n" 1182"}" ; 1183 1184GGML_LOG_INFO ( "%s: testing tensor API for f16 support\n" , __func__ ); 1185ggml_metal_library_t lib = ggml_metal_library_init_from_source( dev , src_tensor_f16 , false ); 1186if ( lib == NULL ) { 1187GGML_LOG_WARN ( "%s: - the tensor API is not supported in this environment - disabling\n" , __func__ ); 1188dev - > props . has_tensor = false; 1189} else { 1190struct ggml_metal_pipeline_with_params ppl = ggml_metal_library_compile_pipeline( lib , "dummy_kernel" , "dummy_kernel" , nil ); 1191if (! ppl . pipeline ) { 1192GGML_LOG_WARN ( "%s: - the tensor API is not supported in this environment - disabling\n" , __func__ ); 1193dev - > props . has_tensor = false; 1194} 1195 1196ggml_metal_library_free ( lib ); 1197} 1198} 1199 1200// try to compile a dummy kernel to determine if the tensor API is supported for bfloat 1201if ( dev - > props . has_tensor && dev - > props . has_bfloat ) { 1202const char * src_tensor_bf16 = "\n" 1203"#include <metal_stdlib> \n" 1204"#include <metal_tensor> \n" 1205"#include <MetalPerformancePrimitives/MetalPerformancePrimitives.h> \n" 1206" \n" 1207"using namespace metal; \n" 1208"using namespace mpp::tensor_ops; \n" 1209" \n" 1210"kernel void dummy_kernel( \n" 1211" tensor<device bfloat, dextents<int32_t, 2>> A [[buffer(0)]], \n" 1212" tensor<device bfloat, dextents<int32_t, 2>> B [[buffer(1)]], \n" 1213" device float * C [[buffer(2)]], \n" 1214" uint2 tgid [[threadgroup_position_in_grid]]) \n" 1215"{ \n" 1216" auto tA = A.slice(0, (int)tgid.y); \n" 1217" auto tB = B.slice((int)tgid.x, 0); \n" 1218" \n" 1219" matmul2d< \n" 1220" matmul2d_descriptor(16, 16, dynamic_extent), \n" 1221" execution_simdgroups<4>> mm; \n" 1222" \n" 1223" auto cT = mm.get_destination_cooperative_tensor<decltype(tA), decltype(tB), float>(); \n" 1224" \n" 1225" auto sA = tA.slice(0, 0); \n" 1226" auto sB = tB.slice(0, 0); \n" 1227" mm.run(sB, sA, cT); \n" 1228" \n" 1229" auto tC = tensor<device float, dextents<int32_t, 2>, tensor_inline>(C, dextents<int32_t, 2>(16, 16)); \n" 1230" \n" 1231" cT.store(tC); \n" 1232"}" ; 1233 1234GGML_LOG_INFO ( "%s: testing tensor API for bfloat support\n" , __func__ ); 1235ggml_metal_library_t lib = ggml_metal_library_init_from_source( dev , src_tensor_bf16 , false ); 1236if ( lib == NULL ) { 1237GGML_LOG_WARN ( "%s: - the tensor API does not support bfloat - disabling bfloat support\n" , __func__ ); 1238dev - > props . has_bfloat = false; 1239} else { 1240struct ggml_metal_pipeline_with_params ppl = ggml_metal_library_compile_pipeline( lib , "dummy_kernel" , "dummy_kernel" , nil ); 1241if (! ppl . pipeline ) { 1242GGML_LOG_WARN ( "%s: - the tensor API does not support bfloat - disabling bfloat support\n" , __func__ ); 1243dev - > props . has_bfloat = false; 1244} 1245 1246ggml_metal_library_free ( lib ); 1247} 1248} 1249 1250dev - > props . use_residency_sets = true; 1251# if defined ( GGML_METAL_HAS_RESIDENCY_SETS ) 1252dev - > props . use_residency_sets = getenv( "GGML_METAL_NO_RESIDENCY" ) == nil ; 1253# endif 1254 1255dev - > props . use_shared_buffers = dev - > props . has_unified_memory ; 1256# if TARGET_OS_OSX 1257// In case of eGPU, shared memory may be preferable. 1258dev - > props . use_shared_buffers | = [ dev - > mtl_device location] == MTLDeviceLocationExternal ; 1259# endif 1260if ( getenv ( "GGML_METAL_SHARED_BUFFERS_DISABLE" ) ! = NULL) { 1261dev - > props . use_shared_buffers = false; 1262} 1263if ( getenv ( "GGML_METAL_SHARED_BUFFERS_ENABLE" ) ! = NULL) { 1264dev - > props . use_shared_buffers = true; 1265} 1266 1267dev - > props . supports_gpu_family_apple7 = [ dev - > mtl_device supportsFamily : MTLGPUFamilyApple7 ]; 1268 1269dev - > props . device_id = ggml_metal_device_id_parse([[ dev - > mtl_device name] UTF8String]); 1270 1271dev - > props . op_offload_min_batch_size = getenv( "GGML_OP_OFFLOAD_MIN_BATCH" ) ? atoi ( getenv ( "GGML_OP_OFFLOAD_MIN_BATCH" )) : 32 ; 1272 1273dev - > props . max_buffer_size = dev - > mtl_device . maxBufferLength ; 1274dev - > props . max_theadgroup_memory_size = dev - > mtl_device . maxThreadgroupMemoryLength ; 1275if ( @ available (macOS 10.12, iOS 16.0, * )) { 1276dev - > props . max_working_set_size = dev -> mtl_device . recommendedMaxWorkingSetSize ; 1277} else { 1278dev - > props . max_working_set_size = dev - > mtl_device . maxBufferLength ; 1279} 1280 1281{ 1282const char * val = getenv( "GGML_METAL_FUSION_DEBUG" ); 1283dev - > finfo = ggml_metal_fusion_info_init( 1284get env( "GGML_METAL_FUSION_DISABLE" ) == nil , 1285v al ? atoi ( val ) : 0 ); 1286} 1287 1288snprintf ( dev - > props . name , sizeof ( dev - > props . name ), "%s%d" , "MTL" , device ); 1289const char * gpu_name = [[dev->mtl_device name] UTF8String]; 1290if ( n_devices > 1 ) { 1291snprintf ( dev - > props . desc , sizeof ( dev - > props . desc ), "%s (dev p%d/v%d)" , 1292g pu_name , dev - > props . device_phys , dev - > props . device_virt ); 1293} else { 1294snprintf ( dev - > props . desc , sizeof ( dev - > props . desc ), "%s" , gpu_name ); 1295} 1296 1297dev - > library = ggml_metal_library_init( dev ); 1298if (! dev - > library ) { 1299GGML_LOG_ERROR ( "%s: error: failed to create library\n" , __func__ ); 1300} 1301 1302if ( dev - > props . use_residency_sets ) { 1303dev - > rsets = ggml_metal_rsets_init( dev ); 1304} else { 1305dev - > rsets = nil; 1306} 1307 1308// print MTL GPU family: 1309GGML_LOG_INFO ( "%s: GPU name: %s (%s)\n" , __func__ , dev - > props . name , dev - > props . desc ); 1310 1311// determine max supported GPU family 1312// https :// developer . apple . com / metal / Metal - Shading - Language - Specification . // https :// developer . apple . com / metal / Metal - Feature - Set - Tables . { 1315for ( int i = MTLGPUFamilyApple1 + 20 ; i >= MTLGPUFamilyApple1 ; -- i ) { 1316if ([ dev - > mtl_device supportsFamily : i ]) { 1317dev - > props . gpu_family = i - ( int ) MTLGPUFamilyApple1 + 1 ; 1318GGML_LOG_INFO ( "%s: GPU family: MTLGPUFamilyApple%d (%d)\n" , __func__ , dev - > props . gpu_family , i ); 1319break; 1320} 1321} 1322 1323for ( int i = MTLGPUFamilyCommon1 + 5 ; i >= MTLGPUFamilyCommon1 ; -- i ) { 1324if ([ dev - > mtl_device supportsFamily : i ]) { 1325GGML_LOG_INFO ( "%s: GPU family: MTLGPUFamilyCommon%d (%d)\n" , __func__ , i - ( int ) MTLGPUFamilyCommon1 + 1 , i ); 1326break; 1327} 1328} 1329 1330for ( int i = MTLGPUFamilyMetal3_GGML + 5 ; i >= MTLGPUFamilyMetal3_GGML ; -- i ) { 1331if ([ dev - > mtl_device supportsFamily : i ]) { 1332GGML_LOG_INFO ( "%s: GPU family: MTLGPUFamilyMetal%d (%d)\n" , __func__ , i - ( int ) MTLGPUFamilyMetal3_GGML + 3 , i ); 1333break; 1334} 1335} 1336} 1337 1338G GML_LOG_INFO ( "%s: simdgroup reduction = %s\n" , __func__ , dev - > props . has_simdgroup_reduction ? "true" : "false" ); 1339GGML_LOG_INFO ( "%s: simdgroup matrix mul. = %s\n" , __func__ , dev - > props . has_simdgroup_mm ? "true" : "false" ); 1340GGML_LOG_INFO ( "%s: has unified memory = %s\n" , __func__ , dev - > props . has_unified_memory ? "true" : "false" ); 1341GGML_LOG_INFO ( "%s: has bfloat = %s\n" , __func__ , dev - > props . has_bfloat ? "true" : "false" ); 1342GGML_LOG_INFO ( "%s: has tensor = %s\n" , __func__ , dev - > props . has_tensor ? "true" : "false" ); 1343GGML_LOG_INFO ( "%s: use residency sets = %s\n" , __func__ , dev - > props . use_residency_sets ? "true" : "false" ); 1344GGML_LOG_INFO ( "%s: use shared buffers = %s\n" , __func__ , dev - > props . use_shared_buffers ? "true" : "false" ); 1345 1346# if TARGET_OS_OSX || ( TARGET_OS_IOS && __clang_major__ >= 15 ) 1347if ( @ available (macOS 10.12, iOS 16.0, * )) { 1348GGML_LOG_INFO ( "%s: recommendedMaxWorkingSetSize = %8.2f MB\n" , __func__ , dev - > props . max_working_set_size / 1e6 ); 1349} 1350# endif 1351} 1352} 1353} 1354 1355return dev; 1356} 1357 1358v oid ggml_metal_device_free(ggml_metal_device_t dev) { 1359assert ( dev ! = NULL ); 1360 1361@ autoreleasepool { 1362ggml_metal_fusion_info_free ( dev - > finfo ); 1363 1364ggml_metal_rsets_free ( dev - > rsets ); 1365 1366ggml_metal_library_free ( dev - > library ); 1367dev - > library = NULL; 1368 1369if ( dev - > mtl_queue ) { 1370[ dev - > mtl_queue release]; 1371dev - > mtl_queue = nil; 1372} 1373 1374if ( dev - > mtl_device ) { 1375[ dev - > mtl_device release]; 1376dev - > mtl_device = nil; 1377} 1378} 1379 1380free ( dev ); 1381} 1382 1383v oid * ggml_metal_device_get_obj ( ggml_metal_device_t dev) { 1384return dev - > mtl_device ; 1385} 1386 1387v oid * ggml_metal_device_get_queue ( ggml_metal_device_t dev) { 1388return dev - > mtl_queue ; 1389} 1390 1391g gml_metal_library_t ggml_metal_device_get_library(ggml_metal_device_t dev) { 1392return dev - >library; 1393} 1394 1395v oid ggml_metal_device_rsets_add(ggml_metal_device_t dev, ggml_metal_rset_t rset) { 1396if ( rset == nil ) { 1397return; 1398} 1399 1400G GML_ASSERT ( dev - > rsets ); 1401 1402[ dev - > rsets - > lock lock]; 1403 1404[ dev - > rsets - > data addObject : rset ]; 1405 1406[ dev - > rsets - > lock unlock]; 1407} 1408 1409v oid ggml_metal_device_rsets_rm(ggml_metal_device_t dev, ggml_metal_rset_t rset) { 1410if ( rset == nil ) { 1411return; 1412} 1413 1414G GML_ASSERT ( dev - > rsets ); 1415 1416[ dev - > rsets - > lock lock]; 1417 1418[ dev - > rsets - > data removeObject : rset ]; 1419 1420[ dev - > rsets - > lock unlock]; 1421} 1422 1423v oid ggml_metal_device_rsets_keep_alive(ggml_metal_device_t dev) { 1424if ( dev - > rsets == NULL ) { 1425return; 1426} 1427 1428a tomic_store_explicit (& dev - > rsets - > d_loop , dev - > rsets - > loops_per_s * dev - > rsets - > keep_alive_s , memory_order_relaxed ); 1429} 1430 1431s truct ggml_metal_event { 1432void * obj ; // id< MTLSharedEvent > 1433 1434a tomic_int value; 1435}; 1436 1437void ggml_metal_event_encode_signal(ggml_metal_event_t ev, ggml_metal_cmd_buf_t cmd_buf_raw) { 1438id < MTLSharedEvent > event = ( id < MTLSharedEvent >) ev - > obj ; 1439 1440id < MTLCommandBuffer > cmd_buf = ( id < MTLCommandBuffer >) cmd_buf_raw; 1441 1442[ cmd_buf encodeSignalEvent : event value : atomic_fetch_add_explicit (& ev - > value , 1 , memory_order_relaxed ) + 1 ]; 1443} 1444 1445v oid ggml_metal_event_encode_wait( ggml_metal_event_t ev, ggml_metal_cmd_buf_t cmd_buf_raw) { 1446i d < MTLSharedEvent > event = ( id < MTLSharedEvent >) ev - > obj ; 1447 1448id < MTLCommandBuffer > cmd_buf = ( id < MTLCommandBuffer >) cmd_buf_raw; 1449 1450[ cmd_buf encodeWaitForEvent : event value : atomic_load_explicit (& ev - > value , memory_order_relaxed )]; 1451} 1452 1453g gml_metal_event_t ggml_metal_device_event_init( ggml_metal_device_t dev) { 1454i d < MTLSharedEvent > event = [dev - > mtl_device newSharedEvent]; 1455 1456ggml_metal_event_t ev = calloc(1, sizeof(struct ggml_metal_event)); 1457 1458ev - > obj = ( __bridge void * ) event ; 1459ev - > value = 0; 1460 1461return ev; 1462} 1463 1464v oid ggml_metal_device_event_free(ggml_metal_device_t dev, ggml_metal_event_t ev) { 1465@ autoreleasepool { 1466id < MTLSharedEvent > event = ev - > obj ; 1467[ event release]; 1468 1469free ( ev ); 1470 1471GGML_UNUSED ( dev ); 1472} 1473} 1474 1475v oid ggml_metal_device_event_synchronize(ggml_metal_device_t dev, ggml_metal_event_t ev) { 1476id < MTLSharedEvent > event = ev - > obj ; 1477const bool res = [event waitUntilSignaledValue:atomic_load_explicit(&ev->value, memory_order_relaxed) timeoutMS:60000]; 1478if (! res ) { 1479GGML_ABORT ( "%s: failed to wait for event\n" , __func__ ); 1480} 1481 1482GGML_UNUSED ( dev ); 1483} 1484 1485v oid ggml_metal_device_get_memory(ggml_metal_device_t dev, size_t * free, size_t * total) { 1486if ( @ available (macOS 10.12, iOS 16.0, * )) { 1487* total = dev - > mtl_device . recommendedMaxWorkingSetSize ; 1488size_t cur = dev - > mtl_device . currentAllocatedSize ; 1489// it ' s possible to allocate more than ` recommendedMaxWorkingSetSize ` 1490* free = * total > cur ? * total - cur : 0 ; 1491} else { 1492* free = 0; 1493* total = 0; 1494} 1495} 1496 1497s tatic bool ggml_metal_supports_mul_mat_op( 1498bool has_simdgroup_reduction, 1499const struct ggml_tensor * op, 1500bool src0_f16_has_mv, 1501bool mm_path) { 1502if (! has_simdgroup_reduction || 1503o p - > src [ 0 ] - > type == GGML_TYPE_NVFP4 || 1504o p - > src [ 0 ] - > type == GGML_TYPE_TQ1_0 ) { 1505return false; 1506} 1507 1508if ( op - > src [ 1 ] - > type ! = GGML_TYPE_F16) { 1509return true; 1510} 1511 1512if ( op - > src [ 0 ] - > type == GGML_TYPE_BF16 ) { 1513return false; 1514} 1515 1516if ( src0_f16_has_mv && op - > src [ 0 ] - > type == GGML_TYPE_F16 ) { 1517return true; 1518} 1519 1520return mm_path; 1521} 1522 1523b ool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_tensor * op) { 1524const bool has_simdgroup_mm = dev->props.has_simdgroup_mm; 1525const bool has_simdgroup_reduction = dev->props.has_simdgroup_reduction; 1526const bool has_bfloat = dev->props.has_bfloat; 1527 1528if (! has_bfloat ) { 1529if ( op - > type == GGML_TYPE_BF16 ) { 1530return false; 1531} 1532 1533for ( size_t i = 0 , n = 3 ; i < n ; ++ i ) { 1534if ( op - > src [ i ] ! = NULL && op - > src [ i ] - > type == GGML_TYPE_BF16 ) { 1535return false; 1536} 1537} 1538} 1539 1540switch ( op - > op ) { 1541case GGML_OP_SCALE : 1542case GGML_OP_FILL : 1543case GGML_OP_CLAMP : 1544case GGML_OP_SQR : 1545case GGML_OP_SQRT : 1546case GGML_OP_SIN : 1547case GGML_OP_COS : 1548case GGML_OP_LOG : 1549return ggml_is_contiguous_rows( op - > src [ 0 ]) && ( op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 ); 1550case GGML_OP_UNARY : 1551switch ( ggml_get_unary_op ( op )) { 1552case GGML_UNARY_OP_TANH : 1553case GGML_UNARY_OP_RELU : 1554case GGML_UNARY_OP_SIGMOID : 1555case GGML_UNARY_OP_GELU : 1556case GGML_UNARY_OP_GELU_ERF : 1557case GGML_UNARY_OP_GELU_QUICK : 1558case GGML_UNARY_OP_SILU : 1559case GGML_UNARY_OP_ELU : 1560case GGML_UNARY_OP_NEG : 1561case GGML_UNARY_OP_ABS : 1562case GGML_UNARY_OP_SGN : 1563case GGML_UNARY_OP_STEP : 1564case GGML_UNARY_OP_HARDSWISH : 1565case GGML_UNARY_OP_HARDSIGMOID : 1566case GGML_UNARY_OP_EXP : 1567case GGML_UNARY_OP_SOFTPLUS : 1568case GGML_UNARY_OP_EXPM1 : 1569case GGML_UNARY_OP_FLOOR : 1570case GGML_UNARY_OP_CEIL : 1571case GGML_UNARY_OP_ROUND : 1572case GGML_UNARY_OP_TRUNC : 1573case GGML_UNARY_OP_XIELU : 1574return ggml_is_contiguous_rows( op - > src [ 0 ]) && ( op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 ); 1575default : 1576return false; 1577} 1578case GGML_OP_SILU_BACK : 1579return ( op - > src [ 0 ] - > type == GGML_TYPE_F32 ) && 1580( op - > src [ 1 ] - > type == GGML_TYPE_F32 ) && 1581( op - > type == GGML_TYPE_F32 ) && 1582g gml_is_contiguous ( op - > src [ 0 ]) && 1583g gml_is_contiguous ( op - > src [ 1 ]) && 1584g gml_is_contiguous ( op ) && 1585g gml_are_same_shape ( op - > src [ 0 ], op - > src [ 1 ]); 1586case GGML_OP_GLU : 1587switch ( ggml_get_glu_op ( op )) { 1588case GGML_GLU_OP_REGLU : 1589case GGML_GLU_OP_GEGLU : 1590case GGML_GLU_OP_SWIGLU : 1591case GGML_GLU_OP_SWIGLU_OAI : 1592case GGML_GLU_OP_GEGLU_ERF : 1593case GGML_GLU_OP_GEGLU_QUICK : 1594case GGML_GLU_OP_SWIGLU_CLAMP : 1595return ggml_is_contiguous_1( op - > src [ 0 ]) && ( op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 ); 1596default : 1597return false; 1598} 1599case GGML_OP_NONE : 1600case GGML_OP_RESHAPE : 1601case GGML_OP_VIEW : 1602case GGML_OP_TRANSPOSE : 1603case GGML_OP_PERMUTE : 1604return true; 1605case GGML_OP_CONCAT : 1606{ 1607const enum ggml_type src0_type = op - > src [ 0 ] - > type ; 1608const enum ggml_type src1_type = op - > src [ 1 ] - > type ; 1609if ( src0_type ! = src1_type || src0_type ! = op - > type ) { 1610return false; 1611} 1612switch (src0_type) { 1613case GGML_TYPE_F32 : 1614case GGML_TYPE_F16 : 1615case GGML_TYPE_I8 : 1616case GGML_TYPE_I16 : 1617case GGML_TYPE_I32 : 1618case GGML_TYPE_I64 : 1619return true; 1620case GGML_TYPE_BF16 : 1621return has_bfloat; 1622case GGML_TYPE_Q4_0 : 1623case GGML_TYPE_Q4_1 : 1624case GGML_TYPE_Q5_0 : 1625case GGML_TYPE_Q5_1 : 1626case GGML_TYPE_Q8_0 : 1627return true; 1628default : 1629return false; 1630} 1631} 1632case GGML_OP_ADD : 1633case GGML_OP_SUB : 1634case GGML_OP_MUL : 1635case GGML_OP_DIV : 1636case GGML_OP_ADD_ID : 1637return ggml_is_contiguous_rows( op - > src [ 0 ]) && ggml_is_contiguous_rows ( op - > src [ 1 ]) && ( op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 ) && ( op - > src [ 0 ] - > type == op - > src [ 1 ] - > type ); 1638case GGML_OP_ACC : 1639return ggml_is_contiguous_rows( op - > src [ 0 ]) && ggml_is_contiguous_rows ( op - > src [ 1 ]) && op - > src [ 0 ] - > type == GGML_TYPE_F32 ; 1640case GGML_OP_REPEAT : 1641case GGML_OP_CONV_TRANSPOSE_1D : 1642return true; 1643case GGML_OP_CONV_TRANSPOSE_2D : 1644return ggml_is_contiguous( op - > src [ 0 ]) && ggml_is_contiguous ( op - > src [ 1 ]) && 1645( op - > src [ 0 ] - > type == GGML_TYPE_F16 || op - > src [ 0 ] - > type == GGML_TYPE_F32 ) && 1646o p - > src [ 1 ] - > type == GGML_TYPE_F32 && 1647o p - > type == GGML_TYPE_F32 ; 1648case GGML_OP_COL2IM_1D : 1649return ( op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 || op - > src [ 0 ] - > type == GGML_TYPE_BF16 ) && 1650o p - > type == op - > src [ 0 ] - > type && 1651g gml_is_contiguous ( op - > src [ 0 ]) && 1652g gml_is_contiguous ( op ); 1653case GGML_OP_CONV_3D : 1654return ggml_is_contiguous( op - > src [ 0 ]) && 1655g gml_is_contiguous ( op - > src [ 1 ]) && 1656( op - > src [ 0 ] - > type == GGML_TYPE_F16 || op - > src [ 0 ] - > type == GGML_TYPE_F32 ) && 1657o p - > src [ 1 ] - > type == GGML_TYPE_F32 ; 1658case GGML_OP_SUM : 1659return has_simdgroup_reduction && ggml_is_contiguous ( op - > src [ 0 ]); 1660case GGML_OP_TRI : 1661return ggml_is_contiguous_rows( op - > src [ 0 ]); 1662case GGML_OP_SUM_ROWS : 1663case GGML_OP_CUMSUM : 1664case GGML_OP_MEAN : 1665case GGML_OP_SOFT_MAX : 1666case GGML_OP_GROUP_NORM : 1667case GGML_OP_L2_NORM : 1668return has_simdgroup_reduction && ggml_is_contiguous_rows ( op - > src [ 0 ]); 1669case GGML_OP_COUNT_EQUAL : 1670return has_simdgroup_reduction && 1671o p - > src [ 0 ] - > type == GGML_TYPE_I32 && 1672o p - > src [ 1 ] - > type == GGML_TYPE_I32 && 1673o p - > type == GGML_TYPE_I64 ; 1674case GGML_OP_ARGMAX : 1675return has_simdgroup_reduction; 1676case GGML_OP_NORM : 1677case GGML_OP_RMS_NORM : 1678return has_simdgroup_reduction && ( ggml_is_contiguous_rows ( op - > src [ 0 ])); 1679case GGML_OP_ROPE : 1680case GGML_OP_ROPE_BACK : 1681return true; 1682case GGML_OP_IM2COL : 1683return ggml_is_contiguous( op - > src [ 1 ]) && op - > src [ 1 ] - > type == GGML_TYPE_F32 && ( op - > type == GGML_TYPE_F16 || op - > type == GGML_TYPE_F32 ); 1684case GGML_OP_CONV_2D : 1685return ggml_is_contiguous( op - > src [ 0 ]) && 1686o p - > src [ 1 ] - > type == GGML_TYPE_F32 && 1687o p - > type == GGML_TYPE_F32 && 1688( op - > src [ 0 ] - > type == GGML_TYPE_F16 || op - > src [ 0 ] - > type == GGML_TYPE_F32 ); 1689case GGML_OP_CONV_2D_DW : 1690return op - > src [ 1 ] - > type == GGML_TYPE_F32 && 1691o p - > type == GGML_TYPE_F32 && 1692( op - > src [ 0 ] - > type == GGML_TYPE_F16 || op - > src [ 0 ] - > type == GGML_TYPE_F32 ); 1693case GGML_OP_UPSCALE : 1694return op - > src [ 0 ] - > type == GGML_TYPE_F32 ; 1695case GGML_OP_POOL_1D : 1696return ggml_is_contiguous( op - > src [ 0 ]) && op - > src [ 0 ] - > type == GGML_TYPE_F32 ; 1697case GGML_OP_POOL_2D : 1698return op - > src [ 0 ] - > type == GGML_TYPE_F32 ; 1699case GGML_OP_PAD : 1700// TODO : add circular padding support for metal, see https://github.com/ggml-org/llama.cpp/pull/16985 1701if ( ggml_get_op_params_i32 ( op , 8 ) ! = 0) { 1702return false; 1703} 1704 1705return ( ggml_get_op_params_i32 ( op , 0 ) == 0 ) && ( ggml_get_op_params_i32 ( op , 2 ) == 0 ) && 1706( ggml_get_op_params_i32 ( op , 4 ) == 0 ) && ( ggml_get_op_params_i32 ( op , 6 ) == 0 ); 1707case GGML_OP_PAD_REFLECT_1D : 1708case GGML_OP_TIMESTEP_EMBEDDING : 1709return op - >src[0] - > type == GGML_TYPE_F32 ; 1710case GGML_OP_LEAKY_RELU : 1711return op - > src [ 0 ] - > type == GGML_TYPE_F32 || op - > src [ 0 ] - > type == GGML_TYPE_F16 ; 1712case GGML_OP_ARGSORT : 1713case GGML_OP_TOP_K : 1714case GGML_OP_ARANGE : 1715return true; 1716case GGML_OP_ROLL : 1717return ggml_is_contiguous( op - > src [ 0 ]); 1718case GGML_OP_FLASH_ATTN_EXT : 1719// for new head sizes, add checks here 1720if ( op - > src [ 0 ] - > ne [ 0 ] ! = 32 && 1721o p - > src [ 0 ] - > ne [ 0 ] ! = 40 && 1722o p - > src [ 0 ] - > ne [ 0 ] ! = 48 && 1723o p - > src [ 0 ] - > ne [ 0 ] ! = 64 && 1724o p - > src [ 0 ] - > ne [ 0 ] ! = 72 && 1725o p - > src [ 0 ] - > ne [ 0 ] ! = 80 && 1726o p - > src [ 0 ] - > ne [ 0 ] ! = 96 && 1727o p - > src [ 0 ] - > ne [ 0 ] ! = 112 && 1728o p - > src [ 0 ] - > ne [ 0 ] ! = 128 && 1729o p - > src [ 0 ] - > ne [ 0 ] ! = 192 && 1730o p - > src [ 0 ] - > ne [ 0 ] ! = 256 && 1731o p - > src [ 0 ] - > ne [ 0 ] ! = 320 && 1732o p - > src [ 0 ] - > ne [ 0 ] ! = 512 && 1733o p - > src [ 0 ] - > ne [ 0 ] ! = 576) { 1734return false; 1735} 1736if ( op - > src [ 1 ] - > type ! = op - > src [ 2 ] - > type ) { 1737return false; 1738} 1739switch ( op - > src [ 1 ] - > type ) { 1740case GGML_TYPE_F32 : 1741case GGML_TYPE_F16 : 1742case GGML_TYPE_Q8_0 : 1743case GGML_TYPE_Q4_0 : 1744case GGML_TYPE_Q4_1 : 1745case GGML_TYPE_Q5_0 : 1746case GGML_TYPE_Q5_1 : 1747break ; 1748case GGML_TYPE_BF16 : 1749if (! has_bfloat ) { 1750return false; 1751} 1752break ; 1753default : 1754return false; 1755} 1756return has_simdgroup_mm; // TODO : over - restricted for vec - kernels 1757case GGML_OP_LIGHTNING_INDEXER : 1758if ( op - > src [ 0 ] - > ne [ 0 ] ! = OP_LIGHTNING_INDEXER_DK || 1759o p - > src [ 0 ] - > ne [ 1 ] ! = OP_LIGHTNING_INDEXER_NH) { 1760return false; 1761} 1762if (! has_simdgroup_mm || 1763o p - > src [ 0 ] - > type ! = GGML_TYPE_F32 || 1764o p - > src [ 2 ] - > type ! = GGML_TYPE_F32 || 1765o p - > src [ 3 ] - > type ! = GGML_TYPE_F16 || 1766o p - > type ! = GGML_TYPE_F32 || 1767! ggml_is_contiguous_rows ( op - > src [ 0 ]) || 1768! ggml_is_contiguous_rows ( op - > src [ 1 ]) || 1769! ggml_is_contiguous_rows ( op - > src [ 2 ]) || 1770! ggml_is_contiguous_rows ( op - > src [ 3 ])) { 1771return false; 1772} 1773switch ( op - > src [ 1 ] - > type ) { 1774case GGML_TYPE_F32 : 1775case GGML_TYPE_F16 : 1776case GGML_TYPE_Q4_0 : 1777case GGML_TYPE_Q4_1 : 1778case GGML_TYPE_Q5_0 : 1779case GGML_TYPE_Q5_1 : 1780case GGML_TYPE_Q8_0 : 1781return true; 1782case GGML_TYPE_BF16 : 1783return has_bfloat; 1784default : 1785return false; 1786} 1787case GGML_OP_DSV4_HC_COMB : 1788return has_simdgroup_reduction && 1789o p - > src [ 0 ] - > type == GGML_TYPE_F32 && 1790o p - > src [ 1 ] - > type == GGML_TYPE_F32 && 1791o p - > src [ 2 ] - > type == GGML_TYPE_F32 && 1792o p - > type == GGML_TYPE_F32 && 1793o p - > src [ 0 ] - > ne [ 0 ] == 24 && 1794o p - > src [ 1 ] - > ne [ 0 ] >= 3 && 1795o p - > src [ 2 ] - > ne [ 0 ] == 24 && 1796g gml_is_contiguous_rows ( op - > src [ 0 ]) && 1797g gml_is_contiguous_rows ( op - > src [ 1 ]) && 1798g gml_is_contiguous_rows ( op - > src [ 2 ]); 1799case GGML_OP_DSV4_HC_PRE : 1800return has_simdgroup_reduction && 1801o p - > src [ 0 ] - > type == GGML_TYPE_F32 && 1802o p - > src [ 1 ] - > type == GGML_TYPE_F32 && 1803o p - > type == GGML_TYPE_F32 && 1804o p - > src [ 0 ] - > ne [ 1 ] == 4 && 1805o p - > src [ 1 ] - > ne [ 0 ] == 4 && 1806g gml_is_contiguous_rows ( op - > src [ 0 ]) && 1807g gml_is_contiguous_rows ( op - > src [ 1 ]); 1808case GGML_OP_DSV4_HC_POST : 1809return has_simdgroup_reduction && 1810o p - > src [ 0 ] - > type == GGML_TYPE_F32 && 1811o p - > src [ 1 ] - > type == GGML_TYPE_F32 && 1812o p - > src [ 2 ] - > type == GGML_TYPE_F32 && 1813o p - > src [ 3 ] - > type == GGML_TYPE_F32 && 1814o p - > type == GGML_TYPE_F32 && 1815o p - > src [ 1 ] - > ne [ 1 ] == 4 && 1816o p - > src [ 2 ] - > ne [ 0 ] == 4 && 1817o p - > src [ 3 ] - > ne [ 0 ] == 4 && 1818o p - > src [ 3 ] - > ne [ 1 ] == 4 && 1819g gml_is_contiguous_rows ( op - > src [ 0 ]) && 1820g gml_is_contiguous_rows ( op - > src [ 1 ]) && 1821g gml_is_contiguous_rows ( op - > src [ 2 ]) && 1822g gml_is_contiguous_rows ( op - > src [ 3 ]); 1823case GGML_OP_SSM_SCAN : 1824return has_simdgroup_reduction; 1825case GGML_OP_SSM_CONV : 1826return has_simdgroup_reduction; 1827case GGML_OP_RWKV_WKV6 : 1828case GGML_OP_RWKV_WKV7 : 1829return true; 1830case GGML_OP_GATED_DELTA_NET : 1831return has_simdgroup_reduction && op - > src [ 2 ] - > ne [ 0 ] % 32 == 0; 1832case GGML_OP_SOLVE_TRI : 1833return has_simdgroup_reduction && op - > src [ 0 ] - > type == GGML_TYPE_F32 ; 1834case GGML_OP_MUL_MAT : 1835return ggml_metal_supports_mul_mat_op( 1836h as_simdgroup_reduction , op , true , 1837g gml_metal_op_mul_mat_use_mm ( op , has_simdgroup_mm )); 1838case GGML_OP_MUL_MAT_ID : 1839return ggml_metal_supports_mul_mat_op( 1840h as_simdgroup_reduction , op , false , 1841g gml_metal_op_mul_mat_id_use_mm ( op , has_simdgroup_mm )); 1842case GGML_OP_SET : 1843case GGML_OP_CPY : 1844case GGML_OP_DUP : 1845case GGML_OP_CONT : 1846{ 1847switch ( op - > src [ 0 ] - > type ) { 1848case GGML_TYPE_F32 : 1849switch ( op - > type ) { 1850case GGML_TYPE_F32 : 1851case GGML_TYPE_F16 : 1852case GGML_TYPE_BF16 : 1853case GGML_TYPE_Q8_0 : 1854case GGML_TYPE_Q1_0 : 1855case GGML_TYPE_Q2_0 : 1856case GGML_TYPE_Q4_0 : 1857case GGML_TYPE_Q4_1 : 1858case GGML_TYPE_Q5_0 : 1859case GGML_TYPE_Q5_1 : 1860case GGML_TYPE_IQ4_NL : 1861case GGML_TYPE_TQ2_0 : 1862case GGML_TYPE_I32 : 1863return true; 1864d efault : 1865return false; 1866} 1867case GGML_TYPE_F16 : 1868switch ( op - > type ) { 1869case GGML_TYPE_F32 : 1870case GGML_TYPE_F16 : 1871return true; 1872d efault : 1873return false; 1874} 1875case GGML_TYPE_BF16 : 1876switch ( op - > type ) { 1877case GGML_TYPE_F32 : 1878case GGML_TYPE_BF16 : 1879return true; 1880d efault : 1881return false; 1882} 1883case GGML_TYPE_Q1_0 : 1884case GGML_TYPE_Q2_0 : 1885case GGML_TYPE_Q4_0 : 1886case GGML_TYPE_Q4_1 : 1887case GGML_TYPE_Q5_0 : 1888case GGML_TYPE_Q5_1 : 1889case GGML_TYPE_Q8_0 : 1890case GGML_TYPE_TQ2_0 : 1891switch ( op - > type ) { 1892case GGML_TYPE_F32 : 1893case GGML_TYPE_F16 : 1894return true; 1895d efault : 1896return false; 1897} 1898case GGML_TYPE_I32 : 1899return op - > type == GGML_TYPE_F32 || op - > type == GGML_TYPE_I32 ; 1900d efault : 1901return false; 1902}; 1903} 1904case GGML_OP_GET_ROWS : 1905return op - > src [ 0 ] - > type ! = GGML_TYPE_NVFP4 && 1906o p - > src [ 0 ] - > type ! = GGML_TYPE_TQ1_0; 1907case GGML_OP_SET_ROWS : 1908{ 1909if ( op - > src [ 0 ] - > type == GGML_TYPE_F16 ) { 1910return op - > type == GGML_TYPE_F16 ; 1911} 1912 1913if ( op - > src [ 0 ] - > type ! = GGML_TYPE_F32) { 1914return false; 1915} 1916 1917switch (op - > type ) { 1918case GGML_TYPE_F32 : 1919case GGML_TYPE_F16 : 1920case GGML_TYPE_BF16 : 1921case GGML_TYPE_Q8_0 : 1922case GGML_TYPE_Q4_0 : 1923case GGML_TYPE_Q4_1 : 1924case GGML_TYPE_Q5_0 : 1925case GGML_TYPE_Q5_1 : 1926case GGML_TYPE_IQ4_NL : 1927case GGML_TYPE_TQ2_0 : 1928return true; 1929d efault : 1930return false; 1931}; 1932} 1933case GGML_OP_DIAG : 1934return true; 1935case GGML_OP_OPT_STEP_ADAMW : 1936case GGML_OP_OPT_STEP_SGD : 1937return has_simdgroup_reduction; 1938d efault : 1939return false; 1940} 1941} 1942 1943con st struct ggml_metal_device_props * ggml_metal_device_get_props ( ggml_metal_device_t dev) { 1944return & dev - > props ; 1945} 1946 1947static void ggml_metal_device_disable_tensor( ggml_metal_device_t dev) { 1948d ev - > props . has_tensor = false; 1949} 1950 1951struct ggml_metal_fusion_info * ggml_metal_device_get_fusion_info ( ggml_metal_device_t dev) { 1952return dev - > finfo ; 1953} 1954 1955// 1956/ / device buffers 1957// 1958 1959/ / max memory buffers that can be mapped to the device 1960# define GGML_METAL_MAX_BUFFERS 64 1961 1962s truct ggml_metal_buffer_wrapper { 1963v oid * data ; 1964s ize_t size; 1965 1966i d < MTLBuffer > metal ; 1967}; 1968 1969s truct ggml_metal_buffer { 1970v oid * all_data ; 1971s ize_t all_size; 1972 1973// if false, the Metal buffer data is allocated in private GPU memory and is not shared with the host 1974b ool is_shared; 1975b ool owned; 1976 1977/ / multiple buffers are used only to avoid the maximum buffer size limitation when using mmap 1978i nt n_buffers; 1979s truct ggml_metal_buffer_wrapper buffers[ GGML_METAL_MAX_BUFFERS ]; 1980 1981b ool use_residency_sets; 1982 1983/ / optional MTLResidencySet 1984/ / note : cannot use explicitly "id<MTLResidencySet>" here because it is not available on certain OSes 1985i d rset; 1986 1987/ / pointers to global device 1988g gml_metal_device_t dev; 1989}; 1990 1991static void ggml_metal_log_allocated_size( id < MTLDevice > device , size_t size_aligned) { 1992# ifndef GGML_METAL_NDEBUG 1993# if TARGET_OS_OSX || ( TARGET_OS_IOS && __clang_major__ >= 15 ) 1994if ( @ available (macOS 10.12, iOS 16.0, * )) { 1995GGML_LOG_DEBUG ( "%s: allocated buffer, size = %8.2f MiB, (%8.2f / %8.2f)\n" , 1996_ _func__ , 1997s ize_aligned / 1024.0 / 1024.0 , 1998d evice . currentAllocatedSize / 1024.0 / 1024.0 , 1999d evice . recommendedMaxWorkingSetSize / 1024.0 / 1024.0 ); 2000 2001if ( device . currentAllocatedSize > device . recommendedMaxWorkingSetSize ) { 2002GGML_LOG_WARN ( "%s: warning: current allocated size is greater than the recommended max working set size\n" , __func__ ); 2003} 2004} else { 2005GGML_LOG_INFO ( "%s: allocated buffer, size = %8.2f MiB, (%8.2f)\n" , 2006_ _func__ , 2007s ize_aligned / 1024.0 / 1024.0 , 2008d evice . currentAllocatedSize / 1024.0 / 1024.0 ); 2009} 2010# endif 2011# endif 2012G GML_UNUSED ( device ); 2013G GML_UNUSED ( size_aligned ); 2014} 2015 2016/ / rset init 2017s tatic bool ggml_metal_buffer_rset_init( ggml_metal_buffer_t buf) { 2018b uf - > rset = nil; 2019 2020if (! buf - > use_residency_sets ) { 2021return true; 2022} 2023 2024# if defined( GGML_METAL_HAS_RESIDENCY_SETS ) 2025if ( @ available (macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, * )) { 2026MTLResidencySetDescriptor * desc = [[ MTLResidencySetDescriptor alloc] init]; 2027desc . label = @ "ggml_metal" ; 2028desc . initialCapacity = buf - > n_buffers ; 2029 2030NSError * error ; 2031buf - > rset = [ buf - > dev - > mtl_device newResidencySetWithDescriptor : desc error : & error ]; 2032if ( error ) { 2033GGML_LOG_ERROR ( "%s: error: %s\n" , __func__ , [[ error description] UTF8String]); 2034[ desc release]; 2035return false; 2036} 2037 2038[ desc release]; 2039 2040for ( int i = 0; i < buf - > n_buffers ; i ++ ) { 2041[ buf - > rset addAllocation : buf - > buffers [ i ].metal]; 2042} 2043 2044[ buf - > rset commit]; 2045[ buf - > rset requestResidency]; 2046 2047return true; 2048} 2049#endif 2050 2051return true; 2052} 2053 2054/ / rset free 2055s tatic void ggml_metal_buffer_rset_free( ggml_metal_buffer_t buf) { 2056# if defined( GGML_METAL_HAS_RESIDENCY_SETS ) 2057if ( @ available (macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, * )) { 2058if ( buf - > rset ) { 2059[ buf - > rset endResidency]; 2060[ buf - > rset removeAllAllocations]; 2061[ buf - > rset commit]; 2062[ buf - > rset release]; 2063} 2064} 2065# else 2066G GML_UNUSED ( buf ); 2067# endif 2068} 2069 2070static void * ggml_metal_host_malloc ( size_t n) { 2071v oid * data = NULL; 2072 2073# if TARGET_OS_OSX 2074k ern_return_t err = vm_allocate(( vm_map_t ) mach_task_self(), ( void * ) & data , n , VM_FLAGS_ANYWHERE ); 2075if ( err ! = KERN_SUCCESS ) { 2076G GML_LOG_ERROR ( "%s: error: vm_allocate failed\n" , __func__ ); 2077return NULL; 2078} 2079# else 2080con st int result = posix_memalign(( void ** ) & data , sysconf ( _SC_PAGESIZE ), n ); 2081if (result ! = 0) { 2082G GML_LOG_ERROR ( "%s: error: posix_memalign failed\n" , __func__ ); 2083return NULL; 2084} 2085#endif 2086 2087return data; 2088} 2089 2090ggml_metal_buffer_t ggml_metal_buffer_init( ggml_metal_device_t dev, size_t size, bool shared) { 2091g gml_metal_buffer_t res = calloc ( 1 , sizeof ( struct ggml_metal_buffer)); 2092 2093re s - > dev = dev; 2094 2095con st size_t size_page = sysconf( _SC_PAGESIZE ); 2096 2097s ize_t size_aligned = size; 2098if (( size_aligned % size_page) != 0) { 2099size_aligned += ( size_page - ( size_aligned % size_page)); 2100} 2101 2102const struct ggml_metal_device_props * props_dev = ggml_metal_device_get_props( dev ); 2103 2104s hared = shared && props_dev - > use_shared_buffers ; 2105 2106/ / allocate shared buffer if the device supports it and it is required by the buffer type 2107if ( shared ) { 2108re s - > all_data = ggml_metal_host_malloc( size_aligned ); 2109re s - > is_shared = true; 2110} else { 2111// use virtual address 2112re s - > all_data = ( void * ) atomic_fetch_add_explicit(& dev - > addr_virt , size_aligned , memory_order_relaxed ); 2113re s - > is_shared = false; 2114} 2115re s - > all_size = size_aligned; 2116 2117re s - > owned = true; 2118 2119re s - > n_buffers = 1; 2120 2121if ( res - > all_data ! = NULL) { 2122res - > buffers [ 0 ].size = size; 2123res - > buffers [ 0 ].metal = nil; 2124 2125if ( size_aligned > 0 ) { 2126if ( props_dev - > use_shared_buffers && shared ) { 2127res - > buffers [ 0 ].metal = [ res - > dev - > mtl_device newBufferWithBytesNoCopy : res - > all_data 2128length : size_aligned 2129options : MTLResourceStorageModeShared 2130deallocator : nil ]; 2131} else { 2132res - > buffers [ 0 ].metal = [ res - > dev - > mtl_device newBufferWithLength : size_aligned options : MTLResourceStorageModePrivate ]; 2133} 2134} 2135 2136res - > buffers [ 0 ].data = res - > all_data ; 2137} 2138 2139if (size_aligned > 0 && ( res - > all_data == NULL || res - > buffers [ 0 ]. metal == nil )) { 2140GGML_LOG_ERROR ( "%s: error: failed to allocate buffer, size = %8.2f MiB\n" , __func__ , size_aligned / 1024.0 / 1024.0 ); 2141free ( res ); 2142return NULL; 2143} 2144 2145re s - > use_residency_sets = props_dev - > use_residency_sets ; 2146 2147if (! ggml_metal_buffer_rset_init ( res )) { 2148G GML_LOG_ERROR ( "%s: error: failed to initialize residency set\n" , __func__ ); 2149f ree ( res ); 2150return NULL; 2151} 2152 2153ggml_metal_device_rsets_add( dev , res - > rset ); 2154 2155// ggml_metal_log_allocated_size ( device , size_aligned ); 2156 2157return res; 2158} 2159 2160g gml_metal_buffer_t ggml_metal_buffer_map( ggml_metal_device_t dev, void * ptr , size_t size, size_t max_tensor_size) { 2161g gml_metal_buffer_t res = calloc ( 1 , sizeof ( struct ggml_metal_buffer)); 2162 2163re s - > dev = dev; 2164 2165re s - > all_data = ptr; 2166re s - > all_size = size; 2167 2168re s - > is_shared = true; 2169re s - > owned = false; 2170 2171re s - > n_buffers = 0; 2172 2173con st size_t size_page = sysconf( _SC_PAGESIZE ); 2174 2175/ / page - align the data ptr 2176{ 2177con st uintptr_t offs = ( uintptr_t ) ptr % size_page; 2178p tr = ( void * ) (( char * ) ptr - offs ); 2179s ize += offs; 2180} 2181 2182s ize_t size_aligned = size; 2183if (( size_aligned % size_page) != 0) { 2184size_aligned += ( size_page - ( size_aligned % size_page)); 2185} 2186 2187con st struct ggml_metal_device_props * props_dev = ggml_metal_device_get_props( dev ); 2188 2189/ / the buffer fits into the max buffer size allowed by the device 2190if ( size_aligned <= props_dev - > max_buffer_size ) { 2191re s - > buffers [ res - > n_buffers ]. data = ptr; 2192re s - > buffers [ res - > n_buffers ]. size = size; 2193re s - > buffers [ res - > n_buffers ]. metal = nil; 2194 2195if ( size_aligned > 0 ) { 2196re s - > buffers [ res - > n_buffers ]. metal = [ res - > dev - > mtl_device newBufferWithBytesNoCopy : ptr length : size_aligned options : MTLResourceStorageModeShared deallocator : nil ]; 2197 2198if ( res - > buffers [ res - > n_buffers ]. metal == nil ) { 2199GGML_LOG_ERROR ( "%s: error: failed to allocate buffer, size = %8.2f MiB\n" , __func__ , size_aligned / 1024.0 / 1024.0 ); 2200free ( res ); 2201return NULL; 2202} 2203} 2204 2205ggml_metal_log_allocated_size( res - > dev - > mtl_device , size_aligned ); 2206 2207++ res - >n_buffers; 2208} else { 2209// this overlap between the views will guarantee that the tensor with the maximum size will fully fit into 2210/ / one of the views 2211con st size_t size_ovlp = (( max_tensor_size + size_page - 1 ) / size_page + 1 ) * size_page ; / / round - up 2 pages just in case 2212con st size_t size_step = props_dev - > max_buffer_size - size_ovlp ; 2213con st size_t size_view = props_dev - > max_buffer_size ; 2214 2215for ( size_t i = 0 ; i < size ; i += size_step ) { 2216con st size_t size_step_aligned = ( i + size_view <= size ) ? size_view : ( size_aligned - i ); 2217 2218re s - > buffers [ res - > n_buffers ]. data = ( void * ) (( uint8_t * ) ptr + i ); 2219re s - > buffers [ res - > n_buffers ]. size = size_step_aligned; 2220re s - > buffers [ res - > n_buffers ]. metal = nil; 2221 2222if ( size_step_aligned > 0 ) { 2223re s - > buffers [ res - > n_buffers ]. metal = [ res - > dev - > mtl_device newBufferWithBytesNoCopy : ( void * ) (( uint8_t * ) ptr + i ) length : size_step_aligned options : MTLResourceStorageModeShared deallocator : nil ]; 2224 2225if ( res - > buffers [ res - > n_buffers ]. metal == nil ) { 2226GGML_LOG_ERROR ( "%s: error: failed to allocate buffer, size = %8.2f MiB\n" , __func__ , size_step_aligned / 1024.0 / 1024.0 ); 2227free ( res ); 2228return NULL; 2229} 2230} 2231 2232ggml_metal_log_allocated_size( res - > dev - > mtl_device , size_step_aligned ); 2233 2234if (i + size_step < size) { 2235GGML_LOG_INFO ( "\n" ); 2236} 2237 2238++ res - > n_buffers ; 2239} 2240} 2241 2242res - > use_residency_sets = props_dev - > use_residency_sets ; 2243 2244if (! ggml_metal_buffer_rset_init ( res )) { 2245G GML_LOG_ERROR ( "%s: error: failed to initialize residency set\n" , __func__ ); 2246f ree ( res ); 2247return NULL; 2248} 2249 2250ggml_metal_device_rsets_add( dev , res - > rset ); 2251 2252return res; 2253} 2254 2255v oid ggml_metal_buffer_free( ggml_metal_buffer_t buf) { 2256@ autoreleasepool { 2257ggml_metal_device_rsets_rm ( buf - > dev , buf - > rset ); 2258 2259for ( int i = 0; i < buf - > n_buffers ; i ++ ) { 2260[ buf - > buffers [ i ].metal release]; 2261} 2262 2263ggml_metal_buffer_rset_free ( buf ); 2264} 2265 2266if (buf - > is_shared && buf - > owned ) { 2267# if TARGET_OS_OSX 2268vm_deallocate (( vm_map_t ) mach_task_self (), ( vm_address_t )buf - > all_data , buf - > all_size ); 2269# else 2270f ree ( buf - > all_data ); 2271# endif 2272} 2273 2274f ree ( buf ); 2275} 2276 2277void * ggml_metal_buffer_get_base ( ggml_metal_buffer_t buf) { 2278return buf - > all_data ; 2279} 2280 2281bool ggml_metal_buffer_is_shared( ggml_metal_buffer_t buf) { 2282return buf - > is_shared ; 2283} 2284 2285void ggml_metal_buffer_memset_tensor( ggml_metal_buffer_t buf, struct ggml_tensor * tensor , uint8_t value, size_t offset, size_t size) { 2286if ( buf - > is_shared ) { 2287memset (( char * ) tensor - > data + offset , value , size ); 2288return; 2289} 2290 2291@ autoreleasepool { 2292// dst 2293struct ggml_metal_buffer_id bid_dst = ggml_metal_buffer_get_id( buf , tensor ); 2294b id_dst . offs += offset; 2295 2296i d < MTLCommandBuffer > cmd_buf = [buf - > dev - > mtl_queue commandBufferWithUnretainedReferences]; 2297 2298{ 2299i d < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 2300 2301[ encoder fillBuffer : bid_dst . metal 2302range : NSMakeRange ( bid_dst . offs , bid_dst . offs + size ) 2303value : value ]; 2304 2305[ encoder endEncoding]; 2306} 2307 2308[ cmd_buf commit]; 2309[ cmd_buf waitUntilCompleted]; 2310} 2311} 2312 2313void ggml_metal_buffer_set_tensor(ggml_metal_buffer_t buf, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size) { 2314if (buf - > is_shared ) { 2315memcpy (( char * ) tensor - > data + offset , data , size ); 2316return; 2317} 2318 2319@ autoreleasepool { 2320// src 2321void * data_ptr = ( void * )( uintptr_t ) data; // "const cast" the src data 2322i d < MTLBuffer > buf_src = [buf - > dev - > mtl_device newBufferWithBytesNoCopy : data_ptr 2323length : size 2324options : MTLResourceStorageModeShared 2325deallocator : nil ]; 2326 2327G GML_ASSERT ( buf_src ); 2328 2329/ / dst 2330struct ggml_metal_buffer_id bid_dst = ggml_metal_buffer_get_id( buf , tensor ); 2331b id_dst . offs += offset; 2332 2333/ / note : for experimentation purposes, here we use a semaphore to wait for the copy to complete 2334// this is alternative to waitUntilCompleted, which should be faster, but don ' t seem to make much difference 2335dispatch_semaphore_t completion_semaphore = dispatch_semaphore_create( 0 ); 2336 2337i d < MTLCommandBuffer > cmd_buf = [buf - > dev - > mtl_queue commandBufferWithUnretainedReferences]; 2338 2339{ 2340i d < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 2341 2342[ encoder copyFromBuffer : buf_src 2343sourceOffset : 0 2344toBuffer : bid_dst . metal 2345destinationOffset : bid_dst . offs 2346size : size ]; 2347 2348[ encoder endEncoding]; 2349} 2350 2351[cmd_buf addCompletedHandler :^ ( id < MTLCommandBuffer > cb ) { 2352/ / TODO : can check for errors here 2353G GML_UNUSED ( cb ); 2354 2355d ispatch_semaphore_signal ( completion_semaphore ); 2356}]; 2357 2358[ cmd_buf commit]; 2359 2360d ispatch_semaphore_wait ( completion_semaphore , DISPATCH_TIME_FOREVER ); 2361d ispatch_release ( completion_semaphore ); 2362 2363// [ cmd_buf waitUntilCompleted]; 2364} 2365} 2366 2367v oid ggml_metal_buffer_get_tensor( ggml_metal_buffer_t buf, const struct ggml_tensor * tensor , void * data , size_t offset, size_t size) { 2368if ( buf - > is_shared ) { 2369memcpy ( data , ( const char * ) tensor - > data + offset , size ); 2370return; 2371} 2372 2373@ autoreleasepool { 2374// src 2375struct ggml_metal_buffer_id bid_src = ggml_metal_buffer_get_id( buf , tensor ); 2376b id_src . offs += offset; 2377 2378/ / dst 2379id< MTLBuffer > buf_dst = [ buf - > dev - > mtl_device newBufferWithBytesNoCopy : data 2380length : size 2381options : MTLResourceStorageModeShared 2382deallocator : nil ]; 2383 2384G GML_ASSERT ( buf_dst ); 2385 2386i d < MTLCommandBuffer > cmd_buf = [buf - > dev - > mtl_queue commandBufferWithUnretainedReferences]; 2387 2388{ 2389i d < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 2390 2391[ encoder copyFromBuffer : bid_src . metal 2392sourceOffset : bid_src . offs 2393toBuffer : buf_dst 2394destinationOffset : 0 2395size : size ]; 2396 2397[ encoder endEncoding]; 2398} 2399 2400[cmd_buf commit]; 2401[cmd_buf waitUntilCompleted]; 2402} 2403} 2404 2405b ool ggml_metal_buffer_cpy_tensor( ggml_metal_buffer_t buf_dst, const struct ggml_tensor * src , struct ggml_tensor * dst ) { 2406g gml_metal_buffer_t buf_src = ( ggml_metal_buffer_t )src - > buffer - > context ; 2407 2408con st size_t size = ggml_nbytes( src ); 2409 2410// if both buffers are shared, we can use memcpy directly 2411if (buf_dst - > is_shared && buf_src - > is_shared ) { 2412memcpy ( dst - > data , src - > data , size ); 2413return true; 2414} 2415 2416// for private buffers, we need to use Metal blit commands 2417@ autoreleasepool { 2418s truct ggml_metal_buffer_id bid_src = ggml_metal_buffer_get_id ( buf_src , src ); 2419s truct ggml_metal_buffer_id bid_dst = ggml_metal_buffer_get_id( buf_dst , dst ); 2420 2421if ( bid_src . metal == nil || bid_dst . metal == nil ) { 2422return false; 2423} 2424 2425i d < MTLCommandBuffer > cmd_buf = [ buf_dst - > dev - > mtl_queue commandBufferWithUnretainedReferences]; 2426 2427{ 2428id < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 2429 2430[ encoder copyFromBuffer : bid_src . metal 2431sourceOffset : bid_src . offs 2432toBuffer : bid_dst . metal 2433destinationOffset : bid_dst . offs 2434size : size ]; 2435 2436[ encoder endEncoding]; 2437} 2438 2439[cmd_buf commit]; 2440[cmd_buf waitUntilCompleted]; 2441} 2442 2443return true; 2444} 2445 2446void ggml_metal_buffer_clear( ggml_metal_buffer_t buf, uint8_t value) { 2447if ( buf - > is_shared ) { 2448memset ( buf - > all_data , value , buf - > all_size ); 2449return; 2450} 2451 2452@ autoreleasepool { 2453i d < MTLCommandBuffer > cmd_buf = [buf - > dev - > mtl_queue commandBufferWithUnretainedReferences]; 2454 2455{ 2456i d < MTLBlitCommandEncoder > encoder = [ cmd_buf blitCommandEncoder]; 2457 2458[ encoder fillBuffer : buf - > buffers [ 0 ]. metal 2459range : NSMakeRange ( 0 , buf - > buffers [ 0 ]. size ) 2460value : value ]; 2461 2462[ encoder endEncoding]; 2463} 2464 2465[ cmd_buf commit]; 2466[ cmd_buf waitUntilCompleted]; 2467} 2468} 2469 2470struct ggml_metal_buffer_id ggml_metal_buffer_get_id( ggml_metal_buffer_t buf, const struct ggml_tensor * t ) { 2471s truct ggml_metal_buffer_id res = { nil , 0 }; 2472 2473con st int64_t tsize = ggml_nbytes( t ); 2474 2475/ / find the view that contains the tensor fully 2476for ( int i = 0 ; i < buf - > n_buffers ; ++ i ) { 2477con st int64_t ioffs = ( int64_t ) t - > data - ( int64_t ) buf - > buffers [ i ]. data ; 2478 2479// GGML_LOG_INFO ( "ioffs = %10ld, tsize = %10ld, sum = %10ld, buf->buffers[%d].size = %10ld\n" , ioffs , tsize , ioffs + tsize , i , buf - > buffers [ i ]. size ); 2480if ( ioffs >= 0 && ioffs + tsize <= ( int64_t ) buf - > buffers [ i ]. size ) { 2481re s . metal = buf - > buffers [ i ]. metal ; 2482re s . offs = ( size_t ) ioffs; 2483 2484// GGML_LOG_INFO ( "%s: tensor '%16s', offs = %8ld\n" , __func__ , t - > name , * offs ); 2485 2486return res; 2487} 2488} 2489 2490GGML_LOG_ERROR( "%s: error: tensor '%s' buffer is nil\n" , __func__ , t - > name ); 2491 2492return res; 2493}