From 21b803f324ade7b357998c1f712df538b244c4b9 Mon Sep 17 00:00:00 2001 From: Glease <4586901+Glease@users.noreply.github.com> Date: Thu, 1 Sep 2022 19:25:03 +0800 Subject: various loading time optimization (#1330) * change to more performant GT_ItemStack2 in some maps The exact performance improvement of this is hard to measure as it impacts both startup time and tick time, but in general identityHashCode should be faster than retrieving stuff from registry. * improve performance for registerUsagesForMaterials index the recipe list based on recipe type, so we no longer do 16*44*444 recipe match. this indexing reduced the total runtime from ~12 second to ~3.7 second. * guess this not belongs here * move type check earlier * more GT_ItemStack2 Co-authored-by: Martin Robertz --- src/main/java/gregtech/api/GregTech_API.java | 2 +- .../gregtech/api/util/GT_OreDictUnificator.java | 24 ++-- .../gregtech/api/util/GT_RecipeRegistrator.java | 127 ++++++++++++++++++++- src/main/java/gregtech/api/util/GT_Utility.java | 28 ++--- 4 files changed, 151 insertions(+), 30 deletions(-) (limited to 'src/main/java/gregtech/api') diff --git a/src/main/java/gregtech/api/GregTech_API.java b/src/main/java/gregtech/api/GregTech_API.java index 56e80d4971..b565190507 100644 --- a/src/main/java/gregtech/api/GregTech_API.java +++ b/src/main/java/gregtech/api/GregTech_API.java @@ -99,7 +99,7 @@ public class GregTech_API { /** * Fixes the HashMap Mappings for ItemStacks once the Server started */ - public static final Collection> sItemStackMappings = new ArrayList<>(); + public static final Collection> sItemStackMappings = new ArrayList<>(); public static final Collection> sFluidMappings = new ArrayList<>(); /** diff --git a/src/main/java/gregtech/api/util/GT_OreDictUnificator.java b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java index 81038645e9..48882bb72f 100644 --- a/src/main/java/gregtech/api/util/GT_OreDictUnificator.java +++ b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java @@ -9,16 +9,18 @@ import gregtech.api.enums.Dyes; import gregtech.api.enums.Materials; import gregtech.api.enums.OrePrefixes; import gregtech.api.enums.SubTag; -import gregtech.api.objects.GT_HashSet; import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.GT_ItemStack2; import gregtech.api.objects.ItemData; import gregtech.api.objects.MaterialStack; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import javax.annotation.Nullable; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -35,9 +37,9 @@ import net.minecraftforge.oredict.OreDictionary; */ public class GT_OreDictUnificator { private static final Map sName2StackMap = new HashMap<>(); - private static final Map sItemStack2DataMap = new HashMap<>(); - private static final Map> sUnificationTable = new HashMap<>(); - private static final GT_HashSet sNoUnificationList = new GT_HashSet<>(); + private static final Map sItemStack2DataMap = new HashMap<>(); + private static final Map> sUnificationTable = new HashMap<>(); + private static final Set sNoUnificationList = new HashSet<>(); public static volatile int VERSION = 509; private static int isRegisteringOre = 0, isAddingOre = 0; private static boolean mRunThroughTheList = true; @@ -53,7 +55,7 @@ public class GT_OreDictUnificator { */ public static void addToBlacklist(ItemStack aStack) { if (GT_Utility.isStackValid(aStack) && !GT_Utility.isStackInList(aStack, sNoUnificationList)) - sNoUnificationList.add(aStack); + sNoUnificationList.add(new GT_ItemStack2(aStack)); } public static boolean isBlacklisted(ItemStack aStack) { @@ -250,7 +252,7 @@ public class GT_OreDictUnificator { ItemStack tStack0 = tGTStack0.toStack(); ItemStack tStack1 = get_nocopy(false, tStack0); if (!GT_Utility.areStacksEqual(tStack0, tStack1)) { - GT_ItemStack tGTStack1 = new GT_ItemStack(tStack1); + GT_ItemStack2 tGTStack1 = new GT_ItemStack2(tStack1); List list = sUnificationTable.computeIfAbsent(tGTStack1, k -> new ArrayList<>()); // greg's original code tries to dedupe the list using List#contains, which won't work // on vanilla ItemStack. I removed it since it never worked and can be slow. @@ -267,7 +269,7 @@ public class GT_OreDictUnificator { List rList = new ArrayList<>(); for (ItemStack aStack : aStacks) { rList.add(aStack); - List tList = sUnificationTable.get(new GT_ItemStack(aStack)); + List tList = sUnificationTable.get(new GT_ItemStack2(aStack)); if (tList != null) { for (ItemStack tStack : tList) { ItemStack tStack1 = GT_Utility.copyAmount(aStack.stackSize, tStack); @@ -294,7 +296,7 @@ public class GT_OreDictUnificator { for (MaterialStack tMaterial : aData.mByProducts) tMaterial.mAmount /= aStack.stackSize; aStack = GT_Utility.copyAmount(1, aStack); } - sItemStack2DataMap.put(new GT_ItemStack(aStack), aData); + sItemStack2DataMap.put(new GT_ItemStack2(aStack), aData); if (aData.hasValidMaterialData()) { long tValidMaterialAmount = aData.mMaterial.mMaterial.contains(SubTag.NO_RECYCLING) ? 0 @@ -308,7 +310,7 @@ public class GT_OreDictUnificator { if (mRunThroughTheList) { if (GregTech_API.sLoadStarted) { mRunThroughTheList = false; - for (Entry tEntry : sItemStack2DataMap.entrySet()) + for (Entry tEntry : sItemStack2DataMap.entrySet()) if (!tEntry.getValue().hasValidPrefixData() || tEntry.getValue().mPrefix.mAllowNormalRecycling) GT_RecipeRegistrator.registerMaterialRecycling( tEntry.getKey().toStack(), tEntry.getValue()); @@ -335,8 +337,8 @@ public class GT_OreDictUnificator { public static ItemData getItemData(ItemStack aStack) { if (GT_Utility.isStackInvalid(aStack)) return null; - ItemData rData = sItemStack2DataMap.get(new GT_ItemStack(aStack)); - if (rData == null) rData = sItemStack2DataMap.get(new GT_ItemStack(aStack, true)); + ItemData rData = sItemStack2DataMap.get(new GT_ItemStack2(aStack)); + if (rData == null) rData = sItemStack2DataMap.get(new GT_ItemStack2(aStack, true)); return rData; } diff --git a/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java index 1b1654d8a6..a01ee332ae 100644 --- a/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java +++ b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java @@ -4,17 +4,25 @@ import static gregtech.api.enums.GT_Values.*; import static gregtech.api.enums.Materials.*; import static gregtech.api.enums.Materials.Void; +import com.google.common.collect.ImmutableList; import gregtech.GT_Mod; import gregtech.api.GregTech_API; import gregtech.api.enums.*; import gregtech.api.objects.ItemData; import gregtech.api.objects.MaterialStack; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import ic2.api.reactor.IReactorComponent; +import java.util.*; +import java.util.stream.Collectors; import net.minecraft.init.Blocks; import net.minecraft.init.Items; +import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; /** * Class for Automatic Recipe registering. @@ -73,6 +81,7 @@ public class GT_RecipeRegistrator { new RecipeShape(sMt1, sMt1, null, sMt2, null, sMt1, sMt2, null, null), new RecipeShape(null, sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2) }; + private static volatile Map> indexedRecipeListCache; private static final String[][] sShapesA = new String[][] { null, null, @@ -121,6 +130,11 @@ public class GT_RecipeRegistrator { }; public static volatile int VERSION = 509; + static { + // flush the cache on post load finish + GregTech_API.sAfterGTPostload.add(() -> indexedRecipeListCache = null); + } + public static void registerMaterialRecycling( ItemStack aStack, Materials aMaterial, long aMaterialAmount, MaterialStack aByproduct) { if (GT_Utility.isStackInvalid(aStack)) return; @@ -445,6 +459,102 @@ public class GT_RecipeRegistrator { } } + private static List getRecipeList(RecipeShape shape) { + boolean force = !GregTech_API.sPostloadStarted || GregTech_API.sPostloadFinished; + if (force || indexedRecipeListCache == null) { + synchronized (GT_RecipeRegistrator.class) { + if (indexedRecipeListCache == null || force) { + indexedRecipeListCache = createIndexedRecipeListCache(); + } + } + } + return indexedRecipeListCache.get(shape); + } + + private static Map> createIndexedRecipeListCache() { + Map> result = new IdentityHashMap<>(); + @SuppressWarnings("unchecked") + ArrayList allRecipeList = + (ArrayList) CraftingManager.getInstance().getRecipeList(); + // filter using the empty slots in the shape. + // if the empty slots doesn't match, the recipe will definitely fail + Map, List> filter = + Arrays.stream(sShapes).collect(Collectors.groupingBy(RecipeShape::getEmptySlots)); + List x = new ArrayList<>(9); + for (IRecipe tRecipe : allRecipeList) { + if (tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe) { + // we don't target shapeless recipes + continue; + } + x.clear(); + ItemStack tStack = tRecipe.getRecipeOutput(); + if (GT_Utility.isStackValid(tStack) + && tStack.getMaxStackSize() == 1 + && tStack.getMaxDamage() > 0 + && !(tStack.getItem() instanceof ItemBlock) + && !(tStack.getItem() instanceof IReactorComponent) + && !GT_ModHandler.isElectricItem(tStack) + && !GT_Utility.isStackInList(tStack, GT_ModHandler.sNonReplaceableItems)) { + if (tRecipe instanceof ShapedOreRecipe) { + boolean allValid = true; + Object[] input = ((ShapedOreRecipe) tRecipe).getInput(); + for (int i = 0, inputLength = input.length; i < inputLength; i++) { + Object tObject = input[i]; + if (tObject != null) { + if (tObject instanceof ItemStack + && (((ItemStack) tObject).getItem() == null + || ((ItemStack) tObject).getMaxStackSize() < 2 + || ((ItemStack) tObject).getMaxDamage() > 0 + || ((ItemStack) tObject).getItem() instanceof ItemBlock)) { + allValid = false; + break; + } + if (tObject instanceof List && ((List) tObject).isEmpty()) { + allValid = false; + break; + } + } else { + x.add(i); + } + } + if (allValid) { + for (RecipeShape s : filter.getOrDefault(x, Collections.emptyList())) { + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } else if (tRecipe instanceof ShapedRecipes) { + boolean allValid = true; + ItemStack[] recipeItems = ((ShapedRecipes) tRecipe).recipeItems; + for (int i = 0, recipeItemsLength = recipeItems.length; i < recipeItemsLength; i++) { + ItemStack tObject = recipeItems[i]; + if (tObject != null + && (tObject.getItem() == null + || tObject.getMaxStackSize() < 2 + || tObject.getMaxDamage() > 0 + || tObject.getItem() instanceof ItemBlock)) { + allValid = false; + break; + } else { + x.add(i); + } + } + if (allValid) { + for (RecipeShape s : filter.getOrDefault(x, Collections.emptyList())) { + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } else { + for (RecipeShape s : sShapes) { + // unknown recipe type. cannot determine empty slots. we choose to add to the recipe list for + // all shapes + result.computeIfAbsent(s, k -> new ArrayList<>()).add(tRecipe); + } + } + } + } + return result; + } + private static synchronized void registerStickStuff(String aPlate, ItemData aItemData, boolean aRecipeReplacing) { ItemStack tStack; for (Materials tMaterial : sRodMaterialList) { @@ -457,7 +567,8 @@ public class GT_RecipeRegistrator { for (int i = 0; i < sShapes.length; i++) { RecipeShape tRecipe = sShapes[i]; - for (ItemStack tCrafted : GT_ModHandler.getVanillyToolRecipeOutputs(tRecipe.shape)) { + for (ItemStack tCrafted : + GT_ModHandler.getRecipeOutputs(getRecipeList(tRecipe), true, tRecipe.shape)) { if (aItemData != null && aItemData.hasValidPrefixMaterialData()) GT_OreDictUnificator.addItemData( tCrafted, @@ -547,5 +658,13 @@ public class GT_RecipeRegistrator { if (stack == sMt2) this.amount2++; } } + + public List getEmptySlots() { + ImmutableList.Builder b = ImmutableList.builder(); + for (int i = 0; i < shape.length; i++) { + if (shape[i] == null) b.add(i); + } + return b.build(); + } } } diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java index 50106795eb..b35b703ab9 100644 --- a/src/main/java/gregtech/api/util/GT_Utility.java +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -44,6 +44,7 @@ import gregtech.api.items.GT_MetaGenerated_Tool; import gregtech.api.net.GT_Packet_Sound; import gregtech.api.objects.CollectorUtils; import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.GT_ItemStack2; import gregtech.api.objects.ItemData; import gregtech.api.threads.GT_Runnable_Sound; import gregtech.api.util.extensions.ArrayExt; @@ -60,21 +61,8 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; @@ -2933,11 +2921,23 @@ public class GT_Utility { return isStackInList(new GT_ItemStack(aStack), aList); } + public static boolean isStackInList(ItemStack aStack, Set aList) { + if (aStack == null) { + return false; + } + return isStackInList(new GT_ItemStack2(aStack), aList); + } + public static boolean isStackInList(GT_ItemStack aStack, Collection aList) { return aStack != null && (aList.contains(aStack) || aList.contains(new GT_ItemStack(aStack.mItem, aStack.mStackSize, W))); } + public static boolean isStackInList(GT_ItemStack2 aStack, Set aList) { + return aStack != null + && (aList.contains(aStack) || aList.contains(new GT_ItemStack2(aStack.mItem, aStack.mStackSize, W))); + } + /** * re-maps all Keys of a Map after the Keys were weakened. */ -- cgit