KetraTerm benchmarks
JMH measurements for parser, core, input, session, render-cache, completion, and Swing behavior. This is a development module, not a consumer library.
Build and run
Use JDK 25 and the repository Gradle wrapper; see the
development prerequisites. Run commands
from the repository root. On Windows, use gradlew.bat in place of ./gradlew.
Compile and package the benchmark harnesses without running measurements:
./gradlew :ketraterm-benchmarks:jmhJar
The executable JAR is written under ketraterm-benchmarks/build/libs. Replace
<jmh-jar> below with its path. List available benchmarks and command options:
java -jar <jmh-jar> -l
java -jar <jmh-jar> -h
Run a selected warmed parser workload and save the raw results:
java -jar <jmh-jar> ".*TerminalParserBenchmark.parseIsolated" -p workload=ascii -wi 3 -i 5 -w 1s -r 1s -f 3 -t 1 -prof gc -rf json -rff parser.json
Filters are regular expressions over benchmark names. -p selects an annotated
parameter; inspect the benchmark's source for available values. An invocation may
process a whole input buffer or batch, so an operation is not necessarily one
byte, cell, or key.
The full warmed suite is available through:
./gradlew :ketraterm-benchmarks:jmh
The Gradle configuration defines suite defaults and excludes the first-input benchmark. Direct JAR runs use annotations unless overridden.
Select a measurement boundary
| Question | Representative benchmarks |
|---|---|
| Parsing without core mutation | TerminalParserBenchmark, TerminalGraphemeBenchmark with pipeline=parser |
| Core writes, scrolling, erasure, and frame copying | TerminalCoreWriteBenchmark, TerminalCoreScrollBenchmark, TerminalCoreEraseBenchmark, TerminalBufferBenchmark |
| History disposal and reuse after extended attributes are cleared | TerminalStorageLifecycleBenchmark |
| Input encoding and session output through transport completion | TerminalInputBenchmark, TerminalPasteBenchmark, TerminalSessionOutputBenchmark |
| OSC 22 parsing and pointer metadata reads | TerminalPointerShapeBenchmark; parsing is per request, three per invocation. Excludes Swing/native cursor updates. |
| Published-frame access and viewport projection | TerminalRenderLeaseBenchmark, RetainedFrameViewportBenchmark |
| Warmed image placement publication and copying | TerminalGraphicsFrameBenchmark; placements=0,1,4096 share one pixel resource. Upload and capacity growth occur in setup; Java2D painting is excluded. |
| Warmed image raster reuse and software painting | TerminalGraphicsPaintBenchmark; opaque/translucent native and scaled images, prompt dividers, and dense shared-resource placements. The opaque-scroll/translucent-scroll workloads also copy alternating live/history viewports, completely hiding and revealing the existing image each invocation. Includes Java2D scaling/clipping allocation, excluding upload, publication, and Swing dispatch. |
| Allocation-permitted image replacement | TerminalGraphicsReplacementBenchmark; alternating raw RGBA uploads replace one image ID, publish/copy the new frame and construct a new Swing raster. Includes Base64 parsing, transfer staging, canonical pixels and cache-pressure eviction; excludes fixture encoding, PNG/zlib decoding and painting. side=64,512 selects the source dimensions. |
| Completion evaluation and directory scanning | TerminalCompletionBenchmark, TerminalDirectoryCompletionBenchmark, TerminalLearnedRankingBenchmark |
| Swing text, search, hyperlinks, and painting | TerminalTextRenderingBenchmark, TerminalSearchScanBenchmark, TerminalSearchRefreshBenchmark, TerminalHyperlinkDiscoveryBenchmark, SwingPaintBenchmark |
Read the selected fixture before interpreting a result. Isolated parsing uses a no-op command sink; frame publication is separate from painting; directory scans use a temporary directory on the filesystem selected by the JVM. These workloads measure different boundaries and cannot substitute for an end-to-end terminal measurement.
Reverse-wrap cursor motion
TerminalReverseWrapBenchmark
measures direct core and parser/host CUB with Int.MAX_VALUE on a fixed 80×24
soft-wrapped screen. mode=0 measures ordinary clamping, 45 checks wrapped
rows, and 1045 cycles using cell-offset arithmetic. Each operation includes
cursor positioning and CUB; construction, seeding, and input encoding occur
outside measurement. Trial teardown verifies the final cursor and history.
java -jar <jmh-jar> ".*TerminalReverseWrapBenchmark.*" -p mode=0,45,1045 -wi 3 -i 3 -w 1s -r 1s -f 2 -t 1 -prof gc -rf json -rff reverse-wrap.json
Application mouse behavior
TerminalMouseBehaviorBenchmark measures one packed routing-state read or four parser/host preference changes. The terminal, parser, and bytes are constructed before measurement. Trial teardown verifies the resulting flags and untouched cursor/history. The GC profile covers these primitive state/protocol paths, rather than Swing event construction or an entire painted frame.
java -jar <jmh-jar> ".*TerminalMouseBehaviorBenchmark.*" -wi 3 -i 3 -w 1s -r 1s -f 2 -t 1 -prof gc -rf json -rff mouse-behavior.json
First parser input
TerminalParserFirstInputBenchmark measures one parser input in each fresh JVM fork. Parser construction and payload encoding happen before timing; lazy initialization triggered by the first input is included. Its setup requires single-shot mode, one thread, no warmup, one measurement iteration, and a batch size of one.
java -jar <jmh-jar> ".*TerminalParserFirstInputBenchmark.*" -wi 0 -i 1 -f 15 -t 1 -bm ss -tu us -prof gc -rf json -rff first-input.json
GC-profiler results include harness activity and do not isolate Unicode table initialization allocation.
Storage lifecycle
TerminalStorageLifecycleBenchmark
uses public core operations on a 120×24 terminal. discardHistory measures ED 3
while preserving visible content and the cursor; clearAll also clears the
viewport and homes the cursor. Both are single-shot measurements with 10,000
history rows. content=ascii fills 119 cells; cluster replaces the first cell
with a two-codepoint combining cluster. clusterEnd fills the first 119 cells
with ASCII and puts a cluster in the final column; denseClusters fills all
120 columns with clusters. The latter two cases exercise disposal when cluster
handles extend to the row's end and when every cell owns a handle.
Each iteration repopulates the same buffer with preallocated payloads outside
timing. Iteration teardown checks history disposal, visible cells, clusters,
and cursor semantics.
java -jar <jmh-jar> ".*TerminalStorageLifecycleBenchmark.(discardHistory|clearAll)" -bm ss -wi 15 -i 15 -f 2 -t 1 -prof gc -rf json -rff storage-history.json
Keep single-shot mode and a batch size of one for those two methods. GC-profiler totals include history repopulation and semantic teardown; they do not isolate allocation in history disposal.
writeShiftErase writes an ASCII row, inserts/deletes a cell at its midpoint,
and erases its trailing half for all 24 rows. clearScreen repeatedly clears an
already blank viewport. extendedHistory=never uses rows that have never held
extended attributes; cleared first writes every cell with a hyperlink and
clears the viewport with a zero hyperlink pen. These warmed methods retain the
same rows and have no invocation or iteration setup. One operation covers one
viewport; trial teardown verifies default attributes and the resulting cells.
java -jar <jmh-jar> ".*TerminalStorageLifecycleBenchmark.(writeShiftErase|clearScreen)" -wi 3 -i 5 -w 1s -r 1s -f 2 -t 1 -prof gc -rf json -rff storage-steady.json
Fresh-terminal log ingestion
TerminalLogIngestionBenchmark
ingests a fixed 6,000-line ASCII log into a fresh fixture for each shot. Its
pipeline parameter selects direct core writes, isolated parsing, or parser-to-core
ingestion. Core and host variants verify retained cells, wrapping, history
eviction, and the final cursor during teardown.
java -jar <jmh-jar> ".*TerminalLogIngestionBenchmark.ingest" -p pipeline=host -p columns=80 -p maxHistory=13107 -p chunkBytes=4096 -bm ss -wi 60 -i 30 -f 3 -t 1 -tu ms -prof gc -rf json -rff log-ingestion.json
Keep single-shot mode and a batch size of one. This workload excludes transport, session publication workers, compatibility projection, and frontend rendering. GC-profiler totals include fixture setup and must not be reported as ingestion allocation alone.
Report results
Record the Git revision, JDK build, JVM arguments, OS, CPU, filesystem or graphics environment where relevant, complete command, parameter values, and raw JMH output. Compare the same workload, timing boundary, warmup, forks, and profiler settings. Check semantic tests before attributing a difference to an optimization.
Report score uncertainty and allocation units alongside the score. Small or rounded allocation results do not establish that a public API never allocates; setup, harness activity, background work, and warmed caches can affect the result. Use additional forks and repeated runs when a difference is close to measurement noise.
See Module.md for source sets and harness dependencies. The Swing rendering guide provides rendering workload context.
TerminalStorageLifecycleBenchmark.rewriteClusters rewrites all 120 × 24 cells
with prebuilt clusters of 2 or 32 codepoints. The buffer and payload are created
once per trial; allocation profiling measures sustained writes and payload reuse
after JMH warmup. Trial teardown verifies every payload and cursor placement.
resizeClusters -p length=32 alternates 2- and 32-codepoint payloads across that
viewport to force dead regions and compaction. Its payloads are also prebuilt;
the final retained prefix is verified outside the measured loop.