VB程序的優化

一、減少加載窗體數目
每一個加載的窗體,無論可視與否,都要佔據一定數量的內存(其數量隨窗體上控件的類型和數量,以及窗體上位圖的大小等的不同而變化)。只在需要顯示時才加載窗體,不再需要時,卸載窗體(而不是隱藏窗體)。記住,任何對窗體的屬性、方法或控件的引用,或對用 New 聲明的窗體變量的引用,都會導致 Visual Basic 加載該窗體。
當使用 Unload 方法卸載窗體時,只能釋放部分窗體所佔空間。要釋放所有空間,可用關鍵字 Nothing 使窗體的引用無效:
Set Form = Nothing
二、減少控件數目
當設計應用程序時,窗體應儘量少用控件。實際的限制取決於控件的類型和系統,但實際上,含有大量控件的窗體將運行緩慢。一項與之相關的技術是:設計時,儘可能地使用控件數組,而不是在窗體上放置大量同類型的控件。
詳細信息 關於控件數組的詳細信息,請參閱“使用 Visual Basic 的標準控件”中的“使用控件數組”。
三、用標籤代替文本框
標籤控件佔用的 Windows 資源比文本框少,因此,在可能的情況下,應使用標籤代替文本框。例如,當窗體上需要一個隱藏的控件保存文本時,使用標籤更有效。
四、保持數據在磁盤文件或資源中,並且只在需要時才加載
在設計時,直接放入應用程序的數據(象屬性或代碼中的文字字符串和數值)將增加運行時應用程序佔用的內存。運行時從磁盤文件或資源中加載數據可減少佔用內存。這對大位圖和字符串特別有價值。
詳細信息 關於嚮應用程序添加資源的詳細信息,請參閱“再論編程”中“利用資源文件進行工作”。


五、組織模塊
Visual Basic 只在需要時才加載模塊—即當代碼調用模塊中的一個過程時,模塊才被加載到內存。如果從未調用一特定模塊中的過程,Visual Basic 決不加載該模塊。因此,儘量把相關的過程放在同一模塊中,讓 Visual Basic 只在需要時才加載模塊。
六、考慮替換 Variant 數據類型
Variant 數據類型使用極其靈活,但是比其它數據類型所佔內存大。當要壓縮應用程序多餘的空間時,應考慮用其它數據類型替代 Variant 變量,特別是替代 Variant 變量數組。
每一個 Variant 佔用 16 個字節,而 Integer 佔 2 個字節,Double 佔 8 個字節。變長字符串變量佔用 4 個字節加上字符串中每一個字符佔用 1 個字節,但是,每一個包含字符串的 Variant 都要佔用 16 個字節加上字符串中每一個字符佔用 1 個字節。因爲它們太大,因此在用作局部變量或過程的參數時,Variant 變量是特別煩人的,這是因爲它們消耗堆棧空間太快。
但在有些情況下,使用其它數據類型替代 Variant,靈活性降低了,爲彌補損失的靈活性,不得不增加更多的代碼。結果是大小沒有真正的減小。
七、使用動態數組,並在刪除時回收內存
使用動態數組代替固定數組。當不再需要動態數組的數據時,用 Erase 或 ReDim Preserve 放棄不需要的數據,並回收數組所用內存。例如,用以下代碼可回收動態數組所用空間:
Erase MyArray
這裏,Erase 完全刪除數組,ReDim Preserve 則只縮短數組而不丟失其內容:
ReDim Preserve MyArray(10, smallernum)
刪除了固定大小數組,也不能回收該數組所佔空間—只是簡單地清除數組每一元素中的值。如果元素是字符串,或包含字符串或數組的 Variant 變量,那麼刪除數組可回收這些字符串或 Variants 所佔內存,而不是數組本身所佔內存。
八、回收被字符串或對象變量用過的空間
當過程結束時,可自動回收(非靜態)局部字符串和數組變量所用空間。但是,全局和模塊級的字符串和數組變量一直存活到整個程序結束。要想應用程序儘量小,就得儘可能回收這些變量所用空間。將零長度字符串賦給字符串變量,可回收其空間:
SomeStringVar = "" '回收空間。
同樣地,將對象變量設置成 Nothing 可回收該對象所用的部分(而不是全部)空間。例如,刪除一個 Form 對象變量:
Global F As New StatusForm
F.Show 1 'Form 加載並以模態顯示。
X = F.Text1.Text '用戶按下按鈕
'隱藏窗體。
Unload F '刪除窗體可視部分。
Set F = Nothing '回收空間(模塊數據)。
即使沒有使用顯式窗體變量,也應注意將不再用的窗體卸載,而不是簡單地隱藏。
九、消除死代碼和無用的變量
在開發和修改應用程序時,可能遺留了死代碼— 代碼中的一個完整過程,而它並沒有被任何地方調用。也可能聲明瞭一些不用的變量。雖然,在創建 .exe 文件中,Visual Basic 確實可刪除無用的常數,但不能刪除無用的變量和死代碼。注意要複查代碼,查找並刪除無用的變量和死代碼。例如,Debug.Print 語句,在運行 .exe 時被忽略,可它常常出現在 .exe 文件中。
當創建 .exe 文件時,含有字符串和變量作爲參數的 Debug.Print 語句不會被編譯。但是,對於含有函數作爲參數的 Debug.Print 語句,其本身被編譯器忽略,而函數則被編譯。因此,在應用程序運行時,函數被調用,但返回值被忽略。因爲,在 .exe 文件中,函數作爲 Debug.Print 的參數出現時,將佔用空間和 CPU 週期時間,所以在生成 .exe 文件前,最好刪除這些語句。
在“編輯”菜單中使用“查找”命令搜索特定變量的引用。或者,當每個模塊都含有 Option Explicit 語句時,通過刪除或註釋該變量的聲明,並運行應用程序,可迅速發現變量是否被使用。若該變量被使用,則 Visual Basic 將出錯。若不出錯,則該變量沒被使用


釋放數組內存


Dim ray() As Integer '動態數組。
ReDim ray(10) '分配存儲空間。
Erase ray '釋放數組所用內存。


從內存中卸載窗體或控件 
語法 
Unload object 
object 所在處是要卸載的 Form 對象或控件數組元素的名稱。 
說明 
當所佔內存另有它用,或需要重新設置窗體、控件的屬性爲初始值時,就有必要卸載窗體或控件。 
在卸載窗體前,會發生 Query_Unload 事件過程,然後是 Form_Unload 事件過程。在其中任一過程中設置 cancel 參數爲 True 可防止窗體被卸載。若爲 MDIForm 對象,先發生 MDIForm 對象的 Query_Unload 事件過程,接着是各 MDI 子窗體 的 Query_Unload 事件過程和 Form_Unload 事件過程,最後是 MDIForm 對象的 Form_Unload 事件過程。 
當窗體卸載之後,所有在運行時放到該窗體上的控件都不再是可訪問的。在設計時放到該窗體上的控件將保持不變;但是,當窗體重新加載時,在運行時對這些控件及其屬性的任何更改將丟失。所有對於窗體屬性的更改也將丟失。對窗體上任何控件的訪問會導致窗體重新加載。 
注意 在卸載窗體時,只有顯示的部件被卸載。和該窗體模塊相關聯的代碼還保持在內存中。 
只有在運行時添加到窗體上的控件數組元素才能用 Unload 語句卸載。重新加載被卸載的控件時,其屬性會被重新初始化。


(二)


 什麼是一個高效的軟件?一個高效的軟件不僅應該比實現同樣功能的軟件運行得更快,還應該消耗更少的系統資源。這篇文章彙集了作者在使用VB進行軟件開發時積累下來的一些經驗,通過一些簡單的例子來向你展示如何寫出高效的VB代碼。其中包含了一些可能對VB程序員非常有幫助的技術。在開始之前,先讓我陳清幾個概念。


讓代碼一次成型:在我接觸到的程序員中,有很多人喜歡先根據功能需求把代碼寫出來,然後在此基礎上優化代碼。最後發現爲了達到優化的目的,他們不得不把代碼再重新寫一遍。所以我建議你在編寫代碼之前就需要考慮優化問題。
 把握好優化的結果和需要花費的工作之間的關係:通常當完成了一段代碼,你需要檢查和修改它。在檢查代碼的過程中,也許你會發現某些循環中的代碼效率還可以得到進一步的改進。在這種情況下,很多追求完美的程序員也許會立馬修改代碼。我的建議是,如果修改這段代碼會使程序的運行時間縮短一秒,你可以修改它。如果只能帶來10毫秒的性能改進,則不做任何改動。這是因爲重寫一段代碼必定會引入新的錯誤,而調試新的代碼必定會花掉你一定的時間。程序員應該在軟件性能和開發軟件需要的工作量之間找一個平衡點,而且10毫秒對於用戶來說也是一個不能體會到的差異。
 在需要使用面向對象方法的時候儘量使用它;VB提供的機制不完全支持面向對象的設計和編碼,但是VB提供了簡單的類。大多數人認爲使用對象將導致代碼的效率降低。對於這一點我個人有些不同的意見;考察代碼的效率不能純粹從運行速度的角度出發,軟件佔用的資源也是需要考慮的因素之一。使用類可以幫助你在整體上提升軟件的性能,這一點我會在後面的例子中詳細說明。
  如何提高代碼的運行速度
  1. 使用整數(Integer)和長整數(Long)
  提高代碼運行速度最簡單的方法莫過於使用正確的數據類型了。也許你不相信,但是正確地選擇數據類型可以大幅度提升代碼的性能。在大多數情況下,程序員可以將Single,Double和Currency類型的變量替換爲Integer或Long類型的變量,因爲VB處理Integer和Long的能力遠遠高於處理其它幾種數據類型。
  在大多數情況下,程序員選擇使用Single或Double的原因是因爲它們能夠保存小數。但是小數也可以保存在Integer類型的變量中。例如程序中約定有三位小數,那麼只需要將保存在Integer變量中的數值除以1000就可以得到結果。根據我的經驗,使用Integer和Long替代 Single,Double和Currency後,代碼的運行速度可以提高將近10倍。
  2. 避免使用變體
  對於一個VB程序員來說,這是再明顯不過的事情了。變體類型的變量需要16個字節的空間來保存數據,而一個整數(Integer)只需要2個字節。通常使用變體類型的目的是爲了減少設計的工作量和代碼量,也有的程序員圖個省事而使用它。但是如果一個軟件經過了嚴格設計和按照規範編碼的話,完全可以避免使用變體類型。
  在這裏順帶提一句,對於Object對象也存在同樣的問題。請看下面的代碼:
Dim FSO  或 Dim FSO as object
Set FSO = New Scripting.FileSystemObject
  上面的代碼由於在申明的時候沒有指定數據類型,在賦值時將浪費內存和CPU時間。正確的代碼:
Dim FSO as New FileSystemObject
  3. 儘量避免使用屬性
  在平時的代碼中,最常見的比較低效的代碼就是在可以使用變量的情況下,反覆使用屬性(Property),尤其是在循環中。要知道存取變量的速度是存取屬性的速度的20倍左右。
Dim intCon as Integer
For intCon = 0 to Ubound(SomVar())
Text1.Text = Text1.Text & vbcrlf & SomeVar(intCon)
Next intCon
  下面這段代碼的執行速度是上面代碼的20倍。
Dim intCon as Integer
Dim sOutput as String
For intCon = 0 to Ubound(SomeVar())
sOutput = sOutput & vbCrlf &
SomeVar(intCon)
Next
Text1.Text = sOutput
  4. 儘量使用數組,避免使用集合
  除非你必須使用集合(Collection),否則你應該儘量使用數組。據測試,數組的存取速度可以達到集合的100倍。這個數字聽起來有點駭人聽聞,但是如果你考慮到集合是一個對象,你就會明白爲什麼差異會這麼大。
  5. 展開小的循環體
  在編碼的時候,有可能遇到這種情況:一個循環體只會循環2到3次,而且循環體由幾行代碼組成。在這種情況下,你可以把循環展開。原因是循環會佔用額外的CPU時間。


  6. 避免使用很短的函數
  和使用小的循環體相同,調用只有幾行代碼的函數也是不經濟的--調用函數所花費的時間或許比執行函數中的代碼需要更長的時間。在這種情況下,你可以把函數中的代碼拷貝到原來調用函數的地方。
  7. 減少對子對象的引用
  在VB中,通過使用.來實現對象的引用。例如:
Form1.Text1.Text
  在上面的例子中,程序引用了兩個對象:Form1和Text1。利用這種方法引用效率很低。但遺憾的是,沒有辦法可以避免它。程序員唯一可以做就是使用With或者將用另一個對象保存子對象(Text1)。
' 使用With
With frmMain.Text1
.Text = "Learn VB"
.Alignment = 0
.Tag = "Its my life"
.BackColor = vbBlack
.ForeColor = vbWhite
End With
  或者
' 使用另一個對象保存子對象
Dim txtTextBox as TextBox
Set txtTextBox = frmMain.Text1
TxtTextBox.Text = "Learn VB"
TxtTextBox.Alignment = 0
TxtTextBox.Tag = "Its my life"
TxtTextBox.BackColor = vbBlack
TxtTextBox.ForeColor = vbWhite
  注意,上面提到的方法只適用於需要對一個對象的子對象進行操作的時候,下面這段代碼不正確:
With Text1
.Text = "Learn VB"
.Alignment = 0
.Tag = "Its my life"
.BackColor = vbBlack
.ForeColor = vbWhite
End With
  很不幸的是,我們常常可以在實際的代碼中發現類似於上面的代碼。這樣做只會使代碼的執行速度更慢。原因是With塊編譯後會形成一個分枝,會增加了額外的處理工作。
  8. 檢查字符串是否爲空
  大多數程序員在檢查字符串是否爲空時會使用下面的方法:
If Text1.Text = "" then
End if
  很不幸,進行字符串比較需要的處理量甚至比讀取屬性還要大。因此建議大家使用下面的方法:
If Len(Text1.Text) = 0 then
End if
  9. 注意Next關鍵字後的變量名
  在Next關鍵字後加上變量名會導致代碼的效率下降。我也不知道爲什麼會這樣,只是一個經驗而已。不過我想很少有程序員會這樣畫蛇添足,畢竟大多數程序員都是惜字如金的人。
' 錯誤的代碼
For iCount = 1 to 10
' 執行操作
Next
' 正確的代碼
For iCount = 1 to 10
' 執行操作
Next iCount
  10. 使用數組,而不是多個變量
  當你有多個保存類似數據的變量時,可以考慮將他們用一個數組代替。在VB中,數組是最高效的數據結構之一。


  11. 使用動態數組,而不是靜態數組
  使用動態數組對代碼的執行速度不會產生太大的影響,但是在某些情況下可以節約大量的資源。


    12. 銷燬對象
  無論編寫的是什麼軟件,程序員都需要考慮在用戶決定終止軟件運行後釋放軟件佔用的內存空間。正確的做法是在退出程序前需要銷燬程序中使用的對象。如:
Dim FSO as New FileSystemObject
' 執行操作
' 銷燬對象
Set FSO = Nothing
對於窗體,可以進行卸載:
Unload frmMain
  或
Set frmMain = Nothing
  13. 變長和定長字符串
  從技術上來說,與變長字符串相比,定長字符串需要較少的處理時間和空間。但是定長字符串的缺點在於在很多情況下,你都需要調用Trim函數以去除字符串末的空字符,這樣反而會降低代碼效率。所以除非是字符串的長度不會變化,否則還是使用變長字符串。
  14. 使用類模塊,而不是ActiveX控件
  除非ActiveX控件涉及到用戶界面,否則儘量使用輕量的對象,例如類。這兩者之間的效率有很大差異。
  15. 使用內部對象
  在涉及到使用ActiveX控件和DLL的時候,很多程序員喜歡將它們編譯好,然後再加入工程中。我建議你最好不要這樣做,因爲從VB連接到一個外部對象需要耗費大量的CPU處理能力。每當你調用方法或存取屬性的時候,都會浪費大量的系統資源。如果你有ActiveX控件或DLL的源代碼,將它們作爲工程的私有對象。
  16. 減少模塊的數量
  有些人喜歡將通用的函數保存在模塊中,對於這一點我表示贊同。但是在一個模塊中只寫上二三十行代碼就有些可笑了。如果你不是非常需要模塊,儘量不要使用它。這樣做的原因是因爲只有在模塊中的函數或變量被調用時,VB纔將模塊加載到內存中;當VB應用程序退出時,纔會從內存中卸載這些模塊。如果代碼中只有一個模塊,VB就只會進行一次加載操作,這樣代碼的效率就得到了提高;反之如果代碼中有多個模塊,VB會進行多次加載操作,代碼的效率會降低。
  17. 使用對象數組
  當設計用戶界面時,對於同樣類型的控件,程序員應該儘量使用對象數組。你可以做一個實驗:在窗口上添加100個PictureBox,每個 PictureBox都有不同的名稱,運行程序。然後創建一個新的工程,同樣在窗口上添加100個PictureBox,不過這一次使用對象數組,運行程序,你可以注意到兩個程序加載時間上的差別。
  18. 使用Move方法
  在改變對象的位置時,有些程序員喜歡使用Width,Height,Top和Left屬性。例如:
Image1.Width = 100
Image1.Height = 100
Image1.Top = 0
Image1.Left = 0
  實際上這樣做效率很低,因爲程序修改了四個屬性,而且每次修改之後,窗口都會被重繪。正確的做法是使用Move方法:
Image1.Move 0,0,100,100
  19. 減少圖片的使用
  圖片將佔用大量內存,而且處理圖片也需要佔用很多CPU資源。在軟件中,如果可能的話,可以考慮用背景色來替代圖片--當然這只是從技術人員的角度出發看這個問題。
  20. 使用ActiveX DLL,而不是ActiveX控件
  如果你設計的ActiveX對象不涉及到用戶界面,使用ActiveX DLL。
來源:http://www.mndsoft.com/blog/article.asp?id=430


編譯優化
  我所見過的很多VB程序員從來沒有使用過編譯選項,也沒有試圖搞清楚各個選項之間的差別。下面讓我們來看一下各個選項的具體含義。
  1.P-代碼(僞代碼)和本機代碼
  你可以選擇將軟件編譯爲P-代碼或是本機代碼。缺省選項是本機代碼。那什麼是P-代碼和本機代碼呢?
P-代碼:當在VB中執行代碼時,VB首先是將代碼編譯爲P-代碼,然後再解釋執行編譯好的P-代碼。在編譯環境下,使用這種代碼要比本機代碼快。選擇P-代碼後,編譯時VB將僞代碼放入一個EXE文件中。
  本機代碼:本機代碼是VB6以後才推出的選項。當編譯爲EXE文件後,本機代碼的執行速度比P-代碼快。選擇本機代碼後,編譯時VB使用機器指令生成EXE文件。
  在使用本機代碼進行編譯時,我發現有時候會引入一些莫名其妙的錯誤。在編譯環境中我的代碼完全正確地被執行了,但是用本機代碼選項生成的EXE文件卻不能正確執行。通常這種情況是在卸載窗口或彈出打印窗口時發生的。我通過在代碼中加入DoEvent語句解決了這個問題。當然出現這種情況的機率非常少,也許有些VB程序員從來沒有遇到過,但是它的確存在。
  在本機代碼中還有幾個選項:
  a) 代碼速度優化:該選項可以編譯出速度較快的執行文件,但執行文件比較大。推薦使用
  b) 代碼大小優化:該選項可以編譯出比較小的執行文件,但是以犧牲速度爲代價的,不推薦使用。
  c) 無優化:該選項只是將P-代碼轉化爲本機代碼,沒有做任何優化。在調試代碼時可以使用。
  d) 針對Pentium Pro優化:雖然該項不是本機代碼中的缺省選項,但是我通常會使用該選項。該選項編譯出的可執行程序在Pentium Pro和Pentium 2以上的機器上可以運行得更快,而在比較老的機器上要稍稍慢一些。考慮到現在用Pentium 2都是落伍,所以推薦大家使用該選項。
  e) 產生符號化調試信息:該項在編譯過程中生成一些調試信息,使用戶可以利用Visual C++一類的工具來調試編譯好的代碼。使用該選項會生成一個.pdf文件,該文件記錄了可執行文件中的標誌信息。當程序擁有API函數或DLL調用時,該選項還是比較有幫助的。
  2. 高級優化
  高級優化中的設置可以幫助你提高軟件的速度,但是有時候也會引入一些錯誤,因此我建議大家儘量小心地使用它們。如果在代碼中有比較大的循環體或者複雜的數學運算時,選中高級優化中的某些項會大幅度提升代碼的性能。如果你使用了高級優化功能,我建議你嚴格測試編譯好的文件。
  a) 假定無別名:可以提高循環體中代碼的執行效率,但是在如果通過變量的引用改變變量值的情況下,例如調用一個方法,變量的引用作爲方法的參數,在方法中改變了變量的值的話,就會引發錯誤。有可能只是返回的結果錯誤,也有可能是導致程序中斷運行的嚴重錯誤。
  b) 取消數組綁定檢查、取消整數溢出檢查和取消浮點錯誤檢查:在程序運行時,如果通過這些檢查發現了錯誤,錯誤處理代碼會處理這些錯誤。但是如果取消了這些檢查,發生了錯誤程序就無法處理。只有當你確定你的代碼中不會出現上面的這些錯誤時,你纔可以使用這些選項。它們將使軟件的性能得到很大的提升。
  c) 允許不捨入的浮點操作:選擇該選項可以是編譯出來的程序更快地處理浮點操作。它唯一的缺點就是在比較兩個浮點數時可能會導致不正確的結果。
  d) 取消Pentium FDIV安全檢查:該選項是針對一些老的Pentium芯片設置的,現在看來已經過時了
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章