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.h
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 :
15 : #ifndef __ENGINE_H__
16 : #define __ENGINE_H__
17 :
18 : #include <algorithm>
19 : #include <functional>
20 : #include <memory>
21 : #include <mutex>
22 : #include <sstream>
23 : #include <stdexcept>
24 : #include <string>
25 : #include <type_traits>
26 : #include <unordered_map>
27 : #include <vector>
28 :
29 : #include <context.h>
30 : #include <mem_allocator.h>
31 : #include <nntrainer_error.h>
32 :
33 : #ifdef ENABLE_OPENCL
34 : #include <cl_context.h>
35 : #endif
36 :
37 : #include "bs_thread_pool_manager.hpp"
38 : #include "singleton.h"
39 :
40 : namespace nntrainer {
41 :
42 : extern std::mutex engine_mutex;
43 : namespace {} // namespace
44 :
45 : /**
46 : * @class Engine contains user-dependent configuration
47 : * @brief App
48 : */
49 : class Engine : public Singleton<Engine> {
50 : protected:
51 : static const int RegisterContextMax = 16;
52 : static nntrainer::Context *nntrainerRegisteredContext[RegisterContextMax];
53 : /// Valgrind complains memory leaks with context registered because
54 : /// every context is alive during the whole application lifecycle
55 : /// and we do not free them. It can be amended by using unique_ptr;
56 : /// however, as we use container and function calls with context,
57 : /// let's not bother modify all the related functions, but waste
58 : /// a few words.
59 :
60 : void initialize() noexcept override;
61 :
62 : void add_default_object();
63 :
64 27 : void registerContext(std::string name, nntrainer::Context *context) {
65 : const std::lock_guard<std::mutex> lock(engine_mutex);
66 : static int registerCount = 0;
67 :
68 : std::transform(name.begin(), name.end(), name.begin(),
69 81 : [](unsigned char c) { return std::tolower(c); });
70 :
71 27 : if (engines.find(name) != engines.end()) {
72 0 : std::stringstream ss;
73 : ss << "Cannot register Context with name : " << name;
74 0 : throw std::invalid_argument(ss.str().c_str());
75 0 : }
76 0 : engines.insert(std::make_pair(name, context));
77 :
78 27 : if (registerCount < RegisterContextMax) {
79 27 : nntrainerRegisteredContext[registerCount] = context;
80 27 : registerCount++;
81 : }
82 :
83 27 : auto alloc = context->getMemAllocator();
84 :
85 27 : allocator.insert(std::make_pair(name, alloc));
86 27 : }
87 :
88 : public:
89 : /**
90 : * @brief Default constructor
91 : */
92 693 : Engine() = default;
93 :
94 : /**
95 : * @brief Default Destructor
96 : */
97 1359 : ~Engine() = default;
98 :
99 : /**
100 : * @brief Release resources allocated by Engine
101 : *
102 : */
103 : void release();
104 :
105 : /**
106 : * @brief register a Context from a shared library
107 : * plugin must have **extern "C" LayerPluggable *ml_train_context_pluggable**
108 : * defined else error
109 : *
110 : * @param library_path a file name of the library
111 : * @param base_path base path to make a full path (optional)
112 : * @throws std::invalid_parameter if library_path is invalid or library is
113 : * invalid
114 : */
115 : int registerContext(const std::string &library_path,
116 : const std::string &base_path = "");
117 :
118 : /**
119 : * @brief get registered a Context
120 : *
121 : * @param name Registered Context Name
122 : * @throws std::invalid_parameter if no context with name
123 : * @return Context Pointer : for register Object factory, casting might be
124 : * needed.
125 : */
126 11595 : nntrainer::Context *getRegisteredContext(std::string name) const {
127 :
128 : std::transform(name.begin(), name.end(), name.begin(),
129 34785 : [](unsigned char c) { return std::tolower(c); });
130 :
131 11595 : if (engines.find(name) == engines.end()) {
132 0 : throw std::invalid_argument("[Engine] " + name +
133 0 : " Context is not registered");
134 : }
135 11595 : return engines.at(name);
136 : }
137 :
138 : std::unordered_map<std::string, std::shared_ptr<nntrainer::MemAllocator>>
139 : getAllocators() {
140 : return allocator;
141 : }
142 :
143 : /**
144 : *
145 : * @brief Get pointer to thread pool manager, contruct it if needed
146 : * @return Pointer to thread pool manager
147 : */
148 : ThreadPoolManager *getThreadPoolManager();
149 :
150 : /**
151 : *
152 : * @brief Parse compute Engine keywords in properties : eg) engine = cpu
153 : * default is "cpu"
154 : * @return Context name
155 : */
156 : std::string parseComputeEngine(const std::vector<std::string> &props) const;
157 :
158 : /**
159 : * @brief Create an Layer Object with Layer name
160 : *
161 : * @param type layer name
162 : * @param props property
163 : * @return unitque_ptr<T> unique pointer to the Layer object
164 : */
165 : std::unique_ptr<nntrainer::Layer>
166 6126 : createLayerObject(const std::string &type,
167 : const std::vector<std::string> &properties = {}) const {
168 6126 : auto ct = getRegisteredContext(parseComputeEngine(properties));
169 6126 : return ct->createLayerObject(type);
170 : }
171 :
172 : /**
173 : * @brief Create an Layer Object with Layer key
174 : *
175 : * @param int_key key
176 : * @param props property
177 : * @return unitque_ptr<T> unique pointer to the Layer object
178 : */
179 : std::unique_ptr<nntrainer::Layer>
180 91 : createLayerObject(const int int_key,
181 : const std::vector<std::string> &properties = {}) const {
182 91 : auto ct = getRegisteredContext(parseComputeEngine(properties));
183 91 : return ct->createLayerObject(int_key);
184 : }
185 :
186 : /**
187 : * @brief Create an Optimizer Object with Optimizer name
188 : *
189 : * @param type Optimizer name
190 : * @param props property
191 : * @return unitque_ptr<T> unique pointer to the Optimizer object
192 : */
193 : std::unique_ptr<nntrainer::Optimizer>
194 743 : createOptimizerObject(const std::string &type,
195 : const std::vector<std::string> &properties = {}) const {
196 743 : auto ct = getRegisteredContext(parseComputeEngine(properties));
197 743 : return ct->createOptimizerObject(type);
198 : }
199 :
200 : /**
201 : * @brief Create an Optimizer Object with Optimizer key
202 : *
203 : * @param int_key key
204 : * @param props property
205 : * @return unitque_ptr<T> unique pointer to the Optimizer object
206 : */
207 : std::unique_ptr<nntrainer::Optimizer>
208 30 : createOptimizerObject(const int int_key,
209 : const std::vector<std::string> &properties = {}) const {
210 30 : auto ct = getRegisteredContext(parseComputeEngine(properties));
211 30 : return ct->createOptimizerObject(int_key);
212 : }
213 :
214 : /**
215 : * @brief Create an LearningRateScheduler Object with type
216 : *
217 : * @param type type of LearningRateScheduler
218 : * @param props property
219 : * @return unitque_ptr<T> unique pointer to the LearningRateScheduler object
220 : */
221 : std::unique_ptr<ml::train::LearningRateScheduler>
222 54 : createLearningRateSchedulerObject(
223 : const std::string &type,
224 : const std::vector<std::string> &properties = {}) const {
225 54 : auto ct = getRegisteredContext(parseComputeEngine(properties));
226 54 : return ct->createLearningRateSchedulerObject(type, properties);
227 : }
228 :
229 : /**
230 : * @brief Create an LearningRateScheduler Object with key
231 : *
232 : * @param int_key key
233 : * @param props property
234 : * @return unitque_ptr<T> unique pointer to the LearningRateScheduler object
235 : */
236 : std::unique_ptr<ml::train::LearningRateScheduler>
237 22 : createLearningRateSchedulerObject(
238 : const int int_key, const std::vector<std::string> &properties = {}) {
239 22 : auto ct = getRegisteredContext(parseComputeEngine(properties));
240 22 : return ct->createLearningRateSchedulerObject(int_key, properties);
241 : }
242 :
243 : /**
244 : * @brief Get Working Path from a relative or representation of a path
245 : * starting from @a working_path_base.
246 : * @param[in] path to make full path
247 : * @return If absolute path is given, returns @a path
248 : * If relative path is given and working_path_base is not set, return
249 : * relative path.
250 : * If relative path is given and working_path_base has set, return absolute
251 : * path from current working directory
252 : */
253 : const std::string getWorkingPath(const std::string &path = "") const;
254 :
255 : /**
256 : * @brief Set Working Directory for a relative path. working directory is set
257 : * canonically
258 : * @param[in] base base directory
259 : * @throw std::invalid_argument if path is not valid for current system
260 : */
261 : void setWorkingDirectory(const std::string &base);
262 :
263 : /**
264 : * @brief unset working directory
265 : *
266 : */
267 2 : void unsetWorkingDirectory() { working_path_base = ""; }
268 :
269 : /**
270 : * @brief query if the appcontext has working directory set
271 : *
272 : * @retval true working path base is set
273 : * @retval false working path base is not set
274 : */
275 : bool hasWorkingDirectory() { return !working_path_base.empty(); }
276 :
277 : private:
278 : /**
279 : * @brief map for Context and Context name
280 : *
281 : */
282 : std::unordered_map<std::string, nntrainer::Context *> engines;
283 :
284 : std::unordered_map<std::string, std::shared_ptr<nntrainer::MemAllocator>>
285 : allocator;
286 :
287 : std::string working_path_base;
288 :
289 : std::mutex thread_pool_manager_mutex_ = {};
290 : std::unique_ptr<ThreadPoolManager> thread_pool_manager_ = {};
291 : };
292 :
293 : namespace plugin {}
294 :
295 : } // namespace nntrainer
296 :
297 : #endif /* __ENGINE_H__ */
|