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