Project
Loading...
Searching...
No Matches
BoundedAllocator.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
15
16#ifndef TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_
17#define TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_
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
32
33namespace o2::its
34{
35
36// #define BOUNDED_MR_STATS
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 BoundedMemoryResource(size_t maxBytes = std::numeric_limits<size_t>::max(),
60 std::pmr::memory_resource* upstream = std::pmr::get_default_resource())
61 : mMaxMemory(maxBytes), mUpstream(upstream) {}
62
64 size_t maxBytes = std::numeric_limits<size_t>::max())
65 : mMaxMemory(maxBytes),
66 mAdaptor(std::make_unique<ExternalAllocatorAdaptor>(alloc)),
67 mUpstream(mAdaptor.get()) {}
68
69 void* do_allocate(size_t bytes, size_t alignment) final
70 {
71 size_t new_used{0};
72 size_t current_used{mUsedMemory.load(std::memory_order_relaxed)};
73 do {
74 new_used = current_used + bytes;
75 if (new_used > mMaxMemory.load(std::memory_order_relaxed)) {
76 mCountThrow.fetch_add(1, std::memory_order_relaxed);
77 throw MemoryLimitExceeded(new_used, current_used,
78 mMaxMemory.load(std::memory_order_relaxed));
79 }
80 } while (!mUsedMemory.compare_exchange_weak(current_used, new_used,
81 std::memory_order_acq_rel,
82 std::memory_order_relaxed));
83
84 void* p{nullptr};
85 try {
86 p = mUpstream->allocate(bytes, alignment);
87 } catch (...) {
88 mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed);
89#ifdef BOUNDED_MR_STATS
90 mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed);
91#endif
92 throw;
93 }
94
95#ifdef BOUNDED_MR_STATS
96 size_t peak = mStats.peak.load(std::memory_order_relaxed);
97 while (new_used > peak &&
98 !mStats.peak.compare_exchange_weak(peak, new_used,
99 std::memory_order_relaxed)) {
100 }
101 mStats.live.fetch_add(1, std::memory_order_relaxed);
102 mStats.nAlloc.fetch_add(1, std::memory_order_relaxed);
103 mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed);
104
105 size_t ma = mStats.maxAlign.load(std::memory_order_relaxed);
106 while (alignment > ma && !mStats.maxAlign.compare_exchange_weak(ma, alignment, std::memory_order_relaxed)) {
107 }
108#endif
109 return p;
110 }
111
112 void do_deallocate(void* p, size_t bytes, size_t alignment) final
113 {
114 mUpstream->deallocate(p, bytes, alignment);
115 mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed);
116#ifdef BOUNDED_MR_STATS
117 mStats.live.fetch_sub(1, std::memory_order_relaxed);
118 mStats.nFree.fetch_add(1, std::memory_order_relaxed);
119 mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed);
120#endif
121 }
122
123 bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final
124 {
125 return this == &other;
126 }
127
128 [[nodiscard]] size_t getUsedMemory() const noexcept
129 {
130 return mUsedMemory.load(std::memory_order_relaxed);
131 }
132 [[nodiscard]] size_t getMaxMemory() const noexcept
133 {
134 return mMaxMemory.load(std::memory_order_relaxed);
135 }
136 [[nodiscard]] size_t getThrowCount() const noexcept
137 {
138 return mCountThrow.load(std::memory_order_relaxed);
139 }
140
141 void setMaxMemory(size_t max)
142 {
143 size_t current = mMaxMemory.load(std::memory_order_relaxed);
144 if (max == current) {
145 return;
146 }
147 for (;;) {
148 size_t used = mUsedMemory.load(std::memory_order_acquire);
149 if (used > max) {
150 mCountThrow.fetch_add(1, std::memory_order_relaxed);
151 throw MemoryLimitExceeded(0, used, max);
152 }
153 if (mMaxMemory.compare_exchange_weak(current, max,
154 std::memory_order_release,
155 std::memory_order_relaxed)) {
156 return;
157 }
158 if (current == max) {
159 return;
160 }
161 }
162 }
163
164#if !defined(__HIPCC__) && !defined(__CUDACC__)
165 std::string asString() const
166 {
167 const auto throw_ = mCountThrow.load(std::memory_order_relaxed);
168 const auto used = static_cast<double>(mUsedMemory.load(std::memory_order_relaxed));
169 const auto maxm = mMaxMemory.load(std::memory_order_relaxed);
170 std::string ret;
171 if (maxm == std::numeric_limits<size_t>::max()) {
172 ret += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB", throw_, used / constants::GB);
173 } else {
174 ret += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%)", throw_, (double)maxm / constants::GB, used / constants::GB, 100.0 * used / (double)maxm);
175 }
176#ifdef BOUNDED_MR_STATS
177 ret += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}",
178 (float)mStats.peak.load(std::memory_order_relaxed) / constants::GB,
179 mStats.live.load(std::memory_order_relaxed),
180 mStats.nAlloc.load(std::memory_order_relaxed),
181 mStats.nFree.load(std::memory_order_relaxed),
182 (float)mStats.totalAlloc.load(std::memory_order_relaxed) / constants::GB,
183 (float)mStats.totalFreed.load(std::memory_order_relaxed) / constants::GB,
184 mStats.maxAlign.load(std::memory_order_relaxed),
185 mStats.upstreamFailures.load(std::memory_order_relaxed));
186#endif
187 return ret;
188 }
189
190 void print() const
191 {
192 LOGP(info, "{}", asString());
193 }
194#endif
195
196 private:
197 std::atomic<size_t> mMaxMemory{std::numeric_limits<size_t>::max()};
198 std::atomic<size_t> mCountThrow{0};
199 std::atomic<size_t> mUsedMemory{0};
200 std::unique_ptr<ExternalAllocatorAdaptor> mAdaptor{nullptr};
201 std::pmr::memory_resource* mUpstream{nullptr};
202
203#ifdef BOUNDED_MR_STATS
204 struct Stats {
205 std::atomic<size_t> peak{0};
206 std::atomic<size_t> live{0};
207 std::atomic<size_t> nAlloc{0};
208 std::atomic<size_t> nFree{0};
209 std::atomic<size_t> totalAlloc{0};
210 std::atomic<size_t> totalFreed{0};
211 std::atomic<size_t> maxAlign{0};
212 std::atomic<size_t> upstreamFailures{0};
213 };
214 Stats mStats{};
215#endif
216};
217
218template <typename T>
219using bounded_vector = std::pmr::vector<T>;
220
221template <typename T>
222inline void deepVectorClear(std::vector<T>& vec)
223{
224 std::vector<T>().swap(vec);
225}
226
227template <typename T>
229{
230 std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource();
231 vec.~bounded_vector<T>();
232 new (&vec) bounded_vector<T>(std::pmr::polymorphic_allocator<T>{tmr});
233}
234
235template <typename T>
236inline void deepVectorClear(std::vector<bounded_vector<T>>& vec, std::pmr::memory_resource* mr = nullptr)
237{
238 for (auto& v : vec) {
239 deepVectorClear(v, mr);
240 }
241}
242
243template <typename T, size_t S>
244inline void deepVectorClear(std::array<bounded_vector<T>, S>& arr, std::pmr::memory_resource* mr = nullptr)
245{
246 for (size_t i{0}; i < S; ++i) {
247 deepVectorClear(arr[i], mr);
248 }
249}
250
251template <typename T>
252inline void clearResizeBoundedVector(bounded_vector<T>& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T())
253{
254 std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource();
255 vec.~bounded_vector<T>();
256 new (&vec) bounded_vector<T>(sz, def, std::pmr::polymorphic_allocator<T>{tmr});
257}
258
259template <typename T>
261{
262 vec.clear();
263 vec.reserve(size);
264 for (size_t i = 0; i < size; ++i) {
265 vec.emplace_back(std::pmr::polymorphic_allocator<bounded_vector<T>>{mr});
266 }
267}
268
269template <typename T, size_t S>
270inline void clearResizeBoundedArray(std::array<bounded_vector<T>, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T())
271{
272 for (size_t i{0}; i < S; ++i) {
273 clearResizeBoundedVector(arr[i], size, mr, def);
274 }
275}
276
277template <typename T>
278inline std::vector<T> toSTDVector(const bounded_vector<T>& b)
279{
280 std::vector<T> t(b.size());
281 std::copy(b.cbegin(), b.cend(), t.begin());
282 return t;
283}
284
285} // namespace o2::its
286
287#endif
int32_t i
MemoryLimitExceeded(size_t attempted, size_t used, size_t max)
void * do_allocate(size_t bytes, size_t alignment) final
BoundedMemoryResource(ExternalAllocator *alloc, size_t maxBytes=std::numeric_limits< size_t >::max())
void do_deallocate(void *p, size_t bytes, size_t alignment) final
size_t getMaxMemory() const noexcept
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept final
BoundedMemoryResource(size_t maxBytes=std::numeric_limits< size_t >::max(), std::pmr::memory_resource *upstream=std::pmr::get_default_resource())
size_t getUsedMemory() const noexcept
size_t getThrowCount() const noexcept
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
auto get(const std::byte *buffer, size_t=0)
Definition DataHeader.h:454
constexpr float GB
Definition Constants.h:30
void deepVectorClear(std::vector< T > &vec)
std::pmr::vector< T > bounded_vector
std::vector< T > toSTDVector(const bounded_vector< T > &b)
void clearResizeBoundedArray(std::array< bounded_vector< T >, S > &arr, size_t size, std::pmr::memory_resource *mr=nullptr, T def=T())
void clearResizeBoundedVector(bounded_vector< T > &vec, size_t sz, std::pmr::memory_resource *mr=nullptr, T def=T())
constexpr size_t max
VectorOfTObjectPtrs other
std::vector< o2::ctf::BufferType > vec