C#-如何提高行为树中的方法重写性能?

C#-如何提高行为树中的方法重写性能?,c#,artificial-intelligence,monogame,C#,Artificial Intelligence,Monogame,我正在使用一个定制的行为树,它利用抽象/虚拟方法覆盖来控制大量(1000+个)游戏中生物的AI。这个组件对游戏的性能至关重要,我正在寻找减少CPU使用的方法。注意:由于游戏的性质,一个生态系统模拟器,我不能简单地停止模拟人工智能的生物是看不见的 树本身依赖于抽象基类例程,该例程提供有关节点和可重写的抽象on_Act()方法的状态信息,在该方法中,每个派生例程实现它应该遵循的逻辑,以决定是否成功或失败 public abstract class Routine { /// ...

我正在使用一个定制的行为树,它利用抽象/虚拟方法覆盖来控制大量(1000+个)游戏中生物的AI。这个组件对游戏的性能至关重要,我正在寻找减少CPU使用的方法。注意:由于游戏的性质,一个生态系统模拟器,我不能简单地停止模拟人工智能的生物是看不见的

树本身依赖于抽象基类例程,该例程提供有关节点和可重写的抽象on_Act()方法的状态信息,在该方法中,每个派生例程实现它应该遵循的逻辑,以决定是否成功或失败

public abstract class Routine
{
    /// ...

    /// <summary>
    /// Override to specify the routines logic.
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="world"></param>
    protected abstract void On_Act(Entity entity, World.World world, ref List<Routine> routineTree);

    public virtual void Act(Entity entity, World.World world, ref List<Routine> currentTree)
    {
        currentTree.Add(this);
        On_Act(entity, world, ref currentTree);
    }

    /// ...

}
公共抽象类例程
{
/// ...
/// 
///重写以指定例程逻辑。
/// 
/// 
/// 
受保护的抽象无效(实体实体,世界。世界,参考列表routineTree);
公共虚拟无效法案(实体实体,世界。世界,参考列表currentTree)
{
currentTree.Add(这个);
论行为(实体、世界、参照树);
}
/// ...
}
实现的一个很好的例子是WalkToLocation类,它会导致实体尝试步行到给定位置,如果到达目标位置则成功,如果在到达目标位置的过程中无法行走则失败

public class WalkToLocation : Routine
{
    public Func<Vector3> TargetLocation { get; set; }

    public WalkToLocation(Func<Vector3> TargetLocation)
    {
        this.TargetLocation = TargetLocation;
    }

    protected override void On_Act(Entity entity, World.World world, ref List<Routine> routineTree)
    {
        IMobile creature = entity as IMobile;
        if (entity != null)
        {
            if (!creature.CapableOfMovement)
            {
                this.Fail();
                return;
            }

            Vector3 targetLocation = world.MapBoundaries.ClampPosition(TargetLocation());

            bool moveToResult;
            moveToResult = creature.WalkTowards(targetLocation, world);


            if (moveToResult == true)
            {
                this.Succeed();
                return;
            }
        }
        else
        {
            throw new ArgumentException("\"entity\" needs to implement IMobile");
        }
    }
}
公共类WalkToLocation:例程
{
public Func TargetLocation{get;set;}
公共步行定位(Func TargetLocation)
{
this.TargetLocation=TargetLocation;
}
受保护的覆盖无效(实体实体、世界、世界、参考列表routineTree)
{
IMobile生物=作为IMobile的实体;
如果(实体!=null)
{
如果(!生物移动能力)
{
这个。失败();
返回;
}
Vector3 targetLocation=world.MapBounders.Clamposition(targetLocation());
bool-moveToResult;
moveToResult=生物。走向(目标位置,世界);
if(moveToResult==true)
{
这个。成功();
返回;
}
}
其他的
{
抛出新ArgumentException(“实体”需要实现IMobile);
}
}
}
最后,它是从一个Decorator类(例程的派生类)调用的,该类将多个例程拉到一起。在这里,“食腐”指示一个实体走向一个可食用的实体并吃掉它。(如果食物源变为空,它还会覆盖子序列成功)

公共类清除:装饰器
{
生物亲本;
公共清道夫(生物亲本)
{
this.parent=parent;
孩子=
新序列(
新的WalkToEntity(()=>(实体)parent.Context.OptimalFoodSource).Reportable(),
新Eat(()=>parent.Context.OptimalFoodSource).Reportable()
);
}
受保护的覆盖无效(SpeciesALRE.World.Entity实体,SpeciesALRE.World.World世界,参考列表routineTree)
{
if(Child.IsRunning())
{
Act(实体、世界、参考routineTree);
}
if(Child.hasfiled())
{
这个。失败();
返回;
}
else if(Child.HasSucceeded())
{
if(parent.Context.closestcarbody==null)
这个。失败();
其他的
这个。成功();
返回;
}
}
}
这一切都很好。然而,从性能的角度来看,这被证明是出人意料的糟糕。我在行为树中花费了超过一半的CPU时间,从分析器中可以看出,其中很大一部分时间都花在了开销上。特别是,像“Act”和“On_Act”这样的方法本身似乎几乎不需要花费任何成本,但每次调用它们时,我都会看到一部分CPU时间消失,而没有子方法


我希望有人对抽象/虚拟方法有更好的理解,在我开始将序列和选择器折叠成单个例程以减少树的深度之前,我能阐明我在何处和为什么会产生如此多的开销,以及如何调整程序以更快地运行,这恰恰违背了一开始就有一个行为树的观点。

您的CPU将尝试尽可能快地运行您的代码。除非它受到其他约束(例如,它正在等待I/O或其他资源)的约束,否则它将以100%或非常接近的速度运行,直到完成为止。见:

在计算机科学中,当一台计算机完成一项任务的时间主要由中央处理器的速度决定时,它是CPU受限的(或计算受限的):处理器利用率很高,可能在几秒钟或几分钟内100%使用。外围设备产生的中断可能处理得很慢,或者无限期地拖延

因此,您似乎没有观察到任何测量结果本身表明存在性能问题


如果您希望提高性能,我怀疑“树”是问题所在。您可能有一些昂贵的子程序,例如搜索最近的其他生物,或探测环境障碍物。对于这些问题,有一些解决方案可以提供帮助,例如二进制空间分区或其他数据结构。如果您想了解更多关于这些的信息,请开始另一个问题。

这是不太可能的。回顾一下,它很好地展示了内存是如何成为瓶颈的。谢谢。在回顾这些文章时,似乎内存访问(特别是例程状态变量)可能是我的一些性能问题的根源。我会继续调查的,我会处理子程序。我现在没有性能结果,但是在示例a中
public class Scavenge : Decorator
{
    Creature parent;

    public Scavenge(Creature parent)
    {
        this.parent = parent;

        Child =
            new Sequence(
                new WalkToEntity(() => (Entity)parent.Context.OptimalFoodSource).Reportable(),
                new Eat(() => parent.Context.OptimalFoodSource).Reportable()
            );

    }

    protected override void On_Act(SpeciesALRE.World.Entity entity, SpeciesALRE.World.World world, ref List<Routine> routineTree)
    {
        if (Child.IsRunning())
        {
            Child.Act(entity, world, ref routineTree);
        }

        if (Child.HasFailed())
        {
            this.Fail();
            return;
        }
        else if (Child.HasSucceeded())
        {
            if (parent.Context.ClosestCorpse == null)
                this.Fail();
            else
                this.Succeed();
            return;
        }
    }
}