InventoryAccessPortBlockEntity 的 InventoryAccessHandler 中,每个 IItemHandler 方法(getSlots、getStackInSlot、extractItem 等)都通过 getConnectedItemHandler() 重新调用 observedInventory.getInventory(),即每次都走一遍 LazyOptional 解析。
当 Create 的 Chute 连接 IAP 时,ItemHelper.extract 每 tick 遍历所有槽位,每个槽位触发多次 inventory 查询,每次都付出完整的解析开销。
性能数据(spark profiler,4000μs 采样间隔)
单个 Chute 连接 IAP(背后为 CombinedInvWrapper):~8ms/tick
IAP 相关帧累计 79 个,总耗时 18700μs(167s 采样内)
调用链:ChuteBlockEntity.tick → handleInput → ItemHelper.extract → IAP Handler → getConnectedItemHandler → LazyOptional.getValue(每次查询都走)
根因
private IItemHandler getConnectedItemHandler() {
if (powered) return null;
IItemHandler handler = observedInventory.getInventory(); // 每次调用都 resolve
if (handler instanceof WrappedItemHandler) return null;
return handler;
}
连接的容器在两次 updateConnectedInventory() 之间不会变化,无需每次重新 resolve。
次要问题
ThreadLocal recursionGuard:MC tick 是单线程的,普通 boolean 字段即可,省去每次调用 3 次 ThreadLocal 操作
preventRecursion 缺少 try-finally:若 value.get() 抛异常,guard 永远不释放
修复思路
// 1. 缓存 handler,仅在 updateConnectedInventory() 时标脏
private IItemHandler cachedHandler;
private boolean handlerDirty = true;
private IItemHandler getConnectedItemHandler() {
if (powered) return null;
if (handlerDirty) {
IItemHandler h = observedInventory.getInventory();
cachedHandler = (h instanceof WrappedItemHandler) ? null : h;
handlerDirty = false;
}
return cachedHandler;
}
// 在 updateConnectedInventory() 开头:
handlerDirty = true;
// 2. ThreadLocal → boolean + try-finally
private boolean inRecursion = false;
private <T> T preventRecursion(Supplier<T> value, T defaultValue) {
if (inRecursion) return defaultValue;
inRecursion = true;
try {
return value.get();
} finally {
inRecursion = false;
}
}
环境
Minecraft 1.20.1, Forge 47.4.10
Create 6.0.8, Create Connected 1.2.3
已在 GitHub main 分支确认问题仍存在
InventoryAccessPortBlockEntity 的 InventoryAccessHandler 中,每个 IItemHandler 方法(getSlots、getStackInSlot、extractItem 等)都通过 getConnectedItemHandler() 重新调用 observedInventory.getInventory(),即每次都走一遍 LazyOptional 解析。
当 Create 的 Chute 连接 IAP 时,ItemHelper.extract 每 tick 遍历所有槽位,每个槽位触发多次 inventory 查询,每次都付出完整的解析开销。
性能数据(spark profiler,4000μs 采样间隔)
单个 Chute 连接 IAP(背后为 CombinedInvWrapper):~8ms/tick
IAP 相关帧累计 79 个,总耗时 18700μs(167s 采样内)
调用链:ChuteBlockEntity.tick → handleInput → ItemHelper.extract → IAP Handler → getConnectedItemHandler → LazyOptional.getValue(每次查询都走)
根因
连接的容器在两次 updateConnectedInventory() 之间不会变化,无需每次重新 resolve。
次要问题
ThreadLocal recursionGuard:MC tick 是单线程的,普通 boolean 字段即可,省去每次调用 3 次 ThreadLocal 操作
preventRecursion 缺少 try-finally:若 value.get() 抛异常,guard 永远不释放
修复思路
环境
Minecraft 1.20.1, Forge 47.4.10
Create 6.0.8, Create Connected 1.2.3
已在 GitHub main 分支确认问题仍存在