diff --git a/applications/test/DynamicList2/Make/files b/applications/test/DynamicList2/Make/files new file mode 100644 index 0000000000..78b8198c6a --- /dev/null +++ b/applications/test/DynamicList2/Make/files @@ -0,0 +1,3 @@ +Test-DynamicList2.C + +EXE = $(FOAM_USER_APPBIN)/Test-DynamicList2 diff --git a/applications/test/DynamicList2/Make/options b/applications/test/DynamicList2/Make/options new file mode 100644 index 0000000000..18e6fe47af --- /dev/null +++ b/applications/test/DynamicList2/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = */ +/* EXE_LIBS = */ diff --git a/applications/test/DynamicList2/Test-DynamicList2.C b/applications/test/DynamicList2/Test-DynamicList2.C new file mode 100644 index 0000000000..b199e67579 --- /dev/null +++ b/applications/test/DynamicList2/Test-DynamicList2.C @@ -0,0 +1,155 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 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 . + +Description + Test allocation patterns when reading into an existing list. + +\*---------------------------------------------------------------------------*/ + +#include "DynamicList.H" +#include "DynamicField.H" +#include "IOstreams.H" +#include "ITstream.H" +#include "OTstream.H" +#include "StringStream.H" +#include "FlatOutput.H" +#include "ListOps.H" +#include "labelRange.H" +#include "labelIndList.H" + +using namespace Foam; + +template +void printInfo +( + const word& tag, + const DynamicList& list, + const bool showSize = true +) +{ + Info<< '<' << tag; + if (showSize) + { + Info<< " size=\"" << list.size() + << "\" capacity=\"" << list.capacity() << "\""; + if (list.cdata()) + { + Info<< " ptr=\"" << name(list.cdata()) << "\""; + } + else + { + Info<< " ptr=\"nullptr\""; + } + } + Info<< '>' << nl << flatOutput(list) << nl + << "\n" << endl; +} + + +template +void printInfo +( + const word& tag, + const DynamicField& list, + const bool showSize = true +) +{ + Info<< '<' << tag; + if (showSize) + { + Info<< " size=\"" << list.size() + << "\" capacity=\"" << list.capacity() << "\""; + if (list.cdata()) + { + Info<< " ptr=\"" << name(list.cdata()) << "\""; + } + else + { + Info<< " ptr=\"nullptr\""; + } + } + Info<< '>' << nl << flatOutput(list) << nl + << "\n" << endl; +} + + +template +void readList +( + DynamicList& output, + const UList& input +) +{ + OTstream os; + os << input; + ITstream is("input", os.tokens()); + + is >> output; +} + +template +void readList +( + DynamicField& output, + const UList& input +) +{ + OTstream os; + os << input; + ITstream is("input", os.tokens()); + + is >> output; +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main(int argc, char *argv[]) +{ + // + { + DynamicList list1; + + list1.resize(4); + ListOps::identity(list1); + + list1.resize(3); + printInfo("", list1); + + // list1.clear(); + // printInfo("", list1); + + list1.setCapacity(3); + printInfo("", list1); + } + + Info<< "\nEnd\n"; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/containers/Bits/PackedList/PackedList.H b/src/OpenFOAM/containers/Bits/PackedList/PackedList.H index 1ae6f228f2..23401256b6 100644 --- a/src/OpenFOAM/containers/Bits/PackedList/PackedList.H +++ b/src/OpenFOAM/containers/Bits/PackedList/PackedList.H @@ -290,7 +290,7 @@ public: inline bool empty() const noexcept; //- The number of elements that can be stored with reallocating - inline label capacity() const; + inline label capacity() const noexcept; //- True if all entries have identical values, and list is non-empty bool uniform() const; @@ -353,17 +353,20 @@ public: //- Alter the size of the underlying storage. // The addressed size will be truncated if needed to fit, but will // remain otherwise untouched. - inline void setCapacity(const label nElem); + inline void setCapacity(const label numElem); //- Reset addressable list size, does not shrink the allocated size. // Optionally specify a value for new elements. - inline void resize(const label nElem, const unsigned int val = 0u); + inline void resize(const label numElem, const unsigned int val = 0u); + + //- Currently identical to resize. Subject to future change (Oct-2021) + inline void resize_nocopy(const label numElem); //- Reserve allocation space for at least this size. // Never shrinks the allocated size. // The list size is adjusted as per DynamicList with // SizeInc=0, SizeMult=2, SizeDiv=1 - inline void reserve(const label nElem); + inline void reserve(const label numElem); //- Clear the list, i.e. set addressable size to zero. // Does not adjust the underlying storage diff --git a/src/OpenFOAM/containers/Bits/PackedList/PackedListI.H b/src/OpenFOAM/containers/Bits/PackedList/PackedListI.H index a525746d39..5d3dbe1dd5 100644 --- a/src/OpenFOAM/containers/Bits/PackedList/PackedListI.H +++ b/src/OpenFOAM/containers/Bits/PackedList/PackedListI.H @@ -388,12 +388,22 @@ inline bool Foam::PackedList::empty() const noexcept template -inline Foam::label Foam::PackedList::capacity() const +inline Foam::label Foam::PackedList::capacity() const noexcept { return elem_per_block * blocks_.size(); } +template +inline void Foam::PackedList::resize_nocopy +( + const label numElem +) +{ + this->resize(numElem); +} + + template inline void Foam::PackedList::resize ( @@ -406,7 +416,7 @@ inline void Foam::PackedList::resize const label oldSize = size(); size_ = newSize; - if (oldSize < size()) + if (oldSize < newSize) { // Fill new elements or newly exposed elements if (val) @@ -438,7 +448,7 @@ inline void Foam::PackedList::resize clear_trailing_bits(); } } - else if (size() < oldSize) + else if (newSize < oldSize) { // The list is now shorter than before, so we zero assign the unused // blocks and any trailing junk. This costs slightly here, but make diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H index d45d836afd..c97645e21f 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H @@ -46,7 +46,6 @@ SourceFiles #define DynamicList_H #include "List.H" -#include // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -92,6 +91,21 @@ class DynamicList template inline void assignDynList(const ListType& list); + //- Alter the size of the underlying storage + // The 'nocopy' option will not attempt to recover old content + inline void doCapacity(const bool nocopy, const label len); + + //- Reserve allocation space for at least this size. + // Never shrinks the allocated size, use setCapacity() for that. + // The 'nocopy' option will not attempt to recover old content + inline void doReserve(const bool nocopy, const label len); + + //- Reserve allocation space for at least this size. + // Never shrinks the allocated size, use setCapacity() for that. + // The 'nocopy' option will not attempt to recover old content + inline void doResize(const bool nocopy, const label len); + + public: // Constructors @@ -121,7 +135,7 @@ public: //- Construct from a FixedList template - inline DynamicList(const FixedList& lst); + inline explicit DynamicList(const FixedList& lst); //- Construct from an initializer list. Size set to list size. inline explicit DynamicList(std::initializer_list lst); @@ -168,23 +182,49 @@ public: // The addressed size will be truncated if needed to fit, but will // remain otherwise untouched. // Use this or reserve() in combination with append(). - inline void setCapacity(const label newCapacity); + inline void setCapacity(const label len); - //- Reserve allocation space for at least this size. + //- Alter the size of the underlying storage, + //- \em without retaining old content. + // The addressed size will be truncated if needed to fit, but will + // remain otherwise untouched. + inline void setCapacity_nocopy(const label len); + + //- Change the value for the list capacity directly (ADVANCED, UNSAFE) + //- Does not perform any memory management or resizing. + inline void setCapacity_unsafe(const label len) noexcept; + + //- Reserve allocation space for at least this size, allocating new + //- space if required and \em retaining old content. // Never shrinks the allocated size, use setCapacity() for that. inline void reserve(const label len); - //- Alter addressable size. - // New space will be allocated if required. - inline void resize(const label newLen); + //- Reserve allocation space for at least this size, allocating new + //- space if required \em without retaining old content. + // Never shrinks the allocated size, use setCapacity() for that. + inline void reserve_nocopy(const label len); - //- Alter addressable size and fill new space with constant value - inline void resize(const label newLen, const T& val); + //- Alter addressable list size, allocating new space if required + //- while \em recovering old content. + // If no reallocation is required, the contents remain untouched. + // Otherwise new entries will be uninitialized. + // Use this to resize the list prior to using the operator[] for + // setting values (as per List usage). + inline void resize(const label len); - //- Alias for resize() + //- Alter addressable size and fill new entries with constant value + inline void resize(const label len, const T& val); + + //- Alter addressable list size, allocating new space if required + //- \em without necessarily recovering old content. + // If no reallocation is required, the contents remain untouched. + // Otherwise all entries will be uninitialized. + inline void resize_nocopy(const label len); + + //- Same as resize() void setSize(const label n) { this->resize(n); } - //- Alias for resize() + //- Same as resize() void setSize(const label n, const T& val) { this->resize(n, val); } //- Clear the addressed list, i.e. set the size to zero. @@ -198,6 +238,9 @@ public: // Returns the previous addressable size. inline label expandStorage() noexcept; + //- Shrink the allocated space to the number of elements used. + inline void shrinkStorage(); + //- Shrink the allocated space to the number of elements used. // Returns a reference to the DynamicList. inline DynamicList& shrink(); diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H index 047aa3423a..972317b735 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H @@ -37,22 +37,98 @@ inline void Foam::DynamicList::assignDynList const ListType& list ) { - const label newLen = list.size(); + const label len = list.size(); - if (newLen <= capacity_) + if (capacity_ < len) { - // Can copy w/o reallocating - adjust addressable size accordingly. - List::setAddressableSize(newLen); - List::operator=(list); + // Needs more space for the copy operation + List::setAddressableSize(capacity_); // Use entire space + List::resize_nocopy(len); + capacity_ = List::size(); + } + + // Perform copy into addressable portion + List::setAddressableSize(len); + List::operator=(list); +} + + +template +inline void Foam::DynamicList::doCapacity +( + const bool nocopy, + const label newCapacity +) +{ + if (newCapacity == capacity_) + { + return; + } + + // Addressable length, possibly truncated by new capacity + const label currLen = min(List::size(), newCapacity); + + // Corner case... + if (List::size() == newCapacity) + { + // Adjust addressable size to trigger proper resizing. + // Using (old size+1) is safe since it does not affect the 'overlap' + // of old and new addressable regions, but incurs fewew copy + // operations than extending to use the current capacity would. + List::setAddressableSize(currLen+1); + } + + if (nocopy) + { + List::resize_nocopy(newCapacity); } else { - // Ensure list size consistency prior to copying. - List::setAddressableSize(capacity_); - - List::operator=(list); - capacity_ = List::size(); + List::resize(newCapacity); } + + capacity_ = List::size(); + List::setAddressableSize(currLen); +} + + +template +inline void Foam::DynamicList::doReserve +( + const bool nocopy, + const label len +) +{ + if (capacity_ < len) + { + // Preserve addressed size + const label currLen = List::size(); + + // Increase capacity (doubling) + capacity_ = max(SizeMin, max(len, label(2*capacity_))); + + if (nocopy) + { + List::resize_nocopy(capacity_); + } + else + { + List::resize(capacity_); + } + List::setAddressableSize(currLen); + } +} + + +template +inline void Foam::DynamicList::doResize +( + const bool nocopy, + const label len +) +{ + this->doReserve(nocopy, len); + List::setAddressableSize(len); } @@ -72,7 +148,7 @@ inline Foam::DynamicList::DynamicList(const label len) List(), capacity_(0) { - reserve(len); + reserve_nocopy(len); } @@ -141,10 +217,9 @@ inline Foam::DynamicList::DynamicList const FixedList& list ) : - capacity_(0) -{ - this->operator=(list); -} + List(list), + capacity_(List::size()) +{} template @@ -201,10 +276,9 @@ inline Foam::DynamicList::DynamicList List&& lst ) : - capacity_(0) -{ - transfer(lst); -} + List(std::move(lst)), + capacity_(List::size()) +{} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // @@ -227,20 +301,30 @@ Foam::DynamicList::capacity_bytes() const noexcept template inline void Foam::DynamicList::setCapacity ( - const label newCapacity + const label len ) { - label currLen = List::size(); - capacity_ = newCapacity; + this->doCapacity(false, len); // nocopy = false +} - if (currLen > capacity_) - { - // Truncate addressed sizes too - currLen = capacity_; - } - List::resize(capacity_); - List::setAddressableSize(currLen); +template +inline void Foam::DynamicList::setCapacity_nocopy +( + const label len +) +{ + this->doCapacity(true, len); // nocopy = true +} + + +template +inline void Foam::DynamicList::setCapacity_unsafe +( + const label len +) noexcept +{ + capacity_ = len; } @@ -250,52 +334,55 @@ inline void Foam::DynamicList::reserve const label len ) { - if (capacity_ < len) - { - // Increase capacity (doubling) - capacity_ = max(SizeMin, max(len, label(2 * capacity_))); - - // Adjust allocated size, leave addressed size untouched - const label currLen = List::size(); - List::resize(capacity_); - List::setAddressableSize(currLen); - } + this->doReserve(false, len); // nocopy = false } template -inline void Foam::DynamicList::resize +inline void Foam::DynamicList::reserve_nocopy ( - const label newLen + const label len ) { - if (capacity_ < newLen) - { - // Increase capacity (doubling) - capacity_ = max(SizeMin, max(newLen, label(2 * capacity_))); - - List::resize(capacity_); - } - - // Adjust addressed size - List::setAddressableSize(newLen); + this->doReserve(true, len); // nocopy = true } template inline void Foam::DynamicList::resize ( - const label newLen, + const label len +) +{ + this->doResize(false, len); // nocopy = false +} + + +template +inline void Foam::DynamicList::resize_nocopy +( + const label len +) +{ + this->doResize(true, len); // nocopy = true +} + + +template +inline void Foam::DynamicList::resize +( + const label len, const T& val ) { - label currLen = List::size(); - resize(newLen); + label idx = List::size(); + resize(len); // Fill newly exposed with constant value - while (currLen < newLen) + while (idx < len) { - this->operator[](currLen++) = val; + this->operator[](idx) = val; + ++idx; } } @@ -320,7 +407,7 @@ inline Foam::label Foam::DynamicList::expandStorage() noexcept { const label currLen = List::size(); - // Allow addressing into the entire list + // Address into the entire list List::setAddressableSize(capacity_); return currLen; @@ -328,20 +415,25 @@ inline Foam::label Foam::DynamicList::expandStorage() noexcept template -inline Foam::DynamicList& -Foam::DynamicList::shrink() +inline void Foam::DynamicList::shrinkStorage() { const label currLen = List::size(); if (currLen < capacity_) { - // Use the full list when resizing - List::setAddressableSize(capacity_); + // Adjust addressable size to trigger proper resizing + List::setAddressableSize(currLen+1); - // Capacity and size are identical - capacity_ = currLen; List::resize(currLen); - // Redundant: List::setAddressableSize(currLen); + capacity_ = List::size(); } +} + + +template +inline Foam::DynamicList& +Foam::DynamicList::shrink() +{ + this->shrinkStorage(); return *this; } @@ -745,14 +837,7 @@ inline void Foam::DynamicList::operator= const FixedList& lst ) { - const label n = lst.size(); - - resize(n); - - for (label i=0; ioperator[](i) = lst[i]; // copy element - } + assignDynList(lst); } diff --git a/src/OpenFOAM/containers/Lists/FixedList/FixedList.H b/src/OpenFOAM/containers/Lists/FixedList/FixedList.H index 3a3a6c63ef..2120e2ce06 100644 --- a/src/OpenFOAM/containers/Lists/FixedList/FixedList.H +++ b/src/OpenFOAM/containers/Lists/FixedList/FixedList.H @@ -298,6 +298,9 @@ public: //- Dummy function, to make FixedList consistent with List inline void resize(const label n); + //- Dummy function, to make FixedList consistent with List + inline void resize_nocopy(const label n); + //- Dummy function, to make FixedList consistent with List void setSize(const label n) { this->resize(n); } diff --git a/src/OpenFOAM/containers/Lists/FixedList/FixedListI.H b/src/OpenFOAM/containers/Lists/FixedList/FixedListI.H index d72b3fb944..b23bd86ba8 100644 --- a/src/OpenFOAM/containers/Lists/FixedList/FixedListI.H +++ b/src/OpenFOAM/containers/Lists/FixedList/FixedListI.H @@ -346,6 +346,16 @@ inline void Foam::FixedList::resize(const label n) #endif } + +template +inline void Foam::FixedList::resize_nocopy(const label n) +{ + #ifdef FULLDEBUG + checkSize(n); + #endif +} + + template inline void Foam::FixedList::fill(const T& val) { diff --git a/src/OpenFOAM/containers/Lists/List/List.C b/src/OpenFOAM/containers/Lists/List/List.C index 1a7186cace..858b046e83 100644 --- a/src/OpenFOAM/containers/Lists/List/List.C +++ b/src/OpenFOAM/containers/Lists/List/List.C @@ -37,54 +37,57 @@ License // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template -void Foam::List::doResize(const label newSize) +void Foam::List::doResize(const label len) { - if (newSize < 0) + if (len == this->size_) { - FatalErrorInFunction - << "bad size " << newSize - << abort(FatalError); + return; } - if (newSize != this->size_) + if (len > 0) { - if (newSize > 0) + // With sign-check to avoid spurious -Walloc-size-larger-than + T* nv = new T[len]; + + const label overlap = min(this->size_, len); + + if (overlap) { - // With sign-check to avoid spurious -Walloc-size-larger-than - T* nv = new T[newSize]; - - const label overlap = min(this->size_, newSize); - - if (overlap) + #ifdef USEMEMCPY + if (is_contiguous::value) { - #ifdef USEMEMCPY - if (is_contiguous::value) + std::memcpy + ( + static_cast(nv), this->v_, overlap*sizeof(T) + ); + } + else + #endif + { + List_ACCESS(T, *this, vp); + for (label i = 0; i < overlap; ++i) { - std::memcpy - ( - static_cast(nv), this->v_, overlap*sizeof(T) - ); - } - else - #endif - { - // No speedup observed for copy assignment on simple types - List_ACCESS(T, *this, vp); - for (label i = 0; i < overlap; ++i) - { - nv[i] = std::move(vp[i]); - } + nv[i] = std::move(vp[i]); } } + } - clear(); - this->size_ = newSize; - this->v_ = nv; - } - else + clear(); + this->size_ = len; + this->v_ = nv; + } + else + { + // Or only #ifdef FULLDEBUG + if (len < 0) { - clear(); + FatalErrorInFunction + << "bad size " << len + << abort(FatalError); } + // #endif + + clear(); } } @@ -257,7 +260,7 @@ Foam::List::List(List& a, bool reuse) { if (reuse) { - // swap content + // Steal content this->v_ = a.v_; a.v_ = nullptr; a.size_ = 0; @@ -435,15 +438,16 @@ Foam::List::~List() // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template -void Foam::List::resize(const label newSize, const T& val) +void Foam::List::resize(const label len, const T& val) { - const label oldSize = this->size_; - this->doResize(newSize); + label idx = this->size_; + this->doResize(len); List_ACCESS(T, *this, vp); - for (label i = oldSize; i < newSize; ++i) + while (idx < len) { - vp[i] = val; + vp[idx] = val; + ++idx; } } @@ -562,7 +566,7 @@ template template void Foam::List::operator=(const FixedList& list) { - reAlloc(label(N)); + reAlloc(static_cast