* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
@ 2023-01-09 17:16 Juri Linkov
2023-01-09 22:33 ` Dmitry Gutov
` (3 more replies)
0 siblings, 4 replies; 28+ messages in thread
From: Juri Linkov @ 2023-01-09 17:16 UTC (permalink / raw)
To: 60691; +Cc: dmitry gutov
X-Debbugs-Cc: Dmitry Gutov <dgutov@yandex.ru>
After more rules were added recently to ruby-ts--font-lock-settings,
font-lock became slow even on very small files. Some measurements:
M-: (benchmark-run 1000 (progn (font-lock-mode -1) (font-lock-mode 1) (font-lock-ensure)))
M-x ruby-mode
(1.3564674989999999 0 0.0)
M-x ruby-ts-mode
(8.349582391999999 2 6.489918534000001)
This is not a problem when files are visited infrequently, but
becomes a problem for diff-syntax fontification that wants to
highlight simultaneously many files from git logs.
So a temporary measure would be not to enable ruby-ts-mode
in internal buffers:
(add-hook 'find-file-hook
(lambda ()
(when (and (eq major-mode 'ruby-mode)
;; Only when not internal as from diff-syntax
(not (string-prefix-p " " (buffer-name))))
(ruby-ts-mode))))
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-09 17:16 bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode Juri Linkov
@ 2023-01-09 22:33 ` Dmitry Gutov
2023-01-10 8:10 ` Juri Linkov
2023-01-12 21:58 ` Yuan Fu
` (2 subsequent siblings)
3 siblings, 1 reply; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-09 22:33 UTC (permalink / raw)
To: Juri Linkov, 60691
Hi!
On 09/01/2023 19:16, Juri Linkov wrote:
> X-Debbugs-Cc: Dmitry Gutov <dgutov@yandex.ru>
>
> After more rules were added recently to ruby-ts--font-lock-settings,
> font-lock became slow even on very small files. Some measurements:
If you saw a particular commit that made things slower, did you try
reverting it? What was the performance after?
> M-: (benchmark-run 1000 (progn (font-lock-mode -1) (font-lock-mode 1) (font-lock-ensure)))
>
> M-x ruby-mode
> (1.3564674989999999 0 0.0)
>
> M-x ruby-ts-mode
> (8.349582391999999 2 6.489918534000001)
I have tried this scenario (which, to be frank, is pretty artificial,
given that fontification is usually performed in chunks, not over the
whole buffer).
Perhaps the results depend on a particular file. The ones I have tried
(ruby.rb and ruby-after-operator-indent.rb) show only 2x difference (or
less). The difference was in favor of ruby-mode, but given the
difference in approaches I wouldn't be surprised if ruby-ts-mode incurs
a fixed overhead somewhere.
> This is not a problem when files are visited infrequently, but
> becomes a problem for diff-syntax fontification that wants to
> highlight simultaneously many files from git logs.
> So a temporary measure would be not to enable ruby-ts-mode
> in internal buffers:
Is it common to try to highlight 1000 or even 100 files in one diff?
> (add-hook 'find-file-hook
> (lambda ()
> (when (and (eq major-mode 'ruby-mode)
> ;; Only when not internal as from diff-syntax
> (not (string-prefix-p " " (buffer-name))))
> (ruby-ts-mode))))
Have you tried similar tests with other -ts- modes? Ones with complex
font-lock rules in particular.
I've tried commenting out different rules in
ruby-ts--font-lock-settings, but none of them seem to have particularly
outsides impact. Performance seems, roughly, inversely proportional to
the number of separate "features".
And if all ts modes turn out to have this problem, perhaps the place to
improve this is inside some common code.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-09 22:33 ` Dmitry Gutov
@ 2023-01-10 8:10 ` Juri Linkov
2023-01-10 14:10 ` Dmitry Gutov
0 siblings, 1 reply; 28+ messages in thread
From: Juri Linkov @ 2023-01-10 8:10 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691
>> After more rules were added recently to ruby-ts--font-lock-settings,
>> font-lock became slow even on very small files. Some measurements:
>
> If you saw a particular commit that made things slower, did you try
> reverting it? What was the performance after?
No particular commit, just adding more rules degrades performance
gradually.
>> M-: (benchmark-run 1000 (progn (font-lock-mode -1) (font-lock-mode 1) (font-lock-ensure)))
>> M-x ruby-mode
>> (1.3564674989999999 0 0.0)
>> M-x ruby-ts-mode
>> (8.349582391999999 2 6.489918534000001)
>
> I have tried this scenario (which, to be frank, is pretty artificial, given
> that fontification is usually performed in chunks, not over the whole
> buffer).
>
> Perhaps the results depend on a particular file. The ones I have tried
> (ruby.rb and ruby-after-operator-indent.rb) show only 2x difference (or
> less). The difference was in favor of ruby-mode, but given the difference
> in approaches I wouldn't be surprised if ruby-ts-mode incurs a fixed
> overhead somewhere.
On test/lisp/progmodes/ruby-mode-resources/ruby.rb I see these numbers:
ruby-mode
(8.701560543000001 95 1.045961102)
ruby-ts-mode
(34.653148898000005 1464 16.904981779)
>> This is not a problem when files are visited infrequently, but
>> becomes a problem for diff-syntax fontification that wants to
>> highlight simultaneously many files from git logs.
>> So a temporary measure would be not to enable ruby-ts-mode
>> in internal buffers:
>
> Is it common to try to highlight 1000 or even 100 files in one diff?
100 is rare, but tens is pretty common, so this problem affects
only this specific case.
>> (add-hook 'find-file-hook
>> (lambda ()
>> (when (and (eq major-mode 'ruby-mode)
>> ;; Only when not internal as from diff-syntax
>> (not (string-prefix-p " " (buffer-name))))
>> (ruby-ts-mode))))
>
> Have you tried similar tests with other -ts- modes? Ones with complex
> font-lock rules in particular.
I tried with c-ts-mode, and it's very fast.
> I've tried commenting out different rules in ruby-ts--font-lock-settings,
> but none of them seem to have particularly outsides impact. Performance
> seems, roughly, inversely proportional to the number of separate
> "features".
Indeed, this is what I see - no particular rule, only their number
affects performance.
> And if all ts modes turn out to have this problem, perhaps the place to
> improve this is inside some common code.
I noticed that while most library files are small, e.g.
libtree-sitter-c.so is 401,528 bytes,
libtree-sitter-ruby.so is 2,130,616 bytes
that means that it has more complex logic
that might explain its performance.
In this case, when nothing could be done to improve performance,
please close this request.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-10 8:10 ` Juri Linkov
@ 2023-01-10 14:10 ` Dmitry Gutov
2023-01-10 17:50 ` Juri Linkov
2023-01-11 12:12 ` Dmitry Gutov
0 siblings, 2 replies; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-10 14:10 UTC (permalink / raw)
To: Juri Linkov, Yuan Fu; +Cc: 60691
On 10/01/2023 10:10, Juri Linkov wrote:
>>> After more rules were added recently to ruby-ts--font-lock-settings,
>>> font-lock became slow even on very small files. Some measurements:
>>
>> If you saw a particular commit that made things slower, did you try
>> reverting it? What was the performance after?
>
> No particular commit, just adding more rules degrades performance
> gradually.
But I don't think I added that many rules recently. No more than a
quarter anyway.
>>> M-: (benchmark-run 1000 (progn (font-lock-mode -1) (font-lock-mode 1) (font-lock-ensure)))
>>> M-x ruby-mode
>>> (1.3564674989999999 0 0.0)
>>> M-x ruby-ts-mode
>>> (8.349582391999999 2 6.489918534000001)
>>
>> I have tried this scenario (which, to be frank, is pretty artificial, given
>> that fontification is usually performed in chunks, not over the whole
>> buffer).
>>
>> Perhaps the results depend on a particular file. The ones I have tried
>> (ruby.rb and ruby-after-operator-indent.rb) show only 2x difference (or
>> less). The difference was in favor of ruby-mode, but given the difference
>> in approaches I wouldn't be surprised if ruby-ts-mode incurs a fixed
>> overhead somewhere.
>
> On test/lisp/progmodes/ruby-mode-resources/ruby.rb I see these numbers:
>
> ruby-mode
> (8.701560543000001 95 1.045961102)
>
> ruby-ts-mode
> (34.653148898000005 1464 16.904981779)
Interesting. It's 12s vs 36s for me, as I've retested now.
>>> This is not a problem when files are visited infrequently, but
>>> becomes a problem for diff-syntax fontification that wants to
>>> highlight simultaneously many files from git logs.
>>> So a temporary measure would be not to enable ruby-ts-mode
>>> in internal buffers:
>>
>> Is it common to try to highlight 1000 or even 100 files in one diff?
>
> 100 is rare, but tens is pretty common, so this problem affects
> only this specific case.
So it's a 0,8-3s delay in those cases? That's not ideal.
>>> (add-hook 'find-file-hook
>>> (lambda ()
>>> (when (and (eq major-mode 'ruby-mode)
>>> ;; Only when not internal as from diff-syntax
>>> (not (string-prefix-p " " (buffer-name))))
>>> (ruby-ts-mode))))
>>
>> Have you tried similar tests with other -ts- modes? Ones with complex
>> font-lock rules in particular.
>
> I tried with c-ts-mode, and it's very fast.
Just how fast is it? The number of font-lock features is has is
comparable (though a little smaller).
I've tried the same benchmark for it in admin/alloc-colors.c, and it
comes out to
(3.2004193190000003 30 0.9609690980000067)
Which seems comparable.
Not sure how to directly test the modes against each other, but if I
enable ruby-ts-mode in the same file, the benchmark comes to 1s.
Or if I enable c-ts-mode in ruby.rb -- 16s.
>> I've tried commenting out different rules in ruby-ts--font-lock-settings,
>> but none of them seem to have particularly outsides impact. Performance
>> seems, roughly, inversely proportional to the number of separate
>> "features".
>
> Indeed, this is what I see - no particular rule, only their number
> affects performance.
>
>> And if all ts modes turn out to have this problem, perhaps the place to
>> improve this is inside some common code.
>
> I noticed that while most library files are small, e.g.
> libtree-sitter-c.so is 401,528 bytes,
> libtree-sitter-ruby.so is 2,130,616 bytes
> that means that it has more complex logic
> that might explain its performance.
ruby is indeed one of the larger ones. Among the ones I have here
compiled, it's exceeded only by cpp. 2.29 MB vs 2.12 MB.
But testing admin/alloc-colors.c with c++-ts-mode vs c-ts-mode gives
very similar performance, so it's unlikely that the complexity of the
grammar is directly responsible.
> In this case, when nothing could be done to improve performance,
> please close this request.
Perhaps Yuan has some further ideas. There are some strong oddities here:
- Some time into debugging and repeating the benchmark again and again,
I get the "Pure Lisp storage overflowed" message. Just once per Emacs
session. It doesn't seem to change much, so it might be unimportant.
- The profiler output looks like this:
18050 75% - font-lock-fontify-syntactically-region
15686 65% - treesit-font-lock-fontify-region
3738 15%
treesit--children-covering-range-recurse
188 0% treesit-fontify-with-override
- When running the benchmark for the first time in a buffer (such as
ruby.rb), the variable treesit--font-lock-fast-mode is usually changed
to t. In one Emacs session, after I changed it to nil and re-ran the
benchmark, the variable stayed nil, and the benchmark ran much faster
(like 10s vs 36s).
In the next session, after I restarted Emacs, that didn't happen: it
always stayed at t, even if I reset it to nil between runs. But if I
comment out the block in treesit-font-lock-fontify-region that uses it
;; (when treesit--font-lock-fast-mode
;; (setq nodes (treesit--children-covering-range-recurse
;; (car nodes) start end (* 4 jit-lock-chunk-size))))
and evaluate the defun, the benchmark runs much faster again: 11s.
(But then I brought it all back, and re-ran the tests, and the variable
stayed nil that time around; to sum up: the way it's turned on is unstable.)
Should treesit--font-lock-fast-mode be locally bound inside that
function, so that it's reset between chunks? Or maybe the condition for
its enabling should be tweaked? E.g. I don't think there are any
particularly large or deep nodes in ruby.rb's parse tree. It's a very
shallow file.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-10 14:10 ` Dmitry Gutov
@ 2023-01-10 17:50 ` Juri Linkov
2023-01-11 12:12 ` Dmitry Gutov
2023-01-11 12:12 ` Dmitry Gutov
1 sibling, 1 reply; 28+ messages in thread
From: Juri Linkov @ 2023-01-10 17:50 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: Yuan Fu, 60691
>>> Is it common to try to highlight 1000 or even 100 files in one diff?
>> 100 is rare, but tens is pretty common, so this problem affects
>> only this specific case.
>
> So it's a 0,8-3s delay in those cases? That's not ideal.
The delay is noticeable, alas.
>> I noticed that while most library files are small, e.g.
>> libtree-sitter-c.so is 401,528 bytes,
>> libtree-sitter-ruby.so is 2,130,616 bytes
>> that means that it has more complex logic
>> that might explain its performance.
>
> ruby is indeed one of the larger ones. Among the ones I have here compiled,
> it's exceeded only by cpp. 2.29 MB vs 2.12 MB.
The winner is libtree-sitter-julia.so with 7.25 MB.
But regarding libtree-sitter-cpp.so I confirm it's 2.3 MB.
And c++-ts-mode is even faster than c-ts-mode.
On the same admin/alloc-colors.c:
c-mode
(33.378821569 1500 17.632000617)
c-ts-mode
(2.1949608069999997 34 0.4119784769999981)
c++-ts-mode
(2.0979403910000003 34 0.39749122499999956)
So size doesn't matter.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-10 17:50 ` Juri Linkov
@ 2023-01-11 12:12 ` Dmitry Gutov
0 siblings, 0 replies; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-11 12:12 UTC (permalink / raw)
To: Juri Linkov; +Cc: Yuan Fu, 60691
On 10/01/2023 19:50, Juri Linkov wrote:
>>>> Is it common to try to highlight 1000 or even 100 files in one diff?
>>> 100 is rare, but tens is pretty common, so this problem affects
>>> only this specific case.
>>
>> So it's a 0,8-3s delay in those cases? That's not ideal.
>
> The delay is noticeable, alas.
Right. I'm somewhat worried for the processing speed
xref--collect-matches too. But that's probably only going to be
noticeable after we add syntax-propertize-function to ruby-ts-mode.
>>> I noticed that while most library files are small, e.g.
>>> libtree-sitter-c.so is 401,528 bytes,
>>> libtree-sitter-ruby.so is 2,130,616 bytes
>>> that means that it has more complex logic
>>> that might explain its performance.
>>
>> ruby is indeed one of the larger ones. Among the ones I have here compiled,
>> it's exceeded only by cpp. 2.29 MB vs 2.12 MB.
>
> The winner is libtree-sitter-julia.so with 7.25 MB.
> But regarding libtree-sitter-cpp.so I confirm it's 2.3 MB.
> And c++-ts-mode is even faster than c-ts-mode.
Yep.
> On the same admin/alloc-colors.c:
>
> c-mode
> (33.378821569 1500 17.632000617)
>
> c-ts-mode
> (2.1949608069999997 34 0.4119784769999981)
>
> c++-ts-mode
> (2.0979403910000003 34 0.39749122499999956)
>
> So size doesn't matter.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-10 14:10 ` Dmitry Gutov
2023-01-10 17:50 ` Juri Linkov
@ 2023-01-11 12:12 ` Dmitry Gutov
1 sibling, 0 replies; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-11 12:12 UTC (permalink / raw)
To: Juri Linkov, Yuan Fu; +Cc: 60691
Yuan? Just making sure you got this message.
On 10/01/2023 16:10, Dmitry Gutov wrote:
> Perhaps Yuan has some further ideas. There are some strong oddities here:
>
> - Some time into debugging and repeating the benchmark again and again,
> I get the "Pure Lisp storage overflowed" message. Just once per Emacs
> session. It doesn't seem to change much, so it might be unimportant.
>
> - The profiler output looks like this:
>
> 18050 75% - font-lock-fontify-syntactically-region
> 15686 65% - treesit-font-lock-fontify-region
> 3738 15% treesit--children-covering-range-recurse
> 188 0% treesit-fontify-with-override
>
> - When running the benchmark for the first time in a buffer (such as
> ruby.rb), the variable treesit--font-lock-fast-mode is usually changed
> to t. In one Emacs session, after I changed it to nil and re-ran the
> benchmark, the variable stayed nil, and the benchmark ran much faster
> (like 10s vs 36s).
>
> In the next session, after I restarted Emacs, that didn't happen: it
> always stayed at t, even if I reset it to nil between runs. But if I
> comment out the block in treesit-font-lock-fontify-region that uses it
>
> ;; (when treesit--font-lock-fast-mode
> ;; (setq nodes (treesit--children-covering-range-recurse
> ;; (car nodes) start end (* 4 jit-lock-chunk-size))))
>
> and evaluate the defun, the benchmark runs much faster again: 11s.
>
> (But then I brought it all back, and re-ran the tests, and the variable
> stayed nil that time around; to sum up: the way it's turned on is
> unstable.)
>
> Should treesit--font-lock-fast-mode be locally bound inside that
> function, so that it's reset between chunks? Or maybe the condition for
> its enabling should be tweaked? E.g. I don't think there are any
> particularly large or deep nodes in ruby.rb's parse tree. It's a very
> shallow file.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-09 17:16 bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode Juri Linkov
2023-01-09 22:33 ` Dmitry Gutov
@ 2023-01-12 21:58 ` Yuan Fu
2023-01-12 23:40 ` Dmitry Gutov
2023-01-18 6:50 ` Yuan Fu
2023-01-29 8:25 ` Yuan Fu
3 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-12 21:58 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691, juri
Dmitry Gutov <dgutov@yandex.ru> writes:
> Yuan? Just making sure you got this message.
Sorry for the delay :-)
> On 10/01/2023 16:10, Dmitry Gutov wrote:
>> Perhaps Yuan has some further ideas. There are some strong oddities here:
>> - Some time into debugging and repeating the benchmark again and
>> again, I get the "Pure Lisp storage overflowed" message. Just once
>> per Emacs session. It doesn't seem to change much, so it might be
>> unimportant.
That sounds like 60653. The next time you encounter it, could you record
the output of M-x memory-usage and M-x memory-report?
>> - The profiler output looks like this:
>> 18050 75% -
>> font-lock-fontify-syntactically-region
>> 15686 65% - treesit-font-lock-fontify-region
>> 3738 15% treesit--children-covering-range-recurse
>> 188 0% treesit-fontify-with-override
>> - When running the benchmark for the first time in a buffer (such as
>> ruby.rb), the variable treesit--font-lock-fast-mode is usually
>> changed to t. In one Emacs session, after I changed it to nil and
>> re-ran the benchmark, the variable stayed nil, and the benchmark ran
>> much faster (like 10s vs 36s).
>> In the next session, after I restarted Emacs, that didn't happen: it
>> always stayed at t, even if I reset it to nil between runs. But if I
>> comment out the block in treesit-font-lock-fontify-region that uses
>> it
>> ;; (when treesit--font-lock-fast-mode
>> ;; (setq nodes (treesit--children-covering-range-recurse
>> ;; (car nodes) start end (* 4 jit-lock-chunk-size))))
>> and evaluate the defun, the benchmark runs much faster again: 11s.
>> (But then I brought it all back, and re-ran the tests, and the
>> variable stayed nil that time around; to sum up: the way it's turned
>> on is unstable.)
>> Should treesit--font-lock-fast-mode be locally bound inside that
>> function, so that it's reset between chunks? Or maybe the condition
>> for its enabling should be tweaked? E.g. I don't think there are any
>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>> very shallow file.
Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
function that checks the maximum depth of a parse tree and the maximum
node span, and turn on the fast-mode if the depth is too large or a node
is too wide. And we do that check once before doing any fontification.
I’ll report back once I add it.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-12 21:58 ` Yuan Fu
@ 2023-01-12 23:40 ` Dmitry Gutov
2023-01-13 7:57 ` Eli Zaretskii
0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-12 23:40 UTC (permalink / raw)
To: Yuan Fu; +Cc: 60691, juri
On 12/01/2023 23:58, Yuan Fu wrote:
>
> Dmitry Gutov <dgutov@yandex.ru> writes:
>
>> Yuan? Just making sure you got this message.
>
> Sorry for the delay :-)
>
>> On 10/01/2023 16:10, Dmitry Gutov wrote:
>>> Perhaps Yuan has some further ideas. There are some strong oddities here:
>>> - Some time into debugging and repeating the benchmark again and
>>> again, I get the "Pure Lisp storage overflowed" message. Just once
>>> per Emacs session. It doesn't seem to change much, so it might be
>>> unimportant.
>
> That sounds like 60653. The next time you encounter it, could you record
> the output of M-x memory-usage and M-x memory-report?
Managed to reproduce this after running the test in a couple of
different files.
But 'M-x memory-usage' says no such command, and 'M-x memory-report'
ends up with this error:
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
memory-report--gc-elem(nil strings)
memory-report--garbage-collect()
memory-report()
funcall-interactively(memory-report)
#<subr call-interactively>(memory-report record nil)
apply(#<subr call-interactively> memory-report (record nil))
call-interactively@ido-cr+-record-current-command(#<subr
call-interactively> memory-report record nil)
apply(call-interactively@ido-cr+-record-current-command #<subr
call-interactively> (memory-report record nil))
call-interactively(memory-report record nil)
command-execute(memory-report record)
execute-extended-command(nil "memory-report" nil)
funcall-interactively(execute-extended-command nil "memory-report" nil)
#<subr call-interactively>(execute-extended-command nil nil)
apply(#<subr call-interactively> execute-extended-command (nil nil))
call-interactively@ido-cr+-record-current-command(#<subr
call-interactively> execute-extended-command nil nil)
apply(call-interactively@ido-cr+-record-current-command #<subr
call-interactively> (execute-extended-command nil nil))
call-interactively(execute-extended-command nil nil)
command-execute(execute-extended-command)
garbage-collect's docstring says:
However, if there was overflow in pure space, and Emacs was dumped
using the "unexec" method, ‘garbage-collect’ returns nil, because
real GC can’t be done.
I don't know if my Emacs was dumped using "unexec", though. ./configure
says I'm using pdumper.
In case that matters, I'm testing the emacs-29 branch.
>>> - The profiler output looks like this:
>>> 18050 75% -
>>> font-lock-fontify-syntactically-region
>>> 15686 65% - treesit-font-lock-fontify-region
>>> 3738 15% treesit--children-covering-range-recurse
>>> 188 0% treesit-fontify-with-override
>>> - When running the benchmark for the first time in a buffer (such as
>>> ruby.rb), the variable treesit--font-lock-fast-mode is usually
>>> changed to t. In one Emacs session, after I changed it to nil and
>>> re-ran the benchmark, the variable stayed nil, and the benchmark ran
>>> much faster (like 10s vs 36s).
>>> In the next session, after I restarted Emacs, that didn't happen: it
>>> always stayed at t, even if I reset it to nil between runs. But if I
>>> comment out the block in treesit-font-lock-fontify-region that uses
>>> it
>>> ;; (when treesit--font-lock-fast-mode
>>> ;; (setq nodes (treesit--children-covering-range-recurse
>>> ;; (car nodes) start end (* 4 jit-lock-chunk-size))))
>>> and evaluate the defun, the benchmark runs much faster again: 11s.
>>> (But then I brought it all back, and re-ran the tests, and the
>>> variable stayed nil that time around; to sum up: the way it's turned
>>> on is unstable.)
>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>> function, so that it's reset between chunks? Or maybe the condition
>>> for its enabling should be tweaked? E.g. I don't think there are any
>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>> very shallow file.
>
> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
> function that checks the maximum depth of a parse tree and the maximum
> node span, and turn on the fast-mode if the depth is too large or a node
> is too wide. And we do that check once before doing any fontification.
>
> I’ll report back once I add it.
Thanks!
And if the check can be fast enough, we could probably do it in the
beginning of fontifying every chunk.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-12 23:40 ` Dmitry Gutov
@ 2023-01-13 7:57 ` Eli Zaretskii
2023-01-13 9:15 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2023-01-13 7:57 UTC (permalink / raw)
To: casouri, Dmitry Gutov; +Cc: 60691, Stefan Monnier, juri
> Cc: 60691@debbugs.gnu.org, juri@linkov.net
> Date: Fri, 13 Jan 2023 01:40:56 +0200
> From: Dmitry Gutov <dgutov@yandex.ru>
>
> Managed to reproduce this after running the test in a couple of
> different files.
>
> But 'M-x memory-usage' says no such command, and 'M-x memory-report'
> ends up with this error:
>
> Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
> memory-report--gc-elem(nil strings)
> memory-report--garbage-collect()
> memory-report()
This means GC is disabled in this session at the time you invoke
memory-report. Which shouldn't happen, of course. It sounds like
your pure Lisp storage overflowed, and that disabled GC.
And I think I see the problem: we use build_pure_c_string in treesit.c
in places that we shouldn't.
Yuan, build_pure_c_string should only be used in places such as
syms_of_treesit, which are called just once, during dumping. Look at
all the other calls to this function in the sources, and you will see
it. In all other cases, you should do one of the following:
. for strings whose text is fixed, define a variable, give it the
value in syms_of_treesit using build_pure_c_string, then use that
variable elsewhere in the source
. for strings whose text depends on run-time information, use
AUTO_STRING or build_string
This is a serious problem, and we should fix it ASAP.
> garbage-collect's docstring says:
>
> However, if there was overflow in pure space, and Emacs was dumped
> using the "unexec" method, ‘garbage-collect’ returns nil, because
> real GC can’t be done.
>
> I don't know if my Emacs was dumped using "unexec", though. ./configure
> says I'm using pdumper.
The above text doesn't account for bugs ;-) Functions that produce
objects in pure space are supposed to be called only during the build,
a.k.a. "when dumping", and for that the text is correct.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-13 7:57 ` Eli Zaretskii
@ 2023-01-13 9:15 ` Yuan Fu
2023-01-13 11:51 ` Eli Zaretskii
0 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-13 9:15 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Juri Linkov, 60691, Stefan Monnier, Dmitry Gutov
> On Jan 12, 2023, at 11:57 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> Cc: 60691@debbugs.gnu.org, juri@linkov.net
>> Date: Fri, 13 Jan 2023 01:40:56 +0200
>> From: Dmitry Gutov <dgutov@yandex.ru>
>>
>> Managed to reproduce this after running the test in a couple of
>> different files.
>>
>> But 'M-x memory-usage' says no such command, and 'M-x memory-report'
>> ends up with this error:
>>
>> Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
>> memory-report--gc-elem(nil strings)
>> memory-report--garbage-collect()
>> memory-report()
>
> This means GC is disabled in this session at the time you invoke
> memory-report. Which shouldn't happen, of course. It sounds like
> your pure Lisp storage overflowed, and that disabled GC.
>
> And I think I see the problem: we use build_pure_c_string in treesit.c
> in places that we shouldn't.
>
> Yuan, build_pure_c_string should only be used in places such as
> syms_of_treesit, which are called just once, during dumping. Look at
> all the other calls to this function in the sources, and you will see
> it. In all other cases, you should do one of the following:
>
> . for strings whose text is fixed, define a variable, give it the
> value in syms_of_treesit using build_pure_c_string, then use that
> variable elsewhere in the source
Can I define a bunch of static C variables and initialize them in syms_of_treesit, or they have to be all Lisp variables? Eg,
static Lisp_Object TREESIT_STAR;
...
void
syms_of_treesit (void)
{
...
TREESIT_STAR = build_pure_c_string ("*");
...
}
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-13 9:15 ` Yuan Fu
@ 2023-01-13 11:51 ` Eli Zaretskii
2023-01-14 3:48 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2023-01-13 11:51 UTC (permalink / raw)
To: Yuan Fu; +Cc: juri, 60691, monnier, dgutov
> From: Yuan Fu <casouri@gmail.com>
> Date: Fri, 13 Jan 2023 01:15:09 -0800
> Cc: Dmitry Gutov <dgutov@yandex.ru>,
> 60691@debbugs.gnu.org,
> Juri Linkov <juri@linkov.net>,
> Stefan Monnier <monnier@iro.umontreal.ca>
>
> > On Jan 12, 2023, at 11:57 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> >
> >> Cc: 60691@debbugs.gnu.org, juri@linkov.net
> >> Date: Fri, 13 Jan 2023 01:40:56 +0200
> >> From: Dmitry Gutov <dgutov@yandex.ru>
> >>
> >> Managed to reproduce this after running the test in a couple of
> >> different files.
> >>
> >> But 'M-x memory-usage' says no such command, and 'M-x memory-report'
> >> ends up with this error:
> >>
> >> Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
> >> memory-report--gc-elem(nil strings)
> >> memory-report--garbage-collect()
> >> memory-report()
> >
> > This means GC is disabled in this session at the time you invoke
> > memory-report. Which shouldn't happen, of course. It sounds like
> > your pure Lisp storage overflowed, and that disabled GC.
> >
> > And I think I see the problem: we use build_pure_c_string in treesit.c
> > in places that we shouldn't.
> >
> > Yuan, build_pure_c_string should only be used in places such as
> > syms_of_treesit, which are called just once, during dumping. Look at
> > all the other calls to this function in the sources, and you will see
> > it. In all other cases, you should do one of the following:
> >
> > . for strings whose text is fixed, define a variable, give it the
> > value in syms_of_treesit using build_pure_c_string, then use that
> > variable elsewhere in the source
>
> Can I define a bunch of static C variables and initialize them in syms_of_treesit, or they have to be all Lisp variables? Eg,
>
> static Lisp_Object TREESIT_STAR;
>
> ...
>
> void
> syms_of_treesit (void)
> {
> ...
> TREESIT_STAR = build_pure_c_string ("*");
> ...
> }
Yes, of course. Look, for example, how coding.c does that:
/* A string that serves as name of the reusable work buffer, and as base
name of temporary work buffers used for code-conversion operations. */
static Lisp_Object Vcode_conversion_workbuf_name;
[...]
void
syms_of_coding (void)
{
[...]
staticpro (&Vcode_conversion_workbuf_name);
Vcode_conversion_workbuf_name = build_pure_c_string (" *code-conversion-work*");
But please keep the convention of naming such variables Vsome_thing,
both regarding the "V" and the fact that the name is otherwise
lower-case.
Thanks.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-13 11:51 ` Eli Zaretskii
@ 2023-01-14 3:48 ` Yuan Fu
2023-01-14 7:29 ` Eli Zaretskii
0 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-14 3:48 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: juri, 60691, monnier, dgutov
> On Jan 13, 2023, at 3:51 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Fri, 13 Jan 2023 01:15:09 -0800
>> Cc: Dmitry Gutov <dgutov@yandex.ru>,
>> 60691@debbugs.gnu.org,
>> Juri Linkov <juri@linkov.net>,
>> Stefan Monnier <monnier@iro.umontreal.ca>
>>
>>> On Jan 12, 2023, at 11:57 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>>>
>>>> Cc: 60691@debbugs.gnu.org, juri@linkov.net
>>>> Date: Fri, 13 Jan 2023 01:40:56 +0200
>>>> From: Dmitry Gutov <dgutov@yandex.ru>
>>>>
>>>> Managed to reproduce this after running the test in a couple of
>>>> different files.
>>>>
>>>> But 'M-x memory-usage' says no such command, and 'M-x memory-report'
>>>> ends up with this error:
>>>>
>>>> Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
>>>> memory-report--gc-elem(nil strings)
>>>> memory-report--garbage-collect()
>>>> memory-report()
>>>
>>> This means GC is disabled in this session at the time you invoke
>>> memory-report. Which shouldn't happen, of course. It sounds like
>>> your pure Lisp storage overflowed, and that disabled GC.
>>>
>>> And I think I see the problem: we use build_pure_c_string in treesit.c
>>> in places that we shouldn't.
>>>
>>> Yuan, build_pure_c_string should only be used in places such as
>>> syms_of_treesit, which are called just once, during dumping. Look at
>>> all the other calls to this function in the sources, and you will see
>>> it. In all other cases, you should do one of the following:
>>>
>>> . for strings whose text is fixed, define a variable, give it the
>>> value in syms_of_treesit using build_pure_c_string, then use that
>>> variable elsewhere in the source
>>
>> Can I define a bunch of static C variables and initialize them in syms_of_treesit, or they have to be all Lisp variables? Eg,
>>
>> static Lisp_Object TREESIT_STAR;
>>
>> ...
>>
>> void
>> syms_of_treesit (void)
>> {
>> ...
>> TREESIT_STAR = build_pure_c_string ("*");
>> ...
>> }
>
> Yes, of course. Look, for example, how coding.c does that:
>
> /* A string that serves as name of the reusable work buffer, and as base
> name of temporary work buffers used for code-conversion operations. */
> static Lisp_Object Vcode_conversion_workbuf_name;
> [...]
> void
> syms_of_coding (void)
> {
> [...]
> staticpro (&Vcode_conversion_workbuf_name);
> Vcode_conversion_workbuf_name = build_pure_c_string (" *code-conversion-work*");
>
> But please keep the convention of naming such variables Vsome_thing,
> both regarding the "V" and the fact that the name is otherwise
> lower-case.
Thanks, I pushed a fix for it. I also used intern_c_string in some places like these:
intern_c_string (":?”)
intern_c_string (":*")
I want to change them to use DEFSYM, but what should be the c name for them?
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-14 3:48 ` Yuan Fu
@ 2023-01-14 7:29 ` Eli Zaretskii
2023-01-14 7:51 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Eli Zaretskii @ 2023-01-14 7:29 UTC (permalink / raw)
To: Yuan Fu; +Cc: juri, 60691, monnier, dgutov
> From: Yuan Fu <casouri@gmail.com>
> Date: Fri, 13 Jan 2023 19:48:40 -0800
> Cc: dgutov@yandex.ru,
> 60691@debbugs.gnu.org,
> juri@linkov.net,
> monnier@iro.umontreal.ca
>
> Thanks, I pushed a fix for it. I also used intern_c_string in some places like these:
>
> intern_c_string (":?”)
> intern_c_string (":*")
>
> I want to change them to use DEFSYM, but what should be the c name for them?
Yes, DEFSYM is better in such cases. The C name can be QCquestion and
QCasterix, for example.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-14 7:29 ` Eli Zaretskii
@ 2023-01-14 7:51 ` Yuan Fu
2023-01-14 8:01 ` Eli Zaretskii
2023-01-14 8:46 ` Andreas Schwab
0 siblings, 2 replies; 28+ messages in thread
From: Yuan Fu @ 2023-01-14 7:51 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: juri, 60691, monnier, dgutov
> On Jan 13, 2023, at 11:29 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Fri, 13 Jan 2023 19:48:40 -0800
>> Cc: dgutov@yandex.ru,
>> 60691@debbugs.gnu.org,
>> juri@linkov.net,
>> monnier@iro.umontreal.ca
>>
>> Thanks, I pushed a fix for it. I also used intern_c_string in some places like these:
>>
>> intern_c_string (":?”)
>> intern_c_string (":*")
>>
>> I want to change them to use DEFSYM, but what should be the c name for them?
>
> Yes, DEFSYM is better in such cases. The C name can be QCquestion and
> QCasterix, for example.
My worry is that they will conflict with, eg, symbol `question’ and `asterix’, if someone ever defines them in the C codebase. Is that not possible?
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-14 7:51 ` Yuan Fu
@ 2023-01-14 8:01 ` Eli Zaretskii
2023-01-14 8:46 ` Andreas Schwab
1 sibling, 0 replies; 28+ messages in thread
From: Eli Zaretskii @ 2023-01-14 8:01 UTC (permalink / raw)
To: Yuan Fu; +Cc: juri, 60691, monnier, dgutov
> From: Yuan Fu <casouri@gmail.com>
> Date: Fri, 13 Jan 2023 23:51:05 -0800
> Cc: dgutov@yandex.ru,
> 60691@debbugs.gnu.org,
> juri@linkov.net,
> monnier@iro.umontreal.ca
>
> > Yes, DEFSYM is better in such cases. The C name can be QCquestion and
> > QCasterix, for example.
>
> My worry is that they will conflict with, eg, symbol `question’ and `asterix’, if someone ever defines them in the C codebase. Is that not possible?
It's possible, but how can a symbol conflict in that case? it will
just be reused.
But if you want to have treesit-specific symbols, you can use names
like QCasterix_treesit.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-14 7:51 ` Yuan Fu
2023-01-14 8:01 ` Eli Zaretskii
@ 2023-01-14 8:46 ` Andreas Schwab
2023-01-14 23:03 ` Yuan Fu
1 sibling, 1 reply; 28+ messages in thread
From: Andreas Schwab @ 2023-01-14 8:46 UTC (permalink / raw)
To: Yuan Fu; +Cc: Eli Zaretskii, dgutov, 60691, monnier, juri
On Jan 13 2023, Yuan Fu wrote:
>> On Jan 13, 2023, at 11:29 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>>
>>> From: Yuan Fu <casouri@gmail.com>
>>> Date: Fri, 13 Jan 2023 19:48:40 -0800
>>> Cc: dgutov@yandex.ru,
>>> 60691@debbugs.gnu.org,
>>> juri@linkov.net,
>>> monnier@iro.umontreal.ca
>>>
>>> Thanks, I pushed a fix for it. I also used intern_c_string in some places like these:
>>>
>>> intern_c_string (":?”)
>>> intern_c_string (":*")
>>>
>>> I want to change them to use DEFSYM, but what should be the c name for them?
>>
>> Yes, DEFSYM is better in such cases. The C name can be QCquestion and
>> QCasterix, for example.
>
> My worry is that they will conflict with, eg, symbol `question’ and `asterix’, if someone ever defines them in the C codebase. Is that not possible?
The C name of the symbol `question' would be Qquestion, without the C
(which stands for the `:' prefix).
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1
"And now for something completely different."
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-14 8:46 ` Andreas Schwab
@ 2023-01-14 23:03 ` Yuan Fu
0 siblings, 0 replies; 28+ messages in thread
From: Yuan Fu @ 2023-01-14 23:03 UTC (permalink / raw)
To: Andreas Schwab; +Cc: Eli Zaretskii, dgutov, 60691, Stefan Monnier, Juri Linkov
> On Jan 14, 2023, at 12:46 AM, Andreas Schwab <schwab@linux-m68k.org> wrote:
>
> On Jan 13 2023, Yuan Fu wrote:
>
>>> On Jan 13, 2023, at 11:29 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>>>
>>>> From: Yuan Fu <casouri@gmail.com>
>>>> Date: Fri, 13 Jan 2023 19:48:40 -0800
>>>> Cc: dgutov@yandex.ru,
>>>> 60691@debbugs.gnu.org,
>>>> juri@linkov.net,
>>>> monnier@iro.umontreal.ca
>>>>
>>>> Thanks, I pushed a fix for it. I also used intern_c_string in some places like these:
>>>>
>>>> intern_c_string (":?”)
>>>> intern_c_string (":*")
>>>>
>>>> I want to change them to use DEFSYM, but what should be the c name for them?
>>>
>>> Yes, DEFSYM is better in such cases. The C name can be QCquestion and
>>> QCasterix, for example.
>>
>> My worry is that they will conflict with, eg, symbol `question’ and `asterix’, if someone ever defines them in the C codebase. Is that not possible?
>
> The C name of the symbol `question' would be Qquestion, without the C
> (which stands for the `:' prefix).
Sorry, I meant `:question’, and `:asterix’.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-09 17:16 bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode Juri Linkov
2023-01-09 22:33 ` Dmitry Gutov
2023-01-12 21:58 ` Yuan Fu
@ 2023-01-18 6:50 ` Yuan Fu
2023-01-19 18:28 ` Dmitry Gutov
2023-01-29 8:25 ` Yuan Fu
3 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-18 6:50 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691, juri
Yuan Fu <casouri@gmail.com> writes:
> Dmitry Gutov <dgutov@yandex.ru> writes:
>
>> Yuan? Just making sure you got this message.
>
> Sorry for the delay :-)
>
>> On 10/01/2023 16:10, Dmitry Gutov wrote:
>>> Perhaps Yuan has some further ideas. There are some strong oddities here:
>>> - Some time into debugging and repeating the benchmark again and
>>> again, I get the "Pure Lisp storage overflowed" message. Just once
>>> per Emacs session. It doesn't seem to change much, so it might be
>>> unimportant.
>
> That sounds like 60653. The next time you encounter it, could you record
> the output of M-x memory-usage and M-x memory-report?
>
>>> - The profiler output looks like this:
>>> 18050 75% -
>>> font-lock-fontify-syntactically-region
>>> 15686 65% - treesit-font-lock-fontify-region
>>> 3738 15% treesit--children-covering-range-recurse
>>> 188 0% treesit-fontify-with-override
>>> - When running the benchmark for the first time in a buffer (such as
>>> ruby.rb), the variable treesit--font-lock-fast-mode is usually
>>> changed to t. In one Emacs session, after I changed it to nil and
>>> re-ran the benchmark, the variable stayed nil, and the benchmark ran
>>> much faster (like 10s vs 36s).
>>> In the next session, after I restarted Emacs, that didn't happen: it
>>> always stayed at t, even if I reset it to nil between runs. But if I
>>> comment out the block in treesit-font-lock-fontify-region that uses
>>> it
>>> ;; (when treesit--font-lock-fast-mode
>>> ;; (setq nodes (treesit--children-covering-range-recurse
>>> ;; (car nodes) start end (* 4 jit-lock-chunk-size))))
>>> and evaluate the defun, the benchmark runs much faster again: 11s.
>>> (But then I brought it all back, and re-ran the tests, and the
>>> variable stayed nil that time around; to sum up: the way it's turned
>>> on is unstable.)
>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>> function, so that it's reset between chunks? Or maybe the condition
>>> for its enabling should be tweaked? E.g. I don't think there are any
>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>> very shallow file.
>
> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
> function that checks the maximum depth of a parse tree and the maximum
> node span, and turn on the fast-mode if the depth is too large or a node
> is too wide. And we do that check once before doing any fontification.
>
> I’ll report back once I add it.
I wrote that function. But I didn’t end up using it. Instead I added a
"grace count", so that the query time has to be longer than the
threshold 5 times before we switch on the fast mode instead of 1.
My main worry is that simply looking at the parse tree would not catch
all the case where there will be expensive queries.
Could you try the latest commit and see if the fast mode still switches
on when it shouldn’t?
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-18 6:50 ` Yuan Fu
@ 2023-01-19 18:28 ` Dmitry Gutov
2023-01-20 22:24 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-19 18:28 UTC (permalink / raw)
To: Yuan Fu; +Cc: 60691, juri
Hi Yuan,
On 18/01/2023 08:50, Yuan Fu wrote:
>>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>>> function, so that it's reset between chunks? Or maybe the condition
>>>> for its enabling should be tweaked? E.g. I don't think there are any
>>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>>> very shallow file.
>>
>> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
>> function that checks the maximum depth of a parse tree and the maximum
>> node span, and turn on the fast-mode if the depth is too large or a node
>> is too wide. And we do that check once before doing any fontification.
>>
>> I’ll report back once I add it.
>
> I wrote that function. But I didn’t end up using it. Instead I added a
> "grace count", so that the query time has to be longer than the
> threshold 5 times before we switch on the fast mode instead of 1.
>
> My main worry is that simply looking at the parse tree would not catch
> all the case where there will be expensive queries.
That might be true, but a criterion that doesn't specify conditions
exactly can give no guarantee against false positives.
> Could you try the latest commit and see if the fast mode still switches
> on when it shouldn’t?
At first it seemed to help, but then I switched the major mode a couple
more times, and ran the benchmark twice more, and the "fast mode"
switched on again.
Which seems to make sense: there is no resetting the counter, right?
So if previously it happened once somehow during a certain scenario, now
I have to repeat the same scenario 4 times, and the condition is met.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-19 18:28 ` Dmitry Gutov
@ 2023-01-20 22:24 ` Yuan Fu
2023-01-22 2:01 ` Dmitry Gutov
0 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-20 22:24 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691, juri
> On Jan 19, 2023, at 10:28 AM, Dmitry Gutov <dgutov@yandex.ru> wrote:
>
> Hi Yuan,
>
> On 18/01/2023 08:50, Yuan Fu wrote:
>>>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>>>> function, so that it's reset between chunks? Or maybe the condition
>>>>> for its enabling should be tweaked? E.g. I don't think there are any
>>>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>>>> very shallow file.
>>>
>>> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
>>> function that checks the maximum depth of a parse tree and the maximum
>>> node span, and turn on the fast-mode if the depth is too large or a node
>>> is too wide. And we do that check once before doing any fontification.
>>>
>>> I’ll report back once I add it.
>> I wrote that function. But I didn’t end up using it. Instead I added a
>> "grace count", so that the query time has to be longer than the
>> threshold 5 times before we switch on the fast mode instead of 1.
>> My main worry is that simply looking at the parse tree would not catch
>> all the case where there will be expensive queries.
>
> That might be true, but a criterion that doesn't specify conditions exactly can give no guarantee against false positives.
The condition is “query is (consistently) slow”, that’s why I thought measuring the time is the most direct way.
>
>> Could you try the latest commit and see if the fast mode still switches
>> on when it shouldn’t?
>
> At first it seemed to help, but then I switched the major mode a couple more times, and ran the benchmark twice more, and the "fast mode" switched on again.
>
> Which seems to make sense: there is no resetting the counter, right?
>
> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
I was hoping that the scenario only happen once, oh well :-) I’ll change the decision based on analyzing the tree’s dimension: too deep or too wide activates the fast mode. Let’s see how it works.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-20 22:24 ` Yuan Fu
@ 2023-01-22 2:01 ` Dmitry Gutov
0 siblings, 0 replies; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-22 2:01 UTC (permalink / raw)
To: Yuan Fu; +Cc: 60691, juri
On 21/01/2023 00:24, Yuan Fu wrote:
>
>
>> On Jan 19, 2023, at 10:28 AM, Dmitry Gutov <dgutov@yandex.ru> wrote:
>>
>> Hi Yuan,
>>
>> On 18/01/2023 08:50, Yuan Fu wrote:
>>>>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>>>>> function, so that it's reset between chunks? Or maybe the condition
>>>>>> for its enabling should be tweaked? E.g. I don't think there are any
>>>>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>>>>> very shallow file.
>>>>
>>>> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
>>>> function that checks the maximum depth of a parse tree and the maximum
>>>> node span, and turn on the fast-mode if the depth is too large or a node
>>>> is too wide. And we do that check once before doing any fontification.
>>>>
>>>> I’ll report back once I add it.
>>> I wrote that function. But I didn’t end up using it. Instead I added a
>>> "grace count", so that the query time has to be longer than the
>>> threshold 5 times before we switch on the fast mode instead of 1.
>>> My main worry is that simply looking at the parse tree would not catch
>>> all the case where there will be expensive queries.
>>
>> That might be true, but a criterion that doesn't specify conditions exactly can give no guarantee against false positives.
>
> The condition is “query is (consistently) slow”, that’s why I thought measuring the time is the most direct way.
The benchmark itself might be artificial, in that it's measuring the
font-lock of a specific buffer, in whole, for 1000 iterations. But Juri
must have come up with the original report based on real usage scenario.
OTOH, the scenario which it might correspond to, is used typing in the
same buffer for a long time (triggering thousands of refontifications,
possibly partial ones). I don't know if it's feasible to try to
reproduce it specifically. But, again, anything that can happen once can
happen 4 more times.
>>> Could you try the latest commit and see if the fast mode still switches
>>> on when it shouldn’t?
>>
>> At first it seemed to help, but then I switched the major mode a couple more times, and ran the benchmark twice more, and the "fast mode" switched on again.
>>
>> Which seems to make sense: there is no resetting the counter, right?
>>
>> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
>
> I was hoping that the scenario only happen once, oh well :-) I’ll change the decision based on analyzing the tree’s dimension: too deep or too wide activates the fast mode. Let’s see how it works.
Thank you, let me know when it's time to test again.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-09 17:16 bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode Juri Linkov
` (2 preceding siblings ...)
2023-01-18 6:50 ` Yuan Fu
@ 2023-01-29 8:25 ` Yuan Fu
2023-01-29 23:07 ` Dmitry Gutov
3 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-29 8:25 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691, juri
Dmitry Gutov <dgutov@yandex.ru> writes:
> On 21/01/2023 00:24, Yuan Fu wrote:
>>
>>> On Jan 19, 2023, at 10:28 AM, Dmitry Gutov <dgutov@yandex.ru> wrote:
>>>
>>> Hi Yuan,
>>>
>>> On 18/01/2023 08:50, Yuan Fu wrote:
>>>>>>> Should treesit--font-lock-fast-mode be locally bound inside that
>>>>>>> function, so that it's reset between chunks? Or maybe the condition
>>>>>>> for its enabling should be tweaked? E.g. I don't think there are any
>>>>>>> particularly large or deep nodes in ruby.rb's parse tree. It's a
>>>>>>> very shallow file.
>>>>>
>>>>> Yeah that is a not-very-clever hack. I’ve got an idea: I can add a C
>>>>> function that checks the maximum depth of a parse tree and the maximum
>>>>> node span, and turn on the fast-mode if the depth is too large or a node
>>>>> is too wide. And we do that check once before doing any fontification.
>>>>>
>>>>> I’ll report back once I add it.
>>>> I wrote that function. But I didn’t end up using it. Instead I added a
>>>> "grace count", so that the query time has to be longer than the
>>>> threshold 5 times before we switch on the fast mode instead of 1.
>>>> My main worry is that simply looking at the parse tree would not catch
>>>> all the case where there will be expensive queries.
>>>
>>> That might be true, but a criterion that doesn't specify conditions exactly can give no guarantee against false positives.
>> The condition is “query is (consistently) slow”, that’s why I
>> thought measuring the time is the most direct way.
>
> The benchmark itself might be artificial, in that it's measuring the
> font-lock of a specific buffer, in whole, for 1000 iterations. But
> Juri must have come up with the original report based on real usage
> scenario.
>
> OTOH, the scenario which it might correspond to, is used typing in the
> same buffer for a long time (triggering thousands of refontifications,
> possibly partial ones). I don't know if it's feasible to try to
> reproduce it specifically. But, again, anything that can happen once
> can happen 4 more times.
>
>>>> Could you try the latest commit and see if the fast mode still switches
>>>> on when it shouldn’t?
>>>
>>> At first it seemed to help, but then I switched the major mode a
>>> couple more times, and ran the benchmark twice more, and the "fast
>>> mode" switched on again.
>>>
>>> Which seems to make sense: there is no resetting the counter, right?
>>>
>>> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
>> I was hoping that the scenario only happen once, oh well :-) I’ll
>> change the decision based on analyzing the tree’s dimension: too
>> deep or too wide activates the fast mode. Let’s see how it works.
>
> Thank you, let me know when it's time to test again.
Sorry for the delay. Now treesit-font-lock-fontify-region uses
treesit-subtree-stat to determine whether to enable the "fast mode". Now
it should be impossible to activate the fast mode on moderately sized
buffers.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-29 8:25 ` Yuan Fu
@ 2023-01-29 23:07 ` Dmitry Gutov
2023-01-29 23:23 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-29 23:07 UTC (permalink / raw)
To: Yuan Fu; +Cc: 60691, juri
Hi Yuan,
On 29/01/2023 10:25, Yuan Fu wrote:
>>>> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
>>> I was hoping that the scenario only happen once, oh well :-) I’ll
>>> change the decision based on analyzing the tree’s dimension: too
>>> deep or too wide activates the fast mode. Let’s see how it works.
>>
>> Thank you, let me know when it's time to test again.
>
> Sorry for the delay. Now treesit-font-lock-fontify-region uses
> treesit-subtree-stat to determine whether to enable the "fast mode". Now
> it should be impossible to activate the fast mode on moderately sized
> buffers.
Thank you, it seems to work just fine in my scenario. And
treesit-subtree-stat makes sense.
I have a few more questions about the current strategy, though.
IIUC, we only do the treesit--font-lock-fast-mode test once in
treesit-font-lock-fontify-region, and then use the detected value for
the whole later life of the buffer. Is that right?
What if the buffer didn't originally have the problematic error nodes we
are guarding from, and then later the user wrote enough code to have at
least one of them? If they didn't close Emacs, or revert the buffer, our
logic still wouldn't use the "fast node", would it?
Or vice versa: if the buffer started out with error nodes, and
consequently, "fast mode", but then the user has edited it so that those
error nodes disappeared, shouldn't the buffer stop using the "fast mode"?
From my measurements, in ruby-mode, at least treesit-subtree-stat is
20-40x faster than refontifying the whole buffer. So one possible
strategy would be to repeat the test every time. I'm not sure it's fast
enough in the "problem" buffers, though, and I don't have any to test.
In those I did test, though, it takes ~1 ms.
But we could repeat the test only once every couple of seconds and/or
after the buffer has changed again. That would hopefully make it a
non-bottleneck in all cases.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-29 23:07 ` Dmitry Gutov
@ 2023-01-29 23:23 ` Yuan Fu
2023-01-30 0:15 ` Dmitry Gutov
0 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-01-29 23:23 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: 60691, juri
> On Jan 29, 2023, at 3:07 PM, Dmitry Gutov <dgutov@yandex.ru> wrote:
>
> Hi Yuan,
>
> On 29/01/2023 10:25, Yuan Fu wrote:
>
>>>>> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
>>>> I was hoping that the scenario only happen once, oh well :-) I’ll
>>>> change the decision based on analyzing the tree’s dimension: too
>>>> deep or too wide activates the fast mode. Let’s see how it works.
>>>
>>> Thank you, let me know when it's time to test again.
>> Sorry for the delay. Now treesit-font-lock-fontify-region uses
>> treesit-subtree-stat to determine whether to enable the "fast mode". Now
>> it should be impossible to activate the fast mode on moderately sized
>> buffers.
>
> Thank you, it seems to work just fine in my scenario. And treesit-subtree-stat makes sense.
>
> I have a few more questions about the current strategy, though.
>
> IIUC, we only do the treesit--font-lock-fast-mode test once in treesit-font-lock-fontify-region, and then use the detected value for the whole later life of the buffer. Is that right?
>
> What if the buffer didn't originally have the problematic error nodes we are guarding from, and then later the user wrote enough code to have at least one of them? If they didn't close Emacs, or revert the buffer, our logic still wouldn't use the "fast node", would it?
>
> Or vice versa: if the buffer started out with error nodes, and consequently, "fast mode", but then the user has edited it so that those error nodes disappeared, shouldn't the buffer stop using the "fast mode"?
>
> From my measurements, in ruby-mode, at least treesit-subtree-stat is 20-40x faster than refontifying the whole buffer. So one possible strategy would be to repeat the test every time. I'm not sure it's fast enough in the "problem" buffers, though, and I don't have any to test.
>
> In those I did test, though, it takes ~1 ms.
>
> But we could repeat the test only once every couple of seconds and/or after the buffer has changed again. That would hopefully make it a non-bottleneck in all cases.
I should mention this in the comments, but the fast mode is only for very rare cases, where the file is mechanically generated and has some peculiarities that causes tree-sitter to work poorly. If the file is hand-written and “normal”, even huge files like xdisp.c is well below the bar. Therefore I don’t think “crossing the line” will realistically happen when editing source files.
Here is the stats of two “problematic files”, named packet and dec_mask, comparing to xdisp.c:
;; max-depth max-width count
;; cut-off 100 4000
;; packet (98159 46581 1895137)
;; dec mask (3 64301 283995)
;; xdisp.c (29 985 218971)
I’d say that any regular source file, even mechanically generated, wouldn’t go beyond ~50 levels in depth, and hand-written files should never has a node that has 4000+ direct children in the parse tree.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-29 23:23 ` Yuan Fu
@ 2023-01-30 0:15 ` Dmitry Gutov
2023-02-01 5:26 ` Yuan Fu
0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Gutov @ 2023-01-30 0:15 UTC (permalink / raw)
To: Yuan Fu; +Cc: juri, 60691-done
On 30/01/2023 01:23, Yuan Fu wrote:
>
>> On Jan 29, 2023, at 3:07 PM, Dmitry Gutov<dgutov@yandex.ru> wrote:
>>
>> Hi Yuan,
>>
>> On 29/01/2023 10:25, Yuan Fu wrote:
>>
>>>>>> So if previously it happened once somehow during a certain scenario, now I have to repeat the same scenario 4 times, and the condition is met.
>>>>> I was hoping that the scenario only happen once, oh well 😄 I’ll
>>>>> change the decision based on analyzing the tree’s dimension: too
>>>>> deep or too wide activates the fast mode. Let’s see how it works.
>>>> Thank you, let me know when it's time to test again.
>>> Sorry for the delay. Now treesit-font-lock-fontify-region uses
>>> treesit-subtree-stat to determine whether to enable the "fast mode". Now
>>> it should be impossible to activate the fast mode on moderately sized
>>> buffers.
>> Thank you, it seems to work just fine in my scenario. And treesit-subtree-stat makes sense.
>>
>> I have a few more questions about the current strategy, though.
>>
>> IIUC, we only do the treesit--font-lock-fast-mode test once in treesit-font-lock-fontify-region, and then use the detected value for the whole later life of the buffer. Is that right?
>>
>> What if the buffer didn't originally have the problematic error nodes we are guarding from, and then later the user wrote enough code to have at least one of them? If they didn't close Emacs, or revert the buffer, our logic still wouldn't use the "fast node", would it?
>>
>> Or vice versa: if the buffer started out with error nodes, and consequently, "fast mode", but then the user has edited it so that those error nodes disappeared, shouldn't the buffer stop using the "fast mode"?
>>
>> From my measurements, in ruby-mode, at least treesit-subtree-stat is 20-40x faster than refontifying the whole buffer. So one possible strategy would be to repeat the test every time. I'm not sure it's fast enough in the "problem" buffers, though, and I don't have any to test.
>>
>> In those I did test, though, it takes ~1 ms.
>>
>> But we could repeat the test only once every couple of seconds and/or after the buffer has changed again. That would hopefully make it a non-bottleneck in all cases.
> I should mention this in the comments, but the fast mode is only for very rare cases, where the file is mechanically generated and has some peculiarities that causes tree-sitter to work poorly. If the file is hand-written and “normal”, even huge files like xdisp.c is well below the bar. Therefore I don’t think “crossing the line” will realistically happen when editing source files.
>
> Here is the stats of two “problematic files”, named packet and dec_mask, comparing to xdisp.c:
>
> ;; max-depth max-width count
> ;; cut-off 100 4000
> ;; packet (98159 46581 1895137)
> ;; dec mask (3 64301 283995)
> ;; xdisp.c (29 985 218971)
>
> I’d say that any regular source file, even mechanically generated, wouldn’t go beyond ~50 levels in depth, and hand-written files should never has a node that has 4000+ direct children in the parse tree.
Oh, thanks for the explanation. Then the current strategy makes sense.
Is xdisp.c absolutely the largest C file in your experience?
According to the above numbers, a file that's only 4x as large could hit
our current cutoff.
Though, TBH, maybe some extreme files do, and they have font-lock
performance reduced somewhat. That's not the end of the world, and it
shouldn't make a difference for the original scenario (diff-syntax
fontification).
Either way, I'm closing this report. Thank you for your help.
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-01-30 0:15 ` Dmitry Gutov
@ 2023-02-01 5:26 ` Yuan Fu
2023-02-01 15:11 ` Dmitry Gutov
0 siblings, 1 reply; 28+ messages in thread
From: Yuan Fu @ 2023-02-01 5:26 UTC (permalink / raw)
To: Dmitry Gutov; +Cc: juri, 60691-done
>> I should mention this in the comments, but the fast mode is only for very rare cases, where the file is mechanically generated and has some peculiarities that causes tree-sitter to work poorly. If the file is hand-written and “normal”, even huge files like xdisp.c is well below the bar. Therefore I don’t think “crossing the line” will realistically happen when editing source files.
>> Here is the stats of two “problematic files”, named packet and dec_mask, comparing to xdisp.c:
>> ;; max-depth max-width count
>> ;; cut-off 100 4000
>> ;; packet (98159 46581 1895137)
>> ;; dec mask (3 64301 283995)
>> ;; xdisp.c (29 985 218971)
>> I’d say that any regular source file, even mechanically generated, wouldn’t go beyond ~50 levels in depth, and hand-written files should never has a node that has 4000+ direct children in the parse tree.
>
> Oh, thanks for the explanation. Then the current strategy makes sense.
>
> Is xdisp.c absolutely the largest C file in your experience?
>
> According to the above numbers, a file that's only 4x as large could hit our current cutoff.
I don’t think these stats increase linearly as the file size increases. Even if there is a file that has a node with 3999 direct children, and the developer adds another one, I’d say it’s better not to turn on “fast mode” immediately.
Yuan
^ permalink raw reply [flat|nested] 28+ messages in thread
* bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode
2023-02-01 5:26 ` Yuan Fu
@ 2023-02-01 15:11 ` Dmitry Gutov
0 siblings, 0 replies; 28+ messages in thread
From: Dmitry Gutov @ 2023-02-01 15:11 UTC (permalink / raw)
To: Yuan Fu; +Cc: 60691-done, juri
On 01/02/2023 07:26, Yuan Fu wrote:
>>> I should mention this in the comments, but the fast mode is only for very rare cases, where the file is mechanically generated and has some peculiarities that causes tree-sitter to work poorly. If the file is hand-written and “normal”, even huge files like xdisp.c is well below the bar. Therefore I don’t think “crossing the line” will realistically happen when editing source files.
>>> Here is the stats of two “problematic files”, named packet and dec_mask, comparing to xdisp.c:
>>> ;; max-depth max-width count
>>> ;; cut-off 100 4000
>>> ;; packet (98159 46581 1895137)
>>> ;; dec mask (3 64301 283995)
>>> ;; xdisp.c (29 985 218971)
>>> I’d say that any regular source file, even mechanically generated, wouldn’t go beyond ~50 levels in depth, and hand-written files should never has a node that has 4000+ direct children in the parse tree.
>> Oh, thanks for the explanation. Then the current strategy makes sense.
>>
>> Is xdisp.c absolutely the largest C file in your experience?
>>
>> According to the above numbers, a file that's only 4x as large could hit our current cutoff.
> I don’t think these stats increase linearly as the file size increases. Even if there is a file that has a node with 3999 direct children, and the developer adds another one, I’d say it’s better not to turn on “fast mode” immediately.
I see your point.
In the previous message I was talking about a different scenario: when a
project has a file 4x the size of xdisp.c, and the user just opens it. I
suspect it's not great to have "fast mode" enabled in that case? Like,
false positive.
Anyway, this is a very theoretical concern on my part.
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2023-02-01 15:11 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-01-09 17:16 bug#60691: 29.0.60; Slow tree-sitter font-lock in ruby-ts-mode Juri Linkov
2023-01-09 22:33 ` Dmitry Gutov
2023-01-10 8:10 ` Juri Linkov
2023-01-10 14:10 ` Dmitry Gutov
2023-01-10 17:50 ` Juri Linkov
2023-01-11 12:12 ` Dmitry Gutov
2023-01-11 12:12 ` Dmitry Gutov
2023-01-12 21:58 ` Yuan Fu
2023-01-12 23:40 ` Dmitry Gutov
2023-01-13 7:57 ` Eli Zaretskii
2023-01-13 9:15 ` Yuan Fu
2023-01-13 11:51 ` Eli Zaretskii
2023-01-14 3:48 ` Yuan Fu
2023-01-14 7:29 ` Eli Zaretskii
2023-01-14 7:51 ` Yuan Fu
2023-01-14 8:01 ` Eli Zaretskii
2023-01-14 8:46 ` Andreas Schwab
2023-01-14 23:03 ` Yuan Fu
2023-01-18 6:50 ` Yuan Fu
2023-01-19 18:28 ` Dmitry Gutov
2023-01-20 22:24 ` Yuan Fu
2023-01-22 2:01 ` Dmitry Gutov
2023-01-29 8:25 ` Yuan Fu
2023-01-29 23:07 ` Dmitry Gutov
2023-01-29 23:23 ` Yuan Fu
2023-01-30 0:15 ` Dmitry Gutov
2023-02-01 5:26 ` Yuan Fu
2023-02-01 15:11 ` Dmitry Gutov
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).