/src/llama.cpp/ggml/src/ggml-backend-reg.cpp
Line | Count | Source |
1 | | #include "ggml-backend-impl.h" |
2 | | #include "ggml-backend.h" |
3 | | #include "ggml-impl.h" |
4 | | #include <algorithm> |
5 | | #include <cstring> |
6 | | #include <filesystem> |
7 | | #include <memory> |
8 | | #include <string> |
9 | | #include <type_traits> |
10 | | #include <vector> |
11 | | #include <cctype> |
12 | | |
13 | | #ifdef _WIN32 |
14 | | # define WIN32_LEAN_AND_MEAN |
15 | | # ifndef NOMINMAX |
16 | | # define NOMINMAX |
17 | | # endif |
18 | | # include <windows.h> |
19 | | #elif defined(__APPLE__) |
20 | | # include <mach-o/dyld.h> |
21 | | # include <dlfcn.h> |
22 | | #else |
23 | | # include <dlfcn.h> |
24 | | # include <unistd.h> |
25 | | #endif |
26 | | |
27 | | // Backend registry |
28 | | #ifdef GGML_USE_CPU |
29 | | #include "ggml-cpu.h" |
30 | | #endif |
31 | | |
32 | | #ifdef GGML_USE_CUDA |
33 | | #include "ggml-cuda.h" |
34 | | #endif |
35 | | |
36 | | #ifdef GGML_USE_METAL |
37 | | #include "ggml-metal.h" |
38 | | #endif |
39 | | |
40 | | #ifdef GGML_USE_SYCL |
41 | | #include "ggml-sycl.h" |
42 | | #endif |
43 | | |
44 | | #ifdef GGML_USE_VULKAN |
45 | | #include "ggml-vulkan.h" |
46 | | #endif |
47 | | |
48 | | #ifdef GGML_USE_WEBGPU |
49 | | #include "ggml-webgpu.h" |
50 | | #endif |
51 | | |
52 | | #ifdef GGML_USE_ZDNN |
53 | | #include "ggml-zdnn.h" |
54 | | #endif |
55 | | |
56 | | #ifdef GGML_USE_OPENCL |
57 | | #include "ggml-opencl.h" |
58 | | #endif |
59 | | |
60 | | #ifdef GGML_USE_HEXAGON |
61 | | #include "ggml-hexagon.h" |
62 | | #endif |
63 | | |
64 | | #ifdef GGML_USE_BLAS |
65 | | #include "ggml-blas.h" |
66 | | #endif |
67 | | |
68 | | #ifdef GGML_USE_RPC |
69 | | #include "ggml-rpc.h" |
70 | | #endif |
71 | | |
72 | | #ifdef GGML_USE_CANN |
73 | | #include "ggml-cann.h" |
74 | | #endif |
75 | | |
76 | | // disable C++17 deprecation warning for std::codecvt_utf8 |
77 | | #if defined(__clang__) |
78 | | # pragma clang diagnostic push |
79 | | # pragma clang diagnostic ignored "-Wdeprecated-declarations" |
80 | | #elif defined(__GNUC__) |
81 | | # pragma GCC diagnostic push |
82 | | # pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
83 | | #endif |
84 | | |
85 | | namespace fs = std::filesystem; |
86 | | |
87 | 0 | static std::string path_str(const fs::path & path) { |
88 | 0 | std::string u8path; |
89 | 0 | try { |
90 | | #if defined(__cpp_lib_char8_t) |
91 | | // C++20 and later: u8string() returns std::u8string |
92 | | std::u8string u8str = path.u8string(); |
93 | | u8path = std::string(reinterpret_cast<const char*>(u8str.c_str())); |
94 | | #else |
95 | | // C++17: u8string() returns std::string |
96 | 0 | u8path = path.u8string(); |
97 | 0 | #endif |
98 | 0 | } catch (...) { |
99 | 0 | } |
100 | 0 | return u8path; |
101 | 0 | } |
102 | | |
103 | | #if defined(__clang__) |
104 | | # pragma clang diagnostic pop |
105 | | #elif defined(__GNUC__) |
106 | | # pragma GCC diagnostic pop |
107 | | #endif |
108 | | |
109 | | #ifdef _WIN32 |
110 | | |
111 | | using dl_handle = std::remove_pointer_t<HMODULE>; |
112 | | |
113 | | struct dl_handle_deleter { |
114 | | void operator()(HMODULE handle) { |
115 | | FreeLibrary(handle); |
116 | | } |
117 | | }; |
118 | | |
119 | | static dl_handle * dl_load_library(const fs::path & path) { |
120 | | // suppress error dialogs for missing DLLs |
121 | | DWORD old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); |
122 | | SetErrorMode(old_mode | SEM_FAILCRITICALERRORS); |
123 | | |
124 | | HMODULE handle = LoadLibraryW(path.wstring().c_str()); |
125 | | |
126 | | SetErrorMode(old_mode); |
127 | | |
128 | | return handle; |
129 | | } |
130 | | |
131 | | static void * dl_get_sym(dl_handle * handle, const char * name) { |
132 | | DWORD old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); |
133 | | SetErrorMode(old_mode | SEM_FAILCRITICALERRORS); |
134 | | |
135 | | void * p = (void *) GetProcAddress(handle, name); |
136 | | |
137 | | SetErrorMode(old_mode); |
138 | | |
139 | | return p; |
140 | | } |
141 | | |
142 | | static const char * dl_error() { |
143 | | return ""; |
144 | | } |
145 | | |
146 | | #else |
147 | | |
148 | | using dl_handle = void; |
149 | | |
150 | | struct dl_handle_deleter { |
151 | 0 | void operator()(void * handle) { |
152 | 0 | dlclose(handle); |
153 | 0 | } |
154 | | }; |
155 | | |
156 | 0 | static void * dl_load_library(const fs::path & path) { |
157 | 0 | dl_handle * handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL); |
158 | |
|
159 | 0 | return handle; |
160 | 0 | } |
161 | | |
162 | 0 | static void * dl_get_sym(dl_handle * handle, const char * name) { |
163 | 0 | return dlsym(handle, name); |
164 | 0 | } |
165 | | |
166 | 0 | static const char * dl_error() { |
167 | 0 | const char *rslt = dlerror(); |
168 | 0 | return rslt != nullptr ? rslt : ""; |
169 | 0 | } |
170 | | |
171 | | #endif |
172 | | |
173 | | using dl_handle_ptr = std::unique_ptr<dl_handle, dl_handle_deleter>; |
174 | | |
175 | | struct ggml_backend_reg_entry { |
176 | | ggml_backend_reg_t reg; |
177 | | dl_handle_ptr handle; |
178 | | }; |
179 | | |
180 | | struct ggml_backend_registry { |
181 | | std::vector<ggml_backend_reg_entry> backends; |
182 | | std::vector<ggml_backend_dev_t> devices; |
183 | | |
184 | 0 | ggml_backend_registry() { |
185 | | #ifdef GGML_USE_CUDA |
186 | | register_backend(ggml_backend_cuda_reg()); |
187 | | #endif |
188 | | #ifdef GGML_USE_METAL |
189 | | register_backend(ggml_backend_metal_reg()); |
190 | | #endif |
191 | | #ifdef GGML_USE_SYCL |
192 | | register_backend(ggml_backend_sycl_reg()); |
193 | | #endif |
194 | | #ifdef GGML_USE_VULKAN |
195 | | register_backend(ggml_backend_vk_reg()); |
196 | | #endif |
197 | | #ifdef GGML_USE_WEBGPU |
198 | | register_backend(ggml_backend_webgpu_reg()); |
199 | | #endif |
200 | | #ifdef GGML_USE_ZDNN |
201 | | register_backend(ggml_backend_zdnn_reg()); |
202 | | #endif |
203 | | #ifdef GGML_USE_OPENCL |
204 | | register_backend(ggml_backend_opencl_reg()); |
205 | | #endif |
206 | | #ifdef GGML_USE_HEXAGON |
207 | | register_backend(ggml_backend_hexagon_reg()); |
208 | | #endif |
209 | | #ifdef GGML_USE_CANN |
210 | | register_backend(ggml_backend_cann_reg()); |
211 | | #endif |
212 | | #ifdef GGML_USE_BLAS |
213 | | register_backend(ggml_backend_blas_reg()); |
214 | | #endif |
215 | | #ifdef GGML_USE_RPC |
216 | | register_backend(ggml_backend_rpc_reg()); |
217 | | #endif |
218 | 0 | #ifdef GGML_USE_CPU |
219 | 0 | register_backend(ggml_backend_cpu_reg()); |
220 | 0 | #endif |
221 | 0 | } |
222 | | |
223 | 0 | ~ggml_backend_registry() { |
224 | | // FIXME: backends cannot be safely unloaded without a function to destroy all the backend resources, |
225 | | // since backend threads may still be running and accessing resources from the dynamic library |
226 | 0 | for (auto & entry : backends) { |
227 | 0 | if (entry.handle) { |
228 | 0 | entry.handle.release(); // NOLINT |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | 0 | void register_backend(ggml_backend_reg_t reg, dl_handle_ptr handle = nullptr) { |
234 | 0 | if (!reg) { |
235 | 0 | return; |
236 | 0 | } |
237 | | |
238 | | #ifndef NDEBUG |
239 | | GGML_LOG_DEBUG("%s: registered backend %s (%zu devices)\n", |
240 | | __func__, ggml_backend_reg_name(reg), ggml_backend_reg_dev_count(reg)); |
241 | | #endif |
242 | 0 | backends.push_back({ reg, std::move(handle) }); |
243 | 0 | for (size_t i = 0; i < ggml_backend_reg_dev_count(reg); i++) { |
244 | 0 | register_device(ggml_backend_reg_dev_get(reg, i)); |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | 0 | void register_device(ggml_backend_dev_t device) { |
249 | | #ifndef NDEBUG |
250 | | GGML_LOG_DEBUG("%s: registered device %s (%s)\n", __func__, ggml_backend_dev_name(device), ggml_backend_dev_description(device)); |
251 | | #endif |
252 | 0 | devices.push_back(device); |
253 | 0 | } |
254 | | |
255 | 0 | ggml_backend_reg_t load_backend(const fs::path & path, bool silent) { |
256 | 0 | dl_handle_ptr handle { dl_load_library(path) }; |
257 | 0 | if (!handle) { |
258 | 0 | if (!silent) { |
259 | 0 | GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_str(path).c_str(), dl_error()); |
260 | 0 | } |
261 | 0 | return nullptr; |
262 | 0 | } |
263 | | |
264 | 0 | auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); |
265 | 0 | if (score_fn && score_fn() == 0) { |
266 | 0 | if (!silent) { |
267 | 0 | GGML_LOG_INFO("%s: backend %s is not supported on this system\n", __func__, path_str(path).c_str()); |
268 | 0 | } |
269 | 0 | return nullptr; |
270 | 0 | } |
271 | | |
272 | 0 | auto backend_init_fn = (ggml_backend_init_t) dl_get_sym(handle.get(), "ggml_backend_init"); |
273 | 0 | if (!backend_init_fn) { |
274 | 0 | if (!silent) { |
275 | 0 | GGML_LOG_ERROR("%s: failed to find ggml_backend_init in %s\n", __func__, path_str(path).c_str()); |
276 | 0 | } |
277 | 0 | return nullptr; |
278 | 0 | } |
279 | | |
280 | 0 | ggml_backend_reg_t reg = backend_init_fn(); |
281 | 0 | if (!reg || reg->api_version != GGML_BACKEND_API_VERSION) { |
282 | 0 | if (!silent) { |
283 | 0 | if (!reg) { |
284 | 0 | GGML_LOG_ERROR("%s: failed to initialize backend from %s: ggml_backend_init returned NULL\n", |
285 | 0 | __func__, path_str(path).c_str()); |
286 | 0 | } else { |
287 | 0 | GGML_LOG_ERROR("%s: failed to initialize backend from %s: incompatible API version (backend: %d, current: %d)\n", |
288 | 0 | __func__, path_str(path).c_str(), reg->api_version, GGML_BACKEND_API_VERSION); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | return nullptr; |
292 | 0 | } |
293 | | |
294 | 0 | GGML_LOG_INFO("%s: loaded %s backend from %s\n", __func__, ggml_backend_reg_name(reg), path_str(path).c_str()); |
295 | |
|
296 | 0 | register_backend(reg, std::move(handle)); |
297 | |
|
298 | 0 | return reg; |
299 | 0 | } |
300 | | |
301 | 0 | void unload_backend(ggml_backend_reg_t reg, bool silent) { |
302 | 0 | auto it = std::find_if(backends.begin(), backends.end(), |
303 | 0 | [reg](const ggml_backend_reg_entry & entry) { return entry.reg == reg; }); |
304 | |
|
305 | 0 | if (it == backends.end()) { |
306 | 0 | if (!silent) { |
307 | 0 | GGML_LOG_ERROR("%s: backend not found\n", __func__); |
308 | 0 | } |
309 | 0 | return; |
310 | 0 | } |
311 | | |
312 | 0 | if (!silent) { |
313 | 0 | GGML_LOG_DEBUG("%s: unloading %s backend\n", __func__, ggml_backend_reg_name(reg)); |
314 | 0 | } |
315 | | |
316 | | // remove devices |
317 | 0 | devices.erase( |
318 | 0 | std::remove_if(devices.begin(), devices.end(), |
319 | 0 | [reg](ggml_backend_dev_t dev) { return ggml_backend_dev_backend_reg(dev) == reg; }), |
320 | 0 | devices.end()); |
321 | | |
322 | | // remove backend |
323 | 0 | backends.erase(it); |
324 | 0 | } |
325 | | }; |
326 | | |
327 | 0 | static ggml_backend_registry & get_reg() { |
328 | 0 | static ggml_backend_registry reg; |
329 | 0 | return reg; |
330 | 0 | } |
331 | | |
332 | | // Internal API |
333 | 0 | void ggml_backend_register(ggml_backend_reg_t reg) { |
334 | 0 | get_reg().register_backend(reg); |
335 | 0 | } |
336 | | |
337 | 0 | void ggml_backend_device_register(ggml_backend_dev_t device) { |
338 | 0 | get_reg().register_device(device); |
339 | 0 | } |
340 | | |
341 | | // Backend (reg) enumeration |
342 | 0 | static bool striequals(const char * a, const char * b) { |
343 | 0 | for (; *a && *b; a++, b++) { |
344 | 0 | if (std::tolower(*a) != std::tolower(*b)) { |
345 | 0 | return false; |
346 | 0 | } |
347 | 0 | } |
348 | 0 | return *a == *b; |
349 | 0 | } |
350 | | |
351 | 0 | size_t ggml_backend_reg_count() { |
352 | 0 | return get_reg().backends.size(); |
353 | 0 | } |
354 | | |
355 | 0 | ggml_backend_reg_t ggml_backend_reg_get(size_t index) { |
356 | 0 | GGML_ASSERT(index < ggml_backend_reg_count()); |
357 | 0 | return get_reg().backends[index].reg; |
358 | 0 | } |
359 | | |
360 | 0 | ggml_backend_reg_t ggml_backend_reg_by_name(const char * name) { |
361 | 0 | for (size_t i = 0; i < ggml_backend_reg_count(); i++) { |
362 | 0 | ggml_backend_reg_t reg = ggml_backend_reg_get(i); |
363 | 0 | if (striequals(ggml_backend_reg_name(reg), name)) { |
364 | 0 | return reg; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | return nullptr; |
368 | 0 | } |
369 | | |
370 | | // Device enumeration |
371 | 0 | size_t ggml_backend_dev_count() { |
372 | 0 | return get_reg().devices.size(); |
373 | 0 | } |
374 | | |
375 | 0 | ggml_backend_dev_t ggml_backend_dev_get(size_t index) { |
376 | 0 | GGML_ASSERT(index < ggml_backend_dev_count()); |
377 | 0 | return get_reg().devices[index]; |
378 | 0 | } |
379 | | |
380 | 0 | ggml_backend_dev_t ggml_backend_dev_by_name(const char * name) { |
381 | 0 | for (size_t i = 0; i < ggml_backend_dev_count(); i++) { |
382 | 0 | ggml_backend_dev_t dev = ggml_backend_dev_get(i); |
383 | 0 | if (striequals(ggml_backend_dev_name(dev), name)) { |
384 | 0 | return dev; |
385 | 0 | } |
386 | 0 | } |
387 | 0 | return nullptr; |
388 | 0 | } |
389 | | |
390 | 0 | ggml_backend_dev_t ggml_backend_dev_by_type(enum ggml_backend_dev_type type) { |
391 | 0 | for (size_t i = 0; i < ggml_backend_dev_count(); i++) { |
392 | 0 | ggml_backend_dev_t dev = ggml_backend_dev_get(i); |
393 | 0 | if (ggml_backend_dev_type(dev) == type) { |
394 | 0 | return dev; |
395 | 0 | } |
396 | 0 | } |
397 | 0 | return nullptr; |
398 | 0 | } |
399 | | |
400 | | // Convenience functions |
401 | 0 | ggml_backend_t ggml_backend_init_by_name(const char * name, const char * params) { |
402 | 0 | ggml_backend_dev_t dev = ggml_backend_dev_by_name(name); |
403 | 0 | if (!dev) { |
404 | 0 | return nullptr; |
405 | 0 | } |
406 | 0 | return ggml_backend_dev_init(dev, params); |
407 | 0 | } |
408 | | |
409 | 0 | ggml_backend_t ggml_backend_init_by_type(enum ggml_backend_dev_type type, const char * params) { |
410 | 0 | ggml_backend_dev_t dev = ggml_backend_dev_by_type(type); |
411 | 0 | if (!dev) { |
412 | 0 | return nullptr; |
413 | 0 | } |
414 | 0 | return ggml_backend_dev_init(dev, params); |
415 | 0 | } |
416 | | |
417 | 0 | ggml_backend_t ggml_backend_init_best(void) { |
418 | 0 | ggml_backend_dev_t dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_GPU); |
419 | 0 | dev = dev ? dev : ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_IGPU); |
420 | 0 | dev = dev ? dev : ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU); |
421 | 0 | if (!dev) { |
422 | 0 | return nullptr; |
423 | 0 | } |
424 | 0 | return ggml_backend_dev_init(dev, nullptr); |
425 | 0 | } |
426 | | |
427 | | // Dynamic loading |
428 | 0 | ggml_backend_reg_t ggml_backend_load(const char * path) { |
429 | 0 | return get_reg().load_backend(path, false); |
430 | 0 | } |
431 | | |
432 | 0 | void ggml_backend_unload(ggml_backend_reg_t reg) { |
433 | 0 | get_reg().unload_backend(reg, true); |
434 | 0 | } |
435 | | |
436 | 0 | static fs::path get_executable_path() { |
437 | | #if defined(__APPLE__) |
438 | | // get executable path |
439 | | std::vector<char> path; |
440 | | uint32_t size; |
441 | | while (true) { |
442 | | size = path.size(); |
443 | | if (_NSGetExecutablePath(path.data(), &size) == 0) { |
444 | | break; |
445 | | } |
446 | | path.resize(size); |
447 | | } |
448 | | std::string base_path(path.data(), size); |
449 | | // remove executable name |
450 | | auto last_slash = base_path.find_last_of('/'); |
451 | | if (last_slash != std::string::npos) { |
452 | | base_path = base_path.substr(0, last_slash); |
453 | | } |
454 | | return base_path + "/"; |
455 | | #elif defined(__linux__) || defined(__FreeBSD__) |
456 | | std::string base_path = "."; |
457 | 0 | std::vector<char> path(1024); |
458 | 0 | while (true) { |
459 | | // get executable path |
460 | 0 | # if defined(__linux__) |
461 | 0 | ssize_t len = readlink("/proc/self/exe", path.data(), path.size()); |
462 | | # elif defined(__FreeBSD__) |
463 | | ssize_t len = readlink("/proc/curproc/file", path.data(), path.size()); |
464 | | # endif |
465 | 0 | if (len == -1) { |
466 | 0 | break; |
467 | 0 | } |
468 | 0 | if (len < (ssize_t) path.size()) { |
469 | 0 | base_path = std::string(path.data(), len); |
470 | | // remove executable name |
471 | 0 | auto last_slash = base_path.find_last_of('/'); |
472 | 0 | if (last_slash != std::string::npos) { |
473 | 0 | base_path = base_path.substr(0, last_slash); |
474 | 0 | } |
475 | 0 | break; |
476 | 0 | } |
477 | 0 | path.resize(path.size() * 2); |
478 | 0 | } |
479 | |
|
480 | 0 | return base_path + "/"; |
481 | | #elif defined(_WIN32) |
482 | | std::vector<wchar_t> path(MAX_PATH); |
483 | | DWORD len = GetModuleFileNameW(NULL, path.data(), path.size()); |
484 | | if (len == 0) { |
485 | | return {}; |
486 | | } |
487 | | std::wstring base_path(path.data(), len); |
488 | | // remove executable name |
489 | | auto last_slash = base_path.find_last_of('\\'); |
490 | | if (last_slash != std::string::npos) { |
491 | | base_path = base_path.substr(0, last_slash); |
492 | | } |
493 | | return base_path + L"\\"; |
494 | | #else |
495 | | return {}; |
496 | | #endif |
497 | 0 | } |
498 | | |
499 | 0 | static fs::path backend_filename_prefix() { |
500 | | #ifdef _WIN32 |
501 | | return fs::u8path("ggml-"); |
502 | | #else |
503 | 0 | return fs::u8path("libggml-"); |
504 | 0 | #endif |
505 | 0 | } |
506 | | |
507 | 0 | static fs::path backend_filename_extension() { |
508 | | #ifdef _WIN32 |
509 | | return fs::u8path(".dll"); |
510 | | #else |
511 | 0 | return fs::u8path(".so"); |
512 | 0 | #endif |
513 | 0 | } |
514 | | |
515 | 0 | static ggml_backend_reg_t ggml_backend_load_best(const char * name, bool silent, const char * user_search_path) { |
516 | | // enumerate all the files that match [lib]ggml-name-*.[so|dll] in the search paths |
517 | 0 | const fs::path name_path = fs::u8path(name); |
518 | 0 | const fs::path file_prefix = backend_filename_prefix().native() + name_path.native() + fs::u8path("-").native(); |
519 | 0 | const fs::path file_extension = backend_filename_extension(); |
520 | |
|
521 | 0 | std::vector<fs::path> search_paths; |
522 | 0 | if (user_search_path == nullptr) { |
523 | | #ifdef GGML_BACKEND_DIR |
524 | | search_paths.push_back(fs::u8path(GGML_BACKEND_DIR)); |
525 | | #endif |
526 | | // default search paths: executable directory, current directory |
527 | 0 | search_paths.push_back(get_executable_path()); |
528 | 0 | search_paths.push_back(fs::current_path()); |
529 | 0 | } else { |
530 | 0 | search_paths.push_back(fs::u8path(user_search_path)); |
531 | 0 | } |
532 | |
|
533 | 0 | int best_score = 0; |
534 | 0 | fs::path best_path; |
535 | |
|
536 | 0 | for (const auto & search_path : search_paths) { |
537 | 0 | if (!fs::exists(search_path)) { |
538 | 0 | GGML_LOG_DEBUG("%s: search path %s does not exist\n", __func__, path_str(search_path).c_str()); |
539 | 0 | continue; |
540 | 0 | } |
541 | 0 | fs::directory_iterator dir_it(search_path, fs::directory_options::skip_permission_denied); |
542 | 0 | for (const auto & entry : dir_it) { |
543 | 0 | if (entry.is_regular_file()) { |
544 | 0 | auto filename = entry.path().filename(); |
545 | 0 | auto ext = entry.path().extension(); |
546 | 0 | if (filename.native().find(file_prefix) == 0 && ext == file_extension) { |
547 | 0 | dl_handle_ptr handle { dl_load_library(entry) }; |
548 | 0 | if (!handle && !silent) { |
549 | 0 | GGML_LOG_ERROR("%s: failed to load %s: %s\n", __func__, path_str(entry.path()).c_str(), dl_error()); |
550 | 0 | } |
551 | 0 | if (handle) { |
552 | 0 | auto score_fn = (ggml_backend_score_t) dl_get_sym(handle.get(), "ggml_backend_score"); |
553 | 0 | if (score_fn) { |
554 | 0 | int s = score_fn(); |
555 | | #ifndef NDEBUG |
556 | | GGML_LOG_DEBUG("%s: %s score: %d\n", __func__, path_str(entry.path()).c_str(), s); |
557 | | #endif |
558 | 0 | if (s > best_score) { |
559 | 0 | best_score = s; |
560 | 0 | best_path = entry.path(); |
561 | 0 | } |
562 | 0 | } else { |
563 | 0 | if (!silent) { |
564 | 0 | GGML_LOG_INFO("%s: failed to find ggml_backend_score in %s\n", __func__, path_str(entry.path()).c_str()); |
565 | 0 | } |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | 0 | } |
572 | |
|
573 | 0 | if (best_score == 0) { |
574 | | // try to load the base backend |
575 | 0 | for (const auto & search_path : search_paths) { |
576 | 0 | fs::path filename = backend_filename_prefix().native() + name_path.native() + backend_filename_extension().native(); |
577 | 0 | fs::path path = search_path / filename; |
578 | 0 | if (fs::exists(path)) { |
579 | 0 | return get_reg().load_backend(path, silent); |
580 | 0 | } |
581 | 0 | } |
582 | 0 | return nullptr; |
583 | 0 | } |
584 | | |
585 | 0 | return get_reg().load_backend(best_path, silent); |
586 | 0 | } |
587 | | |
588 | 0 | void ggml_backend_load_all() { |
589 | 0 | ggml_backend_load_all_from_path(nullptr); |
590 | 0 | } |
591 | | |
592 | 0 | void ggml_backend_load_all_from_path(const char * dir_path) { |
593 | 0 | #ifdef NDEBUG |
594 | 0 | bool silent = true; |
595 | | #else |
596 | | bool silent = false; |
597 | | #endif |
598 | |
|
599 | 0 | ggml_backend_load_best("blas", silent, dir_path); |
600 | 0 | ggml_backend_load_best("cann", silent, dir_path); |
601 | 0 | ggml_backend_load_best("cuda", silent, dir_path); |
602 | 0 | ggml_backend_load_best("hip", silent, dir_path); |
603 | 0 | ggml_backend_load_best("metal", silent, dir_path); |
604 | 0 | ggml_backend_load_best("rpc", silent, dir_path); |
605 | 0 | ggml_backend_load_best("sycl", silent, dir_path); |
606 | 0 | ggml_backend_load_best("vulkan", silent, dir_path); |
607 | 0 | ggml_backend_load_best("opencl", silent, dir_path); |
608 | 0 | ggml_backend_load_best("hexagon", silent, dir_path); |
609 | 0 | ggml_backend_load_best("musa", silent, dir_path); |
610 | 0 | ggml_backend_load_best("cpu", silent, dir_path); |
611 | | // check the environment variable GGML_BACKEND_PATH to load an out-of-tree backend |
612 | 0 | const char * backend_path = std::getenv("GGML_BACKEND_PATH"); |
613 | 0 | if (backend_path) { |
614 | 0 | ggml_backend_load(backend_path); |
615 | 0 | } |
616 | 0 | } |