mirror of
https://github.com/ggml-org/whisper.cpp.git
synced 2026-10-04 21:41:24 +02:00
* ggml-et: Add performance logging
* ggml-et: Quants helpers
* ggml-et: Add MUL_MAT kernel
* ggml-et: Add ROPE kernel
* ggml-et: Add RMS_NORM kernel
* ggml-et: Add GLU kernel
* ggml-et: Add SOFT_MAX kernel
* ggml-et: Add GET_ROWS kernel
* ggml-et: Add CONT kernel
* ggml-et: Add SET_ROWS kernel
* ggml-et: Add MUL_MAT_ID kernel
* ggml-et: Build et kernels as part of ggml
* ggml-et: Embed kernels with fs fallback
* ggml-et: Build fixes
* ggml-et: Add MUL_MAT F32xF32 op
* ggml_et: Add MUL_MAT_ID op
* ggml-et: Disable offloading for debug
* ggml-et: Refactor out block ops
* ggml-et: ggml backend API changes
* ggml-et: Add RESHAPE/TRANSPOSE to supported
* ggml-et: Add CONT_F16
* ggml-et: Add supported ops doc
* gglm-et: Initial doc
* ggml-et: Remove runtime import hacks
We can now import the runtime by a simple find_package(), so we
can cleanup the CMakeLists.txt.
* ggml-et: Fix GET_ROWS kernel
Fix lost batch dimension.
Also clean vibe-comments.
* ggml-et: Fix SET_ROWS kernel
Remove incorrect broadcasting guard.
* ggml-et: Use custom instruction for fp32->fp16
* ggml-et: Vectorize set_rows fp32->fp16
* ggml-et: Fix ROPE kernel (yarn)
ggml-et: fix et_logf
WIP: Fix ramp
WIP: fix ROPE!
* ggml-et: Better sinf
* ggml-et: Fix SOFT_MAX
Add `max_bias` and `sink` support.
* ggml-et: Fix CONT
Reorder from contiguous write to read with atomic stores.
* ggml-et: Fix elmap kernel
Remainder handlin
* ggml-et: Fix MUL_MAT MUL_MAT_ID remainders
* ggml-et: Fix ET-SOC reference
* ggml-et: Fix embed kernels scripts for old python
This allows GGML-ET to build on pre-3.8 python.
* Add sysemu support with compile time flag `-DGGML_ET_SYSEMU=ON` (llama/6)
* Example using ET-Soc-1 emulator configuration
Example usage:
```bash
cmake -B build -DGGML_CUDA=OFF -DGGML_ET=ON -DLLAMA_CURL=OFF -DGGML_CCACHE=ON
cmake --build build --config Release -j $(nproc)
time ./build/bin/test-backend-ops
./build/bin/llama-server \
--model Qwen3-0.6B-Q8_0.gguf \
--alias Qwen3-0.6B-Q8_0 \
-fa 0 \
--ctx-size 1024 \
--no-warmup \
--host 127.0.0.1 \
--port 8080
```
* build: proper dep tracking for kernels
* support host using MOLD linker
* initial multi core GET_ROW F32 implementation
* vectorized q8 dequant
* wip: cland warning clenaups and initial logging refactor
* wip: message default message cleanup
* chore: message cleanups
* cmake cleanup
* migrate to use platform provided functions
* cmake back into subdir
* support et_print() in kernels
* fix: repair kernel building
* perf: operations run async by default
* debug: proper kernel dep tracking and error detection on kenrel launch
* fix: kernel binary dep tracking and fixing get_rows_f32 erroring
* perf: back to doing async kernel runs by default
* perf: vectorize and parallel device memset
* merge matmul work
* misc: align allocation and enable all offload
* misc: delete deadcode and respect memory limits
* fix: repair tensor debug print
* fix: loosen RMS_NORM op percision
* feat: Q4_0 GET_ROWS
* perf: FP32 MUL_MAT using TensorFMA
* update limitations
* perf: redue L1 load in compute_block_dot_product_q8_0
* feat: save kernel mapping (name to id) when profiling is enabled
* chore: memops cleanup
* perf: parallelize softmax by rows
* perf: vectorize 2nd phase of softmax
* perf: ban GET_ROWS from offloaded
* perf: vectorize and non-atomic for eltwise ops and sub support
* perf: vectorize normal rope
* perf: glu runs in parallel
* merge: manually merge saqib's work on kernel fixes
* perf: more vectorized RoPE
* perf: parallelize mul_mat_id
* perf: parallelize set_rows_f32
* perf: vectorize softmax
* feat: support kernel fusion and fuse RMS_NORM + MUL
* fix: mostly resolve test-backend-ops failure in SOFT_MAX and ROPE
* fix: bump max rope dims for gemma
* feat: GeGLU and SCALE support to fully offload Gemma
* perf: faster device memset
* feat: get_rows supporting Q4_K and avoid cont cache coherent issues
* better F32 MM
* feat: NORM for ET backend
* feat: SQR for ET backend
* feat: UNARY on ET
* feat: el_map support broadcasting for ET
* feat: SUM_ROWS in ET backend
* feat: more ops in ET backend
* feat: WKV* operators in ET backend
* perf: parallelize operators across cacheline instead of row
* perf: parallelize get_rows on cacheline
* wip: baseline FlashAttention for ET backend
* wip: enough FA and CPY f32->f16 to run llama 3.1 fully offloaded with FA on
* feat: f16 x f16 -> f32 MM using matrix engine
* wip: f16 FlashAttention using matrix engine
* wip: clean up
* feat: barriers
* perf: optimize FA_F16 in ET
* perf: vectorize pack_k_for_transpose16
* perf: prefetch next loop matrix tile
* perf: FlashAttention 2nd MM uses TensorFMA and optimizations
* cleanup: flashattention reorg
* perf: optimizations and fixes
* feat: L2SCP API and make FlashAttention support DV = 256 for gemma
* perf: parallelize norms beyond single row
* feat: GATED_DELTA_NET support and relaxed L2_NORM requirment
* feat: loosen RMS_NORM, NORM, ROPE contingous req too
* feat: repeat supports brocasting on dim 0 and loosen cont check
* feat: FILL and DIAG operator
* feat: loosen UNARY support chcek
* feat: TRI support
* feat: SOLVE_TRI support
* feat: basic SET support
* feat: loosen CONT req
* perf: fp16_to_fp32 use ASM
* feat: IMROPE support
* feat: PAD support
* feat: global barrier
* fix: view must live on the same backend as backing tensor
* feat: relax CONCAT in ET backend
* feat: dead simple CUMSUM implementation
* feat: basic SSM_CONV support
* feat: loosen CONCAT req
* feat: relax GATED_DELTA_NET and add SET support proper
* cleanup: cleanup LCM math
* feat: SWIGLU single input
* feat: SSM_SCAN support
* feat: el_map supports non aligned tensors in best effort
* feat: basic GROUP_NORM support
* feat: loosen MUL_MAT capablities slightly
* feat: loosen MUL_MAT and GET_ROWS and add IM2COL
* feat: special case for softmax 1x1x1x1
* feat: loosen SOFT_MAX req in ET backend
* fix: el_map unaligned acse fixes
* perf: optimize zero_acc_vec in flash_attn_ext_f16_me
* perf: use hart 1 for packing in MM and FA for FP16
* feat: kernel semaphore
* perf: better instruction sequence in FlashAttention
* fix: gated_delta_net with proper masking
* perf: better parallelization for GATED_DELTA_NET
* perf: parallelize SSM_CONV over nr
* perf: vectorize SSM_CONV
* perf: optimize MUL_MAT for q8
* feat: support Gemma 4
* fix: support multi-device
* feat: broader GLU support
* feat: unary ops supports view
* fix: repair fp16 MM using matrix engine
* perf: handle large N GEMV better
* perf: better q8_0 MM
* perf: better set_rows
* add back deleted files
* fix: repair after merge
* feat: POC version of uberkernel
* feat: RMS_NORM in uberkernel
* feat: add more kernels into usage
* chore: clean up uberkernel compilation
* perf: faster flash attention
* perf: opt flash attention for large seq length
* feat: loosen op bounds. clamp and mean support
* perf: vectorize ssm_scan
* perf: slightly faster FA
* perf: FlashAttention parallel MM and load
* perf: fuse Q8 MM and ADD
* feat: basic conv kernel for ET
* softMAx_test
* set_rows_f32
* get_rows and cont
* testing
* set_rows_exp
* Junk addition
* Narrowing the issue
* Update flash_attn_ext_f16_me.c
Focusing FA_ext_f16_me
* test
* Eviction updated
* Detailed cache eviction debug
* mulmat
* removeal of `BUILD_FOR_UBERKERNEL` flag
* cleaning...
* fix: balance FCC0 count
* feat: implement mul_mat and mul_mat_id for Q4_0 type
* optimize uberkernel plan upload
* add mul_mat q4 into uberkernel
* enable gating flush to just uberkernel
* update docs for ET
* update op support for ET
* et-backend: optimize Q4_0 and Q8_0 mul_mat_id row accumulations
* et-backend: specialize mul_mat_id kernels for Q4_0 and Q8_0
* et-backend: fix RoPE YaRN corr_dim formula and handle degenerate inputs
* test-backend-ops: add DeepSeek-V2-Lite RoPE test coverage
* et-backend: add Q4_0 mul_mat matrix-engine kernel using TensorFMA32
* et-backend: vectorize Q4_0 matrix-engine dequantization
* et-backend: support hybrid matrix/vector engine execution for Q4_0 mul_mat tail
* et-backend: run partial-N tiles on matrix engine for Q4_0 mul_mat
* et-backend: route Q4_0 mul_mat N < 53 to vecdot for better prefill latency
* Update uberkernel.c
* Update unary_f32.c
* gemma 4
* bisect gemma4: enable scale_f32 only
* bisect gemma4: +rms_norm_f32
* bisect gemma4: +rms_norm_mul_f32
* bisect gemma4: disable rms_norm_mul_f32 -- BREAKS OUTPUT
* bisect gemma4: +rope_f32 (skip rms_norm_mul)
* bisect gemma4: +el_map_f32
* bisect gemma4: +softmax_f32
* bisect gemma4: +get_rows_f32
* bisect gemma4: +glu_f32
* bisect gemma4: +mul_mat_f32 +mul_mat_f32_matrix_engine
* bisect gemma4: +mul_mat_f16 +mul_mat_f16_matrix_engine
* bisect gemma4: +mul_mat_Q8_0 +mul_mat_Q4_0
* bisect gemma4: +flash_attn_ext_f32 +flash_attn_ext_f16_me
* bisect gemma4: +mul_mat_id_f32
* bisect gemma4: +sum_rows_f32
* bisect gemma4: +cont_f16
* bisect gemma4: +fill_f32
* bisect gemma4: +unary_f32 (all ops re-enabled except rms_norm_mul)
* Update rms_norm_mul_f32.c
* bisect2 gemma4 n64: +scale_f32 only
* bisect2 gemma4 n64: +rms_norm_f32 +rope_f32
* bisect2 gemma4 n64: +rms_norm_mul_f32 (with ET_UBERKERNEL eviction fix)
* bisect2 gemma4 n64: +el_map +get_rows +glu +softmax (skip rms_norm_mul)
* bisect2 gemma4 n64: all ops enabled except rms_norm_mul
* bisect2 n64: test unary+cont+fill+sum_rows (no mul_mat/flash_attn)
* bisect2 n64: +mul_mat_f32 +mul_mat_f32_matrix_engine
* bisect2 n64: +mul_mat_f16 +mul_mat_f16_matrix_engine
* bisect2 n64: +mul_mat_Q8_0 +mul_mat_Q4_0
* bisect2 n64: +mul_mat_Q8_0 only (disable Q4_0)
* bisect2 n64: +mul_mat_Q4_0 only (Q8_0 breaks)
* bisect2 n64: +mul_mat_id +flash_attn_ext (skip Q8_0)
* run-3: matmul + rms_norm_mul
* run-4
* Revert "run-4"
* run5
* changes after cleanup
* cleanup before upstream
* restrict changes into ET backend
* move kernel embedding from Python to CMake
* move uberkernel gen into CMake
* apply clang format
* update CMake style
* update to match C and C++ style
* use source ggml and quant headers instead of ET's
* MROPE support
* absorb view ops into same branch as none
* fix bad rebase
* add marty1885 to codeowners
* oops
* remove redundant newline
* fix CI editor warnings
---------
Co-authored-by: Vidas <vidas@nuolat.lt>
Co-authored-by: Gianluca Guida <glguida@tlbflush.org>
Co-authored-by: Gianluca Guida <gianluca@nekko.ai>
Co-authored-by: ubergarm <leimgrub@gmail.com>
Co-authored-by: SaqibAkram-10xE <saqib.akram@10xengineers.ai>
Co-authored-by: Rehan Qasim <rehan.qasim@10xengineers.ai>
594 lines
18 KiB
C++
594 lines
18 KiB
C++
#include "ggml-backend-impl.h"
|
|
#include "ggml-backend.h"
|
|
#include "ggml-backend-dl.h"
|
|
#include "ggml-impl.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
#include <cctype>
|
|
|
|
#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# ifndef NOMINMAX
|
|
# define NOMINMAX
|
|
# endif
|
|
# include <windows.h>
|
|
#elif defined(__APPLE__)
|
|
# include <mach-o/dyld.h>
|
|
# include <dlfcn.h>
|
|
#else
|
|
# include <dlfcn.h>
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
// Backend registry
|
|
#ifdef GGML_USE_CPU
|
|
#include "ggml-cpu.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_CUDA
|
|
#include "ggml-cuda.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_METAL
|
|
#include "ggml-metal.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_SYCL
|
|
#include "ggml-sycl.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_VULKAN
|
|
#include "ggml-vulkan.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_WEBGPU
|
|
#include "ggml-webgpu.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_ZDNN
|
|
#include "ggml-zdnn.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_OPENCL
|
|
#include "ggml-opencl.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_HEXAGON
|
|
#include "ggml-hexagon.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_BLAS
|
|
#include "ggml-blas.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_RPC
|
|
#include "ggml-rpc.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_VIRTGPU_FRONTEND
|
|
#include "ggml-virtgpu.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_CANN
|
|
#include "ggml-cann.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_ZENDNN
|
|
#include "ggml-zendnn.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_OPENVINO
|
|
#include "ggml-openvino.h"
|
|
#endif
|
|
|
|
#ifdef GGML_USE_ET
|
|
#include "ggml-et.h"
|
|
#endif
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
static std::string path_str(const fs::path & path) {
|
|
try {
|
|
#if defined(__cpp_lib_char8_t)
|
|
// C++20 and later: u8string() returns std::u8string
|
|
const std::u8string u8str = path.u8string();
|
|
return std::string(reinterpret_cast<const char *>(u8str.data()), u8str.size());
|
|
#else
|
|
// C++17: u8string() returns std::string
|
|
return path.u8string();
|
|
#endif
|
|
} catch (...) {
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
struct ggml_backend_reg_entry {
|
|
ggml_backend_reg_t reg;
|
|
dl_handle_ptr handle;
|
|
};
|
|
|
|
struct ggml_backend_registry {
|
|
std::vector<ggml_backend_reg_entry> backends;
|
|
std::vector<ggml_backend_dev_t> devices;
|
|
|
|
ggml_backend_registry() {
|
|
#ifdef GGML_USE_CUDA
|
|
register_backend(ggml_backend_cuda_reg());
|
|
#endif
|
|
#ifdef GGML_USE_METAL
|
|
register_backend(ggml_backend_metal_reg());
|
|
#endif
|
|
#ifdef GGML_USE_SYCL
|
|
register_backend(ggml_backend_sycl_reg());
|
|
#endif
|
|
#ifdef GGML_USE_VULKAN
|
|
// Add runtime disable check
|
|
if (getenv("GGML_DISABLE_VULKAN") == nullptr) {
|
|
register_backend(ggml_backend_vk_reg());
|
|
} else {
|
|
GGML_LOG_DEBUG("Vulkan backend disabled by GGML_DISABLE_VULKAN environment variable\n");
|
|
}
|
|
#endif
|
|
#ifdef GGML_USE_WEBGPU
|
|
register_backend(ggml_backend_webgpu_reg());
|
|
#endif
|
|
#ifdef GGML_USE_ZDNN
|
|
register_backend(ggml_backend_zdnn_reg());
|
|
#endif
|
|
#ifdef GGML_USE_VIRTGPU_FRONTEND
|
|
register_backend(ggml_backend_virtgpu_reg());
|
|
#endif
|
|
|
|
#ifdef GGML_USE_OPENCL
|
|
register_backend(ggml_backend_opencl_reg());
|
|
#endif
|
|
#ifdef GGML_USE_ZENDNN
|
|
register_backend(ggml_backend_zendnn_reg());
|
|
#endif
|
|
#ifdef GGML_USE_HEXAGON
|
|
register_backend(ggml_backend_hexagon_reg());
|
|
#endif
|
|
#ifdef GGML_USE_CANN
|
|
register_backend(ggml_backend_cann_reg());
|
|
#endif
|
|
#ifdef GGML_USE_BLAS
|
|
register_backend(ggml_backend_blas_reg());
|
|
#endif
|
|
#ifdef GGML_USE_RPC
|
|
register_backend(ggml_backend_rpc_reg());
|
|
#endif
|
|
#ifdef GGML_USE_OPENVINO
|
|
register_backend(ggml_backend_openvino_reg());
|
|
#endif
|
|
#ifdef GGML_USE_ET
|
|
register_backend(ggml_backend_et_reg());
|
|
#endif
|
|
#ifdef GGML_USE_CPU
|
|
register_backend(ggml_backend_cpu_reg());
|
|
#endif
|
|
}
|
|
|
|
~ggml_backend_registry() {
|
|
// FIXME: backends cannot be safely unloaded without a function to destroy all the backend resources,
|
|
// since backend threads may still be running and accessing resources from the dynamic library
|
|
for (auto & entry : backends) {
|
|
if (entry.handle) {
|
|
entry.handle.release(); // NOLINT
|
|
}
|
|
}
|
|
}
|
|
|
|
void register_backend(ggml_backend_reg_t reg, dl_handle_ptr handle = nullptr) {
|
|
if (!reg) {
|
|
return;
|
|
}
|
|
|
|
for (auto & entry : backends) {
|
|
if (entry.reg == reg) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
GGML_LOG_DEBUG("%s: registered backend %s (%zu devices)\n",
|
|
__func__, ggml_backend_reg_name(reg), ggml_backend_reg_dev_count(reg));
|
|
#endif
|
|
backends.push_back({ reg, std::move(handle) });
|
|
for (size_t i = 0; i < ggml_backend_reg_dev_count(reg); i++) {
|
|
register_device(ggml_backend_reg_dev_get(reg, i));
|
|
}
|
|
}
|
|
|
|
void register_device(ggml_backend_dev_t device) {
|
|
for (auto & dev : devices) {
|
|
if (dev == device) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
GGML_LOG_DEBUG("%s: registered device %s (%s)\n", __func__, ggml_backend_dev_name(device), ggml_backend_dev_description(device));
|
|
#endif
|
|
devices.push_back(device);
|
|
}
|
|
|
|
ggml_backend_reg_t load_backend(const fs::path & path, bool silent) {
|
|
dl_handle_ptr handle { dl_load_library(path) };
|
|
if (!handle) {
|
|
if (!silent) {
|
|
GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_str(path).c_str(), dl_error());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score");
|
|
if (score_fn && score_fn() == 0) {
|
|
if (!silent) {
|
|
GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path_str(path).c_str());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
auto backend_init_fn = (ggml_backend_init_t) dl_get_sym(handle.get(), "ggml_backend_init");
|
|
if (!backend_init_fn) {
|
|
if (!silent) {
|
|
GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path_str(path).c_str());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ggml_backend_reg_t reg = backend_init_fn();
|
|
if (!reg || reg->api_version != GGML_BACKEND_API_VERSION) {
|
|
if (!silent) {
|
|
if (!reg) {
|
|
GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n",
|
|
__func__, path_str(path).c_str());
|
|
} else {
|
|
GGML_LOG_ERROR("%s: failed to initialize backend from %s: incompatible API version (backend: %d, current: %d)\n",
|
|
__func__, path_str(path).c_str(), reg->api_version, GGML_BACKEND_API_VERSION);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path_str(path).c_str());
|
|
|
|
register_backend(reg, std::move(handle));
|
|
|
|
return reg;
|
|
}
|
|
|
|
void unload_backend(ggml_backend_reg_t reg, bool silent) {
|
|
auto it = std::find_if(backends.begin(), backends.end(),
|
|
[reg](const ggml_backend_reg_entry & entry) { return entry.reg == reg; });
|
|
|
|
if (it == backends.end()) {
|
|
if (!silent) {
|
|
GGML_LOG_ERROR("%s: backend not found\n", __func__);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!silent) {
|
|
GGML_LOG_DEBUG("%s: unloading %s backend\n", __func__, ggml_backend_reg_name(reg));
|
|
}
|
|
|
|
// remove devices
|
|
devices.erase(
|
|
std::remove_if(devices.begin(), devices.end(),
|
|
[reg](ggml_backend_dev_t dev) { return ggml_backend_dev_backend_reg(dev) == reg; }),
|
|
devices.end());
|
|
|
|
// remove backend
|
|
backends.erase(it);
|
|
}
|
|
};
|
|
|
|
static ggml_backend_registry & get_reg() {
|
|
static ggml_backend_registry reg;
|
|
return reg;
|
|
}
|
|
|
|
// Internal API
|
|
void ggml_backend_register(ggml_backend_reg_t reg) {
|
|
get_reg().register_backend(reg);
|
|
}
|
|
|
|
void ggml_backend_device_register(ggml_backend_dev_t device) {
|
|
get_reg().register_device(device);
|
|
}
|
|
|
|
// Backend (reg) enumeration
|
|
static bool striequals(const char * a, const char * b) {
|
|
for (; *a && *b; a++, b++) {
|
|
if (std::tolower(*a) != std::tolower(*b)) {
|
|
return false;
|
|
}
|
|
}
|
|
return *a == *b;
|
|
}
|
|
|
|
size_t ggml_backend_reg_count() {
|
|
return get_reg().backends.size();
|
|
}
|
|
|
|
ggml_backend_reg_t ggml_backend_reg_get(size_t index) {
|
|
GGML_ASSERT(index < ggml_backend_reg_count());
|
|
return get_reg().backends[index].reg;
|
|
}
|
|
|
|
ggml_backend_reg_t ggml_backend_reg_by_name(const char * name) {
|
|
for (size_t i = 0; i < ggml_backend_reg_count(); i++) {
|
|
ggml_backend_reg_t reg = ggml_backend_reg_get(i);
|
|
if (striequals(ggml_backend_reg_name(reg), name)) {
|
|
return reg;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Device enumeration
|
|
size_t ggml_backend_dev_count() {
|
|
return get_reg().devices.size();
|
|
}
|
|
|
|
ggml_backend_dev_t ggml_backend_dev_get(size_t index) {
|
|
GGML_ASSERT(index < ggml_backend_dev_count());
|
|
return get_reg().devices[index];
|
|
}
|
|
|
|
ggml_backend_dev_t ggml_backend_dev_by_name(const char * name) {
|
|
for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
|
|
ggml_backend_dev_t dev = ggml_backend_dev_get(i);
|
|
if (striequals(ggml_backend_dev_name(dev), name)) {
|
|
return dev;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ggml_backend_dev_t ggml_backend_dev_by_type(enum ggml_backend_dev_type type) {
|
|
for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
|
|
ggml_backend_dev_t dev = ggml_backend_dev_get(i);
|
|
if (ggml_backend_dev_type(dev) == type) {
|
|
return dev;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Convenience functions
|
|
ggml_backend_t ggml_backend_init_by_name(const char * name, const char * params) {
|
|
ggml_backend_dev_t dev = ggml_backend_dev_by_name(name);
|
|
if (!dev) {
|
|
return nullptr;
|
|
}
|
|
return ggml_backend_dev_init(dev, params);
|
|
}
|
|
|
|
ggml_backend_t ggml_backend_init_by_type(enum ggml_backend_dev_type type, const char * params) {
|
|
ggml_backend_dev_t dev = ggml_backend_dev_by_type(type);
|
|
if (!dev) {
|
|
return nullptr;
|
|
}
|
|
return ggml_backend_dev_init(dev, params);
|
|
}
|
|
|
|
ggml_backend_t ggml_backend_init_best(void) {
|
|
ggml_backend_dev_t dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_GPU);
|
|
dev = dev ? dev : ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_IGPU);
|
|
dev = dev ? dev : ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
|
|
if (!dev) {
|
|
return nullptr;
|
|
}
|
|
return ggml_backend_dev_init(dev, nullptr);
|
|
}
|
|
|
|
// Dynamic loading
|
|
ggml_backend_reg_t ggml_backend_load(const char * path) {
|
|
return get_reg().load_backend(path, false);
|
|
}
|
|
|
|
void ggml_backend_unload(ggml_backend_reg_t reg) {
|
|
get_reg().unload_backend(reg, true);
|
|
}
|
|
|
|
static fs::path get_executable_path() {
|
|
#if defined(__APPLE__)
|
|
// get executable path
|
|
std::vector<char> path;
|
|
uint32_t size;
|
|
while (true) {
|
|
size = path.size();
|
|
if (_NSGetExecutablePath(path.data(), &size) == 0) {
|
|
break;
|
|
}
|
|
path.resize(size);
|
|
}
|
|
std::string base_path(path.data(), size);
|
|
// remove executable name
|
|
auto last_slash = base_path.find_last_of('/');
|
|
if (last_slash != std::string::npos) {
|
|
base_path = base_path.substr(0, last_slash);
|
|
}
|
|
return base_path + "/";
|
|
#elif defined(__linux__) || defined(__FreeBSD__)
|
|
std::string base_path = ".";
|
|
std::vector<char> path(1024);
|
|
while (true) {
|
|
// get executable path
|
|
# if defined(__linux__)
|
|
ssize_t len = readlink("/proc/self/exe", path.data(), path.size());
|
|
# elif defined(__FreeBSD__)
|
|
ssize_t len = readlink("/proc/curproc/file", path.data(), path.size());
|
|
# endif
|
|
if (len == -1) {
|
|
break;
|
|
}
|
|
if (len < (ssize_t) path.size()) {
|
|
base_path = std::string(path.data(), len);
|
|
// remove executable name
|
|
auto last_slash = base_path.find_last_of('/');
|
|
if (last_slash != std::string::npos) {
|
|
base_path = base_path.substr(0, last_slash);
|
|
}
|
|
break;
|
|
}
|
|
path.resize(path.size() * 2);
|
|
}
|
|
|
|
return base_path + "/";
|
|
#elif defined(_WIN32)
|
|
std::vector<wchar_t> path(MAX_PATH);
|
|
DWORD len = GetModuleFileNameW(NULL, path.data(), path.size());
|
|
if (len == 0) {
|
|
return {};
|
|
}
|
|
std::wstring base_path(path.data(), len);
|
|
// remove executable name
|
|
auto last_slash = base_path.find_last_of('\\');
|
|
if (last_slash != std::string::npos) {
|
|
base_path = base_path.substr(0, last_slash);
|
|
}
|
|
return base_path + L"\\";
|
|
#else
|
|
return {};
|
|
#endif
|
|
}
|
|
|
|
static fs::path backend_filename_prefix() {
|
|
#ifdef _WIN32
|
|
return fs::u8path("ggml-");
|
|
#else
|
|
return fs::u8path("libggml-");
|
|
#endif
|
|
}
|
|
|
|
static fs::path backend_filename_extension() {
|
|
#ifdef _WIN32
|
|
return fs::u8path(".dll");
|
|
#else
|
|
return fs::u8path(".so");
|
|
#endif
|
|
}
|
|
|
|
static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, const char * user_search_path) {
|
|
// enumerate all the files that match [lib]ggml-name-*.[so|dll] in the search paths
|
|
const fs::path name_path = fs::u8path(name);
|
|
const fs::path file_prefix = backend_filename_prefix().native() + name_path.native() + fs::u8path("-").native();
|
|
const fs::path file_extension = backend_filename_extension();
|
|
|
|
std::vector<fs::path> search_paths;
|
|
if (user_search_path == nullptr) {
|
|
#ifdef GGML_BACKEND_DIR
|
|
search_paths.push_back(fs::u8path(GGML_BACKEND_DIR));
|
|
#endif
|
|
// default search paths: executable directory, current directory
|
|
search_paths.push_back(get_executable_path());
|
|
search_paths.push_back(fs::current_path());
|
|
} else {
|
|
search_paths.push_back(fs::u8path(user_search_path));
|
|
}
|
|
|
|
int best_score = 0;
|
|
fs::path best_path;
|
|
std::error_code ec;
|
|
|
|
for (const auto & search_path : search_paths) {
|
|
if (!fs::exists(search_path, ec)) {
|
|
if (ec) {
|
|
GGML_LOG_DEBUG("%s: posix_stat(%s) failure, error-message: %s\n", __func__, path_str(search_path).c_str(), ec.message().c_str());
|
|
} else {
|
|
GGML_LOG_DEBUG("%s: search path %s does not exist\n", __func__, path_str(search_path).c_str());
|
|
}
|
|
continue;
|
|
}
|
|
fs::directory_iterator dir_it(search_path, fs::directory_options::skip_permission_denied);
|
|
for (const auto & entry : dir_it) {
|
|
if (entry.is_regular_file(ec)) {
|
|
auto filename = entry.path().filename();
|
|
auto ext = entry.path().extension();
|
|
if (filename.native().find(file_prefix) == 0 && ext == file_extension) {
|
|
dl_handle_ptr handle { dl_load_library(entry) };
|
|
if (!handle && !silent) {
|
|
GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_str(entry.path()).c_str(), dl_error());
|
|
}
|
|
if (handle) {
|
|
auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score");
|
|
if (score_fn) {
|
|
int s = score_fn();
|
|
#ifndef NDEBUG
|
|
GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, path_str(entry.path()).c_str(), s);
|
|
#endif
|
|
if (s > best_score) {
|
|
best_score = s;
|
|
best_path = entry.path();
|
|
}
|
|
} else {
|
|
if (!silent) {
|
|
GGML_LOG_INFO("%s: failed to find ggml_backend_score in %s\n", __func__, path_str(entry.path()).c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_score == 0) {
|
|
// try to load the base backend
|
|
for (const auto & search_path : search_paths) {
|
|
fs::path filename = backend_filename_prefix().native() + name_path.native() + backend_filename_extension().native();
|
|
fs::path path = search_path / filename;
|
|
if (std::error_code ec; fs::exists(path, ec)) {
|
|
return get_reg().load_backend(path, silent);
|
|
} else {
|
|
if (ec) {
|
|
GGML_LOG_DEBUG("%s: posix_stat(%s) failure, error-message: %s\n", __func__, path_str(path).c_str(), ec.message().c_str());
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
return get_reg().load_backend(best_path, silent);
|
|
}
|
|
|
|
void ggml_backend_load_all() {
|
|
ggml_backend_load_all_from_path(nullptr);
|
|
}
|
|
|
|
void ggml_backend_load_all_from_path(const char * dir_path) {
|
|
#ifdef NDEBUG
|
|
bool silent = true;
|
|
#else
|
|
bool silent = false;
|
|
#endif
|
|
|
|
ggml_backend_load_best("blas", silent, dir_path);
|
|
ggml_backend_load_best("zendnn", silent, dir_path);
|
|
ggml_backend_load_best("cann", silent, dir_path);
|
|
ggml_backend_load_best("cuda", silent, dir_path);
|
|
ggml_backend_load_best("hip", silent, dir_path);
|
|
ggml_backend_load_best("metal", silent, dir_path);
|
|
ggml_backend_load_best("rpc", silent, dir_path);
|
|
ggml_backend_load_best("sycl", silent, dir_path);
|
|
ggml_backend_load_best("vulkan", silent, dir_path);
|
|
ggml_backend_load_best("virtgpu", silent, dir_path);
|
|
ggml_backend_load_best("opencl", silent, dir_path);
|
|
ggml_backend_load_best("hexagon", silent, dir_path);
|
|
ggml_backend_load_best("musa", silent, dir_path);
|
|
ggml_backend_load_best("openvino", silent, dir_path);
|
|
ggml_backend_load_best("cpu", silent, dir_path);
|
|
// check the environment variable GGML_BACKEND_PATH to load an out-of-tree backend
|
|
const char * backend_path = std::getenv("GGML_BACKEND_PATH");
|
|
if (backend_path) {
|
|
ggml_backend_load(backend_path);
|
|
}
|
|
}
|