PERF: thaumicadditions 储罐每 tick 把同坐标的方块/方块实体查询重算 4 遍(16 次查 12 次冗余) #20

Closed
opened 2026-11-04 09:02:01 +00:00 by layue13 · 2 comments
Owner

调研编号:

现象

储罐方块的 updateEntity 每 tick 为了决定自身方块元数据(0/1/2/3 = 无邻/仅下/仅上/上下都有),把同三个坐标的查询重算了四遍;下/下下两处 TE 也各查两次。全过程没有局部变量承接,也没有任何节流门(无 % N、无 worldTime 判断)——每个储罐方块每 tick 固定产生 16 次方块/方块实体查询,其中 12 次是重复的。同族的老版强化储罐 TileOldReinforcedTank 是同一份代码的复制,同样 12 次重复。

危害类与优先级

  • PERF(每 tick 同参数重复求值、无局部承接)→ 标签 type/perf
  • p3 单次 getBlock/getTileEntity 开销小,但乘数是「每 tick × 每个储罐方块」;储罐塔常见(一列 5–20 格),且这是纯机械改动、行为完全不变

证据(生效源码)

  • 载体:TDLM/thaumicadditions + 分支 origin/main + pin cded7cd3ee59(source 类仓,无 patch 层,代码可点开)
  • 位置:src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-60、src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40
  • 关键原文:
// TileTankBasic.java:27,31,35,39  四条 if 各自把同三个查询重算一遍(无局部承接、无节流门)
      if(!(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic) && !(this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 0) {
      if(!(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 1) {
      if(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic && !(this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 2) {
      if(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 3) {

// TileTankBasic.java:53,56  同坐标 TE 查两次
      boolean var4 = this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord) instanceof TileTankBasic && this.vis + this.taint < this.capacity;
      ...
         this.ejectVis(var5, ((ITubable)this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord)).acceptVis(...), ForgeDirection.UP);
// TileTankBasic.java:59-60  同坐标 TE 查两次
      if(this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord) instanceof TileTankBasic) {
         TileTankBasic var6 = (TileTankBasic)this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord);
  • 计数(同一执行路径、同参数):getBlock(y+1) ×4、getBlock(y-1) ×4、getBlockMetadata(x,y,z) ×4、getTileEntity(y+1) ×2、getTileEntity(y-1) ×2 ⇒ 16 次查询里 12 次重复
  • 上游状态:[x] 未修(upstream 同形,无可点开的 upstream 仓)

触发与复现

  • 触发条件:任意储罐方块进入 tick 即执行;默认配置可达、无需玩家操作。
  • 静态推演:一列 10 格储罐 → 每 tick 160 次查询、其中 120 次是同一坐标的重复查询;20 tps 下 3,200 次/秒,冗余 2,400 次/秒。把 above = world.getBlock(x, y+1, z)、below = world.getBlock(x, y-1, z)、meta = getBlockMetadata(x,y,z)、aboveTe、belowTe 各取一次后复用,查询数降到 4 次/方块/tick,且元数据判定结果逐条相同。
  • 包内相关性:模块 thaumicadditions/sides client,server/默认配置可达 是

落点

  • 主仓:TDLM/thaumicadditions → src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-40(四条 if 的重复取块)、:53-60(重复取 TE)
  • 协同:src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40 是同一份代码的复制,需同步改(只改一处等于没改)

范式

  • 命中范式:F4 帧内不变量重复求值(同一调用内同参数查询重复 ≥2 次且无局部承接;此处是每 tick,非每帧)

引证

  1. 生效源码 permalink — https://git.potatolab.uk/TDLM/thaumicadditions/src/commit/cded7cd3ee59/src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java#L27-L60
  2. 同族文件 permalink — https://git.potatolab.uk/TDLM/thaumicadditions/src/commit/cded7cd3ee59/src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java#L17-L40

来源

  • 线索类型:内部代码审查(无外部报障)
  • 可解析指针:src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-60、src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40
**调研编号**: ## 现象 储罐方块的 `updateEntity` 每 tick 为了决定自身方块元数据(0/1/2/3 = 无邻/仅下/仅上/上下都有),把**同三个坐标的查询重算了四遍**;下/下下两处 TE 也各查两次。全过程没有局部变量承接,也没有任何节流门(无 `% N`、无 worldTime 判断)——每个储罐方块每 tick 固定产生 16 次方块/方块实体查询,其中 12 次是重复的。同族的老版强化储罐 `TileOldReinforcedTank` 是同一份代码的复制,同样 12 次重复。 ## 危害类与优先级 - `PERF`(每 tick 同参数重复求值、无局部承接)→ 标签 `type/perf` - `p3` 单次 `getBlock`/`getTileEntity` 开销小,但乘数是「每 tick × 每个储罐方块」;储罐塔常见(一列 5–20 格),且这是纯机械改动、行为完全不变 ## 证据(生效源码) - 载体:`TDLM/thaumicadditions` + 分支 `origin/main` + pin `cded7cd3ee59`(source 类仓,无 patch 层,代码可点开) - 位置:`src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-60`、`src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40` - 关键原文: ```java // TileTankBasic.java:27,31,35,39 四条 if 各自把同三个查询重算一遍(无局部承接、无节流门) if(!(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic) && !(this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 0) { if(!(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 1) { if(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic && !(this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic) && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 2) { if(this.worldObj.getBlock(this.xCoord, this.yCoord + 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlock(this.xCoord, this.yCoord - 1, this.zCoord) instanceof BlockTankBasic && this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != 3) { // TileTankBasic.java:53,56 同坐标 TE 查两次 boolean var4 = this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord) instanceof TileTankBasic && this.vis + this.taint < this.capacity; ... this.ejectVis(var5, ((ITubable)this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord)).acceptVis(...), ForgeDirection.UP); // TileTankBasic.java:59-60 同坐标 TE 查两次 if(this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord) instanceof TileTankBasic) { TileTankBasic var6 = (TileTankBasic)this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord); ``` - 计数(同一执行路径、同参数):`getBlock(y+1)` ×4、`getBlock(y-1)` ×4、`getBlockMetadata(x,y,z)` ×4、`getTileEntity(y+1)` ×2、`getTileEntity(y-1)` ×2 ⇒ **16 次查询里 12 次重复** - 上游状态:[x] 未修(upstream 同形,无可点开的 upstream 仓) ## 触发与复现 - 触发条件:任意储罐方块进入 tick 即执行;默认配置可达、无需玩家操作。 - [x] 静态推演:一列 10 格储罐 → 每 tick 160 次查询、其中 120 次是同一坐标的重复查询;20 tps 下 **3,200 次/秒**,冗余 2,400 次/秒。把 `above = world.getBlock(x, y+1, z)`、`below = world.getBlock(x, y-1, z)`、`meta = getBlockMetadata(x,y,z)`、`aboveTe`、`belowTe` 各取一次后复用,查询数降到 4 次/方块/tick,**且元数据判定结果逐条相同**。 - 包内相关性:模块 `thaumicadditions`/sides `client,server`/默认配置可达 是 ## 落点 - 主仓:`TDLM/thaumicadditions` → `src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-40`(四条 if 的重复取块)、`:53-60`(重复取 TE) - 协同:`src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40` 是同一份代码的复制,需同步改(只改一处等于没改) ## 范式 - 命中范式:`F4 帧内不变量重复求值`(同一调用内同参数查询重复 ≥2 次且无局部承接;此处是每 tick,非每帧) ## 引证 1. 生效源码 permalink — https://git.potatolab.uk/TDLM/thaumicadditions/src/commit/cded7cd3ee59/src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java#L27-L60 2. 同族文件 permalink — https://git.potatolab.uk/TDLM/thaumicadditions/src/commit/cded7cd3ee59/src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java#L17-L40 ## 来源 - 线索类型:内部代码审查(无外部报障) - 可解析指针:`src/main/java/com/pengu/thaumcraft/additions/tileentity/TileTankBasic.java:27-60`、`src/main/java/com/pengu/thaumcraft/additions/piping/old/tile/TileOldReinforcedTank.java:17-40`
layue13 added the
priority
p3
type
perf
origin/scanarea/performance
labels 2026-11-04 09:02:01 +00:00
Author
Owner

关闭,不改:TileTankBasic 每台每 tick 16 次区块查询,但开销很低且不在任何热点 profile 里;改写反编译的储罐代码没有实测收益。

关闭,不改:`TileTankBasic` 每台每 tick 16 次区块查询,但开销很低且不在任何热点 profile 里;改写反编译的储罐代码没有实测收益。
layue13 reopened this issue 2026-11-07 06:10:04 +00:00
layue13 reopened this issue 2026-11-07 17:59:38 +00:00
Author
Owner

Reopened by the closure re-check: part of this was fixed since (crops/singularity/tank meta), but TileTankBasic.updateEntity still read the neighbour tiles up to 3x/2x per tick. Lossless fix with an old-vs-new equivalence test: TDLM/thaumicadditions PR #32 (not merged yet).

Reopened by the closure re-check: part of this was fixed since (crops/singularity/tank meta), but TileTankBasic.updateEntity still read the neighbour tiles up to 3x/2x per tick. Lossless fix with an old-vs-new equivalence test: TDLM/thaumicadditions PR #32 (not merged yet).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: TDLM/thaumicadditions#20