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/nntrainer/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 0 : void Engine::release() { thread_pool_manager_.reset(); }
66 :
67 : std::string
68 7066 : Engine::parseComputeEngine(const std::vector<std::string> &props) const {
69 39954 : for (auto &prop : props) {
70 : std::string key, value;
71 32888 : int status = nntrainer::getKeyValue(prop, key, value);
72 65776 : if (nntrainer::istrequal(key, "engine")) {
73 : constexpr const auto data =
74 : std::data(props::ComputeEngineTypeInfo::EnumList);
75 0 : for (unsigned int i = 0;
76 0 : i < props::ComputeEngineTypeInfo::EnumList.size(); ++i) {
77 0 : if (nntrainer::istrequal(value.c_str(),
78 0 : props::ComputeEngineTypeInfo::EnumStr[i])) {
79 0 : return props::ComputeEngineTypeInfo::EnumStr[i];
80 : }
81 : }
82 : }
83 : }
84 :
85 7066 : return "cpu";
86 : }
87 :
88 : /**
89 : * @brief Get the Full Path from given string
90 : * @details path is resolved in the following order
91 : * 1) if @a path is absolute, return path
92 : * ----------------------------------------
93 : * 2) if @a base == "" && @a path == "", return "."
94 : * 3) if @a base == "" && @a path != "", return @a path
95 : * 4) if @a base != "" && @a path == "", return @a base
96 : * 5) if @a base != "" && @a path != "", return @a base + "/" + path
97 : *
98 : * @param path path to calculate from base
99 : * @param base base path
100 : * @return const std::string
101 : */
102 6234 : const std::string getFullPath(const std::string &path,
103 : const std::string &base) {
104 : /// if path is absolute, return path
105 6234 : if (path[0] == '/') {
106 : return path;
107 : }
108 :
109 5556 : if (base == std::string()) {
110 4880 : return path == std::string() ? "." : path;
111 : }
112 :
113 6232 : return path == std::string() ? base : base + "/" + path;
114 : }
115 :
116 6234 : const std::string Engine::getWorkingPath(const std::string &path) const {
117 6234 : return getFullPath(path, working_path_base);
118 : }
119 :
120 673 : void Engine::setWorkingDirectory(const std::string &base) {
121 673 : std::filesystem::path base_path(base);
122 :
123 673 : if (!std::filesystem::is_directory(base_path)) {
124 1 : std::stringstream ss;
125 : ss << func_tag << "path is not directory or has no permission: " << base;
126 3 : throw std::invalid_argument(ss.str().c_str());
127 1 : }
128 :
129 672 : char *ret = getRealpath(base.c_str(), nullptr);
130 :
131 672 : if (ret == nullptr) {
132 0 : std::stringstream ss;
133 0 : ss << func_tag << "failed to get canonical path for the path: ";
134 0 : throw std::invalid_argument(ss.str().c_str());
135 0 : }
136 :
137 1344 : working_path_base = std::string(ret);
138 672 : ml_logd("working path base has set: %s", working_path_base.c_str());
139 672 : free(ret);
140 673 : }
141 :
142 0 : int Engine::registerContext(const std::string &library_path,
143 : const std::string &base_path) {
144 0 : const std::string full_path = getFullPath(library_path, base_path);
145 :
146 : void *handle = DynamicLibraryLoader::loadLibrary(full_path.c_str(),
147 : RTLD_LAZY | RTLD_LOCAL);
148 : const char *error_msg = DynamicLibraryLoader::getLastError();
149 :
150 0 : NNTR_THROW_IF(handle == nullptr, std::invalid_argument)
151 : << func_tag << "open plugin failed, reason: " << error_msg;
152 :
153 : nntrainer::ContextPluggable *pluggable =
154 : reinterpret_cast<nntrainer::ContextPluggable *>(
155 : DynamicLibraryLoader::loadSymbol(handle, "ml_train_context_pluggable"));
156 :
157 : error_msg = DynamicLibraryLoader::getLastError();
158 0 : auto close_dl = [handle] { DynamicLibraryLoader::freeLibrary(handle); };
159 0 : NNTR_THROW_IF_CLEANUP(error_msg != nullptr || pluggable == nullptr,
160 : std::invalid_argument, close_dl)
161 : << func_tag << "loading symbol failed, reason: " << error_msg;
162 :
163 0 : auto context = pluggable->createfunc();
164 0 : NNTR_THROW_IF_CLEANUP(context == nullptr, std::invalid_argument, close_dl)
165 : << func_tag << "created pluggable context is null";
166 0 : auto type = context->getName();
167 0 : NNTR_THROW_IF_CLEANUP(type == "", std::invalid_argument, close_dl)
168 : << func_tag << "custom layer must specify type name, but it is empty";
169 :
170 0 : registerContext(type, context);
171 :
172 0 : return 0;
173 : }
174 :
175 0 : ThreadPoolManager *Engine::getThreadPoolManager() {
176 0 : std::lock_guard<std::mutex> lock(thread_pool_manager_mutex_);
177 :
178 0 : if (!thread_pool_manager_) {
179 0 : thread_pool_manager_ = std::make_unique<ThreadPoolManager>();
180 : }
181 :
182 0 : return thread_pool_manager_.get();
183 : }
184 :
185 : } // namespace nntrainer
|