Created
July 16, 2016 09:29
-
-
Save kabutz/a2a5ff6b9b57b60b1495999da200ede5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.management.ManagementFactory; | |
import java.lang.management.ThreadMXBean; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.Arrays; | |
import java.util.Set; | |
import java.util.Timer; | |
import java.util.TimerTask; | |
import java.util.concurrent.ThreadPoolExecutor; | |
import java.util.concurrent.TimeUnit; | |
/** | |
* Hackity hack class for monitoring what the QuantumRenderer thread is doing | |
* in a JavaFX class. Will most probably not work in future versions of JavaFX. | |
* It is used to check saturation of the QuantumRenderer thread. | |
* | |
* Example uses: | |
* | |
* java -classpath PATH_TO_PERFORMANCE_TESTING_FX_LAUNCHER:Modena.jar PerformanceTestingFXLauncher modena.Modena | |
* (You can see that the first page with all the graphical controls does almost 100% saturation of the QuantumRenderer thread) | |
* | |
* java -classpath PATH_TO_PERFORMANCE_TESTING_FX_LAUNCHER:MandelbrotSet.jar PerformanceTestingFXLauncher demo.parallel.Main | |
* | |
* @author Dr Heinz M. Kabutz | |
*/ | |
public class PerformanceTestingFXLauncher { | |
public static void main(String... args) throws ReflectiveOperationException { | |
setupWatcher(); | |
callNextMainMethod(args); | |
} | |
private static void setupWatcher() throws ReflectiveOperationException { | |
ThreadPoolExecutor quantumRendererPool = grabPool(); | |
Thread quantumRendererThread = grabThread(quantumRendererPool); | |
startWatching(quantumRendererPool, quantumRendererThread); | |
} | |
private static void callNextMainMethod(String[] args) throws ReflectiveOperationException { | |
Class<?> runnerClass = Class.forName(args[0]); | |
Method main = runnerClass.getMethod("main", String[].class); | |
main.invoke(null, (Object) Arrays.copyOfRange(args, 1, args.length)); | |
} | |
private static void startWatching(ThreadPoolExecutor quantumRendererPool, | |
Thread quantumRendererThread) { | |
Timer timer = new Timer("QuantumRendererWatcher", true); | |
timer.schedule(new TimerTask() { | |
@Override | |
public void run() { | |
showStats(quantumRendererPool, quantumRendererThread); | |
} | |
}, 5000, 5000); | |
} | |
private static ThreadPoolExecutor grabPool() throws ReflectiveOperationException { | |
Class<? extends ThreadPoolExecutor> qrc = | |
Class.forName("com.sun.javafx.tk.quantum.QuantumRenderer", true, | |
Thread.currentThread().getContextClassLoader()) | |
.asSubclass(ThreadPoolExecutor.class); | |
Method getInstanceMethod = qrc.getMethod("getInstance"); | |
getInstanceMethod.setAccessible(true); | |
return (ThreadPoolExecutor) getInstanceMethod.invoke(qrc); | |
} | |
private static Thread grabThread(ThreadPoolExecutor quantumRendererPool) | |
throws ReflectiveOperationException { | |
Field workersField = ThreadPoolExecutor.class.getDeclaredField("workers"); | |
workersField.setAccessible(true); | |
Set<?> workers = (Set<?>) workersField.get(quantumRendererPool); | |
Object worker = workers.iterator().next(); | |
Field threadField = worker.getClass().getDeclaredField("thread"); | |
threadField.setAccessible(true); | |
return (Thread) threadField.get(worker); | |
} | |
private static void showStats(ThreadPoolExecutor tpe, Thread worker) { | |
long cpuTime = cpuDiff.diffSinceLast(tmxbean.getThreadCpuTime(worker.getId())); | |
long userTime = userDiff.diffSinceLast(tmxbean.getThreadUserTime(worker.getId())); | |
long elapsedTime = elapsedDiff.diffSinceLast(System.currentTimeMillis()); | |
long tasksCompleted = tasksDiff.diffSinceLast(tpe.getCompletedTaskCount()); | |
System.out.println( | |
"QuantumRenderer: " + | |
"tasks = " + tasksCompleted + ", " + | |
"cpu = " + TimeUnit.NANOSECONDS.toMillis(cpuTime) + "ms, " + | |
"user = " + TimeUnit.NANOSECONDS.toMillis(userTime) + "ms, " + | |
"elapsed = " + elapsedTime + "ms, " + | |
"cpu saturation = " + (cpuTime / elapsedTime / 10000) + "%, " + | |
"cpu/task = " + (int) ((double) TimeUnit.NANOSECONDS.toMicros(cpuTime) / tasksCompleted) + "µs" | |
); | |
} | |
private static final ThreadMXBean tmxbean = ManagementFactory.getThreadMXBean(); | |
private static final LongDiff cpuDiff = new LongDiff(0); | |
private static final LongDiff userDiff = new LongDiff(0); | |
private static final LongDiff elapsedDiff = new LongDiff(System.currentTimeMillis()); | |
private static final LongDiff tasksDiff = new LongDiff(0); | |
private static class LongDiff { | |
private long lastValue; | |
public LongDiff(long lastValue) { | |
this.lastValue = lastValue; | |
} | |
public long diffSinceLast(long value) { | |
if (lastValue == 0) { | |
lastValue = value; | |
return value; | |
} else { | |
long temp = value; | |
value = value - lastValue; | |
lastValue = temp; | |
return value; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample output for Modena demo:
QuantumRenderer: tasks = 63, cpu = 4795ms, user = 4617ms, elapsed = 5000ms, cpu saturation = 95%, cpu/task = 76119µs
QuantumRenderer: tasks = 68, cpu = 4887ms, user = 4704ms, elapsed = 5005ms, cpu saturation = 97%, cpu/task = 71872µs
QuantumRenderer: tasks = 67, cpu = 4823ms, user = 4639ms, elapsed = 5001ms, cpu saturation = 96%, cpu/task = 71988µs
QuantumRenderer: tasks = 72, cpu = 4859ms, user = 4672ms, elapsed = 5001ms, cpu saturation = 97%, cpu/task = 67494µs
QuantumRenderer: tasks = 73, cpu = 4820ms, user = 4638ms, elapsed = 5005ms, cpu saturation = 96%, cpu/task = 66039µs
QuantumRenderer: tasks = 72, cpu = 4846ms, user = 4665ms, elapsed = 5000ms, cpu saturation = 96%, cpu/task = 67319µs
QuantumRenderer: tasks = 71, cpu = 4789ms, user = 4598ms, elapsed = 5000ms, cpu saturation = 95%, cpu/task = 67456µs
QuantumRenderer: tasks = 268, cpu = 2840ms, user = 2753ms, elapsed = 5002ms, cpu saturation = 56%, cpu/task = 10597µs
QuantumRenderer: tasks = 300, cpu = 2790ms, user = 2708ms, elapsed = 5003ms, cpu saturation = 55%, cpu/task = 9300µs
QuantumRenderer: tasks = 300, cpu = 2786ms, user = 2704ms, elapsed = 5001ms, cpu saturation = 55%, cpu/task = 9286µs
QuantumRenderer: tasks = 237, cpu = 2289ms, user = 2218ms, elapsed = 5004ms, cpu saturation = 45%, cpu/task = 9661µs
QuantumRenderer: tasks = 6, cpu = 74ms, user = 71ms, elapsed = 5004ms, cpu saturation = 1%, cpu/task = 12467µs
QuantumRenderer: tasks = 0, cpu = 0ms, user = 0ms, elapsed = 5001ms, cpu saturation = 0%, cpu/task = 0µs
QuantumRenderer: tasks = 14, cpu = 189ms, user = 181ms, elapsed = 5001ms, cpu saturation = 3%, cpu/task = 13539µs
QuantumRenderer: tasks = 8, cpu = 73ms, user = 68ms, elapsed = 5001ms, cpu saturation = 1%, cpu/task = 9212µs
QuantumRenderer: tasks = 37, cpu = 1946ms, user = 1868ms, elapsed = 5000ms, cpu saturation = 38%, cpu/task = 52600µs