313 lines
12 KiB
Java
313 lines
12 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.<String, FilledProfileResults.CounterCollector>comparingByValue(
|
||
|
Comparator.comparingLong(p_18489_ -> p_18489_.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> p_18464_, long p_18465_, int p_18466_, long p_18467_, int p_18468_) {
|
||
|
this.entries = p_18464_;
|
||
|
this.startTimeNano = p_18465_;
|
||
|
this.startTimeTicks = p_18466_;
|
||
|
this.endTimeNano = p_18467_;
|
||
|
this.endTimeTicks = p_18468_;
|
||
|
this.tickDuration = p_18468_ - p_18466_;
|
||
|
}
|
||
|
|
||
|
private ProfilerPathEntry getEntry(String p_18526_) {
|
||
|
ProfilerPathEntry profilerpathentry = this.entries.get(p_18526_);
|
||
|
return profilerpathentry != null ? profilerpathentry : EMPTY;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public List<ResultField> getTimes(String p_18493_) {
|
||
|
String s = p_18493_;
|
||
|
ProfilerPathEntry profilerpathentry = this.getEntry("root");
|
||
|
long i = profilerpathentry.getDuration();
|
||
|
ProfilerPathEntry profilerpathentry1 = this.getEntry(p_18493_);
|
||
|
long j = profilerpathentry1.getDuration();
|
||
|
long k = profilerpathentry1.getCount();
|
||
|
List<ResultField> list = Lists.newArrayList();
|
||
|
if (!p_18493_.isEmpty()) {
|
||
|
p_18493_ = p_18493_ + "\u001e";
|
||
|
}
|
||
|
|
||
|
long l = 0L;
|
||
|
|
||
|
for (String s1 : this.entries.keySet()) {
|
||
|
if (isDirectChild(p_18493_, s1)) {
|
||
|
l += this.getEntry(s1).getDuration();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float f = (float)l;
|
||
|
if (l < j) {
|
||
|
l = j;
|
||
|
}
|
||
|
|
||
|
if (i < l) {
|
||
|
i = l;
|
||
|
}
|
||
|
|
||
|
for (String s2 : this.entries.keySet()) {
|
||
|
if (isDirectChild(p_18493_, s2)) {
|
||
|
ProfilerPathEntry profilerpathentry2 = this.getEntry(s2);
|
||
|
long i1 = profilerpathentry2.getDuration();
|
||
|
double d0 = i1 * 100.0 / l;
|
||
|
double d1 = i1 * 100.0 / i;
|
||
|
String s3 = s2.substring(p_18493_.length());
|
||
|
list.add(new ResultField(s3, d0, d1, profilerpathentry2.getCount()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((float)l > f) {
|
||
|
list.add(new ResultField("unspecified", ((float)l - f) * 100.0 / l, ((float)l - f) * 100.0 / i, k));
|
||
|
}
|
||
|
|
||
|
Collections.sort(list);
|
||
|
list.add(0, new ResultField(s, 100.0, l * 100.0 / i, k));
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
private static boolean isDirectChild(String p_18495_, String p_18496_) {
|
||
|
return p_18496_.length() > p_18495_.length() && p_18496_.startsWith(p_18495_) && p_18496_.indexOf(30, p_18495_.length() + 1) < 0;
|
||
|
}
|
||
|
|
||
|
private Map<String, FilledProfileResults.CounterCollector> getCounterValues() {
|
||
|
Map<String, FilledProfileResults.CounterCollector> map = Maps.newTreeMap();
|
||
|
this.entries
|
||
|
.forEach(
|
||
|
(p_18512_, p_18513_) -> {
|
||
|
Object2LongMap<String> object2longmap = p_18513_.getCounters();
|
||
|
if (!object2longmap.isEmpty()) {
|
||
|
List<String> list = SPLITTER.splitToList(p_18512_);
|
||
|
object2longmap.forEach(
|
||
|
(p_145944_, p_145945_) -> map.computeIfAbsent(p_145944_, p_145947_ -> new FilledProfileResults.CounterCollector())
|
||
|
.addValue(list.iterator(), p_145945_)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
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 p_145940_) {
|
||
|
Writer writer = null;
|
||
|
|
||
|
boolean flag;
|
||
|
try {
|
||
|
Files.createDirectories(p_145940_.getParent());
|
||
|
writer = Files.newBufferedWriter(p_145940_, StandardCharsets.UTF_8);
|
||
|
writer.write(this.getProfilerResults(this.getNanoDuration(), this.getTickDuration()));
|
||
|
return true;
|
||
|
} catch (Throwable throwable) {
|
||
|
LOGGER.error("Could not save profiler results to {}", p_145940_, throwable);
|
||
|
flag = false;
|
||
|
} finally {
|
||
|
IOUtils.closeQuietly(writer);
|
||
|
}
|
||
|
|
||
|
return flag;
|
||
|
}
|
||
|
|
||
|
protected String getProfilerResults(long p_18486_, int p_18487_) {
|
||
|
StringBuilder stringbuilder = new StringBuilder();
|
||
|
ReportType.PROFILE.appendHeader(stringbuilder, List.of());
|
||
|
stringbuilder.append("Version: ").append(SharedConstants.getCurrentVersion().getId()).append('\n');
|
||
|
stringbuilder.append("Time span: ").append(p_18486_ / 1000000L).append(" ms\n");
|
||
|
stringbuilder.append("Tick span: ").append(p_18487_).append(" ticks\n");
|
||
|
stringbuilder.append("// This is approximately ")
|
||
|
.append(String.format(Locale.ROOT, "%.2f", p_18487_ / ((float)p_18486_ / 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, p_18487_);
|
||
|
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 p_18498_, int p_18499_) {
|
||
|
p_18498_.append(String.format(Locale.ROOT, "[%02d] ", p_18499_));
|
||
|
|
||
|
for (int i = 0; i < p_18499_; i++) {
|
||
|
p_18498_.append("| ");
|
||
|
}
|
||
|
|
||
|
return p_18498_;
|
||
|
}
|
||
|
|
||
|
private void appendProfilerResults(int p_18482_, String p_18483_, StringBuilder p_18484_) {
|
||
|
List<ResultField> list = this.getTimes(p_18483_);
|
||
|
Object2LongMap<String> object2longmap = ObjectUtils.firstNonNull(this.entries.get(p_18483_), EMPTY).getCounters();
|
||
|
object2longmap.forEach(
|
||
|
(p_18508_, p_18509_) -> indentLine(p_18484_, p_18482_)
|
||
|
.append('#')
|
||
|
.append(p_18508_)
|
||
|
.append(' ')
|
||
|
.append(p_18509_)
|
||
|
.append('/')
|
||
|
.append(p_18509_ / this.tickDuration)
|
||
|
.append('\n')
|
||
|
);
|
||
|
if (list.size() >= 3) {
|
||
|
for (int i = 1; i < list.size(); i++) {
|
||
|
ResultField resultfield = list.get(i);
|
||
|
indentLine(p_18484_, p_18482_)
|
||
|
.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(p_18482_ + 1, p_18483_ + "\u001e" + resultfield.name, p_18484_);
|
||
|
} catch (Exception exception) {
|
||
|
p_18484_.append("[[ EXCEPTION ").append(exception).append(" ]]");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void appendCounterResults(int p_18476_, String p_18477_, FilledProfileResults.CounterCollector p_18478_, int p_18479_, StringBuilder p_18480_) {
|
||
|
indentLine(p_18480_, p_18476_)
|
||
|
.append(p_18477_)
|
||
|
.append(" total:")
|
||
|
.append(p_18478_.selfValue)
|
||
|
.append('/')
|
||
|
.append(p_18478_.totalValue)
|
||
|
.append(" average: ")
|
||
|
.append(p_18478_.selfValue / p_18479_)
|
||
|
.append('/')
|
||
|
.append(p_18478_.totalValue / p_18479_)
|
||
|
.append('\n');
|
||
|
p_18478_.children
|
||
|
.entrySet()
|
||
|
.stream()
|
||
|
.sorted(COUNTER_ENTRY_COMPARATOR)
|
||
|
.forEach(p_18474_ -> this.appendCounterResults(p_18476_ + 1, p_18474_.getKey(), p_18474_.getValue(), p_18479_, p_18480_));
|
||
|
}
|
||
|
|
||
|
private void appendCounters(Map<String, FilledProfileResults.CounterCollector> p_18515_, StringBuilder p_18516_, int p_18517_) {
|
||
|
p_18515_.forEach((p_18503_, p_18504_) -> {
|
||
|
p_18516_.append("-- Counter: ").append(p_18503_).append(" --\n");
|
||
|
this.appendCounterResults(0, "root", p_18504_.children.get("root"), p_18517_, p_18516_);
|
||
|
p_18516_.append("\n\n");
|
||
|
});
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getTickDuration() {
|
||
|
return this.tickDuration;
|
||
|
}
|
||
|
|
||
|
static class CounterCollector {
|
||
|
long selfValue;
|
||
|
long totalValue;
|
||
|
final Map<String, FilledProfileResults.CounterCollector> children = Maps.newHashMap();
|
||
|
|
||
|
public void addValue(Iterator<String> p_18548_, long p_18549_) {
|
||
|
this.totalValue += p_18549_;
|
||
|
if (!p_18548_.hasNext()) {
|
||
|
this.selfValue += p_18549_;
|
||
|
} else {
|
||
|
this.children.computeIfAbsent(p_18548_.next(), p_18546_ -> new FilledProfileResults.CounterCollector()).addValue(p_18548_, p_18549_);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|