【问题标题】:User Defined Functions in Excel and Speed IssuesExcel 中的用户定义函数和速度问题
【发布时间】:2010-11-20 11:13:13
【问题描述】:

我有一个使用几乎所有 UDF 的 Excel 模型。比如说,120 列和 400 多行。计算是先垂直然后水平完成的——首先完成第 1 列的所有计算,然后第 1 列的最终输出是第 2 列的输入,依此类推。在每一列中,我调用大约六个或七个 UDF调用其他 UDF。 UDF 通常会输出一个数组。

每个 UDF 的输入是一些变量,一些范围变量,一些双精度数。范围变量在访问其内容之前在内部转换为数组。

我的问题如下,我可以在没有 UDF 的情况下构建 Excel 模型,并且当我运行模拟时,我可以在 X 小时内完成所有计算。当我使用 UDF 时,模拟时间是 3X 小时或更长。 (要回答这个显而易见的问题,是的,我需要使用 UDF,因为如果我想对模型进行小的更改(比如添加另一种资产类型(它是一个财务模型)),则需要将近一天的时间来重新制作模型如果没有 UDF 以适应新的法律/财务结构,使用 UDF 大约需要 20 分钟来适应不同的财务结构。)

无论如何,我已经关闭了屏幕更新,函数中没有复制和粘贴,Variant类型的使用最少,所有数据都包含在一张表中,我之前将所有范围类型变量转换为数组获取内容。

除了获得更快的计算机或同等设备以使 VBA 代码/Excel 文件运行得更快之外,我还能做什么?如果需要进一步说明,请告诉我。

谢谢!

【问题讨论】:

  • 你能在这里发布一个函数吗?还是简化版让我们看看?
  • 哇,我没想到我会这么快得到回复。我会看看我是否可以发布一周中某个时间发生的事情的简化版本。谢谢你的消息!
  • 听起来你可能正处于这样的地步...

标签: excel optimization vba user-defined-functions


【解决方案1】:

几个一般提示。

  1. 使用你的函数,找出真正的瓶颈在哪里。有关 excel 中的use of timer,请参阅此问题。我敢肯定那里有 VBA 分析器......但你可能不需要走那么远。 (注意:首先对一个数据单元格执行此操作...)

  2. 考虑一下您的设计... 400x120 单元格的数据不是很多。它需要几个小时,这一定很痛苦。 (过去,我在等待 1,000 次 VLOOKUPS() 返回后就破解了它)无论如何,也许不是拥有一堆 UDF,为什么不拥有一个简单的子程序 for..each通过范围并做你需要它做的事情。 48,000 个细胞可能需要几秒钟或几分钟。然后,您可以将子例程与用户的按钮或菜单项相关联。

出于兴趣,我快速查看了选项 2 并创建了 MyUDF(),使用子 DoMyUDF() 调用它,因为活动选择对我来说比在每个单元格中都有 UDF 快 10 倍。

Option Explicit



Function MyUDF(myVar As Variant) As Variant
  MyUDF = myVar * 10
End Function

Sub DoMyUDF()
  Dim r As Range
  Dim c As Variant

  Dim t As Single
  t = Timer


  If TypeName(Selection) <> "Range" Then
    Exit Sub
  End If


  Set r = Selection.Cells

  Application.DisplayStatusBar = True

  For Each c In r
    c.Value = MyUDF(c.Value)

    Application.StatusBar = "DoMyUDF(): " & Format(Timer - t, "#0.0000ms")
  Next

  Debug.Print "DoMyUDF(): " & Format(Timer - t, "#0.0000ms")

End Sub

如果您将 MyUDF() 替换为您的 UDF,这可能只会为您节省 4.5 分钟...但您可能还可以构建其他一些经济体。特别是如果您一遍又一遍地重复相同的计算。

【讨论】:

  • 很有趣,我会试试这个。问题是,虽然 120x400 单元格的数据并不多,但我必须进行 30,000 多次模拟。过去,我能够在一小时内完成此操作(没有 UDF),但现在需要 3 小时左右。我会尝试你的建议,看看会发生什么。同时,感谢您抽出时间回复!
  • 您还可以通过将变量数组加载到工作表的 usedrange 中来加快速度,然后用单元格值填充该数组,并将该变量数组作为 usedrange 的值。这比告诉 Excel 一次用一个值填充每个单独的单元格要快得多...
【解决方案2】:

Excel 处理 UDF 的方式存在一个减速错误。每次计算 UDF 时,Excel 都会刷新 VBE 标题栏(您可以看到它闪烁)。对于大量 UDF,这非常慢。 在手动计算模式下绕过非常简单:只需使用 Application.Calculate 之类的东西从 VBA 启动计算(您可以使用 OnKey 捕获 F9 等)。

有关更多详细信息,请参阅http://www.decisionmodels.com/calcsecretsj.htm

【讨论】:

  • 链接+1,链接所有者+1!
【解决方案3】:

您是否考虑过将 UDF(每个输出单元格调用一次)替换为可以在循环中对一系列单元格进行操作的宏?

UDF 设置/拆卸非常慢,任何事情每个 UDF 调用与其他 UDF 共同执行(例如,从重叠输入中读取)都需要额外的努力。

这样做我已经能够将性能提高 10 到 50 倍 - 不到一个月前,我遇到了一个包含 4000 到 30000 个 UDF 调用的电子表格的情况,将它们替换为在几个命名范围上运行的单个宏。

【讨论】:

    【解决方案4】:

    控制和最小化重新计算

    wks.EnableCalculation = False
    

    Application.Calculation = xlCalculationManual
    

    另外,尽量减少 VBA 和工作簿之间的交换。一次将一组单元格读写到数组中会更快

    MyArray = range("B2:B20000") 
    

    而不是逐个单元格(对于每个...)。

    【讨论】:

    • 啊,是的,应该提到,计算设置为手动。不幸的是,计算的性质要求我不能提前将整个范围作为输入。它是这样工作的,func1(A1:A5) 输出到 A6:A10,然后 func2(A6:A10) 输出到 A11:A15,等等。
    • 你也可以使用 Range("xxx").Calculate if required
    【解决方案5】:

    确保从 VBA 而不是从电子表格开始重新计算,请参阅 http://msdn.microsoft.com/en-us/library/aa730921.aspx#Office2007excelPerf_Overview 以及有关 Faster VBA User-Defined Functions 的部分。 那是 应用程序.计算 比在电子表格中按 F9 快得多(在我的测试用例中是 100 倍)。

    【讨论】:

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