Compare commits

...

2 Commits

Author SHA1 Message Date
d6cc79fe76 FIX: incorrect DynamicList resizing logic (memory pool)
- reserve() did not set the addressable size to the current capacity
  before resizing, which meant that the delete[] would not have the true
  allocated size.
  Only affected memory-pool usage, which was not yet integrated
  (#3381)

ENH: remove unused DynamicList '*_unsafe()' methods

- could result in a mismatch between allocated and addressed sizes,
  which becomes important for memory-pool usage.
2025-06-23 11:08:46 +02:00
9dee2cad3e ENH: integrate memory pool support for List allocations (#3381)
- provides an optional memory management using a memory pool.
  Currently can support Umpire (https://github.com/LLNL/Umpire)

  When available, its use can be controlled by the FOAM_MEMORY_POOL
  environment variable, or the memory_pool Optimisation switch
  (etc/controlDict).

Notes:

  Use of the memory-pool is controlled by the 'is_aligned_type()' test
  and the minimum field size, controlled by the 'use_memory_pool()' test.

  If the memory-pool is not enabled or not required according to the two
  above tests, the allocation falls back to either an aligned or unaligned
  allocation (depending on the field size).

  The thresholds for aligned, unaligned, memory-pool allocation
  are still a compile-time option. Made by direct edit of the
  corrsponding functions.
2025-06-23 11:08:38 +02:00
21 changed files with 945 additions and 120 deletions

View File

@ -204,16 +204,6 @@ int main(int argc, char *argv[])
Info<<" " << *iter;
}
Info<< nl;
Info<< "data:" << Foam::name(ident.cdata())
<< " size:" << ident.size() << nl;
Info<< "resize_unsafe(10)" << nl;
ident.resize_unsafe(10);
Info<< "data:" << Foam::name(ident.cdata())
<< " size:" << ident.size() << nl;
}
if (false)

View File

@ -132,6 +132,11 @@ projectDir="$HOME/OpenFOAM/OpenFOAM-$WM_PROJECT_VERSION"
# projectDir="@PROJECT_DIR@"
: # Safety statement (if the user removed all fallback values)
# [FOAM_MEMORY_POOL] - Optional memory management
# - overrides the 'memory_pool' etc/controlDict entry
# = "true | false | host [size=nn] [incr=nn]"
#export FOAM_MEMORY_POOL="host"
# [FOAM_SIGFPE] - Trap floating-point exceptions.
# - overrides the 'trapFpe' controlDict entry
# = true | false

View File

@ -221,6 +221,9 @@ OptimisationSwitches
// Other
// =====
// Optional memory management (sizing in MB)
// memory_pool "host; size=1024; incr=5"
// Trap floating point exception.
// Can override with FOAM_SIGFPE env variable (true|false)
trapFpe 1;

View File

@ -134,6 +134,11 @@ set projectDir=`lsof +p $$ |& sed -ne 's#^[^/]*##;\@/'"$projectName"'[^/]*/etc/c
# Or optionally hard-coded (eg, with autoconfig)
# set projectDir="@PROJECT_DIR@"
# [FOAM_MEMORY_POOL] - Optional memory management
# - overrides the 'memory_pool' etc/controlDict entry
# = "true | false | host [size=nn] [incr=nn]"
#setenv FOAM_MEMORY_POOL "host"
# [FOAM_SIGFPE] - Trap floating-point exceptions.
# - overrides the 'trapFpe' controlDict entry
# = true | false

View File

@ -3,6 +3,8 @@ MSwindows.C
cpuInfo/cpuInfo.C
memInfo/memInfo.C
memory/MemoryPool.cxx
signals/sigFpe.cxx
signals/sigInt.cxx
signals/sigQuit.cxx

View File

@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "MemoryPool.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
// bool Foam::MemoryPool::create(const std::string& ctrl, bool verbose)
// {
// return false;
// }
bool Foam::MemoryPool::create(bool verbose)
{
// No banner information since it is currently never an option
return false;
}
void Foam::MemoryPool::destroy(bool verbose)
{}
bool Foam::MemoryPool::active() noexcept
{
return false;
}
bool Foam::MemoryPool::suspend() noexcept
{
return false;
}
void Foam::MemoryPool::resume() noexcept
{}
bool Foam::MemoryPool::is_pool(void* ptr)
{
return false;
}
void* Foam::MemoryPool::try_allocate(std::size_t nbytes)
{
return nullptr;
}
bool Foam::MemoryPool::try_deallocate(void* ptr)
{
return (!ptr);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -2,6 +2,7 @@
cd "${0%/*}" || exit # Run from this directory
targetType=libo # Preferred library type
. ${WM_PROJECT_DIR:?}/wmake/scripts/AllwmakeParseArguments $*
. ${WM_PROJECT_DIR:?}/wmake/scripts/have_umpire
#------------------------------------------------------------------------------
# Hack for MacOS (with Gcc).
@ -59,6 +60,47 @@ then
export COMP_FLAGS="-DFOAM_USE_INOTIFY"
fi
#------------------------------------------------------------------------------
# Have -lumpire, but also -lcamp etc.
# Also need to follow the link order
get_umpire_libs()
{
if [ -d "${UMPIRE_LIB_DIR}" ]
then
set -- $(
# Expected link order
for name in umpire fmt camp
do
[ -f "$UMPIRE_LIB_DIR/lib${name}.a" ] && echo "-l$name"
done
)
echo "$@"
else
echo
fi
}
if have_umpire
then
libNames="$(get_umpire_libs)"
if [ -n "$libNames" ]
then
echo " found umpire -- enabling memory pool interface" 1>&2
echo " umpire libs: $libNames" 1>&2
COMP_FLAGS="$COMP_FLAGS -DFOAM_USE_UMPIRE -I${UMPIRE_INC_DIR}"
LINK_FLAGS="$LINK_FLAGS -L${UMPIRE_LIB_DIR} $libNames"
export COMP_FLAGS LINK_FLAGS
else
echo " expecting umpire, but did not resolve the libraries" 1>&2
fi
fi
#------------------------------------------------------------------------------
# Make object (non-shared by default)
# Never want/need openmp, especially for static objects
wmake -no-openmp $targetType

View File

@ -4,6 +4,8 @@ cpuInfo/cpuInfo.C
cpuTime/cpuTimePosix.C
memInfo/memInfo.C
memory/MemoryPool.cxx
signals/sigFpe.cxx
signals/sigSegv.cxx
signals/sigInt.cxx

View File

@ -1 +1,4 @@
EXE_INC = $(COMP_FLAGS)
/* umpire uses old-style cast etc */
EXE_INC = $(COMP_FLAGS) $(c++LESSWARN)
LIBO_LIBS = $(LINK_FLAGS)

View File

@ -0,0 +1,510 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "MemoryPool.H"
#include "debug.H"
#include "dictionary.H"
#include "sigFpe.H"
#include "OSspecific.H" // For getEnv
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
#ifdef FOAM_USE_UMPIRE
// #include <cerrno>
#include <cinttypes>
#include <tuple>
#include "umpire/Allocator.hpp"
#include "umpire/ResourceManager.hpp"
#include "umpire/strategy/AlignedAllocator.hpp"
#include "umpire/strategy/DynamicPoolList.hpp"
static bool disabled_(false);
static umpire::Allocator aligned_allocator;
static umpire::Allocator pooled_allocator;
static umpire::ResourceManager* manager_(nullptr);
static umpire::ResourceManager* suspended_(nullptr);
#endif
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
#ifdef FOAM_USE_UMPIRE
namespace
{
// Different supported allocation types
enum class Types { undefined, none, host, device, managed };
typedef std::tuple<Types, std::size_t, std::size_t> ctrlTuple;
// Extract key=INT, the key includes the '='
int getIntParameter(const std::string& key, const std::string& ctrl)
{
int val(0);
const auto pos = ctrl.find(key);
if (pos == std::string::npos)
{
return val;
}
const char* buf = (ctrl.data() + pos + key.size());
char *endptr = nullptr;
errno = 0;
auto parsed = std::strtoimax(buf, &endptr, 10);
if (errno || endptr == buf)
{
// Some type of error OR no conversion
}
else
{
val = int(parsed);
}
return val;
}
ctrlTuple getControlValues(const std::string& ctrl)
{
ctrlTuple result(Types::undefined, 0, 0);
bool checkParam = false;
// Also find things that look like Switch constants.
// Unfortunately need to do this manually since Switch::find()
// itself would not manage to parse something like "true; size=10"
if (ctrl.empty())
{
// Nothing => undefined
}
else if
(
std::string::npos != ctrl.find("false") // ctrl.contains("false")
|| std::string::npos != ctrl.find("off") // ctrl.contains("off")
|| std::string::npos != ctrl.find("no") // ctrl.contains("no")
|| std::string::npos != ctrl.find("none") // ctrl.contains("none")
)
{
std::get<0>(result) = Types::none;
}
else if
(
std::string::npos != ctrl.find("true") // ctrl.contains("true")
|| std::string::npos != ctrl.find("on") // ctrl.contains("on")
|| std::string::npos != ctrl.find("yes") // ctrl.contains("yes")
|| std::string::npos != ctrl.find("host") // ctrl.contains("host")
|| std::string::npos != ctrl.find("system") // ctrl.contains("system")
)
{
std::get<0>(result) = Types::host;
checkParam = true;
}
// These need more testing
else if
(
std::string::npos != ctrl.find("device") // ctrl.contains("device")
)
{
std::get<0>(result) = Types::device;
checkParam = true;
}
else if
(
std::string::npos != ctrl.find("managed") // ctrl.contains("managed")
)
{
std::get<0>(result) = Types::managed;
checkParam = true;
}
if (checkParam)
{
std::get<1>(result) = getIntParameter("size=", ctrl);
std::get<2>(result) = getIntParameter("incr=", ctrl);
}
return result;
}
bool create_from(const ctrlTuple& controls, bool verbose)
{
using namespace Foam;
if (manager_ || suspended_)
{
// Already created
return true;
}
// Type, initial size, increment
auto [which, size, incr] = controls;
// std::cerr
// << "which=" << int(which)
// << ", size=" << int(size)
// << ", incr=" << int(incr) << '\n';
constexpr size_t MegaByte(1024*1024);
switch (which)
{
case Types::undefined :
{
if (verbose)
{
Info<< "memory pool : unused" << nl;
}
break;
}
case Types::none :
{
if (verbose)
{
Info<< "memory pool : disabled" << nl;
}
break;
}
case Types::host :
{
// Default sizing parameters
if (!size) size = 1024;
if (!incr) incr = 5;
auto& rm = umpire::ResourceManager::getInstance();
manager_ = &rm;
aligned_allocator =
rm.makeAllocator<umpire::strategy::AlignedAllocator>
(
"aligned_allocator",
rm.getAllocator("HOST"),
// alignment
256
);
pooled_allocator =
rm.makeAllocator<umpire::strategy::DynamicPoolList>
(
"openfoam_HOST_pool",
aligned_allocator,
// initial block allocation size
(size*MegaByte),
// incremental block allocation size
(incr*MegaByte)
);
if (verbose)
{
Info<< "memory pool : host (size="
<< int(size) << "MB, incr="
<< int(incr) << "MB)\n";
}
break;
}
case Types::device :
{
auto& rm = umpire::ResourceManager::getInstance();
manager_ = &rm;
aligned_allocator = rm.getAllocator("DEVICE");
pooled_allocator =
rm.makeAllocator<umpire::strategy::DynamicPoolList>
(
"openfoam_DEVICE_pool",
aligned_allocator
);
if (verbose)
{
Info<< "memory pool : device" << nl;
}
break;
}
case Types::managed :
{
// Default sizing parameters
if (!size) size = 10*1024;
if (!incr) incr = 10;
auto& rm = umpire::ResourceManager::getInstance();
manager_ = &rm;
aligned_allocator = rm.getAllocator("UM");
pooled_allocator =
rm.makeAllocator<umpire::strategy::DynamicPoolList>
(
"openfoam_UM_pool",
aligned_allocator,
// initial block allocation size
(size*MegaByte),
// incremental block allocation size
(incr*MegaByte)
);
if (verbose)
{
Info<< "memory pool : managed (size="
<< int(size) << "MB, incr="
<< int(incr) << "MB)\n";
}
break;
}
}
return (which != Types::undefined && which != Types::none);
}
} // End anonymous namespace
#endif // FOAM_USE_UMPIRE
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
// bool Foam::MemoryPool::create(const std::string& ctrl, bool verbose)
// {
// #ifdef FOAM_USE_UMPIRE
// if (manager_ || suspended_)
// {
// // Already created
// return true;
// }
//
// auto controls = getControlValues(ctrl);
//
// return create_from(controls, verbose);
// #else
// return false;
// #endif
// }
bool Foam::MemoryPool::create(bool verbose)
{
#ifdef FOAM_USE_UMPIRE
if (disabled_)
{
// Disallowed
return false;
}
else if (manager_ || suspended_)
{
// Already created
return true;
}
// First check environment
auto controls = getControlValues(Foam::getEnv("FOAM_MEMORY_POOL"));
if (std::get<0>(controls) == Types::none)
{
// Disabled from environment - has highest priority
disabled_ = true;
}
// Currently no easy way to handle <system>/controlDict...
// Fallback from etc/controlDict
if (std::get<0>(controls) == Types::undefined)
{
// From central etc/controlDict
const auto& dict = Foam::debug::optimisationSwitches();
if (auto* eptr = dict.findStream("memory_pool", keyType::LITERAL))
{
const token& firstToken = eptr->front();
if (firstToken.isStringType())
{
controls = getControlValues(firstToken.stringToken());
}
}
}
return create_from(controls, verbose);
#else
if (verbose)
{
Info<< "memory pool : not available" << nl;
}
return false;
#endif
}
void Foam::MemoryPool::destroy(bool verbose)
{
// Nothing currently needed but could add in something like this:
// if (manager_ || suspended_)
// {
// pooled_allocator.release();
// }
// However, need to find the proper sequence within
// Foam::exit() or UPstream::exit() ...
}
bool Foam::MemoryPool::active() noexcept
{
#ifdef FOAM_USE_UMPIRE
return bool(manager_);
#else
return false;
#endif
}
bool Foam::MemoryPool::suspend() noexcept
{
#ifdef FOAM_USE_UMPIRE
bool status(suspended_);
if (manager_) // <- and (!suspended_)
{
std::swap(manager_, suspended_);
}
return status;
#else
return false;
#endif
}
void Foam::MemoryPool::resume() noexcept
{
#ifdef FOAM_USE_UMPIRE
if (suspended_) // <- and (!manager_)
{
std::swap(manager_, suspended_);
}
#endif
}
bool Foam::MemoryPool::is_pool(void* ptr)
{
#ifdef FOAM_USE_UMPIRE
if (ptr)
{
if (manager_)
{
return manager_->hasAllocator(ptr);
}
else if (suspended_)
{
return suspended_->hasAllocator(ptr);
}
}
#endif
return false;
}
void* Foam::MemoryPool::try_allocate(std::size_t nbytes)
{
void* ptr = nullptr;
#ifdef FOAM_USE_UMPIRE
if (manager_)
{
ptr = pooled_allocator.allocate(nbytes);
// std::cerr<< "allocate(" << int(nbytes) << ")\n";
// Optionally fill with NaN (depends on current flags)
Foam::sigFpe::fillNan_if(ptr, nbytes);
if (!ptr)
{
// Pout<< "umpire failed to allocate memory\n";
}
}
#endif
return ptr;
}
bool Foam::MemoryPool::try_deallocate(void* ptr)
{
#ifdef FOAM_USE_UMPIRE
if (ptr)
{
if (manager_)
{
if (manager_->hasAllocator(ptr)) // <- ie, is_pool()
{
// std::cerr<< "deallocate()\n";
manager_->deallocate(ptr);
return true;
}
}
else if (suspended_)
{
// Deallocate even if nominally suspended
if (suspended_->hasAllocator(ptr)) // <- ie, is_pool()
{
// std::cerr<< "deallocate()\n";
suspended_->deallocate(ptr);
return true;
}
}
}
#endif
return (!ptr);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -203,7 +203,7 @@ public:
//- Change the value for the list capacity directly (ADVANCED, UNSAFE)
//- Does not perform any memory management or resizing.
void setCapacity_unsafe(const label len) noexcept { capacity_ = len; }
void setCapacity_unsafe(label len) noexcept { capacity_ = len; }
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
@ -251,11 +251,6 @@ public:
//- Shrink the allocated space to the number of elements used.
inline void shrink_to_fit();
//- Shrink the internal bookkeeping of the allocated space to the
//- number of addressed elements without affecting allocation.
// \note when empty() it will delete any allocated memory.
inline void shrink_unsafe();
// Edit

View File

@ -68,7 +68,9 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity);
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
if (nocopy)
{
List<T>::resize_nocopy(newCapacity);
@ -95,6 +97,9 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
@ -105,8 +110,10 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
}
else
{
List<T>::resize(capacity_);
List<T>::resize_copy(currLen, capacity_);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
}
@ -271,7 +278,7 @@ inline Foam::DynamicList<T, SizeMin>::DynamicList
List<T>(std::move(static_cast<List<T>&>(list))),
capacity_(list.capacity())
{
list.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
list.setCapacity_unsafe(0); // All contents moved
}
@ -285,7 +292,7 @@ inline Foam::DynamicList<T, SizeMin>::DynamicList
List<T>(std::move(static_cast<List<T>&>(list))),
capacity_(list.capacity())
{
list.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
list.setCapacity_unsafe(0); // All contents moved
}
@ -361,8 +368,15 @@ inline void Foam::DynamicList<T, SizeMin>::reserve_exact
// Preserve addressed size
const label currLen = List<T>::size();
capacity_ = len;
List<T>::resize(capacity_);
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// if (!nocopy)
{
List<T>::resize_copy(currLen, len);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
}
@ -449,18 +463,6 @@ inline void Foam::DynamicList<T, SizeMin>::shrink_to_fit()
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::shrink_unsafe()
{
if (List<T>::empty())
{
// Delete storage if empty
List<T>::clear();
}
capacity_ = List<T>::size();
}
template<class T, int SizeMin>
inline void
Foam::DynamicList<T, SizeMin>::swap(List<T>& list)

View File

@ -236,13 +236,6 @@ public:
// Otherwise the contents will be uninitialized.
inline void resize_nocopy(const label len);
//- Change the addressed list size directly without affecting
//- any memory management (advanced usage).
//
// It is left to the caller to avoid \em unsafe lengthening beyond
// the allocated memory region.
inline void resize_unsafe(const label len) noexcept;
//- Alias for resize()
void setSize(const label n) { this->resize(n); }

View File

@ -178,13 +178,6 @@ inline void Foam::List<T>::resize_nocopy(const label len)
}
template<class T>
inline void Foam::List<T>::resize_unsafe(const label len) noexcept
{
UList<T>::setAddressableSize(len);
}
template<class T>
inline T& Foam::List<T>::newElmt(const label i)
{

View File

@ -34,6 +34,7 @@ Description
#ifndef Foam_ListPolicy_H
#define Foam_ListPolicy_H
#include "MemoryPool.H" // Also includes <cstdint>
#include "contiguous.H" // Also includes <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -103,6 +104,18 @@ template<> struct no_linebreak<wordRe> : std::true_type {};
// - use_offload(n) :
// Lower threshold for switching to device offloading
//
//
// Use of the memory-pool is controlled by the 'is_aligned_type()' test
// and the minimum field size, controlled by the 'use_memory_pool()' test.
//
// If the memory-pool is not enabled or not required according to the two
// above tests, the allocation falls back to either an aligned or unaligned
// allocation.
//
// The decision about when to choose aligned vs unaligned allocation
// is still a compile-time option. Made by direct edit of the
// appropriate functions.
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Consider aligned allocation for the given type?
@ -146,27 +159,104 @@ inline constexpr bool use_offload(IntType n) noexcept
}
//- Default alignment for larger fields
inline constexpr std::align_val_t default_alignment() noexcept
{
return std::align_val_t(256);
}
//- Allocate from memory pool (if active), or aligned, or normal
template<class T, class IntType>
inline T* allocate(IntType n)
{
// Plain new
return new T[n];
if constexpr (ListPolicy::is_aligned_type<T>())
{
// Note: threshold for use_memory_pool() >= use_alignment()
if (ListPolicy::use_alignment(n))
{
if
(
void *pool_ptr
(
// Consider memory pool for large amounts of data
ListPolicy::use_memory_pool(n)
? Foam::MemoryPool::try_allocate(sizeof(T)*n)
: nullptr
);
pool_ptr
)
{
// Placement new
return new (pool_ptr) T[n];
}
else
{
return new (ListPolicy::default_alignment()) T[n];
}
}
else
{
// Plain new
return new T[n];
}
}
else
{
// Plain new
return new T[n];
}
}
//- Deallocate from memory pool, or normal
template<class T, class IntType>
inline void deallocate(T* ptr)
{
// Plain new
delete[] ptr;
if constexpr (ListPolicy::is_aligned_type<T>())
{
if (ptr && !Foam::MemoryPool::try_deallocate(ptr))
{
// Plain new
delete[] ptr;
}
}
else
{
// Plain new
delete[] ptr;
}
}
//- Deallocate from memory pool, or aligned, or normal
template<class T, class IntType>
inline void deallocate(T* ptr, [[maybe_unused]] IntType n)
{
// Plain new
delete[] ptr;
if constexpr (ListPolicy::is_aligned_type<T>())
{
// Note: threshold for use_memory_pool() >= use_alignment()
if (ListPolicy::use_alignment(n))
{
if (ptr && !Foam::MemoryPool::try_deallocate(ptr))
{
// Alignment depends on the number of elements
::operator delete[](ptr, ListPolicy::default_alignment());
}
}
else
{
// Plain new
delete[] ptr;
}
}
else
{
// Plain new
delete[] ptr;
}
}

View File

@ -112,23 +112,26 @@ public:
//- Change the value for the list capacity directly (ADVANCED, UNSAFE)
//- Does not perform any memory management or resizing.
void setCapacity_unsafe(const label len) noexcept { capacity_ = len; }
void setCapacity_unsafe(label len) noexcept { capacity_ = len; }
//- Reserve allocation space for at least this size.
// New entries are initialized to nullptr.
inline void reserve(const label len);
//- Reserve allocation space for at least this size.
//- If allocation is required, uses the specified size
//- without any other resizing logic.
// New entries are initialized to nullptr.
inline void reserve_exact(const label len);
//- Alter the addressed list size.
inline void resize(const label newLen);
// New entries are initialized to nullptr.
inline void resize(const label len);
//- Set the addressed list to the given size,
//- deleting all existing entries.
//- Afterwards the list contains all \c nullptr entries.
inline void resize_null(const label newLen);
inline void resize_null(const label len);
//- Clear the addressed list, i.e. set the size to zero.
// Allocated size does not change
@ -140,11 +143,6 @@ public:
//- Shrink the allocated space to the number of elements used.
inline void shrink_to_fit();
//- Shrink the internal bookkeeping of the allocated space to the
//- number of addressed elements without affecting allocation.
// \note when empty() it will delete any allocated memory.
inline void shrink_unsafe();
//- Alias for shrink_to_fit()
void shrink() { this->shrink_to_fit(); }

View File

@ -82,10 +82,7 @@ inline Foam::PtrDynList<T, SizeMin>::PtrDynList
PtrList<T>(std::move(list)),
capacity_(list.capacity())
{
// FUTURE:
// list.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
list.clearStorage(); // capacity=0 etc.
list.setCapacity_unsafe(0); // All contents moved
}
@ -99,10 +96,7 @@ inline Foam::PtrDynList<T, SizeMin>::PtrDynList
PtrList<T>(std::move(list)),
capacity_(list.capacity())
{
// FUTURE:
// list.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
list.clearStorage(); // capacity=0 etc.
list.setCapacity_unsafe(0); // All contents moved
}
@ -135,11 +129,17 @@ inline void Foam::PtrDynList<T, SizeMin>::reserve(const label len)
// Preserve addressed size
const label currLen = PtrList<T>::size();
// Consistent allocated sizing
PtrList<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
// No PtrList<T>::resize_copy(...) -> copying nullptr is cheap
PtrList<T>::resize(capacity_);
capacity_ = PtrList<T>::size();
PtrList<T>::setAddressableSize(currLen);
}
}
@ -153,8 +153,13 @@ inline void Foam::PtrDynList<T, SizeMin>::reserve_exact(const label len)
// Preserve addressed size
const label currLen = PtrList<T>::size();
capacity_ = len;
PtrList<T>::resize(capacity_);
// Consistent allocated sizing
PtrList<T>::setAddressableSize(capacity_);
// No PtrList<T>::resize_copy(...) -> copying nullptr is cheap
PtrList<T>::resize(len);
capacity_ = PtrList<T>::size();
PtrList<T>::setAddressableSize(currLen);
}
}
@ -164,16 +169,12 @@ template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::resize(const label newLen)
{
auto& ptrs = this->ptrs_;
const label oldLen = ptrs.size();
if (capacity_ < newLen)
{
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(newLen, capacity_);
PtrList<T>::resize(capacity_);
// Extend list
this->reserve(newLen);
}
else if (newLen != oldLen)
{
@ -191,13 +192,16 @@ inline void Foam::PtrDynList<T, SizeMin>::resize(const label newLen)
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::resize_null(const label newLen)
inline void Foam::PtrDynList<T, SizeMin>::resize_null(const label len)
{
if (capacity_ < newLen)
if (capacity_ < len)
{
// Consistent allocated sizing
PtrList<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(newLen, capacity_);
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
PtrList<T>::resize_null(capacity_);
}
@ -207,7 +211,7 @@ inline void Foam::PtrDynList<T, SizeMin>::resize_null(const label newLen)
}
// Adjust addressed size
PtrList<T>::setAddressableSize(newLen);
PtrList<T>::setAddressableSize(len);
}
@ -240,18 +244,6 @@ inline void Foam::PtrDynList<T, SizeMin>::shrink_to_fit()
}
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::shrink_unsafe()
{
if (PtrList<T>::empty())
{
// Delete empty list
PtrList<T>::clear();
}
capacity_ = PtrList<T>::size();
}
template<class T, int SizeMin>
inline Foam::label Foam::PtrDynList<T, SizeMin>::squeezeNull()
{

View File

@ -217,7 +217,7 @@ public:
//- Change the value for the list capacity directly (ADVANCED, UNSAFE)
//- Does not perform any memory management or resizing.
void setCapacity_unsafe(const label len) noexcept { capacity_ = len; }
void setCapacity_unsafe(label len) noexcept { capacity_ = len; }
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
@ -265,11 +265,6 @@ public:
//- Shrink the allocated space to the number of elements used.
inline void shrink_to_fit();
//- Shrink the internal bookkeeping of the allocated space to the
//- number of addressed elements without affecting allocation.
// \note when empty() it will delete any allocated memory.
inline void shrink_unsafe();
// Edit

View File

@ -66,7 +66,9 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity);
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
if (nocopy)
{
List<T>::resize_nocopy(newCapacity);
@ -93,6 +95,9 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
@ -103,8 +108,10 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
}
else
{
List<T>::resize(capacity_);
List<T>::resize_copy(currLen, capacity_);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
}
@ -246,7 +253,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
Field<T>(std::move(static_cast<List<T>&>(content))),
capacity_(content.capacity())
{
content.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
content.setCapacity_unsafe(0); // All contents moved
}
@ -259,7 +266,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
Field<T>(std::move(static_cast<List<T>&>(content))),
capacity_(content.capacity())
{
content.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
content.setCapacity_unsafe(0); // All contents moved
}
@ -273,7 +280,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
Field<T>(std::move(static_cast<List<T>&>(content))),
capacity_(content.capacity())
{
content.setCapacity_unsafe(0); // Same as shrink_unsafe() but noexcept
content.setCapacity_unsafe(0); // All contents moved
}
@ -292,7 +299,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
{
Field<T>::transfer(static_cast<List<T>&>(content));
capacity_ = content.capacity();
content.setCapacity_unsafe(0);
content.setCapacity_unsafe(0); // All contents moved
}
else
{
@ -317,7 +324,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
{
Field<T>::transfer(static_cast<List<T>&>(content));
capacity_ = content.capacity();
content.setCapacity_unsafe(0);
content.setCapacity_unsafe(0); // All contents moved
}
else
{
@ -462,8 +469,15 @@ inline void Foam::DynamicField<T, SizeMin>::reserve_exact
// Preserve addressed size
const label currLen = List<T>::size();
capacity_ = len;
List<T>::resize(capacity_);
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// if (!nocopy)
{
List<T>::resize_copy(currLen, len);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
}
@ -551,18 +565,6 @@ inline void Foam::DynamicField<T, SizeMin>::shrink_to_fit()
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::shrink_unsafe()
{
if (List<T>::empty())
{
// Delete storage if empty
List<T>::clear();
}
capacity_ = List<T>::size();
}
template<class T, int SizeMin>
inline void
Foam::DynamicField<T, SizeMin>::swap(List<T>& list)

View File

@ -37,6 +37,7 @@ License
#include "IOobject.H"
#include "dynamicCode.H"
#include "simpleObjectRegistry.H"
#include "MemoryPool.H"
#include "sigFpe.H"
#include "sigInt.H"
#include "sigQuit.H"
@ -2182,6 +2183,9 @@ void Foam::argList::parse
sigQuit::set(bannerEnabled());
sigSegv::set(bannerEnabled());
// Create memory pool (if any) after MPI has been setup
MemoryPool::create(bannerEnabled());
if (UPstream::master() && bannerEnabled())
{
Info<< "fileModificationChecking : "

View File

@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::MemoryPool
Description
Optional memory management using a memory pool such as Umpire
(https://github.com/LLNL/Umpire).
When compiled with Umpire, its use can be controlled by the
\c FOAM_MEMORY_POOL environment variable, or the
\c memory_pool Optimisation switch (etc/controlDict).
It currently looks for any of the following entries, in this order:
- true - same as \em "host"
- false/none - disabled.
- \em "host" - uses host memory pool
- \em "system" - same as \em "host"
- \em "device" - uses device memory pool
- \em "managed" - uses managed host/device memory pool
.
The parameters "size=nn" and "incr=nn" (in MegaBytes) can be used
to specify alternatives to the default sizing.
\*---------------------------------------------------------------------------*/
#ifndef Foam_MemoryPool_H
#define Foam_MemoryPool_H
#include <cstdint> // For size_t
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class MemoryPool Declaration
\*---------------------------------------------------------------------------*/
class MemoryPool
{
public:
// Constructors
//- Create a memory pool instance (if not already active).
// Uses environment or etc/controlDict entry
static bool create(bool verbose = false);
// Destructor
//- Remove the memory pool instance (currently does nothing)
static void destroy(bool verbose = false);
// Member Functions
//- True if pool is active (ie, created and not suspended)
static bool active() noexcept;
//- Suspend use of memory pool (for allocation).
// \return previous suspend status
static bool suspend() noexcept;
//- Resume use of memory pool (if previously active)
static void resume() noexcept;
//- Test if given pointer belongs to the pool
static bool is_pool(void *ptr);
//- Allocate from pool (if active).
// \returns nullptr if the pool is not active
static void* try_allocate(std::size_t nbytes);
//- Deallocate a pointer managed by the pool
// \returns True if a nullptr (no-op) or when the pointer was
// managed by the pool.
static bool try_deallocate(void *ptr);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //