C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

VBAのリスト問題3

こちらの続きです。
VBAのリスト問題2 - C#ATIA

あぁ配列試す前に、書くべきでした・・・・。
ところで、お気付きでしょうか?(何が?)


コレクションの結果(やる度に結果は変わります)のLong型を
扱ったものですが、

**コレクション-キー無し**
Long 代入:0.0390625s
呼出:24.4453125s

**コレクション-キー付き**
Long 代入:0.890625s
呼出:0.3671875s

キー無しは代入に比べ、呼び出しが非常に遅いのですが、
キー付きは呼び出しの方が、代入より速いです。
逆転しています。

ここからは想像です!
何処かにメモリ確保の仕方とかの説明があれば良いのですが、
見つけられませんでした。(きっと何処かには有ると思います)

僕の中ではキー無しの場合は、こんな感じのイメージを持っています。

んーセンス無いな。 まばらに描いているのはメモリ上のアチコチに
散らばっているイメージです。
各Itemは、データの中身(本当は中身のアドレスだと思う)と次の
インデックスがセットになって情報を持っているような気がします。

キー付きの場合は、こんなイメージです。

各データはバラバラに配置されているのですが、キーについては
別の所に連続しておいてあるような気がします。

では確認。

こんな感じでサンプルを作りました。
もう、小出しでは無くドバっと。

'vba

Option Explicit

Private sw_ As StopWatch

Sub collection_key_test()

    Dim Count As Long
    Count = 30000
    
    'コレクション作成
    Dim lst As Collection
    Set lst = create_collection(Count, False)
    
    'キー無し
    Set sw_ = New StopWatch
    sw_.start
    Debug.Print "--キー無し--"
    
    Call non_key_test(lst)
    Call sw_.total("Total" & ":")

    'コレクション作成
    Set lst = create_collection(Count, True)

    'キー付き
    sw_.start
    Debug.Print "--キー付き--"
    
    Call use_key_test(lst)
    Call sw_.total("Total" & ":")
    
End Sub


Private Sub use_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
    For i = 0 To lst.Count - 1
        v = lst.Item(CStr(i))
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub


Private Sub non_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
    For i = 1 To lst.Count
        v = lst(i)
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub

それぞれ30000回Forを行い、1000回毎に時間を書き出します。

比較しやすいように、した結果がこちらです。

キー付きは最初から最後まで、ほぼ一定の時間で読み出せていますが、
キー無しは後半になる程、遅くなっています。
(For Eachも最初から最後まで、ほぼ一定でした)

この結果からすると、上で示した画像のイメージに近い事が分かります。
つまり30000個目にアクセスする為には、29999個目まで辿らないと
30000個目を見つける事が出来ない と言う事です。

じゃぁ試しに、step -1でforしたらどうなるのか?
non_key_test関数のみを書き換えます。

Private Sub non_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
'    For i = 1 To lst.Count
    For i = lst.Count To 1 Step -1
        v = lst(i)
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub

実行結果はこんな感じです。

--キー無し--
30000:0.014892578125s
29000:0.656005859375s
28000:0.630126953125s
27000:0.629150390625s
26000:0.590087890625s
25000:0.56396484375s
・・・
6000:0.14111328125s
5000:0.113037109375s
4000:0.090087890625s
3000:0.06884765625s
2000:0.049072265625s
1000:0.030029296875s
Total:10.10498046875s

Forの後半は、前半より早くなってきています。
恐らくイメージ通りです。

じゃ、For EachがIndexが大きくなっても、遅くならないのか?

Forの場合、ここでListの要素が代入されています。

lstにとって、インデックス "i" が幾つが来るのかが分かっていない
のだろうと思います。

それに対しFor Eachは、ここで直接中身が取り出されています。

その為、次の要素が何かが分かっているような気がします。
(それは、イテレータだと思う)

・・・眠い。