question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

In cookbook and gitter chat describes approach not use direct links in components like this

    class SomeStorage  : IComponent   {
        public GameEntity SomeEntity;
    }

And use Index something like

    public static class UniqIdIndex
    {

        public const string IndexKey = "IndexUniqId";

        public static void AddUniqIndex(this GameContext contexts)
        {
            var positionIndex = new EntityIndex<GameEntity, int>(
                IndexKey,
                contexts.GetGroup(GameMatcher.UniqId),
                (e, c) =>
                {
                    var component = c as UniqIdComponent;
                    return component != null
                        ? (int)component.Id
                        : (int)e.uniqId.Id;
                }
            );

            contexts.AddEntityIndex(positionIndex);
        }


        public static HashSet<GameEntity> GetsByUniqId(this GameContext context, int id)
        {
            var index = (EntityIndex<GameEntity, int>)context.GetEntityIndex(IndexKey);
            //var nodes = ;
            return index.GetEntities(id);
        }


        public static GameEntity GetByUniqId(this GameContext context, int id)
        {
            var nodes = context.GetsByUniqId(id);
            if (nodes.Count == 0)
                return null;

            return nodes.First();
        }
    }

Using an index especially for entities with many components is good. You may create custom lnt64 index for 4-8 components. But using an index for a component with a unique id seems to me a bad idea. Because of performance issues.

I write performance tests.(Win10x64 Ryzen 1700 8cores 64gb ram) Results:

Running performance tests… Name/Iterations/Speed/Mem PerfomanceTests.GetEntyAsRef: 1000000 3 ms 8 kb PerfomanceTests.GetEntyFromArray: 1000000 5 ms 164 kb PerfomanceTests.GetEntyFromFullArray: 1000000 5 ms 13467 kb PerfomanceTests.GetEntyFromFullDict: 1000000 16 ms 14728 kb PerfomanceTests.GetEntyFromIndex: 1000000 171 ms 19423 kb PerfomanceTests.GetEntyFromIndexNoFirst: 1000000 101 ms 19735 kb PerfomanceTests.GetComponentSpeed: 1000000 19 ms 16 kb PerfomanceTests.HasComponentSpeed: 1000000 12 ms 8 kb

Code of test

using System.Collections.Generic;
using ArcanoidWars.Logic;
using ArcanoidWars.Logic.Phisics;
using Entitas;

namespace PerfomanceTests
{

    class SomeStorage
    {
        public GameEntity SomeEntity;
    }

    public class GetEntyAsRef : IPerformanceTest
    {
        int _iterations = 1000000;
        private SomeStorage storage;

        public void Before(int iterations)
        {
            _iterations = iterations;
            storage = new SomeStorage();
            storage.SomeEntity = new GameEntity();
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                var e = storage.SomeEntity;
            }
        }
    }

    public class GetEntyFromArray : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity[] _data;

        public void Before(int iterations)
        {
            _iterations = iterations;
            _data = new GameEntity[10000];
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                var e = _data[i % 10000];
            }
        }
    }

    public class GetEntyFromFullDict : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameContext context;
        private Dictionary<int, GameEntity> _data;

        public void Before(int iterations)
        {
            _iterations = iterations;
            context = new GameContext();
            _data = new Dictionary<int, GameEntity>();
            for (var i = 0; i < 10000; i++)
            {
                var e = context.CreateEntity();

                e.AddUnitName(UnitNames.Solder1);
                e.AddRadius(new Fix32(1));
                e.AddPlayerOwn(PlayerNames.Player0);
                _data[i] = e;
            }

        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                var e = _data[i % 10000];
            }
        }
    }

    public class GetEntyFromFullArray : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameContext context;
        private GameEntity[] _data;

        public void Before(int iterations)
        {
            _iterations = iterations;
            context = new GameContext();
            _data = new GameEntity[10000];
            for (var i = 0; i < 10000; i++)
            {
                var e = context.CreateEntity();

                e.AddUnitName(UnitNames.Solder1);
                e.AddRadius(new Fix32(1));
                e.AddPlayerOwn(PlayerNames.Player0);
                _data[i] = e;
            }
            
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                var e = _data[i % 10000];
            }
        }
    }

    public class GetEntyFromIndex : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity[] _data;
        private GameContext context;
        private int _uniqIdIndex;

        public void Before(int iterations)
        {
            _iterations = iterations;
            context = new GameContext();
            context.OnEntityCreated += AddUniqId;

            context.AddUniqIndex();

            for (var i = 0; i < 10000; i++)
            {
                var e = context.CreateEntity();

                e.AddUnitName(UnitNames.Solder1);
                e.AddRadius(new Fix32(1));
                e.AddPlayerOwn(PlayerNames.Player0);
            }
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                //var e = _data[i % 10000];
                context.GetByUniqId(i % 10000);
            }
        }
        private void AddUniqId(IContext context, IEntity entity)
        {
            var e = (GameEntity)entity;
            //FastIndex[_uniqIdIndex] = e;
            e.AddUniqId(_uniqIdIndex);
            _uniqIdIndex++;
        }
    }

    public class GetEntyFromIndexNoFirst : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity[] _data;
        private GameContext context;
        private int _uniqIdIndex;

        public void Before(int iterations)
        {
            _iterations = iterations;
            context = new GameContext();
            context.OnEntityCreated += AddUniqId;

            context.AddUniqIndex();

            for (var i = 0; i < 10000; i++)
            {
                var e = context.CreateEntity();

                e.AddUnitName(UnitNames.Solder1);
                e.AddRadius(new Fix32(1));
                e.AddPlayerOwn(PlayerNames.Player0);
            }
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                //var e = _data[i % 10000];
                foreach (var e in context.GetsByUniqId(i % 10000))
                {
                    
                }
                
            }
        }
        private void AddUniqId(IContext context, IEntity entity)
        {
            var e = (GameEntity)entity;
            //FastIndex[_uniqIdIndex] = e;
            e.AddUniqId(_uniqIdIndex);
            _uniqIdIndex++;
        }
    }

    public class GetComponentSpeed : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity _entity;


        public void Before(int iterations)
        {
            _iterations = iterations;
            var context = new GameContext();

            _entity = context.CreateEntity();

            _entity.AddUnitName(UnitNames.Solder1);
            _entity.AddRadius(new Fix32(1));
            _entity.AddPlayerOwn(PlayerNames.Player0);
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                var c = _entity.playerOwn;
            }
        }
    }

    public class HasComponentSpeed : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity _entity;


        public void Before(int iterations)
        {
            _iterations = iterations;
            var context = new GameContext();

            _entity = context.CreateEntity();

            _entity.AddUnitName(UnitNames.Solder1);
            _entity.AddRadius(new Fix32(1));
            _entity.AddPlayerOwn(PlayerNames.Player0);
        }

        public void Run()
        {
            for (int i = 0; i < _iterations; i++)
            {
                if (_entity.hasPlayerOwn)
                {

                }
            }
        }
    }

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
echegcommented, Nov 28, 2017

@IDNoise I create test for PrimeIndex

    public class GetEntyByPrimeIndex : IPerformanceTest
    {
        int _iterations = 1000000;
        private GameEntity[] _data;
        private GameContext context;
        private int _uniqIdIndex;

        public void Before(int iterations)
        {
            _iterations = iterations;
            context = new GameContext();

            for (var i = 0; i < 10000; i++)
            {
                var e = context.CreateEntity();

                e.AddUnitName(UnitNames.Solder1);
                e.AddRadius(new Fix32(1));
                e.AddPlayerOwn(PlayerNames.Player0);
            }
        }

        public void Run()
        {
            long c = 0;
            for (int i = 0; i < _iterations; i++)
            {
                //var e = _data[i % 10000];
                var es = context.GetEntities();
                c += es[i % 10000].radius.Radius.RawValue;
            }
        }
    }

PerfomanceTests.GetEntyFromFullArray: 1000000 27 ms 13660 kb PerfomanceTests.GetEntyFromFullDict: 1000000 34 ms 14842 kb PerfomanceTests.GetEntyFromIndex: 1000000 184 ms 19174 kb PerfomanceTests.GetEntyFromIndexNoFirst: 1000000 155 ms 20070 kb PerfomanceTests.GetEntyByPrimeIndex: 1000000 30 ms 13452 kb

I think in most cases use PrimeIndex is good approach.

0reactions
IDNoisecommented, Nov 28, 2017

What i ment is there is already PrimaryEntityIndex with 1 to 1 support (Dict<Key, Entity> and not <Key, HashSet<Entity> as in EntityIndex). You are not required to use entity index at all. You need to select best approach, if single dict get is too slow for you - write own solution, it can be custom index or separate solution. There is no tools that do all jobs good 😃 For me basic primary index was good enough (stored in field) and i had 30+ entities created\removed per frame with ~300 entities that were used in collisions (ships and projectiles) + 300 entities for other stuff.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Lighthouse: Speed Index
Speed Index (SI) is a performance metric that measures how quickly your page is visually complete above-the-fold. SI is dependent on the size...
Read more >
Speed index - MDN Web Docs Glossary
Speed index. Speed Index (SI) is a page load performance metric measuring how quickly the contents of a page are visibly populated.
Read more >
Speed Index: What it is & How to Optimize your Website for it
Speed Index (SI) is an interesting metric when considering page speed. It is absolutely an indication of your page's performance, but it is ......
Read more >
Speed Index - Another Way to Measure Web Performance
Simply put, speed index is how many milliseconds it takes for the visible parts of a webpage to be displayed.
Read more >
What's Speed Index and How to Improve It on WordPress
The Speed Index is the result of a calculation: it's the average rendering speed of a web page and its visual progress, also...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found