aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch')
-rw-r--r--patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch343
1 files changed, 343 insertions, 0 deletions
diff --git a/patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch b/patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch
new file mode 100644
index 0000000000..52bdb3212c
--- /dev/null
+++ b/patches/server/1021-Custom-table-implementation-for-blockstate-state-loo.patch
@@ -0,0 +1,343 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Thu, 11 Mar 2021 20:05:44 -0800
+Subject: [PATCH] Custom table implementation for blockstate state lookups
+
+Testing some redstone intensive machines showed to bring about a 10%
+improvement.
+
+diff --git a/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..57d0cd3ad6f972e986c72a57f1a6e36003f190c2
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
+@@ -0,0 +1,160 @@
++package io.papermc.paper.util.table;
++
++import com.google.common.collect.Table;
++import net.minecraft.world.level.block.state.StateHolder;
++import net.minecraft.world.level.block.state.properties.Property;
++import java.util.Collection;
++import java.util.HashSet;
++import java.util.Map;
++import java.util.Set;
++
++public final class ZeroCollidingReferenceStateTable {
++
++ // upper 32 bits: starting index
++ // lower 32 bits: bitset for contained ids
++ protected final long[] this_index_table;
++ protected final Comparable<?>[] this_table;
++ protected final StateHolder<?, ?> this_state;
++
++ protected long[] index_table;
++ protected StateHolder<?, ?>[][] value_table;
++
++ public ZeroCollidingReferenceStateTable(final StateHolder<?, ?> state, final Map<Property<?>, Comparable<?>> this_map) {
++ this.this_state = state;
++ this.this_index_table = this.create_table(this_map.keySet());
++
++ int max_id = -1;
++ for (final Property<?> property : this_map.keySet()) {
++ final int id = lookup_vindex(property, this.this_index_table);
++ if (id > max_id) {
++ max_id = id;
++ }
++ }
++
++ this.this_table = new Comparable[max_id + 1];
++ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
++ this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue();
++ }
++ }
++
++ public void loadInTable(final Table<Property<?>, Comparable<?>, StateHolder<?, ?>> table,
++ final Map<Property<?>, Comparable<?>> this_map) {
++ final Set<Property<?>> combined = new HashSet<>(table.rowKeySet());
++ combined.addAll(this_map.keySet());
++
++ this.index_table = this.create_table(combined);
++
++ int max_id = -1;
++ for (final Property<?> property : combined) {
++ final int id = lookup_vindex(property, this.index_table);
++ if (id > max_id) {
++ max_id = id;
++ }
++ }
++
++ this.value_table = new StateHolder[max_id + 1][];
++
++ final Map<Property<?>, Map<Comparable<?>, StateHolder<?, ?>>> map = table.rowMap();
++ for (final Property<?> property : map.keySet()) {
++ final Map<Comparable<?>, StateHolder<?, ?>> propertyMap = map.get(property);
++
++ final int id = lookup_vindex(property, this.index_table);
++ final StateHolder<?, ?>[] states = this.value_table[id] = new StateHolder[property.getPossibleValues().size()];
++
++ for (final Map.Entry<Comparable<?>, StateHolder<?, ?>> entry : propertyMap.entrySet()) {
++ if (entry.getValue() == null) {
++ // TODO what
++ continue;
++ }
++
++ states[((Property)property).getIdFor(entry.getKey())] = entry.getValue();
++ }
++ }
++
++
++ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
++ final Property<?> property = entry.getKey();
++ final int index = lookup_vindex(property, this.index_table);
++
++ if (this.value_table[index] == null) {
++ this.value_table[index] = new StateHolder[property.getPossibleValues().size()];
++ }
++
++ this.value_table[index][((Property)property).getIdFor(entry.getValue())] = this.this_state;
++ }
++ }
++
++
++ protected long[] create_table(final Collection<Property<?>> collection) {
++ int max_id = -1;
++ for (final Property<?> property : collection) {
++ final int id = property.getId();
++ if (id > max_id) {
++ max_id = id;
++ }
++ }
++
++ final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32)
++
++ for (final Property<?> property : collection) {
++ final int id = property.getId();
++
++ ret[id >>> 5] |= (1L << (id & 31));
++ }
++
++ int total = 0;
++ for (int i = 1, len = ret.length; i < len; ++i) {
++ ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32;
++ }
++
++ return ret;
++ }
++
++ public Comparable<?> get(final Property<?> state) {
++ final Comparable<?>[] table = this.this_table;
++ final int index = lookup_vindex(state, this.this_index_table);
++
++ if (index < 0 || index >= table.length) {
++ return null;
++ }
++ return table[index];
++ }
++
++ public StateHolder<?, ?> get(final Property<?> property, final Comparable<?> with) {
++ final int withId = ((Property)property).getIdFor(with);
++ if (withId < 0) {
++ return null;
++ }
++
++ final int index = lookup_vindex(property, this.index_table);
++ final StateHolder<?, ?>[][] table = this.value_table;
++ if (index < 0 || index >= table.length) {
++ return null;
++ }
++
++ final StateHolder<?, ?>[] values = table[index];
++
++ if (withId >= values.length) {
++ return null;
++ }
++
++ return values[withId];
++ }
++
++ protected static int lookup_vindex(final Property<?> property, final long[] index_table) {
++ final int id = property.getId();
++ final long bitset_mask = (1L << (id & 31));
++ final long lower_mask = bitset_mask - 1;
++ final int index = id >>> 5;
++ if (index >= index_table.length) {
++ return -1;
++ }
++ final long index_value = index_table[index];
++ final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain
++
++ // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id
++ // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0,
++ // otherwise it comes out as -1.
++ return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check);
++ }
++}
+diff --git a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
+index 89ba12d44865ecddebabbc39c769af4cd8c82702..5f285d190186a2ff5a61d05070593e1d633dd79a 100644
+--- a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
++++ b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
+@@ -39,11 +39,13 @@ public abstract class StateHolder<O, S> {
+ private final ImmutableMap<Property<?>, Comparable<?>> values;
+ private Table<Property<?>, Comparable<?>, S> neighbours;
+ protected final MapCodec<S> propertiesCodec;
++ protected final io.papermc.paper.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Paper - optimise state lookup
+
+ protected StateHolder(O owner, ImmutableMap<Property<?>, Comparable<?>> entries, MapCodec<S> codec) {
+ this.owner = owner;
+ this.values = entries;
+ this.propertiesCodec = codec;
++ this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup
+ }
+
+ public <T extends Comparable<T>> S cycle(Property<T> property) {
+@@ -84,11 +86,11 @@ public abstract class StateHolder<O, S> {
+ }
+
+ public <T extends Comparable<T>> boolean hasProperty(Property<T> property) {
+- return this.values.containsKey(property);
++ return this.optimisedTable.get(property) != null; // Paper - optimise state lookup
+ }
+
+ public <T extends Comparable<T>> T getValue(Property<T> property) {
+- Comparable<?> comparable = this.values.get(property);
++ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
+ if (comparable == null) {
+ throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
+ } else {
+@@ -97,24 +99,18 @@ public abstract class StateHolder<O, S> {
+ }
+
+ public <T extends Comparable<T>> Optional<T> getOptionalValue(Property<T> property) {
+- Comparable<?> comparable = this.values.get(property);
++ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
+ return comparable == null ? Optional.empty() : Optional.of(property.getValueClass().cast(comparable));
+ }
+
+ public <T extends Comparable<T>, V extends T> S setValue(Property<T> property, V value) {
+- Comparable<?> comparable = this.values.get(property);
+- if (comparable == null) {
+- throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner);
+- } else if (comparable.equals(value)) {
+- return (S)this;
+- } else {
+- S object = this.neighbours.get(property, value);
+- if (object == null) {
+- throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
+- } else {
+- return object;
+- }
++ // Paper start - optimise state lookup
++ final S ret = (S)this.optimisedTable.get(property, value);
++ if (ret == null) {
++ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
+ }
++ return ret;
++ // Paper end - optimise state lookup
+ }
+
+ public <T extends Comparable<T>, V extends T> S trySetValue(Property<T> property, V value) {
+@@ -147,7 +143,7 @@ public abstract class StateHolder<O, S> {
+ }
+ }
+
+- this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table));
++ this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.neighbours, this.values); // Paper - optimise state lookup
+ }
+ }
+
+diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
+index 8da64429eaf083578c672cd34f52dd42389ff396..bdd7d9c80eda872496272e46d8772634e44e622a 100644
+--- a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
++++ b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
+@@ -7,6 +7,13 @@ import java.util.Optional;
+ public class BooleanProperty extends Property<Boolean> {
+ private final ImmutableSet<Boolean> values = ImmutableSet.of(true, false);
+
++ // Paper start - optimise iblockdata state lookup
++ @Override
++ public final int getIdFor(final Boolean value) {
++ return value.booleanValue() ? 1 : 0;
++ }
++ // Paper end - optimise iblockdata state lookup
++
+ protected BooleanProperty(String name) {
+ super(name, Boolean.class);
+ }
+diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
+index 0a7a6c0e7635d0ada101074d4229df7edc6dd31b..e658d0b1a025192b48090e865b082ac4d99088d4 100644
+--- a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
++++ b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
+@@ -15,6 +15,15 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
+ private final ImmutableSet<T> values;
+ private final Map<String, T> names = Maps.newHashMap();
+
++ // Paper start - optimise iblockdata state lookup
++ private int[] idLookupTable;
++
++ @Override
++ public final int getIdFor(final T value) {
++ return this.idLookupTable[value.ordinal()];
++ }
++ // Paper end - optimise iblockdata state lookup
++
+ protected EnumProperty(String name, Class<T> type, Collection<T> values) {
+ super(name, type);
+ this.values = ImmutableSet.copyOf(values);
+@@ -28,6 +37,14 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
+ this.names.put(string, enum_);
+ }
+
++ // Paper start - optimise iblockdata state lookup
++ int id = 0;
++ this.idLookupTable = new int[type.getEnumConstants().length];
++ java.util.Arrays.fill(this.idLookupTable, -1);
++ for (final T value : this.getPossibleValues()) {
++ this.idLookupTable[value.ordinal()] = id++;
++ }
++ // Paper end - optimise iblockdata state lookup
+ }
+
+ @Override
+diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
+index cbe6e4579cc55a427dd8f8fe403478faf6d35b8f..765b1092d50608d479bbbe09c457999790ecf42c 100644
+--- a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
++++ b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
+@@ -11,6 +11,16 @@ public class IntegerProperty extends Property<Integer> {
+ public final int min;
+ public final int max;
+
++ // Paper start - optimise iblockdata state lookup
++ @Override
++ public final int getIdFor(final Integer value) {
++ final int val = value.intValue();
++ final int ret = val - this.min;
++
++ return ret | ((this.max - ret) >> 31);
++ }
++ // Paper end - optimise iblockdata state lookup
++
+ protected IntegerProperty(String name, int min, int max) {
+ super(name, Integer.class);
+ if (min < 0) {
+diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
+index 08c2e4ba9147fbea41e0fce26ad20586832a525b..1d8c3f2e57aa9b0830cf7cfb6354fcf636e9c74b 100644
+--- a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
++++ b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
+@@ -24,6 +24,17 @@ public abstract class Property<T extends Comparable<T>> {
+ }, this::getName);
+ private final Codec<Property.Value<T>> valueCodec = this.codec.xmap(this::value, Property.Value::value);
+
++ // Paper start - optimise iblockdata state lookup
++ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
++ private final int id = ID_GENERATOR.getAndIncrement();
++
++ public final int getId() {
++ return this.id;
++ }
++
++ public abstract int getIdFor(final T value);
++ // Paper end - optimise state lookup
++
+ protected Property(String name, Class<T> type) {
+ this.clazz = type;
+ this.name = name;