Clear or resize QVector without releasing memory



  • How can I clear a QVector or resize it to have fewer items without causing the object to release the memory that it currently has reserved? I want to remove all items from the vector, but I also want the vector to keep its memory reserved because I anticipate immediately putting roughly just as many items back into it.

    The implementation of QVector::clear in 5.3.0 releases the memory, as documented. QVector::resize also releases the memory if the new size is less than half of the reserved size. The performance impact of these deallocations, followed by having to reallocate the memory as soon as new items are added back in, is significant. I would rather avoid this significant and counter-productive operation.

    I know about the QVector::squeeze function to release memory when I know it is the most opportune moment, but I have not noticed any functionality to prevent releasing memory.

    Should I be using a different container? I am currently using a QVector because I am making use of its contiguous block of data for more efficient low-level access and iteration.


  • Moderators

    Hi,

    There's no such functionality that I'm aware of.

    Do you call QVector::reserve() before adding your data? The performance hit you see could be from the vector having to reallocate memory multiple times as it grows.

    Also, are you storing large objects in your vector? You can consider storing (shared) pointers instead, so that clearing + adding doesn't involve lots of vector memory.


  • Moderators

    Another thing to consider - if you're going to immediately put some items back in, then maybe you can just overwrite existing ones without explicitly removing them.



  • In a 2011 "qt-interest discussion":http://comments.gmane.org/gmane.comp.lib.qt.general/42277,using QVector::resize(0) was suggested as the best solution.
    However, some posters pointed out that while the docs hinted that resize would not deallocate memory, they didn't explicitly say so. The behavior might have changed since then. You might need to re-check the source code.

    Edit: If QVector::resize() was indeed changed, and no new way to resize a QVector without deallocating memory was introduced, I would actually consider this a semi-bug. Keeping memory reserved can be essential in embedded environments that are short of memory.



  • [quote author="JKSH" date="1400717911"]Do you call QVector::reserve() before adding your data? The performance hit you see could be from the vector having to reallocate memory multiple times as it grows.[/quote]
    I agree that reserving the prior capacity would be better than inserting without reserving, but it is still counterproductive to have to reallocate the same amount of memory right after it was deallocated.

    [quote author="JKSH" date="1400717911"]Also, are you storing large objects in your vector? You consider storing (shared) pointers instead, so that clearing + adding doesn't involve lots of vector memory.[/quote]
    In my particular case, my vector is already a container of pointers. But since this code can be called very frequently, any memory operation introduces a significant impact.

    [quote author="Chris Kawa" date="1400720984"]Another thing to consider - if you're going to immediately put some items back in, then maybe you can just overwrite existing ones without explicitly removing them.[/quote]
    If I knew I had exactly the same number of items every time, I could do this. Unfortunately I cannot guarantee that I will have exactly the same number of items every time. But I do know that in most cases where time efficiency is important, I expect to have very close to the same number of items, so I will likely require close to the same amount of memory. I don't want to have to deal with the extra complexity of handling conditions where I have more or fewer items than last time when the QVector object could very easily do what I want with a lot less effort.

    [quote author="Asperamanca" date="1400755759"]In a 2011 "qt-interest discussion":http://comments.gmane.org/gmane.comp.lib.qt.general/42277,using QVector::resize(0) was suggested as the best solution.
    However, some posters pointed out that while the docs hinted that resize would not deallocate memory, they didn't explicitly say so. The behavior might have changed since then. You might need to re-check the source code.

    Edit: If QVector::resize() was indeed changed, and no new way to resize a QVector without deallocating memory was introduced, I would actually consider this a semi-bug. Keeping memory reserved can be essential in embedded environments that are short of memory.[/quote]
    Thank you for the link. I agree that it would be acceptable to use QVector::resize(0) to clear all elements without releasing the memory. However, this is not the way it works now, at least in version 5.0.2~5.3.0. Right now, if more than half of the reserved elements are unused on a resize, the memory will be released (after copying the remaining data to a smaller memory block).

    It looks like I will have to report this as a bug, as there appear to be no efficient alternatives.



  • After a closer look at the code, it seems that a QVector would already shrink in 4.8

    Here's the relevant code for comparison:

    4.8.1:
    @
    template <typename T>
    void QVector<T>::resize(int asize)
    { realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ?
    QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo<T>::isStatic)
    : d->alloc); }@

    5.3:
    @
    template <typename T>
    void QVector<T>::resize(int asize)
    {
    int newAlloc;
    const int oldAlloc = int(d->alloc);
    QArrayData::AllocationOptions opt;

    if (asize > oldAlloc) { // there is not enough space
        newAlloc = asize;
        opt = QArrayData::Grow;
    } else if (!d->capacityReserved && asize < d->size && asize < (oldAlloc >> 1)) { // we want to shrink
        newAlloc = asize;
        opt = QArrayData::Grow;
    } else {
        newAlloc = oldAlloc;
    }
    reallocData(asize, newAlloc, opt);
    

    }@

    If I didn't misread, the realloc option to shrink the vector is there in 4.8 just as it is in 5.3 - it's just hidden better.

    From which we learn: Don't trust postings, trust the code.





  • I have also needed this behavior and when looking at the code for QVector, it seems

    @
    vec.erase(vec.begin(), vec.end());
    @

    will do what you need. Unless I'm reading the code wrongly. Though, it is not documented to have this behavior and there are some comments in the code that suggests that it could also realloc/shrink, but currently doesn't.

    The vector needs to not be shared at this point, but that would also be the case for clear() or resize(0) etc.



  • Using a "QVarLengthArray":http://qt-project.org/doc/qt-5/qvarlengtharray.html#details solved this issue for my application.

    Although, the documentation does not mention if issuing a clear() will deallocate the part of the array which is stored into the heap, you can be sure it can't delete the part you reserved on the stack. ;)

    However, you are limited to a certain extent on the preallocated stack memory.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.