aboutsummaryrefslogtreecommitdiff
path: root/test/perf-perspective.el
blob: aa433521e789eeed00eae5c8dd7362d872d357bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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