Package nallar.tickthreading.minecraft.profiling

Source Code of nallar.tickthreading.minecraft.profiling.EntityTickProfiler$ChunkCoords

package nallar.tickthreading.minecraft.profiling;

import nallar.tickthreading.minecraft.TickManager;
import nallar.tickthreading.minecraft.TickThreading;
import nallar.tickthreading.minecraft.commands.ProfileCommand;
import nallar.tickthreading.util.CollectionsUtil;
import nallar.tickthreading.util.TableFormatter;
import nallar.tickthreading.util.stringfillers.StringFiller;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.*;

public class EntityTickProfiler {
  public static final EntityTickProfiler ENTITY_TICK_PROFILER = new EntityTickProfiler();
  public static ProfileCommand.ProfilingState profilingState = ProfileCommand.ProfilingState.NONE;
  private int ticks;
  private final AtomicLong totalTime = new AtomicLong();
  private volatile long startTime;

  private EntityTickProfiler() {
  }

  public static synchronized boolean startProfiling(ProfileCommand.ProfilingState profilingState_) {
    if (profilingState != ProfileCommand.ProfilingState.NONE) {
      return false;
    }
    profilingState = profilingState_;
    return true;
  }

  public static synchronized void endProfiling() {
    profilingState = ProfileCommand.ProfilingState.NONE;
  }

  public boolean startProfiling(final Runnable runnable, ProfileCommand.ProfilingState state, final int time, final Collection<World> worlds_) {
    if (time <= 0) {
      throw new IllegalArgumentException("time must be > 0");
    }
    final Collection<World> worlds = new ArrayList<World>(worlds_);
    synchronized (EntityTickProfiler.class) {
      if (!startProfiling(state)) {
        return false;
      }
      for (World world_ : worlds) {
        TickThreading.instance.getManager(world_).profilingEnabled = true;
      }
    }

    Runnable profilingRunnable = new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(1000 * time);
        } catch (InterruptedException ignored) {
        }

        synchronized (EntityTickProfiler.class) {
          endProfiling();
          runnable.run();
          clear();
          for (World world_ : worlds) {
            TickManager tickManager = TickThreading.instance.getManager(world_);
            if (tickManager != null) {
              tickManager.profilingEnabled = false;
            }
          }
        }
      }
    };
    Thread profilingThread = new Thread(profilingRunnable);
    profilingThread.setName("TickProfiler");
    profilingThread.start();
    startTime = System.currentTimeMillis();
    return true;
  }

  public void record(Object o, long time) {
    if (time < 0) {
      time = 0;
    }
    getSingleTime(o).addAndGet(time);
    getSingleInvocationCount(o).incrementAndGet();
    Class<?> clazz = o.getClass();
    getTime(clazz).addAndGet(time);
    getInvocationCount(clazz).incrementAndGet();
    totalTime.addAndGet(time);
  }

  public void clear() {
    invocationCount.clear();
    time.clear();
    totalTime.set(0);
    singleTime.clear();
    singleInvocationCount.clear();
    ticks = 0;
  }

  public void tick() {
    if (profilingState != ProfileCommand.ProfilingState.NONE) {
      ticks++;
    }
  }

  public void writeJSONData(File file) throws IOException {
    TableFormatter tf = new TableFormatter(StringFiller.FIXED_WIDTH);
    tf.recordTables();
    writeData(tf, 20);
    ObjectMapper objectMapper = new ObjectMapper();
    List<Object> tables = tf.getTables();
    long timeProfiled = System.currentTimeMillis() - startTime;
    float tps = ticks * 1000f / timeProfiled;
    tables.add(0, CollectionsUtil.map(
        "TPS", tps,
        "Load", TableFormatter.formatDoubleWithPrecision((MinecraftServer.getTickTime() * 100) / MinecraftServer.getTargetTickTime(), 2)
    ));
    objectMapper.writerWithDefaultPrettyPrinter().writeValue(file, tables);
  }

  public TableFormatter writeStringData(TableFormatter tf) {
    return writeStringData(tf, 5);
  }

  public TableFormatter writeStringData(TableFormatter tf, int elements) {
    long timeProfiled = System.currentTimeMillis() - startTime;
    float tps = ticks * 1000f / timeProfiled;
    tf.sb.append("TPS: ").append(tps).append('\n').append(tf.tableSeparator);
    return writeData(tf, elements);
  }

  public TableFormatter writeData(TableFormatter tf, int elements) {
    Map<Class<?>, Long> time = new HashMap<Class<?>, Long>();
    for (Map.Entry<Class<?>, AtomicLong> entry : this.time.entrySet()) {
      time.put(entry.getKey(), entry.getValue().get());
    }
    Map<Object, Long> singleTime = new HashMap<Object, Long>();
    for (Map.Entry<Object, AtomicLong> entry : this.singleTime.entrySet()) {
      singleTime.put(entry.getKey(), entry.getValue().get());
    }
    double totalTime = this.totalTime.get();
    tf
        .heading("Single Entity")
        .heading("Time/Tick")
        .heading("%");
    final List<Object> sortedSingleKeysByTime = CollectionsUtil.sortedKeys(singleTime, elements);
    for (Object o : sortedSingleKeysByTime) {
      tf
          .row(niceName(o))
          .row(singleTime.get(o) / (1000000d * singleInvocationCount.get(o).get()))
          .row((singleTime.get(o) / totalTime) * 100);
    }
    tf.finishTable();
    tf.sb.append('\n');
    final Map<ChunkCoords, ComparableLongHolder> chunkTimeMap = new HashMap<ChunkCoords, ComparableLongHolder>() {
      @Override
      public ComparableLongHolder get(Object key_) {
        ChunkCoords key = (ChunkCoords) key_;
        ComparableLongHolder value = super.get(key);
        if (value == null) {
          value = new ComparableLongHolder();
          put(key, value);
        }
        return value;
      }
    };
    for (final Map.Entry<Object, Long> singleTimeEntry : singleTime.entrySet()) {
      int x;
      int z;
      int dimension;
      Object o = singleTimeEntry.getKey();
      if (o instanceof Entity) {
        x = ((Entity) o).chunkCoordX;
        z = ((Entity) o).chunkCoordZ;
        dimension = getDimension((Entity) o);
      } else if (o instanceof TileEntity) {
        x = ((TileEntity) o).xCoord >> 4;
        z = ((TileEntity) o).zCoord >> 4;
        dimension = getDimension((TileEntity) o);
      } else {
        throw new RuntimeException("Wrong type: " + o.getClass());
      }
      if (x != Integer.MIN_VALUE) {
        chunkTimeMap.get(new ChunkCoords(x, z, dimension)).value += singleTimeEntry.getValue();
      }
    }
    tf
        .heading("Chunk")
        .heading("Time/Tick")
        .heading("%");
    for (ChunkCoords chunkCoords : CollectionsUtil.sortedKeys(chunkTimeMap, elements)) {
      long chunkTime = chunkTimeMap.get(chunkCoords).value;
      tf
          .row(chunkCoords.dimension + ": " + chunkCoords.chunkXPos + ", " + chunkCoords.chunkZPos)
          .row(chunkTime / (1000000d * ticks))
          .row((chunkTime / totalTime) * 100);
    }
    tf.finishTable();
    tf.sb.append('\n');
    tf
        .heading("All Entities of Type")
        .heading("Time/Tick")
        .heading("%");
    for (Class c : CollectionsUtil.sortedKeys(time, elements)) {
      tf
          .row(niceName(c))
          .row(time.get(c) / (1000000d * ticks))
          .row((time.get(c) / totalTime) * 100);
    }
    tf.finishTable();
    tf.sb.append('\n');
    Map<Class<?>, Long> timePerTick = new HashMap<Class<?>, Long>();
    for (Map.Entry<Class<?>, AtomicLong> entry : this.time.entrySet()) {
      timePerTick.put(entry.getKey(), entry.getValue().get() / invocationCount.get(entry.getKey()).get());
    }
    tf
        .heading("Average Entity of Type")
        .heading("Time/tick")
        .heading("Calls");
    for (Class c : CollectionsUtil.sortedKeys(timePerTick, elements)) {
      tf
          .row(niceName(c))
          .row(timePerTick.get(c) / 1000000d)
          .row(invocationCount.get(c));
    }
    tf.finishTable();
    return tf;
  }

  private static int getDimension(TileEntity o) {
    WorldProvider worldProvider = o.worldObj.provider;
    return worldProvider == null ? -999 : worldProvider.dimensionId;
  }

  private static int getDimension(Entity o) {
    WorldProvider worldProvider = o.worldObj.provider;
    return worldProvider == null ? -999 : worldProvider.dimensionId;
  }

  private static Object niceName(Object o) {
    if (o instanceof TileEntity) {
      return niceName(o.getClass()) + ' ' + ((TileEntity) o).xCoord + ',' + ((TileEntity) o).yCoord + ',' + ((TileEntity) o).zCoord + ':' + getDimension((TileEntity) o);
    } else if (o instanceof Entity) {
      return niceName(o.getClass()) + ' ' + (int) ((Entity) o).posX + ',' + (int) ((Entity) o).posY + ',' + (int) ((Entity) o).posZ + ':' + getDimension((Entity) o);
    }
    return o.toString().substring(0, 48);
  }

  private static String niceName(Class<?> clazz) {
    String name = clazz.getName();
    if (name.contains(".")) {
      String cName = name.substring(name.lastIndexOf('.') + 1);
      String pName = name.substring(0, name.lastIndexOf('.'));
      if (pName.contains(".")) {
        pName = pName.substring(pName.lastIndexOf('.') + 1);
      }
      return (cName.length() < 15 ? pName + '.' : "") + cName;
    }
    return name;
  }

  private final Map<Class<?>, AtomicInteger> invocationCount = new NonBlockingHashMap<Class<?>, AtomicInteger>();
  private final Map<Class<?>, AtomicLong> time = new NonBlockingHashMap<Class<?>, AtomicLong>();
  private final Map<Object, AtomicLong> singleTime = new NonBlockingHashMap<Object, AtomicLong>();
  private final Map<Object, AtomicLong> singleInvocationCount = new NonBlockingHashMap<Object, AtomicLong>();

  private AtomicLong getSingleInvocationCount(Object o) {
    AtomicLong t = singleInvocationCount.get(o);
    if (t == null) {
      synchronized (o) {
        t = singleInvocationCount.get(o);
        if (t == null) {
          t = new AtomicLong();
          singleInvocationCount.put(o, t);
        }
      }
    }
    return t;
  }

  // We synchronize on the class name as it is always the same object
  // We do not synchronize on the class object as that would also
  // prevent any synchronized static methods on it from running
  private AtomicInteger getInvocationCount(Class<?> clazz) {
    AtomicInteger i = invocationCount.get(clazz);
    if (i == null) {
      synchronized (clazz.getName()) {
        i = invocationCount.get(clazz);
        if (i == null) {
          i = new AtomicInteger();
          invocationCount.put(clazz, i);
        }
      }
    }
    return i;
  }

  private AtomicLong getSingleTime(Object o) {
    AtomicLong t = singleTime.get(o);
    if (t == null) {
      synchronized (o) {
        t = singleTime.get(o);
        if (t == null) {
          t = new AtomicLong();
          singleTime.put(o, t);
        }
      }
    }
    return t;
  }

  private AtomicLong getTime(Class<?> clazz) {
    AtomicLong t = time.get(clazz);
    if (t == null) {
      synchronized (clazz.getName()) {
        t = time.get(clazz);
        if (t == null) {
          t = new AtomicLong();
          time.put(clazz, t);
        }
      }
    }
    return t;
  }

  private class ComparableLongHolder implements Comparable<ComparableLongHolder> {
    public long value;

    ComparableLongHolder() {
    }

    @Override
    public int compareTo(final ComparableLongHolder comparableLongHolder) {
      long otherValue = comparableLongHolder.value;
      return (value < otherValue) ? -1 : ((value == otherValue) ? 0 : 1);
    }
  }

  private static final class ChunkCoords {
    public final int chunkXPos;
    public final int chunkZPos;
    public final int dimension;

    ChunkCoords(final int chunkXPos, final int chunkZPos, final int dimension) {
      this.chunkXPos = chunkXPos;
      this.chunkZPos = chunkZPos;
      this.dimension = dimension;
    }

    @Override
    public boolean equals(Object o) {
      return o instanceof ChunkCoords && ((ChunkCoords) o).chunkXPos == this.chunkXPos && ((ChunkCoords) o).chunkZPos == this.chunkZPos && ((ChunkCoords) o).dimension == this.dimension;
    }

    @Override
    public int hashCode() {
      return (chunkXPos << 16) ^ (chunkZPos << 4) ^ dimension;
    }
  }
}
TOP

Related Classes of nallar.tickthreading.minecraft.profiling.EntityTickProfiler$ChunkCoords

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.