Qt Academy has now launched! See how we aim to teach the next generation of developers. Get started
最新版Qt 6.3已正式发布。 了解更多。
最新バージョンQt 6.3がご利用いただけます。 詳細はこちら

Evolution of the QML engine, part 1

QML as a technology has become increasingly important for the success of Qt. It allows for the creation of smooth, animated user interfaces, in line with expectations on the market today. There are three main features, that make it so suitable for UI creation. The first one is the declarative syntax, that makes it extremely easy to create these user interfaces and have both developers and UI designers work on the same code base. Secondly, the technology makes the integration of native code relatively easy, allowing to do much of the application logic and heavy lifting in C++. And finally, the choice of Javascript as an integral part of the QML language allows for easy prototyping and opens the technology up to a wider audience.

The engine as it is today is a fantastic basis and fills most of our users needs. However, it also has a couple of shortcomings and problems that will need to get solved in the future. This series of blogs is going to outline our findings and present the solutions that we are planning to implement for them. The goal will be to get to a better, more flexible and easier to maintain QML engine in the future.

So let's start of with some of the problems we're facing with the current engine.

  • Several object models
    The current QML engine uses the V8 Javascript engine to execute property bindings. Each QML item has several representations internally. One towards V8 (using the public V8 API), one towards the QML engine, and one to expose it as a QObject to native Qt. The problem here is that this requires the QML engine to keep these different representations in sync, leading to a lot of glue code and rather high memory consumption to hold the different representations.
  • Bindings done through JS closures
    Each binding is represented as a JS function closure towards V8. This requires rewriting the expression as a closure, and then letting V8 parse the rewritten expression once again. Evaluating the binding requires calling a JS function in V8 with quite a bit of overhead to instantiate the proper context for executing the binding. This is rather slow and lead to the development of a small expression interpreter (called V4) inside the QML engine that would evaluate simple expressions. This interpreter is many times faster than evaluating through V8.
  • Data type conversions
    One reason why evaluation of expressions in V8 is slow is the overhead of converting data types from Qt to V8 and vice versa. Accessing a Qt property would for example create a QVariant, that would then need to be converted to a V8::Value. If the variant was a string this involved copying the string data. To soften this, various caching mechanisms were introduced, but those came at the cost of increased memory usage and additional complexity in the code.
  • QML scoping rules
    QML is syntactically a tree of items. Inner items implicitly see properties of outer items leading to a well defined scope chain. Unfortunately this scope chain works differently than traditional Javascript and couldn't be directly implemented with existing Javascript engines. The only way to do it directly would have been through nesting slow and deprecated with() statements. The current solution involves a very intrusive patch to V8 and still requires all QML properties to be looked up by name.
  • Throwing away QML type info
    QML as a language has type information. Javascript however is completely untyped. This lead to the fact that all type information got thrown away at QML compile time. Retaining the type information would allow us to optimise QML expressions a lot better.
  • iOS and WinRT support
    iOS does not allow making memory both executable and writable, something that existing JS engines require. WinRT doesn't allow making memory executable at all. This makes it impossible to use V8 on these platforms without writing a full interpreter backend to V8.
  • Working with upstream V8
    As said above, we are currently forced to maintain a rather large and intrusive patch set on top of V8. There is no chance of merging these patches upstream, as V8 fully focuses on the browser use cases.

These different arguments have lead to a research project that was started Roberto Raggi and Aaron Kennedy in Nokia around a year ago, where they explored the feasibility of having a JS engine that is tailored to the use cases QML requires. The project with the codename v4vm was then transferred to Digia, where Simon, Erik and myself took it up and continued with it. It lived as a playground project on qt-project.org for the last couple of months.

Today we integrated this research project into a development branch of Qt Declarative, and we will continue to work on it there from now on.

The new engine contains a fully compliant ECMAScript 5.1 implementation, that runs on all platforms Qt supports. It contains a JIT that currently works on Linux, Mac and iOS. Integration into the current QML engine is currently done through a V8 compatible API layer which we plan to remove in the coming weeks. The engine can run all Qt Quick demos (with some minor glitches).

One of the things to note is that the engine is focused at QML use cases. This implies that we expect QML performance to become a lot better then it is today. Pure JS performance will on the other hand not be quite as good as what you get with V8 as a backend.

Still our current benchmark numbers are very promising also for pure JS code. The V8 benchmark runs currently around three times slower than the JS engine used in Qt 4.8 and Qt Quick 1, and we see a lot of potential for further optimisations.

Details about the new engine and our future plans will come in a followup post. You can find the code in the wip/v4 branch in the qtdeclarative repository. Feel free to play around with it. If you want to join development, feel free to contact us on #qt-v4vm on freenode.

Blog Topics: