Logistics Pipes

Logistics Pipes

13M Downloads

"Cache" SlotMapCache

sameer opened this issue ยท 8 comments

commented

Hi, see below for an explanation and a patchfile with the code.

Store the slot map cache in a WeakHashMap so that it does not need to be recreated every time.
There will be no memory leak - the object referent will be garbage collected if the ISidedInventory has no other references. This has significantly improved performance on my server.

diff --git a/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java b/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
index 5b0ccfb..0849a9b 100644
--- a/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
+++ b/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
@@ -8,7 +8,7 @@

 package logisticspipes.utils;

-import java.util.ArrayList;
+import java.util.*;

 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.inventory.IInventory;
@@ -32,10 +32,13 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {
        private final boolean _forExtraction;
        private int[] _slotMapCache;

+       private WeakHashMap<ISidedInventory,int[]> slotted = new WeakHashMap<ISidedInventory,int[]>();
+
        public SidedInventoryMinecraftAdapter(ISidedInventory sidedInventory, ForgeDirection side, boolean forExtraction) {
                _sidedInventory = sidedInventory;
                _side = side.ordinal();
                _forExtraction = forExtraction;
+               _slotMapCache = slotted.get(sidedInventory);
        }

        private int[] getSlotMap() {
@@ -55,6 +58,7 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {
                                }
                                _slotMapCache = Ints.toArray(list);
                        }
+                       slotted.put(_sidedInventory, _slotMapCache);
                }
                return _slotMapCache;
        }
commented

Updated diff with more optimizations

diff --git a/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java b/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
index 5b0ccfb..f1b3b03 100644
--- a/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
+++ b/common/logisticspipes/utils/SidedInventoryMinecraftAdapter.java
@@ -8,7 +8,7 @@

 package logisticspipes.utils;

-import java.util.ArrayList;
+import java.util.*;

 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.inventory.IInventory;
@@ -30,22 +30,27 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {
        public final ISidedInventory _sidedInventory;
        private final int _side;
        private final boolean _forExtraction;
-       private int[] _slotMapCache;
+       private ArrayList<Integer> _slotMapCache;
+
+       private WeakHashMap<ISidedInventory,ArrayList<Integer>> slotted = new WeakHashMap<ISidedInventory,ArrayList<Integer>>();

        public SidedInventoryMinecraftAdapter(ISidedInventory sidedInventory, ForgeDirection side, boolean forExtraction) {
                _sidedInventory = sidedInventory;
                _side = side.ordinal();
                _forExtraction = forExtraction;
+               _slotMapCache = slotted.get(sidedInventory);
        }

-       private int[] getSlotMap() {
+       private ArrayList<Integer> getSlotMap() {
                if(_slotMapCache == null) {
                        if (_side == ForgeDirection.UNKNOWN.ordinal()) {
                                _slotMapCache = buildAllSidedMap();
                        } else {
-                               ArrayList<Integer> list = new ArrayList<Integer>();

                                int allSlots[] = _sidedInventory.getAccessibleSlotsFromSide(_side);
+
+                               ArrayList<Integer> list = new ArrayList<Integer>(allSlots.length);
+
                                for (int number : allSlots) {
                                        ItemStack item = _sidedInventory.getStackInSlot(number);
                                        if (!list.contains(number) && (!_forExtraction || // check extract condition
@@ -53,17 +58,23 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {
                                                list.add(number);
                                        }
                                }
-                               _slotMapCache = Ints.toArray(list);
+                               _slotMapCache = list;
                        }
+                       slotted.put(_sidedInventory, _slotMapCache);
                }
                return _slotMapCache;
        }

-       private int[] buildAllSidedMap() {
-               ArrayList<Integer> list = new ArrayList<Integer>();
-
+       private ArrayList<Integer> buildAllSidedMap() {
+               int[][] slotty = new int[6][];
+               int size = 0;
                for (int i = 0; i < 6; i++) {
-                       int slots[] = _sidedInventory.getAccessibleSlotsFromSide(i);
+                       slotty[i] = _sidedInventory.getAccessibleSlotsFromSide(i);
+                       if(slotty[i] != null) size += slotty[i].length;
+               }
+               ArrayList<Integer> list = new ArrayList<Integer>(size);
+               for (int i = 0; i < 6; i++) {
+                       int[] slots = slotty[i];
                        for (int number : slots) {
                                ItemStack item = _sidedInventory.getStackInSlot(number);
                                if (!list.contains(number) && (!_forExtraction || // check extract condition
@@ -72,32 +83,27 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {
                                }
                        }
                }
-               int slotmap[] = new int[list.size()];
-               int count = 0;
-               for (int i : list) {
-                       slotmap[count++] = i;
-               }
-               return slotmap;
+               return list;
        }

        @Override
        public int getSizeInventory() {
-               return getSlotMap().length;
+               return getSlotMap().size();
        }

        @Override
        public ItemStack getStackInSlot(int i) {
-               return _sidedInventory.getStackInSlot(getSlotMap()[i]);
+               return _sidedInventory.getStackInSlot(getSlotMap().get(i));
        }

        @Override
        public ItemStack decrStackSize(int i, int j) {
-               return _sidedInventory.decrStackSize(getSlotMap()[i], j);
+               return _sidedInventory.decrStackSize(getSlotMap().get(i), j);
        }

        @Override
        public void setInventorySlotContents(int i, ItemStack itemstack) {
-               _sidedInventory.setInventorySlotContents(getSlotMap()[i], itemstack);
+               _sidedInventory.setInventorySlotContents(getSlotMap().get(i), itemstack);
        }

        @Override
@@ -132,7 +138,7 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {

        @Override
        public ItemStack getStackInSlotOnClosing(int slot) {
-               return _sidedInventory.getStackInSlotOnClosing(getSlotMap()[slot]);
+               return _sidedInventory.getStackInSlotOnClosing(getSlotMap().get(slot));
        }

        @Override
@@ -142,6 +148,6 @@ public final class SidedInventoryMinecraftAdapter implements IInventory {

        @Override
        public boolean isItemValidForSlot(int slot, ItemStack itemstack) {
-               return _sidedInventory.isItemValidForSlot(getSlotMap()[slot], itemstack) && _sidedInventory.canInsertItem(getSlotMap()[slot], itemstack, _side);
+               return _sidedInventory.isItemValidForSlot(getSlotMap().get(slot), itemstack) && _sidedInventory.canInsertItem(getSlotMap().get(slot), itemstack, _side);
        }
 }
commented

What about all those dynamic implementations that change their accessability? Like ThermalExpansion Machines?

commented

0.o I was not aware that that was possible. This would probably not work in those cases then.

My worry was that though there is a slotmapcache, it is recreated every time the object is instantiated...so it isn't really a cache.

commented

There is of course a way to work around that, but the question is if it is still a performance improvement.

commented

It is a cache because it is not recreated for every slot access.
And for the next time, better create a pull request because this is rather hard to read.
How much performance improvement did you get ?

commented

While this is at 20TPS, it oddly gets a lot worse with many extractor modules in crafting chassis connected to ProjectE condensers. Though this is with the fix, the performance is still a bit poorer than I had hoped it would be. ๐Ÿ˜•

And concerning the PR sorry about that I should've done it instead of this.

untitled

commented

@davboecki how about rebuilding the SlotMapCache upon a onNeighborChange() event wich gest fired towards the Pipe?
Wouldnt that help with Dynamic Implementations?

commented

Last time I checked when changing the sideness, TE machines don't call a block change, so that wouldn't help.