跳到正文

FastEvent

作者:ZZZank

加速 Forge/NeoForge 中的事件系统

收藏

介绍

介绍由机器翻译自 Modrinth 原页面,仅供参考。

项目已废弃

长话短说,这种优化方案依然有效,但 Forge/NeoForge 不允许模组修改 EventBus。

与 Minecraft 或 Forge 本身中的类不同,EventBus 并不是作为游戏的一部分加载的,而是作为“library”加载的,因此无法通过 Mixin 或 Transformer 修改 EventBus。

在 1.2.0 版本中,为了进一步提升性能,我使用了一些不太常规的技巧(如果你愿意,也可以称之为“非标准方案”)来修改 ASMEventHandler 类,但遗憾的是,该方案在 1.18.2 及更高版本中已失效。这些版本现已归档,之后可能也不会再获得更新。

FastEvent

是一个 Forge/Neoforge 优化模组,用于优化 Forge 中最基础的系统之一:事件系统。

性能如何

要为每个受支持的 Minecraft 版本制作测试用例并不容易,尤其是这些版本的事件系统彼此并不相同。因此,我将提供一份来自我为 Cleanroom 项目提交的 PR 的 JMH 基准测试报告,其中使用了与 FastEvent 相同的优化方案:

注册 10,000 个事件监听器,不发布事件:

Benchmark                               Mode  Cnt     Score     Error  Units
BusPerformanceTest.register10000Legacy  avgt    5  1126.498 ± 284.633  ms/op
BusPerformanceTest.register10000Modern  avgt    5  1058.961 ± 173.586  ms/op

速度提升约 6.4%。

注册 1,000 个事件监听器,发布事件 10,000 次:

Benchmark                                       Mode  Cnt     Score      Error  Units
BusPerformanceTest.register1000test10000Legacy  avgt    5  4407.963 ± 4250.643  ms/op
BusPerformanceTest.register1000test10000Modern  avgt    5  3550.578 ± 1991.352  ms/op

速度提升约 24%。

原始 PR

https://github.com/CleanroomMC/Cleanroom/pull/328#issuecomment-2801099504

它是如何工作的

(技术细节警告)

当开发者使用 @EventBusSubscriber 和/或 @SubscribeEvent 订阅事件处理器时,EventBus 无法凭空获取事件处理器,而只能获得一个 Method 对象。因此,最直接的做法就是直接使用这个对象:method.invoke(...)。这个调用最终会被 JVM 重定向回原始方法,使事件处理器在订阅事件后能够接收到事件。

但这种方式(method.invoke(...))的速度非常慢。为了提升速度,EventBus 会在运行时为它找到的每个方法生成事件处理器类,从而避免开销高昂的基于反射的调用。

不过,生成类本身也会带来额外的性能损耗。为了进一步提升速度,FastEvent 使用 lambda 构造替代了类生成,从而加快了事件处理器的构建速度。此外,lambda 还是“隐藏的”,这使 JVM 能够进行更多优化。

如果你对 Java 有一定了解,下面的代码示例可能会更直观:

class Listen {
    public void onEvent(Event event) {
    }
}
Listen lis = new Listen();

// EventBus will generate a new class for every event handler
class IEventListener$Listen$onEvent implements IEventListener {
    private Listen instance;
    public IEventListener$Listen$onEvent(Listen instance) {
        this.instance = instance;
    }
    @Override
    public void invoke(Event event) {
        instance.onEvent(event);
    }
}
IEventListener handler = new IEventListener$Listen$onEvent(lis);

// FastEvent uses lambda to generate event handler
IEventListener handler = lis::onEvent;

常见问题

FastEvent 支持哪些 Minecraft 版本?

FastEvent 支持 Minecraft Java 版 1.16.5–1.21.1,共 5 个版本,可用的加载器:Forge / NeoForge。下载时注意选择和你游戏版本一致的文件。

FastEvent 怎么安装?

先用 PCL2 或 HMCL 启动器安装对应版本的 Forge / NeoForge,再把下载的 .jar 文件放进游戏目录的 mods 文件夹,重新启动游戏即可。开启了版本隔离的,mods 文件夹在 versions/版本名 下面。运行环境:客户端 / 服务端。

评论

登录后评论和评分

ZZZank 的其他作品

查看全部 →