diff options
Diffstat (limited to 'src/qml/doc/src/performance.qdoc')
-rw-r--r-- | src/qml/doc/src/performance.qdoc | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/src/qml/doc/src/performance.qdoc b/src/qml/doc/src/performance.qdoc new file mode 100644 index 0000000000..91c9bbbbd7 --- /dev/null +++ b/src/qml/doc/src/performance.qdoc @@ -0,0 +1,1089 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms +** and conditions contained in a signed written agreement between you +** and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtquick2-performance.html +\title QML Performance +\brief performance issues and suggestions + +\tableofcontents + +\section1 Timing Considerations + +As an application developer, you must strive to allow the rendering engine +to achieve a consistent 60 frames-per-second refresh rate. 60 FPS means +that there is approximately 16 milliseconds between each frame in which +processing can be done, which includes the processing required to upload +the draw primitives to the graphics hardware. + +In practice, this means that the application developer should use asynchronous, +event driven programming wherever possible, should use worker threads to do +significant processing, should never manually spin the event loop, and should +never spend more than a couple of milliseconds per frame within blocking functions. +Failure to do so will result in skipped frames, which has a drastic effect on the +user experience. + +\section1 Profiling + +The most important tip is: use the QML profiler included with Qt Creator. Knowing +where time is spent in an application will allow you to focus on problem areas which +actually exist, rather than problem areas which potentially exist. See the Qt Creator +manual for more information on how to use the QML profiling tool. + +Determining which bindings are being run the most often, or which functions your +application is spending the most time in, will allow you to decide whether you need +to optimize the problem areas, or redesign some implementation details of your +application so that the performance is improved. Attempting to optimize code without +profiling is likely to result in very minor rather than significant performance +improvements. + +\section1 JavaScript + +Most QML applications will have a large amount of JavaScript code in them, in the +form of dynamic functions, signal handlers, and property binding expressions. +This is not generally a problem (in fact, due to some optimizations in the QML engine +(bindings compiler, etc) it can for some use-cases be faster than calling a C++ function) +however care must be taken to ensure that unnecessary processing isn't triggered +accidentally. + +\section2 Bindings + +There are two types of bindings in QML: optimized and non-optimized bindings. +It is a good idea to keep binding expressions as simple as possible, since the +QML engine makes use of an optimized binding expression evaluator which can +evaluate simple binding expressions without needing to switch into a full +JavaScript execution environment. These optimized bindings are evaluated far +more efficiently than more complex (non-optimized) bindings. + +Things to avoid in binding expressions to maximize optimizability: +\list + \li declaring intermediate JavaScript variables + \li calling JavaScript functions + \li constructing closures or defining functions within the binding expression + \li accessing properties outside of the immediate context (generally, this means outside the component) + \li writing to other properties as side effects +\endlist + +The QML_COMPILER_STATS environment variable may be set when running a QML application +to print statistics about how many bindings were able to be optimized. + +Bindings are quickest when they know the type of objects and properties they are working +with. This means that non-final property lookup in a binding expression can be slower +in some cases, where it is possible that the type of the property being looked up has +been changed (for example, by a derived type). + +Note that if a binding cannot be optimized by the QML engine's optimized binding +expression evaluator, and thus must be evaluated by the full JavaScript environment, +some of the tips listed above will no longer apply. For example, it can sometimes be +beneficial to cache the result of property resolution in an intermediate JavaScript +variable, in a very complex binding. Upcoming sections have more information on these +sorts of optimizations. + +\section2 Type-Conversion + +One major cost of using JavaScript is that in most cases when a property from a QML +element is accessed, a JavaScript object with an external resource containing the +underlying C++ data (or a reference to it) is created. In most cases, this is fairly +inexpensive, but in others it can be quite expensive. One example of where it is +expensive is assigning a C++ QVariantMap Q_PROPERTY to a QML "variant" property. +Lists can also be expensive, although sequences of specific types (QList of int, +qreal, bool, QString, and QUrl) should be inexpensive; other list types involve an +expensive conversion cost (creating a new JavaScript Array, and adding new elements +one by one, with per-element conversion from C++ type instance to JavaScript value). + +Converting between some basic property types (such as "string" and "url" properties) +can also be expensive. Using the closest matching property type will avoid unnecessary +conversion. + +If you must expose a QVariantMap to QML, use a "var" property rather than a "variant" +property. In general, "property var" should be considered to be superior to +"property variant" for every use-case in QtQuick 2.0 (note that "property variant" +is marked as obsolete in the QtQuick 2.0 documentation), as it allows a true JavaScript +reference to be stored (which can reduce the number of conversions required in certain +expressions). + +\section2 Resolving Properties + +Property resolution takes time. While in some cases the result of a lookup can be +cached and reused, it is always best to avoid doing unnecessary work altogether, if +possible. + +In the following example, we have a block of code which is run often (in this case, it +is the contents of an explicit loop; but it could be a commonly-evaluated binding expression, +for example) and in it, we resolve the element with the "rect" id and its "color" property +multiple times: + +\qml +// bad.qml +import QtQuick 2.0 + +Item { + width: 400 + height: 200 + Rectangle { + id: rect + anchors.fill: parent + color: "blue" + } + + function printValue(which, value) { + console.log(which + " = " + value); + } + + Component.onCompleted: { + var t0 = new Date(); + for (var i = 0; i < 1000; ++i) { + printValue("red", rect.color.r); + printValue("green", rect.color.g); + printValue("blue", rect.color.b); + printValue("alpha", rect.color.a); + } + var t1 = new Date(); + console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); + } +} +\endqml + +We could instead resolve the common base just once in the block: + +\qml +// good.qml +import QtQuick 2.0 + +Item { + width: 400 + height: 200 + Rectangle { + id: rect + anchors.fill: parent + color: "blue" + } + + function printValue(which, value) { + console.log(which + " = " + value); + } + + Component.onCompleted: { + var t0 = new Date(); + for (var i = 0; i < 1000; ++i) { + var rectColor = rect.color; // resolve the common base. + printValue("red", rectColor.r); + printValue("green", rectColor.g); + printValue("blue", rectColor.b); + printValue("alpha", rectColor.a); + } + var t1 = new Date(); + console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); + } +} +\endqml + +Just this simple change results in a significant performance improvement. +Note that the code above can be improved even further (since the property +being looked up never changes during the loop processing), by hoisting the +property resolution out of the loop, as follows: + +\qml +// better.qml +import QtQuick 2.0 + +Item { + width: 400 + height: 200 + Rectangle { + id: rect + anchors.fill: parent + color: "blue" + } + + function printValue(which, value) { + console.log(which + " = " + value); + } + + Component.onCompleted: { + var t0 = new Date(); + var rectColor = rect.color; // resolve the common base outside the tight loop. + for (var i = 0; i < 1000; ++i) { + printValue("red", rectColor.r); + printValue("green", rectColor.g); + printValue("blue", rectColor.b); + printValue("alpha", rectColor.a); + } + var t1 = new Date(); + console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); + } +} +\endqml + +\section2 Property Bindings + +A property binding expression will be re-evaluated if any of the properties +it references are changed. As such, binding expressions should be kept as +simple as possible. + +If you have a loop where you do some processing, but only the final result +of the processing is important, it is often better to update a temporary +accumulator which you afterwards assign to the property you need to update, +rather than incrementally updating the property itself, in order to avoid +triggering re-evaluation of binding expressions during the intermediate +stages of accumulation. + +The following contrived example illustrates this point: + +\qml +// bad.qml +import QtQuick 2.0 + +Item { + id: root + width: 200 + height: 200 + property int accumulatedValue: 0 + + Text { + anchors.fill: parent + text: root.accumulatedValue.toString() + onTextChanged: console.log("text binding re-evaluated") + } + + Component.onCompleted: { + var someData = [ 1, 2, 3, 4, 5, 20 ]; + for (var i = 0; i < someData.length; ++i) { + accumulatedValue = accumulatedValue + someData[i]; + } + } +} +\endqml + +The loop in the onCompleted handler causes the "text" property binding to +be re-evaluated six times (which then results in any other property bindings +which rely on the text value, as well as the onTextChanged signal handler, +to be re-evaluated each time, and lays out the text for display each time). +This is clearly unnecessary in this case, since we really only care about +the final value of the accumulation. + +It could be rewritten as follows: + +\qml +// good.qml +import QtQuick 2.0 + +Item { + id: root + width: 200 + height: 200 + property int accumulatedValue: 0 + + Text { + anchors.fill: parent + text: root.accumulatedValue.toString() + onTextChanged: console.log("text binding re-evaluated") + } + + Component.onCompleted: { + var someData = [ 1, 2, 3, 4, 5, 20 ]; + var temp = accumulatedValue; + for (var i = 0; i < someData.length; ++i) { + temp = temp + someData[i]; + } + accumulatedValue = temp; + } +} +\endqml + +\section2 Sequence tips + +As mentioned earlier, some sequence types are fast (eg, QList<int>, QList<qreal>, +QList<bool>, QList<QString>, QStringList and QList<QUrl>) while others will be +much slower. Aside from using these types wherever possible instead of slower types, +there are some other performance-related semantics you need to be aware of to achieve +the best performance. + +Firstly, there are two different implementations for sequence types: one for where +the sequence is a Q_PROPERTY of a QObject (we'll call this a reference sequence), +and another for where the sequence is returned from a Q_INVOKABLE function of a +QObject (we'll call this a copy sequence). + +A reference sequence is read and written via QMetaObject::property() and thus is read +and written as a QVariant. This means that changing the value of any element in the +sequence from JavaScript will result in three steps occurring: the complete sequence +will be read from the QObject (as a QVariant, but then cast to a sequence of the correct +type); the element at the specified index will be changed in that sequence; and the +complete sequence will be written back to the QObject (as a QVariant). + +A copy sequence is far simpler as the actual sequence is stored in the JavaScript +object's resource data, so no read/modify/write cycle occurs (instead, the resource +data is modified directly). + +Therefore, writes to elements of a reference sequence will be much slower than writes +to elements of a copy sequence. In fact, writing to a single element of an N-element +reference sequence is equivalent in cost to assigning a N-element copy sequence to that +reference sequence, so you're usually better off modifying a temporary copy sequence +and then assigning the result to a reference sequence, during computation. + +Assume the existence (and prior registration into the "Qt.example 1.0" namespace) of the +following C++ type: + +\code +class SequenceTypeExample : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY (QList<qreal> qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged) + +public: + SequenceTypeExample() : QQuickItem() { m_list << 1.1 << 2.2 << 3.3; } + ~SequenceTypeExample() {} + + QList<qreal> qrealListProperty() const { return m_list; } + void setQrealListProperty(const QList<qreal> &list) { m_list = list; emit qrealListPropertyChanged(); } + +signals: + void qrealListPropertyChanged(); + +private: + QList<qreal> m_list; +}; +\endcode + +The following example writes to elements of a reference sequence in a +tight loop, resulting in bad performance: + +\qml +// bad.qml +import QtQuick 2.0 +import Qt.example 1.0 + +SequenceTypeExample { + id: root + width: 200 + height: 200 + + Component.onCompleted: { + var t0 = new Date(); + qrealListProperty.length = 100; + for (var i = 0; i < 500; ++i) { + for (var j = 0; j < 100; ++j) { + qrealListProperty[j] = j; + } + } + var t1 = new Date(); + console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); + } +} +\endqml + +The QObject property read and write in the inner loop caused by the +\c{"qrealListProperty[j] = j"} expression makes this code very suboptimal. Instead, +something functionally equivalent but much faster would be: + +\qml +// good.qml +import QtQuick 2.0 +import Qt.example 1.0 + +SequenceTypeExample { + id: root + width: 200 + height: 200 + + Component.onCompleted: { + var t0 = new Date(); + var someData = [1.1, 2.2, 3.3] + someData.length = 100; + for (var i = 0; i < 500; ++i) { + for (var j = 0; j < 100; ++j) { + someData[j] = j; + } + qrealListProperty = someData; + } + var t1 = new Date(); + console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); + } +} +\endqml + +Secondly, a change signal for the property is emitted if any element in it changes. +If you have many bindings to a particular element in a sequence property, it is better +to create a dynamic property which is bound to that element, and use that dynamic +property as the symbol in the binding expressions instead of the sequence element, +as it will only cause re-evaluation of bindings if its value changes. + +This is an unusual use-case which most clients should never hit, but is worth being +aware of, in case you find yourself doing something like this: + +\qml +// bad.qml +import QtQuick 2.0 +import Qt.example 1.0 + +SequenceTypeExample { + id: root + + property int firstBinding: qrealListProperty[1] + 10; + property int secondBinding: qrealListProperty[1] + 20; + property int thirdBinding: qrealListProperty[1] + 30; + + Component.onCompleted: { + var t0 = new Date(); + for (var i = 0; i < 1000; ++i) { + qrealListProperty[2] = i; + } + var t1 = new Date(); + console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); + } +} +\endqml + +Note that even though only the element at index 2 is modified in the loop, the three +bindings will all be re-evaluated since the granularity of the change signal is that +the entire property has changed. As such, adding an intermediate binding can +sometimes be beneficial: + +\qml +// good.qml +import QtQuick 2.0 +import Qt.example 1.0 + +SequenceTypeExample { + id: root + + property int intermediateBinding: qrealListProperty[1] + property int firstBinding: intermediateBinding + 10; + property int secondBinding: intermediateBinding + 20; + property int thirdBinding: intermediateBinding + 30; + + Component.onCompleted: { + var t0 = new Date(); + for (var i = 0; i < 1000; ++i) { + qrealListProperty[2] = i; + } + var t1 = new Date(); + console.log("elapsed: " + (t1.valueOf() - t0.valueOf()) + " milliseconds"); + } +} +\endqml + +In the above example, only the intermediate binding will be re-evaluated each time, +resulting in a significant performance increase. + +\section2 Value-Type tips + +Value-type properties (font, color, vector3d, etc) have similar QObject property +and change notification semantics to sequence type properties. As such, the tips +given above for sequences are also applicable for value-type properties. While +they are usually less of a problem with value-types (since the number of +sub-properties of a value-type is usually far less than the number of elements +in a sequence), any increase in the number of bindings being re-evaluated needlessly +will have a negative impact on performance. + +\section2 Other JavaScript Objects + +Different JavaScript engines provide different optimizations. The JavaScript engine +which QtQuick 2 uses is optimized for object instantiation and property lookup, but +the optimizations which it provides relies on certain criteria. If your application +does not meet the criteria, the JavaScript engine falls back to a "slow-path" mode +with much worse performance. As such, always try to ensure you meet the following +criteria: + +\list +\li Avoid using eval() if at all possible +\li Do not delete properties of objects +\endlist + +\section1 Common Interface Elements + +\section2 Text Elements + +Calculating text layouts can be a slow operation. Consider using the \c PlainText +format instead of \c StyledText wherever possible, as this reduces the amount of work +required of the layout engine. If you cannot use \c PlainText (as you need to embed +images, or use tags to specify ranges of characters to have certain formatting (bold, +italic, etc) as opposed to the entire text) then you should use \c StyledText. + +You should only use \c AutoText if the text might be (but probably isn't) +\c StyledText as this mode will incur a parsing cost. The \c RichText mode should +not be used, as \c StyledText provides almost all of its features at a fraction of +its cost. + +\section2 Images + +Images are a vital part of any user interface. Unfortunately, they are also a big +source of problems due to the time it takes to load them, the amount of memory they +consume, and the way in which they are used. + +\section3 Asynchronous Loading + +Images are often quite large, and so it is wise to ensure that loading an image doesn't +block the UI thread. Set the "asynchronous" property of the QML Image element to +\c true to enable asynchronous loading of images from the local file system (remote +images are always loaded asynchronously) where this would not result in a negative impact +upon the aesthetics of the user interface. + +Image elements with the "asynchronous" property set to \c true will load images in +a low-priority worker thread. + +\section3 Explicit Source Size + +If your application loads a large image but displays it in a small-sized element, set +the "sourceSize" property to the size of the element being rendered to ensure that the +smaller-scaled version of the image is kept in memory, rather than the large one. + +Beware that changing the sourceSize will cause the image to be reloaded. + +\section3 Avoid Run-time Composition + +Also remember that you can avoid doing composition work at run-time by providing the +pre-composed image resource with your application (e.g., providing elements with shadow +effects). + +\section2 Position Elements With Anchors + +It is more efficient to use anchors rather than bindings to position items +relative to each other. Consider this use of bindings to position rect2 +relative to rect1: + +\code +Rectangle { + id: rect1 + x: 20 + width: 200; height: 200 +} +Rectangle { + id: rect2 + x: rect1.x + y: rect1.y + rect1.height + width: rect1.width - 20 + height: 200 +} +\endcode + +This is achieved more efficiently using anchors: + +\code +Rectangle { + id: rect1 + x: 20 + width: 200; height: 200 +} +Rectangle { + id: rect2 + height: 200 + anchors.left: rect1.left + anchors.top: rect1.bottom + anchors.right: rect1.right + anchors.rightMargin: 20 +} +\endcode + +\section1 Models and Views + +Most applications will have at least one model feeding data to a view. There are +some semantics which application developers need to be aware of, in order to achieve +maximal performance. + +\section2 Custom C++ Models + +It is often desirable to write your own custom model in C++ for use with a view in +QML. While the optimal implementation of any such model will depend heavily on the +use-case it must fulfil, some general guidelines are as follows: + +\list +\li Be as asynchronous as possible +\li Do all processing in a (low priority) worker thread +\li Batch up backend operations so that (potentially slow) I/O and IPC is minimized +\li Use a sliding slice window to cache results, whose parameters are determined with the help of profiling +\endlist + +It is important to note that using a low-priority worker thread is recommended to +minimise the risk of starving the GUI thread (which could result in worse perceived +performance). Also, remember that synchronization and locking mechanisms can be a +significant cause of slow performance, and so care should be taken to avoid +unnecessary locking. + +\section2 ListModel + +QML provides a ListModel element which can be used to feed data to a ListView. +It should suffice for most use-cases and be relatively performant so long as +it is used correctly. + +\section3 Populate Within A Worker Thread + +ListModel elements can be populated in a (low priority) worker thread in JavaScript. The +developer must explicitly call "sync()" on the ListModel from within the WorkerScript to +have the changes synchronized to the main thread. See the WorkerScript documentation +for more information. + +Please note that using a WorkerScript element will result in a separate JavaScript engine +being created (as the JavaScript engine is per-thread). This will result in increased +memory usage. Multiple WorkerScript elements will all use the same worker thread, however, +so the memory impact of using a second or third WorkerScript element is negligible once +an application already uses one. + +\section3 Don't Use Dynamic Roles + +The ListModel element in QtQuick 2.0 is much more performant than in QtQuick 1.0. The +performance improvements mainly come from assumptions about the type of roles within each +element in a given model - if the type doesn't change, the caching performance improves +dramatically. If the type can change dynamically from element to element, this optimization +becomes impossible, and the performance of the model will be an order of magnitude worse. + +Therefore, dynamic typing is disabled by default; the developer must specifically set +the boolean "dynamicRoles" property of the model to enable dynamic typing (and suffer +the attendant performance degradation). We recommend that you do not use dynamic typing +if it is possible to redesign your application to avoid it. + +\section2 Views + +View delegates should be kept as simple as possible. Have just enough QML in the delegate +to display the necessary information. Any additional functionality which is not immediately +required (e.g., if it displays more information when clicked) should not be created until +needed (see the upcoming section on lazy initialization). + +The following list is a good summary of things to keep in mind when designing a delegate: +\list +\li The fewer elements that are in a delegate, the faster they can be created, and thus + the faster the view can be scrolled. +\li Keep the number of bindings in a delegate to a minimum; in particular, use anchors + rather than bindings for relative positioning within a delegate. +\li Avoid using ShaderEffect elements within delegates. +\li Never enable clipping on a delegate. +\endlist + +You may set the \c cacheBuffer property of a view to allow asynchronous creation and +buffering of delegates outside of the visible area. Utilizing a \c cacheBuffer is +recommended for view delegates that are non-trivial and unlikely to be created within a +single frame. + +Be mindful that a \c cacheBuffer keeps additional delegates in-memory and therefore the +value derived from utilizing the \c cacheBuffer must be balanced against additional memory +usage. Developers should use benchmarking to find the best value for their use-case, since +the increased memory pressure caused by utilizing a \c cacheBuffer can, in some rare cases, +cause reduced frame rate when scrolling. + +\section1 Visual Effects + +QtQuick 2 includes several features which allow developers and designers to create +exceptionally appealing user interfaces. Fluidity and dynamic transitions as well +as visual effects can be used to great effect in an application, but some care must +be taken when using some of the features in QML as they can have performance implications. + +\section2 Animations + +In general, animating a property will cause any bindings which reference that property +to be re-evaluated. Usually, this is what is desired but in other cases it may be better +to disable the binding prior to performing the animation, and then reassign the binding +once the animation has completed. + +Avoid running JavaScript during animation. For example, running a complex JavaScript +expression for each frame of an x property animation should be avoided. + +Developers should be especially careful using script animations, as these are run in the main +thread (and therefore can cause frames to be skipped if they take too long to complete). + +\section2 Particles + +The QtQuick 2.0 Particles module allows beautiful particle effects to be integrated +seamlessly into user interfaces. However every platform has different graphics hardware +capabilities, and the Particles module is unable to limit parameters to what your hardware +can gracefully support. The more particles you attempt to render (and the larger they are), +the faster your graphics hardware will need to be in order to render at 60 FPS. Affecting +more particles requires a faster CPU. It is therefore important to test all +particle effects on your target platform carefully, to calibrate the number and size of +particles you can render at 60 FPS. + +It should be noted that a particle system can be disabled when not in use +(e.g., on a non-visible element) to avoid doing unnecessary simulation. + +See the \l{Particle System Performance Guide} for more in-depth information. + +\section2 Shaders + +Shaders written in GLSL allow for complex transformations and visual effects to be written, +however they should be used with care. Using a ShaderEffectSource causes a scene to +prerendered into an FBO before it can be drawn. This extra overhead is quite expensive. + +A ShaderEffect element can imply a ShaderEffectSource (and the indirect rendering costs +associated with that) and also involves uploading a vertex and fragment shader program +(which is then compiled into a GLSL shader). Each fragment shader runs once for every +pixel of the scene, and so these should be kept as simple as possible. + +\section1 Controlling Element Lifetime + +By partitioning an application into simple, modular components, each contained in a single +QML file, you can achieve faster application startup time and better control over memory +usage, and reduce the number of active-but-invisible elements in your application. + +\section2 Lazy Initialization + +The QML engine does some tricky things to try to ensure that loading and initialization of +components doesn't cause frames to be skipped, however there is no better way to reduce +startup time than to avoid doing work you don't need to do, and delaying the work until +it is necessary. This may be achieved by using either \l Loader or creating components +\l {Dynamic Object Management in QML}{dynamically}. + +\section3 Using Loader + +The Loader is an element which allows dynamic loading and unloading of components. + +\list +\li Using the "active" property of a Loader, initialization can be delayed until required. +\li Using the overloaded version of the "setSource()" function, initial property values can + be supplied. +\li Setting the Loader \l {Loader::asynchronous}{asynchronous} property to true may also + improve fluidity while a component is instantiated. +\endlist + +\section3 Using Dynamic Creation + +Developers can use the Qt.createComponent() function to create a component dynamically at +runtime from within JavaScript, and then call createObject() to instantiate it. Depending +on the ownership semantics specified in the call, the developer may have to delete the +created object manually. See \l {Dynamic Object Management in QML} for more information. + +\section2 Destroy Unused Elements + +Elements which are invisible because they are a child of a non-visible element (e.g., the +second tab in a tab-widget, while the first tab is shown) should be initialized lazily in +most cases, and deleted when no longer in use, to avoid the ongoing cost of leaving them +active (e.g., rendering, animations, property binding evaluation, etc). + +An item loaded with a Loader element may be released by resetting the "source" or +"sourceComponent" property of the Loader, while other items may be explicitly +released by calling destroy() on them. In some cases, it may be necessary to +leave the item active, in which case it should be made invisible at the very least. + +See the upcoming section on Rendering for more information on active but invisible elements. + +\section1 Rendering + +The scene graph used for rendering in QtQuick 2.0 allows highly dynamic, animated user +interfaces to be rendered fluidly at 60 FPS. There are some things which can +dramatically decrease rendering performance, however, and developers should be careful +to avoid these pitfalls wherever possible. + +\section2 Clipping + +Clipping is disabled by default, and should only be enabled when required. + +Clipping is a visual effect, NOT an optimization. It increases (rather than reduces) +complexity for the renderer. If clipping is enabled, an item will clip its own painting, +as well as the painting of its children, to its bounding rectangle. This stops the renderer +from being able to reorder the drawing order of elements freely, resulting in a sub-optimal +best-case scene graph traversal. + +Clipping inside a delegate is especially bad and should be avoided at all costs. + +\section2 Over-drawing and Invisible Elements + +If you have elements which are totally covered by other (opaque) elements, it is best to +set their "visible" property to \c false or they will be needlessly drawn. + +Similarly, elements which are invisible (e.g., the second tab in a tab widget, while the +first tab is shown) but need to be initialized at startup time (e.g., if the cost of +instantiating the second tab takes too long to be able to do it only when the tab is +activated), should have their "visible" property set to \c false, in order to avoid the +cost of drawing them (although as previously explained, they will still incur the cost of +any animations or bindings evaluation since they are still active). + +\section2 Manual Layouts + +The scene graph renderer is able to batch up certain operations to minimise the number of +OpenGL state changes required. However, this optimization is only possible for the +built-in layout elements provided by QtQuick 2.0, and cannot be applied to manual layouts. + +Therefore, application developers should use the Row, Column, Grid, GridView and ListView +elements instead of manual layouts wherever possible. + +\section1 Memory Allocation And Collection + +The amount of memory which will be allocated by an application and the way in which that +memory will be allocated are very important considerations. Aside from the obvious +concerns about out-of-memory conditions on memory-constrained devices, allocating memory +on the heap is a fairly computationally expensive operation, and certain allocation +strategies can result in increased fragmentation of data across pages. JavaScript uses +a managed memory heap which is automatically garbage collected, and this provides some +advantages but also has some important implications. + +An application written in QML uses memory from both the C++ heap and an automatically +managed JavaScript heap. The application developer needs to be aware of the subtleties +of each in order to maximise performance. + +\section2 Tips For QML Application Developers + +The tips and suggestions contained in this section are guidelines only, and may not be +applicable in all circumstances. Be sure to benchmark and analyse your application +carefully using empirical metrics, in order to make the best decisions possible. + +\section3 Instantiate and initialize components lazily + +If your application consists of multiple views (for example, multiple tabs) but only +one is required at any one time, you can use lazy instantiation to minimize the +amount of memory you need to have allocated at any given time. See the prior section +on \l{Lazy Initialization} for more information. + +\section3 Destroy unused objects + +If you lazily instantiate components, or dynamically create objects during a JavaScript +expression, it is often better to manually \c{destroy()} them rather than waiting for +automatic garbage collection to do so. See the prior section on +\l{Controlling Element Lifetime} for more information. + +\section3 Don't manually invoke the garbage collector + +In most cases, it is not wise to manually invoke the garbage collector, as it will block +the GUI thread for a substantial period of time. This can result in skipped frames and +jerky animations, which should be avoided at all costs. + +There are some cases where manually invoking the garbage collector is acceptable (and +this is explained in greater detail in an upcoming section), but in most cases, invoking +the garbage collector is unnecessary and counter-productive. + +\section3 Avoid complex bindings + +Aside from the reduced performance of complex bindings (for example, due to having to +enter the JavaScript execution context to perform evaluation), they also take up more +memory both on the C++ heap and the JavaScript heap than bindings which can be +evaluated by QML's optimized binding expression evaluator. + +\section3 Avoid defining multiple identical implicit types + +If a QML element has a custom property defined in QML, it becomes its own implicit type. +This is explained in greater detail in an upcoming section. If multiple identical +implicit types are defined inline in a component, some memory will be wasted. In that +situation it is usually better to explicitly define a new component which can then be +reused. + +Defining a custom property can often be a beneficial performance optimization (for +example, to reduce the number of bindings which are required or re-evaluated), or it +can improve the modularity and maintainability of a component. In those cases, using +custom properties is encouraged; however, the new type should, if it is used more than +once, be split into its own component (.qml file) in order to conserve memory. + +\section3 Re-use existing components + +If you are considering defining a new component, it's worth double checking that such a +component doesn't already exist in the component set for your platform. Otherwise, you +will be forcing the QML engine to generate and store type-data for a type which is +essentially a duplicate of another pre-existing and potentially already loaded component. + +\section3 Use module APIs instead of pragma library scripts + +If you are using a pragma library script to store application-wide instance data, +consider using a QObject module API instead. This should result in better performance, +and will result in less JavaScript heap memory being used. + +\section2 Memory Allocation in a QML Application + +The memory usage of a QML application may be split into two parts: its C++ heap usage, +and its JavaScript heap usage. Some of the memory allocated in each will be unavoidable, +as it is allocated by the QML engine or the JavaScript engine, while the rest is +dependent upon decisions made by the application developer. + +The C++ heap will contain: +\list + \li the fixed and unavoidable overhead of the QML engine (implementation data + structures, context information, and so on) + \li per-component compiled data and type information, including per-type property + metadata, which is generated by the QML engine depending on which modules are + imported by the application and which components the application loads + \li per-object C++ data (including property values) plus a per-element metaobject + hierarchy, depending on which components the application instantiates + \li any data which is allocated specifically by QML imports (libraries) +\endlist + +The JavaScript heap will contain: +\list + \li the fixed and unavoidable overhead of the JavaScript engine itself (including + built-in JavaScript types) + \li the fixed and unavoidable overhead of our JavaScript integration (constructor + functions for loaded types, function templates, and so on) + \li per-type layout information and other internal type-data generated by the JavaScript + engine at runtime, for each type (see note below, regarding types) + \li per-object JavaScript data ("var" properties, JavaScript functions and signal + handlers, and non-optimized binding expressions) + \li variables allocated during expression evaluation +\endlist + +Furthermore, there will be one JavaScript heap allocated for use in the main thread, and +optionally one other JavaScript heap allocated for use in the WorkerScript thread. If an +application does not use a WorkerScript element, that overhead will not be incurred. The +JavaScript heap can be several megabytes in size, and so applications written for +memory-constrained devices may be best served to avoid using the WorkerScript element +despite its usefulness in populating list models asynchronously. + +Note that both the QML engine and the JavaScript engine will automatically generate their +own caches of type-data about observed types. Every component loaded by an application +is a distinct (explicit) type, and every element (component instance) which defines its +own custom properties in QML is an implicit type. Any element (instance of a component) +which does not define any custom properties is considered by the JavaScript and QML engines +to be of the type explicitly defined by the component, rather than its own implicit type. + +Consider the following example: +\qml +import QtQuick 2.0 + +Item { + id: root + + Rectangle { + id: r0 + color: "red" + } + + Rectangle { + id: r1 + color: "blue" + width: 50 + } + + Rectangle { + id: r2 + property int customProperty: 5 + } + + Rectangle { + id: r3 + property string customProperty: "hello" + } + + Rectangle { + id: r4 + property string customProperty: "hello" + } +} +\endqml + +In the previous example, the rectangles \c r0 and \c r1 do not have any custom properties, +and thus the JavaScript and QML engines consider them both to be of the same type. That +is, \c r0 and \c r1 are both considered to be of the explicitly defined \c Rectangle type. +The rectangles \c r2, \c r3 and \c r4 each have custom properties and are each considered +to be different (implicit) types. Note that \c r3 and \c r4 are each considered to be of +different types, even though they have identical property information, simply because the +custom property was not declared in the component which they are instances of. + +If \c r3 and \c r4 were both instances of a \c RectangleWithString component, and that +component definition included the declaration of a string property named \c customProperty, +then \c r3 and \c r4 would be considered to be the same type (that is, they would be +instances of the \c RectangleWithString type, rather than defining their own implicit type). + +\section2 In-Depth Memory Allocation Considerations + +Whenever making decisions regarding memory allocation or performance trade-offs, it is +important to keep in mind the impact of CPU-cache performance, operating system paging, +and JavaScript engine garbage collection. Potential solutions should be benchmarked +carefully in order to ensure that the best one is selected. + +No set of general guidelines can replace a solid understanding of the underlying +principles of computer science combined with a practical knowledge of the implementation +details of the platform for which the application developer is developing. Furthermore, +no amount of theoretical calculation can replace a good set of benchmarks and analysis +tools when making trade-off decisions. + +\section3 Fragmentation + +Fragmentation is a C++ development issue. If the application developer is not defining +any C++ types or plugins, they may safely ignore this section. + +Over time, an application will allocate large portions of memory, write data to that +memory, and subsequently free some portions of that memory once it has finished using +some of the data. This can result in "free" memory being located in non-contiguous +chunks, which cannot be returned to the operating system for other applications to use. +It also has an impact on the caching and access characteristics of the application, as +the "living" data may be spread across many different pages of physical memory. This +in turn could force the operating system to swap which can cause filesystem I/O - which +is, comparatively speaking, an extremely slow operation. + +Fragmentation can be avoided by utilizing pool allocators (and other contiguous memory +allocators), by reducing the amount of memory which is allocated at any one time by +carefully managing object lifetimes, by periodically cleansing and rebuilding caches, +or by utilizing a memory-managed runtime with garbage collection (such as JavaScript). + +\section3 Garbage Collection + +JavaScript provides garbage collection. Memory which is allocated on the JavaScript +heap (as opposed to the C++ heap) is owned by the JavaScript engine. The engine will +periodically collect all unreferenced data on the JavaScript heap, and if fragmentation +becomes an issue, it will compact its heap by moving all "living" data into a contiguous +region of memory (allowing the freed memory to be returned to the operating system). + +\section4 Implications of Garbage Collection + +Garbage collection has advantages and disadvantages. It ensures that fragmentation is +less of an issue, and it means that manually managing object lifetime is less important. +However, it also means that a potentially long-lasting operation may be initiated by the +JavaScript engine at a time which is out of the application developer's control. Unless +JavaScript heap usage is considered carefully by the application developer, the frequency +and duration of garbage collection may have a negative impact upon the application +experience. + +\section4 Manually Invoking the Garbage Collector + +An application written in QML will (most likely) require garbage collection to be +performed at some stage. While garbage collection will be automatically triggered by +the JavaScript engine when the amount of available free memory is low, it is occasionally +better if the application developer makes decisions about when to invoke the garbage +collector manually (although usually this is not the case). + +The application developer is likely to have the best understanding of when an application +is going to be idle for substantial periods of time. If a QML application uses a lot +of JavaScript heap memory, causing regular and disruptive garbage collection cycles +during particularly performance-sensitive tasks (for example, list scrolling, animations, +and so forth), the application developer may be well served to manually invoke the +garbage collector during periods of zero activity. Idle periods are ideal for performing +garbage collection since the user will not notice any degradation of user experience +(skipped frames, jerky animations, and so on) which would result from invoking the garbage +collector while activity is occurring. + +The garbage collector may be invoked manually by calling \c{gc()} within JavaScript. +This will cause a comprehensive collection and compaction cycle to be performed, which +may take from between a few hundred to more than a thousand milliseconds to complete, and +so should be avoided if at all possible. + +\section3 Memory vs Performance Trade-offs + +In some situations, it is possible to trade-off increased memory usage for decreased +processing time. For example, caching the result of a symbol lookup used in a tight loop +to a temporary variable in a JavaScript expression will result in a significant performance +improvement when evaluating that expression, but it involves allocating a temporary variable. +In some cases, these trade-offs are sensible (such as the case above, which is almost always +sensible), but in other cases it may be better to allow processing to take slightly longer +in order to avoid increasing the memory pressure on the system. + +In some cases, the impact of increased memory pressure can be extreme. In some situations, +trading off memory usage for an assumed performance gain can result in increased page-thrash +or cache-thrash, causing a huge reduction in performance. It is always necessary to benchmark +the impact of trade-offs carefully in order to determine which solution is best in a given +situation. + +For in-depth information on cache performance and memory-time trade-offs, please see +Ulrich Drepper's excellent article "What Every Programmer Should Know About Memory" +(available at http://ftp.linux.org.ua/pub/docs/developer/general/cpumemory.pdf as at 18th +April 2012), and for information on C++-specific optimizations, please see Agner Fog's +excellent manuals on optimizing C++ applications (available at +http://www.agner.org/optimize/ as at 18th April 2012). + +*/ |