一. VI 在內存中的結構
打開一個VI的屬性面板(VI Properties),其中的“內存使用”(Memory Usage)是用來查看這個VI內存佔用情況的。它顯示了一個VI內存佔用所包含的四個主要部分:前面板、框圖、代碼和數據,以及這四個部分的總和。但在打開一個VI時,這四段內容並不是同時都會被LabVIEW調入內存的。
當我們打開一個主VI時,主VI連同它的所有子VI的代碼和數據段都會被調入內存。由於主VI的前面板一般情況下是打開的,它的前面板也就同時被調入內存。但是此時主VI的框圖和子VI的前面板、框圖並沒有被調入內存。只有當主動查看主VI的框圖或是打開子VI的前面板和框圖時,它們才會被調入。
基於LabVIEW的這種內存管理的特性,我們在編寫VI的時候可以通過以下方法來優化LabVIEW程序的內存使用。
二. 內存泄漏。
LabVIEW與C語言不同,它沒有任何分配或釋放內存的語句,LabVIEW可以自動管理內存,在適當的時候分配或收回內存資源[1]。這樣就避免了C語言中常見的因為內存管理語句使用不當而引起的內存泄漏。
在LabVIEW中一般只有一種情況能夠引起內存泄漏,即你打開了某些資源,卻忘記了關閉它們。比如,在對文件操作時,我們需要先打開這個文件,返回它的句柄。隨後如果忘記了關閉這個句柄,它所佔用的內存就始終不會被釋放,從而產生內存泄漏。LabVIEW中其它帶有打開句柄的函數或VI也會引起同樣的問題。
由於內存泄漏是動態產生的,我們無法通過VI的屬性面板來查看,但可以通過Windows自帶的任務管理工具來查看LabVIEW程序內存是否有泄漏。也可以使用LabVIEW的Profile (Tools>>Advanced>>Profile VIs)工具來查看某個VI運行時內存的分配情況。
三. 緩存重用
LabVIEW程序主要是數據流驅動型的。數據傳遞到不同節點時往往需要複製一個副本。這是LabVIEW為了防止數據被節點改變引起錯誤所做的一種數據保護措施。只有當目標節點為只讀節點,不可能對輸入數據作任何更改時,才不在這些節點處做備份。例如,數組索引節點(Index)是不會改變數組值的, LabVIEW在這裡就不為輸入數組做備份。對於加減法運算等肯定改變輸入數據的節點,LabVIEW往往需要對輸入或輸出數據作備份。有些 LabVIEW程序,比如涉及到大數組運算的程序,內存消耗極大。其主要原因就是LabVIEW在運算時為數組數據生成了過多的副本。
實際上很多LabVIEW節點是允許使用緩存重用的,這類似C語言調用子函數所使用的地址傳遞。通過合理設計和使用緩存重用節點,可以大大優化 LabVIEW程序的內存使用。使用LabVIEW 7.1的Tool>>Advanced>>Show Buffer Allocations工具可以在VI框圖中查看緩存的分配情況。打開該工具,凡是在框圖中有緩存分配的地方,都會顯示出一個黑點。
下面是幾個最常用節點的試驗結果。LabVIEW節點眾多,不可能一一列舉,文中未提及的節點讀者在編程時自己可以嘗試。
1. 一般順序執行VI中的運算節點
圖1:簡單的順序執行程序
如圖1所示,程序對一個常量加1,然後將結果輸出。“+1”節點輸出端有一個黑點,表示LabVIEW在此處開闢了一個緩存用於保存運算結果。其實完全可以利用輸入數據的內存空間來保存這個運算結果。我們可以通過如下的方法來告知LabVIEW編譯器,在此運算節點處重用輸入數據的內存空間。
首先,用一個控制型數值控制項代替圖中的數值常量,然後分別將VI中的兩個控制項與VI的接線器(Connector Pane)相連。圖2是經過我們優化后的VI,LabVIEW在“+1”節點處沒有開闢新的緩存。LabVIEW中其它運算節點也有類似的性質。
圖2:實現緩存重用
2. 移位寄存器(Shift Register in the Loop Structure)
移位寄存器是LabVIEW內存優化中最為重要的一個節點,因為移位寄存器在循環結構兩端的接線端是強制使用同一內存的。這一特性可以被用來通知LabVIEW在編譯循環內代碼時,重用輸入輸出緩存。
圖3: 對數組進行數值運算的順序執行程序
讓我們分析一下圖3所示的程序:它首先構造了一個數組,然後對這個數組進行了幾次數學運算。每一步運算,LabVIEW都要開闢一塊緩存用以保存運算結果的副本。打開VI屬性面板上的內存使用,可以看到這個VI大約會佔用2.7M的內存空間。其實這些副本都是不必要的,每一步運算的結果都可以被保存到輸入數據的內存空間。我們可以把所用的運算節點都放到一個子VI中,然後利用上一段提到的方法,使子VI中的代碼緩存重用。還有一種方法,利用移位寄存器也可以實現緩存重用。
圖4: 利用移位寄存器實現緩存重用
如圖4,我們可以將運算代碼放在一個只運行一次的循環結構內,由於運算部分的輸入和輸出都與移位寄存器相連,這就相當於通知了LabVIEW,在運算的輸入輸出需要使用同一塊緩存。因而,LabVIEW不再為每一步運算開闢新的緩存而是直接利用輸入數據的緩存保存結果。打開VI屬性面板上的內存使用,可以查看到這個VI的內存佔用已經減少到了原來的六分之一。
3. 庫函數調用節點(Call Library Node)
以傳遞整型參數為例:在參數配置面板,我們可以選擇值傳遞(Pass Value)或選擇指針傳遞(Pass Pointer to Value)。
當選擇了值傳遞時,庫函數調用節點是不會改變該參數的內容的。如果我們在該庫函數調用節點參數的左側接線端引入輸入數據,在輸出端引出輸出參數,那麼輸出數據其實是直接由輸入數據引出的,LabVIEW不會在這個節點處開闢緩存。
在指針傳遞方式時,LabVIEW則認為傳入的數據會被改變。如果輸入數據同時還要發往其它節點,LabVIEW會在此處開闢緩存,為輸入數據作一個副本。選用指針傳遞方式,庫函數調用節點的每一對接線端也同樣是緩存重用的。就是說,庫函數調用節點的輸出值是直接存放在輸入值的緩存空間的。
如果一個參數只用作輸出,我們通常會在庫函數調用節點的輸入接線端為它建立一個輸入常數,這個常數的地址空間並不能直接被利用,它只是為庫函數調用節點開闢的緩存而設置的初始值。不接輸入常數,LabVIEW也會為此參數開闢一塊緩存。但是,這樣每次傳入的參數值都會有變化。例如圖5,庫函數調用節點調用的函數功能是為把輸入的值加1,然後輸出。圖5-a中的輸出值永遠都是1,而圖5-b,每次運行輸出結果都會比前次增加1。這是因為庫函數調用節點每個指針傳遞的參數的輸入輸出用的是同一塊緩存,即每次運行輸入值是上回的輸出值。
圖5: 庫函數調用節點
我們可以利用圖5-c的例子證明LabVIEW某些節點是緩存重用的。每次運行5-c的例子,輸出結果都會比前次增加2。這是因為示例中的參數接線端以及“+1”節點的輸入輸出端所使用的都是同一緩存。
在圖5中的示例中,如果庫函數調用節點輸出的參數是個數組或者字元串,那麼就必須為它相對應的輸入端聯入一個與輸出數據大小一致的數組或字元串。否則,LabVIEW無法知道輸出數據的大小,而使用默認分配的緩存空間很容易出現數組越界錯誤。
四. 小結
緩存重用是LabVIEW內存優化的最重要的一個環節。精心設計的LabVIEW程序可以大大節約內存的佔用,提高運行效率。但是,在編寫完程序后再按照程序優化的技巧回頭去優化一段已有的程序,這並不是一個好的編程方法。我們應該先熟悉理解優化的方法,在以後的開發過程中自然而然地將它們應用在編程中。
[admin via 研發互助社區 ] LabVIEW 程序的內存優化已經有4060次圍觀
http://cocdig.com/docs/show-post-44802.html