| 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 |