Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-21151

[Performance] Non-blocking QML element instantiation

    XMLWordPrintable

    Details

      Description

      In order to achieve fast startup times, and continued smoothness as they run, applications need to be able to instantiate new QML elements - anything from delegates to new tabs etc. - while still maintaining a constant, high frame rate. It is expected that a significant portion of most applications will be lazily loaded and there must not be ugly pausing or skipping in animations or transitions while this is happening.

      There are two main stages to creating a QML object tree - compiling the QML file into an internal bytecode form, and running the compiled code to produce the element tree. Currently, both stages occur synchronously (excluding any asynchronicity introduced by network operations) on the main application thread.

      Compilation

      It is reasonable to expect that QML file compilation can be made to run in a (single) separate thread. To maintain the current behaviour, and to ensure that the initial minimal subset of an application is compiled as quickly as possible, the compilation must appear to be synchronous and blocking by default. Only if a special asynchronous flag is set should local compilation be threaded. The C++ API should only need a minor alteration to support this, as compilation that involves a remote resource is already non-blocking today and consequently all the appropriate progress notification signals exist. Even the case involving remote resources should see a performance improvement as even though it behaves in a non-blocking fashion today, as all the work is still being done on the main thread there will be skips and pauses.

      Execution

      There are too many assumptions in Qt, QML and 3rdparty frameworks like V8 to realistically be able to execute the QML bytecode in a separate thread. It is possible that in the future this might become possible, but it is not an achievable short term goal. Rather than execute QML bytecode in a separate thread, we will try and make the bytecode execution pre-emptible. This should allow us to execute the bytecode across several frames using a degenerative form of "cooperative multitasking". The current thought is that, each frame, the QML 2 scenegraph will indicate to the engine that it has an X ms timeslice in which to execute instantiation code which it will try hard not to exceed. As the time taken in non-engine C++ and JavaScript code cannot be arbitrarily preempted, it is possible that the allocated timeslice maybe overrun, but hopefully it will be good enoughTM.

      QML API

      It is probably unnecessary to expose much new API to QML to allow applications to effectively utilise this new behaviour. At the very least, the Loader element should gain an asynchronous property that will enable support. Loaded components that involve a network resource should be asynchronous by default.

      It is not clear how imperative QML APIs like Qt.createComponent() or Qt.createQmlObject() should be extended. One interesting idea is to not extend the API at all, but instead allow them to be used from within a WorkerScript which would automatically enable non-blocking behaviour. This has the added benefit of encouraging people to use WorkerScript, and encouraging the QML developers to make WorkerScript easier to use.

      Implementation Backlog

      • Add separate-thread support to QDeclarativeDataLoader
      • Remove QDeclarativeEngine thread dependencies from QDeclarativeCompiler
        • QDeclarativeScriptData is initialised in loader thread
        • Engine's meta type hash is modified in loader thread, and read in main thread
        • Review instances of QDeclarativeCleanup
        • Imported libraries are loaded in the loader thread. Add another plugin interface and deprecate the one with initializeEngine().
        • Make QDeclarativeEnginePrivate::Locker class skip locking when possible.
        • Implement QDeclarativeEnginePrivate::deleteInEngineThread
      • Add API to QDeclarativeComponent to enable asynchronous loading
      • Refactor execution to bypass non-root QDeclarativeComponents and remove recursion
      • Allow execution to start at any point given starting state
      • Add preemption check to instruction processing
      • Add API to allow the engine to be accept preemption requests.
      • Allow nested executions to hook into an existing asynchronous execution
      • Ensure that cancellations or deletions mid-run don't cause crashes
      • Implement an optimal incubation controller for scenegraph
      • Add JavaScript API for asynchronous loading
      • Documentation
      • Autotest

        Attachments

          Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            Activity

              People

              Assignee:
              aakenned Aaron Kennedy
              Reporter:
              aakenned Aaron Kennedy
              Votes:
              1 Vote for this issue
              Watchers:
              7 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:

                  Gerrit Reviews

                  There are no open Gerrit changes