Skip to content

Commit ea5589b

Browse files
authored
Introduce basic support for Vector API (#2890)
* Introduce basic support for Vector API * add modules to javadoc too * add assumption comments
1 parent 49b063a commit ea5589b

File tree

15 files changed

+341
-17
lines changed

15 files changed

+341
-17
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ tasks {
9191
minecraftVersion(it)
9292
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
9393
.toTypedArray())
94-
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
94+
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true", "--add-modules=jdk.incubator.vector")
9595
group = "run paper"
9696
runDirectory.set(file("run-$it"))
9797
}

buildSrc/src/main/kotlin/CommonJavaConfig.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
2828
options.isDeprecation = true
2929
options.encoding = "UTF-8"
3030
options.compilerArgs.add("-parameters")
31+
options.compilerArgs.add("--add-modules=jdk.incubator.vector")
3132
}
3233

3334
configurations.all {
@@ -51,12 +52,14 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
5152
tasks.withType<Javadoc>().configureEach {
5253
(options as StandardJavadocDocletOptions).apply {
5354
addStringOption("Xdoclint:none", "-quiet")
55+
addStringOption("-add-modules", "jdk.incubator.vector")
5456
tags(
5557
"apiNote:a:API Note:",
5658
"implSpec:a:Implementation Requirements:",
5759
"implNote:a:Implementation Note:"
5860
)
5961
options.encoding = "UTF-8"
62+
6063
links(
6164
"https://jd.advntr.dev/api/latest/",
6265
"https://logging.apache.org/log4j/2.x/javadoc/log4j-api/",

worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,11 @@ public static class EXPERIMENTAL {
675675
})
676676
public boolean ALLOW_TICK_FLUIDS = false;
677677

678+
@Comment({
679+
"Whether FAWE should use the incubator Vector API to accelerate some operations"
680+
})
681+
public boolean USE_VECTOR_API = false;
682+
678683
}
679684

680685
@Comment({"Web/HTTP connection related settings"})

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.fastasyncworldedit.core.extent.filter;
22

33
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
4+
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
5+
import jdk.incubator.vector.ShortVector;
46

5-
public class CountFilter extends ForkedFilter<CountFilter> {
7+
public class CountFilter extends ForkedFilter<CountFilter> implements VectorizedFilter {
68

79
private int total;
810

@@ -33,4 +35,10 @@ public int getTotal() {
3335
return total;
3436
}
3537

38+
@Override
39+
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
40+
total += set.length();
41+
return set;
42+
}
43+
3644
}

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@
22

33
import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
44
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
5+
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
56
import com.fastasyncworldedit.core.queue.Filter;
7+
import jdk.incubator.vector.ShortVector;
68

79
/**
810
* Filter which links two Filters together for single-filter-input operations.
911
*
1012
* @param <T> Parent which extends Filter
1113
* @param <S> Child which extends Filter
1214
*/
13-
public final class LinkedFilter<T extends Filter, S extends Filter> extends DelegateFilter<T> {
15+
public sealed class LinkedFilter<T extends Filter, S extends Filter> extends DelegateFilter<T> {
1416

1517
private final S child;
1618

19+
@SuppressWarnings({"unchecked", "rawtypes"}) // we defeated the type system
20+
public static <T extends Filter, S extends Filter> LinkedFilter<? extends T, ? extends S> of(T parent, S child) {
21+
if (parent instanceof VectorizedFilter p && child instanceof VectorizedFilter c) {
22+
return new VectorizedLinkedFilter(p, c);
23+
}
24+
return new LinkedFilter<>(parent, child);
25+
}
26+
1727
public LinkedFilter(T parent, S child) {
1828
super(parent);
1929
this.child = child;
@@ -30,8 +40,30 @@ public void applyBlock(FilterBlock block) {
3040
}
3141

3242
@Override
33-
public LinkedFilter<LinkedFilter<T, S>, Filter> newInstance(Filter other) {
43+
public LinkedFilter<? extends LinkedFilter<T, S>, ? extends Filter> newInstance(Filter other) {
3444
return new LinkedFilter<>(this, other);
3545
}
3646

47+
private final static class VectorizedLinkedFilter<T extends VectorizedFilter, S extends VectorizedFilter>
48+
extends LinkedFilter<T, S> implements VectorizedFilter {
49+
50+
public VectorizedLinkedFilter(final T parent, final S child) {
51+
super(parent, child);
52+
}
53+
54+
@Override
55+
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
56+
ShortVector res = getParent().applyVector(get, set);
57+
return getChild().applyVector(get, res);
58+
}
59+
60+
@Override
61+
public LinkedFilter<? extends LinkedFilter<T, S>, Filter> newInstance(Filter other) {
62+
if (other instanceof VectorizedFilter o) {
63+
return new VectorizedLinkedFilter(this, o);
64+
}
65+
return new LinkedFilter<>(this, other);
66+
}
67+
}
68+
3769
}

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22

33
import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
44
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
5+
import com.fastasyncworldedit.core.internal.simd.SimdSupport;
6+
import com.fastasyncworldedit.core.internal.simd.VectorizedFilter;
7+
import com.fastasyncworldedit.core.internal.simd.VectorizedMask;
58
import com.fastasyncworldedit.core.queue.Filter;
69
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
710
import com.sk89q.worldedit.function.mask.Mask;
11+
import jdk.incubator.vector.ShortVector;
12+
import jdk.incubator.vector.VectorMask;
13+
import jdk.incubator.vector.VectorOperators;
814

15+
import java.util.Objects;
916
import java.util.concurrent.atomic.AtomicInteger;
1017

1118
/**
@@ -15,8 +22,8 @@
1522
*/
1623
public class MaskFilter<T extends Filter> extends DelegateFilter<T> {
1724

18-
private final Mask mask;
19-
private final AtomicInteger changes;
25+
final Mask mask;
26+
final AtomicInteger changes;
2027

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

70+
public static class VectorizedMaskFilter<T extends VectorizedFilter> extends MaskFilter<T> implements VectorizedFilter {
71+
72+
private final VectorizedMask vectorizedMask;
73+
74+
public VectorizedMaskFilter(final T other, final Mask root) {
75+
super(other, root);
76+
this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask");
77+
}
78+
79+
public VectorizedMaskFilter(final T other, final Mask root, AtomicInteger changes) {
80+
super(other, root, changes);
81+
this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask");
82+
}
83+
84+
@Override
85+
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
86+
final T parent = getParent();
87+
VectorMask<Short> masked = vectorizedMask.compareVector(set, get);
88+
ShortVector res = parent.applyVector(get, set);
89+
res = set.blend(res, masked);
90+
VectorMask<Short> changed = res.compare(VectorOperators.NE, set);
91+
changes.getAndAdd(changed.trueCount());
92+
return res;
93+
}
94+
95+
@Override
96+
public MaskFilter<?> newInstance(final Filter other) {
97+
if (other instanceof VectorizedFilter o) {
98+
return new VectorizedMaskFilter<>(o, mask);
99+
}
100+
return super.newInstance(other);
101+
}
102+
103+
@SuppressWarnings("unchecked")
104+
@Override
105+
public Filter fork() {
106+
return new VectorizedMaskFilter<>((T) getParent().fork(), mask.copy(), changes);
107+
}
108+
109+
}
110+
63111
}

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import com.sk89q.worldedit.world.block.BlockTypesCache;
2424
import com.sk89q.worldedit.world.registry.BlockMaterial;
2525
import org.enginehub.linbus.tree.LinCompoundTag;
26+
import org.jetbrains.annotations.ApiStatus;
2627

2728
import javax.annotation.Nonnull;
2829
import javax.annotation.Nullable;
2930

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

33+
@ApiStatus.NonExtendable
3234
public class CharFilterBlock extends ChunkFilterBlock {
3335

3436
private static final SetDelegate FULL = (block, value) -> block.setArr[block.index] = value;
@@ -38,10 +40,10 @@ public class CharFilterBlock extends ChunkFilterBlock {
3840
private int minLayer;
3941
private CharGetBlocks get;
4042
private IChunkSet set;
41-
private char[] getArr;
43+
protected char[] getArr;
4244
@Nullable
43-
private char[] setArr;
44-
private SetDelegate delegate;
45+
protected char[] setArr;
46+
protected SetDelegate delegate;
4547
// local
4648
private int layer;
4749
private int index;
@@ -172,7 +174,7 @@ public synchronized final void filter(Filter filter, Region region) {
172174
}
173175

174176
@Override
175-
public synchronized final void filter(Filter filter) {
177+
public synchronized void filter(Filter filter) {
176178
for (y = 0, index = 0; y < 16; y++) {
177179
for (z = 0; z < 16; z++) {
178180
for (x = 0; x < 16; x++, index++) {
@@ -395,7 +397,7 @@ public char getOrdinalChar(Extent orDefault) {
395397
}
396398

397399
//Set delegate
398-
private SetDelegate initSet() {
400+
protected final SetDelegate initSet() {
399401
setArr = set.load(layer);
400402
return delegate = FULL;
401403
}
@@ -427,7 +429,8 @@ public boolean setBiome(int x, int y, int z, BiomeType biome) {
427429
return getExtent().setBiome(x, y, z, biome);
428430
}
429431

430-
private interface SetDelegate {
432+
@ApiStatus.Internal
433+
protected interface SetDelegate {
431434

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

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.fastasyncworldedit.core.internal.simd;
2+
3+
import com.fastasyncworldedit.core.configuration.Settings;
4+
import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter;
5+
import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask;
6+
import com.fastasyncworldedit.core.queue.Filter;
7+
import com.sk89q.worldedit.function.mask.InverseSingleBlockStateMask;
8+
import com.sk89q.worldedit.function.mask.Mask;
9+
import com.sk89q.worldedit.function.pattern.Pattern;
10+
import com.sk89q.worldedit.internal.util.LogManagerCompat;
11+
import com.sk89q.worldedit.world.block.BaseBlock;
12+
import com.sk89q.worldedit.world.block.BlockStateHolder;
13+
import jdk.incubator.vector.ShortVector;
14+
import jdk.incubator.vector.VectorOperators;
15+
16+
import javax.annotation.Nullable;
17+
18+
public class SimdSupport {
19+
20+
private static final boolean VECTOR_API_PRESENT;
21+
22+
static {
23+
boolean vectorApiPresent = false;
24+
try {
25+
Class.forName("jdk.incubator.vector.Vector");
26+
vectorApiPresent = true;
27+
} catch (ClassNotFoundException ignored) {
28+
}
29+
VECTOR_API_PRESENT = vectorApiPresent;
30+
if (!VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API) {
31+
LogManagerCompat.getLogger()
32+
.warn("FAWE use-vector-api is enabled but --add-modules=jdk.incubator.vector is not set.");
33+
}
34+
}
35+
36+
public static boolean useVectorApi() {
37+
return VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API;
38+
}
39+
40+
public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) {
41+
if (!useVectorApi()) {
42+
return null;
43+
}
44+
return switch (mask) {
45+
case SingleBlockStateMask single -> vectorizedTargetMask(single.getBlockState().getOrdinalChar());
46+
case InverseSingleBlockStateMask inverse -> vectorizedTargetMaskInverse(inverse.getBlockState().getOrdinalChar());
47+
default -> null;
48+
};
49+
}
50+
51+
private static VectorizedMask vectorizedTargetMask(char ordinal) {
52+
return (set, get) -> get.compare(VectorOperators.EQ, (short) ordinal);
53+
}
54+
55+
private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) {
56+
return (set, get) -> get.compare(VectorOperators.NE, (short) ordinal);
57+
}
58+
59+
public static @Nullable VectorizedFilter vectorizedPattern(Pattern pattern) {
60+
if (!useVectorApi()) {
61+
return null;
62+
}
63+
return switch (pattern) {
64+
case BaseBlock block -> {
65+
if (block.getNbtReference() == null) {
66+
yield new VectorizedPattern<>(block, block.getOrdinalChar());
67+
}
68+
yield null;
69+
}
70+
case BlockStateHolder<?> blockStateHolder -> new VectorizedPattern<>(
71+
blockStateHolder,
72+
blockStateHolder.getOrdinalChar()
73+
);
74+
default -> null;
75+
};
76+
}
77+
78+
private static final class VectorizedPattern<T extends Filter> extends DelegateFilter<T> implements VectorizedFilter {
79+
80+
private final char ordinal;
81+
82+
public VectorizedPattern(final T parent, char ordinal) {
83+
super(parent);
84+
this.ordinal = ordinal;
85+
}
86+
87+
@Override
88+
public ShortVector applyVector(final ShortVector get, final ShortVector set) {
89+
return ShortVector.broadcast(ShortVector.SPECIES_PREFERRED, ordinal);
90+
}
91+
92+
@Override
93+
public Filter newInstance(final Filter other) {
94+
return new VectorizedPattern<>(other, ordinal);
95+
}
96+
97+
}
98+
99+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.fastasyncworldedit.core.internal.simd;
2+
3+
import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock;
4+
import com.fastasyncworldedit.core.queue.Filter;
5+
import com.sk89q.worldedit.extent.Extent;
6+
import jdk.incubator.vector.ShortVector;
7+
import jdk.incubator.vector.VectorSpecies;
8+
9+
public class VectorizedCharFilterBlock extends CharFilterBlock {
10+
11+
public VectorizedCharFilterBlock(final Extent extent) {
12+
super(extent);
13+
}
14+
15+
@Override
16+
public synchronized void filter(final Filter filter) {
17+
if (!(filter instanceof VectorizedFilter vecFilter)) {
18+
throw new IllegalStateException("Unexpected VectorizedCharFilterBlock " + filter);
19+
}
20+
final VectorSpecies<Short> species = ShortVector.SPECIES_PREFERRED;
21+
initSet(); // set array is null before
22+
char[] setArr = this.setArr;
23+
assert setArr != null;
24+
char[] getArr = this.getArr;
25+
// assume setArr.length == getArr.length == 4096
26+
for (int i = 0; i < 4096; i += species.length()) {
27+
ShortVector set = ShortVector.fromCharArray(species, setArr, i);
28+
ShortVector get = ShortVector.fromCharArray(species, getArr, i);
29+
ShortVector res = vecFilter.applyVector(get, set);
30+
res.intoCharArray(setArr, i);
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)