Skip to content

Commit 1ec5e47

Browse files
committed
- 复制了一份 JCTools 代码作为内部前置。
- 改进了 Ebwizardry 的发射器优化。 - 改进了 ParallelRandomBlockTicker 的性能。
1 parent 6663d1c commit 1ec5e47

File tree

125 files changed

+27981
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+27981
-101
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ plugins {
1212

1313
// Project properties
1414
group = "github.kasuminova.stellarcore"
15-
version = "1.4.17"
15+
version = "1.5.0"
1616

1717
// Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod
1818
java {

src/main/java/github/kasuminova/stellarcore/common/config/StellarCoreConfig.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -979,10 +979,8 @@ public static class CustomLoadingScreen {
979979

980980
public static class EBWizardry {
981981

982-
@Config.Comment({
983-
"(Server Performance) Improved event listening performance for DispenserCastingData.",
984-
"Note: We are currently experiencing strange issues on some devices during testing, please report any unknown crashes with this feature enabled immediately."
985-
})
982+
@Config.Comment("(Server Performance) Improved event listening performance for DispenserCastingData, required mc restart.")
983+
@Config.RequiresMcRestart
986984
@Config.Name("DispenserCastingDataImprovements")
987985
public boolean dispenserCastingData = false;
988986

src/main/java/github/kasuminova/stellarcore/common/itemstack/ItemStackCapInitTask.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,31 @@
33
import github.kasuminova.stellarcore.mixin.util.StellarItemStackCapLoader;
44
import net.minecraft.item.ItemStack;
55

6-
import java.util.concurrent.atomic.AtomicBoolean;
7-
86
@SuppressWarnings("DataFlowIssue")
97
public class ItemStackCapInitTask implements Runnable {
108

119
private final StellarItemStackCapLoader target;
12-
private final AtomicBoolean done = new AtomicBoolean(false);
10+
private volatile boolean done = false;
1311

1412
public ItemStackCapInitTask(final ItemStack target) {
1513
this.target = (StellarItemStackCapLoader) (Object) target;
1614
}
1715

1816
@Override
1917
public synchronized void run() {
20-
if (done.get()) {
18+
if (done) {
2119
return;
2220
}
2321
target.stellar_core$initCap();
24-
done.set(true);
22+
done = true;
2523
}
2624

2725
public boolean isDone() {
28-
return done.get();
26+
return done;
2927
}
3028

3129
public void join() {
32-
if (!isDone()) {
30+
if (!done) {
3331
run();
3432
}
3533
target.stellar_core$joinCapInit();
Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,105 @@
11
package github.kasuminova.stellarcore.common.itemstack;
22

33
import github.kasuminova.stellarcore.common.util.StellarLog;
4-
import io.netty.util.internal.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;
4+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcUnboundedXaddArrayQueue;
55

6+
import java.util.List;
67
import java.util.Queue;
7-
import java.util.concurrent.ConcurrentLinkedQueue;
8+
import java.util.concurrent.CopyOnWriteArrayList;
9+
import java.util.concurrent.atomic.AtomicInteger;
810
import java.util.concurrent.locks.LockSupport;
911

1012
public class ItemStackCapInitializer implements Runnable {
1113

1214
public static final ItemStackCapInitializer INSTANCE = new ItemStackCapInitializer();
13-
15+
16+
private static final int QUEUE_BOUND_SIZE = 100_000;
17+
private static final int MAX_THREADS = Math.min(Runtime.getRuntime().availableProcessors(), 4);
18+
19+
private static final ThreadLocal<Long> PARKED_MICROS = ThreadLocal.withInitial(() -> 0L);
20+
1421
private final Queue<ItemStackCapInitTask> taskQueue = createConcurrentQueue();
1522

16-
private long parkedMicros = 0;
17-
18-
private final Thread worker;
23+
private final AtomicInteger queueSize = new AtomicInteger();
24+
private final List<Thread> workers = new CopyOnWriteArrayList<>();
1925

2026
private ItemStackCapInitializer() {
21-
worker = new Thread(this, "StellarCore-ItemStackCapInitializer");
27+
createWorkerInternal();
28+
}
29+
30+
private synchronized void createWorker() {
31+
if (workers.size() >= MAX_THREADS) {
32+
return;
33+
}
34+
createWorkerInternal();
35+
}
36+
37+
private void createWorkerInternal() {
38+
Thread worker = new Thread(this, "StellarCore-ItemStackCapInitializer-" + workers.size());
2239
worker.start();
40+
worker.setPriority(7);
41+
workers.add(worker);
2342
}
2443

2544
public void addTask(final ItemStackCapInitTask task) {
2645
taskQueue.offer(task);
46+
47+
int tasks = queueSize.incrementAndGet();
48+
int workers = this.workers.size();
49+
if ((workers > 0) && (tasks > (QUEUE_BOUND_SIZE * workers)) && (workers < MAX_THREADS)) {
50+
StellarLog.LOG.warn("[StellarCore-ItemStackCapInitializer] Creating new worker because queue size reached bound size (limit {}).", QUEUE_BOUND_SIZE * workers);
51+
createWorker();
52+
}
2753
}
2854

2955
@Override
3056
public void run() {
31-
StellarLog.LOG.info("[StellarCore] ItemStackCapInitializer started.");
57+
StellarLog.LOG.info("[StellarCore] {} started.", Thread.currentThread().getName());
3258
while (!Thread.currentThread().isInterrupted()) {
3359
ItemStackCapInitTask task;
3460
boolean executed = false;
3561

3662
try {
37-
while ((task = taskQueue.poll()) != null) {
63+
while ((task = pollTask()) != null) {
3864
task.run();
3965
executed = true;
66+
queueSize.decrementAndGet();
4067
}
4168
} catch (Throwable e) {
4269
StellarLog.LOG.error("[StellarCore] ItemStackCapInitializer failed to execute task.", e);
4370
}
4471

4572
if (executed) {
46-
parkedMicros = 0;
73+
PARKED_MICROS.set(0L);
4774
}
4875
park();
4976
}
50-
StellarLog.LOG.warn("[StellarCore] ItemStackCapInitializer stopped, it may be invalid.");
77+
StellarLog.LOG.warn("[StellarCore] {} stopped, it may be invalid.", Thread.currentThread().getName());
5178
}
5279

53-
private void park() {
80+
private ItemStackCapInitTask pollTask() {
81+
return taskQueue.poll();
82+
}
83+
84+
private static void park() {
85+
long parkedMicros = PARKED_MICROS.get();
5486
if (parkedMicros > 100_000) { // 100ms
5587
LockSupport.parkNanos(500_000L); // 0.5ms
56-
parkedMicros += 500;
88+
PARKED_MICROS.set(parkedMicros + 500);
5789
} else if (parkedMicros > 50_000) { // 50ms
5890
LockSupport.parkNanos(100_000L); // 0.1ms
59-
parkedMicros += 100;
91+
PARKED_MICROS.set(parkedMicros + 100);
6092
} else if (parkedMicros > 10_000) { // 10ms
6193
LockSupport.parkNanos(10_000L); // 10μs
62-
parkedMicros += 10;
94+
PARKED_MICROS.set(parkedMicros + 10);
6395
} else {
6496
LockSupport.parkNanos(5_000L); // 5μs
65-
parkedMicros += 5;
97+
PARKED_MICROS.set(parkedMicros + 5);
6698
}
6799
}
68100

69101
private static <E> Queue<E> createConcurrentQueue() {
70-
try {
71-
// May be incompatible with cleanroom.
72-
return new MpscLinkedAtomicQueue<>();
73-
} catch (Throwable e) {
74-
return new ConcurrentLinkedQueue<>();
75-
}
102+
return new MpmcUnboundedXaddArrayQueue<>(1_000, 100 * MAX_THREADS);
76103
}
77104

78105
}

src/main/java/github/kasuminova/stellarcore/common/pool/CanonicalizeWorker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package github.kasuminova.stellarcore.common.pool;
22

33
import github.kasuminova.stellarcore.common.util.StellarLog;
4-
import io.netty.util.internal.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;
4+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;
55

66
import java.util.Queue;
77
import java.util.concurrent.ConcurrentLinkedQueue;

src/main/java/github/kasuminova/stellarcore/common/world/ParallelRandomBlockTicker.java

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package github.kasuminova.stellarcore.common.world;
22

33
import com.github.bsideup.jabel.Desugar;
4+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcArrayQueue;
5+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcUnboundedXaddArrayQueue;
6+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.SpscArrayQueue;
7+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.SpscUnboundedArrayQueue;
8+
import github.kasuminova.stellarcore.shaded.org.jctools.queues.unpadded.MpmcUnpaddedArrayQueue;
49
import it.unimi.dsi.fastutil.ints.IntList;
510
import it.unimi.dsi.fastutil.ints.IntListIterator;
611
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -12,20 +17,17 @@
1217
import net.minecraft.world.World;
1318
import net.minecraft.world.chunk.Chunk;
1419
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
15-
import org.spongepowered.asm.mixin.Unique;
1620

17-
import java.util.Collections;
18-
import java.util.LinkedList;
19-
import java.util.List;
20-
import java.util.Random;
21+
import java.util.*;
22+
import java.util.stream.IntStream;
2123

2224
public class ParallelRandomBlockTicker {
2325

2426
public static final ParallelRandomBlockTicker INSTANCE = new ParallelRandomBlockTicker();
2527

2628
private static final boolean SHOULD_PARALLEL = Runtime.getRuntime().availableProcessors() > 2;
2729

28-
private final List<Tuple<Chunk, List<TickData>>> enqueuedChunks = new ObjectArrayList<>();
30+
private final Queue<Tuple<Chunk, List<TickData>>> enqueuedChunks = new MpmcUnboundedXaddArrayQueue<>(1000);
2931

3032
private World currentWorld = null;
3133
private Random currentRand = null;
@@ -35,11 +37,11 @@ private ParallelRandomBlockTicker() {
3537
}
3638

3739
public void enqueueChunk(final Chunk chunk, final List<TickData> data) {
38-
enqueuedChunks.add(new Tuple<>(chunk, data));
40+
enqueuedChunks.offer(new Tuple<>(chunk, data));
3941
}
4042

4143
public void execute(final World world, final Random rand, final Profiler profiler, final int randomTickSpeed) {
42-
List<Tuple<Chunk, List<TickData>>> enqueuedChunks = this.enqueuedChunks;
44+
Queue<Tuple<Chunk, List<TickData>>> enqueuedChunks = this.enqueuedChunks;
4345
if (enqueuedChunks.isEmpty()) {
4446
return;
4547
}
@@ -48,21 +50,25 @@ public void execute(final World world, final Random rand, final Profiler profile
4850
this.currentRand = rand;
4951
this.profiler = profiler;
5052

51-
boolean parallel = SHOULD_PARALLEL && enqueuedChunks.size() * randomTickSpeed >= 300;
52-
List<List<RandomTickTask>> randomTickData = parallel ? Collections.synchronizedList(new LinkedList<>()) : new LinkedList<>();
53-
54-
(parallel ? enqueuedChunks.parallelStream() : enqueuedChunks.stream()).forEach(entry -> {
55-
Chunk chunk = entry.getFirst();
56-
List<RandomTickTask> collectedData = new ObjectArrayList<>();
57-
for (final TickData tickData : entry.getSecond()) {
58-
List<RandomTickTask> data = getRandomTickData(chunk, tickData);
59-
if (data.isEmpty()) {
60-
continue;
53+
final boolean parallel = SHOULD_PARALLEL && enqueuedChunks.size() * randomTickSpeed >= 300;
54+
final int concurrency = parallel ? Runtime.getRuntime().availableProcessors() : 1;
55+
final List<List<RandomTickTask>> randomTickData = parallel ? Collections.synchronizedList(new LinkedList<>()) : new LinkedList<>();
56+
57+
IntStream stream = parallel ? IntStream.range(0, concurrency).parallel() : IntStream.range(0, concurrency);
58+
stream.forEach(i -> {
59+
Tuple<Chunk, List<TickData>> data;
60+
while ((data = enqueuedChunks.poll()) != null) {
61+
List<RandomTickTask> collectedData = new ObjectArrayList<>();
62+
for (final TickData tickData : data.getSecond()) {
63+
List<RandomTickTask> data1 = getRandomTickData(data.getFirst(), tickData);
64+
if (data1.isEmpty()) {
65+
continue;
66+
}
67+
collectedData.addAll(data1);
68+
}
69+
if (!collectedData.isEmpty()) {
70+
randomTickData.add(collectedData);
6171
}
62-
collectedData.addAll(data);
63-
}
64-
if (!collectedData.isEmpty()) {
65-
randomTickData.add(collectedData);
6672
}
6773
});
6874

src/main/java/github/kasuminova/stellarcore/mixin/StellarCoreEarlyMixinLoader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class StellarCoreEarlyMixinLoader implements IFMLLoadingPlugin {
5151
addMixinCFG("mixins.stellar_core_forge_bakedquad_vertexdata.json", () -> StellarCoreConfig.PERFORMANCE.forge.unpackedBakedQuadVertexDataCanonicalization);
5252
addMixinCFG("mixins.stellar_core_forge_modelloader.json", () -> StellarCoreConfig.PERFORMANCE.vanilla.parallelModelLoader);
5353
addMixinCFG("mixins.stellar_core_hudcaching.json", () -> StellarCoreConfig.PERFORMANCE.vanilla.hudCaching);
54+
addMixinCFG("mixins.stellar_core_ebwizardry_early.json", () -> StellarCoreConfig.PERFORMANCE.ebWizardry.dispenserCastingData);
5455
}
5556

5657
private static void addMixinCFG(final String mixinConfig) {

src/main/java/github/kasuminova/stellarcore/mixin/ebwizardry/MixinDispenserCastingData.java

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

33
import electroblob.wizardry.data.DispenserCastingData;
44
import github.kasuminova.stellarcore.common.config.StellarCoreConfig;
5-
import net.minecraft.tileentity.TileEntity;
6-
import net.minecraft.tileentity.TileEntityDispenser;
75
import net.minecraftforge.fml.common.gameevent.TickEvent;
86
import org.spongepowered.asm.mixin.Mixin;
9-
import org.spongepowered.asm.mixin.Unique;
107
import org.spongepowered.asm.mixin.injection.At;
118
import org.spongepowered.asm.mixin.injection.Inject;
129
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
1310

14-
import java.util.*;
15-
import java.util.stream.Collectors;
16-
1711
@Mixin(value = DispenserCastingData.class, remap = false)
1812
public abstract class MixinDispenserCastingData {
1913

@@ -22,29 +16,7 @@ private static void injectOnWorldTickEvent(final TickEvent.WorldTickEvent event,
2216
if (!StellarCoreConfig.PERFORMANCE.ebWizardry.dispenserCastingData) {
2317
return;
2418
}
25-
if (event.phase == TickEvent.Phase.START || event.world.loadedTileEntityList.size() <= 5_000) {
26-
return;
27-
}
2819
ci.cancel();
29-
30-
// Use parallel stream to find Capability to improve performance.
31-
try {
32-
stellar_core$executeParallel(event.world.loadedTileEntityList);
33-
} catch (ConcurrentModificationException | NullPointerException e) {
34-
// CME?
35-
stellar_core$executeParallel(new ArrayList<>(event.world.loadedTileEntityList));
36-
}
37-
}
38-
39-
@Unique
40-
private static void stellar_core$executeParallel(final List<TileEntity> tileEntityList) {
41-
tileEntityList.parallelStream()
42-
.filter(TileEntityDispenser.class::isInstance)
43-
.map(TileEntityDispenser.class::cast)
44-
.map(DispenserCastingData::get)
45-
.filter(Objects::nonNull)
46-
.collect(Collectors.toCollection(LinkedList::new))
47-
.forEach(DispenserCastingData::update);
4820
}
4921

5022
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package github.kasuminova.stellarcore.mixin.ebwizardry;
2+
3+
import electroblob.wizardry.data.DispenserCastingData;
4+
import net.minecraft.tileentity.TileEntityDispenser;
5+
import net.minecraft.tileentity.TileEntityLockableLoot;
6+
import net.minecraft.util.ITickable;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.Unique;
9+
10+
@Mixin(TileEntityDispenser.class)
11+
public abstract class MixinTileEntityDispenser extends TileEntityLockableLoot implements ITickable {
12+
13+
@Unique
14+
@Override
15+
@SuppressWarnings({"DataFlowIssue", "AddedMixinMembersNamePattern"})
16+
public void update() {
17+
if (world.isRemote) {
18+
return;
19+
}
20+
DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser) (Object) this);
21+
if (data != null) {
22+
data.update();
23+
}
24+
}
25+
26+
}

0 commit comments

Comments
 (0)