在VBA Excel中优化双循环的性能

我很想知道在双循环中执行一组指令的最快方式,以循环遍历二维单元格范围。 我的代码将是这样的:

Sub Test() For i = 1 To 1000000 For j = 1 To 10 'It can be more than 10 'I put a set of instructions here Next j Next i End Sub 

例如,假设我编写了一个简单的代码来实现这样一个下面的任务:

 Sub Test1() T0 = Timer For i = 1 To 1000000 For j = 1 To 10 Cells(i, j) = j + Rnd() Next j Next i InputBox "The runtime of this program is ", "Runtime", Timer - T0 End Sub 

我在我的机器上运行了程序Test1 ,完成了179.6406秒。 由于我没有声明variables(i和j),所以Test1正在运行那些默认为Varianttypes的variables。 然后,我将一行添加到Test1中 ,将variables声明为Long,因为VBA已针对Long进行了优化。 新的程序Test2使我的机器运行时间缩短到168.7539秒(差不多快了11秒)。

为了提高Test2的性能,我closures了Test2代码运行时不需要的Excelfunction。

 Sub Test3() Dim i As Long, j As Long T0 = Timer ScreenUpdateState = Application.ScreenUpdating StatusBarState = Application.DisplayStatusBar CalcState = Application.Calculation EventsState = Application.EnableEvents DisplayPageBreakState = ActiveSheet.DisplayPageBreaks Application.ScreenUpdating = False Application.DisplayStatusBar = False Application.Calculation = xlCalculationManual Application.EnableEvents = False ActiveSheet.DisplayPageBreaks = False For i = 1 To 1000000 For j = 1 To 10 Cells(i, j) = j + Rnd() Next j Next i Application.ScreenUpdating = ScreenUpdateState Application.DisplayStatusBar = StatusBarState Application.Calculation = CalcState Application.EnableEvents = EventsState ActiveSheet.DisplayPageBreaks = DisplayPageBreaksState InputBox "The runtime of this program is ", "Runtime", Timer - T0 End Sub 

以上方法有助于提高Test2Test3在我的机器上完成96.13672秒的性能。 所以我想知道是否有一个更有效的方法来做到这一点。 任何人都可以拿出更快的版本? 如果可能的话,甚至避免双循环过程。

我使用了一个内部的VBAarrays,并在10秒内运行:

 Sub QuickTest() Dim v(1 To 1000000, 1 To 10) As Double For i = 1 To 1000000 For j = 1 To 10 v(i, j) = j + Rnd Next j Next i Range("A1:J1000000") = v End Sub 

注意:

  • 使用内部VBA数组可以避免多次触碰工作表
  • 该数组可以一步转换到工作表单元而不是循环。

编辑#1:

考虑这个续集:

 Sub QuickTest2_The_Sequel() Dim i As Long, j As Long, m As Long, n As Long Range("K1") = Evaluate("Now()") m = 10 n = 1000000 ReDim v(1 To n, 1 To m) As Double For i = 1 To n For j = 1 To m v(i, j) = j + Rnd Next j Next i Range("A1:J" & n) = v Range("K2") = Evaluate("Now()") End Sub 

这里我们使用单元格K1K2来logging开始和结束时间。 我们也使用ReDim而不是Dim来“paramatize”的限制:

在这里输入图像说明

 Sub Checkthis() starttime = Format(Now(), "hh:mm:ss") Dim i, j As Long Dim a(1000000, 10) As Long For i = 1 To 1000000 For j = 1 To 10 a(i, j) = j + Rnd Next j Next i Range("A1:J1000000") = a endtime = Format(Now(), "hh:mm:ss") Elapsed = DateDiff("s", starttime, endtime) MsgBox ("Finished in " & Elapsed & " seconds") End Sub 

尝试下面的代码,它检查列E中的最后一行,循环停在那里。 您可以将“E”修改为您需要的任何列。

 Sub Test3() Dim i As Long, j As Long, LastRow As Long T0 = Timer ' in case Column E allways has data for each row >> you can modify to your needed Column LastRow = Cells(Rows.count, "E").End(xlUp).row ScreenUpdateState = Application.ScreenUpdating StatusBarState = Application.DisplayStatusBar CalcState = Application.Calculation EventsState = Application.EnableEvents DisplayPageBreakState = ActiveSheet.DisplayPageBreaks Application.ScreenUpdating = False Application.DisplayStatusBar = False Application.Calculation = xlCalculationManual Application.EnableEvents = False ActiveSheet.DisplayPageBreaks = False For i = 1 To LastRow For j = 1 To 10 Cells(i, j) = j + Rnd() Next j Next i Application.ScreenUpdating = ScreenUpdateState Application.DisplayStatusBar = StatusBarState Application.Calculation = CalcState Application.EnableEvents = EventsState ActiveSheet.DisplayPageBreaks = DisplayPageBreaksState InputBox "The runtime of this program is ", "Runtime", Timer - T0 End Sub