Skip to content

Instantly share code, notes, and snippets.

@mpaperno
Last active March 3, 2020 21:39
Show Gist options
  • Save mpaperno/e09d1c295eef94a45176c5cb2002a00e to your computer and use it in GitHub Desktop.
Save mpaperno/e09d1c295eef94a45176c5cb2002a00e to your computer and use it in GitHub Desktop.
Qt building strings benchmark (based on question https://lists.qt-project.org/pipermail/interest/2020-February/034547.html)
#include <QtTest/QtTest>
#include <QStringBuilder>
class bm_stringBuilder : public QObject
{
Q_OBJECT
public:
bm_stringBuilder() {}
private slots:
void stringBuilder_data()
{
int i = 0;
QTest::addColumn<int>("testType");
QTest::newRow("QString.arg.arg") << i++;
QTest::newRow("QString.arg") << i++;
QTest::newRow("QStringLiteral.arg") << i++;
QTest::newRow("QLatin1String.arg") << i++;
QTest::newRow("QStringView.arg") << i++;
QTest::newRow("QString.append") << i++;
QTest::newRow("QStringLiteral.append") << i++;
QTest::newRow("QByteArray.append") << i++;
QTest::newRow("QByteArrayLiteral.append") << i++;
QTest::newRow("QString.asprinf") << i++;
QTest::newRow("QStringBuilder") << i++;
QTest::newRow("QTextStream") << i++;
}
void stringBuilder()
{
QString result;
const QString expected("Type: %1; strVal1=This is a test; strVal2=a literal string; intVal1=255 (0x00ff);");
// some constants for replacing/appending in all tests
const QLatin1String strVal1("This is a test");
const QLatin1String strVal2("a literal string");
const int intVal1 = 255;
QFETCH(int, testType);
switch (testType) {
case 0: // QString.arg().arg()... (multi-arg)
QBENCHMARK {
result = QString("Type: %1; strVal1=%2; strVal2=%3; intVal1=%4 (0x%5);")
.arg(testType)
.arg(strVal1, strVal2)
.arg(intVal1)
.arg(intVal1, 4, 16, QChar('0'));
}
QCOMPARE(result, expected.arg(testType));
break;
case 1: // QString.arg()
// The verbose QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')) is faster
// than arg(intVal1, 4, 16, QChar('0')), even if it's the only replacement argument
// in the string (QString("%1").arg(...)).
QBENCHMARK {
result = QString("Type: %1; strVal1=%2; strVal2=%3; intVal1=%4 (0x%5);")
.arg(QString::number(testType), strVal1, strVal2, QString::number(intVal1),
QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')));
}
QCOMPARE(result, expected.arg(testType));
break;
case 2: // QStringLiteral.arg()
QBENCHMARK {
result = QStringLiteral("Type: %1; strVal1=%2; strVal2=%3; intVal1=%4 (0x%5);")
.arg(QString::number(testType), strVal1, strVal2, QString::number(intVal1),
QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')));
}
QCOMPARE(result, expected.arg(testType));
break;
case 3: // QLatin1String.arg()
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QBENCHMARK {
result = QLatin1String("Type: %1; strVal1=%2; strVal2=%3; intVal1=%4 (0x%5);")
.arg(QString::number(testType), strVal1, strVal2, QString::number(intVal1),
QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')));
}
QCOMPARE(result, expected.arg(testType));
#else
QSKIP("Test requires newer Qt version");
#endif
break;
case 4: // QStringView.arg()
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
{
const QString temp = QStringLiteral("Type: %1; strVal1=%2; strVal2=%3; intVal1=%4 (0x%5);");
QBENCHMARK {
result = QStringView(temp)
.arg(QString::number(testType), strVal1, strVal2, QString::number(intVal1),
QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')));
}
QCOMPARE(result, expected.arg(testType));
}
#else
QSKIP("Test requires newer Qt version");
#endif
break;
case 5: // QString.append()
// QString.append(QLatin1String("foo")) is faster than append("foo")
// and same as, or a very tiny bit slower than, append(QStringLiteral("foo"))
QBENCHMARK {
result = QString("Type: ").append(QString::number(testType))
.append(QLatin1String("; strVal1=")).append(strVal1)
.append(QLatin1String("; strVal2=")).append(strVal2)
.append(QLatin1String("; intVal1=")).append(QString::number(intVal1))
.append(QLatin1String(" (0x"))
.append(QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')))
.append(QLatin1String(");"));
}
QCOMPARE(result, expected.arg(testType));
break;
case 6: // QStringLiteral.append()
// Here also append(QStringLiteral("foo")) is same as, or a very tiny bit
// faster than, append(QLatin1String("foo"))
QBENCHMARK {
result = QStringLiteral("Type: ").append(QString::number(testType))
.append(QStringLiteral("; strVal1=")).append(strVal1)
.append(QStringLiteral("; strVal2=")).append(strVal2)
.append(QStringLiteral("; intVal1=")).append(QString::number(intVal1))
.append(QStringLiteral(" (0x"))
.append(QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0')))
.append(QStringLiteral(");"));
}
QCOMPARE(result, expected.arg(testType));
break;
case 7: // QByteArray.append()
QBENCHMARK {
// QByteArray.append("foo") is faster than QByteArray.append(QLatin1String("foo"))
// and about the same as QByteArray.append(QByteArrayLiteral("foo"))
result = QByteArray("Type: ").append(QByteArray::number(testType))
.append("; strVal1=").append(strVal1)
.append("; strVal2=").append(strVal2)
.append("; intVal1=").append(QByteArray::number(intVal1))
.append(" (0x").append(QByteArray::number(intVal1, 16).rightJustified(4, '0'))
.append(");");
}
QCOMPARE(result, expected.arg(testType));
break;
case 8: // QByteArrayLiteral.append()
// Here also append("foo") is faster than append(QLatin1String("foo"))
// and about the same as append(QByteArrayLiteral("foo"))
QBENCHMARK {
result = QByteArrayLiteral("Type: ").append(QByteArray::number(testType))
.append(("; strVal1=")).append(strVal1)
.append(("; strVal2=")).append(strVal2)
.append(("; intVal1=")).append(QByteArray::number(intVal1))
.append((" (0x")).append(QByteArray::number(intVal1, 16).rightJustified(4, '0'))
.append((");"));
}
QCOMPARE(result, expected.arg(testType));
break;
case 9: // QString.asprinf()
QBENCHMARK {
result = QString::asprintf("Type: %d; strVal1=%s; strVal2=%s; intVal1=%d (0x%04x);",
testType, strVal1.data(), strVal2.data(), intVal1, intVal1);
}
QCOMPARE(result, expected.arg(testType));
break;
case 10: // QStringBuilder
// No difference using "+" with QT_USE_QSTRINGBUILDER defined vs.
// using "%" with #include <QStringBuilder>.
// OTOH using "+" w/out QT_USE_QSTRINGBUILDER is almost as slow as the multi- .arg() tests.
// QString::number() is faster here than QByteArray::number()
QBENCHMARK {
result = "Type: " + QString::number(testType)
+ "; strVal1=" + strVal1
+ "; strVal2=" + strVal2
+ "; intVal1=" + QString::number(intVal1)
+ " (0x" + QString::number(intVal1, 16).rightJustified(4, QLatin1Char('0'))
+ ");";
}
QCOMPARE(result, expected.arg(testType));
break;
case 11: // QTextStream
// stream << QLatin1String("foo") is faster than stream << "foo"
QBENCHMARK {
result = QString();
QTextStream out(&result, QIODevice::WriteOnly);
out << QLatin1String("Type: ") << testType
<< QLatin1String("; strVal1=") << strVal1
<< QLatin1String("; strVal2=") << strVal2
<< QLatin1String("; intVal1=") << intVal1
<< QLatin1String(" (0x") << hex << right
<< qSetFieldWidth(4) << qSetPadChar(QLatin1Char('0')) << intVal1
<< qSetFieldWidth(0) << QLatin1String(");");
}
QCOMPARE(result, expected.arg(testType));
break;
default:
break;
}
}
};
QTEST_MAIN(bm_stringBuilder)
#include "bm_stringBuilder.moc"
QT *= testlib
CONFIG *= console
CONFIG *= testcase
TARGET = bm_strings
DEFINES *= QT_USE_QSTRINGBUILDER
SOURCES += $$PWD/bm_stringBuilder.cpp

stringBuilder benchmark results

Command line for all runs: bm_strings -iterations 500000 -median 10 -csv
Measurement is WalltimeMilliseconds

Qt 5.14.1 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 5.3.1 20160406 (Red Hat 5.3.1-6))
Test ms. per iter. ms. total
QString.arg.arg 0.000782 391
QString.arg 0.000378 189
QStringLiteral.arg 0.000334 167
QLatin1String.arg 0.000342 171
QStringView.arg 0.000328 164
QString.append 0.000478 239
QStringLiteral.append 0.000422 211
QByteArray.append 0.000628 314
QByteArrayLiteral.append 0.00058 290
QString.asprinf 0.000716 358
QStringBuilder 0.00028 140
QTextStream 0.00055 275
Qt 5.14.1 (x86_64-little_endian-llp64 shared (dynamic) release build; by MSVC 2017)
Test ms. per iter. ms. total
QString.arg.arg 0.001076 538
QString.arg 0.000568 284
QStringLiteral.arg 0.0005 250
QLatin1String.arg 0.000494 247
QStringView.arg 0.00049 245
QString.append 0.000554 277
QStringLiteral.append 0.000482 241
QByteArray.append 0.000878 439
QByteArrayLiteral.append 0.00083 415
QString.asprinf 0.00087 435
QStringBuilder 0.000396 198
QTextStream 0.000728 364
Qt 5.14.1 (x86_64-little_endian-llp64 shared (dynamic) release build; by GCC 7.3.0 (MinGW-64))
Test ms. per iter. ms. total
QString.arg.arg 0.000884 442
QString.arg 0.00051 255
QStringLiteral.arg 0.000448 224
QLatin1String.arg 0.000434 217
QStringView.arg 0.000448 224
QString.append 0.000534 267
QStringLiteral.append 0.000498 249
QByteArray.append 0.001288 644
QByteArrayLiteral.append 0.001236 618
QString.asprinf 0.000838 419
QStringBuilder 0.000368 184
QTextStream 0.00065 325
Qt 5.12.6 (i386-little_endian-ilp32 shared (dynamic) release build; by GCC 7.3.0 (MinGW-32))
Test ms. per iter. ms. total
QString.arg.arg 0.001244 622
QString.arg 0.000768 384
QStringLiteral.arg 0.000682 341
QString.append 0.000722 361
QStringLiteral.append 0.000642 321
QByteArray.append 0.001208 604
QByteArrayLiteral.append 0.001142 571
QString.asprinf 0.001132 566
QStringBuilder 0.000476 238
QTextStream 0.000914 457
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment