怎麼給VBA代碼提速?


高贊回答已經很全面了,我再從程序員的視角補充一些可有可無的東西,平時基本用不到,真的遇到計算能力弱爆了的電腦(大批量的運算配合低級面向移動設備的省電低電壓處理器會導致Excel窗口會直接消失)或者計算大一些的數據幾個小時才能出結果的時候再考慮參考一下。

  1. 把VBA程序,用VB語言轉化為動態鏈接庫.dll,也就是說我們平時運行VBA,會讓VBA每一句自動轉化為機器語言然後執行然後定位到下一句準備轉化為機器語言,這一個操作在轉化為dll文件後就好了。然後工具-引用,選取這個dll,直接在VBA中寫函數名就可以運行,這個折騰適合願意折騰的人來嘗試。
  2. 熟練應用數組,把數據,從單元格里傳進數組,運算完畢傳回單元格,在數組裡運算速度非常快。跟工作表一交互,速度非常明顯的被拖累,不管是賦值還是取值,速度相差大概一個數量級,也就是數組1秒能完成,工作表的交互需要10秒
  3. 熟練應用dictionary,感覺這個就是vba的靈魂。可以讓你迅速定位到一百萬數據滿足要求的某一行。讓你在1毫秒內判斷某個電話有沒有在100萬數據中出現過。
  4. 熟練應用全局變數。有些變數你的程序每次調用都調用同樣的值,讀取同樣的表生成同樣的變數。每次運行都要執行一遍必然浪費時間。在模塊中聲明public變數,可以讓你的變數永駐內存,下次運行程序無需重新定義。(典型示例:結合上一條中提及的dictionary對象。通過dictionary對象對五十萬條數據生成哈希表的過程大概20秒鐘,每次都運行dictionary操作五十萬條數據必然導致每次都很慢,如果聲明全局變數,這20秒只需要運行一次即可,注意這裡原始數據發生更改的同時要對這個dictionary對象進行及時的更新才可以保證數據每次都是最新的)
  5. VBA是單線程語言,只能佔用多核心CPU的某一核。Excel這個程序中的函數本身就擅長把大量的計算分攤給各個CPU。VBA如果能借用Excel的長處必定會讓程序佔用更大的CPU,以便提高計算速度。利用Application.WorksheetFunction可以更快的對你的VBA的許多運算做性能改進,還是得好好學公式,excel提供了很全面的數學公式,應該說我們用到的東西基本不用自己編了
  6. (待驗證, 推測這些內置矩陣運算會很高效)還要熟練使用ctrl+shift+enter數組及excel自帶數組公式,雖然我也不知道這會不會提高太大效率,我沒試過,可能很多R語言中常用的功能Excel都有提供的方式,只是沒幾個人用excel到這個程度所以沒幾個人會,所以用到這個程度的人也都被告知excel不能做這個了。
  7. 任務管理器-&>詳細信息-&>Excel.exe-&>右鍵單擊-&>設置優先順序-&>實時,這樣就不用擔心自己本身電腦計算能力有限而別的程序又向excel搶CPU和內存資源了。
  8. 什麼變數就聲明什麼類型。幾千的數值就integer,幾萬就long,不要不聲明數據類型,否則內存分配嚴重不合理會導致速度過慢。

其他想到再說


  • 不需要計算的時候, 關計算。(Application.Calculation = xlCalculationManual)。中間需要calculate的話,可以用sheet.calculate range.calculate 做局部計算。
  • 運行關screen update (Application.ScreenUpdating = False)
  • 運行關event (Application.EnableEvents = False)
  • 減少寫入單元格的次數,盡量批量寫入,loop盡量在array裡面做。例子:

"不要用循環將數據加到數組裡面去,這樣速度跟在表裡面做處理沒有什麼區別
ReDim arrRng(3 To FinalRow, 5 To FinalColumn)
ReDim arrISO(7 To FinalRow, 1 To colISO.Count)
For r = 3 To FinalRow
For c = 5 To FinalColumn
arrRng(r, c) = Cells(r, c)
Next c
Next r

"一次處理寫入和輸出
arrRng = .Range("A1:E1000")
Range("F1").Resize(UBound(arrRng), UBound(arrRng, 2)) = arrRng

  • Filter 數組,join然後用instr 搜查速度極快.
  • 不用select,盡量避免activate。
  • Copy直接可以用 range(a).value=range(b).value, 如果value不是日期,還可以用value2代替 value。配合使用resize設定目標單元格區域比較方便,例子:

Set S = Sheets(1).Range("A9:C9")
Sheets(2).Range("B1").Resize(S.Rows.Count, _
S.Columns.Count).Value2 = S.Value2

  • 盡量用autofilter, advancedfilter, find 代替循環。 用循環的話,for each item in object 型的循環 又比其他循環要好一些。

"慢
For i = 1 to 1000
if Cells(i,1) = 1 then Cells(i,2).Value = 2
Next i

"快 For...Each配合offset 用,就可以處理對應單元格前後左右的數據
Dim tmpCell as Range
For Each tmpCell in Range("A1:A1000")
if tmpCell=1 then tmpCell.offset(0,1) = 2
next i

"更快 用countif先確定Loop多少次
Dim L As Integer
Dim R As Range
Dim I As Integer
L = WorksheetFunction.CountIf(Columns(1), 1)
Set R = Range("A1")
For i = 1 To L
Set R = Columns(1).Find(What:=1, After:=R, _
LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=True)
R.offset(0,1) = 2
Next i

  • 預先排序在做搜索(match, vlookup),速度會快很多。
  • Define 變數種類
  • Excel 的自動Table計算和操作都比較慢。

    暫時想到這麼多。1,2,3 記得最後程序結束的時候要開回來。還有error 處理用標籤的話,標籤內也要開回來。


數組是個好東西,要善用數組,減少數組redim次數。在內存無憂的情況下,可以使用固定數組定義方法。一切處理過程都放在數組中進行

另外可以在VBA過程程序的首尾分別加下面兩行代碼

application.ScreenUpdating=False

application.ScreenUpdating=true

運行程序的時候關閉屏幕刷新,可以加快速度。


但現在的硬體, 對數據類型的浪費,應該可以忽視了


推薦閱讀:

Excel VBA進階怎麼學,感覺市面上的書都是入門型的?
自學 VBA 到中等水平一般需要多久?
需要買一本execl vba 有沒有推薦的?
一個word文檔裡邊有很多內容是Access資料庫里的內容,如何能自動綁定到資料庫?
對經濟管理系學生學習編程知識有哪些建議?

TAG:MicrosoftExcel | 面向對象編程 | VBA |