上一篇咱们讲到了AI架构之一的行为树,本篇文章和下一篇文章咱们将对行为树进行优化,在本篇文章中咱们讲到的是内存优化git
上一篇中咱们设计的行为树因为直接采用new进行动态内存分配,没有本身进行管理。所以行为树各节点的存储位置会散布在内存空间的各处,行为树在不一样节点中切换时会致使Cache频繁失效。
经过内存管理改变行为树节点的内存分布,能够显著提升行为树的内存性能。github
咱们能够在BehaviorTree中引入一组内存分配的API来保证各节点尽可能分配在连续的内存上,代码以下数组
BehaviorTree(Behavior*InRoot):Root(InRoot), Buffer(new uint8_t[MaxBehaviorTreeMemory]),Offset(0){} ~BehaviorTree(){ delete[] Buffer; } template<typename T> T* Allocate() { T* Node = new((void*)((uintptr_t)Buffer + Offset)) T; Offset += sizeof(T); assert(Offset < MaxBehaviorTreeMemory); return Node; }
咱们在BehaviorTree中引入一个Allocate函数用来负责全部节点的内存分配。
当行为树被构造时,一块用于保存节点的内存空间Bufffer会随之分配,Allocate函数经过Placement new在Buffer上进行内存分配,经过Offset记录分配已分配内存的偏移地址。
经过这种方式咱们可让节点分配在连续的内存上,同时经过控制分配节点的顺序(如深度遍历广度遍历等),咱们能够进一步减小行为树遍历时产生的Cache失效,提升内存性能。)架构
除了对节点分配进行优化,咱们还能够改变复合节点的内存布局,进一步提高性能。ide
class Composite :public Behavior { public: friend class BehaviorTree; virtual void AddChild(Behavior* InChild) override { assert(ChildrenCount < MaxChildrenPerComposite); ptrdiff_t p = (uintptr_t)InChild - (uintptr_t)this; assert(p < std::numeric_limits<uint16_t>::max()); Children[ChildrenCount++] = static_cast<uint16_t>(p); } Behavior* GetChild(size_t index) { assert(index < MaxChildrenPerComposite); return (Behavior*)((uintptr_t)this + Children[index]); } size_t GetChildrenCount() { return ChildrenCount; } protected: uint16_t Children[MaxChildrenPerComposite]; uint16_t ChildrenCount = 0; };
在如上代码中,咱们经过静态数组代替vector,避免在存储时vector所产生的额外堆操做,经过用保存子节点相对于复合节点的偏移地址来代替直接保存子节点指针以节省内存空间。因为更换了子节点的存储方式,咱们须要经过getchild()函数来根据复合节点地址和子节点偏移地址得到子节点指针。函数
以上,就是关于行为树的内存优化方式,固然凡事无绝对,究竟如何构造行为树应当根据实际状况选择,下一篇咱们将讲述另外一种行为树优化方法。
gihub连接布局