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