镶嵌

镶嵌

通过隔离已加载区域、并行处理独立区域并仅限速导致卡顿的区域,来提升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 在串行和并行区域模式下均失败。
  • 在当前所有权模型下,一个高负载的相连区域无法使用另一个核心。
  • 基准测试结果特定于具体的工作负载和硬件。