Comparing Class, Dictionary, and Struct Performance in Tile Searching in Unity

Introduction

In game development, optimizing code is essential for ensuring smooth gameplay. This is particularly true in Unity, where memory management and execution speed are crucial. The methods used to search and filter large datasets can significantly impact application performance. This article compares three approaches for searching for elements in lists: using classes, dictionaries, and structs. We will investigate how these techniques perform in scenarios that require fast filtering of large collections of data.

Sample Class: TileData

Let’s begin with the TileData class, which represents tile objects. Each tile has a position, type, and amount, forming the basic structure of our data:

public class TileData
{
    public int PositionX { get; private set; }
    public int PositionY { get; private set; }
    public TileType Type { get; private set; }
    public int Amount { get; private set; }

    public TileData(int positionX, int positionY, TileType type, int amount)
    {
        PositionX = positionX;
        PositionY = positionY;
        Type = type;
        Amount = amount;
    }
}

public enum TileType
{
    Water,
    Rock,
    Snow,
    Grass,
    Air
}

Data Initialization

For testing purposes, we create a list of TileData objects with 1,000,000 randomly generated entries. Each object is assigned a random TileType, giving us a realistic example of a large collection to filter.

private void InitTiles()
{
    AllTiles = new List<TileData>();
    System.Array tileTypes = System.Enum.GetValues(typeof(TileType));
    System.Random random = new System.Random(100);

    for (int x = 0; x < 1000; x++)
    {
        for (int y = 0; y < 1000; y++)
        {
            TileType randomType = (TileType)tileTypes.GetValue(random.Next(tileTypes.Length));
            int randomAmount = random.Next(1, 101);
            TileData tile = new TileData(x, y, randomType, randomAmount);
            AllTiles.Add(tile);
        }
    }
    matchingTiles = new List<TileData>(1000 * 1000);
}

Searching with Class

The method below searches for tiles using the class-based approach, employing a linear search through the list. While straightforward, this method can result in longer execution times when dealing with large datasets.

public List<TileData> SearchTiles(TileType tileType, int amount)
{
    matchingTiles.Clear();
    foreach (var tile in AllTiles)
    {
        if (tile.Type == tileType && tile.Amount >= amount)
        {
            matchingTiles.Add(tile);
        }
    }
    return matchingTiles;
}

This method shows a long execution time due to the linear search approach.

Searching with Dictionary

Next, we can improve the search performance by using a dictionary to store tiles by their type. This allows for faster lookups compared to a linear search.

public List<TileData> SearchTilesInDictionary(TileType tileType, int amount)
{
    matchingTiles.Clear();
    if (tileDictionary.TryGetValue(tileType, out var tiles))
    {
        foreach (var tile in tiles)
        {
            if (tile.Amount >= amount)
            {
                matchingTiles.Add(tile);
            }
        }
    }
    return matchingTiles;
}

This shows a significant improvement in performance due to reduced search time using a dictionary.

Changing to Structs

Now let’s explore the performance benefits of using structs instead of classes. Structs are value types, meaning they are stored on the stack rather than the heap. This can lead to improved performance when working with large collections of data.

public struct TileData
{
    public int PositionX { get; private set; }
    public int PositionY { get; private set; }
    public TileType Type { get; private set; }
    public int Amount { get; private set; }

    public TileData(int positionX, int positionY, TileType type, int amount)
    {
        PositionX = positionX;
        PositionY = positionY;
        Type = type;
        Amount = amount;
    }
}

This shows a noticeable performance improvement due to the use of structs instead of classes.

Conclusion

This analysis highlights how selecting an appropriate data structure and search method can greatly enhance performance in Unity.

  • Classes: Best suited for more complex, long-lived objects that benefit from features like inheritance. Because classes are reference types, they are allocated on the heap, resulting in higher memory overhead and slower performance in data-intensive tasks.
  • Dictionaries: Using dictionaries allows for efficient, constant-time lookups, which reduces the need for linear scans. This makes them ideal when fast retrieval of grouped data is needed, significantly improving search performance in scenarios with frequent lookups.
  • Structs: Well-suited for small, homogeneous data sets that require fast access and are frequently created and destroyed. As value types, structs are allocated on the stack, minimizing memory overhead and improving cache locality, which can lead to faster execution times compared to classes.

Selecting the best data structure depends on the specific needs of your project. Testing each approach using Unity’s Profiler is crucial to achieve a balance between performance and memory usage, ensuring efficient gameplay and resource management.

By Rufi

Leave a Reply

Your email address will not be published. Required fields are marked *