// // MIT license // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: MIT // // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API #include #endif #if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC # include #endif #if SYCL_EXT_ONEAPI_VIRTUAL_MEM # include # include # define GGML_SYCL_SUPPORT_VMM #endif #include #include "ggml.h" #include "ggml-sycl.h" #include "ggml-impl.h" #include "ggml-backend-impl.h" #include "ggml-sycl/add-id.hpp" #include "ggml-sycl/backend.hpp" #include "ggml-sycl/common.hpp" #include "ggml-sycl/element_wise.hpp" #include "ggml-sycl/gemm.hpp" #include "ggml-sycl/getrows.hpp" #include "ggml-sycl/norm.hpp" #include "ggml-sycl/presets.hpp" #include "ggml-sycl/quantize.hpp" #include "ggml-sycl/repeat_back.hpp" #include "ggml-sycl/set_rows.hpp" #include "ggml-sycl/set.hpp" #include "ggml-sycl/conv2d.hpp" #include "ggml-sycl/conv2d-dw.hpp" #include "ggml-sycl/conv2d-transpose.hpp" #include "ggml-sycl/ssm_conv.hpp" #include "ggml-sycl/sycl_hw.hpp" #include "ggml-sycl/ssm_scan.hpp" #include "ggml-sycl/fill.hpp" #include "ggml-sycl/cumsum.hpp" #include "ggml-sycl/diag.hpp" #include "ggml-sycl/solve_tri.hpp" #include "ggml-sycl/gated_delta_net.hpp" #include "ggml-sycl/pool.hpp" #include "ggml-sycl/cross_entropy_loss.hpp" #define MEM_SIZE_2M 0x00200000 #define MEM_SIZE_1G 0x40000000 static bool g_sycl_loaded = false; int g_ggml_sycl_debug = 0; int g_ggml_sycl_enable_optimize = 1; int g_ggml_sycl_enable_graph = 0; int g_ggml_sycl_enable_dnn = 1; int g_ggml_sycl_enable_vmm = 1; int g_ggml_sycl_prioritize_dmmv = 0; int g_ggml_sycl_use_async_mem_op = 0; int g_ggml_sycl_use_async_mem_op_requested = 1; int g_ggml_sycl_use_level_zero_api = 0; int g_ggml_sycl_enable_flash_attention = 1; int g_ggml_sycl_dev2dev_memcpy = DEV2DEV_MEMCPY_SYCL; int g_ggml_sycl_usm_system = 0; static ggml_sycl_device_info ggml_sycl_init() { ggml_sycl_device_info info = {}; info.device_count = dpct::dev_mgr::instance().device_count(); if (info.device_count == 0) { GGML_LOG_ERROR("%s: failed to initialize: %s\n", GGML_SYCL_NAME, __func__); return info; } GGML_ASSERT(info.device_count <= GGML_SYCL_MAX_DEVICES); int64_t total_vram = 0; /* This is a bit misleading; reserved for later */ // #if defined(SYCL_USE_XMX) // GGML_LOG_INFO("%s: SYCL_USE_XMX: yes\n", __func__); // #else // GGML_LOG_INFO("%s: SYCL_USE_XMX: no\n", __func__); // #endif for (int i = 0; i < info.device_count; ++i) { dpct::device_info prop; auto & device = dpct::dev_mgr::instance().get_device(i); SYCL_CHECK(CHECK_TRY_ERROR(dpct::get_device_info( prop, device))); #if !defined(GGML_SYCL_SUPPORT_VMM) info.devices[i].vmm = 0; #else info.devices[i].vmm = device.has(sycl::aspect::ext_oneapi_virtual_mem); if (info.devices[i].vmm) { // NB: SYCL's get_mem_granularity always returns the _minimum_ granularity, // but the L0 API requires a larger page size for allocs above 2 MiB and // rejects non-multiples with UR_RESULT_ERROR_INVALID_VALUE [sic]. // Here we clamp it to 2 MiB for simplicity, but other devices may require // calling zeVirtualMemQueryPageSize or yet unexposed public API. const size_t physical_page = 2ull << 20; // 2 MiB info.devices[i].vmm_granularity = std::max( sycl::ext::oneapi::experimental::get_mem_granularity( device, sycl::context(device)), physical_page); } #endif info.default_tensor_split[i] = total_vram; total_vram += prop.get_global_mem_size(); info.devices[i].cc = 100 * prop.get_major_version() + 10 * prop.get_minor_version(); info.devices[i].nsm = prop.get_max_compute_units() / 16; //16: Number of Xe Cores info.devices[i].opt_feature.reorder = device.ext_oneapi_architecture_is(syclex::arch_category::intel_gpu); info.devices[i].smpbo = prop.get_local_mem_size(); info.devices[i].warp_size = WARP_SIZE; info.devices[i].usm_system_support = device.has(sycl::aspect::usm_system_allocations); info.max_work_group_sizes[i] = prop.get_max_work_group_size(); info.devices[i].max_wg_per_cu = info.max_work_group_sizes[i] / prop.get_max_compute_units(); info.devices[i].hw_info = get_device_hw_info(&device); // Only check GPU devices; CPU devices use OpenCL and would otherwise // disable Level Zero for the GPUs on systems without ONEAPI_DEVICE_SELECTOR set. if (device.is_gpu() && device.default_queue().get_backend() != sycl::backend::ext_oneapi_level_zero) { GGML_LOG_WARN("SYCL GPU device %d does not use Level Zero backend, disabling Level Zero memory API\n", i); info.ext_oneapi_level_zero = false; } #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API if (info.ext_oneapi_level_zero && device.is_gpu() && device.default_queue().get_backend() == sycl::backend::ext_oneapi_level_zero) { ze_device_handle_t ze_dev = sycl::get_native(device.default_queue().get_device()); ze_device_properties_t props = {}; props.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES; ze_result_t r = zeDeviceGetProperties(ze_dev, &props); info.devices[i].l0_discrete_gpu = r == ZE_RESULT_SUCCESS && !(props.flags & ZE_DEVICE_PROPERTY_FLAG_INTEGRATED); } #endif } for (int id = 0; id < info.device_count; ++id) { info.default_tensor_split[id] /= total_vram; } #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API // Large buffers can be allocated before ggml_check_sycl() initializes other // g_ggml_sycl_enable_* globals, so initialize this one as early as we can. g_ggml_sycl_use_level_zero_api = info.ext_oneapi_level_zero && ggml_sycl_get_env("GGML_SYCL_USE_LEVEL_ZERO_API", 1); #else g_ggml_sycl_use_level_zero_api = 0; #endif return info; } const ggml_sycl_device_info & ggml_sycl_info() { static ggml_sycl_device_info info = ggml_sycl_init(); return info; } static void print_device_detail(int id, sycl::device &device, std::string device_type) { dpct::device_info prop; SYCL_CHECK(CHECK_TRY_ERROR( dpct::get_device_info(prop, device))); std::string version; version += std::to_string(prop.get_major_version()); version += "."; version += std::to_string(prop.get_minor_version()); device_type = std::regex_replace(device_type, std::regex("ext_oneapi_"), ""); std::string name = std::string(prop.get_name()); name = std::regex_replace(name, std::regex("\\(R\\)"), ""); name = std::regex_replace(name, std::regex("\\(TM\\)"), ""); auto global_mem_size = prop.get_global_mem_size()/1000000; GGML_LOG_INFO("|%2d|%19s|%39s|%7s|%7d|%8d|%5d|%6luM|%21s|\n", id, device_type.c_str(), name.c_str(), version.c_str(), prop.get_max_compute_units(), prop.get_max_work_group_size(), prop.get_max_sub_group_size(), global_mem_size, device.get_info().c_str()); } static void print_device_opt_feature(int device_count) { GGML_LOG_INFO("SYCL Optimization Feature:\n"); GGML_LOG_INFO( "|ID| Device Type|Reorder|\n"); GGML_LOG_INFO( "|--|-------------------|-------|\n"); std::map DeviceNums; for (int id = 0; id < device_count; ++id) { sycl::device device = dpct::dev_mgr::instance().get_device(id); std::string backend_type = get_device_backend_and_type(device); int type_id = DeviceNums[backend_type]++; std::stringstream device_type; device_type << "[" << backend_type << ":" << std::to_string(type_id) << "]"; std::string device_type_s = device_type.str(); device_type_s = std::regex_replace(device_type_s, std::regex("ext_oneapi_"), ""); GGML_LOG_INFO("|%2d|%19s|%7s|\n", id, device_type_s.c_str(), ggml_sycl_info().devices[id].opt_feature.reorder ? "Y": "N"); } } void ggml_backend_sycl_print_sycl_devices() { GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_print_sycl_devices\n"); int device_count = dpct::dev_mgr::instance().device_count(); std::map DeviceNums; GGML_LOG_INFO("Found %d SYCL devices:\n", device_count); GGML_LOG_INFO( "| | | | " " |Max | |Max |Global | |\n"); GGML_LOG_INFO( "| | | | " " |compute|Max work|sub |mem | |\n"); GGML_LOG_INFO( "|ID| Device Type| " "Name|Version|units |group |group|size | Driver version|\n"); GGML_LOG_INFO( "|--|-------------------|---------------------------------------|------" "-|-------|--------|-----|-------|---------------------|\n"); for (int id = 0; id < device_count; ++id) { sycl::device device = dpct::dev_mgr::instance().get_device(id); std::string backend_type = get_device_backend_and_type(device); int type_id = DeviceNums[backend_type]++; std::stringstream device_type; device_type << "[" << backend_type << ":" << std::to_string(type_id) << "]"; print_device_detail(id, device, device_type.str()); } print_device_opt_feature(device_count); } static const char* dev2dev_int2str(int dev2dev) { if (dev2dev == DEV2DEV_MEMCPY_SYCL) { return "SYCL API"; } else if (dev2dev == DEV2DEV_MEMCPY_L0) { return "Level Zero API"; } else { return "Unknown"; } } static void ggml_check_sycl() try { static bool initialized = false; if (!initialized) { g_ggml_sycl_debug = ggml_sycl_get_env("GGML_SYCL_DEBUG", 0); g_ggml_sycl_enable_optimize = ggml_sycl_get_env("GGML_SYCL_ENABLE_OPT", 1); g_ggml_sycl_enable_graph = ggml_sycl_get_env("GGML_SYCL_ENABLE_GRAPH", 0); g_ggml_sycl_enable_dnn = ggml_sycl_get_env("GGML_SYCL_ENABLE_DNN", 1); g_ggml_sycl_enable_vmm = ggml_sycl_get_env("GGML_SYCL_ENABLE_VMM", 1); g_ggml_sycl_prioritize_dmmv = ggml_sycl_get_env("GGML_SYCL_PRIORITIZE_DMMV", 0); g_ggml_sycl_dev2dev_memcpy = ggml_sycl_get_env("GGML_SYCL_DEV2DEV_MEMCPY", DEV2DEV_MEMCPY_SYCL); if (g_ggml_sycl_use_level_zero_api == 0) { g_ggml_sycl_dev2dev_memcpy = DEV2DEV_MEMCPY_SYCL; } #ifdef SYCL_FLASH_ATTN g_ggml_sycl_enable_flash_attention = ggml_sycl_get_env("GGML_SYCL_ENABLE_FLASH_ATTN", 1); #else g_ggml_sycl_enable_flash_attention = 0; #endif g_ggml_sycl_usm_system = ggml_sycl_get_env("GGML_SYCL_USM_SYSTEM", 0); GGML_SYCL_DEBUG("[SYCL] call ggml_check_sycl\n"); GGML_LOG_INFO("Build with Macros:\n"); #if defined(GGML_SYCL_DNNL) GGML_LOG_INFO(" GGML_SYCL_DNNL: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_DNNL: no\n"); #endif #if defined(GGML_SYCL_F16) GGML_LOG_INFO(" GGML_SYCL_F16: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_F16: no\n"); #endif #if defined(GGML_SYCL_FORCE_MMQ) GGML_LOG_INFO(" GGML_SYCL_FORCE_MMQ: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_FORCE_MMQ: no\n"); #endif #if defined(GGML_SYCL_GRAPH) GGML_LOG_INFO(" GGML_SYCL_GRAPH: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_GRAPH: no\n"); #endif #if defined(GGML_SYCL_SUPPORT_LEVEL_ZERO_API) GGML_LOG_INFO(" GGML_SYCL_SUPPORT_LEVEL_ZERO_API: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_SUPPORT_LEVEL_ZERO_API: no\n"); #endif #if defined(GGML_SYCL_SUPPORT_VMM) GGML_LOG_INFO(" GGML_SYCL_SUPPORT_VMM: yes\n"); #else GGML_LOG_INFO(" GGML_SYCL_SUPPORT_VMM: no\n"); #endif GGML_LOG_INFO("Running with Environment Variables:\n"); GGML_LOG_INFO(" GGML_SYCL_DEBUG: %d\n", g_ggml_sycl_debug); #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API GGML_LOG_INFO(" GGML_SYCL_DEV2DEV_MEMCPY: %d (%s)\n", g_ggml_sycl_dev2dev_memcpy, dev2dev_int2str(g_ggml_sycl_dev2dev_memcpy)); #else GGML_LOG_INFO(" GGML_SYCL_DEV2DEV_MEMCPY: %d (%s), enable to SYCL API since missing GGML_SYCL_SUPPORT_LEVEL_ZERO_API\n", g_ggml_sycl_dev2dev_memcpy, dev2dev_int2str(g_ggml_sycl_dev2dev_memcpy)); #endif #if defined(GGML_SYCL_DNNL) GGML_LOG_INFO(" GGML_SYCL_ENABLE_DNN: %d\n", g_ggml_sycl_enable_dnn); #else GGML_LOG_INFO(" GGML_SYCL_ENABLE_DNN: DNN disabled by compile flag\n"); #endif #ifdef SYCL_FLASH_ATTN GGML_LOG_INFO(" GGML_SYCL_ENABLE_FLASH_ATTN: %d\n", g_ggml_sycl_enable_flash_attention); #else GGML_LOG_INFO(" GGML_SYCL_ENABLE_FLASH_ATTN: %d disabled by compile flag\n", g_ggml_sycl_enable_flash_attention); #endif #ifdef GGML_SYCL_GRAPH GGML_LOG_INFO(" GGML_SYCL_ENABLE_GRAPH: %d\n", g_ggml_sycl_enable_graph); #else GGML_LOG_INFO(" GGML_SYCL_ENABLE_GRAPH: graph disabled by compile flag\n"); #endif GGML_LOG_INFO(" GGML_SYCL_ENABLE_OPT: %d\n", g_ggml_sycl_enable_optimize); #if defined(GGML_SYCL_SUPPORT_VMM) GGML_LOG_INFO(" GGML_SYCL_ENABLE_VMM: %d\n", g_ggml_sycl_enable_vmm); #else GGML_LOG_INFO(" GGML_SYCL_ENABLE_VMM: virtual memory extension is not available\n"); #endif GGML_LOG_INFO(" GGML_SYCL_PRIORITIZE_DMMV: %d\n", g_ggml_sycl_prioritize_dmmv); g_ggml_sycl_use_async_mem_op_requested = ggml_sycl_get_env("GGML_SYCL_USE_ASYNC_MEM_OP", 1); GGML_LOG_INFO(" GGML_SYCL_USE_ASYNC_MEM_OP: %d\n", g_ggml_sycl_use_async_mem_op_requested); #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API GGML_LOG_INFO(" GGML_SYCL_USE_LEVEL_ZERO_API: %d\n", g_ggml_sycl_use_level_zero_api); #else GGML_LOG_INFO(" GGML_SYCL_USE_LEVEL_ZERO_API: Disable Level Zero API usage by compile flag\n"); #endif GGML_LOG_INFO(" GGML_SYCL_USM_SYSTEM: %d\n", g_ggml_sycl_usm_system); /* NOT REMOVE, keep it for next optimize for XMX. #if defined(SYCL_USE_XMX) fprintf(stderr, "%s: SYCL_USE_XMX: yes\n", __func__); #else fprintf(stderr, "%s: SYCL_USE_XMX: no\n", __func__); #endif */ // Async USM allocation/free is also useful outside the graph path: it avoids the host waits in the reorder // staging path while preserving queue ordering semantics. Graph support still depends on the extension being // available, but it no longer needs to control the non-graph fast path. #if defined(GGML_SYCL_GRAPH) && SYCL_EXT_ONEAPI_ASYNC_MEMORY_ALLOC g_ggml_sycl_use_async_mem_op = g_ggml_sycl_use_async_mem_op_requested || g_ggml_sycl_enable_graph; if (g_ggml_sycl_use_async_mem_op) { for (unsigned int i = 0; i < dpct::dev_mgr::instance().device_count(); ++i) { if (!dpct::dev_mgr::instance().get_device(i).has(sycl::aspect::ext_oneapi_async_memory_alloc)) { g_ggml_sycl_use_async_mem_op = 0; break; } } } #endif if (CHECK_TRY_ERROR(g_all_sycl_device_count = dpct::dev_mgr::instance().device_count()) != 0) { initialized = true; g_sycl_loaded = false; return; } GGML_ASSERT(g_all_sycl_device_count <= GGML_SYCL_MAX_DEVICES); initialized = true; g_sycl_loaded = true; ggml_backend_sycl_print_sycl_devices(); } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } /* device_index: device index from 0 to n (continue numbers). It is used for device select/set in SYCL backend internal data structure. */ inline void check_allow_gpu_index(const int device_index) { if (device_index >= ggml_sycl_info().device_count) { char error_buf[256]; snprintf( error_buf, sizeof(error_buf), "%s error: device_index:%d is out of range: [0-%d]", __func__, device_index, ggml_sycl_info().device_count - 1); GGML_LOG_ERROR("%s\n", error_buf); assert(false); } } GGML_API void ggml_backend_sycl_get_gpu_list(int *id_list, int max_len) try { GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_get_gpu_list\n"); for(int i=0;i=max_len) break; id_list[i] = i; } return; } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } inline void free_aligned_mem_host(void * memblock) { #ifdef _WIN32 _aligned_free(memblock); #else free(memblock); #endif } // sycl buffer struct ggml_backend_sycl_buffer_context { int device; void * dev_ptr = nullptr; queue_ptr stream; std::string name; optimize_feature opt_feature; std::vector tensor_extras; bool is_usm_system; ggml_backend_sycl_buffer_context(int device, void * dev_ptr, queue_ptr stream, bool is_usm_system) : device(device), dev_ptr(dev_ptr), stream(stream), is_usm_system(is_usm_system) { check_allow_gpu_index(device); name = (GGML_SYCL_NAME + std::to_string(device)); opt_feature = ggml_sycl_info().devices[device].opt_feature; } ~ggml_backend_sycl_buffer_context() { if (dev_ptr != nullptr) { ggml_sycl_set_device(device); if (is_usm_system) free_aligned_mem_host(dev_ptr); else SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(dev_ptr, *stream))); } //release extra used by tensors for (ggml_tensor_extra_gpu * extra : tensor_extras) { release_extra_gpu(extra); } } }; static const char * ggml_backend_sycl_buffer_type_get_name(ggml_backend_buffer_type_t buft); static bool ggml_backend_buffer_is_sycl(ggml_backend_buffer_t buffer) { return buffer->buft->iface.get_name == ggml_backend_sycl_buffer_type_get_name; } static void ggml_backend_sycl_buffer_free_buffer(ggml_backend_buffer_t buffer) try { ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context; ggml_sycl_set_device(ctx->device); delete ctx; } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void * ggml_backend_sycl_buffer_get_base(ggml_backend_buffer_t buffer) { ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context; return ctx->dev_ptr; } static enum ggml_status ggml_backend_sycl_buffer_init_tensor(ggml_backend_buffer_t buffer, ggml_tensor *tensor) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor, "\n").c_str()); ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *)buffer->context; if (tensor->view_src != NULL) { assert(tensor->view_src->buffer->buft == buffer->buft); return GGML_STATUS_SUCCESS; } if (g_ggml_sycl_enable_optimize) { // set reorder extra buffer based on supported type switch (tensor->type) { case GGML_TYPE_Q4_0: case GGML_TYPE_Q8_0: case GGML_TYPE_Q3_K: case GGML_TYPE_Q4_K: case GGML_TYPE_Q5_K: case GGML_TYPE_Q6_K:{ ggml_tensor_extra_gpu * extra = new ggml_tensor_extra_gpu{}; tensor->extra = extra; ctx->tensor_extras.push_back(extra); break; } default: break; } } if (ggml_is_quantized(tensor->type)) { // initialize padding to 0 to avoid possible NaN values size_t original_size = ggml_nbytes(tensor); size_t padded_size = ggml_backend_buft_get_alloc_size(buffer->buft, tensor); if (padded_size > original_size && tensor->view_src == nullptr) { SYCL_CHECK(CHECK_TRY_ERROR(ctx->stream->memset( (char *)tensor->data + original_size, 0, padded_size - original_size).wait())); } } return GGML_STATUS_SUCCESS; } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_buffer_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor *tensor, const void *data, size_t offset, size_t size) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str()); GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset); ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context; ggml_sycl_set_device(ctx->device); auto stream = &(dpct::dev_mgr::instance().get_device(ctx->device).default_queue()); SYCL_CHECK(CHECK_TRY_ERROR(dpct::dev_mgr::instance().get_device(ctx->device).queues_wait_and_throw())); #ifndef _WIN32 // Note: Use host buffer to save the data from mmap(), then copy to device. It's workaround for mmap() issue on PVC GPU. // This function will be called during load model from disk. Use memory buffer replace dynamic won't save more time and brings potential memory leak risk here. char * host_buf = (char *) malloc(size); memcpy(host_buf, data, size); SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy((char *) tensor->data + offset, host_buf, size).wait())); free(host_buf); #else SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy((char *) tensor->data + offset, data, size).wait())); #endif } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_buffer_get_tensor(ggml_backend_buffer_t buffer, const ggml_tensor *tensor, void *data, size_t offset, size_t size) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str()); GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset); ggml_backend_sycl_buffer_context * ctx = ( ggml_backend_sycl_buffer_context *)buffer->context; ggml_sycl_set_device(ctx->device); auto stream = dpct::dev_mgr::instance().get_device(ctx->device).default_queue(); SYCL_CHECK(CHECK_TRY_ERROR( stream.memcpy(data, (const char *)tensor->data + offset, size) .wait())); } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API static bool ggml_sycl_is_l0_discrete_gpu(int device) { return ggml_sycl_info().devices[device].l0_discrete_gpu; } #endif static void dev2dev_memcpy(int device_dst, sycl::queue &q_dst, int device_src, sycl::queue &q_src, void *ptr_dst, const void *ptr_src, size_t size) { #ifdef GGML_SYCL_SUPPORT_LEVEL_ZERO_API if (g_ggml_sycl_dev2dev_memcpy == DEV2DEV_MEMCPY_L0) { // Use Level Zero direct copy for dGPU-to-dGPU transfers. const bool l0_copy_supported = ggml_sycl_is_l0_discrete_gpu(device_dst) && ggml_sycl_is_l0_discrete_gpu(device_src); if (g_ggml_sycl_use_level_zero_api && l0_copy_supported) { auto ze_ctx = sycl::get_native(q_dst.get_context()); auto ze_dev = sycl::get_native(q_dst.get_device()); ze_command_queue_desc_t cq_desc = {ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, nullptr, 0, 0, 0, ZE_COMMAND_QUEUE_MODE_SYNCHRONOUS, ZE_COMMAND_QUEUE_PRIORITY_NORMAL}; ze_command_list_handle_t cl; ze_result_t r = zeCommandListCreateImmediate(ze_ctx, ze_dev, &cq_desc, &cl); if (r == ZE_RESULT_SUCCESS) { GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by L0\n"); r = zeCommandListAppendMemoryCopy(cl, ptr_dst, ptr_src, size, nullptr, 0, nullptr); zeCommandListDestroy(cl); if (r == ZE_RESULT_SUCCESS) { return; } } } } #endif if (g_ggml_sycl_dev2dev_memcpy == DEV2DEV_MEMCPY_SYCL) { if (q_dst.get_device().ext_oneapi_can_access_peer(q_src.get_device(), sycl::ext::oneapi::peer_access::access_supported)) { GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by SYCL\n"); SYCL_CHECK(CHECK_TRY_ERROR(q_dst.memcpy(ptr_dst, ptr_src, size).wait())); return; } } // Host-staged copy GGML_SYCL_DEBUG("[SYCL] dev2dev memcpy by host forward\n"); char *host_buf = (char *)malloc(size); q_src.memcpy(host_buf, (const char *)ptr_src, size).wait(); q_dst.memcpy((char *)ptr_dst, host_buf, size).wait(); free(host_buf); } static bool ggml_backend_sycl_buffer_cpy_tensor(ggml_backend_buffer_t buffer, const ggml_tensor *src, ggml_tensor *dst) try { bool is_cpy_supported = ggml_backend_buffer_is_sycl(src->buffer); GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": dst", dst).c_str()); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(" src", src).c_str()); GGML_SYCL_DEBUG(" is_cpy_supported=%d\n", is_cpy_supported); if (is_cpy_supported) { ggml_backend_sycl_buffer_context * src_ctx = (ggml_backend_sycl_buffer_context *)src->buffer->context; ggml_backend_sycl_buffer_context * dst_ctx = (ggml_backend_sycl_buffer_context *)dst->buffer->context; ggml_sycl_set_device(src_ctx->device); /* DPCT1009:198: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ SYCL_CHECK(CHECK_TRY_ERROR( dpct::dev_mgr::instance().get_device(src_ctx->device).queues_wait_and_throw())); ggml_sycl_set_device(dst_ctx->device); /* DPCT1009:199: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ SYCL_CHECK(CHECK_TRY_ERROR( dpct::dev_mgr::instance().get_device(dst_ctx->device).queues_wait_and_throw())); /* DPCT1009:200: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ queue_ptr stream_dst = dst_ctx->stream; queue_ptr stream_src = src_ctx->stream; size_t size = ggml_nbytes(src); //todo. it's dirty solutino to walkaroud known issue:device2device cross GPUs. dev2dev_memcpy(dst_ctx->device, *stream_dst, src_ctx->device, *stream_src, dst->data, src->data, size); //todo, it's known issue:error in device2device cross GPUs. reused when the issue is fixed. DON"T remove #if 0 SYCL_CHECK(CHECK_TRY_ERROR((*stream).memcpy( (char *)dst->data, (const char *)src->data, size).wait())); /* DPCT1009:201: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ SYCL_CHECK(CHECK_TRY_ERROR( dpct::dev_mgr::instance().get_device(dst_ctx->device).queues_wait_and_throw())); #endif return true; } return false; GGML_UNUSED(buffer); } catch (const sycl::exception & exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) try { GGML_SYCL_DEBUG("[SYCL] call %s: size=%zu\n", __func__, buffer->size); ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context; ggml_sycl_set_device(ctx->device); queue_ptr stream = ctx->stream; SYCL_CHECK( CHECK_TRY_ERROR(dpct::get_current_device().queues_wait_and_throw())); constexpr size_t MAX_CHUNK = 2ULL << 30; // 2 GiB for (size_t off = 0; off < buffer->size; off += MAX_CHUNK) { size_t chunk = std::min(buffer->size - off, MAX_CHUNK); SYCL_CHECK(CHECK_TRY_ERROR( (*stream) .memset(static_cast(ctx->dev_ptr) + off, value, chunk) .wait() )); } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_buffer_memset_tensor(ggml_backend_buffer_t buffer, ggml_tensor * tensor, uint8_t value, size_t offset, size_t size) { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str()); GGML_SYCL_DEBUG(" size=%zu offset=%zu value=%u\n", size, offset, value); ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context; SYCL_CHECK(ggml_sycl_set_device(ctx->device)); auto stream = &(dpct::dev_mgr::instance().get_device(ctx->device).default_queue()); if (size == 0) { return; // Nothing to do } if (tensor->data == nullptr) { GGML_ABORT("Error: Tensor data pointer is null.\n"); } void * target_ptr = static_cast(tensor->data) + offset; SYCL_CHECK(CHECK_TRY_ERROR((*stream).memset(target_ptr, value, size))); SYCL_CHECK(CHECK_TRY_ERROR((*stream).wait())); } static void ggml_backend_sycl_buffer_reset(ggml_backend_buffer_t buffer) { GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__); if (buffer == nullptr) { return; } ggml_backend_sycl_buffer_context * ctx = (ggml_backend_sycl_buffer_context *) buffer->context; if (ctx != nullptr) { for (ggml_tensor_extra_gpu * extra : ctx->tensor_extras) { release_extra_gpu(extra); } ctx->tensor_extras.clear(); // reset the tensor_extras vector } } static const ggml_backend_buffer_i ggml_backend_sycl_buffer_interface = { /* .free_buffer = */ ggml_backend_sycl_buffer_free_buffer, /* .get_base = */ ggml_backend_sycl_buffer_get_base, /* .init_tensor = */ ggml_backend_sycl_buffer_init_tensor, /* .memset_tensor = */ ggml_backend_sycl_buffer_memset_tensor, /* .set_tensor = */ ggml_backend_sycl_buffer_set_tensor, /* .get_tensor = */ ggml_backend_sycl_buffer_get_tensor, /* .set_tensor_2d = */ NULL, /* .get_tensor_2d = */ NULL, /* .cpy_tensor = */ ggml_backend_sycl_buffer_cpy_tensor, /* .clear = */ ggml_backend_sycl_buffer_clear, /* .reset = */ ggml_backend_sycl_buffer_reset, }; // sycl buffer type struct ggml_backend_sycl_buffer_type_context { int device; std::string name; // each buffer type has its own stream queue_ptr stream = nullptr; }; static const char * ggml_backend_sycl_buffer_type_get_name(ggml_backend_buffer_type_t buft) { ggml_backend_sycl_buffer_type_context * ctx = (ggml_backend_sycl_buffer_type_context *)buft->context; return ctx->name.c_str(); } static bool check_usm_system(int device, size_t size) { bool use_usm_system = g_ggml_sycl_usm_system && size >= MEM_SIZE_1G; if (use_usm_system && !ggml_sycl_info().devices[device].usm_system_support) { GGML_LOG_INFO("Device does not support USM system allocations\n"); use_usm_system = false; } return use_usm_system; } inline void * aligned_malloc_host(size_t alignment, size_t size) { #ifdef _WIN32 return _aligned_malloc(size, alignment); #else return aligned_alloc(alignment, size); #endif } static ggml_backend_buffer_t ggml_backend_sycl_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) try { ggml_check_sycl(); ggml_backend_sycl_buffer_type_context * buft_ctx = (ggml_backend_sycl_buffer_type_context *)buft->context; ggml_sycl_set_device(buft_ctx->device); const queue_ptr stream = buft_ctx->stream; size = std::max(size, (size_t)1); // syclMalloc returns null for size 0 /* Alignment below ensures best performance. While in theory it could lead to wasting memory, this is acceptable because in practice only few buffers are allocated and even less exceed the minimum size accepted here for USM system allocations. */ size_t alignment = MEM_SIZE_2M; size_t aligned_size = ((size + alignment - 1) / alignment) * alignment; bool use_usm_system = check_usm_system(buft_ctx->device, aligned_size); void * dev_ptr; if (use_usm_system) { dev_ptr = (void *)aligned_malloc_host(alignment, aligned_size); if (!dev_ptr) { GGML_LOG_ERROR("%s: can't allocate %lu Bytes of memory on host\n", __func__, size); return nullptr; } } else { SYCL_CHECK(CHECK_TRY_ERROR(dev_ptr = (void *)ggml_sycl_malloc_device(size, *stream))); if (!dev_ptr) { GGML_LOG_ERROR("%s: can't allocate %lu Bytes of memory on device\n", __func__, size); return nullptr; } } ggml_backend_sycl_buffer_context * ctx = new ggml_backend_sycl_buffer_context(buft_ctx->device, dev_ptr, buft_ctx->stream, use_usm_system); return ggml_backend_buffer_init(buft, ggml_backend_sycl_buffer_interface, ctx, size); } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static size_t ggml_backend_sycl_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) { return SYCL_BUFFER_ALIGNMENT; GGML_UNUSED(buft); } static size_t ggml_backend_sycl_buffer_type_get_max_size(ggml_backend_buffer_type_t buft) { return dpct::get_current_device().get_max_mem_alloc_size(); GGML_UNUSED(buft); } static size_t ggml_backend_sycl_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) { size_t size = ggml_nbytes(tensor); int64_t ne0 = tensor->ne[0]; if (ggml_is_quantized(tensor->type)) { if (ne0 % MATRIX_ROW_PADDING != 0) { size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING); } } return size; GGML_UNUSED(buft); } static const ggml_backend_buffer_type_i ggml_backend_sycl_buffer_type_interface = { /* .get_name = */ ggml_backend_sycl_buffer_type_get_name, /* .alloc_buffer = */ ggml_backend_sycl_buffer_type_alloc_buffer, /* .get_alignment = */ ggml_backend_sycl_buffer_type_get_alignment, /* .get_max_size = */ ggml_backend_sycl_buffer_type_get_max_size, /* .get_alloc_size = */ ggml_backend_sycl_buffer_type_get_alloc_size, /* .is_host = */ NULL, }; ggml_backend_buffer_type_t ggml_backend_sycl_buffer_type(int device) { static std::mutex mutex; std::lock_guard lock(mutex); auto dev_count = ggml_backend_sycl_get_device_count(); if (device>=dev_count or device<0) { GGML_LOG_ERROR("ggml_backend_sycl_buffer_type error: device_index:%d is out of range [0, %d], miss to call ggml_backend_sycl_set_single_device()\n", device, dev_count-1); GGML_ASSERT(devicedevice; if (device>=ggml_sycl_info().device_count or device<0) { GGML_LOG_ERROR("ggml_backend_sycl_buffer_type error: device_index:%d is out of range [0, %d], miss to call ggml_backend_sycl_set_single_device()\n", device, ggml_sycl_info().device_count-1); GGML_ASSERT(devicestream(i, 0)}, }; } ggml_backend_sycl_buffer_type_initialized = true; } return &ggml_backend_sycl_buffer_types[device]; } // sycl split buffer static int64_t get_row_rounding(ggml_type type, const std::array & tensor_split) { int64_t min_compute_capability = INT_MAX; int64_t max_compute_capability = INT_MIN; for (int i = 0; i < ggml_sycl_info().device_count; ++i) { if (tensor_split[i] < (i + 1 < ggml_sycl_info().device_count ? tensor_split[i + 1] : 1.0f)) { if (min_compute_capability > ggml_sycl_info().devices[i].cc) { min_compute_capability = ggml_sycl_info().devices[i].cc; } if (max_compute_capability < ggml_sycl_info().devices[i].cc) { max_compute_capability = ggml_sycl_info().devices[i].cc; } } } switch(type) { case GGML_TYPE_Q1_0: case GGML_TYPE_Q4_0: case GGML_TYPE_Q4_1: return max_compute_capability >= VER_GEN9 ? 128 : 64; case GGML_TYPE_Q5_0: case GGML_TYPE_Q5_1: case GGML_TYPE_Q8_0: return 64; case GGML_TYPE_F16: case GGML_TYPE_F32: return 1; case GGML_TYPE_Q2_K: case GGML_TYPE_Q3_K: case GGML_TYPE_Q4_K: case GGML_TYPE_Q5_K: case GGML_TYPE_IQ2_XXS: case GGML_TYPE_IQ2_XS: case GGML_TYPE_IQ2_S: case GGML_TYPE_IQ1_S: case GGML_TYPE_IQ1_M: case GGML_TYPE_IQ3_XXS: case GGML_TYPE_IQ4_XS: case GGML_TYPE_IQ4_NL: return max_compute_capability >= VER_GEN9 ? 128 : 64; case GGML_TYPE_IQ3_S: return max_compute_capability >= VER_GEN9 ? 128 : 64; case GGML_TYPE_Q6_K: return 64; default: GGML_ABORT("fatal error"); } } static void get_row_split(int64_t * row_low, int64_t * row_high, const ggml_tensor * tensor, const std::array & tensor_split, int id) { const int64_t nrows = ggml_nrows(tensor); const int64_t rounding = get_row_rounding(tensor->type, tensor_split); *row_low = id == 0 ? 0 : nrows*tensor_split[id]; *row_low -= *row_low % rounding; if (id == ggml_sycl_info().device_count - 1) { *row_high = nrows; } else { *row_high = nrows*tensor_split[id + 1]; *row_high -= *row_high % rounding; } } static size_t ggml_nbytes_split(const struct ggml_tensor * tensor, int nrows_split) { static_assert(GGML_MAX_DIMS == 4, "GGML_MAX_DIMS is not 4 - update this function"); return nrows_split*ggml_row_size(tensor->type, tensor->ne[0]); } struct ggml_backend_sycl_split_buffer_type_context { std::array tensor_split; }; struct ggml_backend_sycl_split_buffer_context { ~ggml_backend_sycl_split_buffer_context() try { for (ggml_tensor_extra_gpu * extra : tensor_extras) { release_extra_gpu(extra, streams); } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } std::vector tensor_extras; std::vector streams; }; static void ggml_backend_sycl_split_buffer_free_buffer(ggml_backend_buffer_t buffer) { ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context; delete ctx; } static void * ggml_backend_sycl_split_buffer_get_base(ggml_backend_buffer_t buffer) { // the pointers are stored in the tensor extras, this is just a dummy address and never dereferenced return (void *)0x1000; GGML_UNUSED(buffer); } static enum ggml_status ggml_backend_sycl_split_buffer_init_tensor(ggml_backend_buffer_t buffer, ggml_tensor *tensor) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor, "\n").c_str()); GGML_ASSERT(tensor->view_src == nullptr); // views of split tensors are not supported ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context; ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context; const int64_t ne0 = tensor->ne[0]; ggml_tensor_extra_gpu * extra = new ggml_tensor_extra_gpu{}; ctx->tensor_extras.push_back(extra); ctx->streams.push_back(&(dpct::get_current_device().default_queue())); for (int i = 0; i < ggml_sycl_info().device_count; ++i) { int64_t row_low, row_high; get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i); int64_t nrows_split = row_high - row_low; if (nrows_split == 0) { continue; } size_t size = ggml_nbytes_split(tensor, nrows_split); const size_t original_size = size; // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses if (ne0 % MATRIX_ROW_PADDING != 0) { size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING); } ggml_sycl_set_device(i); const queue_ptr stream = ctx->streams[i]; char * buf; SYCL_CHECK(CHECK_TRY_ERROR(buf = (char *)ggml_sycl_malloc_device(size, *stream))); if (!buf) { char err_buf[1024]; snprintf(err_buf, 1023, "%s: can't allocate %lu Bytes of memory on device\n", __func__, size); throw std::runtime_error(err_buf); } // set padding to 0 to avoid possible NaN values if (size > original_size) { /* DPCT1009:209: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ SYCL_CHECK(CHECK_TRY_ERROR( (*stream) .memset(buf + original_size, 0, size - original_size) .wait())); } extra->data_device[i] = buf; for (int64_t is = 0; is < GGML_SYCL_MAX_STREAMS; ++is) { /* DPCT1009:210: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ SYCL_CHECK( CHECK_TRY_ERROR(extra->events[i][is] = new sycl::event())); } } tensor->extra = extra; return GGML_STATUS_SUCCESS; } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_split_buffer_set_tensor(ggml_backend_buffer_t buffer, ggml_tensor *tensor, const void *data, size_t offset, size_t size) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str()); GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset); // split tensors must always be set in their entirety at once GGML_ASSERT(offset == 0); GGML_ASSERT(size == ggml_nbytes(tensor)); ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context; ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context; const int64_t ne0 = tensor->ne[0]; const size_t nb1 = tensor->nb[1]; ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *)tensor->extra; for (int i = 0; i < ggml_sycl_info().device_count; ++i) { int64_t row_low, row_high; get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i); int64_t nrows_split = row_high - row_low; if (nrows_split == 0) { continue; } const size_t offset_split = row_low*nb1; size_t size = ggml_nbytes_split(tensor, nrows_split); const size_t original_size = size; // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses if (ne0 % MATRIX_ROW_PADDING != 0) { size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING); } const char * buf_host = (const char *)data + offset_split; /* DPCT1009:211: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ ggml_sycl_set_device(i); const queue_ptr stream = ctx->streams[i]; SYCL_CHECK(CHECK_TRY_ERROR( (*stream) .memcpy(extra->data_device[i], buf_host, original_size) .wait())); } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_split_buffer_get_tensor(ggml_backend_buffer_t buffer, const ggml_tensor *tensor, void *data, size_t offset, size_t size) try { GGML_SYCL_DEBUG("[SYCL] call %s", __func__); GGML_SYCL_DEBUG("%s", debug_get_tensor_str(": tensor", tensor).c_str()); GGML_SYCL_DEBUG(" size=%zu offset=%zu\n", size, offset); // split tensors must always be set in their entirety at once GGML_ASSERT(offset == 0); GGML_ASSERT(size == ggml_nbytes(tensor)); ggml_backend_sycl_split_buffer_context * ctx = (ggml_backend_sycl_split_buffer_context *)buffer->context; ggml_backend_sycl_split_buffer_type_context * buft_ctx = (ggml_backend_sycl_split_buffer_type_context *)buffer->buft->context; const int64_t ne0 = tensor->ne[0]; const size_t nb1 = tensor->nb[1]; ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *)tensor->extra; for (int i = 0; i < ggml_sycl_info().device_count; ++i) { int64_t row_low, row_high; get_row_split(&row_low, &row_high, tensor, buft_ctx->tensor_split, i); int64_t nrows_split = row_high - row_low; if (nrows_split == 0) { continue; } const size_t offset_split = row_low*nb1; size_t size = ggml_nbytes_split(tensor, nrows_split); const size_t original_size = size; // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses if (ne0 % MATRIX_ROW_PADDING != 0) { size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING); } char * buf_host = (char *)data + offset_split; /* DPCT1009:212: SYCL uses exceptions to report errors and does not use the error codes. The original code was commented out and a warning string was inserted. You need to rewrite this code. */ ggml_sycl_set_device(i); const queue_ptr stream = ctx->streams[i]; SYCL_CHECK(CHECK_TRY_ERROR( (*stream) .memcpy(buf_host, extra->data_device[i], original_size) .wait())); } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } static void ggml_backend_sycl_split_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) { GGML_UNUSED(buffer); GGML_UNUSED(value); } static struct ggml_backend_buffer_i ggml_backend_sycl_split_buffer_interface = { /* .free_buffer = */ ggml_backend_sycl_split_buffer_free_buffer, /* .get_base = */ ggml_backend_sycl_split_buffer_get_base, /* .init_tensor = */ ggml_backend_sycl_split_buffer_init_tensor, /* .memset_tensor = */ NULL, /* .set_tensor = */ ggml_backend_sycl_split_buffer_set_tensor, /* .get_tensor = */ ggml_backend_sycl_split_buffer_get_tensor, /* .set_tensor_2d = */ NULL, /* .get_tensor_2d = */ NULL, /* .cpy_tensor = */ NULL, /* .clear = */ ggml_backend_sycl_split_buffer_clear, /* .reset = */ NULL, }; // sycl split buffer type static const char * ggml_backend_sycl_split_buffer_type_get_name(ggml_backend_buffer_type_t buft) { return GGML_SYCL_NAME "_Split"; GGML_UNUSED(buft); } static bool ggml_backend_buffer_is_sycl_split(ggml_backend_buffer_t buffer) { return buffer->buft->iface.get_name == ggml_backend_sycl_split_buffer_type_get_name; } static ggml_backend_buffer_t ggml_backend_sycl_split_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) { // since we don't know the exact split after rounding, we cannot allocate the device buffers at this point // instead, we allocate them for each tensor separately in init_tensor // however, the size still represents the maximum cumulative size of all the device buffers after the tensors are allocated, // as returned by get_alloc_size. this limit is enforced during tensor allocation by ggml-alloc, so it must be correct. ggml_backend_sycl_split_buffer_context * ctx = new ggml_backend_sycl_split_buffer_context(); return ggml_backend_buffer_init(buft, ggml_backend_sycl_split_buffer_interface, ctx, size); } static size_t ggml_backend_sycl_split_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) { return SYCL_BUFFER_ALIGNMENT; GGML_UNUSED(buft); } static size_t ggml_backend_sycl_split_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const ggml_tensor * tensor) { ggml_backend_sycl_split_buffer_type_context * ctx = (ggml_backend_sycl_split_buffer_type_context *)buft->context; size_t total_size = 0; const int64_t ne0 = tensor->ne[0]; for (int i = 0; i < ggml_sycl_info().device_count; ++i) { int64_t row_low, row_high; get_row_split(&row_low, &row_high, tensor, ctx->tensor_split, i); int64_t nrows_split = row_high - row_low; if (nrows_split == 0) { continue; } total_size += ggml_nbytes_split(tensor, nrows_split); // pad last row to a multiple of 512 elements to avoid out-of-bounds memory accesses if (ne0 % MATRIX_ROW_PADDING != 0) { total_size += ggml_row_size(tensor->type, MATRIX_ROW_PADDING - ne0 % MATRIX_ROW_PADDING); } } return total_size; } static bool ggml_backend_sycl_split_buffer_type_is_host(ggml_backend_buffer_type_t buft) { return false; GGML_UNUSED(buft); } static ggml_backend_buffer_type_i ggml_backend_sycl_split_buffer_type_interface = { /* .get_name = */ ggml_backend_sycl_split_buffer_type_get_name, /* .alloc_buffer = */ ggml_backend_sycl_split_buffer_type_alloc_buffer, /* .get_alignment = */ ggml_backend_sycl_split_buffer_type_get_alignment, /* .get_max_size = */ NULL, // defaults to SIZE_MAX /* .get_alloc_size = */ ggml_backend_sycl_split_buffer_type_get_alloc_size, /* .is_host = */ ggml_backend_sycl_split_buffer_type_is_host, }; ggml_backend_buffer_type_t ggml_backend_sycl_split_buffer_type(const float * tensor_split) { static std::mutex mutex; std::lock_guard lock(mutex); GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_split_buffer_type\n"); ggml_check_sycl(); // FIXME: this is not thread safe static std::map, struct ggml_backend_buffer_type> buft_map; std::array tensor_split_arr = {}; bool all_zero = tensor_split == nullptr || std::all_of(tensor_split, tensor_split + GGML_SYCL_MAX_DEVICES, [](float x) { return x == 0.0f; }); if (all_zero) { tensor_split_arr = ggml_sycl_info().default_tensor_split; } else { float split_sum = 0.0f; for (int i = 0; i < ggml_sycl_info().device_count; ++i) { tensor_split_arr[i] = split_sum; split_sum += tensor_split[i]; } for (int i = 0; i < ggml_sycl_info().device_count; ++i) { tensor_split_arr[i] /= split_sum; } } auto it = buft_map.find(tensor_split_arr); if (it != buft_map.end()) { return &it->second; } struct ggml_backend_buffer_type buft { /* .iface = */ ggml_backend_sycl_split_buffer_type_interface, /* .device = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), 0), /* .context = */ new ggml_backend_sycl_split_buffer_type_context{tensor_split_arr}, }; auto result = buft_map.emplace(tensor_split_arr, buft); return &result.first->second; } // host buffer type static const char * ggml_backend_sycl_host_buffer_type_name(ggml_backend_buffer_type_t buft) { return GGML_SYCL_NAME "_Host"; GGML_UNUSED(buft); } static void ggml_backend_sycl_host_buffer_free_buffer(ggml_backend_buffer_t buffer) { free_aligned_mem_host((void *)buffer->context); } static ggml_backend_buffer_t ggml_backend_sycl_host_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) { void * ptr = aligned_malloc_host(TENSOR_ALIGNMENT, size); if (ptr == nullptr) { // fallback to cpu buffer return ggml_backend_buft_alloc_buffer(ggml_backend_cpu_buffer_type(), size); } // FIXME: this is a hack to avoid having to implement a new buffer type ggml_backend_buffer_t buffer = ggml_backend_cpu_buffer_from_ptr(ptr, size); buffer->buft = buft; buffer->iface.free_buffer = ggml_backend_sycl_host_buffer_free_buffer; return buffer; } ggml_backend_buffer_type_t ggml_backend_sycl_host_buffer_type() { GGML_SYCL_DEBUG("[SYCL] call ggml_backend_sycl_host_buffer_type\n"); static struct ggml_backend_buffer_type ggml_backend_sycl_buffer_type_host = { /* .iface = */ { /* .get_name = */ ggml_backend_sycl_host_buffer_type_name, /* .alloc_buffer = */ ggml_backend_sycl_host_buffer_type_alloc_buffer, /* .get_alignment = */ ggml_backend_cpu_buffer_type()->iface.get_alignment, /* .get_max_size = */ NULL, // TODO: return device.maxBufferLength /* .get_alloc_size = */ ggml_backend_cpu_buffer_type()->iface.get_alloc_size, /* .is_host = */ ggml_backend_cpu_buffer_type()->iface.is_host, }, /* .device = */ ggml_backend_reg_dev_get(ggml_backend_sycl_reg(), 0), /* .context = */ nullptr, }; return &ggml_backend_sycl_buffer_type_host; } // buffer pool for sycl (legacy) struct ggml_sycl_pool_leg : public ggml_sycl_pool { static const int MAX_SYCL_BUFFERS = 256; int device; queue_ptr qptr; struct ggml_sycl_buffer { void * ptr = nullptr; size_t size = 0; }; ggml_sycl_buffer buffer_pool[MAX_SYCL_BUFFERS] = {}; size_t pool_size = 0; explicit ggml_sycl_pool_leg(queue_ptr qptr_, int device_) : device(device_), qptr(qptr_) {} ~ggml_sycl_pool_leg() { #ifdef DEBUG_SYCL_POOL int n_cached = 0; size_t bytes_cached = 0; for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) { if (buffer_pool[i].ptr != nullptr) { ++n_cached; bytes_cached += buffer_pool[i].size; } } GGML_LOG_INFO("%s: %d buffers, cached = %.2f MiB\n", __func__, n_cached, bytes_cached / 1024.0 / 1024.0); const auto slots = format_slots_in_alloc_order(); if (!slots.empty()) { GGML_LOG_INFO("%s: slots MiB: %s\n", __func__, slots.c_str()); } #endif for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) { ggml_sycl_buffer & b = buffer_pool[i]; if (b.ptr != nullptr) { SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(b.ptr, *qptr))); pool_size -= b.size; } } GGML_ASSERT(pool_size == 0); } #ifdef DEBUG_SYCL_POOL std::string format_slots_in_alloc_order() const { std::string line; char buf[32]; bool first = true; for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) { if (buffer_pool[i].ptr == nullptr) { continue; } if (!first) { line += '/'; } first = false; snprintf(buf, sizeof(buf), "%.2f", buffer_pool[i].size / 1024.0 / 1024.0); line += buf; } return line; } #endif void * alloc(size_t size, size_t * actual_size) override { #ifdef DEBUG_sycl_MALLOC int nnz = 0; size_t max_size = 0; #endif size_t best_diff = 1ull << 36; int ibest = -1; for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) { ggml_sycl_buffer& b = buffer_pool[i]; if (b.ptr != nullptr) { #ifdef DEBUG_sycl_MALLOC ++nnz; if (b.size > max_size) max_size = b.size; #endif if (b.size >= size) { size_t diff = b.size - size; if (diff < best_diff) { best_diff = diff; ibest = i; if (!best_diff) { void * ptr = b.ptr; *actual_size = b.size; b.ptr = nullptr; b.size = 0; return ptr; } } } } } if (ibest >= 0) { ggml_sycl_buffer& b = buffer_pool[ibest]; void * ptr = b.ptr; *actual_size = b.size; b.ptr = nullptr; b.size = 0; return ptr; } void * ptr; size_t look_ahead_size = (size_t) (1.05 * size); SYCL_CHECK(CHECK_TRY_ERROR(ptr = (void *)ggml_sycl_malloc_device(look_ahead_size, *qptr))); if (!ptr) { GGML_LOG_ERROR("%s: can't allocate %lu Bytes of memory on device/GPU\n", __func__, look_ahead_size); return nullptr; } *actual_size = look_ahead_size; pool_size += look_ahead_size; #ifdef DEBUG_SYCL_MALLOC GGML_LOG_DEBUG("%s[%d]: %d buffers, max_size = %u MB, pool_size = %u MB, requested %u MB\n", __func__, id, nnz, (uint32_t)(max_size/1024/1024), (uint32_t)(g_sycl_pool_size[id]/1024/1024), (uint32_t)(size/1024/1024)); #endif // GGML_SYCL_DEBUG("ggml_sycl_pool_malloc_leg look_ahead_size=%lu, return %p\n", look_ahead_size, ptr); return ptr; } void free(void * ptr, size_t size) override { for (int i = 0; i < MAX_SYCL_BUFFERS; ++i) { ggml_sycl_buffer& b = buffer_pool[i]; if (b.ptr == nullptr) { b.ptr = ptr; b.size = size; return; } } GGML_LOG_WARN("WARNING: sycl buffer pool full, increase MAX_sycl_BUFFERS\n"); SYCL_CHECK(CHECK_TRY_ERROR(ggml_sycl_free_device(ptr, *qptr))); pool_size -= size; } }; // pool with virtual memory management #if defined(GGML_SYCL_SUPPORT_VMM) struct ggml_sycl_pool_vmm : public ggml_sycl_pool { static const size_t SYCL_POOL_VMM_MAX_SIZE = 1ull << 35; // 32 GB int device; sycl::context ctx; sycl::device dev; uintptr_t pool_addr = 0; size_t pool_used = 0; size_t pool_size = 0; size_t granularity; // physical_mem owns the commits (unlike cuMemMap) struct mapping { sycl::ext::oneapi::experimental::physical_mem phys; void * map_ptr; }; std::vector mappings; explicit ggml_sycl_pool_vmm(queue_ptr qptr_, int device_) : device(device_), ctx(qptr_->get_context()), dev(qptr_->get_device()), granularity(ggml_sycl_info().devices[device_].vmm_granularity) { } ~ggml_sycl_pool_vmm() { if (pool_addr == 0) { return; } // Per spec, unmap must (a) match the exact (ptr, size) of an earlier // physical_mem::map() call and (b) precede destruction of the // physical_mem objects (their dtors won't unmap). for (auto & m : mappings) { SYCL_CHECK(CHECK_TRY_ERROR(sycl::ext::oneapi::experimental::unmap( m.map_ptr, m.phys.size(), ctx))); } SYCL_CHECK(CHECK_TRY_ERROR(sycl::ext::oneapi::experimental::free_virtual_mem( pool_addr, SYCL_POOL_VMM_MAX_SIZE, ctx))); } void * alloc(size_t size, size_t * actual_size) override { // round up the allocation size to the alignment to ensure that all allocations are aligned for all data types size = GGML_PAD(size, SYCL_BUFFER_ALIGNMENT); size_t avail = pool_size - pool_used; if (size > avail) { // round up to the next multiple of the granularity size_t reserve_size = GGML_PAD(size - avail, granularity); GGML_ASSERT(pool_size + reserve_size <= SYCL_POOL_VMM_MAX_SIZE); // allocate more physical memory std::optional phys; SYCL_CHECK(CHECK_TRY_ERROR(phys.emplace(dev, ctx, reserve_size))); // reserve virtual address space (if not already reserved) if (pool_addr == 0) { SYCL_CHECK(CHECK_TRY_ERROR( pool_addr = sycl::ext::oneapi::experimental::reserve_virtual_mem( SYCL_POOL_VMM_MAX_SIZE, ctx))); } // map at the end of the pool void * map_ptr = nullptr; SYCL_CHECK(CHECK_TRY_ERROR( map_ptr = phys->map(pool_addr + pool_size, reserve_size, sycl::ext::oneapi::experimental::address_access_mode::read_write))); // stash these so we could unmap this exact range in dtor mappings.push_back({ std::move(*phys), map_ptr, }); // add to the pool pool_size += reserve_size; #ifdef DEBUG_SYCL_MALLOC GGML_LOG_INFO("sycl pool[%d]: size increased to %llu MB (reserved %llu MB)\n", device, (unsigned long long) (pool_size/1024/1024), (unsigned long long) (reserve_size/1024/1024)); #endif } GGML_ASSERT(pool_addr != 0); void * ptr = reinterpret_cast(pool_addr + pool_used); *actual_size = size; pool_used += size; #ifdef DEBUG_SYCL_MALLOC GGML_LOG_INFO("sycl pool[%d]: allocated %llu bytes at %p\n", device, (unsigned long long) size, ptr); #endif return ptr; } void free(void * ptr, size_t size) override { #ifdef DEBUG_SYCL_MALLOC GGML_LOG_INFO("sycl pool[%d]: freed %llu bytes at %p\n", device, (unsigned long long) size, ptr); #endif pool_used -= size; // all deallocations must be in reverse order of the allocations GGML_ASSERT(ptr == reinterpret_cast(pool_addr + pool_used)); } }; #endif // defined(GGML_SYCL_SUPPORT_VMM) struct ggml_sycl_pool_host : public ggml_sycl_pool { queue_ptr qptr; int device; inline static int counter{ 0 }; struct ggml_sycl_buffer { void * ptr = nullptr; size_t size = 0; }; // Set arbitrarly to 64 static constexpr int MAX_POOL_SIZE{ 64 }; std::vector buffer_pool = std::vector(MAX_POOL_SIZE); size_t pool_size = 0; explicit ggml_sycl_pool_host(queue_ptr qptr_, int device_) : qptr(qptr_), device(device_) {} ~ggml_sycl_pool_host() { for (int i = 0; i < MAX_POOL_SIZE; ++i) { ggml_sycl_buffer & b = buffer_pool[i]; if (b.ptr != nullptr) { SYCL_CHECK(CHECK_TRY_ERROR(sycl::free(b.ptr, *qptr))); b.ptr = nullptr; pool_size -= b.size; b.size = 0; } } counter = 0; } void * alloc(size_t size, size_t * actual_size) override { if (counter == MAX_POOL_SIZE) { ggml_sycl_buffer b = buffer_pool[0]; void * ptr = b.ptr; *actual_size = b.size; counter = 1; return ptr; } ggml_sycl_buffer & b = buffer_pool[counter]; if (b.ptr == nullptr) { void * ptr; SYCL_CHECK(CHECK_TRY_ERROR(ptr = (void *) sycl::malloc_host(size, *qptr))); if (!ptr) { GGML_LOG_ERROR("%s: can't allocate %lu Bytes of memory on host\n", __func__, size); return nullptr; } pool_size += size; *actual_size = size; counter = counter + 1; return ptr; } else { ++counter; b.size = size; return b.ptr; } } void free(void * ptr, size_t size) override { // if the pool is not completed add the pointer to it in place of the first nullptr found. // Otherwise do nothing, pointers will be freed once the pool is deallocated. for (int i = 0; i < MAX_POOL_SIZE; ++i) { ggml_sycl_buffer & b = buffer_pool[i]; if (b.ptr == nullptr) { b.ptr = ptr; b.size = size; return; } } } }; std::unique_ptr ggml_backend_sycl_context::new_pool_for_host(queue_ptr qptr, int device) { // return pool for the host to speed up memory management return std::unique_ptr(new ggml_sycl_pool_host(qptr, device)); } std::unique_ptr ggml_backend_sycl_context::new_pool_for_device(queue_ptr qptr, int device) { #if defined(GGML_SYCL_SUPPORT_VMM) if (g_ggml_sycl_enable_vmm && ggml_sycl_info().devices[device].vmm) { return std::unique_ptr(new ggml_sycl_pool_vmm(qptr, device)); } #endif // defined(GGML_SYCL_SUPPORT_VMM) return std::unique_ptr(new ggml_sycl_pool_leg(qptr, device)); } std::unique_ptr ggml_backend_sycl_context::new_fattn_kv_buffers(queue_ptr qptr, int device) { return std::unique_ptr(new ggml_sycl_fattn_kv_buffers(qptr, device)); } /// kernels typedef void (*ggml_sycl_op_mul_mat_t)( ggml_backend_sycl_context & ctx, const ggml_tensor *src0, const ggml_tensor *src1, ggml_tensor *dst, const char *src0_dd_i, const float *src1_ddf_i, const char *src1_ddq_i, float *dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, const int64_t src1_padded_row_size, const queue_ptr &stream); static void mul_mat_p021_f16_f32( const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst, const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y, const sycl::nd_item<3> &item_ct1) { const sycl::half *x = (const sycl::half *)vx; const int row_x = item_ct1.get_local_range(1) * item_ct1.get_group(1) + item_ct1.get_local_id(1); const int channel = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0); const int channel_x = channel / (nchannels_y / nchannels_x); const int nrows_y = ncols_x; const int nrows_dst = nrows_x; const int row_dst = row_x; float tmp = 0.0f; for (int col_x0 = 0; col_x0 < ncols_x; col_x0 += item_ct1.get_local_range(2)) { const int col_x = col_x0 + item_ct1.get_local_id(2); if (col_x >= ncols_x) { break; } // x is transposed and permuted const int ix = row_x*nchannels_x*ncols_x + channel_x*ncols_x + col_x; const float xi = sycl::vec(x[ix]) .convert()[0]; const int row_y = col_x; // y is not transposed but permuted const int iy = channel*nrows_y + row_y; tmp += xi * y[iy]; } // dst is not transposed and not permuted const int idst = channel*nrows_dst + row_dst; // sum up partial sums and write back result #pragma unroll for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) { tmp += dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), tmp, mask); } if (item_ct1.get_local_id(2) == 0) { dst[idst] = tmp; } } static void mul_mat_vec_nc_f16_f32( // nc == non-contiguous const void * __restrict__ vx, const float * __restrict__ y, float * __restrict__ dst, const int ncols_x, const int nrows_x, const int row_stride_x, const int channel_stride_x,const int channel_stride_y, const int channel_x_divisor, const sycl::nd_item<3> &item_ct1) { const sycl::half *x = (const sycl::half *)vx; const int row_x = item_ct1.get_local_range(1) * item_ct1.get_group(1) + item_ct1.get_local_id(1); const int channel = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0); const int channel_x = channel / channel_x_divisor; const int nrows_dst = nrows_x; const int row_dst = row_x; const int idst = channel*nrows_dst + row_dst; float tmp = 0.0f; for (int col_x0 = 0; col_x0 < ncols_x; col_x0 += item_ct1.get_local_range(2)) { const int col_x = col_x0 + item_ct1.get_local_id(2); if (col_x >= ncols_x) { break; } const int row_y = col_x; const int ix = channel_x*channel_stride_x + row_x*row_stride_x + col_x; const int iy = channel * channel_stride_y + row_y; const float xi = sycl::vec(x[ix]) .convert()[0]; tmp += xi * y[iy]; } // sum up partial sums and write back result #pragma unroll for (int mask = WARP_SIZE / 2; mask > 0; mask >>= 1) { tmp += dpct::permute_sub_group_by_xor(item_ct1.get_sub_group(), tmp, mask); } if (item_ct1.get_local_id(2) == 0) { dst[idst] = tmp; } } static void k_sum_rows_f32(const float * x, float * dst, const int ncols, const sycl::nd_item<3> &item_ct1) { const int row = item_ct1.get_group(1); const int col = item_ct1.get_local_id(2); float sum = 0.0f; for (int i = col; i < ncols; i += item_ct1.get_local_range(2)) { sum += x[row * ncols + i]; } sum = warp_reduce_sum(sum, item_ct1); if (col == 0) { dst[row] = sum; } } template static inline void ggml_sycl_swap(T & a, T & b) { T tmp = a; a = b; b = tmp; } template __dpct_inline__ static void k_argsort_f32_i32(const float *x, int *dst, const int ncols, int ncols_pad, const int tasks_per_thread, const sycl::nd_item<3> &item_ct1, uint8_t *dpct_local) { // bitonic sort int col_index = item_ct1.get_local_id(2); int row = item_ct1.get_group(1); for (int i = 0; i < tasks_per_thread; i++) { int col = col_index * tasks_per_thread + i; if (col >= ncols_pad) { return; } } const float * x_row = x + row * ncols; auto dst_row = (int *)dpct_local; // initialize indices for (int i=0;i 0; j /= 2) { for (int i = 0; i < tasks_per_thread; i++) { int col = col_index * tasks_per_thread + i; int ixj = col ^ j; if (ixj > col) { if ((col & k) == 0) { if (dst_row[col] >= ncols || (dst_row[ixj] < ncols && (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] > x_row[dst_row[ixj]] : x_row[dst_row[col]] < x_row[dst_row[ixj]]))) { ggml_sycl_swap(dst_row[col], dst_row[ixj]); } } else { if (dst_row[ixj] >= ncols || (dst_row[col] < ncols && (order == GGML_SORT_ORDER_ASC ? x_row[dst_row[col]] < x_row[dst_row[ixj]] : x_row[dst_row[col]] > x_row[dst_row[ixj]]))) { ggml_sycl_swap(dst_row[col], dst_row[ixj]); } } } item_ct1.barrier(sycl::access::fence_space::local_space); } } } // copy the result to dst without the padding for (int i = 0; i < tasks_per_thread; i++) { int col = col_index * tasks_per_thread + i; if (col < ncols) { dst[row * ncols + col] = dst_row[col]; } } } static void diag_mask_inf_f32(const float * x, float * dst, const int ncols, const int rows_per_channel, const int n_past, const sycl::nd_item<3> &item_ct1) { const int col = item_ct1.get_local_range(1) * item_ct1.get_group(1) + item_ct1.get_local_id(1); const int row = item_ct1.get_local_range(2) * item_ct1.get_group(2) + item_ct1.get_local_id(2); if (col >= ncols) { return; } const int i = row*ncols + col; //dst[i] = col > (n_past + row % rows_per_channel) ? -INFINITY : x[i]; //dst[i] = x[i] - (col > n_past + row % rows_per_channel) * INT_MAX; // equivalent within rounding error but slightly faster on GPU dst[i] = x[i] - (col > n_past + row % rows_per_channel) * FLT_MAX; } static void scale_f32(const float * x, float * dst, const float scale, const float bias, const int k, const sycl::nd_item<3> &item_ct1) { const int i = item_ct1.get_local_range(2) * item_ct1.get_group(2) + item_ct1.get_local_id(2); if (i >= k) { return; } dst[i] = scale * x[i] + bias; } static void ggml_mul_mat_p021_f16_f32_sycl(const void *vx, const float *y, float *dst, const int ncols_x, const int nrows_x, const int nchannels_x, const int nchannels_y, queue_ptr stream) { const sycl::range<3> block_nums(nchannels_y, nrows_x, 1); const sycl::range<3> block_dims(1, 1, WARP_SIZE); { dpct::has_capability_or_fail(stream->get_device(), {sycl::aspect::fp16}); stream->parallel_for( sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { mul_mat_p021_f16_f32(vx, y, dst, ncols_x, nrows_x, nchannels_x, nchannels_y, item_ct1); }); } } static void ggml_mul_mat_vec_nc_f16_f32_sycl( const void *vx, const float *y, float *dst, const int ncols_x, const int nrows_x, const int row_stride_x, const int nchannels_x, const int nchannels_y, const int channel_stride_x, const int channel_stride_y, queue_ptr stream) { const sycl::range<3> block_nums(nchannels_y, nrows_x, 1); const sycl::range<3> block_dims(1, 1, WARP_SIZE); { dpct::has_capability_or_fail(stream->get_device(), {sycl::aspect::fp16}); stream->parallel_for( sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { mul_mat_vec_nc_f16_f32(vx, y, dst, ncols_x, nrows_x, row_stride_x, channel_stride_x, channel_stride_y, nchannels_y / nchannels_x, item_ct1); }); } } static void scale_f32_sycl(const float *x, float *dst, const float scale, const float bias, const int k, queue_ptr stream) { const int num_blocks = (k + SYCL_SCALE_BLOCK_SIZE - 1) / SYCL_SCALE_BLOCK_SIZE; stream->parallel_for( sycl::nd_range<3>(sycl::range<3>(1, 1, num_blocks) * sycl::range<3>(1, 1, SYCL_SCALE_BLOCK_SIZE), sycl::range<3>(1, 1, SYCL_SCALE_BLOCK_SIZE)), [=](sycl::nd_item<3> item_ct1) { scale_f32(x, dst, scale, bias, k, item_ct1); }); } static void sum_rows_f32_sycl(const float *x, float *dst, const int ncols, const int nrows, queue_ptr stream) { const sycl::range<3> block_dims(1, 1, WARP_SIZE); const sycl::range<3> block_nums(1, nrows, 1); stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { k_sum_rows_f32(x, dst, ncols, item_ct1); }); } static int next_power_of_2(int x) { int n = 1; while (n < x) { n *= 2; } return n; } static void init_argsort_indices_padded( int * idx, const int nrows, const int ncols_pad, const sycl::nd_item<1> & item_ct1) { const size_t gid = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0); const size_t total = (size_t) nrows * (size_t) ncols_pad; if (gid >= total) { return; } idx[gid] = (int) (gid % (size_t) ncols_pad); } template static void argsort_f32_i32_global_pass(const float * x, int * idx, const int ncols, const int nrows, const int ncols_pad, const int j, const int k, const sycl::nd_item<1> & item_ct1) { const size_t gid = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0); const size_t total = (size_t) nrows * (size_t) ncols_pad; if (gid >= total) { return; } const int row = (int) (gid / (size_t) ncols_pad); const int col = (int) (gid % (size_t) ncols_pad); const int ixj = col ^ j; if (ixj <= col || ixj >= ncols_pad) { return; } const size_t base = (size_t) row * (size_t) ncols_pad; const size_t pos_a = base + (size_t) col; const size_t pos_b = base + (size_t) ixj; const int a = idx[pos_a]; const int b = idx[pos_b]; bool do_swap = false; if ((col & k) == 0) { if (a >= ncols || (b < ncols && (order == GGML_SORT_ORDER_ASC ? x[(size_t) row * (size_t) ncols + (size_t) a] > x[(size_t) row * (size_t) ncols + (size_t) b] : x[(size_t) row * (size_t) ncols + (size_t) a] < x[(size_t) row * (size_t) ncols + (size_t) b]))) { do_swap = true; } } else { if (b >= ncols || (a < ncols && (order == GGML_SORT_ORDER_ASC ? x[(size_t) row * (size_t) ncols + (size_t) a] < x[(size_t) row * (size_t) ncols + (size_t) b] : x[(size_t) row * (size_t) ncols + (size_t) a] > x[(size_t) row * (size_t) ncols + (size_t) b]))) { do_swap = true; } } if (do_swap) { idx[pos_a] = b; idx[pos_b] = a; } } static void copy_argsort_indices_unpadded(const int * idx_padded, int * dst, const int nrows, const int ncols, const int ncols_pad, const sycl::nd_item<1> & item_ct1) { const size_t gid = item_ct1.get_local_range(0) * item_ct1.get_group(0) + item_ct1.get_local_id(0); const size_t total = (size_t) nrows * (size_t) ncols; if (gid >= total) { return; } const int row = (int) (gid / (size_t) ncols); const int col = (int) (gid % (size_t) ncols); dst[(size_t) row * (size_t) ncols + (size_t) col] = idx_padded[(size_t) row * (size_t) ncols_pad + (size_t) col]; } static void argsort_f32_i32_sycl(const float *x, int *dst, const int ncols, const int nrows, ggml_sort_order order, queue_ptr stream, int device, ggml_sycl_pool & pool) { // bitonic sort requires ncols to be power of 2 const int ncols_pad = next_power_of_2(ncols); const size_t shared_mem = (size_t) ncols_pad * sizeof(int); const size_t smpbo = ggml_sycl_info().devices[device].smpbo; if (shared_mem > smpbo) { ggml_sycl_pool_alloc idx_padded_alloc(pool, (size_t) nrows * (size_t) ncols_pad); int * idx_padded = idx_padded_alloc.get(); constexpr size_t block_size = 256; const size_t total_padded = (size_t) nrows * (size_t) ncols_pad; const size_t nblocks_padded = (total_padded + block_size - 1) / block_size; stream->parallel_for( sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)), [=](sycl::nd_item<1> item_ct1) { init_argsort_indices_padded(idx_padded, nrows, ncols_pad, item_ct1); }); for (int k = 2; k <= ncols_pad; k *= 2) { for (int j = k / 2; j > 0; j /= 2) { if (order == GGML_SORT_ORDER_ASC) { stream->parallel_for( sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)), [=](sycl::nd_item<1> item_ct1) { argsort_f32_i32_global_pass(x, idx_padded, ncols, nrows, ncols_pad, j, k, item_ct1); }); } else if (order == GGML_SORT_ORDER_DESC) { stream->parallel_for( sycl::nd_range<1>(sycl::range<1>(nblocks_padded * block_size), sycl::range<1>(block_size)), [=](sycl::nd_item<1> item_ct1) { argsort_f32_i32_global_pass(x, idx_padded, ncols, nrows, ncols_pad, j, k, item_ct1); }); } else { GGML_ABORT("invalid sort order"); } } } const size_t total = (size_t) nrows * (size_t) ncols; const size_t nblocks = (total + block_size - 1) / block_size; stream->parallel_for(sycl::nd_range<1>(sycl::range<1>(nblocks * block_size), sycl::range<1>(block_size)), [=](sycl::nd_item<1> item_ct1) { copy_argsort_indices_unpadded(idx_padded, dst, nrows, ncols, ncols_pad, item_ct1); }); return; } int nth = 1; int max_block_size = ggml_sycl_info().max_work_group_sizes[device]; while (nth < ncols_pad && nth < max_block_size) nth *= 2; if (nth > max_block_size) nth = max_block_size; const int tasks_per_thread = ncols_pad / nth; const sycl::range<3> block_dims(1, 1, nth); const sycl::range<3> block_nums(1, nrows, 1); if (order == GGML_SORT_ORDER_ASC) { stream->submit([&](sycl::handler &cgh) { sycl::local_accessor dpct_local_acc_ct1( sycl::range<1>(shared_mem), cgh); cgh.parallel_for( sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) { k_argsort_f32_i32( x, dst, ncols, ncols_pad, tasks_per_thread, item_ct1, dpct_local_acc_ct1 .get_multi_ptr() .get()); }); }); } else if (order == GGML_SORT_ORDER_DESC) { stream->submit([&](sycl::handler &cgh) { sycl::local_accessor dpct_local_acc_ct1( sycl::range<1>(shared_mem), cgh); cgh.parallel_for( sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) { k_argsort_f32_i32( x, dst, ncols, ncols_pad, tasks_per_thread, item_ct1, dpct_local_acc_ct1 .get_multi_ptr() .get()); }); }); } else { GGML_ABORT("fatal error"); } } static void top_k_f32_sycl( const float * src, int32_t * dst_indices, const int64_t ncols, const int64_t nrows, const int k, dpct::queue_ptr main_stream ) { const int block_size = 128; const sycl::range<1> block_dims(block_size); const sycl::range<1> grid_dims(nrows); main_stream->submit([&](sycl::handler &cgh) { sycl::local_accessor shared_vals(sycl::range<1>(block_size * k), cgh); sycl::local_accessor shared_idx(sycl::range<1>(block_size * k), cgh); cgh.parallel_for( sycl::nd_range<1>(grid_dims * block_dims, block_dims), [=](sycl::nd_item<1> item_ct1) { const int row = item_ct1.get_group(0); const int tid = item_ct1.get_local_id(0); if (row >= nrows) return; const float * src_row = src + row * ncols; int32_t * dst_idx_row = dst_indices + row * k; float local_vals[32]; int local_idx[32]; for (int i = 0; i < k; i++) { local_vals[i] = -FLT_MAX; local_idx[i] = -1; } for (int col = tid; col < ncols; col += block_size) { float val = src_row[col]; if (val > local_vals[k-1]) { int pos = k - 1; while (pos > 0 && val > local_vals[pos - 1]) { pos--; } for (int i = k - 1; i > pos; i--) { local_vals[i] = local_vals[i - 1]; local_idx[i] = local_idx[i - 1]; } local_vals[pos] = val; local_idx[pos] = col; } } for (int i = 0; i < k; i++) { shared_vals[tid * k + i] = local_vals[i]; shared_idx[tid * k + i] = local_idx[i]; } item_ct1.barrier(sycl::access::fence_space::local_space); if (tid == 0) { float final_vals[32]; int final_idx[32]; for (int i = 0; i < k; i++) { final_vals[i] = -FLT_MAX; final_idx[i] = -1; } for (int t = 0; t < block_size; t++) { for (int i = 0; i < k; i++) { float val = shared_vals[t * k + i]; int idx = shared_idx[t * k + i]; if (val > final_vals[k-1]) { int pos = k - 1; while (pos > 0 && val > final_vals[pos - 1]) { pos--; } for (int j = k - 1; j > pos; j--) { final_vals[j] = final_vals[j - 1]; final_idx[j] = final_idx[j - 1]; } final_vals[pos] = val; final_idx[pos] = idx; } } } for (int i = 0; i < k; i++) { dst_idx_row[i] = final_idx[i]; } if (k > 1) { int32_t temp = dst_idx_row[0]; dst_idx_row[0] = dst_idx_row[1]; dst_idx_row[1] = temp; } } }); }); } static void argmax_f32_i32_sycl(const float *x, int *dst, const int ncols, const int nrows, queue_ptr stream) { const sycl::range<3> block_dims(1, 1, SYCL_ARGMAX_BLOCK_SIZE); const sycl::range<3> block_nums(1, nrows, 1); const size_t shared_mem = 256 * sizeof(float); stream->submit([&](sycl::handler &cgh) { sycl::local_accessor shared_data( sycl::range<1>(shared_mem/sizeof(float)), cgh); sycl::local_accessor shared_indices( sycl::range<1>(shared_mem/sizeof(float)), cgh); cgh.parallel_for( sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) { const int tid = item_ct1.get_local_id(2); const int row = item_ct1.get_global_id(1); float max_val = -INFINITY; int max_idx = -1; for (int col = tid; col < ncols; col += 256) { float val = x[row * ncols + col]; if (val > max_val) { max_val = val; max_idx = col; } } shared_data[tid] = max_val; shared_indices[tid] = max_idx; item_ct1.barrier(sycl::access::fence_space::local_space); for (int stride = 256/2; stride > 0; stride >>= 1) { if (tid < stride) { float val1 = shared_data[tid]; float val2 = shared_data[tid + stride]; if (val2 > val1) { shared_data[tid] = val2; shared_indices[tid] = shared_indices[tid + stride]; } } item_ct1.barrier(sycl::access::fence_space::local_space); } if (tid == 0) { dst[row] = shared_indices[0]; } }); }); } static void diag_mask_inf_f32_sycl(const float *x, float *dst, const int ncols_x, const int nrows_x, const int rows_per_channel, const int n_past, queue_ptr stream) { const sycl::range<3> block_dims(1, SYCL_DIAG_MASK_INF_BLOCK_SIZE, 1); const int block_num_x = (ncols_x + SYCL_DIAG_MASK_INF_BLOCK_SIZE - 1) / SYCL_DIAG_MASK_INF_BLOCK_SIZE; const sycl::range<3> block_nums(1, block_num_x, nrows_x); stream->parallel_for(sycl::nd_range<3>(block_nums * block_dims, block_dims), [=](sycl::nd_item<3> item_ct1) { diag_mask_inf_f32(x, dst, ncols_x, rows_per_channel, n_past, item_ct1); }); } static dpct::err0 ggml_sycl_cpy_tensor_2d(void *dst, const struct ggml_tensor *src, int64_t i3, int64_t i2, int64_t i1_low, int64_t i1_high, queue_ptr stream) try { dpct::memcpy_direction kind; char * src_ptr; if (ggml_backend_buffer_is_host(src->buffer)) { kind = dpct::host_to_device; //GGML_SYCL_DEBUG("%s: Host buffer type src tensor\n", __func__); src_ptr = (char *) src->data; // GGML_SYCL_DEBUG("ggml_sycl_cpy_tensor_2d GGML_BACKEND_TYPE_CPU src_ptr %p\n", src_ptr); } else if (ggml_backend_buffer_is_sycl(src->buffer)) { // If buffer is a SYCL buffer //GGML_SYCL_DEBUG("%s: SYCL buffer type src tensor\n", __func__); kind = dpct::device_to_device; src_ptr = (char *) src->data; } else if (ggml_backend_buffer_is_sycl_split(src->buffer)) { /* If buffer is a SYCL split buffer */ //GGML_SYCL_DEBUG("%s: Split buffer type src tensor\n", __func__); GGML_ASSERT(i1_low == 0 && i1_high == src->ne[1]); kind = dpct::device_to_device; ggml_tensor_extra_gpu * extra = (ggml_tensor_extra_gpu *) src->extra; int id; SYCL_CHECK(CHECK_TRY_ERROR( id = get_current_device_id())); // GGML_SYCL_DEBUG("current device index %d\n", id); src_ptr = (char *) extra->data_device[id]; } else { // GGML_SYCL_DEBUG("GGML_ABORT("fatal error")\n"); GGML_ABORT("fatal error"); } char * dst_ptr = (char *) dst; GGML_TENSOR_LOCALS_1(int64_t, ne, src, ne); GGML_TENSOR_LOCALS(int64_t, nb, src, nb); const enum ggml_type type = src->type; const int64_t ts = ggml_type_size(type); const int64_t bs = ggml_blck_size(type); int64_t i1_diff = i1_high - i1_low; const char * x = src_ptr + i1_low*nb1 + i2*nb2 + i3*nb3; if (nb0 == ts && nb1 == ts*ne0/bs) { // GGML_SYCL_DEBUG("stream->memcpy: dst_ptr=%p, x=%p, size=%lu\n", dst_ptr, x, i1_diff * nb1); // return CHECK_TRY_ERROR(stream->memcpy(dst_ptr, x, i1_diff * nb1)); return CHECK_TRY_ERROR(dpct::async_dpct_memcpy(dst_ptr, x, i1_diff * nb1, kind, *stream)); } else if (nb0 == ts) { return CHECK_TRY_ERROR( dpct::async_dpct_memcpy(dst_ptr, ts * ne0 / bs, x, nb1, ts * ne0 / bs, i1_diff, kind, *stream)); } else { for (int64_t i1 = 0; i1 < i1_diff; i1++) { const void * rx = (const void *) ((const char *) x + i1*nb1); void * rd = (void *) (dst_ptr + i1*ts*ne0/bs); // pretend the row is a matrix with cols=1 dpct::err0 r = CHECK_TRY_ERROR(dpct::async_dpct_memcpy( rd, ts / bs, rx, nb0, ts / bs, ne0, kind, *stream)); /* DPCT1001:85: The statement could not be removed. */ /* DPCT1000:86: Error handling if-stmt was detected but could not be rewritten. */ if (r != 0) return r; } return 0; } } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } inline void ggml_sycl_op_mul_mat_sycl( ggml_backend_sycl_context & ctx, const ggml_tensor *src0, const ggml_tensor *src1, ggml_tensor *dst, const char *src0_dd_i, const float *src1_ddf_i, const char *src1_ddq_i, float *dst_dd_i, const int64_t row_low, const int64_t row_high, const int64_t src1_ncols, const int64_t src1_padded_row_size, const queue_ptr &stream) try { GGML_ASSERT(src0_dd_i != nullptr); GGML_ASSERT(src1_ddf_i != nullptr); GGML_ASSERT(dst_dd_i != nullptr); const int64_t ne00 = src0->ne[0]; const int64_t ne10 = src1->ne[0]; GGML_ASSERT(ne00 == ne10); const int64_t row_diff = row_high - row_low; int id; SYCL_CHECK( CHECK_TRY_ERROR(id = get_current_device_id())); const int64_t ne0 = dst->ne[0]; // used by MKL only // the main device has a larger memory buffer to hold the results from all GPUs // ldc == nrows of the matrix that cuBLAS writes into int ldc = id == ctx.device ? ne0 : row_diff; // used by MKL only #ifdef GGML_SYCL_F16 bool use_fp16 = true; // TODO(Yu) SYCL capability check #else bool use_fp16 = false; #endif #if GGML_SYCL_DNNL && defined(GGML_SYCL_HAS_BF16) // Fast path for bf16 src0 if (src0->type == GGML_TYPE_BF16 && g_ggml_sycl_enable_dnn && ggml_is_contiguous(src0) && row_diff == src0->ne[1]) { using bf16_t = sycl::ext::oneapi::bfloat16; ggml_sycl_pool_alloc src1_as_bf16(ctx.pool(), src1_ncols*ne10); if (src1->type != GGML_TYPE_BF16) { const to_bf16_sycl_t to_bf16_sycl = ggml_get_to_bf16_sycl(src1->type, dst); GGML_ASSERT(to_bf16_sycl != nullptr); to_bf16_sycl(src1_ddf_i, src1_as_bf16.get(), src1_ncols*ne10, stream); } else { stream->memcpy(src1_as_bf16.get(), src1_ddf_i, src1_ncols*ne10*sizeof(bf16_t)); } DnnlGemmWrapper::row_gemm(ctx, row_diff, src1_ncols, ne10, src0_dd_i, DnnlGemmWrapper::to_dt(), src1_as_bf16.get(), DnnlGemmWrapper::to_dt(), dst_dd_i, DnnlGemmWrapper::to_dt(), stream); GGML_UNUSED(dst); GGML_UNUSED(src1_ddq_i); GGML_UNUSED(src1_padded_row_size); return; } #endif if ((src0->type == GGML_TYPE_F16 || ggml_is_quantized(src0->type)) && use_fp16 && ggml_is_contiguous(src0) && row_diff == src0->ne[1] && dst->op_params[0] == GGML_PREC_DEFAULT) { ggml_sycl_pool_alloc src0_as_f16(ctx.pool()); if (src0->type != GGML_TYPE_F16) { scope_op_debug_print scope_dbg_print(__func__, "/to_fp16_sycl", dst, /*num_src=*/2, " : converting src0 to fp16"); const to_fp16_sycl_t to_fp16_sycl = ggml_get_to_fp16_sycl(src0->type, dst); GGML_ASSERT(to_fp16_sycl != nullptr); size_t ne = row_diff*ne00; src0_as_f16.alloc(ne); to_fp16_sycl(src0_dd_i, src0_as_f16.get(), ne, stream); } const sycl::half *src0_ptr = src0->type == GGML_TYPE_F16 ? (const sycl::half *)src0_dd_i : src0_as_f16.get(); ggml_sycl_pool_alloc src1_as_f16(ctx.pool()); if (src1->type != GGML_TYPE_F16) { scope_op_debug_print scope_dbg_print(__func__, "/to_fp16_sycl", dst, /*num_src=*/2, " : converting src1 to fp16"); const to_fp16_sycl_t to_fp16_sycl = ggml_get_to_fp16_sycl(src1->type, dst); GGML_ASSERT(to_fp16_sycl != nullptr); size_t ne = src1_ncols*ne10; src1_as_f16.alloc(ne); to_fp16_sycl(src1_ddf_i, src1_as_f16.get(), ne, stream); } const sycl::half *src1_ptr = src1->type == GGML_TYPE_F16 ? (const sycl::half *)src1->data + src1_padded_row_size : src1_as_f16.get(); #if GGML_SYCL_DNNL if (g_ggml_sycl_enable_dnn) { DnnlGemmWrapper::row_gemm(ctx,row_diff, src1_ncols , ne10, src0_ptr, DnnlGemmWrapper::to_dt(), src1_ptr, DnnlGemmWrapper::to_dt(), dst_dd_i, DnnlGemmWrapper::to_dt(), stream); } else #endif { ggml_sycl_pool_alloc dst_f16(ctx.pool(), row_diff * src1_ncols); const sycl::half alpha_f16 = 1.0f; const sycl::half beta_f16 = 0.0f; SYCL_CHECK(CHECK_TRY_ERROR(dpct::gemm( *stream, oneapi::mkl::transpose::trans, oneapi::mkl::transpose::nontrans, row_diff, src1_ncols, ne10, &alpha_f16, src0_ptr, dpct::library_data_t::real_half, ne00, src1_ptr, dpct::library_data_t::real_half, ne10, &beta_f16, dst_f16.get(), dpct::library_data_t::real_half, ldc, dpct::library_data_t::real_half))); scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2, " : converting dst to fp32"); const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(GGML_TYPE_F16, dst); to_fp32_sycl(dst_f16.get(), dst_dd_i, row_diff*src1_ncols, stream); } } else { ggml_sycl_pool_alloc src0_ddq_as_f32(ctx.pool()); ggml_sycl_pool_alloc src1_ddq_as_f32(ctx.pool()); if (src0->type != GGML_TYPE_F32) { scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2, " : converting src0 to fp32"); const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(src0->type, dst); GGML_ASSERT(to_fp32_sycl != nullptr); src0_ddq_as_f32.alloc(row_diff*ne00); to_fp32_sycl(src0_dd_i, src0_ddq_as_f32.get(), row_diff*ne00, stream); } if (src1->type != GGML_TYPE_F32) { scope_op_debug_print scope_dbg_print(__func__, "/to_fp32_sycl", dst, /*num_src=*/2, " : converting src1 to fp32"); const to_fp32_sycl_t to_fp32_sycl = ggml_get_to_fp32_sycl(src1->type, dst); GGML_ASSERT(to_fp32_sycl != nullptr); src1_ddq_as_f32.alloc(src1_ncols*ne10); to_fp32_sycl(src1_ddf_i, src1_ddq_as_f32.get(), src1_ncols*ne10, stream); } const float * src0_ddf_i = src0->type == GGML_TYPE_F32 ? (const float *) src0_dd_i : src0_ddq_as_f32.get(); const float * src1_ddf1_i = src1->type == GGML_TYPE_F32 ? (const float *) src1_ddf_i : src1_ddq_as_f32.get(); { const int64_t gemm_flops = (int64_t)row_diff * src1_ncols * ne10; const bool use_mkl_direct = gemm_flops < 256 * 256 * 256; #if GGML_SYCL_DNNL if (g_ggml_sycl_enable_dnn && !use_mkl_direct) { DnnlGemmWrapper::row_gemm(ctx, row_diff, src1_ncols, ne10, src0_ddf_i, DnnlGemmWrapper::to_dt(), src1_ddf1_i, DnnlGemmWrapper::to_dt(), dst_dd_i, DnnlGemmWrapper::to_dt(), stream); } else #endif { const float alpha = 1.0f; const float beta = 0.0f; SYCL_CHECK(CHECK_TRY_ERROR(oneapi::mkl::blas::column_major::gemm( *stream, oneapi::mkl::transpose::trans, oneapi::mkl::transpose::nontrans, row_diff, src1_ncols, ne10, dpct::get_value(&alpha, *stream), src0_ddf_i, ne00, src1_ddf1_i, ne10, dpct::get_value(&beta, *stream), dst_dd_i, ldc))); } } } GGML_UNUSED(dst); GGML_UNUSED(src1_ddq_i); GGML_UNUSED(src1_padded_row_size); } catch (sycl::exception const &exc) { std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; std::exit(1); } inline void ggml_sycl_op_sum(ggml_backend_sycl_context & ctx, ggml_tensor *dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT( dst->type == GGML_TYPE_F32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); float * dst_dd = static_cast(dst->data); const int64_t ne = ggml_nelements(dst->src[0]); sum_rows_f32_sycl(src0_dd, dst_dd, ne, 1, main_stream); } inline void ggml_sycl_op_sum_rows(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT( dst->type == GGML_TYPE_F32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); float * dst_dd = static_cast(dst->data); const int64_t ncols = dst->src[0]->ne[0]; const int64_t nrows = ggml_nrows(dst->src[0]); sum_rows_f32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream); } inline void ggml_sycl_op_mean(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT(dst->type == GGML_TYPE_F32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); float * dst_dd = static_cast(dst->data); const int64_t ncols = dst->src[0]->ne[0]; const int64_t nrows = ggml_nrows(dst->src[0]); sum_rows_f32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream); main_stream->parallel_for( sycl::range<1>(nrows), [=](sycl::id<1> row) { dst_dd[row] /= ncols; } ); } inline void ggml_sycl_op_argsort(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT(dst->type == GGML_TYPE_I32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); int32_t * dst_dd = static_cast(dst->data); const int64_t ncols = dst->src[0]->ne[0]; const int64_t nrows = ggml_nrows(dst->src[0]); enum ggml_sort_order order = (enum ggml_sort_order) dst->op_params[0]; argsort_f32_i32_sycl(src0_dd, (int *)dst_dd, ncols, nrows, order, main_stream, ctx.device, ctx.pool()); } static void ggml_sycl_op_top_k(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { const ggml_tensor * src0 = dst->src[0]; GGML_ASSERT(src0); GGML_ASSERT(src0->type == GGML_TYPE_F32); GGML_ASSERT(dst->type == GGML_TYPE_I32); GGML_ASSERT(ggml_is_contiguous(src0)); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(src0->data); int32_t * dst_dd = static_cast(dst->data); const int k = dst->ne[0]; const int64_t ncols = src0->ne[0]; const int64_t nrows = ggml_nrows(src0); GGML_ASSERT(k > 0 && k <= 32); GGML_ASSERT(k <= ncols); top_k_f32_sycl(src0_dd, dst_dd, ncols, nrows, k, main_stream); } inline void ggml_sycl_op_argmax(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT( dst->type == GGML_TYPE_I32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); int32_t * dst_dd = static_cast(dst->data); const int64_t ncols = dst->src[0]->ne[0]; const int64_t nrows = ggml_nrows(dst->src[0]); argmax_f32_i32_sycl(src0_dd, dst_dd, ncols, nrows, main_stream); } inline void ggml_sycl_op_diag_mask_inf(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT( dst->type == GGML_TYPE_F32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); float * dst_dd = static_cast(dst->data); const int64_t ne00 = dst->src[0]->ne[0]; const int64_t ne01 = dst->src[0]->ne[1]; const int nrows0 = ggml_nrows(dst->src[0]); const int n_past = ((int32_t *) dst->op_params)[0]; diag_mask_inf_f32_sycl(src0_dd, dst_dd, ne00, nrows0, ne01, n_past, main_stream); } static void tri_f32_sycl( const float * src, float * dst, const int64_t ne0, const int64_t ne1, const int64_t ne2, const int64_t ne3, const ggml_tri_type ttype, dpct::queue_ptr main_stream ) { const size_t total = (size_t) ne0 * (size_t) ne1 * (size_t) ne2 * (size_t) ne3; main_stream->parallel_for(sycl::range<1>(total), [=](sycl::id<1> tid) { const int64_t idx = (int64_t) tid[0]; const int64_t i0 = idx % ne0; const int64_t t1 = idx / ne0; const int64_t i1 = t1 % ne1; bool keep = false; switch (ttype) { case GGML_TRI_TYPE_LOWER: keep = (i0 < i1); break; case GGML_TRI_TYPE_LOWER_DIAG: keep = (i0 <= i1); break; case GGML_TRI_TYPE_UPPER: keep = (i0 > i1); break; case GGML_TRI_TYPE_UPPER_DIAG: keep = (i0 >= i1); break; default: keep = false; break; } dst[idx] = keep ? src[idx] : 0.0f; }); } static void ggml_sycl_op_tri(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { const ggml_tensor * src0 = dst->src[0]; GGML_ASSERT(src0); GGML_ASSERT(src0->type == GGML_TYPE_F32); GGML_ASSERT(dst->type == GGML_TYPE_F32); GGML_ASSERT(ggml_is_contiguous(src0)); GGML_ASSERT(ggml_is_contiguous(dst)); GGML_ASSERT(ggml_are_same_shape(src0, dst)); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(src0->data); float * dst_dd = static_cast(dst->data); const ggml_tri_type ttype = (ggml_tri_type) ggml_get_op_params_i32(dst, 0); const int64_t ne0 = src0->ne[0]; const int64_t ne1 = src0->ne[1]; const int64_t ne2 = src0->ne[2]; const int64_t ne3 = src0->ne[3]; tri_f32_sycl(src0_dd, dst_dd, ne0, ne1, ne2, ne3, ttype, main_stream); } inline void ggml_sycl_op_scale(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { GGML_ASSERT(dst->src[0]->type == GGML_TYPE_F32); GGML_ASSERT( dst->type == GGML_TYPE_F32); dpct::queue_ptr main_stream = ctx.stream(); SYCL_CHECK(ggml_sycl_set_device(ctx.device)); const float * src0_dd = static_cast(dst->src[0]->data); float * dst_dd = static_cast(dst->data); float scale; float bias; memcpy(&scale, (float *) dst->op_params + 0, sizeof(float)); memcpy(&bias, (float *) dst->op_params + 1, sizeof(float)); scale_f32_sycl(src0_dd, dst_dd, scale, bias, ggml_nelements(dst->src[0]), main_stream); /* DPCT1010:87: SYCL uses exceptions to report errors and does not use the error codes. The call was replaced with 0. You need to rewrite this code. */ SYCL_CHECK(0); } static void ggml_sycl_set_peer_access(const int n_tokens, int main_device) { static bool peer_access_enabled = false; const bool enable_peer_access = n_tokens <= GGML_SYCL_PEER_MAX_BATCH_SIZE; if (peer_access_enabled == enable_peer_access) { return; } #ifdef NDEBUG for (int i = 0; i < ggml_sycl_info().device_count; ++i) { SYCL_CHECK(ggml_sycl_set_device(i)); } for (int i = 0; i < ggml_sycl_info().device_count; ++i) { SYCL_CHECK(ggml_sycl_set_device(i)); for (int id_other = 0; id_other < ggml_sycl_info().device_count; ++id_other) { if (i == id_other) { continue; } if (i != main_device && id_other != main_device) { continue; } // int can_access_peer; // SYCL_CHECK(syclDeviceCanAccessPeer(&can_access_peer, id, id_other)); // if (can_access_peer) { // if (enable_peer_access) { // SYCL_CHECK(syclDeviceEnablePeerAccess(id_other, 0)); // } else { // SYCL_CHECK(syclDeviceDisablePeerAccess(id_other)); // } // } } } #endif // NDEBUG peer_access_enabled = enable_peer_access; } template