Add canonical query semantics, managed metadata and transactions, overlay-aware lookup, reproducible benchmarks, and synchronized API documentation.
218 lines
8.2 KiB
EmacsLisp
218 lines
8.2 KiB
EmacsLisp
;;; tp-benchmark.el --- Batch benchmarks for tp -*- lexical-binding: t -*-
|
|
|
|
;; Copyright (C) 2024-2026 Geekinney
|
|
|
|
;; Author: Geekinney (kinneyzhang666@gmail.com)
|
|
|
|
;;; Commentary:
|
|
|
|
;; Run with:
|
|
;; emacs -Q --batch -L . -l tp-benchmark.el -f tp-benchmark-run
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl-lib)
|
|
(require 'tp)
|
|
|
|
(defconst tp-benchmark--fixed-seeds '(1 7 42 747555)
|
|
"Fixed deterministic benchmark seeds.")
|
|
|
|
(defconst tp-benchmark--generated-seed 8675309
|
|
"Printed generated seed. Fixed so the benchmark output is reproducible.")
|
|
|
|
(defvar tp-bench-fanout-color nil
|
|
"Reactive color variable used by `tp-benchmark-run'.")
|
|
|
|
(defun tp-benchmark--print (plist)
|
|
"Print one benchmark row from PLIST."
|
|
(princ
|
|
(concat
|
|
(mapconcat
|
|
(lambda (key)
|
|
(format "%s=%S" (substring (symbol-name key) 1)
|
|
(plist-get plist key)))
|
|
'(:scenario :status :fixture :seed :requested :actual :operations
|
|
:scanned :changed :refreshed :elapsed :gcs :note)
|
|
" ")
|
|
"\n")))
|
|
|
|
(defun tp-benchmark--random-string (size seed)
|
|
"Return deterministic random string of SIZE using SEED."
|
|
(let ((state seed)
|
|
(chars "abcdefghijklmnopqrstuvwxyz")
|
|
(result (make-string size ?a))
|
|
(pos 0))
|
|
(while (< pos size)
|
|
(setq state (mod (+ (* state 1103515245) 12345) 2147483648))
|
|
(aset result pos (aref chars (mod state (length chars))))
|
|
(setq pos (1+ pos)))
|
|
result))
|
|
|
|
(defun tp-benchmark--measure (scenario fixture seed requested actual body)
|
|
"Measure BODY after GC and print a benchmark row."
|
|
(garbage-collect)
|
|
(let* ((gc-start gcs-done)
|
|
(start (float-time))
|
|
(result (funcall body))
|
|
(elapsed (- (float-time) start)))
|
|
(tp-benchmark--print
|
|
(append
|
|
(list :scenario scenario :status 'ok :fixture fixture :seed seed
|
|
:requested requested :actual actual)
|
|
result
|
|
(list :elapsed elapsed :gcs (- gcs-done gc-start) :note nil)))))
|
|
|
|
(defun tp-benchmark--blocked (scenario fixture seed requested note)
|
|
"Print a blocked benchmark row."
|
|
(tp-benchmark--print
|
|
(list :scenario scenario :status 'blocked :fixture fixture :seed seed
|
|
:requested requested :actual 0 :operations 0 :scanned 0
|
|
:changed 0 :refreshed 0 :elapsed nil :gcs 0 :note note)))
|
|
|
|
(defun tp-benchmark--large-text (size seed)
|
|
"Benchmark large text property set/search for SIZE and SEED."
|
|
(let ((text (tp-benchmark--random-string size seed)))
|
|
(tp-set 0 size '(tp-bench t) text)
|
|
(unless (equal (tp-search text 'tp-bench t) (list (list 0 size t)))
|
|
(error "large-text correctness failed"))
|
|
(set-text-properties 0 size nil text)
|
|
(tp-benchmark--measure
|
|
'large-text 'string seed size size
|
|
(lambda ()
|
|
(tp-set 0 size '(tp-bench t) text)
|
|
(let ((matches (tp-search text 'tp-bench t)))
|
|
(unless (equal matches (list (list 0 size t)))
|
|
(error "timed large-text correctness failed"))
|
|
(list :operations 2 :scanned size :changed size
|
|
:refreshed 0 :note (length matches)))))))
|
|
|
|
(defun tp-benchmark--fragmented (runs seed)
|
|
"Benchmark RUNS fragmented property intervals using SEED."
|
|
(let ((text (make-string runs ?x)))
|
|
(cl-loop for i below runs
|
|
when (zerop (mod i 2))
|
|
do (put-text-property i (1+ i) 'tp-bench i text))
|
|
(unless (= (length (tp-search text 'tp-bench)) (/ (1+ runs) 2))
|
|
(error "fragmented correctness failed"))
|
|
(tp-benchmark--measure
|
|
'fragmented 'string seed runs runs
|
|
(lambda ()
|
|
(let ((matches (tp-search text 'tp-bench)))
|
|
(list :operations 1 :scanned runs :changed (length matches)
|
|
:refreshed 0 :note nil))))))
|
|
|
|
(defun tp-benchmark--define-stack-layers (depth)
|
|
"Define DEPTH benchmark stack layers."
|
|
(cl-loop for i below depth
|
|
do (eval `(define-tp ,(intern (format "tp-bench-stack-%d" i))
|
|
() '(face bold)))))
|
|
|
|
(defun tp-benchmark--stack-depth (depth seed)
|
|
"Benchmark stack operations at DEPTH using SEED."
|
|
(tp-layer-reset)
|
|
(tp-benchmark--define-stack-layers depth)
|
|
(with-temp-buffer
|
|
(insert (make-string 2000 ?s))
|
|
(cl-loop for i below depth
|
|
do (tp-push-layer 1 2001
|
|
(intern (format "tp-bench-stack-%d" i))))
|
|
(unless (= (tp-layer-count 1 2001) depth)
|
|
(error "stack-depth correctness failed"))
|
|
(set-text-properties 1 2001 nil)
|
|
(tp-benchmark--measure
|
|
'stack-depth 'buffer seed depth depth
|
|
(lambda ()
|
|
(cl-loop for i below depth
|
|
do (tp-push-layer 1 2001
|
|
(intern (format "tp-bench-stack-%d" i))))
|
|
(unless (= (tp-layer-count 1 2001) depth)
|
|
(error "timed stack-depth correctness failed"))
|
|
(let ((top (tp-layer-top 1 2001)))
|
|
(list :operations (1+ depth) :scanned 2000 :changed 2000
|
|
:refreshed 0 :note top))))))
|
|
|
|
(defun tp-benchmark--define-fanout-layer (seed)
|
|
"Define one reactive layer for SEED."
|
|
(set 'tp-bench-fanout-color "red")
|
|
(eval '(define-tp tp-bench-fanout ()
|
|
:props '(face (:foreground $tp-bench-fanout-color))
|
|
:data '((tp-bench-fanout-color . "red"))))
|
|
seed)
|
|
|
|
(defun tp-benchmark--reactive-fanout (requested seed)
|
|
"Benchmark reactive fanout REQUESTED using SEED."
|
|
(let ((actual (min requested 200)))
|
|
(tp-layer-reset)
|
|
(tp-reactive-reset)
|
|
(tp-benchmark--define-fanout-layer seed)
|
|
(let ((buffers nil))
|
|
(unwind-protect
|
|
(progn
|
|
(dotimes (i actual)
|
|
(let ((buf (generate-new-buffer
|
|
(format " *tp-bench-fanout-%d*" i))))
|
|
(push buf buffers)
|
|
(with-current-buffer buf
|
|
(insert "x")
|
|
(tp-set 1 2 'tp-bench-fanout))))
|
|
(setq tp-bench-fanout-color "blue")
|
|
(with-current-buffer (car buffers)
|
|
(unless (equal (plist-get (get-text-property 1 'face)
|
|
:foreground)
|
|
"blue")
|
|
(error "reactive-fanout correctness failed")))
|
|
(tp-benchmark--measure
|
|
'reactive-fanout 'buffers seed requested actual
|
|
(lambda ()
|
|
(setq tp-bench-fanout-color "green")
|
|
(list :operations 1 :scanned actual :changed actual
|
|
:refreshed actual
|
|
:note (format "requested=%d actual=%d"
|
|
requested actual)))))
|
|
(mapc (lambda (buf)
|
|
(when (buffer-live-p buf) (kill-buffer buf)))
|
|
buffers)))))
|
|
|
|
(defun tp-benchmark--theme-refresh (seed)
|
|
"Benchmark current theme managed refresh hook using SEED."
|
|
(if (not (fboundp 'tp--refresh-managed-after-theme-change))
|
|
(tp-benchmark--blocked
|
|
'theme-managed-refresh 'buffer seed 1
|
|
"tp--refresh-managed-after-theme-change unavailable")
|
|
(tp-layer-reset)
|
|
(with-temp-buffer
|
|
(insert (make-string 1000 ?t))
|
|
(define-tp tp-bench-theme () '(face (:foreground "red")))
|
|
(tp-push-layer 1 1001 'tp-bench-theme)
|
|
(tp--refresh-managed-after-theme-change 'benchmark)
|
|
(unless tp-theme-last-refreshed-ranges
|
|
(error "theme-refresh correctness failed"))
|
|
(tp-benchmark--measure
|
|
'theme-managed-refresh 'buffer seed 1 1
|
|
(lambda ()
|
|
(tp--refresh-managed-after-theme-change 'benchmark)
|
|
(list :operations 1 :scanned 1000 :changed 0
|
|
:refreshed (length tp-theme-last-refreshed-ranges)
|
|
:note tp-theme-last-refresh-mode))))))
|
|
|
|
(defun tp-benchmark-run ()
|
|
"Run tp benchmarks in batch mode."
|
|
(interactive)
|
|
(princ (format "tp-benchmark emacs=%S generated-seed=%d fixed-seeds=%S\n"
|
|
emacs-version tp-benchmark--generated-seed
|
|
tp-benchmark--fixed-seeds))
|
|
(dolist (seed (append tp-benchmark--fixed-seeds
|
|
(list tp-benchmark--generated-seed)))
|
|
(dolist (size '(100000 1000000))
|
|
(tp-benchmark--large-text size seed))
|
|
(dolist (runs '(1000 10000 50000))
|
|
(tp-benchmark--fragmented runs seed))
|
|
(dolist (depth '(1 5 20 50))
|
|
(tp-benchmark--stack-depth depth seed))
|
|
(dolist (fanout '(1 10 100 500))
|
|
(tp-benchmark--reactive-fanout fanout seed))
|
|
(tp-benchmark--theme-refresh seed)))
|
|
|
|
(provide 'tp-benchmark)
|
|
;;; tp-benchmark.el ends here
|