|
16 | 16 | #ifndef TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ |
17 | 17 | #define TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ |
18 | 18 |
|
19 | | -#include <limits> |
20 | | -#include <memory_resource> |
21 | | -#include <atomic> |
22 | | -#include <new> |
23 | | -#include <vector> |
24 | | - |
25 | | -#if !defined(__HIPCC__) && !defined(__CUDACC__) |
26 | | -#include <format> |
27 | | -#include <string> |
28 | | -#include "GPUCommonLogger.h" |
29 | | -#endif |
30 | | -#include "ITStracking/ExternalAllocator.h" |
31 | | -#include "ITStracking/Constants.h" |
| 19 | +#include "ITSMFTTracking/BoundedAllocator.h" |
32 | 20 |
|
33 | 21 | namespace o2::its |
34 | 22 | { |
35 | 23 |
|
36 | | -// #define BOUNDED_MR_STATS |
37 | | -class BoundedMemoryResource final : public std::pmr::memory_resource |
38 | | -{ |
39 | | - public: |
40 | | - class MemoryLimitExceeded final : public std::bad_alloc |
41 | | - { |
42 | | - public: |
43 | | - MemoryLimitExceeded(size_t attempted, size_t used, size_t max) |
44 | | - { |
45 | | - char buf[256]; |
46 | | - if (attempted != 0) { |
47 | | - (void)snprintf(buf, sizeof(buf), "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", attempted, used, max); |
48 | | - } else { |
49 | | - (void)snprintf(buf, sizeof(buf), "New set maximum below current used (newMax: %zu, used: %zu)", max, used); |
50 | | - } |
51 | | - mMsg = buf; |
52 | | - } |
53 | | - const char* what() const noexcept final { return mMsg.c_str(); } |
54 | | - |
55 | | - private: |
56 | | - std::string mMsg; |
57 | | - }; |
58 | | - |
59 | | - static std::pmr::memory_resource* cachingUpstream() |
60 | | - { |
61 | | - static std::pmr::synchronized_pool_resource pool{std::pmr::get_default_resource()}; |
62 | | - return &pool; |
63 | | - } |
64 | | - |
65 | | - BoundedMemoryResource(size_t maxBytes = std::numeric_limits<size_t>::max(), |
66 | | - std::pmr::memory_resource* upstream = nullptr) |
67 | | - : mMaxMemory(maxBytes), mUpstream(upstream != nullptr ? upstream : cachingUpstream()) {} |
68 | | - |
69 | | - BoundedMemoryResource(ExternalAllocator* alloc, |
70 | | - size_t maxBytes = std::numeric_limits<size_t>::max()) |
71 | | - : mMaxMemory(maxBytes), |
72 | | - mAdaptor(std::make_unique<ExternalAllocatorAdaptor>(alloc)), |
73 | | - mUpstream(mAdaptor.get()) {} |
74 | | - |
75 | | - void* do_allocate(size_t bytes, size_t alignment) final |
76 | | - { |
77 | | - size_t new_used{0}; |
78 | | - size_t current_used{mUsedMemory.load(std::memory_order_relaxed)}; |
79 | | - do { |
80 | | - new_used = current_used + bytes; |
81 | | - if (new_used > mMaxMemory.load(std::memory_order_relaxed)) { |
82 | | - mCountThrow.fetch_add(1, std::memory_order_relaxed); |
83 | | - throw MemoryLimitExceeded(new_used, current_used, |
84 | | - mMaxMemory.load(std::memory_order_relaxed)); |
85 | | - } |
86 | | - } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, |
87 | | - std::memory_order_acq_rel, |
88 | | - std::memory_order_relaxed)); |
89 | | - |
90 | | - void* p{nullptr}; |
91 | | - try { |
92 | | - p = mUpstream->allocate(bytes, alignment); |
93 | | - } catch (...) { |
94 | | - mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); |
95 | | -#ifdef BOUNDED_MR_STATS |
96 | | - mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed); |
97 | | -#endif |
98 | | - throw; |
99 | | - } |
100 | | - |
101 | | - size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed); |
102 | | - while (new_used > peak && |
103 | | - !mPeakUsedMemory.compare_exchange_weak(peak, new_used, |
104 | | - std::memory_order_relaxed)) { |
105 | | - } |
106 | | - |
107 | | -#ifdef BOUNDED_MR_STATS |
108 | | - size_t statsPeak = mStats.peak.load(std::memory_order_relaxed); |
109 | | - while (new_used > statsPeak && |
110 | | - !mStats.peak.compare_exchange_weak(statsPeak, new_used, |
111 | | - std::memory_order_relaxed)) { |
112 | | - } |
113 | | - mStats.live.fetch_add(1, std::memory_order_relaxed); |
114 | | - mStats.nAlloc.fetch_add(1, std::memory_order_relaxed); |
115 | | - mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed); |
116 | | - |
117 | | - size_t ma = mStats.maxAlign.load(std::memory_order_relaxed); |
118 | | - while (alignment > ma && !mStats.maxAlign.compare_exchange_weak(ma, alignment, std::memory_order_relaxed)) { |
119 | | - } |
120 | | -#endif |
121 | | - return p; |
122 | | - } |
123 | | - |
124 | | - void do_deallocate(void* p, size_t bytes, size_t alignment) final |
125 | | - { |
126 | | - mUpstream->deallocate(p, bytes, alignment); |
127 | | - mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); |
128 | | -#ifdef BOUNDED_MR_STATS |
129 | | - mStats.live.fetch_sub(1, std::memory_order_relaxed); |
130 | | - mStats.nFree.fetch_add(1, std::memory_order_relaxed); |
131 | | - mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed); |
132 | | -#endif |
133 | | - } |
134 | | - |
135 | | - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final |
136 | | - { |
137 | | - return this == &other; |
138 | | - } |
139 | | - |
140 | | - [[nodiscard]] size_t getUsedMemory() const noexcept |
141 | | - { |
142 | | - return mUsedMemory.load(std::memory_order_relaxed); |
143 | | - } |
144 | | - [[nodiscard]] size_t getMaxMemory() const noexcept |
145 | | - { |
146 | | - return mMaxMemory.load(std::memory_order_relaxed); |
147 | | - } |
148 | | - [[nodiscard]] size_t getThrowCount() const noexcept |
149 | | - { |
150 | | - return mCountThrow.load(std::memory_order_relaxed); |
151 | | - } |
152 | | - [[nodiscard]] size_t getPeakMemory() const noexcept |
153 | | - { |
154 | | - return mPeakUsedMemory.load(std::memory_order_relaxed); |
155 | | - } |
156 | | - [[nodiscard]] size_t getPeakMemoryDelta() const noexcept |
157 | | - { |
158 | | - const size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed); |
159 | | - const size_t baseline = mPeakBaselineMemory.load(std::memory_order_relaxed); |
160 | | - return peak > baseline ? peak - baseline : 0; |
161 | | - } |
162 | | - |
163 | | - void resetPeakMemory() noexcept |
164 | | - { |
165 | | - const size_t used = mUsedMemory.load(std::memory_order_acquire); |
166 | | - mPeakBaselineMemory.store(used, std::memory_order_release); |
167 | | - mPeakUsedMemory.store(used, std::memory_order_release); |
168 | | - } |
169 | | - |
170 | | - void setMaxMemory(size_t max) |
171 | | - { |
172 | | - size_t current = mMaxMemory.load(std::memory_order_relaxed); |
173 | | - if (max == current) { |
174 | | - return; |
175 | | - } |
176 | | - for (;;) { |
177 | | - size_t used = mUsedMemory.load(std::memory_order_acquire); |
178 | | - if (used > max) { |
179 | | - mCountThrow.fetch_add(1, std::memory_order_relaxed); |
180 | | - throw MemoryLimitExceeded(0, used, max); |
181 | | - } |
182 | | - if (mMaxMemory.compare_exchange_weak(current, max, |
183 | | - std::memory_order_release, |
184 | | - std::memory_order_relaxed)) { |
185 | | - return; |
186 | | - } |
187 | | - if (current == max) { |
188 | | - return; |
189 | | - } |
190 | | - } |
191 | | - } |
192 | | - |
193 | | -#if !defined(__HIPCC__) && !defined(__CUDACC__) |
194 | | - std::string asString() const |
195 | | - { |
196 | | - const auto throw_ = mCountThrow.load(std::memory_order_relaxed); |
197 | | - const auto used = static_cast<double>(mUsedMemory.load(std::memory_order_relaxed)); |
198 | | - const auto peak = static_cast<double>(mPeakUsedMemory.load(std::memory_order_relaxed)); |
199 | | - const auto peakDelta = static_cast<double>(getPeakMemoryDelta()); |
200 | | - const auto maxm = mMaxMemory.load(std::memory_order_relaxed); |
201 | | - std::string ret; |
202 | | - if (maxm == std::numeric_limits<size_t>::max()) { |
203 | | - ret += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB stagepeak={:.2f} GB stagealloc={:.2f} GB", throw_, used / constants::GB, peak / constants::GB, peakDelta / constants::GB); |
204 | | - } else { |
205 | | - ret += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%) stagepeak={:.2f} GB stagealloc={:.2f} GB", throw_, (double)maxm / constants::GB, used / constants::GB, 100.0 * used / (double)maxm, peak / constants::GB, peakDelta / constants::GB); |
206 | | - } |
207 | | -#ifdef BOUNDED_MR_STATS |
208 | | - ret += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}", |
209 | | - (float)mStats.peak.load(std::memory_order_relaxed) / constants::GB, |
210 | | - mStats.live.load(std::memory_order_relaxed), |
211 | | - mStats.nAlloc.load(std::memory_order_relaxed), |
212 | | - mStats.nFree.load(std::memory_order_relaxed), |
213 | | - (float)mStats.totalAlloc.load(std::memory_order_relaxed) / constants::GB, |
214 | | - (float)mStats.totalFreed.load(std::memory_order_relaxed) / constants::GB, |
215 | | - mStats.maxAlign.load(std::memory_order_relaxed), |
216 | | - mStats.upstreamFailures.load(std::memory_order_relaxed)); |
217 | | -#endif |
218 | | - return ret; |
219 | | - } |
220 | | - |
221 | | - void print() const |
222 | | - { |
223 | | - LOGP(info, "{}", asString()); |
224 | | - } |
225 | | -#endif |
226 | | - |
227 | | - private: |
228 | | - std::atomic<size_t> mMaxMemory{std::numeric_limits<size_t>::max()}; |
229 | | - std::atomic<size_t> mCountThrow{0}; |
230 | | - std::atomic<size_t> mUsedMemory{0}; |
231 | | - std::atomic<size_t> mPeakUsedMemory{0}; |
232 | | - std::atomic<size_t> mPeakBaselineMemory{0}; |
233 | | - std::unique_ptr<ExternalAllocatorAdaptor> mAdaptor{nullptr}; |
234 | | - std::pmr::memory_resource* mUpstream{nullptr}; |
235 | | - |
236 | | -#ifdef BOUNDED_MR_STATS |
237 | | - struct Stats { |
238 | | - std::atomic<size_t> peak{0}; |
239 | | - std::atomic<size_t> live{0}; |
240 | | - std::atomic<size_t> nAlloc{0}; |
241 | | - std::atomic<size_t> nFree{0}; |
242 | | - std::atomic<size_t> totalAlloc{0}; |
243 | | - std::atomic<size_t> totalFreed{0}; |
244 | | - std::atomic<size_t> maxAlign{0}; |
245 | | - std::atomic<size_t> upstreamFailures{0}; |
246 | | - }; |
247 | | - Stats mStats{}; |
248 | | -#endif |
249 | | -}; |
250 | | - |
| 24 | +using o2::itsmft::tracking::BoundedMemoryResource; |
251 | 25 | template <typename T> |
252 | | -using bounded_vector = std::pmr::vector<T>; |
253 | | - |
254 | | -template <typename T> |
255 | | -inline void deepVectorClear(std::vector<T>& vec) |
256 | | -{ |
257 | | - std::vector<T>().swap(vec); |
258 | | -} |
259 | | - |
260 | | -template <typename T> |
261 | | -inline void deepVectorClear(bounded_vector<T>& vec, std::pmr::memory_resource* mr = nullptr) |
262 | | -{ |
263 | | - std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); |
264 | | - vec.~bounded_vector<T>(); |
265 | | - new (&vec) bounded_vector<T>(std::pmr::polymorphic_allocator<T>{tmr}); |
266 | | -} |
267 | | - |
268 | | -template <typename T> |
269 | | -inline void deepVectorClear(std::vector<bounded_vector<T>>& vec, std::pmr::memory_resource* mr = nullptr) |
270 | | -{ |
271 | | - for (auto& v : vec) { |
272 | | - deepVectorClear(v, mr); |
273 | | - } |
274 | | -} |
275 | | - |
276 | | -template <typename T, size_t S> |
277 | | -inline void deepVectorClear(std::array<bounded_vector<T>, S>& arr, std::pmr::memory_resource* mr = nullptr) |
278 | | -{ |
279 | | - for (size_t i{0}; i < S; ++i) { |
280 | | - deepVectorClear(arr[i], mr); |
281 | | - } |
282 | | -} |
283 | | - |
284 | | -template <typename T> |
285 | | -inline void clearResizeBoundedVector(bounded_vector<T>& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) |
286 | | -{ |
287 | | - std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); |
288 | | - vec.~bounded_vector<T>(); |
289 | | - new (&vec) bounded_vector<T>(sz, def, std::pmr::polymorphic_allocator<T>{tmr}); |
290 | | -} |
291 | | - |
292 | | -template <typename T> |
293 | | -inline void clearResizeBoundedVector(std::vector<bounded_vector<T>>& vec, size_t size, std::pmr::memory_resource* mr) |
294 | | -{ |
295 | | - vec.clear(); |
296 | | - vec.reserve(size); |
297 | | - for (size_t i = 0; i < size; ++i) { |
298 | | - vec.emplace_back(std::pmr::polymorphic_allocator<bounded_vector<T>>{mr}); |
299 | | - } |
300 | | -} |
301 | | - |
302 | | -template <typename T, size_t S> |
303 | | -inline void clearResizeBoundedArray(std::array<bounded_vector<T>, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) |
304 | | -{ |
305 | | - for (size_t i{0}; i < S; ++i) { |
306 | | - clearResizeBoundedVector(arr[i], size, mr, def); |
307 | | - } |
308 | | -} |
309 | | - |
310 | | -template <typename T> |
311 | | -inline std::vector<T> toSTDVector(const bounded_vector<T>& b) |
312 | | -{ |
313 | | - std::vector<T> t(b.size()); |
314 | | - std::copy(b.cbegin(), b.cend(), t.begin()); |
315 | | - return t; |
316 | | -} |
| 26 | +using bounded_vector = o2::itsmft::tracking::bounded_vector<T>; |
| 27 | +using o2::itsmft::tracking::clearResizeBoundedArray; |
| 28 | +using o2::itsmft::tracking::clearResizeBoundedVector; |
| 29 | +using o2::itsmft::tracking::deepVectorClear; |
| 30 | +using o2::itsmft::tracking::toSTDVector; |
317 | 31 |
|
318 | 32 | } // namespace o2::its |
319 | 33 |
|
320 | | -#endif |
| 34 | +#endif /* TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ */ |
0 commit comments