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