How to search through big arrays?

I am asking, because I currently have something like this:

public class FoodList : MonoBehaviour
{
    public MyClass[] food;
}

[System.Serializable]
public class MyClass
{
    public MyEnum name;
    public string description;
    public float cost;
    public int rating;
}

public enum MyEnum
{
    Pizza,
    Pie,
    Pasta,
    //....
}

So if my food array is fairly big, using this search method may be inefficient:

public MyClass FindFood(MyEnum name)
{
    foreach (MyClass m_food in food)
    {
        if (name == m_food.name) return m_food;
    }
    return null;
}

Would it be more efficient to add a Dictionary to my FoodList class and search through it?

private Dictionary<MyEnum, MyClass> searchList = new Dictionary<MyEnum, MyClass>();
//.......
public MyClass GetFood(MyEnum name)
{
    if (searchList.Contains(name))
    {
        return searchList[name];
    }
    else
    {
        MyClass m_food = FindFood(name);
        searchList.Add(name, m_food);
        return m_food;
    }
}

Or do Dictionaries internally work like my “FindFood” method and it wouldn’t make a difference?
And are there any better ways to search through big arrays? I can think of the “How to look for a lion in a desert” method… But if Dictionaries are not working like my “FindFood” method, then I’d guess that they’d be more efficient… Of course I can also always use MyEnum’s integer value in order to search for a value within an array :stuck_out_tongue:
But which way is the best?

Dictionaries work with hashes, essentially instantaneous lookup regardless of entry count inside.

It might or might not make a difference. Computers are pretty fast.

It would certainly be better to use a Dictionary if the problem is amenable to that, but you probably would never notice a difference.

For every game I have seen, there has never been nearly enough food in the entire game to make a measurable difference. While it’s wasteful, you could probably scan a list of 10000 food items every frame and it wouldn’t matter. Now if you had 1000 different agents in your game and each one of them was scanning a huge list, yeah, that would be bad.

DO NOT OPTIMIZE “JUST BECAUSE…” If you don’t have a problem, DO NOT OPTIMIZE!

If you DO have a problem, there is only ONE way to find out. Always start by using the profiler:

Window → Analysis → Profiler

Failure to use the profiler first means you’re just guessing, making a mess of your code for no good reason.

Not only that but performance on platform A will likely be completely different than platform B. Test on the platform(s) that you care about, and test to the extent that it is worth your effort, and no more.

https://discussions.unity.com/t/841163/2

Remember that optimized code is ALWAYS harder to work with and more brittle, making subsequent feature development difficult or impossible, or incurring massive technical debt on future development.

Notes on optimizing UnityEngine.UI setups:

https://discussions.unity.com/t/846847/2

At a minimum you want to clearly understand what performance issues you are having:

  • running too slowly?
  • loading too slowly?
  • using too much runtime memory?
  • final bundle too large?
  • too much network traffic?
  • something else?

If you are unable to engage the profiler, then your next solution is gross guessing changes, such as “reimport all textures as 32x32 tiny textures” or “replace some complex 3D objects with cubes/capsules” to try and figure out what is bogging you down.

Each experiment you do may give you intel about what is causing the performance issue that you identified. More importantly let you eliminate candidates for optimization. For instance if you swap out your biggest textures with 32x32 stamps and you STILL have a problem, you may be able to eliminate textures as an issue and move onto something else.

This sort of speculative optimization assumes you’re properly using source control so it takes one click to revert to the way your project was before if there is no improvement, while carefully making notes about what you have tried and more importantly what results it has had.

Great to know, thanks :slight_smile:

Ok, I’ll keep that in mind. I come from the Warcraft 3 map making community and there optimization was fairly important, even if you didn’t have any issues. But JASS was fairly bad language, full of memory leaks. Like if you had a local reference variable, such as “Unit”, you HAD to set it to null at the end of the function, or it’d be a memory leak. There was also another type of very common memory leak, which I no longer remember, but I got the habit to always try to optimize my code from there :stuck_out_tongue:

EDIT: Ah, yes, you always had to destroy "group"s you created. Like there were these functions that were putting units in a group, so you could loop through all of them (kind a like the foreach in C#) and when you were done with this group, you HAD to destroy it, or you had another memory leak. And apart from destroying it, I think you also had to set the local group variable to null as well.

C# (actually Java, which C# copied) is almost literally a reaction to that whole memory leak situation. So many Jr,. programmers screwed that stuff up; and a slow memory leak isn’t noticable at first, is very difficult to track down, and always crashing after 1-2 hours is bad. So Java decided it would personally handle that stuff. Even if you wanted to clean up old memory, those commands were removed from the language. Java/C# runs slower – too slow for WarCraft on a PS-1 – but plenty fast for a modern computer. But now we have cheap cell-phones, so Unity has this complicated IL2CPP thing and a C++ back-end.

Today in practice we think about whether we’re in the most critical part of the game and try not to do anything stupid. In a game with 100 monsters, optimizing the player’s code won’t do much. And monsters might run just fine with sloppy code if it’s not doing too much.

You might find Apple’s language Swift amusing. They handle memory reclamation in a more efficient way, but you sometimes (for rings of nodes) have to manually set something to null or use a special half-strength pointer.