314 lines
10 KiB
Java
314 lines
10 KiB
Java
package net.minecraft.util.profiling;
|
|
|
|
import com.google.common.base.Splitter;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import com.mojang.logging.LogUtils;
|
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2LongMaps;
|
|
import java.io.Writer;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import net.minecraft.ReportType;
|
|
import net.minecraft.SharedConstants;
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.apache.commons.lang3.ObjectUtils;
|
|
import org.slf4j.Logger;
|
|
|
|
public class FilledProfileResults implements ProfileResults {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final ProfilerPathEntry EMPTY = new ProfilerPathEntry() {
|
|
@Override
|
|
public long getDuration() {
|
|
return 0L;
|
|
}
|
|
|
|
@Override
|
|
public long getMaxDuration() {
|
|
return 0L;
|
|
}
|
|
|
|
@Override
|
|
public long getCount() {
|
|
return 0L;
|
|
}
|
|
|
|
@Override
|
|
public Object2LongMap<String> getCounters() {
|
|
return Object2LongMaps.emptyMap();
|
|
}
|
|
};
|
|
private static final Splitter SPLITTER = Splitter.on('\u001e');
|
|
private static final Comparator<Entry<String, FilledProfileResults.CounterCollector>> COUNTER_ENTRY_COMPARATOR = Entry.comparingByValue(
|
|
Comparator.comparingLong(counterCollector -> counterCollector.totalValue)
|
|
)
|
|
.reversed();
|
|
private final Map<String, ? extends ProfilerPathEntry> entries;
|
|
private final long startTimeNano;
|
|
private final int startTimeTicks;
|
|
private final long endTimeNano;
|
|
private final int endTimeTicks;
|
|
private final int tickDuration;
|
|
|
|
public FilledProfileResults(Map<String, ? extends ProfilerPathEntry> entries, long startTimeNano, int startTimeTicks, long endTimeNano, int endTimeTicks) {
|
|
this.entries = entries;
|
|
this.startTimeNano = startTimeNano;
|
|
this.startTimeTicks = startTimeTicks;
|
|
this.endTimeNano = endTimeNano;
|
|
this.endTimeTicks = endTimeTicks;
|
|
this.tickDuration = endTimeTicks - startTimeTicks;
|
|
}
|
|
|
|
private ProfilerPathEntry getEntry(String key) {
|
|
ProfilerPathEntry profilerPathEntry = (ProfilerPathEntry)this.entries.get(key);
|
|
return profilerPathEntry != null ? profilerPathEntry : EMPTY;
|
|
}
|
|
|
|
@Override
|
|
public List<ResultField> getTimes(String sectionPath) {
|
|
String string = sectionPath;
|
|
ProfilerPathEntry profilerPathEntry = this.getEntry("root");
|
|
long l = profilerPathEntry.getDuration();
|
|
ProfilerPathEntry profilerPathEntry2 = this.getEntry(sectionPath);
|
|
long m = profilerPathEntry2.getDuration();
|
|
long n = profilerPathEntry2.getCount();
|
|
List<ResultField> list = Lists.<ResultField>newArrayList();
|
|
if (!sectionPath.isEmpty()) {
|
|
sectionPath = sectionPath + "\u001e";
|
|
}
|
|
|
|
long o = 0L;
|
|
|
|
for (String string2 : this.entries.keySet()) {
|
|
if (isDirectChild(sectionPath, string2)) {
|
|
o += this.getEntry(string2).getDuration();
|
|
}
|
|
}
|
|
|
|
float f = (float)o;
|
|
if (o < m) {
|
|
o = m;
|
|
}
|
|
|
|
if (l < o) {
|
|
l = o;
|
|
}
|
|
|
|
for (String string3 : this.entries.keySet()) {
|
|
if (isDirectChild(sectionPath, string3)) {
|
|
ProfilerPathEntry profilerPathEntry3 = this.getEntry(string3);
|
|
long p = profilerPathEntry3.getDuration();
|
|
double d = p * 100.0 / o;
|
|
double e = p * 100.0 / l;
|
|
String string4 = string3.substring(sectionPath.length());
|
|
list.add(new ResultField(string4, d, e, profilerPathEntry3.getCount()));
|
|
}
|
|
}
|
|
|
|
if ((float)o > f) {
|
|
list.add(new ResultField("unspecified", ((float)o - f) * 100.0 / o, ((float)o - f) * 100.0 / l, n));
|
|
}
|
|
|
|
Collections.sort(list);
|
|
list.add(0, new ResultField(string, 100.0, o * 100.0 / l, n));
|
|
return list;
|
|
}
|
|
|
|
private static boolean isDirectChild(String sectionPath, String entry) {
|
|
return entry.length() > sectionPath.length() && entry.startsWith(sectionPath) && entry.indexOf(30, sectionPath.length() + 1) < 0;
|
|
}
|
|
|
|
private Map<String, FilledProfileResults.CounterCollector> getCounterValues() {
|
|
Map<String, FilledProfileResults.CounterCollector> map = Maps.newTreeMap();
|
|
this.entries
|
|
.forEach(
|
|
(string, profilerPathEntry) -> {
|
|
Object2LongMap<String> object2LongMap = profilerPathEntry.getCounters();
|
|
if (!object2LongMap.isEmpty()) {
|
|
List<String> list = SPLITTER.splitToList(string);
|
|
object2LongMap.forEach(
|
|
(stringx, long_) -> ((FilledProfileResults.CounterCollector)map.computeIfAbsent(stringx, stringxx -> new FilledProfileResults.CounterCollector()))
|
|
.addValue(list.iterator(), long_)
|
|
);
|
|
}
|
|
}
|
|
);
|
|
return map;
|
|
}
|
|
|
|
@Override
|
|
public long getStartTimeNano() {
|
|
return this.startTimeNano;
|
|
}
|
|
|
|
@Override
|
|
public int getStartTimeTicks() {
|
|
return this.startTimeTicks;
|
|
}
|
|
|
|
@Override
|
|
public long getEndTimeNano() {
|
|
return this.endTimeNano;
|
|
}
|
|
|
|
@Override
|
|
public int getEndTimeTicks() {
|
|
return this.endTimeTicks;
|
|
}
|
|
|
|
@Override
|
|
public boolean saveResults(Path path) {
|
|
Writer writer = null;
|
|
|
|
boolean var4;
|
|
try {
|
|
Files.createDirectories(path.getParent());
|
|
writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
|
|
writer.write(this.getProfilerResults(this.getNanoDuration(), this.getTickDuration()));
|
|
return true;
|
|
} catch (Throwable var8) {
|
|
LOGGER.error("Could not save profiler results to {}", path, var8);
|
|
var4 = false;
|
|
} finally {
|
|
IOUtils.closeQuietly(writer);
|
|
}
|
|
|
|
return var4;
|
|
}
|
|
|
|
protected String getProfilerResults(long timeSpan, int tickSpan) {
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
ReportType.PROFILE.appendHeader(stringBuilder, List.of());
|
|
stringBuilder.append("Version: ").append(SharedConstants.getCurrentVersion().getId()).append('\n');
|
|
stringBuilder.append("Time span: ").append(timeSpan / 1000000L).append(" ms\n");
|
|
stringBuilder.append("Tick span: ").append(tickSpan).append(" ticks\n");
|
|
stringBuilder.append("// This is approximately ")
|
|
.append(String.format(Locale.ROOT, "%.2f", tickSpan / ((float)timeSpan / 1.0E9F)))
|
|
.append(" ticks per second. It should be ")
|
|
.append(20)
|
|
.append(" ticks per second\n\n");
|
|
stringBuilder.append("--- BEGIN PROFILE DUMP ---\n\n");
|
|
this.appendProfilerResults(0, "root", stringBuilder);
|
|
stringBuilder.append("--- END PROFILE DUMP ---\n\n");
|
|
Map<String, FilledProfileResults.CounterCollector> map = this.getCounterValues();
|
|
if (!map.isEmpty()) {
|
|
stringBuilder.append("--- BEGIN COUNTER DUMP ---\n\n");
|
|
this.appendCounters(map, stringBuilder, tickSpan);
|
|
stringBuilder.append("--- END COUNTER DUMP ---\n\n");
|
|
}
|
|
|
|
return stringBuilder.toString();
|
|
}
|
|
|
|
@Override
|
|
public String getProfilerResults() {
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
this.appendProfilerResults(0, "root", stringBuilder);
|
|
return stringBuilder.toString();
|
|
}
|
|
|
|
private static StringBuilder indentLine(StringBuilder builder, int indents) {
|
|
builder.append(String.format(Locale.ROOT, "[%02d] ", indents));
|
|
|
|
for (int i = 0; i < indents; i++) {
|
|
builder.append("| ");
|
|
}
|
|
|
|
return builder;
|
|
}
|
|
|
|
private void appendProfilerResults(int depth, String sectionPath, StringBuilder builder) {
|
|
List<ResultField> list = this.getTimes(sectionPath);
|
|
Object2LongMap<String> object2LongMap = ObjectUtils.firstNonNull((ProfilerPathEntry)this.entries.get(sectionPath), EMPTY).getCounters();
|
|
object2LongMap.forEach(
|
|
(string, long_) -> indentLine(builder, depth)
|
|
.append('#')
|
|
.append(string)
|
|
.append(' ')
|
|
.append(long_)
|
|
.append('/')
|
|
.append(long_ / this.tickDuration)
|
|
.append('\n')
|
|
);
|
|
if (list.size() >= 3) {
|
|
for (int i = 1; i < list.size(); i++) {
|
|
ResultField resultField = (ResultField)list.get(i);
|
|
indentLine(builder, depth)
|
|
.append(resultField.name)
|
|
.append('(')
|
|
.append(resultField.count)
|
|
.append('/')
|
|
.append(String.format(Locale.ROOT, "%.0f", (float)resultField.count / this.tickDuration))
|
|
.append(')')
|
|
.append(" - ")
|
|
.append(String.format(Locale.ROOT, "%.2f", resultField.percentage))
|
|
.append("%/")
|
|
.append(String.format(Locale.ROOT, "%.2f", resultField.globalPercentage))
|
|
.append("%\n");
|
|
if (!"unspecified".equals(resultField.name)) {
|
|
try {
|
|
this.appendProfilerResults(depth + 1, sectionPath + "\u001e" + resultField.name, builder);
|
|
} catch (Exception var9) {
|
|
builder.append("[[ EXCEPTION ").append(var9).append(" ]]");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void appendCounterResults(int indents, String name, FilledProfileResults.CounterCollector collector, int tickSpan, StringBuilder builder) {
|
|
indentLine(builder, indents)
|
|
.append(name)
|
|
.append(" total:")
|
|
.append(collector.selfValue)
|
|
.append('/')
|
|
.append(collector.totalValue)
|
|
.append(" average: ")
|
|
.append(collector.selfValue / tickSpan)
|
|
.append('/')
|
|
.append(collector.totalValue / tickSpan)
|
|
.append('\n');
|
|
collector.children
|
|
.entrySet()
|
|
.stream()
|
|
.sorted(COUNTER_ENTRY_COMPARATOR)
|
|
.forEach(entry -> this.appendCounterResults(indents + 1, (String)entry.getKey(), (FilledProfileResults.CounterCollector)entry.getValue(), tickSpan, builder));
|
|
}
|
|
|
|
private void appendCounters(Map<String, FilledProfileResults.CounterCollector> counters, StringBuilder builder, int tickSpan) {
|
|
counters.forEach((string, counterCollector) -> {
|
|
builder.append("-- Counter: ").append(string).append(" --\n");
|
|
this.appendCounterResults(0, "root", (FilledProfileResults.CounterCollector)counterCollector.children.get("root"), tickSpan, builder);
|
|
builder.append("\n\n");
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public int getTickDuration() {
|
|
return this.tickDuration;
|
|
}
|
|
|
|
static class CounterCollector {
|
|
long selfValue;
|
|
long totalValue;
|
|
final Map<String, FilledProfileResults.CounterCollector> children = Maps.<String, FilledProfileResults.CounterCollector>newHashMap();
|
|
|
|
public void addValue(Iterator<String> counters, long value) {
|
|
this.totalValue += value;
|
|
if (!counters.hasNext()) {
|
|
this.selfValue += value;
|
|
} else {
|
|
((FilledProfileResults.CounterCollector)this.children.computeIfAbsent((String)counters.next(), string -> new FilledProfileResults.CounterCollector()))
|
|
.addValue(counters, value);
|
|
}
|
|
}
|
|
}
|
|
}
|