LvClass 的一个效率
网上看到一篇很好的文章,转载分享给大家!
前几天,听到了一个客户的抱怨:他编写了一个LabVIEW程序,每次打开主程序就要花费几分钟的时间,这有点令他忍无可忍。我没有见过他的源程序,不过据帮他检查过程序的同事讲,他的问题很可能是使用了大量的LvClass造成的。在他的项目中,包含有上百个类(LvClass)。我以前也听说过LvClass在效率上可能会有些问题,听到了这个消息后,我自己做了一个实验。
LabVIEW Scripting中有一个属性节点可以用来查看内存中所有的VI,我就利用这个VI来查看一个程序到底在装入些什么,令它启动如此之慢。
假设不存在子VI,如果打开某个不在LvClass中的VI(即便这个VI是属于某个lvlib的),只有这个VI会被装入内存。但是,打开某一个LvClass中的VI,我发现不但这个VI会被装入内存,它所在的类中的所有其它的VI也都被调入内存。如果这个类还有父类和祖先类,那么所有父类、祖先类中的VI统统都会被调入内存。
总结一下就是这样:当一个VI被装入内存
- 它的所有子VI都会被装入内存;
- 它所在的类中的所有的VI都会被装入内存;
- 它所在的类的父类中的所有的VI都会被装入内存。
以上3条可以是递归发生的,比如一个主VI A被装入内存,它的子VI B也会被装入内存,和B同属一个类的VI C也要被装入内存,C中有个子VI D,D属于类E,E有个父类F,F中有个方法VI G。尽管G的功能和程序A八杆子都打不着了,但也会被装进来。这大概就是那个用户遇到的问题,表面上他的程序不算太大,但是程序开始启动时,却需要把多于程序本身数倍的不相关的VI都装入内存,这一过程会每次都浪费他几分钟的时间。
鉴于LvClass的这一特性,设计使用它的时候一定要格外小心,否则很可能会造成程序效率的低下。我想到了几点需要注意的地方:
- 如果仅需要对一些VI进行封装,那么应当使用lvlib,而不是lvclass。两者封装的主要区别是,lvclass可以封装对象的属性(也就是模块用到的数据)。
- 类中的VI必须是高内聚的,类中的方法共同完成某一基本功能,不可再分割。应用程序一旦用到这个类中的某个VI,就意味着程序将会使用到类中几乎全部的VI;而不是一个应用程序可能只使用这个类中的某几个VI。
- 继承关系应当尽量简单。没有必要的时候尽量不使用继承。LabVIEW不支持接口,不应创建一个纯虚类,然后当作接口来用。
- 尽量不要嵌套调用。比如在一个类的VI中又去调用另一个类中的VI。
- 打算使用多态这个特性时要注意,多态使得应用程序在运行时,根据对象的类型选择对应的处理方法。但有些选择应当是程序编译时就做出的,它们不适合套用在多态特性上。
举一些例子:
- INI文件读写这个模块比较适合做成类,每个INI文件对应一个类的实例。它有丰富的数据(文件的内容);它的方法有限,基本上只需要打开、读条目、写条目、保存关闭,这四个方法,并且一般的应用程序都会同时使用到这四个方法。
- 复杂仪器的驱动程序不适合做成类。因为驱动程序会提供非常多的功能,示波器有各种触发模式。而一个应用程序通常只用到多种模式中的某一种就够用了。
- 某测试程序可以生成测试报告给用户。用户可以选择几种不同的报告类型。生成报告的模块可以用lvclass来设计。因为生成不同类型的报告的方法间,可重用代码很多,可以为它们设计一个基类。并且,是程序运行时,才选择生成报告类型的。
- 某一测试程序,可以支持多种型号的仪器。因为不同用户使用不同的硬件。对不同型号仪器的支持不适合使用lvclass来设计,因为测试程序发布给用户时,用户的硬件设备是固定的。对仪器的选择应当是程序发布时就决定好的,而不应等到程序每次运行起来后判断。