Excel中的用户定义函数和速度问题

时间:2009-08-18 03:15:42

标签: excel optimization vba user-defined-functions

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

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

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

在任何情况下,我都关闭了屏幕更新,在函数中没有复制和粘贴,Variant类型的使用是最小的,所有数据都包含在一个工作表中,我将所有范围类型变量转换为数组之前得到内容。

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

谢谢!

5 个答案:

答案 0 :(得分:4)

一些一般提示。

  1. 掌握你的功能,找出瓶颈所在的位置。请参阅此问题,了解excel中的use of timer。我确定那里有VBA个人资料......但你可能不需要那么远。 (注意:首先使用一个数据单元执行此操作...)

  2. 想想你的设计... 400x120的数据单元格很多。因为花费数小时必须是痛苦的。 (过去我在等待了一千分钟的VLOOKUPS()返回之后我已经破解了它)无论如何也许没有拥有一堆UDF,为什么没有一个简单的子程序{ {1}}通过范围并完成您需要它做的事情。 48,000个细胞可能需要几秒钟或几分钟。然后,您可以将子例程与用户的按钮或菜单项相关联。

  3. 出于兴趣,我快速浏览了选项2并创建了MyUDF(),使用子for..each来调用它,因为活动选择对我来说比在每个单元格中使用UDF快10倍

    DoMyUDF()

    如果用你的UDF替换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 ,这可能只能节省4.5分钟......但是你可以建立一些其他经济体。特别是如果你一遍又一遍地重复相同的计算。

答案 1 :(得分:3)

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

请参阅http://www.decisionmodels.com/calcsecretsj.htm了解更多详情。

答案 2 :(得分:3)

您是否考虑过使用宏替换UDF(每个输出单元调用一次),该宏可以在循环中的一系列单元格上运行?

UDF设置/拆除非常慢,并且每个UDF调用的任何与其他UDF(例如从重叠输入读取)共同变得额外费力。

我已经能够提高性能10-50倍 - 这种情况不到一个月前涉及一个带有4000-30000 UDF调用的电子表格,用一个在一些命名范围上运行的宏替换它们。

答案 3 :(得分:2)

使用

控制并最小化重新计算
wks.EnableCalculation = False

Application.Calculation = xlCalculationManual

此外,最小化VBA和工作簿之间的交换。一次读取和写入一组单元格到阵列

更快
MyArray = range("B2:B20000") 

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

答案 4 :(得分:2)

确保您从VBA而非电子表格中启动重新计算,请参阅http://msdn.microsoft.com/en-us/library/aa730921.aspx#Office2007excelPerf_Overview 以及关于更快的VBA用户定义函数的部分。 那是   Application.Calculate 比在电子表格中按F9要快得多(在我的测试案例中为100次)。