Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce basic support for Vector API #2890

Merged
merged 3 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -396,7 +398,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 @@ -428,7 +430,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());
dordsor21 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading