2

私は過去数年間に少しずつ変更および修正されたプロジェクトを持っており、あるコードモジュールから次のコードモジュールへの多くは標準化されていません。ある場合には、私はScripting.Dictionaryオブジェクトを使用し、他の場合には、私はCollectionオブジェクトを持っています。これらのそれぞれについて、カウント(つまりFor i = 1 to Obj.Count)による反復と、 。による反復がありますFor...Each

これらのケースのできるだけ多くに同じロジックを適用して、将来の変更をよりシームレスに行えるようにしたいと思いますが、どの方法が最適かはわかりません。(いくつかの特定のケースがいずれかのメソッドに対して作成されたと思いますが、一部のコードは使用可能なメソッドのいずれかを使用できると確信しています。)

これらの方法のどれが最も効果的かを判断するのに役立つテストサブを作成しようとしましたが、結果はやや一貫性がありませんでした。全体として、aのそれぞれをループする方が速いように見えますがItemDictionary場合によっては、aのそれぞれをループする方が速いことがテストで示さItemれましたCollection。差異は、おそらく、任意の時点でシステムで発生している他のすべてに依存します。

ループ内の他のすべてが同じであると仮定して、どの方法が一貫して最速であるかについて、誰かが決定的な答えを持っているかどうか疑問に思いました。または、この質問に自分で答えられるように、結果を返すときにテストサブの一貫性を高める方法はありますか?

私が思いついたテストコード:

Option Explicit

Sub Test_Dictionary_Iteration_Speed()

Dim Coll1 As New Collection, Coll2 As New Collection
Dim Dict1 As New Scripting.Dictionary, Dict2 As New Scripting.Dictionary, Dict3 As New Scripting.Dictionary
Dim i As Integer, j As Integer, l As Integer
Dim StartTime As Single, StopTime As Single
Dim v As Variant
Dim Obj As TestObject 'A custom Class that has only one member variable, MainVal, and no functions/subs

    For i = 0 To 32766
        Set Obj = New TestObject
        Obj.MainVal = i
        Dict1.Add CStr(i), Obj
        Dict2.Add CStr(i), Obj
        Dict3.Add CStr(i), Obj
        Coll1.Add Obj, CStr(i)
        Coll2.Add Obj, CStr(i)
    Next i

    StartTime = Timer()

    For j = 0 To Dict1.Count - 1
        l = CInt(Dict1(CStr(j)).MainVal)
        Set Obj = Dict1(CStr(l)) 'Do something useful within the loop
        Set Obj = Nothing
        Dict1.Remove CStr(l)
    Next j

    StopTime = Timer()

    Debug.Print "Dict1 for x to y: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Dict2.Items
        l = CInt(v.MainVal)
        Set Obj = Dict2(CStr(l))
        Set Obj = Nothing
        Dict2.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Dict2 for each item: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Dict3.Keys
        l = CInt(Dict3(v).MainVal)
        Set Obj = Dict3(CStr(l))
        Set Obj = Nothing
        Dict3.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Dict3 for each key: " & StopTime - StartTime

    '---------- Division between Dictionary and Collection

    StartTime = Timer()

    For j = 0 To Coll1.Count - 1
        l = CInt(Coll1(CStr(j)).MainVal)
        Set Obj = Coll1(CStr(l))
        Set Obj = Nothing
        Coll1.Remove CStr(l)
    Next j

    StopTime = Timer()

    Debug.Print "Coll1 for x to y: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Coll2
        l = CInt(v.MainVal)
        Set Obj = Coll2(CStr(l))
        Set Obj = Nothing
        Coll2.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Coll2 for each item: " & StopTime - StartTime

    Debug.Print vbNewLine & "-----" & vbNewLine   


End Sub

出力の実際の例。「最良の」オプションが常に同じであるとは限らないことを示しています。

xからyのDict1
:0.2011719各アイテムのDict2:
0.1738281各キーのDict3:0.2167969
xからyのColl1: 0.2050781
各アイテムのColl2:0.1386719


xからyのDict1
:0.1875各アイテムのDict2:
0.171875各キーのDict3:0.234375
xからyのColl1: 0.2050781
各アイテムのColl2:0.1542969


xからyのDict1:0.25
各アイテムのDict2:
0.21875各キーのDict3:0.265625
xからyのColl1: 0.234375
各アイテムのColl2:0.171875


xからyのDict1: 0.265625
各アイテムのDict2:0.203125
各キーの
Dict3:0.296875 xからyのColl1:
0.234375各アイテムのColl2:0.21875


xからyのDict1
:0.265625各アイテムのDict2:
0.1875各キーのDict3:0.234375
xからyのColl1: 0.203125
各アイテムのColl2:0.15625


xからyのDict1: 0.28125
各アイテムのDict2:0.1875
各キーのDict3:0.25
xからyのColl1: 0.234375
各アイテムのColl2:0.1875


xからyのDict1 :0.28125
各アイテムのDict2:0.21875
各キーのDict3:0.328125
xからyのColl1:
0.234375各アイテムのColl2:0.234375

4

1 に答える 1

2

特定のステートメントまたはプロシージャの実行時間が問題であるという事実を知らない限り、最適化に人の時間を無駄にするべきではありません。最初に設計とデバッグを行い、次に物事が遅すぎると思われる場合(おそらくそうではないでしょう)、プロファイリングしてから最適化します(実行時間は通常、思ったものとはまったく異なる場所で無駄になります)。

構成は素晴らしく、For Each簡潔で整頓されています。これを使用しない唯一の理由は、ループしているコレクションからアイテムを削除する場合です。次に、特定のアイテムをスキップするリスクがあります。アイテムの削除を計画している場合は、インデックスを逆方向にループします。

于 2012-06-20T15:57:02.990 に答える