Implement advanced caching of ingredient item stacks using soft references
This gives the GC control over when to evict the cache, which combines the benefits of removing the cache entirely with the speed of keeping it
This commit is contained in:
parent
7398b48345
commit
2535174e00
|
|
@ -7,6 +7,7 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
|||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import org.embeddedt.modernfix.forge.recipe.ExtendedIngredient;
|
||||
import org.embeddedt.modernfix.forge.recipe.IngredientItemStacksSoftReference;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -31,6 +32,8 @@ public abstract class IngredientMixin implements ExtendedIngredient {
|
|||
|
||||
@Shadow @Nullable private ItemStack[] itemStacks;
|
||||
|
||||
private volatile IngredientItemStacksSoftReference mfix$cachedItemStacks;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason tag ingredients can be tested without iterating over all items
|
||||
|
|
@ -95,9 +98,8 @@ public abstract class IngredientMixin implements ExtendedIngredient {
|
|||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason remove caching of the item stacks, it won't deduplicate anything with tags (since each Ingredient
|
||||
* instance would make new item stacks anyway, and storing them permanently takes up a lot of memory).
|
||||
* We implement an optimized version of some functions that avoids needing to call this entirely.
|
||||
* @reason Change caching of item stacks to use a soft reference, which allows the GC to evict the array under
|
||||
* memory pressure/when it hasn't been used.
|
||||
*/
|
||||
@Overwrite
|
||||
public ItemStack[] getItems() {
|
||||
|
|
@ -105,6 +107,19 @@ public abstract class IngredientMixin implements ExtendedIngredient {
|
|||
if (this.itemStacks != null) {
|
||||
return this.itemStacks;
|
||||
}
|
||||
var cache = this.mfix$cachedItemStacks;
|
||||
if (cache != null) {
|
||||
var stacks = cache.get();
|
||||
if (stacks != null) {
|
||||
return stacks;
|
||||
}
|
||||
}
|
||||
ItemStack[] result = computeItemsArray();
|
||||
this.mfix$cachedItemStacks = new IngredientItemStacksSoftReference((Ingredient)(Object)this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private ItemStack[] computeItemsArray() {
|
||||
// Fast path for case with one item
|
||||
if (this.values.length == 1) {
|
||||
if (this.values[0] instanceof Ingredient.ItemValue itemValue) {
|
||||
|
|
@ -131,4 +146,9 @@ public abstract class IngredientMixin implements ExtendedIngredient {
|
|||
}
|
||||
return itemList.toArray(ItemStack[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$clearReference() {
|
||||
this.mfix$cachedItemStacks = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@ package org.embeddedt.modernfix.forge.recipe;
|
|||
|
||||
public interface ExtendedIngredient {
|
||||
boolean mfix$hasNoElements();
|
||||
void mfix$clearReference();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package org.embeddedt.modernfix.forge.recipe;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
public class IngredientItemStacksSoftReference extends SoftReference<ItemStack[]> {
|
||||
private final Ingredient ingredient;
|
||||
|
||||
private static final ReferenceQueue<ItemStack[]> QUEUE = new ReferenceQueue<>();
|
||||
private static final Thread DISCARD_THREAD = new Thread(IngredientItemStacksSoftReference::clearReferences, "Ingredient reference clearing thread");
|
||||
|
||||
static {
|
||||
DISCARD_THREAD.setPriority(Thread.NORM_PRIORITY + 2);
|
||||
DISCARD_THREAD.setDaemon(true);
|
||||
DISCARD_THREAD.start();
|
||||
}
|
||||
|
||||
public IngredientItemStacksSoftReference(Ingredient ingredient, ItemStack[] stacks) {
|
||||
super(stacks, QUEUE);
|
||||
this.ingredient = ingredient;
|
||||
}
|
||||
|
||||
private static void clearReferences() {
|
||||
while (true) {
|
||||
Reference<? extends ItemStack[]> ref;
|
||||
try {
|
||||
ref = QUEUE.remove();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
if (ref instanceof IngredientItemStacksSoftReference ingRef && ingRef.ingredient instanceof ExtendedIngredient extIng) {
|
||||
// Null out the reference to the SoftReference object, to allow the SoftReference itself to be garbage collected.
|
||||
extIng.mfix$clearReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user