15

私のプロジェクトでは、さまざまなオブジェクトに対して動的にサイズ変更可能な配列が多数必要です。配列は、単一のクラスの任意の数のオブジェクト、場合によっては数千のオブジェクトを保持できますが、複数のクラスのオブジェクトを保持することはできません。

ほとんどの場合、配列を反復処理するため、キー付きコレクションの使用は理想的ではありません。私には2つの選択肢があると思います。

最初のオプションは、オブジェクトを追加(および配列を展開)し、最初と最後のインデックスとオブジェクト数を取得し、インデックスごとにオブジェクトを取得するためのメソッドを使用して、オブジェクトタイプごとに「リスト」クラスを開発することです(後者の4つは配列が空の場合のエラー処理を含めます)。

2番目のオプションは、Variantデータ型を使用して、同じメソッドで単一の「List」クラスを開発することです。明らかにこれははるかに少ない作業ですが、私は速度について心配しています。型付きオブジェクトよりもバリアントを使用するのはどれくらい遅いですか?取得時に、配列内のバリアントオブジェクトを型付き変数に直接キャストすることに注意してください。

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

キャストによって速度が向上しますか、それともvbaでバリアントに関連するすべてのタイプチェックを実行する必要がありますか?

また、この2番目のオプションは、キーなしのコレクションを使用するよりも高速ですか?コレクションの反復は遅く、ルックアップ用に設計されていることを読みました。これは、キーが設定されていないコレクションに適用されますか、それともキーと値がマップされたコレクションにのみ適用されますか?

アドバイスを提供できる人に感謝します。

4

1 に答える 1

20

私はティム・ウィリアムズのアドバイスに従い、いくつかの速度テストを行いました。

コレクション/配列のタイプごとに、最初にクラス「SpeedTester」の100,000個のオブジェクトを追加しました。これは、長い変数(get / setプロパティを持つ)を保持する単純なシェルオブジェクトでした。変数の値は、ループインデックスの値(1〜100,000)でした。

次に、コレクション/配列内の各オブジェクトにアクセスし、オブジェクトのlongプロパティ値をlong型の新しい変数に割り当てるという2番目のループを実行しました。メソッドごとに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

CollectionIndexedおよびCollectionMappedのメソッドには、コレクション内のオブジェクトの保持が含まれていました。1つ目はキーなしで追加され、2つ目はオブジェクトのlongプロパティが文字列に変換されたキーで追加されました。次に、これらのオブジェクトは、1からc.Countまでのインデックスを使用してforループでアクセスされました。

次の2つの方法は、変数がコレクションに追加された方法で最初の2つと同じでした。ただし、Getループでは、インデックス付きのforループを使用する代わりに、for-eachループを使用しました。

型指定された動的配列は、SpeedTester型の配列を含むカスタムクラスでした。変数が追加されるたびに、配列のサイズが1スロットずつ拡張されました(ReDim Preserveを使用)。get-loopは、配列で一般的な1〜100,000のインデックスを使用するforループでした。

最後に、型指定された静的配列は、100,000スロットで初期化されたSpeedTester型の配列でした。明らかに、これが最速の方法です。不思議なことに、その速度の向上の多くは、追加ではなく取得にありました。サイズを変更する必要があるため、他のメソッドの追加は遅くなると思いましたが、各オブジェクトの取得は動的配列よりも速くはありません。

インデックス付きコレクションのオブジェクトにアクセスするために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
于 2012-09-01T08:32:25.157 に答える