Arrays 通过将范围存储为数组来优化VBA循环?
我有一个计算平均支出的函数。循环从x天向下计算到y天。作为这个函数的一部分,我必须使用一个特定的范围来插值一个数字。然而,这是非常缓慢的 我读到一种加速代码的方法是将范围作为值来读取,而不是将范围作为值来读取,因为每次运行代码时,VBA都会转到Excel来减慢速度 这是真的吗 我现在的代码Arrays 通过将范围存储为数组来优化VBA循环?,arrays,vba,performance,loops,for-loop,Arrays,Vba,Performance,Loops,For Loop,我有一个计算平均支出的函数。循环从x天向下计算到y天。作为这个函数的一部分,我必须使用一个特定的范围来插值一个数字。然而,这是非常缓慢的 我读到一种加速代码的方法是将范围作为值来读取,而不是将范围作为值来读取,因为每次运行代码时,VBA都会转到Excel来减慢速度 这是真的吗 我现在的代码 Function AveragePayout(Time As Double, period) Dim i As Integer Dim sum As Double
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
或者,是否有其他方法来加速此循环的运行
非常感谢 在VBA中,我们通常用来加速宏的是减少代码和工作表之间的交互量 例如:
Dim arr() as Variant
arr = Range("A1:D4")
...
Range("A1:D4") = arr
在您的情况下,只需尝试将
插值曲面
从范围
更改为数组
类型。虽然R.Leruth非常接近,但有几件事需要详细说明
首先,范围
对象速度较慢的原因是您正在处理该值的对象
表示,并且存在绑定到该范围
的事件。因此,将运行计算,需要评估工作表,并且访问值必须通过对象
,而不是通过该对象的内存表示
这种性能下降通常适用于任何范围
操作,性能下降与范围大小直接相关。因此,在100个单元上运行比在1000000个单元上运行快得多
虽然数组的性能时间也是直接关联的,但访问每个值的速度要快得多。这是因为这些值位于内存中,易于访问。数组中没有可依赖的对象。这并不意味着数组总是快。我遇到过阵列操作需要很多分钟或小时的情况,因为我认为它们的初始速度是理所当然的。您会注意到阵列的性能下降,但性能下降的速率要低得多
要创建数组,我们使用变量
类型。请记住,变体可以是任何东西,因此您必须稍微小心。一般惯例是使用Dim Foo作为Variant()
,但是任何接受或返回Variant()
的参数都必须指定Variant()
,而不是Variant
(微小的差异,对代码的巨大影响)。因此,我倾向于使用Dim Foo作为变量
然后我们可以将值从一个范围分配回数组。虽然Foo=Range(“A1:B2”)
在功能上等同于Foo=Range(“A1:B2”)。Value
,但我强烈建议完全限定。因此,我没有尽可能多地依赖隐式属性(.Value
是范围的隐式属性)
因此,我们的准则应该是:
其中,Foo
是您的变量,SomeRange
替换为您的范围
只要Interpolate
函数接受数组
,这应该不会引起任何问题。如果插值
函数不接受数组
,您可能需要找到其他解决方法(或编写自己的方法)
要输出数组,我们只需要创建一个与数组大小相同的范围。有不同的方法可以做到这一点。我倾向于选择这种方法:
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(“A1:B2”).Value
在功能上等同于ActiveSheet.Range(“A1:B2”).Value
,但依赖隐式调用会很快引入bug。尽可能多地把它们压扁。如果需要ActiveSheet
,请使用它。否则,创建一个工作表
变量,并将该变量指向正确的工作表
如果您必须使用ActiveSheet
,那么Dim Foo as Worksheet:Set Foo=ActiveSheet
比只使用ActiveSheet
要好得多(因为ActiveSheet
通常在您确实不需要时会更改,然后一切都会中断
使用数组很幸运。它们会改变性能,但它们绝不是不好的编码实践的借口。请确保正确使用它们,并且不会因为现在可以这样做而引入新的低效率问题。如果我没记错的话,在循环过程中,范围将绑定到excel工作表,从而降低速度(你可能知道)。通过读取值并循环查看,您是否看到了任何改进。您寻求性能改进还有其他原因吗???根本不是答案,但您应该学会使用选项Explicit
!这样可以避免在一行上使用插值
,在下一行上使用插值
。(最终,这可能比加速宏提供更多的价值:)@RJ-AVariant
根据定义,几乎是任何类型的。因此,它可以承担
Range("A1:D4") = arr
Dim Foo as Variant
Foo = SomeRange.Value