@@ -657,6 +657,128 @@ content — relevant for apps like htop, vim, claude-code."
657657 (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms ))))
658658 (delete-process proc))))))))
659659
660+ ; ; =========================================================================
661+ ; ; SECTION 3b: TUI partial-update — static screen + status-line update
662+ ; ; =========================================================================
663+
664+ (defun ghostel-bench--run-tui-partial-scenarios ()
665+ " Benchmark partial-update workload (status-line update over static screen).
666+ The `tui-frame' scenario rewrites every row per iteration, so it cannot
667+ distinguish backends that honor per-row dirty tracking from those that
668+ re-render unconditionally. Here the static screen is rendered once and
669+ only the bottom row is rewritten per iteration — the workload that
670+ status bars, prompt redraws, and most TUI updates actually produce."
671+ (message " \n --- TUI Partial Update (bottom-row update over static screen) ---" )
672+ (message " %- 50s %5 s %8 s %1 0s %8 s " " SCENARIO" " ITERS" " TOTAL(s)" " ITER(ms)" " fps" )
673+ (message " %s " (make-string 90 ?- ))
674+ (let ((partial-iters (* ghostel-bench-iterations 1000 )))
675+ (dolist (size ghostel-bench-terminal-sizes)
676+ (let* ((rows (car size))
677+ (cols (cdr size))
678+ (label (format " %d x%d " rows cols))
679+ (static-frame (ghostel-bench--gen-tui-frame rows cols))
680+ (status-template (format " \e [%d ;1H\e [1;33;41m%% -%d s\e [0m" rows cols)))
681+ ; ; ghostel incremental
682+ (with-temp-buffer
683+ (let* ((static (ghostel-bench--encode-for-backend static-frame 'ghostel ))
684+ (term (ghostel-bench--make-ghostel rows cols))
685+ (ghostel-enable-url-detection nil )
686+ (ghostel-enable-file-detection nil )
687+ (inhibit-read-only t )
688+ (counter 0 ))
689+ (ghostel--write-input term static)
690+ (ghostel--redraw term t )
691+ (let ((result
692+ (ghostel-bench--measure
693+ (format " tui-partial/ghostel-incr/%s " label)
694+ cols partial-iters
695+ (lambda ()
696+ (cl-incf counter)
697+ (ghostel--write-input
698+ term (format status-template (format " status #%d " counter)))
699+ (ghostel--redraw term nil )))))
700+ (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms ))))))
701+ ; ; ghostel full
702+ (with-temp-buffer
703+ (let* ((static (ghostel-bench--encode-for-backend static-frame 'ghostel ))
704+ (term (ghostel-bench--make-ghostel rows cols))
705+ (ghostel-enable-url-detection nil )
706+ (ghostel-enable-file-detection nil )
707+ (inhibit-read-only t )
708+ (counter 0 ))
709+ (ghostel--write-input term static)
710+ (ghostel--redraw term t )
711+ (let ((result
712+ (ghostel-bench--measure
713+ (format " tui-partial/ghostel-full/%s " label)
714+ cols partial-iters
715+ (lambda ()
716+ (cl-incf counter)
717+ (ghostel--write-input
718+ term (format status-template (format " status #%d " counter)))
719+ (ghostel--redraw term t )))))
720+ (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms ))))))
721+ ; ; vterm
722+ (when ghostel-bench-include-vterm
723+ (with-temp-buffer
724+ (let* ((static (ghostel-bench--encode-for-backend static-frame 'vterm ))
725+ (term (ghostel-bench--make-vterm rows cols))
726+ (counter 0 ))
727+ (vterm--write-input term static)
728+ (vterm--redraw term )
729+ (let ((result
730+ (ghostel-bench--measure
731+ (format " tui-partial/vterm/%s " label)
732+ cols partial-iters
733+ (lambda ()
734+ (cl-incf counter)
735+ (vterm--write-input
736+ term (format status-template (format " status #%d " counter)))
737+ (vterm--redraw term )))))
738+ (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms )))))))
739+ ; ; eat
740+ (when ghostel-bench-include-eat
741+ (with-temp-buffer
742+ (let* ((static (ghostel-bench--encode-for-backend static-frame 'eat ))
743+ (term (ghostel-bench--make-eat rows cols))
744+ (inhibit-read-only t )
745+ (counter 0 ))
746+ (eat-term-process-output term static)
747+ (eat-term-redisplay term )
748+ (let ((result
749+ (ghostel-bench--measure
750+ (format " tui-partial/eat/%s " label)
751+ cols partial-iters
752+ (lambda ()
753+ (cl-incf counter)
754+ (eat-term-process-output
755+ term (ghostel-bench--encode-for-backend
756+ (format status-template (format " status #%d " counter))
757+ 'eat ))
758+ (eat-term-redisplay term )))))
759+ (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms ))))
760+ (eat-term-delete term ))))
761+ ; ; term
762+ (when ghostel-bench-include-term
763+ (with-temp-buffer
764+ (let* ((static (ghostel-bench--encode-for-backend static-frame 'term ))
765+ (proc (ghostel-bench--make-term rows cols))
766+ (inhibit-read-only t )
767+ (counter 0 ))
768+ (term-emulate-terminal proc static)
769+ (let ((result
770+ (ghostel-bench--measure
771+ (format " tui-partial/term/%s " label)
772+ cols partial-iters
773+ (lambda ()
774+ (cl-incf counter)
775+ (term-emulate-terminal
776+ proc (ghostel-bench--encode-for-backend
777+ (format status-template (format " status #%d " counter))
778+ 'term ))))))
779+ (message " ^ %. 0f fps " (/ 1000.0 (plist-get result :per-iter-ms ))))
780+ (delete-process proc))))))))
781+
660782; ; =========================================================================
661783; ; SECTION 4: Engine micro-benchmarks (bulk parse/render, single call)
662784; ; =========================================================================
@@ -855,6 +977,7 @@ real-world performance (see PTY and streaming benchmarks for that)."
855977 (ghostel-bench--run-pty-scenarios)
856978 (ghostel-bench--run-stream-scenarios)
857979 (ghostel-bench--run-tui-scenarios)
980+ (ghostel-bench--run-tui-partial-scenarios)
858981 (ghostel-bench--run-engine-scenarios)
859982 (ghostel-bench--print-summary))
860983
0 commit comments