上一篇咱們講到了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連接佈局