From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: 2No2Name <50278648+2No2Name@users.noreply.github.com> Date: Mon, 15 Feb 2021 14:58:55 -0500 Subject: [PATCH] lithium: optimize `BlockPos.iterateOutwards` by caching offsets Code taken from: https://github.com/CaffeineMC/lithium-fabric/pull/123 by 2No2Name licenced under the LGPLv3 License diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..50e9df6056f713b2f1eaf0cb4a23875e0c6c2e2c --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java @@ -0,0 +1,72 @@ +package me.jellysquid.mods.lithium.common.cached_blockpos_iteration; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; +import net.minecraft.core.BlockPosition; + +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @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 BlockPosition POS_ZERO = new BlockPosition(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 (BlockPosition pos : BlockPosition.iterateOutwards(POS_ZERO, xRange, yRange, zRange)) { + entry.add(pos.asLong()); + } + } + + public LongList getOrCompute(int xRange, int yRange, int zRange) { + long key = BlockPosition.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/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java new file mode 100644 index 0000000000000000000000000000000000000000..e2e4f7968a399b4641df07b2931fff6dbc85ddb3 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java @@ -0,0 +1,47 @@ +package me.jellysquid.mods.lithium.common.cached_blockpos_iteration; + +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongList; +import net.minecraft.core.BlockPosition; + +import java.util.Iterator; + +/** + * @author 2No2Name + */ +public class LongList2BlockPosMutableIterable implements Iterable { + + private final LongList positions; + private final int xOffset, yOffset, zOffset; + + public LongList2BlockPosMutableIterable(BlockPosition 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 BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public BlockPosition next() { + long nextPos = this.it.nextLong(); + return this.pos.setValues( + LongList2BlockPosMutableIterable.this.xOffset + BlockPosition.unpackLongX(nextPos), + LongList2BlockPosMutableIterable.this.yOffset + BlockPosition.unpackLongY(nextPos), + LongList2BlockPosMutableIterable.this.zOffset + BlockPosition.unpackLongZ(nextPos)); + } + }; + } + +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/core/BlockPosition.java b/src/main/java/net/minecraft/core/BlockPosition.java index 092de56e31257c1817a04cc9fc2f9c300bbfa058..bf2b1b19b7fa348c5fee61dd0e58144e2c06072f 100644 --- a/src/main/java/net/minecraft/core/BlockPosition.java +++ b/src/main/java/net/minecraft/core/BlockPosition.java @@ -18,10 +18,16 @@ import net.minecraft.world.phys.Vec3D; import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache; // Yatopia +import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.LongList2BlockPosMutableIterable; // Yatopia +import it.unimi.dsi.fastutil.longs.LongList; // Yatopia @Immutable public class BlockPosition extends BaseBlockPosition { + private static final IterateOutwardsCache ITERATE_OUTWARDS_CACHE = new IterateOutwardsCache(50); // Yatopia + private static final LongList HOGLIN_PIGLIN_CACHE = ITERATE_OUTWARDS_CACHE.getOrCompute(8, 4, 8); // Yatopia + public static final Codec a = Codec.INT_STREAM.comapFlatMap((intstream) -> { return SystemUtils.a(intstream, 3).map((aint) -> { return new BlockPosition(aint[0], aint[1], aint[2]); @@ -77,14 +83,17 @@ public class BlockPosition extends BaseBlockPosition { return a((int) (i >> 38) + j, (int) ((i << 52) >> 52) + k, (int) ((i << 26) >> 38) + l); // Paper - simplify/inline } + public static int unpackLongX(long i) { return BlockPosition.b(i); } // Yatopia - OBFHELPER public static int b(long i) { return (int) (i >> 38); // Paper - simplify/inline } + public static int unpackLongY(long i) { return BlockPosition.c(i); } // Yatopia - OBFHELPER public static int c(long i) { return (int) ((i << 52) >> 52); // Paper - simplify/inline } + public static int unpackLongZ(long i) { return BlockPosition.d(i); } // Yatopia - OBFHELPER public static int d(long i) { return (int) ((i << 26) >> 38); // Paper - simplify/inline } @@ -265,7 +274,15 @@ public class BlockPosition extends BaseBlockPosition { }; } + public static Iterable iterateOutwards(BlockPosition blockposition, int p_i, int p_j, int p_k) { return BlockPosition.a(blockposition, p_i, p_j, p_k); } // Yatopia - OBFHELPER public static Iterable a(BlockPosition blockposition, int p_i, int p_j, int p_k) { // Paper - decompile issues - variable name conflicts to inner class field refs + // Yatopia start - lithium: optimize `BlockPos.iterateOutwards` by caching offsets + if (blockposition != me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache.POS_ZERO) { + final LongList positions = p_i == 8 && p_j == 4 && p_k == 8 ? HOGLIN_PIGLIN_CACHE : ITERATE_OUTWARDS_CACHE.getOrCompute(p_i, p_j, p_j); + return new LongList2BlockPosMutableIterable(blockposition, positions); + } + // Yatopia end + int l_decompiled = p_i + p_j + p_k; // Paper - decompile issues int i1 = blockposition.getX(); int j1 = blockposition.getY();