fix(perf): avoid periodic attribution stalls

This commit is contained in:
MoePus committed 2026-08-19 19:21:26 +09:00
1 parent d886b1370b
commit 937c7f2edb
5 files changed
+54 -20

No files matched your search

@@ -0,0 +1,15 @@
package com.moepus.serverwarashi.common.core.owner;
record ChunkKey(String dimension, long chunkPos) {
private static final long MURMUR_C1 = 0xff51afd7ed558ccdL;
private static final long MURMUR_C2 = 0xc4ceb9fe1a85ec53L;
@Override
public int hashCode() {
long mixed = chunkPos;
mixed = (mixed ^ mixed >>> 33) * MURMUR_C1;
mixed = (mixed ^ mixed >>> 33) * MURMUR_C2;
mixed ^= mixed >>> 33;
return 31 * dimension.hashCode() + Long.hashCode(mixed);
}
}
@@ -11,6 +11,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.Ticket;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -41,8 +42,8 @@ public final class PerfAttributionSnapshot {
Map<ChunkKey, List<Bucket>> loaderByChunk,
Map<BlockLoaderRef, List<LoaderTicketTarget>> loaderTargets,
Map<BlockLoaderRef, LoaderControl> loaderControls) {
this.byChunk = Map.copyOf(byChunk);
this.loaderByChunk = Map.copyOf(loaderByChunk);
this.byChunk = Collections.unmodifiableMap(byChunk);
this.loaderByChunk = Collections.unmodifiableMap(loaderByChunk);
this.loaderTargets = copyTargets(loaderTargets);
this.loaderControls = new ConcurrentHashMap<>(loaderControls);
Map<SourceIdentity, Bucket> unique = new HashMap<>();
@@ -52,7 +53,11 @@ public final class PerfAttributionSnapshot {
}
public static PerfAttributionSnapshot capture(MinecraftServer server) {
return captureWithFingerprint(server).snapshot();
CaptureMaps maps = new CaptureMaps();
for (ServerLevel level : server.getAllLevels()) {
captureLevel(level, maps, null, true);
}
return fromCapture(maps);
}
public static Capture captureWithFingerprint(MinecraftServer server) {
@@ -124,9 +129,9 @@ public final class PerfAttributionSnapshot {
for (Bucket bucket : entry.getValue()) {
if (isLoaderSource(bucket)) {
var key = new LoaderRegionPlanner.SourceKey(
entry.getKey().dimension, bucket.source.id,
entry.getKey().dimension(), bucket.source.id,
bucket.source.name, bucket.source.position, bucket.source.loaderRef);
grouped.computeIfAbsent(key, ignored -> new HashSet<>()).add(entry.getKey().chunkPos);
grouped.computeIfAbsent(key, ignored -> new HashSet<>()).add(entry.getKey().chunkPos());
}
}
}
@@ -376,9 +381,6 @@ public final class PerfAttributionSnapshot {
}
}
private record ChunkKey(String dimension, long chunkPos) {
}
private record OwnerIdentity(PerfReportData.AttributionKind kind, String id) {
static OwnerIdentity from(Bucket bucket) {
return new OwnerIdentity(bucket.owner.kind, bucket.owner.id);
@@ -27,7 +27,7 @@ final class TicketTopologyFingerprint {
void addTickets(long chunkPos, Iterable<? extends Ticket<?>> tickets) {
boolean chunkAdded = false;
for (Ticket<?> ticket : tickets) {
if (!isPotentialLoaderTicket(ticket)) {
if (!isAttributableTicketType(ticket.getType())) {
continue;
}
if (!chunkAdded) {
@@ -48,12 +48,7 @@ final class TicketTopologyFingerprint {
value *= FNV_PRIME;
}
private static boolean isPotentialLoaderTicket(Ticket<?> ticket) {
return ticket.getType() != TicketType.PLAYER
&& ticket.getType() != TicketType.START
&& ticket.getType() != TicketType.PORTAL
&& ticket.getType() != TicketType.POST_TELEPORT
&& ticket.getType() != TicketType.DRAGON
&& ticket.getType() != TicketType.UNKNOWN;
static boolean isAttributableTicketType(TicketType<?> type) {
return type != TicketType.UNKNOWN;
}
}
@@ -13,7 +13,6 @@ import java.util.function.IntSupplier;
final class PerfSessionController {
private static final long NANOS_PER_SECOND = 1_000_000_000L;
private static final int TOPOLOGY_CHECK_TICKS = 5;
private static final int ATTRIBUTION_REFRESH_TICKS = 20;
private final IntSupplier sampleIntervalMicros;
private ActiveSession active;
@@ -189,8 +188,7 @@ final class PerfSessionController {
&& session.completedTicks % TOPOLOGY_CHECK_TICKS == 0) {
long topologyFingerprint = PerfAttributionSnapshot.ticketFingerprint(server);
boolean topologyChanged = topologyFingerprint != session.ticketFingerprint;
boolean periodicRefresh = session.completedTicks % ATTRIBUTION_REFRESH_TICKS == 0;
if (!topologyChanged && !periodicRefresh && session.attribution != null) {
if (!topologyChanged && session.attribution != null) {
return;
}
PerfAttributionSnapshot next = PerfAttributionSnapshot.capture(server);
@@ -6,8 +6,9 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
@@ -21,6 +22,29 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PerfAttributionSnapshotTest {
@Test
void distributesPackedChunkCoordinatesAcrossHashSpace() {
Set<Integer> hashes = new HashSet<>();
int positions = 0;
for (int z = -32; z <= 32; z++) {
for (int x = -32; x <= 32; x++) {
hashes.add(new ChunkKey("test:level", ChunkPos.asLong(x, z)).hashCode());
positions++;
}
}
assertTrue(hashes.size() >= positions * 99 / 100,
"Expected well-distributed hashes, got " + hashes.size() + " for " + positions + " keys");
}
@Test
void fingerprintsEveryTicketTypeThatProducesAttribution() {
assertTrue(TicketTopologyFingerprint.isAttributableTicketType(TicketType.PLAYER));
assertTrue(TicketTopologyFingerprint.isAttributableTicketType(TicketType.START));
assertTrue(TicketTopologyFingerprint.isAttributableTicketType(TicketType.FORCED));
assertFalse(TicketTopologyFingerprint.isAttributableTicketType(TicketType.UNKNOWN));
}
@Test
void expandsLevel31TargetsByOneBlockTickingChunk() {
assertEquals(1, LoaderTicketInfluence.blockTickingRadius(31));