Introduce QADP::reallocateGrow()

Added realloc() code path to QMovableArrayOps

Implemented fast realloc() based growing for QADP and used it in
QList::emplaceBack. This gives quite a bit of speedup and shows better
results than 5.15 at 100k+ iterations of "list.append(elem)", meanwhile
also closing a gap between movable types

Task-number: QTBUG-87330
Change-Id: I42fc182ecd93c85600dac622385152fc57735da8
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Andrei Golubev 2020-10-28 18:14:21 +01:00 committed by Lars Knoll
parent 8f7016252a
commit 2e8656779a
4 changed files with 49 additions and 4 deletions

View File

@ -236,6 +236,7 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
qsizetype headerSize = sizeof(QArrayData);
qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
qptrdiff offset = dataPointer ? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data) : headerSize;
Q_ASSERT(offset > 0);
allocSize = reserveExtraBytes(allocSize);
if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot reallocate reliably
@ -245,6 +246,8 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
if (header) {
header->alloc = uint(capacity);
dataPointer = reinterpret_cast<char *>(header) + offset;
} else {
dataPointer = nullptr;
}
return qMakePair(static_cast<QArrayData *>(header), dataPointer);
}

View File

@ -854,6 +854,10 @@ template <class T>
struct QMovableArrayOps
: QGenericArrayOps<T>
{
protected:
typedef QTypedArrayData<T> Data;
public:
// using QGenericArrayOps<T>::appendInitialize;
// using QGenericArrayOps<T>::copyAppend;
// using QGenericArrayOps<T>::moveAppend;
@ -993,6 +997,13 @@ struct QMovableArrayOps
(b++)->~T();
} while (b != e);
}
void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
{
auto pair = Data::reallocateUnaligned(this->d, this->ptr, alloc, option);
this->d = pair.first;
this->ptr = pair.second;
}
};
template <class T, class = void>

View File

@ -244,6 +244,25 @@ public:
return lhs.data() != rhs.data() || lhs.size != rhs.size;
}
static void reallocateGrow(QArrayDataPointer &from, qsizetype n)
{
Q_ASSERT(n > 0);
if constexpr (!QTypeInfo<T>::isRelocatable || alignof(T) > alignof(std::max_align_t)) {
QArrayDataPointer dd(allocateGrow(from, n, QArrayData::AllocateAtEnd));
dd->copyAppend(from.data(), from.data() + from.size);
from.swap(dd);
} else {
if (from.needsDetach()) {
QArrayDataPointer dd(allocateGrow(from, n, QArrayData::AllocateAtEnd));
dd->copyAppend(from.data(), from.data() + from.size);
from.swap(dd);
} else {
from->reallocate(from.constAllocatedCapacity() - from.freeSpaceAtEnd() + n, QArrayData::Grow); // fast path
}
}
}
private:
[[nodiscard]] QPair<Data *, T *> clone() const
{

View File

@ -770,10 +770,22 @@ template<typename... Args>
inline typename QList<T>::reference QList<T>::emplaceBack(Args &&... args)
{
if (d->needsDetach() || !d.freeSpaceAtEnd()) {
DataPointer detached(DataPointer::allocateGrow(d, 1, QArrayData::AllocateAtEnd));
detached->copyAppend(constBegin(), constEnd());
detached->emplace(detached.end(), std::forward<Args>(args)...);
d.swap(detached);
// condition below should follow the condition in QArrayDataPointer::reallocateGrow()
if constexpr (!QTypeInfo<T>::isRelocatable || alignof(T) > alignof(std::max_align_t)) {
// avoid taking a temporary copy of Args
DataPointer detached(DataPointer::allocateGrow(d, 1, QArrayData::AllocateAtEnd));
detached->copyAppend(constBegin(), constEnd());
detached->emplace(detached.end(), std::forward<Args>(args)...);
d.swap(detached);
} else {
// Create an element here to handle cases when a user moves the element
// from a container to the same container. This is required as we call
// reallocate, which could delete the data args points to.
// This should be optimised to only take the copy when really required.
T tmp(std::forward<Args>(args)...);
DataPointer::reallocateGrow(d, 1);
d->emplace(d.end(), std::move(tmp));
}
} else {
d->emplace(d.end(), std::forward<Args>(args)...);
}