unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Alan Mackenzie <acm@muc.de>
To: Chong Yidong <cyd@gnu.org>
Cc: Helmut Eller <eller.helmut@gmail.com>, 10652@debbugs.gnu.org
Subject: bug#10652: 24.0.93; font-lock very slow for C++
Date: Thu, 1 Mar 2012 19:36:22 +0000	[thread overview]
Message-ID: <20120301193622.GA19099@acm.acm> (raw)
In-Reply-To: <874nuekjte.fsf@gnu.org>

Hello Yidong, hello Helmut.

On Sun, Feb 26, 2012 at 05:47:25PM +0800, Chong Yidong wrote:
> Helmut Eller <eller.helmut@gmail.com> writes:

> > Font lock is extremely slow in this file:

> > http://hg.openjdk.java.net/jdk7/jdk7/hotspot/raw-file/b92c45f2bc75/src/share/vm/runtime/globals.hpp

> > Download the file and open it with emacs -Q globals.hpp.  Then
> > scroll around in the file to see how slow it is.

> I got Emacs into an uninterruptible loop while scrolling through the
> buffer :-(

> Looks like a regression against Emacs 23's CC mode, which handles the
> file just fine.  Alan, could you investigate?  Thanks.

OK.  First point: this is the first time I've ever seen a file with a
3350 line macro.  ;-)  There are several places in CC Mode which assume
macros are small, so it's not too surprising that this file goes slowly.

I've elp'd the scrolling, and have the following fix as a first
approximation.  Please try it out, but it's not fully tested, so don't
use it in anger.

Just one thing: if you've already got CC Mode running when you load in
this version, please do

    (setq-default c-macro-names-with-semicolon nil)

before M-x c++-mode, to clear out a stale value.

There are more optimisations I'm looking at at the moment.  Here's the
patch:


diff -r 915250820ea6 cc-engine.el
--- a/cc-engine.el	Wed Feb 29 18:59:34 2012 +0000
+++ b/cc-engine.el	Thu Mar 01 19:23:24 2012 +0000
@@ -222,6 +222,38 @@
 	       (point))))
     c-macro-start))
 
+;; One element macro cache to cope with continual movement within very large
+;; CPP macros.
+(defvar c-macro-cache nil)
+(make-variable-buffer-local 'c-macro-cache)
+;; Nil or cons of the bounds of the most recent CPP form probed by
+;; `c-beginning-of-macro', `c-end-of-macro' or `c-syntactic-end-of-macro'.
+;; The cdr will be nil if we know only the start of the CPP form.
+(defvar c-macro-cache-start-pos nil)
+(make-variable-buffer-local 'c-macro-cache-start-pos)
+;; The starting position from where we determined `c-macro-cache'.
+(defvar c-macro-cache-syntactic nil)
+(make-variable-buffer-local 'c-macro-cache-syntactic)
+;; non-nil iff `c-macro-cache' has both elements set AND the cdr is at a
+;; syntactic end of macro, not merely an apparent one.
+
+(defun c-invalidate-macro-cache (beg end)
+  ;; Called from a before-change function.  If the change region is before or
+  ;; in the macro characterised by `c-macro-cache' etc., nullify it
+  ;; appropriately.  BEG and END are the standard before-change-functions
+  ;; parameters.  END isn't used.
+  (cond
+   ((null c-macro-cache))
+   ((< beg (car c-macro-cache))
+    (setq c-macro-cache nil
+	  c-macro-cache-start-pos nil
+	  c-macro-cache-syntactic nil))
+   ((and (cdr c-macro-cache)
+	 (< beg (cdr c-macro-cache)))
+    (setcdr c-macro-cache nil)
+    (setq c-macro-cache-start-pos beg
+	  c-macro-cache-syntactic nil))))
+
 (defun c-beginning-of-macro (&optional lim)
   "Go to the beginning of a preprocessor directive.
 Leave point at the beginning of the directive and return t if in one,
@@ -229,19 +261,36 @@
 
 Note that this function might do hidden buffer changes.	 See the
 comment at the start of cc-engine.el for more info."
-  (when c-opt-cpp-prefix
-    (let ((here (point)))
-      (save-restriction
-	(if lim (narrow-to-region lim (point-max)))
-	(beginning-of-line)
-	(while (eq (char-before (1- (point))) ?\\)
-	  (forward-line -1))
-	(back-to-indentation)
-	(if (and (<= (point) here)
-		 (looking-at c-opt-cpp-start))
-	    t
-	  (goto-char here)
-	  nil)))))
+  (let ((here (point)))
+    (when c-opt-cpp-prefix
+      (if (and (car c-macro-cache)
+	       (>= (point) (car c-macro-cache))
+	       (or (and (cdr c-macro-cache)
+			(<= (point) (cdr c-macro-cache)))
+		   (<= (point) c-macro-cache-start-pos)))
+	  (unless (< (car c-macro-cache) (or lim (point-min)))
+	    (progn (goto-char (max (or lim (point-min)) (car c-macro-cache)))
+		   (setq c-macro-cache-start-pos
+			 (max c-macro-cache-start-pos here))
+		   t))
+	(setq c-macro-cache nil
+	      c-macro-cache-start-pos nil
+	      c-macro-cache-syntactic nil)
+
+	(save-restriction
+	  (if lim (narrow-to-region lim (point-max)))
+	  (beginning-of-line)
+	  (while (eq (char-before (1- (point))) ?\\)
+	    (forward-line -1))
+	  (back-to-indentation)
+	  (if (and (<= (point) here)
+		   (looking-at c-opt-cpp-start))
+	      (progn
+		(setq c-macro-cache (cons (point) nil)
+		      c-macro-cache-start-pos here)
+		t)
+	    (goto-char here)
+	    nil))))))
 
 (defun c-end-of-macro ()
   "Go to the end of a preprocessor directive.
@@ -251,12 +300,24 @@
 
 Note that this function might do hidden buffer changes.	 See the
 comment at the start of cc-engine.el for more info."
-  (while (progn
-	   (end-of-line)
-	   (when (and (eq (char-before) ?\\)
-		      (not (eobp)))
-	     (forward-char)
-	     t))))
+   (if (and (cdr c-macro-cache)
+	    (<= (point) (cdr c-macro-cache))
+	    (>= (point) (car c-macro-cache)))
+       (goto-char (cdr c-macro-cache))
+     (unless (and (car c-macro-cache)
+		  (<= (point) c-macro-cache-start-pos)
+		  (>= (point) (car c-macro-cache)))
+       (setq c-macro-cache nil
+	     c-macro-cache-start-pos nil
+	     c-macro-cache-syntactic nil))
+     (while (progn
+	      (end-of-line)
+	      (when (and (eq (char-before) ?\\)
+			 (not (eobp)))
+		(forward-char)
+		t)))
+     (when (car c-macro-cache)
+       (setcdr c-macro-cache (point)))))
 
 (defun c-syntactic-end-of-macro ()
   ;; Go to the end of a CPP directive, or a "safe" pos just before.
@@ -271,12 +332,15 @@
   ;; at the start of cc-engine.el for more info.
   (let* ((here (point))
 	 (there (progn (c-end-of-macro) (point)))
-	 (s (parse-partial-sexp here there)))
-    (while (and (or (nth 3 s)	 ; in a string
-		    (nth 4 s))	 ; in a comment (maybe at end of line comment)
-		(> there here))	 ; No infinite loops, please.
-      (setq there (1- (nth 8 s)))
-      (setq s (parse-partial-sexp here there)))
+	 s)
+    (unless c-macro-cache-syntactic
+      (setq s (parse-partial-sexp here there))
+      (while (and (or (nth 3 s)	 ; in a string
+		      (nth 4 s)) ; in a comment (maybe at end of line comment)
+		  (> there here))	; No infinite loops, please.
+	(setq there (1- (nth 8 s)))
+	(setq s (parse-partial-sexp here there)))
+      (setq c-macro-cache-syntactic (car c-macro-cache)))
     (point)))
 
 (defun c-forward-over-cpp-define-id ()
diff -r 915250820ea6 cc-fonts.el
--- a/cc-fonts.el	Wed Feb 29 18:59:34 2012 +0000
+++ b/cc-fonts.el	Thu Mar 01 19:23:24 2012 +0000
@@ -409,7 +409,8 @@
 	       (cc-eval-when-compile
 		 (boundp 'parse-sexp-lookup-properties)))
 	      (BOD-limit
-	       (c-determine-limit 1000)))
+	       (c-determine-limit 500 ;; 1000
+				  )))
 	  (goto-char
 	   (let ((here (point)))
 	     (if (eq (car (c-beginning-of-decl-1 BOD-limit)) 'same)
diff -r 915250820ea6 cc-langs.el
--- a/cc-langs.el	Wed Feb 29 18:59:34 2012 +0000
+++ b/cc-langs.el	Thu Mar 01 19:23:24 2012 +0000
@@ -444,8 +444,10 @@
   ;; For documentation see the following c-lang-defvar of the same name.
   ;; The value here may be a list of functions or a single function.
   t nil
-  c++ '(c-extend-region-for-CPP c-before-change-check-<>-operators)
-  (c objc) 'c-extend-region-for-CPP
+  c++ '(c-extend-region-for-CPP
+	c-before-change-check-<>-operators
+	c-invalidate-macro-cache)
+  (c objc) '(c-extend-region-for-CPP c-invalidate-macro-cache)
   ;; java 'c-before-change-check-<>-operators
   awk 'c-awk-record-region-clear-NL)
 (c-lang-defvar c-get-state-before-change-functions
diff -r 915250820ea6 cc-vars.el
--- a/cc-vars.el	Wed Feb 29 18:59:34 2012 +0000
+++ b/cc-vars.el	Thu Mar 01 19:23:24 2012 +0000
@@ -1653,7 +1653,8 @@
 		    c-macro-names-with-semicolon))))))
     
 (defvar c-macro-names-with-semicolon
-  '("Q_OBJECT" "Q_PROPERTY" "Q_DECLARE" "Q_ENUMS")
+;  '("Q_OBJECT" "Q_PROPERTY" "Q_DECLARE" "Q_ENUMS")
+  nil
   "List of #defined symbols whose expansion ends with a semicolon.
 Alternatively it can be a string, a regular expression which
 matches all such symbols.



-- 
Alan Mackenzie (Nuremberg, Germany).





  reply	other threads:[~2012-03-01 19:36 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-01-30 11:15 bug#10652: 24.0.93; font-lock very slow for C++ Helmut Eller
2012-02-26  9:47 ` Chong Yidong
2012-03-01 19:36   ` Alan Mackenzie [this message]
2012-03-02 22:27   ` Alan Mackenzie
2012-03-16 14:19   ` Alan Mackenzie
2012-03-14  9:45 ` bug#10652: " Toon Claes
2012-03-16 10:18   ` bug#10652: Recursive loop in alloc.c [ Was: font-lock very slow for C++ ] Alan Mackenzie
     [not found]   ` <20120316101838.GB3439@acm.acm>
2012-03-16 10:34     ` Andreas Schwab
2012-03-16 14:24 ` bug#10652: 24.0.93; font-lock very slow for C++ Alan Mackenzie
2012-03-28  7:07 ` bug#10652: " Toon Claes
2012-03-29  9:35   ` Alan Mackenzie
2012-03-29 19:46     ` Toon Claes
2012-03-29 22:00       ` Alan Mackenzie
2012-04-02  7:48         ` Toon Claes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120301193622.GA19099@acm.acm \
    --to=acm@muc.de \
    --cc=10652@debbugs.gnu.org \
    --cc=cyd@gnu.org \
    --cc=eller.helmut@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).