all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
@ 2023-11-29 21:29 Raffael Stocker
  2023-11-30  7:00 ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-11-29 21:29 UTC (permalink / raw)
  To: 67536


Org table re-calculation is very slow, partly due to
math-read-preprocess-string of calc mode consing unnecessarily.  For
example, in one (large) table, I get the following memory usage from the
profiler:

...
     60,252,646  96%   - org-ctrl-c-ctrl-c
     60,248,166  96%    - org-table-calc-current-TBLFM
     60,216,431  96%     - funcall-interactively
     60,205,119  96%      - org-table-recalculate
     49,094,651  78%       - org-table-eval-formula
     32,624,631  52%        - calc-eval
     32,624,631  52%         - calc-do-calc-eval
     32,620,487  52%          - calc-do-calc-eval
     32,611,151  52%           - math-read-exprs
     29,388,838  47%            + math-read-preprocess-string
      2,343,257   3%            + math-read-expr-list
...

The reason for the slow-down seems to be that
math-read-preprocess-string conses a lot, keeping the GC busy.  This is
due to heavy use of replace-regexp-in-string in this function:

(defun math-read-preprocess-string (str)
  "Replace some substrings of STR by Calc equivalents."
  (setq str
        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
                                  "^(\\&)" str))
  (setq str
        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
                                  "_(\\&)" str))
  (let ((rep-list math-read-replacement-list))
    (while rep-list
      ;; consing like a mad-man here:
      (setq str
            (replace-regexp-in-string (nth 0 (car rep-list))
                                      (nth 1 (car rep-list)) str))
      (setq rep-list (cdr rep-list))))
  str)

I would like to propose using a temp buffer instead of kneading the
string into submission:

(defun math-read-preprocess-string (str)
  "Replace some substrings of STR by Calc equivalents."
  (with-temp-buffer
    (insert str)
    (goto-char 0)
    (while (re-search-forward (concat "[" math-read-superscripts "]+") nil t)
      (replace-match "^(\\&)"))
    (goto-char 0)
    (while (re-search-forward (concat "[" math-read-subscripts "]+") nil t)
      (replace-match "_(\\&)"))
    (goto-char 0)
    (let ((rep-list math-read-replacement-list))
      (while rep-list
        (while (re-search-forward (nth 0 (car rep-list)) nil t)
          (replace-match (nth 1 (car rep-list))))
        (goto-char 0)
        (setq rep-list (cdr rep-list))))
    (buffer-string)))

With this replacement, the profiler shows much less memory usage on the
same org table:

...
     30,411,804  91%   - org-ctrl-c-ctrl-c
     30,407,324  91%    - org-table-calc-current-TBLFM
     30,363,932  91%     - funcall-interactively
     30,331,900  91%      - org-table-recalculate
     20,430,223  61%       - org-table-eval-formula
      6,751,852  20%        + org-table-justify-field-maybe
      4,598,523  13%        - calc-eval
      4,586,091  13%         - calc-do-calc-eval
      4,569,619  13%          - calc-do-calc-eval
      4,547,971  13%           - math-read-exprs
      2,297,453   6%            + math-read-expr-list
      1,347,377   4%            + math-read-preprocess-string
          7,296   0%              math-read-token
...

Only I am not sure the replacement is correct in all possible edge cases
(or whether it uses so much less memory because I have overlooked
something).

What do you think? Can a calc-mode expert weigh in here?

Regards,
Raffael





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-11-29 21:29 bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily Raffael Stocker
@ 2023-11-30  7:00 ` Eli Zaretskii
  2023-11-30 18:28   ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2023-11-30  7:00 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: 67536

> From: Raffael Stocker <r.stocker@mnet-mail.de>
> Date: Wed, 29 Nov 2023 22:29:38 +0100
> 
> 
> Org table re-calculation is very slow, partly due to
> math-read-preprocess-string of calc mode consing unnecessarily.  For
> example, in one (large) table, I get the following memory usage from the
> profiler:
> 
> ...
>      60,252,646  96%   - org-ctrl-c-ctrl-c
>      60,248,166  96%    - org-table-calc-current-TBLFM
>      60,216,431  96%     - funcall-interactively
>      60,205,119  96%      - org-table-recalculate
>      49,094,651  78%       - org-table-eval-formula
>      32,624,631  52%        - calc-eval
>      32,624,631  52%         - calc-do-calc-eval
>      32,620,487  52%          - calc-do-calc-eval
>      32,611,151  52%           - math-read-exprs
>      29,388,838  47%            + math-read-preprocess-string
>       2,343,257   3%            + math-read-expr-list

This is not memory usage, this is CPU usage measured by using
memory-allocation functions as triggers to probe for the function that
is being executed.  So its evidence about consing and GC pressure is
indirect at best.

Can you instead look at the values of gcs-done and gc-elapsed before
and after running the offending code, and show the delta of each one
of them, with and without your proposed changes?  Then we will see a
much more direct evidence about the number of GC cycles and the time
spent in GC, and could make the decision about how to improve the
situation.

Thanks.





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-11-30  7:00 ` Eli Zaretskii
@ 2023-11-30 18:28   ` Raffael Stocker
  2023-11-30 19:14     ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-11-30 18:28 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 67536


Eli Zaretskii <eliz@gnu.org> writes:

>> ...
>>      60,252,646  96%   - org-ctrl-c-ctrl-c
>>      60,248,166  96%    - org-table-calc-current-TBLFM
>>      60,216,431  96%     - funcall-interactively
>>      60,205,119  96%      - org-table-recalculate
>>      49,094,651  78%       - org-table-eval-formula
>>      32,624,631  52%        - calc-eval
>>      32,624,631  52%         - calc-do-calc-eval
>>      32,620,487  52%          - calc-do-calc-eval
>>      32,611,151  52%           - math-read-exprs
>>      29,388,838  47%            + math-read-preprocess-string
>>       2,343,257   3%            + math-read-expr-list
>
> This is not memory usage, this is CPU usage measured by using
> memory-allocation functions as triggers to probe for the function that
> is being executed.  So its evidence about consing and GC pressure is
> indirect at best.
>
> Can you instead look at the values of gcs-done and gc-elapsed before
> and after running the offending code, and show the delta of each one
> of them, with and without your proposed changes?  Then we will see a
> much more direct evidence about the number of GC cycles and the time
> spent in GC, and could make the decision about how to improve the
> situation.

I defined the following advice:

--8<---------------cut here---------------start------------->8---

(defun my-gc-status (orig-fun &rest args)
  (let* ((done-bf gcs-done)
         (elapsed-bf gc-elapsed)
         (res (apply orig-fun args))
         (done-af gcs-done)
         (elapsed-af gc-elapsed))
    (message "before:\n\tgcs-done: %d, gc-elapsed: %f" done-bf elapsed-bf)
    (message "after:\n\tgcs-done: %d, difference: %d\n\tgc-elapsed: %f, difference: %f"
            done-af (- done-af done-bf)
            elapsed-af (- elapsed-af elapsed-bf))
    res))
(advice-add 'org-table-recalculate :around #'my-gc-status)

--8<---------------cut here---------------end--------------->8---

I had to put it around ‘org-table-recalculate’ instead of
‘math-read-preprocess-string’ as all the functions below
org-table-recalculate are called very often and have small individual
contributions.  With the original ‘math-read-preprocess-string’ I get the
following typical result:

--8<---------------cut here---------------start------------->8---

before:
	gcs-done: 854, gc-elapsed: 170.601313
after:
	gcs-done: 859, difference: 5
	gc-elapsed: 171.671042, difference: 1.069729

--8<---------------cut here---------------end--------------->8---

I ran the command about twenty times and almost always got the
gcs-done difference of 5, with the occasional 4.  The gc-elapsed is
fairly consistent at 1.07 for the 5 GC runs.

The modified version yields this typical output:

--8<---------------cut here---------------start------------->8---

before:
	gcs-done: 906, gc-elapsed: 181.417292
after:
	gcs-done: 908, difference: 2
	gc-elapsed: 181.847972, difference: 0.430679

--8<---------------cut here---------------end--------------->8---

Again, the gcs-done difference is quite stable at 2, with the occasional
3, the gc-elapsed is also fairly consistent at 0.43 for the 2 GC runs.

So we have about a factor of 2.5 between the elapsed GC times for the
two versions.

Regards,
Raffael





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-11-30 18:28   ` Raffael Stocker
@ 2023-11-30 19:14     ` Eli Zaretskii
  2023-12-01 17:34       ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2023-11-30 19:14 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: 67536

> From: Raffael Stocker <r.stocker@mnet-mail.de>
> Cc: 67536@debbugs.gnu.org
> Date: Thu, 30 Nov 2023 19:28:27 +0100
> 
> 
> (defun my-gc-status (orig-fun &rest args)
>   (let* ((done-bf gcs-done)
>          (elapsed-bf gc-elapsed)
>          (res (apply orig-fun args))
>          (done-af gcs-done)
>          (elapsed-af gc-elapsed))
>     (message "before:\n\tgcs-done: %d, gc-elapsed: %f" done-bf elapsed-bf)
>     (message "after:\n\tgcs-done: %d, difference: %d\n\tgc-elapsed: %f, difference: %f"
>             done-af (- done-af done-bf)
>             elapsed-af (- elapsed-af elapsed-bf))
>     res))
> (advice-add 'org-table-recalculate :around #'my-gc-status)
> 
> --8<---------------cut here---------------end--------------->8---
> 
> I had to put it around ‘org-table-recalculate’ instead of
> ‘math-read-preprocess-string’ as all the functions below
> org-table-recalculate are called very often and have small individual
> contributions.  With the original ‘math-read-preprocess-string’ I get the
> following typical result:
> 
> --8<---------------cut here---------------start------------->8---
> 
> before:
> 	gcs-done: 854, gc-elapsed: 170.601313
> after:
> 	gcs-done: 859, difference: 5
> 	gc-elapsed: 171.671042, difference: 1.069729
> 
> --8<---------------cut here---------------end--------------->8---
> 
> I ran the command about twenty times and almost always got the
> gcs-done difference of 5, with the occasional 4.  The gc-elapsed is
> fairly consistent at 1.07 for the 5 GC runs.
> 
> The modified version yields this typical output:
> 
> --8<---------------cut here---------------start------------->8---
> 
> before:
> 	gcs-done: 906, gc-elapsed: 181.417292
> after:
> 	gcs-done: 908, difference: 2
> 	gc-elapsed: 181.847972, difference: 0.430679
> 
> --8<---------------cut here---------------end--------------->8---
> 
> Again, the gcs-done difference is quite stable at 2, with the occasional
> 3, the gc-elapsed is also fairly consistent at 0.43 for the 2 GC runs.
> 
> So we have about a factor of 2.5 between the elapsed GC times for the
> two versions.

Thanks, sounds like a good optimization to me.





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-11-30 19:14     ` Eli Zaretskii
@ 2023-12-01 17:34       ` Raffael Stocker
  2023-12-01 18:12         ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-01 17:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 67536

[-- Attachment #1: Type: text/plain, Size: 413 bytes --]


Eli Zaretskii <eliz@gnu.org> writes:

> Thanks, sounds like a good optimization to me.

Splendid!

I cleaned it up a bit and ran a few tests against the original function
using an empty string, a string without anything to replace and a string
requiring many replacements.  It seems to behave just as the original.

I use cl-flet and (eval-when-compile (require 'cl-lib)).  I hope that is
ok.

Regards,
Raffael


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: optimized math-read-preprocess-string --]
[-- Type: text/x-patch, Size: 2285 bytes --]

From b3c055cfb42f45ffe662f4fa24ce7b0104949eb2 Mon Sep 17 00:00:00 2001
From: Raffael Stocker <r.stocker@mnet-mail.de>
Date: Fri, 1 Dec 2023 18:24:59 +0100
Subject: [PATCH] * lisp/calc/calc-aent.el (math-read-preprocess-string): cons
 less (bug#67536)

Use a temp buffer instead of working on a string.  This function is
called by calc-eval, which in turn is called repeatedly when
re-calculating org-mode tables.  This function is one of the main
bottlenecks there.
---
 lisp/calc/calc-aent.el | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..84b580a7033 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -27,6 +27,7 @@
 
 (require 'calc)
 (require 'calc-macs)
+(eval-when-compile (require 'cl-lib))
 
 ;; Declare functions which are defined elsewhere.
 (declare-function calc-digit-start-entry "calc" ())
@@ -550,19 +551,17 @@ math-read-subscripts
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (with-temp-buffer
+    (cl-flet ((replace-all (regexp replacement)
+                (goto-char 0)
+                (while (re-search-forward regexp nil t)
+                  (replace-match replacement))))
+      (insert str)
+      (replace-all (concat "[" math-read-superscripts "]+") "^(\\&)")
+      (replace-all (concat "[" math-read-subscripts "]+") "_(\\&)")
+      (dolist (rep-elem math-read-replacement-list)
+        (replace-all (car rep-elem) (cadr rep-elem)))
+      (buffer-string))))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.
-- 
2.43.0


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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-01 17:34       ` Raffael Stocker
@ 2023-12-01 18:12         ` Eli Zaretskii
  2023-12-01 21:10           ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2023-12-01 18:12 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: 67536

> From: Raffael Stocker <r.stocker@mnet-mail.de>
> Cc: 67536@debbugs.gnu.org
> Date: Fri, 01 Dec 2023 18:34:10 +0100
> 
> I cleaned it up a bit and ran a few tests against the original function
> using an empty string, a string without anything to replace and a string
> requiring many replacements.  It seems to behave just as the original.

Thanks.

> I use cl-flet and (eval-when-compile (require 'cl-lib)).  I hope that is
> ok.

I'd prefer a simple internal function, or a lambda.

> +  (with-temp-buffer
> +    (cl-flet ((replace-all (regexp replacement)
> +                (goto-char 0)
> +                (while (re-search-forward regexp nil t)
> +                  (replace-match replacement))))
> +      (insert str)
> +      (replace-all (concat "[" math-read-superscripts "]+") "^(\\&)")
> +      (replace-all (concat "[" math-read-subscripts "]+") "_(\\&)")
> +      (dolist (rep-elem math-read-replacement-list)
> +        (replace-all (car rep-elem) (cadr rep-elem)))
> +      (buffer-string))))

I think buffer-substring-no-properties would be better than
buffer-string, as we don't need to copy any text properties, right?





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-01 18:12         ` Eli Zaretskii
@ 2023-12-01 21:10           ` Raffael Stocker
  2023-12-02  8:03             ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-01 21:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 67536

[-- Attachment #1: Type: text/plain, Size: 450 bytes --]


Eli Zaretskii <eliz@gnu.org> writes:

>> I use cl-flet and (eval-when-compile (require 'cl-lib)).  I hope that is
>> ok.
>
> I'd prefer a simple internal function, or a lambda.

I went with the lambda, it is the more concise choice.

>> +      (buffer-string))))
>
> I think buffer-substring-no-properties would be better than
> buffer-string, as we don't need to copy any text properties, right?

Yes, indeed.  I fixed that too.

Regards,
Raffael


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: optimized math-read-preprocess-string --]
[-- Type: text/x-patch, Size: 2021 bytes --]

From 68ae1e20ae5eb66cc4899a1e442dae3bb6a054ad Mon Sep 17 00:00:00 2001
From: Raffael Stocker <r.stocker@mnet-mail.de>
Date: Fri, 1 Dec 2023 22:08:24 +0100
Subject: [PATCH] * lisp/calc/calc-aent.el (math-read-preprocess-string): cons
 less (bug#67536)

Use a temp buffer instead of working on a string.  This function is
called by calc-eval, which in turn is called repeatedly when
re-calculating org-mode tables.  This function is one of the main
bottlenecks there.
---
 lisp/calc/calc-aent.el | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..b79765fbc1f 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -550,19 +550,16 @@ math-read-subscripts
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (with-temp-buffer
+    (insert str)
+    (mapc (lambda (rep-pair)
+            (goto-char 0)
+            (while (re-search-forward (car rep-pair) nil t)
+              (replace-match (cadr rep-pair))))
+          `((,(concat "[" math-read-superscripts "]+") "^(\\&)")
+            (,(concat "[" math-read-subscripts "]+") "_(\\&)")
+            ,@math-read-replacement-list))
+    (buffer-substring-no-properties (point-min) (point-max))))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.
-- 
2.43.0


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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-01 21:10           ` Raffael Stocker
@ 2023-12-02  8:03             ` Eli Zaretskii
  2023-12-02 14:56               ` Mattias Engdegård
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2023-12-02  8:03 UTC (permalink / raw)
  To: Raffael Stocker, Mattias Engdegård, Stefan Monnier; +Cc: 67536

> From: Raffael Stocker <r.stocker@mnet-mail.de>
> Cc: 67536@debbugs.gnu.org
> Date: Fri, 01 Dec 2023 22:10:22 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> I use cl-flet and (eval-when-compile (require 'cl-lib)).  I hope that is
> >> ok.
> >
> > I'd prefer a simple internal function, or a lambda.
> 
> I went with the lambda, it is the more concise choice.
> 
> >> +      (buffer-string))))
> >
> > I think buffer-substring-no-properties would be better than
> > buffer-string, as we don't need to copy any text properties, right?
> 
> Yes, indeed.  I fixed that too.

Thanks.

Mattias, any comments, or should I install this?





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-02  8:03             ` Eli Zaretskii
@ 2023-12-02 14:56               ` Mattias Engdegård
  2023-12-02 19:26                 ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-02 14:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 67536, Stefan Monnier, Raffael Stocker

[-- Attachment #1: Type: text/plain, Size: 827 bytes --]

2 dec. 2023 kl. 09.03 skrev Eli Zaretskii <eliz@gnu.org>:

> Mattias, any comments, or should I install this?

Well, the patch doesn't look too unreasonable so installing it would leave us better off than before.

Of course we may want to try to do better if this is really a bottleneck. The big job was to detect and locate the inefficiency -- thank you, Raffael! -- so his efforts were instrumental in any case.

There are minor points that could be addressed: `mapc` is often better replaced with `dolist`; the first position of a buffer is 1, not 0; and perhaps iterating through all elements of math-read-replacement-list isn't ideal.

Here's a variant which computes a single regexp to do the job and should cons a bit less.
Raffael, maybe you could see if this makes a difference in Org table performance.


[-- Attachment #2: math-read-preprocess-string.diff --]
[-- Type: application/octet-stream, Size: 2353 bytes --]

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..4c1c464512a 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -547,22 +547,36 @@ math-read-subscripts
   "₀₁₂₃₄₅₆₇₈₉₊₋₍₎" ; 0123456789+-()
   "A string consisting of the subscripts allowed by Calc.")
 
+(defvar math--read-preprocess-re-cache nil
+  "Cached regexp and tag: (REGEXP REPLACEMENTS SUPERSCRIPTS SUBSCRIPTS)")
+
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (unless (and (eq (nth 1 math--read-preprocess-re-cache)
+                   math-read-replacement-list)
+               (eq (nth 2 math--read-preprocess-re-cache)
+                   math-read-superscripts)
+               (eq (nth 3 math--read-preprocess-re-cache)
+                   math-read-subscripts))
+    ;; Cache invalid, recompute.
+    (setq math--read-preprocess-re-cache
+          (list (rx-to-string
+                 `(or (group (in ,math-read-superscripts))
+                      (group (in ,math-read-subscripts))
+                      (group (or ,@(mapcar #'car math-read-replacement-list))))
+                 t)
+                math-read-replacement-list
+                math-read-superscripts
+                math-read-subscripts)))
+  (replace-regexp-in-string
+   (nth 0 math--read-preprocess-re-cache)
+   (lambda (s)
+     (let ((r (cadr (assoc s math-read-replacement-list))))
+       (cond ((match-beginning 1) (concat "^(" r ")"))
+             ((match-beginning 2) (concat "_(" r ")"))
+             (t r))))
+   str t))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.

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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-02 14:56               ` Mattias Engdegård
@ 2023-12-02 19:26                 ` Raffael Stocker
  2023-12-03 10:43                   ` Mattias Engdegård
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-02 19:26 UTC (permalink / raw)
  To: Mattias Engdegård; +Cc: Eli Zaretskii, Stefan Monnier, 67536


Mattias Engdegård <mattias.engdegard@gmail.com> writes:

> There are minor points that could be addressed: `mapc` is often better
> replaced with `dolist`;

Is this the case? As ‘mapc’ is implemented directly in C and ‘dolist’
falls back to ‘while’, I thought it would be the other way around.  But
of course I never compared them.

> the first position of a buffer is 1, not 0;

Yes, I could have done that one better.  I was to lazy to check...

> and perhaps iterating through all elements of math-read-replacement-list isn't ideal.
> Here's a variant which computes a single regexp to do the job and should cons a bit less.

I thought about the possibility of doing this, but I didn't want to open
that can of worms.  Not iterating definitely sounds right, though.

However, I checked your proposed version and there is an issue with
replacement of sub- and superscripts.  Here are a few test strings I
used on my version, with the comparison strings supplied by the
original:

--8<---------------cut here---------------start------------->8---
(string= (math-read-preprocess-string "±⁷⁽⁽⁽⁽₄₄ds₇⅟⅟l⅛⅛µ3¾⁶⁴₍∞≤")
         "+/-^(7(((()_(44)ds_(7)1:1:l(1:8)(1:8)μ3(3:4)^(64)_(()inf<=")
(string= (math-read-preprocess-string "dsfjlsajflk klfsld flsd fkls fkl jfjls")
         "dsfjlsajflk klfsld flsd fkls fkl jfjls")
(string= (math-read-preprocess-string "") "")
--8<---------------cut here---------------end--------------->8---

The last two tests work fine (as there are no replacements), but for the
first, your function produces this incorrect result:

"+/-^(7)^(()^(()^(()^(()_(4)_(4)ds_(7)1:1:l(1:8)(1:8)μ3(3:4)^(6)^(4)_(()inf<="

> Raffael, maybe you could see if this makes a difference in Org table performance.

I'm looking forward to doing so.

Regards,
Raffael





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-02 19:26                 ` Raffael Stocker
@ 2023-12-03 10:43                   ` Mattias Engdegård
  2023-12-03 11:13                     ` Raffael Stocker
  2023-12-05 18:14                     ` Raffael Stocker
  0 siblings, 2 replies; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-03 10:43 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: Eli Zaretskii, Stefan Monnier, 67536

2 dec. 2023 kl. 20.26 skrev Raffael Stocker <r.stocker@mnet-mail.de>:

> Is this the case? As ‘mapc’ is implemented directly in C and ‘dolist’
> falls back to ‘while’, I thought it would be the other way around.

It's the function calls that are expensive. `dolist` just expands to a loop.
It's even worse if the lambda expression accesses variables outside (which wasn't the case here) because that forces creation of a closure.

> However, I checked your proposed version and there is an issue with
> replacement of sub- and superscripts.

Look at that, I got the semantics wrong. Sorry about that. Here's a new patch.
A lot less pretty this time.

In any case, make sure to include unit tests in your final patch.

The whole problem is compounded by the public variables (math-read-replacement-list etc) that we somehow feel a need to keep unchanged just in case some user modifies them, despite this almost certainly never happens.






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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-03 10:43                   ` Mattias Engdegård
@ 2023-12-03 11:13                     ` Raffael Stocker
  2023-12-03 11:58                       ` Mattias Engdegård
  2023-12-05 18:14                     ` Raffael Stocker
  1 sibling, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-03 11:13 UTC (permalink / raw)
  To: Mattias Engdegård; +Cc: Eli Zaretskii, Stefan Monnier, 67536


Mattias Engdegård <mattias.engdegard@gmail.com> writes:


> Look at that, I got the semantics wrong. Sorry about that. Here's a new patch.

Is it possible you forgot the attachment?





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-03 11:13                     ` Raffael Stocker
@ 2023-12-03 11:58                       ` Mattias Engdegård
  0 siblings, 0 replies; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-03 11:58 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: Eli Zaretskii, Stefan Monnier, 67536

[-- Attachment #1: Type: text/plain, Size: 81 bytes --]

> Is it possible you forgot the attachment?

How dare you suggest such a thing!


[-- Attachment #2: math-read-preprocess-string.diff --]
[-- Type: application/octet-stream, Size: 2503 bytes --]

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..131f31afff7 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -547,22 +547,39 @@ math-read-subscripts
   "₀₁₂₃₄₅₆₇₈₉₊₋₍₎" ; 0123456789+-()
   "A string consisting of the subscripts allowed by Calc.")
 
+(defvar math--read-preprocess-re-cache nil
+  "Cached regexp and tag: (REGEXP REPLACEMENTS SUPERSCRIPTS SUBSCRIPTS)")
+
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (unless (and (eq (nth 1 math--read-preprocess-re-cache)
+                   math-read-replacement-list)
+               (eq (nth 2 math--read-preprocess-re-cache)
+                   math-read-superscripts)
+               (eq (nth 3 math--read-preprocess-re-cache)
+                   math-read-subscripts))
+    ;; Cache invalid, recompute.
+    (setq math--read-preprocess-re-cache
+          (list (rx-to-string
+                 `(or (group (+ (in ,math-read-superscripts)))
+                      (group (+ (in ,math-read-subscripts)))
+                      (group (or ,@(mapcar #'car math-read-replacement-list))))
+                 t)
+                math-read-replacement-list
+                math-read-superscripts
+                math-read-subscripts)))
+  (replace-regexp-in-string
+   (nth 0 math--read-preprocess-re-cache)
+   (lambda (s)
+     (let ((r (mapconcat (lambda (c)
+                           (cadr (assoc (char-to-string c)
+                                        math-read-replacement-list)))
+                         s)))
+       (cond ((match-beginning 1) (concat "^(" r ")"))
+             ((match-beginning 2) (concat "_(" r ")"))
+             (t r))))
+   str t))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.

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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-03 10:43                   ` Mattias Engdegård
  2023-12-03 11:13                     ` Raffael Stocker
@ 2023-12-05 18:14                     ` Raffael Stocker
  2023-12-16  9:40                       ` Eli Zaretskii
  1 sibling, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-05 18:14 UTC (permalink / raw)
  To: Mattias Engdegård; +Cc: Eli Zaretskii, Stefan Monnier, 67536

[-- Attachment #1: Type: text/plain, Size: 2235 bytes --]


Mattias Engdegård <mattias.engdegard@gmail.com> writes:

> Here's a new patch.
> A lot less pretty this time.
>
> In any case, make sure to include unit tests in your final patch.

Ok, here it comes.

I have constructed two org tables for the test (see test-input.org) that
are reasonably long and contain a few not too unreasonable formulas.
The nature of the formulas is not too important for the tests, I just
took them from my original "offending" table.

I also added unit tests for the function, as requested.

One of the tables has no special characters, so
‘math-read-preprocess-string’ just has to walk through without doing any
real work (arguably the most common case).  The other table is full of
replaceable stuff.  I used these two tables to compare the original with
the new version of ‘math-read-preprocess-string’.  The results are in
test-results.org.

As before, I compared ‘gcs-done’ and ‘gc-elapsed’ (with standard
settings for ‘gc-cons-threshold’ and ‘gc-cons-percentage’) with both
versions and tables.  I also instrumented ‘org-table-recalculate’ and
‘math-read-preprocess-string’ using elp.  Then I set ‘gc-cons-threshold’
to a large value and ran the same tests again.  For the latter test, I
only report ‘elp-results’ (as no GC was triggered).

I recalculated every table three times per reported elp result, as it
takes three evaluations for the calculation to converge.

Some interesting results are:

- time spent in GC reduces from 2.11 s to  0.78 s for the second table
  (with replacements)
- elp report of average time decreases from 4.6316224e-3 s to
  3.0294569e-4 s for the second table
- with GC prevented, elp reports avg. time 3.4286452e-4 s vs. 4.9444e-5 s
- elp report of elapsed time for ‘org-table-recalculate’ for three evaluations
  (what the user sees) decreases from 8.36 s to 4.11 s (with standard GC
  settings again)

This is a real win especially for large tables.  Needless to say, I am
very excited about this optimization.

(Just for completeness, I quickly compared my version to Mattias'
version, the latter is about a factor of two faster according to elp.)

Regards,
Raffael


[-- Attachment #2: math-read-preprocess-string patch --]
[-- Type: text/x-patch, Size: 5630 bytes --]

From c5bd32ef197d2236928d2d6764bfcab5cef0ea6d Mon Sep 17 00:00:00 2001
From: Raffael Stocker <r.stocker@mnet-mail.de>
Date: Tue, 5 Dec 2023 20:06:36 +0100
Subject: [PATCH] * lisp/calc/calc-aent.el (math-read-preprocess-string):
 optimize (bug#67536)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This is an optimized version of ‘math-read-preprocess-string’ by
Mattias Engdegård <mattias.engdegard@gmail.com>, with unit tests.

This function is called by calc-eval, which in turn is called
repeatedly when re-calculating org-mode tables.  It was one of the
main bottlenecks there and this optimization addresses this by not
repeatedly allocating new strings while iterating through the
replacement list, but instead working through the argument string only
once, using a "flattened" and cached regexp and only one call to
‘replace-regexp-in-string’.
---
 lisp/calc/calc-aent.el       | 43 +++++++++++++++++++++++++-----------
 test/lisp/calc/calc-tests.el | 27 ++++++++++++++++++++++
 2 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..131f31afff7 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -547,22 +547,39 @@ math-read-subscripts
   "₀₁₂₃₄₅₆₇₈₉₊₋₍₎" ; 0123456789+-()
   "A string consisting of the subscripts allowed by Calc.")
 
+(defvar math--read-preprocess-re-cache nil
+  "Cached regexp and tag: (REGEXP REPLACEMENTS SUPERSCRIPTS SUBSCRIPTS)")
+
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (unless (and (eq (nth 1 math--read-preprocess-re-cache)
+                   math-read-replacement-list)
+               (eq (nth 2 math--read-preprocess-re-cache)
+                   math-read-superscripts)
+               (eq (nth 3 math--read-preprocess-re-cache)
+                   math-read-subscripts))
+    ;; Cache invalid, recompute.
+    (setq math--read-preprocess-re-cache
+          (list (rx-to-string
+                 `(or (group (+ (in ,math-read-superscripts)))
+                      (group (+ (in ,math-read-subscripts)))
+                      (group (or ,@(mapcar #'car math-read-replacement-list))))
+                 t)
+                math-read-replacement-list
+                math-read-superscripts
+                math-read-subscripts)))
+  (replace-regexp-in-string
+   (nth 0 math--read-preprocess-re-cache)
+   (lambda (s)
+     (let ((r (mapconcat (lambda (c)
+                           (cadr (assoc (char-to-string c)
+                                        math-read-replacement-list)))
+                         s)))
+       (cond ((match-beginning 1) (concat "^(" r ")"))
+             ((match-beginning 2) (concat "_(" r ")"))
+             (t r))))
+   str t))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.
diff --git a/test/lisp/calc/calc-tests.el b/test/lisp/calc/calc-tests.el
index 5b11dd950ba..08b5f5575db 100644
--- a/test/lisp/calc/calc-tests.el
+++ b/test/lisp/calc/calc-tests.el
@@ -816,5 +816,32 @@ calc-nth-root
          (x (calc-tests--calc-to-number (math-pow 8 '(frac 1 6)))))
     (should (< (abs (- x (sqrt 2.0))) 1.0e-10))))
 
+(ert-deftest calc-math-read-preprocess-string ()
+  "Test replacement of allowed special Unicode symbols."
+  ;; ... doesn't change an empty string
+  (should (string= "" (math-read-preprocess-string "")))
+  ;; ... doesn't change a string without characters from
+  ;; ‘math-read-replacement-list’
+  (let ((str "don't replace here"))
+    (should (string= str (math-read-preprocess-string str))))
+  ;; ... replaces irrespective of position in input string
+  (should (string= "^(1)" (math-read-preprocess-string "¹")))
+  (should (string= "some^(1)" (math-read-preprocess-string "some¹")))
+  (should (string= "^(1)time" (math-read-preprocess-string "¹time")))
+  (should (string= "some^(1)else" (math-read-preprocess-string "some¹else")))
+  ;; ... replaces every element of ‘math-read-replacement-list’ correctly,
+  ;; in particular combining consecutive super-/subscripts into one
+  ;; exponent/subscript
+  (should (string= (concat "+/-*:-/*inf<=>=<=>=μ(1:4)(1:2)(3:4)(1:3)(2:3)"
+                           "(1:5)(2:5)(3:5)(4:5)(1:6)(5:6)"
+                           "(1:8)(3:8)(5:8)(7:8)1:^(0123456789+-()ni)"
+                           "_(0123456789+-())")
+                   (math-read-preprocess-string (mapconcat #'car
+                                                           math-read-replacement-list
+                                                           ""))))
+  ;; ... signals an error if the argument is not a string
+  (should-error (math-read-preprocess-string nil))
+  (should-error (math-read-preprocess-string 42)))
+
 (provide 'calc-tests)
 ;;; calc-tests.el ends here
-- 
2.43.0


[-- Attachment #3: test-input.org --]
[-- Type: text/x-org, Size: 22272 bytes --]

* Test functions

** GC status with default configuration

   To write GC status into a CSV table in buffer *calc-gc*:
   #+begin_src emacs-lisp :results none
     (defun my-gc-status (orig-fun &rest args)
       (let* ((done-bf gcs-done)
              (elapsed-bf gc-elapsed)
              (res (apply orig-fun args))
              (done-af gcs-done)
              (elapsed-af gc-elapsed))
         (with-current-buffer (get-buffer-create "*calc-gc*")
           (goto-char (point-max))
           (insert (format "%d,%d,%d,%f,%f,%f\n"
                           done-bf done-af (- done-af done-bf)
                           elapsed-bf elapsed-af (- elapsed-af elapsed-bf))))
         res))
   #+end_src

   advise:
   #+begin_src emacs-lisp :results none
     (advice-add 'org-table-recalculate :around #'my-gc-status)
   #+end_src

   stupefy:
   #+begin_src emacs-lisp :results none
     (advice-remove 'org-table-recalculate #'my-gc-status)
   #+end_src

** Instrument with ELP

   instrument:
   #+begin_src emacs-lisp :results none
     (elp-instrument-list '(org-table-recalculate math-read-preprocess-string))
   #+end_src

   restore:
   #+begin_src emacs-lisp :results none
     (elp-restore-all)
   #+end_src

** Preventing GC

   For the performance comparison with elp (gcs-done difference should always
   be zero):
   #+begin_src emacs-lisp :results none
     (defmacro with-gc-unlikely (&rest body)
       (declare (indent 0))
       `(let ((gc-cons-threshold (* 200 gc-cons-threshold)))
          ,@body))

     (defun my-gc-status (orig-fun &rest args)
       (with-gc-unlikely
         (let* ((done-bf gcs-done)
                (elapsed-bf gc-elapsed)
                (res (apply orig-fun args))
                (done-af gcs-done)
                (elapsed-af gc-elapsed))
           (with-current-buffer (get-buffer-create "*calc-gc*")
             (goto-char (point-max))
             (insert (format "%d,%d,%d,%f,%f,%f\n"
                             done-bf done-af (- done-af done-bf)
                             elapsed-bf elapsed-af (- elapsed-af elapsed-bf))))
           res)))
   #+end_src

* Table without replacements

  |   |    a |    b |    c |    d |   e |   f |    g |  h |     i |    j |      k |   l |
  |---+------+------+------+------+-----+-----+------+----+-------+------+--------+-----|
  |   |    1 |  5.0 | 1.00 |  150 | 200 |     |      |    |       |      |        |     |
  |   |    2 |  5.1 | 1.01 |  151 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    3 |  5.2 | 1.02 |  152 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    4 |  5.3 | 1.03 |  153 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    5 |  5.4 | 1.04 |  154 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    6 |  5.5 | 1.05 |  155 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    7 |  5.6 | 1.06 |  156 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    8 |  5.7 | 1.07 |  157 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |    9 |  5.8 | 1.08 |  158 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   10 |  5.9 | 1.09 |  159 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   11 |  6.0 | 1.10 |  160 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   12 |  6.1 | 1.11 |  161 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   13 |  6.2 | 1.12 |  162 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   14 |  6.3 | 1.13 |  163 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   15 |  6.4 | 1.14 |  164 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   16 |  6.5 | 1.15 |  165 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   17 |  6.6 | 1.16 |  166 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   18 |  6.7 | 1.17 |  167 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   19 |  6.8 | 1.18 |  168 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   20 |  6.9 | 1.19 |  169 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   21 |  7.0 | 1.20 |  170 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   22 |  7.1 | 1.21 |  171 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   23 |  7.2 | 1.22 |  172 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   24 |  7.3 | 1.23 |  173 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   25 |  7.4 | 1.24 |  174 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   26 |  7.5 | 1.25 |  175 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   27 |  7.6 | 1.26 |  176 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   28 |  7.7 | 1.27 |  177 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   29 |  7.8 | 1.28 |  178 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   30 |  7.9 | 1.29 |  179 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   31 |  8.0 | 1.30 |  180 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   32 |  8.1 | 1.31 |  181 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   33 |  8.2 | 1.32 |  182 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   34 |  8.3 | 1.33 |  183 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   35 |  8.4 | 1.34 |  184 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   36 |  8.5 | 1.35 |  185 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   37 |  8.6 | 1.36 |  186 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   38 |  8.7 | 1.37 |  187 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   39 |  8.8 | 1.38 |  188 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   40 |  8.9 | 1.39 |  189 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   41 |  9.0 | 1.40 |  190 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   42 |  9.1 | 1.41 |  191 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   43 |  9.2 | 1.42 |  192 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   44 |  9.3 | 1.43 |  193 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   45 |  9.4 | 1.44 |  194 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   46 |  9.5 | 1.45 |  195 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   47 |  9.6 | 1.46 |  196 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   48 |  9.7 | 1.47 |  197 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   49 |  9.8 | 1.48 |  198 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   |   50 |  9.9 | 1.49 |  199 |   1 | 0.1 | 0.01 |  1 |  1.01 |  1.1 |   2.11 |   4 |
  |   | 1275 | 3725 | 6225 | 8725 |  49 | 4.9 | 0.49 | 49 | 49.49 | 53.9 | 103.39 | 196 |
  |   |      |      |      |      |  40 |  4. |  0.4 | 40 |  40.4 |  44. |   84.4 | 208 |
  #+tblfm: @<<<$6..@>>>$9=@@#$-4-@-1$-4::@3$10..@>>>$11=$-4+$-2::@3$12..@>>>$12=$10+$11::@>>$6..@>>$12=vsum(@<<<..@>>>)::@>$6..@>$12=@>>*40/vlen(@<<<..@>>>)


* Table with replacements
  
  |   | a                                        | b    | c                  |    d |                                        e |           f | g |    h |                                        i |          j |            k |        l |
  |---+------------------------------------------+------+--------------------+------+------------------------------------------+-------------+---+------+------------------------------------------+------------+--------------+----------|
  |   | 1²³                                      | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.50 |                                      200 |             |   |      |                                          |            |              |          |
  |   | 2²³                                      | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.51 |                                  8388607 |         1:4 | 0 | 0.01 |                                  8388607 |       0.26 |    8388607.3 |     13:4 |
  |   | 3²³                                      | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.52 |                              94134790219 |        -1:8 | 0 | 0.01 |                              94134790219 |     -0.115 | 94134790000. |     23:8 |
  |   | 4²³                                      | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.53 |                           70274600998837 |         1:4 | 0 | 0.01 |                           70274600998837 |       0.26 | 7.0274601e13 |     13:4 |
  |   | 5²³                                      | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.54 |                        11850560210900461 |         1:4 | 0 | 0.01 |                        11850560210900461 |       0.26 | 1.1850560e16 |     13:4 |
  |   | 6²³                                      | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.55 |                       777809294098524691 |        -3:4 | 0 | 0.01 |                       777809294098524691 |      -0.74 | 7.7780929e17 |      9:4 |
  |   | 7²³                                      | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.56 |                     26579017117027313527 |           0 | 0 | 0.01 |                     26579017117027313527 |       0.01 | 2.6579017e19 |        3 |
  |   | 8²³                                      | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.57 |                    562927063018624735369 |         1:8 | 0 | 0.01 |                    562927063018624735369 |      0.135 | 5.6292706e20 |     25:8 |
  |   | 9²³                                      | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.58 |                   8272642309293795444217 |         1:4 | 0 | 0.01 |                   8272642309293795444217 |       0.26 | 8.2726423e21 |     13:4 |
  |   | 10²³                                     | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.59 |                  91137061880347498904071 |        -1:8 | 0 | 0.01 |                  91137061880347498904071 |     -0.115 | 9.1137062e22 |     23:8 |
  |   | 11²³                                     | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.60 |                 795430243255237372246531 |         1:4 | 0 | 0.01 |                 795430243255237372246531 |       0.26 | 7.9543024e23 |     13:4 |
  |   | 12²³                                     | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.61 |                5729307023693999638873597 |         1:4 | 0 | 0.01 |                5729307023693999638873597 |       0.26 | 5.7293070e24 |     13:4 |
  |   | 13²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.62 |               35129168146463879355925669 |        -3:4 | 0 | 0.01 |               35129168146463879355925669 |      -0.74 | 3.5129168e25 |      9:4 |
  |   | 14²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.63 |              187831787473568379115174747 |           0 | 0 | 0.01 |              187831787473568379115174747 |       0.01 | 1.8783179e26 |        3 |
  |   | 15²³                                     | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.64 |              892688453514900676148638831 |         1:8 | 0 | 0.01 |              892688453514900676148638831 |      0.135 | 8.9268845e26 |     25:8 |
  |   | 16²³                                     | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.65 |             3829486010739638927965637521 |         1:4 | 0 | 0.01 |             3829486010739638927965637521 |       0.26 | 3.8294860e27 |     13:4 |
  |   | 17²³                                     | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.66 |            15015808743718002702962568817 |        -1:8 | 0 | 0.01 |            15015808743718002702962568817 |     -0.115 | 1.5015809e28 |     23:8 |
  |   | 18²³                                     | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.67 |            54380144713162404110759711119 |         1:4 | 0 | 0.01 |            54380144713162404110759711119 |       0.26 | 5.4380145e28 |     13:4 |
  |   | 19²³                                     | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.68 |           183481914331285799334907290427 |         1:4 | 0 | 0.01 |           183481914331285799334907290427 |       0.26 | 1.8348191e29 |     13:4 |
  |   | 20²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.69 |           581031172054692272751773932741 |        -3:4 | 0 | 0.01 |           581031172054692272751773932741 |      -0.74 | 5.8103117e29 |      9:4 |
  |   | 21²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.70 |          1737720075108218291929075869661 |           0 | 0 | 0.01 |          1737720075108218291929075869661 |       0.01 | 1.7377201e30 |        3 |
  |   | 22²³                                     | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.71 |          4934832426904611970797152049187 |         1:8 | 0 | 0.01 |          4934832426904611970797152049187 |      0.135 | 4.9348324e30 |     25:8 |
  |   | 23²³                                     | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.72 |         13369054697835081771628804991719 |         1:4 | 0 | 0.01 |         13369054697835081771628804991719 |       0.26 | 1.3369055e31 |     13:4 |
  |   | 24²³                                     | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.73 |         34691856035580593151023361791257 |        -1:8 | 0 | 0.01 |         34691856035580593151023361791257 |     -0.115 | 3.4691856e31 |     23:8 |
  |   | 25²³                                     | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.74 |         86536223116591531988846458813801 |         1:4 | 0 | 0.01 |         86536223116591531988846458813801 |       0.26 | 8.6536223e31 |     13:4 |
  |   | 26²³                                     | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.75 |        208148597830180538087306455564951 |         1:4 | 0 | 0.01 |        208148597830180538087306455564951 |       0.26 | 2.0814860e32 |     13:4 |
  |   | 27²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.76 |        484128023348879958510326019614707 |        -3:4 | 0 | 0.01 |        484128023348879958510326019614707 |      -0.74 | 4.8412802e32 |      9:4 |
  |   | 28²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.77 |       1091519211706195535082261784467469 |           0 | 0 | 0.01 |       1091519211706195535082261784467469 |       0.01 | 1.0915192e33 |        3 |
  |   | 29²³                                     | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.78 |       2390816337712139701886699259577237 |         1:8 | 0 | 0.01 |       2390816337712139701886699259577237 |      0.135 | 2.3908163e33 |     25:8 |
  |   | 30²³                                     | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.79 |       5097597164950584229259181627260011 |         1:4 | 0 | 0.01 |       5097597164950584229259181627260011 |       0.26 | 5.0975972e33 |     13:4 |
  |   | 31²³                                     | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.80 |      10598993761349280264138724244295391 |        -1:8 | 0 | 0.01 |      10598993761349280264138724244295391 |     -0.115 | 1.0598994e34 |     23:8 |
  |   | 32²³                                     | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.81 |      21525063224229340764105246389465377 |         1:4 | 0 | 0.01 |      21525063224229340764105246389465377 |       0.26 | 2.1525063e34 |     13:4 |
  |   | 33²³                                     | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.82 |      42760274649603301511494764029638369 |         1:4 | 0 | 0.01 |      42760274649603301511494764029638369 |       0.26 | 4.2760275e34 |     13:4 |
  |   | 34²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.83 |      83201458704419485706598664449198367 |        -3:4 | 0 | 0.01 |      83201458704419485706598664449198367 |      -0.74 | 8.3201459e34 |      9:4 |
  |   | 35²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.84 |     158760784408286602815807834041699371 |           0 | 0 | 0.01 |     158760784408286602815807834041699371 |       0.01 | 1.5876078e35 |        3 |
  |   | 36²³                                     | ¼    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.85 |     297412932573705245606943964728832981 |         1:8 | 0 | 0.01 |     297412932573705245606943964728832981 |      0.135 | 2.9741293e35 |     25:8 |
  |   | 37²³                                     | ½    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.86 |     547549339963094416313749615179701197 |         1:4 | 0 | 0.01 |     547549339963094416313749615179701197 |       0.26 | 5.4754934e35 |     13:4 |
  |   | 38²³                                     | ⅜    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.87 |     991608514451644290273448360554554419 |        -1:8 | 0 | 0.01 |     991608514451644290273448360554554419 |     -0.115 | 9.9160851e35 |     23:8 |
  |   | 39²³                                     | ⅝    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.88 |    1768013704441562415313865863052354647 |         1:4 | 0 | 0.01 |    1768013704441562415313865863052354647 |       0.26 | 1.7680137e36 |     13:4 |
  |   | 40²³                                     | ⅞    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.89 |    3106029033705805621429846963330259881 |         1:4 | 0 | 0.01 |    3106029033705805621429846963330259881 |       0.26 | 3.1060290e36 |     13:4 |
  |   | 41²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.90 |    5380469351373086882278320020632149721 |        -3:4 | 0 | 0.01 |    5380469351373086882278320020632149721 |      -0.74 | 5.3804694e36 |      9:4 |
  |   | 42²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.91 |    9196583172440313947144261252213072167 |           0 | 0 | 0.01 |    9196583172440313947144261252213072167 |       0.01 | 9.1965832e36 |        3 |
  |   | 43²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.92 |   15520307789897775154042511507628315619 |           0 | 0 | 0.01 |   15520307789897775154042511507628315619 |       0.01 | 1.5520308e37 |        3 |
  |   | 44²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.93 |   25876066985093668061082244549398146077 |           0 | 0 | 0.01 |   25876066985093668061082244549398146077 |       0.01 | 2.5876067e37 |        3 |
  |   | 45²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.94 |   42644153941059927848679763042442769541 |           0 | 0 | 0.01 |   42644153941059927848679763042442769541 |       0.01 | 4.2644154e37 |        3 |
  |   | 46²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.95 |   69503605249637021781459803541531167611 |           0 | 0 | 0.01 |   69503605249637021781459803541531167611 |       0.01 | 6.9503605e37 |        3 |
  |   | 47²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.96 |  112085784774797397069918106867756478287 |           0 | 0 | 0.01 |  112085784774797397069918106867756478287 |       0.01 | 1.1208578e38 |        3 |
  |   | 48²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.97 |  178930596300122251281501674041276321969 |           0 | 0 | 0.01 |  178930596300122251281501674041276321969 |       0.01 | 1.7893060e38 |        3 |
  |   | 49²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.98 |  282873888982998391468387417871686072657 |           0 | 0 | 0.01 |  282873888982998391468387417871686072657 |       0.01 | 2.8287389e38 |        3 |
  |   | 50²³                                     | ⅛    | 1⁽¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁺¹⁾ | 1.99 |  443044564542626266505505897305435506351 |           0 | 0 | 0.01 |  443044564542626266505505897305435506351 |       0.01 | 4.4304456e38 |        3 |
  |   | 3125129969101606324488851263499533700625 | 73:4 | 6225               | 8725 | 1192092895507812499999999999999999999999 |        -1:8 | 0 | 0.49 | 1192092895507812499999999999999999999999 |      0.365 | 1.1920929e39 |   1175:8 |
  |   |                                          |      |                    |      |  973137057557397959183673469387755102040 | -0.10204082 | 0 |  0.4 |  973137057557397959183673469387755102040 | 0.29795918 | 9.7313706e38 | 15275:98 |
  #+tblfm: @<<<$6..@>>>$9=@@#$-4-@-1$-4::@4$10..@>>>$11=$-4+$-2::@4$12..@>>>$12=$10+$11::@>>$6..@>>$12=vsum(@<<<..@>>>)::@>$6..@>$12=@>>*40/vlen(@<<<..@>>>)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: test-results.org --]
[-- Type: text/x-org, Size: 43288 bytes --]

* Old Version

**  Standard GC settings

*** Table without replacements

    GC status:
    #+NAME: old-gc-default
    | gcs-done (before) | gcs-done (after) | diff | gc-elapsed (before) | gc-elapsed (after)) |     diff |
    |-------------------+------------------+------+---------------------+---------------------+----------|
    |              1054 |             1059 |    5 |          250.503539 |          252.134850 | 1.631311 |
    |              1059 |             1064 |    5 |          252.134850 |          253.747052 | 1.612202 |
    |              1065 |             1070 |    5 |          253.908064 |          255.376975 | 1.468911 |
    |              1073 |             1078 |    5 |          256.266807 |          257.903993 | 1.637186 |
    |              1079 |             1084 |    5 |          258.230357 |          259.607997 | 1.377640 |
    |              1084 |             1089 |    5 |          259.607997 |          261.243876 | 1.635879 |
    |              1093 |             1098 |    5 |          262.296405 |          263.931859 | 1.635454 |
    |              1098 |             1104 |    6 |          263.931859 |          265.753341 | 1.821483 |
    |              1104 |             1109 |    5 |          265.753341 |          267.341256 | 1.587915 |
    |              1113 |             1118 |    5 |          268.496715 |          270.139111 | 1.642396 |
    |              1119 |             1124 |    5 |          270.463732 |          272.096347 | 1.632615 |
    |              1124 |             1130 |    6 |          272.096347 |          273.892863 | 1.796516 |
    |              1133 |             1138 |    5 |          274.866291 |          276.498017 | 1.631726 |
    |              1139 |             1144 |    5 |          276.825122 |          278.438680 | 1.613558 |
    |              1144 |             1150 |    6 |          278.438680 |          280.193824 | 1.755144 |
    |              1153 |             1158 |    5 |          281.167176 |          282.808119 | 1.640944 |
    |              1159 |             1164 |    5 |          283.135641 |          284.769703 | 1.634062 |
    |              1165 |             1170 |    5 |          285.096659 |          286.539210 | 1.442551 |
    |              1172 |             1177 |    5 |          287.194050 |          288.831969 | 1.637918 |
    |              1177 |             1182 |    5 |          288.831969 |          290.465695 | 1.633726 |
    |              1183 |             1188 |    5 |          290.627810 |          292.188706 | 1.560895 |
    |              1190 |             1195 |    5 |          292.843087 |          294.480884 | 1.637797 |
    |              1195 |             1200 |    5 |          294.480884 |          296.120286 | 1.639402 |
    |              1201 |             1206 |    5 |          296.282287 |          297.834479 | 1.552192 |
    |              1208 |             1213 |    5 |          298.487386 |          300.129894 | 1.642508 |
    |              1213 |             1219 |    6 |          300.129894 |          301.934027 | 1.804133 |
    |              1219 |             1224 |    5 |          301.934027 |          303.557198 | 1.623171 |
    |              1226 |             1231 |    5 |          304.047446 |          305.685723 | 1.638277 |
    |              1232 |             1237 |    5 |          306.014809 |          307.648914 | 1.634105 |
    |              1237 |             1242 |    5 |          307.648914 |          309.279031 | 1.630117 |


    |     min |      max |       avg |      stddev |
    |---------+----------+-----------+-------------|
    | 1.37764 | 1.821483 | 1.6277245 | 0.092682838 |
    #+tblfm: $1=vmin(remote(old-gc-default,@<<$6..@>$6))::$2=vmax(remote(old-gc-default,@<<$6..@>$6))::$3=vmean(remote(old-gc-default,@<<$6..@>$6))::$4=vsdev(remote(old-gc-default,@<<$6..@>$6))

    ELP results:

    #+NAME: old-elp-otr
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  6.313133362 | 2.1043777873 |
    | org-table-recalculate |          3 |  6.246987188 | 2.0823290626 |
    | org-table-recalculate |          3 | 6.6551302920 | 2.2183767640 |
    | org-table-recalculate |          3 |  6.674029333 | 2.2246764443 |
    | org-table-recalculate |          3 |  6.598810578 |  2.199603526 |
    | org-table-recalculate |          3 |  6.321018298 | 2.1070060993 |
    | org-table-recalculate |          3 |  6.441169476 |  2.147056492 |
    | org-table-recalculate |          3 |  6.441422936 | 2.1471409786 |
    | org-table-recalculate |          3 |  6.678658607 | 2.2262195356 |
    | org-table-recalculate |          3 |  6.678658607 | 2.2262195356 |


    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 2.0823291 | 2.2262195 | 2.1683006 | 0.057259862 |
    #+tblfm: $1=vmin(remote(old-elp-otr,@<<$4..@>$4))::$2=vmax(remote(old-elp-otr,@<<$4..@>$4))::$3=vmean(remote(old-elp-otr,@<<$4..@>$4))::$4=vsdev(remote(old-elp-otr,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 6.2469872 | 6.6786586 | 6.5049019 | 0.17177959 |
    #+tblfm: $1=vmin(remote(old-elp-otr,@<<$3..@>$3))::$2=vmax(remote(old-elp-otr,@<<$3..@>$3))::$3=vmean(remote(old-elp-otr,@<<$3..@>$3))::$4=vsdev(remote(old-elp-otr,@<<$3..@>$3))

    #+NAME: old-elp-mrps
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1071 | 3.2478232829 | 0.0030325147 |
    | math-read-preprocess-string |       1071 | 4.8031509200 | 0.0044847347 |
    | math-read-preprocess-string |       1071 | 2.9357323269 | 0.0027411132 |
    | math-read-preprocess-string |       1071 | 3.5837934030 | 0.0033462123 |
    | math-read-preprocess-string |       1071 | 2.8837118310 | 0.0026925413 |
    | math-read-preprocess-string |       1071 | 3.2555897690 | 0.0030397663 |
    | math-read-preprocess-string |       1071 | 4.2443911260 | 0.0039630169 |
    | math-read-preprocess-string |       1071 | 3.5876369039 | 0.0033498010 |
    | math-read-preprocess-string |       1071 | 2.9449534269 | 0.0027497230 |
    | math-read-preprocess-string |       1071 | 2.9449534269 | 0.0027497230 |


    |          min |          max |          avg |       stddev |
    |--------------+--------------+--------------+--------------|
    | 2.6925413e-3 | 4.4847347e-3 | 3.2149146e-3 | 5.9643835e-4 |
    #+tblfm: $1=vmin(remote(old-elp-mrps,@<<$4..@>$4))::$2=vmax(remote(old-elp-mrps,@<<$4..@>$4))::$3=vmean(remote(old-elp-mrps,@<<$4..@>$4))::$4=vsdev(remote(old-elp-mrps,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 2.8837118 | 4.8031509 | 3.4431736 | 0.63878545 |
    #+tblfm: $1=vmin(remote(old-elp-mrps,@<<$3..@>$3))::$2=vmax(remote(old-elp-mrps,@<<$3..@>$3))::$3=vmean(remote(old-elp-mrps,@<<$3..@>$3))::$4=vsdev(remote(old-elp-mrps,@<<$3..@>$3))
    

*** Table with replacements

    #+NAME: old-gc-default-rep
    | gcs-done (before) | gcs-done (after) | diff | gc-elapsed (before) | gc-elapsed (after)) |     diff |
    |-------------------+------------------+------+---------------------+---------------------+----------|
    |              1280 |             1288 |    8 |          320.820101 |          323.266063 | 2.445962 |
    |              1288 |             1295 |    7 |          323.266063 |          325.216725 | 1.950662 |
    |              1296 |             1303 |    7 |          325.378476 |          327.348383 | 1.969907 |
    |              1305 |             1312 |    7 |          328.034073 |          330.261592 | 2.227520 |
    |              1312 |             1320 |    8 |          330.261592 |          332.362158 | 2.100566 |
    |              1320 |             1327 |    7 |          332.362158 |          334.321168 | 1.959009 |
    |              1328 |             1335 |    7 |          334.651412 |          336.492029 | 1.840617 |
    |              1336 |             1343 |    7 |          336.715810 |          338.702283 | 1.986473 |
    |              1343 |             1350 |    7 |          338.702283 |          340.473741 | 1.771458 |
    |              1351 |             1359 |    8 |          340.636036 |          343.044857 | 2.408820 |
    |              1359 |             1366 |    7 |          343.044857 |          344.975912 | 1.931055 |
    |              1367 |             1374 |    7 |          345.305170 |          347.026135 | 1.720965 |
    |              1422 |             1429 |    7 |          360.910961 |          363.195230 | 2.284269 |
    |              1430 |             1437 |    7 |          363.358932 |          365.613196 | 2.254264 |
    |              1437 |             1445 |    8 |          365.613196 |          368.061331 | 2.448135 |
    |              1446 |             1453 |    7 |          368.392605 |          370.633835 | 2.241230 |
    |              1453 |             1460 |    7 |          370.633835 |          372.695029 | 2.061194 |
    |              1461 |             1468 |    7 |          372.856827 |          374.823342 | 1.966515 |
    |              1469 |             1476 |    7 |          375.154456 |          377.442675 | 2.288219 |
    |              1477 |             1484 |    7 |          377.606173 |          379.756170 | 2.149997 |
    |              1484 |             1491 |    7 |          379.756170 |          381.722527 | 1.966357 |
    |              1492 |             1500 |    8 |          381.886163 |          384.337690 | 2.451527 |
    |              1500 |             1507 |    7 |          384.337690 |          386.335021 | 1.997332 |
    |              1507 |             1515 |    8 |          386.335021 |          388.471281 | 2.136260 |
    |              1517 |             1524 |    7 |          389.125091 |          391.294374 | 2.169283 |
    |              1524 |             1531 |    7 |          391.294374 |          393.257728 | 1.963355 |
    |              1532 |             1539 |    7 |          393.420813 |          395.501185 | 2.080372 |
    |              1540 |             1547 |    7 |          395.832669 |          398.100091 | 2.267421 |
    |              1547 |             1554 |    7 |          398.100091 |          400.232270 | 2.132180 |
    |              1555 |             1562 |    7 |          400.394950 |          402.530697 | 2.135748 |


    |      min |      max |       avg |     stddev |
    |----------+----------+-----------+------------|
    | 1.720965 | 2.451527 | 2.1102224 | 0.19575167 |
    #+tblfm: $1=vmin(remote(old-gc-default-rep,@<<$6..@>$6))::$2=vmax(remote(old-gc-default-rep,@<<$6..@>$6))::$3=vmean(remote(old-gc-default-rep,@<<$6..@>$6))::$4=vsdev(remote(old-gc-default-rep,@<<$6..@>$6))

    ELP results:

    #+NAME: old-elp-otr-rep
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  8.386124891 | 2.7953749636 |
    | org-table-recalculate |          3 |   8.31112995 | 2.7703766499 |
    | org-table-recalculate |          3 |  7.615444065 |  2.538481355 |
    | org-table-recalculate |          3 |  8.047824394 | 2.6826081313 |
    | org-table-recalculate |          3 | 9.0429742769 | 3.0143247589 |
    | org-table-recalculate |          3 |  8.293004052 |  2.764334684 |
    | org-table-recalculate |          3 |  8.454038725 | 2.8180129083 |
    | org-table-recalculate |          3 |  8.598015534 |  2.866005178 |
    | org-table-recalculate |          3 | 8.2478522599 | 2.7492840866 |
    | org-table-recalculate |          3 | 8.5751938890 | 2.8583979630 |


    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 2.5384814 | 3.0143248 | 2.7857201 | 0.12423245 |
    #+tblfm: $1=vmin(remote(old-elp-otr-rep,@<<$4..@>$4))::$2=vmax(remote(old-elp-otr-rep,@<<$4..@>$4))::$3=vmean(remote(old-elp-otr-rep,@<<$4..@>$4))::$4=vsdev(remote(old-elp-otr-rep,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 7.6154441 | 9.0429743 | 8.3571602 | 0.37269735 |
    #+tblfm: $1=vmin(remote(old-elp-otr-rep,@<<$3..@>$3))::$2=vmax(remote(old-elp-otr-rep,@<<$3..@>$3))::$3=vmean(remote(old-elp-otr-rep,@<<$3..@>$3))::$4=vsdev(remote(old-elp-otr-rep,@<<$3..@>$3))


    #+NAME: old-elp-mrps-rep
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1062 | 5.3424330849 | 0.0050305396 |
    | math-read-preprocess-string |       1062 |  5.007848726 | 0.0047154884 |
    | math-read-preprocess-string |       1062 | 4.3856793110 | 0.0041296415 |
    | math-read-preprocess-string |       1062 | 5.1801638940 | 0.0048777437 |
    | math-read-preprocess-string |       1062 | 5.0423656429 | 0.0047479902 |
    | math-read-preprocess-string |       1062 | 4.9785316350 | 0.0046878828 |
    | math-read-preprocess-string |       1062 | 5.0846951129 | 0.0047878485 |
    | math-read-preprocess-string |       1062 | 5.2302427329 | 0.0049248989 |
    | math-read-preprocess-string |       1062 | 4.5067303730 | 0.0042436255 |
    | math-read-preprocess-string |       1062 | 4.4291400419 | 0.0041705650 |


    |          min |          max |          avg |       stddev |
    |--------------+--------------+--------------+--------------|
    | 4.1296415e-3 | 5.0305396e-3 | 4.6316224e-3 | 3.2812072e-4 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-rep,@<<$4..@>$4))::$2=vmax(remote(old-elp-mrps-rep,@<<$4..@>$4))::$3=vmean(remote(old-elp-mrps-rep,@<<$4..@>$4))::$4=vsdev(remote(old-elp-mrps-rep,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 4.3856793 | 5.3424331 | 4.9187831 | 0.34846421 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-rep,@<<$3..@>$3))::$2=vmax(remote(old-elp-mrps-rep,@<<$3..@>$3))::$3=vmean(remote(old-elp-mrps-rep,@<<$3..@>$3))::$4=vsdev(remote(old-elp-mrps-rep,@<<$3..@>$3))
    

** GC prevented

*** Table without replacements

    ELP results:

    #+NAME: old-elp-otr-gcp
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 | 3.2403264490 | 1.0801088163 |
    | org-table-recalculate |          3 |  3.355133199 |  1.118377733 |
    | org-table-recalculate |          3 | 3.3670167070 | 1.1223389023 |
    | org-table-recalculate |          3 | 3.3725147580 | 1.1241715860 |
    | org-table-recalculate |          3 |  3.362646752 | 1.1208822506 |
    | org-table-recalculate |          3 |  3.341763135 | 1.1139210449 |
    | org-table-recalculate |          3 |  3.481111953 |  1.160370651 |
    | org-table-recalculate |          3 |    3.4572762 |    1.1524254 |
    | org-table-recalculate |          3 | 3.3516910289 |  1.117230343 |
    | org-table-recalculate |          3 | 3.4367006460 |  1.145566882 |


    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 1.0801088 | 1.1603707 | 1.1255394 | 0.022828419 |
    #+tblfm: $1=vmin(remote(old-elp-otr-gcp,@<<$4..@>$4))::$2=vmax(remote(old-elp-otr-gcp,@<<$4..@>$4))::$3=vmean(remote(old-elp-otr-gcp,@<<$4..@>$4))::$4=vsdev(remote(old-elp-otr-gcp,@<<$4..@>$4))

    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 3.2403264 | 3.4811120 | 3.3766181 | 0.068485256 |
    #+tblfm: $1=vmin(remote(old-elp-otr-gcp,@<<$3..@>$3))::$2=vmax(remote(old-elp-otr-gcp,@<<$3..@>$3))::$3=vmean(remote(old-elp-otr-gcp,@<<$3..@>$3))::$4=vsdev(remote(old-elp-otr-gcp,@<<$3..@>$3))

    #+NAME: old-elp-mrps-gcp
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1071 | 0.2493200720 | 0.0002327918 |
    | math-read-preprocess-string |       1071 | 0.2506797709 | 0.0002340614 |
    | math-read-preprocess-string |       1071 | 0.2526552609 | 0.0002359059 |
    | math-read-preprocess-string |       1071 | 0.2530469949 | 0.0002362717 |
    | math-read-preprocess-string |       1071 | 0.2542395049 | 0.0002373851 |
    | math-read-preprocess-string |       1071 | 0.2536397500 | 0.0002368251 |
    | math-read-preprocess-string |       1071 | 0.2555863690 | 0.0002386427 |
    | math-read-preprocess-string |       1071 | 0.2541568160 | 0.0002373079 |
    | math-read-preprocess-string |       1071 | 0.2546775069 | 0.0002377941 |
    | math-read-preprocess-string |       1071 |  0.255253958 | 0.0002383323 |


    |         min |         max |         avg |       stddev |
    |-------------+-------------+-------------+--------------|
    | 2.327918e-4 | 2.386427e-4 | 2.365318e-4 | 1.8636123e-6 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-gcp,@<<$4..@>$4))::$2=vmax(remote(old-elp-mrps-gcp,@<<$4..@>$4))::$3=vmean(remote(old-elp-mrps-gcp,@<<$4..@>$4))::$4=vsdev(remote(old-elp-mrps-gcp,@<<$4..@>$4))

    |        min |        max |        avg |       stddev |
    |------------+------------+------------+--------------|
    | 0.24932007 | 0.25558637 | 0.25332560 | 1.9959338e-3 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-gcp,@<<$3..@>$3))::$2=vmax(remote(old-elp-mrps-gcp,@<<$3..@>$3))::$3=vmean(remote(old-elp-mrps-gcp,@<<$3..@>$3))::$4=vsdev(remote(old-elp-mrps-gcp,@<<$3..@>$3))


*** Table with replacements

    #+NAME: old-elp-otr-gcp-rep
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 | 4.6323348929 | 1.5441116309 |
    | org-table-recalculate |          3 |  4.415962572 |  1.471987524 |
    | org-table-recalculate |          3 |  4.452308577 |  1.484102859 |
    | org-table-recalculate |          3 |  4.426317972 | 1.4754393239 |
    | org-table-recalculate |          3 |  4.458149586 |  1.486049862 |
    | org-table-recalculate |          3 |  4.513410515 | 1.5044701716 |
    | org-table-recalculate |          3 |  4.545595521 |  1.515198507 |
    | org-table-recalculate |          3 |  4.575623074 | 1.5252076913 |
    | org-table-recalculate |          3 |  4.498293244 | 1.4994310813 |
    | org-table-recalculate |          3 |  4.570841693 | 1.5236138976 |


    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 1.4719875 | 1.5441116 | 1.5029613 | 0.023892680 |
    #+tblfm: $1=vmin(remote(old-elp-otr-gcp-rep,@<<$4..@>$4))::$2=vmax(remote(old-elp-otr-gcp-rep,@<<$4..@>$4))::$3=vmean(remote(old-elp-otr-gcp-rep,@<<$4..@>$4))::$4=vsdev(remote(old-elp-otr-gcp-rep,@<<$4..@>$4))

    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 4.4159626 | 4.6323349 | 4.5088838 | 0.071678039 |
    #+tblfm: $1=vmin(remote(old-elp-otr-gcp-rep,@<<$3..@>$3))::$2=vmax(remote(old-elp-otr-gcp-rep,@<<$3..@>$3))::$3=vmean(remote(old-elp-otr-gcp-rep,@<<$3..@>$3))::$4=vsdev(remote(old-elp-otr-gcp-rep,@<<$3..@>$3))

    #+NAME: old-elp-mrps-gcp-rep
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1062 | 0.3624429339 | 0.0003412833 |
    | math-read-preprocess-string |       1062 | 0.3620876769 | 0.0003409488 |
    | math-read-preprocess-string |       1062 | 0.3638540500 | 0.0003426120 |
    | math-read-preprocess-string |       1062 | 0.3637396369 | 0.0003425043 |
    | math-read-preprocess-string |       1062 | 0.3637937650 | 0.0003425553 |
    | math-read-preprocess-string |       1062 | 0.3643354190 | 0.0003430653 |
    | math-read-preprocess-string |       1062 | 0.3652952959 | 0.0003439692 |
    | math-read-preprocess-string |       1062 | 0.3657365130 | 0.0003443846 |
    | math-read-preprocess-string |       1062 | 0.3646950459 | 0.0003434039 |
    | math-read-preprocess-string |       1062 | 0.3652415269 | 0.0003439185 |


    |         min |         max |          avg |       stddev |
    |-------------+-------------+--------------+--------------|
    | 3.409488e-4 | 3.443846e-4 | 3.4286452e-4 | 1.1270640e-6 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-gcp-rep,@<<$4..@>$4))::$2=vmax(remote(old-elp-mrps-gcp-rep,@<<$4..@>$4))::$3=vmean(remote(old-elp-mrps-gcp-rep,@<<$4..@>$4))::$4=vsdev(remote(old-elp-mrps-gcp-rep,@<<$4..@>$4))

    |        min |        max |        avg |       stddev |
    |------------+------------+------------+--------------|
    | 0.36208768 | 0.36573651 | 0.36412219 | 1.1969413e-3 |
    #+tblfm: $1=vmin(remote(old-elp-mrps-gcp-rep,@<<$3..@>$3))::$2=vmax(remote(old-elp-mrps-gcp-rep,@<<$3..@>$3))::$3=vmean(remote(old-elp-mrps-gcp-rep,@<<$3..@>$3))::$4=vsdev(remote(old-elp-mrps-gcp-rep,@<<$3..@>$3))


* New version

** Standard GC settings

*** Table without replacements

    #+NAME: new-gc-default
    | gcs-done (before) | gcs-done (after) | diff | gc-elapsed (before) | gc-elapsed (after)) |     diff |
    |-------------------+------------------+------+---------------------+---------------------+----------|
    |              1762 |             1765 |    3 |          500.857333 |          501.867029 | 1.009696 |
    |              1765 |             1767 |    2 |          501.867029 |          502.528777 | 0.661748 |
    |              1768 |             1770 |    2 |          502.858457 |          503.519755 | 0.661298 |
    |              1772 |             1774 |    2 |          504.182249 |          504.860215 | 0.677966 |
    |              1774 |             1777 |    3 |          504.860215 |          505.853030 | 0.992815 |
    |              1778 |             1780 |    2 |          506.182351 |          506.846879 | 0.664528 |
    |              1782 |             1785 |    3 |          507.510026 |          508.501020 | 0.990994 |
    |              1785 |             1787 |    2 |          508.501020 |          509.164213 | 0.663193 |
    |              1788 |             1790 |    2 |          509.494431 |          510.155896 | 0.661465 |
    |              1792 |             1794 |    2 |          510.819410 |          511.482648 | 0.663238 |
    |              1794 |             1797 |    3 |          511.482648 |          512.474957 | 0.992309 |
    |              1797 |             1799 |    2 |          512.474957 |          513.135728 | 0.660771 |
    |              1801 |             1803 |    2 |          513.709183 |          514.372867 | 0.663683 |
    |              1803 |             1806 |    3 |          514.372867 |          515.288420 | 0.915553 |
    |              1806 |             1808 |    2 |          515.288420 |          515.949551 | 0.661131 |
    |              1810 |             1812 |    2 |          516.611724 |          517.271720 | 0.659996 |
    |              1813 |             1815 |    2 |          517.602450 |          518.265565 | 0.663115 |
    |              1815 |             1817 |    2 |          518.265565 |          518.927351 | 0.661786 |
    |              1819 |             1821 |    2 |          519.501519 |          520.162033 | 0.660514 |
    |              1822 |             1824 |    2 |          520.492218 |          521.155180 | 0.662963 |
    |              1826 |             1828 |    2 |          521.814697 |          522.480184 | 0.665487 |
    |              1829 |             1831 |    2 |          522.813010 |          523.474466 | 0.661456 |
    |              1832 |             1834 |    2 |          523.806391 |          524.467553 | 0.661163 |
    |              1835 |             1837 |    2 |          524.713867 |          525.379362 | 0.665494 |
    |              1839 |             1841 |    2 |          526.041673 |          526.702274 | 0.660601 |
    |              1841 |             1843 |    2 |          526.702274 |          527.363186 | 0.660912 |
    |              1844 |             1846 |    2 |          527.693024 |          528.353593 | 0.660569 |
    |              1848 |             1850 |    2 |          529.014078 |          529.675499 | 0.661421 |
    |              1851 |             1853 |    2 |          529.920856 |          530.582954 | 0.662098 |
    |              1853 |             1855 |    2 |          530.582954 |          531.248341 | 0.665387 |
    |              1858 |             1858 |    0 |          532.155097 |          532.155097 | 0.000000 |
    |              1861 |             1861 |    0 |          533.088355 |          533.088355 | 0.000000 |


    | min |      max |        avg |     stddev |
    |-----+----------+------------+------------|
    |  0. | 1.009696 | 0.67104219 | 0.21145837 |
    #+tblfm: $1=vmin(remote(new-gc-default,@<<$6..@>$6))::$2=vmax(remote(new-gc-default,@<<$6..@>$6))::$3=vmean(remote(new-gc-default,@<<$6..@>$6))::$4=vsdev(remote(new-gc-default,@<<$6..@>$6))


    ELP results:

    #+NAME: new-elp-otr
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  3.647963828 | 1.2159879426 |
    | org-table-recalculate |          3 |  3.650498402 | 1.2168328006 |
    | org-table-recalculate |          3 |  3.631013093 | 1.2103376976 |
    | org-table-recalculate |          3 |  3.630224191 | 1.2100747303 |
    | org-table-recalculate |          3 |  3.564292429 | 1.1880974763 |
    | org-table-recalculate |          3 |  3.308181369 |  1.102727123 |
    | org-table-recalculate |          3 | 3.3063494049 | 1.1021164683 |
    | org-table-recalculate |          3 | 3.3000922879 | 1.1000307626 |
    | org-table-recalculate |          3 |  3.297214875 | 1.0990716249 |
    | org-table-recalculate |          3 |  3.301936163 | 1.1006453876 |


    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 1.0990716 | 1.2168328 | 1.1545922 | 0.057120960 |
    #+tblfm: $1=vmin(remote(new-elp-otr,@<<$4..@>$4))::$2=vmax(remote(new-elp-otr,@<<$4..@>$4))::$3=vmean(remote(new-elp-otr,@<<$4..@>$4))::$4=vsdev(remote(new-elp-otr,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 3.2972149 | 3.6504984 | 3.4637766 | 0.17136288 |
    #+tblfm: $1=vmin(remote(new-elp-otr,@<<$3..@>$3))::$2=vmax(remote(new-elp-otr,@<<$3..@>$3))::$3=vmean(remote(new-elp-otr,@<<$3..@>$3))::$4=vsdev(remote(new-elp-otr,@<<$3..@>$3))

    #+NAME: new-elp-mrps
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1071 | 0.0359088070 |    3.352e-05 |
    | math-read-preprocess-string |       1071 | 0.3684005519 | 0.0003439781 |
    | math-read-preprocess-string |       1071 | 0.3658023279 | 0.0003415521 |
    | math-read-preprocess-string |       1071 | 0.0354963120 |    3.314e-05 |
    | math-read-preprocess-string |       1071 | 0.0361226520 |    3.372e-05 |
    | math-read-preprocess-string |       1071 | 0.6992056110 | 0.0006528530 |
    | math-read-preprocess-string |       1071 | 0.0361133740 |    3.371e-05 |
    | math-read-preprocess-string |       1071 | 0.3664842619 | 0.0003421888 |
    | math-read-preprocess-string |       1071 | 0.0355913820 |    3.323e-05 |
    | math-read-preprocess-string |       1071 | 0.0358934579 |    3.351e-05 |


    |      min |        max |         avg |       stddev |
    |----------+------------+-------------+--------------|
    | 3.314e-5 | 6.52853e-4 | 1.881402e-4 | 2.1884648e-4 |
    #+tblfm: $1=vmin(remote(new-elp-mrps,@<<$4..@>$4))::$2=vmax(remote(new-elp-mrps,@<<$4..@>$4))::$3=vmean(remote(new-elp-mrps,@<<$4..@>$4))::$4=vsdev(remote(new-elp-mrps,@<<$4..@>$4))

    |         min |        max |        avg |     stddev |
    |-------------+------------+------------+------------|
    | 0.035496312 | 0.69920561 | 0.20150187 | 0.23438169 |
    #+tblfm: $1=vmin(remote(new-elp-mrps,@<<$3..@>$3))::$2=vmax(remote(new-elp-mrps,@<<$3..@>$3))::$3=vmean(remote(new-elp-mrps,@<<$3..@>$3))::$4=vsdev(remote(new-elp-mrps,@<<$3..@>$3))


*** Table with replacements

    #+NAME: new-gc-default-rep
    | gcs-done (before) | gcs-done (after) | diff | gc-elapsed (before) | gc-elapsed (after)) |     diff |
    |-------------------+------------------+------+---------------------+---------------------+----------|
    |              1876 |             1878 |    2 |          537.611898 |          538.275925 | 0.664028 |
    |              1879 |             1881 |    2 |          538.563095 |          539.228150 | 0.665055 |
    |              1883 |             1885 |    2 |          539.893418 |          540.562692 | 0.669274 |
    |              1886 |             1889 |    3 |          540.900052 |          541.897165 | 0.997113 |
    |              1889 |             1892 |    3 |          541.897165 |          542.896032 | 0.998867 |
    |              1892 |             1895 |    3 |          542.896032 |          543.886817 | 0.990785 |
    |              1896 |             1899 |    3 |          544.218485 |          545.211749 | 0.993265 |
    |              1900 |             1902 |    2 |          545.455264 |          546.119421 | 0.664157 |
    |              1903 |             1905 |    2 |          546.364589 |          547.031535 | 0.666946 |
    |              1906 |             1909 |    3 |          547.278365 |          548.272865 | 0.994500 |
    |              1909 |             1912 |    3 |          548.272865 |          549.274114 | 1.001249 |
    |              1912 |             1915 |    3 |          549.274114 |          550.262985 | 0.988871 |
    |              1916 |             1919 |    3 |          550.509902 |          551.511680 | 1.001778 |
    |              1919 |             1921 |    2 |          551.511680 |          552.180745 | 0.669065 |
    |              1922 |             1924 |    2 |          552.427591 |          553.094352 | 0.666761 |
    |              1926 |             1928 |    2 |          553.673631 |          554.342719 | 0.669088 |
    |              1929 |             1932 |    3 |          554.678384 |          555.680999 | 1.002615 |
    |              1932 |             1935 |    3 |          555.680999 |          556.685500 | 1.004501 |
    |              1936 |             1939 |    3 |          557.019569 |          558.012522 | 0.992953 |
    |              1939 |             1941 |    2 |          558.012522 |          558.676676 | 0.664154 |
    |              1942 |             1944 |    2 |          558.932476 |          559.596460 | 0.663984 |
    |              1946 |             1948 |    2 |          560.173468 |          560.840249 | 0.666782 |
    |              1949 |             1951 |    2 |          561.105505 |          561.773733 | 0.668227 |
    |              1952 |             1954 |    2 |          562.020766 |          562.689543 | 0.668777 |
    |              1955 |             1958 |    3 |          563.022710 |          564.024487 | 1.001777 |
    |              1958 |             1961 |    3 |          564.024487 |          565.028165 | 1.003678 |
    |              1961 |             1964 |    3 |          565.028165 |          566.023460 | 0.995295 |
    |              1965 |             1968 |    3 |          566.352768 |          567.351071 | 0.998304 |
    |              1968 |             1970 |    2 |          567.351071 |          568.015533 | 0.664461 |
    |              1971 |             1973 |    2 |          568.260103 |          568.922794 | 0.662691 |
    |              1976 |             1976 |    0 |          569.851582 |          569.851582 | 0.000000 |
    |              1978 |             1978 |    0 |          570.511303 |          570.511303 | 0.000000 |


    | min |      max |        avg |     stddev |
    |-----+----------+------------+------------|
    |  0. | 1.004501 | 0.77996878 | 0.26165024 |
    #+tblfm: $1=vmin(remote(new-gc-default-rep,@<<$6..@>$6))::$2=vmax(remote(new-gc-default-rep,@<<$6..@>$6))::$3=vmean(remote(new-gc-default-rep,@<<$6..@>$6))::$4=vsdev(remote(new-gc-default-rep,@<<$6..@>$6))


    ELP results:

    #+NAME: new-elp-otr-rep
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  3.602455605 |  1.200818535 |
    | org-table-recalculate |          3 |  4.601049409 | 1.5336831363 |
    | org-table-recalculate |          3 |  3.934937821 | 1.3116459403 |
    | org-table-recalculate |          3 |  4.594367169 | 1.5314557229 |
    | org-table-recalculate |          3 | 3.9486397760 | 1.3162132586 |
    | org-table-recalculate |          3 | 4.2893050450 | 1.4297683483 |
    | org-table-recalculate |          3 |  3.934609579 | 1.3115365263 |
    | org-table-recalculate |          3 |  3.611487882 |  1.203829294 |
    | org-table-recalculate |          3 |  4.611609242 | 1.5372030806 |
    | org-table-recalculate |          3 | 3.9416291149 | 1.3138763716 |


    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 1.2008185 | 1.5372031 | 1.3690030 | 0.13058859 |
    #+tblfm: $1=vmin(remote(new-elp-otr-rep,@<<$4..@>$4))::$2=vmax(remote(new-elp-otr-rep,@<<$4..@>$4))::$3=vmean(remote(new-elp-otr-rep,@<<$4..@>$4))::$4=vsdev(remote(new-elp-otr-rep,@<<$4..@>$4))

    |       min |       max |       avg |     stddev |
    |-----------+-----------+-----------+------------|
    | 3.6024556 | 4.6116092 | 4.1070091 | 0.39176578 |
    #+tblfm: $1=vmin(remote(new-elp-otr-rep,@<<$3..@>$3))::$2=vmax(remote(new-elp-otr-rep,@<<$3..@>$3))::$3=vmean(remote(new-elp-otr-rep,@<<$3..@>$3))::$4=vsdev(remote(new-elp-otr-rep,@<<$3..@>$3))

    #+NAME: new-elp-mrps-rep
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1062 | 0.3885816630 | 0.0003658961 |
    | math-read-preprocess-string |       1062 | 0.3892037710 | 0.0003664818 |
    | math-read-preprocess-string |       1062 | 0.7196623169 | 0.0006776481 |
    | math-read-preprocess-string |       1062 |  0.055396946 |    5.216e-05 |
    | math-read-preprocess-string |       1062 | 0.3883244280 | 0.0003656538 |
    | math-read-preprocess-string |       1062 | 0.3913531000 | 0.0003685057 |
    | math-read-preprocess-string |       1062 | 0.0541917650 |    5.102e-05 |
    | math-read-preprocess-string |       1062 | 0.0549385280 |    5.173e-05 |
    | math-read-preprocess-string |       1062 | 0.3868773030 | 0.0003642912 |
    | math-read-preprocess-string |       1062 | 0.3887666309 | 0.0003660702 |


    |      min |         max |          avg |       stddev |
    |----------+-------------+--------------+--------------|
    | 5.102e-5 | 6.776481e-4 | 3.0294569e-4 | 1.9828409e-4 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-rep,@<<$4..@>$4))::$2=vmax(remote(new-elp-mrps-rep,@<<$4..@>$4))::$3=vmean(remote(new-elp-mrps-rep,@<<$4..@>$4))::$4=vsdev(remote(new-elp-mrps-rep,@<<$4..@>$4))

    |         min |        max |        avg |     stddev |
    |-------------+------------+------------+------------|
    | 0.054191765 | 0.71966232 | 0.32172965 | 0.21057592 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-rep,@<<$3..@>$3))::$2=vmax(remote(new-elp-mrps-rep,@<<$3..@>$3))::$3=vmean(remote(new-elp-mrps-rep,@<<$3..@>$3))::$4=vsdev(remote(new-elp-mrps-rep,@<<$3..@>$3))


** GC prevented

*** Table without replacements

    ELP results:

    #+NAME: new-elp-otr-gcp
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  2.493044023 | 0.8310146743 |
    | org-table-recalculate |          3 |  2.512677176 | 0.8375590586 |
    | org-table-recalculate |          3 |  2.538733559 | 0.8462445196 |
    | org-table-recalculate |          3 | 2.5243518540 | 0.8414506180 |
    | org-table-recalculate |          3 |  2.507517527 | 0.8358391756 |
    | org-table-recalculate |          3 |  2.569699372 | 0.8565664573 |
    | org-table-recalculate |          3 |  2.468403993 |  0.822801331 |
    | org-table-recalculate |          3 |  2.512327016 | 0.8374423386 |
    | org-table-recalculate |          3 |  2.478930529 | 0.8263101763 |
    | org-table-recalculate |          3 |  2.537201331 |  0.845733777 |


    |        min |        max |        avg |      stddev |
    |------------+------------+------------+-------------|
    | 0.82280133 | 0.85656646 | 0.83809621 | 0.010032900 |
    #+tblfm: $1=vmin(remote(new-elp-otr-gcp,@<<$4..@>$4))::$2=vmax(remote(new-elp-otr-gcp,@<<$4..@>$4))::$3=vmean(remote(new-elp-otr-gcp,@<<$4..@>$4))::$4=vsdev(remote(new-elp-otr-gcp,@<<$4..@>$4))

    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 2.4684040 | 2.5696994 | 2.5142886 | 0.030098701 |
    #+tblfm: $1=vmin(remote(new-elp-otr-gcp,@<<$3..@>$3))::$2=vmax(remote(new-elp-otr-gcp,@<<$3..@>$3))::$3=vmean(remote(new-elp-otr-gcp,@<<$3..@>$3))::$4=vsdev(remote(new-elp-otr-gcp,@<<$3..@>$3))


    #+NAME: new-elp-mrps-gcp
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1071 | 0.0347882809 |    3.248e-05 |
    | math-read-preprocess-string |       1071 | 0.0353020950 |    3.296e-05 |
    | math-read-preprocess-string |       1071 | 0.0348920780 |    3.257e-05 |
    | math-read-preprocess-string |       1071 | 0.0345307570 |    3.224e-05 |
    | math-read-preprocess-string |       1071 | 0.0345681400 |    3.227e-05 |
    | math-read-preprocess-string |       1071 | 0.0347633370 |    3.245e-05 |
    | math-read-preprocess-string |       1071 | 0.0349174600 |    3.260e-05 |
    | math-read-preprocess-string |       1071 |  0.034960621 |    3.264e-05 |
    | math-read-preprocess-string |       1071 | 0.0350518849 |    3.272e-05 |
    | math-read-preprocess-string |       1071 | 0.0347729759 |    3.246e-05 |


    |      min |      max |       avg |       stddev |
    |----------+----------+-----------+--------------|
    | 3.224e-5 | 3.296e-5 | 3.2539e-5 | 2.1194601e-7 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-gcp,@<<$4..@>$4))::$2=vmax(remote(new-elp-mrps-gcp,@<<$4..@>$4))::$3=vmean(remote(new-elp-mrps-gcp,@<<$4..@>$4))::$4=vsdev(remote(new-elp-mrps-gcp,@<<$4..@>$4))

    |         min |         max |         avg |       stddev |
    |-------------+-------------+-------------+--------------|
    | 0.034530757 | 0.035302095 | 0.034854763 | 2.2659130e-4 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-gcp,@<<$3..@>$3))::$2=vmax(remote(new-elp-mrps-gcp,@<<$3..@>$3))::$3=vmean(remote(new-elp-mrps-gcp,@<<$3..@>$3))::$4=vsdev(remote(new-elp-mrps-gcp,@<<$3..@>$3))
    

*** Table with replacements

    ELP results:

    #+NAME: new-elp-otr-gcp-rep
    | Function Name         | Call Count | Elapsed Time | Average Time |
    |-----------------------+------------+--------------+--------------|
    | org-table-recalculate |          3 |  2.927262049 | 0.9757540163 |
    | org-table-recalculate |          3 |   2.97768807 | 0.9925626900 |
    | org-table-recalculate |          3 |  2.875336418 | 0.9584454726 |
    | org-table-recalculate |          3 | 2.9480709679 | 0.9826903226 |
    | org-table-recalculate |          3 |   2.95586239 | 0.9852874633 |
    | org-table-recalculate |          3 |  2.984782702 | 0.9949275673 |
    | org-table-recalculate |          3 | 2.9521534359 | 0.9840511453 |
    | org-table-recalculate |          3 |  2.962699571 | 0.9875665236 |
    | org-table-recalculate |          3 |  2.951152968 |  0.983717656 |
    | org-table-recalculate |          3 | 2.9822409910 | 0.9940803303 |


    |        min |        max |        avg |      stddev |
    |------------+------------+------------+-------------|
    | 0.95844547 | 0.99492757 | 0.98390832 | 0.010705480 |
    #+tblfm: $1=vmin(remote(new-elp-otr-gcp-rep,@<<$4..@>$4))::$2=vmax(remote(new-elp-otr-gcp-rep,@<<$4..@>$4))::$3=vmean(remote(new-elp-otr-gcp-rep,@<<$4..@>$4))::$4=vsdev(remote(new-elp-otr-gcp-rep,@<<$4..@>$4))

    |       min |       max |       avg |      stddev |
    |-----------+-----------+-----------+-------------|
    | 2.8753364 | 2.9847827 | 2.9517250 | 0.032116440 |
    #+tblfm: $1=vmin(remote(new-elp-otr-gcp-rep,@<<$3..@>$3))::$2=vmax(remote(new-elp-otr-gcp-rep,@<<$3..@>$3))::$3=vmean(remote(new-elp-otr-gcp-rep,@<<$3..@>$3))::$4=vsdev(remote(new-elp-otr-gcp-rep,@<<$3..@>$3))

    #+NAME: new-elp-mrps-gcp-rep
    | Function Name               | Call Count | Elapsed Time | Average Time |
    |-----------------------------+------------+--------------+--------------|
    | math-read-preprocess-string |       1062 | 0.0522976359 |    4.924e-05 |
    | math-read-preprocess-string |       1062 | 0.0525888469 |    4.951e-05 |
    | math-read-preprocess-string |       1062 | 0.0525499260 |    4.948e-05 |
    | math-read-preprocess-string |       1062 | 0.0524278629 |    4.936e-05 |
    | math-read-preprocess-string |       1062 | 0.0523294250 |    4.927e-05 |
    | math-read-preprocess-string |       1062 | 0.0527783960 |    4.969e-05 |
    | math-read-preprocess-string |       1062 | 0.0524896099 |    4.942e-05 |
    | math-read-preprocess-string |       1062 |  0.052624055 |    4.955e-05 |
    | math-read-preprocess-string |       1062 | 0.0526563890 |    4.958e-05 |
    | math-read-preprocess-string |       1062 | 0.0524006549 |    4.934e-05 |


    |      min |      max |       avg |       stddev |
    |----------+----------+-----------+--------------|
    | 4.924e-5 | 4.969e-5 | 4.9444e-5 | 1.4385178e-7 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-gcp-rep,@<<$4..@>$4))::$2=vmax(remote(new-elp-mrps-gcp-rep,@<<$4..@>$4))::$3=vmean(remote(new-elp-mrps-gcp-rep,@<<$4..@>$4))::$4=vsdev(remote(new-elp-mrps-gcp-rep,@<<$4..@>$4))

    |         min |         max |         avg |       stddev |
    |-------------+-------------+-------------+--------------|
    | 0.052297636 | 0.052778396 | 0.052514280 | 1.5308925e-4 |
    #+tblfm: $1=vmin(remote(new-elp-mrps-gcp-rep,@<<$3..@>$3))::$2=vmax(remote(new-elp-mrps-gcp-rep,@<<$3..@>$3))::$3=vmean(remote(new-elp-mrps-gcp-rep,@<<$3..@>$3))::$4=vsdev(remote(new-elp-mrps-gcp-rep,@<<$3..@>$3))
    

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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-05 18:14                     ` Raffael Stocker
@ 2023-12-16  9:40                       ` Eli Zaretskii
  2023-12-18 10:55                         ` Mattias Engdegård
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2023-12-16  9:40 UTC (permalink / raw)
  To: mattias.engdegard, Raffael Stocker; +Cc: monnier, 67536

Mattias, is this okay with you?  Should I install these patches?

> From: Raffael Stocker <r.stocker@mnet-mail.de>
> Cc: Eli Zaretskii <eliz@gnu.org>, Stefan Monnier <monnier@iro.umontreal.ca>,
>  67536@debbugs.gnu.org
> Date: Tue, 05 Dec 2023 19:14:36 +0100
> 
> Mattias Engdegård <mattias.engdegard@gmail.com> writes:
> 
> > Here's a new patch.
> > A lot less pretty this time.
> >
> > In any case, make sure to include unit tests in your final patch.
> 
> Ok, here it comes.
> 
> I have constructed two org tables for the test (see test-input.org) that
> are reasonably long and contain a few not too unreasonable formulas.
> The nature of the formulas is not too important for the tests, I just
> took them from my original "offending" table.
> 
> I also added unit tests for the function, as requested.
> 
> One of the tables has no special characters, so
> ‘math-read-preprocess-string’ just has to walk through without doing any
> real work (arguably the most common case).  The other table is full of
> replaceable stuff.  I used these two tables to compare the original with
> the new version of ‘math-read-preprocess-string’.  The results are in
> test-results.org.
> 
> As before, I compared ‘gcs-done’ and ‘gc-elapsed’ (with standard
> settings for ‘gc-cons-threshold’ and ‘gc-cons-percentage’) with both
> versions and tables.  I also instrumented ‘org-table-recalculate’ and
> ‘math-read-preprocess-string’ using elp.  Then I set ‘gc-cons-threshold’
> to a large value and ran the same tests again.  For the latter test, I
> only report ‘elp-results’ (as no GC was triggered).
> 
> I recalculated every table three times per reported elp result, as it
> takes three evaluations for the calculation to converge.
> 
> Some interesting results are:
> 
> - time spent in GC reduces from 2.11 s to  0.78 s for the second table
>   (with replacements)
> - elp report of average time decreases from 4.6316224e-3 s to
>   3.0294569e-4 s for the second table
> - with GC prevented, elp reports avg. time 3.4286452e-4 s vs. 4.9444e-5 s
> - elp report of elapsed time for ‘org-table-recalculate’ for three evaluations
>   (what the user sees) decreases from 8.36 s to 4.11 s (with standard GC
>   settings again)
> 
> This is a real win especially for large tables.  Needless to say, I am
> very excited about this optimization.
> 
> (Just for completeness, I quickly compared my version to Mattias'
> version, the latter is about a factor of two faster according to elp.)
> 
> Regards,
> Raffael





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-16  9:40                       ` Eli Zaretskii
@ 2023-12-18 10:55                         ` Mattias Engdegård
  2023-12-18 11:39                           ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-18 10:55 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 67536, monnier, Raffael Stocker

[-- Attachment #1: Type: text/plain, Size: 702 bytes --]

16 dec. 2023 kl. 10.40 skrev Eli Zaretskii <eliz@gnu.org>:

> Mattias, is this okay with you?  Should I install these patches?

So very sorry for the hiatus. I've been a bit poorly and getting back will take some time.

Raffael, thank you for your dogged measurements. Of course my clumsy code didn't preserve the possibility for `math-read-replacement-list` to translate strings longer than a single character but that's what I get for sending off-cuff patches that way.

Here's another off-cuff patch that might work better (demonstrating that I've learned nothing).

Again it's most likely that no user ever changes `math-read-replacement-list\x1e` and the code could be simplified a lot.


[-- Attachment #2: calc-aent.diff --]
[-- Type: application/octet-stream, Size: 1652 bytes --]

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 131f31afff7..ae04a850f5d 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -562,8 +562,8 @@ math-read-preprocess-string
     ;; Cache invalid, recompute.
     (setq math--read-preprocess-re-cache
           (list (rx-to-string
-                 `(or (group (+ (in ,math-read-superscripts)))
-                      (group (+ (in ,math-read-subscripts)))
+                 `(or (or (+ (in ,math-read-superscripts))
+                          (group (+ (in ,math-read-subscripts))))
                       (group (or ,@(mapcar #'car math-read-replacement-list))))
                  t)
                 math-read-replacement-list
@@ -572,13 +572,15 @@ math-read-preprocess-string
   (replace-regexp-in-string
    (nth 0 math--read-preprocess-re-cache)
    (lambda (s)
-     (let ((r (mapconcat (lambda (c)
-                           (cadr (assoc (char-to-string c)
-                                        math-read-replacement-list)))
-                         s)))
-       (cond ((match-beginning 1) (concat "^(" r ")"))
-             ((match-beginning 2) (concat "_(" r ")"))
-             (t r))))
+     (if (match-beginning 2)
+         (cadr (assoc s math-read-replacement-list))
+       (concat (if (match-beginning 1) "_" "^")
+               "("
+               (mapconcat (lambda (c)
+                            (cadr (assoc (char-to-string c)
+                                         math-read-replacement-list)))
+                          s)
+               ")")))
    str t))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr

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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-18 10:55                         ` Mattias Engdegård
@ 2023-12-18 11:39                           ` Raffael Stocker
  2023-12-19 16:16                             ` Mattias Engdegård
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-18 11:39 UTC (permalink / raw)
  To: Mattias Engdegård; +Cc: Eli Zaretskii, monnier, 67536

[-- Attachment #1: Type: text/plain, Size: 655 bytes --]


Mattias Engdegård <mattias.engdegard@gmail.com> writes:

> Of course my clumsy code didn't preserve the possibility for
> `math-read-replacement-list` to translate strings longer than a single
> character but that's what I get for sending off-cuff patches that way.

...and my test did not catch that edge case.  I extended the test with
this and an empty ‘math-read-replacement-list’ for good measure
(although I don't quite know which use case that might be).  The test
fails for the previous version and succeeds for the original and this
new one.

Have I missed any other edge cases in the test?

I appended the updated patch.


[-- Attachment #2: 0001-lisp-calc-calc-aent.el-math-read-preprocess-string-o.patch --]
[-- Type: text/x-patch, Size: 6268 bytes --]

From 66f63f993d87161cfeaf07366eb9dc68738b5891 Mon Sep 17 00:00:00 2001
From: Raffael Stocker <r.stocker@mnet-mail.de>
Date: Mon, 18 Dec 2023 12:35:33 +0100
Subject: [PATCH] * lisp/calc/calc-aent.el (math-read-preprocess-string):
 optimize (bug#67536)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This is an optimized version of ‘math-read-preprocess-string’ by
Mattias Engdegård <mattias.engdegard@gmail.com>, with unit tests.

This function is called by calc-eval, which in turn is called
repeatedly when re-calculating org-mode tables.  It was one of the
main bottlenecks there and this optimization addresses this by not
repeatedly allocating new strings while iterating through the
replacement list, but instead working through the argument string only
once, using a "flattened" and cached regexp and only one call to
‘replace-regexp-in-string’.
---
 lisp/calc/calc-aent.el       | 45 +++++++++++++++++++++++++-----------
 test/lisp/calc/calc-tests.el | 37 +++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/lisp/calc/calc-aent.el b/lisp/calc/calc-aent.el
index 66ede3295ae..ae04a850f5d 100644
--- a/lisp/calc/calc-aent.el
+++ b/lisp/calc/calc-aent.el
@@ -547,22 +547,41 @@ math-read-subscripts
   "₀₁₂₃₄₅₆₇₈₉₊₋₍₎" ; 0123456789+-()
   "A string consisting of the subscripts allowed by Calc.")
 
+(defvar math--read-preprocess-re-cache nil
+  "Cached regexp and tag: (REGEXP REPLACEMENTS SUPERSCRIPTS SUBSCRIPTS)")
+
 ;;;###autoload
 (defun math-read-preprocess-string (str)
   "Replace some substrings of STR by Calc equivalents."
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-superscripts "]+")
-                                  "^(\\&)" str))
-  (setq str
-        (replace-regexp-in-string (concat "[" math-read-subscripts "]+")
-                                  "_(\\&)" str))
-  (let ((rep-list math-read-replacement-list))
-    (while rep-list
-      (setq str
-            (replace-regexp-in-string (nth 0 (car rep-list))
-                                      (nth 1 (car rep-list)) str))
-      (setq rep-list (cdr rep-list))))
-  str)
+  (unless (and (eq (nth 1 math--read-preprocess-re-cache)
+                   math-read-replacement-list)
+               (eq (nth 2 math--read-preprocess-re-cache)
+                   math-read-superscripts)
+               (eq (nth 3 math--read-preprocess-re-cache)
+                   math-read-subscripts))
+    ;; Cache invalid, recompute.
+    (setq math--read-preprocess-re-cache
+          (list (rx-to-string
+                 `(or (or (+ (in ,math-read-superscripts))
+                          (group (+ (in ,math-read-subscripts))))
+                      (group (or ,@(mapcar #'car math-read-replacement-list))))
+                 t)
+                math-read-replacement-list
+                math-read-superscripts
+                math-read-subscripts)))
+  (replace-regexp-in-string
+   (nth 0 math--read-preprocess-re-cache)
+   (lambda (s)
+     (if (match-beginning 2)
+         (cadr (assoc s math-read-replacement-list))
+       (concat (if (match-beginning 1) "_" "^")
+               "("
+               (mapconcat (lambda (c)
+                            (cadr (assoc (char-to-string c)
+                                         math-read-replacement-list)))
+                          s)
+               ")")))
+   str t))
 
 ;; The next few variables are local to math-read-exprs (and math-read-expr
 ;; in calc-ext.el), but are set in functions they call.
diff --git a/test/lisp/calc/calc-tests.el b/test/lisp/calc/calc-tests.el
index 5b11dd950ba..e49df216019 100644
--- a/test/lisp/calc/calc-tests.el
+++ b/test/lisp/calc/calc-tests.el
@@ -816,5 +816,42 @@ calc-nth-root
          (x (calc-tests--calc-to-number (math-pow 8 '(frac 1 6)))))
     (should (< (abs (- x (sqrt 2.0))) 1.0e-10))))
 
+(ert-deftest calc-math-read-preprocess-string ()
+  "Test replacement of allowed special Unicode symbols."
+  ;; ... doesn't change an empty string
+  (should (string= "" (math-read-preprocess-string "")))
+  ;; ... doesn't change a string without characters from
+  ;; ‘math-read-replacement-list’
+  (let ((str "don't replace here"))
+    (should (string= str (math-read-preprocess-string str))))
+  ;; ... replaces irrespective of position in input string
+  (should (string= "^(1)" (math-read-preprocess-string "¹")))
+  (should (string= "some^(1)" (math-read-preprocess-string "some¹")))
+  (should (string= "^(1)time" (math-read-preprocess-string "¹time")))
+  (should (string= "some^(1)else" (math-read-preprocess-string "some¹else")))
+  ;; ... replaces every element of ‘math-read-replacement-list’ correctly,
+  ;; in particular combining consecutive super-/subscripts into one
+  ;; exponent/subscript
+  (should (string= (concat "+/-*:-/*inf<=>=<=>=μ(1:4)(1:2)(3:4)(1:3)(2:3)"
+                           "(1:5)(2:5)(3:5)(4:5)(1:6)(5:6)"
+                           "(1:8)(3:8)(5:8)(7:8)1:^(0123456789+-()ni)"
+                           "_(0123456789+-())")
+                   (math-read-preprocess-string (mapconcat #'car
+                                                           math-read-replacement-list
+                                                           ""))))
+  ;; ... replaces strings of more than a single character correctly
+  (let ((math-read-replacement-list (append
+                                     math-read-replacement-list
+                                     '(("𝚤𝚥" "ij"))
+                                     '(("¼½" "(1:4)(1:2)")))))
+    (should (string= "(1:4)(1:2)ij"
+                     (math-read-preprocess-string "¼½𝚤𝚥"))))
+  ;; ... handles an empty replacement list gracefully
+  (let ((math-read-replacement-list '()))
+    (should (string= "¼" (math-read-preprocess-string "¼"))))
+  ;; ... signals an error if the argument is not a string
+  (should-error (math-read-preprocess-string nil))
+  (should-error (math-read-preprocess-string 42)))
+
 (provide 'calc-tests)
 ;;; calc-tests.el ends here
-- 
2.43.0


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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-18 11:39                           ` Raffael Stocker
@ 2023-12-19 16:16                             ` Mattias Engdegård
  2023-12-19 17:09                               ` Raffael Stocker
  0 siblings, 1 reply; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-19 16:16 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: Eli Zaretskii, monnier, 67536

18 dec. 2023 kl. 12.39 skrev Raffael Stocker <r.stocker@mnet-mail.de>:

> ...and my test did not catch that edge case.  I extended the test with
> this and an empty ‘math-read-replacement-list’ for good measure
> (although I don't quite know which use case that might be).  The test
> fails for the previous version and succeeds for the original and this
> new one.

Thanks, now pushed to master.
The code could be sped up further if necessary but I suppose this will do for now. Otherwise, let us know.






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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-19 16:16                             ` Mattias Engdegård
@ 2023-12-19 17:09                               ` Raffael Stocker
  2023-12-19 18:15                                 ` Mattias Engdegård
  0 siblings, 1 reply; 20+ messages in thread
From: Raffael Stocker @ 2023-12-19 17:09 UTC (permalink / raw)
  To: Mattias Engdegård; +Cc: Eli Zaretskii, monnier, 67536


Mattias Engdegård <mattias.engdegard@gmail.com> writes:

> 18 dec. 2023 kl. 12.39 skrev Raffael Stocker <r.stocker@mnet-mail.de>:
>
>> ...and my test did not catch that edge case.  I extended the test with
>> this and an empty ‘math-read-replacement-list’ for good measure
>> (although I don't quite know which use case that might be).  The test
>> fails for the previous version and succeeds for the original and this
>> new one.
>
> Thanks, now pushed to master.
> The code could be sped up further if necessary but I suppose this will do for now. Otherwise, let us know.

Yes, I think this will be fine.  Thanks!





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

* bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily
  2023-12-19 17:09                               ` Raffael Stocker
@ 2023-12-19 18:15                                 ` Mattias Engdegård
  0 siblings, 0 replies; 20+ messages in thread
From: Mattias Engdegård @ 2023-12-19 18:15 UTC (permalink / raw)
  To: Raffael Stocker; +Cc: Eli Zaretskii, 67536-done

19 dec. 2023 kl. 18.09 skrev Raffael Stocker <r.stocker@mnet-mail.de>:

> Yes, I think this will be fine.  Thanks!

Closing.






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

end of thread, other threads:[~2023-12-19 18:15 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-11-29 21:29 bug#67536: 29.1; Calc mode's math-read-preprocess-string conses unnecessarily Raffael Stocker
2023-11-30  7:00 ` Eli Zaretskii
2023-11-30 18:28   ` Raffael Stocker
2023-11-30 19:14     ` Eli Zaretskii
2023-12-01 17:34       ` Raffael Stocker
2023-12-01 18:12         ` Eli Zaretskii
2023-12-01 21:10           ` Raffael Stocker
2023-12-02  8:03             ` Eli Zaretskii
2023-12-02 14:56               ` Mattias Engdegård
2023-12-02 19:26                 ` Raffael Stocker
2023-12-03 10:43                   ` Mattias Engdegård
2023-12-03 11:13                     ` Raffael Stocker
2023-12-03 11:58                       ` Mattias Engdegård
2023-12-05 18:14                     ` Raffael Stocker
2023-12-16  9:40                       ` Eli Zaretskii
2023-12-18 10:55                         ` Mattias Engdegård
2023-12-18 11:39                           ` Raffael Stocker
2023-12-19 16:16                             ` Mattias Engdegård
2023-12-19 17:09                               ` Raffael Stocker
2023-12-19 18:15                                 ` Mattias Engdegård

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.