Fix performance issue with QList::insert() for complex T

When storing complex types in the list and inserting in the middle,
we ended up in some cases moving the items in the list onto itself,
to make space for 0 new items.

Obviously that's not a very good idea. It was not a huge deal for
POD or relocatable types as we'd use memmove in that case which would
return quickly. But for complex types, we actually did copy around
half of the items stored in the list onto themselves.

Change-Id: I54467dccf2e17ba4a604bded755242197dd96b06
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
Lars Knoll 2020-10-31 15:33:57 +01:00
parent 4d49459e4b
commit edd1e931d1

View File

@ -288,7 +288,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
@ -303,7 +303,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtBegin());
@ -321,7 +321,8 @@ public:
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
@ -334,7 +335,8 @@ public:
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
@ -543,7 +545,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
@ -596,7 +598,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtBegin());
@ -646,7 +648,8 @@ public:
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
@ -695,7 +698,8 @@ public:
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
@ -873,7 +877,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
@ -895,7 +899,7 @@ public:
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(b < e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT((e - b) <= this->freeSpaceAtBegin());
@ -918,7 +922,8 @@ public:
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
@ -937,7 +942,8 @@ public:
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(!this->isShared());
Q_ASSERT(n);
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
@ -1169,6 +1175,8 @@ public:
{
Q_ASSERT(!this->isShared() || n == 0);
Q_ASSERT(size_t(this->allocatedCapacity() - this->size) >= n);
if (!n)
return;
Base::insert(GrowsForwardTag{}, this->end(), n, t);
}
@ -1200,8 +1208,10 @@ public:
// free space by reallocating more frequently)
T *where = this->begin() + i;
const auto beginSize = sizeToInsertAtBegin(where, n);
Base::insert(GrowsBackwardsTag{}, where, beginSize, copy);
Base::insert(GrowsForwardTag{}, where, qsizetype(n) - beginSize, copy);
if (beginSize)
Base::insert(GrowsBackwardsTag{}, where, beginSize, copy);
if (n - beginSize)
Base::insert(GrowsForwardTag{}, where, n - beginSize, copy);
}
}
}
@ -1227,8 +1237,10 @@ public:
// free space by reallocating more frequently)
T *where = this->begin() + i;
const auto k = sizeToInsertAtBegin(where, n);
Base::insert(GrowsBackwardsTag{}, where, data, data + k);
Base::insert(GrowsForwardTag{}, where, data + k, data + n);
if (k)
Base::insert(GrowsBackwardsTag{}, where, data, data + k);
if (k != n)
Base::insert(GrowsForwardTag{}, where, data + k, data + n);
}
}