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 <lars.knoll@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
02a5928fa5
commit
12718e5ea2
@ -316,12 +316,15 @@ public:
|
||||
void append(rvalue_ref t) { emplaceBack(std::move(t)); }
|
||||
void append(const QList<T> &l) { append(l.constBegin(), l.constEnd()); }
|
||||
void append(QList<T> &&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 <typename ...Args>
|
||||
reference emplaceBack(Args&&... args) { return *emplace(count(), std::forward<Args>(args)...); }
|
||||
|
||||
template <typename ...Args>
|
||||
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<T>::remove(qsizetype i, qsizetype n)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void QList<T>::prepend(parameter_type t)
|
||||
{ insert(0, 1, t); }
|
||||
template <typename T>
|
||||
void QList<T>::prepend(rvalue_ref t)
|
||||
{ insert(0, std::move(t)); }
|
||||
|
||||
template<typename T>
|
||||
inline T QList<T>::value(qsizetype i, parameter_type defaultValue) const
|
||||
{
|
||||
@ -711,6 +707,29 @@ inline void QList<T>::append(QList<T> &&other)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
inline typename QList<T>::reference QList<T>::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>(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>(args)...);
|
||||
}
|
||||
return *d.begin();
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline typename QList<T>::iterator
|
||||
|
Loading…
Reference in New Issue
Block a user