mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-06-18 14:50:56 +00:00
basic profiler implementation
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
package com.dfsek.terra.profiler;
|
||||||
|
|
||||||
|
public class Frame {
|
||||||
|
private final String id;
|
||||||
|
private final long start;
|
||||||
|
|
||||||
|
public Frame(String id) {
|
||||||
|
this.id = id;
|
||||||
|
this.start = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.dfsek.terra.profiler;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface Profiler {
|
||||||
|
ProfilerImpl INSTANCE = new ProfilerImpl();
|
||||||
|
|
||||||
|
static Profiler getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(String frame);
|
||||||
|
|
||||||
|
void pop(String frame);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
Map<String, Timings> getTimings();
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.dfsek.terra.profiler;
|
||||||
|
|
||||||
|
import com.dfsek.terra.profiler.exception.MalformedStackException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class ProfilerImpl implements Profiler {
|
||||||
|
private static final ThreadLocal<Stack<Frame>> THREAD_STACK = ThreadLocal.withInitial(Stack::new);
|
||||||
|
private static final ThreadLocal<Map<String, Timings>> TIMINGS = ThreadLocal.withInitial(HashMap::new);
|
||||||
|
private final List<Map<String, Timings>> accessibleThreadMaps = new ArrayList<>();
|
||||||
|
private volatile boolean running = false;
|
||||||
|
|
||||||
|
|
||||||
|
protected ProfilerImpl() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void push(String frame) {
|
||||||
|
if(running) THREAD_STACK.get().push(new Frame(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pop(String frame) {
|
||||||
|
if(running) {
|
||||||
|
long time = System.nanoTime();
|
||||||
|
Stack<Frame> stack = THREAD_STACK.get();
|
||||||
|
|
||||||
|
Map<String, Timings> timingsMap = TIMINGS.get();
|
||||||
|
|
||||||
|
if(timingsMap.size() == 0) {
|
||||||
|
synchronized(accessibleThreadMaps) {
|
||||||
|
accessibleThreadMaps.add(timingsMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timings bottom = timingsMap.computeIfAbsent(stack.get(0).getId(), id -> new Timings());
|
||||||
|
|
||||||
|
for(int i = 1; i < stack.size(); i++) {
|
||||||
|
bottom = bottom.getSubItem(stack.get(i).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame top = stack.pop();
|
||||||
|
if(!top.getId().equals(frame)) throw new MalformedStackException("Expected " + frame + ", found " + top);
|
||||||
|
|
||||||
|
bottom.addTime(time - top.getStart());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Timings> getTimings() {
|
||||||
|
Map<String, Timings> map = new HashMap<>();
|
||||||
|
accessibleThreadMaps.forEach(map::putAll);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.dfsek.terra.profiler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Timings {
|
||||||
|
private final Map<String, Timings> subItems = new HashMap<>();
|
||||||
|
|
||||||
|
private final List<Long> timings = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addTime(long time) {
|
||||||
|
timings.add(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Long> getTimings() {
|
||||||
|
return timings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double average() {
|
||||||
|
return (double) timings.stream().reduce(0L, Long::sum) / timings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long max() {
|
||||||
|
return timings.stream().mapToLong(Long::longValue).max().orElse(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long min() {
|
||||||
|
return timings.stream().mapToLong(Long::longValue).min().orElse(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timings getSubItem(String id) {
|
||||||
|
return subItems.computeIfAbsent(id, s -> new Timings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(int indent) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.append("Avg ").append(average() / 1000000).append("ms");
|
||||||
|
|
||||||
|
subItems.forEach((id, timings) -> {
|
||||||
|
builder.append('\n');
|
||||||
|
for(int i = 0; i <= indent; i++) {
|
||||||
|
builder.append('\t');
|
||||||
|
}
|
||||||
|
builder.append(id).append(": ").append(timings.toString(indent + 1));
|
||||||
|
});
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.dfsek.terra.profiler.exception;
|
||||||
|
|
||||||
|
public class MalformedStackException extends ProfilerException {
|
||||||
|
private static final long serialVersionUID = -3009539681021691054L;
|
||||||
|
|
||||||
|
public MalformedStackException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MalformedStackException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MalformedStackException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.dfsek.terra.profiler.exception;
|
||||||
|
|
||||||
|
public class ProfilerException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 8206737998791649002L;
|
||||||
|
|
||||||
|
public ProfilerException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfilerException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfilerException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package profiler;
|
||||||
|
|
||||||
|
import com.dfsek.terra.profiler.Profiler;
|
||||||
|
|
||||||
|
public class ProfilerTest {
|
||||||
|
//@Test
|
||||||
|
public static void main(String... a) throws InterruptedException {
|
||||||
|
Profiler.INSTANCE.start();
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
doThing();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
doThirdOtherThing();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
doOtherThing();
|
||||||
|
}
|
||||||
|
Profiler.INSTANCE.stop();
|
||||||
|
Profiler.INSTANCE.getTimings().forEach((id, timings) -> {
|
||||||
|
System.out.println(id + ": " + timings.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doThing() throws InterruptedException {
|
||||||
|
Profiler.INSTANCE.push("thing");
|
||||||
|
Thread.sleep(1);
|
||||||
|
doOtherThing();
|
||||||
|
Profiler.INSTANCE.pop("thing");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doOtherThing() throws InterruptedException {
|
||||||
|
Profiler.INSTANCE.push("thing2");
|
||||||
|
Thread.sleep(2);
|
||||||
|
doThirdOtherThing();
|
||||||
|
Profiler.INSTANCE.pop("thing2");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doThirdOtherThing() throws InterruptedException {
|
||||||
|
Profiler.INSTANCE.push("thing3");
|
||||||
|
Thread.sleep(2);
|
||||||
|
Profiler.INSTANCE.pop("thing3");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user