Skip to content

Commit

Permalink
- 复制了一份 JCTools 代码作为内部前置。
Browse files Browse the repository at this point in the history
- 改进了 Ebwizardry 的发射器优化。
- 改进了 ParallelRandomBlockTicker 的性能。
  • Loading branch information
KasumiNova committed Oct 3, 2024
1 parent 6663d1c commit 1ec5e47
Show file tree
Hide file tree
Showing 125 changed files with 27,981 additions and 101 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {

// Project properties
group = "github.kasuminova.stellarcore"
version = "1.4.17"
version = "1.5.0"

// Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod
java {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -979,10 +979,8 @@ public static class CustomLoadingScreen {

public static class EBWizardry {

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,31 @@
import github.kasuminova.stellarcore.mixin.util.StellarItemStackCapLoader;
import net.minecraft.item.ItemStack;

import java.util.concurrent.atomic.AtomicBoolean;

@SuppressWarnings("DataFlowIssue")
public class ItemStackCapInitTask implements Runnable {

private final StellarItemStackCapLoader target;
private final AtomicBoolean done = new AtomicBoolean(false);
private volatile boolean done = false;

public ItemStackCapInitTask(final ItemStack target) {
this.target = (StellarItemStackCapLoader) (Object) target;
}

@Override
public synchronized void run() {
if (done.get()) {
if (done) {
return;
}
target.stellar_core$initCap();
done.set(true);
done = true;
}

public boolean isDone() {
return done.get();
return done;
}

public void join() {
if (!isDone()) {
if (!done) {
run();
}
target.stellar_core$joinCapInit();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,78 +1,105 @@
package github.kasuminova.stellarcore.common.itemstack;

import github.kasuminova.stellarcore.common.util.StellarLog;
import io.netty.util.internal.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcUnboundedXaddArrayQueue;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class ItemStackCapInitializer implements Runnable {

public static final ItemStackCapInitializer INSTANCE = new ItemStackCapInitializer();


private static final int QUEUE_BOUND_SIZE = 100_000;
private static final int MAX_THREADS = Math.min(Runtime.getRuntime().availableProcessors(), 4);

private static final ThreadLocal<Long> PARKED_MICROS = ThreadLocal.withInitial(() -> 0L);

private final Queue<ItemStackCapInitTask> taskQueue = createConcurrentQueue();

private long parkedMicros = 0;

private final Thread worker;
private final AtomicInteger queueSize = new AtomicInteger();
private final List<Thread> workers = new CopyOnWriteArrayList<>();

private ItemStackCapInitializer() {
worker = new Thread(this, "StellarCore-ItemStackCapInitializer");
createWorkerInternal();
}

private synchronized void createWorker() {
if (workers.size() >= MAX_THREADS) {
return;
}
createWorkerInternal();
}

private void createWorkerInternal() {
Thread worker = new Thread(this, "StellarCore-ItemStackCapInitializer-" + workers.size());
worker.start();
worker.setPriority(7);
workers.add(worker);
}

public void addTask(final ItemStackCapInitTask task) {
taskQueue.offer(task);

int tasks = queueSize.incrementAndGet();
int workers = this.workers.size();
if ((workers > 0) && (tasks > (QUEUE_BOUND_SIZE * workers)) && (workers < MAX_THREADS)) {
StellarLog.LOG.warn("[StellarCore-ItemStackCapInitializer] Creating new worker because queue size reached bound size (limit {}).", QUEUE_BOUND_SIZE * workers);
createWorker();
}
}

@Override
public void run() {
StellarLog.LOG.info("[StellarCore] ItemStackCapInitializer started.");
StellarLog.LOG.info("[StellarCore] {} started.", Thread.currentThread().getName());
while (!Thread.currentThread().isInterrupted()) {
ItemStackCapInitTask task;
boolean executed = false;

try {
while ((task = taskQueue.poll()) != null) {
while ((task = pollTask()) != null) {
task.run();
executed = true;
queueSize.decrementAndGet();
}
} catch (Throwable e) {
StellarLog.LOG.error("[StellarCore] ItemStackCapInitializer failed to execute task.", e);
}

if (executed) {
parkedMicros = 0;
PARKED_MICROS.set(0L);
}
park();
}
StellarLog.LOG.warn("[StellarCore] ItemStackCapInitializer stopped, it may be invalid.");
StellarLog.LOG.warn("[StellarCore] {} stopped, it may be invalid.", Thread.currentThread().getName());
}

private void park() {
private ItemStackCapInitTask pollTask() {
return taskQueue.poll();
}

private static void park() {
long parkedMicros = PARKED_MICROS.get();
if (parkedMicros > 100_000) { // 100ms
LockSupport.parkNanos(500_000L); // 0.5ms
parkedMicros += 500;
PARKED_MICROS.set(parkedMicros + 500);
} else if (parkedMicros > 50_000) { // 50ms
LockSupport.parkNanos(100_000L); // 0.1ms
parkedMicros += 100;
PARKED_MICROS.set(parkedMicros + 100);
} else if (parkedMicros > 10_000) { // 10ms
LockSupport.parkNanos(10_000L); // 10μs
parkedMicros += 10;
PARKED_MICROS.set(parkedMicros + 10);
} else {
LockSupport.parkNanos(5_000L); // 5μs
parkedMicros += 5;
PARKED_MICROS.set(parkedMicros + 5);
}
}

private static <E> Queue<E> createConcurrentQueue() {
try {
// May be incompatible with cleanroom.
return new MpscLinkedAtomicQueue<>();
} catch (Throwable e) {
return new ConcurrentLinkedQueue<>();
}
return new MpmcUnboundedXaddArrayQueue<>(1_000, 100 * MAX_THREADS);
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package github.kasuminova.stellarcore.common.pool;

import github.kasuminova.stellarcore.common.util.StellarLog;
import io.netty.util.internal.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.atomic.MpscLinkedAtomicQueue;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package github.kasuminova.stellarcore.common.world;

import com.github.bsideup.jabel.Desugar;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcArrayQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.MpmcUnboundedXaddArrayQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.SpscArrayQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.SpscUnboundedArrayQueue;
import github.kasuminova.stellarcore.shaded.org.jctools.queues.unpadded.MpmcUnpaddedArrayQueue;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntListIterator;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
Expand All @@ -12,20 +17,17 @@
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import org.spongepowered.asm.mixin.Unique;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.*;
import java.util.stream.IntStream;

public class ParallelRandomBlockTicker {

public static final ParallelRandomBlockTicker INSTANCE = new ParallelRandomBlockTicker();

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

private final List<Tuple<Chunk, List<TickData>>> enqueuedChunks = new ObjectArrayList<>();
private final Queue<Tuple<Chunk, List<TickData>>> enqueuedChunks = new MpmcUnboundedXaddArrayQueue<>(1000);

private World currentWorld = null;
private Random currentRand = null;
Expand All @@ -35,11 +37,11 @@ private ParallelRandomBlockTicker() {
}

public void enqueueChunk(final Chunk chunk, final List<TickData> data) {
enqueuedChunks.add(new Tuple<>(chunk, data));
enqueuedChunks.offer(new Tuple<>(chunk, data));
}

public void execute(final World world, final Random rand, final Profiler profiler, final int randomTickSpeed) {
List<Tuple<Chunk, List<TickData>>> enqueuedChunks = this.enqueuedChunks;
Queue<Tuple<Chunk, List<TickData>>> enqueuedChunks = this.enqueuedChunks;
if (enqueuedChunks.isEmpty()) {
return;
}
Expand All @@ -48,21 +50,25 @@ public void execute(final World world, final Random rand, final Profiler profile
this.currentRand = rand;
this.profiler = profiler;

boolean parallel = SHOULD_PARALLEL && enqueuedChunks.size() * randomTickSpeed >= 300;
List<List<RandomTickTask>> randomTickData = parallel ? Collections.synchronizedList(new LinkedList<>()) : new LinkedList<>();

(parallel ? enqueuedChunks.parallelStream() : enqueuedChunks.stream()).forEach(entry -> {
Chunk chunk = entry.getFirst();
List<RandomTickTask> collectedData = new ObjectArrayList<>();
for (final TickData tickData : entry.getSecond()) {
List<RandomTickTask> data = getRandomTickData(chunk, tickData);
if (data.isEmpty()) {
continue;
final boolean parallel = SHOULD_PARALLEL && enqueuedChunks.size() * randomTickSpeed >= 300;
final int concurrency = parallel ? Runtime.getRuntime().availableProcessors() : 1;
final List<List<RandomTickTask>> randomTickData = parallel ? Collections.synchronizedList(new LinkedList<>()) : new LinkedList<>();

IntStream stream = parallel ? IntStream.range(0, concurrency).parallel() : IntStream.range(0, concurrency);
stream.forEach(i -> {
Tuple<Chunk, List<TickData>> data;
while ((data = enqueuedChunks.poll()) != null) {
List<RandomTickTask> collectedData = new ObjectArrayList<>();
for (final TickData tickData : data.getSecond()) {
List<RandomTickTask> data1 = getRandomTickData(data.getFirst(), tickData);
if (data1.isEmpty()) {
continue;
}
collectedData.addAll(data1);
}
if (!collectedData.isEmpty()) {
randomTickData.add(collectedData);
}
collectedData.addAll(data);
}
if (!collectedData.isEmpty()) {
randomTickData.add(collectedData);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class StellarCoreEarlyMixinLoader implements IFMLLoadingPlugin {
addMixinCFG("mixins.stellar_core_forge_bakedquad_vertexdata.json", () -> StellarCoreConfig.PERFORMANCE.forge.unpackedBakedQuadVertexDataCanonicalization);
addMixinCFG("mixins.stellar_core_forge_modelloader.json", () -> StellarCoreConfig.PERFORMANCE.vanilla.parallelModelLoader);
addMixinCFG("mixins.stellar_core_hudcaching.json", () -> StellarCoreConfig.PERFORMANCE.vanilla.hudCaching);
addMixinCFG("mixins.stellar_core_ebwizardry_early.json", () -> StellarCoreConfig.PERFORMANCE.ebWizardry.dispenserCastingData);
}

private static void addMixinCFG(final String mixinConfig) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@

import electroblob.wizardry.data.DispenserCastingData;
import github.kasuminova.stellarcore.common.config.StellarCoreConfig;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.*;
import java.util.stream.Collectors;

@Mixin(value = DispenserCastingData.class, remap = false)
public abstract class MixinDispenserCastingData {

Expand All @@ -22,29 +16,7 @@ private static void injectOnWorldTickEvent(final TickEvent.WorldTickEvent event,
if (!StellarCoreConfig.PERFORMANCE.ebWizardry.dispenserCastingData) {
return;
}
if (event.phase == TickEvent.Phase.START || event.world.loadedTileEntityList.size() <= 5_000) {
return;
}
ci.cancel();

// Use parallel stream to find Capability to improve performance.
try {
stellar_core$executeParallel(event.world.loadedTileEntityList);
} catch (ConcurrentModificationException | NullPointerException e) {
// CME?
stellar_core$executeParallel(new ArrayList<>(event.world.loadedTileEntityList));
}
}

@Unique
private static void stellar_core$executeParallel(final List<TileEntity> tileEntityList) {
tileEntityList.parallelStream()
.filter(TileEntityDispenser.class::isInstance)
.map(TileEntityDispenser.class::cast)
.map(DispenserCastingData::get)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedList::new))
.forEach(DispenserCastingData::update);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package github.kasuminova.stellarcore.mixin.ebwizardry;

import electroblob.wizardry.data.DispenserCastingData;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.tileentity.TileEntityLockableLoot;
import net.minecraft.util.ITickable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;

@Mixin(TileEntityDispenser.class)
public abstract class MixinTileEntityDispenser extends TileEntityLockableLoot implements ITickable {

@Unique
@Override
@SuppressWarnings({"DataFlowIssue", "AddedMixinMembersNamePattern"})
public void update() {
if (world.isRemote) {
return;
}
DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser) (Object) this);
if (data != null) {
data.update();
}
}

}
Loading

0 comments on commit 1ec5e47

Please sign in to comment.