Line | Branch | Exec | Source |
---|---|---|---|
1 | #ifndef LYTHON_UTILITIES_ALLOCATOR_HEADER | ||
2 | #define LYTHON_UTILITIES_ALLOCATOR_HEADER | ||
3 | |||
4 | #include <functional> | ||
5 | #include <memory> | ||
6 | #include <unordered_map> | ||
7 | #include <vector> | ||
8 | |||
9 | #include "logging/logging.h" | ||
10 | |||
11 | namespace lython { | ||
12 | |||
13 | void show_alloc_stats(); | ||
14 | |||
15 | namespace meta { | ||
16 | |||
17 | // NOTE: All those should not depend on each other during deinit time | ||
18 | // https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2 | ||
19 | struct Stat { | ||
20 | int allocated = 0; | ||
21 | int deallocated = 0; | ||
22 | int bytes = 0; | ||
23 | int size_alloc = 0; | ||
24 | int size_free = 0; | ||
25 | int startup_count = 0; | ||
26 | }; | ||
27 | |||
28 | bool& is_type_registry_available(); | ||
29 | |||
30 | struct TypeRegistry { | ||
31 | std::vector<Stat> stat; | ||
32 | bool print_stats = false; | ||
33 | std::unordered_map<int, std::string> id_to_name; | ||
34 | int type_counter = 0; | ||
35 | |||
36 | static TypeRegistry& instance() { | ||
37 |
4/8✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6662 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
6670 | static TypeRegistry obj; |
38 | 6670 | return obj; | |
39 | } | ||
40 | |||
41 | TypeRegistry() { is_type_registry_available() = true; } | ||
42 | |||
43 | ~TypeRegistry() { | ||
44 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (print_stats) { |
45 | ✗ | show_alloc_stats(); | |
46 | } | ||
47 | |||
48 | 8 | is_type_registry_available() = false; | |
49 | 8 | } | |
50 | }; | ||
51 | |||
52 | inline std::vector<Stat>& stats() { return TypeRegistry::instance().stat; } | ||
53 | |||
54 | inline int& _get_id() { return TypeRegistry::instance().type_counter; } | ||
55 | |||
56 | inline int _new_id() { | ||
57 | 631 | auto r = _get_id(); | |
58 | 631 | _get_id() += 1; | |
59 |
1/2✓ Branch 3 taken 631 times.
✗ Branch 4 not taken.
|
631 | stats().push_back(Stat()); |
60 | 631 | return r; | |
61 | } | ||
62 | |||
63 | inline std::unordered_map<int, std::string>& typenames() { | ||
64 | 903 | return TypeRegistry::instance().id_to_name; | |
65 | } | ||
66 | |||
67 | // Generate a unique ID for a given type | ||
68 | template <typename T> | ||
69 | int type_id() { | ||
70 |
4/8✓ Branch 0 taken 2392 times.
✓ Branch 1 taken 133217 times.
✓ Branch 3 taken 2392 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2392 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
268072 | static int _id = _new_id(); |
71 | 268072 | return _id; | |
72 | } | ||
73 | |||
74 | template <typename T> | ||
75 | int _register_type_once(const char* str) { | ||
76 |
4/4✓ Branch 1 taken 672 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 664 times.
|
1343 | if (!is_type_registry_available()) |
77 | 16 | return 0; | |
78 | |||
79 |
1/2✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
|
1327 | auto tid = type_id<T>(); |
80 |
2/4✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
|
1327 | auto result = typenames().find(tid); |
81 | |||
82 |
3/4✓ Branch 1 taken 666 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 131 times.
✓ Branch 6 taken 535 times.
|
1327 | if (result == typenames().end()) { |
83 |
4/8✓ Branch 1 taken 131 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 131 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 131 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 131 times.
✗ Branch 11 not taken.
|
259 | typenames().insert({type_id<T>(), str}); |
84 | } | ||
85 | 1327 | return tid; | |
86 | } | ||
87 | |||
88 | // Insert a type name override | ||
89 | template <typename T> | ||
90 | void override_typename(const char* str) { | ||
91 |
1/1✓ Branch 1 taken 2340 times.
|
4680 | auto tid = type_id<T>(); |
92 |
3/3✓ Branch 1 taken 2340 times.
✓ Branch 4 taken 2340 times.
✓ Branch 7 taken 2340 times.
|
4680 | typenames()[tid] = str; |
93 | 4680 | } | |
94 | |||
95 | // Insert a type name override | ||
96 | template <typename T> | ||
97 | 108 | const char* register_type(const char* str) { | |
98 |
4/8✓ Branch 0 taken 566 times.
✓ Branch 1 taken 53216 times.
✓ Branch 3 taken 566 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 566 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
106886 | static int _ = _register_type_once<T>(str); |
99 | 106886 | return str; | |
100 | } | ||
101 | |||
102 | // Return the type name of a function | ||
103 | // You can specialize it to override | ||
104 | template <typename T> | ||
105 | const char* type_name() { | ||
106 | auto result = typenames().find(type_id<T>()); | ||
107 | |||
108 | if (result == typenames().end()) { | ||
109 | const char* name = typeid(T).name(); | ||
110 | register_type<T>(name); | ||
111 | return "<none>"; | ||
112 | } | ||
113 | |||
114 | return (result->second).c_str(); | ||
115 | }; | ||
116 | |||
117 | inline const char* type_name(int class_id) { | ||
118 | 568 | std::string const& name = typenames()[class_id]; | |
119 | 568 | return name.c_str(); | |
120 | }; | ||
121 | |||
122 | template <typename T> | ||
123 | Stat& get_stat() { | ||
124 | 320220 | return stats()[type_id<T>()]; | |
125 | } | ||
126 | |||
127 | // When type info is not available at compile time | ||
128 | // often when deleting a derived class | ||
129 | inline Stat& get_stat(int class_id) { return stats()[class_id]; } | ||
130 | |||
131 | } // namespace meta | ||
132 | |||
133 | inline void show_alloc_stats_on_destroy(bool enabled) { | ||
134 | 1 | meta::TypeRegistry::instance().print_stats = enabled; | |
135 | 1 | } | |
136 | |||
137 | namespace device { | ||
138 | |||
139 | template <typename Device> | ||
140 | class DeviceAllocatorTrait { | ||
141 | static void* malloc(std::size_t n) { return Device::malloc(n); } | ||
142 | |||
143 | static bool free(void* ptr, std::size_t n) { return Device::free(ptr, n); } | ||
144 | }; | ||
145 | |||
146 | #ifdef __CUDACC__ | ||
147 | struct CUDA: public DeviceAllocatorTrait<CUDA> { | ||
148 | void* malloc(std::size_t n); | ||
149 | bool free(void* ptr, std::size_t n); | ||
150 | }; | ||
151 | #endif | ||
152 | |||
153 | struct CPU: public DeviceAllocatorTrait<CPU> { | ||
154 | static void* malloc(std::size_t n); | ||
155 | |||
156 | static bool free(void* ptr, std::size_t n); | ||
157 | }; | ||
158 | |||
159 | } // namespace device | ||
160 | |||
161 | inline void manual_free(int class_id, std::size_t n) { | ||
162 | 2439 | meta::get_stat(class_id).deallocated += 1; | |
163 | 2439 | meta::get_stat(class_id).size_free += int(n); | |
164 | 2439 | } | |
165 | |||
166 | template <typename T, typename Device> | ||
167 | class Allocator { | ||
168 | public: | ||
169 | using size_type = std::size_t; | ||
170 | using difference_type = std::ptrdiff_t; | ||
171 | using pointer = T*; | ||
172 | using const_pointer = const T*; | ||
173 | using reference = T&; | ||
174 | using const_reference = const T&; | ||
175 | using value_type = T; | ||
176 | |||
177 | template <typename _Tp1> | ||
178 | struct rebind { | ||
179 | using other = Allocator<_Tp1, Device>; | ||
180 | }; | ||
181 | |||
182 | bool operator==(Allocator const&) const { return true; } | ||
183 | |||
184 | bool operator!=(Allocator const& alloc) const { return !(*this == alloc); } | ||
185 | |||
186 | // template <typename... Args> | ||
187 | // static void construct(T *value, Args &&...args) { | ||
188 | // new ((void *)value) T(std::forward<Args>(args)...); | ||
189 | // } | ||
190 | |||
191 | static void deallocate(pointer p, std::size_t n) { | ||
192 | 47766 | manual_free(meta::type_id<T>(), n); | |
193 | 47766 | Device::free(static_cast<void*>(p), n * sizeof(T)); | |
194 | 47766 | return; | |
195 | } | ||
196 | |||
197 | static T* allocate(std::size_t n, const void* = nullptr) { | ||
198 | 106740 | meta::register_type<T>(typeid(T).name()); | |
199 | 106740 | meta::get_stat<T>().allocated += 1; | |
200 | 106740 | meta::get_stat<T>().size_alloc += int(n); | |
201 | 106740 | meta::get_stat<T>().bytes = int(sizeof(T)); | |
202 | 106740 | return static_cast<T*>(Device::malloc(n * sizeof(T))); | |
203 | } | ||
204 | |||
205 | Allocator() noexcept {} | ||
206 | |||
207 | Allocator(const Allocator& a) noexcept {} | ||
208 | |||
209 | template <class U> | ||
210 | Allocator(const Allocator<U, Device>& a) noexcept {} | ||
211 | |||
212 | ~Allocator() noexcept = default; | ||
213 | }; | ||
214 | |||
215 | template <typename V> | ||
216 | using SharedPtr = std::shared_ptr<V>; | ||
217 | |||
218 | template <typename _Tp, typename... _Args> | ||
219 | inline SharedPtr<_Tp> make_shared(_Args&&... __args) { | ||
220 | typedef typename std::remove_cv<_Tp>::type _Tp_nc; | ||
221 | return std::allocate_shared<_Tp>(Allocator<_Tp_nc, device::CPU>(), | ||
222 | std::forward<_Args>(__args)...); | ||
223 | } | ||
224 | |||
225 | template <typename V> | ||
226 | using UniquePtr = std::unique_ptr<V>; | ||
227 | |||
228 | template <typename _Tp, typename... _Args> | ||
229 | inline UniquePtr<_Tp> make_unique(_Args&&... __args) { | ||
230 | auto ptr = Allocator<_Tp, device::CPU>().allocate(1); | ||
231 | return UniquePtr<_Tp>(new (ptr) _Tp(std::forward<_Args>(__args)...)); | ||
232 | } | ||
233 | |||
234 | } // namespace lython | ||
235 | |||
236 | #endif | ||
237 |