• 易迪拓培训,专注于微波、射频、天线设计工程师的培养
首页 > 测试测量 > Labview 虚拟仪器 > labview深入探索------类型转换、数组字符串与内存管理

labview深入探索------类型转换、数组字符串与内存管理

录入:edatop.com    点击:
labview同其它任何高级语言一样,都支持多种基本数据类型和复合数据类型,基本数据类型包括U8、U16、U32、I8、I16、I32,SGL,DBL,EXT等等,复合数据类型包括数组、簇、字符串、路径等。


同其它高级语言一样,也拥有基本的程序结构,比如,顺序结构、条件结构,循环结构等。也拥有一些自己独特的程序结构,如队列、通告、信号、集合等等。

无论是数据类型还是程序结构,都是和内存的使用紧密地结合在一起的,所以深入地了解数据类型和类型之间的转换以及结构在内存中的存储形式是非常重要的。

LABVIEW 中涉及到数据类型转换时,会引起内存复制操作。大的数据类型结构比如数组,字符串和簇在内存中实际占的空间比我们预想的要大,因为LABVIEW同时在内存中也增加了很多必要的信息,正如我们在类型描述符中谈到的。使用局部变量和全局变量也要引起内存的复制操作,但是,恰当的编程方式可以避免这个问题。

(一)类型转换是如何使用内存的

类型转换会产生两个问题:增加了类型转换时间 需要开辟新的BUFFER,增大了内存空间的使用。

有些类型转换是必要的,但是更多的情况下是我们编程时不注意造成的,对于人为发生的类型转换要极力避免。

LV是图形化编程方式,连线的颜色和线型代表了不同的数据类型,同时在转换处还会有一个强制转换点,一般是黑色或者红色,我们很容易看到在那里发生了隐含的类型转换。


上面三个都是要完成+1操作,由于+节点虽然是多态的,可以适应各种数据类型,但是默认是DOUBLE型的,由于我先生成了常量1,黄色,导致U32在+节点处发生的类型转换,最后输出的是DOUBLE类型。而第二个我是先连接的U32,后自动生成的常量1,LABVIEW自动产生的U32常量而不是 DOUBLE,第三个直接调用+1节点,所以不会发生类型转换。所以编程风格是非常重要


上面的图中,因为SUBVI需要的是DOUBLE类型的数组,而输入数组是I16的,所以产生了一个强制转换点,因此,会产生同I16SIZE相同的额外的DOUBLE数组。
对于SIZE非常大的数组,不恰当的类型转换耗费的内存是惊人的,看下面的图


上图第一个框图,循环产生10000*64=80K的数组,因为没有类型转换,有效地内存重用,数据流上的执行数据是80K,前面板数组指示器的操作数据是80K,总计160K

看看上图中第二个框图,他用了惊人的400K,不过是乘以一个扩展精度的标量,形成一个扩展精度的数组,然后右重新强制转换成DOUBLE数组。看一下是如何产生的。

---循环80K
---强制转换乘法后,数据流上的执行数据是160K
---强制转换成DOUBLE数组,生成另外的80K执行数据
---数组指示器 80K操作数据
总计:80K+160K+80K+80K=400K

可以看出中间乘法端子出现的EXT是毫无意义的,不经意间,额外的240K内存被耗费掉了。

所以在数据流中如果数据类型不变,我们将节省240K的内存,一个重要的原则就是:

尽量始终在数据流中保持同一种数据类型




上图中,EXAMPEL1和EXAMPLE2是基本相当的,一个是隐含的强制转换点,一个是用显式的SGL--》DOUBLE转换节点,
它们用的内存是完全相同的,但性能稍微有些差异,隐含转换速度稍微快了一点,并且节省的框图的空间,比较方便。

而EXAMPE 3和EXAMPLE 4则是完全不同的,EXAPLE 3发生了二次隐含强制转换,而EXAPLE 4只有一次显式转换。节省了内存和运行时间。

转换的位置也是很重要的,看看下面的图



方法一中,两个数组分别产生8K的数据,其中上面的A的8K经过乘法后得到重用,在转换C处,产生了4K的执行数据(SGL 4BYTE),加上指示器中的操作数据4K,总计24K

方法二中,两个数组分别产生8K的数据,显式转换后,C、D处各产生4K的执行数据,乘法后C处的4K得到重用,加上指示器中操作数据4K,总计:8k+8k+4k+4k+4k=28k

方法三中,循环中对DOUBLE数据显式转换成SGL,因此两个数组各产生4K的数据,其中A处的4K经过乘法节点后得到重用,加上指示器的4K,总计12K。

对于标量,比如DOUBLE,64位,8个字节,即使发生了强制类型转换,可以不必要考虑的它的内存损失。对于字符串和数组,由于它的SIZE是很容易被改变的又没有SIZE限制,要特别重视。


(二)数组和字符串

LABVIEW中的数组和字符串操作是最影响内存的使用和程序的运行速度。它影响速度的原因在于不断调用内存管理器来改变数组或者字符串的SIZE,LABVIEW本身也会在必要的时候改变数组和字符串的存储位置,比如在内存紧张的时候,可能被转移到虚拟内存。



上面的节点是需要特别注意的,因为它们都直接改变的数组和字符串的大小(元素个数),需要不断地调用内存管理器,如果是在一个循环中,会极大地降低程序的性能。

重点看一下BUILD ARRAY函数节点。

数组所占内存的总量,等于元素个数(size)乘以每个元素所占字节数。
比如包含10000个数据的DOUBLE类型,DOUBLE 64位,8个字节,占内存总量=10K*8=80K,但这是数组最后所占的字节数。当你改变数组大小时,由于LV不能重用输入BUFFER,必须进行额外的内存复制,所以在改变的过程中,会极大消耗内存。所以为了有效地利用内存,尽量不要在循环中改变数组或者字符串的大小。这样就可以重用输入BUFFER 到输出BUFFER。

建立一个数组有很多种方式,各种方式有很大区别。


for循环是固定次数循环,因此在循环开始之前,就会为索引输出的数组申请内存空间,一次完成,在循环内部,不需要改变数组的大小,因此效率是最高的。

第二种方法是效率最差的,每次循环时都要增加数组的大小,循环调用内存管理器,是需要极力避免的方法。

第三种方法用的初始化数组节点,整个数组的的内存空间也是一次申请的,在循环中替换数组的各个元素,而替换操作是可以重用内存的,这也是一个常用的比较好的方法,但是FOR循环是LV的基本结构,所以无疑效率是最高的。
[p]


很多时候,我们无法预先知道我们需要的数组大小,比如我们需要一个字符串数组,来记录报警信息,无法知道会有多少报警发生,这样就需要根据实际情况,来不断地改变数组的大小,一个比较好的方法是每次申请一定数量的内存,而不是一个元素的大小,这样,当内存再次需要的时候,我们可以再次申请,当整个循环结束的时候,我们可以去掉多余的空间,得到结果。



内存碎片的问题,对于任何编程语言都是存在的,当内存碎片越来越多的情况下,程序运行会越来越慢,内存管理器要进行大量的搜索试图找到可用的空间,碎片是如何产生的那,比如我们有三个操作ABC,A、B、C分别申请了三块内存,他们是连续的,当B的内存释放的时候,在A、C中间的内存区域就成了碎片,当我们在一个子VI中不断地用BUILD ARRAY或者conctenate string 时,不断引起内存的申请和释放,这样,内存空间碎片会越来越多。也就是内存的间隙越来越多,作为LV的用户,我们没有办法控制内存碎片,只能尽量地使用固定长度的数组或者字符串,我们可以做一个标志,用不太可能的数据来填充这个数组,这样通过这个特殊的数或者特殊字符,就可以得到实际数据。

labview内部是如何存储数组和字符串的

了解数组在内存中的存储形式非常重要,有助于合理组织数据,高效利用内存.有助于理解和使用CIN,有助于理解和使用动态链接库.一般都会认为只有大的数组结构或者字符串才消耗大量的内存,其实不然,由于LABVIEW在内存中构造数据的特殊形式,较小的数组或者字符串有时也会占用可观的内存.



对于一维数值型数组,它包括四个字节(I32)的数组长度,之后是连续的数据元素所占的空间。

对于二维的数值型数组,它包括两个I32(8个字节)表示数组行列长度,之后是按行存储的元素序列。

正如我们看到的,LV在内存中是一段连续的空间来存储数据的,这样当需要增加数组长度的时候,由于内存碎片的影响,LV可能不得不移动整个数组到一个新的内存位置。除了存储实际数据,LV还额外增加了数组长度(I32类型),所以,对一维数组,最大可以包括2的32次方-1个元素。

BOOLEAN型数据是字节型数据,该字节非零则为TRUE,为0则为FALSE,BOOLEAN型数组在内存中的分布看下图。



同C语言一样,字符串是一个字符型数组,不同的是,C字符串并不表明字符串长度,它是以作为结束符的,而LV则不同,它的字符串本身就包含了长度信息,同数组一样。


所以,一个字符串最多可包含2^32-1个字符,约2G字符。


字符串数组是非常特殊的,这也是CLF和CIN的难点。字符串在内存中是连续存储的,但是字符串数组却不同与一般的连续存放的二维数组,字符串数组中的元素,即字符串是以长度开头连续存储的,字符串数组本身是存储在内存中另外位置的。




字符串数组是以I32长度开头的,这和一般数组是相同的,接下来是每个字符串的首地址指针,U32数值,表示字符串在内存中的地址(第一个字符),称做句柄(HANDLE)。

从内存中的不同区域访问数据,相对于连续区域,需要消耗更多的时间,因此,用一个长的字符串代替字符串数组,程序会更加高效。

由于字符串数组的这种组织形式,大量的空字符串组成的数组耗费的内存空间也是很大的,需要特别引起注意。




10K个字符串包括10K个句柄,总计10K*4=40K空间,每个字符串的长度是4个字节,总计10K*4=40K,如果加上前面板的操作数据,那所占内存空间就是非常大的了。

上面描述了数组和字符串在内存中的组织形式,还有一点也需要注意,LABVIEW是以句柄的形式,来描述数组和字符串本身的,句柄是WINDOWS里常用的概念,是指针的指针,比如窗口句柄HWND,它本身是U32,四个字节,它包含的内容是指向一个数据结构的指针,因为数据结构在内存中是可以重新分配的,所以它的地址会经常发生变化,而句柄的地址是不会变化的,这样通过句柄,我们就可以跟踪这个变化的数据结构,LABVIEW中的字符串和数组也是采用这种方式。
另外,对于每个数组和字符串,在长度信息之前还有一个16个字节的头部信息,(LV内部使用,类型描述符),这样,对于一个最简单的数组,它至少需要24个字节:HANDLE(4)+HEADER(16)+SIZE(4)=24 BYTE。

点击浏览:矢量网络分析仪、频谱仪、示波器,使用操作培训教程

上一篇:labview的软件计数器和FIFO BUFFER
下一篇:labview编程技巧-----xcontrol如何发送用户事件

微波射频测量操作培训课程详情>>
射频和天线工程师培训课程详情>>

  网站地图