unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* State of the overlay tree branch?
@ 2018-03-18 20:14 Sebastian Sturm
  2018-03-18 20:39 ` Eli Zaretskii
  2018-03-26 13:06 ` Stefan Monnier
  0 siblings, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-18 20:14 UTC (permalink / raw)
  To: emacs-devel

Hi,

after finding that the feature/noverlay branch does wonders for my 
editing experience[1], I'd like to reinvigorate the discussion on its 
inclusion into master. Are there plans for a merge with the emacs-27 
master branch? Any critical issues blocking such a merge?

If a recent Emacs 27 variant with the overlay branch feature was 
available, I'd be happy to evaluate that in my daily work.

best regards,
Sebastian Sturm

[1] I'm using cquery for my C++ editing needs, which comes with an 
overlay-based semantic highlighting mechanism. With my emacs 
configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per 
character insertion, which consume ~20 to 25 ms each when performing 
edits close to the bottom of a 66KB C++ file (measured using 
(benchmark-run 1000 (line-number-at-pos (point))) on a release build of 
emacs-27/git commit #9942734...). Using the noverlay branch, this figure 
drops to ~160us per call.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 20:14 State of the overlay tree branch? Sebastian Sturm
@ 2018-03-18 20:39 ` Eli Zaretskii
  2018-03-18 21:04   ` Sebastian Sturm
  2018-03-21 14:14   ` Sebastien Chapuis
  2018-03-26 13:06 ` Stefan Monnier
  1 sibling, 2 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-18 20:39 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Sun, 18 Mar 2018 21:14:53 +0100
> 
> [1] I'm using cquery for my C++ editing needs, which comes with an 
> overlay-based semantic highlighting mechanism. With my emacs 
> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per 
> character insertion, which consume ~20 to 25 ms each when performing 
> edits close to the bottom of a 66KB C++ file (measured using 
> (benchmark-run 1000 (line-number-at-pos (point))) on a release build of 
> emacs-27/git commit #9942734...). Using the noverlay branch, this figure 
> drops to ~160us per call.

If lsp-mode/lsp-ui needs a fast line counter, one can easily be
provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
and should run circles around count-lines (used by line-number-at-pos).

(I'm not sure I even understand how overlays come into play here,
btw.)



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 20:39 ` Eli Zaretskii
@ 2018-03-18 21:04   ` Sebastian Sturm
  2018-03-18 23:03     ` Sebastian Sturm
  2018-03-19  6:28     ` Eli Zaretskii
  2018-03-21 14:14   ` Sebastien Chapuis
  1 sibling, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-18 21:04 UTC (permalink / raw)
  To: emacs-devel

I also found it surprising that overlays would slow down line counting, 
but since I don't know anything about the architecture of the Emacs 
display engine, or its overlay implementation, I figured that overlays 
must be to blame because

(i) the issue went away after switching to the feature/noverlay branch

(ii) configuring the semantic highlighter to use its font-lock backend 
also resolved the performance issue (though with the font-lock backend, 
highlights are easily messed up by editing operations which makes the 
overlay variant far more appealing)

I also found that some other heavy users of overlays such as 
avy-goto-word-0-{above,below} feel faster with the feature/noverlay 
branch, so I'd welcome a merge of the overlay branch even if there was a 
technically superior alternative to line-number-at-pos that didn't 
suffer from overlay-related performance issues.

That being said, your suggestion sounds intriguing. What would be 
required to expose find_newline to Lisp? Would I simply have to wrap it 
in one of Emacs's DEFINE_<something> macros? Is there some documentation 
on the Emacs C backend?

On 03/18/2018 09:39 PM, Eli Zaretskii wrote:
 >> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
 >> Date: Sun, 18 Mar 2018 21:14:53 +0100
 >>
 >> [1] I'm using cquery for my C++ editing needs, which comes with an
 >> overlay-based semantic highlighting mechanism. With my emacs
 >> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per
 >> character insertion, which consume ~20 to 25 ms each when performing
 >> edits close to the bottom of a 66KB C++ file (measured using
 >> (benchmark-run 1000 (line-number-at-pos (point))) on a release build of
 >> emacs-27/git commit #9942734...). Using the noverlay branch, this figure
 >> drops to ~160us per call.
 >
 > If lsp-mode/lsp-ui needs a fast line counter, one can easily be
 > provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
 > and should run circles around count-lines (used by line-number-at-pos).
 >
 > (I'm not sure I even understand how overlays come into play here,
 > btw.)



^ permalink raw reply	[flat|nested] 54+ messages in thread

* RE: State of the overlay tree branch?
       [not found] ` <<834lldp18f.fsf@gnu.org>
@ 2018-03-18 21:37   ` Drew Adams
  2018-03-19  1:33     ` Stefan Monnier
  2018-03-19  6:33     ` Eli Zaretskii
  0 siblings, 2 replies; 54+ messages in thread
From: Drew Adams @ 2018-03-18 21:37 UTC (permalink / raw)
  To: Eli Zaretskii, Sebastian Sturm; +Cc: emacs-devel

> If lsp-mode/lsp-ui needs a fast line counter, one can easily be
> provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
> and should run circles around count-lines (used by line-number-at-pos).

Having a fast line counter in Elisp would be terrific.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 21:04   ` Sebastian Sturm
@ 2018-03-18 23:03     ` Sebastian Sturm
  2018-03-18 23:20       ` Sebastian Sturm
  2018-03-19  6:36       ` Eli Zaretskii
  2018-03-19  6:28     ` Eli Zaretskii
  1 sibling, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-18 23:03 UTC (permalink / raw)
  To: emacs-devel

concerning the performance improvement with noverlay, it seems I spoke 
to soon. I've now had the issue reappear, both with the noverlay branch, 
and with the semantic highlighter set to use font-lock. Sorry for the 
misinformation.
Again, however, line-number-at-pos shows up as a large CPU time consumer 
in the profiler report, and benchmark-run still reports several ms per 
invocation (though this time it's usually around 2 to 4 ms instead of 
the 20 to 25 I measured earlier), so I'd still be very much interested 
in a faster line-number-at-pos implementation.

On 03/18/2018 10:04 PM, Sebastian Sturm wrote:
> I also found it surprising that overlays would slow down line counting, 
> but since I don't know anything about the architecture of the Emacs 
> display engine, or its overlay implementation, I figured that overlays 
> must be to blame because
> 
> (i) the issue went away after switching to the feature/noverlay branch
> 
> (ii) configuring the semantic highlighter to use its font-lock backend 
> also resolved the performance issue (though with the font-lock backend, 
> highlights are easily messed up by editing operations which makes the 
> overlay variant far more appealing)
> 
> I also found that some other heavy users of overlays such as 
> avy-goto-word-0-{above,below} feel faster with the feature/noverlay 
> branch, so I'd welcome a merge of the overlay branch even if there was a 
> technically superior alternative to line-number-at-pos that didn't 
> suffer from overlay-related performance issues.
> 
> That being said, your suggestion sounds intriguing. What would be 
> required to expose find_newline to Lisp? Would I simply have to wrap it 
> in one of Emacs's DEFINE_<something> macros? Is there some documentation 
> on the Emacs C backend?
> 
> On 03/18/2018 09:39 PM, Eli Zaretskii wrote:
>  >> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
>  >> Date: Sun, 18 Mar 2018 21:14:53 +0100
>  >>
>  >> [1] I'm using cquery for my C++ editing needs, which comes with an
>  >> overlay-based semantic highlighting mechanism. With my emacs
>  >> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per
>  >> character insertion, which consume ~20 to 25 ms each when performing
>  >> edits close to the bottom of a 66KB C++ file (measured using
>  >> (benchmark-run 1000 (line-number-at-pos (point))) on a release build of
>  >> emacs-27/git commit #9942734...). Using the noverlay branch, this 
> figure
>  >> drops to ~160us per call.
>  >
>  > If lsp-mode/lsp-ui needs a fast line counter, one can easily be
>  > provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
>  > and should run circles around count-lines (used by line-number-at-pos).
>  >
>  > (I'm not sure I even understand how overlays come into play here,
>  > btw.)
> 



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 23:03     ` Sebastian Sturm
@ 2018-03-18 23:20       ` Sebastian Sturm
  2018-03-19  6:43         ` Eli Zaretskii
  2018-03-19  6:36       ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-18 23:20 UTC (permalink / raw)
  To: emacs-devel

for the record, I just switched back to emacs master (no noverlay) and 
the time reported by (benchmark-run 1000 (line-number-at-pos (point)) 
increased by a factor of ~40, to 75-80s. At this level, editing is 
unbearably slow. With the semantic highlighter disabled, the same 
measurement yields ~2.5s (still painfully slow, but borderline usable), 
so about the same time reported by the noverlay branch.
To me, this suggests that noverlay indeed improves performance, though 
not necessarily to the level I had previously claimed. find_newline may 
solve this particular issue completely.
Since the time taken by line-number-at-pos seems to fluctuate wildly for 
(to me) unknown reasons, I'll try and see if I can set up a systematic 
way to collect reliable data.

On 03/19/2018 12:03 AM, Sebastian Sturm wrote:
> concerning the performance improvement with noverlay, it seems I spoke 
> to soon. I've now had the issue reappear, both with the noverlay branch, 
> and with the semantic highlighter set to use font-lock. Sorry for the 
> misinformation.
> Again, however, line-number-at-pos shows up as a large CPU time consumer 
> in the profiler report, and benchmark-run still reports several ms per 
> invocation (though this time it's usually around 2 to 4 ms instead of 
> the 20 to 25 I measured earlier), so I'd still be very much interested 
> in a faster line-number-at-pos implementation.
> 
> On 03/18/2018 10:04 PM, Sebastian Sturm wrote:
>> I also found it surprising that overlays would slow down line 
>> counting, but since I don't know anything about the architecture of 
>> the Emacs display engine, or its overlay implementation, I figured 
>> that overlays must be to blame because
>>
>> (i) the issue went away after switching to the feature/noverlay branch
>>
>> (ii) configuring the semantic highlighter to use its font-lock backend 
>> also resolved the performance issue (though with the font-lock 
>> backend, highlights are easily messed up by editing operations which 
>> makes the overlay variant far more appealing)
>>
>> I also found that some other heavy users of overlays such as 
>> avy-goto-word-0-{above,below} feel faster with the feature/noverlay 
>> branch, so I'd welcome a merge of the overlay branch even if there was 
>> a technically superior alternative to line-number-at-pos that didn't 
>> suffer from overlay-related performance issues.
>>
>> That being said, your suggestion sounds intriguing. What would be 
>> required to expose find_newline to Lisp? Would I simply have to wrap 
>> it in one of Emacs's DEFINE_<something> macros? Is there some 
>> documentation on the Emacs C backend?
>>
>> On 03/18/2018 09:39 PM, Eli Zaretskii wrote:
>>  >> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
>>  >> Date: Sun, 18 Mar 2018 21:14:53 +0100
>>  >>
>>  >> [1] I'm using cquery for my C++ editing needs, which comes with an
>>  >> overlay-based semantic highlighting mechanism. With my emacs
>>  >> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per
>>  >> character insertion, which consume ~20 to 25 ms each when performing
>>  >> edits close to the bottom of a 66KB C++ file (measured using
>>  >> (benchmark-run 1000 (line-number-at-pos (point))) on a release 
>> build of
>>  >> emacs-27/git commit #9942734...). Using the noverlay branch, this 
>> figure
>>  >> drops to ~160us per call.
>>  >
>>  > If lsp-mode/lsp-ui needs a fast line counter, one can easily be
>>  > provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
>>  > and should run circles around count-lines (used by 
>> line-number-at-pos).
>>  >
>>  > (I'm not sure I even understand how overlays come into play here,
>>  > btw.)
>>
> 



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 21:37   ` Drew Adams
@ 2018-03-19  1:33     ` Stefan Monnier
  2018-03-19  6:50       ` Eli Zaretskii
  2018-03-19  6:33     ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19  1:33 UTC (permalink / raw)
  To: emacs-devel

>> If lsp-mode/lsp-ui needs a fast line counter, one can easily be
>> provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
>> and should run circles around count-lines (used by line-number-at-pos).
> Having a fast line counter in Elisp would be terrific.

It should be pretty easy to provide such a thing by relying on a cache
of the last call.  Tho Sebastian's experience seems to indicate that the
current code doesn't only suffer from the time to count LF but also from
the time to process the markers.

I seem to remember someone else experiencing a similar problem and
suggesting that the problem lies in the charpos_to_bytepos (and/or
bytepos_to_charpos) conversion function, which iterates through all the
markers to try and find a "nearby" marker (because markers keep track
of both their bytepos and their charpos).  Looking for a nearby marker
to avoid scanning the whole buffer is a good idea in many cases, but not
if scanning the list of markers takes more time than scanning the
whole buffer.


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 21:04   ` Sebastian Sturm
  2018-03-18 23:03     ` Sebastian Sturm
@ 2018-03-19  6:28     ` Eli Zaretskii
  1 sibling, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19  6:28 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Sun, 18 Mar 2018 22:04:13 +0100
> 
> I also found it surprising that overlays would slow down line counting, 
> but since I don't know anything about the architecture of the Emacs 
> display engine, or its overlay implementation, I figured that overlays 
> must be to blame because
> 
> (i) the issue went away after switching to the feature/noverlay branch
> 
> (ii) configuring the semantic highlighter to use its font-lock backend 
> also resolved the performance issue (though with the font-lock backend, 
> highlights are easily messed up by editing operations which makes the 
> overlay variant far more appealing)

If you look at the implementation of count-lines, you will see it just
calls forward-line, which pays no attention to overlays (as I'd
expect).

> I also found that some other heavy users of overlays such as 
> avy-goto-word-0-{above,below} feel faster with the feature/noverlay 
> branch, so I'd welcome a merge of the overlay branch even if there was a 
> technically superior alternative to line-number-at-pos that didn't 
> suffer from overlay-related performance issues.

That's unrelated: we want to merge that branch when it's ready for
several good reasons.  But counting lines shouldn't be one of those
reasons.

> That being said, your suggestion sounds intriguing. What would be 
> required to expose find_newline to Lisp? Would I simply have to wrap it 
> in one of Emacs's DEFINE_<something> macros?

DEFUN, more accurately.  But yes.

Actually, I see that forward-line already calls find_newline almost
directly, so it should be fast enough.  Exposing find_line should
perhaps be able to produce some speedup (because you don't need to set
point, like forward-line does), but I doubt that it would be
significant.

> Is there some documentation on the Emacs C backend?

There's the "Writing Emacs Primitives" section of the "Internals"
appendix.

Turning back to your original problem, viz.:

 >> [1] I'm using cquery for my C++ editing needs, which comes with an
 >> overlay-based semantic highlighting mechanism. With my emacs
 >> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per
 >> character insertion, which consume ~20 to 25 ms each when performing
 >> edits close to the bottom of a 66KB C++ file (measured using
 >> (benchmark-run 1000 (line-number-at-pos (point))) on a release build of
 >> emacs-27/git commit #9942734...). Using the noverlay branch, this figure
 >> drops to ~160us per call.

it looks strange to me that you get such long times.  I just tried
Emacs 26.0.91 near the end of xdisp.c (a 1MB file with over 33K
lines), and I get 3 ms per call there.  So I wonder how come you get 4
or 5 ms per call in a file that is 50 times smaller, because if I run
the above benchmark with point at 70K, I get 0.16 ms per call.  In an
unoptimized build of the current master branch, I get 0.25 ms per call
under these conditions.  (And you could cache the result if you need 6
calls in the same vicinity, and after the initial call only call
forward-line to compute relative increments.)

This is with a 5-year old i7-2600 box with a 3.40 GHz clock.  Is your
CPU significantly slower?



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 21:37   ` Drew Adams
  2018-03-19  1:33     ` Stefan Monnier
@ 2018-03-19  6:33     ` Eli Zaretskii
  1 sibling, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19  6:33 UTC (permalink / raw)
  To: Drew Adams; +Cc: s.sturm, emacs-devel

> Date: Sun, 18 Mar 2018 14:37:39 -0700 (PDT)
> From: Drew Adams <drew.adams@oracle.com>
> Cc: emacs-devel@gnu.org
> 
> > If lsp-mode/lsp-ui needs a fast line counter, one can easily be
> > provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
> > and should run circles around count-lines (used by line-number-at-pos).
> 
> Having a fast line counter in Elisp would be terrific.

Actually, I see that line-number-at-pos, count-lines, and forward-line
should already be fast, as they do exactly what I thought, something I
failed to see originally.  If that's not "fast enough", a test case
showing the problem would be a good starting point.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 23:03     ` Sebastian Sturm
  2018-03-18 23:20       ` Sebastian Sturm
@ 2018-03-19  6:36       ` Eli Zaretskii
  1 sibling, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19  6:36 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Mon, 19 Mar 2018 00:03:11 +0100
> 
> Again, however, line-number-at-pos shows up as a large CPU time consumer 
> in the profiler report, and benchmark-run still reports several ms per 
> invocation (though this time it's usually around 2 to 4 ms instead of 
> the 20 to 25 I measured earlier), so I'd still be very much interested 
> in a faster line-number-at-pos implementation.

2 to 4 ms for 6 calls is as fast as you can get for a 70K file.  But
you should be able to issue just one call, and replace the other 5
with relative counting using count-lines or forward-line, which should
then count only a small number of lines from the original location.
Right?



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 23:20       ` Sebastian Sturm
@ 2018-03-19  6:43         ` Eli Zaretskii
  2018-03-19  9:53           ` Sebastian Sturm
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19  6:43 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Mon, 19 Mar 2018 00:20:13 +0100
> 
> for the record, I just switched back to emacs master (no noverlay) and 
> the time reported by (benchmark-run 1000 (line-number-at-pos (point)) 
> increased by a factor of ~40, to 75-80s. At this level, editing is 
> unbearably slow. With the semantic highlighter disabled, the same 
> measurement yields ~2.5s (still painfully slow, but borderline usable), 
> so about the same time reported by the noverlay branch.

You will have to explain why overlays and the semantic highlighter
affect line-counting.  How about presenting a profile produced by
"M-x profiler-report"?

And the timings you measure are 2.5 _milliseconds_ (the benchmark runs
1000 times), right?  If so, I cannot understand why you say that's
borderline usable, because IME such short times are imperceptible by
humans.  I guess some other factor is at work here, so I'd suggest to
describe more details about your use case.

> Since the time taken by line-number-at-pos seems to fluctuate wildly for 
> (to me) unknown reasons, I'll try and see if I can set up a systematic 
> way to collect reliable data.

Yes, please do.  I'm guessing there's some factor here that is
important to consider.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19  1:33     ` Stefan Monnier
@ 2018-03-19  6:50       ` Eli Zaretskii
  2018-03-19 12:29         ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19  6:50 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Sun, 18 Mar 2018 21:33:05 -0400
> 
> >> If lsp-mode/lsp-ui needs a fast line counter, one can easily be
> >> provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
> >> and should run circles around count-lines (used by line-number-at-pos).
> > Having a fast line counter in Elisp would be terrific.
> 
> It should be pretty easy to provide such a thing by relying on a cache
> of the last call.

This is already coded, see display_count_lines.  That's what the
native line-number display uses.  Exposing it to Lisp should be easy.
But I don't believe it could be orders of magnitude faster than
count-lines, even though it doesn't need to convert character position
to byte position.

I'm guessing something entirely different and unrelated to
line-counting per se is at work here.

> Tho Sebastian's experience seems to indicate that the
> current code doesn't only suffer from the time to count LF but also from
> the time to process the markers.

Not sure what marker processing did you have in mind.  Can you
elaborate?

> I seem to remember someone else experiencing a similar problem and
> suggesting that the problem lies in the charpos_to_bytepos (and/or
> bytepos_to_charpos) conversion function, which iterates through all the
> markers to try and find a "nearby" marker (because markers keep track
> of both their bytepos and their charpos).  Looking for a nearby marker
> to avoid scanning the whole buffer is a good idea in many cases, but not
> if scanning the list of markers takes more time than scanning the
> whole buffer.

But find_newline doesn't look for markers, and it converts character
to byte position just 2 times.  Or am I missing something?



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19  6:43         ` Eli Zaretskii
@ 2018-03-19  9:53           ` Sebastian Sturm
  2018-03-19 12:57             ` Eli Zaretskii
  2018-03-19 14:56             ` Stefan Monnier
  0 siblings, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-19  9:53 UTC (permalink / raw)
  To: emacs-devel

On 03/19/2018 07:43 AM, Eli Zaretskii wrote:
 >> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
 >> Date: Mon, 19 Mar 2018 00:20:13 +0100
 >>
 >> for the record, I just switched back to emacs master (no noverlay) and
 >> the time reported by (benchmark-run 1000 (line-number-at-pos (point))
 >> increased by a factor of ~40, to 75-80s. At this level, editing is
 >> unbearably slow. With the semantic highlighter disabled, the same
 >> measurement yields ~2.5s (still painfully slow, but borderline usable),
 >> so about the same time reported by the noverlay branch.
 >
 > You will have to explain why overlays and the semantic highlighter
 > affect line-counting.  How about presenting a profile produced by
 > "M-x profiler-report"?

please find below a profiler report taken this morning (on my PC at 
work, which doesn't suffer from the performance issue as much as my 2014 
MacBook Pro, but even here the issue is clearly noticeable)

 >
 > And the timings you measure are 2.5 _milliseconds_ (the benchmark runs
 > 1000 times), right?  If so, I cannot understand why you say that's
 > borderline usable, because IME such short times are imperceptible by
 > humans.  I guess some other factor is at work here, so I'd suggest to
 > describe more details about your use case.

well no, it's about 2.5ms per call to line-number-at-pos, which is 
called at least 6 times per character insertion (with my Emacs config, 
at least). Which already makes for 15ms per character insertion, 
excluding anything else done by cc-mode or lsp-mode.

 >> Since the time taken by line-number-at-pos seems to fluctuate wildly for
 >> (to me) unknown reasons, I'll try and see if I can set up a systematic
 >> way to collect reliable data.
 >
 > Yes, please do.  I'm guessing there's some factor here that is
 > important to consider.

I wrote a simple-minded measurement routine that can at least report the 
time taken between successive character insertions, and I noticed that 
better alternatives have been brought up in response to my other mailing 
list thread (titled "Latency profiling?"). I haven't had time to prepare 
a systematic test case yet, but I will look into it when I get home from 
work this evening.


- command-execute                                                5676  73%
  - call-interactively                                            5676  73%
   - funcall-interactively                                        5676  73%
    - self-insert-command                                         5080  66%
     - lsp-on-change                                              2144  27%
      - lsp--text-document-content-change-event                   1964  25%
       - lsp--point-to-position                                   1964  25%
          line-number-at-pos                                      1964  25%
      + json-encode                                                164   2%
      + file-truename                                               16   0%
     - c-after-change                                             1784  23%
      - mapc                                                      1772  23%
       + #<compiled 0x1a24bed>                                    1772  23%
      - c-invalidate-sws-region-after                               12   0%
       - c-invalidate-sws-region-after-ins                          12   0%
        - c-beginning-of-macro                                       8   0%
           back-to-indentation                                       4   0%
        - c-literal-limits                                           4   0%
           c-state-full-pp-to-literal                                4   0%
     - lsp-before-change                                          1016  13%
      - lsp--point-to-position                                    1016  13%
         line-number-at-pos                                       1016  13%
     - sp--post-self-insert-hook-handler                            56   0%
      - sp-insert-pair                                              28   0%
       - sp--pair-to-insert                                         24   0%
        - sp--all-pairs-to-insert                                   20   0%
         - sp--looking-back-p                                        8   0%
            sp--looking-back                                         8   0%
         + sp--do-action-p                                           8   0%
       + sp--get-closing-regexp                                      4   0%
      + sp--all-pairs-to-insert                                     16   0%
      + sp-escape-open-delimiter                                    12   0%
     + c-before-change                                              28   0%
     - jit-lock-after-change                                         4   0%
      + run-hook-with-args                                           4   0%
    + counsel-M-x                                                  360   4%
    + evil-open-below                                              232   3%
- lsp-ui-doc--make-request                                        643   8%
    line-number-at-pos                                             627   8%
  + file-truename                                                    8   0%
    url-hexify-string                                                4   0%
- lsp-ui-sideline                                                 528   6%
    line-number-at-pos                                             528   6%
+ timer-event-handler                                             327   4%
+ evil-escape-pre-command-hook                                    220   2%
+ #<compiled 0xb54291>                                            141   1%
+ redisplay_internal (C function)                                  88   1%
+ ...                                                              29   0%
+ yas--post-command-handler                                        20   0%
+ global-spacemacs-whitespace-cleanup-mode-check-buffers            4   0%
+ internal-timer-start-idle                                         4   0%
+ flycheck-display-error-at-point-soon                              4   0%
   flycheck-error-list-update-source                                 4   0%



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19  6:50       ` Eli Zaretskii
@ 2018-03-19 12:29         ` Stefan Monnier
  2018-03-19 13:02           ` Eli Zaretskii
  0 siblings, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19 12:29 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

>> It should be pretty easy to provide such a thing by relying on a cache
>> of the last call.
> This is already coded, see display_count_lines.

I don't see any cache in display_count_lines but yes, the code that uses
display_count_lines does do such caching and we could/should expose it
to Lisp.

In nlinum.el I also have something similar (called
nlinum--line-number-at-pos).

> But I don't believe it could be orders of magnitude faster than
> count-lines, even though it doesn't need to convert character position
> to byte position.

Scanning from the last used position can be *very* different from
scanning from point-min.  So yes, it can be orders of magnitude faster
(I wrote nlinum--line-number-at-pos for that reason: I sadly didn't
write down the test case I used back then, but the difference was
very significant).

> I'm guessing something entirely different and unrelated to
> line-counting per se is at work here.

Agreed.

>> Tho Sebastian's experience seems to indicate that the
>> current code doesn't only suffer from the time to count LF but also from
>> the time to process the markers.
> Not sure what marker processing did you have in mind.  Can you
> elaborate?

The

  for (tail = BUF_MARKERS (b); tail; tail = tail->next)

loop in buf_charpos_to_bytepos and buf_bytepos_to_charpos.

> But find_newline doesn't look for markers, and it converts character
> to byte position just 2 times.  Or am I missing something?

The idea is that the above loop (even if called only twice) might be
sufficient to make line-number-at-pos take 0.2s.

I don't know that it's the culprit, I'm just mentioning that
possibility, since noverlays removes all the overlay-induced markers
which would significantly reduce the number of markers over which the
above loops.

Note that those loops stop as soon as we're within 50 chars of the goal,
and they also stop as soon as there's no non-ascii char between the
"best bounds so far".

So for them to cause the slow down seen here, we'd need not only
a very large number of markers but also additional conditions that might
not be very likely.
But it's still a possibility.


        Stefan



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19  9:53           ` Sebastian Sturm
@ 2018-03-19 12:57             ` Eli Zaretskii
  2018-03-19 14:56             ` Stefan Monnier
  1 sibling, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19 12:57 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Mon, 19 Mar 2018 10:53:52 +0100
> 
> On 03/19/2018 07:43 AM, Eli Zaretskii wrote:
>  >> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
>  >> Date: Mon, 19 Mar 2018 00:20:13 +0100
>  >>
>  >> for the record, I just switched back to emacs master (no noverlay) and
>  >> the time reported by (benchmark-run 1000 (line-number-at-pos (point))
>  >> increased by a factor of ~40, to 75-80s. At this level, editing is
>  >> unbearably slow. With the semantic highlighter disabled, the same
>  >> measurement yields ~2.5s (still painfully slow, but borderline usable),
>  >> so about the same time reported by the noverlay branch.
>  >
>  > You will have to explain why overlays and the semantic highlighter
>  > affect line-counting.  How about presenting a profile produced by
>  > "M-x profiler-report"?
> 
> please find below a profiler report taken this morning (on my PC at 
> work, which doesn't suffer from the performance issue as much as my 2014 
> MacBook Pro, but even here the issue is clearly noticeable)

That profile says that self-insert-command takes a large percentage of
the time.  So I think we should look into the reasons for such a
strange place to spend hundreds of microseconds.  According to the
profile, line-number-at-pos takes about the same percentage of time as
self-insert-command does.  And that is even before you optimize the
successive calls to line-counting code to take advantage of the
previously computed value for some close line.

>  > And the timings you measure are 2.5 _milliseconds_ (the benchmark runs
>  > 1000 times), right?  If so, I cannot understand why you say that's
>  > borderline usable, because IME such short times are imperceptible by
>  > humans.  I guess some other factor is at work here, so I'd suggest to
>  > describe more details about your use case.
> 
> well no, it's about 2.5ms per call to line-number-at-pos, which is 
> called at least 6 times per character insertion (with my Emacs config, 
> at least). Which already makes for 15ms per character insertion, 
> excluding anything else done by cc-mode or lsp-mode.

Then, as I said, I don't understand why it takes so much on your
system.  I get times that are 10 times faster.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 12:29         ` Stefan Monnier
@ 2018-03-19 13:02           ` Eli Zaretskii
  2018-03-19 13:43             ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19 13:02 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@IRO.UMontreal.CA>
> Cc: emacs-devel@gnu.org
> Date: Mon, 19 Mar 2018 08:29:40 -0400
> 
> > But I don't believe [display_count_lines] could be orders of
> > magnitude faster than count-lines, even though it doesn't need to
> > convert character position to byte position.
> 
> Scanning from the last used position can be *very* different from
> scanning from point-min.  So yes, it can be orders of magnitude faster

Well, in my measurements it's already very fast.  I don't understand
why the OP sees times that are 10 times slower.

> The
> 
>   for (tail = BUF_MARKERS (b); tail; tail = tail->next)
> 
> loop in buf_charpos_to_bytepos and buf_bytepos_to_charpos.
> 
> > But find_newline doesn't look for markers, and it converts character
> > to byte position just 2 times.  Or am I missing something?
> 
> The idea is that the above loop (even if called only twice) might be
> sufficient to make line-number-at-pos take 0.2s.

I very much doubt that loop could take such a long time.  And running
a benchmark 1000 times means that the 2nd through 1000th iteration
find the mapping much faster, probably bypassing the loop entirely.

> So for them to cause the slow down seen here, we'd need not only
> a very large number of markers but also additional conditions that might
> not be very likely.
> But it's still a possibility.

I'll believe it when I see it.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 13:02           ` Eli Zaretskii
@ 2018-03-19 13:43             ` Stefan Monnier
  2018-03-19 14:28               ` Eli Zaretskii
  0 siblings, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19 13:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

>> The idea is that the above loop (even if called only twice) might be
>> sufficient to make line-number-at-pos take 0.2s.
> I very much doubt that loop could take such a long time.

I also find it unlikely.

> And running a benchmark 1000 times means that the 2nd through 1000th
> iteration find the mapping much faster, probably bypassing the
> loop entirely.

Quite likely.

BTW, when thinking about how to avoid this loop's worst case (regardless
if it's the source of the current problem), I was thinking that we could
probably make that worst case even less likely by replacing the
hardcoded "50" with a limit that grows as the loop progresses.

The patch below should make sure that the total number of iterations
(through markers + through chars) is no more than 2*buffer-size, no matter
how many markers there are.
[ Rather than incrementing by 1 we should ideally increment by
  a number that corresponds to how much slower is each iteration through
  markers compared to the iteration through chars, but I have no idea
  what that number would typically be.  ]

WDYT?


        Stefan


diff --git a/src/marker.c b/src/marker.c
index f61701f2f6..bfe6de486e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -141,6 +141,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = 50;
 
   eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
 
@@ -180,8 +181,10 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - best_below < distance)
 	break;
+      else
+        distance++;
     }
 
   /* We get here if we did not exactly hit one of the known places.
@@ -293,6 +296,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = 50;
 
   eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
 
@@ -323,8 +327,10 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - best_below < distance)
 	break;
+      else
+        distance++;
     }
 
   /* We get here if we did not exactly hit one of the known places.



^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 13:43             ` Stefan Monnier
@ 2018-03-19 14:28               ` Eli Zaretskii
  2018-03-19 14:39                 ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-19 14:28 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@IRO.UMontreal.CA>
> Cc: emacs-devel@gnu.org
> Date: Mon, 19 Mar 2018 09:43:12 -0400
> 
> BTW, when thinking about how to avoid this loop's worst case (regardless
> if it's the source of the current problem), I was thinking that we could
> probably make that worst case even less likely by replacing the
> hardcoded "50" with a limit that grows as the loop progresses.
> 
> The patch below should make sure that the total number of iterations
> (through markers + through chars) is no more than 2*buffer-size, no matter
> how many markers there are.
> [ Rather than incrementing by 1 we should ideally increment by
>   a number that corresponds to how much slower is each iteration through
>   markers compared to the iteration through chars, but I have no idea
>   what that number would typically be.  ]
> 
> WDYT?

Could be a good idea, but I suggest to time its improvement before we
decide.  I've seen a few surprises in that area.




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 14:28               ` Eli Zaretskii
@ 2018-03-19 14:39                 ` Stefan Monnier
  0 siblings, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19 14:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

> Could be a good idea, but I suggest to time its improvement before we
> decide.  I've seen a few surprises in that area.

I see some speed up in my artificial test (where I create an insane
number of markers at point-min and then ask to goto-char to the middle
of a midsized buffer) but I can't see any impact whatsoever (neither
positive nor negative) in real tests (unsurprisingly).

I'd rather first have a non-artificial case where I can measure
a speed up.


        Stefan



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19  9:53           ` Sebastian Sturm
  2018-03-19 12:57             ` Eli Zaretskii
@ 2018-03-19 14:56             ` Stefan Monnier
  2018-03-19 15:07               ` Sebastian Sturm
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19 14:56 UTC (permalink / raw)
  To: emacs-devel

> well no, it's about 2.5ms per call to line-number-at-pos, which is called at
> least 6 times per character insertion (with my Emacs config, at
> least). Which already makes for 15ms per character insertion, excluding
> anything else done by cc-mode or lsp-mode.

Since you say that the noverlay branch helps, could you check the number
of overlays involved?  E.g.

    M-: (length (overlays-in (point-min) (point-max))) RET

If there are more overlays than chars in this buffer, maybe there's
a problem in some Elisp that creates too many overlays?

If there aren't that many overlays, then I wonder why the noverlays
branch would make such a significant difference.

Also, if you can reliably reproduce the "slow editing", would it be
possible to make a recipe for it that we can try and reproduce on
our side?


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 14:56             ` Stefan Monnier
@ 2018-03-19 15:07               ` Sebastian Sturm
  2018-03-19 15:13                 ` Stefan Monnier
  2018-03-20  1:23                 ` Sebastian Sturm
  0 siblings, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-19 15:07 UTC (permalink / raw)
  To: emacs-devel

for the file I was complaining about, the number returned is 3080 
(doesn't exceed the number of chars though, (point-max) = 67641). Will 
try to obtain usable timing data later today when I get home from work. 
Thanks!

On 03/19/2018 03:56 PM, Stefan Monnier wrote:
>> well no, it's about 2.5ms per call to line-number-at-pos, which is called at
>> least 6 times per character insertion (with my Emacs config, at
>> least). Which already makes for 15ms per character insertion, excluding
>> anything else done by cc-mode or lsp-mode.
> 
> Since you say that the noverlay branch helps, could you check the number
> of overlays involved?  E.g.
> 
>      M-: (length (overlays-in (point-min) (point-max))) RET
> 
> If there are more overlays than chars in this buffer, maybe there's
> a problem in some Elisp that creates too many overlays?
> 
> If there aren't that many overlays, then I wonder why the noverlays
> branch would make such a significant difference.
> 
> Also, if you can reliably reproduce the "slow editing", would it be
> possible to make a recipe for it that we can try and reproduce on
> our side?
> 
> 
>          Stefan
> 
> 

-- 
Sebastian Sturm
Research & Development

Phone:	+49 (0) 6155 7808977
Fax:	+49 (0) 6155 7802880
Email:	s.sturm@arkona-technologies.de
Web:	www.arkona-technologies.de

arkona technologies GmbH
Im Leuschnerpark 4
64347 Griesheim
Germany
Amtsgericht / Commercial Register of Darmstadt, HRB 90080
USt-ID: DE273794666
Steuernummer / Tax-ID: 007 / 228 / 19331
Geschäftsführung / Managing Director: Rainer Sturm

This e-mail may contain confidential and/or privileged information. If 
you are not the intended recipient (or have received this e-mail in 
error) please notify the sender immediately and delete this message and 
any attachments from your system. Any unauthorised copying, disclosure 
or distribution of the material in this e-mail is strictly forbidden.

We cannot accept any responsibility for the accuracy or completeness of 
this message as it has been transmitted over a public network. If, 
despite our use of anti-virus software, a virus enters your systems in 
connection with the sending of the e-mail, you may not hold us liable 
for any damages that may possibly arise in that connection. We will 
accept liability which by law we cannot exclude.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 15:07               ` Sebastian Sturm
@ 2018-03-19 15:13                 ` Stefan Monnier
  2018-03-20  1:23                 ` Sebastian Sturm
  1 sibling, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-19 15:13 UTC (permalink / raw)
  To: emacs-devel

> for the file I was complaining about, the number returned is 3080 (doesn't
> exceed the number of chars though, (point-max) = 67641). Will try to obtain
> usable timing data later today when I get home from work. Thanks!

Thanks.

It's large enough that noverlay might indeed make a difference for some
operations, but not large enough to explain why line-number-at-pos would
be so slow, I think.


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-19 15:07               ` Sebastian Sturm
  2018-03-19 15:13                 ` Stefan Monnier
@ 2018-03-20  1:23                 ` Sebastian Sturm
  2018-03-20  6:30                   ` Eli Zaretskii
  2018-03-22 20:52                   ` Stefan Monnier
  1 sibling, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-20  1:23 UTC (permalink / raw)
  To: emacs-devel

after disabling cquery for testing purposes (which leaves me with 45 
overlays generated by flycheck for my troublesome C++ file), I'm now 
generating a large number of overlays using the following function:

(defun test-highlight ()
         (save-excursion
           (with-silent-modifications
             (let ((stepsize 10))
               (widen)
               (goto-char 1)
               (cl-loop for n from (point-min) upto (- (point-max) 
stepsize) by stepsize do
                 (let ((ov (make-overlay n (+ (1- stepsize) n))))
                   (overlay-put ov 'cquery-sem-highlight t)))))))

Evaluating the following function without additional overlays (beyond 
the flycheck ones, that is) yields the following results:

(defun benchmark-often ()
   (cl-loop for n from 1 upto 20 do
            (message (format "iteration %d: %f" n (nth 0 (benchmark-run 
(line-number-at-pos (point))))))))

1st run:
iteration 1: 0.001213
iteration 2: 0.001170
iteration 3: 0.001170
iteration 4: 0.001238
iteration 5: 0.001163
iteration 6: 0.001153
iteration 7: 0.000421
iteration 8: 0.000426
iteration 9: 0.000322
iteration 10: 0.000301
iteration 11: 0.000291
iteration 12: 0.000292
iteration 13: 0.000291
iteration 14: 0.000291
iteration 15: 0.000295
iteration 16: 0.000289
iteration 17: 0.000289
iteration 18: 0.000288
iteration 19: 0.000288
iteration 20: 0.000287

2nd run:
iteration 1: 0.001044
iteration 2: 0.000942
iteration 3: 0.000935
iteration 4: 0.000935
iteration 5: 0.000935
iteration 6: 0.000932
iteration 7: 0.000954
iteration 8: 0.000940
iteration 9: 0.000933
iteration 10: 0.000625
iteration 11: 0.000545
iteration 12: 0.000428
iteration 13: 0.000362
iteration 14: 0.000346
iteration 15: 0.000325
iteration 16: 0.000309
iteration 17: 0.000309
iteration 18: 0.000316
iteration 19: 0.000310
iteration 20: 0.000308

after evaluating (test-highlight) the figures are as follows:
1st run:
iteration 1: 0.026012
iteration 2: 0.020334
iteration 3: 0.020250
iteration 4: 0.020349
iteration 5: 0.020501
iteration 6: 0.020635
iteration 7: 0.020302
iteration 8: 0.020426
iteration 9: 0.020440
iteration 10: 0.020441
iteration 11: 0.020515
iteration 12: 0.020525
iteration 13: 0.020383
iteration 14: 0.020510
iteration 15: 0.019829
iteration 16: 0.019899
iteration 17: 0.019950
iteration 18: 0.019828
iteration 19: 0.019901
iteration 20: 0.019819

2nd run:
iteration 1: 0.026526
iteration 2: 0.020051
iteration 3: 0.020100
iteration 4: 0.020080
iteration 5: 0.020080
iteration 6: 0.020249
iteration 7: 0.020087
iteration 8: 0.020005
iteration 9: 0.019980
iteration 10: 0.019985
iteration 11: 0.020077
iteration 12: 0.019979
iteration 13: 0.020060
iteration 14: 0.020092
iteration 15: 0.019954
iteration 16: 0.019766
iteration 17: 0.019432
iteration 18: 0.019491
iteration 19: 0.019458
iteration 20: 0.019482

I'm not allowed to share my employer's source code as a test case, so I 
tried the same procedure with the similarly large DeclBase.h from the 
public LLVM repository. To my surprise, DeclBase.h didn't suffer from 
any performance issues at all. Switching to fundamental-mode while 
visiting my file didn't change anything, so I assume that c-mode isn't 
to blame either.
There have been claims of overlay-related performance issues with cquery 
and some large(-ish) open-source C or C++ files, so I'll try to locate 
these files and hope that at least one of them exhibits this issue as well.

On 03/19/2018 04:07 PM, Sebastian Sturm wrote:
> for the file I was complaining about, the number returned is 3080 
> (doesn't exceed the number of chars though, (point-max) = 67641). Will 
> try to obtain usable timing data later today when I get home from work. 
> Thanks!
> 
> On 03/19/2018 03:56 PM, Stefan Monnier wrote:
>>> well no, it's about 2.5ms per call to line-number-at-pos, which is 
>>> called at
>>> least 6 times per character insertion (with my Emacs config, at
>>> least). Which already makes for 15ms per character insertion, excluding
>>> anything else done by cc-mode or lsp-mode.
>>
>> Since you say that the noverlay branch helps, could you check the number
>> of overlays involved?  E.g.
>>
>>      M-: (length (overlays-in (point-min) (point-max))) RET
>>
>> If there are more overlays than chars in this buffer, maybe there's
>> a problem in some Elisp that creates too many overlays?
>>
>> If there aren't that many overlays, then I wonder why the noverlays
>> branch would make such a significant difference.
>>
>> Also, if you can reliably reproduce the "slow editing", would it be
>> possible to make a recipe for it that we can try and reproduce on
>> our side?
>>
>>
>>          Stefan
>>
>>
> 



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-20  1:23                 ` Sebastian Sturm
@ 2018-03-20  6:30                   ` Eli Zaretskii
  2018-03-21  0:36                     ` Sebastian Sturm
  2018-03-22 20:52                   ` Stefan Monnier
  1 sibling, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-20  6:30 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Tue, 20 Mar 2018 02:23:02 +0100
> 
> 1st run:
> iteration 1: 0.001213
> iteration 2: 0.001170
> [...]
> 
> after evaluating (test-highlight) the figures are as follows:
> 1st run:
> iteration 1: 0.026012
> iteration 2: 0.020334

So, between 20-fold and 100-fold slow-down.

> I'm not allowed to share my employer's source code as a test case, so I 
> tried the same procedure with the similarly large DeclBase.h from the 
> public LLVM repository. To my surprise, DeclBase.h didn't suffer from 
> any performance issues at all. Switching to fundamental-mode while 
> visiting my file didn't change anything, so I assume that c-mode isn't 
> to blame either.

So it's still a mystery why your original file produces such a large
slowdown with overlays.

Can you show the results of "M-x profiler-report" for the slow test
with your original source file?  It could have some clues.  If that's
impossible, I can only repeat my suggestion to use perf to find the
code in Emacs that takes the lion's share of the processing time.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-20  6:30                   ` Eli Zaretskii
@ 2018-03-21  0:36                     ` Sebastian Sturm
  2018-03-21  6:47                       ` Eli Zaretskii
  2018-03-22 13:16                       ` Stefan Monnier
  0 siblings, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-21  0:36 UTC (permalink / raw)
  To: emacs-devel

 > So it's still a mystery why your original file produces such a large
 > slowdown with overlays.
 >
 > Can you show the results of "M-x profiler-report" for the slow test
 > with your original source file?  It could have some clues.  If that's
 > impossible, I can only repeat my suggestion to use perf to find the
 > code in Emacs that takes the lion's share of the processing time.

this is the profiler report I get for the slow case (BTW, is there a way 
to have the profiler resolve functions within line-number-at-pos? I 
tried increasing profiler-max-stack-depth to 32, but the profiler still 
didn't show anything below line-number-at-pos)

- command-execute                                               20522  97%
  - call-interactively                                           20522  97%
   - funcall-interactively                                       20061  94%
    - eval-expression                                            18609  87%
     - eval                                                      18609  87%
      - benchmark-often                                          18609  87%
       - let*                                                    18609  87%
        - while                                                  18609  87%
         - message                                               18388  86%
          - format                                               18376  86%
           - nth                                                 18376  86%
            - let                                                18376  86%
             - list                                              18376  86%
              - let                                              18368  86%
                 line-number-at-pos                              18348  86%
            ws-butler-after-change                                   4   0%
    + evil-open-below                                              614   2%
    + counsel-M-x                                                  575   2%
    + undo-tree-undo                                                79   0%
    + next-line                                                     68   0%
    + evil-normal-state                                             52   0%
    + evil-previous-line                                            48   0%
    + previous-line                                                  7   0%
    + evil-emacs-state                                               6   0%
    + evil-insert                                                    3   0%
   + byte-code                                                     461   2%
+ redisplay_internal (C function)                                 254   1%
+ timer-event-handler                                             116   0%
+ evil-escape-pre-command-hook                                     82   0%
+ ...                                                              63   0%
+ global-spacemacs-whitespace-cleanup-mode-check-buffers 
47   0%
+ yas--post-command-handler                                        33   0%
+ evil-repeat-post-hook                                             6   0%
+ evil-visual-post-command                                          5   0%
+ flycheck-handle-signal                                            4   0%
   evil-snipe-mode-check-buffers                                     4   0%
   global-undo-tree-mode-check-buffers                               4   0%
+ sp--save-pre-command-state                                        2   0%
+ xselect-convert-to-string                                         2   0%
+ evil-visual-pre-command                                           1   0%
+ flycheck-pos-tip-hide-messages                                    1   0%
+ which-key--hide-popup                                             1   0%

with perf, the ("self") time taken by buf_charpos_to_bytepos increases 
from ~60% (fast case) to >98%. This is the diff generated by perf diff 
<fast.perf> <slow.perf>:

# Event 'cycles'
#
# Baseline    Delta  Shared Object         Symbol 

# ........  .......  .................... 
..........................................
#
     57.77%  +40.30%  emacs-27.0.50         [.] buf_charpos_to_bytepos
     11.12%  -10.70%  libc-2.23.so          [.] __memrchr
      6.48%   -6.19%  emacs-27.0.50         [.] assq_no_quit
      5.92%   -5.62%  emacs-27.0.50         [.] find_cache_boundary
      4.26%   -4.07%  emacs-27.0.50         [.] set_buffer_internal_2
      4.10%   -3.73%  emacs-27.0.50         [.] find_newline
      3.54%           libc-2.23.so          [.] __memmove_avx_unaligned
      1.25%   -1.19%  emacs-27.0.50         [.] region_cache_forward
      0.46%   -0.41%  [kernel.kallsyms]     [k] 0xffffffff83004eb0
      0.26%   -0.26%  emacs-27.0.50         [.] 
revalidate_region_cache.isra.1
      0.25%   -0.23%  emacs-27.0.50         [.] find_interval
      0.23%   -0.23%  emacs-27.0.50         [.] swap_in_symval_forwarding
      0.19%   -0.18%  emacs-27.0.50         [.] do_symval_forwarding
      0.16%   -0.15%  emacs-27.0.50         [.] eval_sub
      0.16%   -0.15%  emacs-27.0.50         [.] x_produce_glyphs
      0.13%           libXft.so.2.3.2       [.] XftCharIndex
      0.13%   -0.12%  libXft.so.2.3.2       [.] XftGlyphExtents
      0.12%   -0.12%  emacs-27.0.50         [.] store_symval_forwarding
      0.12%   -0.11%  emacs-27.0.50         [.] exec_byte_code
      0.09%           emacs-27.0.50         [.] Fsymbol_value
      0.08%   -0.08%  emacs-27.0.50         [.] x_get_glyph_overhangs
      0.07%           emacs-27.0.50         [.] find_symbol_value
      0.07%           libc-2.23.so          [.] __memcpy_avx_unaligned
      0.07%           emacs-27.0.50         [.] Fassq
      0.07%           emacs-27.0.50         [.] insert_1_both.part.9
      0.06%           emacs-27.0.50         [.] offset_intervals
      0.06%           libc-2.23.so          [.] __memcmp_sse4_1
      0.06%           emacs-27.0.50         [.] next_element_from_buffer
      0.05%   -0.05%  emacs-27.0.50         [.] move_it_in_display_line_to
      0.05%   -0.04%  emacs-27.0.50         [.] get_next_display_element
      0.05%   -0.04%  libXft.so.2.3.2       [.] XftFontCheckGlyph
      0.05%   -0.04%  libX11.so.6.3.0       [.] _XSetClipRectangles
      0.04%           libc-2.23.so          [.] __GI___printf_fp_l
      0.04%           emacs-27.0.50         [.] styled_format
      0.04%   -0.03%  emacs-27.0.50         [.] xftfont_text_extents
      0.04%   -0.03%  emacs-27.0.50         [.] draw_glyphs
      0.04%           emacs-27.0.50         [.] display_line
      0.04%           emacs-27.0.50         [.] validate_interval_range
      0.04%           emacs-27.0.50         [.] update_window_fringes
      0.04%           libX11.so.6.3.0       [.] 0x000000000001b65a
      0.04%   -0.03%  emacs-27.0.50         [.] lookup_char_property
      0.04%           emacs-27.0.50         [.] balance_an_interval
      0.03%           emacs-27.0.50         [.] composition_compute_stop_pos
      0.03%           libpthread-2.23.so    [.] pthread_mutex_unlock
      0.03%   -0.03%  emacs-27.0.50         [.] x_draw_glyph_string
      0.03%   -0.03%  emacs-27.0.50         [.] mem_insert
      0.03%   -0.03%  libpthread-2.23.so    [.] pthread_mutex_lock
      0.03%           libc-2.23.so          [.] malloc
      0.03%           emacs-27.0.50         [.] update_window_line
      0.03%           emacs-27.0.50         [.] Ffuncall
      0.03%   -0.03%  emacs-27.0.50         [.] get_glyph_face_and_encoding
      0.03%   -0.02%  emacs-27.0.50         [.] set_internal
      0.03%   -0.02%  emacs-27.0.50         [.] update_window
      0.03%           libXft.so.2.3.2       [.] XftDrawSrcPicture
      0.03%           libc-2.23.so          [.] vfprintf
      0.03%   -0.02%  emacs-27.0.50         [.] do_one_unbind.constprop.20
      0.03%           emacs-27.0.50         [.] set_iterator_to_next
      0.02%           emacs-27.0.50         [.] specbind
      0.02%           libc-2.23.so          [.] __memset_avx2
      0.02%   -0.02%  libX11.so.6.3.0       [.] _XFlushGCCache
      0.02%           emacs-27.0.50         [.] 
lookup_glyphless_char_display
      0.02%           libc-2.23.so          [.] _int_malloc
      0.02%           emacs-27.0.50         [.] set_cursor_from_row.isra.40
      0.02%   -0.02%  emacs-27.0.50         [.] message_dolog.part.60
      0.02%           emacs-27.0.50         [.] gap_left
      0.02%   -0.02%  emacs-27.0.50         [.] unbind_to
      0.02%   -0.02%  emacs-27.0.50         [.] init_iterator
      0.02%           emacs-27.0.50         [.] Fget_buffer
      0.02%   -0.01%  emacs-27.0.50         [.] overlays_at
      0.02%   -0.01%  [wl]                  [k] osl_readl
      0.02%   -0.01%  emacs-27.0.50         [.] set_point_both
      0.02%           emacs-27.0.50         [.] message3_nolog
      0.02%           libX11.so.6.3.0       [.] _XGetRequest
      0.02%           libc-2.23.so          [.] __sprintf_chk
      0.02%           emacs-27.0.50         [.] adjust_markers_for_insert
      0.02%           emacs-27.0.50         [.] adjust_suspend_auto_hscroll
      0.02%           emacs-27.0.50         [.] 
get_char_property_and_overlay
      0.01%           emacs-27.0.50         [.] window_box_width
      0.01%           emacs-27.0.50         [.] Fcons
      0.01%           libxcb.so.1.1.0       [.] 0x0000000000009cca
      0.01%   -0.01%  emacs-27.0.50         [.] arith_driver
      0.01%   -0.01%  emacs-27.0.50         [.] resize_mini_window
      0.01%           emacs-27.0.50         [.] unchain_marker
      0.01%           emacs-27.0.50         [.] face_for_char
      0.01%           emacs-27.0.50         [.] unblock_input_to
      0.01%           emacs-27.0.50         [.] unblock_input
      0.01%           emacs-27.0.50         [.] 
balance_possible_root_interval
      0.01%           emacs-27.0.50         [.] add_text_properties_1
      0.01%           emacs-27.0.50         [.] save_restriction_restore
      0.01%           emacs-27.0.50         [.] funcall_subr
      0.01%           emacs-27.0.50         [.] gap_right
      0.01%   -0.01%  emacs-27.0.50         [.] Fcurrent_buffer
      0.01%           libXrender.so.1.3.0   [.] XRenderFillRectangle
      0.01%           libXext.so.6.4.0      [.] XdbeSwapBuffers
      0.01%           emacs-27.0.50         [.] 
get_glyph_string_clip_rects.part.72
      0.01%           emacs-27.0.50         [.] Fwhile
      0.01%           emacs-27.0.50         [.] window_wants_mode_line
      0.01%           emacs-27.0.50         [.] display_echo_area_1
      0.01%           libXft.so.2.3.2       [.] XftDrawSetClipRectangles
      0.01%           emacs-27.0.50         [.] append_space_for_newline
      0.01%           emacs-27.0.50         [.] x_update_end
      0.01%           emacs-27.0.50         [.] Fforward_line
      0.01%           emacs-27.0.50         [.] memrchr@plt
      0.01%           emacs-27.0.50         [.] make_uninit_multibyte_string
      0.01%           emacs-27.0.50         [.] prepare_to_modify_buffer_1
      0.01%           emacs-27.0.50         [.] Fstring_equal
      0.01%           emacs-27.0.50         [.] init_glyph_string
      0.01%           libc-2.23.so          [.] _IO_old_init
      0.01%           libXrender.so.1.3.0   [.] XRenderFindDisplay
      0.01%           emacs-27.0.50         [.] Fnreverse
      0.01%           emacs-27.0.50         [.] arithcompare
      0.01%           emacs-27.0.50         [.] font_get_frame_data
      0.01%           emacs-27.0.50         [.] window_box_left
      0.01%           libX11.so.6.3.0       [.] XSetClipRectangles
      0.01%   -0.01%  emacs-27.0.50         [.] Flength
      0.01%           emacs-27.0.50         [.] previous_interval
      0.01%           libXft.so.2.3.2       [.] 0x0000000000007294
      0.01%           emacs-27.0.50         [.] 
x_compute_glyph_string_overhangs
      0.01%           emacs-27.0.50         [.] XftCharIndex@plt
      0.01%           libXrender.so.1.3.0   [.] XRenderCompositeString16
      0.01%           emacs-27.0.50         [.] message3
      0.01%           emacs-27.0.50         [.] xftfont_encode_char
      0.01%           emacs-27.0.50         [.] assign_row
      0.01%           emacs-27.0.50         [.] prepare_desired_row
      0.01%           emacs-27.0.50         [.] xftfont_draw
      0.01%           libXext.so.6.4.0      [.] XextFindDisplay
      0.01%           libpthread-2.23.so    [.] 
pthread_cond_broadcast@@GLIBC_2.3.2
      0.01%           libXft.so.2.3.2       [.] XftDrawGlyphs
      0.01%           emacs-27.0.50         [.] marker_position
      0.01%           emacs-27.0.50         [.] funcall_lambda
      0.01%           emacs-27.0.50         [.] gettime
      0.01%           libpthread-2.23.so    [.] __GI___libc_recvmsg
      0.01%           emacs-27.0.50         [.] lisp_time_struct
      0.01%           emacs-27.0.50         [.] echo_area_display
      0.01%           emacs-27.0.50         [.] CHECK_MARKER
      0.01%           emacs-27.0.50         [.] del_range_2
      0.01%           emacs-27.0.50         [.] prepare_face_for_display
      0.01%           emacs-27.0.50         [.] interval_deletion_adjustment
      0.01%           libc-2.23.so          [.] strlen
      0.01%           emacs-27.0.50         [.] verify_interval_modification
      0.01%           emacs-27.0.50         [.] count_size_as_multibyte
      0.01%           emacs-27.0.50         [.] adjust_overlays_for_insert
      0.01%           libXrender.so.1.3.0   [.] 
XRenderSetPictureClipRectangles
      0.01%           emacs-27.0.50         [.] buffer_local_value
      0.01%           emacs-27.0.50         [.] adjust_window_count
      0.01%           emacs-27.0.50         [.] 
notice_overwritten_cursor.part.52
      0.01%           libc-2.23.so          [.] __GI___writev
      0.01%           emacs-27.0.50         [.] record_in_backtrace
      0.01%           emacs-27.0.50         [.] save_restriction_save
      0.01%           emacs-27.0.50         [.] Fget_text_property
      0.01%   -0.00%  [vdso]                [.] __vdso_clock_gettime
      0.01%           emacs-27.0.50         [.] window_wants_header_line
      0.01%           emacs-27.0.50         [.] Fmessage
      0.01%           emacs-27.0.50         [.] FUNCTIONP
      0.01%           emacs-27.0.50         [.] window_display_table
      0.01%           emacs-27.0.50         [.] maybe_quit
      0.01%           emacs-27.0.50         [.] clear_glyph_matrix
      0.01%   -0.00%  emacs-27.0.50         [.] with_echo_area_buffer
      0.01%           libc-2.23.so          [.] __libc_enable_asynccancel
      0.01%           emacs-27.0.50         [.] should_produce_line_number
      0.01%           emacs-27.0.50         [.] x_set_glyph_string_clipping
      0.01%           emacs-27.0.50         [.] set_message_1
      0.01%           emacs-27.0.50         [.] del_range_both
      0.01%           emacs-27.0.50         [.] record_insert
      0.01%           emacs-27.0.50         [.] adjust_overlays_for_delete
      0.01%   +0.00%  libc-2.23.so          [.] _int_free
      0.01%           libc-2.23.so          [.] __strchrnul
      0.01%           emacs-27.0.50         [.] Fgoto_char
      0.01%           emacs-27.0.50         [.] free_misc
      0.01%           emacs-27.0.50         [.] draw_window_fringes
      0.01%           emacs-27.0.50         [.] x_flush.isra.37.part.38
      0.01%           libpthread-2.23.so    [.] __errno_location
      0.01%           emacs-27.0.50         [.] make_float
      0.01%           emacs-27.0.50         [.] fill_glyph_string
      0.01%           emacs-27.0.50         [.] Fget
      0.01%           emacs-27.0.50         [.] Flocal_variable_p
      0.01%           emacs-27.0.50         [.] decode_time_components
      0.01%           emacs-27.0.50         [.] XftGlyphExtents@plt
      0.01%           emacs-27.0.50         [.] row_for_charpos_p
      0.01%           emacs-27.0.50         [.] load_overlay_strings
      0.01%           emacs-27.0.50         [.] allocate_misc
      0.01%   -0.00%  libX11.so.6.3.0       [.] _XSend
      0.01%           libX11.so.6.3.0       [.] _XData32
      0.01%   -0.00%  emacs-27.0.50         [.] buf_bytepos_to_charpos
      0.01%           emacs-27.0.50         [.] face_at_buffer_position
      0.01%           emacs-27.0.50         [.] invalidate_current_column
      0.01%           emacs-27.0.50         [.] find_composition
      0.01%           emacs-27.0.50         [.] x_draw_window_cursor
      0.01%           emacs-27.0.50         [.] x_flip_and_flush
      0.01%           libc-2.23.so          [.] _IO_no_init
      0.01%           emacs-27.0.50         [.] Ftime_subtract
      0.01%           emacs-27.0.50         [.] row_hash
      0.01%           emacs-27.0.50         [.] lookup_basic_face
      0.01%           emacs-27.0.50         [.] memcpy@plt
      0.01%           emacs-27.0.50         [.] Fset_buffer
      0.01%           emacs-27.0.50         [.] produce_special_glyphs
      0.00%           emacs-27.0.50         [.] 
x_draw_glyph_string_background.part.44
      0.00%           [vdso]                [.] 0x0000000000000939
      0.00%           emacs-27.0.50         [.] make_save_obj_obj_obj_obj
      0.00%           libc-2.23.so          [.] __GI___libc_poll
      0.00%           emacs-27.0.50         [.] do_specbind
      0.00%           libXft.so.2.3.2       [.] XftGlyphRender
      0.00%           emacs-27.0.50         [.] move_it_to
      0.00%           emacs-27.0.50         [.] make_current.isra.14
      0.00%           emacs-27.0.50         [.] Fnext_single_property_change
      0.00%           emacs-27.0.50         [.] handle_face_prop
      0.00%           libX11.so.6.3.0       [.] _XFlush
      0.00%           emacs-27.0.50         [.] unwind_with_echo_area_buffer
      0.00%           emacs-27.0.50         [.] evaporate_overlays
      0.00%           emacs-27.0.50         [.] sort_overlays
      0.00%           emacs-27.0.50         [.] del_range_1
      0.00%           libX11.so.6.3.0       [.] XFlush
      0.00%           emacs-27.0.50         [.] try_window
      0.00%           emacs-27.0.50         [.] x_set_glyph_string_gc
      0.00%           libc-2.23.so          [.] malloc_consolidate
      0.00%           emacs-27.0.50         [.] handle_stop
      0.00%           emacs-27.0.50         [.] recenter_overlay_lists
      0.00%           libX11.so.6.3.0       [.] pthread_mutex_lock@plt
      0.00%           emacs-27.0.50         [.] modify_text_properties
      0.00%           emacs-27.0.50         [.] delete_interval
      0.00%           emacs-27.0.50         [.] window_text_bottom_y
      0.00%   +0.00%  emacs-27.0.50         [.] copy_text
      0.00%           emacs-27.0.50         [.] set_default_internal
      0.00%   +0.00%  libxcb.so.1.1.0       [.] xcb_writev
      0.00%           emacs-27.0.50         [.] temp_set_point_both
      0.00%           emacs-27.0.50         [.] insert_1_both
      0.00%           emacs-27.0.50         [.] record_property_change
      0.00%           emacs-27.0.50         [.] lisp_align_malloc
      0.00%           emacs-27.0.50         [.] x_mark_frame_dirty
      0.00%   +0.00%  emacs-27.0.50         [.] text_quoting_style
      0.00%           libc-2.23.so          [.] __GI___mempcpy
      0.00%           emacs-27.0.50         [.] Ffloat_time
      0.00%           emacs-27.0.50         [.] x_write_glyphs
      0.00%   +0.00%  emacs-27.0.50         [.] insert_from_string_1
      0.00%           emacs-27.0.50         [.] Fpoint
      0.00%           libc-2.23.so          [.] __vsprintf_chk
      0.00%           libc-2.23.so          [.] hack_digit
      0.00%           emacs-27.0.50         [.] grow_specpdl
      0.00%           emacs-27.0.50         [.] time_arith
      0.00%           emacs-27.0.50         [.] update_end
      0.00%           emacs-27.0.50         [.] allocate_string_data
      0.00%           emacs-27.0.50         [.] save_excursion_restore
      0.00%   +0.00%  emacs-27.0.50         [.] Flet
      0.00%           emacs-27.0.50         [.] mem_rotate_right
      0.00%           emacs-27.0.50         [.] fetch_buffer_markers
      0.00%           emacs-27.0.50         [.] move_gap_both
      0.00%           emacs-27.0.50         [.] xmalloc
      0.00%           emacs-27.0.50         [.] update_begin
      0.00%           emacs-27.0.50         [.] float_arith_driver
      0.00%           emacs-27.0.50         [.] make_specified_string
      0.00%           emacs-27.0.50         [.] handle_composition_prop
      0.00%           emacs-27.0.50         [.] display_and_set_cursor
      0.00%           emacs-27.0.50         [.] compute_line_metrics
      0.00%           emacs-27.0.50         [.] clock_gettime@plt
      0.00%           libc-2.23.so          [.] free
      0.00%           emacs-27.0.50         [.] Fplist_get
      0.00%           libgdk-3.so.0.1800.9  [.] gdk_display_manager_get
      0.00%           emacs-27.0.50         [.] make_interval
      0.00%           libxcb.so.1.1.0       [.] xcb_poll_for_event
      0.00%           emacs-27.0.50         [.] adjust_markers_for_delete
      0.00%           emacs-27.0.50         [.] do_pending_window_change
      0.00%           emacs-27.0.50         [.] XftDrawGlyphs@plt
      0.00%           libc-2.23.so          [.] _itoa_word
      0.00%           emacs-27.0.50         [.] disassemble_lisp_time
      0.00%           emacs-27.0.50         [.] Ftext_properties_at
      0.00%           emacs-27.0.50         [.] composition_reseat_it
      0.00%           libX11.so.6.3.0       [.] _XEventsQueued
      0.00%           emacs-27.0.50         [.] minmax_driver
      0.00%           emacs-27.0.50         [.] set_marker_restricted_both
      0.00%           emacs-27.0.50         [.] graft_intervals_into_buffer
      0.00%           emacs-27.0.50         [.] disp_char_vector
      0.00%           libc-2.23.so          [.] __clock_gettime
      0.00%           emacs-27.0.50         [.] CHECK_STRING_OR_BUFFER
      0.00%           emacs-27.0.50         [.] intervals_equal
      0.00%           emacs-27.0.50         [.] ensure_echo_area_buffers
      0.00%           emacs-27.0.50         [.] invalidate_buffer_caches
      0.00%           emacs-27.0.50         [.] set_marker_both
      0.00%   +0.00%  emacs-27.0.50         [.] record_unwind_protect
      0.00%           emacs-27.0.50         [.] run_hook_with_args
      0.00%           emacs-27.0.50         [.] set_point_from_marker
      0.00%           emacs-27.0.50         [.] list4
      0.00%           libX11.so.6.3.0       [.] XSetClipMask
      0.00%           emacs-27.0.50         [.] record_buffer_markers
      0.00%           emacs-27.0.50         [.] default_value
              +0.00%  libXext.so.6.4.0      [.] 0x000000000000bf10
              +0.00%  emacs-27.0.50         [.] Fsetq
              +0.00%  emacs-27.0.50         [.] reseat_1
              +0.00%  emacs-27.0.50         [.] update_compositions

this is what perf annotate shows when invoked on buf_charpos_to_bytepos 
(slow case):

        │     ↓ jle    438 
                ▒
   4,39 │       mov    0x20(%rax),%r8 
                ▒
   8,38 │       mov    %rdx,%rbp 
                ▒
   0,05 │2f0:   mov    %rbp,%rdx 
                ▒
   0,13 │       mov    %r8,%rdi 
                ▒
   2,70 │       sub    %r13,%rdx 
                ▒
   2,22 │       sub    %rbx,%rdi 
                ▒
   0,05 │       cmp    %rdi,%rdx 
                ▒
        │     ↑ je     1e9 
                ▒
        │ 
                ▒
        │           /* If we are down to a range of 50 chars, 
                ▒
        │              don't bother checking any other markers; 
                ▒
        │              scan the intervening chars directly now.  */ 
                ▒
        │           if (best_above - best_below < 50) 
                ▒
   3,21 │305:   cmp    $0x31,%rdx 
                ▒
        │     ↓ jle    480 
                ▒
        │       CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b)); 
                ▒
        │ 
                ▒
        │       if (b == cached_buffer && BUF_MODIFF (b) == 
cached_modiff)              ▒
        │         CONSIDER (cached_charpos, cached_bytepos); 
                ▒
        │ 
                ▒
        │       for (tail = BUF_MARKERS (b); tail; tail = tail->next) 
                ▒
   2,50 │       mov    0x10(%rax),%rax 
                ▒
   4,25 │       test   %rax,%rax 
                ▒
        │     ↓ je     480 
                ▒
        │         { 
                ◆
        │           CONSIDER (tail->charpos, tail->bytepos); 
                ▒
   2,64 │31c:   mov    0x18(%rax),%rdx 
                ▒
  59,16 │       cmp    %rdx,%rsi 
                ▒
        │     ↓ je     638 
                ▒
   5,70 │       cmp    %rdx,%rsi 
                ▒
        │     ↑ jl     2e0 
                ▒
   0,00 │       cmp    %rdx,%r13 
                ▒
        │     ↓ jge    438 
                ▒
   0,00 │       mov    0x20(%rax),%rbx 
                ▒
   0,01 │       mov    %rdx,%r13 
                ▒
        │     ↑ jmp    2f0 
                ▒
        │     marker_byte_position(): 
                ▒
        │       if (!buf) 
                ▒
        │         error ("Marker does not point anywhere"); 
                ▒
        │ 
                ▒
        │       eassert (BUF_BEG_BYTE (buf) <= m->bytepos && m->bytepos 
<= BUF_Z_BYTE (b▒
        │ 
                ▒
        │       return m->bytepos; 
                ▒
        │340:   mov    0x20(%rdi),%rbx 
                ▒
        │     ↑ jmpq   cb 
                ▒
        │       nop 
                ▒
        │     buf_charpos_to_bytepos(): 
                ▒
        │ 
                ▒
        │          If at any point we can tell that the space between 
those             ▒
        │          two best approximations is all single-byte, 
                ▒
        │          we interpolate the result immediately.  */ 
                ▒
        │ 
                ▒
        │       CONSIDER (BUF_PT (b), BUF_PT_BYTE (b)); 
                ▒
        │350:   mov    0x2f0(%rdi),%r13 
                ▒
        │       cmp    %rsi,%r13 
                ▒

to my eyes, the fast case doesn't look too different though:

        │     ↓ jle    438
   0,83 │       mov    0x20(%rax),%r8
   0,93 │       mov    %rdx,%rbp
        │2f0:   mov    %rbp,%rdx
   0,06 │       mov    %r8,%rdi
   0,20 │       sub    %r13,%rdx
   0,11 │       sub    %rbx,%rdi
   0,12 │       cmp    %rdi,%rdx
        │     ↑ je     1e9
        │
        │           /* If we are down to a range of 50 chars,
        │              don't bother checking any other markers;
        │              scan the intervening chars directly now.  */
        │           if (best_above - best_below < 50)
  11,24 │305:   cmp    $0x31,%rdx
        │     ↓ jle    480
        │       CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));
        │
        │       if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
        │         CONSIDER (cached_charpos, cached_bytepos);
        │
        │       for (tail = BUF_MARKERS (b); tail; tail = tail->next)
   0,16 │       mov    0x10(%rax),%rax
  10,41 │       test   %rax,%rax
        │     ↓ je     480
        │         {
        │           CONSIDER (tail->charpos, tail->bytepos);
   3,19 │31c:   mov    0x18(%rax),%rdx
  42,86 │       cmp    %rdx,%rsi
        │     ↓ je     638
  10,36 │       cmp    %rdx,%rsi
        │     ↑ jl     2e0
   2,24 │       cmp    %rdx,%r13
        │     ↓ jge    438
   0,76 │       mov    0x20(%rax),%rbx
   0,43 │       mov    %rdx,%r13
        │     ↑ jmp    2f0
        │     marker_byte_position():
        │       if (!buf)
        │         error ("Marker does not point anywhere");
        │
        │       eassert (BUF_BEG_BYTE (buf) <= m->bytepos && m->bytepos 
<= BUF_Z_BYTE (bu
        │
        │       return m->bytepos;
        │340:   mov    0x20(%rdi),%rbx
        │     ↑ jmpq   cb
        │       nop
        │     buf_charpos_to_bytepos():
        │
        │          If at any point we can tell that the space between those
        │          two best approximations is all single-byte,
        │          we interpolate the result immediately.  */
        │
        │       CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
        │350:   mov    0x2f0(%rdi),%r13
        │       cmp    %rsi,%r13

I hope this is of some use, but I'll keep looking for open source files 
to reproduce the issue



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-21  0:36                     ` Sebastian Sturm
@ 2018-03-21  6:47                       ` Eli Zaretskii
  2018-03-22 13:16                       ` Stefan Monnier
  1 sibling, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-21  6:47 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Wed, 21 Mar 2018 01:36:38 +0100
> 
> this is the profiler report I get for the slow case (BTW, is there a way 
> to have the profiler resolve functions within line-number-at-pos?

Yes: load simple.el manually before running the benchmark.

> with perf, the ("self") time taken by buf_charpos_to_bytepos increases 
> from ~60% (fast case) to >98%. This is the diff generated by perf diff 
> <fast.perf> <slow.perf>:
> 
> # Event 'cycles'
> #
> # Baseline    Delta  Shared Object         Symbol 
> 
> # ........  .......  .................... 
> ..........................................
> #
>      57.77%  +40.30%  emacs-27.0.50         [.] buf_charpos_to_bytepos

This seems to confirm Stefan's guess that converting character
positions to byte positions takes most of the time, which might make
sense with a lot of overlays (because each overlay uses 2 markers).

Does your code call line-number-at-pos at random positions in the
buffer, or are the positions close to one another?  If the latter, you
might be better off calling count-lines directly, starting at the line
where you previously calculated the line number, instead of calling
line-number-at-pos, which always begins at point-min.




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 20:39 ` Eli Zaretskii
  2018-03-18 21:04   ` Sebastian Sturm
@ 2018-03-21 14:14   ` Sebastien Chapuis
  2018-03-21 15:35     ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Sebastien Chapuis @ 2018-03-21 14:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Sebastian Sturm, emacs-devel


Eli Zaretskii writes:

>> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
>> Date: Sun, 18 Mar 2018 21:14:53 +0100
>>
>> [1] I'm using cquery for my C++ editing needs, which comes with an
>> overlay-based semantic highlighting mechanism. With my emacs
>> configuration, lsp-mode/lsp-ui emit 6 calls to line-number-at-pos per
>> character insertion, which consume ~20 to 25 ms each when performing
>> edits close to the bottom of a 66KB C++ file (measured using
>> (benchmark-run 1000 (line-number-at-pos (point))) on a release build of
>> emacs-27/git commit #9942734...). Using the noverlay branch, this figure
>> drops to ~160us per call.
>
> If lsp-mode/lsp-ui needs a fast line counter, one can easily be
> provided by exposing find_newline to Lisp.  IME, it's lightning-fast,
> and should run circles around count-lines (used by line-number-at-pos).
>
> (I'm not sure I even understand how overlays come into play here,
> btw.)

The language server protocol defines a position in file with zero-indexed
line and column offsets [1]:

```
interface Position {
	line: number;
	character: number;
}
```

lsp-mode uses heavily line-number-at-pos to convert an Emacs buffer
point to a LSP position, and vice-versa [2].
This can happen thousands times on each keystroke.

If Emacs could provide a function to do the conversion very fast (or at
least faster than with line-number-at-pos), it would be great.


[1] https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#position
[2] The conversion from a LSP position to point doesn't use
line-number-at-pos, but forward-line and forward-char. It's still very slow.

--
Sebastien Chapuis



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-21 14:14   ` Sebastien Chapuis
@ 2018-03-21 15:35     ` Eli Zaretskii
  0 siblings, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-21 15:35 UTC (permalink / raw)
  To: Sebastien Chapuis; +Cc: s.sturm, emacs-devel

> From: Sebastien Chapuis <sebastien@chapu.is>
> Cc: Sebastian Sturm <s.sturm@arkona-technologies.de>, emacs-devel@gnu.org
> Date: Wed, 21 Mar 2018 15:14:18 +0100
> 
> The language server protocol defines a position in file with zero-indexed
> line and column offsets [1]:
> 
> ```
> interface Position {
> 	line: number;
> 	character: number;
> }
> ```
> 
> lsp-mode uses heavily line-number-at-pos to convert an Emacs buffer
> point to a LSP position, and vice-versa [2].

line-number-at-pos is inefficient, in that it always counts from the
beginning of the buffer.  By keeping already computed line numbers
around, you could make a faster implementation if you count relative
line offsets using count-lines instead.

> This can happen thousands times on each keystroke.

_Thousands_ times for _each_ keystroke?  Why is that?  Most keystrokes
only change a single line, so how come you need thousands of lines
recounted each time?

> If Emacs could provide a function to do the conversion very fast (or at
> least faster than with line-number-at-pos), it would be great.

Given the above, I think you need to describe the issue in more
details, before we even begin designing the solution.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-21  0:36                     ` Sebastian Sturm
  2018-03-21  6:47                       ` Eli Zaretskii
@ 2018-03-22 13:16                       ` Stefan Monnier
  2018-03-22 19:54                         ` Sebastian Sturm
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-22 13:16 UTC (permalink / raw)
  To: emacs-devel

> this is the profiler report I get for the slow case (BTW, is there a way to
> have the profiler resolve functions within line-number-at-pos?

It should do that without you asking.  I mean, it won't show you
`goto-char` and `point-min` kind of things since these are "inlined"
(actually turned into their own byte-code), but `count-lines` should
definitely be there.

>              - let                                              18368  86%
>                 line-number-at-pos                              18348  86%

It's very odd that there's no `count-lines` down here (and `count-lines`
is a perfectly normal Elisp function that's not inlined or otherwise
treated specially), since it should be where most of the time is spent!

If we assume the code works as intended, it would imply that the time is
not spent in `count-lines` but elsewhere, e.g. in `goto-char`.

> with perf, the ("self") time taken by buf_charpos_to_bytepos increases from
> ~60% (fast case) to >98%. This is the diff generated by perf diff
> <fast.perf> <slow.perf>:

So I guess that could be it: the (goto-char opoint) spends an inordinate
amount of time in buf_charpos_to_bytepos.

Could you try the patch below, to see if it makes a difference?


        Stefan


diff --git a/src/marker.c b/src/marker.c
index 7773c4fce0..3d808fd6fa 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -141,6 +141,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = 50;
 
   eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
 
@@ -180,8 +181,10 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - best_below < distance)
 	break;
+      else
+        distance = distance + 10;
     }
 
   /* We get here if we did not exactly hit one of the known places.




^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-22 13:16                       ` Stefan Monnier
@ 2018-03-22 19:54                         ` Sebastian Sturm
  2018-03-22 20:04                           ` Sebastian Sturm
  0 siblings, 1 reply; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-22 19:54 UTC (permalink / raw)
  To: emacs-devel

 >
 > It should do that without you asking.  I mean, it won't show you
 > `goto-char` and `point-min` kind of things since these are "inlined"
 > (actually turned into their own byte-code), but `count-lines` should
 > definitely be there.
 >
 >>               - let 
18368  86%
 >>                  line-number-at-pos 
18348  86%
 >
 > It's very odd that there's no `count-lines` down here (and `count-lines`
 > is a perfectly normal Elisp function that's not inlined or otherwise
 > treated specially), since it should be where most of the time is spent!

when first evaluating simple.el as Eli suggested, the profiler indeed 
shows that pretty much all time is spent somewhere within `count-lines`. 
Unfortunately the patch didn't seem to help though I'll double-check at 
home (also, it seemed to improve baseline performance, but I'm not sure 
if I should regard that as a random fluctuation, will need to do more 
measurements).
Also, this time even the first measurement with a small number of 
overlays was extremely slow, not sure what to make of that.

In general however, the trend seems clear to me --- for this one file, 
overlays hurt `line-number-at-pos` performance very much, except when 
using the noverlay branch.
Are there other things I should look for in my file that might 
negatively affect performance (strange codepoints or anything else that 
might perhaps upset `buf_charpos_to_bytepos`)?

baseline (emacs-27 master #667cdf42..., approx. 5-10 overlays created by 
flycheck):
iteration 1: 0.012696
iteration 2: 0.002595
iteration 3: 0.002606
iteration 4: 0.002601
iteration 5: 0.002649
iteration 6: 0.002605
iteration 7: 0.002594
iteration 8: 0.002601
iteration 9: 0.002603
iteration 10: 0.002601
iteration 11: 0.002606
iteration 12: 0.002626
iteration 13: 0.002603
iteration 14: 0.002642
iteration 15: 0.002599
iteration 16: 0.002598
iteration 17: 0.002600
iteration 18: 0.002601
iteration 19: 0.002599
iteration 20: 0.002608

emacs-27 master, approx. 6200 overlays created by (test-highlight)
iteration 1: 0.022795
iteration 2: 0.015560
iteration 3: 0.015697
iteration 4: 0.015913
iteration 5: 0.015894
iteration 6: 0.016063
iteration 7: 0.015928
iteration 8: 0.015890
iteration 9: 0.015278
iteration 10: 0.015515
iteration 11: 0.015327
iteration 12: 0.015326
iteration 13: 0.015574
iteration 14: 0.015319
iteration 15: 0.015370
iteration 16: 0.015354
iteration 17: 0.015333
iteration 18: 0.015312
iteration 19: 0.015481
iteration 20: 0.015411

emacs-27 master + patch "distance+10", ~10 overlays created by flycheck:
iteration 1: 0.002938
iteration 2: 0.001132
iteration 3: 0.000550
iteration 4: 0.000468
iteration 5: 0.000470
iteration 6: 0.000451
iteration 7: 0.000449
iteration 8: 0.000451
iteration 9: 0.000450
iteration 10: 0.000449
iteration 11: 0.000448
iteration 12: 0.000452
iteration 13: 0.000451
iteration 14: 0.000443
iteration 15: 0.000448
iteration 16: 0.000443
iteration 17: 0.000444
iteration 18: 0.000445
iteration 19: 0.000445
iteration 20: 0.000445

emacs-27 master + patch "distance+10", ~6200 overlays created by 
(test-highlight):
iteration 1: 0.019673
iteration 2: 0.014469
iteration 3: 0.014491
iteration 4: 0.014430
iteration 5: 0.014493
iteration 6: 0.014704
iteration 7: 0.014741
iteration 8: 0.014536
iteration 9: 0.014433
iteration 10: 0.014469
iteration 11: 0.014429
iteration 12: 0.014509
iteration 13: 0.014484
iteration 14: 0.014487
iteration 15: 0.014524
iteration 16: 0.014449
iteration 17: 0.014501
iteration 18: 0.014469
iteration 19: 0.014429
iteration 20: 0.014507

noverlay branch (#886933...), ~10 overlays:
iteration 1: 0.002370
iteration 2: 0.001191
iteration 3: 0.001189
iteration 4: 0.001162
iteration 5: 0.001095
iteration 6: 0.001096
iteration 7: 0.000522
iteration 8: 0.000531
iteration 9: 0.000369
iteration 10: 0.000365
iteration 11: 0.000365
iteration 12: 0.000366
iteration 13: 0.000365
iteration 14: 0.000380
iteration 15: 0.000367
iteration 16: 0.000365
iteration 17: 0.000365
iteration 18: 0.000365
iteration 19: 0.000363
iteration 20: 0.000368

noverlay branch, ~6200 overlays:
iteration 1: 0.001878
iteration 2: 0.001831
iteration 3: 0.001838
iteration 4: 0.001826
iteration 5: 0.001027
iteration 6: 0.000722
iteration 7: 0.000531
iteration 8: 0.000479
iteration 9: 0.000480
iteration 10: 0.000479
iteration 11: 0.000479
iteration 12: 0.000479
iteration 13: 0.000478
iteration 14: 0.000479
iteration 15: 0.000478
iteration 16: 0.000478
iteration 17: 0.000482
iteration 18: 0.000479
iteration 19: 0.000480
iteration 20: 0.000476



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-22 19:54                         ` Sebastian Sturm
@ 2018-03-22 20:04                           ` Sebastian Sturm
  0 siblings, 0 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-22 20:04 UTC (permalink / raw)
  To: emacs-devel

for the record, this is what I get with the (non-noverlay, non-patched) 
emacs-27 master branch after calling `(set-buffer-multibyte nil)`. Guess 
this merely confirms what you already found out, but for me this is a 
viable workaround until the issue is properly resolved. Will keep 
looking for an open source test file over the weekend

emacs-27 master, ~6200 overlays, enable-multibyte-characters = nil:
iteration 1: 0.000300
iteration 2: 0.000268
iteration 3: 0.000267
iteration 4: 0.000268
iteration 5: 0.000267
iteration 6: 0.000266
iteration 7: 0.000263
iteration 8: 0.000263
iteration 9: 0.000277
iteration 10: 0.000269
iteration 11: 0.000264
iteration 12: 0.000266
iteration 13: 0.000263
iteration 14: 0.000265
iteration 15: 0.000273
iteration 16: 0.000267
iteration 17: 0.000266
iteration 18: 0.000266
iteration 19: 0.000265
iteration 20: 0.000263


On 03/22/2018 08:54 PM, Sebastian Sturm wrote:
>  >
>  > It should do that without you asking.  I mean, it won't show you
>  > `goto-char` and `point-min` kind of things since these are "inlined"
>  > (actually turned into their own byte-code), but `count-lines` should
>  > definitely be there.
>  >
>  >>               - let 18368  86%
>  >>                  line-number-at-pos 18348  86%
>  >
>  > It's very odd that there's no `count-lines` down here (and `count-lines`
>  > is a perfectly normal Elisp function that's not inlined or otherwise
>  > treated specially), since it should be where most of the time is spent!
> 
> when first evaluating simple.el as Eli suggested, the profiler indeed 
> shows that pretty much all time is spent somewhere within `count-lines`. 
> Unfortunately the patch didn't seem to help though I'll double-check at 
> home (also, it seemed to improve baseline performance, but I'm not sure 
> if I should regard that as a random fluctuation, will need to do more 
> measurements).
> Also, this time even the first measurement with a small number of 
> overlays was extremely slow, not sure what to make of that.
> 
> In general however, the trend seems clear to me --- for this one file, 
> overlays hurt `line-number-at-pos` performance very much, except when 
> using the noverlay branch.
> Are there other things I should look for in my file that might 
> negatively affect performance (strange codepoints or anything else that 
> might perhaps upset `buf_charpos_to_bytepos`)?
> 
> baseline (emacs-27 master #667cdf42..., approx. 5-10 overlays created by 
> flycheck):
> iteration 1: 0.012696
> iteration 2: 0.002595
> iteration 3: 0.002606
> iteration 4: 0.002601
> iteration 5: 0.002649
> iteration 6: 0.002605
> iteration 7: 0.002594
> iteration 8: 0.002601
> iteration 9: 0.002603
> iteration 10: 0.002601
> iteration 11: 0.002606
> iteration 12: 0.002626
> iteration 13: 0.002603
> iteration 14: 0.002642
> iteration 15: 0.002599
> iteration 16: 0.002598
> iteration 17: 0.002600
> iteration 18: 0.002601
> iteration 19: 0.002599
> iteration 20: 0.002608
> 
> emacs-27 master, approx. 6200 overlays created by (test-highlight)
> iteration 1: 0.022795
> iteration 2: 0.015560
> iteration 3: 0.015697
> iteration 4: 0.015913
> iteration 5: 0.015894
> iteration 6: 0.016063
> iteration 7: 0.015928
> iteration 8: 0.015890
> iteration 9: 0.015278
> iteration 10: 0.015515
> iteration 11: 0.015327
> iteration 12: 0.015326
> iteration 13: 0.015574
> iteration 14: 0.015319
> iteration 15: 0.015370
> iteration 16: 0.015354
> iteration 17: 0.015333
> iteration 18: 0.015312
> iteration 19: 0.015481
> iteration 20: 0.015411
> 
> emacs-27 master + patch "distance+10", ~10 overlays created by flycheck:
> iteration 1: 0.002938
> iteration 2: 0.001132
> iteration 3: 0.000550
> iteration 4: 0.000468
> iteration 5: 0.000470
> iteration 6: 0.000451
> iteration 7: 0.000449
> iteration 8: 0.000451
> iteration 9: 0.000450
> iteration 10: 0.000449
> iteration 11: 0.000448
> iteration 12: 0.000452
> iteration 13: 0.000451
> iteration 14: 0.000443
> iteration 15: 0.000448
> iteration 16: 0.000443
> iteration 17: 0.000444
> iteration 18: 0.000445
> iteration 19: 0.000445
> iteration 20: 0.000445
> 
> emacs-27 master + patch "distance+10", ~6200 overlays created by 
> (test-highlight):
> iteration 1: 0.019673
> iteration 2: 0.014469
> iteration 3: 0.014491
> iteration 4: 0.014430
> iteration 5: 0.014493
> iteration 6: 0.014704
> iteration 7: 0.014741
> iteration 8: 0.014536
> iteration 9: 0.014433
> iteration 10: 0.014469
> iteration 11: 0.014429
> iteration 12: 0.014509
> iteration 13: 0.014484
> iteration 14: 0.014487
> iteration 15: 0.014524
> iteration 16: 0.014449
> iteration 17: 0.014501
> iteration 18: 0.014469
> iteration 19: 0.014429
> iteration 20: 0.014507
> 
> noverlay branch (#886933...), ~10 overlays:
> iteration 1: 0.002370
> iteration 2: 0.001191
> iteration 3: 0.001189
> iteration 4: 0.001162
> iteration 5: 0.001095
> iteration 6: 0.001096
> iteration 7: 0.000522
> iteration 8: 0.000531
> iteration 9: 0.000369
> iteration 10: 0.000365
> iteration 11: 0.000365
> iteration 12: 0.000366
> iteration 13: 0.000365
> iteration 14: 0.000380
> iteration 15: 0.000367
> iteration 16: 0.000365
> iteration 17: 0.000365
> iteration 18: 0.000365
> iteration 19: 0.000363
> iteration 20: 0.000368
> 
> noverlay branch, ~6200 overlays:
> iteration 1: 0.001878
> iteration 2: 0.001831
> iteration 3: 0.001838
> iteration 4: 0.001826
> iteration 5: 0.001027
> iteration 6: 0.000722
> iteration 7: 0.000531
> iteration 8: 0.000479
> iteration 9: 0.000480
> iteration 10: 0.000479
> iteration 11: 0.000479
> iteration 12: 0.000479
> iteration 13: 0.000478
> iteration 14: 0.000479
> iteration 15: 0.000478
> iteration 16: 0.000478
> iteration 17: 0.000482
> iteration 18: 0.000479
> iteration 19: 0.000480
> iteration 20: 0.000476
> 



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-20  1:23                 ` Sebastian Sturm
  2018-03-20  6:30                   ` Eli Zaretskii
@ 2018-03-22 20:52                   ` Stefan Monnier
  2018-03-22 23:11                     ` Sebastian Sturm
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-22 20:52 UTC (permalink / raw)
  To: emacs-devel

> (defun benchmark-often ()
>   (cl-loop for n from 1 upto 20 do
>            (message (format "iteration %d: %f" n (nth 0 (benchmark-run
> (line-number-at-pos (point))))))))
                      ^^^^^^^
Where is this "point" in your tests (I expect the timing to vary
significantly depending on this).

> 1st run:
> iteration 1: 0.001213
> iteration 2: 0.001170
> iteration 3: 0.001170
> iteration 4: 0.001238
> iteration 5: 0.001163
> iteration 6: 0.001153
> iteration 7: 0.000421
> iteration 8: 0.000426
> iteration 9: 0.000322
> iteration 10: 0.000301
> iteration 11: 0.000291
> iteration 12: 0.000292
> iteration 13: 0.000291
> iteration 14: 0.000291
> iteration 15: 0.000295
> iteration 16: 0.000289
> iteration 17: 0.000289
> iteration 18: 0.000288
> iteration 19: 0.000288
> iteration 20: 0.000287

I recommend you don't bother outputting all 20 results: better summarize
it by getting rid of the first test and then giving e.g. the sum or the
median of the rest.

> I'm not allowed to share my employer's source code as a test case, so
> I tried the same procedure with the similarly large DeclBase.h from the
> public LLVM repository. To my surprise, DeclBase.h didn't suffer from any
> performance issues at all.

My crystal ball tells me that DeclBase.h is pure ASCII so byte<->char
conversion is trivial, whereas your file likely contains umlauts and
other disreputable characters.

Here's a similar test case to yours but which builds up its own
artificial buffer with a few non-ascii chars to spice things up:

       (with-temp-buffer
         (dotimes (i 1000)
           (insert "lksajflahalskjdféefawrgfrüegf\n"))
         (let ((txtbuf (current-buffer)))
           (dotimes (s 4)
             (with-temp-buffer
               (insert-buffer-substring txtbuf)
               (let ((stepsize (lsh 10 (* 4 s))))
                 (cl-loop for n from (point-min) upto (- (point-max) stepsize)
                          by stepsize do
                          (let ((ov (make-overlay n (+ (1- stepsize) n))))
                            (overlay-put ov 'cquery-sem-highlight t))))
               (dotimes (i 4)
                 (let ((timing
                        (benchmark-run 1000
                          (line-number-at-pos
                           (+ (point-min) (* i (/ (buffer-size) 4)))))))
                   (message "ols=%S pos=%S/4 time=%.4f (+ %S)"
                            (/ (buffer-size) (lsh 10 (* 4 s))) i
                            (car timing) (cdr timing)))
                 )))))

This gave me (on my top-of-the-line Thinkpad T61 using Debian's `emacs25`):

    ols=3000 pos=0/4 time=0.0018 (+ (0 0.0))
    ols=3000 pos=1/4 time=6.1074 (+ (0 0.0))
    ols=3000 pos=2/4 time=10.6876 (+ (0 0.0))
    ols=3000 pos=3/4 time=13.7854 (+ (0 0.0))
    ols=187 pos=0/4 time=0.0016 (+ (0 0.0))
    ols=187 pos=1/4 time=0.3055 (+ (0 0.0))
    ols=187 pos=2/4 time=0.6001 (+ (0 0.0))
    ols=187 pos=3/4 time=0.8903 (+ (0 0.0))
    ols=11 pos=0/4 time=0.0015 (+ (0 0.0))
    ols=11 pos=1/4 time=0.0769 (+ (1 0.006324223))
    ols=11 pos=2/4 time=0.1439 (+ (0 0.0))
    ols=11 pos=3/4 time=0.2215 (+ (0 0.0))
    ols=0 pos=0/4 time=0.0015 (+ (0 0.0))
    ols=0 pos=1/4 time=0.0548 (+ (0 0.0))
    ols=0 pos=2/4 time=0.1102 (+ (0 0.0))
    ols=0 pos=3/4 time=0.1690 (+ (0 0.0))



        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-22 20:52                   ` Stefan Monnier
@ 2018-03-22 23:11                     ` Sebastian Sturm
  2018-03-23  5:03                       ` Stefan Monnier
  2018-03-23  8:07                       ` Eli Zaretskii
  0 siblings, 2 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-22 23:11 UTC (permalink / raw)
  To: emacs-devel

On 03/22/2018 09:52 PM, Stefan Monnier wrote:
>> (defun benchmark-often ()
>>    (cl-loop for n from 1 upto 20 do
>>             (message (format "iteration %d: %f" n (nth 0 (benchmark-run
>> (line-number-at-pos (point))))))))
>                        ^^^^^^^
> Where is this "point" in your tests (I expect the timing to vary
> significantly depending on this).

yes, these tests were all performed close to the very bottom of the file 
as I knew the issue to get worse towards the buffer end

> My crystal ball tells me that DeclBase.h is pure ASCII so byte<->char
> conversion is trivial, whereas your file likely contains umlauts and
> other disreputable characters.
> 
> Here's a similar test case to yours but which builds up its own
> artificial buffer with a few non-ascii chars to spice things up:

thank you! I'm very glad you could come up with a reproducible test 
case, and it's true that my file contains two instances of the greek 
letter "μ" that seem to cause this performance issue (though I was 
surprised to see that this few characters could have such a poisonous 
effect). Likewise, when replacing the topmost part in your benchmark 
function with the following:

(A)  (dotimes (i 1000)
        (insert "pure ascii pure ascii pure ascii\n"))

(B)  (dotimes (i 500)
        (insert "pure ascii pure ascii pure ascii\n"))
        (insert "μ")
      (dotimes (i 500)
        (insert "pure ascii pure ascii pure ascii\n"))

respectively, I obtain the following timing results:

(A)
ols=3300 pos=0/4 time=0.0014 (+ (0 0.0))
ols=3300 pos=1/4 time=0.0155 (+ (0 0.0))
ols=3300 pos=2/4 time=0.0281 (+ (0 0.0))
ols=3300 pos=3/4 time=0.0447 (+ (0 0.0))
ols=206 pos=0/4 time=0.0007 (+ (0 0.0))
ols=206 pos=1/4 time=0.0130 (+ (0 0.0))
ols=206 pos=2/4 time=0.0283 (+ (0 0.0))
ols=206 pos=3/4 time=0.0447 (+ (0 0.0))
ols=12 pos=0/4 time=0.0007 (+ (0 0.0))
ols=12 pos=1/4 time=0.0129 (+ (0 0.0))
ols=12 pos=2/4 time=0.0281 (+ (0 0.0))
ols=12 pos=3/4 time=0.0447 (+ (0 0.0))
ols=0 pos=0/4 time=0.0007 (+ (0 0.0))
ols=0 pos=1/4 time=0.0134 (+ (0 0.0))
ols=0 pos=2/4 time=0.0297 (+ (0 0.0))
ols=0 pos=3/4 time=0.0463 (+ (0 0.0))

(B)
ols=3300 pos=0/4 time=0.0007 (+ (0 0.0))
ols=3300 pos=1/4 time=0.0301 (+ (0 0.0))
ols=3300 pos=2/4 time=0.0482 (+ (0 0.0))
ols=3300 pos=3/4 time=8.0213 (+ (0 0.0))
ols=206 pos=0/4 time=0.0007 (+ (0 0.0))
ols=206 pos=1/4 time=0.0141 (+ (0 0.0))
ols=206 pos=2/4 time=0.0325 (+ (0 0.0))
ols=206 pos=3/4 time=0.1786 (+ (0 0.0))
ols=12 pos=0/4 time=0.0007 (+ (0 0.0))
ols=12 pos=1/4 time=0.0136 (+ (0 0.0))
ols=12 pos=2/4 time=0.0323 (+ (0 0.0))
ols=12 pos=3/4 time=0.0794 (+ (0 0.0))
ols=0 pos=0/4 time=0.0009 (+ (0 0.0))
ols=0 pos=1/4 time=0.0139 (+ (0 0.0))
ols=0 pos=2/4 time=0.0326 (+ (0 0.0))
ols=0 pos=3/4 time=0.0632 (+ (0 0.0))

by comparison, these are my results using the noverlay branch:

(A)
ols=3300 pos=0/4 time=0.0012 (+ (0 0.0))
ols=3300 pos=1/4 time=0.0132 (+ (0 0.0))
ols=3300 pos=2/4 time=0.0291 (+ (0 0.0))
ols=3300 pos=3/4 time=0.0448 (+ (0 0.0))
ols=206 pos=0/4 time=0.0007 (+ (0 0.0))
ols=206 pos=1/4 time=0.0132 (+ (0 0.0))
ols=206 pos=2/4 time=0.0290 (+ (0 0.0))
ols=206 pos=3/4 time=0.0454 (+ (0 0.0))
ols=12 pos=0/4 time=0.0008 (+ (0 0.0))
ols=12 pos=1/4 time=0.0131 (+ (0 0.0))
ols=12 pos=2/4 time=0.0287 (+ (0 0.0))
ols=12 pos=3/4 time=0.0452 (+ (0 0.0))
ols=0 pos=0/4 time=0.0007 (+ (0 0.0))
ols=0 pos=1/4 time=0.0131 (+ (0 0.0))
ols=0 pos=2/4 time=0.0289 (+ (0 0.0))
ols=0 pos=3/4 time=0.0457 (+ (0 0.0))

(B)
ols=3300 pos=0/4 time=0.0015 (+ (0 0.0))
ols=3300 pos=1/4 time=0.0177 (+ (0 0.0))
ols=3300 pos=2/4 time=0.0345 (+ (0 0.0))
ols=3300 pos=3/4 time=0.0544 (+ (0 0.0))
ols=206 pos=0/4 time=0.0008 (+ (0 0.0))
ols=206 pos=1/4 time=0.0136 (+ (0 0.0))
ols=206 pos=2/4 time=0.0317 (+ (0 0.0))
ols=206 pos=3/4 time=0.0537 (+ (0 0.0))
ols=12 pos=0/4 time=0.0007 (+ (0 0.0))
ols=12 pos=1/4 time=0.0135 (+ (0 0.0))
ols=12 pos=2/4 time=0.0319 (+ (0 0.0))
ols=12 pos=3/4 time=0.0537 (+ (0 0.0))
ols=0 pos=0/4 time=0.0007 (+ (0 0.0))
ols=0 pos=1/4 time=0.0146 (+ (0 0.0))
ols=0 pos=2/4 time=0.0318 (+ (0 0.0))
ols=0 pos=3/4 time=0.0554 (+ (0 0.0))

since noverlay performs so well, I guess the technical issue here is 
already solved and I'll just have to wait for it to make it into the 
master branch. Until then I'll continue using feature/noverlay, but if a 
more recent merge with master was made available, I'd be interested in 
testing that.
thanks again for all the helpful responses in this thread,
Sebastian



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-22 23:11                     ` Sebastian Sturm
@ 2018-03-23  5:03                       ` Stefan Monnier
  2018-03-23 12:25                         ` Sebastian Sturm
  2018-03-23  8:07                       ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23  5:03 UTC (permalink / raw)
  To: emacs-devel

> since noverlay performs so well, I guess the technical issue here is already
> solved and I'll just have to wait for it to make it into the master
> branch.

Yes and no: there can still be many markers (even without having any
overlays) and that poses the same problem (and the noverlays branch
doesn't change that).

I've made some further tests and found that the patch I sent indeed
doesn't help tremendously with "distance += 10" but that when we reach
"+= 50" the effect is more significant.

Could you try the patch below (ideally not just with artificial tests
but also in actual use) to see if it helps?


        Stefan


diff --git a/src/marker.c b/src/marker.c
index 7773c4fce0..6ab0d3d61e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x)
   CHECK_TYPE (MARKERP (x), Qmarkerp, x);
 }
 
+/* When converting bytes from/to chars, we look through the list of
+   markers to try and find a good starting point (since markers keep
+   track of both bytepos and charpos at the same time).
+   But if there are many markers, it can take too much time to find a "good"
+   marker from which to start.  Worse yet: if it takes a long time and we end
+   up finding a nearby markers, we won't add a new marker to cache this
+   result, so next time around we'll have to go through this same long list
+   to (re)find this best marker.  So the further down the list of
+   markers we go, the less demanding we are w.r.t what is a good marker.
+
+   The previous code used INITIAL=50 and INCREMENT=0 and this lead to
+   really poor performance when there are many markers.
+   I haven't tried to tweak INITIAL, but my experiments on my trusty Thinkpad
+   T61 using various artificial test cases seem to suggest that INCREMENT=50
+   might be "the best compromise": it significantly improved the
+   worst case and it was rarely slower and never by much.
+
+   The asymptotic behavior is still poor, tho, so in largish buffers with many
+   overlays (e.g. 300KB and 30K overlays), it can still be a bottlneck.  */
+#define BYTECHAR_DISTANCE_INITIAL 50
+#define BYTECHAR_DISTANCE_INCREMENT 50
+
 /* Return the byte position corresponding to CHARPOS in B.  */
 
 ptrdiff_t
@@ -141,6 +163,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
 
@@ -180,8 +203,10 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - best_below < distance)
 	break;
+      else
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.
@@ -293,6 +318,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
 
@@ -323,8 +349,10 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - best_below < distance)
 	break;
+      else
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.




^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-22 23:11                     ` Sebastian Sturm
  2018-03-23  5:03                       ` Stefan Monnier
@ 2018-03-23  8:07                       ` Eli Zaretskii
  2018-03-23  9:08                         ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23  8:07 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Fri, 23 Mar 2018 00:11:16 +0100
> 
> since noverlay performs so well, I guess the technical issue here is 
> already solved and I'll just have to wait for it to make it into the 
> master branch.

As Stefan points out, the overlays are not the reason.  The reason are
the number of markers in the buffer; each overlay defines 2 markers.
And there could be many markers in a buffer even if there are no
overlays.

Btw, why do you have so many overlays in these buffers?  Is this part
of lsp-mode implementation, or is the reason unrelated?



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23  8:07                       ` Eli Zaretskii
@ 2018-03-23  9:08                         ` Eli Zaretskii
  2018-03-23 10:15                           ` Sebastian Sturm
  2018-03-23 12:12                           ` Stefan Monnier
  0 siblings, 2 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23  9:08 UTC (permalink / raw)
  To: s.sturm; +Cc: emacs-devel

> Date: Fri, 23 Mar 2018 11:07:26 +0300
> From: Eli Zaretskii <eliz@gnu.org>
> Cc: emacs-devel@gnu.org
> 
> Btw, why do you have so many overlays in these buffers?  Is this part
> of lsp-mode implementation, or is the reason unrelated?

Also, what about my suggestion to count lines in a relative manner,
using count-lines from a line with a known number?  You never replied
to that suggestion.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23  9:08                         ` Eli Zaretskii
@ 2018-03-23 10:15                           ` Sebastian Sturm
  2018-03-23 12:39                             ` Eli Zaretskii
  2018-03-23 12:12                           ` Stefan Monnier
  1 sibling, 1 reply; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-23 10:15 UTC (permalink / raw)
  To: emacs-devel

On 03/23/2018 10:08 AM, Eli Zaretskii wrote:
>> Date: Fri, 23 Mar 2018 11:07:26 +0300
>> From: Eli Zaretskii <eliz@gnu.org>
>> Cc: emacs-devel@gnu.org
>>
>> Btw, why do you have so many overlays in these buffers?  Is this part
>> of lsp-mode implementation, or is the reason unrelated?

this is not related to lsp-mode itself, but to the semantic highlighter 
implemented by emacs-cquery (which is built on top of lsp-mode but 
implements additional features offered by the cquery backend). When set 
to use overlays (which provides a better visual experience than 
font-lock, as font-lock tends to get out of sync with the buffer during 
editing), the semantic highlighter retrieves a list of symbols within 
the current buffer and creates overlays to provide semantically 
meaningful syntax highlighting. It's not a feature I couldn't live 
without, but it's very precise and in principle could probably be faster 
than the syntax highlighting provided by cc-mode as C++ parsing is 
handled asynchronously by the clang-based native backend.

> Also, what about my suggestion to count lines in a relative manner,
> using count-lines from a line with a known number?  You never replied
> to that suggestion.

you're right, sorry. In my opinion, a caching mechanism might be a very 
useful thing to have if it provides further performance benefits on top 
of what the noverlay branch has to offer. However, since count-lines may 
not be the only function that has to convert between char and byte 
positions (or is it?), and since the noverlay branch seems to resolve 
the overlay issue without having to introduce additional complexity in 
the elisp layer, implementing a caching mechanism before noverlay is 
merged into the master branch seems like a premature optimization to me.
Of course this is a layman's opinion (and maybe the case of "few 
overlays but many markers" is not as pathological as it appears to me); 
if you think a line number cache should be implemented, I'll go and 
discuss that with the lsp-mode maintainers (assuming that they are among 
the heaviest users of line-number-at-pos).
Sebastian



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23  9:08                         ` Eli Zaretskii
  2018-03-23 10:15                           ` Sebastian Sturm
@ 2018-03-23 12:12                           ` Stefan Monnier
  2018-03-23 12:40                             ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 12:12 UTC (permalink / raw)
  To: emacs-devel

> Also, what about my suggestion to count lines in a relative manner,
> using count-lines from a line with a known number?  You never replied
> to that suggestion.

FWIW, I believe in his case most of the time is spent outside of the
actual "count the lines" (aka forward-line) code, so counting fewer
lines because we start from a more nearby position probably won't help
very much.


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23  5:03                       ` Stefan Monnier
@ 2018-03-23 12:25                         ` Sebastian Sturm
  2018-03-23 12:47                           ` Eli Zaretskii
  0 siblings, 1 reply; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-23 12:25 UTC (permalink / raw)
  To: emacs-devel

I haven't tested this very extensively yet, but artificial benchmark 
results are now comparable to the noverlay branch and editing seems 
similarly fluid. Many thanks for that!
On a tangential note, removing the bottleneck now brought up another 
hotspot in my performance profile (less severe than the one previously 
reported) that seems to be due to cc-mode itself. Since I don't want to 
derail this thread, I'll try to implement some of the suggestions made 
in the "Latency profiling?" thread and come up with some reproducible 
data on that other issue.

On 03/23/2018 06:03 AM, Stefan Monnier wrote:
>> since noverlay performs so well, I guess the technical issue here is already
>> solved and I'll just have to wait for it to make it into the master
>> branch.
> 
> Yes and no: there can still be many markers (even without having any
> overlays) and that poses the same problem (and the noverlays branch
> doesn't change that).
> 
> I've made some further tests and found that the patch I sent indeed
> doesn't help tremendously with "distance += 10" but that when we reach
> "+= 50" the effect is more significant.
> 
> Could you try the patch below (ideally not just with artificial tests
> but also in actual use) to see if it helps?
> 
> 
>          Stefan
> 
> 
> diff --git a/src/marker.c b/src/marker.c
> index 7773c4fce0..6ab0d3d61e 100644
> --- a/src/marker.c
> +++ b/src/marker.c
> @@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x)
>     CHECK_TYPE (MARKERP (x), Qmarkerp, x);
>   }
>   
> +/* When converting bytes from/to chars, we look through the list of
> +   markers to try and find a good starting point (since markers keep
> +   track of both bytepos and charpos at the same time).
> +   But if there are many markers, it can take too much time to find a "good"
> +   marker from which to start.  Worse yet: if it takes a long time and we end
> +   up finding a nearby markers, we won't add a new marker to cache this
> +   result, so next time around we'll have to go through this same long list
> +   to (re)find this best marker.  So the further down the list of
> +   markers we go, the less demanding we are w.r.t what is a good marker.
> +
> +   The previous code used INITIAL=50 and INCREMENT=0 and this lead to
> +   really poor performance when there are many markers.
> +   I haven't tried to tweak INITIAL, but my experiments on my trusty Thinkpad
> +   T61 using various artificial test cases seem to suggest that INCREMENT=50
> +   might be "the best compromise": it significantly improved the
> +   worst case and it was rarely slower and never by much.
> +
> +   The asymptotic behavior is still poor, tho, so in largish buffers with many
> +   overlays (e.g. 300KB and 30K overlays), it can still be a bottlneck.  */
> +#define BYTECHAR_DISTANCE_INITIAL 50
> +#define BYTECHAR_DISTANCE_INCREMENT 50
> +
>   /* Return the byte position corresponding to CHARPOS in B.  */
>   
>   ptrdiff_t
> @@ -141,6 +163,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
>     struct Lisp_Marker *tail;
>     ptrdiff_t best_above, best_above_byte;
>     ptrdiff_t best_below, best_below_byte;
> +  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
>   
>     eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
>   
> @@ -180,8 +203,10 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
>         /* If we are down to a range of 50 chars,
>   	 don't bother checking any other markers;
>   	 scan the intervening chars directly now.  */
> -      if (best_above - best_below < 50)
> +      if (best_above - best_below < distance)
>   	break;
> +      else
> +        distance += BYTECHAR_DISTANCE_INCREMENT;
>       }
>   
>     /* We get here if we did not exactly hit one of the known places.
> @@ -293,6 +318,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
>     struct Lisp_Marker *tail;
>     ptrdiff_t best_above, best_above_byte;
>     ptrdiff_t best_below, best_below_byte;
> +  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
>   
>     eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
>   
> @@ -323,8 +349,10 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
>         /* If we are down to a range of 50 chars,
>   	 don't bother checking any other markers;
>   	 scan the intervening chars directly now.  */
> -      if (best_above - best_below < 50)
> +      if (best_above - best_below < distance)
>   	break;
> +      else
> +        distance += BYTECHAR_DISTANCE_INCREMENT;
>       }
>   
>     /* We get here if we did not exactly hit one of the known places.
> 
> 

-- 
Sebastian Sturm
Research & Development

Phone:	+49 (0) 6155 7808977
Fax:	+49 (0) 6155 7802880
Email:	s.sturm@arkona-technologies.de
Web:	www.arkona-technologies.de

arkona technologies GmbH
Im Leuschnerpark 4
64347 Griesheim
Germany
Amtsgericht / Commercial Register of Darmstadt, HRB 90080
USt-ID: DE273794666
Steuernummer / Tax-ID: 007 / 228 / 19331
Geschäftsführung / Managing Director: Rainer Sturm

This e-mail may contain confidential and/or privileged information. If 
you are not the intended recipient (or have received this e-mail in 
error) please notify the sender immediately and delete this message and 
any attachments from your system. Any unauthorised copying, disclosure 
or distribution of the material in this e-mail is strictly forbidden.

We cannot accept any responsibility for the accuracy or completeness of 
this message as it has been transmitted over a public network. If, 
despite our use of anti-virus software, a virus enters your systems in 
connection with the sending of the e-mail, you may not hold us liable 
for any damages that may possibly arise in that connection. We will 
accept liability which by law we cannot exclude.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 10:15                           ` Sebastian Sturm
@ 2018-03-23 12:39                             ` Eli Zaretskii
  0 siblings, 0 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23 12:39 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Fri, 23 Mar 2018 11:15:48 +0100
> 
> > Also, what about my suggestion to count lines in a relative manner,
> > using count-lines from a line with a known number?  You never replied
> > to that suggestion.
> 
> you're right, sorry. In my opinion, a caching mechanism might be a very 
> useful thing to have if it provides further performance benefits on top 
> of what the noverlay branch has to offer. However, since count-lines may 
> not be the only function that has to convert between char and byte 
> positions (or is it?), and since the noverlay branch seems to resolve 
> the overlay issue without having to introduce additional complexity in 
> the elisp layer, implementing a caching mechanism before noverlay is 
> merged into the master branch seems like a premature optimization to me.

It isn't premature optimization, because a buffer could have many
markers even if it has no or only a few overlays.

> Of course this is a layman's opinion (and maybe the case of "few 
> overlays but many markers" is not as pathological as it appears to me); 
> if you think a line number cache should be implemented, I'll go and 
> discuss that with the lsp-mode maintainers (assuming that they are among 
> the heaviest users of line-number-at-pos).

I think the effect should be at least measured before the decision
whether to do that is made.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 12:12                           ` Stefan Monnier
@ 2018-03-23 12:40                             ` Eli Zaretskii
  2018-03-23 12:55                               ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23 12:40 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Fri, 23 Mar 2018 08:12:41 -0400
> 
> > Also, what about my suggestion to count lines in a relative manner,
> > using count-lines from a line with a known number?  You never replied
> > to that suggestion.
> 
> FWIW, I believe in his case most of the time is spent outside of the
> actual "count the lines" (aka forward-line) code, so counting fewer
> lines because we start from a more nearby position probably won't help
> very much.

I'm not sure, because the profile indicates memrchr is a significant
runner-up in the CPU time usage.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 12:25                         ` Sebastian Sturm
@ 2018-03-23 12:47                           ` Eli Zaretskii
  2018-03-23 13:19                             ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23 12:47 UTC (permalink / raw)
  To: Sebastian Sturm; +Cc: emacs-devel

> From: Sebastian Sturm <s.sturm@arkona-technologies.de>
> Date: Fri, 23 Mar 2018 13:25:19 +0100
> 
> I haven't tested this very extensively yet, but artificial benchmark 
> results are now comparable to the noverlay branch and editing seems 
> similarly fluid. Many thanks for that!

I'd be interested to see a comparison with a code that ignores the
markers entirely, and uses just these 4:

  CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
  CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
  CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
  CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));

That's because BYTECHAR_DISTANCE_INCREMENT is probably a function of
the number of markers.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 12:40                             ` Eli Zaretskii
@ 2018-03-23 12:55                               ` Stefan Monnier
  0 siblings, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 12:55 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

> I'm not sure, because the profile indicates memrchr is a significant
> runner-up in the CPU time usage.

Oh, indeed I wasn't clear: it won't solve the "too many markers" case,
but it will still be useful, especially in the case where there aren't
too many markers.

My experience with nlinum--line-number-at-pos is that it's a low-hanging
fruit.  If the solution can be implemented directly inside
line-number-at-pos it'd be even better.


        Stefan



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 12:47                           ` Eli Zaretskii
@ 2018-03-23 13:19                             ` Stefan Monnier
  2018-03-23 13:37                               ` Noam Postavsky
  2018-03-23 14:22                               ` Eli Zaretskii
  0 siblings, 2 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 13:19 UTC (permalink / raw)
  To: emacs-devel

> I'd be interested to see a comparison with a code that ignores the
> markers entirely, and uses just these 4:
>
>   CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
>   CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
>   CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
>   CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));

I tried that, and in the synthetic benchmark which tries to reproduce
Sebastian's lsp-mode situation the result was indeed much better, but
then in other benchmarks it caused very significant slowdowns.

> That's because BYTECHAR_DISTANCE_INCREMENT is probably a function of
> the number of markers.

I haven't investigated closely enough to be sure, but in my tests with
INCREMENT=50 I reached points where adding more overlays did not make
things worse any more, so I suspect that the ideal INCREMENT depends on
the buffer size more than on the number of markers.

In any case, my patch is just a "quick hack" to try and reduce the pain,
but it doesn't really solve the problem: the slow down with many markers
and a large buffer still grows pretty significantly with the size of
the buffer.  I think it's worse than O(sqrt N).

If we want to really solve this problem, we should use an algorithm with
at most an O(log N) complexity, e.g. keeping the markers in
a red-black tree, or inside a sorted array (probably with a gap like we
have for the buffer text) so we can do a binary search.


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 13:19                             ` Stefan Monnier
@ 2018-03-23 13:37                               ` Noam Postavsky
  2018-03-23 13:55                                 ` Stefan Monnier
  2018-03-23 14:22                               ` Eli Zaretskii
  1 sibling, 1 reply; 54+ messages in thread
From: Noam Postavsky @ 2018-03-23 13:37 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Emacs developers

On 23 March 2018 at 09:19, Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> In any case, my patch is just a "quick hack" to try and reduce the pain,
> but it doesn't really solve the problem: the slow down with many markers
> and a large buffer still grows pretty significantly with the size of
> the buffer.  I think it's worse than O(sqrt N).
>
> If we want to really solve this problem, we should use an algorithm with
> at most an O(log N) complexity, e.g. keeping the markers in
> a red-black tree, or inside a sorted array (probably with a gap like we
> have for the buffer text) so we can do a binary search.

Is this related to Bug#24548 "Long GC delays with many non-detached
markers (PATCH)"
aka Bug#29439 "Quadratic complexity in sweep_markers"?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24548
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29439



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 13:37                               ` Noam Postavsky
@ 2018-03-23 13:55                                 ` Stefan Monnier
  0 siblings, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 13:55 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: Emacs developers

>> If we want to really solve this problem, we should use an algorithm with
>> at most an O(log N) complexity, e.g. keeping the markers in
>> a red-black tree, or inside a sorted array (probably with a gap like we
>> have for the buffer text) so we can do a binary search.
>
> Is this related to Bug#24548 "Long GC delays with many non-detached
> markers (PATCH)"
> aka Bug#29439 "Quadratic complexity in sweep_markers"?

Not directly, no,


        Stefan



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 13:19                             ` Stefan Monnier
  2018-03-23 13:37                               ` Noam Postavsky
@ 2018-03-23 14:22                               ` Eli Zaretskii
  2018-03-23 14:39                                 ` Stefan Monnier
  2018-03-23 19:39                                 ` Stefan Monnier
  1 sibling, 2 replies; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-23 14:22 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Fri, 23 Mar 2018 09:19:11 -0400
> 
> > I'd be interested to see a comparison with a code that ignores the
> > markers entirely, and uses just these 4:
> >
> >   CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
> >   CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
> >   CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
> >   CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));
> 
> I tried that, and in the synthetic benchmark which tries to reproduce
> Sebastian's lsp-mode situation the result was indeed much better, but
> then in other benchmarks it caused very significant slowdowns.

In what benchmarks did it cause significant slowdowns?

> > That's because BYTECHAR_DISTANCE_INCREMENT is probably a function of
> > the number of markers.
> 
> I haven't investigated closely enough to be sure, but in my tests with
> INCREMENT=50 I reached points where adding more overlays did not make
> things worse any more, so I suspect that the ideal INCREMENT depends on
> the buffer size more than on the number of markers.

Could be.  My point was that it isn't a constant.

> If we want to really solve this problem, we should use an algorithm with
> at most an O(log N) complexity, e.g. keeping the markers in
> a red-black tree, or inside a sorted array (probably with a gap like we
> have for the buffer text) so we can do a binary search.

Yes, agreed.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 14:22                               ` Eli Zaretskii
@ 2018-03-23 14:39                                 ` Stefan Monnier
  2018-03-23 19:39                                 ` Stefan Monnier
  1 sibling, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 14:39 UTC (permalink / raw)
  To: emacs-devel

>> > I'd be interested to see a comparison with a code that ignores the
>> > markers entirely, and uses just these 4:
>> >   CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
>> >   CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
>> >   CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
>> >   CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));
>> I tried that, and in the synthetic benchmark which tries to reproduce
>> Sebastian's lsp-mode situation the result was indeed much better, but
>> then in other benchmarks it caused very significant slowdowns.
> In what benchmarks did it cause significant slowdowns?

Can't remember exactly, I think it was bad enough for a test case
which seemed pretty realistic so I discarded that option.
Basically what I remember is that I got the impression that it would
probably harm more users than the problem at hand.


        Stefan


PS: BTW, the number of markers is not the only issue: the order in which
they are created also matters.  Maybe we should try Sebastian's test
case but creating the markers/overlays in random order (and if that
helps, we could get a similar effect by making the GC randomly shuffle
the buffers's list of markers ;-).




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 14:22                               ` Eli Zaretskii
  2018-03-23 14:39                                 ` Stefan Monnier
@ 2018-03-23 19:39                                 ` Stefan Monnier
  2018-03-25 15:11                                   ` Stefan Monnier
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-23 19:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

OK, I think I'm tired of these experiments.
Here's my current test, along with the patches I use with it.

You can test it with something like

    src/emacs -Q --batch -l ~/tmp/foo.el --eval '(setq internal--bytechar-distance-increment 50 internal--randomize-markers t)' --eval '(bytechar-test 3000 nil)' 

Shuffling the markers can make a noticeable difference in some cases but
we're only talking about a factor of 2 or 3.  It doesn't have much
negative impact, so it's not a bad option, but the algorithmic
problem remains anyway.


        Stefan


(defun bytechar-test (buffer-kb &optional forward)
  (random "seed")
  (with-temp-buffer
    (dotimes (i (* buffer-kb 33))
      (insert "lksajflahalskjdféefawrgfrüegf\n"))
    (message "buffer-size = %SkB" (/ (buffer-size) 1024))
    (let ((txtbuf (current-buffer))
          (goto-iterations (/ 10000000 buffer-kb))
          (line-iterations (/ 20000 buffer-kb))
          (markers ()))
      (dotimes (s 4)
        (with-temp-buffer
          (insert-buffer-substring txtbuf)
          (let ((stepsize (lsh 10 (* 4 s))))
            (if forward
                (cl-loop for n from (point-min) upto (point-max) by stepsize do
                         (push (copy-marker n) markers))
              (cl-loop for n from (point-max) downto (point-min) by stepsize do
                       (push (copy-marker n) markers))))
          ;; The GC's internal--randomize-markers just brings-to-front every
          ;; 8th marker, so when starting with an ordered list of markers (like
          ;; in our case), we need to run the GC at least 8 times before the
          ;; whole list starts to look somewhat shuffled.
          (dotimes (i 20) (garbage-collect))
          (let ((timing
                 (benchmark-run goto-iterations
                   (goto-char (+ (point-min) (random (buffer-size)))))))
            (message "ols=%S goto-random time=%.4f (+ %S)"
                     (/ (buffer-size) (lsh 10 (* 4 s)))
                     (car timing) (cdr timing)))
          (garbage-collect)             ;throw away the transient markers
          (let ((timing
                 (benchmark-run line-iterations
                   (dotimes (i 5)
                     (line-number-at-pos
                      (+ (point-min) (* i (/ (buffer-size) 4))))))))
            (message "nbmks=%S pos=*/4 time=%.4f (+ %S)"
                     (/ (buffer-size) (lsh 10 (* 4 s)))
                     (car timing) (cdr timing)))
          (dotimes (i 5)
            (let ((timing
                   (benchmark-run line-iterations
                     (line-number-at-pos
                      (+ (point-min) (* i (/ (buffer-size) 4)))))))
              (message "nbmks=%S pos=%S/4 time=%.4f (+ %S)"
                       (/ (buffer-size) (lsh 10 (* 4 s))) i
                       (car timing) (cdr timing))))
          )))))

diff --git a/lisp/emacs-lisp/benchmark.el b/lisp/emacs-lisp/benchmark.el
index b86b56b81e..2f4e38fe35 100644
--- a/lisp/emacs-lisp/benchmark.el
+++ b/lisp/emacs-lisp/benchmark.el
@@ -50,7 +50,7 @@ benchmark-run
 garbage collections that ran, and the time taken by garbage collection.
 See also `benchmark-run-compiled'."
   (declare (indent 1) (debug t))
-  (unless (natnump repetitions)
+  (unless (or (natnump repetitions) (symbolp repetitions))
     (setq forms (cons repetitions forms)
 	  repetitions 1))
   (let ((i (make-symbol "i"))
@@ -58,7 +58,7 @@ benchmark-run
 	(gc (make-symbol "gc")))
     `(let ((,gc gc-elapsed)
 	   (,gcs gcs-done))
-       (list ,(if (> repetitions 1)
+       (list ,(if (or (symbolp repetitions) (> repetitions 1))
 		  ;; Take account of the loop overhead.
 		  `(- (benchmark-elapse (dotimes (,i ,repetitions)
 					  ,@forms))
@@ -101,7 +101,7 @@ benchmark
 For non-interactive use see also `benchmark-run' and
 `benchmark-run-compiled'."
   (interactive "p\nxForm: ")
-  (let ((result (eval `(benchmark-run ,repetitions ,form))))
+  (let ((result (eval `(benchmark-run ,repetitions ,form) t)))
     (if (zerop (nth 1 result))
 	(message "Elapsed time: %fs" (car result))
       (message "Elapsed time: %fs (%fs in %d GCs)" (car result)
diff --git a/src/alloc.c b/src/alloc.c
index 7ba872aaee..16d11e34cd 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -7270,10 +7270,22 @@ static void
 unchain_dead_markers (struct buffer *buffer)
 {
   struct Lisp_Marker *this, **prev = &BUF_MARKERS (buffer);
+  /* In order to try and avoid worst case behaviors in buf_charpos_to_bytepos
+     we try and randomize the order of markers here.  */
+  unsigned i = 4;
 
   while ((this = *prev))
     if (this->gcmarkbit)
-      prev = &this->next;
+      {
+        if (!randomize_markers || i++ % 8)
+          prev = &this->next;
+        else
+          { /* Move this one to front, just to randomize things a bit.  */
+            *prev = this->next;
+            this->next = BUF_MARKERS (buffer);
+            BUF_MARKERS (buffer) = this;
+          }
+      }
     else
       {
         this->buffer = NULL;
@@ -7752,6 +7764,9 @@ The time is in seconds as a floating point value.  */);
   DEFVAR_INT ("gcs-done", gcs_done,
               doc: /* Accumulated number of garbage collections done.  */);
 
+  DEFVAR_BOOL ("internal--randomize-markers", randomize_markers, doc: /*  */);
+  randomize_markers = true;
+  
   defsubr (&Scons);
   defsubr (&Slist);
   defsubr (&Svector);
diff --git a/src/marker.c b/src/marker.c
index 3d808fd6fa..7c1d164927 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x)
   CHECK_TYPE (MARKERP (x), Qmarkerp, x);
 }
 
+/* When converting bytes from/to chars, we look through the list of
+   markers to try and find a good starting point (since markers keep
+   track of both bytepos and charpos at the same time).
+   But if there are many markers, it can take too much time to find a "good"
+   marker from which to start.  Worse yet: if it takes a long time and we end
+   up finding a nearby markers, we won't add a new marker to cache this
+   result, so next time around we'll have to go through this same long list
+   to (re)find this best marker.  So the further down the list of
+   markers we go, the less demanding we are w.r.t what is a good marker.
+
+   The previous code used INITIAL=50 and INCREMENT=0 and this lead to
+   really poor performance when there are many markers.
+   I haven't tried to tweak INITIAL, but my experiments on my trusty Thinkpad
+   T61 using various artificial test cases seem to suggest that INCREMENT=50
+   might be "the best compromise": it significantly improved the
+   worst case and it was rarely slower and never by much.
+
+   The asymptotic behavior is still poor, tho, so in largish buffers with many
+   overlays (e.g. 300KB and 30K overlays), it can still be a bottlneck.  */
+#define BYTECHAR_DISTANCE_INITIAL 50
+#define BYTECHAR_DISTANCE_INCREMENT bytechar_distance_increment
+
 /* Return the byte position corresponding to CHARPOS in B.  */
 
 ptrdiff_t
@@ -141,7 +163,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
-  ptrdiff_t distance = 50;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
 
@@ -184,7 +206,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
       if (best_above - best_below < distance)
 	break;
       else
-        distance++;
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.
@@ -296,7 +318,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
-  ptrdiff_t distance = 50;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
 
@@ -330,7 +352,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
       if (best_above - best_below < distance)
 	break;
       else
-        distance++;
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.
@@ -756,4 +778,9 @@ syms_of_marker (void)
   defsubr (&Scopy_marker);
   defsubr (&Smarker_insertion_type);
   defsubr (&Sset_marker_insertion_type);
+
+  DEFVAR_INT ("internal--bytechar-distance-increment",
+              bytechar_distance_increment,
+              doc: /* Haha */);
+  bytechar_distance_increment = 50;
 }



^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-23 19:39                                 ` Stefan Monnier
@ 2018-03-25 15:11                                   ` Stefan Monnier
  2018-03-25 16:39                                     ` Eli Zaretskii
  0 siblings, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-25 15:11 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

> OK, I think I'm tired of these experiments.

Taking a break finally let me get rid of my blinders so I could see
more clearly what's going on:

All this time is really spent in find_newline (rather than in things
like goto-char).  The problem goes as follows:

A - (line-number-at-pos POS) will call find_newline, making it scan all
    positions between point-min and POS.
B1- if find_newline uses the newline cache, at each newline encountered it
    will call buf_charpos_to_bytepos.
B2- if find_newline does not use the newline cache, at each newline
    encountered it will call buf_bytepos_to_charpos.
  - Each time buf_charpos_to_bytepos/buf_bytepos_to_charpos is called
    it will "CONSIDER" the known positions at point-min, point-max, and at
    the position of the last-call (i.e. at the previous newline).
    Then it will loop through all the markers until the distance between
    the nearest position before POS and the nearest position after POS
    are within 50 of each other.
C - since one of the known positions is the one of the last newline, the
    distance between the nearest position and POS (before considering any
    marker) is usually pretty small: the length of the current line.
    It's even often smaller than 50.  But that doesn't let us stop because
    the marker loop doesn't pay attention to the distance to POS in order
    to decide to stop: it only consider the distance between the current upper
    and lower bound and since we're scanning in one direction, all the
    recently seen positions (added as markers) are on the same side of
    POS as the last seen newline so they don't help.

So there are various ways to solve this problem.  So far, I tried to
make the markers-loop give up earlier, which helps (C) to some extent but
without attacking the core of its problem which is to pay attention to
the case where one of the bounds is already very close to POS even tho
the other is still way off.

The patch below tweaks my previous patch to take this into account.
The result is now that my test cases stay fast (mostly unaffected by the
number of markers) even for large buffers with a large number of markers.

Note that the above shows that there are other optimisations which would
also solve this problem (and would be worthwhile independently).
A - change line-number-at-pos so it doesn't always rescan all the way
    from point-min.  This would really circumvent the whole problem
    (contrarily to what I thought before with my blinders on, thanks Eli
    for insisting on that).
B - change find_newline so it doesn't call
    buf_charpos_to_bytepos/buf_bytepos_to_charpos at each newline.
    E.g. in the no-newline-cache case it'd probably be faster to
    loop through each char using INC/DEC_BOTH and checking if we're at
    \n, than the current code which uses mem(r)chr to look for the
    bytepos of the next \n and then calls buf_bytepos_to_charpos to get
    the corresponding charpos.  Alternatively, we could just delay the
    computation of the charpos until the end (currently we update it
    at each newline, for the purpose of filling the newline-cache).


-- Stefan


diff --git a/src/marker.c b/src/marker.c
index 7773c4fce0..f869b3f948 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x)
   CHECK_TYPE (MARKERP (x), Qmarkerp, x);
 }
 
+/* When converting bytes from/to chars, we look through the list of
+   markers to try and find a good starting point (since markers keep
+   track of both bytepos and charpos at the same time).
+   But if there are many markers, it can take too much time to find a "good"
+   marker from which to start.  Worse yet: if it takes a long time and we end
+   up finding a nearby markers, we won't add a new marker to cache this
+   result, so next time around we'll have to go through this same long list
+   to (re)find this best marker.  So the further down the list of
+   markers we go, the less demanding we are w.r.t what is a good marker.
+
+   The previous code used INITIAL=50 and INCREMENT=0 and this lead to
+   really poor performance when there are many markers.
+   I haven't tried to tweak INITIAL, but my experiments on my trusty Thinkpad
+   T61 using various artificial test cases seem to suggest that INCREMENT=50
+   might be "the best compromise": it significantly improved the
+   worst case and it was rarely slower and never by much.
+
+   The asymptotic behavior is still poor, tho, so in largish buffers with many
+   overlays (e.g. 300KB and 30K overlays), it can still be a bottlneck.  */
+#define BYTECHAR_DISTANCE_INITIAL 50
+#define BYTECHAR_DISTANCE_INCREMENT 50
+
 /* Return the byte position corresponding to CHARPOS in B.  */
 
 ptrdiff_t
@@ -141,6 +163,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
 
@@ -180,8 +203,11 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - charpos < distance
+          || charpos - best_below < distance)
 	break;
+      else
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.
@@ -293,6 +319,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
   struct Lisp_Marker *tail;
   ptrdiff_t best_above, best_above_byte;
   ptrdiff_t best_below, best_below_byte;
+  ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
 
   eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
 
@@ -323,8 +350,11 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
       /* If we are down to a range of 50 chars,
 	 don't bother checking any other markers;
 	 scan the intervening chars directly now.  */
-      if (best_above - best_below < 50)
+      if (best_above - bytepos < distance
+          || bytepos - best_below < distance)
 	break;
+      else
+        distance += BYTECHAR_DISTANCE_INCREMENT;
     }
 
   /* We get here if we did not exactly hit one of the known places.



^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-25 15:11                                   ` Stefan Monnier
@ 2018-03-25 16:39                                     ` Eli Zaretskii
  2018-03-25 17:35                                       ` Stefan Monnier
  0 siblings, 1 reply; 54+ messages in thread
From: Eli Zaretskii @ 2018-03-25 16:39 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@IRO.UMontreal.CA>
> Cc: emacs-devel@gnu.org
> Date: Sun, 25 Mar 2018 11:11:14 -0400
> 
> B1- if find_newline uses the newline cache, at each newline encountered it
>     will call buf_charpos_to_bytepos.
> B2- if find_newline does not use the newline cache, at each newline
>     encountered it will call buf_bytepos_to_charpos.

Agree with B1, but not with B2.  Unless I'm overlooking something,
when the newline cache is disabled, we use memchr, which can search a
contiguous sequence of bytes in a loop, without translating
byte-to-character positions.  It only needs this translation at the
beginning of search, after hitting the gap, and when the search is
completed.

> So there are various ways to solve this problem.  So far, I tried to
> make the markers-loop give up earlier, which helps (C) to some extent but
> without attacking the core of its problem which is to pay attention to
> the case where one of the bounds is already very close to POS even tho
> the other is still way off.
> 
> The patch below tweaks my previous patch to take this into account.
> The result is now that my test cases stay fast (mostly unaffected by the
> number of markers) even for large buffers with a large number of markers.

This is a good change, I think.  But it emphasizes even more the fact
that if we instead expose to Lisp display_count_lines, which is
basically a stripped-down version of find_newline with all the
unnecessary ballast removed, we will get an even better performance in
applications that need to count lines a lot.

> Note that the above shows that there are other optimisations which would
> also solve this problem (and would be worthwhile independently).
> A - change line-number-at-pos so it doesn't always rescan all the way
>     from point-min.  This would really circumvent the whole problem
>     (contrarily to what I thought before with my blinders on, thanks Eli
>     for insisting on that).
> B - change find_newline so it doesn't call
>     buf_charpos_to_bytepos/buf_bytepos_to_charpos at each newline.
>     E.g. in the no-newline-cache case it'd probably be faster to
>     loop through each char using INC/DEC_BOTH and checking if we're at
>     \n, than the current code which uses mem(r)chr to look for the
>     bytepos of the next \n and then calls buf_bytepos_to_charpos to get
>     the corresponding charpos.  Alternatively, we could just delay the
>     computation of the charpos until the end (currently we update it
>     at each newline, for the purpose of filling the newline-cache).

I think we need not touch find_newline.  It is a very frequently used
workhorse, and needs to produce decent performance for every one of
its callers.  By contrast, applications whose primary need is to count
lines, let alone do that _thousands_ of times per keystroke, should
have a dedicated function optimized for that job alone.  It's not a
coincidence the native line-number display uses display_count_lines ;-)

Thanks.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-25 16:39                                     ` Eli Zaretskii
@ 2018-03-25 17:35                                       ` Stefan Monnier
  0 siblings, 0 replies; 54+ messages in thread
From: Stefan Monnier @ 2018-03-25 17:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

> Agree with B1, but not with B2.  Unless I'm overlooking something,
> when the newline cache is disabled, we use memchr, which can search a
> contiguous sequence of bytes in a loop, without translating
> byte-to-character positions.  It only needs this translation at the
> beginning of search, after hitting the gap, and when the search is
> completed.

Hmm... indeed, when the newline cache is completely disabled the problem should
not appear.

>> The patch below tweaks my previous patch to take this into account.
>> The result is now that my test cases stay fast (mostly unaffected by the
>> number of markers) even for large buffers with a large number of markers.
> This is a good change, I think.  But it emphasizes even more the fact
> that if we instead expose to Lisp display_count_lines, which is
> basically a stripped-down version of find_newline with all the
> unnecessary ballast removed, we will get an even better performance in
> applications that need to count lines a lot.

Yes, fixing A is definitely worthwhile.


        Stefan



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-18 20:14 State of the overlay tree branch? Sebastian Sturm
  2018-03-18 20:39 ` Eli Zaretskii
@ 2018-03-26 13:06 ` Stefan Monnier
  2018-03-27 20:59   ` Sebastian Sturm
  1 sibling, 1 reply; 54+ messages in thread
From: Stefan Monnier @ 2018-03-26 13:06 UTC (permalink / raw)
  To: emacs-devel

> after finding that the feature/noverlay branch does wonders for my editing
> experience[1], I'd like to reinvigorate the discussion on its inclusion into
> master.

While I also hope that branch can be merged soon, I've installed a patch
into Emacs's `master` which should hopefully solve your
immediate problems.


        Stefan




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: State of the overlay tree branch?
  2018-03-26 13:06 ` Stefan Monnier
@ 2018-03-27 20:59   ` Sebastian Sturm
  0 siblings, 0 replies; 54+ messages in thread
From: Sebastian Sturm @ 2018-03-27 20:59 UTC (permalink / raw)
  To: emacs-devel

thank you, the master branch now handles my use case a lot better than 
before

On 03/26/2018 03:06 PM, Stefan Monnier wrote:
>> after finding that the feature/noverlay branch does wonders for my editing
>> experience[1], I'd like to reinvigorate the discussion on its inclusion into
>> master.
> 
> While I also hope that branch can be merged soon, I've installed a patch
> into Emacs's `master` which should hopefully solve your
> immediate problems.
> 
> 
>          Stefan
> 
> 



^ permalink raw reply	[flat|nested] 54+ messages in thread

end of thread, other threads:[~2018-03-27 20:59 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-18 20:14 State of the overlay tree branch? Sebastian Sturm
2018-03-18 20:39 ` Eli Zaretskii
2018-03-18 21:04   ` Sebastian Sturm
2018-03-18 23:03     ` Sebastian Sturm
2018-03-18 23:20       ` Sebastian Sturm
2018-03-19  6:43         ` Eli Zaretskii
2018-03-19  9:53           ` Sebastian Sturm
2018-03-19 12:57             ` Eli Zaretskii
2018-03-19 14:56             ` Stefan Monnier
2018-03-19 15:07               ` Sebastian Sturm
2018-03-19 15:13                 ` Stefan Monnier
2018-03-20  1:23                 ` Sebastian Sturm
2018-03-20  6:30                   ` Eli Zaretskii
2018-03-21  0:36                     ` Sebastian Sturm
2018-03-21  6:47                       ` Eli Zaretskii
2018-03-22 13:16                       ` Stefan Monnier
2018-03-22 19:54                         ` Sebastian Sturm
2018-03-22 20:04                           ` Sebastian Sturm
2018-03-22 20:52                   ` Stefan Monnier
2018-03-22 23:11                     ` Sebastian Sturm
2018-03-23  5:03                       ` Stefan Monnier
2018-03-23 12:25                         ` Sebastian Sturm
2018-03-23 12:47                           ` Eli Zaretskii
2018-03-23 13:19                             ` Stefan Monnier
2018-03-23 13:37                               ` Noam Postavsky
2018-03-23 13:55                                 ` Stefan Monnier
2018-03-23 14:22                               ` Eli Zaretskii
2018-03-23 14:39                                 ` Stefan Monnier
2018-03-23 19:39                                 ` Stefan Monnier
2018-03-25 15:11                                   ` Stefan Monnier
2018-03-25 16:39                                     ` Eli Zaretskii
2018-03-25 17:35                                       ` Stefan Monnier
2018-03-23  8:07                       ` Eli Zaretskii
2018-03-23  9:08                         ` Eli Zaretskii
2018-03-23 10:15                           ` Sebastian Sturm
2018-03-23 12:39                             ` Eli Zaretskii
2018-03-23 12:12                           ` Stefan Monnier
2018-03-23 12:40                             ` Eli Zaretskii
2018-03-23 12:55                               ` Stefan Monnier
2018-03-19  6:36       ` Eli Zaretskii
2018-03-19  6:28     ` Eli Zaretskii
2018-03-21 14:14   ` Sebastien Chapuis
2018-03-21 15:35     ` Eli Zaretskii
2018-03-26 13:06 ` Stefan Monnier
2018-03-27 20:59   ` Sebastian Sturm
     [not found] <<c24f8534-5245-026e-da18-f6be7b9702bf@arkona-technologies.de>
     [not found] ` <<834lldp18f.fsf@gnu.org>
2018-03-18 21:37   ` Drew Adams
2018-03-19  1:33     ` Stefan Monnier
2018-03-19  6:50       ` Eli Zaretskii
2018-03-19 12:29         ` Stefan Monnier
2018-03-19 13:02           ` Eli Zaretskii
2018-03-19 13:43             ` Stefan Monnier
2018-03-19 14:28               ` Eli Zaretskii
2018-03-19 14:39                 ` Stefan Monnier
2018-03-19  6:33     ` Eli Zaretskii

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).