From: Kelly Dean <kelly@prtime.org>
To: Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: emacs-devel@gnu.org
Subject: [PATCH] (Updated) Run hook when variable is set
Date: Mon, 09 Feb 2015 03:24:51 +0000 [thread overview]
Message-ID: <fnnc8lOjrDN9KDM6Z27odWFxaub5dcZS6ZBYMcgtPbx@local> (raw)
In-Reply-To: <jwvmw4pwyog.fsf-monnier+emacs@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 4621 bytes --]
Stefan Monnier wrote:
> No, the idea was rather to do:
>
> (defun set-internal-1 (args)
> (block nil ; Because Elisp isn't CL
> (if (constant-or-hooked-p)
> (if (constant-p)
> (if (forbidden-p)
> (error "setting constant")
> (return))
> (funcall symbol-watch-function ..args..))
> (do-some-stuff)
> (and-many-more-lines-of-stuff)
> (set-some-variable))))
Then the many more lines of stuff have to be copied into symbol-watch-function. Those lines can't just be skipped; they're necessary for correctly setting localized and forwarded symbols. By default, hooking a symbol must not change the behavior of Emacs, except for slowing it down. A hook handler can optionally change the behavior, but just hooking the symbol must cause no change if the handler doesn't.
I decided to solve the problem by factoring out the many more lines of stuff to a separate function, so they don't have to be duplicated. This introduces an extra function call for them, but that's ok, since setting localized and forwarded symbols isn't the hot path (and they're already slow anyway). This change has no effect on the hot path.
> I think watchpoints usually have the functionality of catching the
> modifications, being able to see the "before-change value" and being
> able to replace the assignment with something else. So "watch" makes
> a lot of sense to me.
Um, ok. I already improved the names, but I can change them again if necessary.
Updated patches attached. The first applies to 24.4. The second applies to trunk (using «patch», not «git apply»).
Feature improvements:
It reports the environment as «dyn-local» rather than «invalid» for set-default within a dynamic let-binding, since you said that's a valid thing to do.
It passes not only the new value being set, but also the current value of the variable.
The function names are more informative.
The API now uses function advice instead of a standard hook. Whenever a hooked variable is set, symbol-setter-function is called. Since it's the setter function, it's natural that overriding it would override the setting that occurs, so this is how I implemented the API. As a result, it's not necessary to explicitly set the variable in your handler, which means it's not necessary to temporarily unhook it either; all you have to do is return the value you want to set it to. See the docstring for symbol-setter-function for details.
Performance improvements:
I put «hooked» into «constant», and removed my branches from the hot paths of specbind and set_internal.
I did the minor optimizations of set_internal I mentioned previously, plus the relevant one in specbind and in find_symbol_value. Also in a few others that aren't on hot paths, but they were trivial so I did them anyway.
I inlined grow_specpdl, which speeds up specbind, which more than compensates for the conditional branch that varhook requires in unbind_to. I also inlined the first part (the hot path) of set_internal (i.e. of set_internal_1 in my patch). Both of those are fairly small and not used in very many places. The cost is an increase in the size of the Emacs executable by about 20kB (out of 18.9MB on my system, so that's about 0.1%).
The result of these changes is a measurable performance improvement compared to unmodified Emacs for the common cases, i.e. for setting and dynamically binding plainval (non-buffer-local, non-forwarded) symbols. So now, my patch actually _does_ make Emacs faster. ;-) Here are the benchmarks:
(require 'cl)
(defun test ()
(setq x 0)
(benchmark-run-compiled 100000 (incf x)))
(defun test1 ()
(setq x 0)
(benchmark-run-compiled
(do () ((> x 100000)) (incf x))))
(defun test2 ()
(benchmark-run-compiled 1000
(letrec
((foo
(lambda (x)
(let ((x (1+ x)))
(if (> x 100)
x
(1- (funcall foo x)))))))
(funcall foo 0))))
Execution time results to 4 significant digits:
test
minimum average maximum
Emacs 24.4 0.006253 0.007259 0.009721
with patch 0.001816 0.002148 0.002425
Average improvement: 70%
test1
minimum average maximum
Emacs 24.4 0.01074 0.01075 0.01078
with patch 0.009081 0.009301 0.009671
Average improvement: 13%
test2
minimum average maximum
Emacs 24.4 0.04401 0.04409 0.04417
with patch 0.04196 0.04202 0.04207
Average improvement: 4.7%
For all three tests, the slowest run with the patch was faster than the fastest run without the patch.
[-- Attachment #2: varhook-advice-24.4.patch.gz --]
[-- Type: application/octet-stream, Size: 8998 bytes --]
[-- Attachment #3: varhook-advice.patch.gz --]
[-- Type: application/octet-stream, Size: 8632 bytes --]
next prev parent reply other threads:[~2015-02-09 3:24 UTC|newest]
Thread overview: 110+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-20 2:54 Proposal to change cursor appearance to indicate region activation Kelly Dean
2013-04-20 7:23 ` Drew Adams
2015-01-22 5:38 ` [PATCH] " Kelly Dean
2015-01-22 14:25 ` Stefan Monnier
2015-01-23 3:08 ` [PATCH] " Kelly Dean
2015-01-23 4:55 ` Stefan Monnier
2015-01-23 11:07 ` Kelly Dean
2015-01-23 17:49 ` Drew Adams
2015-01-24 3:06 ` Kelly Dean
2015-01-24 4:52 ` Stefan Monnier
2015-01-24 9:22 ` Kelly Dean
2015-01-25 14:29 ` Stefan Monnier
2015-01-28 9:15 ` [PATCH] Run hook when variable is set Kelly Dean
2015-01-28 9:23 ` [PATCH] Proposal to change cursor appearance to indicate region activation Kelly Dean
2015-01-28 11:24 ` David Kastrup
2015-01-28 12:13 ` David Kastrup
2015-01-29 10:46 ` Kelly Dean
2015-01-29 11:16 ` David Kastrup
2015-01-30 7:20 ` Kelly Dean
2015-01-30 9:19 ` David Kastrup
2015-01-30 10:05 ` Kelly Dean
2015-01-30 10:12 ` David Kastrup
2015-01-30 9:43 ` Kelly Dean
2015-01-28 19:25 ` [PATCH] Run hook when variable is set Stefan Monnier
2015-01-29 8:20 ` Kelly Dean
2015-01-29 8:28 ` Lars Ingebrigtsen
2015-01-29 14:58 ` Stefan Monnier
2015-01-30 7:34 ` Kelly Dean
2015-01-30 15:55 ` Stefan Monnier
2015-01-31 9:18 ` Kelly Dean
2015-01-31 20:48 ` Stefan Monnier
2015-02-02 5:40 ` Kelly Dean
2015-02-02 15:57 ` Stefan Monnier
2015-02-03 19:56 ` Kelly Dean
2015-02-03 22:49 ` Stefan Monnier
2015-02-05 3:10 ` [PATCH] (Updated) " Kelly Dean
2015-02-05 13:57 ` Stefan Monnier
2015-02-06 5:34 ` Kelly Dean
2015-02-06 14:42 ` Stefan Monnier
2015-02-07 12:27 ` Kelly Dean
2015-02-07 15:09 ` Stefan Monnier
2015-02-09 3:24 ` Kelly Dean [this message]
2015-02-12 19:58 ` Stefan Monnier
2015-02-13 23:08 ` Kelly Dean
2015-02-14 0:55 ` Stefan Monnier
2015-02-14 22:19 ` Kelly Dean
2015-02-15 20:25 ` Stefan Monnier
2015-02-17 2:22 ` Kelly Dean
2015-02-17 23:07 ` Richard Stallman
2015-02-18 3:19 ` The purpose of makunbound (Was: Run hook when variable is set) Kelly Dean
2015-02-18 5:48 ` The purpose of makunbound Stefan Monnier
2015-02-18 8:51 ` Kelly Dean
2015-02-18 14:34 ` Stefan Monnier
2015-02-18 18:53 ` Kelly Dean
2015-02-18 22:42 ` Stefan Monnier
2015-02-19 10:36 ` Kelly Dean
2015-02-22 0:18 ` Kelly Dean
2015-02-19 10:45 ` Kelly Dean
2015-02-19 13:33 ` Stefan Monnier
2015-02-19 23:51 ` Kelly Dean
2015-02-20 1:59 ` Stefan Monnier
2015-02-20 9:35 ` Kelly Dean
2015-02-20 16:55 ` Stefan Monnier
2015-02-20 2:58 ` Stephen J. Turnbull
2015-02-20 0:56 ` Richard Stallman
2015-02-20 9:02 ` Kelly Dean
2015-02-20 15:41 ` Richard Stallman
2015-02-21 5:45 ` Stephen J. Turnbull
2015-02-22 0:32 ` Kelly Dean
2015-02-22 8:45 ` Andreas Schwab
2015-02-18 5:15 ` [PATCH] (Updated) Run hook when variable is set Kelly Dean
2015-02-18 22:37 ` Stefan Monnier
2015-02-18 22:37 ` Stefan Monnier
2015-02-19 10:35 ` Kelly Dean
2015-02-19 13:30 ` Stefan Monnier
2015-02-20 6:48 ` Kelly Dean
2015-02-20 19:29 ` Stefan Monnier
2015-02-21 14:18 ` Kelly Dean
2015-02-21 20:51 ` Stefan Monnier
2015-02-22 0:32 ` Kelly Dean
2015-02-22 10:40 ` Stephen J. Turnbull
2015-02-22 21:35 ` Stefan Monnier
2015-02-23 3:09 ` Kelly Dean
2015-02-23 4:19 ` Stefan Monnier
2015-02-20 20:27 ` Proposal for debugging/testing option Kelly Dean
2015-02-24 16:28 ` Stefan Monnier
2015-02-14 20:37 ` [PATCH] (Updated) Run hook when variable is set Johan Bockgård
2015-02-15 19:36 ` Stefan Monnier
2015-02-15 19:53 ` Patches: inline vs. attachment, compressed vs. uncompressed. [was: Run hook when variable is set] Alan Mackenzie
2015-02-06 9:55 ` [PATCH] (Updated) Run hook when variable is set Kelly Dean
2015-01-30 23:29 ` [PATCH] " Richard Stallman
2015-01-31 9:23 ` Kelly Dean
2015-01-31 23:16 ` Richard Stallman
2015-02-02 5:41 ` Kelly Dean
2015-02-01 2:04 ` Alexis
2015-02-01 4:05 ` Stefan Monnier
2015-02-01 8:58 ` David Kastrup
2015-01-29 16:06 ` Eli Zaretskii
2015-01-30 7:14 ` Kelly Dean
2015-01-30 9:08 ` Eli Zaretskii
2015-01-23 20:34 ` [PATCH] Proposal to change cursor appearance to indicate region activation Stefan Monnier
2015-01-24 0:25 ` Kelly Dean
2015-01-23 10:01 ` Tassilo Horn
2015-01-23 17:49 ` Drew Adams
2015-01-23 10:06 ` Eli Zaretskii
2015-01-23 11:40 ` Kelly Dean
2015-01-23 11:56 ` Eli Zaretskii
2015-01-22 5:41 ` Kelly Dean
2013-11-23 13:34 ` Stefan Monnier
2013-11-23 20:25 ` Drew Adams
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=fnnc8lOjrDN9KDM6Z27odWFxaub5dcZS6ZBYMcgtPbx@local \
--to=kelly@prtime.org \
--cc=emacs-devel@gnu.org \
--cc=monnier@IRO.UMontreal.CA \
/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).