From 12718e5ea289df1d3f113bf0ff4e64ea8961e20d Mon Sep 17 00:00:00 2001 From: Andrei Golubev Date: Thu, 29 Oct 2020 12:11:49 +0100 Subject: [PATCH] Add QList::emplaceFront() that fixes huge performance issue in prepend Prepend in QList was using insert() logic that always uses append-aware functions. This results in the fact that freeSpaceAtBegin() is always 0 and forces us to actually allocate on *every* call (with the same capacity!). Vicious cycle is hot-fixable with introduction of emplaceFront (or anything prepend-aware, really) This brings me from 632ms to 0.65ms for 100k iterations of list.prepend(int(0)). Still ~3x worse than QList in 5.15 but much faster than QVector, which takes 382ms in the same workload Not addressed: - QString/QBA - Other prepend functions in QList e.g. prepend(it1, it2) - Lower-level array operations that should just be extended Task-number: QTBUG-86583 Change-Id: Ie82b07d81a67605cd308d9fabf9532d57935647f Reviewed-by: Lars Knoll Reviewed-by: Thiago Macieira --- src/corelib/tools/qlist.h | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 96efe28a310..15c2e53e1dc 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -316,12 +316,15 @@ public: void append(rvalue_ref t) { emplaceBack(std::move(t)); } void append(const QList &l) { append(l.constBegin(), l.constEnd()); } void append(QList &&l); - void prepend(rvalue_ref t); - void prepend(parameter_type t); + void prepend(rvalue_ref t) { emplaceFront(std::move(t)); } + void prepend(parameter_type t) { emplaceFront(t); } template reference emplaceBack(Args&&... args) { return *emplace(count(), std::forward(args)...); } + template + inline reference emplaceFront(Args&&... args); + iterator insert(qsizetype i, parameter_type t) { return insert(i, 1, t); } iterator insert(qsizetype i, qsizetype n, parameter_type t); @@ -651,13 +654,6 @@ inline void QList::remove(qsizetype i, qsizetype n) } } -template -inline void QList::prepend(parameter_type t) -{ insert(0, 1, t); } -template -void QList::prepend(rvalue_ref t) -{ insert(0, std::move(t)); } - template inline T QList::value(qsizetype i, parameter_type defaultValue) const { @@ -711,6 +707,29 @@ inline void QList::append(QList &&other) } } +template +template +inline typename QList::reference QList::emplaceFront(Args &&... args) +{ + const bool shouldGrow = d->shouldGrowBeforeInsert(d.begin(), 1); + const auto newSize = size() + 1; + if (d->needsDetach() || newSize > d->constAllocatedCapacity() || shouldGrow) { + const auto flags = d->detachFlags() | Data::GrowsBackwards; + DataPointer detached(DataPointer::allocateGrow(d, newSize, flags)); + + T tmp(std::forward(args)...); + detached->copyAppend(constBegin(), constEnd()); + // insert here makes sure we have extra free space at beginning. we + // actually need a proper copyPrepend here instead. + detached->insert(detached.begin(), 1, std::move(tmp)); + d.swap(detached); + } else { + // ### replace with emplaceFront + d->emplace(d.begin(), std::forward(args)...); + } + return *d.begin(); +} + template inline typename QList::iterator