From ce1260cf70e530f01fa87627b1535627dc15ae49 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Mon, 25 Sep 2023 10:58:13 +0200 Subject: [PATCH] ENH: improve DynamicList shrink and swapping - shrink_to_fit() corresponds to std::vector naming. For DynamicList it is a *binding* request. - shrink_unsafe() simply adjusts the capacity() to match the current size() without forcing a re-allocation. Useful when collapsing to a non-dynamic list to avoid reallocation and copying contents. The memory cleanup will still occur properly at a later stage. - DynamicList::swap(List&) simple recovery of content into a non-dynamic List that also ensures that the capacity is correctly updated. STYLE: promote List::capacity() to public visibility (like std::vector) STYLE: remove unused expandStorage() method - simply a wrapper for resize(capacity()) --- .../test/DynamicList2/Test-DynamicList2.C | 25 ++++++- .../Lists/DynamicList/DynamicList.H | 19 ++--- .../Lists/DynamicList/DynamicListI.H | 63 ++++++++++------ src/OpenFOAM/containers/Lists/List/List.C | 11 +-- src/OpenFOAM/containers/Lists/List/List.H | 5 +- src/OpenFOAM/containers/Lists/List/UList.H | 15 ++-- .../PtrLists/PtrDynList/PtrDynList.H | 19 +++-- .../PtrLists/PtrDynList/PtrDynListI.H | 64 +++++++++++------ .../containers/PtrLists/UPtrList/UPtrList.H | 5 +- .../containers/PtrLists/UPtrList/UPtrListI.H | 9 ++- .../fields/Fields/DynamicField/DynamicField.H | 22 +++--- .../Fields/DynamicField/DynamicFieldI.H | 72 ++++++++++++------- 12 files changed, 220 insertions(+), 109 deletions(-) diff --git a/applications/test/DynamicList2/Test-DynamicList2.C b/applications/test/DynamicList2/Test-DynamicList2.C index c97d6248e1..f51ffdaf5d 100644 --- a/applications/test/DynamicList2/Test-DynamicList2.C +++ b/applications/test/DynamicList2/Test-DynamicList2.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2021-2022 OpenCFD Ltd. + Copyright (C) 2021-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -144,6 +144,29 @@ int main(int argc, char *argv[]) list1.setCapacity(3); printInfo("", list1); + + std::fill_n(std::back_inserter(list1), 10, 5); + Info<< "back_inserter to fill some values" << nl; + printInfo("", list1); + + // Not very efficient, but just test for capability + DynamicList list2; + + list2.resize(5); + ListOps::identity(list2); + + Info<< "initial list" << nl; + printInfo("", list2); + + labelRange range(10, 5); + std::copy(range.begin(), range.end(), std::back_inserter(list2)); + Info<< "back_inserter to append some values" << nl; + printInfo("", list2); + + range.reset(0, 4); + std::copy_n(range.begin(), range.size(), std::back_inserter(list2)); + Info<< "back_inserter to append more values" << nl; + printInfo("", list2); } Info<< "\nEnd\n"; diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H index 691f233e27..21542201f6 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H @@ -167,7 +167,7 @@ public: // Member Functions - // Access + // Capacity //- Normal lower capacity limit - the SizeMin template parameter static constexpr label min_size() noexcept { return SizeMin; } @@ -241,20 +241,23 @@ public: //- Clear the list and delete storage. inline void clearStorage(); - //- Expand the addressable size to fit the allocated capacity. - // Returns the previous addressable size. - inline label expandStorage() noexcept; - //- Shrink the allocated space to the number of elements used. - inline void shrinkStorage(); + inline void shrink_to_fit(); - //- Shrink the allocated space to the number of elements used. - // Returns a reference to the DynamicList. + //- 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(); + + //- Calls shrink_to_fit() and returns a reference to the DynamicList. inline DynamicList& shrink(); // Edit + //- Swap with plain List content. Implies shrink_to_fit(). + inline void swap(List& list); + //- Swap content, independent of sizing parameter template inline void swap(DynamicList& other) noexcept; diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H index 432adc9dff..10918caac8 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H @@ -422,41 +422,64 @@ inline void Foam::DynamicList::clearStorage() template -inline Foam::label Foam::DynamicList::expandStorage() noexcept -{ - const label currLen = List::size(); - - // Address into the entire list - List::setAddressableSize(capacity_); - - return currLen; -} - - -template -inline void Foam::DynamicList::shrinkStorage() +inline void Foam::DynamicList::shrink_to_fit() { const label currLen = List::size(); if (currLen < capacity_) { // Adjust addressable size to trigger proper resizing List::setAddressableSize(currLen+1); - List::resize(currLen); capacity_ = List::size(); } } +template +inline void Foam::DynamicList::shrink_unsafe() +{ + if (List::empty()) + { + // Delete storage if empty + List::clear(); + } + capacity_ = List::size(); +} + + template inline Foam::DynamicList& Foam::DynamicList::shrink() { - this->shrinkStorage(); + this->shrink_to_fit(); return *this; } +template +inline void +Foam::DynamicList::swap(List& list) +{ + if + ( + static_cast*>(this) + == static_cast*>(&list) + ) + { + return; // Self-swap is a no-op + } + + // Remove unused storage + this->shrink_to_fit(); + + // Swap storage and addressable size + UList::swap(list); + + // Update capacity + capacity_ = List::size(); +} + + template template inline void Foam::DynamicList::swap @@ -485,9 +508,8 @@ template inline void Foam::DynamicList::transfer(List& list) { - // Take over storage, clear addressing for list - capacity_ = list.size(); List::transfer(list); + capacity_ = List::size(); } @@ -508,12 +530,11 @@ Foam::DynamicList::transfer return; // Self-assignment is a no-op } - // Take over storage as-is (without shrink, without using SizeMin) - // clear addressing and storage for old lst. + // Take over storage as-is (without shrink) capacity_ = list.capacity(); List::transfer(static_cast&>(list)); - list.clearStorage(); // Ensure capacity=0 + list.clearStorage(); // capacity=0 etc. } @@ -662,7 +683,7 @@ inline void Foam::DynamicList::push_back ) { push_back(std::move(static_cast&>(list))); - list.clearStorage(); // Ensure capacity=0 + list.clearStorage(); // Deletion, capacity=0 etc. } diff --git a/src/OpenFOAM/containers/Lists/List/List.C b/src/OpenFOAM/containers/Lists/List/List.C index c2fa0870ec..57d64cd23a 100644 --- a/src/OpenFOAM/containers/Lists/List/List.C +++ b/src/OpenFOAM/containers/Lists/List/List.C @@ -351,12 +351,13 @@ template template void Foam::List::transfer(DynamicList& list) { - // Shrink the allocated space to the number of elements used - list.shrink(); - transfer(static_cast&>(list)); + // Remove existing contents before anything else. + clear(); - // Ensure DynamicList has proper capacity=0 too - list.clearStorage(); + // Shrink the allocated space to the number of elements used + list.shrink_to_fit(); + transfer(static_cast&>(list)); + list.clearStorage(); // Deletion, capacity=0 etc. } diff --git a/src/OpenFOAM/containers/Lists/List/List.H b/src/OpenFOAM/containers/Lists/List/List.H index ec0283589c..99d6cbde2e 100644 --- a/src/OpenFOAM/containers/Lists/List/List.H +++ b/src/OpenFOAM/containers/Lists/List/List.H @@ -115,9 +115,6 @@ class List // Methods as per DynamicList to simplify code maintenance - //- Stub method for internal naming as per DynamicList - label capacity() const noexcept { return UList::size(); } - //- Stub method for internal naming as per DynamicList void setCapacity_nocopy(const label len) { resize_nocopy(len); } @@ -289,7 +286,7 @@ public: // Member Operators //- Assignment to UList operator. Takes linear time - void operator=(const UList& a); + void operator=(const UList& list); //- Assignment operator. Takes linear time void operator=(const List& list); diff --git a/src/OpenFOAM/containers/Lists/List/UList.H b/src/OpenFOAM/containers/Lists/List/UList.H index 1d70c02703..1913b7952c 100644 --- a/src/OpenFOAM/containers/Lists/List/UList.H +++ b/src/OpenFOAM/containers/Lists/List/UList.H @@ -482,9 +482,12 @@ public: //- True if List is empty (ie, size() is zero) bool empty() const noexcept { return !size_; } - //- The number of elements in the List + //- The number of elements in the container label size() const noexcept { return size_; } + //- Size of the underlying storage. + label capacity() const noexcept { return size_; } + //- The size of the largest possible UList static constexpr label max_size() noexcept { return labelMax; } @@ -497,22 +500,22 @@ public: //- Equality operation on ULists of the same type. // Returns true when the ULists are element-wise equal // (using UList::value_type::operator==). Takes linear time - bool operator==(const UList& a) const; + bool operator==(const UList& list) const; //- The opposite of the equality operation. Takes linear time - bool operator!=(const UList& a) const; + bool operator!=(const UList& list) const; //- Compare two ULists lexicographically. Takes linear time bool operator<(const UList& list) const; //- Compare two ULists lexicographically. Takes linear time - bool operator>(const UList& a) const; + bool operator>(const UList& list) const; //- Return true if !(a > b). Takes linear time - bool operator<=(const UList& a) const; + bool operator<=(const UList& list) const; //- Return true if !(a < b). Takes linear time - bool operator>=(const UList& a) const; + bool operator>=(const UList& list) const; // Reading/writing diff --git a/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynList.H b/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynList.H index 365a2f4249..cf8691ddf3 100644 --- a/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynList.H +++ b/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynList.H @@ -121,12 +121,16 @@ public: //- Clear the list and delete storage. inline void clearStorage(); - //- Expand the addressable size to fit the allocated capacity. - // Returns the previous addressable size. - inline label expandStorage() noexcept; - //- Shrink the allocated space to the number of elements used. - inline void shrink(); + 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(); + + //- Calls shrink_to_fit() + void shrink() { shrink_to_fit(); } // Edit @@ -136,9 +140,12 @@ public: // \return the number of non-null entries inline label squeezeNull(); + //- Swap with plain PtrList content. Implies shrink_to_fit(). + inline void swap(PtrList& list); + //- Swap content, independent of sizing parameter template - inline void swap(PtrDynList& other); + inline void swap(PtrDynList& other) noexcept; //- Transfer contents of the argument PtrList into this. inline void transfer(PtrList& list); diff --git a/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynListI.H b/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynListI.H index a94cf0a4c5..2067d97d07 100644 --- a/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynListI.H +++ b/src/OpenFOAM/containers/PtrLists/PtrDynList/PtrDynListI.H @@ -177,32 +177,31 @@ inline void Foam::PtrDynList::clearStorage() template -inline Foam::label Foam::PtrDynList::expandStorage() noexcept -{ - const label currLen = PtrList::size(); - - // Allow addressing into the entire list - PtrList::setAddressableSize(capacity_); - - return currLen; -} - - -template -inline void Foam::PtrDynList::shrink() +inline void Foam::PtrDynList::shrink_to_fit() { const label currLen = PtrList::size(); if (currLen < capacity_) { // Adjust addressable size to trigger proper resizing PtrList::setAddressableSize(currLen+1); - PtrList::resize(currLen); capacity_ = PtrList::size(); } } +template +inline void Foam::PtrDynList::shrink_unsafe() +{ + if (PtrList::empty()) + { + // Delete empty list + PtrList::clear(); + } + capacity_ = PtrList::size(); +} + + template inline Foam::label Foam::PtrDynList::squeezeNull() { @@ -212,12 +211,35 @@ inline Foam::label Foam::PtrDynList::squeezeNull() } +template +inline void Foam::PtrDynList::swap(PtrList& list) +{ + if + ( + static_cast*>(this) + == static_cast*>(&list) + ) + { + return; // Self-swap is a no-op + } + + // Remove unused storage + this->shrink_to_fit(); + + // Swap storage and addressable size + UPtrList::swap(list); + + // Update capacity + capacity_ = PtrList::size(); +} + + template template inline void Foam::PtrDynList::swap ( PtrDynList& other -) +) noexcept { if ( @@ -248,9 +270,8 @@ inline void Foam::PtrDynList::transfer(PtrList& list) return; // Self assignment is a no-op } - // Take over storage, clear addressing for list - capacity_ = list.size(); PtrList::transfer(list); + capacity_ = PtrList::size(); } @@ -270,10 +291,11 @@ inline void Foam::PtrDynList::transfer return; // Self assignment is a no-op } - // Take over storage as-is (without shrink, without using SizeMin) + // Take over storage as-is (without shrink) capacity_ = list.capacity(); - PtrList::transfer(list); - list.clearStorage(); // Ensure capacity=0 + + PtrList::transfer(static_cast&>(list)); + list.clearStorage(); // capacity=0 etc. } @@ -496,7 +518,7 @@ template inline void Foam::PtrDynList::reorder(const labelUList& oldToNew) { // Shrinking first is a bit annoying, but saves needing a special version. - shrink(); + this->shrink_to_fit(); PtrList::reorder(oldToNew); } diff --git a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H index a25a52fa10..d39707632d 100644 --- a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H +++ b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H @@ -254,6 +254,9 @@ public: //- The number of entries in the list inline label size() const noexcept; + //- Size of the underlying storage. + inline label capacity() const noexcept; + //- The number of non-null entries in the list inline label count() const noexcept; @@ -322,7 +325,7 @@ public: inline void push_back(UPtrList&& other); //- Swap content - inline void swap(UPtrList& list); + inline void swap(UPtrList& list) noexcept; //- Transfer contents into this list and annul the argument inline void transfer(UPtrList& list); diff --git a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H index 37cc87a16e..6b9d545066 100644 --- a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H +++ b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H @@ -123,6 +123,13 @@ inline Foam::label Foam::UPtrList::size() const noexcept } +template +inline Foam::label Foam::UPtrList::capacity() const noexcept +{ + return ptrs_.capacity(); +} + + template inline Foam::label Foam::UPtrList::count() const noexcept { @@ -213,7 +220,7 @@ inline void Foam::UPtrList::free() template -inline void Foam::UPtrList::swap(UPtrList& list) +inline void Foam::UPtrList::swap(UPtrList& list) noexcept { ptrs_.swap(list.ptrs_); } diff --git a/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H b/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H index 1135129a03..74a887a6dc 100644 --- a/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H +++ b/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H @@ -178,7 +178,7 @@ public: // Member Functions - // Sizing + // Capacity //- Normal lower capacity limit - the SizeMin template parameter static constexpr label min_size() noexcept { return SizeMin; } @@ -190,6 +190,9 @@ public: // \note Only meaningful for contiguous data inline std::streamsize capacity_bytes() const noexcept; + + // Sizing + //- Alter the size of the underlying storage. // The addressed size will be truncated if needed to fit, but will // remain otherwise untouched. @@ -246,20 +249,23 @@ public: //- Clear the list and delete storage. inline void clearStorage(); - //- Expand the addressable size to fit the allocated capacity. - // Returns the previous addressable size. - inline label expandStorage() noexcept; - //- Shrink the allocated space to the number of elements used. - inline void shrinkStorage(); + inline void shrink_to_fit(); - //- Shrink the allocated space to the number of elements used. - // Returns a reference to the DynamicField. + //- 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(); + + //- Calls shrink_to_fit() and returns a reference to the DynamicField. inline DynamicField& shrink(); // Edit + //- Swap with plain List content. Implies shrink_to_fit(). + inline void swap(List& list); + //- Swap content, independent of sizing parameter template inline void swap(DynamicField& other); diff --git a/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H b/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H index 426edd4f05..b430ed25d0 100644 --- a/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H +++ b/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H @@ -438,19 +438,7 @@ inline void Foam::DynamicField::clearStorage() template -inline Foam::label Foam::DynamicField::expandStorage() noexcept -{ - const label currLen = List::size(); - - // Allow addressing into the entire list - List::setAddressableSize(capacity_); - - return currLen; -} - - -template -inline void Foam::DynamicField::shrinkStorage() +inline void Foam::DynamicField::shrink_to_fit() { const label currLen = List::size(); @@ -458,22 +446,57 @@ inline void Foam::DynamicField::shrinkStorage() { // Adjust addressable size to trigger proper resizing List::setAddressableSize(currLen+1); - List::resize(currLen); capacity_ = List::size(); } } +template +inline void Foam::DynamicField::shrink_unsafe() +{ + if (List::empty()) + { + // Delete storage if empty + List::clear(); + } + capacity_ = List::size(); +} + + template inline Foam::DynamicField& Foam::DynamicField::shrink() { - this->shrinkStorage(); + this->shrink_to_fit(); return *this; } +template +inline void +Foam::DynamicField::swap(List& list) +{ + if + ( + static_cast*>(this) + == static_cast*>(&list) + ) + { + return; // Self-swap is a no-op + } + + // Remove unused storage + this->shrink_to_fit(); + + // Swap storage and addressable size + UList::swap(list); + + // Update capacity + capacity_ = List::size(); +} + + template template inline void Foam::DynamicField::swap @@ -529,9 +552,8 @@ inline void Foam::DynamicField::swap template inline void Foam::DynamicField::transfer(List& list) { - // Take over storage, clear addressing for list - capacity_ = list.size(); Field::transfer(list); + capacity_ = Field::size(); } @@ -551,12 +573,10 @@ inline void Foam::DynamicField::transfer return; // Self-assignment is a no-op } - // Take over storage as-is (without shrink, without using SizeMin) - // clear addressing and storage for old list. + // Take over storage as-is (without shrink) capacity_ = list.capacity(); - Field::transfer(static_cast&>(list)); - list.clearStorage(); // Ensure capacity=0 + list.clearStorage(); // capacity=0 etc. } @@ -576,12 +596,10 @@ inline void Foam::DynamicField::transfer return; // Self-assignment is a no-op } - // Take over storage as-is (without shrink, without using SizeMin) - // clear addressing and storage for old list. + // Take over storage as-is (without shrink) capacity_ = list.capacity(); - Field::transfer(static_cast&>(list)); - list.clearStorage(); // Ensure capacity=0 + list.clearStorage(); // capacity=0 etc. } @@ -818,10 +836,10 @@ inline Foam::Istream& Foam::DynamicField::readList // The logic should be the same and this avoids duplicate code DynamicList list; - (*this).swap(list); + this->swap(list); list.readList(is); - (*this).swap(list); + this->swap(list); return is; }