Search Unity

Some questions about efficiency of lists/retrieving elements or data from list

Discussion in 'Scripting' started by Jimmy-P, Mar 28, 2018.

  1. Jimmy-P

    Jimmy-P

    Joined:
    Jul 10, 2014
    Posts:
    59
    I'm not a very accomplished programmer, so please bear with me.

    I need to keep track of data across a few scripts attached to a gameobject, and there could be a relatively large number of these gameobjects. (10-100 but ideally it should be doable to have a few hundred) (Only one script is actually attached to the GO, it instantiates the other scripts as needed, so that is the only one that extends Monobehavior)

    What I'm doing right now is have one primary class, let's call it Class1, act as an interface between the gameobject in Unity and the other classes, so instantiating, calling methods, etc. It has only local variables, except for a list - List1. List1 is populated with 5-40 instances of Class2, each of which has a bunch of variables, let's say 20 ints and 20 strings in case I need to add more. Each Class2 instance has an instance of Class3, which holds another list, List2, and no other variables. List2 is populated with instances of Class4, which holds 4 ints and 1 string of 2 characters. Index 0 in List1 gets around 40 instances, other indices get 3-10. All the 4 classes have some methods, not sure if that matters.

    I hope that makes sense. Now, sometimes (Every 3-24 seconds depending on game speed setting) this method is called in Class1:

    Code (CSharp):
    1. public void Method()
    2.     {
    3.         int List1Size = List1.Count;
    4.        
    5.         for( int i = 1; i < List1Size; i++)//I deliberately skip index 0 in List1
    6.             {//Search List1 for elements that have Bool1 set to true
    7.             if(List1[i].Bool1)
    8.                 {//I know I should probably use getters/setters instead of public variables
    9.                     int localInt = List1[i].Int1;
    10.                     string localString = List1[i].String1;
    11.                     int List2Size = List1[i].Class3.List2.Count;
    12.  
    13.                     for(int i2 = 0; i2 < List2Size; i2++)
    14.                     {//This loop searches through the lists, then makes appropriate transactions
    15.                         if(List1[i].Class3.List2[i2].getString() == localString)
    16.                         {
    17.                             int anotherLocalInt = List1[0].Class3.List2[i2].getIntValue() * localInt;
    18.                          
    19.                             if(anotherLocalInt <= List1[i].Class3.List2[0].getAnotherIntValue())
    20.                             {
    21.                             //Changing values in one object
    22.                             List1[i].Class3.List2[i2].modifyIntValue(localString, localInt);
    23.                             List1[0].Class3.List2[i2].modifyIntValue(localString, (localInt * -1));
    24.                             //Changing values in another object
    25.                             List1[i].Class3.List2[0].modifyIntValue("String parameter", (anotherLocalInt) * -1);
    26.                             List1[0].Class3.List2[0].modifyIntValue("String parameter", anotherLocalInt);
    27.                             List1[i].Bool1 = false;
    28.                             break;
    29.                             }
    30.                             //
    31.                             else
    32.                             {
    33.                                 //Calls a method in List1[i].Class3.List2[i2]
    34.                             }
    35.        }    
    36.    }
    37. }
    To me, it feels like quite a costly system considering the amount of loops sorting through lists, and especially because I'm getting and comparing a string value to decide which object to work with. Rough calculation, with List1 avg size 20 and List2 avg size 10 means around 10kb of memory per gameobject (This calculation is probably way off)

    However - List2 can definitely be sorted, and while it will be necessary to add/remove elements from List1, it won't be done very often so I think I can implement an algorithm to sort List1 whenever elements are added or removed. So I can definitely implement a binary search algorithm for List2, and probably for List1. And perhaps most importantly, this system does exactly what I want it to, and I can easily make changes an additions, which I know will be necessary.

    I have an idea for a different system which basically moves Class4/List 2 to a separate gameobject and then Class1-3 can look up values from there instead. I'm fairly sure this would be more efficient but I don't want to start working on it unless my current system will be too expensive.

    Whew, that was long. If you read it, thank you. Would be very appreciative of input and ideas, and let me know if something is unclear (I'll be surprised if it's all clear, lol)
     
  2. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    > To me, it feels like quite a costly system

    Use the Unity Profiler to confirm this suspicion. What do you see, what FPS do you get, what bottlenecks do you have?
     
    Jimmy-P likes this.
  3. daxiongmao

    daxiongmao

    Joined:
    Feb 2, 2016
    Posts:
    412
    Use the profiler to see what is taking the longest. You can add your own profiler begins/end sections to measure specific parts.

    It will all depend on the time needed do the actual work vs the searching. If the work is 20x slower than the loop search it won't help much speeding up the loop. You would want to spread things across more frames if possible.

    One way off the top of my head is don't look through the whole list. Keep a separate list of items that their bool is true.
    You must have some point that you change it. So put those items into a need processing list. Then take them out when done. Possibly the same for your other list too.
     
    Jimmy-P likes this.
  4. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    10-100 is a small number - 10000 is a large number :)

    But do what people above said - profile and separate lists instead. The best way of optimising something is not having to do it at all. If the lists had appropriate names rather than just numbers perhaps we could also guess a better approach, but with this anonymised form we have no context to know from ;)
     
    Jimmy-P likes this.
  5. Jimmy-P

    Jimmy-P

    Joined:
    Jul 10, 2014
    Posts:
    59
    Thanks, I didn't know about the profiler. Played around with it a bit, will need to experiment more I think. I've also only inserted a couple of objects into each list so far to make sure the basic system works, so I'll need to test things out with bigger lists.
    Thanks for the tips, it actually occurred to me to use a "need processing" list instead, so that is one of the first things I'll do. Spreading things out over more frames is also a good idea, and should be pretty straightforward with some tweaks. The actual work is just getting and setting int values, with simple if/else statements inside short loops, so I think if I can reduce the time spent testing list element values in loops, it should increase performance a bit.

    You're right, I just figured that 100 would be a large number if each gameobject contains a lot of data. I anonymised the names in the code to make it readable without having to go into too much detail about what I'm trying to do, just because I felt my post was already more than long enough :)

    I'll probably test out an alternate system and compare performance in the profiler before I do anything else, I strongly suspect I have a better idea, and I could still apply the other suggestions. Will be good practice anyway.
     
  6. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    There's the old saying in programming: "Make one to throw away."

    The first version is usually a sacrificial version to test the concept of whatever you're making. The systems don't matter. The next iteration will always look more solid.