1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "qmltime.h" |
30 | |
31 | #include <QQmlEngine> |
32 | #include <QDebug> |
33 | #include <QGuiApplication> |
34 | #include <QElapsedTimer> |
35 | #include <QQmlContext> |
36 | #include <QQuickItem> |
37 | |
38 | #include <private/qquickview_p.h> |
39 | |
40 | Timer *Timer::m_timer = nullptr; |
41 | |
42 | Timer::Timer() |
43 | : m_component(nullptr) |
44 | , m_willparent(false) |
45 | , m_item(new QQuickItem) |
46 | { |
47 | if (m_timer) |
48 | qWarning(msg: "Timer: Timer already registered" ); |
49 | QQuickViewPrivate::get(view: &m_view)->setRootObject(m_item); |
50 | m_timer = this; |
51 | } |
52 | |
53 | QQmlComponent *Timer::component() const |
54 | { |
55 | return m_component; |
56 | } |
57 | |
58 | void Timer::setComponent(QQmlComponent *c) |
59 | { |
60 | m_component = c; |
61 | } |
62 | |
63 | Timer *Timer::timerInstance() |
64 | { |
65 | return m_timer; |
66 | } |
67 | |
68 | void Timer::run(uint iterations) |
69 | { |
70 | QQmlContext context(qmlContext(this)); |
71 | |
72 | QObject *o = m_component->create(context: &context); |
73 | QQuickItem *i = qobject_cast<QQuickItem *>(object: o); |
74 | if (m_willparent && i) |
75 | i->setParentItem(m_item); |
76 | delete o; |
77 | |
78 | runTest(&context, iterations); |
79 | } |
80 | |
81 | bool Timer::willParent() const |
82 | { |
83 | return m_willparent; |
84 | } |
85 | |
86 | void Timer::setWillParent(bool p) |
87 | { |
88 | m_willparent = p; |
89 | } |
90 | |
91 | void Timer::runTest(QQmlContext *context, uint iterations) |
92 | { |
93 | QElapsedTimer t; |
94 | t.start(); |
95 | for (uint ii = 0; ii < iterations; ++ii) { |
96 | QObject *o = m_component->create(context); |
97 | QQuickItem *i = qobject_cast<QQuickItem *>(object: o); |
98 | if (m_willparent && i) |
99 | i->setParentItem(m_item); |
100 | delete o; |
101 | } |
102 | |
103 | int e = t.elapsed(); |
104 | |
105 | qWarning() << "Total:" << e << "ms, Per iteration:" << qreal(e) / qreal(iterations) << "ms" ; |
106 | |
107 | } |
108 | |
109 | void usage(const char *name) |
110 | { |
111 | qWarning(msg: "Usage: %s [-iterations <count>] [-parent] <qml file>\n" , name); |
112 | |
113 | qWarning(msg: "qmltime is a tool for benchmarking the runtime cost of instantiating\n" |
114 | "a QML component. It is typically run as follows:\n" |
115 | "\n" |
116 | "%s path/to/benchmark.qml\n" |
117 | "\n" |
118 | "If the -parent option is specified, the component being measured will also\n" |
119 | "be parented to an item already in the scene.\n" |
120 | "\n" |
121 | "If the -iterations option is specified, the benchmark will run the specified\n" |
122 | "number of iterations. If -iterations is not specified, 1024 iterations\n" |
123 | "are performed.\n" |
124 | "\n" |
125 | "qmltime expects the file to be benchmarked to contain a certain structure.\n" |
126 | "Specifically, it requires the presence of a QmlTime.Timer element. For example,\n" |
127 | "say we wanted to benchmark the following list delegate:\n" |
128 | "\n" |
129 | "Rectangle {\n" |
130 | " color: \"green\"\n" |
131 | " width: 400; height: 100\n" |
132 | " Text {\n" |
133 | " anchors.centerIn: parent\n" |
134 | " text: name\n" |
135 | " }\n" |
136 | "}\n" |
137 | "\n" |
138 | "we would create a benchmark file that looks like this:\n" |
139 | "\n" |
140 | "import QtQuick 2.0\n" |
141 | "import QmlTime 1.0 as QmlTime\n" |
142 | "\n" |
143 | "Item {\n" |
144 | "\n" |
145 | " property string name: \"Bob Smith\"\n" |
146 | "\n" |
147 | " QmlTime.Timer {\n" |
148 | " component: Rectangle {\n" |
149 | " color: \"green\"\n" |
150 | " width: 400; height: 100\n" |
151 | " Text {\n" |
152 | " anchors.centerIn: parent\n" |
153 | " text: name\n" |
154 | " }\n" |
155 | " }\n" |
156 | " }\n" |
157 | "}\n" |
158 | "\n" |
159 | "The outer Item functions as a dummy data provider for any additional\n" |
160 | "data required by the bindings in the component being benchmarked (in the\n" |
161 | "example above we provide a \"name\" property).\n" |
162 | "\n" |
163 | "When started, the component is instantiated once before running\n" |
164 | "the benchmark, which means that the reported time does not include\n" |
165 | "compile time (as the results of compilation are cached internally).\n" |
166 | "In this sense the times reported by qmltime best correspond to the\n" |
167 | "costs associated with delegate creation in the view classes, where the\n" |
168 | "same delegate is instantiated over and over. Conversely, it is not a\n" |
169 | "good approximation for e.g. Loader, which typically only instantiates\n" |
170 | "an element once (and so for Loader the compile time is very relevant\n" |
171 | "to the overall cost)." , name); |
172 | |
173 | exit(status: -1); |
174 | } |
175 | |
176 | int main(int argc, char ** argv) |
177 | { |
178 | QGuiApplication app(argc, argv); |
179 | QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); |
180 | |
181 | uint iterations = 1024; |
182 | QString filename; |
183 | bool willParent = false; |
184 | |
185 | for (int ii = 1; ii < argc; ++ii) { |
186 | QByteArray arg(argv[ii]); |
187 | |
188 | if (arg == "-iterations" ) { |
189 | if (ii + 1 < argc) { |
190 | ++ii; |
191 | QByteArray its(argv[ii]); |
192 | bool ok = false; |
193 | iterations = its.toUInt(ok: &ok); |
194 | if (!ok || iterations == 0) |
195 | usage(name: argv[0]); |
196 | } else { |
197 | usage(name: argv[0]); |
198 | } |
199 | } else if (arg == "-parent" ) { |
200 | willParent = true; |
201 | } else if (arg == "-help" ) { |
202 | usage(name: argv[0]); |
203 | } else { |
204 | filename = QLatin1String(argv[ii]); |
205 | } |
206 | } |
207 | |
208 | if (filename.isEmpty()) |
209 | usage(name: argv[0]); |
210 | |
211 | QQmlEngine engine; |
212 | QQmlComponent component(&engine, filename); |
213 | if (component.isError()) { |
214 | qWarning() << component.errors(); |
215 | return -1; |
216 | } |
217 | |
218 | QObject *obj = component.create(); |
219 | if (!obj) { |
220 | qWarning() << component.errors(); |
221 | return -1; |
222 | } |
223 | |
224 | Timer *timer = Timer::timerInstance(); |
225 | if (!timer) { |
226 | qWarning() << "A QmlTime.Timer instance is required." ; |
227 | return -1; |
228 | } |
229 | |
230 | timer->setWillParent(willParent); |
231 | |
232 | if (!timer->component()) { |
233 | qWarning() << "The timer has no component" ; |
234 | return -1; |
235 | } |
236 | |
237 | timer->run(iterations); |
238 | |
239 | return 0; |
240 | } |
241 | |