Results 1 to 29 of 29

Thread: How to access object in collection more quickly

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    How to access object in collection more quickly

    How you access an object in a collection makes up to a magnitude of difference:

    VB Code:
    1. Dim c As Collection
    2. Dim o As New SomeObject
    3. Dim ll As Long
    4. Dim idx As Long
    5.  
    6. Debug.Print Now
    7.  
    8. Debug.Print "Populate collection"
    9. Call measureTimeStart
    10. Set c = New Collection
    11. For ll = 1 To 1000
    12.     Set o = New SomeObject
    13.     c.Add o
    14. Next
    15. Call measureTimeStop
    16.  
    17. Debug.Print "For Each"
    18. Call measureTimeStart
    19. For ll = 1 To 1000
    20.     For Each o In c
    21.         If o.id = 1234567890123# Then
    22.         End If
    23.     Next
    24. Next
    25. Call measureTimeStop
    26.  
    27. Debug.Print "By index"
    28. Call measureTimeStart
    29. For ll = 1 To 1000
    30.     For idx = 1 To c.Count
    31.         Set o = c(idx)
    32.         If o.id = 1234567890123# Then
    33.         End If
    34.     Next
    35. Next
    36. Call measureTimeStop
    37.  
    38. Debug.Print "CallByName"
    39. Call measureTimeStart
    40. For ll = 1 To 1000
    41.     For idx = 1 To c.Count
    42.         If CallByName(c.Item(idx), "id", VbGet) = 1234567890123# Then
    43.         End If
    44.     Next
    45. Next
    46. Call measureTimeStop
    47.  
    48. Debug.Print vbNewLine

    The results:
    Code:
    2019-03-18 7:28:00 PM 
    Populate collection
    5.797ms
    For Each
    500.9564ms
    By index
    5587.8786ms
    CallByName
    8702.121ms
    Clarification:
    MeasureTime* are my accurate time-measurement functions which use QueryPerformance* in kernel32.
    SomeObject is a simple class module with an "id" property of the Currency type.

    500ms is still a long time for just 1 000 000 objects.

    Do you know of any faster methods?
    Last edited by OldClock; Mar 18th, 2019 at 04:23 PM.

  2. #2
    PowerPoster Zvoni's Avatar
    Join Date
    Sep 2012
    Location
    To the moon and then left
    Posts
    4,440

    Re: How to access object in collection more quickly

    If you don't need the "special" methods of a collection (For Each, Insert/Remove at Positon, Unique Key etc.) use an array and be done with it
    Last edited by Zvoni; Tomorrow at 31:69 PM.
    ----------------------------------------------------------------------------------------

    One System to rule them all, One Code to find them,
    One IDE to bring them all, and to the Framework bind them,
    in the Land of Redmond, where the Windows lie
    ---------------------------------------------------------------------------------
    People call me crazy because i'm jumping out of perfectly fine airplanes.
    ---------------------------------------------------------------------------------
    Code is like a joke: If you have to explain it, it's bad

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    That is completely off-topic.

  4. #4

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    Using Olaf Schmidt's RichClient5:
    Code:
    2019-03-18 8:12:54 PM 
    Populate collection
    126.9146ms
    For Each
    578.7012ms
    By index
    494.5912ms
    CallByName
    2761.0677ms

  5. #5
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,041

    Re: How to access object in collection more quickly

    If there's anything like a Dictionary in VB6, they tend to be FAR faster, as long as you can be looking things up by a specific key all the time. If you are looking things up by random fields, it gets more interesting.
    My usual boring signature: Nothing

  6. #6
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,156

    Re: How to access object in collection more quickly

    Quote Originally Posted by OldClock View Post
    Do you know of any faster methods?
    You can try accessing collection items *by key* -- which is he fastest way but this means you'll have to change the way you populate your collection.

    Currently you just pile the items w/ c.Add o like in an array/list but you'll have to come up with *unique* keys like in a dictionary e.g. try c.Add o, "#" & o.id

    cheers,
    </wqw>

  7. #7
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,451

    Re: How to access object in collection more quickly

    Looks like you are running each loop 1000 times over 1000 item collections, so it's actually 500ms for 1,000,000 tests.

    In my tests of the VB6 Collection vs. the RC5 Collection, they are both about the same with For Each, but RC5 outperforms the VB6 Collection by Index dramatically (and is also about 2x as fast as For...Each).


    My RC5 Code below. NOTE I skipped the object property access since that is unrelated to testing the performance of the collection object.

    Code:
    Sub Test()
        Dim c As cCollection
        Dim o As New SomeObject
        Dim ll As Long
        Dim idx As Long
         
        Debug.Print Now
         
        Set c = New cCollection
        
        Debug.Print "Populate collection"
        New_c.Timing True
        For ll = 1 To 1000
            Set o = New SomeObject
            c.Add o
        Next
        Debug.Print New_c.Timing
        
        Debug.Print "For Each"
        New_c.Timing True
        For ll = 1 To 1000
            For Each o In c
            Next
        Next
        Debug.Print New_c.Timing
        
        Debug.Print "By index"
        New_c.Timing True
        For ll = 1 To 1000
            For idx = 0 To c.Count - 1
                Set o = c.ItemByIndex(idx)
            Next
        Next
        Debug.Print New_c.Timing
         
        Debug.Print vbNewLine
    End Sub
    Timing for 1,000,000 accesses:

    Code:
    For Each
     368.55msec
    
    By index
     244.33msec
    Last edited by jpbro; Mar 18th, 2019 at 04:00 PM.

  8. #8
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,451

    Re: How to access object in collection more quickly

    BTW I just tried an object array (e.g. Dim X(1 to 1000) As Object), and it performed the best, but not by too much. Around 180ms vs. 244ms for the RC5 cCollection access by Index. So if you need the extra features of the cCollection object, you won't be sacrificing much speed. If you are using those features, then you might as well just use an object array.

  9. #9

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    Here is a comparison using the "Dictionary" class.
    Note: I haven't used Dictionaries before, so there might be a better way of accessing the data than the way I used below. This is the best I could figure out with no documentation available.

    vb Code:
    1. Dim d As Dictionary
    2. Dim k As Variant
    3. Dim o As New SomeObject
    4. Dim ll As Long
    5. Dim idx As Long
    6.  
    7. Debug.Print "Populate dictionary"
    8. Call measureTimeStart
    9. Set d = New Dictionary
    10. For ll = 1 To 1000
    11.     Set o = New SomeObject
    12.     d.Add ll, o
    13. Next
    14. Call measureTimeStop
    15.  
    16. Debug.Print "By key, directly"
    17. Call measureTimeStart
    18. For ll = 1 To 1000
    19.     For Each k In d.Keys
    20.         If d(k).id = 1234567890123# Then
    21.         End If
    22.     Next
    23. Next
    24. Call measureTimeStop
    25.  
    26. Debug.Print "By key, set"
    27. Call measureTimeStart
    28. For ll = 1 To 1000
    29.     For Each k In d.Keys
    30.         Set o = d(k)
    31.         If o.id = 1234567890123# Then
    32.         End If
    33.     Next
    34. Next
    35. Call measureTimeStop
    36.  
    37. Debug.Print "By key, index"
    38. Call measureTimeStart
    39. For ll = 1 To 1000
    40.     For idx = 0 To d.Count - 1
    41.         Set o = d.Items(idx)
    42.         If o.id = 1234567890123# Then
    43.         End If
    44.     Next
    45. Next
    46. Call measureTimeStop

    Interestingly enough, there was a significant difference when I set the key to a long vs a string.

    Timing results using string as key
    vb Code:
    1. d.Add "key" & ll, o
    Code:
    Populate dictionary
    7.7568ms
    By key, directly
    3469.8339ms
    By key, set
    1175.8321ms
    By key, index
    67188.6856ms
    Timing results using long as key
    vb Code:
    1. d.Add ll, o
    Code:
    Populate dictionary
    6.1332ms
    By key, directly
    2807.276ms
    By key, set
    696.7535ms
    By key, index
    60142.6665ms
    Last edited by OldClock; Mar 18th, 2019 at 04:18 PM.

  10. #10

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    Same test as in first post but this time using a string key, and instead of "CallByName" I tested accessing by key as suggested by wqweto.

    vb Code:
    1. Dim c As Collection
    2. Dim o As New SomeObject
    3. Dim ll As Long
    4. Dim idx As Long
    5.  
    6. Debug.Print Now
    7. Debug.Print "ll"
    8.  
    9. Debug.Print "Populate collection"
    10. Call measureTimeStart
    11. Set c = New Collection
    12. For ll = 1 To 1000
    13.     Set o = New SomeObject
    14.     c.Add o, "key" & ll
    15. Next
    16. Call measureTimeStop
    17.  
    18. Debug.Print "For Each"
    19. Call measureTimeStart
    20. For ll = 1 To 1000
    21.     For Each o In c
    22.         If o.id = 1234567890123# Then
    23.         End If
    24.     Next
    25. Next
    26. Call measureTimeStop
    27.  
    28. Debug.Print "By index"
    29. Call measureTimeStart
    30. For ll = 1 To 1000
    31.     For idx = 1 To c.Count
    32.         Set o = c(idx)
    33.         If o.id = 1234567890123# Then
    34.         End If
    35.     Next
    36. Next
    37. Call measureTimeStop
    38.  
    39. Debug.Print "By key"
    40. Call measureTimeStart
    41. For ll = 1 To 1000
    42.     For idx = 1 To c.Count
    43.         Set o = c("key" & idx)
    44.         If o.id = 1234567890123# Then
    45.         End If
    46.     Next
    47. Next
    48. Call measureTimeStop

    Timing results using string as key
    vb Code:
    1. c.Add o, "key" & ll

    Code:
    Populate collection
    9.8956ms
    For Each
    412.3755ms
    By index
    5158.9206ms
    By key
    1666.4321ms
    Timing results using long as key
    vb Code:
    1. c.Add o, ll

    Code:
    Populate collection
    7.33ms
    For Each
    417.5861ms
    By index
    5180.8298ms
    By key
    1672.3316ms

  11. #11

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    > Looks like you are running each loop 1000 times over 1000 item collections, so it's actually 500ms for 1,000,000 tests.

    Thank you for spotting that typo, corrected.

  12. #12
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,937

    Re: How to access object in collection more quickly

    OldClock,

    I read your OP and also took a good look at your code. I also scanned all the subsequent posts, but I'd just like to address your OP.

    The very first thing I noticed was that you're not using the String key of the collection. The power of collections comes with the use of this String key. They are organized into a Binary-Tree for quick search and access of the Variant data (in your case, the object) of the collection.

    I studied your example code, and couldn't make heads-or-tails of what you're actually trying to do. Maybe it was just some test code for speed testing. If you would really like to use a collection with lots of items and you want to subsequently find them quickly, you really should find a way to use the String key.

    Just trying to figure out your code, you might use your ID as the String key. Or, in other circumstances, I've used CStr(ObjPtr(SomeObject)) as the String key. It just depends on what you're trying to do.

    Without a key (String), collections are no better (and possibly worse) than an array. In that case, the only advantage you'd have is that you could use the .Add method rather than having to mess with ReDim. If you ReDim one-item-at-a-time, a collection's .Add method will be much faster. But if you "step" your array, and maybe add 1000 items at a time (and then ReDim to correct size at the end), an array will be faster.

    And, as stated above, if you don't mind an additional project reference, the dictionary is even faster than the collection. However, again, it will depend on you having some defined key. It's these keys that give all of these things their power.

    I'll admit that I do occasionally use collections without keys, but only for a quick gathering of a few items. For instance, if I don't want to keep a Dir$ active, and I want to quickly gather the files in a folder, I might use a collection to store these file names. There are other circumstances as well. But, in these cases, because there are so few items, I've got no concern over speed.

    Good Luck,
    Elroy

    EDIT1: I just noticed that Wqweto gave you essentially the same advice I'm giving you. The key is the key.
    Last edited by Elroy; Mar 18th, 2019 at 08:52 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  13. #13
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: How to access object in collection more quickly

    Scripting.Dictionary has issues of its own. For example iteration must be done by extracting every item (or key) into an array and then iterating that. It has no COM enumerator. This can eat performance alive if your Dictionary is large and you aren't paying attention.

  14. #14

  15. #15
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,156

    Re: How to access object in collection more quickly

    Dictionary's IEnumVARIANT support came up 5+ years ago when I had to cobble up this sample gist, trying to prove or reject its support for For Each through DISPID_NEWENUM (-4).

    cheers,
    </wqw>

  16. #16
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,687

    Re: How to access object in collection more quickly

    wqweto, just checking it supports as least from XP.
    Code:
    11352	1:02:33.591 PM	1	ole32.dll	IDictionary::Invoke ( DISPID_NEWENUM, IID_NULL, 1033, DISPATCH_METHOD | DISPATCH_PROPERTYGET, 0x0018f9fc, 0x0018fa0c, 0x0018f9dc, 0x0018fa28 )	S_OK		0.0011768
    ...
    11625	1:02:33.623 PM	1	scrrun.dll	ITypeInfo::Invoke ( 0x002a1948, DISPID_NEWENUM, DISPATCH_METHOD | DISPATCH_PROPERTYGET, 0x0018f9fc, 0x0018fa0c, 0x0018f9dc, 0x0018fa28 )	S_OK		0.0000435
    ...
    11631	1:02:33.623 PM	1	ole32.dll	IDictionary::_NewEnum ( 0x0018f6d4 )	S_OK		0.0000147
    ...
    11640	1:02:33.623 PM	1	ole32.dll	IUnknown::QueryInterface ( IEnumVARIANT, 0x0018fa68 )	S_OK		0.0000015
    11641	1:02:33.623 PM	1	ole32.dll	IUnknown::Release (  )	1		0.0000015
    11642	1:02:33.623 PM	1	ole32.dll	IEnumVARIANT::Next ( 1, 0x0018fabc, NULL )	S_OK		0.0000103
    ...
    11643	1:02:33.623 PM	1	scrrun.dll	VariantCopy ( 0x0018fabc, 0x002a1998 )	S_OK		0.0000073

  17. #17
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: How to access object in collection more quickly

    That's good news. Then most of the examples of iteration Microsoft shows for Dictionary are just sloppy?

    Most of them look like:

    Code:
    Dim a, d, i             'Create some variables
    Set d = CreateObject("Scripting.Dictionary")
    d.Add "a", "Athens"     'Add some keys and items
    d.Add "b", "Belgrade"
    d.Add "c", "Cairo"
    a = d.Items             'Get the items
    For i = 0 To d.Count -1 'Iterate the array
        Print a(i)          'Print item
    Next
    Or else they do a For Each on the .Items method itself.


    I tried this and got the keys:

    Code:
    Dim d, e    
    Set d = CreateObject("Scripting.Dictionary")
    d.Add "a", "Athens"
    d.Add "b", "Belgrade"
    d.Add "c", "Cairo"
    For Each e In d
        Debug.Print e
    Next
    Code:
    a
    b
    c
    Last edited by dilettante; Mar 19th, 2019 at 04:37 AM.

  18. #18
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: How to access object in collection more quickly

    Here is the result on my computer.
    I also added the test on my own class_Collection (heavilly used) wich implements more things (like dictionnary), and seems slower when retrieving by a key.
    I will check if I can optimize search by key
    NB : Enum is not implemented

    19/03/2019 11:03:21

    Populate collection
    1,7764
    Collection-For Each
    81,7892
    Collection-By index
    665,1863
    Collection-By index with item
    687,2527
    Collection-By key
    680,7462
    Collection-By key item
    648,9414


    Populate class_Collection
    4,2593
    class_Collection-By index
    490,5783
    class_Collection-By key
    1311,0298

  19. #19
    PowerPoster Zvoni's Avatar
    Join Date
    Sep 2012
    Location
    To the moon and then left
    Posts
    4,440

    Re: How to access object in collection more quickly

    Quote Originally Posted by OldClock View Post
    That is completely off-topic.
    How is my answer offtopic when 2 people referenced the same i did in my answer!
    Last edited by Zvoni; Tomorrow at 31:69 PM.
    ----------------------------------------------------------------------------------------

    One System to rule them all, One Code to find them,
    One IDE to bring them all, and to the Framework bind them,
    in the Land of Redmond, where the Windows lie
    ---------------------------------------------------------------------------------
    People call me crazy because i'm jumping out of perfectly fine airplanes.
    ---------------------------------------------------------------------------------
    Code is like a joke: If you have to explain it, it's bad

  20. #20
    Fanatic Member
    Join Date
    Jan 2015
    Posts
    596

    Re: How to access object in collection more quickly

    NB Bis, here is the test code :
    Code:
    Option Explicit
    
    Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
    Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
    
    Dim cTimer              As Currency
    
    Private Sub measureTimeStart()
       QueryPerformanceCounter cTimer
    End Sub
    
    Private Sub measureTimeStop()
       Dim cTimerBis        As Currency
    
       QueryPerformanceCounter cTimerBis
    
       Debug.Print cTimerBis - cTimer
    
    End Sub
    
    Private Sub Form_Load()
       Dim c                As Collection
       Dim o                As New Class1
       Dim ll               As Long
       Dim idx              As Long
    
       Debug.Print Now
    
       Debug.Print "Populate collection"
       Call measureTimeStart
       Set c = New Collection
       For ll = 1 To 1000
          Set o = New Class1
          c.Add o, "key" & ll
       Next
       Call measureTimeStop
    
       Debug.Print "Collection-For Each"
       Call measureTimeStart
       For ll = 1 To 1000
          For Each o In c
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print "Collection-By index"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c.Count
             Set o = c(idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print "Collection-By index with item"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c.Count
             Set o = c.Item(idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print "Collection-By key"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c.Count
             Set o = c("key" & idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print "Collection-By key item"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c.Count
             Set o = c.Item("key" & idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print
       Debug.Print
    
       ' *** Class_Collection
       Dim c2               As class_Collection
       Debug.Print "Populate class_Collection"
       Call measureTimeStart
       Set c2 = New class_Collection
       For ll = 1 To 1000
          Set o = New Class1
          c2.AddItem o, "key" & ll
       Next
       Call measureTimeStop
    
       '   Debug.Print "For Each"
       '   Call measureTimeStart
       '   For ll = 1 To 1000
       '      For Each o In c2
       '         If o.id = 1234567890123# Then
       '         End If
       '      Next
       '   Next
       '   Call measureTimeStop
    
       Debug.Print "class_Collection-By index"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c2.ItemCount
             Set o = c2.Item(idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
       Debug.Print "class_Collection-By key"
       Call measureTimeStart
       For ll = 1 To 1000
          For idx = 1 To c2.ItemCount
             Set o = c2.Item("key" & idx)
             If o.id = 1234567890123# Then
             End If
          Next
       Next
       Call measureTimeStop
    
    End Sub

  21. #21
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,687

    Re: How to access object in collection more quickly

    I tried this and got the keys:
    Yes, it iterates through the keys, you can use the key to access the item. It's the quite cheap operation because the dictionary is a hash table.
    Alternatively you can access the keys using the internal structure of the hash table:
    Code:
    ' ObjPtr+&H18 (Dic.Count)
    ' Get the items
    Private Function Items(Dic As Dictionary) As Variant
        Dim pItm As Long, loc() As Variant, i As Long
        
        ReDim loc(Dic.Count - 1)
    
        GetMem4 ByVal ObjPtr(Dic) + &H1C, pItm
    
        Do
            VariantCopy loc(i), ByVal pItm + &H18
    
            GetMem4 ByVal pItm + 4, pItm
            i = i + 1
        Loop While pItm
        
        Items = loc
    End Function
    You can read the description of the dictionary object, it also contains the other codes to work with the dictionary.

  22. #22
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    5,905

    Re: How to access object in collection more quickly

    As long as the Key is not used to retrieve data, but instead some property of the objects in the collection needs to be matched, then you will always end up iterating through all items.

    In that case you can better an in-memory DB or a ADO recordset.

  23. #23
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,253

    Re: How to access object in collection more quickly

    Quote Originally Posted by OldClock View Post
    Here is a comparison using the "Dictionary" class.
    Note: I haven't used Dictionaries before, so there might be a better way of accessing the data than the way I used below. This is the best I could figure out with no documentation available.
    No matter, if you're using a Dictionary or a Collection for that -
    looking at your code (and how you compare against the stored Objects-ID) -
    I guess you only want to find a "stored Object" fast (using its "somewhat longer than a Long" ID).

    So, why not add the Object in question using its ID as the Key?
    (and yes - that Key may be of a different type than a VBString - as e.g. a Currency when you need some longer "Int-Value").

    Here's your performance-test-loop, reduced to what's only needed, in case you only want a "fast way to find your o.ID"...
    (using an RC5.cSortedDictionary):
    Code:
    Private Sub Form_Click()
       Dim d As cSortedDictionary, o As SomeObject
       Dim k As Currency, ll As Long
    
       New_c.Timing True
         Set d = New_c.SortedDictionary
         For ll = 1 To 1000
             k = 123456789000000@ + ll 'just an example for a longer currency-typed Key
             Set o = New SomeObject
                 o.ID = k 'store the current Test-Key in the Object (this could be an internal ID of the Obj itself of course)
             d.Add o.ID, o '<- use the current (Currency-typed) o.ID-Key to store it in the dict
         Next
       Debug.Print "Populate dictionary: "; New_c.Timing
        
        
       New_c.Timing True
         For ll = 1 To 1000
             k = 123456789000000@ + ll
             Set o = d(k) 'there's no need to iterate through the collection, to find the o.ID - since it was used as the Dict-Key
             If o.ID <> k Then MsgBox "this shouldn't happen" 'a little test, just to show that the proper Object was indeed found
         Next
       Debug.Print "Obj-By-Key, directly: "; New_c.Timing
    End Sub
    The above comes out like that - performance-wise:
    Code:
    Populate dictionary:  2.41msec
    Obj-By-Key, directly:  0.41msec
    Olaf

  24. #24
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,451

    Re: How to access object in collection more quickly

    Quote Originally Posted by Schmidt View Post
    Here's your performance-test-loop, reduced to what's only needed, in case you only want a "fast way to find your o.ID"...
    (using an RC5.cSortedDictionary):
    I think you hit the nail on the head there. I took the original question at face value and stripped the "irrelevant" object property access since I didn't think those lines had anything to do with accessing collections more quickly, but I think you're right that the goal was to find a particular object.ID within a collection. If that's the case, then clearly the key should be the object.ID *unless* the Collection Key is also being used for other purposes, in which case multiple keys are needed and @Arnoutdv's database recommendation makes sense.

    @OldClock - perhaps you could elaborate on what you are hoping/trying to accomplish?

  25. #25

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    > So, why not add the Object in question using its ID as the Key?

    In real life, that's what I do, and retrieve them from a collection by key. But there are cases when you need to iterate through all the objects of a collection, and that's where this test yields interesting results.

    Note that in my test code I wrote "If o.id = 1234567890123# Then End If" and the intention behind it is causing confusion. I added that filler code as I was worried that a loop which does nothing would be optimized-away by the compiler. It wasn't the best choice of filler code. The purpose of the loop is not to find some specific object by ID, but to do something to all objects which are stored in that collection.

    In most of my real-life use cases, the keys are formed using '"key" & obj.id' where the ID is of the currency data type, and the IDs are neither in order nor of a complete series.

  26. #26
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,156

    Re: How to access object in collection more quickly

    Quote Originally Posted by OldClock View Post
    The purpose of the loop is not to find some specific object by ID, but to do something to all objects which are stored in that collection.
    This reduces the question to a non-issue as For Each looping (using objects' custom IEnumVARIANT impl behind the scenes) is the fastest way intended by the container's creators . . . and this almost equalizes all of them on performance, bringing speed very close to plain array looping (which is theoretically fastest).

    cheers,
    </wqw>

  27. #27
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    5,905

    Re: How to access object in collection more quickly

    I would create specific Indices / Lookup tables for these kind of requirements.
    For each needed property create an additional lookup array or collection for quick access.
    But beware, before you know it you have recreated the functionality of a disconnected ADO recordset.

  28. #28
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,156

    Re: How to access object in collection more quickly

    I'm heavily using VBA.Collections as (composite) key lookup indexes w/ disconnected ADO.Recordsets because Find method/Filter property have abysmal performance even with !MyField.Properties("Optimize").Value = True in-memory indexing.

    cheers,
    </wqw>

  29. #29

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: How to access object in collection more quickly

    Schmidt what is the difference between "Set d = New_c.SortedDictionary" and "Set d = New cSortedDictionary"?

    I noticed that "Set ds = New_c.SortedDictionary" takes ~130ms the first time it's called, and 0.1ms subsequent times, so in the test code below I moved that outside the timers.

    Test using Schmidt's cSortedDictionary:

    vb Code:
    1. Dim dc As cSortedDictionary
    2. Dim ds As cSortedDictionary
    3. Dim o As New SomeObject
    4. Dim ll As Long
    5. Dim idx As Long
    6.  
    7. Set dc = New_c.SortedDictionary
    8. Set ds = New_c.SortedDictionary
    9.  
    10. Debug.Print vbNewLine & Now
    11.  
    12. Debug.Print "Populate dictionary, string as key"
    13. Call measureTimeStart
    14. For ll = 1 To 1000
    15.     Set o = New SomeObject
    16.     o.id = CCur(ll)
    17.     ds.Add CStr(o.id), o
    18. Next
    19. Call measureTimeStop
    20.  
    21. Debug.Print "By key, string"
    22. Call measureTimeStart
    23. For ll = 1 To 1000
    24.     For idx = 0 To ds.Count - 1
    25.         Set o = ds(CStr(ll))
    26.     Next
    27. Next
    28. Call measureTimeStop
    29.  
    30. Debug.Print "Populate dictionary, currency as key"
    31. Call measureTimeStart
    32. For ll = 1 To 1000
    33.     Set o = New SomeObject
    34.     o.id = CCur(ll)
    35.     dc.Add o.id, o
    36. Next
    37. Call measureTimeStop
    38.  
    39. Debug.Print "By key, currency"
    40. Call measureTimeStart
    41. For ll = 1 To 1000
    42.     For idx = 0 To dc.Count - 1
    43.         Set o = dc(CCur(ll))
    44.     Next
    45. Next
    46. Call measureTimeStop

    Timing results:
    Code:
    2019-03-20 2:08:32 PM
    Populate dictionary, string as key
    5.7931ms
    By key, string
    762.6873ms
    Populate dictionary, currency as key
    6.1477ms
    By key, currency
    480.3076ms

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width