feat(perf): add stack fallback sampling
This commit is contained in:
1 parent
4a107b4f20
commit
03bf028855
10 files changed
+303
-16
No files matched your search
+29
-4
@@ -10,6 +10,7 @@ import java.util.Objects;
|
||||
|
||||
public record PerfReportData(long reportId,
|
||||
double serverMspt,
|
||||
long lastTickGcMillis,
|
||||
long elapsedMillis,
|
||||
long generatedAtEpochMillis,
|
||||
long tickCount,
|
||||
@@ -27,6 +28,9 @@ public record PerfReportData(long reportId,
|
||||
if (reportId < 0L) {
|
||||
throw new IllegalArgumentException("Report ID must be non-negative");
|
||||
}
|
||||
if (lastTickGcMillis < 0L) {
|
||||
throw new IllegalArgumentException("Last tick GC time must be non-negative");
|
||||
}
|
||||
if (omittedChunks < 0 || omittedTickables < 0
|
||||
|| omittedOwnerSources < 0 || omittedLoaderSources < 0) {
|
||||
throw new IllegalArgumentException("Omitted report counts must be non-negative");
|
||||
@@ -51,7 +55,27 @@ public record PerfReportData(long reportId,
|
||||
List<TickableEntry> tickables,
|
||||
List<OwnerEntry> owners,
|
||||
List<LoaderSourceEntry> loaderSources) {
|
||||
this(0L, serverMspt, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples,
|
||||
this(0L, serverMspt, 0L, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples,
|
||||
intervalMicros, omittedChunks, omittedTickables, omittedOwnerSources,
|
||||
omittedLoaderSources, chunks, tickables, owners, loaderSources);
|
||||
}
|
||||
|
||||
public PerfReportData(long reportId,
|
||||
double serverMspt,
|
||||
long elapsedMillis,
|
||||
long generatedAtEpochMillis,
|
||||
long tickCount,
|
||||
long totalSamples,
|
||||
int intervalMicros,
|
||||
int omittedChunks,
|
||||
int omittedTickables,
|
||||
int omittedOwnerSources,
|
||||
int omittedLoaderSources,
|
||||
List<ChunkEntry> chunks,
|
||||
List<TickableEntry> tickables,
|
||||
List<OwnerEntry> owners,
|
||||
List<LoaderSourceEntry> loaderSources) {
|
||||
this(reportId, serverMspt, 0L, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples,
|
||||
intervalMicros, omittedChunks, omittedTickables, omittedOwnerSources,
|
||||
omittedLoaderSources, chunks, tickables, owners, loaderSources);
|
||||
}
|
||||
@@ -68,19 +92,20 @@ public record PerfReportData(long reportId,
|
||||
List<ChunkEntry> chunks,
|
||||
List<TickableEntry> tickables,
|
||||
List<OwnerEntry> owners) {
|
||||
this(0L, serverMspt, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples, intervalMicros,
|
||||
this(0L, serverMspt, 0L, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples, intervalMicros,
|
||||
omittedChunks, omittedTickables, omittedOwnerSources, 0,
|
||||
chunks, tickables, owners, List.of());
|
||||
}
|
||||
|
||||
public static PerfReportData empty(int intervalMicros) {
|
||||
return new PerfReportData(0L, 0.0D, 0L, 0L, 0L, 0L, intervalMicros,
|
||||
return new PerfReportData(0L, 0.0D, 0L, 0L, 0L, 0L, 0L, intervalMicros,
|
||||
0, 0, 0, 0, List.of(), List.of(), List.of(), List.of());
|
||||
}
|
||||
|
||||
public PerfReportData withLoaderSources(List<LoaderSourceEntry> nextLoaderSources) {
|
||||
return loaderSources.equals(nextLoaderSources) ? this : new PerfReportData(
|
||||
reportId, serverMspt, elapsedMillis, generatedAtEpochMillis, tickCount, totalSamples,
|
||||
reportId, serverMspt, lastTickGcMillis, elapsedMillis, generatedAtEpochMillis,
|
||||
tickCount, totalSamples,
|
||||
intervalMicros, omittedChunks, omittedTickables, omittedOwnerSources,
|
||||
omittedLoaderSources, chunks, tickables, owners, nextLoaderSources);
|
||||
}
|
||||
|
||||
+4
-1
@@ -66,7 +66,10 @@ public final class PerfReportTextRenderer {
|
||||
id += "#" + entry.entityId();
|
||||
}
|
||||
if (entry.type() == TickType.TASK) {
|
||||
return id + " @ " + entry.eventLoop();
|
||||
return entry.eventLoop().isEmpty() ? id : id + " @ " + entry.eventLoop();
|
||||
}
|
||||
if (entry.type() == TickType.HANDLER) {
|
||||
return entry.type() + " " + entry.eventLoop();
|
||||
}
|
||||
if (!entry.positioned()) {
|
||||
return id + " @ " + entry.dimension();
|
||||
|
||||
+6
-2
@@ -34,6 +34,7 @@ final class PerfSampleAggregator {
|
||||
private int chunkDropsUntilReplacement = REPLACEMENT_INTERVAL;
|
||||
private long tickCount;
|
||||
private long totalTickNanos;
|
||||
private long lastTickGcMillis;
|
||||
|
||||
PerfSampleAggregator(long seed) {
|
||||
reportId = seed;
|
||||
@@ -50,6 +51,7 @@ final class PerfSampleAggregator {
|
||||
void accept(TickSamples tick) {
|
||||
tickCount++;
|
||||
totalTickNanos += tick.durationNanos;
|
||||
lastTickGcMillis = tick.gcTimeMillis;
|
||||
if (tick.totalSamples == 0) {
|
||||
ownerAggregator.updateMetadata(tick.attribution);
|
||||
} else {
|
||||
@@ -72,7 +74,8 @@ final class PerfSampleAggregator {
|
||||
PerfReportData.TickableEntry::metric);
|
||||
PerfOwnerAggregator.Build ownerBuild = ownerAggregator.build(tickCount, nanosPerSample);
|
||||
double serverMspt = tickCount == 0 ? 0.0D : totalTickNanos / 1_000_000.0D / tickCount;
|
||||
return new PerfReportData(reportId, serverMspt, elapsedNanos / 1_000_000L, System.currentTimeMillis(),
|
||||
return new PerfReportData(reportId, serverMspt, lastTickGcMillis,
|
||||
elapsedNanos / 1_000_000L, System.currentTimeMillis(),
|
||||
tickCount, totalSamples, intervalMicros,
|
||||
omitted(limitedChunks.omitted(), droppedChunkHashes),
|
||||
omitted((long) sampledTickables.omitted() + limitedTickables.omitted(), droppedTickableHashes),
|
||||
@@ -261,7 +264,8 @@ final class PerfSampleAggregator {
|
||||
TickSamples.TickableKey key,
|
||||
long ticks,
|
||||
double nanosPerSample) {
|
||||
return new PerfReportData.TickableEntry(key.type(), key.dimension(), className(key.workClass()),
|
||||
String displayName = key.displayName().isEmpty() ? className(key.workClass()) : key.displayName();
|
||||
return new PerfReportData.TickableEntry(key.type(), key.dimension(), displayName,
|
||||
nullableId(key.detailId()), key.detailKind(), key.entityId(), positioned,
|
||||
x, y, z, chunkPosition, key.eventLoop(),
|
||||
stats.metric(ticks, nanosPerSample));
|
||||
|
||||
+20
-1
@@ -24,10 +24,12 @@ public final class PerfSamplerSession {
|
||||
private final long durationNanos;
|
||||
private final SplittableRandom intervalRandom;
|
||||
private final AnalysisClock analysisClock;
|
||||
private final TickGcTimeTracker gcTimeTracker = new TickGcTimeTracker();
|
||||
private final Thread samplerThread;
|
||||
private final WindowsTimerResolution timerResolution = new WindowsTimerResolution();
|
||||
private volatile PerfReportData latestReport;
|
||||
private volatile TickSamples currentTick;
|
||||
private TickSamples betweenTicks = new TickSamples(null);
|
||||
private long sampleAttempts;
|
||||
private long acceptedTicks;
|
||||
private int publishedEntryKinds;
|
||||
@@ -125,7 +127,11 @@ public final class PerfSamplerSession {
|
||||
if (analysisClock.state() != AnalysisClock.State.RUNNING) {
|
||||
throw new IllegalStateException("Cannot begin a tick while sampling is not running");
|
||||
}
|
||||
currentTick = new TickSamples(attribution);
|
||||
TickSamples tick = new TickSamples(attribution);
|
||||
tick.mergeSamples(betweenTicks);
|
||||
betweenTicks = new TickSamples(null);
|
||||
tick.gcStartMillis = gcTimeTracker.snapshot();
|
||||
currentTick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +152,7 @@ public final class PerfSamplerSession {
|
||||
return;
|
||||
}
|
||||
tick.durationNanos = durationNanos;
|
||||
tick.gcTimeMillis = gcTimeTracker.elapsedSince(tick.gcStartMillis);
|
||||
currentTick = null;
|
||||
closedTicks.add(tick);
|
||||
}
|
||||
@@ -159,6 +166,7 @@ public final class PerfSamplerSession {
|
||||
Measurement measurement;
|
||||
synchronized (stateLock) {
|
||||
currentTick = null;
|
||||
betweenTicks = new TickSamples(null);
|
||||
analysisClock.stop();
|
||||
measurement = measurementLocked();
|
||||
}
|
||||
@@ -216,6 +224,15 @@ public final class PerfSamplerSession {
|
||||
}
|
||||
TickSamples expected = currentTick;
|
||||
PerfWorkTracker.Snapshot snapshot = PerfWorkTracker.snapshot();
|
||||
if (snapshot == null) {
|
||||
snapshot = PerfStackAttributor.capture(PerfWorkTracker.profiledThread());
|
||||
} else if (snapshot.type() == TickType.ENTITY) {
|
||||
PerfWorkTracker.Snapshot handler =
|
||||
PerfStackAttributor.captureTickHandler(PerfWorkTracker.profiledThread());
|
||||
if (handler != null) {
|
||||
snapshot = handler;
|
||||
}
|
||||
}
|
||||
synchronized (stateLock) {
|
||||
SamplerStatus status = samplerStatusLocked();
|
||||
if (status != SamplerStatus.RUNNING) {
|
||||
@@ -224,6 +241,8 @@ public final class PerfSamplerSession {
|
||||
sampleAttempts++;
|
||||
if (expected != null && currentTick == expected) {
|
||||
expected.sample(snapshot);
|
||||
} else if (expected == null && currentTick == null && snapshot != null) {
|
||||
betweenTicks.sample(snapshot);
|
||||
}
|
||||
return SamplerStatus.RUNNING;
|
||||
}
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.moepus.serverwarashi.common.modules.performance.sampling;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.Map;
|
||||
|
||||
final class PerfStackAttributor {
|
||||
private static final int MAX_STACK_DEPTH = 96;
|
||||
private static final String EVENT_BUS_PACKAGE = "net.neoforged.bus.";
|
||||
private static final String EVENT_BUS_CLASS = "net.neoforged.bus.EventBus";
|
||||
private static final String EVENT_HOOKS_CLASS = "net.neoforged.neoforge.event.EventHooks";
|
||||
private static final String MINECRAFT_SERVER_CLASS = "net.minecraft.server.MinecraftServer";
|
||||
private static final String UNKNOWN_NAME = "Unknown";
|
||||
private static final ThreadMXBean THREADS = ManagementFactory.getThreadMXBean();
|
||||
private static final Map<String, TickEvent> TICK_EVENTS = Map.of(
|
||||
"fireServerTickPre", tickEvent("ServerTickEvent.Pre"),
|
||||
"fireServerTickPost", tickEvent("ServerTickEvent.Post"),
|
||||
"fireLevelTickPre", tickEvent("LevelTickEvent.Pre"),
|
||||
"fireLevelTickPost", tickEvent("LevelTickEvent.Post"),
|
||||
"firePlayerTickPre", tickEvent("PlayerTickEvent.Pre"),
|
||||
"firePlayerTickPost", tickEvent("PlayerTickEvent.Post"),
|
||||
"fireEntityTickPre", tickEvent("EntityTickEvent.Pre"),
|
||||
"fireEntityTickPost", tickEvent("EntityTickEvent.Post")
|
||||
);
|
||||
|
||||
private PerfStackAttributor() {
|
||||
}
|
||||
|
||||
static PerfWorkTracker.Snapshot capture(Thread thread) {
|
||||
StackTraceElement[] stack = captureStack(thread);
|
||||
return stack == null ? null : classify(stack);
|
||||
}
|
||||
|
||||
static PerfWorkTracker.Snapshot captureTickHandler(Thread thread) {
|
||||
StackTraceElement[] stack = captureStack(thread);
|
||||
return stack == null ? null : findTickHandler(stack);
|
||||
}
|
||||
|
||||
private static StackTraceElement[] captureStack(Thread thread) {
|
||||
if (thread == null || !thread.isAlive()) {
|
||||
return null;
|
||||
}
|
||||
ThreadInfo info = THREADS.getThreadInfo(thread.threadId(), MAX_STACK_DEPTH);
|
||||
return info == null ? null : info.getStackTrace();
|
||||
}
|
||||
|
||||
static PerfWorkTracker.Snapshot classify(StackTraceElement[] stack) {
|
||||
PerfWorkTracker.Snapshot handler = findTickHandler(stack);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
return isNormalTickIdle(stack) ? null : unknown();
|
||||
}
|
||||
|
||||
private static PerfWorkTracker.Snapshot findTickHandler(StackTraceElement[] stack) {
|
||||
for (int hookIndex = 0; hookIndex < stack.length; hookIndex++) {
|
||||
StackTraceElement hook = stack[hookIndex];
|
||||
TickEvent event = tickEvent(hook);
|
||||
if (event == null) {
|
||||
continue;
|
||||
}
|
||||
StackTraceElement handler = handlerAboveDispatch(stack, hookIndex);
|
||||
if (handler != null) {
|
||||
return handler(handler, event);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TickEvent tickEvent(StackTraceElement frame) {
|
||||
return EVENT_HOOKS_CLASS.equals(frame.getClassName())
|
||||
? TICK_EVENTS.get(frame.getMethodName()) : null;
|
||||
}
|
||||
|
||||
private static StackTraceElement handlerAboveDispatch(StackTraceElement[] stack, int hookIndex) {
|
||||
boolean foundPost = false;
|
||||
for (int index = hookIndex - 1; index >= 0; index--) {
|
||||
StackTraceElement frame = stack[index];
|
||||
if (isDispatchInfrastructure(frame)) {
|
||||
foundPost |= isEventBusPost(frame);
|
||||
continue;
|
||||
}
|
||||
return foundPost ? frame : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isDispatchInfrastructure(StackTraceElement frame) {
|
||||
String className = frame.getClassName();
|
||||
return className.startsWith(EVENT_BUS_PACKAGE)
|
||||
|| className.startsWith("java.lang.invoke.")
|
||||
|| className.startsWith("java.lang.reflect.")
|
||||
|| className.startsWith("jdk.internal.reflect.");
|
||||
}
|
||||
|
||||
private static boolean isEventBusPost(StackTraceElement frame) {
|
||||
return EVENT_BUS_CLASS.equals(frame.getClassName()) && "post".equals(frame.getMethodName());
|
||||
}
|
||||
|
||||
private static boolean isNormalTickIdle(StackTraceElement[] stack) {
|
||||
boolean waiting = false;
|
||||
boolean betweenTicks = false;
|
||||
for (StackTraceElement frame : stack) {
|
||||
waiting |= "waitForTasks".equals(frame.getMethodName());
|
||||
betweenTicks |= MINECRAFT_SERVER_CLASS.equals(frame.getClassName())
|
||||
&& "waitUntilNextTick".equals(frame.getMethodName());
|
||||
}
|
||||
return waiting && betweenTicks;
|
||||
}
|
||||
|
||||
private static PerfWorkTracker.Snapshot handler(StackTraceElement frame, TickEvent event) {
|
||||
String fullClass = readableClassName(frame.getClassName());
|
||||
String shortClass = shortClassName(frame.getClassName());
|
||||
String fullName = fullClass + "#" + frame.getMethodName() + "(" + event.fullName() + ")";
|
||||
String shortName = shortClass + "#" + frame.getMethodName() + "(" + event.shortName() + ")";
|
||||
return snapshot(TickType.HANDLER, HandlerWork.class, shortName, fullName);
|
||||
}
|
||||
|
||||
private static PerfWorkTracker.Snapshot unknown() {
|
||||
return snapshot(TickType.TASK, UnknownWork.class, UNKNOWN_NAME, "");
|
||||
}
|
||||
|
||||
private static PerfWorkTracker.Snapshot snapshot(
|
||||
TickType type, Class<?> workClass, String displayName, String fullName) {
|
||||
return new PerfWorkTracker.Snapshot(type, null, workClass, null, TickDetailKind.NONE,
|
||||
-1, false, 0, 0, 0, false, fullName, displayName);
|
||||
}
|
||||
|
||||
private static TickEvent tickEvent(String nestedName) {
|
||||
String packageName = "net.neoforged.neoforge.event.tick.";
|
||||
return new TickEvent(nestedName, packageName + nestedName);
|
||||
}
|
||||
|
||||
private static String readableClassName(String binaryName) {
|
||||
int hiddenSuffix = binaryName.indexOf('/');
|
||||
String visible = hiddenSuffix < 0 ? binaryName : binaryName.substring(0, hiddenSuffix);
|
||||
return visible.replace('$', '.');
|
||||
}
|
||||
|
||||
private static String shortClassName(String binaryName) {
|
||||
int packageEnd = binaryName.lastIndexOf('.');
|
||||
return readableClassName(binaryName.substring(packageEnd + 1));
|
||||
}
|
||||
|
||||
private record TickEvent(String shortName, String fullName) {
|
||||
}
|
||||
|
||||
private static final class HandlerWork {
|
||||
}
|
||||
|
||||
private static final class UnknownWork {
|
||||
}
|
||||
}
|
||||
+25
-3
@@ -17,7 +17,7 @@ public final class PerfWorkTracker {
|
||||
|
||||
public static volatile boolean isProfiling;
|
||||
private static volatile Slot current;
|
||||
private static Thread serverThread;
|
||||
private static volatile Thread serverThread;
|
||||
private static int depth;
|
||||
private static int overflowDepth;
|
||||
|
||||
@@ -123,6 +123,10 @@ public final class PerfWorkTracker {
|
||||
return null;
|
||||
}
|
||||
|
||||
static Thread profiledThread() {
|
||||
return isProfiling ? serverThread : null;
|
||||
}
|
||||
|
||||
private static boolean canPublish() {
|
||||
return isProfiling && Thread.currentThread() == serverThread;
|
||||
}
|
||||
@@ -169,7 +173,23 @@ public final class PerfWorkTracker {
|
||||
int y,
|
||||
int z,
|
||||
boolean chunkPosition,
|
||||
String eventLoop) {
|
||||
String eventLoop,
|
||||
String displayName) {
|
||||
public Snapshot(TickType type,
|
||||
ResourceLocation dimension,
|
||||
Class<?> workClass,
|
||||
ResourceLocation detailId,
|
||||
TickDetailKind detailKind,
|
||||
int entityId,
|
||||
boolean positioned,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
boolean chunkPosition,
|
||||
String eventLoop) {
|
||||
this(type, dimension, workClass, detailId, detailKind, entityId,
|
||||
positioned, x, y, z, chunkPosition, eventLoop, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Slot {
|
||||
@@ -187,6 +207,7 @@ public final class PerfWorkTracker {
|
||||
private int z;
|
||||
private boolean chunkPosition;
|
||||
private String eventLoop;
|
||||
private String displayName;
|
||||
|
||||
void publishChunk(ResourceLocation dimension, LevelChunk chunk) {
|
||||
var pos = chunk.getPos();
|
||||
@@ -259,6 +280,7 @@ public final class PerfWorkTracker {
|
||||
version++;
|
||||
this.type = type;
|
||||
this.workClass = workClass;
|
||||
displayName = "";
|
||||
}
|
||||
|
||||
private void finishPublish() {
|
||||
@@ -279,7 +301,7 @@ public final class PerfWorkTracker {
|
||||
}
|
||||
Snapshot result = new Snapshot(type, dimension, workClass, detailId, detailKind,
|
||||
entityId, positioned,
|
||||
x, y, z, chunkPosition, eventLoop);
|
||||
x, y, z, chunkPosition, eventLoop, displayName);
|
||||
return before == version ? result : null;
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.moepus.serverwarashi.common.modules.performance.sampling;
|
||||
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.List;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
final class TickGcTimeTracker {
|
||||
private final LongSupplier totalCollectionMillis;
|
||||
|
||||
TickGcTimeTracker() {
|
||||
List<GarbageCollectorMXBean> collectors = List.copyOf(ManagementFactory.getGarbageCollectorMXBeans());
|
||||
totalCollectionMillis = () -> totalCollectionMillis(collectors);
|
||||
}
|
||||
|
||||
TickGcTimeTracker(LongSupplier totalCollectionMillis) {
|
||||
this.totalCollectionMillis = totalCollectionMillis;
|
||||
}
|
||||
|
||||
long snapshot() {
|
||||
return totalCollectionMillis.getAsLong();
|
||||
}
|
||||
|
||||
long elapsedSince(long snapshot) {
|
||||
return Math.max(0L, snapshot() - snapshot);
|
||||
}
|
||||
|
||||
private static long totalCollectionMillis(List<GarbageCollectorMXBean> collectors) {
|
||||
long total = 0L;
|
||||
for (GarbageCollectorMXBean collector : collectors) {
|
||||
long collectionTime = collector.getCollectionTime();
|
||||
if (collectionTime > 0L) {
|
||||
total += collectionTime;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
+22
-3
@@ -13,6 +13,8 @@ final class TickSamples {
|
||||
final Map<ChunkKey, Integer> chunks = new HashMap<>();
|
||||
PerfAttributionSnapshot attribution;
|
||||
long durationNanos;
|
||||
long gcStartMillis;
|
||||
long gcTimeMillis;
|
||||
int totalSamples;
|
||||
boolean refreshReport;
|
||||
|
||||
@@ -33,6 +35,13 @@ final class TickSamples {
|
||||
}
|
||||
}
|
||||
|
||||
void mergeSamples(TickSamples source) {
|
||||
totalSamples += source.totalSamples;
|
||||
source.tickables.forEach((key, count) ->
|
||||
tickables.computeIfAbsent(key, ignored -> new TickCount()).merge(count));
|
||||
source.chunks.forEach((key, count) -> chunks.merge(key, count, Integer::sum));
|
||||
}
|
||||
|
||||
record ChunkKey(String dimension, long position) {
|
||||
static ChunkKey from(PerfWorkTracker.Snapshot snapshot) {
|
||||
if (!snapshot.positioned() || snapshot.dimension() == null) {
|
||||
@@ -52,17 +61,18 @@ final class TickSamples {
|
||||
TickDetailKind detailKind,
|
||||
int entityId,
|
||||
long fixedPosition,
|
||||
String eventLoop) {
|
||||
String eventLoop,
|
||||
String displayName) {
|
||||
static TickableKey from(PerfWorkTracker.Snapshot snapshot) {
|
||||
String dimension = snapshot.dimension() == null ? "" : snapshot.dimension().toString();
|
||||
long position = switch (snapshot.type()) {
|
||||
case CHUNK -> ChunkPos.asLong(snapshot.x(), snapshot.z());
|
||||
case BLOCK_ENTITY -> BlockPos.asLong(snapshot.x(), snapshot.y(), snapshot.z());
|
||||
case ENTITY, TASK -> 0L;
|
||||
case ENTITY, TASK, HANDLER -> 0L;
|
||||
};
|
||||
return new TickableKey(snapshot.type(), dimension, snapshot.workClass(), snapshot.detailId(),
|
||||
snapshot.detailKind(),
|
||||
snapshot.entityId(), position, snapshot.eventLoop());
|
||||
snapshot.entityId(), position, snapshot.eventLoop(), snapshot.displayName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,5 +92,14 @@ final class TickSamples {
|
||||
y = snapshot.y();
|
||||
z = snapshot.z();
|
||||
}
|
||||
|
||||
void merge(TickCount source) {
|
||||
count += source.count;
|
||||
positioned = source.positioned;
|
||||
chunkPosition = source.chunkPosition;
|
||||
x = source.x;
|
||||
y = source.y;
|
||||
z = source.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -4,5 +4,6 @@ public enum TickType {
|
||||
CHUNK,
|
||||
ENTITY,
|
||||
BLOCK_ENTITY,
|
||||
TASK
|
||||
TASK,
|
||||
HANDLER
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public final class PerfReportCodec {
|
||||
EntryBudget budget = new EntryBudget();
|
||||
buffer.writeVarLong(report.reportId());
|
||||
buffer.writeDouble(report.serverMspt());
|
||||
buffer.writeVarLong(report.lastTickGcMillis());
|
||||
buffer.writeVarLong(report.elapsedMillis());
|
||||
buffer.writeVarLong(report.generatedAtEpochMillis());
|
||||
buffer.writeVarLong(report.tickCount());
|
||||
@@ -40,6 +41,7 @@ public final class PerfReportCodec {
|
||||
EntryBudget budget = new EntryBudget();
|
||||
long reportId = buffer.readVarLong();
|
||||
double serverMspt = buffer.readDouble();
|
||||
long lastTickGcMillis = buffer.readVarLong();
|
||||
long elapsedMillis = buffer.readVarLong();
|
||||
long generatedAtEpochMillis = buffer.readVarLong();
|
||||
long tickCount = buffer.readVarLong();
|
||||
@@ -53,7 +55,7 @@ public final class PerfReportCodec {
|
||||
List<PerfReportData.TickableEntry> tickables = readList(buffer, PerfReportCodec::readTickable, budget);
|
||||
List<PerfReportData.OwnerEntry> owners = readList(buffer, PerfReportCodec::readOwner, budget);
|
||||
List<PerfReportData.LoaderSourceEntry> loaders = PerfLoaderReportCodec.read(buffer, budget);
|
||||
return new PerfReportData(reportId, serverMspt, elapsedMillis, generatedAtEpochMillis,
|
||||
return new PerfReportData(reportId, serverMspt, lastTickGcMillis, elapsedMillis, generatedAtEpochMillis,
|
||||
tickCount, totalSamples, intervalMicros, omittedChunks, omittedTickables,
|
||||
omittedOwnerSources, omittedLoaderSources, chunks, tickables, owners, loaders);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user