Advertisement

Save Other type Instance for Cache Locality

Started by May 05, 2023 12:01 AM
2 comments, last by Winteradio 1 year, 7 months ago

Hello, I'm Junior Game Engine Dev. In this day, I'm tried to use Entity Component System for my project. So, the code like this ISystem.h

< ISystem.h >

#ifndef __ISYSTEM_H__
#define __ISYSTEM_H__

class ISystem
{
public :
ISystem() {};
virtual ~ISystem() {};

public :
virtual void Update( float DeltaTime ) = 0;
};

#endif __ISYSTEM_H__

< RenderSystem.h >

#ifndef __RENDERSYSTEM_H__
#define __RENDERSYSTEM_H__

#include "ISystem.h"

class RenderSystem : public ISystem
{
public :
RenderSystem() : ISystem() {};
virtual ~RenderSystem() {};

public :
virtual void Update( float DeltaTime )
{
// Rendering Logic ...
}
};

#endif __RENDERSYSTEM_H__

< PhysicsSystem.h >

#ifndef __PHYSICSSYSTEM_H__
#define __PHYSICSSYSTEM_H__

#include "ISystem.h"

class PhysicsSystem : public ISystem
{
public :
PhysicsSystem() : ISystem() {};
virtual ~PhysicsSystem() {};

public :
virtual void Update( float DeltaTime )
{
// Calculate Force, Velocity ... 
}
};

#endif __PHYSICSSYSTEM_H__

And, SystemManager is the object that register, delete system and change system's running sequence.

< SystemManager.h >

#ifndef __SYSTEMMANAGER_H__
#define __SYSTEMMANAGER_H__

#include "ISystem.h"

class SystemManager
{
private :
SystemManager() {};
~SystemManager() {};

public :
void Register( ISystem*& Other )
{
m_Data.push_back( Other );
}

void Remove( ISystem*& Other )
{
// Logic for finding specific System and removing on m_Data;
}

void Move( ISystem*& Other, int Index )
{
// Logic for finding specific System of Index and switching Other System;
}

void Run( float DeltaTime)
{
for ( auto System : m_Data )
{
System->Update( DeltaTime );
}
}

private :
static SystemManager m_SystemManager;
std::vector< ISystem* > m_Data;
};

SystemManager SystemManager::m_SystemManager;

#endif __SYSTEMMANAGER_H__

But, I thought, the Other Systems are other type. So, they can not save near on memory.
I just make vector for ISystem*

< main.cpp >

#include "SystemManager.h"
#include "RenderSystem.h"
#include "PhysicsSystem.h"

int main()
{
// ... Some Logic other game module ... //

SystemManager::Register( new RenderSystem() );
SystemManager::Register( new PhysicsSystem() );

// ... Some Logic other game module ... //

return 0;
};

In this situation, the m_Data just save ISystem's pointer.
So, RenderSystem and PhysicsSystem are not saved near on memroy.
How to save each System that are differenct type and inheritance ISystem near on memory?

Winteradio said:
How to save each System that are differenct type and inheritance ISystem near on memory?

If you declare the systems on the stack like I showed in the other post, then it's likely that the objects will end up in memory nearby each other, whereas using the new operator they could end up anywhere. It probably doesn't make a difference here though. Cache effects are only worth considering if you are operating over lots of data in a tight inner loop. The engine systems are so high level that it doesn't matter where they are located in memory. On the other hand, you might want to allocate all physics or graphics objects contiguous in an array, so that the iteration within inner loops is faster.

Keep in mind that cache lines are usually 64 bytes, so if your data is farther than that apart, or if each item is bigger than 64 bytes, you will pay a penalty for cache misses anyway, even if you make everything contiguous. In most cases, except highly optimized inner loops over a moderate amount of data with repeated access to the same locations, it's not worth worrying about cache effects. So, it makes a big difference what order you traverse the pixels in an image (row major or column major), or how you do big matrix multiplication, but for everyday code it's a non-issue.

I think that many people overdesign their ECS in order to make everything contiguous (the “every entity is just an index" approach). This leads to slower performance overall because they are optimizing for cache where it doesn't matter (e.g. gameplay code), and adding unnecessary overhead in the process. You can get much bigger performance wins by just being careful about when/if you allocate memory, because memory allocation is one of the slowest things you can do in a program in one line of code.

Advertisement

Thanks for your opinion.

I finally understood “the core” through your previous and current answers.

For systems,
why is it good to declare to the stack,
for systems in the engine, why memory is less important than Component and Entity, Node, etc.,
what part of Cache was I missing, and
whether I was trying to over-apply ECS, etc...

Looking back, This post was a duplicate post, but thank you for your kind reply.

This topic is closed to new replies.

Advertisement