emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Thierry Banel <tbanelwebmin@free.fr>
To: emacs-orgmode@gnu.org
Subject: Re: speeding up Babel Gnuplot
Date: Sun, 01 Jan 2017 21:17:15 +0100	[thread overview]
Message-ID: <586963CB.1000006@free.fr> (raw)
In-Reply-To: <5864217C.7060001@free.fr>

Babel Gnuplot can be further accelerated. A table is converted to a
temporary file readable by Gnuplot. I found two issues in this process:
1. the temporary file is generated twice,
2. the generation is quadratic.

I have not provided a committable patch because I am not happy with myfixes.

Of course there is no hurry in fixing that because large tables do not
happen so often.

------------------------------

1. Temporary generated twice
Because org-babel-gnuplot-process-vars is called twice.

There is no obvious fix. Here is a dirty patch. It caches the name of
the temporary file in the 'param' list.

#+BEGIN_SRC elisp
  (defun org-babel-gnuplot-process-vars (params)
    "Extract variables from PARAMS and process the variables.
  Dumps all vectors into files and returns an association list
  of variable names and the related value to be used in the gnuplot
  code."
    (let ((*org-babel-gnuplot-missing* (cdr (assq :missing params))))
      (mapcar
       (lambda (pair)
     (cons
      (car pair) ;; variable name
      (let ((val (cdr pair))) ;; variable value
        (if (not (listp val))
        val
          (let ((temp-file (org-babel-temp-file "gnuplot-"))
            (first  (car val)))
        (setcdr pair temp-file)  ;; <------ caching here
        (org-babel-gnuplot-table-to-data
         (if (or (listp first) (symbolp first))
             val
           (mapcar 'list val))
         temp-file params))))))
       (org-babel--get-vars params))))
#+END_SRC

------------------------------

2. Quadratic behavior
The spot is at ox.el::5119(the lambda in org-export-table-row-number).

This lambda is called a number of times equal to the square of thesize
of the table being plotted. For a 2000 rows table, this is
2000x2000 = four millions times. The cache a few lines before does
nothelp because each row is visited only once.

I don't know how to fix that. Hereafter is a quick-and-dirty patch to
turn the behavior into a linear one (lambda called 2000 timesinstead).
The cache is pre-filled upon creation with a single call to
org-element-map. So instead of calling org-element-map for each row
(forcing it to go through all previous rows over and over), it iscalled
only once.

#+BEGIN_SRC elisp
(defun prefill-cache (table cache info)
  (let ((number 0))
    (org-element-map   ;; <--- called once here
          table
      'table-row
      (lambda (row)
        (if (eq (org-element-property :type row) 'standard)
           (progn
          (puthash row number cache)
          (cl-incf number))))
      info )))

(defun org-export-table-row-number (table-row info)
  "Return TABLE-ROW number.
INFO is a plist used as a communication channel.  Return value is
zero-based and ignores separators.  The function returns nil for
special columns and separators."
  (let* ((cache (or (plist-get info :table-row-number-cache)
                    (let ((table (make-hash-table :test #'eq)))
                      (plist-put info :table-row-number-cache table)
                      (prefill-cache
                        (org-export-get-parent-table table-row)
                        table
                        info)
                     table)))
         (cached (gethash table-row cache 'no-cache)))
    (if (not (eq cached 'no-cache)) cached
      (puthash table-row
       (and (eq (org-element-property :type table-row) 'standard)
            (not (org-export-table-row-is-special-p table-row info))
            (let ((number 0))
              (org-element-map   ;; <--- called many times here
                (org-export-get-parent-table table-row)
                'table-row
                (lambda (row)
                  (cond ((eq row table-row) number)
                    ((eq (org-element-property :type row) 'standard)
                     (cl-incf number) nil)))
                info 'first-match)))
           cache))))
#+END_SRC

------------------------------

3. Test case

First generate a 2000 rows table:

#+BEGIN_SRC elisp :results none
  (goto-char (point-max))
  (insert "#+name: data\n")
  (let ((i 2000))
    (while (> i 0)
      (goto-char (point-max))
      (insert (format "| %4s |\n" i))
      (setq i (1- i))))
#+END_SRC

Then run Babel Gnuplot:

#+BEGIN_SRC gnuplot :var data=data :file x.svg :session none :term svg
plot data
#+END_SRC

  parent reply	other threads:[~2017-01-01 20:17 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-28 20:33 speeding up Babel Gnuplot Thierry Banel
2016-12-29 20:04 ` Nicolas Goaziou
2016-12-29 20:34   ` Thierry Banel
2017-01-01 20:17 ` Thierry Banel [this message]
2017-01-01 23:34   ` Nicolas Goaziou
2017-01-02 20:11     ` Thierry Banel
2017-01-03 21:40       ` Thierry Banel
2017-01-03 21:55         ` Nicolas Goaziou
2017-01-03 23:06           ` Thierry Banel
2017-01-04 22:36             ` Nicolas Goaziou
2017-01-05 20:47               ` Thierry Banel
2017-01-06  9:41                 ` Nicolas Goaziou
2017-01-06 18:24                   ` Thierry Banel
2017-01-04 17:32         ` Achim Gratz
2017-01-04 20:29           ` Thierry Banel
2017-01-04 23:15           ` Charles C. Berry
2017-01-05 20:23             ` Thierry Banel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=586963CB.1000006@free.fr \
    --to=tbanelwebmin@free.fr \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).