3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-11-16 16:10:07 +01:00

Introduce basic support for Vector API (#2890)

* Introduce basic support for Vector API

* add modules to javadoc too

* add assumption comments
Dieser Commit ist enthalten in:
Hannes Greule 2024-09-15 17:00:56 +02:00 committet von GitHub
Ursprung 49b063a187
Commit ea5589b1f0
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
15 geänderte Dateien mit 341 neuen und 17 gelöschten Zeilen

Datei anzeigen

@ -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"))
}

Datei anzeigen

@ -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 {
@ -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/",

Datei anzeigen

@ -675,6 +675,11 @@ public class Settings extends Config {
})
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"})

Datei anzeigen

@ -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;
@ -33,4 +35,10 @@ public class CountFilter extends ForkedFilter<CountFilter> {
return total;
}
@Override
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
total += set.length();
return set;
}
}

Datei anzeigen

@ -2,7 +2,9 @@ package com.fastasyncworldedit.core.extent.filter;
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.
@ -10,10 +12,18 @@ import com.fastasyncworldedit.core.queue.Filter;
* @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;
@ -30,8 +40,30 @@ public final class LinkedFilter<T extends Filter, S extends Filter> extends Dele
}
@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);
}
}
}

Datei anzeigen

@ -2,10 +2,17 @@ package com.fastasyncworldedit.core.extent.filter;
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;
/**
@ -15,8 +22,8 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
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());
@ -60,4 +67,45 @@ public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
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);
}
}
}

Datei anzeigen

@ -23,12 +23,14 @@ import com.sk89q.worldedit.world.block.BlockTypes;
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;
@ -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;
@ -172,7 +174,7 @@ public class CharFilterBlock extends ChunkFilterBlock {
}
@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++) {
@ -395,7 +397,7 @@ public class CharFilterBlock extends ChunkFilterBlock {
}
//Set delegate
private SetDelegate initSet() {
protected final SetDelegate initSet() {
setArr = set.load(layer);
return delegate = FULL;
}
@ -427,7 +429,8 @@ public class CharFilterBlock extends ChunkFilterBlock {
return getExtent().setBiome(x, y, z, biome);
}
private interface SetDelegate {
@ApiStatus.Internal
protected interface SetDelegate {
void set(@Nonnull CharFilterBlock block, char value);

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -0,0 +1,8 @@
package com.fastasyncworldedit.core.internal.simd;
import com.fastasyncworldedit.core.queue.Filter;
import jdk.incubator.vector.ShortVector;
public interface VectorizedFilter extends Filter {
ShortVector applyVector(ShortVector get, ShortVector set);
}

Datei anzeigen

@ -0,0 +1,40 @@
package com.fastasyncworldedit.core.internal.simd;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import jdk.incubator.vector.ShortVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
public interface VectorizedMask {
default void processChunks(IChunk chunk, IChunkGet get, IChunkSet set) {
for (int layer = get.getMinSectionPosition(); layer <= get.getMaxSectionPosition(); layer++) {
final char[] sectionSet = set.loadIfPresent(layer);
if (sectionSet == null) {
continue;
}
final char[] sectionGet = get.load(layer);
processSection(layer, sectionSet, sectionGet);
}
}
default void processSection(int layer, char[] set, char[] get) {
final VectorSpecies<Short> species = ShortVector.SPECIES_PREFERRED;
// assume that set.length % species.elementSize() == 0
for (int i = 0; i < set.length; i += species.length()) {
ShortVector vectorSet = ShortVector.fromCharArray(species, set, i);
ShortVector vectorGet = ShortVector.fromCharArray(species, get, i);
vectorSet = processVector(vectorSet, vectorGet);
vectorSet.intoCharArray(set, i);
}
}
default ShortVector processVector(ShortVector set, ShortVector get) {
return set.blend(0, compareVector(set, get).not());
}
VectorMask<Short> compareVector(ShortVector set, ShortVector get);
}

Datei anzeigen

@ -2,6 +2,9 @@ package com.fastasyncworldedit.core.queue;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.IBatchProcessorHolder;
import com.fastasyncworldedit.core.internal.simd.SimdSupport;
import com.fastasyncworldedit.core.internal.simd.VectorizedCharFilterBlock;
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.BlockVector2;
@ -146,8 +149,12 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
if (newChunk != null) {
chunk = newChunk;
if (block == null) {
if (SimdSupport.useVectorApi() && filter instanceof VectorizedFilter) {
block = new VectorizedCharFilterBlock(this);
} else {
block = this.createFilterBlock();
}
}
block.initChunk(chunkX, chunkZ);
chunk.filterBlocks(filter, block, region, full);
}

Datei anzeigen

@ -14,6 +14,8 @@ import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder;
import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor;
import com.fastasyncworldedit.core.function.mask.BlockMaskBuilder;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.internal.simd.SimdSupport;
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
@ -223,7 +225,9 @@ public class ParallelQueueExtent extends PassthroughExtent {
@Override
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
return this.changes = apply(region, new LinkedFilter<>(pattern, new CountFilter()), true).getChild().getTotal();
VectorizedFilter vectorizedPattern = SimdSupport.vectorizedPattern(pattern);
var filter = LinkedFilter.of(vectorizedPattern == null ? pattern : vectorizedPattern, new CountFilter());
return this.changes = apply(region, filter, true).getChild().getTotal();
}
@Override

Datei anzeigen

@ -24,6 +24,8 @@ import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.internal.simd.SimdSupport;
import com.fastasyncworldedit.core.internal.simd.VectorizedMask;
import com.fastasyncworldedit.core.queue.Filter;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
@ -35,6 +37,7 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import java.util.function.LongFunction;
import static com.google.common.base.Preconditions.checkNotNull;
@ -47,6 +50,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
//FAWE start
private final LongFunction<ChunkFilterBlock> getOrCreateFilterBlock;
private Mask mask;
private @Nullable VectorizedMask vectorizedMask;
//FAWE end
/**
@ -59,16 +63,23 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
super(extent);
checkNotNull(mask);
this.mask = mask;
this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask);
//FAWE start
this.getOrCreateFilterBlock = FaweCache.INSTANCE.createMainThreadSafeCache(() -> new CharFilterBlock(getExtent()));
//FAWE end
}
//FAWE start
private MaskingExtent(Extent extent, Mask mask, LongFunction<ChunkFilterBlock> getOrCreateFilterBlock) {
private MaskingExtent(
Extent extent,
Mask mask,
LongFunction<ChunkFilterBlock> getOrCreateFilterBlock,
@Nullable VectorizedMask vectorizedMask
) {
super(extent);
checkNotNull(mask);
this.mask = mask;
this.vectorizedMask = vectorizedMask;
this.getOrCreateFilterBlock = getOrCreateFilterBlock;
}
//FAWE end
@ -90,6 +101,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask);
}
//FAWE start
@ -105,6 +117,10 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
@Override
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
if (this.vectorizedMask != null) {
this.vectorizedMask.processChunks(chunk, get, set);
return set;
}
final ChunkFilterBlock filter = getOrCreateFilterBlock.apply(Thread.currentThread().getId());
filter.initChunk(chunk.getX(), chunk.getZ());
return filter.filter(chunk, get, set, this);
@ -122,12 +138,12 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
if (child == getExtent()) {
return this;
}
return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock);
return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask);
}
@Override
public Filter fork() {
return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock);
return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask);
}
@Override

Datei anzeigen

@ -22,7 +22,11 @@ package com.sk89q.worldedit.function.mask;
import com.fastasyncworldedit.core.extent.filter.MaskFilter;
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
import com.fastasyncworldedit.core.function.mask.InverseMask;
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.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import javax.annotation.Nullable;
@ -64,7 +68,21 @@ public interface Mask {
return null;
}
@SuppressWarnings({"unchecked", "rawtypes"})
default <T extends Filter> MaskFilter<T> toFilter(T filter) {
final VectorizedMask mask = SimdSupport.vectorizedTargetMask(this);
if (mask != null) {
VectorizedFilter vectorizedFilter = null;
if (filter instanceof VectorizedFilter vf) {
vectorizedFilter = vf;
} else if (filter instanceof Pattern p) {
vectorizedFilter = SimdSupport.vectorizedPattern(p);
}
if (vectorizedFilter != null) {
// also pass original?
return new MaskFilter.VectorizedMaskFilter(vectorizedFilter, this);
}
}
return new MaskFilter<>(filter, this);
}