Closed kamoii closed 3 years ago
I have also observed that add-hook appears in the memory profile and I don't understand it yet to be honest. There seems to be some weird copying/memory-leak going on in add-hook.
Ping @clemera, this also happens for the Selectrum add-hook it seems. Any idea?
No, this seems strange :man_shrugging:
Btw, what does this memory profiler measure. I don't understand this. Is it an allocation profiler? Or does it measure the amount of live data? But the consult/selectrum data should be gone after closing the completing-read session.
EDIT: Just checked, it measures allocations as expected.
I'm not an profiling expert myself, maybe someone else is able to provide a more detailed explanation.
I did a few experiments.
When I remove the (:append ...)
and simply prepend the hooks the problem goes away since then no sorting takes place.
Furthermore using the following macro instead of minibuffer-with-setup-hook fixes the issue, since I am not adding lambdas to the hooks but symbols. For some reason the add-hook function has problem with lambda values and seems to perform deep copies of those.
(defmacro my-minibuffer-with-setup-hook (fun &rest body)
(declare (indent 1) (debug t))
(let ((hook (make-symbol "setup-hook"))
(funsym (make-symbol "fun"))
(append nil))
(when (eq (car-safe fun) :append)
(setq append '(t) fun (cadr fun)))
`(let ((,hook (make-symbol "hook-sym"))
(,funsym ,fun))
(fset ,hook
(lambda ()
;; Clear out this hook so it does not interfere
;; with any recursive minibuffer usage.
(remove-hook 'minibuffer-setup-hook ,hook)
(funcall ,funsym)))
(unwind-protect
(progn
(add-hook 'minibuffer-setup-hook ,hook ,@append)
,@body)
(remove-hook 'minibuffer-setup-hook ,hook)))))
Lambdas have made problems before in combination with Evil, see #152. For this reason I am already using fset symbols in Consult for the post-command-hooks. The fset trick is something I found in set-transient-map.
Elisp is broken.
Thank you for looking into this, this should also be reported upstream I guess. I will steal your macro for Selectrum and use it for now :+1:
@clemera Do you suggest we use this my-minibuffer macro in Consult and Selectrum for now?
@clemera @minad Thansk a lot!
Updated consult
and its so snappy now. Took memory proffile and the add-hook
is gone. Also the total memory allocation reduced alot.
56,846,572 89% - consult-buffer
56,846,572 89% - consult--buffer
56,846,572 89% - consult--multi
53,076,020 83% - consult--read
53,076,020 83% - apply
53,076,020 83% - #<compiled -0x1bf0d3486ff553f9>
53,076,020 83% - consult--with-async-1
53,076,020 83% - #<compiled 0x3e0275e58912bf7>
53,076,020 83% - consult--with-preview-1
53,074,964 83% - #<compiled -0xbfb98ce1324d29c>
53,074,964 83% - completing-read
53,074,964 83% - selectrum-completing-read
5,530,436 8% + selectrum-read
70,240 0% + consult--multi-candidates
4,360 0% consult--multi-predicate
1,056 0% + consult--multi-preprocess
@clemera Thank you for pushing the fix to selectrum too. @kamoii Thank you for the report and checking again.
Now the problem should still be reported upstream. It should be pretty easy to reproduce.
Start memory profiler and invoke consult-buffer a few times.
Upstream bugreport:
Mail: https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-02/msg00329.html Tracker: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46326
For those interested in the technicalities of why this issue occurred, I've finally figured it out.
(consult-buffer)
makes a big closure A, adds it to minibuffer-setup-hook
and then removes it from there. Even though add/remove-hook use equal
for
their function comparisons, this all happens quickly since (equal A A)
is
inexpensive to compute, equal
short-circuits on objects that are eq
.
Removing A from the hook will not remove it from its hook--depth-alist
. You
can check this by evaluating (get 'minibuffer-setup-hook 'hook--depth-alist)
,
see Emacs bug#46414.
The second call to (consult-buffer)
makes a closure B, which isn't eq to A.
It is however equal to A. Add-hook will look for B in the hook--depth-alist
with equal
, leading to evaluation of (equal A B)
which is expensive to
compute. For objects exceeding depth of 10, equal
allocates a hash table and
does some consing for the purpose of loop detection, which is what we see in
the memory profiler.
Great, thank you for figuring this out! I agree with you regarding the steps to fix the problem:
Using
consult-buffer
frequently, I noticed it somtimes has this little delay before it show its candidate. I assumed it was because of GC, so I profiledconsult-buffer
by starting profiler and invokingconsult-buffer
a few times. Following is part of the memory profile result:add-hook
's total memory usage is 75% of the total consumed memory, which is quite high. I don't know why we needadd-hook
and why it consumes so much memory. Isadd-hook
supposed to consume such memory or maybe this is some kind of performance bug?