diff options
author | Ihor Radchenko <yantar92@gmail.com> | 2021-05-06 14:13:20 +0800 |
---|---|---|
committer | Bastien <bzg@gnu.org> | 2021-05-15 13:58:05 +0200 |
commit | ba273278a5da0b746ebbe39050b51613961364a8 (patch) | |
tree | 7a7fcc4a3915339857f0b3dd79c79225790645d0 | |
parent | 8b0b206b52769b1be0c8aad9a05807ade3513138 (diff) | |
download | org-mode-ba273278a5da0b746ebbe39050b51613961364a8.tar.gz |
Use cache in org-up-heading-safe
* lisp/org.el (org-up-heading-safe): Use buffer-local cache to store
positions of parent headings. The cache is invalidated when buffer
text is changed, according to `buffer-chars-modified-tick'.
(org--up-heading-cache): Buffer-local hash-table storing the cache.
(org--up-heading-cache-tick): The buffer modification state for
currently active `org--up-heading-cache'.
The optimisation is critical when running agenda or org-entry-get
queries using property/tag inheritance. In such scenarios
`org-up-heading-safe' can be called thousands of times. For example,
building todo agenda on large number of headings lead to the following
benchmark results:
Benchmark:
1. (elp-instrument-function #'org-up-heading-safe)
2. Run agenda
3. (elp-results) ;; function, # calls, total time, avg time
Without cache:
org-up-heading-safe 27555 8.4234025759 0.0003056941
With cache, first run:
org-up-heading-safe 23227 0.5300747539 2.282...e-05
With cache, second run on unchanged buffer:
org-up-heading-safe 23227 0.1447754880 6.233...e-06
The final speedup is 1-2 orders of magnitude (~15-56 times).
-rw-r--r-- | lisp/org.el | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/lisp/org.el b/lisp/org.el index 04abb73..a30dbed 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -20460,6 +20460,10 @@ This function considers both visible and invisible heading lines. With argument, move up ARG levels." (outline-up-heading arg t)) +(defvar-local org--up-heading-cache nil + "Buffer-local `org-up-heading-safe' cache.") +(defvar-local org--up-heading-cache-tick nil + "Buffer `buffer-chars-modified-tick' in `org--up-heading-cache'.") (defun org-up-heading-safe () "Move to the heading line of which the present line is a subheading. This version will not throw an error. It will return the level of the @@ -20469,10 +20473,28 @@ Also, this function will be a lot faster than `outline-up-heading', because it relies on stars being the outline starters. This can really make a significant difference in outlines with very many siblings." (when (ignore-errors (org-back-to-heading t)) - (let ((level-up (1- (funcall outline-level)))) - (and (> level-up 0) - (re-search-backward (format "^\\*\\{1,%d\\} " level-up) nil t) - (funcall outline-level))))) + (let (level-cache) + (unless org--up-heading-cache + (setq org--up-heading-cache (make-hash-table))) + (if (and (eq (buffer-chars-modified-tick) org--up-heading-cache-tick) + (setq level-cache (gethash (point) org--up-heading-cache))) + (when (<= (point-min) (car level-cache) (point-max)) + ;; Parent is inside accessible part of the buffer. + (progn (goto-char (car level-cache)) + (cdr level-cache))) + ;; Buffer modified. Invalidate cache. + (unless (eq (buffer-chars-modified-tick) org--up-heading-cache-tick) + (setq-local org--up-heading-cache-tick + (buffer-chars-modified-tick)) + (clrhash org--up-heading-cache)) + (let* ((level-up (1- (funcall outline-level))) + (pos (point)) + (result (and (> level-up 0) + (re-search-backward + (format "^\\*\\{1,%d\\} " level-up) nil t) + (funcall outline-level)))) + (when result (puthash pos (cons (point) result) org--up-heading-cache)) + result))))) (defun org-up-heading-or-point-min () "Move to the heading line of which the present is a subheading, or point-min. |