Skip to content

Commit fa3743f

Browse files
authored
CArena: shrink_in_place and operator<< (AMReX-Codes#3621)
## Summary Implement CArena::shrink_in_place, which is used by PODVector::shrink_to_fit. It avoids a new memory allocation and data movement. Add operator<< to CArena. This helps debugging. ## Additional background Follow-up on AMReX-Codes#3426. ## Checklist The proposed changes: - [ ] fix a bug or incorrect behavior in AMReX - [x] add new capabilities to AMReX - [ ] changes answers in the test suite to more than roundoff level - [ ] are likely to significantly affect the results of downstream AMReX users - [ ] include documentation in the code and/or rst files, if appropriate
1 parent 6e2b831 commit fa3743f

File tree

2 files changed

+105
-10
lines changed

2 files changed

+105
-10
lines changed

Src/Base/AMReX_CArena.H

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
#include <AMReX_Arena.H>
66

77
#include <cstddef>
8-
#include <set>
9-
#include <vector>
8+
#include <functional>
9+
#include <iosfwd>
1010
#include <map>
1111
#include <mutex>
12-
#include <unordered_set>
13-
#include <functional>
12+
#include <set>
1413
#include <string>
14+
#include <unordered_set>
15+
#include <vector>
1516

1617
namespace amrex {
1718

@@ -57,7 +58,7 @@ public:
5758
* Try to shrink in-place
5859
*/
5960
[[nodiscard]] void*
60-
shrink_in_place (void* pt, std::size_t sz) final;
61+
shrink_in_place (void* pt, std::size_t new_size) final;
6162

6263
/**
6364
* \brief Free up allocated memory. Merge neighboring free memory chunks
@@ -164,15 +165,15 @@ protected:
164165
MemStat* m_stat;
165166
};
166167

168+
//! The list of blocks allocated via ::operator new().
169+
std::vector<std::pair<void*,std::size_t> > m_alloc;
170+
167171
/**
168172
* \brief The type of our freelist and blocklist.
169173
* We use a set sorted from lo to hi memory addresses.
170174
*/
171175
using NL = std::set<Node>;
172176

173-
//! The list of blocks allocated via ::operator new().
174-
std::vector<std::pair<void*,std::size_t> > m_alloc;
175-
176177
/**
177178
* \brief The free list of allocated but not currently used blocks.
178179
* Maintained in lo to hi memory sorted order.
@@ -198,6 +199,8 @@ protected:
198199

199200

200201
std::mutex carena_mutex;
202+
203+
friend std::ostream& operator<< (std::ostream& os, const CArena& arena);
201204
};
202205

203206
}

Src/Base/AMReX_CArena.cpp

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace amrex {
1414

1515
#include <utility>
1616
#include <cstring>
17+
#include <iostream>
1718

1819
namespace amrex {
1920

@@ -203,9 +204,61 @@ CArena::alloc_in_place (void* pt, std::size_t szmin, std::size_t szmax)
203204
}
204205

205206
void*
206-
CArena::shrink_in_place (void* /*pt*/, std::size_t sz)
207+
CArena::shrink_in_place (void* pt, std::size_t new_size)
207208
{
208-
return alloc(sz); // xxxxx TODO
209+
if ((pt == nullptr) || (new_size == 0)) { return nullptr; }
210+
211+
new_size = Arena::align(new_size);
212+
213+
std::lock_guard<std::mutex> lock(carena_mutex);
214+
215+
auto busy_it = m_busylist.find(Node(pt,nullptr,0));
216+
if (busy_it == m_busylist.end()) {
217+
amrex::Abort("CArena::shrink_in_place: unknown pointer");
218+
return nullptr;
219+
}
220+
AMREX_ASSERT(m_freelist.find(*busy_it) == m_freelist.end());
221+
222+
auto const old_size = busy_it->size();
223+
224+
if (new_size > old_size) {
225+
amrex::Abort("CArena::shrink_in_place: wrong size. Cannot shrink to a larger size.");
226+
return nullptr;
227+
} else if (new_size == old_size) {
228+
return pt;
229+
} else {
230+
auto const leftover_size = old_size - new_size;
231+
232+
void* pt2 = static_cast<char*>(pt) + new_size;
233+
Node new_free_node(pt2, busy_it->owner(), leftover_size);
234+
235+
void* pt_end = static_cast<char*>(pt) + old_size;
236+
auto free_it = m_freelist.find(Node(pt_end,nullptr,0));
237+
if ((free_it == m_freelist.end()) || ! new_free_node.coalescable(*free_it)) {
238+
m_freelist.insert(free_it, new_free_node);
239+
} else {
240+
auto& node = const_cast<Node&>(*free_it);
241+
// This is safe because the free list is std::set and the
242+
// modification of `block` does not change the order of elements
243+
// in the container, even though Node's operator< uses block.
244+
node.block(pt2);
245+
node.size(leftover_size + node.size());
246+
}
247+
248+
const_cast<Node&>(*busy_it).size(new_size);
249+
250+
m_actually_used -= leftover_size;
251+
252+
#ifdef AMREX_TINY_PROFILING
253+
if (m_do_profiling) {
254+
TinyProfiler::memory_free(old_size, busy_it->mem_stat());
255+
auto* stat = TinyProfiler::memory_alloc(new_size, m_profiling_stats);
256+
const_cast<Node&>(*busy_it).mem_stat(stat);
257+
}
258+
#endif
259+
260+
return pt;
261+
}
209262
}
210263

211264
void
@@ -439,4 +492,43 @@ CArena::PrintUsage (std::ostream& os, std::string const& name, std::string const
439492
<< m_busylist.size() << " busy blocks, " << m_freelist.size() << " free blocks\n";
440493
}
441494

495+
std::ostream& operator<< (std::ostream& os, const CArena& arena)
496+
{
497+
os << "CArea:\n"
498+
<< " Hunk size: " << arena.m_hunk << "\n"
499+
<< " Memory allocated: " << arena.m_used << "\n"
500+
<< " Memory actually used: " << arena.m_actually_used << "\n";
501+
502+
if (arena.m_alloc.empty()) {
503+
os << " No memory allocations\n";
504+
} else {
505+
os << " List of memory alloations: (address, size)\n";
506+
for (auto const& a : arena.m_alloc) {
507+
os << " " << a.first << ", " << a.second << "\n";
508+
}
509+
}
510+
511+
if (arena.m_freelist.empty()) {
512+
os << " No free nodes\n";
513+
} else {
514+
os << " List of free nodes: (address, owner, size)\n";
515+
for (auto const& a : arena.m_freelist) {
516+
os << " " << a.block() << ", " << a.owner() << ", "
517+
<< a.size() << "\n";
518+
}
519+
}
520+
521+
if (arena.m_busylist.empty()) {
522+
os << " No busy nodes\n";
523+
} else {
524+
os << " List of busy nodes: (address, owner, size)\n";
525+
for (auto const& a : arena.m_busylist) {
526+
os << " " << a.block() << ", " << a.owner() << ", "
527+
<< a.size() << "\n";
528+
}
529+
}
530+
531+
return os;
532+
}
533+
442534
}

0 commit comments

Comments
 (0)