Skip to content

Commit

Permalink
Introduce basic support for Vector API (#2890)
Browse files Browse the repository at this point in the history
* Introduce basic support for Vector API

* add modules to javadoc too

* add assumption comments
  • Loading branch information
SirYwell authored Sep 15, 2024
1 parent 49b063a commit ea5589b
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 17 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ tasks {
minecraftVersion(it)
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
.toTypedArray())
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true", "--add-modules=jdk.incubator.vector")
group = "run paper"
runDirectory.set(file("run-$it"))
}
Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/CommonJavaConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
options.isDeprecation = true
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.compilerArgs.add("--add-modules=jdk.incubator.vector")
}

configurations.all {
Expand All @@ -51,12 +52,14 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).apply {
addStringOption("Xdoclint:none", "-quiet")
addStringOption("-add-modules", "jdk.incubator.vector")
tags(
"apiNote:a:API Note:",
"implSpec:a:Implementation Requirements:",
"implNote:a:Implementation Note:"
)
options.encoding = "UTF-8"

links(
"https://jd.advntr.dev/api/latest/",
"https://logging.apache.org/log4j/2.x/javadoc/log4j-api/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,11 @@ public static class EXPERIMENTAL {
})
public boolean ALLOW_TICK_FLUIDS = false;

@Comment({
"Whether FAWE should use the incubator Vector API to accelerate some operations"
})
public boolean USE_VECTOR_API = false;

}

@Comment({"Web/HTTP connection related settings"})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.fastasyncworldedit.core.extent.filter;

import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
import jdk.incubator.vector.ShortVector;

public class CountFilter extends ForkedFilter<CountFilter> {
public class CountFilter extends ForkedFilter<CountFilter> implements VectorizedFilter {

private int total;

Expand Down Expand Up @@ -33,4 +35,10 @@ public int getTotal() {
return total;
}

@Override
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
total += set.length();
return set;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,28 @@

import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
import com.fastasyncworldedit.core.queue.Filter;
import jdk.incubator.vector.ShortVector;

/**
* Filter which links two Filters together for single-filter-input operations.
*
* @param <T> Parent which extends Filter
* @param <S> Child which extends Filter
*/
public final class LinkedFilter<T extends Filter, S extends Filter> extends DelegateFilter<T> {
public sealed class LinkedFilter<T extends Filter, S extends Filter> extends DelegateFilter<T> {

private final S child;

@SuppressWarnings({"unchecked", "rawtypes"}) // we defeated the type system
public static <T extends Filter, S extends Filter> LinkedFilter<? extends T, ? extends S> of(T parent, S child) {
if (parent instanceof VectorizedFilter p && child instanceof VectorizedFilter c) {
return new VectorizedLinkedFilter(p, c);
}
return new LinkedFilter<>(parent, child);
}

public LinkedFilter(T parent, S child) {
super(parent);
this.child = child;
Expand All @@ -30,8 +40,30 @@ public void applyBlock(FilterBlock block) {
}

@Override
public LinkedFilter<LinkedFilter<T, S>, Filter> newInstance(Filter other) {
public LinkedFilter<? extends LinkedFilter<T, S>, ? extends Filter> newInstance(Filter other) {
return new LinkedFilter<>(this, other);
}

private final static class VectorizedLinkedFilter<T extends VectorizedFilter, S extends VectorizedFilter>
extends LinkedFilter<T, S> implements VectorizedFilter {

public VectorizedLinkedFilter(final T parent, final S child) {
super(parent, child);
}

@Override
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
ShortVector res = getParent().applyVector(get, set);
return getChild().applyVector(get, res);
}

@Override
public LinkedFilter<? extends LinkedFilter<T, S>, Filter> newInstance(Filter other) {
if (other instanceof VectorizedFilter o) {
return new VectorizedLinkedFilter(this, o);
}
return new LinkedFilter<>(this, other);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.internal.simd.SimdSupport;
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
import com.fastasyncworldedit.core.internal.simd.VectorizedMask;
import com.fastasyncworldedit.core.queue.Filter;
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
import com.sk89q.worldedit.function.mask.Mask;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorOperators;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
Expand All @@ -15,8 +22,8 @@
*/
public class MaskFilter<T extends Filter> extends DelegateFilter<T> {

private final Mask mask;
private final AtomicInteger changes;
final Mask mask;
final AtomicInteger changes;

public MaskFilter(T other, Mask root) {
this(other, root, new AtomicInteger());
Expand Down Expand Up @@ -60,4 +67,45 @@ public Filter fork() {
return new MaskFilter<>(getParent().fork(), mask.copy(), changes);
}

public static class VectorizedMaskFilter<T extends VectorizedFilter> extends MaskFilter<T> implements VectorizedFilter {

private final VectorizedMask vectorizedMask;

public VectorizedMaskFilter(final T other, final Mask root) {
super(other, root);
this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask");
}

public VectorizedMaskFilter(final T other, final Mask root, AtomicInteger changes) {
super(other, root, changes);
this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask");
}

@Override
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
final T parent = getParent();
VectorMask<Short> masked = vectorizedMask.compareVector(set, get);
ShortVector res = parent.applyVector(get, set);
res = set.blend(res, masked);
VectorMask<Short> changed = res.compare(VectorOperators.NE, set);
changes.getAndAdd(changed.trueCount());
return res;
}

@Override
public MaskFilter<?> newInstance(final Filter other) {
if (other instanceof VectorizedFilter o) {
return new VectorizedMaskFilter<>(o, mask);
}
return super.newInstance(other);
}

@SuppressWarnings("unchecked")
@Override
public Filter fork() {
return new VectorizedMaskFilter<>((T) getParent().fork(), mask.copy(), changes);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.jetbrains.annotations.ApiStatus;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static com.sk89q.worldedit.world.block.BlockTypesCache.states;

@ApiStatus.NonExtendable
public class CharFilterBlock extends ChunkFilterBlock {

private static final SetDelegate FULL = (block, value) -> block.setArr[block.index] = value;
Expand All @@ -38,10 +40,10 @@ public class CharFilterBlock extends ChunkFilterBlock {
private int minLayer;
private CharGetBlocks get;
private IChunkSet set;
private char[] getArr;
protected char[] getArr;
@Nullable
private char[] setArr;
private SetDelegate delegate;
protected char[] setArr;
protected SetDelegate delegate;
// local
private int layer;
private int index;
Expand Down Expand Up @@ -172,7 +174,7 @@ public synchronized final void filter(Filter filter, Region region) {
}

@Override
public synchronized final void filter(Filter filter) {
public synchronized void filter(Filter filter) {
for (y = 0, index = 0; y < 16; y++) {
for (z = 0; z < 16; z++) {
for (x = 0; x < 16; x++, index++) {
Expand Down Expand Up @@ -395,7 +397,7 @@ public char getOrdinalChar(Extent orDefault) {
}

//Set delegate
private SetDelegate initSet() {
protected final SetDelegate initSet() {
setArr = set.load(layer);
return delegate = FULL;
}
Expand Down Expand Up @@ -427,7 +429,8 @@ public boolean setBiome(int x, int y, int z, BiomeType biome) {
return getExtent().setBiome(x, y, z, biome);
}

private interface SetDelegate {
@ApiStatus.Internal
protected interface SetDelegate {

void set(@Nonnull CharFilterBlock block, char value);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.fastasyncworldedit.core.internal.simd;

import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask;
import com.fastasyncworldedit.core.queue.Filter;
import com.sk89q.worldedit.function.mask.InverseSingleBlockStateMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorOperators;

import javax.annotation.Nullable;

public class SimdSupport {

private static final boolean VECTOR_API_PRESENT;

static {
boolean vectorApiPresent = false;
try {
Class.forName("jdk.incubator.vector.Vector");
vectorApiPresent = true;
} catch (ClassNotFoundException ignored) {
}
VECTOR_API_PRESENT = vectorApiPresent;
if (!VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API) {
LogManagerCompat.getLogger()
.warn("FAWE use-vector-api is enabled but --add-modules=jdk.incubator.vector is not set.");
}
}

public static boolean useVectorApi() {
return VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API;
}

public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) {
if (!useVectorApi()) {
return null;
}
return switch (mask) {
case SingleBlockStateMask single -> vectorizedTargetMask(single.getBlockState().getOrdinalChar());
case InverseSingleBlockStateMask inverse -> vectorizedTargetMaskInverse(inverse.getBlockState().getOrdinalChar());
default -> null;
};
}

private static VectorizedMask vectorizedTargetMask(char ordinal) {
return (set, get) -> get.compare(VectorOperators.EQ, (short) ordinal);
}

private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) {
return (set, get) -> get.compare(VectorOperators.NE, (short) ordinal);
}

public static @Nullable VectorizedFilter vectorizedPattern(Pattern pattern) {
if (!useVectorApi()) {
return null;
}
return switch (pattern) {
case BaseBlock block -> {
if (block.getNbtReference() == null) {
yield new VectorizedPattern<>(block, block.getOrdinalChar());
}
yield null;
}
case BlockStateHolder<?> blockStateHolder -> new VectorizedPattern<>(
blockStateHolder,
blockStateHolder.getOrdinalChar()
);
default -> null;
};
}

private static final class VectorizedPattern<T extends Filter> extends DelegateFilter<T> implements VectorizedFilter {

private final char ordinal;

public VectorizedPattern(final T parent, char ordinal) {
super(parent);
this.ordinal = ordinal;
}

@Override
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
return ShortVector.broadcast(ShortVector.SPECIES_PREFERRED, ordinal);
}

@Override
public Filter newInstance(final Filter other) {
return new VectorizedPattern<>(other, ordinal);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.fastasyncworldedit.core.internal.simd;

import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock;
import com.fastasyncworldedit.core.queue.Filter;
import com.sk89q.worldedit.extent.Extent;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorSpecies;

public class VectorizedCharFilterBlock extends CharFilterBlock {

public VectorizedCharFilterBlock(final Extent extent) {
super(extent);
}

@Override
public synchronized void filter(final Filter filter) {
if (!(filter instanceof VectorizedFilter vecFilter)) {
throw new IllegalStateException("Unexpected VectorizedCharFilterBlock " + filter);
}
final VectorSpecies<Short> species = ShortVector.SPECIES_PREFERRED;
initSet(); // set array is null before
char[] setArr = this.setArr;
assert setArr != null;
char[] getArr = this.getArr;
// assume setArr.length == getArr.length == 4096
for (int i = 0; i < 4096; i += species.length()) {
ShortVector set = ShortVector.fromCharArray(species, setArr, i);
ShortVector get = ShortVector.fromCharArray(species, getArr, i);
ShortVector res = vecFilter.applyVector(get, set);
res.intoCharArray(setArr, i);
}
}
}
Loading

0 comments on commit ea5589b

Please sign in to comment.