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