aboutsummaryrefslogtreecommitdiffstats
path: root/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp')
-rw-r--r--tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp204
1 files changed, 188 insertions, 16 deletions
diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
index d7c54703ad..7db01180be 100644
--- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
+++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
@@ -35,6 +35,16 @@
// This benchmark produces performance statistics
// for the standard set of elements, properties and expressions which
// are provided in the QtDeclarative library (QtQml and QtQuick).
+//
+// Note that we have hand-rolled our own benchmark harness, rather
+// than directly using QBENCHMARK, in order to avoid contaminating
+// the benchmark results with unwanted initialization or side-effects
+// and allow us to more completely isolate the required specific area
+// (compilation, instantiation, or cached type-data instantiation)
+// that we wish to benchmark.
+
+#define AVERAGE_OVER_N 10
+#define IGNORE_N_OUTLIERS 2
class ModuleApi : public QObject
{
@@ -197,6 +207,18 @@ void tst_librarymetrics_performance::metrics_data()
QTest::newRow("062) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.qml");
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile the given QML input.
+// Note that this deliberately does NOT include the time taken to
+// construct the QML engine.
+// Also note that between each iteration, we attempt to clean the
+// engine state (destroying and reconstructing the engine, clearing
+// the QML type registrations, etc) to simulate a cold-start environment.
+//
+// The benchmark result is expected to be dominated by QML parsing,
+// compilation, and optimization paths, and since the compiled component
+// is never instantiated, no construction or binding evaluation should
+// occur.
void tst_librarymetrics_performance::compilation()
{
QFETCH(QUrl, qmlfile);
@@ -211,37 +233,148 @@ void tst_librarymetrics_performance::compilation()
}
}
- QBENCHMARK {
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ }
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has NOT been cleared in between each iteration.
+// Thus, cached type data should be used when instantiating the object.
+//
+// The benchmark result is expected to be dominated by QObject
+// hierarchy construction and first-time binding evaluation.
void tst_librarymetrics_performance::instantiation_cached()
{
QFETCH(QUrl, qmlfile);
+
cleanState(&e);
+ QList<qint64> nResults;
- QBENCHMARK {
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ c.loadUrl(qmlfile);
QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
delete o;
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has been cleared in between each iteration.
+// This will cause the engine to parse, compile and optimize the
+// input QML in each iteration. After compilation, the component
+// will be instantiated, and so QObject hierarchy construction and
+// first time binding evaluation will contribute to the result.
+//
+// The compilation phase is expected to dominate the result, however
+// in some cases (complex positioning via expensive bindings, or
+// other pathological cases) instantiation may dominate. Those
+// cases are prime candidates for further investigation...
void tst_librarymetrics_performance::instantiation()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
+ }
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
}
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
void tst_librarymetrics_performance::positioners_data()
@@ -256,19 +389,58 @@ void tst_librarymetrics_performance::positioners_data()
QTest::newRow("07) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.2.qml");
}
-// this test triggers repositioning a large number of times,
+// This method triggers repositioning a large number of times,
// so we can track the cost of different repositioning methods.
+// Note that the engine state is cleared before every iteration,
+// so the benchmark result will include the cost of compilation
+// as well as instantiation and first-time binding evaluation.
+//
+// The repositioning triggered within the QML is expected
+// to dominate the benchmark time, especially if slow-path
+// binding evaluation occurs.
void tst_librarymetrics_performance::positioners()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
}
+
+ // sort the list
+ qSort(nResults);
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
QTEST_MAIN(tst_librarymetrics_performance)