2012-08-31 10 views
10

私のプロジェクトでは、オブジェクトごとに動的にサイズ変更可能な配列が必要です。配列は、1つのクラスの任意の数のオブジェクトを保持できますが、複数のクラスのオブジェクトは保持できません。VBA:バリアント配列と型付き配列の比較速度とキー以外のコレクションの比較

ほとんど私は配列を反復処理するので、キー付きコレクションの使用は理想的ではありません。

最初のオプションは、オブジェクトの追加(および配列の拡張)、最初のインデックスと最後のインデックスの取得、およびオブジェクトカウントのメソッドを使用して、各オブジェクトタイプの 'List'クラスを開発することです。インデックスでオブジェクトを取得します(後者の4つは、配列が空の場合のエラー処理を含みます)。

第2のオプションは、バリアントデータ型を使用して、同じメソッドで単一の 'List'クラスを開発することです。明らかにこれははるかに少ない仕事ですが、私はスピードに懸念しています。タイプされたオブジェクトよりもバリアントを使用するのがどれほど遅いですか?ラ、私は常に検索時に入力された変数に直接配列の変異型のオブジェクトをキャストされることに注意してください:

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

は、速度を向上させるキャスティングない、またはVBAは、まだすべての型チェックに関連付けられているを実行する必要がありませんバリアント?

また、この2番目のオプションは、キー以外のコレクションを使用するよりも高速ですか?私はコレクションの反復が遅い、ルックアップのために設計されていることを読んだ。非キー付きコレクション、またはキー値マッピングされたコレクションのみに適用されますか?

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

+0

どのくらいの頻度でサイズを変更していますか? VBAでは、これが頻繁に起こらない場合や、新しい配列のサイズを知っていて、(コレクションではなく)配列を使い続ける場合、 'redim preserve'を使って動的配列のサイズ変更を行うことができます。 – enderland

+1

差異が重要かどうかは、正確なユースケースによります。しかし、それは非常に簡単にテストするように思える:任意の比較を実行している? –

答えて

14

私はティム・ウィリアムスのアドバイスに従い、スピードテストを行った。

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

次に、コレクション/配列内の各オブジェクトにアクセスし、そのオブジェクトの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 

方法コレクションインデックス付きとマップされたコレクションは、コレクション内のオブジェクトを保持して関与しました。最初のキーはキーなしで追加され、2番目のキーにはオブジェクトの長いプロパティが文字列に変換されたキーが追加されました。これらのオブジェクトは、1からc.Countのインデックスを使用してforループでアクセスされました。

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

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

最後に、タイプされた静的配列は単純に10000スロットで初期化されたSpeedTester型の配列でした。明らかに、これは最速の方法です。不思議にも、速度向上の多くは、追加ではなく取得であった。私は、サイズ変更の必要性のために、他のメソッドのほうが遅くなると思っていましたが、各オブジェクトを取得するのは動的配列よりも速くないでしょう。

私は、forループとfor-eachループを使用してインデックス付きコレクションのオブジェクトにアクセスすることの違いに驚きました。マッピングされたコレクションの主要なルックアップスピードにも驚いていました。インデックス作成よりはるかに高速で、静的配列以外のすべてのメソッドに匹敵しました。

要するに、それらは私のプロジェクトにとって実行可能なすべての選択肢です(最初と最後のメソッドを除いて、最初は遅いため、最後は動的にサイズ変更可能な配列が必要です)。コレクションが実際にどのように実装されているか、または動的配列と静的配列の実装の違いについては、私は全く知りません。それ以上の洞察があれば幸いです。

EDIT:

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オブジェクトクラス:(動的配列を使用して)テスト自体

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 fNumber As Long 

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

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

このコードを投稿できますか?私は実際に静的なサイズの配列に対して動的な配列の各反復のサイズを変更すると、そこにほとんど損失がないことに驚いています!私はこれを、単一の 'long'データ型よりもかなり大きいオブジェクト型で試してみるかもしれません。 – enderland

+0

+1異なるアプローチの徹底的なテストをする時間を取ってください。あなたが何千ものオブジェクトで作業しているような印象を受けたので、これらのメソッドのすべて(おそらくインデックス付きコレクションを除く)のように見えるはずです。 –

+0

Tim、はい私のプロジェクトでは、オブジェクトが数千点しか必要ありませんが、徹底的に考えるのが一番良いと思いました。 – Swiftslide