The best way to assess the impact of qmlcachegenplus (or even qmlcachegen) on the performance of QML is using it with the existing qmlbench project. Unfortunately the qmlbench tests have to be converted into well-behaved QML modules for that to work. In particular:
- Write CMakeLists.txt files for all benchmarks, declaring them as pure QML modules, much like QtQuick.tooling is handled in qtdeclarative. This results in the QML files being pre-compiled using qmlcachegen(plus). As a result we get a plugin and a qmldir file for each such module.
- Find a place in the qmlbench build directory to install those modules. Do not install them into Qt's global import path.
- Make sure that importing such modules actually loads the pre-compiled code. I'm pretty sure it doesn't, yet. Pure QML modules are not quite finished.
- Allow the qmlbench application to test a module rather than a "bunch of files in a directory". Such a test run would properly import the module and instantiate its types in a civilized way. This might require that qmlbench's own QML rigging is converted into another well-behaved module.
After playing around with this some more, the following ideas have come up for optimizing the generated code:
- Currently we marshal any results of any binding or function into a V4 value. This is wasteful because most of the time we actually want the original return type and will just convert it back. Refactor the calling code to avoid the extra conversions.
- Reading properties through QJSValue and QMetaObject is, predictably, slow. The QML engine has an optimized mechanism for this. We should create an API for it so that qmlcachegenplus-generated code can use it.
- Even better: Once we know that a property has been captured, we can directly call its read accessor. There's no need to interact with the engine at all. The problem is only knowing whether it has been captured before. For that we can probably use the existing lookup mechanism.
- Often, we have multiple options for storing a value retrieved as result of some call or from some property. We may, for example, get a QJSValue of which we know that it contains a QObject*, or we may create an array and we can choose what kind of array. Currently we decide the concrete storage at the place where we first see the value. We might instead decide on that when using the value so that we avoid unnecessary conversions.
- We should implement much of the JS string API on top of QString, in order to avoid converting back and forth between QString and V4 string.
- QQmlListReference is incredibly slow. We should enable QSequentialIterable or the lower level QMetaSequence to access QQmlListProperty instead.