C++ 访问派生类中成员模板函数中的元素时,无序的_映射导致性能低下
我正试图在一个游戏引擎项目上实现一个基于组件的体系结构。每个游戏对象都有一个C++ 访问派生类中成员模板函数中的元素时,无序的_映射导致性能低下,c++,performance,templates,inheritance,unordered-map,C++,Performance,Templates,Inheritance,Unordered Map,我正试图在一个游戏引擎项目上实现一个基于组件的体系结构。每个游戏对象都有一个unordered_映射,其中包含一个指向组件基类的指针。此时,我只有一个组件派生类,即Transform类。我想实现这个基于组件的架构,类似于Unity的约定:我想通过调用成员模板函数(如GetComponent())来获得游戏对象的组件 以下是标题: 组件.h enum type{ TRANSFORM // more will be added later }; class Component // b
unordered_映射
,其中包含一个指向组件
基类的指针。此时,我只有一个组件派生类,即Transform
类。我想实现这个基于组件的架构,类似于Unity的约定:我想通过调用成员模板函数(如GetComponent()
)来获得游戏对象的组件
以下是标题:
组件.h
enum type{
TRANSFORM // more will be added later
};
class Component // base class
{
public:
Component() : _owner(NULL) {}
virtual ~Component(){}
static type Type;
protected:
GameObject* _owner;
};
class Transform : public Component
{
public:
Transform();
~Transform();
static type Type;
void Rotate(float deg);
// to be encapsulated later on
Vector2D _position;
float _rotation;
Vector2D _scale;
};
class GameObject
{
public:
GameObject();
~GameObject();
void Update();
//and more member functions
template<class T>
T* GetComponent();
private:
// some more private members
unordered_map<type, Component*> _componentList; // only 1 component of each type
};
template<class T>
T* GameObject::GetComponent()
{
return static_cast<T*>(_componentList[T::Type]);
}
Transform.h
enum type{
TRANSFORM // more will be added later
};
class Component // base class
{
public:
Component() : _owner(NULL) {}
virtual ~Component(){}
static type Type;
protected:
GameObject* _owner;
};
class Transform : public Component
{
public:
Transform();
~Transform();
static type Type;
void Rotate(float deg);
// to be encapsulated later on
Vector2D _position;
float _rotation;
Vector2D _scale;
};
class GameObject
{
public:
GameObject();
~GameObject();
void Update();
//and more member functions
template<class T>
T* GetComponent();
private:
// some more private members
unordered_map<type, Component*> _componentList; // only 1 component of each type
};
template<class T>
T* GameObject::GetComponent()
{
return static_cast<T*>(_componentList[T::Type]);
}
GameObject.h
enum type{
TRANSFORM // more will be added later
};
class Component // base class
{
public:
Component() : _owner(NULL) {}
virtual ~Component(){}
static type Type;
protected:
GameObject* _owner;
};
class Transform : public Component
{
public:
Transform();
~Transform();
static type Type;
void Rotate(float deg);
// to be encapsulated later on
Vector2D _position;
float _rotation;
Vector2D _scale;
};
class GameObject
{
public:
GameObject();
~GameObject();
void Update();
//and more member functions
template<class T>
T* GetComponent();
private:
// some more private members
unordered_map<type, Component*> _componentList; // only 1 component of each type
};
template<class T>
T* GameObject::GetComponent()
{
return static_cast<T*>(_componentList[T::Type]);
}
类游戏对象
{
公众:
GameObject();
~GameObject();
无效更新();
//和更多的成员功能
模板
T*GetComponent();
私人:
//还有一些私人成员
无序的\u映射\u组件列表;//每种类型只有一个组件
};
模板
T*GameObject::GetComponent()
{
返回静态类型转换(_componentList[T::Type]);
}
我最初的实现使用了
std::vector
来保持组件*
,应用程序以60 fps的速度运行(我还有一个帧速率控制器,它只将fps限制在60 fps)。当我切换到unordered_map
来访问这些组件指针时,性能下降到15 FPS
我只画了两个四边形,在这一点上,我每帧只调用了6次GetComponent()
,所以场景中没有太多内容
我尝试了什么? 我尝试使用
const char*
,std::string
,type_info
和最后的enum type
作为无序_映射
的键值,但没有什么真正的帮助:所有实现都让我获得了15-16 FPS
是什么导致此性能问题?我怎样才能孤立这个问题
我希望我提供了足够的细节,如有必要,请随意要求更多的代码。免责声明:尽管下面的信息无论如何都应该适用,但是在这种简单的情况下,考虑到性能上的巨大差异,第一个基本的健全性检查是首先确保启用了构建优化。有了它的方式
unordered_map
最终被设计成一个相当大的容器,预先分配了大量的存储桶
请看这里:
在这里:
虽然计算散列索引很简单,但是对于这样小的无序映射
访问的内存量(以及在这些内存之间的移动量)很容易变成缓存缺失瓶颈,访问频率与从实体检索组件接口一样高
对于实体组件系统,通常您没有那么多与实体关联的组件——可能最多有几十个,通常只有几个。因此,std::vector
实际上是一种更合适的结构,主要是在引用的局部性方面(每次从实体中获取组件接口时都可以重复访问的小数组)。虽然是次要的一点,std::vector::operator[]
也是一个简单内联的函数
如果您想比这里的std::vector
做得更好(但我只是在分析并确定您需要它之后才建议这样做),前提是您可以为一个实体中通常可用的组件数量推断出一些常见的大小写上限,N
,类似的方法可能会更好:
struct ComponentList
{
Component* components[N];
Component** ptr;
int num;
};
首先将ptr
设置为components
,然后通过ptr
访问它们。插入新组件会增加num
。当num>=4
(罕见情况)时,更改ptr
以指向具有较大大小的动态分配块。销毁ComponentList
时,如果ptr!=组件
。如果您存储的元素少于N
(虽然std::vector
通常也会以初始容量及其增长方式进行存储),这会浪费一点内存,但它会将您的实体和提供的组件列表变成一个完全连续的结构,除非num>N
。因此,您获得了最佳的引用位置,并且可能比您开始时获得的结果更好(我从帧速率的显著下降中假设,您从实体中提取组件的频率非常高,这在ECS中并不少见)
考虑到可以从实体访问组件接口的频率,以及经常在非常紧密的循环中访问组件接口的频率,这可能是值得努力的
然而,考虑到数据的典型规模(实体中可用的组件数量),您最初选择的std::vector
实际上是更好的选择。对于非常小的数据集,基本的线性时间序列搜索通常优于更复杂的数据结构,您通常希望更多地关注内存/缓存效率
我尝试使用const char*、std::string、type_info和finally enum
键入无序_映射的键值,但没有任何真正的帮助:all
实现了15-16 FPS
请注意,对于键,您需要一些可以在恒定时间内进行比较的东西,比如积分值。在这种情况下,一种可能很方便的方法是一个内部字符串,它只存储一个
int
,以在方便性和性能之间取得平衡(允许客户端通过string
构造它们,但在组件查找期间通过int
进行比较)。实体通常只有很少的组件,也许5个,最多30个。对于30个元素,具有线性搜索的向量优于散列映射。我只会坚持向量。我对游戏编程一无所知,但你不能解释一下吗