在C#中优化多重调度通知算法?

2024-04-02

抱歉,我想不出更好的方法来描述这个问题。基本上,我正在尝试在游戏中实现碰撞系统。我希望能够注册一个“碰撞处理程序”来处理可以转换为特定类型的两个对象(以任一顺序给出)的任何碰撞。因此,如果Player : Ship : Entity and Laser : Particle : Entity,以及处理程序(Ship, Particle) and (Laser, Entity)注册比碰撞(Laser, Player),两个处理程序都应该收到通知,参数的顺序正确,并且发生冲突(Laser, Laser)应仅通知第二个处理程序。

一个代码片段说了一千个字,所以这就是我现在正在做的事情(天真的方法):

    public IObservable<Collision<T1, T2>> onCollisionsOf<T1, T2>()
        where T1 : Entity
        where T2 : Entity
    {
        Type t1 = typeof(T1);
        Type t2 = typeof(T2);
        Subject<Collision<T1, T2>> obs = new Subject<Collision<T1, T2>>();
        _onCollisionInternal += delegate(Entity obj1, Entity obj2)
        {
            if (t1.IsAssignableFrom(obj1.GetType()) && t2.IsAssignableFrom(obj2.GetType()))
                obs.OnNext(new Collision<T1, T2>((T1) obj1, (T2) obj2));
            else if (t1.IsAssignableFrom(obj2.GetType()) && t2.IsAssignableFrom(obj1.GetType()))
                obs.OnNext(new Collision<T1, T2>((T1) obj2, (T2) obj1));
        };
        return obs;
    }

然而,这种方法相当慢(可测量;实施此方法后我损失了约 2 FPS),所以我正在寻找一种方法来减少几个周期/分配。

我想到了(例如,花了一个小时实现,然后因为自己是个白痴而把头撞到墙上)一种方法,根据哈希码将类型按顺序排列,然后将它们放入字典中,每个条目都是该类型对的处理程序的链接列表,带有布尔值指示处理程序是否希望反转参数的顺序。不幸的是,这不适用于派生类型,因为如果传入派生类型,它不会通知基类型的订阅者。任何人都可以想出一种比检查每个类型对更好的方法(twice)看看是否匹配?

谢谢, 罗伯特


所有这些反思都不会很快。注意每次碰撞时反射是如何发生的。那就是问题所在。

一些想法。

第一个想法:访客模式。

在 OO 语言中实现相当快速的双虚拟调度的标准方法是通过构建访问者模式的实现。

您能否实现一种访问者模式,使访问者中的每个接受者都维护一个“在这种情况下要做的事情”列表?然后,您的加法器方法包括确定编写新“要做的事情”的正确位置,然后将委托添加到该事情。当冲突发生时,您启动访问者进行双重调度,找到要做的事情的委托,并调用它。

您是否需要两次以上的调度?高效的多虚拟调度是可行的,但并不那么简单。

想法二:动态调度

C# 4 具有动态调度。它的工作方式是我们在第一次遇到调用站点时使用反射来分析调用站点。然后,我们动态生成新的 IL 来执行调用,并缓存 IL。在第二次调用时,我们反思参数以查看它们是否与之前的类型完全相同。如果是,我们将重新使用现有的 IL 并调用它。如果没有,我们再进行分析。如果参数通常只有几种类型,那么缓存很快就会开始只有命中而没有未命中,并且考虑到所有因素,性能实际上相当不错。当然比每次都进行大量反思要快。我们每次做的唯一反思就是分析参数的运行时类型。

想法三:实现自己的动态调度

DLR 所做的事情并没有什么神奇之处。它会进行一次分析,吐出一些 IL 并缓存结果。我怀疑你的痛苦正在发生,因为你每次都在重新进行分析。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在C#中优化多重调度通知算法? 的相关文章

随机推荐