* Tooltips GC overhead @ 2015-07-28 12:45 martin rudalics 2015-07-28 13:53 ` Eli Zaretskii 2015-07-28 23:51 ` Stefan Monnier 0 siblings, 2 replies; 47+ messages in thread From: martin rudalics @ 2015-07-28 12:45 UTC (permalink / raw) To: emacs-devel Showing a tooltip here incurs one or two garbage collection cycles. This is just as much as an entire `scroll-down' in a C mode buffer. Experienced on Gtk+ and Windows builds. Does anyone have an idea what's causing that? martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-28 12:45 Tooltips GC overhead martin rudalics @ 2015-07-28 13:53 ` Eli Zaretskii 2015-07-28 15:09 ` martin rudalics 2015-07-28 23:51 ` Stefan Monnier 1 sibling, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-07-28 13:53 UTC (permalink / raw) To: martin rudalics; +Cc: emacs-devel > Date: Tue, 28 Jul 2015 14:45:31 +0200 > From: martin rudalics <rudalics@gmx.at> > > Showing a tooltip here incurs one or two garbage collection cycles. I need to show a tooltip 3 times before GC kicks in. > Does anyone have an idea what's causing that? Looking at the value returned by garbage-collect might give some ideas. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-28 13:53 ` Eli Zaretskii @ 2015-07-28 15:09 ` martin rudalics 0 siblings, 0 replies; 47+ messages in thread From: martin rudalics @ 2015-07-28 15:09 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel >> Showing a tooltip here incurs one or two garbage collection cycles. > > I need to show a tooltip 3 times before GC kicks in. That's what I get with emacs -Q and no files visited. With more buffers and files visited things get worse soon. > Looking at the value returned by garbage-collect might give some > ideas. I tried that before and it's hardly instructive. Less characters and considerably less intervals consed than on the average. As expected, I think. Thanks for trying, martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-28 12:45 Tooltips GC overhead martin rudalics 2015-07-28 13:53 ` Eli Zaretskii @ 2015-07-28 23:51 ` Stefan Monnier 2015-07-29 7:18 ` martin rudalics 1 sibling, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-07-28 23:51 UTC (permalink / raw) To: martin rudalics; +Cc: emacs-devel > Showing a tooltip here incurs one or two garbage collection cycles. > This is just as much as an entire `scroll-down' in a C mode buffer. > Experienced on Gtk+ and Windows builds. Does anyone have an idea what's > causing that? You could try M-x profiler-start RET mem RET, tho IIRC it counts all allocations rather than only those that affect calling the GC. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-28 23:51 ` Stefan Monnier @ 2015-07-29 7:18 ` martin rudalics 2015-07-29 14:29 ` Paul Eggert 0 siblings, 1 reply; 47+ messages in thread From: martin rudalics @ 2015-07-29 7:18 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel > You could try M-x profiler-start RET mem RET, tho IIRC it counts all > allocations rather than only those that affect calling the GC. This gets me something like the tree below. Can someone help me to interpret it? I am completely lost. For example from - timer-event-handler 56,501,327 4% - apply 56,501,327 4% - tooltip-timeout 56,500,283 4% - run-hook-with-args-until-success 56,500,283 4% - tooltip-help-tips 56,500,283 4% - tooltip-show 56,500,283 4% - x-show-tip 25,190,423 6% - face-set-after-frame-default 24,981,305 5% + face-spec-recalc 24,955,421 5% face-list 19,836 0% + frame-set-background-mode 1,044 0% run-at-time 996 0% frame-windows-min-size 996 0% + mouse-autoselect-window-select 1,044 0% + command-execute 3,441,959 5% + redisplay_internal (C function) 439,724 0% mode-line-modified-help-echo 8,188 0% + tooltip-show-help 6,264 0% mouse-fixup-help-message 512 0% ... 0 0% I deduce that `tooltip-show' is responsible for allocating 56,500,283 bytes (4% of what?). Apparently, from this amount `x-show-tip' spends 25,190,423 bytes (6% of what?). Who's responsible for the remaining 30 or so million bytes? If the percentages are for the total number of bytes allocated, why do 56 millions contribute 4% and three millions (as for `command-execute') 5%? Expanding the tree further gets me - timer-event-handler 56,501,327 4% - apply 56,501,327 4% - tooltip-timeout 56,500,283 4% - run-hook-with-args-until-success 56,500,283 4% - tooltip-help-tips 56,500,283 4% - tooltip-show 56,500,283 4% - x-show-tip 25,190,423 6% - face-set-after-frame-default 24,981,305 5% - face-spec-recalc 24,955,421 5% + face-spec-set-2 12,677,521 3% - make-face-x-resource-internal 8,684,863 -3% - set-face-attributes-from-resources 8,684,863 -3% + set-face-attribute-from-resource 1,401,887 2% + face-spec-reset-face 3,494,237 5% + face-spec-choose 16,324 0% face-list 19,836 0% + frame-set-background-mode 1,044 0% run-at-time 996 0% frame-windows-min-size 996 0% + mouse-autoselect-window-select 1,044 0% + command-execute 3,441,959 5% + redisplay_internal (C function) 439,724 0% mode-line-modified-help-echo 8,188 0% + tooltip-show-help 6,264 0% mouse-fixup-help-message 512 0% ... 0 0% Again between `set-face-attributes-from-resources' with its 8,684,863 (why -3%?) and `set-face-attribute-from-resource' with its 1,401,887 I have a gap of 7 million bytes. What am I missing? Is the tree printing routine deficient? Thanks, martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 7:18 ` martin rudalics @ 2015-07-29 14:29 ` Paul Eggert 2015-07-29 15:40 ` martin rudalics 0 siblings, 1 reply; 47+ messages in thread From: Paul Eggert @ 2015-07-29 14:29 UTC (permalink / raw) To: martin rudalics, Stefan Monnier; +Cc: emacs-devel martin rudalics wrote: > Is the tree printing routine deficient? Yes. Please try 'configure --with-wide-int'. We really should make that the default. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 14:29 ` Paul Eggert @ 2015-07-29 15:40 ` martin rudalics 2015-07-29 16:33 ` Eli Zaretskii 2015-07-29 16:45 ` Paul Eggert 0 siblings, 2 replies; 47+ messages in thread From: martin rudalics @ 2015-07-29 15:40 UTC (permalink / raw) To: Paul Eggert, Stefan Monnier; +Cc: emacs-devel >> Is the tree printing routine deficient? > > Yes. Please try 'configure --with-wide-int'. We really should make that the default. Does that mean I have to recompile all elisp files? Because just doing that (on Windows 32) followed by `make' makes the number appear more or less the same and the percentages mostly negative. For example, I now get - tooltip-show 37,978,113 -9% - x-show-tip 17,048,133 -8% The numbers on my Debian 64 Gtk build are roughly the same (although the percentages show up correctly there). So if you are only concerned about the percentages, then this could be a problem of my Windows builds (or the gcc I use there). Anyway. If you do `profiler-start', move the mouse over your mode line for a few seconds so that tooltips show up a couple of times and then do `profiler-report' and look at the results: Can you tell me from your report where `tooltip-show' allocates more than half of the bytes it allocates if _not_ in `x-show-tip'? martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 15:40 ` martin rudalics @ 2015-07-29 16:33 ` Eli Zaretskii 2015-07-29 18:05 ` martin rudalics 2015-07-29 16:45 ` Paul Eggert 1 sibling, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-07-29 16:33 UTC (permalink / raw) To: martin rudalics; +Cc: eggert, monnier, emacs-devel > Date: Wed, 29 Jul 2015 17:40:25 +0200 > From: martin rudalics <rudalics@gmx.at> > Cc: emacs-devel <emacs-devel@gnu.org> > > >> Is the tree printing routine deficient? > > > > Yes. Please try 'configure --with-wide-int'. We really should make that the default. > > Does that mean I have to recompile all elisp files? No, of course not. Otherwise, how would an Emacs tarball made on a 64-bit platform work out of the box on a 32-bit platform? > Because just doing > that (on Windows 32) followed by `make' makes the number appear more or > less the same and the percentages mostly negative. For example, I now > get > > - tooltip-show 37,978,113 -9% > - x-show-tip 17,048,133 -8% I think this is what Paul alluded to. > The numbers on my Debian 64 Gtk build are roughly the same (although the > percentages show up correctly there). The --with-wide-int option is a no-op in 64-bit builds, AFAIK. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 16:33 ` Eli Zaretskii @ 2015-07-29 18:05 ` martin rudalics 2015-07-29 21:15 ` Paul Eggert 0 siblings, 1 reply; 47+ messages in thread From: martin rudalics @ 2015-07-29 18:05 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, monnier, emacs-devel >> Does that mean I have to recompile all elisp files? > > No, of course not. Otherwise, how would an Emacs tarball made on a > 64-bit platform work out of the box on a 32-bit platform? OK. I wouldn't have had the time anyway ;-) >> Because just doing >> that (on Windows 32) followed by `make' makes the number appear more or >> less the same and the percentages mostly negative. For example, I now >> get >> >> - tooltip-show 37,978,113 -9% >> - x-show-tip 17,048,133 -8% > > I think this is what Paul alluded to. But this was --with-wide-int. >> The numbers on my Debian 64 Gtk build are roughly the same (although the >> percentages show up correctly there). > > The --with-wide-int option is a no-op in 64-bit builds, AFAIK. I didn't configure --with-wide-int on that build. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 18:05 ` martin rudalics @ 2015-07-29 21:15 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics ` (2 more replies) 0 siblings, 3 replies; 47+ messages in thread From: Paul Eggert @ 2015-07-29 21:15 UTC (permalink / raw) To: martin rudalics; +Cc: emacs-devel martin rudalics wrote: > But this was --with-wide-int. Weird. Perhaps my guess about your problem was wrong, though there is clearly a bug there in computing percentages, which I'll see if I can fix. I can't reproduce your problem on Fedora 21, configured with gcc 5.1.0 -m32 and --without-imagemagick (ImageMagick breaks the build on my native x86-64 platform due to library incompatibilities) and running emacs -Q. When I tried profiler-start with mem, then letting tooltips show over the modeline, then running profiler-report, I got the following, which seems benign. - command-execute 985,993 99% - call-interactively 985,993 99% - funcall-interactively 984,565 99% - execute-extended-command 984,565 99% - command-execute 949,171 96% - call-interactively 949,171 96% - funcall-interactively 949,163 96% - profiler-report 943,516 95% - profiler-report-memory 943,516 95% profiler-memory-profile 943,516 95% - profiler-start 5,647 0% message 1,535 0% - sit-for 5,662 0% - redisplay 2,532 0% - redisplay_internal (C function) 1,874 0% - tool-bar-make-keymap 1,350 0% - tool-bar-make-keymap-1 1,350 0% - mapcar 1,350 0% - #<compiled 0x22d16eb> 1,350 0% - eval 1,350 0% - find-image 1,350 0% image-search-load-path 1,350 0% kill-this-buffer-enabled-p 524 0% - read-event 534 0% mouse-fixup-help-message 534 0% - byte-code 1,428 0% - read-extended-command 1,428 0% - completing-read 1,428 0% - completing-read-default 1,428 0% - read-from-minibuffer 786 0% - command-execute 128 0% call-interactively 128 0% - timer-event-handler 1,252 0% - apply 1,252 0% - tooltip-timeout 1,252 0% - run-hook-with-args-until-success 1,252 0% - tooltip-help-tips 1,252 0% tooltip-show 1,252 0% ... 0 0% ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 21:15 ` Paul Eggert @ 2015-07-30 6:00 ` martin rudalics 2015-07-30 7:08 ` martin rudalics 2015-08-01 10:49 ` martin rudalics 2 siblings, 0 replies; 47+ messages in thread From: martin rudalics @ 2015-07-30 6:00 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel > I can't reproduce your problem on Fedora 21, configured with gcc 5.1.0 > -m32 and --without-imagemagick (ImageMagick breaks the build on my > native x86-64 platform due to library incompatibilities) and running > emacs -Q. When I tried profiler-start with mem, then letting tooltips > show over the modeline, then running profiler-report, I got the > following, which seems benign. What can one conclude from this: > profiler-memory-profile 943,516 95% That Emacs spent 95% of its allocations in `profiler-memory-profile'? So all overhead went to the profiler itself? This output makes even less sense than mine :-( martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 21:15 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics @ 2015-07-30 7:08 ` martin rudalics 2015-07-30 7:19 ` Paul Eggert 2015-08-01 10:49 ` martin rudalics 2 siblings, 1 reply; 47+ messages in thread From: martin rudalics @ 2015-07-30 7:08 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel > I can't reproduce your problem on Fedora 21, configured with gcc 5.1.0 > -m32 and --without-imagemagick (ImageMagick breaks the build on my > native x86-64 platform due to library incompatibilities) and running > emacs -Q. When I tried profiler-start with mem, then letting tooltips > show over the modeline, then running profiler-report, I got the > following, which seems benign. > > - command-execute 985,993 99% > - call-interactively 985,993 99% > - funcall-interactively 984,565 99% > - execute-extended-command 984,565 99% > - command-execute 949,171 96% > - call-interactively 949,171 96% > - funcall-interactively 949,163 96% > - profiler-report 943,516 95% > - profiler-report-memory 943,516 95% > profiler-memory-profile 943,516 95% > - profiler-start 5,647 0% > message 1,535 0% I was able to reproduce something comparable here on Windows (in a --with-wide-int build): - command-execute 1,572,084 96% - call-interactively 1,572,084 96% - funcall-interactively 1,529,090 94% - execute-extended-command 1,246,749 76% - command-execute 1,071,389 66% - call-interactively 1,071,389 66% - funcall-interactively 1,071,381 66% - profiler-report 1,071,381 66% - profiler-report-memory 1,071,381 66% - profiler-memory-profile 936,852 57% profiler-make-profile 1,560 0% + profiler-report-profile-other-window 134,529 8% + execute-extended-command--shorter 117,026 7% + sit-for 40,850 2% delete-other-windows 274,505 16% + next-line 7,836 0% + byte-code 42,994 2% + redisplay_internal (C function) 41,588 2% + timer-event-handler 5,220 0% + tooltip-show-help 2,088 0% mouse-fixup-help-message 1,499 0% ... 0 0% I suppose you didn't wait until the tooltips really showed up. When I do wait I get something like: - timer-event-handler 15,930,094 28% - apply 15,925,918 28% - tooltip-timeout 15,924,874 28% - run-hook-with-args-until-success 15,924,874 28% - tooltip-help-tips 15,924,874 28% - tooltip-show 15,924,874 28% - x-show-tip 675,546 3% - face-set-after-frame-default 637,389 3% - face-spec-recalc 629,704 3% + face-spec-reset-face 605,688 3% + face-spec-set-2 11,040 0% + face-spec-choose 7,756 0% face-list 4,176 0% jit-lock-context-fontify 1,044 0% + timer-inc-time 3,132 0% + command-execute 1,396,925 7% + redisplay_internal (C function) 660,432 3% + tooltip-show-help 5,220 0% + frame-windows-min-size 1,044 0% + eldoc-schedule-timer 1,044 0% ... 0 0% and after another attempt: + timer-event-handler 16,921,522 -22% + command-execute 1,934,186 9% + redisplay_internal (C function) 1,081,661 5% + #<compiled 0x5afd1f> 189,536 0% + mouse-fixup-help-message 12,528 0% + clear-transient-map 6,820 0% + tooltip-show-help 5,220 0% + clear-transient-map 4,492 0% gui-set-selection 1,044 0% + eldoc-schedule-timer 1,044 0% ... 0 0% So the percentages apparently get wrong over time, maybe due to some overflow. But I'm quite sure that the backtrace unifying algorithm (`profiler-calltree-build-unified') is inherently broken. I'm not sure though whether there's any chance to fix that in the first place. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 7:08 ` martin rudalics @ 2015-07-30 7:19 ` Paul Eggert 2015-07-30 9:05 ` martin rudalics 0 siblings, 1 reply; 47+ messages in thread From: Paul Eggert @ 2015-07-30 7:19 UTC (permalink / raw) To: martin rudalics; +Cc: emacs-devel martin rudalics wrote: > > I suppose you didn't wait until the tooltips really showed up. No, I waited. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 7:19 ` Paul Eggert @ 2015-07-30 9:05 ` martin rudalics 2015-07-30 16:36 ` Eli Zaretskii 2016-03-06 9:21 ` martin rudalics 0 siblings, 2 replies; 47+ messages in thread From: martin rudalics @ 2015-07-30 9:05 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel >> I suppose you didn't wait until the tooltips really showed up. > > No, I waited. This is hopeless. Evaluating (let ((c cons-cells-consed) (v vector-cells-consed)) (tooltip-show "foobar") (message "cons: %s vector: %s" (- cons-cells-consed c) (- vector-cells-consed v))) gives here "cons: 58696 vector: 9443". Evaluating (let ((c cons-cells-consed) (v vector-cells-consed)) (x-show-tip "foobar") (message "cons: %s vector: %s" (- cons-cells-consed c) (- vector-cells-consed v))) gets me "cons: 58649 vector: 9443", so the remainder of `tooltip-show' is responsible for just 47 additional conses. We can ignore that. I suppose most of the overhead goes to x_create_tip_frame - a normal `make-frame' here gets me "cons: 37464 vector: 33018". Calculating the tooltip position doesn't seem to add much overhead. So the only way to fix this is to hide tooltip frames instead of deleting and re-creating them. If this can be done properly. And - ceterum censeo - the profiler should be fixed. In its current form it's more confusing than enlightening. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 9:05 ` martin rudalics @ 2015-07-30 16:36 ` Eli Zaretskii 2015-07-30 19:53 ` martin rudalics 2016-03-06 9:21 ` martin rudalics 1 sibling, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-07-30 16:36 UTC (permalink / raw) To: martin rudalics; +Cc: eggert, emacs-devel > Date: Thu, 30 Jul 2015 11:05:47 +0200 > From: martin rudalics <rudalics@gmx.at> > Cc: emacs-devel@gnu.org > > >> I suppose you didn't wait until the tooltips really showed up. > > > > No, I waited. > > This is hopeless. Evaluating > > (let ((c cons-cells-consed) > (v vector-cells-consed)) > (tooltip-show "foobar") > (message "cons: %s vector: %s" > (- cons-cells-consed c) > (- vector-cells-consed v))) > > gives here "cons: 58696 vector: 9443". Evaluating > > (let ((c cons-cells-consed) > (v vector-cells-consed)) > (x-show-tip "foobar") > (message "cons: %s vector: %s" > (- cons-cells-consed c) > (- vector-cells-consed v))) > > gets me "cons: 58649 vector: 9443", so the remainder of `tooltip-show' > is responsible for just 47 additional conses. We can ignore that. > > I suppose most of the overhead goes to x_create_tip_frame - a normal > `make-frame' here gets me "cons: 37464 vector: 33018". Calculating the > tooltip position doesn't seem to add much overhead. So the only way to > fix this is to hide tooltip frames instead of deleting and re-creating > them. If this can be done properly. According to my testing, over 80% of consing happens inside face-spec-recalc which is called by face-set-after-frame-default on all the known faces. I guess your numbers above are from a non-Q session with a lot of additional faces, because in "emacs -Q" I get "only" about 15000 conses from a single tool-tip. So maybe one other idea is to skip faces that are obviously unused in tip frames. Or maybe we should just make gc-cons-threshold higher ;-) This is not new, btw: Emacs 24.1 exhibits approximately the same numbers, Emacs 23 conses only two thirds of that, and Emacs 22.3 one third. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 16:36 ` Eli Zaretskii @ 2015-07-30 19:53 ` martin rudalics 2015-07-30 23:09 ` Stefan Monnier 2015-07-31 6:47 ` Eli Zaretskii 0 siblings, 2 replies; 47+ messages in thread From: martin rudalics @ 2015-07-30 19:53 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, emacs-devel > According to my testing, over 80% of consing happens inside > face-spec-recalc which is called by face-set-after-frame-default on > all the known faces. What's so special about `face-spec-recalc'? > I guess your numbers above are from a non-Q session with a lot of > additional faces, because in "emacs -Q" I get "only" about 15000 > conses from a single tool-tip. Correct. emacs -Q gets me 9324 conses and 3546 vectors. > So maybe one other idea is to skip faces that are obviously unused in > tip frames. Hmmm... Maybe I should reduce the number of my faces. They seem to cause a combinatory explosion. > Or maybe we should just make gc-cons-threshold higher ;-) I have a problem with overlays or visibility specification which occasionally causes an inexplicable 5-10 secs delay here. Then I started to display the number of GC cycles in the modeline to find out whether GC is responsible and accidentally noticed that tooltips cause more GCs than anything else. Funnily there's never a disruptive effect due to GC. > This is not new, btw: Emacs 24.1 exhibits approximately the same > numbers, Emacs 23 conses only two thirds of that, and Emacs 22.3 one > third. So with Emacs 22.3 you had one GC every ninth tooltip? martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 19:53 ` martin rudalics @ 2015-07-30 23:09 ` Stefan Monnier 2015-07-30 23:33 ` Drew Adams 2015-07-31 6:47 ` Eli Zaretskii 1 sibling, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-07-30 23:09 UTC (permalink / raw) To: martin rudalics; +Cc: Eli Zaretskii, eggert, emacs-devel >> So maybe one other idea is to skip faces that are obviously unused in >> tip frames. > Hmmm... Maybe I should reduce the number of my faces. They seem to > cause a combinatory explosion. I guess we (c|sh)ould optimize frame creation algorithmically (e.g. making face-spec-recalc lazy so that the faces are "recalc"ulated only if needed). I'm a heavy user of frames, and I've never been satisfied by the time needed to create a new frame, so I'd be happy to see some improvement in this area. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: Tooltips GC overhead 2015-07-30 23:09 ` Stefan Monnier @ 2015-07-30 23:33 ` Drew Adams 2015-07-31 6:48 ` Eli Zaretskii 0 siblings, 1 reply; 47+ messages in thread From: Drew Adams @ 2015-07-30 23:33 UTC (permalink / raw) To: Stefan Monnier, martin rudalics; +Cc: Eli Zaretskii, eggert, emacs-devel > I'm a heavy user of frames, and I've never been satisfied by the time > needed to create a new frame, so I'd be happy to see some improvement in > this area. I too use frames heavily. And I remember that frame creation could be slow on UNIX - back in the Dark Ages, at least. But AFAICT it is nearly instantaneous on MS Windows. Maybe something can be done at a lower level to speed things up on other platforms? ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 23:33 ` Drew Adams @ 2015-07-31 6:48 ` Eli Zaretskii 0 siblings, 0 replies; 47+ messages in thread From: Eli Zaretskii @ 2015-07-31 6:48 UTC (permalink / raw) To: Drew Adams; +Cc: rudalics, eggert, monnier, emacs-devel > Date: Thu, 30 Jul 2015 16:33:33 -0700 (PDT) > From: Drew Adams <drew.adams@oracle.com> > Cc: Eli Zaretskii <eliz@gnu.org>, eggert@cs.ucla.edu, emacs-devel@gnu.org > > > I'm a heavy user of frames, and I've never been satisfied by the time > > needed to create a new frame, so I'd be happy to see some improvement in > > this area. > > I too use frames heavily. And I remember that frame creation > could be slow on UNIX - back in the Dark Ages, at least. But > AFAICT it is nearly instantaneous on MS Windows. Maybe > something can be done at a lower level to speed things up on > other platforms? The code in question runs on all platforms for GUI frames, so it is as slow on Unix as it is fast on Windows. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 19:53 ` martin rudalics 2015-07-30 23:09 ` Stefan Monnier @ 2015-07-31 6:47 ` Eli Zaretskii 1 sibling, 0 replies; 47+ messages in thread From: Eli Zaretskii @ 2015-07-31 6:47 UTC (permalink / raw) To: martin rudalics; +Cc: eggert, emacs-devel > Date: Thu, 30 Jul 2015 21:53:47 +0200 > From: martin rudalics <rudalics@gmx.at> > CC: eggert@cs.ucla.edu, emacs-devel@gnu.org > > > According to my testing, over 80% of consing happens inside > > face-spec-recalc which is called by face-set-after-frame-default on > > all the known faces. > > What's so special about `face-spec-recalc'? It loops over all the faces; for each one of them, it first calls face-spec-reset-face, which loops over all the face attributes, then calls face-spec-set-2, which again sets face attributes. If you look at face-spec-reset-face, you'll see that it, too, conses quite a lot. > > This is not new, btw: Emacs 24.1 exhibits approximately the same > > numbers, Emacs 23 conses only two thirds of that, and Emacs 22.3 one > > third. > > So with Emacs 22.3 you had one GC every ninth tooltip? I get more, but not because of faces. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-30 9:05 ` martin rudalics 2015-07-30 16:36 ` Eli Zaretskii @ 2016-03-06 9:21 ` martin rudalics 2016-03-08 8:02 ` martin rudalics 1 sibling, 1 reply; 47+ messages in thread From: martin rudalics @ 2016-03-06 9:21 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1734 bytes --] > I suppose most of the overhead goes to x_create_tip_frame - a normal > `make-frame' here gets me "cons: 37464 vector: 33018". Calculating the > tooltip position doesn't seem to add much overhead. So the only way to > fix this is to hide tooltip frames instead of deleting and re-creating > them. If this can be done properly. I now tweaked the code to do that. If nobody objects I intend to apply the attached patch in the next days. Optionally reuse tooltip frames instead of deleting/recreating them. * src/frame.c (tooltip_reuse_hidden_frame): New option. * src/w32fns.c (x_create_tip_frame): Remove argument TEXT. Fix handling of dividers. (x_hide_tip): New function. (Fx_show_tip): Try to reuse old tooltip frame when `tooltip-reuse-hidden-frame' is non-nil and frame parameters have not changed. Insert STRING here instead of passing it to x_create_tip_frame. Compute size of tooltip window with Fwindow_text_pixel_size. Obey Vw32_tooltip_extra_pixels when padding tooltip window. (Fx_hide_tip): Call x_hide_tip. (Vw32_tooltip_extra_pixels): New variable. * src/xdisp.c (Fwindow_text_pixel_size): Don't return negative y value. Fix doc-string. * src/xfns.c (x_create_tip_frame): Remove argument TEXT. Call make_frame with mini_p argument false. (x_hide_tip): New function. (Fx_show_tip): Try to reuse old tooltip frame when `tooltip-reuse-hidden-frame' is non-nil and frame parameters have not changed. Insert STRING here instead of passing it to x_create_tip_frame. Compute size of tooltip window with Fwindow_text_pixel_size. Obey Vw32_tooltip_extra_pixels when padding tooltip window. (Fx_hide_tip): Call x_hide_tip. * lisp/cus-start.el (tooltip-reuse-hidden-frame): Add customization entry. martin [-- Attachment #2: lightweight-tooltips.diff --] [-- Type: text/plain, Size: 48374 bytes --] diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 5be61ce..1c10bf7 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -311,6 +311,7 @@ minibuffer-prompt-properties--setter (const :tag "Always" t) (repeat (symbol :tag "Parameter"))) "25.1") + (tooltip-reuse-hidden-frame tooltip boolean "26.1") ;; fringe.c (overflow-newline-into-fringe fringe boolean) ;; image.c diff --git a/src/frame.c b/src/frame.c index fd9f3ce..d536468 100644 --- a/src/frame.c +++ b/src/frame.c @@ -5262,6 +5262,21 @@ The function `frame--size-history' displays the value of this variable in a more readable form. */); frame_size_history = Qnil; + DEFVAR_BOOL ("tooltip-reuse-hidden-frame", tooltip_reuse_hidden_frame, + doc: /* Non-nil means reuse hidden tooltip frames. +When this is nil, delete a tooltip frame when hiding the associated +tooltip. When this is non-nil, make the tooltip frame invisible only, +so it can be reused when the next tooltip is shown. + +Setting this to non-nil may drastically reduce the consing overhead +incurred by creating new tooltip frames. However, a value of non-nil +means also that intermittent changes of faces or `default-frame-alist' +are not applied when showing a tooltip in a reused frame. + +This variable is effective only with the X toolkit (and there only when +Gtk+ tooltips are not used) and on Windows. */); + tooltip_reuse_hidden_frame = false; + staticpro (&Vframe_list); defsubr (&Sframep); diff --git a/src/w32fns.c b/src/w32fns.c index 10c8af7..2dfbdc3 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -6416,8 +6416,6 @@ no value of TYPE (always string in the MS Windows case). */) Tool tips ***********************************************************************/ -static Lisp_Object x_create_tip_frame (struct w32_display_info *, - Lisp_Object, Lisp_Object); static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, Lisp_Object, int, int, int *, int *); @@ -6452,8 +6450,7 @@ unwind_create_tip_frame (Lisp_Object frame) /* Create a frame for a tooltip on the display described by DPYINFO. - PARMS is a list of frame parameters. TEXT is the string to - display in the tip frame. Value is the frame. + PARMS is a list of frame parameters. Value is the frame. Note that functions called here, esp. x_default_parameter can signal errors, for instance when a specified color name is @@ -6461,8 +6458,7 @@ unwind_create_tip_frame (Lisp_Object frame) when this happens. */ static Lisp_Object -x_create_tip_frame (struct w32_display_info *dpyinfo, - Lisp_Object parms, Lisp_Object text) +x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) { struct frame *f; Lisp_Object frame; @@ -6471,8 +6467,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, ptrdiff_t count = SPECPDL_INDEX (); struct kboard *kb; bool face_change_before = face_change; - Lisp_Object buffer; - struct buffer *old_buffer; int x_width = 0, x_height = 0; /* Use this general default value to start with until we know if @@ -6496,23 +6490,9 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, frame = Qnil; /* Make a frame without minibuffer nor mode-line. */ f = make_frame (false); - f->wants_modeline = 0; + f->wants_modeline = false; XSETFRAME (frame, f); - AUTO_STRING (tip, " *tip*"); - buffer = Fget_buffer_create (tip); - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (FRAME_ROOT_WINDOW (f), buffer, false, false); - old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (buffer)); - bset_truncate_lines (current_buffer, Qnil); - specbind (Qinhibit_read_only, Qt); - specbind (Qinhibit_modification_hooks, Qt); - Ferase_buffer (); - Finsert (1, &text); - set_buffer_internal_1 (old_buffer); - record_unwind_protect (unwind_create_tip_frame, frame); /* By setting the output method, we're essentially saying that @@ -6546,7 +6526,7 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, { fset_name (f, name); f->explicit_name = true; - /* use the frame's title when getting resources for this frame. */ + /* Use the frame's title when getting resources for this frame. */ specbind (Qx_resource_name, name); } @@ -6576,14 +6556,10 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, parms = Fcons (Fcons (Qinternal_border_width, value), parms); } + x_default_parameter (f, parms, Qinternal_border_width, make_number (1), "internalBorderWidth", "internalBorderWidth", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qright_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qbottom_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - /* Also do the stuff which must be set before the window exists. */ x_default_parameter (f, parms, Qforeground_color, build_string ("black"), "foreground", "Foreground", RES_TYPE_STRING); @@ -6610,6 +6586,9 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, f->fringe_cols = 0; f->left_fringe_width = 0; f->right_fringe_width = 0; + /* No dividers on tip frame. */ + f->right_divider_width = 0; + f->bottom_divider_width = 0; block_input (); my_create_tip_window (f); @@ -6636,7 +6615,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, SET_FRAME_LINES (f, 0); adjust_frame_size (f, width * FRAME_COLUMN_WIDTH (f), height * FRAME_LINE_HEIGHT (f), 0, true, Qtip_frame); - /* Add `tooltip' frame parameter's default value. */ if (NILP (Fframe_parameter (frame, Qtooltip))) Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil)); @@ -6654,8 +6632,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object fg = Fframe_parameter (frame, Qforeground_color); Lisp_Object colors = Qnil; - /* Set tip_frame here, so that */ - tip_frame = frame; call2 (Qface_set_after_frame_default, frame, Qnil); if (!EQ (bg, Fframe_parameter (frame, Qbackground_color))) @@ -6787,6 +6763,48 @@ compute_tip_xy (struct frame *f, *root_x = min_x; } +/* Hide tooltip. Delete its frame if DELETE is true. */ +static Lisp_Object +x_hide_tip (bool delete) +{ + if (!NILP (tip_timer)) + { + call1 (Qcancel_timer, tip_timer); + tip_timer = Qnil; + } + + if (NILP (tip_frame) + || (!delete && FRAMEP (tip_frame) + && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + return Qnil; + else + { + ptrdiff_t count; + Lisp_Object was_open = Qnil; + + count = SPECPDL_INDEX (); + specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); + + if (FRAMEP (tip_frame)) + { + if (delete) + { + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + x_make_frame_invisible (XFRAME (tip_frame)); + + was_open = Qt; + } + else + tip_frame = Qnil; + + return unbind_to (count, was_open); + } +} + DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, doc: /* Show STRING in a \"tooltip\" window on frame FRAME. @@ -6820,15 +6838,16 @@ A tooltip's maximum size is specified by `x-max-tooltip-size'. Text larger than the specified size is clipped. */) (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) { - struct frame *f; + struct frame *f, *tip_f; struct window *w; int root_x, root_y; struct buffer *old_buffer; struct text_pos pos; int i, width, height; - bool seen_reversed_p; int old_windows_or_buffers_changed = windows_or_buffers_changed; ptrdiff_t count = SPECPDL_INDEX (); + ptrdiff_t count_1; + Lisp_Object window, size; specbind (Qinhibit_redisplay, Qt); @@ -6852,91 +6871,155 @@ Text larger than the specified size is clipped. */) if (NILP (last_show_tip_args)) last_show_tip_args = Fmake_vector (make_number (3), Qnil); - if (!NILP (tip_frame)) + if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) { Lisp_Object last_string = AREF (last_show_tip_args, 0); Lisp_Object last_frame = AREF (last_show_tip_args, 1); Lisp_Object last_parms = AREF (last_show_tip_args, 2); - if (EQ (frame, last_frame) - && !NILP (Fequal (last_string, string)) + if (FRAME_VISIBLE_P (XFRAME (tip_frame)) + && EQ (frame, last_frame) + && !NILP (Fequal_including_properties (last_string, string)) && !NILP (Fequal (last_parms, parms))) { - struct frame *f = XFRAME (tip_frame); - /* Only DX and DY have changed. */ + tip_f = XFRAME (tip_frame); if (!NILP (tip_timer)) { Lisp_Object timer = tip_timer; + tip_timer = Qnil; call1 (Qcancel_timer, timer); } block_input (); - compute_tip_xy (f, parms, dx, dy, FRAME_PIXEL_WIDTH (f), - FRAME_PIXEL_HEIGHT (f), &root_x, &root_y); + compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f), + FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y); /* Put tooltip in topmost group and in position. */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST, root_x, root_y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Ensure tooltip is on top of other topmost windows (eg menus). */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + /* Let redisplay know that we have made the frame visible already. */ + SET_FRAME_VISIBLE (tip_f, 1); + ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE); unblock_input (); + goto start_timer; } - } + else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame)) + { + bool delete = false; + Lisp_Object tail, elt, parm, last; - /* Hide a previous tip, if any. */ - Fx_hide_tip (); + /* Check if every parameter in PARMS has the same value in + last_parms. This may destruct last_parms which, however, + will be recreated below. */ + for (tail = parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + /* The left, top, right and bottom parameters are handled + by compute_tip_xy so they can be ignored here. */ + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) + && !EQ (parm, Qright) && !EQ (parm, Qbottom)) + { + last = Fassq (parm, last_parms); + if (NILP (Fequal (Fcdr (elt), Fcdr (last)))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } + + /* Now check if there's a parameter left in last_parms with a + non-nil value. */ + for (tail = last_parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) + && !EQ (parm, Qbottom) && !NILP (Fcdr (elt))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + } + + x_hide_tip (delete); + } + else + x_hide_tip (true); + } + else + x_hide_tip (true); ASET (last_show_tip_args, 0, string); ASET (last_show_tip_args, 1, frame); ASET (last_show_tip_args, 2, parms); - /* Add default values to frame parameters. */ - if (NILP (Fassq (Qname, parms))) - parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); - if (NILP (Fassq (Qinternal_border_width, parms))) - parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); - if (NILP (Fassq (Qright_divider_width, parms))) - parms = Fcons (Fcons (Qright_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qbottom_divider_width, parms))) - parms = Fcons (Fcons (Qbottom_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qborder_width, parms))) - parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); - if (NILP (Fassq (Qborder_color, parms))) - parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); - if (NILP (Fassq (Qbackground_color, parms))) - parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), - parms); - /* Block input until the tip has been fully drawn, to avoid crashes when drawing tips in menus. */ block_input (); - /* Create a frame for the tooltip, and record it in the global - variable tip_frame. */ - frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, string); - f = XFRAME (frame); + if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) + { + /* Add default values to frame parameters. */ + if (NILP (Fassq (Qname, parms))) + parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); + if (NILP (Fassq (Qinternal_border_width, parms))) + parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); + if (NILP (Fassq (Qborder_width, parms))) + parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); + if (NILP (Fassq (Qborder_color, parms))) + parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); + if (NILP (Fassq (Qbackground_color, parms))) + parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), + parms); + + /* Create a frame for the tooltip, and record it in the global + variable tip_frame. */ + if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms))) + { + /* Creating the tip frame failed. */ + unblock_input (); + return unbind_to (count, Qnil); + } + } + + tip_f = XFRAME (tip_frame); + window = FRAME_ROOT_WINDOW (tip_f); + AUTO_STRING (tip, " *tip*"); + set_window_buffer (window, Fget_buffer_create (tip), false, false); + w = XWINDOW (window); + w->pseudo_window_p = true; - /* Set up the frame's root window. */ - w = XWINDOW (FRAME_ROOT_WINDOW (f)); + /* Set up the frame's root window. Note: The following code does not + try to size the window or its frame correctly. Its only purpose is + to make the subsequent text size calculations work. The right + sizes should get installed when the toolkit gets back to us. */ w->left_col = 0; w->top_line = 0; w->pixel_left = 0; w->pixel_top = 0; if (CONSP (Vx_max_tooltip_size) - && INTEGERP (XCAR (Vx_max_tooltip_size)) - && XINT (XCAR (Vx_max_tooltip_size)) > 0 - && INTEGERP (XCDR (Vx_max_tooltip_size)) - && XINT (XCDR (Vx_max_tooltip_size)) > 0) + && RANGED_INTEGERP (1, XCAR (Vx_max_tooltip_size), INT_MAX) + && RANGED_INTEGERP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) { w->total_cols = XFASTINT (XCAR (Vx_max_tooltip_size)); w->total_lines = XFASTINT (XCDR (Vx_max_tooltip_size)); @@ -6947,164 +7030,71 @@ Text larger than the specified size is clipped. */) w->total_lines = 40; } - w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (f); - w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (f); - - FRAME_TOTAL_COLS (f) = WINDOW_TOTAL_COLS (w); - adjust_frame_glyphs (f); - w->pseudo_window_p = true; + w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f); + w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f); + FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w); + adjust_frame_glyphs (tip_f); - /* Display the tooltip text in a temporary buffer. */ + /* Insert STRING into the root window's buffer and fit the frame to + the buffer. */ + count_1 = SPECPDL_INDEX (); old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (XWINDOW (FRAME_ROOT_WINDOW (f))->contents)); + set_buffer_internal_1 (XBUFFER (w->contents)); bset_truncate_lines (current_buffer, Qnil); + specbind (Qinhibit_read_only, Qt); + specbind (Qinhibit_modification_hooks, Qt); + specbind (Qinhibit_point_motion_hooks, Qt); + Ferase_buffer (); + Finsert (1, &string); clear_glyph_matrix (w->desired_matrix); clear_glyph_matrix (w->current_matrix); SET_TEXT_POS (pos, BEGV, BEGV_BYTE); - try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); - - /* Compute width and height of the tooltip. */ - width = height = 0; - seen_reversed_p = false; - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - /* Stop at the first empty row at the end. */ - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - - /* Let the row go over the full width of the frame. */ - row->full_width_p = true; - - row_width = row->pixel_width; - if (row->used[TEXT_AREA]) - { - if (!row->reversed_p) - { - /* There's a glyph at the end of rows that is used to - place the cursor there. Don't include the width of - this glyph. */ - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - else - { - /* There could be a stretch glyph at the beginning of R2L - rows that is produced by extend_face_to_end_of_line. - Don't count that glyph. */ - struct glyph *g = row->glyphs[TEXT_AREA]; - - if (g->type == STRETCH_GLYPH && NILP (g->object)) - { - row_width -= g->pixel_width; - seen_reversed_p = true; - } - } - } - - height += row->height; - width = max (width, row_width); - } - - /* If we've seen partial-length R2L rows, we need to re-adjust the - tool-tip frame width and redisplay it again, to avoid over-wide - tips due to the stretch glyph that extends R2L lines to full - width of the frame. */ - if (seen_reversed_p) - { - /* PXW: Why do we do the pixel-to-cols conversion only if - seen_reversed_p holds? Don't we have to set other fields of - the window/frame structure? - - w->total_cols and FRAME_TOTAL_COLS want the width in columns, - not in pixels. */ - w->pixel_width = width; - width /= WINDOW_FRAME_COLUMN_WIDTH (w); - w->total_cols = width; - FRAME_TOTAL_COLS (f) = width; - SET_FRAME_WIDTH (f, width); - adjust_frame_glyphs (f); - w->pseudo_window_p = 1; - clear_glyph_matrix (w->desired_matrix); - clear_glyph_matrix (w->current_matrix); - try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); - width = height = 0; - /* Recompute width and height of the tooltip. */ - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - row->full_width_p = true; - row_width = row->pixel_width; - if (row->used[TEXT_AREA] && !row->reversed_p) - { - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - - height += row->height; - width = max (width, row_width); - } - } - - /* Add the frame's internal border to the width and height the w32 - window should have. */ - height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - - /* Move the tooltip window where the mouse pointer is. Resize and - show it. - - PXW: This should use the frame's pixel coordinates. */ - compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y); - + try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + /* Calculate size of tooltip window. */ + size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, + make_number (w->pixel_height), Qnil); + /* Add the frame's internal border to calculated size. */ + width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + /* Calculate position of tooltip frame. */ + compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); + + /* Show tooltip frame. */ { - /* Adjust Window size to take border into account. */ RECT rect; + int pad = (NUMBERP (Vw32_tooltip_extra_pixels) + ? max (0, XINT (Vw32_tooltip_extra_pixels)) + : FRAME_COLUMN_WIDTH (tip_f)); + rect.left = rect.top = 0; rect.right = width; rect.bottom = height; - AdjustWindowRect (&rect, f->output_data.w32->dwStyle, false); - - /* Position and size tooltip, and put it in the topmost group. - The add-on of FRAME_COLUMN_WIDTH to the 5th argument is a - peculiarity of w32 display: without it, some fonts cause the - last character of the tip to be truncated or wrapped around to - the next line. */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST, + AdjustWindowRect (&rect, tip_f->output_data.w32->dwStyle, + FRAME_EXTERNAL_MENU_BAR (tip_f)); + + /* Position and size tooltip and put it in the topmost group. */ + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST, root_x, root_y, - rect.right - rect.left + FRAME_COLUMN_WIDTH (f), + rect.right - rect.left + pad, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Ensure tooltip is on top of other topmost windows (eg menus). */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Let redisplay know that we have made the frame visible already. */ - SET_FRAME_VISIBLE (f, 1); + SET_FRAME_VISIBLE (tip_f, 1); - ShowWindow (FRAME_W32_WINDOW (f), SW_SHOWNOACTIVATE); + ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE); } - /* Draw into the window. */ w->must_be_updated_p = true; update_single_window (w); - - unblock_input (); - - /* Restore original current buffer. */ set_buffer_internal_1 (old_buffer); + unbind_to (count_1, Qnil); + unblock_input (); windows_or_buffers_changed = old_windows_or_buffers_changed; start_timer: @@ -7121,31 +7111,7 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, Value is t if tooltip was open, nil otherwise. */) (void) { - ptrdiff_t count; - Lisp_Object deleted, frame, timer; - - /* Return quickly if nothing to do. */ - if (NILP (tip_timer) && NILP (tip_frame)) - return Qnil; - - frame = tip_frame; - timer = tip_timer; - tip_frame = tip_timer = deleted = Qnil; - - count = SPECPDL_INDEX (); - specbind (Qinhibit_redisplay, Qt); - specbind (Qinhibit_quit, Qt); - - if (!NILP (timer)) - call1 (Qcancel_timer, timer); - - if (FRAMEP (frame)) - { - delete_frame (frame, Qnil); - deleted = Qt; - } - - return unbind_to (count, deleted); + return x_hide_tip (!tooltip_reuse_hidden_frame); } \f /*********************************************************************** @@ -9745,6 +9711,7 @@ syms_of_w32fns (void) DEFSYM (Qmm_size, "mm-size"); DEFSYM (Qframes, "frames"); DEFSYM (Qtip_frame, "tip-frame"); + DEFSYM (Qassq_delete_all, "assq-delete-all"); DEFSYM (Qunicode_sip, "unicode-sip"); #if defined WINDOWSNT && !defined HAVE_DBUS DEFSYM (QCicon, ":icon"); @@ -10057,6 +10024,18 @@ Default is nil. This variable has effect only on Windows Vista and later. */); w32_disable_new_uniscribe_apis = 0; + DEFVAR_LISP ("w32-tooltip-extra-pixels", + Vw32_tooltip_extra_pixels, + doc: /* Number of pixels added after tooltip text. +On Windows some fonts may cause the last character of a tooltip be +truncated or wrapped around to the next line. Adding some extra space +at the end of the toooltip works around this problem. + +This variable specifies the number of pixels that shall be added. The +default value t means to add the width of one canonical character of the +tip frame. */); + Vw32_tooltip_extra_pixels = Qt; + #if 0 /* TODO: Port to W32 */ defsubr (&Sx_change_window_property); defsubr (&Sx_delete_window_property); diff --git a/src/xdisp.c b/src/xdisp.c index b9d496e..5b96144 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -9794,26 +9794,28 @@ the maximum pixel-height of all text lines. The optional argument FROM, if non-nil, specifies the first text position and defaults to the minimum accessible position of the buffer. -If FROM is t, use the minimum accessible position that is not a newline -character. TO, if non-nil, specifies the last text position and +If FROM is t, use the minimum accessible position that starts a +non-empty line. TO, if non-nil, specifies the last text position and defaults to the maximum accessible position of the buffer. If TO is t, -use the maximum accessible position that is not a newline character. +use the maximum accessible position that ends a non-empty line. The optional argument X-LIMIT, if non-nil, specifies the maximum text width that can be returned. X-LIMIT nil or omitted, means to use the -pixel-width of WINDOW's body; use this if you do not intend to change -the width of WINDOW. Use the maximum width WINDOW may assume if you -intend to change WINDOW's width. In any case, text whose x-coordinate -is beyond X-LIMIT is ignored. Since calculating the width of long lines -can take some time, it's always a good idea to make this argument as -small as possible; in particular, if the buffer contains long lines that -shall be truncated anyway. +pixel-width of WINDOW's body; use this if you want to know how high +WINDOW should be become in order to fit all of its buffer's text with +the width of WINDOW unaltered. Use the maximum width WINDOW may assume +if you intend to change WINDOW's width. In any case, text whose +x-coordinate is beyond X-LIMIT is ignored. Since calculating the width +of long lines can take some time, it's always a good idea to make this +argument as small as possible; in particular, if the buffer contains +long lines that shall be truncated anyway. The optional argument Y-LIMIT, if non-nil, specifies the maximum text -height that can be returned. Text lines whose y-coordinate is beyond -Y-LIMIT are ignored. Since calculating the text height of a large -buffer can take some time, it makes sense to specify this argument if -the size of the buffer is unknown. +height (exluding the height of the mode- or header-line, if any) that +can be returned. Text lines whose y-coordinate is beyond Y-LIMIT are +ignored. Since calculating the text height of a large buffer can take +some time, it makes sense to specify this argument if the size of the +buffer is large or unknown. Optional argument MODE-AND-HEADER-LINE nil or omitted means do not include the height of the mode- or header-line of WINDOW in the return @@ -9831,7 +9833,7 @@ include the height of both, if present, in the return value. */) ptrdiff_t start, end, pos; struct text_pos startp; void *itdata = NULL; - int c, max_y = -1, x = 0, y = 0; + int c, max_x = 0, max_y = 0, x = 0, y = 0; CHECK_BUFFER (buffer); b = XBUFFER (buffer); @@ -9876,11 +9878,13 @@ include the height of both, if present, in the return value. */) end = max (start, min (XINT (to), ZV)); } - if (!NILP (y_limit)) - { - CHECK_NUMBER (y_limit); - max_y = min (XINT (y_limit), INT_MAX); - } + if (!NILP (x_limit) && RANGED_INTEGERP (0, x_limit, INT_MAX)) + max_x = XINT (x_limit); + + if (NILP (y_limit)) + max_y = INT_MAX; + else if (RANGED_INTEGERP (0, y_limit, INT_MAX)) + max_y = XINT (y_limit); itdata = bidi_shelve_cache (); SET_TEXT_POS (startp, start, CHAR_TO_BYTE (start)); @@ -9890,27 +9894,30 @@ include the height of both, if present, in the return value. */) x = move_it_to (&it, end, -1, max_y, -1, MOVE_TO_POS | MOVE_TO_Y); else { - CHECK_NUMBER (x_limit); - it.last_visible_x = min (XINT (x_limit), INFINITY); + it.last_visible_x = max_x; /* Actually, we never want move_it_to stop at to_x. But to make sure that move_it_in_display_line_to always moves far enough, - we set it to INT_MAX and specify MOVE_TO_X. */ - x = move_it_to (&it, end, INT_MAX, max_y, -1, - MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y); + we set it to INT_MAX and specify MOVE_TO_X. Also bound width + value by X-LIMIT. */ + x = min (move_it_to (&it, end, INT_MAX, max_y, -1, + MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y), + max_x); } - y = it.current_y + it.max_ascent + it.max_descent; + /* Subtract height of header-line which was counted automatically by + start_display. */ + y = min (it.current_y + it.max_ascent + it.max_descent + - WINDOW_HEADER_LINE_HEIGHT (w), + max_y); - if (!EQ (mode_and_header_line, Qheader_line) - && !EQ (mode_and_header_line, Qt)) - /* Do not count the header-line which was counted automatically by - start_display. */ - y = y - WINDOW_HEADER_LINE_HEIGHT (w); + if (EQ (mode_and_header_line, Qheader_line) + || EQ (mode_and_header_line, Qt)) + /* Re-add height of header-line as requested. */ + y = y + WINDOW_HEADER_LINE_HEIGHT (w); if (EQ (mode_and_header_line, Qmode_line) || EQ (mode_and_header_line, Qt)) - /* Do count the mode-line which is not included automatically by - start_display. */ + /* Add height of mode-line as requested. */ y = y + WINDOW_MODE_LINE_HEIGHT (w); bidi_unshelve_cache (itdata, false); diff --git a/src/xfns.c b/src/xfns.c index 2a50a5a..c1ce1b7 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -5303,8 +5303,6 @@ no value of TYPE (always string in the MS Windows case). */) Tool tips ***********************************************************************/ -static Lisp_Object x_create_tip_frame (struct x_display_info *, - Lisp_Object, Lisp_Object); static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, Lisp_Object, int, int, int *, int *); @@ -5348,9 +5346,7 @@ unwind_create_tip_frame (Lisp_Object frame) when this happens. */ static Lisp_Object -x_create_tip_frame (struct x_display_info *dpyinfo, - Lisp_Object parms, - Lisp_Object text) +x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) { struct frame *f; Lisp_Object frame; @@ -5359,7 +5355,6 @@ x_create_tip_frame (struct x_display_info *dpyinfo, ptrdiff_t count = SPECPDL_INDEX (); bool face_change_before = face_change; Lisp_Object buffer; - struct buffer *old_buffer; int x_width = 0, x_height = 0; if (!dpyinfo->terminal->name) @@ -5375,23 +5370,9 @@ x_create_tip_frame (struct x_display_info *dpyinfo, error ("Invalid frame name--not a string or nil"); frame = Qnil; - f = make_frame (true); + f = make_frame (false); + f->wants_modeline = false; XSETFRAME (frame, f); - - AUTO_STRING (tip, " *tip*"); - buffer = Fget_buffer_create (tip); - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (FRAME_ROOT_WINDOW (f), buffer, false, false); - old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (buffer)); - bset_truncate_lines (current_buffer, Qnil); - specbind (Qinhibit_read_only, Qt); - specbind (Qinhibit_modification_hooks, Qt); - Ferase_buffer (); - Finsert (1, &text); - set_buffer_internal_1 (old_buffer); - record_unwind_protect (unwind_create_tip_frame, frame); f->terminal = dpyinfo->terminal; @@ -5633,8 +5614,6 @@ x_create_tip_frame (struct x_display_info *dpyinfo, { Lisp_Object bg = Fframe_parameter (frame, Qbackground_color); - /* Set tip_frame here, so that */ - tip_frame = frame; call2 (Qface_set_after_frame_default, frame, Qnil); if (!EQ (bg, Fframe_parameter (frame, Qbackground_color))) @@ -5773,6 +5752,85 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object } +/* Hide tooltip. Delete its frame if DELETE is true. */ +static Lisp_Object +x_hide_tip (bool delete) +{ + if (!NILP (tip_timer)) + { + call1 (Qcancel_timer, tip_timer); + tip_timer = Qnil; + } + + + if (NILP (tip_frame) + || (!delete && FRAMEP (tip_frame) + && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + return Qnil; + else + { + ptrdiff_t count; + Lisp_Object was_open = Qnil; + + count = SPECPDL_INDEX (); + specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); + +#ifdef USE_GTK + { + /* When using system tooltip, tip_frame is the Emacs frame on + which the tip is shown. */ + struct frame *f = XFRAME (tip_frame); + + if (FRAME_LIVE_P (f) && xg_hide_tooltip (f)) + { + tip_frame = Qnil; + was_open = Qt; + } + } +#endif + + if (FRAMEP (tip_frame)) + { + if (delete) + { + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + x_make_frame_invisible (XFRAME (tip_frame)); + + was_open = Qt; + +#ifdef USE_LUCID + /* Bloodcurdling hack alert: The Lucid menu bar widget's + redisplay procedure is not called when a tip frame over + menu items is unmapped. Redisplay the menu manually... */ + { + Widget w; + struct frame *f = SELECTED_FRAME (); + if (FRAME_X_P (f) && FRAME_LIVE_P (f)) + { + w = f->output_data.x->menubar_widget; + + if (!DoesSaveUnders (FRAME_DISPLAY_INFO (f)->screen) + && w != NULL) + { + block_input (); + xlwmenu_redisplay (w); + unblock_input (); + } + } + } +#endif /* USE_LUCID */ + } + else + tip_frame = Qnil; + + return unbind_to (count, was_open); + } +} + DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, doc: /* Show STRING in a "tooltip" window on frame FRAME. A tooltip window is a small X window displaying a string. @@ -5805,15 +5863,16 @@ A tooltip's maximum size is specified by `x-max-tooltip-size'. Text larger than the specified size is clipped. */) (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) { - struct frame *f; + struct frame *f, *tip_f; struct window *w; int root_x, root_y; struct buffer *old_buffer; struct text_pos pos; - int i, width, height; - bool seen_reversed_p; + int width, height; int old_windows_or_buffers_changed = windows_or_buffers_changed; ptrdiff_t count = SPECPDL_INDEX (); + ptrdiff_t count_1; + Lisp_Object window, size; specbind (Qinhibit_redisplay, Qt); @@ -5862,22 +5921,23 @@ Text larger than the specified size is clipped. */) if (NILP (last_show_tip_args)) last_show_tip_args = Fmake_vector (make_number (3), Qnil); - if (!NILP (tip_frame)) + if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) { Lisp_Object last_string = AREF (last_show_tip_args, 0); Lisp_Object last_frame = AREF (last_show_tip_args, 1); Lisp_Object last_parms = AREF (last_show_tip_args, 2); - if (EQ (frame, last_frame) - && !NILP (Fequal (last_string, string)) + if (FRAME_VISIBLE_P (XFRAME (tip_frame)) + && EQ (frame, last_frame) + && !NILP (Fequal_including_properties (last_string, string)) && !NILP (Fequal (last_parms, parms))) { - struct frame *tip_f = XFRAME (tip_frame); - /* Only DX and DY have changed. */ + tip_f = XFRAME (tip_frame); if (!NILP (tip_timer)) { Lisp_Object timer = tip_timer; + tip_timer = Qnil; call1 (Qcancel_timer, timer); } @@ -5888,41 +5948,103 @@ Text larger than the specified size is clipped. */) XMoveWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f), root_x, root_y); unblock_input (); + goto start_timer; } - } + else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame)) + { + bool delete = false; + Lisp_Object tail, elt, parm, last; + + /* Check if every parameter in PARMS has the same value in + last_parms unless it should be ignored by means of + Vtooltip_reuse_hidden_frame_parameters. This may destruct + last_parms which, however, will be recreated below. */ + for (tail = parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + /* The left, top, right and bottom parameters are handled + by compute_tip_xy so they can be ignored here. */ + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) + && !EQ (parm, Qright) && !EQ (parm, Qbottom)) + { + last = Fassq (parm, last_parms); + if (NILP (Fequal (Fcdr (elt), Fcdr (last)))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } - /* Hide a previous tip, if any. */ - Fx_hide_tip (); + /* Now check if every parameter in what is left of last_parms + with a non-nil value has an association in PARMS unless it + should be ignored by means of + Vtooltip_reuse_hidden_frame_parameters. */ + for (tail = last_parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) + && !EQ (parm, Qbottom) && !NILP (Fcdr (elt))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + } + + x_hide_tip (delete); + } + else + x_hide_tip (true); + } + else + x_hide_tip (true); ASET (last_show_tip_args, 0, string); ASET (last_show_tip_args, 1, frame); ASET (last_show_tip_args, 2, parms); - /* Add default values to frame parameters. */ - if (NILP (Fassq (Qname, parms))) - parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); - if (NILP (Fassq (Qinternal_border_width, parms))) - parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); - if (NILP (Fassq (Qborder_width, parms))) - parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); - if (NILP (Fassq (Qbottom_divider_width, parms))) - parms = Fcons (Fcons (Qbottom_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qright_divider_width, parms))) - parms = Fcons (Fcons (Qright_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qborder_color, parms))) - parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); - if (NILP (Fassq (Qbackground_color, parms))) - parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), - parms); - - /* Create a frame for the tooltip, and record it in the global - variable tip_frame. */ - frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, string); - f = XFRAME (frame); - - /* Set up the frame's root window. */ - w = XWINDOW (FRAME_ROOT_WINDOW (f)); + if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) + { + /* Add default values to frame parameters. */ + if (NILP (Fassq (Qname, parms))) + parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); + if (NILP (Fassq (Qinternal_border_width, parms))) + parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); + if (NILP (Fassq (Qborder_width, parms))) + parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); + if (NILP (Fassq (Qborder_color, parms))) + parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); + if (NILP (Fassq (Qbackground_color, parms))) + parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), + parms); + + /* Create a frame for the tooltip, and record it in the global + variable tip_frame. */ + if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms))) + /* Creating the tip frame failed. */ + return unbind_to (count, Qnil); + } + + tip_f = XFRAME (tip_frame); + window = FRAME_ROOT_WINDOW (tip_f); + AUTO_STRING (tip, " *tip*"); + set_window_buffer (window, Fget_buffer_create (tip), false, false); + w = XWINDOW (window); + w->pseudo_window_p = true; + + /* Set up the frame's root window. Note: The following code does not + try to size the window or its frame correctly. Its only purpose is + to make the subsequent text size calculations work. The right + sizes should get installed when the toolkit gets back to us. */ w->left_col = 0; w->top_line = 0; w->pixel_left = 0; @@ -5941,130 +6063,47 @@ Text larger than the specified size is clipped. */) w->total_lines = 40; } - w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (f); - w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (f); - - FRAME_TOTAL_COLS (f) = w->total_cols; - adjust_frame_glyphs (f); - w->pseudo_window_p = true; + w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f); + w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f); + FRAME_TOTAL_COLS (tip_f) = w->total_cols; + adjust_frame_glyphs (tip_f); - /* Display the tooltip text in a temporary buffer. */ + /* Insert STRING into root window's buffer and fit the frame to the + buffer. */ + count_1 = SPECPDL_INDEX (); old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (XWINDOW (FRAME_ROOT_WINDOW (f))->contents)); + set_buffer_internal_1 (XBUFFER (w->contents)); bset_truncate_lines (current_buffer, Qnil); + specbind (Qinhibit_read_only, Qt); + specbind (Qinhibit_modification_hooks, Qt); + specbind (Qinhibit_point_motion_hooks, Qt); + Ferase_buffer (); + Finsert (1, &string); clear_glyph_matrix (w->desired_matrix); clear_glyph_matrix (w->current_matrix); SET_TEXT_POS (pos, BEGV, BEGV_BYTE); - try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); - - /* Compute width and height of the tooltip. */ - width = height = 0; - seen_reversed_p = false; - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - /* Stop at the first empty row at the end. */ - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - - /* Let the row go over the full width of the frame. */ - row->full_width_p = true; - - row_width = row->pixel_width; - if (row->used[TEXT_AREA]) - { - /* There's a glyph at the end of rows that is used to place - the cursor there. Don't include the width of this glyph. */ - if (!row->reversed_p) - { - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - else - { - /* There could be a stretch glyph at the beginning of R2L - rows that is produced by extend_face_to_end_of_line. - Don't count that glyph. */ - struct glyph *g = row->glyphs[TEXT_AREA]; - - if (g->type == STRETCH_GLYPH && NILP (g->object)) - { - row_width -= g->pixel_width; - seen_reversed_p = true; - } - } - } - - height += row->height; - width = max (width, row_width); - } - - /* If we've seen partial-length R2L rows, we need to re-adjust the - tool-tip frame width and redisplay it again, to avoid over-wide - tips due to the stretch glyph that extends R2L lines to full - width of the frame. */ - if (seen_reversed_p) - { - /* w->total_cols and FRAME_TOTAL_COLS want the width in columns, - not in pixels. */ - w->pixel_width = width; - width /= WINDOW_FRAME_COLUMN_WIDTH (w); - w->total_cols = width; - FRAME_TOTAL_COLS (f) = width; - SET_FRAME_WIDTH (f, width); - adjust_frame_glyphs (f); - clear_glyph_matrix (w->desired_matrix); - clear_glyph_matrix (w->current_matrix); - try_window (FRAME_ROOT_WINDOW (f), pos, 0); - width = height = 0; - /* Recompute width and height of the tooltip. */ - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - row->full_width_p = true; - row_width = row->pixel_width; - if (row->used[TEXT_AREA] && !row->reversed_p) - { - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - - height += row->height; - width = max (width, row_width); - } - } - - /* Add the frame's internal border to the width and height the X - window should have. */ - height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - - /* Move the tooltip window where the mouse pointer is. Resize and - show it. */ - compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y); - + try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + /* Calculate size of tooltip window. */ + size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, + make_number (w->pixel_height), Qnil); + /* Add the frame's internal border to calculated size. */ + width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + + /* Calculate position of tooltip frame. */ + compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); + + /* Show tooltip frame. */ block_input (); - XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XMoveResizeWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f), root_x, root_y, width, height); - XMapRaised (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + XMapRaised (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f)); unblock_input (); - /* Draw into the window. */ w->must_be_updated_p = true; update_single_window (w); - - /* Restore original current buffer. */ set_buffer_internal_1 (old_buffer); + unbind_to (count_1, Qnil); windows_or_buffers_changed = old_windows_or_buffers_changed; start_timer: @@ -6081,66 +6120,9 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, Value is t if tooltip was open, nil otherwise. */) (void) { - ptrdiff_t count; - Lisp_Object deleted, frame, timer; - - /* Return quickly if nothing to do. */ - if (NILP (tip_timer) && NILP (tip_frame)) - return Qnil; - - frame = tip_frame; - timer = tip_timer; - tip_frame = tip_timer = deleted = Qnil; - - count = SPECPDL_INDEX (); - specbind (Qinhibit_redisplay, Qt); - specbind (Qinhibit_quit, Qt); - - if (!NILP (timer)) - call1 (Qcancel_timer, timer); - -#ifdef USE_GTK - { - /* When using system tooltip, tip_frame is the Emacs frame on which - the tip is shown. */ - struct frame *f = XFRAME (frame); - if (FRAME_LIVE_P (f) && xg_hide_tooltip (f)) - frame = Qnil; - } -#endif - - if (FRAMEP (frame)) - { - delete_frame (frame, Qnil); - deleted = Qt; - -#ifdef USE_LUCID - /* Bloodcurdling hack alert: The Lucid menu bar widget's - redisplay procedure is not called when a tip frame over menu - items is unmapped. Redisplay the menu manually... */ - { - Widget w; - struct frame *f = SELECTED_FRAME (); - if (FRAME_X_P (f) && FRAME_LIVE_P (f)) - { - w = f->output_data.x->menubar_widget; - - if (!DoesSaveUnders (FRAME_DISPLAY_INFO (f)->screen) - && w != NULL) - { - block_input (); - xlwmenu_redisplay (w); - unblock_input (); - } - } - } -#endif /* USE_LUCID */ - } - - return unbind_to (count, deleted); + return x_hide_tip (!tooltip_reuse_hidden_frame); } - \f /*********************************************************************** File selection dialog @@ -6802,6 +6784,7 @@ syms_of_xfns (void) DEFSYM (Qcancel_timer, "cancel-timer"); DEFSYM (Qfont_param, "font-parameter"); DEFSYM (Qmono, "mono"); + DEFSYM (Qassq_delete_all, "assq-delete-all"); #ifdef USE_CAIRO DEFSYM (Qpdf, "pdf"); ^ permalink raw reply related [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2016-03-06 9:21 ` martin rudalics @ 2016-03-08 8:02 ` martin rudalics 0 siblings, 0 replies; 47+ messages in thread From: martin rudalics @ 2016-03-08 8:02 UTC (permalink / raw) Cc: emacs-devel > If nobody objects I intend to apply > the attached patch in the next days. Done as commit 59c7a5d..80864c2 master -> master. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 21:15 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics 2015-07-30 7:08 ` martin rudalics @ 2015-08-01 10:49 ` martin rudalics 2015-08-03 10:34 ` Stefan Monnier 2 siblings, 1 reply; 47+ messages in thread From: martin rudalics @ 2015-08-01 10:49 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel > Weird. Perhaps my guess about your problem was wrong, though there is > clearly a bug there in computing percentages, which I'll see if I can > fix. Whatever you've done: With latest trunk/master all percentages come up correctly. So many thanks for your efforts. The following excerpt from `profiler-report' still exhibits the other problem: - timer-event-handler 21,655,992 90% - apply 21,655,992 90% - tooltip-timeout 21,655,992 90% - run-hook-with-args-until-success 21,655,992 90% - tooltip-help-tips 21,655,992 90% - tooltip-show 21,655,992 90% - x-show-tip 1,119,104 4% - face-set-after-frame-default 1,119,104 4% - face-spec-recalc 1,102,240 4% + face-spec-reset-face 1,045,708 4% + face-spec-set-2 21,924 0% + face-spec-choose 15,816 0% face-list 5,220 0% + command-execute 1,399,342 5% + redisplay_internal (C function) 870,687 3% + tooltip-show-help 8,352 0% ... 0 0% The absolute value for `x-show-tip' is surely wrong. `x-show-tip' is responsible for 90% (or maybe 89%) of the allocations. Thanks again, martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-01 10:49 ` martin rudalics @ 2015-08-03 10:34 ` Stefan Monnier 2015-08-03 14:56 ` Eli Zaretskii 0 siblings, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-03 10:34 UTC (permalink / raw) To: martin rudalics; +Cc: Paul Eggert, emacs-devel > The absolute value for `x-show-tip' is surely wrong. `x-show-tip' > is responsible for 90% (or maybe 89%) of the allocations. A potential source of such errors is that we count the moment when "malloc" is called, and this can be misleading: you could do a thousand "cons" calls in x-show-tip, all satisfied from the list of free cons-cells, and then a single "cons" call elsewhere which finds the free list to be empty and hence does a "malloc" which will re-fill the free list with another thousand cons cells. Often/usually, this should be "random" and hence irrelevant statistically (as long as we have enough samples), but there might well be cases where the allocation patterns end up always shifting the blame elsewhere. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-03 10:34 ` Stefan Monnier @ 2015-08-03 14:56 ` Eli Zaretskii 2015-08-03 21:13 ` Stefan Monnier 0 siblings, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-08-03 14:56 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Date: Mon, 03 Aug 2015 06:34:52 -0400 > Cc: Paul Eggert <eggert@cs.ucla.edu>, emacs-devel@gnu.org > > > The absolute value for `x-show-tip' is surely wrong. `x-show-tip' > > is responsible for 90% (or maybe 89%) of the allocations. > > A potential source of such errors is that we count the moment when > "malloc" is called, and this can be misleading: you could do a thousand > "cons" calls in x-show-tip, all satisfied from the list of free > cons-cells, and then a single "cons" call elsewhere which finds the free > list to be empty and hence does a "malloc" which will re-fill the free > list with another thousand cons cells. Indeed, when I looked into this issue, I tracked increments to consing_since_gc. Perhaps we should change the "memory" profiler to do the same. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-03 14:56 ` Eli Zaretskii @ 2015-08-03 21:13 ` Stefan Monnier 2015-08-04 2:38 ` Eli Zaretskii 0 siblings, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-03 21:13 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, emacs-devel > Indeed, when I looked into this issue, I tracked increments to > consing_since_gc. Perhaps we should change the "memory" profiler to > do the same. The problem is that it would slow down functions like `cons'. And given that the "memory profiler" is very rarely useful, I'm not really willing to pay that slowdown. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-03 21:13 ` Stefan Monnier @ 2015-08-04 2:38 ` Eli Zaretskii 2015-08-04 7:31 ` Stefan Monnier 0 siblings, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-08-04 2:38 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org > Date: Mon, 03 Aug 2015 17:13:03 -0400 > > > Indeed, when I looked into this issue, I tracked increments to > > consing_since_gc. Perhaps we should change the "memory" profiler to > > do the same. > > The problem is that it would slow down functions like `cons'. I don't see why it has to. And we could do that only when the memory profiler is invoked. In any case, it's much better than the current useless profile. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-04 2:38 ` Eli Zaretskii @ 2015-08-04 7:31 ` Stefan Monnier 2015-08-04 13:28 ` Eli Zaretskii 2015-08-08 13:41 ` Nix 0 siblings, 2 replies; 47+ messages in thread From: Stefan Monnier @ 2015-08-04 7:31 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, emacs-devel >> The problem is that it would slow down functions like `cons'. > I don't see why it has to. I don't see how it wouldn't have to ;-) > And we could do that only when the memory profiler is invoked. AFAICT it would require an extra test somewhere in Fcons, and that test will always have to be performed. > In any case, it's much better than the current useless profile. I'm not convinced it'd make it sufficiently better to justify the cost. I'd be OK with adding this "more precise allocation profiler" as a compile-time option, so we could at least see if the improved precision does make it more useful. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-04 7:31 ` Stefan Monnier @ 2015-08-04 13:28 ` Eli Zaretskii 2015-08-07 14:52 ` Stefan Monnier 2015-08-08 13:41 ` Nix 1 sibling, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-08-04 13:28 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org > Date: Tue, 04 Aug 2015 03:31:52 -0400 > > > And we could do that only when the memory profiler is invoked. > > AFAICT it would require an extra test somewhere in Fcons, and that > test will always have to be performed. Just looking at the implementation of Fcons, I find it hard to believe a single comparison can have any significant effect on its efficiency. So I'm unsure how this could be of any practical concern. > > In any case, it's much better than the current useless profile. > > I'm not convinced it'd make it sufficiently better Well, it allowed me in this case to identify the source of the problem quickly, efficiently, and accurately. > to justify the cost. Please show the costs, then, and please do that with some kind of quantitative analysis, so that we could have some numbers to discuss, instead of just arguing about personal preferences and opinions. > I'd be OK with adding this "more precise allocation profiler" as > a compile-time option, so we could at least see if the improved > precision does make it more useful. IME, compile-time options of this kind are rarely useful, because they are never there when you need them. Especially if the person who needs to perform measurements doesn't build their Emacs. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-04 13:28 ` Eli Zaretskii @ 2015-08-07 14:52 ` Stefan Monnier 2015-08-07 15:23 ` Eli Zaretskii 0 siblings, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-07 14:52 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, emacs-devel > Just looking at the implementation of Fcons, I find it hard to believe > a single comparison can have any significant effect on its efficiency. > So I'm unsure how this could be of any practical concern. Fcons is a core operation executed fairly frequently, so any slowdown (no matter how small) needs to be justified by a major advantage. Making the allocation profiler (which so far has never been useful to anyone, AFAICT) slightly more precise, is not obviously a "major advantage". At the very least, I'd need positive proof that the extra precision has been useful at least once. >> > In any case, it's much better than the current useless profile. >> I'm not convinced it'd make it sufficiently better > Well, it allowed me in this case to identify the source of the problem > quickly, efficiently, and accurately. IIUC, something *else* allowed you to do that. I don't think anyone has actually tried to improve the precision of the allocation profiler and found it to help in this case. It's quite possible that some other problems might still prevent it from being useful in this case. >> I'd be OK with adding this "more precise allocation profiler" as >> a compile-time option, so we could at least see if the improved >> precision does make it more useful. > IME, compile-time options of this kind are rarely useful, because they > are never there when you need them. Especially if the person who > needs to perform measurements doesn't build their Emacs. Until I have positive proof that the extra precision is useful in some cases, I'll reject such changes in the default build. So it's either "rejected" or "depends on a compile-time option" for now. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 14:52 ` Stefan Monnier @ 2015-08-07 15:23 ` Eli Zaretskii 2015-08-07 17:26 ` Stefan Monnier 2015-08-07 18:26 ` Paul Eggert 0 siblings, 2 replies; 47+ messages in thread From: Eli Zaretskii @ 2015-08-07 15:23 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org > Date: Fri, 07 Aug 2015 10:52:34 -0400 > > > Just looking at the implementation of Fcons, I find it hard to believe > > a single comparison can have any significant effect on its efficiency. > > So I'm unsure how this could be of any practical concern. > > Fcons is a core operation executed fairly frequently, so any slowdown > (no matter how small) needs to be justified by a major advantage. Without some kind of quantitative criterion, this sounds irrational to me. Would a 0.001% slow-down be acceptable? How about 0.1%? There must exist some threshold below which any slow-down can be ignored, and the question I'm asking is what is that threshold? > >> > In any case, it's much better than the current useless profile. > >> I'm not convinced it'd make it sufficiently better > > Well, it allowed me in this case to identify the source of the problem > > quickly, efficiently, and accurately. > > IIUC, something *else* allowed you to do that. That something else was a watchpoint put on the variable, followed by semi-manual computation of the frequency distribution of functions that caused the watchpoint to fire. That's exactly what a profiler would have done for me, if it were looking at that variable instead of counting calls to malloc. > Until I have positive proof that the extra precision is useful in some > cases, I'll reject such changes in the default build. So it's either > "rejected" or "depends on a compile-time option" for now. Too bad. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 15:23 ` Eli Zaretskii @ 2015-08-07 17:26 ` Stefan Monnier 2015-08-07 18:20 ` Eli Zaretskii 2015-08-07 18:26 ` Paul Eggert 1 sibling, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-07 17:26 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, emacs-devel > Without some kind of quantitative criterion, this sounds irrational to > me. Would a 0.001% slow-down be acceptable? How about 0.1%? There > must exist some threshold below which any slow-down can be ignored, > and the question I'm asking is what is that threshold? For a functionality whose usefulness has not been proven, the threshold is 0%. > That something else was a watchpoint put on the variable, followed by > semi-manual computation of the frequency distribution of functions > that caused the watchpoint to fire. That's exactly what a profiler > would have done for me, if it were looking at that variable instead of > counting calls to malloc. I'd welcome a patch which uses such a "sampling watchpoint", since it would even speed up the code by removing the code that counts calls to malloc. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 17:26 ` Stefan Monnier @ 2015-08-07 18:20 ` Eli Zaretskii 2015-08-07 21:25 ` Stefan Monnier 0 siblings, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-08-07 18:20 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org > Date: Fri, 07 Aug 2015 13:26:54 -0400 > > > Without some kind of quantitative criterion, this sounds irrational to > > me. Would a 0.001% slow-down be acceptable? How about 0.1%? There > > must exist some threshold below which any slow-down can be ignored, > > and the question I'm asking is what is that threshold? > > For a functionality whose usefulness has not been proven, the threshold > is 0%. That's irrational. > > That something else was a watchpoint put on the variable, followed by > > semi-manual computation of the frequency distribution of functions > > that caused the watchpoint to fire. That's exactly what a profiler > > would have done for me, if it were looking at that variable instead of > > counting calls to malloc. > > I'd welcome a patch which uses such a "sampling watchpoint", since it > would even speed up the code by removing the code that counts calls > to malloc. Sorry, your jokes are beyond me. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 18:20 ` Eli Zaretskii @ 2015-08-07 21:25 ` Stefan Monnier 2015-08-08 6:46 ` Eli Zaretskii 0 siblings, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-07 21:25 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, emacs-devel >> I'd welcome a patch which uses such a "sampling watchpoint", since it >> would even speed up the code by removing the code that counts calls >> to malloc. > Sorry, your jokes are beyond me. You must have misunderstood, because that was no joke at all. I really mean it: I'd be happy to see a patch which uses the same trick used by GDB to add a "watchpoint". Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 21:25 ` Stefan Monnier @ 2015-08-08 6:46 ` Eli Zaretskii 2015-08-08 7:01 ` David Kastrup 0 siblings, 1 reply; 47+ messages in thread From: Eli Zaretskii @ 2015-08-08 6:46 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, eggert, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org > Date: Fri, 07 Aug 2015 17:25:37 -0400 > > >> I'd welcome a patch which uses such a "sampling watchpoint", since it > >> would even speed up the code by removing the code that counts calls > >> to malloc. > > Sorry, your jokes are beyond me. > > You must have misunderstood, because that was no joke at all. I really > mean it: I'd be happy to see a patch which uses the same trick used by > GDB to add a "watchpoint". GDB uses debug registers and the debug interface that catches the resulting signal/exception, something a well-behaving program that is not a debugger should never do. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-08 6:46 ` Eli Zaretskii @ 2015-08-08 7:01 ` David Kastrup 0 siblings, 0 replies; 47+ messages in thread From: David Kastrup @ 2015-08-08 7:01 UTC (permalink / raw) To: Eli Zaretskii; +Cc: rudalics, eggert, Stefan Monnier, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Stefan Monnier <monnier@iro.umontreal.ca> >> Cc: rudalics@gmx.at, eggert@cs.ucla.edu, emacs-devel@gnu.org >> Date: Fri, 07 Aug 2015 17:25:37 -0400 >> >> >> I'd welcome a patch which uses such a "sampling watchpoint", since it >> >> would even speed up the code by removing the code that counts calls >> >> to malloc. >> > Sorry, your jokes are beyond me. >> >> You must have misunderstood, because that was no joke at all. I really >> mean it: I'd be happy to see a patch which uses the same trick used by >> GDB to add a "watchpoint". > > GDB uses debug registers and the debug interface that catches the > resulting signal/exception, something a well-behaving program that is > not a debugger should never do. Well, but in this case we want to debug a problem. So it might be worth considering how to make it easy and/or standard to generate/retrieve the desired information from a debugger. Interpreted languages like GUILE/Elisp tend to be a real mess to debug regarding the setting of break points and trapping on virtual machine registers and so on. The question is how one could make it easier to immerse the debugger in the world view of an interpreted/VM application and use its hardware tools for debugging problems not cast in terms of actual assembly code on the executing CPU. Zero-expense Lisp-level hooks that only become available within a debugging session (and of course have quite non-zero cost when activated but zero when not) but do not require recompilation would fall in that class. Of course, that would be a nice long-term solution. But the mere potential for a long-term solution does not solve short-term problems. In particular if nobody is working on the long-term solutions. -- David Kastrup ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 15:23 ` Eli Zaretskii 2015-08-07 17:26 ` Stefan Monnier @ 2015-08-07 18:26 ` Paul Eggert 2015-08-07 18:59 ` Eli Zaretskii 1 sibling, 1 reply; 47+ messages in thread From: Paul Eggert @ 2015-08-07 18:26 UTC (permalink / raw) To: Eli Zaretskii, Stefan Monnier; +Cc: rudalics, emacs-devel Eli Zaretskii wrote: > Without some kind of quantitative criterion, this sounds irrational to > me. I don't know -- adding gingerbread to Fcons, even if it's a small inefficiency now, is likely to lead to greater inefficiencies later as it will place more design constraints on future attempts to improve performance. In the meantime surely it's enough to make it a compile-time option. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-07 18:26 ` Paul Eggert @ 2015-08-07 18:59 ` Eli Zaretskii 0 siblings, 0 replies; 47+ messages in thread From: Eli Zaretskii @ 2015-08-07 18:59 UTC (permalink / raw) To: Paul Eggert; +Cc: rudalics, monnier, emacs-devel > Date: Fri, 07 Aug 2015 11:26:12 -0700 > From: Paul Eggert <eggert@cs.ucla.edu> > CC: rudalics@gmx.at, emacs-devel@gnu.org > > Eli Zaretskii wrote: > > Without some kind of quantitative criterion, this sounds irrational to > > me. > > I don't know -- adding gingerbread to Fcons, even if it's a small inefficiency > now, is likely to lead to greater inefficiencies later as it will place more > design constraints on future attempts to improve performance. Don't be ridiculous; all it takes is a single test followed by a function call that is only made if the test succeeded (i.e. a profile is in progress). We do that all the time, and no one has ever looked back. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-04 7:31 ` Stefan Monnier 2015-08-04 13:28 ` Eli Zaretskii @ 2015-08-08 13:41 ` Nix 2015-08-08 16:10 ` Stefan Monnier 1 sibling, 1 reply; 47+ messages in thread From: Nix @ 2015-08-08 13:41 UTC (permalink / raw) To: Stefan Monnier; +Cc: rudalics, Eli Zaretskii, eggert, emacs-devel On 4 Aug 2015, Stefan Monnier told this: >>> The problem is that it would slow down functions like `cons'. >> I don't see why it has to. > > I don't see how it wouldn't have to ;-) > >> And we could do that only when the memory profiler is invoked. > > AFAICT it would require an extra test somewhere in Fcons, and that > test will always have to be performed. The cost you're trying to defend against here is a mispredicted branch and corresponding pipeline stall, so if you mark it with __builtin_expect() (or analogue in other compilers) you can make the 'profiler off' case the predicted branch, and its cost when off should drop to nearly zero. (Or so it seems to me.) -- NULL && (void) ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-08 13:41 ` Nix @ 2015-08-08 16:10 ` Stefan Monnier 2015-08-08 16:13 ` David Kastrup 0 siblings, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2015-08-08 16:10 UTC (permalink / raw) To: Nix; +Cc: rudalics, Eli Zaretskii, eggert, emacs-devel > The cost you're trying to defend against here is a mispredicted branch No, I assume the branch would be properly predicted since it would always take the same path. Stefan ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-08 16:10 ` Stefan Monnier @ 2015-08-08 16:13 ` David Kastrup 2015-08-10 17:05 ` Nix 0 siblings, 1 reply; 47+ messages in thread From: David Kastrup @ 2015-08-08 16:13 UTC (permalink / raw) To: Stefan Monnier; +Cc: Nix, rudalics, Eli Zaretskii, eggert, emacs-devel Stefan Monnier <monnier@IRO.UMontreal.CA> writes: >> The cost you're trying to defend against here is a mispredicted branch > > No, I assume the branch would be properly predicted since it would > always take the same path. If cons is encountered with high frequency. But of course, if it is not, why bother in the first place? -- David Kastrup ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-08 16:13 ` David Kastrup @ 2015-08-10 17:05 ` Nix 2015-08-10 17:44 ` David Kastrup 0 siblings, 1 reply; 47+ messages in thread From: Nix @ 2015-08-10 17:05 UTC (permalink / raw) To: David Kastrup Cc: rudalics, Eli Zaretskii, eggert, Stefan Monnier, emacs-devel On 8 Aug 2015, David Kastrup uttered the following: > Stefan Monnier <monnier@IRO.UMontreal.CA> writes: > >>> The cost you're trying to defend against here is a mispredicted branch >> >> No, I assume the branch would be properly predicted since it would >> always take the same path. > > If cons is encountered with high frequency. But of course, if it is > not, why bother in the first place? In that case I have no idea what you're trying to defend against. The cost of a single conditional and predicted branch is drowned in the overhead of the allocation that cons has to do anyway. I'd be astonished if you could ever see any performance impact whatsoever. -- NULL && (void) ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-08-10 17:05 ` Nix @ 2015-08-10 17:44 ` David Kastrup 0 siblings, 0 replies; 47+ messages in thread From: David Kastrup @ 2015-08-10 17:44 UTC (permalink / raw) To: Nix; +Cc: rudalics, Eli Zaretskii, eggert, Stefan Monnier, emacs-devel Nix <nix@esperi.org.uk> writes: > On 8 Aug 2015, David Kastrup uttered the following: > >> Stefan Monnier <monnier@IRO.UMontreal.CA> writes: >> >>>> The cost you're trying to defend against here is a mispredicted branch >>> >>> No, I assume the branch would be properly predicted since it would >>> always take the same path. >> >> If cons is encountered with high frequency. But of course, if it is >> not, why bother in the first place? > > In that case I have no idea what you're trying to defend against. The > cost of a single conditional and predicted branch is drowned in the > overhead of the allocation that cons has to do anyway. Cons cells are pooled, so in a long session the consolidated cost is that of getting from the free list (cheap) and sweeping back into it once a mark phase no longer visits the cell. Which would be the rule rather than the exception for high-frequency consing. > I'd be astonished if you could ever see any performance impact > whatsoever. Cons cells for Lisp should end up quite cheaper than arbitrary-size allocations. -- David Kastrup ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 15:40 ` martin rudalics 2015-07-29 16:33 ` Eli Zaretskii @ 2015-07-29 16:45 ` Paul Eggert 2015-07-29 18:05 ` martin rudalics 1 sibling, 1 reply; 47+ messages in thread From: Paul Eggert @ 2015-07-29 16:45 UTC (permalink / raw) To: martin rudalics, Stefan Monnier; +Cc: emacs-devel martin rudalics wrote: > Does that mean I have to recompile all elisp files? No, just the .o files, src/temacs, src/bootstrap-emacs, and src/emacs. > Anyway. If you do `profiler-start', move the mouse over your mode line > for a few seconds so that tooltips show up a couple of times and then do > `profiler-report' and look at the results: Can you tell me from your > report where `tooltip-show' allocates more than half of the bytes it > allocates if _not_ in `x-show-tip'? I presume those bytes are allocated directly by tooltip-show. By "directly" I mean via a function written in C, e.g., "cons". ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 16:45 ` Paul Eggert @ 2015-07-29 18:05 ` martin rudalics 2015-07-29 21:17 ` Paul Eggert 0 siblings, 1 reply; 47+ messages in thread From: martin rudalics @ 2015-07-29 18:05 UTC (permalink / raw) To: Paul Eggert, Stefan Monnier; +Cc: emacs-devel >> Does that mean I have to recompile all elisp files? > > No, just the .o files, src/temacs, src/bootstrap-emacs, and src/emacs. Good. I did that. >> Anyway. If you do `profiler-start', move the mouse over your mode line >> for a few seconds so that tooltips show up a couple of times and then do >> `profiler-report' and look at the results: Can you tell me from your >> report where `tooltip-show' allocates more than half of the bytes it >> allocates if _not_ in `x-show-tip'? > > I presume those bytes are allocated directly by tooltip-show. By "directly" I mean via a function written in C, e.g., "cons". There are one call of `copy-sequence', two calls of `stringp', three of `setf', one of `propertize' and one of `selected-frame'. Together these would allocate some 20 million bytes? OTOH the `copy-sequence', `face-attribute' and `alist-get' calls get nowhere listed. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 18:05 ` martin rudalics @ 2015-07-29 21:17 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics 0 siblings, 1 reply; 47+ messages in thread From: Paul Eggert @ 2015-07-29 21:17 UTC (permalink / raw) To: martin rudalics; +Cc: emacs-devel martin rudalics wrote: > There are one call of `copy-sequence', two calls of `stringp', three of > `setf', one of `propertize' and one of `selected-frame'. Together these > would allocate some 20 million bytes? That's what it's saying, yes. > OTOH the `copy-sequence', `face-attribute' and `alist-get' calls get > nowhere listed. copy-sequence and propertize are in C. Either can easily allocate millions of bytes. Perhaps face-attribute doesn't allocate storage; that would explain its not being listed. alist-get is in a call to the setf macro and you may need to investigate what setf is really doing. One way to look into that is to disassemble the byte code (type "M-x disassemble RET tooltip-show RET") and see what it does. ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Tooltips GC overhead 2015-07-29 21:17 ` Paul Eggert @ 2015-07-30 6:00 ` martin rudalics 0 siblings, 0 replies; 47+ messages in thread From: martin rudalics @ 2015-07-30 6:00 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel >> There are one call of `copy-sequence', two calls of `stringp', three of >> `setf', one of `propertize' and one of `selected-frame'. Together these >> would allocate some 20 million bytes? > > That's what it's saying, yes. > >> OTOH the `copy-sequence', `face-attribute' and `alist-get' calls get >> nowhere listed. > > copy-sequence and propertize are in C. Either can easily allocate millions of bytes. Just that `copy-sequence' makes a copy of a three elements alist. And `propertize' usually propertizes a text of a at most hundred characters here. If that function were that expensive, Emcas would be stuck most of the time. > Perhaps face-attribute doesn't allocate storage; that would explain its not being listed. This would make things even more mysterious. > alist-get is in a call to the setf macro and you may need to > investigate what setf is really doing. One way to look into that is > to disassemble the byte code (type "M-x disassemble RET tooltip-show > RET") and see what it does. I suppose running `tooltip-show' interpreted might be the easier way. In any case, neither the profiler nor tooltips appear usable in their current implementation. martin ^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2016-03-08 8:02 UTC | newest] Thread overview: 47+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-07-28 12:45 Tooltips GC overhead martin rudalics 2015-07-28 13:53 ` Eli Zaretskii 2015-07-28 15:09 ` martin rudalics 2015-07-28 23:51 ` Stefan Monnier 2015-07-29 7:18 ` martin rudalics 2015-07-29 14:29 ` Paul Eggert 2015-07-29 15:40 ` martin rudalics 2015-07-29 16:33 ` Eli Zaretskii 2015-07-29 18:05 ` martin rudalics 2015-07-29 21:15 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics 2015-07-30 7:08 ` martin rudalics 2015-07-30 7:19 ` Paul Eggert 2015-07-30 9:05 ` martin rudalics 2015-07-30 16:36 ` Eli Zaretskii 2015-07-30 19:53 ` martin rudalics 2015-07-30 23:09 ` Stefan Monnier 2015-07-30 23:33 ` Drew Adams 2015-07-31 6:48 ` Eli Zaretskii 2015-07-31 6:47 ` Eli Zaretskii 2016-03-06 9:21 ` martin rudalics 2016-03-08 8:02 ` martin rudalics 2015-08-01 10:49 ` martin rudalics 2015-08-03 10:34 ` Stefan Monnier 2015-08-03 14:56 ` Eli Zaretskii 2015-08-03 21:13 ` Stefan Monnier 2015-08-04 2:38 ` Eli Zaretskii 2015-08-04 7:31 ` Stefan Monnier 2015-08-04 13:28 ` Eli Zaretskii 2015-08-07 14:52 ` Stefan Monnier 2015-08-07 15:23 ` Eli Zaretskii 2015-08-07 17:26 ` Stefan Monnier 2015-08-07 18:20 ` Eli Zaretskii 2015-08-07 21:25 ` Stefan Monnier 2015-08-08 6:46 ` Eli Zaretskii 2015-08-08 7:01 ` David Kastrup 2015-08-07 18:26 ` Paul Eggert 2015-08-07 18:59 ` Eli Zaretskii 2015-08-08 13:41 ` Nix 2015-08-08 16:10 ` Stefan Monnier 2015-08-08 16:13 ` David Kastrup 2015-08-10 17:05 ` Nix 2015-08-10 17:44 ` David Kastrup 2015-07-29 16:45 ` Paul Eggert 2015-07-29 18:05 ` martin rudalics 2015-07-29 21:17 ` Paul Eggert 2015-07-30 6:00 ` martin rudalics
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).