From d3d338e64b872094abb50a33ae418de8d13d735b Mon Sep 17 00:00:00 2001 From: JozsefA Date: Fri, 26 Mar 2021 22:02:19 -0700 Subject: [PATCH] STE tick optimization. - SmartTileEntity#tick down to 3.32% from 11.64% cpu time. - Measured by JFR over 2 sessions each >8min. - Test world was the deployer fields. - Iterating over HashMap values is slow. - Collect TileEntityBehaviours into a list when the contents of SmartTileEntity#behaviours changes. --- .../tileEntity/SmartTileEntity.java | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java index 9404b3544..37605d859 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java @@ -17,7 +17,9 @@ import net.minecraftforge.items.CapabilityItemHandler; public abstract class SmartTileEntity extends SyncedTileEntity implements ITickableTileEntity { - private Map, TileEntityBehaviour> behaviours; + private final Map, TileEntityBehaviour> behaviours; + // Internally maintained to be identical to behaviorMap.values() in order to improve iteration performance. + private final List behaviourList; private boolean initialized; private boolean firstNbtRead; private int lazyTickRate; @@ -36,6 +38,9 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka ArrayList list = new ArrayList<>(); addBehaviours(list); list.forEach(b -> behaviours.put(b.getType(), b)); + + behaviourList = new ArrayList<>(list.size()); + updateBehaviorList(); } public abstract void addBehaviours(List behaviours); @@ -58,13 +63,11 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka lazyTick(); } - behaviours.values() - .forEach(TileEntityBehaviour::tick); + behaviourList.forEach(TileEntityBehaviour::tick); } public void initialize() { - behaviours.values() - .forEach(TileEntityBehaviour::initialize); + behaviourList.forEach(TileEntityBehaviour::initialize); lazyTick(); } @@ -99,10 +102,11 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka ArrayList list = new ArrayList<>(); addBehavioursDeferred(list); list.forEach(b -> behaviours.put(b.getType(), b)); + + updateBehaviorList(); } super.read(compound); - behaviours.values() - .forEach(tb -> tb.read(compound, clientPacket)); + behaviourList.forEach(tb -> tb.read(compound, clientPacket)); } /** @@ -110,8 +114,7 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka */ protected void write(CompoundNBT compound, boolean clientPacket) { super.write(compound); - behaviours.values() - .forEach(tb -> tb.write(compound, clientPacket)); + behaviourList.forEach(tb -> tb.write(compound, clientPacket)); } @Override @@ -130,19 +133,31 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka } protected void forEachBehaviour(Consumer action) { - behaviours.values() - .forEach(action); + behaviourList.forEach(action); } protected void attachBehaviourLate(TileEntityBehaviour behaviour) { behaviours.put(behaviour.getType(), behaviour); behaviour.initialize(); + + updateBehaviorList(); } protected void removeBehaviour(BehaviourType type) { TileEntityBehaviour remove = behaviours.remove(type); - if (remove != null) + if (remove != null) { remove.remove(); + updateBehaviorList(); + } + } + + // We don't trust the input to the API will be sane, so we + // update all the contents whenever something changes. It's + // simpler than trying to manipulate the list one element at + // a time. + private void updateBehaviorList() { + behaviourList.clear(); + behaviourList.addAll(behaviours.values()); } @SuppressWarnings("unchecked")