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