From: Zack Weinberg <zack@codesourcery.com>
Subject: byte-opt.el addition - optimize list of compile-time constants
Date: Wed, 08 Dec 2004 01:21:54 -0800 [thread overview]
Message-ID: <87u0qxgn65.fsf@codesourcery.com> (raw)
Consider a construct like the following, which might appear in a major
mode definition. (This example is from my half-written major mode for
editing GCC machine descriptions. These are mostly Lisp-like but
contain large blocks of embedded C, which it would be nice to apply
c-mode to. The MMM library tries to make that possible. It doesn't
work very well with c-mode right now, but that's irrelevant to this email.)
(when (require 'mmm-auto nil t)
(mmm-add-classes
'((md-embedded-c
:submode c-mode
:front "^{"
:back "^}"
:include-front t
:include-back t
;; If the 'back' } is on a line by itself, include the carriage
;; return too; this makes the submode background highlight look
;; less strange.
:back-offset #'(lambda () (when (eq (following-char) ?\n)
(forward-char 1)))
)))
One would like the anonymous :back-offset function to be
byte-compiled. This does not happen (with 21.3.1) because the byte
compiler does not look inside a quoted form for lambda expressions.
I can make it get byte-compiled by using ` instead of ':
(when (require 'mmm-auto nil t)
(mmm-add-classes
`((md-embedded-c
:submode c-mode
:front "^{"
:back "^}"
:include-front t
:include-back t
;; If the 'back' } is on a line by itself, include the carriage
;; return too; this makes the submode background highlight look
;; less strange.
:back-offset #'(lambda () (when (eq (following-char) ?\n)
(forward-char 1)))
)))
The byte compiler then sees, after ` gets macroexpanded,
(list (list
'md-embedded-c
:submode 'c-mode
:front "^{"
:back "^}"
:include-front t
:include-back t
:back-offset (function (lambda () ...))))
and it *does* look into that for the nested lambda and compile it.
However, the cost of this is a substantially less efficient byte-code
sequence for the outer form. Instead of having the entire
S-expression in the constant vector, each individual component goes
into the vector, and a rather lengthy byte-code sequence is generated
to execute the (list (list ...)) at runtime. This does not matter
terribly much for *this* example, which is going to be executed once
when the file is loaded, but I can easily imagine circumstances where
it would matter. I can work around this by writing (eval-when-compile
...) around the `(...) form, but that's just plain ugly.
I dug through the byte-compiler a bit and determined that it makes no
attempt whatsoever to optimize (list ...) expressions. So I wrote a
basic byte-optimize-list function, and here it is, suitable for being
slapped into byte-opt.el.
(defun byte-optimize-list (form)
; (list) -> nil
(if (null (cdr form))
nil
; (list X Y Z ...) -> (quote (X Y Z)) when X Y Z are all
; compile-time constants. In addition to the set of things for
; which byte-compile-constp is true, detect embedded
; (function ...) expressions and compile them, which turns them
; into compile-time constants.
(catch :nonconstant
(let ((new-form
(mapcar (lambda (elem)
(if (consp elem)
(cond
; (quote X) -> X
((eq (car elem) 'quote)
(car (cdr elem)))
; (function X) -> byte-compiled X
((eq (car elem) 'function)
(byte-compile (car (cdr elem))))
; ??? (lambda ...) -> byte-compile it
((eq (car elem) 'lambda)
(byte-compile elem))
(t (throw :nonconstant form)))
(if (byte-compile-constp elem)
elem
(throw :nonconstant form))))
(cdr form))))
; If we get here, new-form is a list of constants; turn it
; into a constant.
(list 'quote new-form)))))
(put 'list 'byte-optimizer 'byte-optimize-list)
This does have several issues - I'm not proposing it be put in as is.
First, I'm unsure I'm unsure whether it is appropriate to detect and
byte-compile a bare lambda. The manual dithers on the subject.
Second, I'm not sure why I'm even getting uncompiled lambdas in the
list. Shouldn't the master byte-opt loop, operating depth-first, have
already done it? Having to do it here means that if we have a list
which has several lambda expressions followed by something that isn't
a compile-time constant, we'll compile the lambdas, discover the
non-constant, throw away the compiled functions, and have to do them
over again later.
Rather less important is the question of whether I should be
trying to optimize lists that aren't entirely constants. It is
possible that, e.g. given
(list 1 2 3 (+ var1 var2) 4 5 6)
it would be appropriate to translate it to
(nconc '(1 2 3) (+ var1 var2) '(4 5 6))
But I can see that being not a good idea, as well, so I didn't do it.
Finally, I'm not sure about coding style. The catch/let/mapcar/nested
conditionals sequence is rather ugly, IMO, but I couldn't think of a
better way.
Thoughts?
(please cc:, I'm not subscribed to emacs-devel.)
zw
next reply other threads:[~2004-12-08 9:21 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-12-08 9:21 Zack Weinberg [this message]
2004-12-08 16:56 ` byte-opt.el addition - optimize list of compile-time constants Stefan Monnier
2004-12-08 18:59 ` Zack Weinberg
2004-12-08 19:27 ` Stefan Monnier
2004-12-08 19:45 ` Zack Weinberg
2004-12-08 19:56 ` Stefan Monnier
2004-12-08 20:14 ` Nick Roberts
2004-12-08 22:47 ` Zack Weinberg
2004-12-08 23:40 ` Stefan Monnier
2004-12-09 1:20 ` Zack Weinberg
2004-12-09 2:11 ` Stefan Monnier
2004-12-09 2:33 ` Zack Weinberg
2004-12-09 2:46 ` Miles Bader
2004-12-09 3:08 ` Zack Weinberg
2004-12-09 3:28 ` Miles Bader
2004-12-09 3:48 ` Zack Weinberg
2004-12-09 4:04 ` Miles Bader
2004-12-09 4:41 ` Zack Weinberg
2004-12-09 4:52 ` Stefan Monnier
2004-12-09 5:33 ` Zack Weinberg
2004-12-09 5:39 ` Miles Bader
2004-12-09 6:49 ` Zack Weinberg
2004-12-09 15:22 ` Thien-Thi Nguyen
2004-12-10 5:50 ` Richard Stallman
2004-12-09 9:22 ` David Kastrup
2004-12-09 4:54 ` Miles Bader
2004-12-09 9:20 ` David Kastrup
2004-12-09 4:35 ` Stefan Monnier
2004-12-09 4:55 ` Zack Weinberg
2004-12-09 5:13 ` Stefan Monnier
2004-12-09 9:10 ` David Kastrup
2004-12-08 19:33 ` Paul Pogonyshev
2004-12-09 10:34 ` Andreas Schwab
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=87u0qxgn65.fsf@codesourcery.com \
--to=zack@codesourcery.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).