【问题标题】:VBA: Iteration speed of variant array vs. typed array vs. non-keyed collectionVBA:变量数组与类型数组与非键集合的迭代速度
【发布时间】:2012-08-26 18:13:03
【问题描述】:

我的项目需要一组用于不同对象的可动态调整大小的数组。一个数组可以包含任意数量的对象,可能是数千个单个类的对象,但不能包含多个类的对象。

大多数情况下,我将遍历数组,因此使用键控集合并不理想。我想我有两个选择:

第一个选项是为每个对象类型开发一个“List”类,其中包含添加对象(和扩展数组)、获取 First 和 Last 索引以及对象计数以及通过索引检索对象的方法(后 4 将包括错误处理,以防数组为空)。

第二个选项是使用 Variant 数据类型开发一个具有相同方法的单个“List”类。显然这是少了很多工作,但我担心速度。使用变体比使用类型对象慢多少?请注意,我将始终在检索时将数组中的变体对象直接转换为类型化变量,例如:

Dim myObject As MyClass
Set myObject = variantList.Get(i)

强制转换是否提高了速度,还是 vba 仍然需要执行与变体相关的所有类型检查?

另外,第二个选项会比使用无键集合更快吗?我读过 Collection 迭代很慢,它们是为查找而设计的。这适用于无键集合,还是仅适用于键值映射集合?

感谢任何可以提供建议的人。

【问题讨论】:

  • 您多久调整一次它们的大小? VBA 允许通过redim preserve 动态调整数组大小,如果这种情况不经常发生和/或您知道新数组的大小,并允许您继续使用数组(而不是集合)。
  • 任何差异是否显着可能取决于您的确切用例。不过似乎很容易测试:您进行过任何比较吗?

标签: arrays vba collections iteration dynamic-arrays


【解决方案1】:

我听从了蒂姆·威廉姆斯的建议,做了一些速度测试。

对于每种类型的集合/数组,我首先添加了 100,000 个“SpeedTester”类对象,它只是一个包含长变量(具有 get/set 属性)的 shell 对象。变量的值是循环索引的值(1 到 100,000 之间)

然后我进行了第二个循环,其中涉及访问集合/数组中的每个对象并将对象的 long 属性值分配给 long 类型的新变量。我对每个方法执行了 3 轮,然后平均了 And 和 get 循环的时间。

结果如下:

Method                      Avg Add Time    Avg Get Time    Total Time
Collection Indexed             0.305          25.498         25.803
Collection Mapped              1.021           0.320          1.342
Collection Indexed For Each    0.334           0.033          0.367
Collection Mapped For Each     1.084           0.039          1.123
Dynamic Array Typed            0.303           0.039          0.342
Static Array Typed             0.251           0.016          0.266

Collection Indexed 和 Collection Mapped 方法涉及将对象保存在集合中。第一个添加没有键,第二个添加了一个键,该键是对象的长属性转换为字符串。然后使用从 1 到 c.Count 的索引在 for 循环中访问这些对象

接下来的两个方法在变量添加到集合的方式上与前两个相同。但是,对于 Get 循环,我没有使用带索引的 for 循环,而是使用了 for-each 循环。

类型化的动态数组是一个包含 SpeedTester 类型数组的自定义类。每次添加变量时,数组的大小都会扩展 1 个插槽(使用 ReDim Preserve)。 get 循环是一个使用从 1 到 100,000 的索引的 for 循环,这对于数组来说是典型的。

最后,输入的静态数组只是一个 SpeedTester 类型的数组,它被初始化为 100,000 个插槽。显然这是最快的方法。奇怪的是,它的大部分速度提升是在获取而不是添加。由于需要调整大小,我会假设其他方法的添加会更慢,而获取每个对象不会比动态数组快。

我对使用 for 循环和 for-each 循环访问索引集合的对象之间的区别感到震惊。映射集合的键查找速度也让我感到惊讶 - 比索引快得多,并且与静态数组以外的所有其他方法相当。

简而言之,它们都是我项目的可行替代方案(除了第一种和最后一种方法,首先是因为它的速度很慢,最后是因为我需要动态调整大小的数组)。我对集合的实际实现方式或动态数组和静态数组之间的实现差异一无所知。任何进一步的见解将不胜感激。

编辑: 测试本身的代码(使用动态数组)

Public Sub TestSpeed()
    Dim ts As Double
    ts = Timer()

    Dim c As TesterList
    Set c = New TesterList

    Dim aTester As SpeedTester

    Dim i As Long
    For i = 1 To 100000
        Set aTester = New SpeedTester
        aTester.Number = i

        Call c.Add(aTester)
    Next i

    Dim taa As Double
    taa = Timer()

    For i = c.FirstIndex To c.LastIndex
        Set aTester = c.Item(i)

        Dim n As Long
        n = aTester.Number
    Next i

    Dim tag As Double
    tag = Timer()

    MsgBox "Time to add: " & (taa - ts) & vbNewLine & "Time to get: " & (tag - taa)
End Sub

对于动态数组类TesterList:

Private fTesters() As SpeedTester

Public Property Get FirstIndex() As Long
    On Error GoTo Leave

    FirstIndex = LBound(fTesters)

Leave:
    On Error GoTo 0
End Property

Public Property Get LastIndex() As Long
    On Error GoTo Leave

    LastIndex = UBound(fTesters)

Leave:
    On Error GoTo 0
End Property

Public Sub Add(pTester As SpeedTester)
    On Error Resume Next

    ReDim Preserve fTesters(1 To UBound(fTesters) + 1) As SpeedTester
    If Err.Number <> 0 Then
        ReDim fTesters(1 To 1) As SpeedTester
    End If

    Set fTesters(UBound(fTesters)) = pTester

    On Error GoTo 0
End Sub

Public Function Item(i As Long) As SpeedTester
    On Error GoTo Leave

    Set Item = fTesters(i)

Leave:
    On Error GoTo 0
End Function

最后是非常简单的 SpeedTester 对象类:

Private fNumber As Long

Public Property Get Number() As Long
    Number = fNumber
End Property

Public Property Let Number(pNumber As Long)
    fNumber = pNumber
End Property

【讨论】:

  • 你能贴出这段代码吗?我真的很惊讶动态数组调整每次迭代的大小与静态大小的数组相比损失很小!我可能会尝试使用比单个 long 数据类型大得多的对象类型。
  • +1 用于花时间对不同方法进行彻底测试。似乎所有这些方法(可能除了索引集合之外)都适合您的项目,因为我的印象是您会使用数千个对象?
  • 蒂姆,是的,我的项目只需要几千个对象,但我认为最好彻底。
猜你喜欢
  • 2013-09-13
  • 2016-12-05
  • 2011-08-22
  • 2011-06-17
  • 2014-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多