Project
Loading...
Searching...
No Matches
BoundedAllocator.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 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.
11
13
14#include <cstdio>
15#include <format>
16
17#include "GPUCommonLogger.h"
19
21{
22
24{
25 char buf[256];
26 if (attempted != 0) {
27 (void)snprintf(buf, sizeof(buf), "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", attempted, used, max);
28 } else {
29 (void)snprintf(buf, sizeof(buf), "New set maximum below current used (newMax: %zu, used: %zu)", max, used);
30 }
31 mMsg = buf;
32}
33
35{
36 return mMsg.c_str();
37}
38
40{
41 static std::pmr::synchronized_pool_resource pool{std::pmr::get_default_resource()};
42 return &pool;
43}
44
46 : mMaxMemory(maxBytes), mUpstream(upstream != nullptr ? upstream : cachingUpstream())
47{
48}
49
50BoundedMemoryResource::BoundedMemoryResource(std::unique_ptr<std::pmr::memory_resource> upstream, size_t maxBytes)
51 : mMaxMemory(maxBytes), mOwnedUpstream(std::move(upstream)), mUpstream(mOwnedUpstream.get())
52{
53}
54
55void* BoundedMemoryResource::do_allocate(size_t bytes, size_t alignment)
56{
57 size_t newUsed{0};
58 size_t currentUsed{mUsedMemory.load(std::memory_order_relaxed)};
59 do {
60 newUsed = currentUsed + bytes;
61 if (newUsed > mMaxMemory.load(std::memory_order_relaxed)) {
62 mCountThrow.fetch_add(1, std::memory_order_relaxed);
63 throw MemoryLimitExceeded(newUsed, currentUsed, mMaxMemory.load(std::memory_order_relaxed));
64 }
65 } while (!mUsedMemory.compare_exchange_weak(currentUsed, newUsed, std::memory_order_acq_rel, std::memory_order_relaxed));
66
67 void* p{nullptr};
68 try {
69 p = mUpstream->allocate(bytes, alignment);
70 } catch (...) {
71 mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed);
72#ifdef BOUNDED_MR_STATS
73 mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed);
74#endif
75 throw;
76 }
77
78 size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed);
79 while (newUsed > peak && !mPeakUsedMemory.compare_exchange_weak(peak, newUsed, std::memory_order_relaxed)) {
80 }
81
82#ifdef BOUNDED_MR_STATS
83 size_t statsPeak = mStats.peak.load(std::memory_order_relaxed);
84 while (newUsed > statsPeak && !mStats.peak.compare_exchange_weak(statsPeak, newUsed, std::memory_order_relaxed)) {
85 }
86 mStats.live.fetch_add(1, std::memory_order_relaxed);
87 mStats.nAlloc.fetch_add(1, std::memory_order_relaxed);
88 mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed);
89
90 size_t maxAlignment = mStats.maxAlign.load(std::memory_order_relaxed);
91 while (alignment > maxAlignment && !mStats.maxAlign.compare_exchange_weak(maxAlignment, alignment, std::memory_order_relaxed)) {
92 }
93#endif
94 return p;
95}
96
97void BoundedMemoryResource::do_deallocate(void* p, size_t bytes, size_t alignment)
98{
99 mUpstream->deallocate(p, bytes, alignment);
100 mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed);
101#ifdef BOUNDED_MR_STATS
102 mStats.live.fetch_sub(1, std::memory_order_relaxed);
103 mStats.nFree.fetch_add(1, std::memory_order_relaxed);
104 mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed);
105#endif
106}
107
108bool BoundedMemoryResource::do_is_equal(const std::pmr::memory_resource& other) const noexcept
109{
110 return this == &other;
111}
112
114{
115 return mUsedMemory.load(std::memory_order_relaxed);
116}
117
119{
120 return mMaxMemory.load(std::memory_order_relaxed);
121}
122
124{
125 return mCountThrow.load(std::memory_order_relaxed);
126}
127
129{
130 return mPeakUsedMemory.load(std::memory_order_relaxed);
131}
132
134{
135 const size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed);
136 const size_t baseline = mPeakBaselineMemory.load(std::memory_order_relaxed);
137 return peak > baseline ? peak - baseline : 0;
138}
139
141{
142 const size_t used = mUsedMemory.load(std::memory_order_acquire);
143 mPeakBaselineMemory.store(used, std::memory_order_release);
144 mPeakUsedMemory.store(used, std::memory_order_release);
145}
146
148{
149 size_t current = mMaxMemory.load(std::memory_order_relaxed);
150 if (max == current) {
151 return;
152 }
153 for (;;) {
154 const size_t used = mUsedMemory.load(std::memory_order_acquire);
155 if (used > max) {
156 mCountThrow.fetch_add(1, std::memory_order_relaxed);
157 throw MemoryLimitExceeded(0, used, max);
158 }
159 if (mMaxMemory.compare_exchange_weak(current, max, std::memory_order_release, std::memory_order_relaxed)) {
160 return;
161 }
162 if (current == max) {
163 return;
164 }
165 }
166}
167
169{
170 const auto throwCount = mCountThrow.load(std::memory_order_relaxed);
171 const auto used = static_cast<double>(mUsedMemory.load(std::memory_order_relaxed));
172 const auto peak = static_cast<double>(mPeakUsedMemory.load(std::memory_order_relaxed));
173 const auto peakDelta = static_cast<double>(getPeakMemoryDelta());
174 const auto maxMemory = mMaxMemory.load(std::memory_order_relaxed);
175 std::string result;
176 if (maxMemory == std::numeric_limits<size_t>::max()) {
177 result += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB stagepeak={:.2f} GB stagealloc={:.2f} GB", throwCount, used / o2::its::constants::GB, peak / o2::its::constants::GB, peakDelta / o2::its::constants::GB);
178 } else {
179 result += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%) stagepeak={:.2f} GB stagealloc={:.2f} GB", throwCount, static_cast<double>(maxMemory) / o2::its::constants::GB, used / o2::its::constants::GB, 100.0 * used / static_cast<double>(maxMemory), peak / o2::its::constants::GB, peakDelta / o2::its::constants::GB);
180 }
181#ifdef BOUNDED_MR_STATS
182 result += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}",
183 static_cast<float>(mStats.peak.load(std::memory_order_relaxed)) / o2::its::constants::GB,
184 mStats.live.load(std::memory_order_relaxed),
185 mStats.nAlloc.load(std::memory_order_relaxed),
186 mStats.nFree.load(std::memory_order_relaxed),
187 static_cast<float>(mStats.totalAlloc.load(std::memory_order_relaxed)) / o2::its::constants::GB,
188 static_cast<float>(mStats.totalFreed.load(std::memory_order_relaxed)) / o2::its::constants::GB,
189 mStats.maxAlign.load(std::memory_order_relaxed),
190 mStats.upstreamFailures.load(std::memory_order_relaxed));
191#endif
192 return result;
193}
194
196{
197 LOGP(info, "{}", asString());
198}
199
200} // namespace o2::itsmft::tracking
MemoryLimitExceeded(size_t attempted, size_t used, size_t max)
static std::pmr::memory_resource * cachingUpstream()
BoundedMemoryResource(size_t maxBytes=std::numeric_limits< size_t >::max(), std::pmr::memory_resource *upstream=nullptr)
GLuint64EXT * result
Definition glcorearb.h:5662
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:27
constexpr size_t max
VectorOfTObjectPtrs other