char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Łukasz Ślusarczyksycl : fix oneDNN scratchpad breaking the pool free order (#28704)661643e43

char/aliceai-t5-moe
32.9 KiB1014 linesraw
1//
2// MIT license
3// Copyright (C) 2024 Intel Corporation
4// SPDX-License-Identifier: MIT
5//
6
7//
8// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
9// See https://llvm.org/LICENSE.txt for license information.
10// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
11//
12
13#ifndef GGML_SYCL_COMMON_HPP
14#define GGML_SYCL_COMMON_HPP
15
16#include <cstddef>
17#include <fstream>
18#include <iostream>
19#include <string>
20
21#include "base.hpp"
22#include "dpct/helper.hpp"
23#include "ggml.h"
24#include "ggml-impl.h"
25#include "ggml-sycl.h"
26#include "presets.hpp"
27#include "type.hpp"
28#include "sycl_hw.hpp"
29#include "fattn-buffers.hpp"
30#include "memtrace.hpp"
31
32namespace syclexp = sycl::ext::oneapi::experimental;
33
34#if defined(__INTEL_LLVM_COMPILER) && __has_include(<sycl/ext/oneapi/bfloat16.hpp>)
35    #include <sycl/ext/oneapi/bfloat16.hpp>
36    #ifndef GGML_SYCL_HAS_BF16
37        #define GGML_SYCL_HAS_BF16
38    #endif
39#endif
40
41#if GGML_SYCL_DNNL
42#include "dnnl.hpp"
43#include "dnnl_sycl.hpp"
44#endif
45
46#define GGML_COMMON_DECL_SYCL
47#define GGML_COMMON_IMPL_SYCL
48#define SYCL_FLASH_ATTN //remove it to disable FLASH_ATTENTION in building.
49#define SYCL_FAST_FP16  //don't change. remove it will break fattn-tile.hpp building
50#define GGML_SYCL_FA_ALL_QUANTS //define it to enable all quantization types in flash attention. undefine it to only support F16, Q4_0 and Q8_0 in flash attention.
51
52/* suppress warning spam */
53#pragma clang diagnostic push
54#pragma clang diagnostic ignored "-Wnested-anon-types"
55#include "ggml-common.h"
56#pragma clang diagnostic pop
57#include "ggml-impl.h"
58
59void* ggml_sycl_host_malloc(size_t size);
60void ggml_sycl_host_free(void* ptr);
61
62
63extern int g_ggml_sycl_debug;
64extern int g_ggml_sycl_enable_optimize;
65extern int g_ggml_sycl_enable_fusion;
66extern int g_ggml_sycl_enable_esimd;
67extern int g_ggml_sycl_prioritize_dmmv;
68extern int g_ggml_sycl_enable_flash_attention;
69extern int g_ggml_sycl_dev2dev_memcpy;
70extern int g_ggml_sycl_fa_onednn;
71extern int g_ggml_sycl_fa_onednn_max_kv;
72extern int g_ggml_sycl_enable_mkl_fa;
73extern int g_ggml_sycl_memtrace;
74extern int g_ggml_sycl_memtrace_step;
75
76
77#define CHECK_TRY_ERROR(expr)                                            \
78  [&]() {                                                                \
79    try {                                                                \
80      expr;                                                              \
81      return dpct::success;                                              \
82    } catch (std::exception const& e) {                                  \
83      std::cerr << e.what() << "\nException caught at file:" << __FILE__ \
84                << ", line:" << __LINE__ << ", func:" << __func__        \
85                << std::endl;                                            \
86      return dpct::default_error;                                        \
87    }                                                                    \
88  }()
89
90
91#define __SYCL_ARCH__ DPCT_COMPATIBILITY_TEMP
92#define VER_4VEC 610 // todo for hardware optimize.
93#define VER_GEN9 700 // todo for hardware optimize.
94#define VER_GEN12 1000000 // todo for hardware optimize.
95#define VER_GEN13 (VER_GEN12 + 1030) // todo for hardware optimize.
96
97#define GGML_SYCL_MAX_NODES 8192 // TODO: adapt to hardwares
98
99// define for XMX in Intel GPU
100// TODO: currently, it's not used for XMX really.
101#if !defined(GGML_SYCL_FORCE_MMQ)
102    #define SYCL_USE_XMX
103#endif
104
105// max batch size to use MMQ kernels when tensor cores are available
106#define MMQ_MAX_BATCH_SIZE 32
107
108// dmmv = dequantize_mul_mat_vec
109#ifndef GGML_SYCL_DMMV_X
110#define GGML_SYCL_DMMV_X 32
111#endif
112#ifndef GGML_SYCL_MMV_Y
113#define GGML_SYCL_MMV_Y 1
114#endif
115
116typedef sycl::queue *queue_ptr;
117
118enum ggml_sycl_backend_gpu_mode {
119  SYCL_UNSET_GPU_MODE = -1,
120  SYCL_SINGLE_GPU_MODE = 0,
121  SYCL_MUL_GPU_MODE
122};
123
124enum ggml_sycl_dev2dev_memcpy_mode {
125  DEV2DEV_MEMCPY_SYCL = 0,
126  DEV2DEV_MEMCPY_L0 = 1,
127  DEV2DEV_MEMCPY_FORWARD = 2
128};
129
130static_assert(sizeof(sycl::half) == sizeof(ggml_fp16_t), "wrong fp16 size");
131
132static void crash() {
133  int* ptr = NULL;
134  *ptr = 0;
135}
136
137[[noreturn]] static void ggml_sycl_error(
138    const char* stmt,
139    const char* func,
140    const char* file,
141    const int line,
142    const char* msg) {
143  fprintf(stderr, "SYCL error: %s: %s\n", stmt, msg);
144  fprintf(stderr, "  in function %s at %s:%d\n", func, file, line);
145  GGML_ABORT("SYCL error");
146}
147
148#define SYCL_CHECK(err)                                                                                    \
149    do {                                                                                                   \
150        auto err_ = (err);                                                                                 \
151        if (err_ != 0)                                                                                     \
152            ggml_sycl_error(#err, __func__, __FILE__, __LINE__, "Exception caught in this line of code."); \
153    } while (0)
154
155#if DPCT_COMPAT_RT_VERSION >= 11100
156#define GGML_SYCL_ASSUME(x) __builtin_assume(x)
157#else
158#define GGML_SYCL_ASSUME(x)
159#endif // DPCT_COMPAT_RT_VERSION >= 11100
160
161#ifdef GGML_SYCL_F16
162typedef sycl::half dfloat; // dequantize float
163typedef sycl::half2 dfloat2;
164#else
165typedef float dfloat; // dequantize float
166typedef sycl::float2 dfloat2;
167#endif // GGML_SYCL_F16
168
169#define MMVQ_MAX_BATCH_SIZE  8
170
171static int g_all_sycl_device_count = -1;
172static bool g_ggml_backend_sycl_buffer_type_initialized = false;
173
174static ggml_sycl_backend_gpu_mode g_ggml_sycl_backend_gpu_mode =
175    SYCL_UNSET_GPU_MODE;
176
177static void* g_scratch_buffer = nullptr;
178static size_t g_scratch_size = 0; // disabled by default
179static size_t g_scratch_offset = 0;
180
181[[noreturn]] static inline void bad_arch(const sycl::stream& stream_ct1) {
182  stream_ct1 << "ERROR: ggml-sycl was compiled without support for the "
183                "current GPU architecture.\n";
184  // __trap();
185  std::exit(1);
186
187  (void)bad_arch; // suppress unused function warning
188}
189
190int get_current_device_id();
191
192inline int ggml_sycl_get_device() {
193    return get_current_device_id();
194}
195
196inline dpct::err0 ggml_sycl_set_device(const int device) try {
197  int current_device_id;
198  SYCL_CHECK(CHECK_TRY_ERROR(current_device_id = get_current_device_id()));
199
200  // GGML_SYCL_DEBUG("ggml_sycl_set_device device_id=%d,
201  // current_device_id=%d\n", device, current_device);
202  if (device == current_device_id) {
203    return 0;
204  }
205
206  return CHECK_TRY_ERROR(dpct::select_device(device));
207} catch (sycl::exception const& exc) {
208  std::cerr << exc.what() << "Exception caught at file:" << __FILE__
209            << ", line:" << __LINE__ << std::endl;
210  crash();
211  std::exit(1);
212}
213
214//////////////////////
215struct optimize_feature {
216    bool reorder=false;
217};
218
219struct sycl_device_info {
220    int cc;  // compute capability
221    int nsm; // number of streaming multiprocessors (CUDA) maps to the maximum
222             // number of compute units on a SYCL device.
223    // size_t  smpb;               // max. shared memory per block
224    size_t  smpbo;              // max. shared memory per block (with opt-in)
225    int warp_size;     // WARP_SIZE(16)|WARP_32_SIZE(32)|WARP_16_SIZE(16). For Intel GPU, 16 is better in most cases. Some OP support 32 only.
226    int max_wg_per_cu; // max work groups per compute unit - refer to
227                       // cudaOccupancyMaxActiveBlocksPerMultiprocessor
228    bool    vmm;                // virtual memory support
229    bool    l0_device_type_valid;
230    bool    l0_discrete_gpu;    // Level Zero backend and not an integrated GPU
231    size_t  vmm_granularity;    // granularity of virtual memory
232    size_t  total_vram;
233    sycl_hw_info hw_info;
234    optimize_feature opt_feature;
235    bool    usm_system_support; // support for USM system allocations
236};
237
238
239struct ggml_sycl_device_info {
240    int device_count;
241
242    sycl_device_info devices[GGML_SYCL_MAX_DEVICES] = {};
243
244    std::array<float, GGML_SYCL_MAX_DEVICES> default_tensor_split = {};
245
246    int max_work_group_sizes[GGML_SYCL_MAX_DEVICES] = {0};
247
248    bool ext_oneapi_level_zero = true; // sycl::backend::ext_oneapi_level_zero used by all enumerated GPU devices
249};
250
251const ggml_sycl_device_info & ggml_sycl_info();
252
253static constexpr size_t SYCL_BUFFER_ALIGNMENT = 128;
254
255struct ggml_sycl_pool {
256    virtual ~ggml_sycl_pool() = default;
257
258    virtual void * alloc(size_t size, size_t * actual_size) = 0;
259    virtual void free(void * ptr, size_t size) = 0;
260};
261
262template<typename T>
263struct ggml_sycl_pool_alloc {
264    ggml_sycl_pool * pool = nullptr;
265    T * ptr = nullptr;
266    size_t actual_size = 0;
267
268    explicit ggml_sycl_pool_alloc(ggml_sycl_pool & pool) : pool(&pool) {
269    }
270
271    ggml_sycl_pool_alloc(ggml_sycl_pool & pool, size_t size) : pool(&pool) {
272        alloc(size);
273    }
274
275    ~ggml_sycl_pool_alloc() {
276        if (ptr != nullptr) {
277            pool->free(ptr, actual_size);
278        }
279    }
280
281    T * realloc(size_t size) {
282        GGML_ASSERT(pool != nullptr);
283        if (ptr)
284            pool->free(ptr, actual_size);
285        ptr = (T *) pool->alloc(size * sizeof(T), &this->actual_size);
286        return ptr;
287    }
288
289    // size is in number of elements
290    T * alloc(size_t size) {
291        GGML_ASSERT(pool != nullptr);
292        GGML_ASSERT(ptr == nullptr);
293        ptr = (T *) pool->alloc(size * sizeof(T), &this->actual_size);
294        return ptr;
295    }
296
297    T * alloc(ggml_sycl_pool & pool, size_t size) {
298        this->pool = &pool;
299        return alloc(size);
300    }
301
302    T * get() {
303        return ptr;
304    }
305
306    ggml_sycl_pool_alloc() = default;
307    ggml_sycl_pool_alloc(const ggml_sycl_pool_alloc &) = delete;
308    ggml_sycl_pool_alloc(ggml_sycl_pool_alloc &&) = delete;
309    ggml_sycl_pool_alloc& operator=(const ggml_sycl_pool_alloc &) = delete;
310    ggml_sycl_pool_alloc& operator=(ggml_sycl_pool_alloc &&) = delete;
311};
312
313// backend interface
314
315struct ggml_tensor_extra_gpu {
316  void* data_device[GGML_SYCL_MAX_DEVICES]; // 1 pointer for each device for split
317                                       // tensors
318  dpct::event_ptr events[GGML_SYCL_MAX_DEVICES]
319                        [GGML_SYCL_MAX_STREAMS]; // events for synchronizing multiple GPUs
320  optimize_feature optimized_feature;
321};
322
323extern int g_ggml_sycl_use_level_zero_api;
324void * ggml_sycl_malloc_device(size_t size, sycl::queue &q,
325                               ggml_sycl_mem_type type = GGML_SYCL_MEM_DIRECT);
326void ggml_sycl_free_device(void *ptr, sycl::queue &q);
327
328void release_extra_gpu(ggml_tensor_extra_gpu * extra, std::vector<queue_ptr> streams={});
329
330struct mmid_row_mapping {
331    int32_t i1;
332    int32_t i2;
333};
334
335namespace sycl_ex = sycl::ext::oneapi::experimental;
336struct ggml_backend_sycl_context {
337    int device;
338    std::string name;
339    optimize_feature opt_feature;
340
341    queue_ptr qptrs[GGML_SYCL_MAX_DEVICES][GGML_SYCL_MAX_STREAMS] = { { nullptr } };
342
343    explicit ggml_backend_sycl_context(int device) :
344        device(device),
345        name(GGML_SYCL_NAME + std::to_string(device)) {
346        opt_feature = ggml_sycl_info().devices[device].opt_feature;
347    }
348
349    queue_ptr stream(int device, int stream) {
350        if (qptrs[device][stream] == nullptr) {
351            qptrs[device][stream] = &(dpct::get_device(device).default_queue());
352        }
353        return qptrs[device][stream];
354    }
355
356    queue_ptr stream() {
357        return stream(device, 0);
358    }
359
360#if GGML_SYCL_DNNL
361    dnnl::engine make_engine(sycl::queue* q) {
362        // Get the device associated with the queue
363        sycl::device dev = q->get_device();
364        // Get the context associated with the queue
365        sycl::context ctx = q->get_context();
366        const dnnl::engine eng = dnnl::sycl_interop::make_engine(dev, ctx);
367        return eng;
368    }
369
370    std::unordered_map<sycl::queue*, dnnl::stream> stream_map;
371    std::unordered_map<sycl::queue*, dnnl::engine> engine_map;
372    dnnl::stream stream_dnnl(int device, int _stream) {
373        auto q = stream(device, _stream);
374        return stream_dnnl(q);
375    }
376    dnnl::engine engine_dnnl(sycl::queue* qptr) {
377        auto it = engine_map.find(qptr);
378        if (it == engine_map.end()) {
379            auto eng = make_engine(qptr);
380            engine_map[qptr] = eng;
381            return eng;
382        }
383        else
384        {
385            return it->second;
386        }
387    }
388    dnnl::stream stream_dnnl(sycl::queue* qptr) {
389        auto it = stream_map.find(qptr);
390        if (it == stream_map.end()) {
391            auto eng = engine_dnnl(qptr);
392            auto stream = dnnl::sycl_interop::make_stream(eng, *qptr);
393            stream_map[qptr] = stream;
394            return stream;
395        }
396        else
397        {
398            return it->second;
399        }
400    }
401    dnnl::stream stream_dnnl() {
402        return stream_dnnl(device, 0);
403    }
404#endif
405
406    // pool
407    std::unique_ptr<ggml_sycl_pool> pools[GGML_SYCL_MAX_DEVICES];
408
409    std::unique_ptr<ggml_sycl_fattn_kv_buffers> fattn_bufs[GGML_SYCL_MAX_DEVICES];
410
411    std::unique_ptr<ggml_sycl_pool> host_pools[GGML_SYCL_MAX_DEVICES];
412
413    std::vector<mmid_row_mapping> mmid_row_mapping_host;
414
415    static std::unique_ptr<ggml_sycl_pool> new_pool_for_device(queue_ptr qptr, int device);
416
417    static std::unique_ptr<ggml_sycl_pool> new_pool_for_host(queue_ptr qptr, int device);
418
419    static std::unique_ptr<ggml_sycl_fattn_kv_buffers> new_fattn_kv_buffers(queue_ptr qptr, int device);
420
421    ggml_sycl_pool & pool(int device) {
422        if (pools[device] == nullptr) {
423            pools[device] = new_pool_for_device(stream(device,0), device);
424        }
425        return *pools[device];
426    }
427
428    ggml_sycl_pool & pool() {
429        return pool(device);
430    }
431
432    ggml_sycl_fattn_kv_buffers & fattn_buffers(int device) {
433        if (fattn_bufs[device] == nullptr) {
434            fattn_bufs[device] = new_fattn_kv_buffers(stream(device, 0), device);
435        }
436        return *fattn_bufs[device];
437    }
438
439    ggml_sycl_fattn_kv_buffers & fattn_buffers() {
440        return fattn_buffers(device);
441    }
442
443#ifdef GGML_SYCL_GRAPH
444    std::unique_ptr<sycl_ex::command_graph<sycl_ex::graph_state::executable>> exec_graph = nullptr;
445#endif
446
447    ggml_sycl_pool & host_pool(int device) {
448        if (host_pools[device] == nullptr) {
449            host_pools[device] = new_pool_for_host(stream(device, 0), device);
450        }
451        return *host_pools[device];
452    }
453
454    ggml_sycl_pool & host_pool() { return host_pool(device); }
455};
456
457// common device functions
458
459static __dpct_inline__ float warp_reduce_sum(float x,
460    const sycl::nd_item<3>& item_ct1) {
461#pragma unroll
462    for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
463        x += dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), x, mask);
464    }
465    return x;
466}
467
468static __dpct_inline__ sycl::float2
469warp_reduce_sum(sycl::float2 a, const sycl::nd_item<3>& item_ct1) {
470#pragma unroll
471    for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
472        a.x() += dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), a.x(),
473            mask);
474        a.y() += dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), a.y(),
475            mask);
476    }
477    return a;
478}
479
480/* use WARP_SIZE or WARP_32_SIZE*/
481template <int width>
482static __dpct_inline__ int warp_reduce_sum(int x) {
483  return sycl::reduce_over_group(
484      sycl::ext::oneapi::this_work_item::get_sub_group(), x, sycl::plus<>());
485}
486
487/* use WARP_SIZE or WARP_32_SIZE*/
488template <int width>
489static __dpct_inline__ float warp_reduce_sum(float x) {
490#pragma unroll
491  for (int offset = width / 2; offset > 0; offset >>= 1) {
492    x += dpct::permute_sub_group_by_xor(
493        sycl::ext::oneapi::this_work_item::get_sub_group(), x, offset, width);
494  }
495  return x;
496}
497
498/* use WARP_SIZE or WARP_32_SIZE*/
499template <int width>
500static __dpct_inline__ float warp_reduce_sum(float x, const sycl::nd_item<3>& item_ct1) {
501#pragma unroll
502  for (int offset = width / 2; offset > 0; offset >>= 1) {
503    x += dpct::permute_sub_group_by_xor(
504        item_ct1.get_sub_group(), x, offset);
505  }
506  return x;
507}
508
509/* use WARP_SIZE or WARP_32_SIZE*/
510template <int width>
511static __dpct_inline__ sycl::float2 warp_reduce_sum(sycl::float2 a) {
512#pragma unroll
513  for (int offset = width / 2; offset > 0; offset >>= 1) {
514    a.x() += dpct::permute_sub_group_by_xor(
515        sycl::ext::oneapi::this_work_item::get_sub_group(), a.x(), offset,
516        width);
517    a.y() += dpct::permute_sub_group_by_xor(
518        sycl::ext::oneapi::this_work_item::get_sub_group(), a.y(), offset,
519        width);
520  }
521  return a;
522}
523
524/* use WARP_SIZE or WARP_32_SIZE*/
525template <int width>
526static __dpct_inline__ sycl::half2 warp_reduce_sum(sycl::half2 a) {
527#pragma unroll
528  for (int offset = width / 2; offset > 0; offset >>= 1) {
529    a = a + dpct::permute_sub_group_by_xor(
530                sycl::ext::oneapi::this_work_item::get_sub_group(), a, offset,
531                width);
532  }
533  return a;
534}
535
536static constexpr int ggml_sycl_get_physical_warp_size() {
537  // todo: for old iGPU + dGPU case, need to be changed.
538  return WARP_SIZE;
539}
540
541/* use WARP_SIZE or WARP_32_SIZE*/
542template <int width>
543static __dpct_inline__ int warp_reduce_all(int x) {
544    if (width == ggml_sycl_get_physical_warp_size()) {
545        return sycl::all_of_group(
546            sycl::ext::oneapi::this_work_item::get_sub_group(),
547            (~0xffffffff &
548             (0x1 << sycl::ext::oneapi::this_work_item::get_sub_group()
549                         .get_local_linear_id())) ||
550                x);
551    } else {
552#pragma unroll
553        for (int offset = width / 2; offset > 0; offset >>= 1) {
554            x = dpct::permute_sub_group_by_xor(
555                    sycl::ext::oneapi::this_work_item::get_sub_group(), x,
556                    offset, width) &&
557                x;
558        }
559        return x;
560    }
561}
562
563/* use WARP_SIZE or WARP_32_SIZE*/
564template <int width>
565static __dpct_inline__ int warp_reduce_any(int x) {
566    if (width == ggml_sycl_get_physical_warp_size()) {
567        return sycl::any_of_group(
568            sycl::ext::oneapi::this_work_item::get_sub_group(),
569            (0xffffffff &
570             (0x1 << sycl::ext::oneapi::this_work_item::get_sub_group()
571                         .get_local_linear_id())) &&
572                x);
573    } else {
574#pragma unroll
575        for (int offset = width / 2; offset > 0; offset >>= 1) {
576            x = dpct::permute_sub_group_by_xor(
577                    sycl::ext::oneapi::this_work_item::get_sub_group(), x,
578                    offset, width) ||
579                x;
580        }
581        return x;
582    }
583}
584
585/* use WARP_SIZE or WARP_32_SIZE*/
586template <int width>
587static __dpct_inline__ float warp_reduce_max(float x) {
588#pragma unroll
589  for (int offset = width / 2; offset > 0; offset >>= 1) {
590    x = sycl::fmax(x, dpct::permute_sub_group_by_xor(
591                          sycl::ext::oneapi::this_work_item::get_sub_group(), x,
592                          offset, width));
593  }
594  return x;
595}
596
597static __dpct_inline__ float warp_reduce_max(float x,
598    const sycl::nd_item<3>& item_ct1) {
599#pragma unroll
600    for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) {
601        x = sycl::fmax(x, dpct::permute_sub_group_by_xor(
602            item_ct1.get_sub_group(), x, mask));
603    }
604    return x;
605}
606
607/* Helper for Computing the linear offset of a ggml_tensor given
608per-dimension sizes, strides, and indices */
609template<int N>
610__dpct_inline__ size_t calculate_offset(const std::array<int, N> & strides, const std::array<int, N> & indices) {
611    size_t offset = 0;
612#pragma unroll
613    for (int i = 0; i < N; i++) {
614        auto index_i = indices[i];
615        offset += strides[i] * index_i;
616    }
617    return offset;
618}
619
620// Helper for vec loading aligned data
621template <typename Tp, int n>
622inline sycl::vec<Tp, n> vec_aligned_load(const Tp* aligned_ptr) {
623    return *reinterpret_cast<const sycl::vec<Tp, n>*>(aligned_ptr);
624}
625
626// Helper for accessing pointers with no warnings
627template <typename Tp, int dim>
628static __dpct_inline__ Tp* get_pointer(sycl::local_accessor<Tp, dim> acc) {
629    return acc.template get_multi_ptr<sycl::access::decorated::no>().get();
630}
631
632int64_t downsample_sycl_global_range(int64_t accumulate_block_num, int64_t block_size);
633
634constexpr size_t ceil_div(const size_t m, const size_t n) {
635    return (m + n - 1) / n;
636}
637
638bool gpu_has_xmx(sycl::device &dev);
639
640int ggml_sycl_get_env(const char *env_name, int default_val);
641
642template <int N, class T> std::string debug_get_array_str(const std::string & prefix, const T array[N]) {
643    if (LIKELY(!g_ggml_sycl_debug)) {
644        return "";
645    }
646    std::stringstream ss;
647    ss << prefix << "=[";
648    for (std::size_t i = 0; i < N - 1; ++i) {
649        ss << array[i] << ", ";
650    }
651    if constexpr (N > 0) {
652        ss << array[N - 1];
653    }
654    ss << "]";
655    return ss.str();
656}
657
658inline std::string debug_get_tensor_str(const std::string &prefix,
659        const ggml_tensor *tensor, const std::string &suffix = "") {
660    std::stringstream ss;
661    if (LIKELY(!g_ggml_sycl_debug)) { return ss.str(); }
662    ss << prefix.c_str() << "=";
663    if (tensor) {
664        ss << "'" << tensor->name << "':type=" << ggml_type_name(tensor->type);
665        ss << debug_get_array_str<GGML_MAX_DIMS>(";ne", tensor->ne);
666        ss << debug_get_array_str<GGML_MAX_DIMS>(";nb", tensor->nb);
667
668        if (!ggml_is_contiguous(tensor)) { ss << ";strided"; }
669        if (ggml_is_permuted(tensor)) { ss << ";permuted"; }
670    } else {
671        ss << "nullptr";
672    }
673    ss << suffix;
674    return ss.str();
675}
676
677// Use scope_op_debug_print to log operations coming from running a model
678struct scope_op_debug_print {
679    // Use string_views to avoid the cost of creating a string and concatenating them
680    // string_views must be alive for as long as the object is alive
681    // scope_op_debug_print are used with string literals in practice which are stored in constant space so always accessible
682    scope_op_debug_print(const std::string_view & func, const std::string_view & func_suffix, const ggml_tensor * dst,
683                         std::size_t num_src, const std::string_view & suffix = "") :
684        func(func),
685        func_suffix(func_suffix) {
686        if (LIKELY(!g_ggml_sycl_debug)) {
687            return;
688        }
689        GGML_SYCL_DEBUG("[SYCL][OP] call %s%s:", func.data(), func_suffix.data());
690        GGML_SYCL_DEBUG("%s", debug_get_tensor_str(" dst", dst).c_str());
691        if (dst) {
692            for (std::size_t i = 0; i < num_src; ++i) {
693                GGML_SYCL_DEBUG("%s", debug_get_tensor_str("\tsrc" + std::to_string(i), dst->src[i]).c_str());
694            }
695        }
696        GGML_SYCL_DEBUG("%s\n", suffix.data());
697    }
698
699    scope_op_debug_print(const std::string_view & func, const ggml_tensor * dst, std::size_t num_src,
700                         const std::string_view & suffix = "") :
701        scope_op_debug_print(func, "", dst, num_src, suffix) {}
702
703    ~scope_op_debug_print() { GGML_SYCL_DEBUG("[SYCL][OP] call %s%s done\n", func.data(), func_suffix.data()); }
704
705  private:
706    std::string_view func;
707    std::string_view func_suffix;
708};
709
710static __dpct_inline__ float get_alibi_slope(const float    max_bias,
711                                             const uint32_t h,
712                                             const uint32_t n_head_log2,
713                                             const float    m0,
714                                             const float    m1) {
715    if (max_bias <= 0.0f) {
716        return 1.0f;
717    }
718    const float base = h < n_head_log2 ? m0 : m1;
719    const int   exph = h < n_head_log2 ? h + 1 : 2*(h - n_head_log2) + 1;
720
721    return dpct::pow(base, exph);
722}
723
724static const sycl::uint3 init_fastdiv_values(uint32_t d) {
725    GGML_ASSERT(d != 0);
726
727    uint32_t L = 0;
728    while (L < 32 && (uint32_t{ 1 } << L) < d) {
729        L++;
730    }
731
732    uint32_t mp = (uint32_t) ((uint64_t{ 1 } << 32) * ((uint64_t{ 1 } << L) - d) / d + 1);
733    return sycl::uint3(mp, L, d);
734}
735
736// Maximum number of bytes that can be copied in a single instruction.
737// Set by test result.
738static constexpr int ggml_sycl_get_max_cpy_bytes() {
739    return 16;
740}
741
742// Aligned memory transfers of 8/16 bytes can be faster than 2 transfers with 4 bytes.
743template <int nbytes, int alignment = 0>
744static __dpct_inline__ void ggml_sycl_memcpy_1(void * dst, const void * src) {
745    if constexpr (alignment != 0) {
746        static_assert(nbytes % alignment == 0, "bad alignment");
747    }
748    constexpr int nb_per_cpy = alignment == 0 ? nbytes : alignment;
749
750#pragma unroll
751    for (int i = 0; i < nbytes/nb_per_cpy; ++i) {
752        if constexpr (nb_per_cpy == 1) {
753            ((char *) dst)[i] = ((const char *) src)[i];
754        } else if constexpr (nb_per_cpy == 2) {
755            ((short *) dst)[i] = ((const short *) src)[i];
756        } else if constexpr (nb_per_cpy == 4) {
757            ((int *) dst)[i] = ((const int *) src)[i];
758        } else if constexpr (nb_per_cpy == 8) {
759            ((sycl::int2 *) dst)[i] = ((const sycl::int2 *) src)[i];
760        } else if constexpr (nb_per_cpy == 16) {
761            ((sycl::int4 *) dst)[i] = ((const sycl::int4 *) src)[i];
762        } else {
763            static_assert(nbytes == 0 && nbytes == -1, "bad nbytes");
764        }
765    }
766}
767template <typename T>
768sycl::half2 __dpct_inline__ make_half2( T x, T y) {
769    sycl::half2 res(static_cast<sycl::half>(x),static_cast<sycl::half>(y));
770    return res;
771}
772
773static __dpct_inline__ uint32_t fastdiv(uint32_t n, const sycl::uint3 fastdiv_values) {
774    const uint32_t hi = sycl::mul_hi<unsigned>(n, fastdiv_values.x());
775    return (hi + n) >> fastdiv_values.y();
776}
777
778
779template <typename T>
780sycl::float2 __dpct_inline__ make_float2( T x, T y) {
781    sycl::float2 res(static_cast<float>(x),static_cast<float>(y));
782    return res;
783}
784
785sycl::float2 __dpct_inline__ __half22float2(sycl::half2 &H) {
786    sycl::float2 float2_value(static_cast<float>(H.x()), static_cast<float>(H.y()));
787    return float2_value;
788}
789
790static __dpct_inline__ sycl::uint2 fast_div_modulo(uint32_t n, const sycl::uint3 fastdiv_values) {
791    const uint32_t div_val = fastdiv(n, fastdiv_values);
792    const uint32_t mod_val = n - div_val * fastdiv_values.z();
793    return sycl::uint2(div_val, mod_val);
794}
795
796static __dpct_inline__ int ggml_sycl_dp4a(const int a, const int b, int c) {
797    return dpct::dp4a(a, b, c);
798}
799
800static __dpct_inline__ float ggml_sycl_e8m0_to_fp32(uint8_t x) {
801    uint32_t bits;
802    if (x == 0) {
803        bits = 0x00400000;
804    } else {
805        bits = (uint32_t) x << 23;
806    }
807
808    float result;
809    memcpy(&result, &bits, sizeof(float));
810    return result;
811}
812
813sycl::float2 __dpct_inline__ __half22float2(const sycl::half2 &H) {
814    sycl::float2 float2_value(static_cast<float>(H.x()), static_cast<float>(H.y()));
815    return float2_value;
816}
817
818float __dpct_inline__ __half2float(sycl::half H) {
819    return static_cast<float>(H);
820}
821
822static __dpct_inline__ void ggml_sycl_mad(float & acc, const float v, const float u) {
823    acc += v*u;
824}
825
826static __dpct_inline__ void ggml_sycl_mad(float & acc, const sycl::float2 v, const sycl::float2 u) {
827    acc += v.x() * u.x();
828    acc += v.y() * u.y();
829}
830
831static __dpct_inline__ void ggml_sycl_mad(float & acc, const sycl::half2 v, const sycl::half2 u) {
832#ifdef GGML_SYCL_F16
833    const sycl::float2 tmp = (v * u).template convert<float, sycl::rounding_mode::automatic>();
834    acc += tmp.x() + tmp.y();
835#else
836    const sycl::float2 tmpv = __half22float2(v);
837    const sycl::float2 tmpu = __half22float2(u);
838    acc += tmpv.x() * tmpu.x();
839    acc += tmpv.y() * tmpu.y();
840#endif // GGML_SYCL_F16
841}
842
843static __dpct_inline__ void ggml_sycl_mad(sycl::half2 & acc, const sycl::half2 v, const sycl::half2 u) {
844#ifdef GGML_SYCL_F16
845    acc += v*u;
846#else
847    const sycl::float2 tmpv = __half22float2(v);
848    const sycl::float2 tmpu = __half22float2(u);
849    sycl::float2 tmpacc = __half22float2(acc);
850    // tmpacc.x += tmpv.x() * tmpu.x();
851    // tmpacc.y += tmpv.y() * tmpu.y();
852    sycl::float2 tmp1(tmpacc.x() + tmpv.x() * tmpu.x(), tmpacc.y() + tmpv.y() * tmpu.y());
853    acc = make_half2(tmp1.x(), tmp1.y());
854#endif // GGML_SYCL_F16
855}
856
857template <int n>
858struct ggml_sycl_unroll {
859    template <typename Func, typename... Args>
860    void operator()(const Func & f, Args... args) const {
861        f(n - 1, args...);
862        ggml_sycl_unroll<n - 1>{}(f, args...);
863    }
864};
865
866template <>
867struct ggml_sycl_unroll<1> {
868    template <typename Func, typename... Args>
869    void operator()(const Func & f, Args... args) const {
870        f(0, args...);
871    }
872};
873
874static __dpct_inline__ sycl::half2 ggml_sycl_hmax2(const sycl::half2 a, const sycl::half2 b) {
875    sycl::half2 ret;
876    reinterpret_cast<sycl::half &>(ret.x()) =
877        sycl::vec<float, 1>(sycl::fmax(a[0], b[0])).convert<sycl::half, sycl::rounding_mode::automatic>()[0];
878    reinterpret_cast<sycl::half &>(ret.y()) =
879        sycl::vec<float, 1>(sycl::fmax(a[1], b[1])).convert<sycl::half, sycl::rounding_mode::automatic>()[0];
880    return ret;
881}
882
883static __dpct_inline__ sycl::half ggml_sycl_hmax(const sycl::half a, const sycl::half b) {
884    return sycl::vec<float, 1>(
885               sycl::fmax(sycl::vec<sycl::half, 1>(a).convert<float, sycl::rounding_mode::automatic>()[0],
886                          sycl::vec<sycl::half, 1>(b).convert<float, sycl::rounding_mode::automatic>()[0]))
887        .convert<sycl::half, sycl::rounding_mode::automatic>()[0];
888}
889
890static __dpct_inline__ uint32_t __hgt2_mask(const sycl::half2 a, const sycl::half2 b) {
891    const uint32_t mask_low  = 0x0000FFFF * (float(a[0]) > float(b[0]));
892    const uint32_t mask_high = 0xFFFF0000 * (float(a[1]) > float(b[1]));
893    return mask_low | mask_high;
894}
895
896static __dpct_inline__ uint32_t fastmodulo(uint32_t n, const sycl::uint3 fastdiv_values) {
897    // expects  fastdiv_values to contain <mp, L, divisor> in <x, y, z> (see init_fastdiv_values)
898    return n - fastdiv(n, fastdiv_values) * fastdiv_values.z();
899}
900
901static bool fast_fp16_available(const int cc) {
902    GGML_UNUSED(cc);
903    return true;   //Intel GPUs always support FP16.
904}
905
906enum class block_reduce_method {
907    MAX,
908    SUM,
909};
910
911template<block_reduce_method method_t, typename T, int warp_size>
912struct block_reduce_policy;
913
914template <typename T, typename... Ts>
915inline constexpr bool is_any = (std::is_same_v<T, Ts> || ...);
916
917template<typename...>
918inline constexpr bool ggml_sycl_dependent_false_v = false;
919
920#define WARP_32_SIZE 32
921
922template <typename T, int warp_size> struct block_reduce_policy<block_reduce_method::SUM, T, warp_size> {
923    static T reduce(T val) {
924        if constexpr (is_any<T, float, sycl::float2, sycl::half2, int>) {
925            return warp_reduce_sum<warp_size>(val);
926        } else {
927            static_assert(ggml_sycl_dependent_false_v<T>, "Unsupported type for block reduce sum");
928        }
929    }
930
931    static T sentinel() {
932        if constexpr (std::is_same_v<T, float>) {
933            return 0.0f;
934        } else if constexpr (std::is_same_v<T, sycl::float2>) {
935            return sycl::float2(0.0f, 0.0f);
936        } else if constexpr (std::is_same_v<T, sycl::half2>) {
937            return sycl::half2(0.0f, 0.0f);
938        } else if constexpr (std::is_same_v<T, int>) {
939            return 0;
940        } else {
941            static_assert(ggml_sycl_dependent_false_v<T>, "Unsupported type for block reduce sum");
942        }
943    }
944};
945
946template <typename T, int warp_size> struct block_reduce_policy<block_reduce_method::MAX, T, warp_size> {
947    static T reduce(T val) {
948        if constexpr (is_any<T, float, sycl::half2>) {
949            return warp_reduce_max<warp_size>(val);
950        } else {
951            static_assert(ggml_sycl_dependent_false_v<T>, "Unsupported type for block reduce max");
952        }
953    }
954
955    static T sentinel() {
956        if constexpr (std::is_same_v<T, float>) {
957            return -INFINITY;
958        } else if constexpr (std::is_same_v<T, sycl::half2>) {
959            return sycl::half2(-INFINITY, -INFINITY);
960        } else {
961            static_assert(ggml_sycl_dependent_false_v<T>, "Unsupported type for block reduce max");
962        }
963    }
964};
965
966
967template <block_reduce_method reduce_method_t, int warp_size, typename T>
968static T block_reduce(T val, T * shared_vals, int block_size_template) {
969    auto item_ct1                 = sycl::ext::oneapi::this_work_item::get_nd_item<3>();
970    val                           = block_reduce_policy<reduce_method_t, T,warp_size>::reduce(val);
971    const int block_size = block_size_template == 0 ? item_ct1.get_local_range(2) : block_size_template;
972    const int nthreads = item_ct1.get_local_range(2);
973    const int nwarps = nthreads / WARP_SIZE;
974
975    if (block_size > warp_size) {
976        assert((block_size <= 1024) && (block_size % warp_size) == 0);
977        const int warp_id = item_ct1.get_local_id(2) / warp_size;
978        const int lane_id = item_ct1.get_local_id(2) % warp_size;
979        if (lane_id == 0) {
980            shared_vals[warp_id] = val;
981        }
982        item_ct1.barrier(sycl::access::fence_space::local_space);
983
984        size_t nreduce = nwarps / WARP_SIZE;
985        float tmp = 0.f;
986        if (lane_id < (static_cast<int>(block_size) / warp_size)) {
987            for (size_t i = 0; i < nreduce; i += 1)
988            {
989                tmp += shared_vals[lane_id + i * WARP_SIZE];
990            }
991        }
992        return block_reduce_policy<reduce_method_t, T, warp_size>::reduce(tmp);
993    }
994    return val;
995}
996
997static __dpct_inline__ float ggml_sycl_ue4m3_to_fp32(uint8_t x) {
998    // UE4M3 is unsigned: 4 exp bits (bias 7), 3 mantissa bits, no sign, no NaN.
999    // exp == 0xF is a valid exponent (256-448 range), not NaN.
1000    if (x == 0 || x == 0x7F) {
1001        return 0.0f;
1002    }
1003    const int exp = (x >> 3) & 0xF;
1004    const int man = x & 0x7;
1005    float raw;
1006    if (exp == 0) {
1007        raw = man * (1.0f / 8.0f) * sycl::pow(2.0f, -6.0f);
1008    } else {
1009        raw = (1.0f + man / 8.0f) * sycl::pow(2.0f, (float) exp - 7.0f);
1010    }
1011    return raw * 0.5f;
1012}
1013
1014#endif // GGML_SYCL_COMMON_HPP