Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2024 Jijoong Moon <jijoong.moon@samsung.com>
4 : *
5 : * @file engine.cpp
6 : * @date 27 December 2024
7 : * @brief This file contains engine context related functions and classes that
8 : * manages the engines (NPU, GPU, CPU) of the current environment
9 : * @see https://github.com/nnstreamer/nntrainer
10 : * @author Jijoong Moon <jijoong.moon@samsung.com>
11 : * @bug No known bugs except for NYI items
12 : *
13 : */
14 : #include <filesystem>
15 : #include <iostream>
16 : #include <sstream>
17 : #include <string>
18 : #include <vector>
19 :
20 : #include <app_context.h>
21 : #include <base_properties.h>
22 : #include <context.h>
23 : #include <dynamic_library_loader.h>
24 : #include <engine.h>
25 :
26 : static std::string solib_suffix = ".so";
27 : static std::string contextlib_suffix = "context.so";
28 : static const std::string func_tag = "[Engine] ";
29 :
30 : namespace nntrainer {
31 :
32 : std::mutex engine_mutex;
33 :
34 : std::once_flag global_engine_init_flag;
35 :
36 : nntrainer::Context
37 : *Engine::nntrainerRegisteredContext[Engine::RegisterContextMax];
38 :
39 27 : void Engine::add_default_object() {
40 : /// @note all layers should be added to the app_context to guarantee that
41 : /// createLayer/createOptimizer class is created
42 :
43 27 : auto &app_context = nntrainer::AppContext::Global();
44 :
45 27 : init_backend(); // initialize cpu backend
46 27 : registerContext("cpu", &app_context);
47 :
48 : #ifdef ENABLE_OPENCL
49 : auto &cl_context = nntrainer::ClContext::Global();
50 :
51 : registerContext("gpu", &cl_context);
52 : #endif
53 27 : }
54 :
55 27 : void Engine::initialize() noexcept {
56 : try {
57 27 : add_default_object();
58 0 : } catch (std::exception &e) {
59 0 : ml_loge("registering layers failed!!, reason: %s", e.what());
60 0 : } catch (...) {
61 0 : ml_loge("registering layer failed due to unknown reason");
62 0 : }
63 27 : };
64 :
65 : std::string
66 7065 : Engine::parseComputeEngine(const std::vector<std::string> &props) const {
67 39953 : for (auto &prop : props) {
68 : std::string key, value;
69 32888 : int status = nntrainer::getKeyValue(prop, key, value);
70 65776 : if (nntrainer::istrequal(key, "engine")) {
71 : constexpr const auto data =
72 : std::data(props::ComputeEngineTypeInfo::EnumList);
73 0 : for (unsigned int i = 0;
74 0 : i < props::ComputeEngineTypeInfo::EnumList.size(); ++i) {
75 0 : if (nntrainer::istrequal(value.c_str(),
76 0 : props::ComputeEngineTypeInfo::EnumStr[i])) {
77 0 : return props::ComputeEngineTypeInfo::EnumStr[i];
78 : }
79 : }
80 : }
81 : }
82 :
83 7065 : return "cpu";
84 : }
85 :
86 : /**
87 : * @brief Get the Full Path from given string
88 : * @details path is resolved in the following order
89 : * 1) if @a path is absolute, return path
90 : * ----------------------------------------
91 : * 2) if @a base == "" && @a path == "", return "."
92 : * 3) if @a base == "" && @a path != "", return @a path
93 : * 4) if @a base != "" && @a path == "", return @a base
94 : * 5) if @a base != "" && @a path != "", return @a base + "/" + path
95 : *
96 : * @param path path to calculate from base
97 : * @param base base path
98 : * @return const std::string
99 : */
100 6234 : const std::string getFullPath(const std::string &path,
101 : const std::string &base) {
102 : /// if path is absolute, return path
103 6234 : if (path[0] == '/') {
104 : return path;
105 : }
106 :
107 5556 : if (base == std::string()) {
108 4880 : return path == std::string() ? "." : path;
109 : }
110 :
111 6232 : return path == std::string() ? base : base + "/" + path;
112 : }
113 :
114 6234 : const std::string Engine::getWorkingPath(const std::string &path) const {
115 6234 : return getFullPath(path, working_path_base);
116 : }
117 :
118 673 : void Engine::setWorkingDirectory(const std::string &base) {
119 673 : std::filesystem::path base_path(base);
120 :
121 673 : if (!std::filesystem::is_directory(base_path)) {
122 1 : std::stringstream ss;
123 : ss << func_tag << "path is not directory or has no permission: " << base;
124 3 : throw std::invalid_argument(ss.str().c_str());
125 1 : }
126 :
127 672 : char *ret = getRealpath(base.c_str(), nullptr);
128 :
129 672 : if (ret == nullptr) {
130 0 : std::stringstream ss;
131 0 : ss << func_tag << "failed to get canonical path for the path: ";
132 0 : throw std::invalid_argument(ss.str().c_str());
133 0 : }
134 :
135 1344 : working_path_base = std::string(ret);
136 672 : ml_logd("working path base has set: %s", working_path_base.c_str());
137 672 : free(ret);
138 673 : }
139 :
140 0 : int Engine::registerContext(const std::string &library_path,
141 : const std::string &base_path) {
142 0 : const std::string full_path = getFullPath(library_path, base_path);
143 :
144 : void *handle = DynamicLibraryLoader::loadLibrary(full_path.c_str(),
145 : RTLD_LAZY | RTLD_LOCAL);
146 : const char *error_msg = DynamicLibraryLoader::getLastError();
147 :
148 0 : NNTR_THROW_IF(handle == nullptr, std::invalid_argument)
149 : << func_tag << "open plugin failed, reason: " << error_msg;
150 :
151 : nntrainer::ContextPluggable *pluggable =
152 : reinterpret_cast<nntrainer::ContextPluggable *>(
153 : DynamicLibraryLoader::loadSymbol(handle, "ml_train_context_pluggable"));
154 :
155 : error_msg = DynamicLibraryLoader::getLastError();
156 0 : auto close_dl = [handle] { DynamicLibraryLoader::freeLibrary(handle); };
157 0 : NNTR_THROW_IF_CLEANUP(error_msg != nullptr || pluggable == nullptr,
158 : std::invalid_argument, close_dl)
159 : << func_tag << "loading symbol failed, reason: " << error_msg;
160 :
161 0 : auto context = pluggable->createfunc();
162 0 : NNTR_THROW_IF_CLEANUP(context == nullptr, std::invalid_argument, close_dl)
163 : << func_tag << "created pluggable context is null";
164 0 : auto type = context->getName();
165 0 : NNTR_THROW_IF_CLEANUP(type == "", std::invalid_argument, close_dl)
166 : << func_tag << "custom layer must specify type name, but it is empty";
167 :
168 0 : registerContext(type, context);
169 :
170 0 : return 0;
171 : }
172 :
173 : } // namespace nntrainer
|