Skip to content
  • 6 commits
  • 1 file changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 27, 2012
@jlr 6-15x Speed improvement by enhancing two key functions, see commit me…
…ssage for benchmarks.

Replaced parse-partial-sexp with syntax-ppss, a cached version of the same fn.
Used in both rainbow-delimiters-depth and rainbow-delimiters-char-ineligible-p.

char-ineligible-p is run on every single delim as it gets highlighted.
depth is checked every time jit-lock fn gets called (once per pass).

The mode is 6-15x faster at highlighting delims now.

This first step is a naive improvement, just replacing the call
to parse-partial-sexp with syntax-ppss; there is more to gain.
Specifically we get a list of delim locations from syntax-ppss which
will allow us to avoid using re-search-forward+delim-type check;
instead we can just (goto-char) for each member of the list given to
us by syntax-ppss.

Benchmarks:
syntax-ppss benchmarks:: 2012-04-27

* rainbow-delimiters-char-ineligible-p:

*** rainbow-delimiters-char-ineligible-p-old::
***** uses (save-excursion (beginning-of-defun) (parse-partial-sexp (point) loc)))
Run on an ordinary small defun, meaning beg-of-defun+parse-partial-sexp have
lower overhead:
(benchmark-run 10000 (rainbow-delimiters-char-ineligible-p-old (point)))
0.382
0.322
0.3241
0.3244
0.5235 + gc

Run on a LARGE color-theme defun, which causes lag for beg-of-defun +
parse-partial-sexp. Speed degrades badly with large fn size.
(benchmark-run 10000 (rainbow-delimiters-char-ineligible-p-old (point)))
13.86086
14.709195 + gc
15.463448
Massive performance degradation - no wonder some people reported slowdown.

*** rainbow-delimiters-char-ineligible-p:
***** uses (syntax-ppss loc), the cached version of parse-partial-sexp
Run on an ordinary small defun:
(benchmark-run 10000 (rainbow-delimiters-char-ineligible-p (point)))
0.0287
0.1183
-0.708 + gc
0.1132
0.1172
0.1172
0.125999
0.12016
On a small defun, it's about .4s slower for 10,000 runs than the old version.
However, on large defuns...

Run on the same LARGE defun as the char-ineligible-p-old benchmark above:
(benchmark-run 10000 (rainbow-delimiters-char-ineligible-p (point)))
1.69163 + gc
0.94135
1.06332
0.93187
0.93072
On the order of a 13x speed improvement over -old fn on large defuns.

* Improvements to jit-lock fn speed from char-ineligible-p change:
*** rainbow-delimiters-propertize-region using OLD char-ineligible-p-old:
Run propertize-regions on the rainbow-delimiters.el file:
(benchmark-run 10 (rainbow-delimiters-propertize-region 16800 19000))
0.07045
0.06604
0.06445
0.06915
0.06829

Run propertize-region with old char-ineligible-p on a massive color-theme defun:
 - from 23800, 26000 (same region size as 16800 to 19000)
(benchmark-run 10 (rainbow-delimiters-propertize-region 23800 26000))
3.4262
2.7935
4.0044
2.8100
3.3926
2.8331
3.6192

*** rainbow-delimiters-propertize-region using NEW char-ineligible-p:
Run propertize-region on the rainbow-delimiters.el file:
(benchmark-run 10 (rainbow-delimiters-propertize-region 16800 19000))

0.1075
0.1066
0.3578 + gc
0.1028
We see a slowdown from the new version in jit-lock. This is likely due to
the region from 16800-19000 only containing small fns not affected by the
lag introduced by larger fns.

Run propertize-region on a massive color-theme defun:
 - from 23800, 26000 (same region size as 16800 to 19000)
(benchmark-run 10 (rainbow-delimiters-propertize-region 23800 26000))
0.1583
0.1527
0.1539
0.1544
0.1557
Huge improvement, down from about 3 seconds to 0.15s (20x improvement).

* rainbow-delimiters-depth speed increase:
*** Old rainbow-delimiters-depth:
***** Uses beg-of-defun+parse-partial-sexp, returns depth
Run on smaller defun:
(benchmark-run 10000 (rainbow-delimiters-depth-old (point)))
0.0608 + gc
0.31211
0.25139
0.50449 + gc
0.24204
0.25056

Run on a LARGE color-theme defun:
11.9003
12.5897
15.2455 + gc
14.8487
14.1190

*** New rainbow-delimiters-depth:
***** uses syntax-ppss instead of beg-of-defun+parse-partial-sexp, returns depth
(benchmark-run 10000 (rainbow-delimiters-depth (point)))
0.15836
0.17015
0.41939 + gc
0.16874
0.13655
0.13726
0.14030
0.62721 + gc
0.13498
0.23817
Doubled speed from old fn but slower gcs when they happen.

Run on a LARGE color-theme defun:
(benchmark-run 10000 (rainbow-delimiters-depth (point)))
0.8147
0.8021
0.8139
0.6038
Huge improvement, down from 12-15s to 0.8s (~17.5x improvement)

* Total speed improvement from depth+char-ineligible-p optimizations:

Run rainbow-delimiters-propertize-region on a relatively smaller defun:
(benchmark-run 10 (rainbow-delimiters-propertize-region 16800 19000))
0.10449
0.41737 + gc
0.10436
0.10183
0.11046
0.10177
0.10482
0.10229
Down from 0.65 seconds to 0.1 seconds (~6x improvement).

Run on a LARGE color-theme defun:
(benchmark-run 10 (rainbow-delimiters-propertize-region 23800 26000))
0.13815
0.20409
0.18792
0.19268
0.18909
0.18806
0.26894
0.18979
Down from ~3s to 0.2s. (15x improvement).
9d0683d
@jlr Rename syntax-table functions to be clearer. 1d71bda
Commits on Apr 28, 2012
@jlr Double the speed of main jit-lock highlighting fn (see commit msg for…
… benchmarks); eliminate bottleneck in large files.

* Benchmarks:

These benchmarks are after replacing parse-partial-sexp with syntax-ppss
and using defsubst in place of defun in a few more critical paths.

*** Ordinary section of code:
(benchmark-run 10 (rainbow-delimiters-propertize-region 16800 19000))
0.0403
0.0355
0.0315
0.0419
0.0319
0.0376
Before this round of optimizations (syntax-ppss+defsubst) this benchmark
took ~0.068 seconds. (Improvement: ~2x)

*** Segment where a bottleneck was hit earlier:
(benchmark-run 10 (rainbow-delimiters-propertize-region 23800 26000))
0.0842
0.0821
0.0905
0.0843
0.0784
0.0822
Before this round of optimizations (syntax-ppss+defsubst) this benchmark
took ~3.2 seconds. (Improvement: ~40x)
164b55b
@jlr Color scheme for light backgrounds. 493b514
@jlr Release rainbow-delimiters version 1.3.4.
Changelog:
 1.3.4 (2012-04-27)
  - Further optimize highlighting speed. Benchmarks show 2x improvement.
  - Color scheme for light backgrounds.
  - Eliminate bottleneck seen in certain large files.
    A large file which revealed this bottleneck now highlights ~40x faster.
  - Correct broken/incorrect highlighting reported in certain language
d2c5b21
@jlr Merge branch 'master' of github.com:jlr/rainbow-delimiters 8a8ee4e
Showing with 52 additions and 46 deletions.
  1. +52 −46 rainbow-delimiters.el
View
98 rainbow-delimiters.el
@@ -1,14 +1,14 @@
;;; rainbow-delimiters.el --- Highlight nested parens, brackets, braces a different color at each depth.
-;; Copyright (C) 2010-2011 Jeremy L. Rayman.
+;; Copyright (C) 2010-2012 Jeremy L. Rayman.
;; Author: Jeremy L. Rayman <jeremy.rayman@gmail.com>
;; Maintainer: Jeremy L. Rayman <jeremy.rayman@gmail.com>
;; Created: 2010-09-02
-;; Version: 1.3.3
+;; Version: 1.3.4
;; Keywords: faces, convenience, lisp, matching, tools, rainbow, rainbow parentheses, rainbow parens
;; EmacsWiki: http://www.emacswiki.org/emacs/RainbowDelimiters
;; Github: http://github.com/jlr/rainbow-delimiters
-;; URL: http://www.emacswiki.org/emacs/download/rainbow-delimiters.el
+;; URL: http://github.com/jlr/rainbow-delimiters/raw/master/rainbow-delimiters.el
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -32,15 +32,14 @@
;; to spot matching delimiters, orient yourself in the code, and tell which
;; statements are at a given level.
;;
-;; Great care has been taken to make this mode FAST. You should see no
-;; discernible change in scrolling or editing speed while using it,
+;; Great care has been taken to make this mode FAST. You shouldn't see
+;; any discernible change in scrolling or editing speed while using it,
;; even in delimiter-rich languages like Clojure, Lisp, and Scheme.
;;
;; Default colors are subtle, with the philosophy that syntax highlighting
-;; shouldn't being visually intrusive. Color schemes are always a matter
-;; of taste. If you take the time to design a new color scheme,
-;; please share it (even a simple list of colors works) on the EmacsWiki
-;; page or via github.
+;; shouldn't be visually intrusive. Color schemes are always a matter of
+;; taste. If you take the time to design a new color scheme, please share
+;; (even a simple list of colors works) on the EmacsWiki page or via github.
;; EmacsWiki: http://www.emacswiki.org/emacs/RainbowDelimiters
;; Github: http://github.com/jlr/rainbow-delimiters
@@ -58,9 +57,12 @@
;; 4. Activate the mode in your init file.
;; You can choose to enable it only in certain modes, or Emacs-wide:
;;
-;; - To enable it only in specific modes, add lines like the following:
+;; - To enable it only in certain modes, add lines like the following:
;; (add-hook 'clojure-mode-hook 'rainbow-delimiters-mode)
;;
+;; - To enable it in all programming-related emacs modes (Emacs 24+):
+;; (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
+;;
;; - To activate the mode globally, add to your init file:
;; (global-rainbow-delimiters-mode)
;;
@@ -75,9 +77,8 @@
;; To customize various options, including the color scheme:
;; M-x customize-group rainbow-delimiters
;;
-;; color-theme.el users:
-;; If you use the color-theme package, you can specify custom colors
-;; by adding the appropriate faces to your theme.
+;; deftheme / color-theme.el users:
+;; You can specify custom colors by adding the appropriate faces to your theme.
;; - Faces take the form of:
;; 'rainbow-delimiters-depth-#-face' with # being the depth.
;; Depth begins at 1, the outermost color.
@@ -121,6 +122,12 @@
;; 1.3.3 (2011-11-25)
;; - Backwards compatibility with Emacs versions prior to 23.2.
;; Defines "with-silent-modifications" if undefined.
+;; 1.3.4 (2012-04-27)
+;; - Further optimize highlighting speed. Benchmarks show 2x improvement.
+;; - Color scheme for light backgrounds.
+;; - Eliminate bottleneck seen in certain large files.
+;; A large file which revealed this bottleneck now highlights ~40x faster.
+;; - Correct broken/incorrect highlighting reported in certain languages.
;;; TODO:
@@ -128,7 +135,7 @@
;; for users of C-like languages.
;; - Python style - increase depth with each new indentation.
;; - Add support for nested tags (XML, HTML)
-;; - Set up proper example color-theme.el themes for rainbow-delimiters mode.
+;; - Set up proper example defthemes for rainbow-delimiters faces.
;; - Intelligent support for other languages: Ruby, LaTeX tags, et al.
;;; Issues:
@@ -214,56 +221,56 @@ Nil disables brace highlighting."
;; Faces for highlighting delimiters by nested level:
(defface rainbow-delimiters-depth-1-face
- '((((background light)) (:foreground "grey55"))
+ '((((background light)) (:foreground "#707183"))
(((background dark)) (:foreground "grey55")))
"Nested delimiters face, depth 1 - outermost set."
:tag "Rainbow Delimiters Depth 1 Face -- OUTERMOST"
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-2-face
- '((((background light)) (:foreground "#93a8c6"))
+ '((((background light)) (:foreground "#7388d6"))
(((background dark)) (:foreground "#93a8c6")))
"Nested delimiters face, depth 2."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-3-face
- '((((background light)) (:foreground "#b0b1a3"))
+ '((((background light)) (:foreground "#909183"))
(((background dark)) (:foreground "#b0b1a3")))
"Nested delimiters face, depth 3."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-4-face
- '((((background light)) (:foreground "#97b098"))
+ '((((background light)) (:foreground "#709870"))
(((background dark)) (:foreground "#97b098")))
"Nested delimiters face, depth 4."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-5-face
- '((((background light)) (:foreground "#aebed8"))
+ '((((background light)) (:foreground "#907373"))
(((background dark)) (:foreground "#aebed8")))
"Nested delimiters face, depth 5."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-6-face
- '((((background light)) (:foreground "#b0b0b3"))
+ '((((background light)) (:foreground "#6276ba"))
(((background dark)) (:foreground "#b0b0b3")))
"Nested delimiters face, depth 6."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-7-face
- '((((background light)) (:foreground "#90a890"))
+ '((((background light)) (:foreground "#858580"))
(((background dark)) (:foreground "#90a890")))
"Nested delimiters face, depth 7."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-8-face
- '((((background light)) (:foreground "#a2b6da"))
+ '((((background light)) (:foreground "#80a880"))
(((background dark)) (:foreground "#a2b6da")))
"Nested delimiters face, depth 8."
:group 'rainbow-delimiters-faces)
(defface rainbow-delimiters-depth-9-face
- '((((background light)) (:foreground "#9cb6ad"))
+ '((((background light)) (:foreground "#887070"))
(((background dark)) (:foreground "#9cb6ad")))
"Nested delimiters face, depth 9."
:group 'rainbow-delimiters-faces)
@@ -302,16 +309,16 @@ For example: 'rainbow-delimiters-depth-1-face'."
;;; Nesting level
-(defvar rainbow-delimiters-all-delimiters-syntax-table nil
+(defvar rainbow-delimiters-syntax-table nil
"Syntax table (inherited from buffer major-mode) which uses all delimiters.
When rainbow-delimiters-minor-mode is first activated, it sets this variable and
the other rainbow-delimiters specific syntax tables based on the current
major-mode. The syntax table is constructed by the function
-'rainbow-delimiters-make-syntax-table-all-delimiters'.")
+'rainbow-delimiters-make-syntax-table'.")
-;; syntax-table: used with parse-partial-sexp for determining current depth.
-(defun rainbow-delimiters-make-syntax-table-all-delimiters (syntax-table)
+;; syntax-table: used with syntax-ppss for determining current depth.
+(defun rainbow-delimiters-make-syntax-table (syntax-table)
"Inherit SYNTAX-TABLE and add delimiters intended to be highlighted by mode."
(let ((table (copy-syntax-table syntax-table)))
(modify-syntax-entry ?\( "() " table)
@@ -322,16 +329,14 @@ major-mode. The syntax table is constructed by the function
(modify-syntax-entry ?\} "){" table)
table))
-(defun rainbow-delimiters-depth (point)
- "Return # of nested levels of parens, brackets, braces POINT is inside of."
- (save-excursion
- (beginning-of-defun)
- (let ((depth
- (with-syntax-table rainbow-delimiters-all-delimiters-syntax-table
- (car (parse-partial-sexp (point) point)))))
- (if (>= depth 0)
- depth
- 0)))) ; ignore negative depths created by unmatched closing parens.
+(defsubst rainbow-delimiters-depth (loc)
+ "Return # of nested levels of parens, brackets, braces LOC is inside of."
+ (let ((depth
+ (with-syntax-table rainbow-delimiters-syntax-table
+ (car (syntax-ppss loc)))))
+ (if (>= depth 0)
+ depth
+ 0))) ; ignore negative depths created by unmatched closing parens.
;;; Text properties
@@ -397,17 +402,14 @@ Sets text properties:
rear-nonsticky nil))))
-(defun rainbow-delimiters-char-ineligible-p (loc)
+(defsubst rainbow-delimiters-char-ineligible-p (loc)
"Return t if char at LOC should be skipped, e.g. if inside a comment.
Returns t if char at loc meets one of the following conditions:
- Inside a string.
- Inside a comment.
- Is an escaped char, e.g. ?\)"
- (let ((parse-state (save-excursion
- (beginning-of-defun)
- ;; (point) is at beg-of-defun; loc is the char location
- (parse-partial-sexp (point) loc))))
+ (let ((parse-state (syntax-ppss loc)))
(or
(nth 3 parse-state) ; inside string?
(nth 4 parse-state) ; inside comment?
@@ -435,11 +437,11 @@ LOC is location of character (delimiter) to be colorized."
;;; JIT-Lock functionality
;; Used to skip delimiter-by-delimiter `rainbow-delimiters-propertize-region'.
-(defvar rainbow-delimiters-delim-regex "\\(\(\\|\)\\|\\[\\|\\]\\|\{\\|\}\\)"
+(defconst rainbow-delimiters-delim-regex "\\(\(\\|\)\\|\\[\\|\\]\\|\{\\|\}\\)"
"Regex matching all opening and closing delimiters the mode highlights.")
;; main function called by jit-lock:
-(defun rainbow-delimiters-propertize-region (start end)
+(defsubst rainbow-delimiters-propertize-region (start end)
"Highlight delimiters in region between START and END.
Used by jit-lock for dynamic highlighting."
@@ -498,14 +500,18 @@ Used by jit-lock for dynamic highlighting."
(rainbow-delimiters-unpropertize-region (point-min) (point-max)))
(jit-lock-register 'rainbow-delimiters-propertize-region t)
;; Create necessary syntax tables inheriting from current major-mode.
- (set (make-local-variable 'rainbow-delimiters-all-delimiters-syntax-table)
- (rainbow-delimiters-make-syntax-table-all-delimiters (syntax-table)))))
+ (set (make-local-variable 'rainbow-delimiters-syntax-table)
+ (rainbow-delimiters-make-syntax-table (syntax-table)))))
;;;###autoload
(defun rainbow-delimiters-mode-enable ()
(rainbow-delimiters-mode 1))
;;;###autoload
+(defun rainbow-delimiters-mode-disable ()
+ (rainbow-delimiters-mode 0))
+
+;;;###autoload
(define-globalized-minor-mode global-rainbow-delimiters-mode
rainbow-delimiters-mode rainbow-delimiters-mode-enable)

No commit comments for this range

Something went wrong with that request. Please try again.