char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
21f6b0d22
char/aliceai-t5-moe
1#include "topk-radix.hpp" 2 3#include "common.hpp" 4 5#include <algorithm> 6 7// Large-k top-k by radix select on an order-preserving unsigned key. 8// 9// The k-th largest key of a row is found by four most-significant-first passes over its 10// 8-bit digits: histogram the digit over the candidate set, walk the buckets from the 11// top, and recurse into the bucket where the running count reaches what is still 12// needed. Everything strictly above that bucket is in the top-k. A final pass emits 13// every column whose key beats the pivot, then exactly as many pivot-equal columns as 14// are still missing, so duplicate keys yield exactly k distinct indices. 15// 16// SLM holds only the histogram, so unlike the scan-merge kernels the cost does not grow 17// with k. One work-group owns a row and runs every pass, so a top-k is one launch and 18// needs no pool scratch. The row is re-read once per pass rather than compacted, which 19// keeps the candidate set implicit: (key & mask) == prefix. 20// 21// The output is the set of winning indices in no particular order, which is what the 22// reference op provides (it swaps its first two outputs to say so) and what 23// test-backend-ops compares. 24 25static constexpr int SYCL_TOP_K_RADIX_BITS = 8 ; 26static constexpr int SYCL_TOP_K_RADIX_BUCKETS = 1 <<SYCL_TOP_K_RADIX_BITS ; 27// Private histogram copies, interleaved per bucket so neighbouring lanes hit 28// neighbouring banks. Lanes of one instruction spread over the copies, which is what 29// bounds the atomic serialisation on tie-heavy rows. 30static constexpr int SYCL_TOP_K_RADIX_HIST_COPIES = 8 ; 31static constexpr int SYCL_TOP_K_RADIX_HIST_SIZE = SYCL_TOP_K_RADIX_BUCKETS * SYCL_TOP_K_RADIX_HIST_COPIES ; 32// Past the histogram: pivot digit, pivot bucket count, remaining need, then the two 33// emit counters. 34static constexpr int SYCL_TOP_K_RADIX_SLM_WORDS = SYCL_TOP_K_RADIX_HIST_SIZE + 5 ; 35 36// Larger float <=> larger key. The reference comparator is a plain float '>', under which 37// -0.0 and +0.0 tie, so -0.0 is folded onto +0.0 first. NaN has no defined order in the 38// reference (its comparator is not a strict weak order on NaN); here a positive NaN keys 39// above +inf and a negative NaN below -inf, which at least makes the result deterministic. 40static inline uint32_t top_k_radix_key (float f ) { 41uint32_t u = sycl::bit_cast < uint32_t > (f ); 42if (u == 0x80000000u ) { 43u = 0u ; 44 } 45return (u & 0x80000000u ) ? ~u : (u |0x80000000u ); 46} 47 48static void top_k_radix_select_f32 ( 49const float * src , 50int32_t * dst_idx , 51const int ncols , 52const int k , 53uint32_t * slm , 54const sycl::nd_item < 1 > & item_ct1 55) { 56using local_atomic = sycl::atomic_ref < uint32_t , sycl::memory_order::relaxed , 57 sycl::memory_scope::work_group , 58 sycl::access::address_space::local_space > ; 59 60const int tid = item_ct1 .get_local_id (0 ); 61const int block_size = item_ct1 .get_local_range (0 ); 62 63uint32_t * hist = slm ; 64uint32_t * s_digit = slm + SYCL_TOP_K_RADIX_HIST_SIZE ; 65uint32_t * s_bucket = slm + SYCL_TOP_K_RADIX_HIST_SIZE + 1 ; 66uint32_t * s_need = slm + SYCL_TOP_K_RADIX_HIST_SIZE + 2 ; 67uint32_t * s_cnt_gt = slm + SYCL_TOP_K_RADIX_HIST_SIZE + 3 ; 68uint32_t * s_cnt_eq = slm + SYCL_TOP_K_RADIX_HIST_SIZE + 4 ; 69 70if (tid == 0 ) { 71* s_cnt_gt = 0 ; 72* s_cnt_eq = 0 ; 73 } 74 75const int copy = tid & (SYCL_TOP_K_RADIX_HIST_COPIES - 1 ); 76 77uint32_t prefix = 0 ;// digits fixed so far, in place 78uint32_t mask = 0 ;// which bits of prefix are fixed 79uint32_t need = (uint32_t )k ; 80 81for (int shift = 32 - SYCL_TOP_K_RADIX_BITS ;shift >=0 ;shift -= SYCL_TOP_K_RADIX_BITS ) { 82for (int i = tid ;i < SYCL_TOP_K_RADIX_HIST_SIZE ;i += block_size ) { 83hist [i ]= 0 ; 84 } 85item_ct1 .barrier (sycl::access::fence_space::local_space ); 86 87for (int col = tid ;col < ncols ;col += block_size ) { 88const uint32_t key = top_k_radix_key (src [col ]); 89if ((key & mask )== prefix ) { 90const uint32_t bucket = (key >>shift )& (SYCL_TOP_K_RADIX_BUCKETS - 1 ); 91local_atomic (hist [bucket * SYCL_TOP_K_RADIX_HIST_COPIES + copy ]).fetch_add (1u ); 92 } 93 } 94item_ct1 .barrier (sycl::access::fence_space::local_space ); 95 96// Lane t takes bucket 255 - t, so an inclusive scan over lanes counts from the top 97// bucket downward. The pivot is the unique bucket whose cumulative count first 98// reaches need; the previous cumulative count is what the higher buckets contribute. 99uint32_t cnt = 0 ; 100if (tid < SYCL_TOP_K_RADIX_BUCKETS ) { 101const uint32_t * h = hist + (SYCL_TOP_K_RADIX_BUCKETS - 1 - tid )* SYCL_TOP_K_RADIX_HIST_COPIES ; 102for (int c = 0 ;c < SYCL_TOP_K_RADIX_HIST_COPIES ;c ++ ) { 103cnt += h [c ]; 104 } 105 } 106const uint32_t incl = sycl::inclusive_scan_over_group (item_ct1 .get_group (),cnt , sycl::plus < uint32_t > ()); 107 108if (tid < SYCL_TOP_K_RADIX_BUCKETS && incl >=need && incl - cnt < need ) { 109* s_digit = (uint32_t ) (SYCL_TOP_K_RADIX_BUCKETS - 1 - tid ); 110* s_bucket = cnt ; 111* s_need = need - (incl - cnt ); 112 } 113item_ct1 .barrier (sycl::access::fence_space::local_space ); 114 115const uint32_t digit = * s_digit ; 116const uint32_t bucket_cnt = * s_bucket ; 117need = * s_need ; 118prefix |=digit <<shift ; 119mask |= (uint32_t ) (SYCL_TOP_K_RADIX_BUCKETS - 1 ) <<shift ; 120 121// Every candidate in the pivot bucket is wanted: the remaining digits cannot 122// change the answer, and the masked emit below is exact as it stands. 123if (bucket_cnt == need ) { 124break ; 125 } 126// The next pass rewrites hist and s_*; the reads above must land first. 127item_ct1 .barrier (sycl::access::fence_space::local_space ); 128 } 129 130item_ct1 .barrier (sycl::access::fence_space::local_space ); 131 132// Exactly k - need columns have (key & mask) > prefix; the first need of the pivot-equal 133// columns fill the tail. Both counters live in SLM since the whole row is this group. 134const uint32_t base_eq = (uint32_t )k - need ; 135 136for (int col = tid ;col < ncols ;col += block_size ) { 137const uint32_t kp = top_k_radix_key (src [col ])& mask ; 138if (kp > prefix ) { 139const uint32_t pos = local_atomic (* s_cnt_gt ).fetch_add (1u ); 140dst_idx [pos ]= col ; 141 }else if (kp == prefix ) { 142const uint32_t pos = local_atomic (* s_cnt_eq ).fetch_add (1u ); 143if (pos < need ) { 144dst_idx [base_eq + pos ]= col ; 145 } 146 } 147 } 148} 149 150static void top_k_radix_f32_sycl ( 151ggml_backend_sycl_context & ctx , 152const float * src , 153int32_t * dst_indices , 154const int64_t ncols , 155const int64_t nrows , 156const int k , 157 dpct::queue_ptr main_stream 158) { 159GGML_ASSERT (ncols <=INT32_MAX ); 160 161// One group per row; every pass is a strided sweep of the row, so lanes in flight is the 162// only lever, and the device's own limit is the answer -- there is nothing here that 163// wants a smaller group. Must still cover the 256 buckets for the scan step. 164const int block_size = ggml_sycl_info ().max_work_group_sizes [ctx .device ]; 165GGML_ASSERT (block_size >=SYCL_TOP_K_RADIX_BUCKETS ); 166 167const sycl::range < 1 > block_dims (block_size ); 168const sycl::range < 1 > grid_dims (nrows ); 169 170main_stream -> submit ([& ](sycl::handler & cgh ) { 171 sycl::local_accessor < uint32_t ,1 > slm (sycl::range < 1 > (SYCL_TOP_K_RADIX_SLM_WORDS ),cgh ); 172 173cgh .parallel_for ( 174 sycl::nd_range < 1 > (grid_dims * block_dims ,block_dims ), 175 [= ](sycl::nd_item < 1 > item_ct1 ) { 176const int row = item_ct1 .get_group (0 ); 177 178top_k_radix_select_f32 ( 179src + (int64_t )row * ncols ,dst_indices + (int64_t )row * k , 180 (int )ncols ,k , 181slm .get_multi_ptr < sycl::access::decorated::no > ().get (), 182item_ct1 ); 183 }); 184 }); 185} 186 187// One work-group owns a whole row above, which leaves the device idle whenever a graph 188// has fewer rows than it has cores -- the common case at batch size 1, where the 189// sparse-attention indexer and the backend sampler both top-k a single row. The kernels 190// below spread one row over several groups instead. 191// 192// A digit pass now needs the whole row's histogram before any group can pick the pivot, 193// so the per-pass state moves to global memory and the passes become separate launches: 194// a work-group barrier no longer spans the row. Each group still accumulates into SLM 195// and contributes 256 global atomics at the end, so global traffic is per-group, not 196// per-element. The last group to finish a pass (the one whose fetch_add returns G - 1) 197// does the scan for the row and clears the histogram for the next pass, which keeps the 198// launch count at one per digit rather than two. 199// 200// Running all four digits unconditionally costs nothing in correctness: once a bucket 201// holds exactly the elements still needed, later digits only extend the prefix, and the 202// count of columns above that longer prefix grows by exactly as much as `need` shrinks. 203// The emit below therefore stays exact whatever pass the answer settled on. 204 205static constexpr int SYCL_TOP_K_RADIX_ROW_DONE = SYCL_TOP_K_RADIX_BUCKETS + 0 ; 206static constexpr int SYCL_TOP_K_RADIX_ROW_PREFIX = SYCL_TOP_K_RADIX_BUCKETS + 1 ; 207static constexpr int SYCL_TOP_K_RADIX_ROW_MASK = SYCL_TOP_K_RADIX_BUCKETS + 2 ; 208static constexpr int SYCL_TOP_K_RADIX_ROW_NEED = SYCL_TOP_K_RADIX_BUCKETS + 3 ; 209static constexpr int SYCL_TOP_K_RADIX_ROW_CNT_GT = SYCL_TOP_K_RADIX_BUCKETS + 4 ; 210static constexpr int SYCL_TOP_K_RADIX_ROW_CNT_EQ = SYCL_TOP_K_RADIX_BUCKETS + 5 ; 211static constexpr int SYCL_TOP_K_RADIX_ROW_WORDS = SYCL_TOP_K_RADIX_BUCKETS + 6 ; 212 213// How wide the split goes is a property of the device, not of the model: enough groups to 214// cover the cores, and no more. Past that the extra groups add histogram traffic without 215// adding bandwidth (measured on this device: 20 and 40 groups tie, 60 and 160 lose). 216// 217// nsm is max_compute_units / 16, i.e. it counts an Xe core as 16 EUs. That is a core's 218// width on Xe-HPG, but an Xe2 core is 8 XVEs wide, so on Battlemage the field reads half 219// the cores actually present (10 for a 20-core B60). The measured curve is flat from one 220// group per core to two and only falls off at three, so a factor of two covers the device 221// on Xe2 and lands in the flat region on Xe-HPG. It is the one number here that a correct 222// core count would remove; it was tuned on Xe2 and has not been measured on Xe-HPG. 223static constexpr int SYCL_TOP_K_RADIX_GROUPS_PER_NSM = 2 ; 224// Splitting trades one kernel for five. Below the width at which the single-group kernel 225// runs longer than those four extra launches, it wins on its own; measured break-even on 226// this device sits just under 64K columns. 227static constexpr int SYCL_TOP_K_RADIX_MIN_SPLIT_COLS = 65536 ; 228// A partition thinner than this cannot keep a group's sweep busy. 229static constexpr int SYCL_TOP_K_RADIX_MIN_PART_COLS = 4096 ; 230 231static int top_k_radix_split_groups (const int device ,const int64_t ncols ,const int64_t nrows ) { 232const int64_t target = (int64_t )SYCL_TOP_K_RADIX_GROUPS_PER_NSM * ggml_sycl_info ().devices [device ].nsm ; 233 234// One group per row already, so a graph with rows enough to cover the device gains 235// nothing from splitting and would only pay the extra launches. 236if (ncols < SYCL_TOP_K_RADIX_MIN_SPLIT_COLS || nrows >=target ) { 237return 1 ; 238 } 239 240const int64_t by_rows = target /nrows ;// floor: never overshoot a row that is nearly covered 241const int64_t by_cols = ncols /SYCL_TOP_K_RADIX_MIN_PART_COLS ; 242 243return (int ) std::max < int64_t > (1 , std::min (by_rows ,by_cols )); 244} 245 246using top_k_radix_gatomic = sycl::atomic_ref < uint32_t , sycl::memory_order::relaxed , 247 sycl::memory_scope::device , 248 sycl::access::address_space::global_space > ; 249 250static void top_k_radix_split_pass_f32 ( 251const float * src , 252uint32_t * state , 253const int ncols , 254const int k , 255const int shift , 256const bool first , 257const int part , 258const int nparts , 259uint32_t * slm , 260const sycl::nd_item < 1 > & item_ct1 261) { 262using local_atomic = sycl::atomic_ref < uint32_t , sycl::memory_order::relaxed , 263 sycl::memory_scope::work_group , 264 sycl::access::address_space::local_space > ; 265 266const int tid = item_ct1 .get_local_id (0 ); 267const int block_size = item_ct1 .get_local_range (0 ); 268 269uint32_t * hist = slm ; 270uint32_t * s_last = slm + SYCL_TOP_K_RADIX_HIST_SIZE ; 271uint32_t * s_row = slm + SYCL_TOP_K_RADIX_HIST_SIZE + 1 ;// prefix, mask, need 272 273// The previous launch is the barrier that publishes these, so a plain load is enough. 274// One lane reads them and the group takes them from SLM: a device-scope atomic load 275// is uncached here, and having every work-item issue three of them off the same 276// address costs more than the whole sweep below. 277if (tid == 0 ) { 278s_row [0 ]= first ?0u :state [SYCL_TOP_K_RADIX_ROW_PREFIX ]; 279s_row [1 ]= first ?0u :state [SYCL_TOP_K_RADIX_ROW_MASK ]; 280s_row [2 ]= first ? (uint32_t )k :state [SYCL_TOP_K_RADIX_ROW_NEED ]; 281 } 282 283for (int i = tid ;i < SYCL_TOP_K_RADIX_HIST_SIZE ;i += block_size ) { 284hist [i ]= 0 ; 285 } 286item_ct1 .barrier (sycl::access::fence_space::local_space ); 287 288const uint32_t prefix = s_row [0 ]; 289const uint32_t mask = s_row [1 ]; 290const uint32_t need = s_row [2 ]; 291 292const int copy = tid & (SYCL_TOP_K_RADIX_HIST_COPIES - 1 ); 293const int chunk = (ncols + nparts - 1 ) /nparts ; 294const int col0 = part * chunk ; 295const int col1 = std::min (ncols ,col0 + chunk ); 296 297for (int col = col0 + tid ;col < col1 ;col += block_size ) { 298const uint32_t key = top_k_radix_key (src [col ]); 299if ((key & mask )== prefix ) { 300const uint32_t bucket = (key >>shift )& (SYCL_TOP_K_RADIX_BUCKETS - 1 ); 301local_atomic (hist [bucket * SYCL_TOP_K_RADIX_HIST_COPIES + copy ]).fetch_add (1u ); 302 } 303 } 304item_ct1 .barrier (sycl::access::fence_space::local_space ); 305 306// One global atomic per bucket per group, not per element. 307for (int b = tid ;b < SYCL_TOP_K_RADIX_BUCKETS ;b += block_size ) { 308uint32_t sum = 0 ; 309for (int c = 0 ;c < SYCL_TOP_K_RADIX_HIST_COPIES ;c ++ ) { 310sum += hist [b * SYCL_TOP_K_RADIX_HIST_COPIES + c ]; 311 } 312if (sum ) { 313top_k_radix_gatomic (state [b ]).fetch_add (sum ); 314 } 315 } 316 317// Publish this group's bins, then claim the scan if this group is the row's last. 318// The group-wide barrier flushes the atomics above; only the claiming lane needs the 319// release, so the device-scope fence is paid once per group rather than per work-item. 320item_ct1 .barrier (sycl::access::fence_space::global_and_local ); 321if (tid == 0 ) { 322 sycl::atomic_fence (sycl::memory_order::release , sycl::memory_scope::device ); 323 sycl::atomic_ref < uint32_t , sycl::memory_order::acq_rel , sycl::memory_scope::device , 324 sycl::access::address_space::global_space > done (state [SYCL_TOP_K_RADIX_ROW_DONE ]); 325* s_last = (done .fetch_add (1u )== (uint32_t ) (nparts - 1 )) ?1u :0u ; 326 } 327item_ct1 .barrier (sycl::access::fence_space::local_space ); 328 329if (* s_last == 0u ) { 330return ; 331 } 332 sycl::atomic_fence (sycl::memory_order::acquire , sycl::memory_scope::device ); 333 334// Lane t takes bucket 255 - t, so an inclusive scan counts down from the top bucket. 335uint32_t cnt = 0 ; 336if (tid < SYCL_TOP_K_RADIX_BUCKETS ) { 337cnt = top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_BUCKETS - 1 - tid ]).load (); 338 } 339const uint32_t incl = sycl::inclusive_scan_over_group (item_ct1 .get_group (),cnt , sycl::plus < uint32_t > ()); 340 341if (tid < SYCL_TOP_K_RADIX_BUCKETS && incl >=need && incl - cnt < need ) { 342const uint32_t digit = (uint32_t ) (SYCL_TOP_K_RADIX_BUCKETS - 1 - tid ); 343top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_PREFIX ]).store (prefix | (digit <<shift )); 344top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_MASK ]).store ( 345mask | ((uint32_t ) (SYCL_TOP_K_RADIX_BUCKETS - 1 ) <<shift )); 346top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_NEED ]).store (need - (incl - cnt )); 347 } 348item_ct1 .barrier (sycl::access::fence_space::local_space ); 349 350// Clear for the next pass; the next launch is the barrier that orders this. 351for (int b = tid ;b < SYCL_TOP_K_RADIX_BUCKETS ;b += block_size ) { 352top_k_radix_gatomic (state [b ]).store (0u ); 353 } 354if (tid == 0 ) { 355top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_DONE ]).store (0u ); 356 } 357} 358 359static void top_k_radix_split_emit_f32 ( 360const float * src , 361int32_t * dst_idx , 362uint32_t * state , 363const int ncols , 364const int k , 365const int part , 366const int nparts , 367uint32_t * slm , 368const sycl::nd_item < 1 > & item_ct1 369) { 370using local_atomic = sycl::atomic_ref < uint32_t , sycl::memory_order::relaxed , 371 sycl::memory_scope::work_group , 372 sycl::access::address_space::local_space > ; 373 374const int tid = item_ct1 .get_local_id (0 ); 375const int block_size = item_ct1 .get_local_range (0 ); 376 377uint32_t * s_gt = slm ; 378uint32_t * s_eq = slm + 1 ; 379uint32_t * s_base_gt = slm + 2 ; 380uint32_t * s_base_eq = slm + 3 ; 381 382uint32_t * s_row = slm + 4 ;// prefix, mask, need 383 384if (tid == 0 ) { 385* s_gt = 0 ; 386* s_eq = 0 ; 387s_row [0 ]= state [SYCL_TOP_K_RADIX_ROW_PREFIX ]; 388s_row [1 ]= state [SYCL_TOP_K_RADIX_ROW_MASK ]; 389s_row [2 ]= state [SYCL_TOP_K_RADIX_ROW_NEED ]; 390 } 391item_ct1 .barrier (sycl::access::fence_space::local_space ); 392 393const uint32_t prefix = s_row [0 ]; 394const uint32_t mask = s_row [1 ]; 395const uint32_t need = s_row [2 ]; 396 397// Exactly k - need columns beat the pivot; the first need pivot-equal ones fill the tail. 398const uint32_t base_eq = (uint32_t )k - need ; 399 400const int chunk = (ncols + nparts - 1 ) /nparts ; 401const int col0 = part * chunk ; 402const int col1 = std::min (ncols ,col0 + chunk ); 403 404// Counting first and reserving one range per group keeps the row's two counters out of 405// the inner loop: a per-element global atomic on a single address serialises the whole 406// emit, and at k in the thousands that alone outweighs every read the kernel does. 407for (int col = col0 + tid ;col < col1 ;col += block_size ) { 408const uint32_t kp = top_k_radix_key (src [col ])& mask ; 409if (kp > prefix ) { 410local_atomic (* s_gt ).fetch_add (1u ); 411 }else if (kp == prefix ) { 412local_atomic (* s_eq ).fetch_add (1u ); 413 } 414 } 415item_ct1 .barrier (sycl::access::fence_space::local_space ); 416 417if (tid == 0 ) { 418const uint32_t n_gt = * s_gt ; 419const uint32_t n_eq = * s_eq ; 420* s_base_gt = n_gt ?top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_CNT_GT ]).fetch_add (n_gt ) :0u ; 421* s_base_eq = n_eq ?top_k_radix_gatomic (state [SYCL_TOP_K_RADIX_ROW_CNT_EQ ]).fetch_add (n_eq ) :0u ; 422* s_gt = 0 ; 423* s_eq = 0 ; 424 } 425item_ct1 .barrier (sycl::access::fence_space::local_space ); 426 427const uint32_t base_gt_g = * s_base_gt ; 428const uint32_t base_eq_g = * s_base_eq ; 429 430for (int col = col0 + tid ;col < col1 ;col += block_size ) { 431const uint32_t kp = top_k_radix_key (src [col ])& mask ; 432if (kp > prefix ) { 433dst_idx [base_gt_g + local_atomic (* s_gt ).fetch_add (1u )]= col ; 434 }else if (kp == prefix ) { 435const uint32_t pos = base_eq_g + local_atomic (* s_eq ).fetch_add (1u ); 436if (pos < need ) { 437dst_idx [base_eq + pos ]= col ; 438 } 439 } 440 } 441} 442 443static void top_k_radix_split_f32_sycl ( 444ggml_backend_sycl_context & ctx , 445const float * src , 446int32_t * dst_indices , 447const int64_t ncols , 448const int64_t nrows , 449const int k , 450const int nparts , 451 dpct::queue_ptr main_stream 452) { 453GGML_ASSERT (ncols <=INT32_MAX ); 454GGML_ASSERT (nparts > 1 ); 455 456const int block_size = ggml_sycl_info ().max_work_group_sizes [ctx .device ]; 457GGML_ASSERT (block_size >=SYCL_TOP_K_RADIX_BUCKETS ); 458 459const size_t state_words = (size_t )nrows * SYCL_TOP_K_RADIX_ROW_WORDS ; 460ggml_sycl_pool_alloc < uint32_t > state_alloc (ctx .pool (),state_words ); 461uint32_t * state = state_alloc .get (); 462 463// Zero histogram, done counter and both emit counters. prefix/mask/need are seeded by 464// the first pass, which ignores the stored values. 465// The queue is in-order, so the passes below are already ordered after this fill. 466SYCL_CHECK (CHECK_TRY_ERROR (main_stream -> memset (state ,0 ,state_words * sizeof (uint32_t )))); 467 468const sycl::range < 1 > block_dims (block_size ); 469const sycl::range < 1 > grid_dims (nrows * nparts ); 470 471bool first = true; 472for (int shift = 32 - SYCL_TOP_K_RADIX_BITS ;shift >=0 ;shift -= SYCL_TOP_K_RADIX_BITS ) { 473const bool is_first = first ; 474first = false; 475main_stream -> submit ([& ](sycl::handler & cgh ) { 476 sycl::local_accessor < uint32_t ,1 > slm (sycl::range < 1 > (SYCL_TOP_K_RADIX_HIST_SIZE + 4 ),cgh ); 477 478cgh .parallel_for ( 479 sycl::nd_range < 1 > (grid_dims * block_dims ,block_dims ), 480 [= ](sycl::nd_item < 1 > item_ct1 ) { 481const int g = item_ct1 .get_group (0 ); 482const int row = g /nparts ; 483const int part = g %nparts ; 484 485top_k_radix_split_pass_f32 ( 486src + (int64_t )row * ncols , 487state + (int64_t )row * SYCL_TOP_K_RADIX_ROW_WORDS , 488 (int )ncols ,k ,shift ,is_first ,part ,nparts , 489slm .get_multi_ptr < sycl::access::decorated::no > ().get (), 490item_ct1 ); 491 }); 492 }); 493 } 494 495main_stream -> submit ([& ](sycl::handler & cgh ) { 496 sycl::local_accessor < uint32_t ,1 > slm (sycl::range < 1 > (8 ),cgh ); 497 498cgh .parallel_for ( 499 sycl::nd_range < 1 > (grid_dims * block_dims ,block_dims ), 500 [= ](sycl::nd_item < 1 > item_ct1 ) { 501const int g = item_ct1 .get_group (0 ); 502const int row = g /nparts ; 503const int part = g %nparts ; 504 505top_k_radix_split_emit_f32 ( 506src + (int64_t )row * ncols , 507dst_indices + (int64_t )row * k , 508state + (int64_t )row * SYCL_TOP_K_RADIX_ROW_WORDS , 509 (int )ncols ,k ,part ,nparts , 510slm .get_multi_ptr < sycl::access::decorated::no > ().get (), 511item_ct1 ); 512 }); 513 }); 514} 515 516void ggml_sycl_top_k_radix ( 517ggml_backend_sycl_context & ctx , 518const float * src , 519int32_t * dst_indices , 520const int64_t ncols , 521const int64_t nrows , 522const int k , 523 dpct::queue_ptr main_stream 524) { 525const int nparts = top_k_radix_split_groups (ctx .device ,ncols ,nrows ); 526if (nparts > 1 ) { 527top_k_radix_split_f32_sycl (ctx ,src ,dst_indices ,ncols ,nrows ,k ,nparts ,main_stream ); 528 }else { 529top_k_radix_f32_sycl (ctx ,src ,dst_indices ,ncols ,nrows ,k ,main_stream ); 530 } 531}