跳到主要内容

LabVIEW 的运行效率

· 阅读需 10 分钟

https://lv.qizhen.xyz/

一、找到程序运行速度的瓶颈

想要提高程序的运行效率,首先要找到程序运行的瓶颈在哪里。LabVIEW 程序的运行也符合 80/20 定理:20%的程序代码占用了80%的运行时间。如果能找到这20%的代码,加以优化,就可以达到事半功倍的效果。 对于已经编写好的程序,可以通过内存和信息工具来查看程序中每个 VI 运行了多长时间。对程序的效率进行优化,要从最耗时的 VI 着手。 内存和信息工具可以从 LabVIEW 的菜单项 Tools->Profile->Performance and Memory 中启动。图1 是这个工具的界面。

图1:内存和信息(Profile Performance and Memory)工具

在内存和信息工具中会列出一个程序中的全部子 VI。在运行这个程序之前,先按下工具界面上的 Start 按钮,工具就开始为所有的子 VI 进行统计了。你的程序运行结束后,点击工具上的 Snapshot,就会显示出每个子 VI 在刚才的运行中占用了多少 CPU 时间。按照 VI Time 降序排序,排在最前面的几个 VI 就是程序的瓶颈,是需要重点优化的对象。 一个子 VI 占用了大量 CPU 时间,有可能是因为它内部的运算较为复杂,那就需要打开它,对它的算法进行优化。但更有可能的是因为这个 VI 被程序执行的次数太多。这时,你就要考虑程序结构了,是否可以减少这个 VI 的运行次数,比如把它从某些不必要的循环中挪出去,或者拆分这个 VI 的代码,把没有必要循环执行的部分分离出去,挪到循环体外面。

并不是所有的运行效率问题都可以在内存和信息工具中体现出来的。 VI Time 列出的只是子 VI 的 CPU 占用时间,如果你的程序里存在大量的不必要延时,或者程序常常被某些低速工作(如读写外部仪器,通过网络传输数据等)所阻塞。这样的程序效率肯定也是很低的,但是这一类的低效率因素在内存和信息工具上是体现不出来的。 有些非常耗用 CPU 的操作也无法体现在内存和信息工具上。比如我在《LabVIEW 的线程》第四章中会提到的使用 OpenGL 的例子,由于程序线程设计不当,CPU 被大量消耗在线程切换上。从系统资源管理器看,CPU 被 LabVIEW 占满,在内存和信息工具却看不到任何一个 VI 占用了如此多的 CPU 时间。

在多核 CPU 的计算机上,由于程序可以在多个 CPU 内核上同时执行,某些子 VI 虽然占用的大量的 CPU 时间,如果程序线程设置合理,是可以让这些 VI 不影响到程序的整体效率的。

二、程序慢在哪里?

仅仅使用内存和信息工具还不能发现所有程序效率问题的。并且一旦程序的主体部分已经完成,再对其进行修改,成本是比较高的。尤其是涉及到结构性的改动时更是如此:以前做过的测试需要重新做,构建在这个模块之上的代码需要作相应更新。如果时间紧迫,同时考虑到这种代码改动所带来的风险,完全可能在程序完成后就无法再对其性能进行优化了。 所以最有效的编写高效率程序的方法是在设计程序结构的时候,就考虑到可能会影响程序效率的所有因素,直接设计出高效率的程序。而不是在程序完成后,再回头查找程序瓶颈。

下面讨论的是一些常见的运行比较慢的程序代码部分。一个程序运行效率的瓶颈通常就出现在这些部分。所以在设计程序时,对这些部分要格外注意。

    a) 读写外设、文件

相对于计算机的中央处理器、内存读写的速度而言,计算机的外围设备的处理和传输数据的速度是非常慢的。比如,GPIB 的传输速率最高也只有 1Mbps,比内存的传输速率低了两个数量级以上。在一个测试应用软件中,造成整个系统效率低下的瓶颈很可能就在于这类数据传输当中,程序的大部分时间都消耗在等待外部数据上了。

    b) 界面

界面刷新和等待事件也是比较耗费时间的工作,这是由于人的反应速度远不如计算机引起的。比如你可以设置屏幕上的数据指示控件中的数值以每秒一千次的速度刷新,但是这对于用户来说毫无意义,因为人眼和大脑根本处理不了如此快速的变化。还有,在显示给用户一条信息后,等待用户的后续指令也需要等待一段时间。

    c) 循环内的运算

设计循环的时候总是要格外小心些,因为就算一段代码运行得再快,循环个几千,甚至几百万次,耗费是时间也不得了了。所以越是执行次数多的循环,他内部代码的效率对整体影响越大。

    d) Global Variable

全局变量不但会破坏LabVIEW的代码风格,并且它的代码读写速度也是特别的慢。

    e) 子VI

使用子VI是会有一定开销的,但是我们在其它文章(LabVIEW 程序的内存优化)里曾经讨论过,使用子VI利大于弊。从这一点来说,子 VI 使用得越多越好。不过需要注意的是,动态调用子VI的速度是非常慢的。因为他需要先把被调用的VI从磁盘装入到内存中,然后才能运行。而且,装载 VI 的工作一定是在界面线程(LabVIEW 的执行系统)中执行的。如果被动态调用的 VI 太大,就会迟滞界面刷新,影响用户的感觉。

    f) 调试信息

这一条对于已经做成可执行文件的程序是没有意义,因为 LabVIEW 在把 VI 转换成可执行文件的时候,一定会去除调试信息的。但是还有相当一部分程序是以 .vi 文件的格式,直接在 LabVIEW 的编译环境中运行的,去除调试信息可以让这种程序降低约 50% 的 CPU 占用时间和内存。

    g) 多线程和内存使用不当

LabVIEW 是自动多线程运行的,并且自动开辟、回收内存空间。这意味着对于 LabVIEW 初级用户来说,可以不去关心有关线程和内存的问题。但是对于高级用户而言,需要追求更高的效率,还是需要考虑多线程和内存对程序的影响的。