我有一个计算平均支出的函数。循环从x天到y天计算。作为此函数的一部分,我必须使用特定范围插入数字。但这非常慢。
我读到一种加速代码的方法是将范围读取为值而不是范围,因为每次运行代码时都会通过转到Excel来降低VBA的速度。
这是真的吗?
我目前的代码。
Function AveragePayout(Time As Double, period)
Dim i As Integer
Dim sum As Double
Dim interpolate_surface As Range
Set interpolate_surface = Range("A1", "D4")
If Time < period Then
AveragePayout = 0
Else
For i = 1 To period
interpolated_val = Interpolation(interpolate_surface, 5, Time)
sum = sum + CustomPricer(interpolated_value)
Time = Time - 1
Next i
AveragePayout = sum / period
End If
End Function
我正在考虑将第5行更改为下面的内容,然后在VBA矩阵/数组上运行插值,而不是每个循环都返回到Excel文档(这显然会大大减慢函数的速度:
Set interpolate_surface = Range("A1", "D4").Value2
或者还有其他方法来加速这个循环的运行吗?
非常感谢。
答案 0 :(得分:1)
虽然R.Leruth非常接近,但有一些事情需要详细阐述。
首先,Range
对象较慢的原因是因为您正在处理该值的Object
表示,并且存在绑定到该Range
的事件。因此,计算将运行,需要评估工作表,访问该值必须通过Object
,而不是通过该对象的内存中表示。
此性能下降通常适用于任何Range
操作,性能下降直接与范围的大小相关联。因此,在100个细胞上操作比在1,000,000个细胞上操作更快。
虽然阵列的演奏时间也直接关联,但访问每个值的时间多更快。这是因为值在内存中且易于访问。没有Objects
依赖于数组。这并不意味着数组总是快。我遇到了几分钟或几小时的数组操作实例,因为我认为他们的初始速度是理所当然的。您会注意到阵列的性能下降,但性能下降的速度远远低于
要创建数组,我们使用Variant
类型。请记住Variant
可以是任何东西,所以你必须要小心。一般约定是使用Dim Foo as Variant()
但是接受或返回Variant()
的任何参数必须被赋予Variant()
而不是Variant
(次要差异,对代码的巨大影响) 。因此,我倾向于使用Dim Foo as Variant
。
然后我们可以将范围中的值分配回数组。虽然Foo = Range("A1:B2")
在功能上等同于Foo = Range("A1:B2").Value
,但我强烈建议完全限定。因此,我尽可能不依赖于隐式属性(.Value
是Range
的隐式属性)。
所以我们的代码应该:
Dim Foo as Variant
Foo = SomeRange.Value
Foo
是您的变量,SomeRange
替换为您的范围。
只要您的Interpolate
函数接受array
,就不会出现任何问题。如果Interpolate
函数不接受array
,您可能需要找到另一种解决方法(或编写自己的解决方法)。
要输出数组,我们只需要创建一个与数组大小相同的范围。有不同的方法可以做到这一点。我倾向于选择这种方法:
SomeRange.Resize(UBound(SomeArray, 1) - LBound(SomeArray, 1) + 1, Ubound(SomeArray, 2) - LBound(SomeArray, 2) + 1)
所有这一切都需要一些范围(应该是单个单元格),并通过数组中的列数和数组中的行数来调整该范围。我使用(Ubound - Lbound) + 1
,因为对于基于0的数组,这将返回Ubound + 1
,对于基于1的数组,它将返回Ubound
。它使事情比为同一目的创建If
块更简单。
确保所有这一切的最后一件事是您的Range
变量是完全合格的。请注意,Range("A1:B2").Value
在功能上等同于ActiveSheet.Range("A1:B2").Value
,但同样,依赖隐式调用会很快引入错误。尽可能地挤出那些。如果您需要ActiveSheet
,请使用它。否则,创建一个Worksheet
变量并将该变量指向正确的工作表。
如果你必须使用ActiveSheet
那么Dim Foo as Worksheet : Set Foo = ActiveSheet
比使用ActiveSheet
要好得多(因为ActiveSheet
通常会改变如果你真的不需要它,那么一切都会破裂。
使用数组好运。它们性能正在发生变化,但它们绝不是糟糕编码实践的借口。确保你正确使用它们,并且因为你现在可以而没有引入新的低效率。
答案 1 :(得分:0)
我们通常在VBA中加速宏的工作是减少代码和工作表之间的交互量。
例如:
获取数组中的所有必要值
Dim arr() as Variant
arr = Range("A1:D4")
对待值
...
把它们放回去
Range("A1:D4") = arr
在您的情况下,只需尝试将interpolated_surface
从Range
更改为array
类型。