char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
153d324bc
char/aliceai-t5-moe
1#include "ggml-zendnn.h" 2 3#include "ggml-backend-impl.h" 4#include "ggml-impl.h" 5 6#define GGML_COMMON_DECL_CPP 7#include "ggml-common.h" 8 9#include "zendnnl.hpp" 10 11#include <cstring> 12 13 14struct ggml_backend_zendnn_context { 15int n_threads = GGML_DEFAULT_N_THREADS ; 16 std::unique_ptr < char []> work_data ; 17size_t work_size = 0 ; 18}; 19 20template < typename T > 21zendnnl::common::data_type_t ggml_to_zendnn_type () { 22if constexpr (std::is_same_v < T ,float > ) { 23return zendnnl::common::data_type_t::f32 ; 24 }else if constexpr (std::is_same_v < T ,ggml_bf16_t > ) { 25return zendnnl::common::data_type_t::bf16 ; 26 }else if constexpr (std::is_same_v < T ,block_q8_0 > ) { 27return zendnnl::common::data_type_t::s8 ; 28 }else { 29return zendnnl::common::data_type_t::none ; 30 } 31} 32 33/** 34* Builds the matmul_params shared by ggml_zendnn_matmul() and ggml_zendnn_group_matmul(): 35* dtype selection plus, for Q8_0 weights, dynamic-quant setup. Callers still need to set 36* quant_params.src_scale.dims themselves, since that depends on the batch size(s) in use. 37*/ 38template < typename TA ,typename TB ,typename TC > 39static zendnnl::lowoha::matmul::matmul_params ggml_zendnn_make_matmul_params (ggml_backend_zendnn_context * ctx ) { 40 zendnnl::lowoha::matmul::matmul_params params ; 41params .dtypes .src = ggml_to_zendnn_type < TB > (); 42params .dtypes .wei = ggml_to_zendnn_type < TA > (); 43params .dtypes .dst = ggml_to_zendnn_type < TC > (); 44params .num_threads = ctx -> n_threads ; 45 46if constexpr (std::is_same_v < TA ,block_q8_0 > ) { 47params .dtypes .compute = zendnnl::common::data_type_t::s8 ; 48params .dynamic_quant = true; 49params .quant_params .src_scale .buff = nullptr ; 50params .quant_params .src_scale .dt = zendnnl::common::data_type_t::bf16 ; 51params .packing .pack_format_b = 1 ; 52 } 53return params ; 54} 55 56/** 57* ZenDNN matmul: computes C = B * A. 58* 59* - A: weights, shape (k, m), column-major (each column is a weight vector for one output). 60* - B: input, shape (n, k), row-major (each row is an input sample). 61* - C: output, shape (n, m), row-major. 62* 63* Dimensions: 64* m = output features (columns of C, columns of A) 65* n = batch size (rows of C, rows of B) 66* k = inner dimension (columns of B, rows of A) 67*/ 68template < typename TA ,typename TB ,typename TC > 69static bool ggml_zendnn_matmul (ggml_backend_zendnn_context * ctx ,int64_t m ,int64_t n ,int64_t k , 70const TA * A ,int64_t lda ,const TB * B ,int64_t ldb ,TC * C , 71int64_t ldc ) { 72 73 zendnnl::lowoha::matmul::matmul_params params = ggml_zendnn_make_matmul_params < TA ,TB ,TC > (ctx ); 74 75 zendnnl::lowoha::matmul::matmul_batch_params_t batch_params ; 76 77if constexpr (std::is_same_v < TA ,block_q8_0 > ) { 78params .quant_params .src_scale .dims = {n ,k /QK8_0 }; 79 } 80 81 zendnnl::error_handling::status_t status = zendnnl::lowoha::matmul::matmul_direct ( 82'r' , false, true,// row-major, don't transpose B, transpose A (because it's column-major) 83n ,// M: rows of B and C 84m ,// N: cols of A^T and C 85k ,// K: cols of B, rows of A 861.0f ,// alpha 87B ,ldb ,// src: B[n,k] 88A ,lda ,// weight: A[k,m] column-major (transposed) 89nullptr ,// bias 900.0f ,// beta 91C ,ldc ,// output C[n,m] 92 true,// is_weights_const 93batch_params ,// batch_params 94params // params 95 ); 96 97if (status != zendnnl::error_handling::status_t::success ) { 98GGML_LOG_ERROR ("%s, ZenDNN matmul failed: status=%d\n" ,__func__ ,static_cast < int > (status )); 99return false; 100 } 101return true; 102} 103 104static bool ggml_zendnn_gemm (ggml_backend_zendnn_context * ctx ,int64_t m ,int64_t n ,int64_t k , 105const void * A ,int64_t lda ,const void * B ,int64_t ldb ,void * C , 106int64_t ldc ,int Atype ,int Btype ,int Ctype ) { 107 108assert (m >=0 ); 109assert (n >=0 ); 110assert (k >=0 ); 111assert (lda >=k ); 112assert (ldb >=k ); 113assert (ldc >=m ); 114 115// categorize types 116switch (Atype ) { 117case GGML_TYPE_F32 : 118if (Btype != GGML_TYPE_F32 || Ctype != GGML_TYPE_F32 ) 119return false; 120return ggml_zendnn_matmul < float ,float ,float > ( 121ctx ,m ,n ,k , 122 (const float * )A ,lda , 123 (const float * )B ,ldb , 124 (float * )C ,ldc ); 125case GGML_TYPE_BF16 : 126if (Btype != GGML_TYPE_BF16 ) 127return false; 128if (Ctype == GGML_TYPE_BF16 ) 129return ggml_zendnn_matmul < ggml_bf16_t ,ggml_bf16_t ,ggml_bf16_t > ( 130ctx ,m ,n ,k , 131 (const ggml_bf16_t * )A ,lda , 132 (const ggml_bf16_t * )B ,ldb , 133 (ggml_bf16_t * )C ,ldc ); 134if (Ctype == GGML_TYPE_F32 ) 135return ggml_zendnn_matmul < ggml_bf16_t ,ggml_bf16_t ,float > ( 136ctx ,m ,n ,k , 137 (const ggml_bf16_t * )A ,lda , 138 (const ggml_bf16_t * )B ,ldb , 139 (float * )C ,ldc ); 140return false; 141case GGML_TYPE_Q8_0 : 142if (Btype != GGML_TYPE_F32 || Ctype != GGML_TYPE_F32 ) 143return false; 144return ggml_zendnn_matmul < block_q8_0 ,float ,float > ( 145ctx ,m ,n ,k , 146 (const block_q8_0 * )A ,lda , 147 (const float * )B ,ldb , 148 (float * )C ,ldc ); 149default : 150return false;// unsupported type 151 } 152} 153 154static void ggml_zendnn_compute_forward_mul_mat ( 155ggml_backend_zendnn_context * ctx , 156ggml_tensor * dst ) { 157 158const ggml_tensor * src0 = dst -> src [0 ];// weights 159const ggml_tensor * src1 = dst -> src [1 ];// inputs 160 161GGML_TENSOR_BINARY_OP_LOCALS 162 163ggml_type const vec_dot_type = src0 -> type ; 164ggml_from_float_t const from_float = ggml_get_type_traits (vec_dot_type )-> from_float_ref ; 165 166GGML_ASSERT (ne0 == ne01 ); 167GGML_ASSERT (ne1 == ne11 ); 168GGML_ASSERT (ne2 == ne12 ); 169GGML_ASSERT (ne3 == ne13 ); 170 171// we don't support permuted src0 or src1 172GGML_ASSERT (nb00 == ggml_type_size (src0 -> type )); 173GGML_ASSERT (nb10 == ggml_type_size (src1 -> type )); 174 175// dst cannot be transposed or permuted 176GGML_ASSERT (nb0 == sizeof (float )); 177GGML_ASSERT (nb0 <=nb1 ); 178GGML_ASSERT (nb1 <=nb2 ); 179GGML_ASSERT (nb2 <=nb3 ); 180 181// broadcast factors 182const int64_t r2 = ne12 /ne02 ; 183const int64_t r3 = ne13 /ne03 ; 184 185void * work_data = ctx -> work_data .get (); 186 187// ZenDNN requires FP32 for dynamic quantization, so conversion is skipped 188if (src1 -> type != vec_dot_type && src0 -> type != GGML_TYPE_Q8_0 ) { 189const size_t nbw1 = ggml_row_size (vec_dot_type ,ne10 ); 190const size_t nbw2 = nbw1 * ne11 ; 191const size_t nbw3 = nbw2 * ne12 ; 192const size_t desired_wsize = ne13 * nbw3 ; 193if (ctx -> work_size < desired_wsize ) { 194ctx -> work_data .reset (new char [desired_wsize ]); 195ctx -> work_size = desired_wsize ; 196 } 197work_data = ctx -> work_data .get (); 198 199// #pragma omp parallel for num_threads(ctx->n_threads) 200#pragma omp parallel for collapse(3) num_threads(ctx->n_threads) schedule(static) 201for (int64_t i13 = 0 ;i13 < ne13 ;++ i13 ) { 202for (int64_t i12 = 0 ;i12 < ne12 ;++ i12 ) { 203for (int64_t i11 = 0 ;i11 < ne11 ;++ i11 ) { 204const float * src1_f32 = (float * )((char * )src1 -> data + i11 * nb11 + i12 * nb12 + i13 * nb13 ); 205void * src1_conv = (char * )work_data + i11 * nbw1 + i12 * nbw2 + i13 * nbw3 ; 206from_float (src1_f32 ,src1_conv ,ne10 ); 207 } 208 } 209 } 210 } 211 212for (int64_t i13 = 0 ;i13 < ne13 ;i13 ++ ) { 213for (int64_t i12 = 0 ;i12 < ne12 ;i12 ++ ) { 214const void * wdata = (src1 -> type == vec_dot_type || src0 -> type == GGML_TYPE_Q8_0 ) ?src1 -> data :work_data ; 215const size_t row_size = ggml_row_size (vec_dot_type ,ne10 ); 216if (!ggml_zendnn_gemm (ctx , 217ne01 ,// m 218ne11 ,// n 219ne10 ,// k 220static_cast < const char *> (src0 -> data )+ (i12 /r2 )* nb02 + (i13 /r3 )* nb03 , 221ne00 ,// lda 222static_cast < const char *> (wdata )+ (i12 * ne11 + i13 * ne12 * ne11 )* row_size , 223ne10 ,// ldb 224static_cast < char *> (dst -> data )+ i12 * nb2 + i13 * nb3 , 225ne01 ,// ldc 226src0 -> type , 227src0 -> type == GGML_TYPE_Q8_0 ?GGML_TYPE_F32 :vec_dot_type , 228dst -> type )) 229GGML_ABORT ("%s: ZenDNN gemm failed\n" ,__func__ ); 230 } 231 } 232} 233 234struct mmid_row_mapping { 235int32_t i1 ; 236int32_t i2 ; 237}; 238 239/** 240* ZenDNN batched matmul: computes C[i] = B[i] * A[i] for every active expert i via a single 241* group_matmul_direct() call. Batched analogue of ggml_zendnn_matmul() - see its docs for the 242* per-expert A/B/C shape convention. m and k are shared by every expert; n (batch size) varies 243* per expert, hence the vector. 244*/ 245template < typename TA ,typename TB ,typename TC > 246static bool ggml_zendnn_group_matmul (ggml_backend_zendnn_context * ctx ,int64_t m ,int64_t k , 247const std::vector < int64_t > & n , 248const std::vector < const void *> & A ,int64_t lda , 249const std::vector < const void *> & B ,int64_t ldb , 250const std::vector < void *> & C ,int64_t ldc ) { 251 252const int n_experts = n .size (); 253 254 zendnnl::lowoha::matmul::matmul_params base_params = ggml_zendnn_make_matmul_params < TA ,TB ,TC > (ctx ); 255 256 std::vector < char > layout (n_experts ,'r' ); 257 std::vector < bool > trans_a (n_experts ,false ); 258 std::vector < bool > trans_b (n_experts ,true ); 259 std::vector < int > batch_m (n_experts ); 260 std::vector < int > batch_n (n_experts ,m ); 261 std::vector < int > batch_k (n_experts ,k ); 262 std::vector < float > alpha (n_experts ,1.0f ); 263 std::vector < float > beta (n_experts ,0.0f ); 264 std::vector < const void *> bias (n_experts ,nullptr ); 265 std::vector < int > lda_v (n_experts ,lda ); 266 std::vector < int > ldb_v (n_experts ,ldb ); 267 std::vector < int > ldc_v (n_experts ,ldc ); 268 std::vector < bool > is_wei_const (n_experts ,true ); 269 std::vector < zendnnl::lowoha::matmul::matmul_params > params (n_experts ,base_params ); 270 271for (int i = 0 ;i < n_experts ;i ++ ) { 272batch_m [i ]= n [i ]; 273 274// src_scale.dims depends on this expert's row count, unlike the rest of base_params 275if constexpr (std::is_same_v < TA ,block_q8_0 > ) { 276params [i ].quant_params .src_scale .dims = {n [i ],k /QK8_0 }; 277 } 278 } 279 280 zendnnl::error_handling::status_t status = zendnnl::lowoha::matmul::group_matmul_direct ( 281layout ,trans_a ,trans_b ,batch_m ,batch_n ,batch_k ,alpha , 282B ,ldb_v ,A ,lda_v ,bias ,beta , 283C ,ldc_v ,is_wei_const ,params ); 284 285if (status != zendnnl::error_handling::status_t::success ) { 286GGML_LOG_ERROR ("%s, ZenDNN group matmul failed: status=%d\n" ,__func__ ,static_cast < int > (status )); 287return false; 288 } 289return true; 290} 291 292static bool ggml_zendnn_group_gemm (ggml_backend_zendnn_context * ctx ,int64_t m ,int64_t k , 293const std::vector < int64_t > & n , 294const std::vector < const void *> & A ,int64_t lda , 295const std::vector < const void *> & B ,int64_t ldb , 296const std::vector < void *> & C ,int64_t ldc , 297int Atype ,int Btype ,int Ctype ) { 298 299assert (m >=0 ); 300for (size_t i = 0 ;i < n .size ();i ++ ) { 301assert (n [i ] >=0 ); 302 } 303assert (k >=0 ); 304assert (lda >=k ); 305assert (ldb >=k ); 306assert (ldc >=m ); 307 308// categorize types 309switch (Atype ) { 310case GGML_TYPE_F32 : 311if (Btype != GGML_TYPE_F32 || Ctype != GGML_TYPE_F32 ) 312return false; 313return ggml_zendnn_group_matmul < float ,float ,float > (ctx ,m ,k ,n ,A ,lda ,B ,ldb ,C ,ldc ); 314case GGML_TYPE_BF16 : 315if (Btype != GGML_TYPE_BF16 ) 316return false; 317if (Ctype == GGML_TYPE_BF16 ) 318return ggml_zendnn_group_matmul < ggml_bf16_t ,ggml_bf16_t ,ggml_bf16_t > ( 319ctx ,m ,k ,n ,A ,lda ,B ,ldb ,C ,ldc ); 320if (Ctype == GGML_TYPE_F32 ) 321return ggml_zendnn_group_matmul < ggml_bf16_t ,ggml_bf16_t ,float > (ctx ,m ,k ,n ,A ,lda ,B ,ldb ,C ,ldc ); 322return false; 323case GGML_TYPE_Q8_0 : 324if (Btype != GGML_TYPE_F32 || Ctype != GGML_TYPE_F32 ) 325return false; 326return ggml_zendnn_group_matmul < block_q8_0 ,float ,float > (ctx ,m ,k ,n ,A ,lda ,B ,ldb ,C ,ldc ); 327default : 328return false;// unsupported type 329 } 330} 331 332static void ggml_zendnn_compute_forward_mul_mat_id ( 333ggml_backend_zendnn_context * ctx , 334ggml_tensor * dst ) { 335 336const ggml_tensor * src0 = dst -> src [0 ];// expert weights 337const ggml_tensor * src1 = dst -> src [1 ];// inputs 338const ggml_tensor * ids = dst -> src [2 ];// expert ids 339 340GGML_TENSOR_BINARY_OP_LOCALS 341 342// exit for no tokens to process 343if (ne2 == 0 || ne11 == 0 ) { 344return ; 345 } 346 347ggml_type const vec_dot_type = src0 -> type ; 348ggml_from_float_t const from_float = ggml_get_type_traits (vec_dot_type )-> from_float_ref ; 349 350// we don't support permuted src0 or src1 351GGML_ASSERT (nb00 == ggml_type_size (src0 -> type )); 352GGML_ASSERT (nb10 == ggml_type_size (src1 -> type )); 353 354// dst cannot be transposed or permuted 355GGML_ASSERT (nb0 == sizeof (float )); 356GGML_ASSERT (nb0 <=nb1 ); 357GGML_ASSERT (nb1 <=nb2 ); 358GGML_ASSERT (nb2 <=nb3 ); 359 360GGML_ASSERT (ne03 == 1 ); 361GGML_ASSERT (ne13 == 1 ); 362GGML_ASSERT (ne3 == 1 ); 363 364// row groups 365const int n_ids = ids -> ne [0 ];// n_expert_used 366const int n_as = ne02 ;// n_experts 367 368 std::vector < int64_t > matrix_row_counts (n_as ,0 ); 369 std::vector < std::vector < mmid_row_mapping >> matrix_rows (n_as ); 370 371int64_t total_rows = 0 ; 372int n_active_experts = 0 ; 373// group rows by expert (preprocessing step) 374for (int64_t iid1 = 0 ;iid1 < ids -> ne [1 ];++ iid1 ) { 375for (int id = 0 ;id < n_ids ;++ id ) { 376const int32_t i02 = * (const int32_t * )((const char * )ids -> data + iid1 * ids -> nb [1 ]+ id * ids -> nb [0 ]); 377 378GGML_ASSERT (i02 >=0 && i02 < n_as ); 379 380if (matrix_row_counts [i02 ]== 0 ) { 381n_active_experts ++ ; 382 } 383matrix_rows [i02 ].push_back ({id ,iid1 }); 384matrix_row_counts [i02 ]++ ; 385total_rows ++ ; 386 } 387 } 388 389if (total_rows == 0 ) { 390return ;// no rows to process 391 } 392 393const size_t row_size = ggml_row_size (vec_dot_type ,ne10 ); 394 395// For Q8_0, src1 is always F32; the gather buffer must hold F32 rows (ne10*4 bytes), 396// not Q8_0-encoded rows (row_size ≈ ne10/32*34 bytes) — they differ by ~4x. 397const size_t f32_row_size = (size_t )ne10 * sizeof (float ); 398const size_t gather_row_size = (src0 -> type == GGML_TYPE_Q8_0 ) ?f32_row_size :row_size ; 399 400if (src1 -> type != vec_dot_type && src0 -> type != GGML_TYPE_Q8_0 ) { 401GGML_ASSERT (src1 -> type == GGML_TYPE_F32 ); 402 } 403 404// size for MoE gather/scatter buffers 405const size_t wdata_cur_size = total_rows * gather_row_size ; 406const size_t dst_cur_size = total_rows * ggml_row_size (dst -> type ,ne01 ); 407 408// allocate single buffer for all needs 409const size_t total_size = wdata_cur_size + dst_cur_size ; 410if (ctx -> work_size < total_size ) { 411ctx -> work_data .reset (new char [total_size ]); 412ctx -> work_size = total_size ; 413 } 414 415// partition the buffer 416char * wdata_cur = ctx -> work_data .get (); 417char * dst_cur = wdata_cur + wdata_cur_size ; 418 419// per-expert data collected during gather, handed to ggml_zendnn_group_gemm() as one batch 420 std::vector < int64_t > expert_row_count (n_active_experts ); 421 std::vector < const void *> batch_src (n_active_experts ); 422 std::vector < const void *> batch_wei (n_active_experts ); 423 std::vector < void *> batch_dst (n_active_experts ); 424 425// precompute per-expert buffer offsets and batch indices for the parallel loop below 426 std::vector < int64_t > expert_wdata_off (n_as ,0 ); 427 std::vector < int64_t > expert_dst_off (n_as ,0 ); 428 std::vector < int > expert_batch_idx (n_as ,-1 ); 429 { 430int64_t w_off = 0 ; 431int64_t d_off = 0 ; 432int batch_idx = 0 ; 433for (int64_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 434if (matrix_row_counts [cur_a ]== 0 ) { 435continue ; 436 } 437expert_wdata_off [cur_a ]= w_off ; 438expert_dst_off [cur_a ]= d_off ; 439expert_batch_idx [cur_a ]= batch_idx ; 440w_off += matrix_row_counts [cur_a ]* gather_row_size ; 441d_off += matrix_row_counts [cur_a ]* ggml_row_size (dst -> type ,ne01 ); 442batch_idx ++ ; 443 } 444 } 445 446// gather + inline-convert input rows into each expert's batch slot 447#pragma omp parallel for num_threads(ctx->n_threads) schedule(static) 448for (int64_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 449const int64_t cne1 = matrix_row_counts [cur_a ]; 450 451if (cne1 == 0 ) { 452continue ; 453 } 454 455const int64_t w_off = expert_wdata_off [cur_a ]; 456const int64_t d_off = expert_dst_off [cur_a ]; 457const int batch_idx = expert_batch_idx [cur_a ]; 458 459for (int64_t ir1 = 0 ;ir1 < cne1 ;++ ir1 ) { 460const mmid_row_mapping & row_mapping = matrix_rows [cur_a ][ir1 ]; 461const int64_t id = row_mapping .i1 ; 462const int64_t i11 = id %ne11 ; 463const int64_t i12 = row_mapping .i2 ; 464 465const char * src_row = (const char * )src1 -> data + i11 * nb11 + i12 * nb12 ; 466void * dst_row = wdata_cur + w_off + ir1 * gather_row_size ; 467 468if (src1 -> type != vec_dot_type && src0 -> type != GGML_TYPE_Q8_0 ) { 469from_float ((const float * )src_row ,dst_row ,ne10 ); 470 }else { 471// no conversion: src1 already matches vec_dot_type, or src0 is Q8_0, whose 472// ZenDNN dynamic quantization requires the row to stay in F32 473 std::memcpy (dst_row ,src_row ,gather_row_size ); 474 } 475 } 476 477expert_row_count [batch_idx ]= cne1 ; 478batch_src [batch_idx ]= wdata_cur + w_off ; 479batch_wei [batch_idx ]= (const char * )src0 -> data + cur_a * nb02 ; 480batch_dst [batch_idx ]= dst_cur + d_off ; 481 } 482 483if (!ggml_zendnn_group_gemm (ctx , 484ne01 ,// m 485ne10 ,// k 486expert_row_count ,// n (per expert) 487batch_wei ,ne00 ,// A: weights, lda 488batch_src ,ne10 ,// B: input, ldb 489batch_dst ,ne01 ,// C: output, ldc 490src0 -> type , 491src0 -> type == GGML_TYPE_Q8_0 ?GGML_TYPE_F32 :vec_dot_type , 492dst -> type )) 493GGML_ABORT ("%s: ZenDNN group gemm failed\n" ,__func__ ); 494 495// scatter output rows to destination 496#pragma omp parallel for num_threads(ctx->n_threads) schedule(static) 497for (int64_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 498const int64_t cne1 = matrix_row_counts [cur_a ]; 499 500if (cne1 == 0 ) { 501continue ; 502 } 503 504const int64_t d_off = expert_dst_off [cur_a ]; 505 506for (int64_t ir1 = 0 ;ir1 < cne1 ;++ ir1 ) { 507const mmid_row_mapping & row_mapping = matrix_rows [cur_a ][ir1 ]; 508const int64_t id = row_mapping .i1 ; 509const int64_t i1 = id ; 510const int64_t i2 = row_mapping .i2 ; 511 512 std::memcpy ( 513 (char * )dst -> data + i1 * nb1 + i2 * nb2 , 514dst_cur + d_off + ir1 * ggml_row_size (dst -> type ,ne01 ), 515ggml_row_size (dst -> type ,ne01 ) 516 ); 517 } 518 } 519} 520 521// backend interface 522 523static const char * ggml_backend_zendnn_get_name (ggml_backend_t backend ) { 524return "ZenDNN" ; 525 526GGML_UNUSED (backend ); 527} 528 529static void ggml_backend_zendnn_free (ggml_backend_t backend ) { 530ggml_backend_zendnn_context * ctx = (ggml_backend_zendnn_context * )backend -> context ; 531delete ctx ; 532delete backend ; 533} 534 535static ggml_status ggml_backend_zendnn_graph_compute (ggml_backend_t backend ,ggml_cgraph * cgraph ) { 536ggml_backend_zendnn_context * ctx = (ggml_backend_zendnn_context * )backend -> context ; 537 538for (int i = 0 ;i < cgraph -> n_nodes ;i ++ ) { 539struct ggml_tensor * node = cgraph -> nodes [i ]; 540 541if ((node -> flags & GGML_TENSOR_FLAG_COMPUTE )== 0 ) { 542continue ; 543 } 544 545switch (node -> op ) { 546case GGML_OP_MUL_MAT : 547ggml_zendnn_compute_forward_mul_mat (ctx ,node ); 548break ; 549case GGML_OP_MUL_MAT_ID : 550ggml_zendnn_compute_forward_mul_mat_id (ctx ,node ); 551break ; 552case GGML_OP_NONE : 553case GGML_OP_RESHAPE : 554case GGML_OP_VIEW : 555case GGML_OP_PERMUTE : 556case GGML_OP_TRANSPOSE : 557break ; 558 559default : 560GGML_ABORT ("%s: unsupported op %s\n" ,__func__ ,ggml_op_desc (node )); 561 } 562 } 563 564return GGML_STATUS_SUCCESS ; 565 566GGML_UNUSED (backend ); 567} 568 569static struct ggml_backend_i ggml_backend_zendnn_i = { 570/* .get_name = */ ggml_backend_zendnn_get_name , 571/* .free = */ ggml_backend_zendnn_free , 572/* .set_tensor_async = */ NULL , 573/* .get_tensor_async = */ NULL , 574/* .set_tensor_2d_async = */ NULL , 575/* .get_tensor_2d_async = */ NULL , 576/* .cpy_tensor_async = */ NULL , 577/* .synchronize = */ NULL , 578/* .graph_plan_create = */ NULL , 579/* .graph_plan_free = */ NULL , 580/* .graph_plan_update = */ NULL , 581/* .graph_plan_compute = */ NULL , 582/* .graph_compute = */ ggml_backend_zendnn_graph_compute , 583/* .event_record = */ NULL , 584/* .event_wait = */ NULL , 585/* .graph_optimize = */ NULL , 586}; 587 588static ggml_guid_t ggml_backend_zendnn_guid (void ) { 589static const char * guid_str = "AMD-ZENDNN-ACCEL" ; 590return reinterpret_cast < ggml_guid_t > (const_cast < char *> (guid_str )); 591} 592 593ggml_backend_t ggml_backend_zendnn_init (void ) { 594ggml_backend_zendnn_context * ctx = new ggml_backend_zendnn_context ; 595 596ggml_backend_t backend = new ggml_backend { 597/* .guid = */ ggml_backend_zendnn_guid (), 598/* .iface = */ ggml_backend_zendnn_i , 599/* .device = */ ggml_backend_reg_dev_get (ggml_backend_zendnn_reg (),0 ), 600/* .context = */ ctx , 601 }; 602 603return backend ; 604} 605 606bool ggml_backend_is_zendnn (ggml_backend_t backend ) { 607return backend != NULL && ggml_guid_matches (backend -> guid ,ggml_backend_zendnn_guid ()); 608} 609 610void ggml_backend_zendnn_set_n_threads (ggml_backend_t backend_zendnn ,int n_threads ) { 611GGML_ASSERT (ggml_backend_is_zendnn (backend_zendnn )); 612 613ggml_backend_zendnn_context * ctx = (ggml_backend_zendnn_context * )backend_zendnn -> context ; 614ctx -> n_threads = n_threads ; 615} 616 617// device interface 618static const char * ggml_backend_zendnn_device_get_name (ggml_backend_dev_t dev ) { 619return "ZenDNN" ; 620 621GGML_UNUSED (dev ); 622} 623/** 624* ZenDNN is AMD's performance library providing optimized primitives and implementations 625* for deep learning workloads on AMD CPUs. It targets improved performance for common 626* neural network operations on AMD architectures. For more information, see: 627* https://www.amd.com/en/developer/zendnn.html 628*/ 629static const char * ggml_backend_zendnn_device_get_description (ggml_backend_dev_t dev ) { 630return "ZenDNN: AMD optimized primitives backend for GGML (optimized for AMD CPUs)" ; 631 632GGML_UNUSED (dev ); 633} 634 635static void ggml_backend_zendnn_device_get_memory (ggml_backend_dev_t dev ,size_t * free ,size_t * total ) { 636* free = 0 ; 637* total = 0 ; 638 639GGML_UNUSED (dev ); 640} 641 642static enum ggml_backend_dev_type ggml_backend_zendnn_device_get_type (ggml_backend_dev_t dev ) { 643return GGML_BACKEND_DEVICE_TYPE_ACCEL ; 644 645GGML_UNUSED (dev ); 646} 647 648static void ggml_backend_zendnn_device_get_props (ggml_backend_dev_t dev ,struct ggml_backend_dev_props * props ) { 649props -> name = ggml_backend_zendnn_device_get_name (dev ); 650props -> description = ggml_backend_zendnn_device_get_description (dev ); 651props -> type = ggml_backend_zendnn_device_get_type (dev ); 652ggml_backend_zendnn_device_get_memory (dev ,& props -> memory_free ,& props -> memory_total ); 653props -> caps = { 654/* .async = */ false, 655/* .host_buffer = */ false, 656/* .buffer_from_host_ptr = */ true, 657/* .events = */ false, 658/* .mmap_support = */ true, 659 }; 660} 661 662static ggml_backend_t ggml_backend_zendnn_device_init_backend (ggml_backend_dev_t dev ,const char * params ) { 663ggml_backend_t backend = ggml_backend_zendnn_init (); 664if (backend == NULL ) { 665GGML_LOG_ERROR ("%s: error: failed to initialize ZenDNN backend\n" ,__func__ ); 666return NULL ; 667 } 668 669return backend ; 670 671GGML_UNUSED (dev ); 672GGML_UNUSED (params ); 673} 674 675static ggml_backend_buffer_type_t ggml_backend_zendnn_device_get_buffer_type (ggml_backend_dev_t dev ) { 676return ggml_backend_cpu_buffer_type (); 677 678GGML_UNUSED (dev ); 679} 680 681static ggml_backend_buffer_t ggml_backend_zendnn_device_buffer_from_host_ptr (ggml_backend_dev_t dev ,void * ptr ,size_t size ,size_t max_tensor_size ) { 682return ggml_backend_cpu_buffer_from_ptr (ptr ,size ); 683 684GGML_UNUSED (dev ); 685GGML_UNUSED (max_tensor_size ); 686} 687 688static bool ggml_zendnn_adaptive_fallback_enabled () { 689static const bool enabled = std::getenv ("GGML_ZENDNN_ADAPTIVE_FALLBACK" )== nullptr || 690 std::atoi (std::getenv ("GGML_ZENDNN_ADAPTIVE_FALLBACK" ))!= 0 ; 691return enabled ; 692} 693 694static bool ggml_backend_zendnn_device_supports_op (ggml_backend_dev_t dev ,const struct ggml_tensor * op ) { 695switch (op -> op ) { 696case GGML_OP_NONE : 697case GGML_OP_RESHAPE : 698case GGML_OP_VIEW : 699case GGML_OP_PERMUTE : 700case GGML_OP_TRANSPOSE : 701return true; 702 703case GGML_OP_MUL_MAT : 704case GGML_OP_MUL_MAT_ID : 705 { 706const ggml_tensor * weights = op -> src [0 ]; 707const ggml_tensor * inputs = op -> src [1 ]; 708 709const int64_t ne10 = inputs -> ne [0 ]; 710const int64_t ne0 = op -> ne [0 ]; 711const int64_t ne1 = op -> ne [1 ]; 712const int64_t min_batch = 1 ; 713 714if (!ggml_is_contiguous (weights )|| !ggml_is_contiguous (inputs )) { 715return false; 716 } 717 718if (ggml_zendnn_adaptive_fallback_enabled ()) { 719const int64_t K = inputs -> ne [0 ]; 720const int64_t N = (inputs -> ne [1 ]* inputs -> ne [2 ]* inputs -> ne [3 ]); 721const int64_t M = weights -> ne [1 ]; 722if (K <=256 || N <=128 || M <=96 ) { 723return false; 724 } 725 726// MUL_MAT_ID's gather+matmul+scatter approach favors a moderate expert count 727if (op -> op == GGML_OP_MUL_MAT_ID ) { 728const int64_t n_experts = weights -> ne [2 ]; 729const int64_t max_experts = 32 ; 730if (n_experts > max_experts ) { 731return false; 732 } 733 734// fall back once the average rows per expert (N / n_experts) is too thin 735// to amortize each per-expert GEMM's overhead 736if (N /n_experts <=32 ) { 737return false; 738 } 739 } 740 } 741else if (ne0 < min_batch || ne1 < min_batch || ne10 < min_batch ) { 742return false; 743 } 744 745switch (weights -> type ) { 746case GGML_TYPE_F32 : 747case GGML_TYPE_BF16 : 748case GGML_TYPE_Q8_0 : 749return true; 750default : 751return false; 752 } 753 }break ; 754 755default : 756return false; 757 } 758 759GGML_UNUSED (dev ); 760} 761 762static bool ggml_backend_zendnn_device_supports_buft (ggml_backend_dev_t dev ,ggml_backend_buffer_type_t buft ) { 763return ggml_backend_buft_is_host (buft ); 764 765GGML_UNUSED (dev ); 766} 767 768static const struct ggml_backend_device_i ggml_backend_zendnn_device_i = { 769/* .get_name = */ ggml_backend_zendnn_device_get_name , 770/* .get_description = */ ggml_backend_zendnn_device_get_description , 771/* .get_memory = */ ggml_backend_zendnn_device_get_memory , 772/* .get_type = */ ggml_backend_zendnn_device_get_type , 773/* .get_props = */ ggml_backend_zendnn_device_get_props , 774/* .init_backend = */ ggml_backend_zendnn_device_init_backend , 775/* .get_buffer_type = */ ggml_backend_zendnn_device_get_buffer_type , 776/* .get_host_buffer_type = */ NULL , 777/* .buffer_from_host_ptr = */ ggml_backend_zendnn_device_buffer_from_host_ptr , 778/* .supports_op = */ ggml_backend_zendnn_device_supports_op , 779/* .supports_buft = */ ggml_backend_zendnn_device_supports_buft , 780/* .offload_op = */ NULL , 781/* .event_new = */ NULL , 782/* .event_free = */ NULL , 783/* .event_synchronize = */ NULL , 784}; 785 786// backend reg interface 787static const char * ggml_backend_zendnn_reg_get_name (ggml_backend_reg_t reg ) { 788return "ZenDNN" ; 789 790GGML_UNUSED (reg ); 791} 792 793static size_t ggml_backend_zendnn_reg_get_device_count (ggml_backend_reg_t reg ) { 794return 1 ; 795 796GGML_UNUSED (reg ); 797} 798 799static ggml_backend_dev_t ggml_backend_zendnn_reg_get_device (ggml_backend_reg_t reg ,size_t index ) { 800GGML_ASSERT (index == 0 ); 801 802static ggml_backend_device ggml_backend_zendnn_device = { 803/* .iface = */ ggml_backend_zendnn_device_i , 804/* .reg = */ reg , 805/* .context = */ nullptr , 806 }; 807 808return & ggml_backend_zendnn_device ; 809} 810 811static void * ggml_backend_zendnn_get_proc_address (ggml_backend_reg_t reg ,const char * name ) { 812if (std::strcmp (name ,"ggml_backend_set_n_threads" )== 0 ) { 813return (void * )ggml_backend_zendnn_set_n_threads ; 814 } 815return NULL ; 816 817GGML_UNUSED (reg ); 818GGML_UNUSED (name ); 819} 820 821static const struct ggml_backend_reg_i ggml_backend_zendnn_reg_i = { 822/* .get_name = */ ggml_backend_zendnn_reg_get_name , 823/* .get_device_count = */ ggml_backend_zendnn_reg_get_device_count , 824/* .get_device = */ ggml_backend_zendnn_reg_get_device , 825/* .get_proc_address = */ ggml_backend_zendnn_get_proc_address , 826}; 827 828ggml_backend_reg_t ggml_backend_zendnn_reg (void ) { 829static struct ggml_backend_reg ggml_backend_zendnn_reg = { 830/* .api_version = */ GGML_BACKEND_API_VERSION , 831/* .iface = */ ggml_backend_zendnn_reg_i , 832/* .context = */ NULL , 833 }; 834 835return & ggml_backend_zendnn_reg ; 836} 837 838GGML_BACKEND_DL_IMPL (ggml_backend_zendnn_reg )