char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
a2878d30d
char/aliceai-t5-moe
1#include "ggml-metal.h" 2 3#include "ggml-impl.h" 4#include "ggml-backend-impl.h" 5 6#include "ggml-metal-device.h" 7#include "ggml-metal-fusion.h" 8#include "ggml-metal-context.h" 9#include "ggml-metal-ops.h" 10#include "ggml-metal-tuning.h" 11 12#include <mutex> 13#include <string> 14 15#define GGML_METAL_NAME "MTL" 16#define GGML_METAL_MAX_DEVICES 16 17 18// number of Metal devices 19// note: can be overridden with GGML_METAL_DEVICES env to simulate virtual devices 20static int g_devices = 1 ; 21 22// forward declaration 23static bool ggml_backend_buffer_is_metal (ggml_backend_buffer_t buffer ); 24 25//////////////////////////////////////////////////////////////////////////////// 26// backend interface 27//////////////////////////////////////////////////////////////////////////////// 28 29// shared buffer 30 31static void ggml_backend_metal_buffer_shared_free_buffer (ggml_backend_buffer_t buffer ) { 32ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 33 34GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 35 36ggml_metal_buffer_free (ctx ); 37} 38 39static void * ggml_backend_metal_buffer_shared_get_base (ggml_backend_buffer_t buffer ) { 40ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 41 42GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 43 44return ggml_metal_buffer_get_base (ctx ); 45} 46 47static void ggml_backend_metal_buffer_shared_memset_tensor (ggml_backend_buffer_t buffer ,ggml_tensor * tensor ,uint8_t value ,size_t offset ,size_t size ) { 48ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 49 50GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 51 52ggml_metal_buffer_memset_tensor (ctx ,tensor ,value ,offset ,size ); 53} 54 55static void ggml_backend_metal_buffer_shared_set_tensor (ggml_backend_buffer_t buffer ,ggml_tensor * tensor ,const void * data ,size_t offset ,size_t size ) { 56ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 57 58GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 59 60ggml_metal_buffer_set_tensor (ctx ,tensor ,data ,offset ,size ); 61} 62 63static void ggml_backend_metal_buffer_shared_get_tensor (ggml_backend_buffer_t buffer ,const ggml_tensor * tensor ,void * data ,size_t offset ,size_t size ) { 64ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 65 66GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 67 68ggml_metal_buffer_get_tensor (ctx ,tensor ,data ,offset ,size ); 69} 70 71static bool ggml_backend_metal_buffer_shared_cpy_tensor (ggml_backend_buffer_t buffer ,const ggml_tensor * src ,ggml_tensor * dst ) { 72ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 73 74GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 75 76if (!ggml_backend_buffer_is_metal (src -> buffer )) { 77return false; 78 } 79 80return ggml_metal_buffer_cpy_tensor (ctx ,src ,dst ); 81} 82 83static void ggml_backend_metal_buffer_shared_clear (ggml_backend_buffer_t buffer ,uint8_t value ) { 84ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 85 86GGML_ASSERT (ggml_metal_buffer_is_shared (ctx )); 87 88ggml_metal_buffer_clear (ctx ,value ); 89} 90 91static ggml_backend_buffer_i ggml_backend_metal_buffer_shared_i = { 92/* .free_buffer = */ ggml_backend_metal_buffer_shared_free_buffer , 93/* .get_base = */ ggml_backend_metal_buffer_shared_get_base , 94/* .init_tensor = */ NULL , 95/* .memset_tensor = */ ggml_backend_metal_buffer_shared_memset_tensor , 96/* .set_tensor = */ ggml_backend_metal_buffer_shared_set_tensor , 97/* .get_tensor = */ ggml_backend_metal_buffer_shared_get_tensor , 98/* .set_tensor_2d = */ NULL , 99/* .get_tensor_2d = */ NULL , 100/* .cpy_tensor = */ ggml_backend_metal_buffer_shared_cpy_tensor , 101/* .clear = */ ggml_backend_metal_buffer_shared_clear , 102/* .reset = */ NULL , 103}; 104 105// private buffer 106 107static void ggml_backend_metal_buffer_private_free_buffer (ggml_backend_buffer_t buffer ) { 108ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 109 110GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 111 112ggml_metal_buffer_free (ctx ); 113} 114 115static void * ggml_backend_metal_buffer_private_get_base (ggml_backend_buffer_t buffer ) { 116ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 117 118GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 119 120return ggml_metal_buffer_get_base (ctx ); 121} 122 123static void ggml_backend_metal_buffer_private_memset_tensor (ggml_backend_buffer_t buffer ,ggml_tensor * tensor ,uint8_t value ,size_t offset ,size_t size ) { 124ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 125 126GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 127 128ggml_metal_buffer_memset_tensor (ctx ,tensor ,value ,offset ,size ); 129} 130 131static void ggml_backend_metal_buffer_private_set_tensor (ggml_backend_buffer_t buffer ,ggml_tensor * tensor ,const void * data ,size_t offset ,size_t size ) { 132ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 133 134GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 135 136ggml_metal_buffer_set_tensor (ctx ,tensor ,data ,offset ,size ); 137} 138 139static void ggml_backend_metal_buffer_private_get_tensor (ggml_backend_buffer_t buffer ,const ggml_tensor * tensor ,void * data ,size_t offset ,size_t size ) { 140ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 141 142GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 143 144ggml_metal_buffer_get_tensor (ctx ,tensor ,data ,offset ,size ); 145} 146 147static bool ggml_backend_metal_buffer_private_cpy_tensor (ggml_backend_buffer_t buffer ,const ggml_tensor * src ,ggml_tensor * dst ) { 148ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 149 150GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 151 152if (!ggml_backend_buffer_is_metal (src -> buffer )) { 153return false; 154 } 155 156return ggml_metal_buffer_cpy_tensor (ctx ,src ,dst ); 157} 158 159static void ggml_backend_metal_buffer_private_clear (ggml_backend_buffer_t buffer ,uint8_t value ) { 160ggml_metal_buffer_t ctx = (ggml_metal_buffer_t )buffer -> context ; 161 162GGML_ASSERT (!ggml_metal_buffer_is_shared (ctx )); 163 164ggml_metal_buffer_clear (ctx ,value ); 165} 166 167static ggml_backend_buffer_i ggml_backend_metal_buffer_private_i = { 168/* .free_buffer = */ ggml_backend_metal_buffer_private_free_buffer , 169/* .get_base = */ ggml_backend_metal_buffer_private_get_base , 170/* .init_tensor = */ NULL , 171/* .memset_tensor = */ ggml_backend_metal_buffer_private_memset_tensor , 172/* .set_tensor = */ ggml_backend_metal_buffer_private_set_tensor , 173/* .get_tensor = */ ggml_backend_metal_buffer_private_get_tensor , 174/* .set_tensor_2d = */ NULL , 175/* .get_tensor_2d = */ NULL , 176/* .cpy_tensor = */ ggml_backend_metal_buffer_private_cpy_tensor , 177/* .clear = */ ggml_backend_metal_buffer_private_clear , 178/* .reset = */ NULL , 179}; 180 181static bool ggml_backend_buffer_is_metal (ggml_backend_buffer_t buffer ) { 182return buffer -> iface .free_buffer == ggml_backend_metal_buffer_shared_free_buffer || 183buffer -> iface .free_buffer == ggml_backend_metal_buffer_private_free_buffer ; 184} 185 186// 187// buffer types 188// 189 190struct ggml_backend_metal_buffer_type { 191int device ; 192 std::string name ; 193}; 194 195struct ggml_backend_metal_buffer_type_deleter { 196void operator()(ggml_backend_metal_buffer_type * ctx )const { 197delete ctx ; 198 } 199}; 200 201typedef std::unique_ptr < ggml_backend_metal_buffer_type ,ggml_backend_metal_buffer_type_deleter > ggml_backend_metal_buffer_type_ptr ; 202 203// common method for allocating shread or private Metal buffers 204static ggml_backend_buffer_t ggml_backend_metal_buffer_type_alloc_buffer (ggml_backend_buffer_type_t buft ,size_t size ,bool shared ) { 205ggml_metal_device_t ctx_dev = (ggml_metal_device_t )buft -> device -> context ; 206ggml_metal_buffer_t res = ggml_metal_buffer_init (ctx_dev ,size ,shared ); 207 208if (res == NULL ) { 209GGML_LOG_ERROR ("%s: failed to allocate Metal buffer of %zu bytes (out of memory)\n" ,__func__ ,size ); 210return NULL ; 211 } 212 213ggml_backend_buffer_i buf_i = ggml_metal_buffer_is_shared (res ) 214 ?ggml_backend_metal_buffer_shared_i 215 :ggml_backend_metal_buffer_private_i ; 216 217return ggml_backend_buffer_init (buft ,buf_i ,res ,size ); 218} 219 220static size_t ggml_backend_metal_buffer_type_get_alloc_size (ggml_backend_buffer_type_t buft ,const ggml_tensor * tensor ) { 221size_t res = ggml_nbytes (tensor ); 222 223// some operations require additional memory for fleeting data: 224switch (tensor -> op ) { 225case GGML_OP_MUL_MAT_ID : 226 { 227res += ggml_metal_op_mul_mat_id_extra_tpe (tensor ); 228res += ggml_metal_op_mul_mat_id_extra_ids (tensor ); 229 }break ; 230case GGML_OP_FLASH_ATTN_EXT : 231 { 232res += ggml_metal_op_flash_attn_ext_extra_pad (tensor ); 233res += ggml_metal_op_flash_attn_ext_extra_blk (tensor ); 234res += ggml_metal_op_flash_attn_ext_extra_tmp (tensor ); 235res += ggml_metal_op_flash_attn_ext_extra_kv_f16 (tensor ); 236res += ggml_metal_op_flash_attn_ext_extra_idx (tensor ); 237 }break ; 238case GGML_OP_CUMSUM : 239case GGML_OP_ARGSORT : 240 { 241res *=2 ; 242 }break ; 243case GGML_OP_TOP_K : 244 { 245res = 2 * sizeof (int32_t )* ggml_nelements (tensor -> src [0 ]); 246 }break ; 247default : 248break ; 249 } 250 251return res ; 252 253GGML_UNUSED (buft ); 254} 255 256// default (shared) buffer type 257 258static const char * ggml_backend_metal_buffer_type_shared_get_name (ggml_backend_buffer_type_t buft ) { 259ggml_backend_metal_buffer_type * ctx = (ggml_backend_metal_buffer_type * )buft -> context ; 260 261return ctx -> name .c_str (); 262} 263 264static ggml_backend_buffer_t ggml_backend_metal_buffer_type_shared_alloc_buffer (ggml_backend_buffer_type_t buft ,size_t size ) { 265return ggml_backend_metal_buffer_type_alloc_buffer (buft ,size , true); 266} 267 268static size_t ggml_backend_metal_buffer_type_shared_get_alignment (ggml_backend_buffer_type_t buft ) { 269return 32 ; 270 271GGML_UNUSED (buft ); 272} 273 274static size_t ggml_backend_metal_buffer_type_shared_get_max_size (ggml_backend_buffer_type_t buft ) { 275ggml_metal_device_t ctx_dev = (ggml_metal_device_t )buft -> device -> context ; 276 277return ggml_metal_device_get_props (ctx_dev )-> max_buffer_size ; 278} 279 280static size_t ggml_backend_metal_buffer_type_shared_get_alloc_size (ggml_backend_buffer_type_t buft ,const ggml_tensor * tensor ) { 281return ggml_backend_metal_buffer_type_get_alloc_size (buft ,tensor ); 282} 283 284static bool ggml_backend_metal_buffer_type_shared_is_host (ggml_backend_buffer_type_t buft ) { 285return false; 286 287GGML_UNUSED (buft ); 288} 289 290static ggml_backend_buffer_type_t ggml_backend_metal_buffer_type_shared (int device ) { 291static std::mutex mutex ; 292 std::lock_guard < std::mutex > lock (mutex ); 293 294static std::vector < ggml_backend_buffer_type > bufts ; 295static std::vector < ggml_backend_metal_buffer_type_ptr > ctxs ; 296 297static bool initialized = false; 298if (!initialized ) { 299bufts .reserve (g_devices ); 300ctxs .reserve (g_devices ); 301 302for (int i = 0 ;i < g_devices ;++ i ) { 303ggml_backend_metal_buffer_type * raw_ctx = 304new ggml_backend_metal_buffer_type { 305/* .device = */ i , 306/* .name = */ GGML_METAL_NAME + std::to_string (i ), 307 }; 308ctxs .emplace_back (raw_ctx ); 309 310ggml_backend_buffer_type buft = { 311/* .iface = */ { 312/* .get_name = */ ggml_backend_metal_buffer_type_shared_get_name , 313/* .alloc_buffer = */ ggml_backend_metal_buffer_type_shared_alloc_buffer , 314/* .get_alignment = */ ggml_backend_metal_buffer_type_shared_get_alignment , 315/* .get_max_size = */ ggml_backend_metal_buffer_type_shared_get_max_size , 316/* .get_alloc_size = */ ggml_backend_metal_buffer_type_shared_get_alloc_size , 317/* .is_host = */ ggml_backend_metal_buffer_type_shared_is_host , 318 }, 319/* .device = */ ggml_backend_reg_dev_get (ggml_backend_metal_reg (),i ), 320/* .context = */ raw_ctx , 321 }; 322 323bufts .emplace_back (buft ); 324 } 325 326initialized = true; 327 } 328 329return & bufts [device ]; 330} 331 332// default (private) buffer type 333 334static const char * ggml_backend_metal_buffer_type_private_get_name (ggml_backend_buffer_type_t buft ) { 335ggml_backend_metal_buffer_type * ctx = (ggml_backend_metal_buffer_type * )buft -> context ; 336 337return ctx -> name .c_str (); 338} 339 340static ggml_backend_buffer_t ggml_backend_metal_buffer_type_private_alloc_buffer (ggml_backend_buffer_type_t buft ,size_t size ) { 341return ggml_backend_metal_buffer_type_alloc_buffer (buft ,size , false); 342} 343 344static size_t ggml_backend_metal_buffer_type_private_get_alignment (ggml_backend_buffer_type_t buft ) { 345return 32 ; 346 347GGML_UNUSED (buft ); 348} 349 350static size_t ggml_backend_metal_buffer_type_private_get_max_size (ggml_backend_buffer_type_t buft ) { 351ggml_metal_device_t ctx_dev = (ggml_metal_device_t )buft -> device -> context ; 352 353return ggml_metal_device_get_props (ctx_dev )-> max_buffer_size ; 354} 355 356static size_t ggml_backend_metal_buffer_type_private_get_alloc_size (ggml_backend_buffer_type_t buft ,const ggml_tensor * tensor ) { 357return ggml_backend_metal_buffer_type_get_alloc_size (buft ,tensor ); 358} 359 360static bool ggml_backend_metal_buffer_type_private_is_host (ggml_backend_buffer_type_t buft ) { 361return false; 362 363GGML_UNUSED (buft ); 364} 365 366static ggml_backend_buffer_type_t ggml_backend_metal_buffer_type_private (int device ) { 367static std::mutex mutex ; 368 std::lock_guard < std::mutex > lock (mutex ); 369 370static std::vector < ggml_backend_buffer_type > bufts ; 371static std::vector < ggml_backend_metal_buffer_type_ptr > ctxs ; 372 373static bool initialized = false; 374if (!initialized ) { 375bufts .reserve (g_devices ); 376ctxs .reserve (g_devices ); 377 378for (int i = 0 ;i < g_devices ;++ i ) { 379ggml_backend_metal_buffer_type * raw_ctx = new ggml_backend_metal_buffer_type { 380/* .device = */ i , 381/* .name = */ GGML_METAL_NAME + std::to_string (i )+ "_Private" 382 }; 383ctxs .emplace_back (raw_ctx ); 384 385ggml_backend_buffer_type buft = { 386/* .iface = */ { 387/* .get_name = */ ggml_backend_metal_buffer_type_private_get_name , 388/* .alloc_buffer = */ ggml_backend_metal_buffer_type_private_alloc_buffer , 389/* .get_alignment = */ ggml_backend_metal_buffer_type_private_get_alignment , 390/* .get_max_size = */ ggml_backend_metal_buffer_type_private_get_max_size , 391/* .get_alloc_size = */ ggml_backend_metal_buffer_type_private_get_alloc_size , 392/* .is_host = */ ggml_backend_metal_buffer_type_private_is_host , 393 }, 394/* .device = */ ggml_backend_reg_dev_get (ggml_backend_metal_reg (),i ), 395/* .context = */ raw_ctx , 396 }; 397 398bufts .emplace_back (buft ); 399 } 400 401initialized = true; 402 } 403 404return & bufts [device ]; 405} 406 407// mapped buffer type 408 409static const char * ggml_backend_metal_buffer_type_mapped_get_name (ggml_backend_buffer_type_t buft ) { 410ggml_backend_metal_buffer_type * ctx = (ggml_backend_metal_buffer_type * )buft -> context ; 411 412return ctx -> name .c_str (); 413} 414 415static ggml_backend_buffer_t ggml_backend_metal_buffer_type_mapped_alloc_buffer (ggml_backend_buffer_type_t buft ,size_t size ) { 416// for mapped buffers, prefer shared memory 417return ggml_backend_metal_buffer_type_alloc_buffer (buft ,size , true); 418} 419 420static size_t ggml_backend_metal_buffer_type_mapped_get_alignment (ggml_backend_buffer_type_t buft ) { 421return 32 ; 422 423GGML_UNUSED (buft ); 424} 425 426static size_t ggml_backend_metal_buffer_type_mapped_get_max_size (ggml_backend_buffer_type_t buft ) { 427ggml_metal_device_t ctx_dev = (ggml_metal_device_t )buft -> device -> context ; 428 429return ggml_metal_device_get_props (ctx_dev )-> max_buffer_size ; 430} 431 432static size_t ggml_backend_metal_buffer_type_mapped_get_alloc_size (ggml_backend_buffer_type_t buft ,const ggml_tensor * tensor ) { 433return ggml_backend_metal_buffer_type_get_alloc_size (buft ,tensor ); 434} 435 436static bool ggml_backend_metal_buffer_type_mapped_is_host (ggml_backend_buffer_type_t buft ) { 437return false; 438 439GGML_UNUSED (buft ); 440} 441 442static ggml_backend_buffer_type_t ggml_backend_metal_buffer_type_mapped (int device ) { 443static std::mutex mutex ; 444 std::lock_guard < std::mutex > lock (mutex ); 445 446static std::vector < ggml_backend_buffer_type > bufts ; 447static std::vector < ggml_backend_metal_buffer_type_ptr > ctxs ; 448 449static bool initialized = false; 450if (!initialized ) { 451bufts .reserve (g_devices ); 452ctxs .reserve (g_devices ); 453 454for (int i = 0 ;i < g_devices ;++ i ) { 455ggml_backend_metal_buffer_type * raw_ctx = new ggml_backend_metal_buffer_type { 456/* .device = */ i , 457/* .name = */ GGML_METAL_NAME + std::to_string (i )+ "_Mapped" 458 }; 459ctxs .emplace_back (raw_ctx ); 460 461// note: not obvious, but this buffer type still needs to implement .alloc_buffer: 462// https://github.com/ggml-org/llama.cpp/pull/15832#discussion_r2333177099 463ggml_backend_buffer_type buft = { 464/* .iface = */ { 465/* .get_name = */ ggml_backend_metal_buffer_type_mapped_get_name , 466/* .alloc_buffer = */ ggml_backend_metal_buffer_type_mapped_alloc_buffer , 467/* .get_alignment = */ ggml_backend_metal_buffer_type_mapped_get_alignment , 468/* .get_max_size = */ ggml_backend_metal_buffer_type_mapped_get_max_size , 469/* .get_alloc_size = */ ggml_backend_metal_buffer_type_mapped_get_alloc_size , 470/* .is_host = */ ggml_backend_metal_buffer_type_mapped_is_host , 471 }, 472/* .device = */ ggml_backend_reg_dev_get (ggml_backend_metal_reg (),i ), 473/* .context = */ raw_ctx , 474 }; 475 476bufts .emplace_back (buft ); 477 } 478 479initialized = true; 480 } 481 482return & bufts [device ]; 483} 484 485// backend 486 487static const char * ggml_backend_metal_name (ggml_backend_t backend ) { 488ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 489 490return ggml_metal_get_name (ctx ); 491} 492 493static void ggml_backend_metal_free (ggml_backend_t backend ) { 494ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 495 496// wait for any ongoing async operations to finish 497ggml_metal_synchronize (ctx ); 498 499ggml_metal_free (ctx ); 500 501free (backend ); 502} 503 504static void ggml_backend_metal_synchronize (ggml_backend_t backend ) { 505ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 506 507ggml_metal_synchronize (ctx ); 508} 509 510static void ggml_backend_metal_set_tensor_async (ggml_backend_t backend ,ggml_tensor * tensor ,const void * data ,size_t offset ,size_t size ) { 511ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 512 513ggml_metal_set_tensor_async (ctx ,tensor ,data ,offset ,size ); 514} 515 516static void ggml_backend_metal_get_tensor_async (ggml_backend_t backend ,const ggml_tensor * tensor ,void * data ,size_t offset ,size_t size ) { 517ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 518 519ggml_metal_get_tensor_async (ctx ,tensor ,data ,offset ,size ); 520} 521 522static bool ggml_backend_metal_cpy_tensor_async (ggml_backend_t backend_src ,ggml_backend_t backend_dst ,const ggml_tensor * src ,ggml_tensor * dst ) { 523if (!ggml_backend_is_metal (backend_src )|| !ggml_backend_is_metal (backend_dst )) { 524return false; 525 } 526 527if (!ggml_backend_buffer_is_metal (src -> buffer )|| !ggml_backend_buffer_is_metal (dst -> buffer )) { 528return false; 529 } 530 531ggml_metal_t ctx_src = (ggml_metal_t )backend_src -> context ; 532ggml_metal_t ctx_dst = (ggml_metal_t )backend_dst -> context ; 533 534//ggml_backend_buffer_t buf_src = src->view_src ? src->view_src->buffer : src->buffer; 535//ggml_backend_buffer_t buf_dst = dst->view_src ? dst->view_src->buffer : dst->buffer; 536 537//ggml_metal_buffer_t buf_ctx_src = (ggml_metal_buffer_t)buf_src->context; 538//ggml_metal_buffer_t buf_ctx_dst = (ggml_metal_buffer_t)buf_dst->context; 539 540return ggml_metal_cpy_tensor_async (ctx_src ,ctx_dst ,src ,dst ); 541} 542 543static enum ggml_status ggml_backend_metal_graph_compute (ggml_backend_t backend ,ggml_cgraph * cgraph ) { 544ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 545 546return ggml_metal_graph_compute (ctx ,cgraph ); 547} 548 549static void ggml_backend_metal_event_record (ggml_backend_t backend ,ggml_backend_event_t event ) { 550ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 551ggml_metal_event_t ev = (ggml_metal_event_t )event -> context ; 552 553ggml_metal_event_record (ctx ,ev ); 554} 555 556static void ggml_backend_metal_event_wait (ggml_backend_t backend ,ggml_backend_event_t event ) { 557ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 558ggml_metal_event_t ev = (ggml_metal_event_t )event -> context ; 559 560ggml_metal_event_wait (ctx ,ev ); 561} 562 563static void ggml_backend_metal_graph_optimize (ggml_backend_t backend ,ggml_cgraph * cgraph ,ggml_backend_graph_optimize_params * params ) { 564GGML_UNUSED (params ); 565 566ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 567 568ggml_metal_graph_optimize (ctx ,cgraph ); 569} 570 571static void ggml_backend_metal_set_n_cb (ggml_backend_t backend ,int n_cb ) { 572GGML_ASSERT (ggml_backend_is_metal (backend )); 573 574ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 575 576ggml_metal_set_n_cb (ctx ,n_cb ); 577} 578 579static ggml_backend_i ggml_backend_metal_i = { 580/* .get_name = */ ggml_backend_metal_name , 581/* .free = */ ggml_backend_metal_free , 582/* .set_tensor_async = */ ggml_backend_metal_set_tensor_async , 583/* .get_tensor_async = */ ggml_backend_metal_get_tensor_async , 584/* .set_tensor_2d_async = */ NULL , 585/* .get_tensor_2d_async = */ NULL , 586/* .cpy_tensor_async = */ ggml_backend_metal_cpy_tensor_async ,// only needed for multi-GPU setups 587/* .synchronize = */ ggml_backend_metal_synchronize , 588/* .graph_plan_create = */ NULL , 589/* .graph_plan_free = */ NULL , 590/* .graph_plan_update = */ NULL , 591/* .graph_plan_compute = */ NULL , 592/* .graph_compute = */ ggml_backend_metal_graph_compute , 593/* .event_record = */ ggml_backend_metal_event_record , 594/* .event_wait = */ ggml_backend_metal_event_wait , 595/* .graph_optimize = */ ggml_backend_metal_graph_optimize , 596}; 597 598static ggml_guid_t ggml_backend_metal_guid (void ) { 599static ggml_guid guid = {0x81 ,0xa1 ,0x8b ,0x1e ,0x71 ,0xec ,0x79 ,0xed ,0x2b ,0x85 ,0xdc ,0x8a ,0x61 ,0x98 ,0x30 ,0xe6 }; 600return & guid ; 601} 602 603ggml_backend_t ggml_backend_metal_init (void ) { 604ggml_backend_dev_t dev = ggml_backend_reg_dev_get (ggml_backend_metal_reg (),0 ); 605ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 606 607ggml_metal_t ctx = ggml_metal_init (ctx_dev ); 608if (ctx == NULL ) { 609GGML_LOG_ERROR ("%s: error: failed to allocate context\n" ,__func__ ); 610return NULL ; 611 } 612 613ggml_backend_t backend = (ggml_backend_t )malloc (sizeof (ggml_backend )); 614 615* backend = { 616/* .guid = */ ggml_backend_metal_guid (), 617/* .interface = */ ggml_backend_metal_i , 618/* .device = */ dev , 619/* .context = */ ctx , 620 }; 621 622ggml_backend_metal_set_n_cb (backend ,1 ); 623 624return backend ; 625} 626 627bool ggml_backend_is_metal (ggml_backend_t backend ) { 628return backend != NULL && ggml_guid_matches (backend -> guid ,ggml_backend_metal_guid ()); 629} 630 631void ggml_backend_metal_set_abort_callback (ggml_backend_t backend ,ggml_abort_callback abort_callback ,void * user_data ) { 632GGML_ASSERT (ggml_backend_is_metal (backend )); 633 634ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 635 636ggml_metal_set_abort_callback (ctx ,abort_callback ,user_data ); 637} 638 639bool ggml_backend_metal_supports_family (ggml_backend_t backend ,int family ) { 640GGML_ASSERT (ggml_backend_is_metal (backend )); 641 642ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 643 644return ggml_metal_supports_family (ctx ,family ); 645} 646 647void ggml_backend_metal_capture_next_compute (ggml_backend_t backend ) { 648GGML_ASSERT (ggml_backend_is_metal (backend )); 649 650ggml_metal_t ctx = (ggml_metal_t )backend -> context ; 651 652ggml_metal_capture_next_compute (ctx ); 653} 654 655// backend device 656 657static const char * ggml_backend_metal_device_get_name (ggml_backend_dev_t dev ) { 658ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 659 660const ggml_metal_device_props * props_dev = ggml_metal_device_get_props (ctx_dev ); 661 662return props_dev -> name ; 663} 664 665static const char * ggml_backend_metal_device_get_description (ggml_backend_dev_t dev ) { 666ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 667 668return ggml_metal_device_get_props (ctx_dev )-> desc ; 669} 670 671static void ggml_backend_metal_device_get_memory (ggml_backend_dev_t dev ,size_t * free ,size_t * total ) { 672ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 673 674ggml_metal_device_get_memory (ctx_dev ,free ,total ); 675} 676 677static enum ggml_backend_dev_type ggml_backend_metal_device_get_type (ggml_backend_dev_t dev ) { 678return GGML_BACKEND_DEVICE_TYPE_GPU ; 679 680GGML_UNUSED (dev ); 681} 682 683static void ggml_backend_metal_device_get_props (ggml_backend_dev_t dev ,ggml_backend_dev_props * props ) { 684props -> name = ggml_backend_metal_device_get_name (dev ); 685props -> description = ggml_backend_metal_device_get_description (dev ); 686props -> type = ggml_backend_metal_device_get_type (dev ); 687 688ggml_backend_metal_device_get_memory (dev ,& props -> memory_free ,& props -> memory_total ); 689 690props -> caps = { 691/* .async = */ true, 692/* .host_buffer = */ false, 693/* .buffer_from_host_ptr = */ true, 694/* .events = */ true, 695/* .mmap_support = */ true, 696 }; 697} 698 699static ggml_backend_t ggml_backend_metal_device_init_backend (ggml_backend_dev_t dev ,const char * params ) { 700ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 701 702ggml_metal_t ctx = ggml_metal_init (ctx_dev ); 703if (ctx == NULL ) { 704GGML_LOG_ERROR ("%s: error: failed to allocate context\n" ,__func__ ); 705return NULL ; 706 } 707 708ggml_backend_t backend = (ggml_backend_t )malloc (sizeof (ggml_backend )); 709 710* backend = { 711/* .guid = */ ggml_backend_metal_guid (), 712/* .interface = */ ggml_backend_metal_i , 713/* .device = */ dev , 714/* .context = */ ctx , 715 }; 716 717ggml_backend_metal_set_n_cb (backend ,1 ); 718 719return backend ; 720 721GGML_UNUSED (params ); 722} 723 724static ggml_backend_buffer_type_t ggml_backend_metal_device_get_buffer_type (ggml_backend_dev_t dev ) { 725ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 726 727const ggml_metal_device_props * props_dev = ggml_metal_device_get_props (ctx_dev ); 728 729return props_dev -> use_shared_buffers ?ggml_backend_metal_buffer_type_shared (props_dev -> device ) :ggml_backend_metal_buffer_type_private (props_dev -> device ); 730} 731 732static ggml_backend_buffer_t ggml_backend_metal_device_buffer_mapped (ggml_backend_dev_t dev ,void * ptr ,size_t size ,size_t max_tensor_size ) { 733ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 734 735ggml_metal_buffer_t res = ggml_metal_buffer_map (ctx_dev ,ptr ,size ,max_tensor_size ); 736 737const ggml_metal_device_props * props_dev = ggml_metal_device_get_props (ctx_dev ); 738 739return ggml_backend_buffer_init (ggml_backend_metal_buffer_type_mapped (props_dev -> device ),ggml_backend_metal_buffer_shared_i ,res ,size ); 740} 741 742static bool ggml_backend_metal_device_supports_op (ggml_backend_dev_t dev ,const ggml_tensor * op ) { 743ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 744 745return ggml_metal_device_supports_op (ctx_dev ,op ); 746} 747 748static bool ggml_backend_metal_device_supports_buft (ggml_backend_dev_t dev ,ggml_backend_buffer_type_t buft ) { 749return 750buft -> device == dev && ( 751buft -> iface .get_name == ggml_backend_metal_buffer_type_shared_get_name || 752buft -> iface .get_name == ggml_backend_metal_buffer_type_private_get_name || 753buft -> iface .get_name == ggml_backend_metal_buffer_type_mapped_get_name ); 754 755GGML_UNUSED (dev ); 756} 757 758static int64_t get_op_batch_size (const ggml_tensor * op ) { 759switch (op -> op ) { 760case GGML_OP_MUL_MAT : 761return op -> ne [1 ]; 762case GGML_OP_MUL_MAT_ID : 763return op -> ne [2 ]; 764default : 765return ggml_nrows (op ); 766 } 767} 768 769static bool ggml_backend_metal_device_offload_op (ggml_backend_dev_t dev ,const ggml_tensor * op ) { 770ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 771 772return (op -> op == GGML_OP_MUL_MAT || 773op -> op == GGML_OP_MUL_MAT_ID )&& 774get_op_batch_size (op ) >=ggml_metal_device_get_props (ctx_dev )-> op_offload_min_batch_size ; 775} 776 777static ggml_backend_event_t ggml_backend_metal_device_event_new (ggml_backend_dev_t dev ) { 778ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 779 780ggml_metal_event_t event = ggml_metal_device_event_init (ctx_dev ); 781GGML_ASSERT (event ); 782 783ggml_backend_event_t ev = new ggml_backend_event { 784/* .device = */ dev , 785/* .context = */ event , 786 }; 787 788return ev ; 789} 790 791static void ggml_backend_metal_device_event_free (ggml_backend_dev_t dev ,ggml_backend_event_t event ) { 792ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 793 794ggml_metal_event_t ev = (ggml_metal_event_t )event -> context ; 795 796ggml_metal_device_event_free (ctx_dev ,ev ); 797 798delete event ; 799} 800 801static void ggml_backend_metal_device_event_synchronize (ggml_backend_dev_t dev ,ggml_backend_event_t event ) { 802ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 803 804ggml_metal_event_t evt = (ggml_metal_event_t )event -> context ; 805 806ggml_metal_device_event_synchronize (ctx_dev ,evt ); 807} 808 809static ggml_backend_device_i ggml_backend_metal_device_i = { 810/* .get_name = */ ggml_backend_metal_device_get_name , 811/* .get_description = */ ggml_backend_metal_device_get_description , 812/* .get_memory = */ ggml_backend_metal_device_get_memory , 813/* .get_type = */ ggml_backend_metal_device_get_type , 814/* .get_props = */ ggml_backend_metal_device_get_props , 815/* .init_backend = */ ggml_backend_metal_device_init_backend , 816/* .get_buffer_type = */ ggml_backend_metal_device_get_buffer_type , 817/* .get_host_buffer_type = */ NULL , 818/* .buffer_from_host_ptr = */ ggml_backend_metal_device_buffer_mapped , 819/* .supports_op = */ ggml_backend_metal_device_supports_op , 820/* .supports_buft = */ ggml_backend_metal_device_supports_buft , 821/* .offload_op = */ ggml_backend_metal_device_offload_op , 822/* .event_new = */ ggml_backend_metal_device_event_new , 823/* .event_free = */ ggml_backend_metal_device_event_free , 824/* .event_synchronize = */ ggml_backend_metal_device_event_synchronize , 825}; 826 827// backend registry 828 829struct ggml_backend_metal_reg { 830 std::vector < ggml_backend_dev_t > devices ; 831}; 832 833typedef struct ggml_backend_metal_reg * ggml_backend_metal_reg_t ; 834 835static ggml_backend_metal_reg_t ggml_backend_metal_reg_init (void ) { 836ggml_backend_metal_reg_t ctx = new struct ggml_backend_metal_reg ; 837 838return ctx ; 839} 840 841static void ggml_backend_metal_reg_free (ggml_backend_metal_reg_t ctx ) { 842delete ctx ; 843} 844 845struct ggml_backend_metal_reg_deleter { 846void operator()(ggml_backend_metal_reg_t ctx ) { 847ggml_backend_metal_reg_free (ctx ); 848 } 849}; 850 851typedef std::unique_ptr < struct ggml_backend_metal_reg ,ggml_backend_metal_reg_deleter > ggml_backend_metal_reg_ptr ; 852 853static const char * ggml_backend_metal_reg_get_name (ggml_backend_reg_t reg ) { 854return GGML_METAL_NAME ; 855 856GGML_UNUSED (reg ); 857} 858 859static size_t ggml_backend_metal_reg_device_count (ggml_backend_reg_t reg ) { 860ggml_backend_metal_reg_t ctx = (ggml_backend_metal_reg_t )reg -> context ; 861return ctx -> devices .size (); 862} 863 864static ggml_backend_dev_t ggml_backend_metal_reg_device_get (ggml_backend_reg_t reg ,size_t index ) { 865ggml_backend_metal_reg_t ctx = (ggml_backend_metal_reg_t )reg -> context ; 866GGML_ASSERT (index < ctx -> devices .size ()); 867return ctx -> devices [index ]; 868} 869 870static ggml_backend_feature g_ggml_backend_metal_features []= { 871#if defined(GGML_METAL_EMBED_LIBRARY ) 872 {"EMBED_LIBRARY" ,"1" }, 873#endif 874 {NULL ,NULL }, 875}; 876 877static ggml_backend_feature * ggml_backend_metal_get_features (ggml_backend_reg_t reg ) { 878return g_ggml_backend_metal_features ; 879 880GGML_UNUSED (reg ); 881} 882 883// test/tune-only override for the FA vec (Q, NE) selection, reached via proc_address. 884static void ggml_backend_metal_tuning_set_fa_vec_override (int Q ,int NE ) { 885 ggml_metal_tuning::fa_vec_set_override ({ (int8_t )Q , (int8_t )NE }); 886} 887 888static void ggml_backend_metal_tuning_clear_fa_vec_override (void ) { 889 ggml_metal_tuning::fa_vec_clear_override (); 890} 891 892static int ggml_backend_metal_tuning_fa_vec_ne11_bucket (int64_t ne11 ) { 893return ggml_metal_tuning::fa_vec_ne11_bucket (ne11 ); 894} 895 896static int ggml_backend_metal_tuning_fa_vec_ne01_bucket (int64_t ne01 ) { 897return ggml_metal_tuning::fa_vec_ne01_bucket (ne01 ); 898} 899 900static int ggml_backend_metal_tuning_fa_vec_baseline_ne (int dk ,int dv ) { 901return ggml_metal_tuning::fa_vec_baseline_ne (dk ,dv ); 902} 903 904static const char * ggml_backend_metal_tuning_device_token (ggml_backend_dev_t dev ) { 905ggml_metal_device_t ctx_dev = (ggml_metal_device_t )dev -> context ; 906 907return ggml_metal_device_id_token (ggml_metal_device_get_props (ctx_dev )-> device_id ); 908} 909 910// generic fusion debugging API (ad-hoc proc-address mechanism): the test resolves the device 911// fusion context once and passes that opaque handle to the rest of the functions 912typedef void * ggml_backend_fusion_t ; 913 914static ggml_backend_fusion_t ggml_backend_metal_fusion_get (ggml_backend_dev_t dev ) { 915return ggml_metal_device_get_fusion_info ((ggml_metal_device_t )dev -> context ); 916} 917 918static void ggml_backend_metal_fusion_stats_init (ggml_backend_fusion_t finfo ) { 919ggml_metal_fusion_info_stats_init ((struct ggml_metal_fusion_info * )finfo ); 920} 921 922static void ggml_backend_metal_fusion_stats_reset (ggml_backend_fusion_t finfo ) { 923ggml_metal_fusion_info_stats_reset ((struct ggml_metal_fusion_info * )finfo ); 924} 925 926static int ggml_backend_metal_fusion_stats_get (ggml_backend_fusion_t finfo ,const char ** labels ,uint64_t * counts ,int n ) { 927return ggml_metal_fusion_info_stats_get ((struct ggml_metal_fusion_info * )finfo ,labels ,counts ,n ); 928} 929 930static void ggml_backend_metal_fusion_set_enabled (ggml_backend_fusion_t finfo ,bool enabled ) { 931ggml_metal_fusion_info_set_enabled ((struct ggml_metal_fusion_info * )finfo ,enabled ); 932} 933 934static void * ggml_backend_metal_get_proc_address (ggml_backend_reg_t reg ,const char * name ) { 935if (strcmp (name ,"ggml_backend_get_features" )== 0 ) { 936return (void * )ggml_backend_metal_get_features ; 937 } 938if (strcmp (name ,"ggml_backend_metal_tuning_set_fa_vec_override" )== 0 ) { 939return (void * )ggml_backend_metal_tuning_set_fa_vec_override ; 940 } 941if (strcmp (name ,"ggml_backend_metal_tuning_clear_fa_vec_override" )== 0 ) { 942return (void * )ggml_backend_metal_tuning_clear_fa_vec_override ; 943 } 944if (strcmp (name ,"ggml_backend_metal_tuning_fa_vec_ne11_bucket" )== 0 ) { 945return (void * )ggml_backend_metal_tuning_fa_vec_ne11_bucket ; 946 } 947if (strcmp (name ,"ggml_backend_metal_tuning_fa_vec_ne01_bucket" )== 0 ) { 948return (void * )ggml_backend_metal_tuning_fa_vec_ne01_bucket ; 949 } 950if (strcmp (name ,"ggml_backend_metal_tuning_fa_vec_baseline_ne" )== 0 ) { 951return (void * )ggml_backend_metal_tuning_fa_vec_baseline_ne ; 952 } 953if (strcmp (name ,"ggml_backend_metal_tuning_device_token" )== 0 ) { 954return (void * )ggml_backend_metal_tuning_device_token ; 955 } 956// generic fusion debugging API (ad-hoc proc-address mechanism, not part of the official 957// ggml backend interface yet; a backend that adopts it exports these exact names) 958if (strcmp (name ,"ggml_backend_fusion_get" )== 0 ) { 959return (void * )ggml_backend_metal_fusion_get ; 960 } 961if (strcmp (name ,"ggml_backend_fusion_stats_init" )== 0 ) { 962return (void * )ggml_backend_metal_fusion_stats_init ; 963 } 964if (strcmp (name ,"ggml_backend_fusion_stats_reset" )== 0 ) { 965return (void * )ggml_backend_metal_fusion_stats_reset ; 966 } 967if (strcmp (name ,"ggml_backend_fusion_stats_get" )== 0 ) { 968return (void * )ggml_backend_metal_fusion_stats_get ; 969 } 970if (strcmp (name ,"ggml_backend_fusion_set_enabled" )== 0 ) { 971return (void * )ggml_backend_metal_fusion_set_enabled ; 972 } 973 974return NULL ; 975 976GGML_UNUSED (reg ); 977} 978 979static ggml_backend_reg_i ggml_backend_metal_reg_i = { 980/* .get_name = */ ggml_backend_metal_reg_get_name , 981/* .get_device_count = */ ggml_backend_metal_reg_device_count , 982/* .get_device = */ ggml_backend_metal_reg_device_get , 983/* .get_proc_address = */ ggml_backend_metal_get_proc_address , 984}; 985 986static ggml_backend_dev_t ggml_backend_metal_device_init (ggml_backend_reg_t reg ,int device ) { 987return new ggml_backend_device { 988/* .iface = */ ggml_backend_metal_device_i , 989/* .reg = */ reg , 990/* .context = */ ggml_metal_device_get (device ,g_devices ), 991 }; 992} 993 994static void ggml_backend_metal_device_free (ggml_backend_dev_t dev ) { 995delete dev ; 996} 997 998struct ggml_backend_device_deleter { 999void operator()(ggml_backend_dev_t ctx ) { 1000ggml_backend_metal_device_free (ctx ); 1001 } 1002}; 1003 1004typedef std::unique_ptr < ggml_backend_device ,ggml_backend_device_deleter > ggml_backend_device_ptr ; 1005 1006ggml_backend_reg_t ggml_backend_metal_reg (void ) { 1007static ggml_backend_reg reg ; 1008static bool initialized = false; 1009 1010 { 1011static std::mutex mutex ; 1012 std::lock_guard < std::mutex > lock (mutex ); 1013 1014const char * env = getenv ("GGML_METAL_DEVICES" ); 1015if (env ) { 1016g_devices = atoi (env ); 1017 } 1018 1019static std::vector < ggml_backend_device_ptr > devs ; 1020 1021if (!initialized ) { 1022// workaround macOS limitation (kIOGPUCommandBufferCallbackErrorImpactingInteractivity) until proper fix becomes possible 1023// ref: https://github.com/ggml-org/llama.cpp/issues/20141#issuecomment-4272947703 1024setenv ("AGX_RELAX_CDM_CTXSTORE_TIMEOUT" ,"1" , true); 1025 1026static ggml_backend_metal_reg_ptr reg_ctx (ggml_backend_metal_reg_init ()); 1027 1028for (int i = 0 ;i < g_devices ;++ i ) { 1029auto * dev = ggml_backend_metal_device_init (& reg ,i ); 1030devs .emplace_back (dev ); 1031 1032reg_ctx -> devices .push_back (dev ); 1033 } 1034 1035reg = { 1036/* .api_version = */ GGML_BACKEND_API_VERSION , 1037/* .iface = */ ggml_backend_metal_reg_i , 1038/* .context = */ reg_ctx .get (), 1039 }; 1040 } 1041 1042initialized = true; 1043 } 1044 1045return & reg ; 1046} 1047 1048GGML_BACKEND_DL_IMPL (ggml_backend_metal_reg )