From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 8 Nov 2024 00:06:34 +0100 Subject: [PATCH] Lithium: IterateOutwardsCache By: 2No2Name <2No2Name@web.de> As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java index a64e5997b94cc8173f0512d1e282355f14f098ec..1d0f67e10a061225dfe99cbd935c4fb5a4cbe22b 100644 --- a/src/main/java/net/minecraft/core/BlockPos.java +++ b/src/main/java/net/minecraft/core/BlockPos.java @@ -343,7 +343,19 @@ public class BlockPos extends Vec3i { }; } + // JettPack start - lithium: cached iterate outwards + private static final org.dreeam.leaf.util.cache.IterateOutwardsCache ITERATE_OUTWARDS_CACHE = new org.dreeam.leaf.util.cache.IterateOutwardsCache(50); + private static final it.unimi.dsi.fastutil.longs.LongList HOGLIN_PIGLIN_CACHE = ITERATE_OUTWARDS_CACHE.getOrCompute(8, 4, 8); + // JettPack end + + public static Iterable withinManhattan(BlockPos center, int rangeX, int rangeY, int rangeZ) { + // JettPack start - lithium: cached iterate outwards + if (center != org.dreeam.leaf.util.cache.IterateOutwardsCache.POS_ZERO) { + final it.unimi.dsi.fastutil.longs.LongList positions = rangeX == 8 && rangeY == 4 && rangeZ == 8 ? HOGLIN_PIGLIN_CACHE : ITERATE_OUTWARDS_CACHE.getOrCompute(rangeX, rangeY, rangeZ); + return new org.dreeam.leaf.util.cache.LongList2BlockPosMutableIterable(center, positions); + } + // JettPack end int i = rangeX + rangeY + rangeZ; // Paper start - rename variables to fix conflict with anonymous class (remap fix) int centerX = center.getX(); diff --git a/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java b/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..9ae6b0a5e1e1c8584a982b1dbd65da3f2de38eaa --- /dev/null +++ b/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java @@ -0,0 +1,71 @@ +package org.dreeam.leaf.util.cache; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import net.minecraft.core.BlockPos; + +/** + * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000 + */ +public class IterateOutwardsCache { + //POS_ZERO must not be replaced with BlockPos.ORIGIN, otherwise iterateOutwards at BlockPos.ORIGIN will not use the cache + public static final BlockPos POS_ZERO = new BlockPos(0,0,0); + + + private final ConcurrentHashMap table; + private final int capacity; + private final Random random; + + public IterateOutwardsCache(int capacity) { + this.capacity = capacity; + this.table = new ConcurrentHashMap<>(31); + this.random = new Random(); + } + + private void fillPositionsWithIterateOutwards(LongList entry, int xRange, int yRange, int zRange) { + // Add all positions to the cached list + for (BlockPos pos : BlockPos.withinManhattan(POS_ZERO, xRange, yRange, zRange)) { + entry.add(pos.asLong()); + } + } + + public LongList getOrCompute(int xRange, int yRange, int zRange) { + long key = BlockPos.asLong(xRange, yRange, zRange); + + LongArrayList entry = this.table.get(key); + if (entry != null) { + return entry; + } + + // Cache miss: compute and store + entry = new LongArrayList(128); + + this.fillPositionsWithIterateOutwards(entry, xRange, yRange, zRange); + + //decrease the array size, as of now it won't be modified anymore anyways + entry.trim(); + + //this might overwrite an entry as the same entry could have been computed and added during this thread's computation + //we do not use computeIfAbsent, as it can delay other threads for too long + Object previousEntry = this.table.put(key, entry); + + + if (previousEntry == null && this.table.size() > this.capacity) { + //prevent a memory leak by randomly removing about 1/8th of the elements when the exceed the desired capacity is exceeded + final Iterator iterator = this.table.keySet().iterator(); + //prevent an unlikely infinite loop caused by another thread filling the table concurrently using counting + for (int i = -this.capacity; iterator.hasNext() && i < 5; i++) { + Long key2 = iterator.next(); + //random is not threadsafe, but it doesn't matter here, because we don't need quality random numbers + if (this.random.nextInt(8) == 0 && key2 != key) { + iterator.remove(); + } + } + } + + return entry; + } +} \ No newline at end of file diff --git a/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java b/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java new file mode 100644 index 0000000000000000000000000000000000000000..bdc1e6a9093dd403e9acacf1ffea873c4738f8ec --- /dev/null +++ b/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java @@ -0,0 +1,46 @@ +package org.dreeam.leaf.util.cache; + +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongList; +import java.util.Iterator; +import net.minecraft.core.BlockPos; + +/** + * @author 2No2Name + */ +public class LongList2BlockPosMutableIterable implements Iterable { + + private final LongList positions; + private final int xOffset, yOffset, zOffset; + + public LongList2BlockPosMutableIterable(BlockPos offset, LongList posList) { + this.xOffset = offset.getX(); + this.yOffset = offset.getY(); + this.zOffset = offset.getZ(); + this.positions = posList; + } + + @Override + public Iterator iterator() { + return new Iterator() { + + private final LongIterator it = LongList2BlockPosMutableIterable.this.positions.iterator(); + private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public net.minecraft.core.BlockPos next() { + long nextPos = this.it.nextLong(); + return this.pos.set( + LongList2BlockPosMutableIterable.this.xOffset + BlockPos.getX(nextPos), + LongList2BlockPosMutableIterable.this.yOffset + BlockPos.getY(nextPos), + LongList2BlockPosMutableIterable.this.zOffset + BlockPos.getZ(nextPos)); + } + }; + } + +} \ No newline at end of file