QString: use new assign() in operator=({QByteArray, QChar, char *})
operator=(~) and assign() share similar names but, until now, have not shared the same functionality. This patch introduces the usage of QString::assign() within the non-sharing assignment operators to effectively boost efficiency by reusing the available capacity. Since we're re-using the capacity we update the test case in places where they don't hold true anymore. Since these assignment operators are frequently used in many places, both within Qt and non-Qt code, this patch comes with benchmarks. The preview of the benchmark results are compared with this patch and before this patch. The results show a boost in performance for the QByteArray and 'const char*' overload. The QLatin1StringView overload already preserved the capacity and has a better performance than the assign() alternative, so don't us it there. (x86_64-little_endian-lp64 shared (dynamic) release build (O3); by gcc 13.2.1, endeavouros ; 13th Gen Intel(R) Core(TM) i9-13900K benchmarks executed with -perf -iterations 1000000 * The last value at the EOL represent the string size. QString &operator=(const QByteArray &a) (current) 64.3 cycles/iter; 300 instructions/iter; 17 nsec/iter (5) 65.8 cycles/iter; 366 instructions/iter; 12 nsec/iter (10) 62.9 cycles/iter; 301 instructions/iter; 11.5 nsec/iter (20) 61.3 cycles/iter; 315 instructions/iter; 11.1 nsec/iter (50) 71.4 cycles/iter; 386 instructions/iter; 13 nsec/iter (100) 136.9 cycles/iter; 811 instructions/iter; 24.5 nsec/iter (500) 245.8 cycles/iter; 1394 instructions/iter; 42.5 nsec/iter (1'000) QString &operator=(const QByteArray &a) (before) 78 cycles/iter; 399 instructions/iter; 15.3 nsec/iter (5) 82.3 cycles/iter; 465 instructions/iter; 15 nsec/iter (10) 76.7 cycles/iter; 400 instructions/iter; 14 nsec/iter (20) 79.5 cycles/iter; 414 instructions/iter; 14.5 nsec/iter (50) 91.4 cycles/iter; 485 instructions/iter; 16.7 nsec/iter (100) 189 cycles/iter; 910 instructions/iter; 34.4 nsec/iter (500) 320 cycles/iter; 1666 instructions/iter; 56 nsec/iter (1'000) QString &operator=(const char *ch) (current) 70 cycles/iter; 317 instructions/iter; 12 nsec/iter (5) 71 cycles/iter; 383 instructions/iter; 12.3 nsec/iter (10) 64 cycles/iter; 318 instructions/iter; 11.1 nsec/iter (20) 69 cycles/iter; 340 instructions/iter; 12 nsec/iter (50) 77 cycles/iter; 419 instructions/iter; 13.5 nsec/iter (100) 141 cycles/iter; 899 instructions/iter; 24.4 nsec/iter (500) 280 cycles/iter; 1518 instructions/iter; 48.4 nsec/iter (1'000) QString &operator=(const char *ch) (before) 86.7 cycles/iter; 416 instructions/iter; 15 nsec/iter (5) 87.8 cycles/iter; 482 instructions/iter; 15.7 nsec/iter (10) 82.4 cycles/iter; 417 instructions/iter; 14.3 nsec/iter (20) 90.2 cycles/iter; 443 instructions/iter; 15.6 nsec/iter (50) 101.4 cycles/iter; 518 instructions/iter; 17.7 nsec/iter (100) 204.4 cycles/iter; 994 instructions/iter; 36.5 nsec/iter (500) 337.9 cycles/iter; 1789 instructions/iter; 58.9 nsec/iter (1'000) * current implemented as: assign(other) QString &operator=(QLatin1StringView other) (current) 47.4 cycles/iter; 237 instructions/iter; 8.2 nsec/iter (5) 46.2 cycles/iter; 237 instructions/iter; 7.9 nsec/iter (10) 46.8 cycles/iter; 255 instructions/iter; 8 nsec/iter (20) 59 cycles/iter; 273 instructions/iter; 10.2 nsec/iter (50) 55 cycles/iter; 300 instructions/iter; 9.5 nsec/iter (100) 94.3 cycles/iter; 525 instructions/iter; 16.3 nsec/iter (500) 166 cycles/iter; 804 instructions/iter; 28.7 nsec/iter (1'000) QString &operator=(QLatin1StringView other) (before) 14 cycles/iter; 79 instructions/iter; 2.5 nsec/iter (5) 14 cycles/iter; 79 instructions/iter; 2.6 nsec/iter (10) 16 cycles/iter; 97 instructions/iter; 3 nsec/iter (20) 19 cycles/iter; 115 instructions/iter; 3.5 nsec/iter (50) 23 cycles/iter; 142 instructions/iter; 4.2 nsec/iter (100) 91 cycles/iter; 367 instructions/iter; 16.6 nsec/iter (500) 131 cycles/iter; 646 instructions/iter; 23.4 nsec/iter (1'000) Task-number: QTBUG-106201 Change-Id: Ie852f6abd1cf16164802acddb048eae5df59758f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
76e56eb164
commit
b134718c11
@ -2873,16 +2873,7 @@ QString &QString::operator=(QLatin1StringView other)
|
||||
*/
|
||||
QString &QString::operator=(QChar ch)
|
||||
{
|
||||
const qsizetype capacityAtEnd = capacity() - d.freeSpaceAtBegin();
|
||||
if (isDetached() && capacityAtEnd >= 1) { // assumes d->alloc == 0 -> !isDetached() (sharedNull)
|
||||
// re-use existing capacity:
|
||||
d.data()[0] = ch.unicode();
|
||||
d.data()[1] = 0;
|
||||
d.size = 1;
|
||||
} else {
|
||||
operator=(QString(ch));
|
||||
}
|
||||
return *this;
|
||||
return assign(1, ch);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -786,10 +786,21 @@ public:
|
||||
: QString(fromUtf8(a))
|
||||
{}
|
||||
QT_ASCII_CAST_WARN inline QString &operator=(const char *ch)
|
||||
{ return (*this = fromUtf8(ch)); }
|
||||
{
|
||||
if (!ch) {
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
return assign(ch);
|
||||
}
|
||||
QT_ASCII_CAST_WARN inline QString &operator=(const QByteArray &a)
|
||||
{ return (*this = fromUtf8(a)); }
|
||||
|
||||
{
|
||||
if (a.isNull()) {
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
return assign(a);
|
||||
}
|
||||
// these are needed, so it compiles with STL support enabled
|
||||
QT_ASCII_CAST_WARN inline QString &prepend(const char *s)
|
||||
{ return prepend(QUtf8StringView(s)); }
|
||||
|
@ -486,6 +486,7 @@ private slots:
|
||||
void operator_pluseq_qbytearray_data() { operator_pluseq_data(); }
|
||||
void operator_pluseq_charstar() { operator_pluseq_impl<const char *, QString &(QString::*)(const char *)>(); }
|
||||
void operator_pluseq_charstar_data() { operator_pluseq_data(); }
|
||||
void operator_assign_symmetry();
|
||||
#endif // !defined(QT_RESTRICTED_CAST_FROM_ASCII) && !defined(QT_NO_CAST_FROM_ASCII)
|
||||
|
||||
void operator_pluseq_special_cases();
|
||||
@ -3706,6 +3707,29 @@ void tst_QString::operator_eqeq_bytearray()
|
||||
QVERIFY(!(expected != src.constData()));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QString::operator_assign_symmetry()
|
||||
{
|
||||
{
|
||||
QString str("DATA");
|
||||
str.operator=(QString());
|
||||
QCOMPARE_EQ(str.capacity(), 0);
|
||||
QVERIFY(str.isNull());
|
||||
}
|
||||
{
|
||||
QString str("DATA");
|
||||
str.operator=(QByteArray());
|
||||
QCOMPARE_EQ(str.capacity(), 0);
|
||||
QVERIFY(str.isNull());
|
||||
}
|
||||
{
|
||||
QString str("DATA");
|
||||
const char *data = nullptr;
|
||||
str.operator=(data);
|
||||
QCOMPARE_EQ(str.capacity(), 0);
|
||||
QVERIFY(str.isNull());
|
||||
}
|
||||
}
|
||||
#endif // !defined(QT_RESTRICTED_CAST_FROM_ASCII) && !defined(QT_NO_CAST_FROM_ASCII)
|
||||
|
||||
void tst_QString::swap()
|
||||
@ -8552,12 +8576,14 @@ void tst_QString::assignQChar()
|
||||
// assign to null QString:
|
||||
s = sp;
|
||||
QCOMPARE(s, QString(sp));
|
||||
QCOMPARE(s.capacity(), 1);
|
||||
|
||||
// assign to non-null QString with enough capacity:
|
||||
s.clear();
|
||||
s.squeeze();
|
||||
s.reserve(3);
|
||||
s = QLatin1String("foo");
|
||||
const int capacity = s.capacity();
|
||||
QCOMPARE(capacity, 3);
|
||||
QCOMPARE(s.capacity(), 3);
|
||||
s = sp;
|
||||
QCOMPARE(s, QString(sp));
|
||||
QCOMPARE(s.capacity(), capacity);
|
||||
@ -8567,7 +8593,6 @@ void tst_QString::assignQChar()
|
||||
QString s2 = s;
|
||||
s = sp;
|
||||
QCOMPARE(s, QString(sp));
|
||||
QCOMPARE(s.capacity(), 1);
|
||||
|
||||
// assign to empty QString:
|
||||
s = QString(u""_s);
|
||||
@ -8575,7 +8600,6 @@ void tst_QString::assignQChar()
|
||||
QCOMPARE(s.capacity(), 0);
|
||||
s = sp;
|
||||
QCOMPARE(s, QString(sp));
|
||||
QCOMPARE(s.capacity(), 1);
|
||||
}
|
||||
|
||||
void tst_QString::isRightToLeft_data()
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
#include <QStringList>
|
||||
#include <QByteArray>
|
||||
#include <QLatin1StringView>
|
||||
#include <QFile>
|
||||
#include <QTest>
|
||||
#include <limits>
|
||||
@ -43,10 +45,22 @@ private slots:
|
||||
void toDouble_data();
|
||||
void toDouble();
|
||||
|
||||
// operator=(~)
|
||||
#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII)
|
||||
void operator_assign_BA() { operator_assign<QByteArray>(); }
|
||||
void operator_assign_BA_data() { operator_assign_data(); }
|
||||
void operator_assign_char() { operator_assign<const char*>(); };
|
||||
void operator_assign_char_data() { operator_assign_data();}
|
||||
#endif
|
||||
void operator_assign_L1SV() { operator_assign<QLatin1StringView>(); }
|
||||
void operator_assign_L1SV_data() { operator_assign_data(); }
|
||||
|
||||
private:
|
||||
void section_data_impl(bool includeRegExOnly = true);
|
||||
template <typename RX> void section_impl();
|
||||
template <typename Integer> void number_impl();
|
||||
template <typename T> void operator_assign();
|
||||
void operator_assign_data();
|
||||
};
|
||||
|
||||
tst_QString::tst_QString()
|
||||
@ -450,6 +464,47 @@ void tst_QString::toDouble()
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
template <typename T> void tst_QString::operator_assign()
|
||||
{
|
||||
QFETCH(QByteArray, data);
|
||||
QString str(data.size(), Qt::Uninitialized);
|
||||
|
||||
T tdata;
|
||||
if constexpr (std::is_same_v<T, const char*>) {
|
||||
tdata = data.constData();
|
||||
} else if constexpr (std::is_same_v<T, QLatin1String>) {
|
||||
tdata = T(data.constData(), data.size());
|
||||
} else {
|
||||
tdata = T(data.constData(), data.size());
|
||||
tdata.detach();
|
||||
}
|
||||
|
||||
QBENCHMARK {
|
||||
str.operator=(tdata);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QString::operator_assign_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
|
||||
QByteArray data;
|
||||
data.fill('a', 5);
|
||||
QTest::newRow("length: 5") << data;
|
||||
data.fill('b', 10);
|
||||
QTest::newRow("length: 10") << data;
|
||||
data.fill('c', 20);
|
||||
QTest::newRow("length: 20") << data;
|
||||
data.fill('d', 50);
|
||||
QTest::newRow("length: 50") << data;
|
||||
data.fill('e', 100);
|
||||
QTest::newRow("length: 100") << data;
|
||||
data.fill('f', 500);
|
||||
QTest::newRow("length: 500") << data;
|
||||
data.fill('g', 1'000);
|
||||
QTest::newRow("length: 1'000") << data;
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QString)
|
||||
|
||||
#include "tst_bench_qstring.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user