【问题标题】:Effect of Screen Updating屏幕更新效果
【发布时间】:2012-09-12 15:37:36
【问题描述】:

我一直在测量代码执行时间,以衡量在本地执行脚本和在服务器上执行脚本之间的差异。有一次我忘了禁用screen updating,我很庆幸在仔细考虑之前我对闪光灯不敏感:

当我第一次开始使用VBA 时,我一直认为它只是为了防止最终用户认为他们的 PC 即将崩溃而使用它。当我开始阅读更多关于提高代码效率的内容时,我明白了它的用途,但screen updating 对代码执行时间的影响有多大?

【问题讨论】:

  • 根据您的代码在做什么,将Application.Calculation 设置为“手动”通常也会大大提高速度。对于长时间运行的宏,您通常会同时执行这两种操作。不过不要忘记重置计算 - 这是一个持久设置。
  • Aslo,关闭运行Screenupdating,您可以通过Application.Statusbar = 让用户知道进度。然后设置为False 使其恢复正常。有些人需要看到进展,否则他们认为他们的计算机已冻结并强制关闭所有内容。
  • 我也看过,Excel中默认的数据格式其实是Currency,所以最好尽量使用Currency作为数据值而不是Double,避免数据转换。
  • 附注:您可以不时将DoEvents 放入您的代码中,以防止您的Excel 冻结。我在循环中执行if n mod 5000 then DoEvents 之类的操作以保持 Excel 响应,同时也防止减速。它似乎运作良好......

标签: vba excel optimization


【解决方案1】:

只有在代码与 Excel 交互的方式导致屏幕内容发生更改时,关闭屏幕更新才会对执行时间产生影响。屏幕变化量越大,影响就越大。其他发布的答案恰当地证明了这一点。

其他可能影响执行时间的应用程序设置是计算和事件处理。使用此代码模板作为起点(错误处理程序确保这些属性在 sub 结束时重新打开,即使它出错)

Sub YourSub()
    On Error GoTo EH

    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False

    ' Code here

CleanUp:
    On Error Resume Next
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
Exit Sub
EH:
    ' Do error handling
    Resume CleanUp
End Sub

还有其他技术可以进一步提高执行速度。

最有用的包括

  1. 尽可能避免使用SelectActivateActiveCell/Sheet/Workbook。而是声明和分配变量并引用它们。
  2. 引用大范围时,将 Range 数据复制到变体数组进行处理,然后将结果复制回范围。
  3. 使用Range.SpecialCellsRange.FindRange.AutoFilter 限制引用的单元格数量。

在 SO 上有很多这些技术的例子。

【讨论】:

  • 声明工作簿与活动工作簿如何加快代码速度?
  • @FreeSoftwareServers 速度明智,主要是因为使用 Active*(或 SelectSelection)的代码涉及与 Excel 交互的额外不必要步骤,这都需要时间。对于您的用例:运行一些测试!
【解决方案2】:

如果您想看一个关于为什么ScreenUpdating 很重要的相当激烈的示例,请运行以下代码。在没有ScreenUpdating = false 的情况下,我在 Excel 2011 中运行此交换需要大约 45 倍的时间!这是一个巨大的时间差异。

Sub testScreenUpdating()

    Dim i As Integer
    Dim numbSwitches As Integer
    Dim results As String

    'swap between sheets this number of times
    numbSwitches = 1000

    'keep track of time
    Dim startTime As Double
    startTime = Time

    'swap between sheets 1/2 (need both sheets or this will crash)
    For i = 1 To numbSwitches
        Sheets(1 + (i Mod 2)).Select
    Next i

    'get results
    results = "Screen Updating not disabled: " & Format(Time - startTime, "hh:mm:ss") & " seconds"
    startTime = Time

    'scenario 2 - screenupdating disabled

    Application.ScreenUpdating = False

    'swap between sheets 1/2 (need both sheets or this will crash)
    For i = 1 To numbSwitches
        Sheets(1 + (i Mod 2)).Select
    Next i

    Application.ScreenUpdating = True

    'get results for part two
    results = results & vbCrLf & "Screen Updating IS disabled: " & Format(Time - startTime, "hh:mm:ss") & " seconds"

    'show results
    MsgBox results


End Sub

此外,虽然我们讨论的是提高效率的方法,但另一个关键点是 SelectSelectionActivate 很少(如果有的话)是必要的。当您录制宏时,它总是会使用这些宏,但在极少数情况下您需要在代码中实际使用它们。同样,标题中带有Active 的任何内容(例如ActiveCell)通常都表明您的代码会变慢,因为您可能正在选择单元格。

您几乎总是可以专门引用单元格/工作表并避免选择。例如:

msgbox (Worksheets(1).Range("A1").value) 

无论您当前是否在第一个工作表上,都可以使用。一个常见的新 VBA 错误是执行以下操作:

Worksheets(1).Select
msgbox (Range("A1").value)

这是一个不需要的步骤。

这会显着增加代码运行时间。

【讨论】:

【解决方案3】:

首先我一直在使用 Richie (UK) Post #7 Here 编写的脚本

它只是简单地迭代一个循环来改变一个单元格中 i 的值。我稍微改变了它,让它循环 10,000 次,我执行 10 次以获得样本量。

屏幕更新对我的代码执行速度有何影响?

这些是Screen Updating 被禁用和启用时的执行时长:

Disabled    Enabled
0.61909653  2.105066913
0.619555829 2.106865363
0.620805767 2.106866315
0.625528325 2.102403315
0.625319976 2.0991179
0.621287448 2.105103142
0.621540236 2.101392665
0.624537531 2.106866716
0.620401789 2.109004449

如您所见,当Screen Updating 未被禁用时,执行代码所需的时间几乎是 3.5 倍。

这两个代码都是使用 VB 编辑器中的“运行”按钮执行的,而不是“观看”电子表格。

代码开头和结尾的两行简单代码:

Application.ScreenUpdating = False
Application.ScreenUpdating = True

但是,它们会对您的执行效率产生很大影响!

注意:显然Screen Updating 的优势在这里为许多人所熟知,但这可能对初学者有好处,我发现查看数字很有趣!

【讨论】:

    【解决方案4】:

    关于屏幕更新有一件重要的事情需要了解,我在之前的任何答案中都没有看到。从我自己的测试中,我发现关闭和打开屏幕更新大约需要 15 毫秒(通过 Excel 互操作在 C# 中测试)。如果您将执行任何花费更少时间的事情,请记住这一点。毕竟不要在某个循环中多次打开/关闭屏幕更新。那将是真正的性能杀手。

    如果你想快速使用 C++,还有一个注意事项(你可能不想听到)。它通常比 VBA 快 5 到 10 倍(不要在这里抓到我,这取决于您的实际操作)。

    【讨论】:

      【解决方案5】:

      我知道这是一个旧线程,但是:

      1. 设置ScreenUpdating = true,但记得将其设置回原来的值。
      2. 更改工作簿也会重置ScreenUpdating

      【讨论】:

        猜你喜欢
        • 2022-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多