用户自定义控件中 Control, Type Def. 和 Strict Type Def. 的区别

    为了解释清楚,先定义一下要用到的概念。我们把以 .ctl 文件名定义的控件叫做用户自定义控件,把通过拖拽或打开这个 .ctl 文件在 VI 上生成的控件叫做实例。

LabVIEW 的用户自定义控件包括了三种定义形式:打开一个 .ctl 文件,在它上方的“control”下拉条中有三个选择,分别是无关联控件(Control)、类型定义(Type Def.)或者严格类型定义(Strict Type Def.)。
无关联控件是指这个控件与它的实例之间没有任何关联。例如,你制作了一个漂亮的按钮控件保存在 .ctl 文件中。需要用到它时,通过拖拽或打开这个 .ctl 文件就可以在 VI 中生成这个用户自定义控件的一个实例。这个实例一旦生成,就和原用户自定义控件无任何关联了。无论是你修改这个实例,还是修改原用户自定义控件,都不会对另一方产生任何影响。
类型定义控件是指实例控件与用户自定义控件的空间类型是相关联的。比如,你的用户自定义控件是一个数值型控件,那么它的所有实例控件也都是数值型的。如果我们在 .ctl 文件中把用户自定义控件的类型改为字符串,那么它已有的所有实例都将自动变成字符串类型。
有时候,只是类型相关联还不够。比如对于 Ring(Enum,Combo Box)这类的控件来说,如果在用户自定义控件中添加了一项内容(item),一般总是希望它所有的实例也同时添加这一选项。如果使用类型定义控件,因为控件类型没变,还是 Ring,实例们是不会自动跟随更新的。这时就需要使用严格类型定义控件。选择严格类型定义后,不但实例与用户自定义控件的类型是相关联的,其他一些控件属性,比如颜色等等,也是相关联的。

使用严格类型定义时有一点容易被误解:严格类型定义只是与实例控件相关联,由它生成的实例常量的属性是不与之关联的。实例常量是指通过拖拽或生成常量等方法,在程序框图上生成的一个与 .ctl 文件相关联的常量。比如在 Ring 型用户自定义控件中添加了一项内容,相关的实例常量是不会发生任何改变的。很多人按常理想,认为常量也应当自动更新,但事实上不行。这也是我不采用它做常量定义的原因之一。(参见:在 LabVIEW 中使用常量定义

查看博客版《我和 LabVIEW》的其它文章

其它常用调试工具和方法

    除了断点和探针这两种最常用的调试工具外,我们也经常要借助一些其它的工具和方法来找到程序的问题所在。

1. 性能和内存查看工具(Profile Performance and Memory)

    调试的目的并不一定仅要找出功能性错误,有时是要找到程序效率低下的原因,或者潜在危险,如内存泄漏等。这时就要用到 LabVIEW 的性能和内存查看工具了。参见:LabVIEW 的运行效率 1 – 找到程序运行速度的瓶颈

2.  显示缓存分配工具(Show Buffer Allocation)

    显示缓存分配工具是另一检查 LabVIEW 代码内存分配情况的强大工具。参见:LabVIEW 程序的内存优化

3. 程序框图禁用结构(Diagram Disable Structure)

    调试首先要找到问题发生的部位。有时候,我们可以使用探针一路跟踪数据在程序执行过程中的变化。如果数据在某个节点的输出与预期的不一致,这个节点很可能就是问题所在。还有些情况,不是靠这种简单方法就可以找出问题的。比如程序中出现的数组越界的错误,在错误发生后,程序可能还会正常运行一段不确定的时间,然后崩溃,或报错。这种程序报错,或者崩溃的地方有可能在每次调试时都不同,或者找到了最终出错的代码,发现他是个最基本的 LabVIEW 节点,不能再根据去调试了,而这个节点出错的可能性基本为零,错误肯定是其他地方引起的。
    调试这种问题,一般就是把一部分代码禁止掉,看看程序运行是否还有问题。如果没有问题了,说明有毛病的代码被禁止运行,则在把禁止代码的范围再缩小;如果问题又出现了,说明是刚刚被放出来的代码有毛病,则对这部分代码再禁掉一部份,继续调试。知道找出引起问题的一个或几个节点,改正它们。在这个仅用部分调试代码的过程中,使用程序框图禁用结构是最为方便的了,它就好象是 C 语言中用来做注释的关键符号“/*   */”或者“//”。使用它可以方便的把一部分代码框住,禁用,如图1。

 
图1:程序框图禁用结构

    使用程序框图禁用结构需要注意的一点是,这个结构可以有多个 Disable 的页面,同时会有一个 Enable 的页面。调试人员可能还要在 Enable 的页面作一些改动,比如为输出数据添加一些虚拟值,以使后续程序可以程序可以正确运行下去。例如图2,为了让后续的程序继续正确运行,需要把 reverence 和 error 数据线连接上。

 
图2:修改 Enable 页面

4. 条件禁用结构(Conditional Disable Diagram)

    LabVIEW 中还有一个类似于 C 语言中 #if,#ifdef 的结构,就是条件禁用结构。使用条件禁用结构可以让某些代码在特定的条件下不运行。与条件结构(Case Structrue)相区别,条件结构在运行时决定执行哪一个页面中的代码;而条件禁用结构是在编译时就已决定好执行哪一个页面的代码了,不被执行的页面的代码在运行时都不会被装入内存。
    利用条件禁用结构的这一特性,可以把分别需要在调试时和发布后的代码放在不同的条件禁用结构页面内。这样,既可以在不同条件下运行不同的代码,有不会使程序留有冗余的代码。图3 的是一个条件禁用结构应用的典型例子,用户希望在开发调试时,如果错误数据线上出现错误,则探出错误信息的对话框;而在发布之后,又错误发生,也不可以弹出对话框。


图3:使用条件禁用结构控制调试时和发布后程序的不同行为

    点击条件禁用结构右键弹出菜单中的 Edit Condition For This Subdiagram… 条目可以弹出条件配置窗口,在这个窗口改变使本页运行的条件。LabVIEW 有一些预定义的符号(Symbol)可供条件禁用结构使用,比如 TARGET_TYPE 表示目标代码在什么系统下运行。如果条件是“TARGET_TYPE == Mac”表示目标代码运行在苹果机上。
    如果你有工程文件“*.lvproj”,那么还可以在工程文件的属性->条件禁用符号栏下配置自己需要的符号。如图3中的例子,就是我自己在工程的属性对话框中添加了一个“DEBUG”符号,这样我就可以通过更改 DEBUG 符号的值来控制是否弹出程序的错误对话框。

5. 使用消息对话框和文件

    有一些错误是在关闭了调试信息后才出现的,或者出错的代码部分不允许使用 LabVIEW 的调试环境。这时就要使用类似 C 语言中 printf() 的功能了。具体实现方法就是把可以的数据在程序中用 messagebox 显示出来,这样就可以跟踪察看程序是在哪一部分出错的。还可以把所有相关的数据都保存在一个状态记录文件中,察看这个记录文件,就可以找出可以的错误。
    状态记录文件可以与第4节提到的条件禁用结构联合起来使用,设置一个调试开关,再调试运行方式下记录下所有状态信息;在正式发布后不再记录仪提高程序运行效率。

相关文章:
    我和 LabVIEW

编辑

正常访问Google网页快照的方法

转载的,原文在:http://www.williamlong.info/archives/634.html
真不知道为什么,要把网页快照封掉!


  目前,Google的网页快照几乎完全无法访问,对Google的使用者来说非常不便,本文将从Google网页快照无法访问的原理出发,逐步分析出一个可以正常访问Google网页快照的方法。

  根据Google自己的介绍,“Google储存网页的快照,当存有网页的服务器暂时出现故障时仍可浏览该网页的内容”。因此说,网页快照服务器本身出问题的可能性非常小,否则就成笑话了,那么为什么我们都无法访问Google网页快照呢?我们只要分析出不能访问的具体原因,就能知道正常访问 Google网页快照的方法。

  举个例子分析一下,比如使用Google来搜索“搜索引擎”这个词,可以看到如下图的界面。 

  这时我们看一下“网页快照”的URL地址,可以发现是下面的地址:

  http://72.14.235.104/search?q=cache:aPVaeKi1wxcJ:www.baidu.com/+%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E&hl=zh-CN&ct=clnk&cd=1

  根据我们的测试,72.14.235.104这个IP是可以访问的,完全可以ping通,如下图所示,这说明网页快照服务器本身实际没有任何问题,完全可以直接访问到。 

  但是我们点击“网页快照”链接后,却出现“该页无法显示”的提示。如下图所示,这显然是关键字过滤的现象。

  为了分析出具体是什么词语影响,我们还可以在Yahoo下搜索“search?q=cache”这个词,会发现Yahoo也出现了“该页无法显示”的提示。

  现在,聪明的读者应该已经明白这意味着什么了。既然知道了原因,那么解决方法应该很简单了。就是在search?和q=cache中插入一些字符,将这个词分开即可,就可以让Google的网页快照“恢复正常”。

  还是上面提到的这个页面的网页快照,我们将“网页快照”的URL地址复制到地址栏,然后将“search?q=cache”中间插入一个“&” 符号,使得该词语变成“search?&q=cache”,这时候按下回车,我们就可以看到了久违了的Google网页快照,如下图所示。

  当然,这是分析出网页快照无法使用的原理,具体使用中,如果我们每次都这样用快照,那就显得非常麻烦了,如果大家需要频繁使用,建议安装一些浏览器的开发插件,自动将搜索结果中的字符串进行替换,这样就可以完全自动实现Google网页快照的正常浏览访问,关于详细的代码安装和下载,请查看文章《正常访问Google网页快照的插件代码》。

 

 

断点和探针

1. 断点

    断点和探针是调试 LabVIEW 代码时最常用的两个工具。LabVIEW 中的断点在使用和功能上都比较简单、直观:使用工具选板上的断点工具,在想要设置或者取消断点的代码处点击鼠标即可;或直接在程序框图的节点、数据线上右击鼠标,就可以看到设置或取消断点的菜单项。
    断点几乎可以设置在程序的任何部分。当程序运行至断点处,就会暂停,等待调试人员的下一步操作。很多其他语言的调试环境都有条件断点,LabVIEW 的端点没有类似的设置,LabVIEW 是使用条件探针来实现条件断点功能的。
    断点是会保存在 VI 中的。关闭带有断点的 VI,程序执行至断点处还是会停下来,并且这个 VI 会被自动打开。
    如果某个 VI 不允许你设置断点,很可能这个 VI 被设为不允许调试了。此时,只要在 VI 属性中重新设置一下即可。(LabVIEW 的调试环境.2

2. 探针

    探针的功能类似于其他语言调试环境中的查看窗口,用于显示变量当前状态下的数据。LabVIEW 与其他语言不同之处在于,LabVIEW 是数据流驱动型的图形化编程语言。LabVIEW 中的数据传递主要不是使用变量,而是通过节点之间的连线完成的。所以 LabVIEW 的探针也不是针对变量的,而是加在某根数据线上的。
    LabVIEW 的探针也是图形化显示的。比如为一根数字类型的数据线加探针,探针一般就是一个数字型显示控件,见图1。Error Cluster 类型的数据线的探针,则看上去就像是个 Error Cluster,见图2。


图1、图2:数值型和错误信息型数据线的探针

3. 选取其他类型控件作为探针

    如果你觉得 LabVIEW 默认的探针不美观或不适用,则可以在数据线上点击鼠标右键,选择 Custom Probe -> Controls -> … 选取一个其他控件作为探针,如图3。但是要注意,你选取的控件的数据类型要与数据线的数据类型一致才可以。


图3:使用仪表盘控件作为数值型数据线的探针

4. 条件探针

    在你设置断点后,程序在每次执行到断点的时候都会停下来。但有的时候,调试者希望程序只在被监测的数据满足某一条件时,才暂停运行。比如,被监测的数据在正常情况下应大于零,调试者希望一旦数据小于零则暂停。在 LabVIEW 中,可以使用条件探针来实现这样的功能。

 
图4:数值型条件探针

    以图4 为例,如果你希望程序中的循环在运行 8 次以后才停下来,就可以使用条件探针。在记录循环次数的 i 的输出数据线上点击鼠标右键,选择 Custom Probe 下以 Conditional 开头的探针,打开探针上的 Condition 页,就可以设置条件了。此时,若被探测的数据满足你所设置的条件,程序就会暂停。

5. 用户自定义探针

    如果你觉得 LabVIEW 自带的探针功能还不够强大,或者你自己创建了一种数据类型,而LabVIEW 没有适合它的探针,这时你可以自己创造一个满意的探针出来。
    用户自定义的探针其实也是一个 VI。LabVIEW 自带了一些已经做好的探针,这些探针都被放置在 <lvdir>\vi.lib\_probes 文件夹下。你可以打开这里面的 VI 看一看已有的自定义探针是如何做的。比如我们在图4 中所使用的 I32 型条件探针的 VI 是 ConditionalSigned32.vi。    
    需要新建一个自定义探针时,先在数据线上点击鼠标右键,选择 Custom Probe -> New。这时 LabVIEW 会弹出一个向导界面。按照向导的提示,输入所需信息,LabVIEW 会为你生成一个用作探针的 VI 框架,对这个 VI 稍作修改,即可成为一个新的探针。
   这个探针 VI 有一个输入和一个输出。输入的是被探测的数据,输出是一个布尔类型,表示程序是否需要暂停。这个 VI 的界面也就是探针的外观。探针所实现的功能完全依赖于如何对其编程。

相关文章:
    LabVIEW 的调试环境
   《我和 LabVIEW》的其它文章

编辑

循证医学对管理理论的启示

    循证医学(evidence-based medicine, EBM)的思想起源于19世纪中叶,但是直到最近的十几年间才开始流行起来。循证医学是指遵循科学依据(证据)的医学。其核心思想是医疗决策(即患者的处理、治疗指南和医疗政策的制定等)应在现有的最好的临床研究依据(证据)基础上作出。

    诊病当然要有科学依据,这还用提出个什么思想吗,难道医生平时不是这么做的吗?目前,相当多的医生还真就不是这么做的。举个例子,你某一天觉得身体不适,需要看医生。如果你去看西医,他很可能会给你开一些小药片。如果你去看中医,那么你将会得到一大包草药。如果你拿着你的药片去质问中医:为什么你开的药和刚才的医生不一样,你有什么依据可以证明你这一大包草药,比他的药片对我的疾病更有利,多半你会被轰出来。反之,去问西医也可能是一样的结果。这还应该是在医疗条件比较好的地方。如果你去农村的小诊所,不论什么病,得到的可能都是几针青霉素而已。
   小诊所的青霉素治疗法肯定还会延续很长一个时期,因为它看起来对相当一部分病人还是挺有效的。以上提到的几位医生,在为病人开药的时候,所采用的都不是循证的方法,他们所根据的只是自己的经验,而不论这种经验是否禁得住推敲。
    循证医学的思想一直得不到实践,是因为实践它的难度的确很大。比如要论证一包草药和几粒药片,哪一个对你的病证疗效更好。这需要对不同的人群做长期大量的跟踪实验调查。外界因素又很容易影响这种实验的结果,所以实验要求非常苛刻。即使有了实验结果,目前也没高效的途径可以把这种结论及时传达到乡村小医院,供那里的医生作为诊病依据。既然没有太多可供参考的外部信息,医生们也只好靠自己的经验了。
    过去几十年里信息产业的蓬勃发展终于给循证医学的实践提供了可能性。互联网为全世界的医生提供了进行交流的途径。为每一例病证查找治疗依据已经不能么困难了。

    管理领域存在着与医疗领域同样的弊病,管理者做决策,凭借的往往不是事实根据,而是自己的经验。比如一个老板问员工,采用什么手段才能提高公司的销售额。如果那个员工以前在dell做,他八成会建议做直销;安利跳槽过来的肯定会推荐做传销。事实上,也许他们谁都没有切实的证据可以证明自己提出的方案最为有利。
    换一个我自己的例子:如果老板来问我:我们打算开发一个XX项目,用什么编程语言最合适啊?我会毫不犹豫地说:LabVIEW。我才不管 LabVIEW 是否真的比 VC、VB 更适合这个项目呢,只要我用着 LabVIEW 比别的顺手就行了。
    而作为老板,他在决定是否采用某一建议时,常常也是没有可供参考的证据,最后还是要靠自己的经验和直觉来作决定。而经验和直觉有时则是错误的。

    循证管理比循证医学更困难。因为人的不同个体之间的生理差距是比较小的:如果一种药物能治好你的感冒,基本上它也就可以治好别人的感冒。但是不同企业和组织间的差别就太大了,它包括有行业上的差别、规模的差别、文化的差别等等。这些差异都会使得在某个企业颇为有效的管理方法到了另一个企业就适得其反。而且,管理领域与医疗领域不一样,没有大量的个体供作对比试验。也就是说,管理者已知的案例没一个与眼前的问题有相同背景,没有一个可以直接套用的解决方案。

    现在的循证管理与循证医学的思想一样,在刚出现时遇到了同样的困难,也许这些困难不是短期就可以解决的。但是,正如循证医学终于有了可以实践的机会一样,随着通讯手段的进步革新,以及管理案例的不断积累,终归有一天循证管理能够得以实施。

—————–

     把循证医学的思想引入到管理领域,不是我的创意。我是在以前曾经读到的一篇文章里看到这个想法的。当时我正在上生物医学专业的课程,所以对这个想法印象比较深。今天又突然想起了这件事,所以把我的想法总结一下,记录下来作为备考。

参考资料:
    《循证医学
    《循证医学与21世纪医学教育
    《生活随笔》

编辑

LabVIEW 的调试环境

1. LabVIEW 的全局选项

在 LabVIEW 8.2 中打开 Tools -> Options 菜单项,选择其中的 Debugging,会出现如下四个选项:


图1:LabVIEW 与调试相关的选项

a) Show data flow during execution highlighting 表示在高亮显示执行的过程中显示数据的流动。
b) Auto probe during execution highlighting 表示在高亮显示执行的过程中,数据从每个接线端流出时,显示数据的数值。
c) Show warnings in Error List dialog by default 表示在默认情况下,在错误列表的对话框中显示警告信息。
d) Prompt to investigate internal errors on startup 表示在 LabVIEW 启动时检查是否存在内部错误。

如果你仅从字面上还不能理解上述几个选项的含义,不要紧,后面的章节里会详细介绍它们的含义。

2. VI 的属性

某些 VI 的属性设置可能会导致你无法调试这个 VI。比如,VI 被设置为有密码保护,而你又不知道密码是什么;又比如,VI 被设置为不允许调试等。禁止 VI 调试可以大大提高 VI 的运行速度,降低 VI 的内存占用,所以,在 VI 发布给用户之前,最好把它设为不可调试。


图2:VI 的属性设置

3. 调试工具

VI 程序框图上的工具栏中,某些按键是用于调试的。


图3:正在运行的一个 VI 的程序框图

图3 是一个正在运行的 VI 的程序框图。我们看到的工具栏上的按钮的图形,基本就可以猜出它的功能了。
用于停止整个程序的执行。
用于暂停或者继续程序的执行。
用于启动高亮显示执行。在高亮显示执行时,LabVIEW 会放慢代码的执行速度,并且在程序执行到每一个节点时,高亮显示这个正在被执行的结点。高亮显示执行的速度非常慢,所以启用它要非常小心。如果启动高亮显示的同时,你的某个 VI 前面板是模式的(modal),那么你想中途关掉它是不可能的了,你只能非常痛苦地等待程序的结束,或杀掉整个 LabVIEW 进程。
用于保留 VI 程序框图上数据线中的数据。
用于单步执行,它们三个分别表示进入、跳过或跳出某个节点、结构以及子 VI。
下拉框表示 VI 的调用关系。打开下拉框,可以看到当前 VI 从低层到高层的逐级被调用关系。选择下拉菜单中的某一项,即可跳到那个 VI 被调用的地方。
是设置断点的地方。
是设置探针的地方。图3 上的悬浮窗口显示的就是探针所在处的数据。

在需要设置断点和探针的地方按鼠标右键,在弹出菜单里可以选择 Set Breakpoint 或者 Probe,或者通过使用工具选板(Tool Palette)上的断点和探针工具进行设置。

 

相关文章:
断点和探针
博客版《我和 LabVIEW》

LabVIEW 的运行效率 2 – 程序慢在哪里

二、程序慢在哪里?

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

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

    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 初级用户来说,可以不去关心有关线程和内存的问题。但是对于高级用户而言,需要追求更高的效率,还是需要考虑多线程和内存对程序的影响的。

相关文章:
     LabVIEW 的运行效率 1 – 找到程序运行速度的瓶颈
    《我和 LabVIEW》的其它文章

编辑

LabVIEW 的运行效率 1 – 找到程序运行速度的瓶颈

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

想要提高程序的运行效率,首先要找到程序运行的瓶颈在哪里。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 不影响到程序的整体效率的。

相关文章:
LabVIEW 的运行效率 2 – 程序慢在哪里
博客版《我和 LabVIEW》

我其它的 blog

用 XControl 实现面向组件的编程

    XControl 是 LabVIEW 8 中出现的新功能。关于 XControl 功能介绍和实现方法可以参见这个网址:http://zone.ni.com/devzone/conceptd.nsf/webmain/1AE46AF02D67AF468625706E006E577C
    面向组件的编程(Component Oriented Programming ,COP)技术建立在对象技术之上,它是对象技术的进一步发展。“类”这个概念仍然是组件技术中一个基础的概念,但是组件技术更核心的概念是“接口”。组件技术的主要目标是复用—粗粒度的复用,组件的核心是接口。

    LabVIEW 为我们提供了大量漂亮的控件,可以让我们非常方便地就搭建出一个程序界面来。然而,对于追求完美的用户而言,LabVIEW 提供的为数有限的控件是远远不够的。比如图1,是 LabVIEW 8.2 的一个新功能—导入导入共享库向导的界面。在它右上方有四个按钮,这四个按钮有着特殊的外观图标,在 LabVIEW 中并没有直接提供这样的按钮。要拥有这样的按钮,并保存下来以供再次使用,就只能自己来制作这样一个自定义控件。关于(用户自定义控件可以参考文章《用户自定义控件中 Control, Type Def. 和 Strict Type Def. 的区别》)

  
图1:LabVIEW 8.2 中 Import Shared Library 的界面

    自定义控件虽然可以定义控件的外观,但无法定义控件的行为。功能复杂一点的控件,.ctl 文件就爱莫能助了。还是以图1 为例,它的 Include Paths 控件是“一个”功能比较复杂的控件,它比 LabVIEW 自带的列表框多了编辑功能。用户添加或编辑一个路径时,这个控件要为用户在所编辑的路径项目上,显示出供编辑使用的文本框和浏览路径按钮。
    实际上这个编辑功能是由三个 LabVIEW 提供的标准控件合作完成的:一个 Listbox、一个 String 和一个 Button 控件。它们的行为(位置、是否可见,值,等等)是在程序运行时决定的:当用户选择编辑控件中某一路径时,程序就把 String 和 Button 挪到 Listbox 上需要编辑的那一项,并遮挡住 Listbox 原本的内容。这样,用户只能在 String 控件内输入内容,或者点击浏览按钮选择一个路径。编辑完成,程序把 String 控件的值写到 Listbox 上相应的项目中。

    我们虽然看不见图1 例子中的程序框图,但是可以想象,上述的一系列操作,如判断 String 和 Button 应当显示的位置;然后挪动它们、把 String 值传给 Listbox;处理用户对他们操作的消息等等,都会为这个程序添加不少复杂的代码。这些代码应该是与程序的其它部分没有任何直接关系的。但是如果把它们也写在这个界面 VI 的程序框图上,一方面影响了程序的可读性;另一方面,编程人员有可能在更改程序其它问题时,一不小心改变了这部分代码,降低了代码的安全性。
    从逻辑关系上来看,图1 中上半部分的 Listbox、String、浏览按钮以及右上方四个操作按钮,合作共同完成一个功能,与它们之外的界面控件没有什么直接关联,所以他们七个应当被作为一个整体,或者说是一个组件。这个组件需要与程序其它模块之间的接口就只是一个字符串数组—用于输入或输出一组路径。其它的数据和操作,都应当是组件私有的、外部不可见的。
    在 LabVIEW 8 之前,想分离和封装出这样一个组件是非常困难的。因为既然这七个控件都在这个 VI 的面板上,对它们的操作和相应的代码必须放在这个 VI 的程序框图上,无法与其他代码隔离开。当然也不是说绝对没有办法,比如你可以使用 sub-panel、动态注册事件等方法,强行地把它们的代码分隔开。但是这些方法既不简单直观,使用它们又可能会让程序变得更为复杂、难以阅读和维护。

    XControl 的出现终于为这个问题提供了一个比较完美的解决方案。图1 中我们提到的七个应当划分在同一组件的控件可以被制作成一个 XControl。这个 XControl 的外观就是图1 中上半部分七个控件组合在一起的样子。XControl 与用户自定义控件相比,它不仅定义了控件的外观,更重要的是,开发人员可以通过编写 LabVIEW 代码定义 XControl 的行为,这些代码是对外隐藏的。开发人员还可以定义 XControl 的属性和方法,通过 Property Node 和 Invoke Node 在程序中使用这些属性和方法。
    同样完成选取一组路径这个功能,可以有各种不同的界面。比如各种 C++ 编译器都会提供类似的功能,但它们外观各不相同。你可以利用 XControl,编写多个外观、行为大相径庭的组件。但是,只要他们的接口相同—都是一个字符串数组,用户就可以在这些组件内任意互换,选用自己喜欢的组件,而不需改动程序的任何其它部分!
    现在,XControl 仍然不太令人满意的地方是它还不支持用户自定义的事件。

    XControl 具有封装的特性。因此我在《利用 LabVIEW 工程库实现面向对象编程》一文中提到,同样可以使用 XControl 来达到面向对象的编程方法。但是 XControl 不具备继承和多态的特性。与之相比较,Library 和 LVClass 只能够把程序中的某些功能封装成模块,而涉及到包含界面的模块,就无能为力了。XControl 则非常适合制作有界面的程序组件。

参考文章:
   
一个 XControl 的实例
    我和 LabVIEW
    面向对象与面向组件小议

编辑