深刻解析QML引擎, 第3部分: 绑定类型

原文 QML Engine Internals, Part 3: Binding Typeshtml

译者注:这个解析QML引擎的文章共4篇,分析很是透彻,在国内几乎没有找到相似的分析,为了便于国内的QT/QML爱好者和工做者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来。翻译并非彻底直译,有不足之处,请指正,谢谢!数组

———————————————————————————————————————————安全

上一篇 绑定(Bindings)   下一篇 自定义解析器闭包

这篇博文是深刻解析QML引擎系列博文的第三篇。在上一篇博文中,咱们揭示了QML引擎中的绑定是如何运做的。在这篇文章中,咱们将深刻了解不一样的绑定类型。某些内容是我在开发者日对话QtQuick Under the Hood中讲过的。除此以外,这篇博文中将涵盖一些新的内容。函数

简要回顾

在回顾以前,让咱们快速地浏览一个简单的绑定:学习

 
图1 绑定例子

每个像这样的绑定其实是一个JavaScript函数,运行时由V8引擎执行。执行的结果就是函数的返回值,而后将它设置给文本属性。因为V8并不知道Qt对象和属性,当遇到一个对象(如parent)或一个属性(如width)时,它就请求QML中的上下文包裹类和对象包裹类去解析它们。当一个绑定被执行时,这些包裹类会记录那些被访问了的属性,能够自动将每一个属性的改变信号(例如widthChanged())链接到一个能够从新执行绑定的槽函数。测试

如今咱们已经从新温习了一遍绑定的工做原理,让咱们趁热打铁,继续分析不一样的绑定方式。优化

绑定方式

在上一篇文章中,我指出每个绑定都被解析成一个QQmlBinding对象的实例。这实际上是一个哄骗孩子的谎话。若是每个绑定都由QQmlBinding表示,则开销会很是大。一个典型的QML应用,即便没有上千个绑定,至少也有成百个绑定,因此须要让每个绑定更加轻量级。此外,当加载一个QML文件时,每个绑定都是单独编译的。所以在加载过程当中会屡次调用V8编译器,给系统形成不小的开销。ui

QV8Bindings

为了解决QQmlBinding形成的开销问题,使用了另一个绑定类,取了一个容易混淆的名字:QV8Bindings。QV8Bindings内部用了一个数组来存放QML文件中的全部绑定,绑定用更加轻量级的QV8Bindings::Binding结构体来表示。QML的开发者过去花了很大力气去减小这种结构的内存占用,他们甚至发现指针的最后两位由于对齐的关系而没有被使用。而后丧心病狂地利用这些空间去保存标志位,最终作到一个QV8Bindings::Binding只占用了64个字节。翻译

QV8Bindings和QQmlBinding相比,有一个大优点是,全部绑定都是在一块儿编译的,因此只须要调用一次V8编译器。在QQmlCompiler ::completeComponentBuild()函数中,你会发现,在编译QML文件时,全部的绑定函数会组成一个大的JavaScript程序,并存储在QQmlCompiledData(用于包含QML文件中全部类型的编译数据)。当QML文件第一次实例化时,QV8Bindings::QV8Bindings()将对绑定程序进行编译,编译后保存在QQmlCompiledData中,而后将源代码丢弃。当再次实例化相同的QML文件时,QML引擎将直接使用QQmlCompiledData中已编译的绑定程序,并不须要再编译一次它们。然而QQmlBinding却不是这样,每次实例化QML文件都须要执行一次编译。

小结:由于QV8Bindings把QML文件中全部的绑定组织在一块儿,因此能够花费更少的内存,并只执行一次编译。

那为何咱们不抛弃QQmlBinding?这个类为何还依旧存在呢?某些状况下,绑定是不可共享的,例如它们使用了闭包或者使用了eval()函数。在这种状况下,每一个绑定函数须要不一样的上下文。所以不能和具备相同上下文的其余绑定一块儿编译。所以在这种特殊状况下,将会使用QQmlBinding来表示绑定。当编译一个QML文件时,是由QQmlCompiler::completeComponentBuild()来断定采用哪一种绑定方式。另外,SharedBindingTester会检测绑定应该用QV8Bindings,仍是QQmlBinding。SharedBindingTester就是一个JS AST的访问者。若是你查看一下代码,你会发现SharedBindingTester也会测试哪些绑定是安全的,同时在QML文件初始化时避免屡次执行绑定,源代码的提交信息作了最好的描述。

为了让QML代码更加的简洁,QQmlBinding和QV8Bindings::Binding都从QQmlAbstractBinding继承。

QV4Bindings

假如你看过一些QML引擎的代码,你极可能已注意到QV4Bindings类,这个类也是QQmlAbstractBinding的子类。它是另外一个绑定类型吗?和什么有关呢?与QV8Bindings相同的是,它也是QML文件中全部绑定的集合。不一样的是,QV4Bindings只保存所谓优化过的绑定,也有人错误和混淆地称之为编译过的绑定。有一些绑定是能够被优化的,它们会用QV4Bindings表示,有一些绑定不能被优化,它们会用QV8Bindings来表示。

那么这个优化是什么呢?QV4Bindings并不禁V8引擎执行,它会被编译成字节码,经过一个字节码解析器执行。这个字节码编译器和解析器没法处理全部的JavaScript表达式,由于不可能提早编译全部的JavaScript。

可是为何使用字节码呢?V8引擎会编译成机器码,难道不比一个字节码解析器快吗?结果证实它真的没有字节码解析器快,V8引擎执行绑定时,须要调用QML来解析对象和属性,这个处理须要很大的开销。另外当一个函数被屡次调用时,V8引擎可能会在比较繁忙的状况下从新编译一个函数,以此作更多地优化。对于QML的状况而言,全部这些处理都会形成很大的开销,由于QML一般包含不少只有一句代码的绑定。这里有一个我为开发者日准备的基准测试结果。在测试中,我只是简单的让QML引擎执行一个绑定几百次。这是一个可让V4引擎轻松处理的简单绑定。为了和V8引擎比较,设置环境变量QML_DISABLE_OPTIMIZER=1来彻底禁用V4绑定。

 
图2 V4和V8绑定执行效率对比

如你所见,在这种特定情形下,V4字节码引擎的确比V8快多了。

从本质上说,V4就是一个寄存器机器。和CPU相同的是,它具备的寄存器,用来存储临时值。不一样的是,它不会从内存加载和储存值——它从类的属性加载和储存值。设置环境变量QML_BINDINGS_DUMP=1,让咱们看一个简单的绑定:

 
图3 简单绑定例子

其指令输出是:

 
图4 绑定执行指令

如你所见,属性width和height被加载到寄存器0和1中,而后这些寄存器乘起来,把结果保存在文本属性中(文本属性在QQuickText类中的属性编号是42)。FetchAndSubscribe指令不只加载属性,也会监听它的改变信号,从而实现绑定的自动更新。从上文的"汇编"代码中,你还能够发现另外一个优点:V4编译器是在编译时解析对象和属性,并将属性的索引保存在字节码中。因此在运行时,就能够直接经过索引访问属性,不用经过属性名字进行查找。而V8引擎则须要调用QML对象和上下文包装器来解析对象和属性,这固然会产生更多的开销。可是缺点是,V4引擎没法处理动态对象,例如那些经过setContextProperty()从C++导出的对象。若是绑定中含这种动态对象,则须要使用QV8Binding。

总结

概括起来,有3个绑定类型,都是从QQmlAbstractBinding继承:

1. QV4Bindings::Binding

2. QV8Bindings::Binding

3. QQmlBinding

QV4Bindings是最快的,由于其使用了自定义的字节码引擎。QV8Bindings和QQmlBinding都是使用V8 JS引擎执行,但QV8Bindings将全部的绑定组织在一块儿,一次性编译,然而QQmlBindings会在每一个QML组件实例化过程当中一个一个地进行编译。

这有一个展现全部绑定类型的(没啥用的)例子:

 
图5 三种类型的绑定的例子

设置环境变量QML_COMPILER_DUMP=1,你会看到QML编译器使用了两次STORE_COMPILED_BINDING,一次STORE_V8_BINDING和一次STORE_BINDING。

STORE_BINDING为QQmlBinding,它用于font.pointSize,由于绑定使用了eval(),所以不能够被共享。

anchors.centerIn的绑定和文字都是V4绑定(STORE_COMPILED_BINDING指令,QV4Bindings:: Binding类)。

最后,font.wordSpacing是一个普通的QV8Bindings::Binding(STORE_V8_BINDING指令)。V4的字节码编译器和解析器应对三元运算符彻底没有问题,可是求补运算还没有实现,因此QML编译器选择使用V8绑定。

在这个系列的下一篇博文中,咱们将尝试自定义解析器。若是有什么疑问或者对QML应用和研究感兴趣的朋友,欢迎加入咱们进行讨论(QQ群:280689979)。如需转载,无须咱们受权,但须要注明原文连接(该文的连接),及原做者,谢谢!

上一篇 绑定(Bindings)  下一篇 自定义解析器

做者:猿基地 连接:https://www.jianshu.com/p/d752ba649817 来源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
相关文章
相关标签/搜索