startup timint
This commit is contained in:
@@ -31,10 +31,17 @@ public final class Main extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) {
|
||||
// First mark: time from JVM start through JavaFX toolkit + GL pipeline init (start() is the
|
||||
// first app code the toolkit runs). Usually the dominant slice of cold startup.
|
||||
StartupTiming.mark("toolkit ready (start)");
|
||||
config = AppConfig.load();
|
||||
StartupTiming.mark("config loaded");
|
||||
|
||||
metrics = new TerminalMetrics(config.fontFamily(), config.fontSize());
|
||||
StartupTiming.mark("fonts loaded");
|
||||
compositor = new Compositor(config, metrics);
|
||||
// Includes the first Ghostty.open (native dlopen) and the first pty spawn.
|
||||
StartupTiming.mark("compositor ready");
|
||||
// When the last pane closes — whether via the close-pane key or because a pane's process
|
||||
// exited on its own — tear down and quit.
|
||||
compositor.setOnEmpty(() -> {
|
||||
@@ -60,6 +67,7 @@ public final class Main extends Application {
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
compositor.render();
|
||||
StartupTiming.firstFrame();
|
||||
}
|
||||
}.start();
|
||||
|
||||
@@ -72,6 +80,7 @@ public final class Main extends Application {
|
||||
// to honour, so place it on the screen under the mouse pointer instead.
|
||||
centreOnActiveScreen(stage, config.windowWidth(), config.windowHeight());
|
||||
stage.show();
|
||||
StartupTiming.mark("stage shown");
|
||||
// Ask the window manager to raise and focus the new window so the user can type right
|
||||
// away; the canvas requestFocus() below only routes events within the scene.
|
||||
stage.toFront();
|
||||
|
||||
48
src/main/java/com/gregor/jprototerm/StartupTiming.java
Normal file
48
src/main/java/com/gregor/jprototerm/StartupTiming.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.gregor.jprototerm;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
/**
|
||||
* Opt-in startup phase timing, enabled with {@code -Djprototerm.timing=true} (e.g. via
|
||||
* {@code JAVA_TOOL_OPTIONS}); otherwise every method is a cheap no-op and prints nothing.
|
||||
*
|
||||
* <p>Each {@link #mark(String)} prints one line to stderr with the time since the previous mark and
|
||||
* the total since JVM start, so a cold launch breaks down into its phases — toolkit/GL init vs
|
||||
* config load vs font loading vs first frame. The anchor is the JVM's own start time (the closest
|
||||
* proxy we have to "process start"), so the first mark includes JVM bootstrap and JavaFX toolkit
|
||||
* init, which is usually the dominant cost.
|
||||
*/
|
||||
final class StartupTiming {
|
||||
private static final boolean ENABLED = Boolean.getBoolean("jprototerm.timing");
|
||||
// Epoch millis; getStartTime() is the JVM's start, the earliest timestamp we can anchor to.
|
||||
private static final long JVM_START_MILLIS = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
private static long lastMillis = -1;
|
||||
private static boolean firstFrameSeen;
|
||||
|
||||
private StartupTiming() {
|
||||
}
|
||||
|
||||
/** Records a phase boundary, printing the delta since the previous mark and since JVM start. */
|
||||
static void mark(String phase) {
|
||||
if (!ENABLED) {
|
||||
return;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
long sinceStart = now - JVM_START_MILLIS;
|
||||
long sinceLast = lastMillis < 0 ? sinceStart : now - lastMillis;
|
||||
lastMillis = now;
|
||||
System.err.printf("[timing] %-22s +%5d ms (%5d ms since JVM start)%n", phase, sinceLast, sinceStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the first rendered frame exactly once, then becomes a no-op. Safe and cheap to call
|
||||
* from the render loop every frame (it only ever touches FX-thread state).
|
||||
*/
|
||||
static void firstFrame() {
|
||||
if (!ENABLED || firstFrameSeen) {
|
||||
return;
|
||||
}
|
||||
firstFrameSeen = true;
|
||||
mark("first frame");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user