Arrays 通过将范围存储为数组来优化VBA循环?

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

我有一个计算平均支出的函数。循环从x天向下计算到y天。作为这个函数的一部分,我必须使用一个特定的范围来插值一个数字。然而,这是非常缓慢的

我读到一种加速代码的方法是将范围作为值来读取,而不是将范围作为值来读取,因为每次运行代码时,VBA都会转到Excel来减慢速度

这是真的吗

我现在的代码

    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-A
    Variant
    根据定义,几乎是任何类型的。因此,它可以承担
    Range("A1:D4") = arr
    
    Dim Foo as Variant
    Foo = SomeRange.Value