char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
57291f264
char/aliceai-t5-moe
1#include "ggml-alloc.h" 2#include "../ggml/src/ggml-backend-impl.h" 3#include "ggml-cpp.h" 4#include "../ggml/src/ggml-impl.h" 5#include "ggml.h" 6 7#include <algorithm> 8#include <exception> 9#include <memory> 10#include <vector> 11 12// 13// dummy backend with configurable max_buffer_size, tracks allocations 14 15uint8_t * const alloc_base = (uint8_t * )16 ; 16 17struct dummy_backend_context { 18size_t max_buffer_size = 64 ; 19size_t alignment = 8 ; 20 21ggml_backend_buffer_i buffer_interface ; 22ggml_backend_device device ; 23ggml_backend backend ; 24 std::vector < ggml_backend_buffer_t > buffers ; 25 26size_t allocated_total ()const { 27size_t n = 0 ; 28for (ggml_backend_buffer_t buf :buffers ) { 29n += ggml_backend_buffer_get_size (buf ); 30 } 31return n ; 32 } 33}; 34 35// ggml_backend_buffer_type interface 36 37static const char * dummy_backend_buffer_type_get_name (ggml_backend_buffer_type_t ) { 38return "dummy_buffer_type" ; 39} 40 41static ggml_backend_buffer_t dummy_backend_buffer_type_alloc_buffer (ggml_backend_buffer_type_t buft ,size_t size ) { 42dummy_backend_context * ctx = (dummy_backend_context * )buft -> context ; 43ggml_backend_buffer_t & buffer = ctx -> buffers .emplace_back (); 44buffer = ggml_backend_buffer_init (buft ,ctx -> buffer_interface ,ctx ,size ); 45return buffer ; 46} 47 48static size_t dummy_backend_buffer_type_get_alignment (ggml_backend_buffer_type_t buft ) { 49dummy_backend_context * ctx = (dummy_backend_context * )buft -> context ; 50return ctx -> alignment ; 51} 52 53static size_t dummy_backend_buffer_type_get_max_size (ggml_backend_buffer_type_t buft ) { 54dummy_backend_context * ctx = (dummy_backend_context * )buft -> context ; 55return ctx -> max_buffer_size ; 56} 57 58static bool dummy_backend_buffer_type_is_host (ggml_backend_buffer_type_t ) { 59return true; 60} 61 62// ggml_backend_buffer interface 63 64static void dummy_backend_buffer_free_buffer (ggml_backend_buffer_t buffer ) { 65dummy_backend_context * ctx = (dummy_backend_context * )buffer -> context ; 66 67auto i = std::find (ctx -> buffers .begin (),ctx -> buffers .end (),buffer ); 68GGML_ASSERT (i != ctx -> buffers .end ()); 69ctx -> buffers .erase (i ); 70} 71 72static void * dummy_backend_buffer_get_base (ggml_backend_buffer_t ) { 73return alloc_base ; 74} 75 76static ggml_status dummy_backend_buffer_init_tensor (ggml_backend_buffer_t ,ggml_tensor * ) { 77return GGML_STATUS_SUCCESS ; 78} 79 80static void dummy_backend_buffer_memset_tensor (ggml_backend_buffer_t ,ggml_tensor * ,uint8_t ,size_t ,size_t ) {} 81 82static void dummy_backend_buffer_set_tensor (ggml_backend_buffer_t ,ggml_tensor * ,const void * ,size_t ,size_t ) {} 83 84static void dummy_backend_buffer_get_tensor (ggml_backend_buffer_t ,const ggml_tensor * ,void * ,size_t ,size_t ) {} 85 86static void dummy_backend_buffer_clear (ggml_backend_buffer_t ,uint8_t ) {} 87 88// ggml_backend_device interface 89 90static enum ggml_backend_dev_type dummy_backend_device_get_type (ggml_backend_dev_t ) { 91return GGML_BACKEND_DEVICE_TYPE_CPU ; 92} 93 94static bool dummy_backend_device_supports_op (ggml_backend_dev_t ,const ggml_tensor * ) { 95return true; 96} 97 98static bool dummy_backend_device_supports_buft (ggml_backend_dev_t device ,ggml_backend_buffer_type_t buft ) { 99return device -> context == buft -> context ; 100} 101 102// ggml_backend interface 103 104static const char * dummy_backend_get_name (ggml_backend_t ) { 105return "dummy_backend" ; 106} 107 108// dummy_backend 109 110struct dummy_backend { 111 std::unique_ptr < dummy_backend_context > context ; 112ggml_backend_buffer_type buffer_type ; 113}; 114 115static dummy_backend dummy_backend_init (size_t max_buffer_size ,size_t alignment = 8 ) { 116dummy_backend b {}; 117b .context = std::make_unique < dummy_backend_context > (); 118b .context -> alignment = alignment ; 119b .context -> max_buffer_size = max_buffer_size ; 120 121b .context -> buffer_interface .free_buffer = dummy_backend_buffer_free_buffer ; 122b .context -> buffer_interface .get_base = dummy_backend_buffer_get_base ; 123b .context -> buffer_interface .init_tensor = dummy_backend_buffer_init_tensor ; 124b .context -> buffer_interface .memset_tensor = dummy_backend_buffer_memset_tensor ; 125b .context -> buffer_interface .set_tensor = dummy_backend_buffer_set_tensor ; 126b .context -> buffer_interface .get_tensor = dummy_backend_buffer_get_tensor ; 127b .context -> buffer_interface .clear = dummy_backend_buffer_clear ; 128 129b .context -> device .context = b .context .get (); 130b .context -> device .iface .get_type = dummy_backend_device_get_type ; 131b .context -> device .iface .supports_op = dummy_backend_device_supports_op ; 132b .context -> device .iface .supports_buft = dummy_backend_device_supports_buft ; 133 134b .context -> backend .context = b .context .get (); 135b .context -> backend .device = & b .context -> device ; 136b .context -> backend .iface .get_name = dummy_backend_get_name ; 137 138b .buffer_type .device = & b .context -> device ; 139b .buffer_type .context = b .context .get (); 140b .buffer_type .iface .get_name = dummy_backend_buffer_type_get_name ; 141b .buffer_type .iface .alloc_buffer = dummy_backend_buffer_type_alloc_buffer ; 142b .buffer_type .iface .get_alignment = dummy_backend_buffer_type_get_alignment ; 143b .buffer_type .iface .get_max_size = dummy_backend_buffer_type_get_max_size ; 144b .buffer_type .iface .is_host = dummy_backend_buffer_type_is_host ; 145return b ; 146} 147 148// 149// test utilities 150 151struct test_context_with_graph { 152ggml_context * ctx ; 153ggml_cgraph * graph ; 154ggml_context_ptr ctx_ptr ; 155}; 156 157static test_context_with_graph make_context () { 158ggml_init_params params {}; 159params .mem_size = 48 * ggml_tensor_overhead ()+ ggml_graph_overhead (); 160params .no_alloc = true; 161 162ggml_context * ctx = ggml_init (params ); 163ggml_context_ptr ctx_ptr = ggml_context_ptr (ctx ); 164ggml_cgraph * graph = ggml_new_graph (ctx ); 165return {ctx ,graph , std::move (ctx_ptr ) }; 166} 167 168static ggml_tensor * make_input_1d (ggml_context * ctx ,int64_t n_elements ) { 169ggml_tensor * t = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,n_elements ); 170ggml_set_input (t ); 171return t ; 172} 173 174static ggml_tensor * make_input_with_size (ggml_context * ctx ,size_t size_bytes ) { 175GGML_ASSERT (size_bytes %4 == 0 ); 176return make_input_1d (ctx ,size_bytes /4 ); 177} 178 179static void assign_names (ggml_context * ctx ,const char * prefix = "x" ) { 180int i = 0 ; 181for (ggml_tensor * t = ggml_get_first_tensor (ctx );t ;t = ggml_get_next_tensor (ctx ,t )) { 182ggml_format_name (t ,"%s%d" ,prefix ,i ++ ); 183 } 184} 185 186static int get_leaf_id (ggml_cgraph * graph ,const char * tensor_name ) { 187for (int i = 0 ;i < graph -> n_leafs ;++ i ) { 188if (strncmp (graph -> leafs [i ]-> name ,tensor_name ,GGML_MAX_NAME )== 0 ) { 189return i ; 190 } 191 } 192fprintf (stderr ,"leaf not found: %s\n" ,tensor_name ); 193return -1 ; 194} 195 196static int get_node_id (ggml_cgraph * graph ,const char * tensor_name ) { 197for (int i = 0 ;i < graph -> n_nodes ;++ i ) { 198if (strncmp (graph -> nodes [i ]-> name ,tensor_name ,GGML_MAX_NAME )== 0 ) { 199return i ; 200 } 201 } 202fprintf (stderr ,"node not found: %s" ,tensor_name ); 203return -1 ; 204} 205 206static ggml_gallocr_ptr allocate_graph (ggml_cgraph * graph ,ggml_tensor * out ,ggml_backend_buffer_type_t buft ) { 207ggml_set_output (out ); 208ggml_build_forward_expand (graph ,out ); 209 210ggml_gallocr_ptr galloc = ggml_gallocr_ptr (ggml_gallocr_new (buft )); 211bool result = ggml_gallocr_alloc_graph (galloc .get (),graph ); 212GGML_ASSERT (result ); 213return galloc ; 214} 215 216// 217// correctness checks for result allocations 218 219static void check_all_allocated (ggml_cgraph * graph ) { 220for (int i = 0 ;i < ggml_graph_n_nodes (graph );++ i ) { 221ggml_tensor * t = ggml_graph_node (graph ,i ); 222GGML_ASSERT (t -> buffer != nullptr ); 223GGML_ASSERT (t -> data != nullptr ); 224 } 225} 226 227static void check_max_size (ggml_context * ctx ) { 228for (ggml_tensor * t = ggml_get_first_tensor (ctx );t ;t = ggml_get_next_tensor (ctx ,t )) { 229auto buft = ggml_backend_buffer_get_type (t -> buffer ); 230size_t max_size = ggml_backend_buft_get_max_size (buft ); 231size_t offset = (char * )t -> data - (char * )ggml_backend_buffer_get_base (t -> buffer ); 232GGML_ASSERT (t -> data >=ggml_backend_buffer_get_base (t -> buffer )); 233GGML_ASSERT ((size_t )offset + ggml_nbytes (t ) <=max_size ); 234 } 235} 236 237static bool can_reuse_memory (ggml_cgraph * graph ,int current_i ,ggml_tensor * current ,ggml_tensor * other ) { 238if (other -> flags & GGML_TENSOR_FLAG_OUTPUT ) { 239return false; 240 } 241// Check if `other` is still "alive", ie. an input to any node after the `current` op 242for (int i = current_i ;i < ggml_graph_n_nodes (graph );++ i ) { 243ggml_tensor * t = ggml_graph_node (graph ,i ); 244for (int s = 0 ;s < GGML_MAX_SRC ;s ++ ) { 245if (t == current && ggml_op_can_inplace (t -> op )) { 246continue ; 247 } 248if (t -> src [s ]== other ) { 249return false; 250 } 251if (t -> src [s ]&& t -> src [s ]-> view_src == other ) { 252return false; 253 } 254 } 255 } 256return true; 257} 258 259static bool memory_overlap (ggml_tensor * a ,ggml_tensor * b ) { 260if (a -> buffer != b -> buffer ) { 261return false; 262 } 263int64_t a0 = (int64_t )a -> data ; 264int64_t a1 = a0 + ggml_nbytes (a ); 265int64_t b0 = (int64_t )b -> data ; 266int64_t b1 = b0 + ggml_nbytes (b ); 267return a1 > b0 && b1 > a0 ; 268} 269 270static ggml_tensor * get_view_source (ggml_tensor * t ) { 271while (t -> view_src ) { 272t = t -> view_src ; 273 } 274return t ; 275} 276 277static void check_no_overlap (ggml_cgraph * graph ) { 278for (int i = 0 ;i < ggml_graph_n_nodes (graph );++ i ) { 279for (int j = 0 ;j < i ;++ j ) { 280ggml_tensor * t = ggml_graph_node (graph ,i ); 281ggml_tensor * o = ggml_graph_node (graph ,j ); 282GGML_ASSERT (t != o ); 283 284if (get_view_source (t )== get_view_source (o )) { 285continue ; 286 } 287if (memory_overlap (t ,o )) { 288GGML_ASSERT (can_reuse_memory (graph ,i ,t ,o )); 289 } 290 } 291 } 292} 293 294// 295// test cases 296 297// Scenario where the first backend buffer is completely exhausted and there are further 298// tensors which require a second buffer 299static void test_max_size_too_many_tensors () { 300dummy_backend backend = dummy_backend_init (16 ); 301auto [ctx ,graph ,ctx_ptr ]= make_context (); 302 303ggml_tensor * x [7 ]; 304x [0 ]= make_input_with_size (ctx ,8 ); 305x [1 ]= make_input_with_size (ctx ,8 ); 306x [2 ]= make_input_with_size (ctx ,8 ); 307x [3 ]= ggml_mul (ctx ,x [0 ],x [1 ]); 308x [4 ]= ggml_add (ctx ,x [1 ],x [2 ]); 309x [5 ]= ggml_add (ctx ,x [3 ],x [0 ]); 310x [6 ]= ggml_add (ctx ,x [4 ],x [5 ]); 311assign_names (ctx ); 312 313ggml_gallocr_ptr galloc = allocate_graph (graph ,x [6 ],& backend .buffer_type ); 314check_all_allocated (graph ); 315check_no_overlap (graph ); 316check_max_size (ctx ); 317GGML_ASSERT (backend .context -> allocated_total () <=16 + 16 ); 318} 319 320// Scenario where there is some space left in the first buffer, but not enough to accommodate 321// a larger tensor, so a second buffer is required 322static void test_max_size_tensor_too_large () { 323dummy_backend backend = dummy_backend_init (32 ); 324auto [ctx ,graph ,ctx_ptr ]= make_context (); 325 326ggml_tensor * x [3 ]; 327x [0 ]= make_input_with_size (ctx ,16 );// chunk 0, [0 , 16) 328x [1 ]= make_input_with_size (ctx ,8 );// chunk 0, [16, 24) 329x [2 ]= ggml_concat (ctx ,x [0 ],x [1 ],0 );// chunk 1, [0 , 24) 330assign_names (ctx ); 331 332ggml_gallocr_ptr galloc = allocate_graph (graph ,x [2 ],& backend .buffer_type ); 333check_all_allocated (graph ); 334check_no_overlap (graph ); 335check_max_size (ctx ); 336GGML_ASSERT (backend .context -> allocated_total () <=32 + 24 ); 337} 338 339// Scenario where a single tensor exceeds the max buffer size - in this case the allocator 340// should try to create a bigger buffer anyway, and wait for the backend to throw an error. 341// Backends may report an artificially lower max size in some cases for compatibility reasons. 342static void test_tensor_larger_than_max_size () { 343dummy_backend backend = dummy_backend_init (16 ); 344auto [ctx ,graph ,ctx_ptr ]= make_context (); 345 346ggml_tensor * x [2 ]; 347x [0 ]= make_input_with_size (ctx ,24 ); 348x [1 ]= ggml_scale (ctx ,x [0 ],2.0f ); 349assign_names (ctx ); 350 351ggml_gallocr_ptr galloc = allocate_graph (graph ,x [1 ],& backend .buffer_type ); 352check_all_allocated (graph ); 353check_no_overlap (graph ); 354GGML_ASSERT (backend .context -> allocated_total ()== 24 ); 355} 356 357// This test assumes a max of 16 buffer chunks, and tries to allocate tensors that would 358// require more. Expectation is that the last buffer should grow to fit everything, 359// leaving it to the backend to error out if it can't allocate that much. 360static void test_not_enough_chunks () { 361const int max_chunks = 16 ; 362const int max_size = 8 ; 363 364dummy_backend backend = dummy_backend_init (max_size ); 365auto [ctx ,graph ,ctx_ptr ]= make_context (); 366 367ggml_tensor * x [max_chunks + 1 ]; 368for (int i = 0 ;i < max_chunks + 1 ;++ i ) { 369x [i ]= make_input_with_size (ctx ,max_size ); 370 } 371ggml_tensor * acc = x [0 ]; 372for (int i = 0 ;i < max_chunks ;++ i ) { 373acc = ggml_add (ctx ,acc ,x [i + 1 ]); 374 } 375assign_names (ctx ); 376 377ggml_gallocr_ptr galloc = allocate_graph (graph ,acc ,& backend .buffer_type ); 378check_all_allocated (graph ); 379check_no_overlap (graph ); 380GGML_ASSERT (backend .context -> allocated_total ()> max_chunks * max_size ); 381} 382 383// Fill up leftover unallocated space of a chunk after allocating a large tensor that 384// requires a new chunk. 385static void test_fill_leftover_space () { 386dummy_backend backend = dummy_backend_init (16 ); 387auto [ctx ,graph ,ctx_ptr ]= make_context (); 388 389ggml_tensor * x [4 ]; 390x [0 ]= make_input_with_size (ctx ,8 ); 391x [1 ]= ggml_pad (ctx ,x [0 ],2 ,0 ,0 ,0 ); 392x [3 ]= ggml_mean (ctx ,x [1 ]); 393assign_names (ctx ); 394 395ggml_gallocr_ptr galloc = allocate_graph (graph ,x [3 ],& backend .buffer_type ); 396check_all_allocated (graph ); 397check_no_overlap (graph ); 398check_max_size (ctx ); 399GGML_ASSERT (backend .context -> allocated_total () <=12 + 16 ); 400} 401 402// Check that views don't require any extra memory 403static void test_view_inplace () { 404dummy_backend backend = dummy_backend_init (32 ); 405auto [ctx ,graph ,ctx_ptr ]= make_context (); 406 407ggml_tensor * x [6 ]; 408x [0 ]= make_input_1d (ctx ,4 );// chunk 0, [0, 16) 409x [1 ]= ggml_reshape_2d (ctx ,x [0 ],2 ,2 );// view of x0 410x [2 ]= ggml_permute (ctx ,x [1 ],1 ,0 ,2 ,3 );// view of x0 411x [3 ]= ggml_view_1d (ctx ,x [2 ],2 ,4 );// view of x0 412x [4 ]= make_input_1d (ctx ,2 );// chunk 0, [16, 24) 413x [5 ]= ggml_add (ctx ,x [3 ],x [4 ]);// reuse (inplace add) 414assign_names (ctx ); 415 416ggml_gallocr_ptr galloc = allocate_graph (graph ,x [5 ],& backend .buffer_type ); 417check_all_allocated (graph ); 418check_no_overlap (graph ); 419check_max_size (ctx ); 420GGML_ASSERT (backend .context -> allocated_total () <=24 ); 421} 422 423static void test_reuse_and_free () { 424dummy_backend backend = dummy_backend_init (40 ); 425auto [ctx ,graph ,ctx_ptr ]= make_context (); 426 427ggml_tensor * x [9 ]; 428x [0 ]= make_input_with_size (ctx ,24 ); 429x [1 ]= make_input_with_size (ctx ,8 ); 430x [2 ]= make_input_with_size (ctx ,8 ); 431x [3 ]= ggml_add (ctx ,x [1 ],x [2 ]);// reuse, free x2 432x [4 ]= ggml_pad (ctx ,x [0 ],2 ,0 ,0 ,0 );// alloc new buffer, free x0 433x [5 ]= ggml_scale (ctx ,x [4 ],2.0f );// alloc from free block 434x [6 ]= ggml_add (ctx ,x [4 ],x [5 ]);// reuse, free x5 435x [7 ]= ggml_view_1d (ctx ,x [6 ],2 ,8 );// view 436x [8 ]= ggml_add (ctx ,x [3 ],x [7 ]);// reuse 437assign_names (ctx ); 438 439ggml_gallocr_ptr galloc = allocate_graph (graph ,x [8 ],& backend .buffer_type ); 440check_all_allocated (graph ); 441check_no_overlap (graph ); 442check_max_size (ctx ); 443GGML_ASSERT (backend .context -> allocated_total () <=40 + 32 + 32 ); 444} 445 446static void test_merge_free_block (size_t max_buffer_size ) { 447dummy_backend backend = dummy_backend_init (max_buffer_size ); 448auto [ctx ,graph ,ctx_ptr ]= make_context (); 449 450ggml_tensor * x [9 ]; 451x [0 ]= make_input_with_size (ctx ,16 ); 452x [1 ]= make_input_with_size (ctx ,16 ); 453x [2 ]= make_input_with_size (ctx ,16 ); 454x [3 ]= ggml_mean (ctx ,x [0 ]); 455x [4 ]= ggml_mean (ctx ,x [1 ]); 456x [5 ]= ggml_pad (ctx ,x [2 ],2 ,0 ,0 ,0 ); 457x [6 ]= ggml_add (ctx ,x [3 ],x [4 ]); 458x [7 ]= ggml_pad (ctx ,x [6 ],5 ,0 ,0 ,0 ); 459x [8 ]= ggml_add (ctx ,x [5 ],x [7 ]); 460assign_names (ctx ); 461 462ggml_gallocr_ptr galloc = allocate_graph (graph ,x [8 ],& backend .buffer_type ); 463check_all_allocated (graph ); 464check_no_overlap (graph ); 465check_max_size (ctx ); 466GGML_ASSERT (backend .context -> allocated_total () <=32 + 32 + 24 ); 467} 468 469// Check that previously allocated but freed memory is preferred over allocating 470// additional memory, even if the remaining space in a chunk would match tensor size better 471static void test_prefer_already_allocated_memory () { 472dummy_backend backend = dummy_backend_init (32 ,/*align*/ 4 ); 473auto [ctx ,graph ,ctx_ptr ]= make_context (); 474 475ggml_tensor * x [3 ]; 476x [0 ]= make_input_with_size (ctx ,24 );// [24b][8b unused] 477x [1 ]= ggml_mean (ctx ,x [0 ]);// [24b free][4b][4b unused] 478x [2 ]= ggml_mean (ctx ,x [1 ]);// should be allocated in the 24b block 479assign_names (ctx ); 480 481ggml_gallocr_ptr galloc = allocate_graph (graph ,x [2 ],& backend .buffer_type ); 482check_all_allocated (graph ); 483check_no_overlap (graph ); 484GGML_ASSERT (backend .context -> allocated_total () <=28 ); 485} 486 487// test for allocating on multiple devices with some tensors in the graph 488// allocated externally (not by gallocr). 489static void test_multiple_buffer_types () { 490dummy_backend backend_a = dummy_backend_init (32 ); 491dummy_backend backend_b = dummy_backend_init (SIZE_MAX ); 492 493auto [ctx_a ,_a ,ctx_a_ptr ]= make_context (); 494auto [ctx_b ,_b ,ctx_b_ptr ]= make_context (); 495auto [ctx ,graph ,ctx_ptr ]= make_context (); 496 497ggml_tensor * a [2 ]; 498a [0 ]= make_input_with_size (ctx_a ,16 ); 499a [1 ]= make_input_with_size (ctx_a ,16 ); 500assign_names (ctx_a ,"a" ); 501 502ggml_tensor * b [2 ]; 503b [0 ]= make_input_with_size (ctx_b ,24 ); 504b [1 ]= make_input_with_size (ctx_b ,4 ); 505assign_names (ctx_b ,"b" ); 506 507ggml_tensor * x [9 ]; 508x [0 ]= make_input_with_size (ctx ,16 ); 509x [1 ]= ggml_mul (ctx ,x [0 ],a [0 ]); 510x [2 ]= ggml_pad (ctx ,x [1 ],2 ,0 ,0 ,0 ); 511x [3 ]= ggml_mul (ctx ,x [2 ],b [0 ]); 512x [4 ]= ggml_mean (ctx ,x [3 ]); 513x [5 ]= ggml_add (ctx ,x [4 ],b [1 ]); 514x [6 ]= ggml_pad (ctx ,x [5 ],3 ,0 ,0 ,0 ); 515x [7 ]= ggml_add (ctx ,x [6 ],a [1 ]); 516x [8 ]= ggml_scale (ctx ,x [7 ],2.0f ); 517assign_names (ctx ,"x" ); 518 519ggml_backend_buffer_ptr buf_a (ggml_backend_alloc_ctx_tensors_from_buft (ctx_a ,& backend_a .buffer_type )); 520ggml_backend_buffer_ptr buf_b (ggml_backend_alloc_ctx_tensors_from_buft (ctx_b ,& backend_b .buffer_type )); 521ggml_backend_buffer_type_t bufts [2 ]= {& backend_a .buffer_type ,& backend_b .buffer_type }; 522 523// assign buffer types manually to avoid extra complexity from backend scheduler 524ggml_set_output (x [8 ]); 525ggml_build_forward_expand (graph ,x [8 ]); 526 527GGML_ASSERT (graph -> n_leafs == 5 ); 528int leaf_buffer_ids [5 ]; 529leaf_buffer_ids [get_leaf_id (graph ,"a0" )]= 0 ; 530leaf_buffer_ids [get_leaf_id (graph ,"a1" )]= 0 ; 531leaf_buffer_ids [get_leaf_id (graph ,"b0" )]= 1 ; 532leaf_buffer_ids [get_leaf_id (graph ,"b1" )]= 1 ; 533leaf_buffer_ids [get_leaf_id (graph ,"x0" )]= 0 ; 534 535GGML_ASSERT (graph -> n_nodes == 8 ); 536int node_buffer_ids [8 ]; 537node_buffer_ids [get_node_id (graph ,"x1" )]= 0 ; 538node_buffer_ids [get_node_id (graph ,"x2" )]= 0 ; 539node_buffer_ids [get_node_id (graph ,"x3" )]= 1 ; 540node_buffer_ids [get_node_id (graph ,"x4" )]= 1 ; 541node_buffer_ids [get_node_id (graph ,"x5" )]= 1 ; 542node_buffer_ids [get_node_id (graph ,"x6" )]= 1 ; 543node_buffer_ids [get_node_id (graph ,"x7" )]= 0 ; 544node_buffer_ids [get_node_id (graph ,"x8" )]= 0 ; 545 546ggml_gallocr_ptr galloc (ggml_gallocr_new_n (bufts ,2 )); 547ggml_gallocr_reserve_n (galloc .get (),graph ,node_buffer_ids ,leaf_buffer_ids ); 548ggml_gallocr_alloc_graph (galloc .get (),graph ); 549 550check_all_allocated (graph ); 551check_no_overlap (graph ); 552check_max_size (ctx ); 553GGML_ASSERT (backend_a .context -> allocated_total () <=32 + 32 + 24 ); 554GGML_ASSERT (backend_b .context -> allocated_total () <=32 + 24 ); 555} 556 557static void test_buffer_size_zero () { 558dummy_backend backend_a = dummy_backend_init (SIZE_MAX ); 559dummy_backend backend_b = dummy_backend_init (SIZE_MAX ); 560auto [ctx ,graph ,ctx_ptr ]= make_context (); 561 562ggml_tensor * x [2 ]; 563x [0 ]= make_input_with_size (ctx ,16 ); 564x [1 ]= ggml_scale (ctx ,x [0 ],2.0f ); 565 566ggml_set_output (x [1 ]); 567ggml_build_forward_expand (graph ,x [1 ]); 568 569int leaf_buffer_ids [1 ]= {0 }; 570int node_buffer_ids [1 ]= {0 }; 571 572ggml_backend_buffer_type_t bufts [2 ]= {& backend_a .buffer_type ,& backend_b .buffer_type }; 573ggml_gallocr_ptr galloc = ggml_gallocr_ptr (ggml_gallocr_new_n (bufts ,2 )); 574bool res1 = ggml_gallocr_reserve_n (galloc .get (),graph ,node_buffer_ids ,leaf_buffer_ids ); 575bool res2 = ggml_gallocr_alloc_graph (galloc .get (),graph ); 576GGML_ASSERT (res1 && res2 ); 577 578check_all_allocated (graph ); 579GGML_ASSERT (backend_a .context -> allocated_total ()== 16 ); 580GGML_ASSERT (backend_b .context -> allocated_total ()== 0 ); 581} 582 583// Test re-using gallocr for a different graph. The new graph has the same 584// total size, but one of the chunks is larger, so reallocation is required. 585static void test_reallocation () { 586dummy_backend backend = dummy_backend_init (32 ,/*align*/ 4 ); 587ggml_gallocr_ptr galloc ; 588 { 589auto [ctx ,graph ,ctx_ptr ]= make_context (); 590ggml_tensor * x [4 ]; 591x [0 ]= make_input_with_size (ctx ,24 ); 592x [1 ]= make_input_with_size (ctx ,16 ); 593x [2 ]= ggml_view_1d (ctx ,x [0 ],4 ,0 ); 594x [3 ]= ggml_add (ctx ,x [2 ],x [1 ]); 595assign_names (ctx ); 596 597galloc = allocate_graph (graph ,x [3 ],& backend .buffer_type ); 598check_all_allocated (graph ); 599GGML_ASSERT (backend .context -> allocated_total ()== 40 ); 600 } 601 { 602auto [ctx ,graph ,ctx_ptr ]= make_context (); 603ggml_tensor * x [3 ]; 604x [0 ]= make_input_with_size (ctx ,20 ); 605x [1 ]= make_input_with_size (ctx ,20 ); 606x [2 ]= ggml_add (ctx ,x [0 ],x [1 ]); 607assign_names (ctx ); 608ggml_set_output (x [2 ]); 609ggml_build_forward_expand (graph ,x [2 ]); 610 611bool result = ggml_gallocr_alloc_graph (galloc .get (),graph ); 612GGML_ASSERT (result ); 613check_all_allocated (graph ); 614GGML_ASSERT (backend .context -> allocated_total ()== 40 ); 615 } 616} 617 618static void test_backend_graph_optimize (ggml_backend_t ,ggml_cgraph * graph ,ggml_backend_graph_optimize_params * params ) { 619GGML_ASSERT (graph -> n_nodes == 3 ); 620params -> add_alloc_dep (params -> user_data ,graph -> nodes [0 ],graph -> nodes [2 ]); 621} 622 623static bool graph_reuses_allocation (bool add_alloc_dep ) { 624auto [ctx ,graph ,ctx_ptr ]= make_context (); 625 626ggml_tensor * x [4 ]; 627x [0 ]= make_input_with_size (ctx ,16 ); 628x [1 ]= ggml_scale (ctx ,x [0 ],2.0f ); 629x [2 ]= ggml_scale (ctx ,x [1 ],2.0f ); 630x [3 ]= ggml_scale (ctx ,x [2 ],2.0f ); 631 632ggml_set_output (x [3 ]); 633ggml_build_forward_expand (graph ,x [3 ]); 634 635dummy_backend backend = dummy_backend_init (SIZE_MAX ); 636if (add_alloc_dep ) { 637backend .context -> backend .iface .graph_optimize = test_backend_graph_optimize ; 638 } 639 640ggml_backend_t backend_ptr = & backend .context -> backend ; 641ggml_backend_buffer_type_t buft = & backend .buffer_type ; 642ggml_backend_sched_ptr sched (ggml_backend_sched_new (& backend_ptr ,& buft ,1 ,8 , false, true)); 643GGML_ASSERT (ggml_backend_sched_alloc_graph (sched .get (),graph )); 644 645return x [1 ]-> data == x [2 ]-> data ; 646} 647 648static void test_graph_optimize_alloc_dep () { 649GGML_ASSERT (graph_reuses_allocation (false)); 650GGML_ASSERT (!graph_reuses_allocation (true)); 651} 652 653static void run (const char * name ,void (* f )()) { 654printf ("%s " ,name ); 655fflush (stdout ); 656f (); 657printf ("PASSED\n" ); 658} 659 660int main () { 661run ("test_max_size_too_many_tensors" ,test_max_size_too_many_tensors ); 662run ("test_max_size_tensor_too_large" ,test_max_size_tensor_too_large ); 663run ("test_tensor_larger_than_max_size" ,test_tensor_larger_than_max_size ); 664run ("test_not_enough_chunks" ,test_not_enough_chunks ); 665run ("test_fill_leftover_space" ,test_fill_leftover_space ); 666run ("test_view_inplace" ,test_view_inplace ); 667run ("test_reuse_and_free" ,test_reuse_and_free ); 668run ("test_merge_free_block(32)" , []() {test_merge_free_block (32 ); }); 669run ("test_merge_free_block(SIZE_MAX)" , []() {test_merge_free_block (SIZE_MAX ); }); 670run ("test_prefer_already_allocated_memory" ,test_prefer_already_allocated_memory ); 671run ("test_multiple_buffer_types" ,test_multiple_buffer_types ); 672run ("test_buffer_size_zero" ,test_buffer_size_zero ); 673run ("test_reallocation" ,test_reallocation ); 674run ("test_graph_optimize_alloc_dep" ,test_graph_optimize_alloc_dep ); 675return 0 ; 676}