From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: npostavs@users.sourceforge.net Newsgroups: gmane.emacs.bugs Subject: bug#25122: 24.5; function describe-variable hangs on large variables Date: Sun, 12 Mar 2017 12:07:43 -0400 Message-ID: <87tw6y7a28.fsf@users.sourceforge.net> References: <20161206022112.GF25778@E15-2016.optimum.net> <87twahk19y.fsf@gmail.com> <87d1h4fld5.fsf@users.sourceforge.net> <871sxkyv2m.fsf@gmail.com> <87mvcs8j7w.fsf@users.sourceforge.net> <87k27vtxou.fsf@gmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1489335197 19954 195.159.176.226 (12 Mar 2017 16:13:17 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sun, 12 Mar 2017 16:13:17 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) Cc: 25122@debbugs.gnu.org, Boruch Baum To: Thierry Volpiatto Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sun Mar 12 17:13:11 2017 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cn67J-0004OR-2p for geb-bug-gnu-emacs@m.gmane.org; Sun, 12 Mar 2017 17:13:09 +0100 Original-Received: from localhost ([::1]:47558 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cn61a-0000g1-91 for geb-bug-gnu-emacs@m.gmane.org; Sun, 12 Mar 2017 12:07:14 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:58190) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cn61T-0000fv-Jf for bug-gnu-emacs@gnu.org; Sun, 12 Mar 2017 12:07:09 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cn61O-0001ob-IT for bug-gnu-emacs@gnu.org; Sun, 12 Mar 2017 12:07:07 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:54592) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cn61O-0001oT-Ds for bug-gnu-emacs@gnu.org; Sun, 12 Mar 2017 12:07:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1cn61O-0007Ap-1L for bug-gnu-emacs@gnu.org; Sun, 12 Mar 2017 12:07:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: npostavs@users.sourceforge.net Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 12 Mar 2017 16:07:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 25122 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 25122-submit@debbugs.gnu.org id=B25122.148933479327537 (code B ref 25122); Sun, 12 Mar 2017 16:07:01 +0000 Original-Received: (at 25122) by debbugs.gnu.org; 12 Mar 2017 16:06:33 +0000 Original-Received: from localhost ([127.0.0.1]:52791 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cn60v-0007A5-Do for submit@debbugs.gnu.org; Sun, 12 Mar 2017 12:06:33 -0400 Original-Received: from mail-it0-f49.google.com ([209.85.214.49]:35006) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cn60t-00079t-Ex for 25122@debbugs.gnu.org; Sun, 12 Mar 2017 12:06:31 -0400 Original-Received: by mail-it0-f49.google.com with SMTP id m27so17933862iti.0 for <25122@debbugs.gnu.org>; Sun, 12 Mar 2017 09:06:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=e8Ilt2LeU2fwTMN/Y/fTbCx9cutrNRzYLEtD5CCRjT0=; b=V/NIWL2OTPZlgVRzMMzSSLBfLPtfjL8HmvUH8aidvbVgX0DkeWsOW0vRU/RTRFna0E drg41Qot94irlIKt/kx1Jqgjn5EM77a293YKjDg6DWbccl0DqD1IVc33mbb9NW3rgKqq 9h/cUVwNie6XWo7YfAe4NFcctNXeHs+HOyIKGQXLPqgUNQQVH2eu2pnHgmy5jASovRoU e51XDQrSGwCS5zi3KD6EG0uGjnInbqKG/NFdzmxROiYqucM0O0ZadCcUP30aekIjlki0 vmt15q0DClP78eHgV3JIgNHUUGTv5wWd3crDtUWhRp2DlVCcqE7rru3io+GCduCmmvOh tFJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:references:date :in-reply-to:message-id:user-agent:mime-version; bh=e8Ilt2LeU2fwTMN/Y/fTbCx9cutrNRzYLEtD5CCRjT0=; b=LWd0489Hv20zzRrDVAymVGvBMEHyKXz/ypowcKZpQ7k7Wi0YcNfmJK4/SLLQ7TwHED Z1uV/ia+N6PIHBw2ZHObV7As1QeLFyHtNx038Sn866/U2IXtLd5GP3Kv+4PQ/RX/OwIn n9j3sTU62dyT8sNl2BmPhWaMQRJrcCqe9ywxTLQ8Iiz8MH68MZLF43UY9DRpe2MZ10gs w+X3Fus7iT2uXoFpHlcUyMapwHFdDQP01WYNbkZZlHie6mykA1lkaWVFcE3RCgLvq/WE m5lYCP2VNT5s6acnTyQGLlLNgl/3qQdd4bBYUn60cCmvAJZ4/P+kibIiGFsLqWD0+nGB Abkw== X-Gm-Message-State: AFeK/H02siNuLILimX0phVMm35J5qGiNx9PqfC+egtpQQtqqtYZwAweIT008FlK003Q8iA== X-Received: by 10.36.10.13 with SMTP id 13mr7310121itw.110.1489334785969; Sun, 12 Mar 2017 09:06:25 -0700 (PDT) Original-Received: from zony ([45.2.7.65]) by smtp.googlemail.com with ESMTPSA id x100sm2524096ita.12.2017.03.12.09.06.24 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sun, 12 Mar 2017 09:06:25 -0700 (PDT) In-Reply-To: <87k27vtxou.fsf@gmail.com> (Thierry Volpiatto's message of "Sat, 11 Mar 2017 20:34:09 +0100") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:130520 Archived-At: --=-=-= Content-Type: text/plain Thierry Volpiatto writes: > npostavs@users.sourceforge.net writes: > >> I've been attempting an alternate approach which prettyprints the object >> while scanning it instead of the current way of printing and then >> reindenting. Without optimizing, it's about 3 times as fast as the >> current pp (it's the pp-prin1 in the benchmarks below), though more than >> 3 times slower than your mapc pp trick. On the other hand, it also >> doesn't yet handle function-specific indentation or any compound >> structure apart from lists, so I'm not sure if it will end up being much >> faster. >> >> (benchmark 1 '(with-temp-buffer (pp-prin1 long-list (current-buffer)) nil)) "Elapsed time: 3.391232s (0.565806s in 11 GCs)" >> (benchmark 1 '(progn (pp-to-string long-list) nil)) "Elapsed time: 9.988515s (0.148034s in 3 GCs)" >> (benchmark 1 '(progn (with-output-to-string (mapc 'pp long-list)) nil)) "Elapsed time: 0.983493s (0.144424s in 3 GCs)" >> (benchmark 1 '(progn (cl-prin1-to-string long-list) nil)) "Elapsed time: 0.511617s (0.152483s in 3 GCs)" >> (benchmark 1 '(progn (prin1-to-string long-list) nil)) "Elapsed time: 0.029320s" > > Interesting, thanks to work on this. With a couple of minor optimizations it's down to about 1.8s. The first is to reuse the tempbuffer instead of letting cl-prin1-to-string make a new one each time. Second is to add (eval-when-compile (cl-proclaim '(optimize (speed 3) (safety 0)))) This also stops these warnings (I guess they're caused by the "safety" code?): ../../emacs-master/lisp/emacs-lisp/pp.el:159:19:Warning: value returned from (aref state 6) is unused ../../emacs-master/lisp/emacs-lisp/pp.el:204:24:Warning: value returned from (aref state 10) is unused New times: (benchmark 1 '(with-temp-buffer (pp-prin1 long-list (current-buffer)) nil)) "Elapsed time: 1.800146s (0.231706s in 6 GCs)" (benchmark 1 '(progn (pp-to-string long-list) nil)) "Elapsed time: 9.950225s (0.154100s in 4 GCs)" (benchmark 1 '(progn (with-output-to-string (mapc 'pp long-list)) nil)) "Elapsed time: 0.980923s (0.149787s in 4 GCs)" I foolishly neglected to write down what exactly long-list was before, starting from emacs -Q this seems to approximate it though: (progn (require 'pp) (require 'dabbrev) (require 'edebug) (require 'cc-mode) (require 'vc) (setq long-list load-history) (length long-list)) ;=> 142 --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v2-0001-New-pretty-printer-Bug-25122.patch Content-Description: patch >From e4b1a2ef3b4b11466e81d639a09ff671318e0968 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sat, 11 Mar 2017 00:09:36 -0500 Subject: [PATCH v2] New pretty printer (Bug#25122) * lisp/emacs-lisp/pp.el (pp-state): New struct. (pp--scan): New function, measures length of sublists (actually "logical blocks" to allow for more customizable grouping than just by lists). Calls pp--print when scanned tokens are too wide to fit on a single line. (pp--print): New function, prints tokens horizontally or vertically depending on whether the sublist can fit within the line. (pp-prin1): New function, entry point for pp--scan and pp-print. (pp-print-object): New generic function. (pp-print-object) : New method, prettyprinter for lists. --- lisp/emacs-lisp/pp.el | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el index 7ef46a48bd..8c2ed24ffd 100644 --- a/lisp/emacs-lisp/pp.el +++ b/lisp/emacs-lisp/pp.el @@ -24,6 +24,9 @@ ;;; Code: +(eval-when-compile (require 'cl-lib)) +(require 'ring) + (defvar font-lock-verbose) (defgroup pp nil @@ -121,6 +124,159 @@ pp-display-expression (setq buffer-read-only nil) (set (make-local-variable 'font-lock-verbose) nil))))) +(eval-when-compile + ;; FIXME: should we try to restore original settings? (how?) + (cl-proclaim '(optimize (speed 3) (safety 0)))) + +(cl-defstruct (pp-state (:constructor + make-pp-state + (stream + tempbuffer + right-margin + &aux + (left-margin 0) + (indent '(0)) + (scan-depth 0) + (print-depth 0) + (print-width 0) + (scan-width 0) + (block-mode (list nil)) + (fifo (make-ring 30))))) + stream + tempbuffer + right-margin ; how far we may go. + left-margin ; how far printer has gone + print-width ; total width of tokens printed so far. + indent ; left-margin, stack per depth. + scan-width ; total width of tokens scanned so far. + scan-depth + print-depth + block-widths + block-mode ; `:vertical', `:horizontal', nil (undecided); stack per depth. + fifo + ) + +(defun pp--print (state) + (cl-symbol-macrolet ((stream (pp-state-stream state)) + (depth (pp-state-print-depth state)) + (scan-depth (pp-state-scan-depth state)) + (fifo (pp-state-fifo state)) + (left-margin (pp-state-left-margin state)) + (width (pp-state-print-width state)) + (indent (pp-state-indent state)) + (right-margin (pp-state-right-margin state)) + (block-mode (pp-state-block-mode state))) + (catch 'rescan + (while (not (ring-empty-p fifo)) + (pcase (ring-remove fifo) + ((and `(,len . :open-block) token) + (if (<= len 0) + ;; Not ready to print this yet! + (progn (ring-insert-at-beginning fifo token) + (throw 'rescan nil)) + (cl-incf depth) + (push left-margin indent) + (push (if (> (+ left-margin len) right-margin) + :vertical :horizontal) + block-mode))) + (:close-block (cl-decf depth) (pop indent) (pop block-mode)) + (:blank + (pcase (car block-mode) + (:vertical + (terpri stream) + (princ (make-string (car indent) ?\s) stream) + (setf left-margin (car indent))) + ((or :horizontal 'nil) + (write-char ?\s stream) + (cl-incf left-margin)) + (_ (error "oops"))) + (cl-incf width)) + (:eof nil) + ((and (pred characterp) char) + (write-char char stream) + (cl-incf left-margin (char-width char)) + (cl-incf width (char-width char))) + (string + (princ string stream) + (cl-incf left-margin (string-width string)) + (cl-incf width (string-width string)))))))) + +(defun pp--scan (token state) + (cl-symbol-macrolet ((stream (pp-state-stream state)) + (depth (pp-state-scan-depth state)) + (print-depth (pp-state-print-depth state)) + (fifo (pp-state-fifo state)) + (width (pp-state-scan-width state)) + (right-margin (pp-state-right-margin state)) + (block-widths (pp-state-block-widths state))) + (cl-flet ((scanlen (len) (cl-incf width len))) + (cl-assert (> (ring-size fifo) (ring-length fifo))) + (ring-insert fifo token) + (pcase token + (:open-block + (cl-incf depth) + (let ((block-token (cons (- width) (ring-remove fifo 0)))) + (push block-token block-widths) + (ring-insert fifo block-token))) + (:close-block + (cl-incf (caar block-widths) width) + (when (> (caar block-widths) right-margin) + (pp--print state)) + (cl-decf depth) + (pop block-widths)) + (:blank (scanlen 1)) + (:eof (pp--print state)) + ((pred characterp) (scanlen (char-width token))) + (_ (scanlen (string-width token))))) + (when block-widths + (when (> (+ (caar block-widths) width) right-margin) + (dolist (block-width block-widths) + (setf (car block-width) (+ right-margin 1)))) + (when (> (caar block-widths) right-margin) + (pp--print state))))) + +(defvar cl-print-readably) ; cl-print.el + +(defun pp-prin1 (object &optional stream right-margin) + (unless right-margin + (setq right-margin fill-column)) + (with-temp-buffer + (let ((cl-print-readably nil) + (state (make-pp-state (or stream standard-output) (current-buffer) + right-margin))) + (pp--scan :open-block state) + (prog1 (pp-print-object object state) + (pp--scan :close-block state) + (pp--scan :eof state))))) + + +(cl-defgeneric pp-print-object (object state) + ;; Fallback to standard `cl-print-object'. + (pp--scan (with-current-buffer (pp-state-tempbuffer state) + (cl-prin1 object (current-buffer)) + (prog1 (buffer-string) + (erase-buffer))) + state) + object) + +(cl-defmethod pp-print-object ((list cons) state) + (pcase list + (`(,head . ,tail) + (pp--scan "(" state) + (pp--scan :open-block state) + (pp-print-object head state) + (while (consp tail) + (pp--scan :blank state) + (pp-print-object (pop tail) state)) + (when tail + (pp--scan :blank state) + (pp--scan ?\. state) + (pp--scan :blank state) + (pp-print-object tail state)) + (pp--scan :close-block state) + (pp--scan ")" state))) + list) + ;;;###autoload (defun pp-eval-expression (expression) "Evaluate EXPRESSION and pretty-print its value. -- 2.11.1 --=-=-=--