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