Skip to content

Commit

Permalink
fix: fix abysmal tectonic performance w/ dfc
Browse files Browse the repository at this point in the history
  • Loading branch information
ishland committed Nov 17, 2024
1 parent 9d41a4d commit 8dce14e
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.ishland.c2me.opts.dfc.common.ast.binary.AddNode;
import com.ishland.c2me.opts.dfc.common.ast.binary.MaxNode;
import com.ishland.c2me.opts.dfc.common.ast.binary.MaxShortNode;
import com.ishland.c2me.opts.dfc.common.ast.binary.MinNode;
import com.ishland.c2me.opts.dfc.common.ast.binary.MinShortNode;
import com.ishland.c2me.opts.dfc.common.ast.binary.MulNode;
import com.ishland.c2me.opts.dfc.common.ast.misc.CacheLikeNode;
import com.ishland.c2me.opts.dfc.common.ast.misc.ConstantNode;
Expand All @@ -29,8 +31,6 @@
import net.minecraft.world.gen.densityfunction.DensityFunctionTypes;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;

public class McToAst {

Expand All @@ -48,8 +48,22 @@ public static AstNode toAst(DensityFunction df) {
case DensityFunctionTypes.BinaryOperationLike f -> switch (f.type()) {
case ADD -> new AddNode(toAst(f.argument1()), toAst(f.argument2()));
case MUL -> new MulNode(toAst(f.argument1()), toAst(f.argument2()));
case MIN -> new MinNode(toAst(f.argument1()), toAst(f.argument2()));
case MAX -> new MaxNode(toAst(f.argument1()), toAst(f.argument2()));
case MIN -> {
double rightMin = f.argument2().minValue();
if (f.argument1().minValue() < rightMin) {
yield new MinShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMin);
} else {
yield new MinNode(toAst(f.argument1()), toAst(f.argument2()));
}
}
case MAX -> {
double rightMax = f.argument2().maxValue();
if (f.argument1().maxValue() > rightMax) {
yield new MaxShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMax);
} else {
yield new MaxNode(toAst(f.argument1()), toAst(f.argument2()));
}
}
};
case DensityFunctionTypes.BlendDensity f -> toAst(f.input());
case DensityFunctionTypes.Clamp f -> new MaxNode(new ConstantNode(f.minValue()), new MinNode(new ConstantNode(f.maxValue()), toAst(f.input())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;

public class MaxNode extends AbstractBinaryNode { // missed optimization: left > right.maxValue
public class MaxNode extends AbstractBinaryNode {

public MaxNode(AstNode left, AstNode right) {
super(left, right);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.ishland.c2me.opts.dfc.common.ast.binary;

import com.ishland.c2me.opts.dfc.common.ast.AstNode;
import com.ishland.c2me.opts.dfc.common.ast.EvalType;
import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;

public class MaxShortNode extends AbstractBinaryNode {

private final double rightMax;

public MaxShortNode(AstNode left, AstNode right, double rightMax) {
super(left, right);
this.rightMax = rightMax;
}

@Override
protected AstNode newInstance(AstNode left, AstNode right) {
return new MaxShortNode(left, right, this.rightMax);
}

@Override
public double evalSingle(int x, int y, int z, EvalType type) {
double evaled = this.left.evalSingle(x, y, z, type);
return evaled >= this.rightMax ? evaled : Math.max(evaled, this.right.evalSingle(x, y, z, type));
}

@Override
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.left.evalMulti(res, x, y, z, type);
for (int i = 0; i < res.length; i++) {
res[i] = res[i] >= this.rightMax ? res[i] : Math.max(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
}
}

@Override
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);

Label minLabel = new Label();

context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(this.rightMax);
m.cmpl(Type.DOUBLE_TYPE);
m.iflt(minLabel);
m.areturn(Type.DOUBLE_TYPE);

m.visitLabel(minLabel);
context.callDelegateSingle(m, rightMethod);
m.invokestatic(
Type.getInternalName(Math.class),
"max",
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE),
false
);
m.areturn(Type.DOUBLE_TYPE);
}

@Override
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
context.delegateToSingle(m, localVarConsumer, this);
m.areturn(Type.VOID_TYPE);
}

@Override
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;

public class MinNode extends AbstractBinaryNode { // missed optimization: left < right.minValue
public class MinNode extends AbstractBinaryNode {

public MinNode(AstNode left, AstNode right) {
super(left, right);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.ishland.c2me.opts.dfc.common.ast.binary;

import com.ishland.c2me.opts.dfc.common.ast.AstNode;
import com.ishland.c2me.opts.dfc.common.ast.EvalType;
import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;

public class MinShortNode extends AbstractBinaryNode {

private final double rightMin;

public MinShortNode(AstNode left, AstNode right, double rightMin) {
super(left, right);
this.rightMin = rightMin;
}

@Override
protected AstNode newInstance(AstNode left, AstNode right) {
return new MinShortNode(left, right, this.rightMin);
}

@Override
public double evalSingle(int x, int y, int z, EvalType type) {
double evaled = this.left.evalSingle(x, y, z, type);
return evaled <= this.rightMin ? evaled : Math.min(evaled, this.right.evalSingle(x, y, z, type));
}

@Override
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.left.evalMulti(res, x, y, z, type);
for (int i = 0; i < res.length; i++) {
res[i] = res[i] <= this.rightMin ? res[i] : Math.min(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
}
}

@Override
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);

Label minLabel = new Label();

context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(this.rightMin);
m.cmpg(Type.DOUBLE_TYPE);
m.ifgt(minLabel);
m.areturn(Type.DOUBLE_TYPE);

m.visitLabel(minLabel);
context.callDelegateSingle(m, rightMethod);
m.invokestatic(
Type.getInternalName(Math.class),
"min",
Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE),
false
);
m.areturn(Type.DOUBLE_TYPE);
}

@Override
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
context.delegateToSingle(m, localVarConsumer, this);
m.areturn(Type.VOID_TYPE);
}

@Override
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ishland.c2me.opts.dfc.common.ast.AstNode;
import com.ishland.c2me.opts.dfc.common.ast.EvalType;
import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;

Expand All @@ -19,36 +20,47 @@ protected AstNode newInstance(AstNode left, AstNode right) {

@Override
public double evalSingle(int x, int y, int z, EvalType type) {
return this.left.evalSingle(x, y, z, type) * this.right.evalSingle(x, y, z, type);
double evaled = this.left.evalSingle(x, y, z, type);
return evaled == 0.0 ? 0.0 : evaled * this.right.evalSingle(x, y, z, type);
}

@Override
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
double[] res1 = new double[res.length];
this.left.evalMulti(res, x, y, z, type);
this.right.evalMulti(res1, x, y, z, type);
for (int i = 0; i < res1.length; i++) {
res[i] *= res1[i];
for (int i = 0; i < res.length; i++) {
res[i] = res[i] == 0.0 ? 0.0 : res[i] * this.right.evalSingle(x[i], y[i], z[i], type);
}
}

@Override
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);

Label notZero = new Label();

context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(0.0);
m.cmpl(Type.DOUBLE_TYPE);
m.ifne(notZero);
m.dconst(0.0);
m.areturn(Type.DOUBLE_TYPE);

m.visitLabel(notZero);
context.callDelegateSingle(m, rightMethod);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}

@Override
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
context.delegateToSingle(m, localVarConsumer, this);
m.areturn(Type.VOID_TYPE);
}

@Override
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.ishland.c2me.opts.dfc.common.ast.dfvisitor;

import net.minecraft.world.gen.chunk.ChunkNoiseSampler;
import net.minecraft.world.gen.densityfunction.DensityFunction;
import net.minecraft.world.gen.densityfunction.DensityFunctionTypes;

public class StripBlending implements DensityFunction.DensityFunctionVisitor {

public static final StripBlending INSTANCE = new StripBlending();

private StripBlending() {
}

@Override
public DensityFunction apply(DensityFunction densityFunction) {
return switch (densityFunction) {
case ChunkNoiseSampler.BlendAlphaDensityFunction f -> DensityFunctionTypes.constant(1.0);
case ChunkNoiseSampler.BlendOffsetDensityFunction f -> DensityFunctionTypes.constant(0.0);
case DensityFunctionTypes.BlendAlpha f -> DensityFunctionTypes.constant(1.0);
case DensityFunctionTypes.BlendOffset f -> DensityFunctionTypes.constant(0.0);
default -> densityFunction;
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -416,34 +416,7 @@ private record ValuesMethodDef(boolean isConst, String generatedMethod, float co

@Override
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String singleMethod = context.newSingleMethod(this);
context.doCountedLoop(m, localVarConsumer, idx -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);

{
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);

m.invokevirtual(
context.className,
singleMethod,
BytecodeGen.Context.SINGLE_DESC,
false
);
}

m.astore(Type.DOUBLE_TYPE);
});
context.delegateToSingle(m, localVarConsumer, this);
m.areturn(Type.VOID_TYPE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ishland.c2me.opts.dfc.common.ast.AstNode;
import com.ishland.c2me.opts.dfc.common.ast.EvalType;
import com.ishland.c2me.opts.dfc.common.ast.McToAst;
import com.ishland.c2me.opts.dfc.common.ast.dfvisitor.StripBlending;
import com.ishland.c2me.opts.dfc.common.ast.misc.ConstantNode;
import com.ishland.c2me.opts.dfc.common.ast.misc.RootNode;
import com.ishland.c2me.opts.dfc.common.util.ArrayCache;
Expand Down Expand Up @@ -78,7 +79,7 @@ public static DensityFunction compile(DensityFunction densityFunction, Reference
AstNode ast = vif.getAstNode();
return new CompiledDensityFunction(compile0(ast), vif.getBlendingFallback());
}
AstNode ast = McToAst.toAst(densityFunction);
AstNode ast = McToAst.toAst(densityFunction.apply(StripBlending.INSTANCE));
if (ast instanceof ConstantNode constantNode) {
return DensityFunctionTypes.constant(constantNode.getValue());
}
Expand Down Expand Up @@ -498,6 +499,37 @@ public void doCountedLoop(InstructionAdapter m, LocalVarConsumer localVarConsume
m.visitLabel(end);
}

public void delegateToSingle(InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer, AstNode current) {
String singleMethod = this.newSingleMethod(current);
this.doCountedLoop(m, localVarConsumer, idx -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);

{
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);

m.invokevirtual(
this.className,
singleMethod,
BytecodeGen.Context.SINGLE_DESC,
false
);
}

m.astore(Type.DOUBLE_TYPE);
});
}

public void genPostprocessingMethod(String name, Consumer<InstructionAdapter> generator) {
if (this.postProcessMethods.contains(name)) {
return;
Expand Down

0 comments on commit 8dce14e

Please sign in to comment.