ENH: add ListPolicy::reserve_size() helper (related to #3348)

- central way to calculate reverse sizes for dynamic containers.

  For example;
      reserve_size<16, 2>(len, cap);     // min-size=16, ratio=2
      reserve_size<16, 3, 2>(len, cap);  // min-size=16, ratio=1.5

  replaces this type of code that was used in several places:
      max(SizeMin, max(len, label(2*capacity_)));

  The caller will have already checked (len < cap) before deciding
  to make this call.

ENH: updates for DynamicList/DynamicField handling

- add reserve_exact() method, which is like reserve() but without any
  extra sizing heuristics

- add DynamicField 'reuse' constructors, consistent with Field constructors

- sync allocated size before list destruction.
  This may help when using aligned allocation strategies.
This commit is contained in:
Mark Olesen 2025-04-01 10:56:23 +02:00
parent 7f062a8f5e
commit b8a0706e72
20 changed files with 334 additions and 40 deletions

View File

@ -1,3 +1,3 @@
Test-CircularBuffer.C
Test-CircularBuffer.cxx
EXE = $(FOAM_USER_APPBIN)/Test-CircularBuffer

View File

@ -140,8 +140,46 @@ int main(int argc, char *argv[])
argList::addBoolOption("ListList", "Test list of list functionality");
argList::addBoolOption("flag");
argList::addBoolOption("reserve", "Test ListPolicy for reserve_size");
#include "setRootCase.H"
if (args.found("reserve"))
{
using namespace Foam::ListPolicy;
using control = std::pair<label, label>;
for
(
const auto& tup :
{
control{ 10, 5 },
control{ 20, 25 }
}
)
{
const auto [len, capacity] = tup;
Info<< "test " << tup << nl;
auto size = reserve_size<16,2>(len, capacity);
Info<< " => " << size << " (ratio 2)" << nl;
size = reserve_size<16,3,2>(len, capacity);
Info<< " => " << size << " (ratio 3/2)" << nl;
size = reserve_size<16,13,8>(len, capacity);
Info<< " => " << size << " (ratio " << (13.0/8) << ')' << nl;
size = reserve_size<16,25,16>(len, capacity);
Info<< " => " << size << " (ratio " << (25.0/16) << ')' << nl;
}
Info<< nl << "\nEnd" << endl;
return 0;
}
{
List<label> ident(15);
Foam::identity(ident, 0);

View File

@ -393,6 +393,11 @@ public:
// Never shrinks the allocated size.
inline void reserve(const label numElem);
//- Reserve allocation space for at least this size
//- (uses the specified size without any other resizing strategy).
// Never shrinks the allocated size.
inline void reserve_exact(const label numElem);
//- Clear the list, i.e. set addressable size to zero.
// Does not adjust the underlying storage
inline void clear();

View File

@ -547,7 +547,26 @@ inline void Foam::PackedList<Width>::reserve(const label numElem)
blocks_.resize
(
// SizeMin=16, allocation doubling
max(16, max(newLen, 2*oldLen)),
Foam::max(16, Foam::max(newLen, 2*oldLen)),
0u
);
}
}
template<unsigned Width>
inline void Foam::PackedList<Width>::reserve_exact(const label numElem)
{
const label oldLen = blocks_.size();
const label newLen = num_blocks(numElem);
// Allocate more capacity if necessary
if (oldLen < newLen)
{
blocks_.resize
(
// SizeMin=16
Foam::max(16, newLen),
0u
);
}

View File

@ -38,7 +38,14 @@ void Foam::CircularBuffer<T>::doReserve
{
// Increase capacity (doubling)
const label newCapacity =
max(min_size(), max(len+1, label(2*storage_.size())));
Foam::max(min_size(), Foam::max(len+1, label(2*storage_.size())));
// OR
// Foam::ListPolicy::reserve_size<min_size(), 2>
// (
// len+1,
// storage_.size()
// );
if (nocopy || empty())
{

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022-2023 OpenCFD Ltd.
Copyright (C) 2022-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -122,7 +122,7 @@ inline constexpr Foam::CircularBuffer<T>::CircularBuffer() noexcept
template<class T>
inline Foam::CircularBuffer<T>::CircularBuffer(const label len)
:
storage_(max(min_size(), len + 1)),
storage_(Foam::max(min_size(), len+1)),
begin_(0),
end_(0)
{}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -165,6 +165,10 @@ public:
explicit DynamicList(Istream& is);
//- Destructor, sync allocated size before List destruction
~DynamicList() { List<T>::setAddressableSize(capacity_); }
// Member Functions
// Capacity
@ -208,6 +212,12 @@ public:
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve_nocopy(const label len);
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
//- If allocation is required, uses the specified size
//- without any other resizing logic.
inline void reserve_exact(const label len);
//- Alter addressable list size, allocating new space if required
//- while \em recovering old content.
// If no reallocation is required, the contents remain untouched.

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -66,7 +66,7 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
}
// Addressable length, possibly truncated by new capacity
const label currLen = min(List<T>::size(), newCapacity);
const label currLen = Foam::min(List<T>::size(), newCapacity);
// Corner case...
if (List<T>::size() == newCapacity)
@ -104,8 +104,9 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
if (nocopy)
{
@ -355,6 +356,24 @@ inline void Foam::DynamicList<T, SizeMin>::reserve_nocopy
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::reserve_exact
(
const label len
)
{
if (capacity_ < len)
{
// Preserve addressed size
const label currLen = List<T>::size();
capacity_ = len;
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
}
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize
(

View File

@ -153,7 +153,7 @@ bool Foam::DynamicList<T, SizeMin>::readBracketList(Istream& is)
List<T> currChunk(std::move(*(chunks[chunki])));
chunks[chunki].reset(nullptr);
const label localLen = min(currChunk.size(), totalCount);
const label localLen = Foam::min(currChunk.size(), totalCount);
dest = std::move
(

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2023 OpenCFD Ltd.
Copyright (C) 2017-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -44,7 +44,7 @@ void Foam::List<T>::doResize(const label len)
if (len > 0)
{
// With sign-check to avoid spurious -Walloc-size-larger-than
const label overlap = min(this->size_, len);
const label overlap = Foam::min(this->size_, len);
if (overlap > 0)
{

View File

@ -153,7 +153,7 @@ bool Foam::List<T>::readBracketList(Istream& is)
List<T> currChunk(std::move(*(chunks[chunki])));
chunks[chunki].reset(nullptr);
const label localLen = min(currChunk.size(), totalCount);
const label localLen = Foam::min(currChunk.size(), totalCount);
dest = std::move
(

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2024 OpenCFD Ltd.
Copyright (C) 2015-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -645,7 +645,7 @@ void Foam::inplaceSubset
const label outlen = (select.size() - select.count());
const label len = min(input.size(), select.size());
const label len = Foam::min(input.size(), select.size());
for (label i=0; i < len; ++i)
{
@ -1285,9 +1285,7 @@ void Foam::ListOps::setValue
const T& val
)
{
const label len = list.size();
const label count = locations.size();
const label end = min(count, len);
const label end = Foam::min(list.size(), locations.size());
// The efficiency is modest
for (label index = 0; index < end; ++index)

View File

@ -86,6 +86,63 @@ template<> struct no_linebreak<word> : std::true_type {};
template<> struct no_linebreak<wordRe> : std::true_type {};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Calculate a reserve size (eg, doubling) based on the request length
//- and the current capacity
template<int SizeMin, int Numerator, class IntType>
inline IntType reserve_size(IntType requested, IntType capacity)
{
static_assert(Numerator > 1, "Invalid numerator");
// The caller already checks this:
// if (requested < capacity) { return capacity; }
IntType size(capacity*Numerator);
if (size < requested)
{
size = requested;
}
if constexpr (SizeMin > 0) // The min size is optional
{
if (size < SizeMin)
{
size = SizeMin;
}
}
return size;
}
//- Calculate a reserve size based on the request length
//- and the current capacity
template<int SizeMin, int Numerator, int Denominator, class IntType>
inline IntType reserve_size(IntType requested, IntType capacity)
{
static_assert(Numerator > Denominator, "Invalid numerator");
static_assert(Denominator > 0, "Invalid denominator");
// The caller already checks this:
// if (requested < capacity) { return capacity; }
// Very unlikely that capacity is less than Denominator,
// so divide before multiply to avoid overflow
IntType size((capacity/Denominator)*Numerator);
if (size < requested)
{
size = requested;
}
if constexpr (SizeMin > 0) // The min size is optional
{
if (size < SizeMin)
{
size = SizeMin;
}
}
return size;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Classification of list/container uniformity.

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2023 OpenCFD Ltd.
Copyright (C) 2018-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -96,8 +96,8 @@ public:
inline explicit PtrDynList(UList<T*>& list);
//- Destructor
~PtrDynList() = default;
//- Destructor, sync allocated size before list destruction
~PtrDynList() { PtrList<T>::setAddressableSize(capacity_); }
// Member Functions
@ -110,6 +110,11 @@ public:
//- Reserve allocation space for at least this size.
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.
inline void reserve_exact(const label len);
//- Alter the addressed list size.
inline void resize(const label newLen);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2023 OpenCFD Ltd.
Copyright (C) 2018-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -122,8 +122,9 @@ inline void Foam::PtrDynList<T, SizeMin>::reserve(const label len)
// Preserve addressed size
const label currLen = PtrList<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
PtrList<T>::resize(capacity_);
PtrList<T>::setAddressableSize(currLen);
@ -131,6 +132,21 @@ inline void Foam::PtrDynList<T, SizeMin>::reserve(const label len)
}
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::reserve_exact(const label len)
{
if (capacity_ < len)
{
// Preserve addressed size
const label currLen = PtrList<T>::size();
capacity_ = len;
PtrList<T>::resize(capacity_);
PtrList<T>::setAddressableSize(currLen);
}
}
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::resize(const label newLen)
{
@ -140,8 +156,9 @@ inline void Foam::PtrDynList<T, SizeMin>::resize(const label newLen)
if (capacity_ < newLen)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(newLen, label(2*capacity_)));
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(newLen, capacity_);
PtrList<T>::resize(capacity_);
}
@ -165,8 +182,9 @@ inline void Foam::PtrDynList<T, SizeMin>::resize_null(const label newLen)
{
if (capacity_ < newLen)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(newLen, label(2*capacity_)));
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(newLen, capacity_);
PtrList<T>::resize_null(capacity_);
}

View File

@ -521,6 +521,8 @@ public:
// Increase capacity (doubling)
newCapacity =
Foam::max(label(len), label(2*storage_.size()));
// ratio=1.5:
// Foam::max(label(len), label((storage_.size()/2)*3));
}
// Info<<"request:" << len

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -147,6 +147,17 @@ public:
template<int AnySizeMin>
inline DynamicField(DynamicList<T, AnySizeMin>&& content) noexcept;
//- Copy or move construct from DynamicField
template<int AnySizeMin>
inline DynamicField(DynamicField<T, AnySizeMin>& content, bool reuse);
//- Copy or move construct from DynamicList
template<int AnySizeMin>
inline DynamicField(DynamicList<T, AnySizeMin>& content, bool reuse);
//- Copy or move construct from List
inline DynamicField(List<T>& content, bool reuse);
//- Construct by 1 to 1 mapping from the given field
inline DynamicField
(
@ -176,6 +187,10 @@ public:
inline tmp<DynamicField<T, SizeMin>> clone() const;
//- Destructor, sync allocated size before list destruction
~DynamicField() { List<T>::setAddressableSize(capacity_); }
// Member Functions
// Capacity
@ -219,6 +234,12 @@ public:
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve_nocopy(const label len);
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
//- If allocation is required, uses the specified size
//- without any other resizing logic.
inline void reserve_exact(const label len);
//- Alter addressable list size, allocating new space if required
//- while \em recovering old content.
// If no reallocation is required, the contents remain untouched.

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -64,7 +64,7 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
}
// Addressable length, possibly truncated by new capacity
const label currLen = min(List<T>::size(), newCapacity);
const label currLen = Foam::min(List<T>::size(), newCapacity);
// Corner case - see comments in DynamicList doCapacity
if (List<T>::size() == newCapacity)
@ -98,8 +98,9 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
// Increase capacity (eg, doubling)
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
if (nocopy)
{
@ -268,6 +269,77 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
DynamicField<T, AnySizeMin>& content,
bool reuse
)
:
Field<T>(),
capacity_(0)
{
if (reuse)
{
Field<T>::transfer(static_cast<List<T>&>(content));
capacity_ = content.capacity();
content.setCapacity_unsafe(0);
}
else
{
Field<T>::operator=(content);
capacity_ = content.size();
}
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
DynamicList<T, AnySizeMin>& content,
bool reuse
)
:
Field<T>(),
capacity_(0)
{
if (reuse)
{
Field<T>::transfer(static_cast<List<T>&>(content));
capacity_ = content.capacity();
content.setCapacity_unsafe(0);
}
else
{
Field<T>::operator=(content);
capacity_ = content.size();
}
}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
List<T>& content,
bool reuse
)
:
Field<T>(),
capacity_(content.size())
{
if (reuse)
{
Field<T>::transfer(content);
}
else
{
Field<T>::operator=(content);
}
}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
@ -381,6 +453,24 @@ inline void Foam::DynamicField<T, SizeMin>::reserve_nocopy
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::reserve_exact
(
const label len
)
{
if (capacity_ < len)
{
// Preserve addressed size
const label currLen = List<T>::size();
capacity_ = len;
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
}
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize
(

View File

@ -1083,10 +1083,19 @@ Foam::labelList Foam::surfaceFeatures::selectFeatureEdges
{
DynamicList<label> selectedEdges;
// Presizing
{
label count = 0;
if (regionEdges) count += nRegionEdges();
if (externalEdges) count += nExternalEdges();
if (internalEdges) count += nInternalEdges();
selectedEdges.reserve_exact(count);
}
if (regionEdges)
{
selectedEdges.setCapacity(selectedEdges.size() + nRegionEdges());
for (label i = 0; i < externalStart_; i++)
{
selectedEdges.append(featureEdges_[i]);
@ -1095,8 +1104,6 @@ Foam::labelList Foam::surfaceFeatures::selectFeatureEdges
if (externalEdges)
{
selectedEdges.setCapacity(selectedEdges.size() + nExternalEdges());
for (label i = externalStart_; i < internalStart_; i++)
{
selectedEdges.append(featureEdges_[i]);
@ -1105,8 +1112,6 @@ Foam::labelList Foam::surfaceFeatures::selectFeatureEdges
if (internalEdges)
{
selectedEdges.setCapacity(selectedEdges.size() + nInternalEdges());
for (label i = internalStart_; i < featureEdges_.size(); i++)
{
selectedEdges.append(featureEdges_[i]);