diff options
| author | Constantine Vetoshev <vetoshev@gmail.com> | 2026-03-31 06:56:45 -0700 |
|---|---|---|
| committer | Constantine Vetoshev <vetoshev@gmail.com> | 2026-03-31 06:57:49 -0700 |
| commit | 80691af9480431cc3cbb70914d0ff0d70447a0e1 (patch) | |
| tree | 70d7b65b473ee102bae84a4a9941f5ef2c06a353 | |
| parent | 0c47d653daac3a38851c75b40d7dbcdc938c887c (diff) | |
Add a test to help track down performance changes.
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | test/perf-perspective.el | 128 |
2 files changed, 135 insertions, 1 deletions
@@ -1,14 +1,20 @@ EMACS ?= emacs ELFILES := perspective.el ELCFILES = $(ELFILES:.el=.elc) +TESTFILES := $(wildcard test/test-*.el) all: test .PHONY: test test: - $(EMACS) -nw -Q -batch -L . -l ert $(addprefix -l ,$(wildcard test/*.el)) \ + $(EMACS) -nw -Q -batch -L . -l ert $(addprefix -l ,$(TESTFILES)) \ --eval "(ert-run-tests-batch-and-exit)" +.PHONY: perf +perf: + $(EMACS) -nw -Q -batch -L . -l test/perf-perspective.el \ + --eval "(persp-perf-run)" + .PHONY: compile compile: $(ELCFILES) diff --git a/test/perf-perspective.el b/test/perf-perspective.el new file mode 100644 index 0000000..aa43352 --- /dev/null +++ b/test/perf-perspective.el @@ -0,0 +1,128 @@ +;;; perf-perspective.el --- Performance harness for Perspective -*- lexical-binding: t; -*- + +;; Licensed under the same terms as Emacs and under the MIT license. + +;; URL: http://github.com/nex3/perspective-el +;; Created: 2026-03-30 + +;;; Commentary: + +;; This file provides an opt-in batch harness for comparing Perspective buffer +;; killing behavior across revisions. It is intentionally not part of the +;; default ERT suite. +;; +;; Run from the repository root with: +;; +;; make perf +;; +;; Or directly: +;; +;; emacs -Q --batch -L . -l test/perf-perspective.el --eval "(persp-perf-run)" +;; +;; Customize the run with: +;; +;; (let ((persp-perf-num-perspectives 16) +;; (persp-perf-buffers-per-perspective 24) +;; (persp-perf-temp-buffers-per-perspective 8) +;; (persp-perf-iterations 15)) +;; (persp-perf-run)) + +;;; Code: + +(require 'benchmark) +(require 'cl-lib) +(require 'perspective) + +(defvar persp-perf-num-perspectives 12 + "How many non-main perspectives to create for the performance harness.") + +(defvar persp-perf-buffers-per-perspective 20 + "How many ordinary buffers to create in each perspective.") + +(defvar persp-perf-temp-buffers-per-perspective 6 + "How many temporary buffers to create in each perspective.") + +(defvar persp-perf-iterations 10 + "How many times to run each benchmark scenario.") + +(defun persp-perf--cleanup-buffers () + "Kill all live buffers except the startup scratch buffer." + (let ((scratch (get-buffer "*scratch*"))) + (dolist (buffer (buffer-list)) + (when (and (buffer-live-p buffer) + (not (eq buffer scratch))) + (kill-buffer buffer))))) + +(defmacro persp-perf--with-environment (&rest body) + "Run BODY in a fresh Perspective environment and clean up afterward." + (declare (indent 0)) + `(let ((persp-suppress-no-prefix-key-warning t) + (persp-feature-flag-prevent-killing-last-buffer-in-perspective t)) + (persp-perf--cleanup-buffers) + (persp-mode 1) + (unwind-protect + (progn ,@body) + (persp-mode -1) + (persp-perf--cleanup-buffers)))) + +(defun persp-perf--populate-perspectives () + "Populate multiple perspectives with ordinary and temporary buffers." + (cl-loop for idx from 1 to persp-perf-num-perspectives do + (let ((persp-name (format "P%d" idx))) + (persp-switch persp-name) + (cl-loop for buf-idx from 1 to persp-perf-buffers-per-perspective do + (switch-to-buffer (format "*%s-buf-%02d*" persp-name buf-idx))) + (cl-loop for tmp-idx from 1 to persp-perf-temp-buffers-per-perspective do + (switch-to-buffer (format " %s-temp-%02d" persp-name tmp-idx))))) + (persp-switch "main")) + +(defun persp-perf--scenario-unrelated-kill () + "Kill a buffer from the current perspective while other perspectives are unrelated." + (persp-perf--with-environment + (persp-perf--populate-perspectives) + (let ((target (switch-to-buffer "*perf-target*"))) + (switch-to-buffer "*perf-helper*") + (switch-to-buffer target) + (kill-buffer target)))) + +(defun persp-perf--scenario-shared-keeper () + "Kill a shared buffer that must remain alive in another perspective." + (persp-perf--with-environment + (let ((ido-ignore-buffers '("^\\*scratch\\*"))) + (persp-perf--populate-perspectives) + (let ((target (switch-to-buffer "*perf-target*"))) + (persp-switch "P1") + (switch-to-buffer target) + (persp-switch "main") + (switch-to-buffer "*perf-helper*") + (switch-to-buffer target) + (kill-buffer target))))) + +(defun persp-perf--benchmark (name function) + "Run benchmark NAME by calling FUNCTION `persp-perf-iterations' times." + (let ((result (benchmark-run persp-perf-iterations (funcall function)))) + (message "%s" name) + (message " iterations: %d" persp-perf-iterations) + (message " elapsed: %.6fs" (nth 0 result)) + (message " gc-count: %d" (nth 1 result)) + (message " gc-elapsed: %.6fs" (nth 2 result)) + (message ""))) + +(defun persp-perf-run () + "Run the Perspective performance harness in batch mode." + (interactive) + (message "Perspective performance harness") + (message " perspectives: %d" persp-perf-num-perspectives) + (message " regular buffers / perspective: %d" persp-perf-buffers-per-perspective) + (message " temp buffers / perspective: %d" persp-perf-temp-buffers-per-perspective) + (message "") + (persp-perf--benchmark + "Scenario 1: kill buffer with unrelated other perspectives" + #'persp-perf--scenario-unrelated-kill) + (persp-perf--benchmark + "Scenario 2: kill shared buffer that another perspective must keep" + #'persp-perf--scenario-shared-keeper)) + +(provide 'perf-perspective) + +;;; perf-perspective.el ends here |
