Skip to content

Commit

Permalink
Drastically reduce memory footprint of ZeroCollidingReferenceStateTable
Browse files Browse the repository at this point in the history
We can linearize the table lookup by generating an index per
state using the property values.
  • Loading branch information
Spottedleaf committed Aug 10, 2024
1 parent cbdcab7 commit edad8be
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 201 deletions.
2 changes: 1 addition & 1 deletion ConcurrentUtil
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.mixin.bitstorage;

import ca.spottedleaf.concurrentutil.util.IntegerUtil;
import net.minecraft.util.BitStorage;
import net.minecraft.util.SimpleBitStorage;
import org.spongepowered.asm.mixin.Final;
Expand All @@ -10,8 +11,6 @@
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

@Mixin(SimpleBitStorage.class)
abstract class SimpleBitStorageMixin implements BitStorage {
Expand Down Expand Up @@ -45,7 +44,7 @@ abstract class SimpleBitStorageMixin implements BitStorage {
// since index is always [0, 4095] (i.e 12 bits), multiplication by a magic value here (20 bits)
// fits exactly in an int and allows us to use integer arithmetic
for (int bits = 1; bits < BETTER_MAGIC.length; ++bits) {
BETTER_MAGIC[bits] = (0xFFFFF / (64 / bits)) + 1;
BETTER_MAGIC[bits] = (int)IntegerUtil.getUnsignedDivisorMagic(64L / bits, 20);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,36 @@
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(BooleanProperty.class)
abstract class BooleanPropertyMixin extends Property<Boolean> implements PropertyAccess<Boolean> {
protected BooleanPropertyMixin(String string, Class<Boolean> class_) {
super(string, class_);
}

@Override
public final int moonrise$getIdFor(final Boolean value) {
return value.booleanValue() ? 1 : 0;
}

/**
* @reason Hook into constructor to init fields
* @author Spottedleaf
*/
@Inject(
method = "<init>",
at = @At(
value = "RETURN"
)
)
private void init(final CallbackInfo ci) {
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
}

/**
* This skips all ops after the identity comparison in the original code.
*
Expand All @@ -30,14 +52,4 @@ protected BooleanPropertyMixin(String string, Class<Boolean> class_) {
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
return false;
}

@Override
public final boolean moonrise$requiresDefaultImpl() {
return false;
}

@Override
public final int moonrise$getIdFor(final Boolean value) {
return value.booleanValue() ? 1 : 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,34 @@
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;

@Mixin(EnumProperty.class)
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {

@Shadow
public abstract Collection<T> getPossibleValues();

@Unique
private int[] idLookupTable;

protected EnumPropertyMixin(String string, Class<T> class_) {
super(string, class_);
}

@Override
public final int moonrise$getIdFor(final T value) {
final Class<T> target = this.getValueClass();
return ((value.getClass() != target && value.getDeclaringClass() != target)) ? -1 : this.idLookupTable[value.ordinal()];
}

/**
* @reason Hook into constructor to init fields
* @author Spottedleaf
Expand All @@ -32,12 +45,21 @@ protected EnumPropertyMixin(String string, Class<T> class_) {
)
)
private void init(final CallbackInfo ci) {
final Collection<T> values = this.getPossibleValues();
final Class<T> clazz = this.getValueClass();

int id = 0;
this.idLookupTable = new int[getValueClass().getEnumConstants().length];
java.util.Arrays.fill(this.idLookupTable, -1);
for (final T value : this.getPossibleValues()) {
this.idLookupTable[value.ordinal()] = id++;
this.idLookupTable = new int[clazz.getEnumConstants().length];
Arrays.fill(this.idLookupTable, -1);
final T[] byId = (T[])Array.newInstance(clazz, values.size());

for (final T value : values) {
final int valueId = id++;
this.idLookupTable[value.ordinal()] = valueId;
byId[valueId] = value;
}

this.moonrise$setById(byId);
}

/**
Expand All @@ -49,15 +71,4 @@ private void init(final CallbackInfo ci) {
public boolean equals(final Object obj) {
return this == obj;
}

@Override
public final boolean moonrise$requiresDefaultImpl() {
return false;
}

@Override
public final int moonrise$getIdFor(final T value) {
final Class<T> target = this.getValueClass();
return ((value.getClass() != target && value.getDeclaringClass() != target)) ? -1 : this.idLookupTable[value.ordinal()];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(IntegerProperty.class)
abstract class IntegerPropertyMixin extends Property<Integer> implements PropertyAccess<Integer> {
Expand All @@ -23,6 +26,14 @@ protected IntegerPropertyMixin(String string, Class<Integer> class_) {
super(string, class_);
}

@Override
public final int moonrise$getIdFor(final Integer value) {
final int val = value.intValue();
final int ret = val - this.min;

return ret | ((this.max - ret) >> 31);
}

/**
* @reason Properties are identity comparable
* @author Spottedleaf
Expand All @@ -33,16 +44,25 @@ public boolean equals(final Object obj) {
return this == obj;
}

@Override
public final boolean moonrise$requiresDefaultImpl() {
return false;
}
/**
* @reason Hook into constructor to init fields
* @author Spottedleaf
*/
@Inject(
method = "<init>",
at = @At(
value = "RETURN"
)
)
private void init(final CallbackInfo ci) {
final int min = this.min;
final int max = this.max;

@Override
public final int moonrise$getIdFor(final Integer value) {
final int val = value.intValue();
final int ret = val - this.min;
final Integer[] byId = new Integer[max - min + 1];
for (int i = min; i <= max; ++i) {
byId[i - min] = Integer.valueOf(i);
}

return ret | ((this.max - ret) >> 31);
this.moonrise$setById(byId);
}
}
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
package ca.spottedleaf.moonrise.mixin.blockstate_propertyaccess;

import ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;

@Mixin(Property.class)
abstract class PropertyMixin<T extends Comparable<T>> implements PropertyAccess<T> {

@Shadow
public abstract Collection<T> getPossibleValues();

@Unique
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();

@Unique
private int id;

@Unique
private Object2IntOpenHashMap<T> defaultById;
private T[] byId;

@Override
public final int moonrise$getId() {
return this.id;
}

@Override
public final T moonrise$getById(final int id) {
final T[] byId = this.byId;
return id < 0 || id >= byId.length ? null : this.byId[id];
}

@Override
public final void moonrise$setById(final T[] byId) {
if (this.byId != null) {
throw new IllegalStateException();
}
this.byId = byId;
}

@Override
public abstract int moonrise$getIdFor(final T value);

/**
* @reason Hook into constructor to init fields
* @author Spottedleaf
*/
@Inject(
method = "<init>",
at = @At(
value = "RETURN"
)
method = "<init>",
at = @At(
value = "RETURN"
)
)
private void initId(final CallbackInfo ci) {
this.id = ID_GENERATOR.getAndIncrement();

final Collection<T> values = this.getPossibleValues();

if (this.moonrise$requiresDefaultImpl()) {
this.defaultById = new Object2IntOpenHashMap<>(values.size());

int id = 0;
for (final T value : values) {
this.defaultById.put(value, id++);
}
}
}

@Override
public final int moonrise$getId() {
return this.id;
}

/**
Expand All @@ -67,10 +67,4 @@ private void initId(final CallbackInfo ci) {
public boolean equals(final Object obj) {
return this == obj;
}

// add default implementation in case mods create their own Properties
@Override
public int moonrise$getIdFor(final T value) {
return this.defaultById.getOrDefault(value, -1);
}
}
Loading

0 comments on commit edad8be

Please sign in to comment.