GNU bug report logs - #69241
[PATCH] Jsonrpc: improve performance of process filter function

Previous Next

Package: emacs;

Reported by: Daniel Pettersson <daniel <at> dpettersson.net>

Date: Sun, 18 Feb 2024 18:25:07 UTC

Severity: normal

Tags: patch

Done: Eli Zaretskii <eliz <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 69241 in the body.
You can then email your comments to 69241 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 18 Feb 2024 18:25:07 GMT) Full text and rfc822 format available.

Acknowledgement sent to Daniel Pettersson <daniel <at> dpettersson.net>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sun, 18 Feb 2024 18:25:07 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: bug-gnu-emacs <at> gnu.org
Subject: [PATCH] Jsonrpc: improve performance of process filter function
Date: Sun, 18 Feb 2024 02:53:01 +0100
[Message part 1 (text/plain, inline)]
Tags: patch


This was issue was discovered during elpa package dape's development,
where an adapter was sending 72000 notifications on startup which leads
to emacs looping over timer--time-less-p for > 50 seconds and after the
fix for less then 1 second.  The "integrity" of timer order are messed
with but as timer_check runs all of the ripe timers in the same while
loop it only becomes an question of execution order.  This change uses
timer.el internal api `timer--triggered', but this might be fine as it's
tightly coupled with keyboard.

In GNU Emacs 30.0.50 (build 1, aarch64-apple-darwin23.1.0, NS
 appkit-2487.20 Version 14.1.1 (Build 23B81)) of 2023-12-20 built on
 Daniels-Air
Repository revision: 281be72422f42fcc84d43f50723a3e91b7d03cbc
Repository branch: master
Windowing system distributor 'Apple', version 10.3.2487
System Description:  macOS 14.1.1

[0001-Jsonrpc-improve-performance-of-process-filter-functi.patch (text/patch, attachment)]

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Wed, 28 Feb 2024 12:24:02 GMT) Full text and rfc822 format available.

Message #8 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: 69241 <at> debbugs.gnu.org
Subject: Fixed patch issues
Date: Wed, 28 Feb 2024 13:22:50 +0100
[Message part 1 (text/plain, inline)]
The previous patch included an large regression, where "batch" of
messages where handled as an LIFO.  This patch should fix the issue and
also removes the dependency on timer.el internal apis.  Which makes it
instead dependent on undocumented behavior that timers of equal time are
executed in LIFO order.

[0001-Jsonrpc-improve-performance-of-process-filter-functi.patch (text/x-patch, inline)]
From c4d5ddb9ce5cdb8c283928daf6b166e4ce5a430d Mon Sep 17 00:00:00 2001
From: Daniel Pettersson <daniel <at> dpettersson.net>
Date: Wed, 28 Feb 2024 13:03:56 +0100
Subject: [PATCH] Jsonrpc: improve performance of process filter function

`run-at-time' keeps `timer-list' list sorted by inserting each timer
based on the timer value.  This means that `timer--time-less-p' needs is
executed ~ N*N/2 times for each N pending messages.  This means that
jsonrpc becomes unusable for connections that generate a lot messages at
the same time.

* lisp/jsonrpc.el (Version): Bump to 1.0.25
(jsonrpc--process-filter): Improve performance
---
 lisp/jsonrpc.el | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index 14fe0447008..5037d8c5b2b 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -4,7 +4,7 @@
 
 ;; Author: João Távora <joaotavora <at> gmail.com>
 ;; Keywords: processes, languages, extensions
-;; Version: 1.0.24
+;; Version: 1.0.25
 ;; Package-Requires: ((emacs "25.2"))
 
 ;; This is a GNU ELPA :core package.  Avoid functionality that is not
@@ -760,10 +760,11 @@ jsonrpc--process-filter
                                 (setq message
                                       (plist-put message :jsonrpc-json
                                                  (buffer-string)))
-                                (process-put proc 'jsonrpc-mqueue
-                                             (nconc (process-get proc
-                                                                 'jsonrpc-mqueue)
-                                                    (list message)))))
+                                ;; Put new messages at the front of the queue,
+                                ;; this is correct as the order is reversed
+                                ;; before putting the timers on `timer-list'.
+                                (push message
+                                      (process-get proc 'jsonrpc-mqueue))))
                           (goto-char message-end)
                           (let ((inhibit-read-only t))
                             (delete-region (point-min) (point)))
@@ -782,11 +783,20 @@ jsonrpc--process-filter
           ;; non-locally (typically the reply to a request), so do
           ;; this all this processing in top-level loops timer.
           (cl-loop
+           ;; `timer-activate' orders timers by time, which is an
+           ;; very expensive operation when jsonrpc-mqueue is large,
+           ;; therefore the time object is reused for each timer
+           ;; created.
+           with time = (current-time)
            for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
-           do (run-at-time 0 nil
-                           (lambda (m) (with-temp-buffer
-                                         (jsonrpc-connection-receive conn m)))
-                           msg)))))))
+           do (let ((timer (timer-create)))
+                (timer-set-time timer time)
+                (timer-set-function timer
+                                    (lambda (conn msg)
+                                      (with-temp-buffer
+                                        (jsonrpc-connection-receive conn msg)))
+                                    (list conn msg))
+                (timer-activate timer))))))))
 
 (defun jsonrpc--remove (conn id &optional deferred-spec)
   "Cancel CONN's continuations for ID, including its timer, if it exists.
-- 
2.39.3 (Apple Git-145)


Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sat, 09 Mar 2024 08:57:01 GMT) Full text and rfc822 format available.

Message #11 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>,
 João Távora <joaotavora <at> gmail.com>
Cc: 69241 <at> debbugs.gnu.org
Subject: Re: bug#69241: Fixed patch issues
Date: Sat, 09 Mar 2024 10:55:51 +0200



Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sat, 09 Mar 2024 08:58:02 GMT) Full text and rfc822 format available.

Message #14 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>,
 João Távora <joaotavora <at> gmail.com>
Cc: 69241 <at> debbugs.gnu.org
Subject: Re: bug#69241: Fixed patch issues
Date: Sat, 09 Mar 2024 10:56:42 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Date: Wed, 28 Feb 2024 13:22:50 +0100
> 
> The previous patch included an large regression, where "batch" of
> messages where handled as an LIFO.  This patch should fix the issue and
> also removes the dependency on timer.el internal apis.  Which makes it
> instead dependent on undocumented behavior that timers of equal time are
> executed in LIFO order.

João, any comments or suggestions?  Should I install this?

> >From c4d5ddb9ce5cdb8c283928daf6b166e4ce5a430d Mon Sep 17 00:00:00 2001
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Date: Wed, 28 Feb 2024 13:03:56 +0100
> Subject: [PATCH] Jsonrpc: improve performance of process filter function
> 
> `run-at-time' keeps `timer-list' list sorted by inserting each timer
> based on the timer value.  This means that `timer--time-less-p' needs is
> executed ~ N*N/2 times for each N pending messages.  This means that
> jsonrpc becomes unusable for connections that generate a lot messages at
> the same time.
> 
> * lisp/jsonrpc.el (Version): Bump to 1.0.25
> (jsonrpc--process-filter): Improve performance
> ---
>  lisp/jsonrpc.el | 28 +++++++++++++++++++---------
>  1 file changed, 19 insertions(+), 9 deletions(-)
> 
> diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
> index 14fe0447008..5037d8c5b2b 100644
> --- a/lisp/jsonrpc.el
> +++ b/lisp/jsonrpc.el
> @@ -4,7 +4,7 @@
>  
>  ;; Author: João Távora <joaotavora <at> gmail.com>
>  ;; Keywords: processes, languages, extensions
> -;; Version: 1.0.24
> +;; Version: 1.0.25
>  ;; Package-Requires: ((emacs "25.2"))
>  
>  ;; This is a GNU ELPA :core package.  Avoid functionality that is not
> @@ -760,10 +760,11 @@ jsonrpc--process-filter
>                                  (setq message
>                                        (plist-put message :jsonrpc-json
>                                                   (buffer-string)))
> -                                (process-put proc 'jsonrpc-mqueue
> -                                             (nconc (process-get proc
> -                                                                 'jsonrpc-mqueue)
> -                                                    (list message)))))
> +                                ;; Put new messages at the front of the queue,
> +                                ;; this is correct as the order is reversed
> +                                ;; before putting the timers on `timer-list'.
> +                                (push message
> +                                      (process-get proc 'jsonrpc-mqueue))))
>                            (goto-char message-end)
>                            (let ((inhibit-read-only t))
>                              (delete-region (point-min) (point)))
> @@ -782,11 +783,20 @@ jsonrpc--process-filter
>            ;; non-locally (typically the reply to a request), so do
>            ;; this all this processing in top-level loops timer.
>            (cl-loop
> +           ;; `timer-activate' orders timers by time, which is an
> +           ;; very expensive operation when jsonrpc-mqueue is large,
> +           ;; therefore the time object is reused for each timer
> +           ;; created.
> +           with time = (current-time)
>             for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
> -           do (run-at-time 0 nil
> -                           (lambda (m) (with-temp-buffer
> -                                         (jsonrpc-connection-receive conn m)))
> -                           msg)))))))
> +           do (let ((timer (timer-create)))
> +                (timer-set-time timer time)
> +                (timer-set-function timer
> +                                    (lambda (conn msg)
> +                                      (with-temp-buffer
> +                                        (jsonrpc-connection-receive conn msg)))
> +                                    (list conn msg))
> +                (timer-activate timer))))))))
>  
>  (defun jsonrpc--remove (conn id &optional deferred-spec)
>    "Cancel CONN's continuations for ID, including its timer, if it exists.
> -- 
> 2.39.3 (Apple Git-145)
> 




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sat, 09 Mar 2024 10:59:02 GMT) Full text and rfc822 format available.

Message #17 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: João Távora <joaotavora <at> gmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, Daniel Pettersson <daniel <at> dpettersson.net>
Subject: Re: bug#69241: Fixed patch issues
Date: Sat, 9 Mar 2024 10:56:22 +0000
On Sat, Mar 9, 2024 at 8:56 AM Eli Zaretskii <eliz <at> gnu.org> wrote:
>
> > From: Daniel Pettersson <daniel <at> dpettersson.net>
> > Date: Wed, 28 Feb 2024 13:22:50 +0100
> >
> > The previous patch included an large regression, where "batch" of
> > messages where handled as an LIFO.  This patch should fix the issue and
> > also removes the dependency on timer.el internal apis.  Which makes it
> > instead dependent on undocumented behavior that timers of equal time are
> > executed in LIFO order.
>
> João, any comments or suggestions?  Should I install this?

From where I stand, Daniel Pettersson is the new Jsonrpc maintainer.  I'd give
him push rights so he can install this and push any follow-up fixes easily.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 10 Mar 2024 14:30:02 GMT) Full text and rfc822 format available.

Message #20 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: João Távora <joaotavora <at> gmail.com>
Cc: 69241 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#69241: Fixed patch issues
Date: Sun, 10 Mar 2024 15:28:21 +0100
João Távora <joaotavora <at> gmail.com> writes:

> From where I stand, Daniel Pettersson is the new Jsonrpc maintainer.  I'd give
> him push rights so he can install this and push any follow-up fixes easily.

As I have previously stated I am willing to take on the maintainer role
for Jsonrpc.  

> +           ;; `timer-activate' orders timers by time, which is an
> +           ;; very expensive operation when jsonrpc-mqueue is large,
> +           ;; therefore the time object is reused for each timer
> +           ;; created.

I am interested in what both of you think about relying on undocumented
behavior.

To grasp the scope of this issue, an adapter server sent 50 000
messages. With `read-process-output-max' set to the platforms max, each
of those messages where placed on `timer-list' in the same call to
jsonrpc's filter function, which then had to be sorted as O(N^2)
(calls to timer--time-less-p). 

Which makes Jsonrpc unusable for that particular server.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 10 Mar 2024 14:43:02 GMT) Full text and rfc822 format available.

Message #23 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: João Távora <joaotavora <at> gmail.com>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#69241: Fixed patch issues
Date: Sun, 10 Mar 2024 14:41:07 +0000
On Sun, Mar 10, 2024 at 2:28 PM Daniel Pettersson
<daniel <at> dpettersson.net> wrote:

> As I have previously stated I am willing to take on the maintainer role
> for Jsonrpc.

Thank you Daniel.  It'll be in very good hands.

> > +           ;; `timer-activate' orders timers by time, which is an
> > +           ;; very expensive operation when jsonrpc-mqueue is large,
> > +           ;; therefore the time object is reused for each timer
> > +           ;; created.
>
> I am interested in what both of you think about relying on undocumented
> behavior.
>
> To grasp the scope of this issue, an adapter server sent 50 000
> messages. With `read-process-output-max' set to the platforms max, each
> of those messages where placed on `timer-list' in the same call to
> jsonrpc's filter function, which then had to be sorted as O(N^2)
> (calls to timer--time-less-p).
>
> Which makes Jsonrpc unusable for that particular server.

I think your analysis is sound, Daniel. As you have noticed the run-at-time 0
is not to actually  use a timer, but just to get a call that is independent
from the current  call stack which may be nested because of the use case
of bug#67945.

The calls must be processed in the order of the queue though, but in
theory that shouldn't lead to O(N^2) if a suitable technique is found (perhaps
different from (run-at-time 0)).

Finally 50.000 is a gigantic amount -- are all of these essential?  Can't the
server coalesce this data into fewer messages within the DAP protocol?

João




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 10 Mar 2024 16:48:02 GMT) Full text and rfc822 format available.

Message #26 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Sun, 10 Mar 2024 18:46:45 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: Eli Zaretskii <eliz <at> gnu.org>,  69241 <at> debbugs.gnu.org
> Date: Sun, 10 Mar 2024 15:28:21 +0100
> 
> As I have previously stated I am willing to take on the maintainer role
> for Jsonrpc.  

Thank you.  Should we reflect that in admin/MAINTAINERS?

> To grasp the scope of this issue, an adapter server sent 50 000
> messages. With `read-process-output-max' set to the platforms max, each
> of those messages where placed on `timer-list' in the same call to
> jsonrpc's filter function, which then had to be sorted as O(N^2)
> (calls to timer--time-less-p). 

I don't see where the list is sorted with the above behavior.  Can you
point out which code you allude to?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 10 Mar 2024 20:07:01 GMT) Full text and rfc822 format available.

Message #29 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Sun, 10 Mar 2024 21:05:46 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Thank you.  Should we reflect that in admin/MAINTAINERS?

That is fine by me.  I am not sure what the impact of that would be?

> I don't see where the list is sorted with the above behavior.  Can you
> point out which code you allude to?

Let's continue use the implementation on `master' and my example to
give it some.

In my example process property 'jsonrpc-mqueue is an list of ~50,000
messages. 

In `jsonrpc--process-filter' we evaluate `run-at-time' macro, which in
turn invokes `timer--activate'.

(cl-defun jsonrpc--process-filter (proc string)
...
          (cl-loop
           for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
           do (run-at-time 0 nil
                           (lambda (m) (with-temp-buffer
                                         (jsonrpc-connection-receive conn m)))
                           msg)))))))

`timer--active' makes sure that the `timer-list' is sorted by time to
trigger in ascending order:

(defun timer--activate (timer &optional triggered-p reuse-cell idle)
...
      ;; Skip all timers to trigger before the new one.
      (while (and timers (timer--time-less-p (car timers) timer))
	(setq last timers
	      timers (cdr timers)))
      ;; Insert new timer after last which possibly means in front of queue.
      (setf (cond (last (cdr last))
                  (idle timer-idle-list)
                  (t    timer-list))
            reuse-cell)

This is especially bad when the time of the timer is created inside a
loop as in `jsonrpc--process-filter'.

Let's take the last loop iteration in the `cl-loop'; timer_{N} from
N messages.

timer_{N} will always be compared with timer_{1}, timer_{2},
... timer_{N-2}, timer_{N-1}.

This ends up with doing N^2/2 timer comparisons in
`jsonrpc--process-filter'.

Hopefully my ramblings makes things clearer.

/Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Sun, 10 Mar 2024 23:32:02 GMT) Full text and rfc822 format available.

Message #32 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 00:30:32 +0100
Daniel Pettersson <daniel <at> dpettersson.net> writes:

> In my example process property 'jsonrpc-mqueue is an list of ~50,000
> messages. 

I have to make an correction here, even if it does not really matter but
an extra zero seams to have made it's way into these conversation.  The
benchmark I have been benchmarking against is ~5,000 messages.

12,500,000 (N^2/2) calls to `timer--time-less-p' is enough to make any
machine crawl to a halt.

> /Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 09:46:02 GMT) Full text and rfc822 format available.

Message #35 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: João Távora <joaotavora <at> gmail.com>
Cc: 69241 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 10:44:38 +0100
João Távora <joaotavora <at> gmail.com> writes:

> The calls must be processed in the order of the queue though, but in
> theory that shouldn't lead to O(N^2) if a suitable technique is found (perhaps
> different from (run-at-time 0)).

The first patch I sent had this issue of them being process out of
order.  But the second patch ensures that they are handled correctly.
It may look like they don't but they do :)

1. We push each message on 'jsonrpc-mqueue (Order is reversed)
2. We pop each item of 'jsonrpc-mqueue and push (Guaranteed by sharing
the same `time' for each timer) the item on `timer-list'.
3. Timers are evaluated in the order of `timer-list'.

Simplified:
(setf timer-list (append (reverse (reverse messages)) timer-list))

:)

This is not that straight forward and loops back to my point of relying
on timer.el internals, but as I see it there is no other way, if one
would like to get around the internal sorting of timers in timer.el
which I would very much like to do.

> Finally 50.000 is a gigantic amount -- are all of these essential?  Can't the
> server coalesce this data into fewer messages within the DAP protocol?

Sorry I made an error it's 5000, this is js-debug sending an list of
javascript sources for "medium" sized js React project and no that cant
be configured.  Which as I stated in my other reply leads to 12,500,000
(N^2/2) calls to `timer--time-less-p' (timer sorting in timer.el) but
with my patch only requires 5000.

(why does an "medium" sized modern js web project require 5000 sources
is the real question we need to ask ourselves)

/Daniel







Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 12:25:02 GMT) Full text and rfc822 format available.

Message #38 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 14:24:00 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> Date: Sun, 10 Mar 2024 21:05:46 +0100
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> > Thank you.  Should we reflect that in admin/MAINTAINERS?
> 
> That is fine by me.  I am not sure what the impact of that would be?

Just that you express interest in this package and don't mind to be
CC'ed when some issue about it comes up.

> > I don't see where the list is sorted with the above behavior.  Can you
> > point out which code you allude to?
> 
> Let's continue use the implementation on `master' and my example to
> give it some.
> 
> In my example process property 'jsonrpc-mqueue is an list of ~50,000
> messages. 
> 
> In `jsonrpc--process-filter' we evaluate `run-at-time' macro, which in
> turn invokes `timer--activate'.
> 
> (cl-defun jsonrpc--process-filter (proc string)
> ...
>           (cl-loop
>            for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
>            do (run-at-time 0 nil
>                            (lambda (m) (with-temp-buffer
>                                          (jsonrpc-connection-receive conn m)))
>                            msg)))))))
> 
> `timer--active' makes sure that the `timer-list' is sorted by time to
> trigger in ascending order:
> 
> (defun timer--activate (timer &optional triggered-p reuse-cell idle)
> ...
>       ;; Skip all timers to trigger before the new one.
>       (while (and timers (timer--time-less-p (car timers) timer))
> 	(setq last timers
> 	      timers (cdr timers)))
>       ;; Insert new timer after last which possibly means in front of queue.
>       (setf (cond (last (cdr last))
>                   (idle timer-idle-list)
>                   (t    timer-list))
>             reuse-cell)
> 
> This is especially bad when the time of the timer is created inside a
> loop as in `jsonrpc--process-filter'.
> 
> Let's take the last loop iteration in the `cl-loop'; timer_{N} from
> N messages.
> 
> timer_{N} will always be compared with timer_{1}, timer_{2},
> ... timer_{N-2}, timer_{N-1}.
> 
> This ends up with doing N^2/2 timer comparisons in
> `jsonrpc--process-filter'.

So the problem is not with timer--activate, the problem is with
jsonrpc--process-filter which calls timer--activate, and the net
effect of those two is the N^2 complexity, is that right?  Your
original statement was that the list of timers is sorted with N^2
complexity, and that is the part to which I responded.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 12:40:02 GMT) Full text and rfc822 format available.

Message #41 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 13:38:28 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Just that you express interest in this package and don't mind to be
> CC'ed when some issue about it comes up.

Oh that's fine.

> So the problem is not with timer--activate, the problem is with
> jsonrpc--process-filter which calls timer--activate, and the net
> effect of those two is the N^2 complexity, is that right?  Your
> original statement was that the list of timers is sorted with N^2
> complexity, and that is the part to which I responded.

Correct, sorry if I was being unclear.

/Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 13:15:02 GMT) Full text and rfc822 format available.

Message #44 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 15:11:38 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: Eli Zaretskii <eliz <at> gnu.org>,  69241 <at> debbugs.gnu.org
> Date: Mon, 11 Mar 2024 10:44:38 +0100
> 
> João Távora <joaotavora <at> gmail.com> writes:
> 
> > The calls must be processed in the order of the queue though, but in
> > theory that shouldn't lead to O(N^2) if a suitable technique is found (perhaps
> > different from (run-at-time 0)).
> 
> The first patch I sent had this issue of them being process out of
> order.  But the second patch ensures that they are handled correctly.
> It may look like they don't but they do :)
> 
> 1. We push each message on 'jsonrpc-mqueue (Order is reversed)
> 2. We pop each item of 'jsonrpc-mqueue and push (Guaranteed by sharing
> the same `time' for each timer) the item on `timer-list'.
> 3. Timers are evaluated in the order of `timer-list'.

Can't the code activate the timers in reverse order?  Then each
timer--activate will likely compare with just one timer, the first
one.

> This is not that straight forward and loops back to my point of relying
> on timer.el internals, but as I see it there is no other way, if one
> would like to get around the internal sorting of timers in timer.el
> which I would very much like to do.

Timers must fire in the order of their time, so they must be kept
sorted, or else running the timer functions and removing the timer
from the list will be much more complex.

Assuming that timers are sorted is not relying on the internals, if
you ask me.

> (why does an "medium" sized modern js web project require 5000 sources
> is the real question we need to ask ourselves)

Indeed.  Any idea of what the answer could be?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 14:49:01 GMT) Full text and rfc822 format available.

Message #47 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 15:48:02 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Can't the code activate the timers in reverse order?  Then each
> timer--activate will likely compare with just one timer, the first
> one.

In my patch they do.  As all timers are using the same time list and
pushed on timer-list in the correct order.  Feels like *I* have pushed
this thread into bikeshedding territory :)

> Timers must fire in the order of their time, so they must be kept
> sorted, or else running the timer functions and removing the timer
> from the list will be much more complex.
>
> Assuming that timers are sorted is not relying on the internals, if
> you ask me.

Great,  what actions do I need to take to move forward with this
maintainership thing?

/Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 16:28:02 GMT) Full text and rfc822 format available.

Message #50 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 18:27:09 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> Date: Mon, 11 Mar 2024 15:48:02 +0100
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> > Can't the code activate the timers in reverse order?  Then each
> > timer--activate will likely compare with just one timer, the first
> > one.
> 
> In my patch they do.  As all timers are using the same time list and
> pushed on timer-list in the correct order.  Feels like *I* have pushed
> this thread into bikeshedding territory :)

Maybe.  But then why we are talking about N^2 order, if you already
have a way to fix that?

> > Timers must fire in the order of their time, so they must be kept
> > sorted, or else running the timer functions and removing the timer
> > from the list will be much more complex.
> >
> > Assuming that timers are sorted is not relying on the internals, if
> > you ask me.
> 
> Great,  what actions do I need to take to move forward with this
> maintainership thing?

It's basically up to you, but maybe I don't understand what you are
asking.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 19:05:02 GMT) Full text and rfc822 format available.

Message #53 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 20:03:22 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Maybe.  But then why we are talking about N^2 order, if you already
> have a way to fix that?

It's in the first reply to this bug.  I believe we got sidetracked.

> It's basically up to you, but maybe I don't understand what you are
> asking.

I am up for it, I thought that maybe some actions need to be taken on my
end to get that process rolling (getting push permission etc.).

/Daniel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 19:22:02 GMT) Full text and rfc822 format available.

Message #56 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 21:18:30 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> Date: Mon, 11 Mar 2024 20:03:22 +0100
> 
> > It's basically up to you, but maybe I don't understand what you are
> > asking.
> 
> I am up for it, I thought that maybe some actions need to be taken on my
> end to get that process rolling (getting push permission etc.).

Write access to the repository is a separate decision.  We usually
make it after observing your patches and review comments to them over
some period, to make sure you are on top of our conventions and
procedures.  So no need to wait for that or see it as a prerequisite
for your development and maintenance work.

Thanks.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Mon, 11 Mar 2024 19:41:02 GMT) Full text and rfc822 format available.

Message #59 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Mon, 11 Mar 2024 20:39:18 +0100
[Message part 1 (text/plain, inline)]
Eli Zaretskii <eliz <at> gnu.org> writes:

> Write access to the repository is a separate decision.  We usually
> make it after observing your patches and review comments to them over
> some period, to make sure you are on top of our conventions and
> procedures.  So no need to wait for that or see it as a prerequisite
> for your development and maintenance work.

That makes sense, actually I appreciate the hand holding :) Is there any
thing more that you need from me regarding the original issue?  I feel
like I have a decent enough understanding of Jsonrpc so I am happy to be
noted as an maintainer contact in admin/MAINTAINERS.

Also I am a bit bad at reading tones in email form.  I am sorry if I
made any faux pas in this thread.

Re attaching the patch as to not get it confused with the first patch sent.
[0001-Jsonrpc-improve-performance-of-process-filter-functi.patch (text/x-patch, inline)]
From c4d5ddb9ce5cdb8c283928daf6b166e4ce5a430d Mon Sep 17 00:00:00 2001
From: Daniel Pettersson <daniel <at> dpettersson.net>
Date: Wed, 28 Feb 2024 13:03:56 +0100
Subject: [PATCH] Jsonrpc: improve performance of process filter function

`run-at-time' keeps `timer-list' list sorted by inserting each timer
based on the timer value.  This means that `timer--time-less-p' needs is
executed ~ N*N/2 times for each N pending messages.  This means that
jsonrpc becomes unusable for connections that generate a lot messages at
the same time.

* lisp/jsonrpc.el (Version): Bump to 1.0.25
(jsonrpc--process-filter): Improve performance
---
 lisp/jsonrpc.el | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index 14fe0447008..5037d8c5b2b 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -4,7 +4,7 @@
 
 ;; Author: João Távora <joaotavora <at> gmail.com>
 ;; Keywords: processes, languages, extensions
-;; Version: 1.0.24
+;; Version: 1.0.25
 ;; Package-Requires: ((emacs "25.2"))
 
 ;; This is a GNU ELPA :core package.  Avoid functionality that is not
@@ -760,10 +760,11 @@ jsonrpc--process-filter
                                 (setq message
                                       (plist-put message :jsonrpc-json
                                                  (buffer-string)))
-                                (process-put proc 'jsonrpc-mqueue
-                                             (nconc (process-get proc
-                                                                 'jsonrpc-mqueue)
-                                                    (list message)))))
+                                ;; Put new messages at the front of the queue,
+                                ;; this is correct as the order is reversed
+                                ;; before putting the timers on `timer-list'.
+                                (push message
+                                      (process-get proc 'jsonrpc-mqueue))))
                           (goto-char message-end)
                           (let ((inhibit-read-only t))
                             (delete-region (point-min) (point)))
@@ -782,11 +783,20 @@ jsonrpc--process-filter
           ;; non-locally (typically the reply to a request), so do
           ;; this all this processing in top-level loops timer.
           (cl-loop
+           ;; `timer-activate' orders timers by time, which is an
+           ;; very expensive operation when jsonrpc-mqueue is large,
+           ;; therefore the time object is reused for each timer
+           ;; created.
+           with time = (current-time)
            for msg = (pop (process-get proc 'jsonrpc-mqueue)) while msg
-           do (run-at-time 0 nil
-                           (lambda (m) (with-temp-buffer
-                                         (jsonrpc-connection-receive conn m)))
-                           msg)))))))
+           do (let ((timer (timer-create)))
+                (timer-set-time timer time)
+                (timer-set-function timer
+                                    (lambda (conn msg)
+                                      (with-temp-buffer
+                                        (jsonrpc-connection-receive conn msg)))
+                                    (list conn msg))
+                (timer-activate timer))))))))
 
 (defun jsonrpc--remove (conn id &optional deferred-spec)
   "Cancel CONN's continuations for ID, including its timer, if it exists.
-- 
2.39.3 (Apple Git-145)


Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Tue, 12 Mar 2024 03:31:01 GMT) Full text and rfc822 format available.

Message #62 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 05:27:48 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> Date: Mon, 11 Mar 2024 20:39:18 +0100
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> > Write access to the repository is a separate decision.  We usually
> > make it after observing your patches and review comments to them over
> > some period, to make sure you are on top of our conventions and
> > procedures.  So no need to wait for that or see it as a prerequisite
> > for your development and maintenance work.
> 
> That makes sense, actually I appreciate the hand holding :) Is there any
> thing more that you need from me regarding the original issue?  I feel
> like I have a decent enough understanding of Jsonrpc so I am happy to be
> noted as an maintainer contact in admin/MAINTAINERS.

I have all the information I need, and will make the change soon.

> Also I am a bit bad at reading tones in email form.  I am sorry if I
> made any faux pas in this thread.

You didn't, no worries.

> Re attaching the patch as to not get it confused with the first patch sent.

Thanks, will install soon.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Tue, 12 Mar 2024 13:30:03 GMT) Full text and rfc822 format available.

Message #65 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 15:28:52 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> Date: Mon, 11 Mar 2024 20:39:18 +0100
> 
> Re attaching the patch as to not get it confused with the first patch sent.

Thanks, installed on the master branch.

A few nits for the future:

 . Please make sure the lines in the commit log message are wrapped at
   column 65 at most, so that the generated ChangeLog file, which
   indents the entries by a TAB, has lines wrapped at 74 columns.
 . Each log entry should end in a period, i.e. be a complete sentence.
 . Please always mention the bug number, if known, as part of the
   commit log messages.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Tue, 12 Mar 2024 13:33:02 GMT) Full text and rfc822 format available.

Message #68 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: daniel <at> dpettersson.net
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 15:31:50 +0200
> Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
> Date: Tue, 12 Mar 2024 05:27:48 +0200
> From: Eli Zaretskii <eliz <at> gnu.org>
> 
> > From: Daniel Pettersson <daniel <at> dpettersson.net>
> > Cc: joaotavora <at> gmail.com,  69241 <at> debbugs.gnu.org
> > Date: Mon, 11 Mar 2024 20:39:18 +0100
> > 
> > Eli Zaretskii <eliz <at> gnu.org> writes:
> > 
> > > Write access to the repository is a separate decision.  We usually
> > > make it after observing your patches and review comments to them over
> > > some period, to make sure you are on top of our conventions and
> > > procedures.  So no need to wait for that or see it as a prerequisite
> > > for your development and maintenance work.
> > 
> > That makes sense, actually I appreciate the hand holding :) Is there any
> > thing more that you need from me regarding the original issue?  I feel
> > like I have a decent enough understanding of Jsonrpc so I am happy to be
> > noted as an maintainer contact in admin/MAINTAINERS.
> 
> I have all the information I need, and will make the change soon.

Done.

> > Re attaching the patch as to not get it confused with the first patch sent.
> 
> Thanks, will install soon.

And done.

Should we close this bug now?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Tue, 12 Mar 2024 14:34:01 GMT) Full text and rfc822 format available.

Message #71 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 15:33:08 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Thanks, installed on the master branch.

Great, thanks!

> A few nits for the future:
>
>  . Please make sure the lines in the commit log message are wrapped at
>    column 65 at most, so that the generated ChangeLog file, which
>    indents the entries by a TAB, has lines wrapped at 74 columns.
>  . Each log entry should end in a period, i.e. be a complete sentence.
>  . Please always mention the bug number, if known, as part of the
>    commit log messages.

Will do better in next patch, thanks again.

/Daniel






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#69241; Package emacs. (Tue, 12 Mar 2024 14:36:02 GMT) Full text and rfc822 format available.

Message #74 received at 69241 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Pettersson <daniel <at> dpettersson.net>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 69241 <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 15:34:20 +0100
Eli Zaretskii <eliz <at> gnu.org> writes:

> Should we close this bug now?

Yes

/Daniel




Reply sent to Eli Zaretskii <eliz <at> gnu.org>:
You have taken responsibility. (Tue, 12 Mar 2024 15:02:01 GMT) Full text and rfc822 format available.

Notification sent to Daniel Pettersson <daniel <at> dpettersson.net>:
bug acknowledged by developer. (Tue, 12 Mar 2024 15:02:02 GMT) Full text and rfc822 format available.

Message #79 received at 69241-done <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Daniel Pettersson <daniel <at> dpettersson.net>
Cc: 69241-done <at> debbugs.gnu.org, joaotavora <at> gmail.com
Subject: Re: bug#69241: Fixed patch issues
Date: Tue, 12 Mar 2024 17:00:26 +0200
> From: Daniel Pettersson <daniel <at> dpettersson.net>
> Cc: 69241 <at> debbugs.gnu.org,  joaotavora <at> gmail.com
> Date: Tue, 12 Mar 2024 15:34:20 +0100
> 
> Eli Zaretskii <eliz <at> gnu.org> writes:
> 
> > Should we close this bug now?
> 
> Yes

Thanks, done.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 10 Apr 2024 11:24:07 GMT) Full text and rfc822 format available.

This bug report was last modified 1 year and 72 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.