Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
performance.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\page qtquick-performance.html
6
\meta {keywords} {qmltopic}
7
\title Performance considerations and suggestions
8
\keyword QML Performance Considerations And Suggestions
9
\brief Discussion of performance-related trade-offs and best-practices
10
11
\section1 Timing Considerations
12
13
As an application developer, you typically strive to allow the rendering
14
engine to achieve a consistent 60 frames-per-second refresh rate. Depending on
15
your hardware and requirements the number may be different, but 60 FPS is very
16
common. 60 FPS means that there is approximately 16 milliseconds between each
17
frame in which processing can be done, which includes the processing required
18
to upload the draw primitives to the graphics hardware.
19
20
In practice, this means that the application developer should:
21
\list
22
\li use asynchronous, event-driven programming wherever possible
23
\li use worker threads to do significant processing
24
\li never manually spin the event loop
25
\li never spend more than a couple of milliseconds per frame within blocking functions
26
\endlist
27
28
Failure to do so will result in skipped frames, which has a drastic effect on the
29
user experience.
30
31
\note A pattern which is tempting, but should \e never be used, is creating your
32
own QEventLoop or calling QCoreApplication::processEvents() in order to avoid
33
blocking within a backend code block, such as C++, invoked from QML. This is
34
dangerous, because when an event loop is entered in a signal handler or
35
binding, the QML engine continues to run other bindings, animations,
36
transitions, etc. Those bindings can then cause side effects which, for
37
example, destroy the hierarchy containing your event loop.
38
39
\section1 Profiling
40
41
42
The most important tip is: use \l{QML Profiler} included in \QC
43
(or a trace viewer in \QVSC).
44
Knowing where time is spent in an application will allow you to focus on
45
problem areas which actually exist, rather than problem areas which potentially
46
exist. See \l{\QC: Profiling QML Applications} and \l{\QVSC: Profile QML code}
47
for more information.
48
49
Determining which bindings are being run the most often, or which functions your
50
application is spending the most time in, will allow you to decide whether you need
51
to optimize the problem areas, or redesign some implementation details of your
52
application so that the performance is improved. Attempting to optimize code without
53
profiling is likely to result in very minor rather than significant performance
54
improvements.
55
56
\section1 JavaScript Code
57
58
Most QML applications will have some JavaScript code in them, in the form of
59
property binding expressions, functions, and signal handlers. This is generally
60
not a problem. Thanks to advanced tooling such as the \l{Qt Quick Compiler},
61
simple functions and bindings can be very fast. However, care must be taken to
62
ensure that unnecessary processing isn't triggered accidentally. The
63
\l{QML Profiler} can show copious detail about JavaScript execution and what
64
triggered it.
65
66
\section2 Type-Conversion
67
68
One major cost of using JavaScript is that in some cases when a property from a QML
69
type is accessed, a JavaScript object with an external resource containing the
70
underlying C++ data (or a reference to it) is created. In most cases, this is fairly
71
inexpensive, but in others it can be quite expensive. Care is to be taken when
72
handling large and complicated \l{QML Value Types}{value types} or
73
\l{QML Sequence Types}{sequence types}. These have to be copied by the QML engine
74
whenever you change them in place or assign them to a different property. When this
75
becomes a bottleneck, consider using \l{QML Object Types}{object types} instead.
76
Lists of object types do not have the same problem as lists of value types because
77
lists of object types are implemented using \l{QQmlListProperty}.
78
79
Most conversions between simple value types are cheap. There are exceptions,
80
though. Creating a \e url from a \e string can involve constructing a \l{QUrl}
81
instance, which is costly.
82
83
\section2 Resolving Properties
84
85
Property resolution takes time. While lookups are typically optimized to run
86
much faster on subsequent executions, it is always best to avoid doing
87
unnecessary work altogether, if possible.
88
89
In the following example, we have a block of code which is run often (in this
90
case, it is the contents of an explicit loop; but it could be a
91
commonly-evaluated binding expression, for example) and in it, we resolve the
92
object with the "rect" id and its "color" property multiple times:
93
94
\qml
95
// bad.qml
96
import QtQuick
97
98
Item {
99
width: 400
100
height: 200
101
Rectangle {
102
id: rect
103
anchors.fill: parent
104
color: "blue"
105
}
106
107
function printValue(which: string, value: real) {
108
console.log(which + " = " + value);
109
}
110
111
Component.onCompleted: {
112
var t0 = new Date();
113
for (var i = 0; i < 1000; ++i) {
114
printValue("red", rect.color.r);
115
printValue("green", rect.color.g);
116
printValue("blue", rect.color.b);
117
printValue("alpha", rect.color.a);
118
}
119
var t1 = new Date();
120
console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");
121
}
122
}
123
\endqml
124
125
Every time \c{rect.color} is retrieved, the QML engine has to:
126
\list
127
\li Allocate a value type wrapper on the JavaScript heap.
128
\li Run the getter of \l{Rectangle}'s \c color property.
129
\li Copy the resulting \l{QColor} into the value type wrapper.
130
\endlist
131
132
We don't have to do this 4 times. We can instead resolve the common base
133
just once in the block:
134
135
\qml
136
// good.qml
137
import QtQuick
138
139
Item {
140
width: 400
141
height: 200
142
Rectangle {
143
id: rect
144
anchors.fill: parent
145
color: "blue"
146
}
147
148
function printValue(which: string, value: real) {
149
console.log(which + " = " + value);
150
}
151
152
Component.onCompleted: {
153
var t0 = new Date();
154
for (var i = 0; i < 1000; ++i) {
155
var rectColor = rect.color; // resolve the common base.
156
printValue("red", rectColor.r);
157
printValue("green", rectColor.g);
158
printValue("blue", rectColor.b);
159
printValue("alpha", rectColor.a);
160
}
161
var t1 = new Date();
162
console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");
163
}
164
}
165
\endqml
166
167
Just this simple change results in a significant performance improvement.
168
Note that the code above can be improved even further (since the property
169
being looked up never changes during the loop processing), by hoisting the
170
property resolution out of the loop, as follows:
171
172
\qml
173
// better.qml
174
import QtQuick
175
176
Item {
177
width: 400
178
height: 200
179
Rectangle {
180
id: rect
181
anchors.fill: parent
182
color: "blue"
183
}
184
185
function printValue(which: string, value: real) {
186
console.log(which + " = " + value);
187
}
188
189
Component.onCompleted: {
190
var t0 = new Date();
191
var rectColor = rect.color; // resolve the common base outside the tight loop.
192
for (var i = 0; i < 1000; ++i) {
193
printValue("red", rectColor.r);
194
printValue("green", rectColor.g);
195
printValue("blue", rectColor.b);
196
printValue("alpha", rectColor.a);
197
}
198
var t1 = new Date();
199
console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");
200
}
201
}
202
\endqml
203
204
\section2 Property Bindings
205
206
A property binding expression will be re-evaluated if any of the properties
207
it references are changed. As such, binding expressions should be kept as
208
simple as possible.
209
210
If you have a loop where you do some processing, but only the final result
211
of the processing is important, it is often better to update a temporary
212
accumulator which you afterwards assign to the property you need to update,
213
rather than incrementally updating the property itself, in order to avoid
214
triggering re-evaluation of binding expressions during the intermediate
215
stages of accumulation.
216
217
The following contrived example illustrates this point:
218
219
\qml
220
// bad.qml
221
import QtQuick
222
223
Item {
224
id: root
225
width: 200
226
height: 200
227
property int accumulatedValue: 0
228
229
Text {
230
anchors.fill: parent
231
text: root.accumulatedValue.toString()
232
onTextChanged: console.log("text binding re-evaluated")
233
}
234
235
Component.onCompleted: {
236
var someData = [ 1, 2, 3, 4, 5, 20 ];
237
for (var i = 0; i < someData.length; ++i) {
238
accumulatedValue = accumulatedValue + someData[i];
239
}
240
}
241
}
242
\endqml
243
244
The loop in the onCompleted handler causes the "text" property binding to
245
be re-evaluated six times (which then results in any other property bindings
246
which rely on the text value, as well as the onTextChanged signal handler,
247
to be re-evaluated each time, and lays out the text for display each time).
248
This is clearly unnecessary in this case, since we really only care about
249
the final value of the accumulation.
250
251
It could be rewritten as follows:
252
253
\qml
254
// good.qml
255
import QtQuick
256
257
Item {
258
id: root
259
width: 200
260
height: 200
261
property int accumulatedValue: 0
262
263
Text {
264
anchors.fill: parent
265
text: root.accumulatedValue.toString()
266
onTextChanged: console.log("text binding re-evaluated")
267
}
268
269
Component.onCompleted: {
270
var someData = [ 1, 2, 3, 4, 5, 20 ];
271
var temp = accumulatedValue;
272
for (var i = 0; i < someData.length; ++i) {
273
temp = temp + someData[i];
274
}
275
accumulatedValue = temp;
276
}
277
}
278
\endqml
279
280
\section2 Sequence tips
281
282
As mentioned earlier, \l{QML Sequence Types}{sequences of value types} have to
283
be handled with care.
284
285
Firstly, sequence types show different behavior in two distinct scenarios:
286
\list
287
\li if the sequence is a Q_PROPERTY of a QObject (we'll call this a reference
288
sequence),
289
\li if the sequence is returned from a Q_INVOKABLE function of a QObject (we'll
290
call this a copy sequence).
291
\endlist
292
293
A reference sequence is read and written via the \l{QMetaObject} whenever it
294
changes, either in your JavaScript code, or on the original object. As an
295
optimization, reference sequences (as well as reference
296
\l{QML Value Types}{value types}) may be loaded lazily. The actual content is
297
then only retrieved when they are first used. This means that changing the
298
value of any element in the sequence from JavaScript will result in:
299
\list
300
\li Possibly reading the content from the QObject (if lazy-loaded).
301
\li Changing the element at the specified index in that sequence.
302
\li Writing the whole sequence back to the QObject.
303
\endlist
304
305
A copy sequence is far simpler as the actual sequence is stored in the JavaScript
306
object's resource data, so no read/modify/write cycle occurs (instead, the resource
307
data is modified directly).
308
309
Therefore, writes to elements of a reference sequence will be much slower than writes
310
to elements of a copy sequence. In fact, writing to a single element of an N-element
311
reference sequence is equivalent in cost to assigning a N-element copy sequence to that
312
reference sequence, so you're usually better off modifying a temporary copy sequence
313
and then assigning the result to a reference sequence, during computation.
314
315
Assume the existence (and prior registration into the "Qt.example" namespace) of the
316
following C++ type:
317
318
\code
319
class SequenceTypeExample : public QQuickItem
320
{
321
Q_OBJECT
322
Q_PROPERTY (QList<qreal> qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged)
323
324
public:
325
SequenceTypeExample() : QQuickItem() { m_list << 1.1 << 2.2 << 3.3; }
326
~SequenceTypeExample() {}
327
328
QList<qreal> qrealListProperty() const { return m_list; }
329
void setQrealListProperty(const QList<qreal> &list) { m_list = list; emit qrealListPropertyChanged(); }
330
331
signals:
332
void qrealListPropertyChanged();
333
334
private:
335
QList<qreal> m_list;
336
};
337
\endcode
338
339
The following example writes to elements of a reference sequence in a
340
tight loop, resulting in bad performance:
341
342
\qml
343
// bad.qml
344
import QtQuick
345
import Qt.example
346
347
SequenceTypeExample {
348
id: root
349
width: 200
350
height: 200
351
352
Component.onCompleted: {
353
var t0 = new Date();
354
qrealListProperty.length = 100;
355
for (var i = 0; i < 500; ++i) {
356
for (var j = 0; j < 100; ++j) {
357
qrealListProperty[j] = j;
358
}
359
}
360
var t1 = new Date();
361
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
362
}
363
}
364
\endqml
365
366
The QObject property read and write in the inner loop caused by the
367
\c{"qrealListProperty[j] = j"} expression makes this code very suboptimal. Instead,
368
something functionally equivalent but much faster would be:
369
370
\qml
371
// good.qml
372
import QtQuick
373
import Qt.example
374
375
SequenceTypeExample {
376
id: root
377
width: 200
378
height: 200
379
380
Component.onCompleted: {
381
var t0 = new Date();
382
var someData = [1.1, 2.2, 3.3]
383
someData.length = 100;
384
for (var i = 0; i < 500; ++i) {
385
for (var j = 0; j < 100; ++j) {
386
someData[j] = j;
387
}
388
qrealListProperty = someData;
389
}
390
var t1 = new Date();
391
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
392
}
393
}
394
\endqml
395
396
Another common pattern that should be avoided is read-modify-write loops where each
397
element is read, modified, and written back to the sequence property. Similar to the
398
previous example, this causes QObject property reads and writes in every iteration:
399
400
\qml
401
// bad.qml
402
import QtQuick
403
import Qt.example
404
405
SequenceTypeExample {
406
id: root
407
width: 200
408
height: 200
409
410
Component.onCompleted: {
411
var t0 = new Date();
412
qrealListProperty.length = 100;
413
for (var i = 0; i < 500; ++i) {
414
for (var j = 0; j < 100; ++j) {
415
qrealListProperty[j] = qrealListProperty[j] * 2;
416
}
417
}
418
var t1 = new Date();
419
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
420
}
421
}
422
\endqml
423
424
Instead, create a manual copy of the sequence, modify the copy, and then assign
425
the result back to the property:
426
427
\qml
428
// good.qml
429
import QtQuick
430
import Qt.example
431
432
SequenceTypeExample {
433
id: root
434
width: 200
435
height: 200
436
437
Component.onCompleted: {
438
var t0 = new Date();
439
for (var i = 0; i < 500; ++i) {
440
let data = [...qrealListProperty];
441
for (var j = 0; j < 100; ++j) {
442
data[j] = data[j] * 2;
443
}
444
qrealListProperty = data;
445
}
446
var t1 = new Date();
447
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
448
}
449
}
450
\endqml
451
452
Secondly, a change signal for the property is emitted if any element in it changes.
453
If you have many bindings to a particular element in a sequence property, it is better
454
to create a dynamic property which is bound to that element, and use that dynamic
455
property as the symbol in the binding expressions instead of the sequence element,
456
as it will only cause re-evaluation of bindings if its value changes.
457
458
This is an unusual use-case which most clients should never hit, but is worth being
459
aware of, in case you find yourself doing something like this:
460
461
\qml
462
// bad.qml
463
import QtQuick
464
import Qt.example
465
466
SequenceTypeExample {
467
id: root
468
469
property int firstBinding: qrealListProperty[1] + 10;
470
property int secondBinding: qrealListProperty[1] + 20;
471
property int thirdBinding: qrealListProperty[1] + 30;
472
473
Component.onCompleted: {
474
var t0 = new Date();
475
for (var i = 0; i < 1000; ++i) {
476
qrealListProperty[2] = i;
477
}
478
var t1 = new Date();
479
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
480
}
481
}
482
\endqml
483
484
Note that even though only the element at index 2 is modified in the loop, the three
485
bindings will all be re-evaluated since the granularity of the change signal is that
486
the entire property has changed. As such, adding an intermediate binding can
487
sometimes be beneficial:
488
489
\qml
490
// good.qml
491
import QtQuick
492
import Qt.example
493
494
SequenceTypeExample {
495
id: root
496
497
property int intermediateBinding: qrealListProperty[1]
498
property int firstBinding: intermediateBinding + 10;
499
property int secondBinding: intermediateBinding + 20;
500
property int thirdBinding: intermediateBinding + 30;
501
502
Component.onCompleted: {
503
var t0 = new Date();
504
for (var i = 0; i < 1000; ++i) {
505
qrealListProperty[2] = i;
506
}
507
var t1 = new Date();
508
console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds");
509
}
510
}
511
\endqml
512
513
In the above example, only the intermediate binding will be re-evaluated each time,
514
resulting in a significant performance increase.
515
516
\section2 Value-Type tips
517
518
\l{QML Value Types}{Value type} properties (font, color, vector3d, etc) have
519
similar QObject property and change notification semantics to sequence type
520
properties. As such, the tips given above for sequences are also applicable for
521
value type properties. While they are usually less of a problem with value
522
types (since the number of sub-properties of a value type is usually far less
523
than the number of elements in a sequence), any increase in the number of
524
bindings being re-evaluated needlessly will have a negative impact on
525
performance.
526
527
\section2 General Performance Tips
528
529
General JavaScript performance considerations resulting from the language
530
design are applicable also to QML. Most prominently:
531
532
\list
533
\li Avoid using eval() if at all possible
534
\li Do not delete properties of objects
535
\endlist
536
537
\section1 Common Interface Elements
538
539
\section2 Text Elements
540
541
Calculating text layouts can be a slow operation. Consider using the \c PlainText
542
format instead of \c StyledText wherever possible, as this reduces the amount of work
543
required of the layout engine. If you cannot use \c PlainText (as you need to embed
544
images, or use tags to specify ranges of characters to have certain formatting (bold,
545
italic, etc) as opposed to the entire text) then you should use \c StyledText.
546
547
You should only use \c AutoText if the text might be (but probably isn't)
548
\c StyledText as this mode will incur a parsing cost. The \c RichText mode should
549
not be used, as \c StyledText provides almost all of its features at a fraction of
550
its cost.
551
552
\section2 Images
553
554
Images are a vital part of any user interface. Unfortunately, they are also a big
555
source of problems due to the time it takes to load them, the amount of memory they
556
consume, and the way in which they are used.
557
558
\section3 Asynchronous Loading
559
560
Images are often quite large, and so it is wise to ensure that loading an image doesn't
561
block the UI thread. Set the "asynchronous" property of the QML Image element to
562
\c true to enable asynchronous loading of images from the local file system (remote
563
images are always loaded asynchronously) where this would not result in a negative impact
564
upon the aesthetics of the user interface.
565
566
Image elements with the "asynchronous" property set to \c true will load images in
567
a low-priority worker thread.
568
569
\section3 Explicit Source Size
570
571
If your application loads a large image but displays it in a small-sized element, set
572
the "sourceSize" property to the size of the element being rendered to ensure that the
573
smaller-scaled version of the image is kept in memory, rather than the large one.
574
575
Beware that changing the sourceSize will cause the image to be reloaded.
576
577
\section3 Avoid Run-time Composition
578
579
Also remember that you can avoid doing composition work at run-time by providing the
580
pre-composed image resource with your application (for example, providing elements with shadow
581
effects).
582
583
\section3 Avoid Smoothing Images
584
585
Enable \c{image.smooth} only if required. It is slower on some hardware, and it has no visual
586
effect if the image is displayed in its natural size.
587
588
\section3 Painting
589
590
Avoid painting the same area several times. Use Item as root element rather than Rectangle
591
to avoid painting the background several times.
592
593
\section2 Position Elements With Anchors
594
595
It is more efficient to use anchors rather than bindings to position items
596
relative to each other. Consider this use of bindings to position rect2
597
relative to rect1:
598
599
\code
600
Rectangle {
601
id: rect1
602
x: 20
603
width: 200; height: 200
604
}
605
Rectangle {
606
id: rect2
607
x: rect1.x
608
y: rect1.y + rect1.height
609
width: rect1.width - 20
610
height: 200
611
}
612
\endcode
613
614
This is achieved more efficiently using anchors:
615
616
\code
617
Rectangle {
618
id: rect1
619
x: 20
620
width: 200; height: 200
621
}
622
Rectangle {
623
id: rect2
624
height: 200
625
anchors.left: rect1.left
626
anchors.top: rect1.bottom
627
anchors.right: rect1.right
628
anchors.rightMargin: 20
629
}
630
\endcode
631
632
Positioning with bindings (by assigning binding expressions to the x, y, width
633
and height properties of visual objects, rather than using anchors) is
634
relatively slow, although it allows maximum flexibility.
635
636
If the layout is not dynamic, the most performant way to specify the layout is
637
via static initialization of the x, y, width and height properties. Item
638
coordinates are always relative to their parent, so if you wanted to be a fixed
639
offset from your parent's 0,0 coordinate you should not use anchors. In the
640
following example the child Rectangle objects are in the same place, but the
641
anchors code shown is not as resource efficient as the code which
642
uses fixed positioning via static initialization:
643
644
\code
645
Rectangle {
646
width: 60
647
height: 60
648
Rectangle {
649
id: fixedPositioning
650
x: 20
651
y: 20
652
width: 20
653
height: 20
654
}
655
Rectangle {
656
id: anchorPositioning
657
anchors.fill: parent
658
anchors.margins: 20
659
}
660
}
661
\endcode
662
663
\section1 Models and Views
664
665
Most applications will have at least one model feeding data to a view. There are
666
some semantics which application developers need to be aware of, in order to achieve
667
maximal performance.
668
669
\section2 Custom C++ Models
670
671
It is often desirable to write your own custom model in a backend language, such
672
as C++, for use with a view in QML. While the optimal implementation of any
673
such model will depend heavily on the use-case it must fulfil, some general
674
guidelines are as follows:
675
676
\list
677
\li Be as asynchronous as possible
678
\li Do all processing in a (low priority) worker thread
679
\li Batch up backend operations so that (potentially slow) I/O and IPC is minimized
680
\endlist
681
682
It is important to note that using a low-priority worker thread is recommended to
683
minimize the risk of starving the GUI thread (which could result in worse perceived
684
performance). Also, remember that synchronization and locking mechanisms can be a
685
significant cause of slow performance, and so care should be taken to avoid
686
unnecessary locking.
687
688
\section2 ListModel QML Type
689
690
\l{Qt Qml Models} provides a \l{ListModel} type which can be used to feed data
691
to a \l{ListView}. It is useful for quick prototyping, but not suitable for
692
larger amounts of data. Use a proper \l{QAbstractItemModel} where necessary.
693
694
\section3 Populate Within A Worker Thread
695
696
\l{ListModel} elements can be populated in a (low priority) worker thread in
697
JavaScript. The developer must explicitly call \c{sync()} on the \l{ListModel}
698
from within the \l{WorkerScript} to have the changes synchronized to the main
699
thread. See the \l{WorkerScript} documentation for more information.
700
701
Please note that using a \l{WorkerScript} element will result in a separate
702
JavaScript engine being created (as the JavaScript engine is per-thread). This
703
will result in increased memory usage. Multiple \l{WorkerScript} elements will
704
all use the same worker thread, however, so the memory impact of using a second
705
or third \l{WorkerScript} element is negligible once an application already
706
uses one. On the flip side, however, the additional worker scripts do not run
707
in parallel.
708
709
\section3 Don't Use Dynamic Roles
710
711
The \l{ListModel} element assumes the types of roles within each element in a
712
given model are stable for optimization purposes. If the type can change
713
dynamically from element to element, the performance of the model will be much
714
worse.
715
716
Therefore, dynamic typing is disabled by default; the developer must
717
specifically set the boolean \c{dynamicRoles} property of the model to enable
718
dynamic typing (and suffer the attendant performance degradation). We recommend
719
that you do not use dynamic typing unless absolutely necessary.
720
721
\section2 Views
722
723
View delegates should be kept as simple as possible. Have just enough QML in
724
the delegate to display the necessary information. Any additional functionality
725
which is not immediately required (for example, if it displays more information
726
when clicked) should not be created until needed (see the upcoming section on
727
lazy initialization).
728
729
The following list is a good summary of things to keep in mind when designing a
730
delegate:
731
\list
732
\li The fewer elements that are in a delegate, the faster they can be created,
733
and thus the faster the view can be scrolled.
734
\li Keep the number of bindings in a delegate to a minimum; in particular, use
735
anchors rather than bindings for relative positioning within a delegate.
736
\li Avoid using \l{ShaderEffect} elements within delegates.
737
\li Never enable clipping on a delegate.
738
\endlist
739
740
You may set the \c cacheBuffer property of a view to allow asynchronous
741
creation and buffering of delegates outside of the visible area. Utilizing a
742
\c cacheBuffer is recommended for view delegates that are non-trivial and
743
unlikely to be created within a single frame.
744
745
Bear in mind that a \c cacheBuffer keeps additional delegates in-memory.
746
Therefore, the value derived from utilizing the \c cacheBuffer must be balanced
747
against additional memory usage. Developers should use benchmarking to find the
748
best value for their use-case, since the increased memory pressure caused by
749
utilizing a \c cacheBuffer can, in some rare cases, cause reduced frame rate
750
when scrolling.
751
752
For additional performance improvements, consider enabling item reuse in views.
753
See \l{ListView#Reusing Items}{Reusing Items for ListView} and
754
\l{TableView#Reusing items}{Reusing Items for TableView and TreeView} for more
755
information.
756
757
\section1 Visual Effects
758
759
\l{Qt Quick} includes several features which allow developers and designers to
760
create exceptionally appealing user interfaces. Fluidity and dynamic transitions
761
as well as visual effects can be used to great effect in an application, but
762
some care must be taken when using some of the features in QML as they can have
763
performance implications.
764
765
\section2 Animations
766
767
In general, animating a property will cause any bindings which reference that property
768
to be re-evaluated. Usually, this is what is desired but in other cases it may be better
769
to disable the binding prior to performing the animation, and then reassign the binding
770
once the animation has completed.
771
772
Avoid running JavaScript during animation. For example, running a complex JavaScript
773
expression for each frame of an x property animation should be avoided.
774
775
Developers should be especially careful using script animations, as these are run in the main
776
thread (and therefore can cause frames to be skipped if they take too long to complete).
777
778
\section2 Particles
779
780
The \l{QtQuick.Particles}{Qt Quick Particles} module allows beautiful particle effects to be integrated
781
seamlessly into user interfaces. However, every platform has different graphics hardware
782
capabilities, and the Particles module is unable to limit parameters to what your hardware
783
can gracefully support. The more particles you attempt to render (and the larger they are),
784
the faster your graphics hardware will need to be in order to render at 60 FPS. Affecting
785
more particles requires a faster CPU. It is therefore important to test all
786
particle effects on your target platform carefully, to calibrate the number and size of
787
particles you can render at 60 FPS.
788
789
It should be noted that a particle system can be disabled when not in use
790
(for example, on a non-visible element) to avoid doing unnecessary simulation.
791
792
See the \l{Particle System Performance Guide} for more in-depth information.
793
794
\section1 Controlling Element Lifetime
795
796
By partitioning an application into simple, modular components, each contained in a single
797
QML file, you can achieve faster application startup time and better control over memory
798
usage, and reduce the number of active-but-invisible elements in your application.
799
800
\section2 Lazy Initialization
801
802
The QML engine does some tricky things to try to ensure that loading and initialization of
803
components doesn't cause frames to be skipped. However, there is no better way to reduce
804
startup time than to avoid doing work you don't need to do, and delaying the work until
805
it is necessary. This may be achieved by using either \l{Loader}.
806
807
\section3 Using Loader
808
809
The Loader is an element which allows dynamic loading and unloading of components.
810
811
\list
812
\li Using the "active" property of a Loader, initialization can be delayed until required.
813
\li Using the overloaded version of the "setSource()" function, initial property values can
814
be supplied.
815
\li Setting the Loader \l {Loader::asynchronous}{asynchronous} property to true may also
816
improve fluidity while a component is instantiated.
817
\endlist
818
819
\section2 Destroy Unused Elements
820
821
Elements which are invisible because they are a child of a non-visible element (for example, the
822
second tab in a tab-widget, while the first tab is shown) should be initialized lazily in
823
most cases, and deleted when no longer in use, to avoid the ongoing cost of leaving them
824
active (for example, rendering, animations, property binding evaluation, etc).
825
826
An item loaded with a Loader element may be released by resetting the "source" or
827
"sourceComponent" property of the Loader, while other items may be explicitly
828
released by calling destroy() on them. In some cases, it may be necessary to
829
leave the item active, in which case it should be made invisible at the very least.
830
831
See the upcoming section on Rendering for more information on active but invisible elements.
832
833
\section1 Rendering
834
835
The scene graph used for rendering in \l{Qt Quick} allows highly dynamic, animated user
836
interfaces to be rendered fluidly at 60 FPS. There are some things which can
837
dramatically decrease rendering performance, however, and developers should be careful
838
to avoid these pitfalls wherever possible.
839
840
\target clipping-performance
841
\section2 Clipping
842
843
Clipping is disabled by default, and should only be enabled when required.
844
845
Clipping is a visual effect, NOT an optimization. It increases (rather than reduces)
846
complexity for the renderer. If clipping is enabled, an item will clip its own painting,
847
as well as the painting of its children, to its bounding rectangle. This stops the renderer
848
from being able to reorder the drawing order of elements freely, resulting in a sub-optimal
849
best-case scene graph traversal.
850
851
Clipping inside a delegate is especially bad and should be avoided at all costs.
852
853
\section2 Over-drawing and Invisible Elements
854
855
If you have elements which are totally covered by other (opaque) elements, it is best to
856
set their "visible" property to \c false or they will be drawn needlessly.
857
858
Similarly, elements which are invisible (for example, the second tab in a tab widget, while the
859
first tab is shown) but need to be initialized at startup time (for example, if the cost of
860
instantiating the second tab takes too long to be able to do it only when the tab is
861
activated), should have their "visible" property set to \c false, in order to avoid the
862
cost of drawing them (although as previously explained, they will still incur the cost of
863
any animations or bindings evaluation since they are still active).
864
865
\section2 Translucent vs Opaque
866
867
Opaque content is generally a lot faster to draw than translucent. The reason being
868
that translucent content needs blending and that the renderer can potentially optimize
869
opaque content better.
870
871
An image with one translucent pixel is treated as fully translucent, even though it
872
is mostly opaque. The same is true for an \l BorderImage with transparent edges.
873
874
\section2 Shaders
875
876
The \l ShaderEffect type makes it possible to place GLSL code inline in a Qt Quick application with
877
very little overhead. However, it is important to realize that the fragment program needs to run
878
for every pixel in the rendered shape. When deploying to low-end hardware and the shader
879
is covering a large amount of pixels, one should keep the fragment shader to a few instructions
880
to avoid poor performance.
881
882
Shaders written in GLSL allow for complex transformations and visual effects to be written,
883
however they should be used with care. Using a \l ShaderEffectSource causes a scene to be
884
prerendered into an FBO before it can be drawn. This extra overhead can be quite expensive.
885
886
\section1 Memory Allocation And Collection
887
888
The amount of memory which will be allocated by an application and the way in which that
889
memory will be allocated are very important considerations. Aside from the obvious
890
concerns about out-of-memory conditions on memory-constrained devices, allocating memory
891
on the heap is a fairly computationally expensive operation, and certain allocation
892
strategies can result in increased fragmentation of data across pages. JavaScript uses
893
a managed memory heap which is automatically garbage collected, and this has some
894
advantages, but also some important implications.
895
896
An application written in QML uses memory from both the C++ heap and an automatically
897
managed JavaScript heap. The application developer needs to be aware of the subtleties
898
of each in order to maximise performance.
899
900
\section2 Tips For QML Application Developers
901
902
The tips and suggestions contained in this section are guidelines only, and may not be
903
applicable in all circumstances. Be sure to benchmark and analyze your application
904
carefully using empirical metrics, in order to make the best decisions possible.
905
906
\section3 Instantiate and initialize components lazily
907
908
If your application consists of multiple views (for example, multiple tabs) but only
909
one is required at any one time, you can use lazy instantiation to minimize the
910
amount of memory you need to have allocated at any given time. See the prior section
911
on \l{Lazy Initialization} for more information.
912
913
\section3 Destroy unused objects
914
915
If you lazy load components, or create objects dynamically during a JavaScript
916
expression, it is often better to \c{destroy()} them manually rather than wait for
917
automatic garbage collection to do so. See the prior section on
918
\l{Controlling Element Lifetime} for more information.
919
920
\section3 Don't manually invoke the garbage collector
921
922
In most cases, it is not wise to manually invoke the garbage collector, as it will block
923
the GUI thread for a substantial period of time. This can result in skipped frames and
924
jerky animations, which should be avoided at all costs.
925
926
There are some cases where manually invoking the garbage collector is acceptable (and
927
this is explained in greater detail in an upcoming section), but in most cases, invoking
928
the garbage collector is unnecessary and counter-productive.
929
930
\section3 Avoid defining multiple identical implicit types
931
932
If a QML element has a custom property defined in QML, it becomes its own implicit type.
933
This is explained in greater detail in an upcoming section. If multiple identical
934
implicit types are defined in a \l{Component}, some memory will be wasted. In that
935
situation it is usually better to explicitly define a new component which can then be
936
reused. Consider defining an inline component using the \c{component} keyword in such
937
a case.
938
939
Defining a custom property can often be a beneficial performance optimization (for
940
example, to reduce the number of bindings which are required or re-evaluated), or it
941
can improve the modularity and maintainability of a component. In those cases, using
942
custom properties is encouraged. However, the new type should, if it is used more than
943
once, be split into its own component (inline or .qml file) in order to conserve memory.
944
945
\section3 Reuse existing components
946
947
If you are considering defining a new component, it's worth double checking that such a
948
component doesn't already exist in the component set for your platform. Otherwise, you
949
will be forcing the QML engine to generate and store type-data for a type which is
950
essentially a duplicate of another pre-existing and potentially already loaded component.
951
952
\section3 Use singleton types instead of pragma library scripts
953
954
If you are using a pragma library script to store application-wide instance data,
955
consider using a QObject singleton type instead. This should result in better performance,
956
and will result in less JavaScript heap memory being used.
957
958
\section2 Memory Allocation in a QML Application
959
960
The memory usage of a QML application may be split into two parts: its native
961
heap usage and its JavaScript heap usage. Some of the memory allocated in
962
each will be unavoidable,
963
as it is allocated by the QML engine or the JavaScript engine, while the rest is
964
dependent upon decisions made by the application developer.
965
966
The native heap will contain:
967
\list
968
\li the fixed and unavoidable overhead of the QML engine (implementation data
969
structures, context information, and so on);
970
\li per-component compiled data and type information, including per-type property
971
metadata, which is generated or loaded from the \l{The QML Disk Cache}{disk cache}
972
by the QML engine depending on which modules and which components are loaded by the
973
application;
974
\li per-object C++ data (including property values) plus a per-element metaobject
975
hierarchy, depending on which components the application instantiates;
976
\li any data which is allocated specifically by QML imports (libraries).
977
\endlist
978
979
The JavaScript heap will contain:
980
\list
981
\li the fixed and unavoidable overhead of the JavaScript engine itself (including
982
built-in JavaScript types);
983
\li the fixed and unavoidable overhead of our JavaScript integration (constructor
984
functions for loaded types, function templates, and so on);
985
\li per-type layout information and other internal type-data generated by the JavaScript
986
engine at runtime, for each type (see note below, regarding types);
987
\li per-object JavaScript data ("var" properties, JavaScript functions and signal
988
handlers, and non-optimized binding expressions);
989
\li variables allocated during expression evaluation.
990
\endlist
991
992
Furthermore, there will be one JavaScript heap allocated for use in the main thread, and
993
optionally one other JavaScript heap allocated for use in the WorkerScript thread. If an
994
application does not use a WorkerScript element, that overhead will not be incurred. The
995
JavaScript heap can be several megabytes in size, and so applications written for
996
memory-constrained devices may be best served by avoiding the WorkerScript element.
997
998
Note that both the QML engine and the JavaScript engine will automatically generate their
999
own caches of type-data about observed types. Every component loaded by an application
1000
is a distinct (explicit) type, and every element (component instance) that defines its
1001
own custom properties in QML is an implicit type. Any element (instance of a component)
1002
that does not define any custom property is considered by the JavaScript and QML engines
1003
to be of the type explicitly defined by the component, rather than its own implicit type.
1004
1005
Consider the following example:
1006
\qml
1007
import QtQuick
1008
1009
Item {
1010
id: root
1011
1012
Rectangle {
1013
id: r0
1014
color: "red"
1015
}
1016
1017
Rectangle {
1018
id: r1
1019
color: "blue"
1020
width: 50
1021
}
1022
1023
Rectangle {
1024
id: r2
1025
property int customProperty: 5
1026
}
1027
1028
Rectangle {
1029
id: r3
1030
property string customProperty: "hello"
1031
}
1032
1033
Rectangle {
1034
id: r4
1035
property string customProperty: "hello"
1036
}
1037
}
1038
\endqml
1039
1040
In the previous example, the rectangles \c r0 and \c r1 do not have any custom properties,
1041
and thus the JavaScript and QML engines consider them both to be of the same type. That
1042
is, \c r0 and \c r1 are both considered to be of the explicitly defined \c Rectangle type.
1043
The rectangles \c r2, \c r3 and \c r4 each have custom properties and are each considered
1044
to be of different (implicit) types. Note that \c r3 and \c r4 are each considered to be of
1045
different types, even though they have identical property information, simply because the
1046
custom property was not declared in the component which they are instances of.
1047
1048
If \c r3 and \c r4 were both instances of a \c RectangleWithString component, and that
1049
component definition included the declaration of a string property named \c customProperty,
1050
then \c r3 and \c r4 would be considered to be of the same type (that is, they would be
1051
instances of the \c RectangleWithString type, rather than defining their own implicit type).
1052
1053
\section2 In-Depth Memory Allocation Considerations
1054
1055
Whenever making decisions regarding memory allocation or performance trade-offs, it is
1056
important to keep in mind the impact of CPU-cache performance, operating system paging,
1057
and JavaScript engine garbage collection. Potential solutions should be benchmarked
1058
carefully in order to ensure that the best one is selected.
1059
1060
No set of general guidelines can replace a solid understanding of the underlying
1061
principles of computer science combined with a practical knowledge of the implementation
1062
details of the platform for which the application developer is developing. Furthermore,
1063
no amount of theoretical calculation can replace a good set of benchmarks and analysis
1064
tools when making trade-off decisions.
1065
1066
\section3 Fragmentation
1067
1068
Fragmentation is a C++ development issue. If the application developer is not defining
1069
any C++ types or plugins, they may safely ignore this section.
1070
1071
Over time, an application will allocate large portions of memory, write data to that
1072
memory, and subsequently free some portions of it once it has finished using
1073
some of the data. This can result in "free" memory being located in non-contiguous
1074
chunks, which cannot be returned to the operating system for other applications to use.
1075
It also has an impact on the caching and access characteristics of the application, as
1076
the "living" data may be spread across many different pages of physical memory. This
1077
in turn could force the operating system to swap, which can cause filesystem I/O - which
1078
is, comparatively speaking, an extremely slow operation.
1079
1080
Fragmentation can be avoided by utilizing pool allocators (and other contiguous memory
1081
allocators), by reducing the amount of memory which is allocated at any one time by
1082
carefully managing object lifetimes, by periodically cleansing and rebuilding caches,
1083
or by utilizing a memory-managed runtime with garbage collection (such as JavaScript).
1084
1085
\section3 Garbage Collection
1086
1087
JavaScript provides garbage collection. Memory which is allocated on the JavaScript
1088
heap (as opposed to the native heap) is owned by the JavaScript engine. The engine will
1089
periodically collect all unreferenced data on the JavaScript heap.
1090
1091
\section4 Implications of Garbage Collection
1092
1093
Garbage collection has advantages and disadvantages. It means that manually managing
1094
object lifetime is less important.
1095
However, it also means that a potentially long-lasting operation may be initiated by the
1096
JavaScript engine at a time which is out of the application developer's control. Unless
1097
JavaScript heap usage is considered carefully by the application developer, the frequency
1098
and duration of garbage collection may have a negative impact upon the application
1099
experience. Since Qt 6.8, the garbage collector is incremental, which means it will
1100
incur shorter, but potentially more interruptions.
1101
1102
\section4 Manually Invoking the Garbage Collector
1103
1104
An application written in QML will (most likely) require garbage collection to be
1105
performed at some stage. While garbage collection will be automatically triggered by
1106
the JavaScript engine on its own schedule, it is occasionally better if the
1107
application developer makes decisions about when to invoke the garbage
1108
collector manually (although usually this is not the case).
1109
1110
The application developer is likely to have the best understanding of when an application
1111
is going to be idle for substantial periods of time. If a QML application uses a lot
1112
of JavaScript heap memory, causing regular and disruptive garbage collection cycles
1113
during particularly performance-sensitive tasks (for example, list scrolling, animations,
1114
and so forth), the application developer may be well served to manually invoke the
1115
garbage collector during periods of zero activity. Idle periods are ideal for performing
1116
garbage collection since the user will not notice any degradation of user experience
1117
(skipped frames, jerky animations, and so on) which would result from invoking the garbage
1118
collector while activity is occurring.
1119
1120
The garbage collector may be invoked manually by calling \c{gc()} within JavaScript.
1121
This will cause a full, non-incremental collection cycle to be performed, which
1122
may take from between a few hundred to more than a thousand milliseconds to complete, and
1123
so should be avoided if at all possible.
1124
1125
\section3 Memory vs Performance Trade-offs
1126
1127
In some situations, it is possible to trade-off increased memory usage for decreased
1128
processing time. For example, caching the result of a symbol lookup used in a tight loop
1129
to a temporary variable in a JavaScript expression will result in a significant performance
1130
improvement when evaluating that expression, but it involves allocating a temporary variable.
1131
In some cases, these trade-offs are sensible (such as the case above, which is almost always
1132
sensible), but in other cases it may be better to allow processing to take slightly longer
1133
in order to avoid increasing the memory pressure on the system.
1134
1135
In some cases, the impact of increased memory pressure can be extreme. In some situations,
1136
trading off memory usage for an assumed performance gain can result in increased page-thrash
1137
or cache-thrash, causing a huge reduction in performance. It is always necessary to benchmark
1138
the impact of trade-offs carefully in order to determine which solution is best in a given
1139
situation.
1140
1141
For in-depth information on cache performance and memory-time trade-offs, refer to the following
1142
articles:
1143
\list
1144
\li Ulrich Drepper's excellent article: "What Every Programmer Should Know About Memory",
1145
at: \l{https://people.freebsd.org/~lstewart/articles/cpumemory.pdf}.
1146
\li Agner Fog's excellent manuals on optimizing C++ applications at:
1147
\l{http://www.agner.org/optimize/}.
1148
\endlist
1149
1150
\section1 Fast Boot and Startup Optimization
1151
1152
Based on real-world experience optimizing Qt Quick applications for fast boot,
1153
consider the following best practices:
1154
1155
\list
1156
\li Design your application to start fast from the beginning. Think what you
1157
want the user to see first.
1158
\li Use the \l{QML Profiler} to identify bottlenecks in startup.
1159
\li Use chain loading. Run only as many \l{Loader}{loaders} as you have cores
1160
in your CPU (e.g two cores: two loaders running at the same time).
1161
\li The first \l{Loader}{loader} should not be asynchronous, so that some
1162
content is shown immediately. Trigger the asynchronous loaders after.
1163
\li Connect to back-end services only when required.
1164
\li Create \l{QML Modules} that are imported when required. Using lazily-loaded
1165
modules and types you can can make non-critical services available to your
1166
application as needed.
1167
\li Optimize your PNG/JPG images using tools such as optipng.
1168
\li Optimize your 3D models by reducing the amount of vertices and removing
1169
parts that are not visible.
1170
\li Optimise the 3D model loading by using glTF.
1171
\li Limit use of clip and opacity, as these can impact performance.
1172
\li Measure GPU limitations and take those into account when designing the UI.
1173
See \l{QRhi#Frame captures and performance profiling}{Frame Captures and
1174
Performance Profiling} for more information.
1175
\li Use \l{Qt Quick Compiler} to pre-compile the QML files.
1176
\li Investigate if static linking is possible for your architecture.
1177
\li Strive for declarative bindings instead of imperative signal handlers.
1178
\li Keep property bindings simple. In general, keep QML code simple, fun and
1179
readable. Good performance follows.
1180
\li Replace complex controls with images or shaders if creation time is an
1181
issue.
1182
\endlist
1183
1184
Do not:
1185
\list
1186
\li Go overboard with QML. Even if you use QML, you don’t need to do absolutely
1187
everything in QML.
1188
\li Initialize everything in your main.cpp.
1189
\li Create big singletons that contain all the required interfaces.
1190
\li Create complex delegates for \l{ListView} or other views.
1191
\li Use clip unless absolutely necessary.
1192
\li Fall into the common trap of overusing Loaders. \l{Loader} is great for
1193
lazy-loading larger things like application pages, but introduces too much
1194
overhead for loading simple things. It’s not black magic that speeds up
1195
anything and everything. It’s an extra item with an extra QML context.
1196
\endlist
1197
1198
These practices help achieve sub-second startup times and smooth user
1199
experiences, especially on embedded devices.
1200
1201
*/
qtdeclarative
src
quick
doc
src
concepts
performance
performance.qdoc
Generated on
for Qt by
1.16.1