Elisp: Benchmark, Test Speed
Benchmark Library
a benchmark lib is bundled with emacs.
(require 'benchmark)
Benchmark Function
benchmark-run
-
(benchmark-run &optional REPETITIONS &rest FORMS)
Time execution of FORMS.
If REPETITIONS is supplied as a number, run FORMS that many times, accounting for the overhead of the resulting loop. Otherwise run FORMS once.
Return a list of the total elapsed time for execution, the number of garbage collections that ran, and the time taken by garbage collection. See also
benchmark-run-compiled
.
Example: Byte Compiled Code
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2023-08-07 ;; comparing speed of compiled code (require 'benchmark) (defun xtest (n) "Return the time, in seconds, to run N iterations of a loop." (while (> (setq n (1- n)) 0)) n ) (setq xi 10000000) (benchmark-run 1 (xtest xi)) ;; (0.7 0 0.0) ;; using benchmark-run-compiled does not make a difference. strange (benchmark-run-compiled 1 (xtest xi)) ;; (0.6 0 0.0) ;; actually compile the code make a difference (byte-compile 'xtest) (benchmark-run 1 (xtest xi)) ;; (0.1 0 0.0) ;; bytecompiled code is some 7 times faster in this example
Example: current-word vs get-thing-at-point
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2023-08-02 2023-08-07 ;; comparing speed of ways to get current word (require 'benchmark) (setq xi 100000) (benchmark-run xi (current-word)) ;; some ;; (0.19 7 0.15) (benchmark-run xi (xah-get-thing-at-point 'word)) ;; some ;; (0.55 14 0.33) ;; byte compiled version (benchmark-run xi (xah-get-thing-at-point 'word)) ;; some ;; (0.24 5 0.11) (benchmark-run xi (thing-at-point 'word)) ;; some ;; (1.1 28 0.6)
Example: diff ways of checking the char before cursor is any of whitespace
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2023-08-02 ;; comparing speed of diff ways of checking the char before cursor is any of whitespace (require 'benchmark) (setq xi 1000000) (benchmark-run xi (progn (or (eq (char-before) 32) (eq (char-before) 9) (eq (char-before) 10)))) ;; (0.21 0 0.0) (benchmark-run xi (if (eq (point-min) (point)) nil (prog2 (backward-char) (looking-at "[ \t\n]") (forward-char)))) ;; (0.43 0 0.0) (benchmark-run xi (string-match "[ \t\n]" (char-to-string (char-before)))) ;; (0.73 20 0.47) (benchmark-run xi (looking-back "[ \t\n]")) ;; (1.5 43 1.0) (benchmark-run xi (looking-back "[ \t\n]" (1- (point)))) ;; (1.6 42 1.0)
Example: Remove Prefx String
;; speed of replace a prefix string (setq xstr "00000000000000000000000000000000000000000000000ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt0000000000000000000000" xpre "00000000000000000000000000000000000000000000000" xcount 10000 ) ;; using builtin (benchmark-run xcount (require 'subr-x) (string-remove-prefix xpre xstr)) ;; (0.02 1 0.02) ;; manual string manipulation (benchmark-run xcount (let (xhead) (setq xhead (substring xstr 0 (length xpre))) (if (string-equal xpre xhead) (progn (substring xstr (length xhead))) xstr))) ;; (0.03 1 0.02) ;; use replace-regexp-in-string (benchmark-run xcount (let (xhead) (replace-regexp-in-string (concat "^" xpre) "" xstr t t))) ;; (0.07 2 0.04) ;; use a temp buffer (benchmark-run xcount (let (xhead) (with-temp-buffer (insert xstr) (goto-char (point-min)) (when (search-forward xpre nil t) (delete-region (point-min) (point))) (buffer-substring (point-min) (point-max))))) ;; (0.24 6 0.13)
Example: Test a Script
(benchmark-run 1 (xahsite-update-search)) ;; (15.8 34 0.71)
Example: insert per list item vs join list and insert once
;; 2024-01-24 ;; compare the speed of inserting huge list into a buffer ;; insert one item at a time ;; vs ;; mapconcat and insert just once ;; conclusion: insert just once is a magnitude faster. ;; the more items, the more slow is insert per item (setq xpaths nil) (dotimes (i 2000 xpaths) (push (number-to-string i) xpaths)) (benchmark-run 10 (let ((xbuf (get-buffer-create "xx888" t))) (with-current-buffer xbuf (erase-buffer) (mapc (lambda (x) (insert x "\n")) xpaths)) (display-buffer xbuf))) ;; (0.1 0 0.0) (benchmark-run 10 (let ((xbuf (get-buffer-create "xx888" t))) (with-current-buffer xbuf (erase-buffer) (insert (mapconcat 'identity xpaths "\n"))) (display-buffer xbuf))) ;; (0.001 0 0.0)
Example: Speed of Set Values in Vector to Variable
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2024-03-19 ;; comparing speed of ways to set vector of length 2 to 2 vars ;; the values is is supposed from a function that return [begin end] positions of html tag. ;; xBeginEndVec is a result of a function, eg get-tag-boundary (setq xBeginEndVec (vector 300 400)) ;; traditional way, use a temp var (defun xx1 () (let (xbeg xend) (let ((xtmp xBeginEndVec)) (setq xbeg (aref xtmp 0) xend (aref xtmp 1))))) ;; using lambda to hold data. eval once. (defun xx2 () (let (xbeg xend) ((lambda (x) (setq xbeg (aref x 0) xend (aref x 1))) xBeginEndVec))) ;; using pattern matching (defun xx3 () (let (xbeg xend) (seq-setq (xbeg xend) xBeginEndVec))) (require 'benchmark) (setq xi 100000) (benchmark-run xi (xx1)) ;; (0.14 5 0.09) (benchmark-run xi (xx2)) ;; (0.14 5 0.09) (benchmark-run xi (xx3)) ;; (0.19 6 0.12)
Example: nreverse vs reverse
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2024-03-22 ;; comparing speed of nreverse vs reverse ;; nreverse is destructive, reverse is not ;; so, nreverse should be faster? else why two? ;; conclusion. both are too fast to tell ;; turns out, both are written in c (require 'benchmark) (setq xlist (number-sequence 1 10000)) (benchmark-run 1 (setq x1 (nreverse xlist))) ;; (6.8e-05 0 0.0) (benchmark-run 1 (setq x2 (reverse xlist))) ;; (2e-06 0 0.0)
Example: sort vs seq-sort
;; -*- coding: utf-8; lexical-binding: t; -*- ;; 2024-03-22 ;; comparing speed of sort vs seq-sort ;; sort is destructive vs seq-sort is not ;; sort is written in c ;; conclusion, sort is 10 times faster, but only if have a list of ten thousands items to be able to tell (require 'benchmark) (setq xlist1 (mapcar (lambda (x) (random 999)) (number-sequence 1 10000))) (setq xlist2 (copy-sequence xlist1)) (equal xlist1 xlist2) ;; t (benchmark-run 1 (setq xresult1 (sort xlist1 '> )) ) ;; (0.0003 0 0.0) (benchmark-run 1 (setq xresult2 (seq-sort '> xlist2 ))) ;; (0.003 0 0.0) (equal xresult1 xresult2) ;; t