
镶嵌
通过隔离已加载区域、并行处理独立区域并仅限速导致卡顿的区域,来提升Minecraft服务器性能。
Tessellate
Tessellate 是一款适用于 Minecraft 1.21.1 的 NeoForge 性能优化模组。它将远处已加载的区域分组为独立的区域,并让它们在不同的 CPU 核心上运行。如果某个区域的计算开销过大,Tessellate 可以降低该区域的速度,而不会拖累服务器的其他部分。
| 情况 | Tessellate 的作用 |
|---|---|
| 多个远处基地负载较高 | 并发运行它们所在的区域 |
| 某个区域超出服务器预算 | 仅降低该区域的模拟速率 |
| 两个区域接近到足以相互作用 | 在它们 tick 之前进行合并 |
| 工作线程到达不安全的共享状态 | 本次会话回退到串行 tick |
当玩家或强制加载区域分散开时,此模组效果最佳。一个密集的基地、刷怪塔或机器集群仍然是一个区域,因此 Tessellate 无法将这部分工作拆分到多个核心上。它也不能替代诸如 Lithium 之类的单线程优化模组。
要求与安装
- Minecraft 1.21.1
- NeoForge 21.1.x
- Java 21
- 无必需的优化模组依赖
将 JAR 文件放入服务器的 mods 文件夹即可。客户端安装是可选的:安装了客户端的玩家可以获得 GPU 渲染的区域地图和边界叠加层,而其他玩家仍可以使用粒子回退方案。
性能
为了进行具体比较,我们对四个强制加载且相隔 2048 个方块的区域进行了基准测试,每个区域有 1200 只持续存在的僵尸。并行 tick 将中位 MSPT 降低了 61.2%,将 p95 MSPT 降低了 62.5%。
| 模式 | 中位 MSPT | 中位 p95 | 20 TPS 下的区域数 | 最慢区域 |
|---|---|---|---|---|
| 串行区域 tick | 40.55 毫秒 | 50.80 毫秒 | 0/4 和 1/4 | 10.0-10.6 TPS |
| 并行区域 tick | 15.75 毫秒 | 19.05 毫秒 | 两次运行均为 4/4 | 20.0 TPS |
两种模式均在 Minecraft 1.21.1、NeoForge 21.1.248、Java 21 以及相同的九个模组配置下运行。此测试特意围绕几个独立区域构建;它并不保证在所有地方都能获得相同的加速效果。硬件、区域布局、实体数量和已安装的模组都会产生影响。
工作原理
Tessellate 将已加载的世界划分为 4x4 区块的区块段。相邻的实体 tick 区块段在运行前会合并成一个区域,这确保了可能相互作用的区域在同一工作线程上。独立的区域可以并行运行。数据包、生命周期回调、存档以及其他共享工作仍然通过主线程处理。
区域地图会随世界变化:随着区块的加载和卸载,区域会形成、合并、拆分和消失。
区域 TPS:切片而非跳过
Tessellate 每个 tick 都会测量每个区域。如果服务器即将错过其 tick 时间目标,则开销最大的区域会获得一个 tick 除数。除数为 4 意味着该区域以 5 TPS 运行,而未受影响的区域可以保持在 20 TPS。
每四个 tick 运行整个区域一次会产生正确的平均速率,但在游戏中的体验会很差。Tessellate 会将这些工作分散到每个 tick 中:
| 每四 tick 门控 | 每个 tick 切片 | |
|---|---|---|
| 旁观者 TPS | 19.6 | 20.0 |
| 平均 MSPT | 20.5 毫秒 | 23.7 毫秒 |
| p95 MSPT | 210.9 毫秒 | 32.9 毫秒 |
权衡
限流有一个有意为之的代价:当一个区域超出其配额时,该区域内的一切都会运行得更慢。生物移动更慢,刷怪塔产量更低,漏斗搬运物品更少。这对于控制卡顿源很有用,但 Tessellate 无法将其与一个合法繁忙的基地区分开来。
如果你宁愿让整个服务器低于 20 TPS,可以禁用自适应限流:
[regions]
adaptiveThrottling = false
区域跟踪、并行 tick 和诊断功能仍然可用。
配置
设置存储在 config/tessellate-common.toml 中。
| 选项 | 默认值 | 用途 |
|---|---|---|
regions.enabled |
true |
启用区域跟踪;禁用会使模组失效 |
regions.adaptiveThrottling |
true |
启用区域 TPS 控制 |
regions.budgetMillis |
25.0 |
为区域工作保留的最低预算 |
regions.targetTickMillis |
45.0 |
Tick 时间目标;调高以延迟干预 |
regions.maxTickDivisor |
16 |
允许的最慢区域速率,即 1.25 TPS |
regions.minThrottleMillis |
2.0 |
低于此开销的区域永远不会被减速 |
regions.sectionShift |
2 |
区域网格粒度,即 4x4 区块 |
regions.parallelTicking |
true |
在工作线程上运行独立区域 |
regions.directWorkerChunkReads |
true |
直接解析已加载的工作线程区块读取 |
regions.parallelNaturalSpawning |
true |
并发运行所属区域的刷怪搜索 |
regions.shardEntityStorage |
true |
启用并行 tick 所需的存储隔离 |
regions.asyncRegionLoops |
true |
允许区域在没有每 tick 全局加入的情况下运行 |
regions.scopedScheduledTicks |
true |
使用区域拥有的方块和流体 tick 调度器 |
regions.scopedBlockEvents |
true |
使用区域拥有的方块事件队列 |
串行诊断模式:
[regions]
parallelTicking = false
asyncRegionLoops = false
命令
/tessellate regions:显示区域地图、开销、tick 速率和执行模式/tessellate phases:报告工作线程/主线程边界、计时、失败和队列深度/tessellate violations:报告线程所有权违规;此列表应为空/tessellate visualize:切换区域地图和边界可视化
兼容性
我们测试了独立的区域循环,测试环境包括:
- 仅 NeoForge
- Lithium 0.15.4
- Mekanism 和 Mekanism Generators 10.7.19.85
- Naturalist 2.0.3、Citadel 2.7.1、Friends & Foes 4.0.27、Resourceful Lib 3.0.12 和 ScalableLux 0.3.0-alpha.0.8
- C2ME 0.4.0-alpha.0.120 以及上述完整模组集
Lithium 需要一些特殊处理,因此 Tessellate 会将其两个世界级缓存替换为工作线程安全的等效实现。C2ME 是可选的;Tessellate 在没有它的情况下也能运行,并且不依赖它。
我们测试的内容
| 检查项 | 结果 |
|---|---|
| JVM 回归测试套件 | 81 项测试通过 |
| 仅 Tessellate 的 NeoForge GameTest | 所有 5 项必需测试通过 |
| 计划 tick | 30 分钟,422 次重建周期,无失败或无排队的剩余工作 |
| 方块事件 | 30 分钟,1,001,984 次回调,数据包保持在主线程上 |
| 自然生成 | 并行重叠已确认;全局和局部上限均遵守 |
| 延迟写入 | 6,947 次世界写入和 2,592 次实体回调被精确重放 |
| 全模组存档/卸载 | 1,200 个实体在一次 49 区块卸载/重载后精确存活 |
| 主线程边界 | 8,519 个延迟操作均衡,零待处理工作 |
它不能做什么
- Tessellate 保护它已知的共享状态,但另一个模组仍然可以通过其自身的全局状态引入竞争条件。当 Tessellate 检测到所有权违规时,它会回退到串行 tick。
- C2ME 0.4.0-alpha.0.120 通过了实时基准测试、存档/卸载、重启和关闭测试,但其自然生成 GameTest 在串行和并行区域模式下均失败。
- 在当前所有权模型下,一个高负载的相连区域无法使用另一个核心。
- 基准测试结果特定于具体的工作负载和硬件。
正在加载版本记录…
正在加载评论…
评论在新手盒子客户端中发表,这里同步展示。