* Asynchronous org-agenda-redo @ 2019-12-12 8:18 Ihor Radchenko 2019-12-12 12:17 ` Adam Porter 2019-12-12 12:51 ` Diego Zamboni 0 siblings, 2 replies; 17+ messages in thread From: Ihor Radchenko @ 2019-12-12 8:18 UTC (permalink / raw) To: emacs-orgmode Mailinglist I am thinking if it is possible to implement org-agenda-redo asynchronously. Rebuilding agenda should normally not affect any buffer except agenda buffer. So, it should be sufficient to block any agenda modifying commands in the agenda buffer, redo the agenda buffer in separate thread, and replace the old agenda with the calculated one. Then, emacs should remain responsive while updating agenda (except for modifying the agenda buffer). For example, this naive code kind of works (forgetting that buffer-local variables will not be passed to the thread): (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) (make-thread oldfun "org-agenda-redo")) The problem is that emacs does not become responsive... Another approach would be using async.el package, which allows calling arbitrary function in subordinate emacs process. Then, the main emacs instance should not be "frozen" (I use same approach for tangling and it works fine). However, the question is how to pass the .org and agenda buffers to this subordinate process. Opening the .org files there is not a good option since it would give too much overhead to this asynchronous agenda. Any suggestions? Alternative ideas? Best, Ihor ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-12 8:18 Asynchronous org-agenda-redo Ihor Radchenko @ 2019-12-12 12:17 ` Adam Porter 2019-12-12 15:46 ` Ihor Radchenko 2019-12-12 12:51 ` Diego Zamboni 1 sibling, 1 reply; 17+ messages in thread From: Adam Porter @ 2019-12-12 12:17 UTC (permalink / raw) To: emacs-orgmode Be sure to read the Emacs Lisp manual regarding threads. They are cooperative, so functions called as threads must yield back to the main thread for Emacs to do anything else before the function returns. If you're feeling adventurous, you could experiment with adding yields in relevant agenda functions. But that wouldn't be suitable for merging into Org, because that yielding also decreases performance generally. As long as Elisp threads are cooperative, they are of very limited use. Generating agendas with async.el in a separate Emacs process is an interesting idea, but probably generally impractical for a few reasons: 1. The process would have to load the same Org buffers, which takes time, especially in large buffers. Depending on configuration, it can take some time, indeed. 2. The process would also have to load the same packages (or, at least, all the necessary ones, which depends on configuration), which takes time. 3. Ensuring that configuration and state between the main Emacs process and the separate, agenda-generating process is not necessarily simple. Consider as well that if a buffer had unsaved changes, those would not be readable by the other process, which would lead to invalid results. One could force the buffers to be saved first, but that may not always be desirable, as saving buffers can have side effects. If your agenda buffers are taking too long to refresh, you might consider org-ql's views/saved-searches as an alternative. The built-in caching in org-ql significantly improves performance, especially when refreshing views. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-12 12:17 ` Adam Porter @ 2019-12-12 15:46 ` Ihor Radchenko 2019-12-13 6:49 ` Adam Porter 0 siblings, 1 reply; 17+ messages in thread From: Ihor Radchenko @ 2019-12-12 15:46 UTC (permalink / raw) To: Adam Porter, emacs-orgmode > Be sure to read the Emacs Lisp manual regarding threads. They are > cooperative, so functions called as threads must yield back to the main > thread for Emacs to do anything else before the function returns. I tried to read the manual, but I clearly misunderstand something. The manual says: > Currently, thread switching will occur upon explicit request via > ‘thread-yield’, when waiting for keyboard input... So, except directly calling thread-yield, it should be possible to trigger switching the current thread when keyboard input is expected. I tried the following demo code: (defun test () (let ((a 0)) (dotimes (_ 5) (setq a (1+ a)) (sleep-for 2) (message "%s" a)))) (progn ;This should return to command loop quickly (make-thread #'test) (message "Executed...")); `eval-last-sexp' here I can move around the buffer while the progn is running. However, it is not the case with `org-agenda-redo' for a reason I do not fully understand. For the async.el, I agree that loading packages may take time, but I believe that the configuration might be transferred more easily (though it may depend on the org-agenda and user-defined org-agenda-skip-functions implementation). > 1. The process would have to load the same Org buffers, which takes > time, especially in large buffers. Depending on configuration, it > can take some time, indeed. > 3. Ensuring that configuration and state between the main Emacs process > and the separate, agenda-generating process is not necessarily > simple. Consider as well that if a buffer had unsaved changes, > those would not be readable by the other process, which would lead > to invalid results. One could force the buffers to be saved first, > but that may not always be desirable, as saving buffers can have > side effects. Why cannot org-buffer simply be copied into the subordinate process? If all be buffer-locals, text properties, and overlays are copied directly from the main emacs process, there may be no need to even initialise org-mode (the idea is to do something similar to clone-buffer). The question though is whether buffer-locals + overlays + propertized .org files text + org-agenda-buffer copy can be sufficient to make the org-agenda-redo run properly. Are there any other buffers, variables, or other environment settings used by org-agenda-redo? > If your agenda buffers are taking too long to refresh, you might > consider org-ql's views/saved-searches as an alternative. ... I know org-ql and I am pretty sure that it will improve performance. Actually, if one can make built-in org-agenda asynchronous, org-ql can probably use similar approach and become even faster :) I am trying on default org-agenda now mostly because my current config is heavily geared towards default agenda and I am not sure if refactoring everything to use org-ql will worth it at the end in terms of performance. I use too many slow custom skip-functions. > ... The built-in > caching in org-ql significantly improves performance, especially when > refreshing views. Yeah. I wish org-entry-get and other org-get* functions support caching as well... Or, at least, org-agenda functions might also support simple caching based on file modifications. Best, Ihor Adam Porter <adam@alphapapa.net> writes: > Be sure to read the Emacs Lisp manual regarding threads. They are > cooperative, so functions called as threads must yield back to the main > thread for Emacs to do anything else before the function returns. > > If you're feeling adventurous, you could experiment with adding yields > in relevant agenda functions. But that wouldn't be suitable for merging > into Org, because that yielding also decreases performance generally. > > As long as Elisp threads are cooperative, they are of very limited use. > > Generating agendas with async.el in a separate Emacs process is an > interesting idea, but probably generally impractical for a few reasons: > > 1. The process would have to load the same Org buffers, which takes > time, especially in large buffers. Depending on configuration, it > can take some time, indeed. > 2. The process would also have to load the same packages (or, at least, > all the necessary ones, which depends on configuration), which takes > time. > 3. Ensuring that configuration and state between the main Emacs process > and the separate, agenda-generating process is not necessarily > simple. Consider as well that if a buffer had unsaved changes, > those would not be readable by the other process, which would lead > to invalid results. One could force the buffers to be saved first, > but that may not always be desirable, as saving buffers can have > side effects. > > If your agenda buffers are taking too long to refresh, you might > consider org-ql's views/saved-searches as an alternative. The built-in > caching in org-ql significantly improves performance, especially when > refreshing views. > > -- Ihor Radchenko, PhD, Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong University, Xi'an, China Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-12 15:46 ` Ihor Radchenko @ 2019-12-13 6:49 ` Adam Porter 2019-12-13 8:35 ` Ihor Radchenko 0 siblings, 1 reply; 17+ messages in thread From: Adam Porter @ 2019-12-13 6:49 UTC (permalink / raw) To: emacs-orgmode Ihor Radchenko <yantar92@gmail.com> writes: >> Be sure to read the Emacs Lisp manual regarding threads. They are >> cooperative, so functions called as threads must yield back to the main >> thread for Emacs to do anything else before the function returns. > > I tried to read the manual, but I clearly misunderstand something. > The manual says: > >> Currently, thread switching will occur upon explicit request via >> ‘thread-yield’, when waiting for keyboard input... > > So, except directly calling thread-yield, it should be possible to > trigger switching the current thread when keyboard input is expected. > I tried the following demo code: > > (defun test () > (let ((a 0)) > (dotimes (_ 5) > (setq a (1+ a)) > (sleep-for 2) > (message "%s" a)))) > > (progn ;This should return to command loop quickly > (make-thread #'test) > (message "Executed...")); `eval-last-sexp' here > > I can move around the buffer while the progn is running. > However, it is not the case with `org-agenda-redo' for a reason I do not > fully understand. Org Agenda code does not wait for keyboard input; it's busy building the agenda. This is the case with most code in Emacs: it's not written to be asynchronous, and it doesn't return to the main thread until done. So you can sprinkle yields here and there and maybe be able to move point around while some code is running, but that will decrease performance, as well as introducing another level of complexity and another class of bugs (e.g. what if the user modifies a buffer while the agenda code is scanning it?). >> 1. The process would have to load the same Org buffers, which takes >> time, especially in large buffers. Depending on configuration, it >> can take some time, indeed. > >> 3. Ensuring that configuration and state between the main Emacs process >> and the separate, agenda-generating process is not necessarily >> simple. Consider as well that if a buffer had unsaved changes, >> those would not be readable by the other process, which would lead >> to invalid results. One could force the buffers to be saved first, >> but that may not always be desirable, as saving buffers can have >> side effects. > > Why cannot org-buffer simply be copied into the subordinate process? If > all be buffer-locals, text properties, and overlays are copied directly > from the main emacs process, there may be no need to even initialise > org-mode (the idea is to do something similar to clone-buffer). AFAIK there exists no way to do such a thing. Buffers are not designed to be serialized/deserialized like that. You could try writing some Elisp code to do it, but the end result would probably be much slower than existing agenda code, as well as more difficult to debug. > The question though is whether buffer-locals + overlays + propertized > .org files text + org-agenda-buffer copy can be sufficient to make the > org-agenda-redo run properly. Are there any other buffers, variables, > or other environment settings used by org-agenda-redo? As you can see in org-agenda.el, it's complicated. Remember that an Emacs process is like a Lisp image, full of state. The more symbols and other structures you copy to the async Emacs process (by printing and reading them as text, remember), the slower it's going to be--and it will always be slower than not using async. >> If your agenda buffers are taking too long to refresh, you might >> consider org-ql's views/saved-searches as an alternative. ... > > I know org-ql and I am pretty sure that it will improve performance. > Actually, if one can make built-in org-agenda asynchronous, org-ql can > probably use similar approach and become even faster :) Asynchronous code is not faster; it's generally slower because of yielding and synchronization. > I am trying on default org-agenda now mostly because my current config > is heavily geared towards default agenda and I am not sure if > refactoring everything to use org-ql will worth it at the end in terms > of performance. I use too many slow custom skip-functions. org-ql doesn't use skip functions, just queries. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-13 6:49 ` Adam Porter @ 2019-12-13 8:35 ` Ihor Radchenko 2019-12-13 9:39 ` Ihor Radchenko 2019-12-14 4:50 ` Adam Porter 0 siblings, 2 replies; 17+ messages in thread From: Ihor Radchenko @ 2019-12-13 8:35 UTC (permalink / raw) To: Adam Porter, emacs-orgmode > Org Agenda code does not wait for keyboard input; it's busy building the > agenda. This is the case with most code in Emacs: it's not written to > be asynchronous, and it doesn't return to the main thread until done. > So you can sprinkle yields here and there and maybe be able to move > point around while some code is running, but that will decrease > performance, as well as introducing another level of complexity and > another class of bugs (e.g. what if the user modifies a buffer while the > agenda code is scanning it?). Thanks for the explanation. > AFAIK there exists no way to do such a thing. Buffers are not designed > to be serialized/deserialized like that. You could try writing some > Elisp code to do it, but the end result would probably be much slower > than existing agenda code, as well as more difficult to debug. Yeah. Even re-initialisation of, for example, overlays in org buffer is likely to take too much time. > As you can see in org-agenda.el, it's complicated. Remember that an > Emacs process is like a Lisp image, full of state. The more symbols and > other structures you copy to the async Emacs process (by printing and > reading them as text, remember), the slower it's going to be--and it > will always be slower than not using async. > Asynchronous code is not faster; it's generally slower because of > yielding and synchronization. I see now that generating agenda in separate process will cause too much overheads. Anyway, I will try to throw yields into agenda code just to check how bad the performance can degrade. > org-ql doesn't use skip functions, just queries. Skip functions are essentially used-defined queries as soon as the queries are tested against every headline. I can rewrite my skip functions into queries, but I don't expect much improvement since org-ql seems to use org-entry-get, which is the main performance bottleneck for my agenda generation. Best, Ihor adam Porter <adam@alphapapa.net> writes: > Ihor Radchenko <yantar92@gmail.com> writes: > >>> Be sure to read the Emacs Lisp manual regarding threads. They are >>> cooperative, so functions called as threads must yield back to the main >>> thread for Emacs to do anything else before the function returns. >> >> I tried to read the manual, but I clearly misunderstand something. >> The manual says: >> >>> Currently, thread switching will occur upon explicit request via >>> ‘thread-yield’, when waiting for keyboard input... >> >> So, except directly calling thread-yield, it should be possible to >> trigger switching the current thread when keyboard input is expected. >> I tried the following demo code: >> >> (defun test () >> (let ((a 0)) >> (dotimes (_ 5) >> (setq a (1+ a)) >> (sleep-for 2) >> (message "%s" a)))) >> >> (progn ;This should return to command loop quickly >> (make-thread #'test) >> (message "Executed...")); `eval-last-sexp' here >> >> I can move around the buffer while the progn is running. >> However, it is not the case with `org-agenda-redo' for a reason I do not >> fully understand. > > Org Agenda code does not wait for keyboard input; it's busy building the > agenda. This is the case with most code in Emacs: it's not written to > be asynchronous, and it doesn't return to the main thread until done. > So you can sprinkle yields here and there and maybe be able to move > point around while some code is running, but that will decrease > performance, as well as introducing another level of complexity and > another class of bugs (e.g. what if the user modifies a buffer while the > agenda code is scanning it?). > >>> 1. The process would have to load the same Org buffers, which takes >>> time, especially in large buffers. Depending on configuration, it >>> can take some time, indeed. >> >>> 3. Ensuring that configuration and state between the main Emacs process >>> and the separate, agenda-generating process is not necessarily >>> simple. Consider as well that if a buffer had unsaved changes, >>> those would not be readable by the other process, which would lead >>> to invalid results. One could force the buffers to be saved first, >>> but that may not always be desirable, as saving buffers can have >>> side effects. >> >> Why cannot org-buffer simply be copied into the subordinate process? If >> all be buffer-locals, text properties, and overlays are copied directly >> from the main emacs process, there may be no need to even initialise >> org-mode (the idea is to do something similar to clone-buffer). > > AFAIK there exists no way to do such a thing. Buffers are not designed > to be serialized/deserialized like that. You could try writing some > Elisp code to do it, but the end result would probably be much slower > than existing agenda code, as well as more difficult to debug. > >> The question though is whether buffer-locals + overlays + propertized >> .org files text + org-agenda-buffer copy can be sufficient to make the >> org-agenda-redo run properly. Are there any other buffers, variables, >> or other environment settings used by org-agenda-redo? > > As you can see in org-agenda.el, it's complicated. Remember that an > Emacs process is like a Lisp image, full of state. The more symbols and > other structures you copy to the async Emacs process (by printing and > reading them as text, remember), the slower it's going to be--and it > will always be slower than not using async. > >>> If your agenda buffers are taking too long to refresh, you might >>> consider org-ql's views/saved-searches as an alternative. ... >> >> I know org-ql and I am pretty sure that it will improve performance. >> Actually, if one can make built-in org-agenda asynchronous, org-ql can >> probably use similar approach and become even faster :) > > Asynchronous code is not faster; it's generally slower because of > yielding and synchronization. > >> I am trying on default org-agenda now mostly because my current config >> is heavily geared towards default agenda and I am not sure if >> refactoring everything to use org-ql will worth it at the end in terms >> of performance. I use too many slow custom skip-functions. > > org-ql doesn't use skip functions, just queries. > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-13 8:35 ` Ihor Radchenko @ 2019-12-13 9:39 ` Ihor Radchenko 2019-12-14 4:59 ` Adam Porter 2019-12-14 4:50 ` Adam Porter 1 sibling, 1 reply; 17+ messages in thread From: Ihor Radchenko @ 2019-12-13 9:39 UTC (permalink / raw) To: Adam Porter, emacs-orgmode >> Asynchronous code is not faster; it's generally slower because of >> yielding and synchronization. > Anyway, I will try to throw yields into agenda code just to check how > bad the performance can degrade. With the following code, org-agenda-redo runs for 21 second on my system, while without threads it is 16 seconds. However, emacs remains responsive during rebuilding agenda! (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) (make-thread (lambda () (funcall oldfun all)) "org-agenda-redo")) (define-advice org-agenda-skip-eval (:around (oldfun form) make-async) (thread-join (make-thread (lambda () (funcall oldfun form)) "org-agenda-skip-eval"))) The problem, of course, is that touching agenda buffer and org buffers may be risky while org-agenda-redo is running. Wondering if it is possible to block user commands during that time. Best, Ihor Ihor Radchenko <yantar92@gmail.com> writes: >> Org Agenda code does not wait for keyboard input; it's busy building the >> agenda. This is the case with most code in Emacs: it's not written to >> be asynchronous, and it doesn't return to the main thread until done. >> So you can sprinkle yields here and there and maybe be able to move >> point around while some code is running, but that will decrease >> performance, as well as introducing another level of complexity and >> another class of bugs (e.g. what if the user modifies a buffer while the >> agenda code is scanning it?). > > Thanks for the explanation. > >> AFAIK there exists no way to do such a thing. Buffers are not designed >> to be serialized/deserialized like that. You could try writing some >> Elisp code to do it, but the end result would probably be much slower >> than existing agenda code, as well as more difficult to debug. > > Yeah. Even re-initialisation of, for example, overlays in org buffer is > likely to take too much time. > >> As you can see in org-agenda.el, it's complicated. Remember that an >> Emacs process is like a Lisp image, full of state. The more symbols and >> other structures you copy to the async Emacs process (by printing and >> reading them as text, remember), the slower it's going to be--and it >> will always be slower than not using async. > >> Asynchronous code is not faster; it's generally slower because of >> yielding and synchronization. > > I see now that generating agenda in separate process will cause too much > overheads. > Anyway, I will try to throw yields into agenda code just to check how > bad the performance can degrade. > >> org-ql doesn't use skip functions, just queries. > > Skip functions are essentially used-defined queries as soon as the > queries are tested against every headline. > I can rewrite my skip functions into queries, but I don't expect much > improvement since org-ql seems to use org-entry-get, which is the main > performance bottleneck for my agenda generation. > > Best, > Ihor > > adam Porter <adam@alphapapa.net> writes: > >> Ihor Radchenko <yantar92@gmail.com> writes: >> >>>> Be sure to read the Emacs Lisp manual regarding threads. They are >>>> cooperative, so functions called as threads must yield back to the main >>>> thread for Emacs to do anything else before the function returns. >>> >>> I tried to read the manual, but I clearly misunderstand something. >>> The manual says: >>> >>>> Currently, thread switching will occur upon explicit request via >>>> ‘thread-yield’, when waiting for keyboard input... >>> >>> So, except directly calling thread-yield, it should be possible to >>> trigger switching the current thread when keyboard input is expected. >>> I tried the following demo code: >>> >>> (defun test () >>> (let ((a 0)) >>> (dotimes (_ 5) >>> (setq a (1+ a)) >>> (sleep-for 2) >>> (message "%s" a)))) >>> >>> (progn ;This should return to command loop quickly >>> (make-thread #'test) >>> (message "Executed...")); `eval-last-sexp' here >>> >>> I can move around the buffer while the progn is running. >>> However, it is not the case with `org-agenda-redo' for a reason I do not >>> fully understand. >> >> Org Agenda code does not wait for keyboard input; it's busy building the >> agenda. This is the case with most code in Emacs: it's not written to >> be asynchronous, and it doesn't return to the main thread until done. >> So you can sprinkle yields here and there and maybe be able to move >> point around while some code is running, but that will decrease >> performance, as well as introducing another level of complexity and >> another class of bugs (e.g. what if the user modifies a buffer while the >> agenda code is scanning it?). >> >>>> 1. The process would have to load the same Org buffers, which takes >>>> time, especially in large buffers. Depending on configuration, it >>>> can take some time, indeed. >>> >>>> 3. Ensuring that configuration and state between the main Emacs process >>>> and the separate, agenda-generating process is not necessarily >>>> simple. Consider as well that if a buffer had unsaved changes, >>>> those would not be readable by the other process, which would lead >>>> to invalid results. One could force the buffers to be saved first, >>>> but that may not always be desirable, as saving buffers can have >>>> side effects. >>> >>> Why cannot org-buffer simply be copied into the subordinate process? If >>> all be buffer-locals, text properties, and overlays are copied directly >>> from the main emacs process, there may be no need to even initialise >>> org-mode (the idea is to do something similar to clone-buffer). >> >> AFAIK there exists no way to do such a thing. Buffers are not designed >> to be serialized/deserialized like that. You could try writing some >> Elisp code to do it, but the end result would probably be much slower >> than existing agenda code, as well as more difficult to debug. >> >>> The question though is whether buffer-locals + overlays + propertized >>> .org files text + org-agenda-buffer copy can be sufficient to make the >>> org-agenda-redo run properly. Are there any other buffers, variables, >>> or other environment settings used by org-agenda-redo? >> >> As you can see in org-agenda.el, it's complicated. Remember that an >> Emacs process is like a Lisp image, full of state. The more symbols and >> other structures you copy to the async Emacs process (by printing and >> reading them as text, remember), the slower it's going to be--and it >> will always be slower than not using async. >> >>>> If your agenda buffers are taking too long to refresh, you might >>>> consider org-ql's views/saved-searches as an alternative. ... >>> >>> I know org-ql and I am pretty sure that it will improve performance. >>> Actually, if one can make built-in org-agenda asynchronous, org-ql can >>> probably use similar approach and become even faster :) >> >> Asynchronous code is not faster; it's generally slower because of >> yielding and synchronization. >> >>> I am trying on default org-agenda now mostly because my current config >>> is heavily geared towards default agenda and I am not sure if >>> refactoring everything to use org-ql will worth it at the end in terms >>> of performance. I use too many slow custom skip-functions. >> >> org-ql doesn't use skip functions, just queries. >> >> > -- Ihor Radchenko, PhD, Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong University, Xi'an, China Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-13 9:39 ` Ihor Radchenko @ 2019-12-14 4:59 ` Adam Porter 2019-12-22 6:54 ` Ihor Radchenko 0 siblings, 1 reply; 17+ messages in thread From: Adam Porter @ 2019-12-14 4:59 UTC (permalink / raw) To: emacs-orgmode Ihor Radchenko <yantar92@gmail.com> writes: >>> Asynchronous code is not faster; it's generally slower because of >>> yielding and synchronization. > >> Anyway, I will try to throw yields into agenda code just to check how >> bad the performance can degrade. > > With the following code, org-agenda-redo runs for 21 second on my > system, while without threads it is 16 seconds. However, emacs remains > responsive during rebuilding agenda! > > (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) > (make-thread (lambda () (funcall oldfun all)) "org-agenda-redo")) > (define-advice org-agenda-skip-eval (:around (oldfun form) make-async) > (thread-join (make-thread (lambda () (funcall oldfun form)) "org-agenda-skip-eval"))) That's a very elegant way to add the threading! The performance penalty is noticeable, but the tradeoff could be worth it in some cases, like a background agenda refresh on a timer, or after a "remote" edit. I can imagine an org-agenda-refresh-async command that would add that advice and remove them in an unwind-protect. > The problem, of course, is that touching agenda buffer and org buffers > may be risky while org-agenda-redo is running. > Wondering if it is possible to block user commands during that time. The first thing that comes to mind is to set buffer-read-only on each buffer before it is scanned, and unset it when done with a buffer. That might not be doable with advice. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-14 4:59 ` Adam Porter @ 2019-12-22 6:54 ` Ihor Radchenko 2019-12-24 0:36 ` Adam Porter 0 siblings, 1 reply; 17+ messages in thread From: Ihor Radchenko @ 2019-12-22 6:54 UTC (permalink / raw) To: Adam Porter, emacs-orgmode > That's a very elegant way to add the threading! Thanks for kind words. > The performance penalty > is noticeable, but the tradeoff could be worth it in some cases, like a > background agenda refresh on a timer, or after a "remote" edit. I can > imagine an org-agenda-refresh-async command that would add that advice > and remove them in an unwind-protect. That may be a good way to go. Probably, even refreshing agenda in a separate copy of agenda buffer, so that the current version remains usable. > The first thing that comes to mind is to set buffer-read-only on each > buffer before it is scanned, and unset it when done with a buffer. That > might not be doable with advice. This is not enough. If you accidentally move the point in the buffer being processed by agenda, the results may be unpredictable (org-agenda-get-* functions move across the buffer with re-search-forward). Any ideas how to deal with this? Best, Ihor Adam Porter <adam@alphapapa.net> writes: > Ihor Radchenko <yantar92@gmail.com> writes: > >>>> Asynchronous code is not faster; it's generally slower because of >>>> yielding and synchronization. >> >>> Anyway, I will try to throw yields into agenda code just to check how >>> bad the performance can degrade. >> >> With the following code, org-agenda-redo runs for 21 second on my >> system, while without threads it is 16 seconds. However, emacs remains >> responsive during rebuilding agenda! >> >> (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) >> (make-thread (lambda () (funcall oldfun all)) "org-agenda-redo")) >> (define-advice org-agenda-skip-eval (:around (oldfun form) make-async) >> (thread-join (make-thread (lambda () (funcall oldfun form)) "org-agenda-skip-eval"))) > > That's a very elegant way to add the threading! The performance penalty > is noticeable, but the tradeoff could be worth it in some cases, like a > background agenda refresh on a timer, or after a "remote" edit. I can > imagine an org-agenda-refresh-async command that would add that advice > and remove them in an unwind-protect. > >> The problem, of course, is that touching agenda buffer and org buffers >> may be risky while org-agenda-redo is running. >> Wondering if it is possible to block user commands during that time. > > The first thing that comes to mind is to set buffer-read-only on each > buffer before it is scanned, and unset it when done with a buffer. That > might not be doable with advice. > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-22 6:54 ` Ihor Radchenko @ 2019-12-24 0:36 ` Adam Porter 0 siblings, 0 replies; 17+ messages in thread From: Adam Porter @ 2019-12-24 0:36 UTC (permalink / raw) To: emacs-orgmode Ihor Radchenko <yantar92@gmail.com> writes: > If you accidentally move the point in the buffer being processed by > agenda, the results may be unpredictable (org-agenda-get-* functions > move across the buffer with re-search-forward). I'm afraid that's the basic problem with threading in Emacs: shared state. I don't know of a way to work around that. I suppose you could modify a lot of the agenda code to, e.g. store the position of the current search in a variable and go to it before each search. Of course, that would also reduce performance to some extent. It's a hard problem. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-13 8:35 ` Ihor Radchenko 2019-12-13 9:39 ` Ihor Radchenko @ 2019-12-14 4:50 ` Adam Porter 2019-12-16 7:23 ` Ihor Radchenko 1 sibling, 1 reply; 17+ messages in thread From: Adam Porter @ 2019-12-14 4:50 UTC (permalink / raw) To: emacs-orgmode Ihor Radchenko <yantar92@gmail.com> writes: >> org-ql doesn't use skip functions, just queries. > > Skip functions are essentially used-defined queries as soon as the > queries are tested against every headline. Skip functions aren't necessary with org-ql, because the query itself can have arbitrary Lisp code. So, of course, you can call custom functions in queries, even your own skip functions (with `not', of course), but in most cases, they can be covered with built-in predicates. > I can rewrite my skip functions into queries, but I don't expect much > improvement since org-ql seems to use org-entry-get, which is the main > performance bottleneck for my agenda generation. org-entry-get is only called for the (property) predicate. It's the correct way to get Org properties, because it handles special properties, inheritance, etc. However, when possible, queries are optimized to a whole-buffer regexp search that finds possible matches. So, for example, a query like '(property "owner" "yantar") would be optimized to a whole-buffer regexp search that would be very fast. See function org-ql--query-preamble. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-14 4:50 ` Adam Porter @ 2019-12-16 7:23 ` Ihor Radchenko 2019-12-16 10:32 ` Adam Porter 0 siblings, 1 reply; 17+ messages in thread From: Ihor Radchenko @ 2019-12-16 7:23 UTC (permalink / raw) To: Adam Porter, emacs-orgmode > So, of course, you can call custom > functions in queries, even your own skip functions (with `not', of > course), but in most cases, they can be covered with built-in > predicates. Unfortunately, it does not seem to be the case for me. My main agenda view needs to take into account multiple properties, which cannot be handled within framework of built-in org-ql predicates (AFAIK): - =:SHOWFROMTIME:= (always inheriting) :: The purpose of this is to be able to assign specific projects for different days of week or, say, show the home items only in the evening of weekdays and not annoy me at work when I cannot do it any way. Hence, I can focus on the items I really need to do now in this agenda. Additionally, the time of the day after midnight is treated specially here. If =org-extend-today-until= is not 0 and the current time is before its value, the current time is still considered to be yesterday. - =:SHOWFROMDATE:= :: The purpose of this is to be able to postpone the scheduled tasks for future if I cannot do it. The property is formatted as an org date. This property is especially useful if there is something more pressing, so that there is a temptation to reschedule less pressing event to another day. If the more pressing task is done earlier than expected, the postponed tasks can be still find in normal agenda view (not in the [[id:ff70b03f-3876-4b2b-9aab-c3209bd31cb8][focused]] one). - =:SHOWDATES:= (always inheriting) :: It contains dairy =sexps= to set when the project should be shown. For example, I may want to work on Saturday once or twice, but the working items should not be shown on weekend normally. Hence, I can define it. Or some things can only be done on specific dates (say, going to some shop, which is open few days a week only) > org-entry-get is only called for the (property) predicate. It's the > correct way to get Org properties, because it handles special > properties, inheritance, etc. However, when possible, queries are > optimized to a whole-buffer regexp search that finds possible matches. > So, for example, a query like '(property "owner" "yantar") would be > optimized to a whole-buffer regexp search that would be very fast. See > function org-ql--query-preamble. Thanks for the info! I did not know about this optimisation in org-ql. org-entry-get consumes most of cpu time while building my agenda views. Though I don't think that there will be much difference for me since most of my property conditions in agenda involve inherited properties. Regards, Ihor Adam Porter <adam@alphapapa.net> writes: > Ihor Radchenko <yantar92@gmail.com> writes: > >>> org-ql doesn't use skip functions, just queries. >> >> Skip functions are essentially used-defined queries as soon as the >> queries are tested against every headline. > > Skip functions aren't necessary with org-ql, because the query itself > can have arbitrary Lisp code. So, of course, you can call custom > functions in queries, even your own skip functions (with `not', of > course), but in most cases, they can be covered with built-in > predicates. > >> I can rewrite my skip functions into queries, but I don't expect much >> improvement since org-ql seems to use org-entry-get, which is the main >> performance bottleneck for my agenda generation. > > org-entry-get is only called for the (property) predicate. It's the > correct way to get Org properties, because it handles special > properties, inheritance, etc. However, when possible, queries are > optimized to a whole-buffer regexp search that finds possible matches. > So, for example, a query like '(property "owner" "yantar") would be > optimized to a whole-buffer regexp search that would be very fast. See > function org-ql--query-preamble. > > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-16 7:23 ` Ihor Radchenko @ 2019-12-16 10:32 ` Adam Porter 0 siblings, 0 replies; 17+ messages in thread From: Adam Porter @ 2019-12-16 10:32 UTC (permalink / raw) To: emacs-orgmode Ihor Radchenko <yantar92@gmail.com> writes: >> So, of course, you can call custom functions in queries, even your >> own skip functions (with `not', of course), but in most cases, they >> can be covered with built-in predicates. > > Unfortunately, it does not seem to be the case for me. My main agenda > view needs to take into account multiple properties, which cannot be > handled within framework of built-in org-ql predicates (AFAIK): > > - =:SHOWFROMTIME:= (always inheriting) :: The purpose of this is to be > able to assign specific projects for different days of week or, > say, show the home items only in the evening of weekdays and not > annoy me at work when I cannot do it any way. Hence, I can focus on > the items I really need to do now in this agenda. Additionally, the > time of the day after midnight is treated specially here. If > =org-extend-today-until= is not 0 and the current time is before > its value, the current time is still considered to be yesterday. > - =:SHOWFROMDATE:= :: The purpose of this is to be able to postpone the > scheduled tasks for future if I cannot do it. The property is > formatted as an org date. This property is especially useful if > there is something more pressing, so that there is a temptation to > reschedule less pressing event to another day. If the more pressing > task is done earlier than expected, the postponed tasks can be > still find in normal agenda view (not in the > [[id:ff70b03f-3876-4b2b-9aab-c3209bd31cb8][focused]] one). > - =:SHOWDATES:= (always inheriting) :: It contains dairy =sexps= to set > when the project should be shown. For example, I may want to work on > Saturday once or twice, but the working items should not be shown on > weekend normally. Hence, I can define it. Or some things can only be > done on specific dates (say, going to some shop, which is open few > days a week only) There may still be some performance improvement available for these kind of queries. For example, assuming that a function `yantar/showfromdate' is a predicate which accepts the value of property SHOWFROMDATE as its argument and returns non-nil when an entry matches a query, an org-ql query like this could work: (and (property "SHOWFROMDATE") (yantar/showfromdate (property "SHOWFROMDATE"))) The first (property "SHOWFROMDATE") clause allows org-ql to optimize the query using a regexp preamble that jumps directly to entries having that property, while the second clause passes the value of the property to the custom predicate using the built-in predicate (property "SHOWFROMDATE") to retrieve the property from the org-ql node-value cache, which reduces the number of calls to org-entry-get. (That the `property' predicate returns the value of the property when called with only a property argument is a helpful feature that I should document...) > Thanks for the info! I did not know about this optimisation in org-ql. > org-entry-get consumes most of cpu time while building my agenda views. > Though I don't think that there will be much difference for me since > most of my property conditions in agenda involve inherited properties. For properties using inheritance, the preamble regexp can't be used to optimize the query across the buffer (I have some ideas about how to do that, maybe someday...), but the node-value cache can improve performance of retrieving the values of inherited properties within a buffer, because it avoids repeated calls to org-entry-get going up an outline tree. For example, see the benchmark here: https://github.com/alphapapa/org-ql/blob/master/notes.org#caching-of-inherited-tags The node-value cache, which is used for properties, uses essentially the same code as the tag cache benchmarked here (I haven't yet converted the tag cache to use the generic node-value cache), and it provides about a 6x speedup, so property-inheritance-based queries should benefit similarly. So it sounds like your custom agendas would benefit from being written as org-ql queries. Please let me know if you try it, as your examples would give the code a good workout and might reveal some improvements to be made. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-12 8:18 Asynchronous org-agenda-redo Ihor Radchenko 2019-12-12 12:17 ` Adam Porter @ 2019-12-12 12:51 ` Diego Zamboni 2019-12-12 14:58 ` Ihor Radchenko 1 sibling, 1 reply; 17+ messages in thread From: Diego Zamboni @ 2019-12-12 12:51 UTC (permalink / raw) To: Ihor Radchenko; +Cc: emacs-orgmode Mailinglist [-- Attachment #1: Type: text/plain, Size: 1624 bytes --] Hi Ihor, I cannot answer your question, but I am curious about using async together with tangling, since for some of my buffers, tangling takes some time and freezes Emacs in the process. Do you have some examples of this that you could share? Thanks, --Diego On Thu, Dec 12, 2019 at 9:21 AM Ihor Radchenko <yantar92@gmail.com> wrote: > I am thinking if it is possible to implement org-agenda-redo > asynchronously. > > Rebuilding agenda should normally not affect any buffer except agenda > buffer. So, it should be sufficient to block any agenda modifying > commands in the agenda buffer, redo the agenda buffer in separate > thread, and replace the old agenda with the calculated one. > Then, emacs should remain responsive while updating agenda (except for > modifying the agenda buffer). > > For example, this naive code kind of works (forgetting that buffer-local > variables will not be passed to the thread): > > (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) > (make-thread oldfun "org-agenda-redo")) > > The problem is that emacs does not become responsive... > > Another approach would be using async.el package, which allows calling > arbitrary function in subordinate emacs process. Then, the main emacs > instance should not be "frozen" (I use same approach for tangling and it > works fine). > > However, the question is how to pass the .org and agenda buffers to this > subordinate process. Opening the .org files there is not a good option > since it would give too much overhead to this asynchronous agenda. > > Any suggestions? Alternative ideas? > > Best, > Ihor > > > [-- Attachment #2: Type: text/html, Size: 2109 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-agenda-redo 2019-12-12 12:51 ` Diego Zamboni @ 2019-12-12 14:58 ` Ihor Radchenko 2019-12-15 11:56 ` Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) Diego Zamboni 0 siblings, 1 reply; 17+ messages in thread From: Ihor Radchenko @ 2019-12-12 14:58 UTC (permalink / raw) To: Diego Zamboni; +Cc: emacs-orgmode Mailinglist Hi Diego, > I cannot answer your question, but I am curious about using async together > with tangling, since for some of my buffers, tangling takes some time and > freezes Emacs in the process. Do you have some examples of this that you > could share? See the relevant code from my config below. Let me know if you have any questions or suggestions. Also, my config is written in the way that everything related to user interaction can be disabled if I load emacs for tangling (with org-tangle-flag). This is to speed up the async process. #+begin_src emacs-lisp (defvar yant/org-babel-tangle-async-process-list nil "Plist of (file . process) for all the currently running async tangle processes.") (defun yant/org-babel-tangle-async (file &optional target-file lang) "Invoke `org-babel-tangle-file' asynchronously." (require 'async) (let ((oldproc (plist-get yant/org-babel-tangle-async-process-list file))) (when (or (not oldproc) (async-wait oldproc)) (message "Tangling %s..." (buffer-file-name)) (plist-put yant/org-babel-tangle-async-process-list file (async-start (let ((args (list file target-file lang))) `(lambda () (require 'org) (setq org-tangle-flag t) (load "~/.emacs.d/config.el") (apply #'org-babel-tangle-file ',args))) (let ((message-string (format "Tangling (%S %S %S) completed." file target-file lang))) `(lambda (result) (message ,message-string)))))))) (defvar yant/auto-tangle-list nil "List of files, which can be safely tangled on save. The list is saved between Emacs sessions.") (when init-flag (use-package savehist :config (add-to-list 'savehist-additional-variables 'yant/auto-tangle-list)) (savehist-mode +1) (defun yant/toggle-buffer-auto-tangle (arg) "Toggle auto tangling of a buffer." (interactive "P") (if (not (eq major-mode 'org-mode)) (message "Org-mode is not active in buffer \"%s\"" (buffer-name)) (cond ((not arg) (if (member (buffer-file-name) yant/auto-tangle-list) (progn (setq yant/auto-tangle-list (delete (buffer-file-name) yant/auto-tangle-list)) (message "Auto tangling disabled for %s" (buffer-file-name))) (add-to-list 'yant/auto-tangle-list (buffer-file-name)) (message "Auto tangling enabled for %s" (buffer-file-name)))) ((or (and (not (listp arg)) (> arg 0)) (equal arg '(4))) (add-to-list 'yant/auto-tangle-list (buffer-file-name)) (message "Auto tangling enabled for %s" (buffer-file-name))) (t (setq yant/auto-tangle-list (delete (buffer-file-name) yant/auto-tangle-list)) (message "Auto tangling disabled for %s" (buffer-file-name)))))) (bind-key "C-c C-*" #'yant/toggle-buffer-auto-tangle org-mode-map)) (defun yant/org-babel-tangle-current-buffer-async () "Tangle current buffer asynchronously." (when (and (eq major-mode 'org-mode) (member (buffer-file-name) yant/auto-tangle-list)) (yant/org-babel-tangle-async (buffer-file-name)))) (add-hook 'after-save-hook #'yant/org-babel-tangle-current-buffer-async) #+end_src Diego Zamboni <diego@zzamboni.org> writes: > Hi Ihor, > > I cannot answer your question, but I am curious about using async together > with tangling, since for some of my buffers, tangling takes some time and > freezes Emacs in the process. Do you have some examples of this that you > could share? > > Thanks, > --Diego > > > On Thu, Dec 12, 2019 at 9:21 AM Ihor Radchenko <yantar92@gmail.com> wrote: > >> I am thinking if it is possible to implement org-agenda-redo >> asynchronously. >> >> Rebuilding agenda should normally not affect any buffer except agenda >> buffer. So, it should be sufficient to block any agenda modifying >> commands in the agenda buffer, redo the agenda buffer in separate >> thread, and replace the old agenda with the calculated one. >> Then, emacs should remain responsive while updating agenda (except for >> modifying the agenda buffer). >> >> For example, this naive code kind of works (forgetting that buffer-local >> variables will not be passed to the thread): >> >> (define-advice org-agenda-redo (:around (oldfun &optional all) make-async) >> (make-thread oldfun "org-agenda-redo")) >> >> The problem is that emacs does not become responsive... >> >> Another approach would be using async.el package, which allows calling >> arbitrary function in subordinate emacs process. Then, the main emacs >> instance should not be "frozen" (I use same approach for tangling and it >> works fine). >> >> However, the question is how to pass the .org and agenda buffers to this >> subordinate process. Opening the .org files there is not a good option >> since it would give too much overhead to this asynchronous agenda. >> >> Any suggestions? Alternative ideas? >> >> Best, >> Ihor >> >> >> -- Ihor Radchenko, PhD, Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong University, Xi'an, China Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg ^ permalink raw reply [flat|nested] 17+ messages in thread
* Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) 2019-12-12 14:58 ` Ihor Radchenko @ 2019-12-15 11:56 ` Diego Zamboni 2019-12-15 13:40 ` Ihor Radchenko 2019-12-15 13:41 ` Ihor Radchenko 0 siblings, 2 replies; 17+ messages in thread From: Diego Zamboni @ 2019-12-15 11:56 UTC (permalink / raw) To: Ihor Radchenko; +Cc: emacs-orgmode Mailinglist [-- Attachment #1: Type: text/plain, Size: 7593 bytes --] Hi Ihor, On Thu, Dec 12, 2019 at 4:00 PM Ihor Radchenko <yantar92@gmail.com> wrote: > See the relevant code from my config below. Let me know if you have any > questions or suggestions. > Thank you, this was extremely helpful. I am now using a modified version of your code (see below) with the following comments/questions: - For me, the tangle works if I only load the org library in the sub-process, so I disabled the loading of my config. I guess this depends heavily and what other configuration one has loaded, but I think for tangling, not much else is needed :) - In you code, the process-list variable was not actually being modified since plist-put returns the new value but does not modify the variable. I tried a few variations wrapping the plist-put in (setq ...), and adding corresponding code to clear the element after the tangle was finished, but could not get it to work. As this is not really critical for me, I just removed the whole process-checking code, significantly simplifying the rest. - I wanted my code to report the time the tangle took. This works well by passing the time as the result of the initial lambda. Interestingly, it works if I return the time already formatted as a string (as in my code) but not if I return the result of (float-time ..) directly. Here's my code: #+begin_src emacs-lisp (defun zz/org-babel-tangle-async (file) "Invoke `org-babel-tangle-file' asynchronously." (message "Tangling %s..." (buffer-file-name)) (async-start (let ((args (list file))) `(lambda () (require 'org) ;;(load "~/.emacs.d/init.el") (let ((start-time (current-time))) (apply #'org-babel-tangle-file ',args) (format "%.2f" (float-time (time-since start-time)))))) (let ((message-string (format "Tangling %S completed after " file))) `(lambda (tangle-time) (message (concat ,message-string (format "%s seconds" tangle-time))))))) (defun zz/org-babel-tangle-current-buffer-async () "Tangle current buffer asynchronously." (zz/org-babel-tangle-async (buffer-file-name))) #+end_src Thanks again for your help, and for the inspiration to finally get this done, it had been bugging me for a while :) --Diego > Also, my config is written in the way that everything related to user > interaction can be disabled if I load emacs for tangling > (with org-tangle-flag). This is to speed up the async process. > > #+begin_src emacs-lisp > (defvar yant/org-babel-tangle-async-process-list nil > "Plist of (file . process) for all the currently running async tangle > processes.") > > > (defun yant/org-babel-tangle-async (file &optional target-file lang) > "Invoke `org-babel-tangle-file' asynchronously." > (require 'async) > (let ((oldproc (plist-get yant/org-babel-tangle-async-process-list > file))) > (when (or (not oldproc) > (async-wait oldproc)) > (message "Tangling %s..." (buffer-file-name)) > (plist-put yant/org-babel-tangle-async-process-list > file > (async-start > (let ((args (list file target-file lang))) > `(lambda () > (require 'org) > (setq org-tangle-flag t) > (load "~/.emacs.d/config.el") > (apply #'org-babel-tangle-file ',args))) > (let ((message-string (format "Tangling (%S %S %S) > completed." file target-file lang))) > `(lambda (result) (message ,message-string)))))))) > > (defvar yant/auto-tangle-list nil > "List of files, which can be safely tangled on save. > The list is saved between Emacs sessions.") > > (when init-flag > (use-package savehist > :config > (add-to-list 'savehist-additional-variables 'yant/auto-tangle-list)) > (savehist-mode +1) > (defun yant/toggle-buffer-auto-tangle (arg) > "Toggle auto tangling of a buffer." > (interactive "P") > (if (not (eq major-mode 'org-mode)) > (message "Org-mode is not active in buffer \"%s\"" (buffer-name)) > (cond ((not arg) > (if (member (buffer-file-name) yant/auto-tangle-list) > (progn (setq yant/auto-tangle-list (delete > (buffer-file-name) yant/auto-tangle-list)) > (message "Auto tangling disabled for %s" > (buffer-file-name))) > (add-to-list 'yant/auto-tangle-list (buffer-file-name)) > (message "Auto tangling enabled for %s" > (buffer-file-name)))) > ((or (and (not (listp arg)) (> arg 0)) > (equal arg '(4))) > (add-to-list 'yant/auto-tangle-list (buffer-file-name)) > (message "Auto tangling enabled for %s" (buffer-file-name))) > (t > (setq yant/auto-tangle-list (delete (buffer-file-name) > yant/auto-tangle-list)) > (message "Auto tangling disabled for %s" > (buffer-file-name)))))) > > (bind-key "C-c C-*" #'yant/toggle-buffer-auto-tangle org-mode-map)) > > (defun yant/org-babel-tangle-current-buffer-async () > "Tangle current buffer asynchronously." > (when (and (eq major-mode 'org-mode) > (member (buffer-file-name) yant/auto-tangle-list)) > (yant/org-babel-tangle-async (buffer-file-name)))) > > (add-hook 'after-save-hook #'yant/org-babel-tangle-current-buffer-async) > #+end_src > > > Diego Zamboni <diego@zzamboni.org> writes: > > > Hi Ihor, > > > > I cannot answer your question, but I am curious about using async > together > > with tangling, since for some of my buffers, tangling takes some time and > > freezes Emacs in the process. Do you have some examples of this that you > > could share? > > > > Thanks, > > --Diego > > > > > > On Thu, Dec 12, 2019 at 9:21 AM Ihor Radchenko <yantar92@gmail.com> > wrote: > > > >> I am thinking if it is possible to implement org-agenda-redo > >> asynchronously. > >> > >> Rebuilding agenda should normally not affect any buffer except agenda > >> buffer. So, it should be sufficient to block any agenda modifying > >> commands in the agenda buffer, redo the agenda buffer in separate > >> thread, and replace the old agenda with the calculated one. > >> Then, emacs should remain responsive while updating agenda (except for > >> modifying the agenda buffer). > >> > >> For example, this naive code kind of works (forgetting that buffer-local > >> variables will not be passed to the thread): > >> > >> (define-advice org-agenda-redo (:around (oldfun &optional all) > make-async) > >> (make-thread oldfun "org-agenda-redo")) > >> > >> The problem is that emacs does not become responsive... > >> > >> Another approach would be using async.el package, which allows calling > >> arbitrary function in subordinate emacs process. Then, the main emacs > >> instance should not be "frozen" (I use same approach for tangling and it > >> works fine). > >> > >> However, the question is how to pass the .org and agenda buffers to this > >> subordinate process. Opening the .org files there is not a good option > >> since it would give too much overhead to this asynchronous agenda. > >> > >> Any suggestions? Alternative ideas? > >> > >> Best, > >> Ihor > >> > >> > >> > > -- > Ihor Radchenko, > PhD, > Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) > State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong > University, Xi'an, China > Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg > [-- Attachment #2: Type: text/html, Size: 9846 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) 2019-12-15 11:56 ` Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) Diego Zamboni @ 2019-12-15 13:40 ` Ihor Radchenko 2019-12-15 13:41 ` Ihor Radchenko 1 sibling, 0 replies; 17+ messages in thread From: Ihor Radchenko @ 2019-12-15 13:40 UTC (permalink / raw) To: Diego Zamboni; +Cc: emacs-orgmode Mailinglist Thanks for the feedback. > - For me, the tangle works if I only load the org library in the > sub-process, so I disabled the loading of my config. I guess this depends > heavily and what other configuration one has loaded, but I think for > tangling, not much else is needed :) Note that your version tangling may not work as expected if your init file changes any default headers args affecting the tangling. For example, I set (:comments . "link") in my init file. Not loading init file would disallow detangling in my case. > - In you code, the process-list variable was not actually being modified > since plist-put returns the new value but does not modify the > variable. (facepalm) > I > tried a few variations wrapping the plist-put in (setq ...), and adding > corresponding code to clear the element after the tangle was finished, but > could not get it to work. Well. Things did not work because plist-put compares keys by #'eq, which returns nil on equal strings. > - I wanted my code to report the time the tangle took. This works well by > passing the time as the result of the initial lambda. Interestingly, it > works if I return the time already formatted as a string (as in my code) > but not if I return the result of (float-time ..) directly. Thanks for this neat idea. Will also grab it to my config. float-time did not work because of bug in async.el. It uses backward-sexp to find the returned value of the lambda (in the process buffer). But backwards-sexp fails on float (it moves point just after the point). Best, Ihor Diego Zamboni <diego@zzamboni.org> writes: > Hi Ihor, > > On Thu, Dec 12, 2019 at 4:00 PM Ihor Radchenko <yantar92@gmail.com> wrote: > >> See the relevant code from my config below. Let me know if you have any >> questions or suggestions. >> > > Thank you, this was extremely helpful. I am now using a modified version of > your code (see below) with the following comments/questions: > > - For me, the tangle works if I only load the org library in the > sub-process, so I disabled the loading of my config. I guess this depends > heavily and what other configuration one has loaded, but I think for > tangling, not much else is needed :) > > - In you code, the process-list variable was not actually being modified > since plist-put returns the new value but does not modify the variable. I > tried a few variations wrapping the plist-put in (setq ...), and adding > corresponding code to clear the element after the tangle was finished, but > could not get it to work. As this is not really critical for me, I just > removed the whole process-checking code, significantly simplifying the rest. > > - I wanted my code to report the time the tangle took. This works well by > passing the time as the result of the initial lambda. Interestingly, it > works if I return the time already formatted as a string (as in my code) > but not if I return the result of (float-time ..) directly. > > Here's my code: > > #+begin_src emacs-lisp > (defun zz/org-babel-tangle-async (file) > "Invoke `org-babel-tangle-file' asynchronously." > (message "Tangling %s..." (buffer-file-name)) > (async-start > (let ((args (list file))) > `(lambda () > (require 'org) > ;;(load "~/.emacs.d/init.el") > (let ((start-time (current-time))) > (apply #'org-babel-tangle-file ',args) > (format "%.2f" (float-time (time-since start-time)))))) > (let ((message-string (format "Tangling %S completed after " file))) > `(lambda (tangle-time) > (message (concat ,message-string > (format "%s seconds" tangle-time))))))) > > (defun zz/org-babel-tangle-current-buffer-async () > "Tangle current buffer asynchronously." > (zz/org-babel-tangle-async (buffer-file-name))) > #+end_src > > Thanks again for your help, and for the inspiration to finally get this > done, it had been bugging me for a while :) > > --Diego > > > >> Also, my config is written in the way that everything related to user >> interaction can be disabled if I load emacs for tangling >> (with org-tangle-flag). This is to speed up the async process. >> >> #+begin_src emacs-lisp >> (defvar yant/org-babel-tangle-async-process-list nil >> "Plist of (file . process) for all the currently running async tangle >> processes.") >> >> >> (defun yant/org-babel-tangle-async (file &optional target-file lang) >> "Invoke `org-babel-tangle-file' asynchronously." >> (require 'async) >> (let ((oldproc (plist-get yant/org-babel-tangle-async-process-list >> file))) >> (when (or (not oldproc) >> (async-wait oldproc)) >> (message "Tangling %s..." (buffer-file-name)) >> (plist-put yant/org-babel-tangle-async-process-list >> file >> (async-start >> (let ((args (list file target-file lang))) >> `(lambda () >> (require 'org) >> (setq org-tangle-flag t) >> (load "~/.emacs.d/config.el") >> (apply #'org-babel-tangle-file ',args))) >> (let ((message-string (format "Tangling (%S %S %S) >> completed." file target-file lang))) >> `(lambda (result) (message ,message-string)))))))) >> >> (defvar yant/auto-tangle-list nil >> "List of files, which can be safely tangled on save. >> The list is saved between Emacs sessions.") >> >> (when init-flag >> (use-package savehist >> :config >> (add-to-list 'savehist-additional-variables 'yant/auto-tangle-list)) >> (savehist-mode +1) >> (defun yant/toggle-buffer-auto-tangle (arg) >> "Toggle auto tangling of a buffer." >> (interactive "P") >> (if (not (eq major-mode 'org-mode)) >> (message "Org-mode is not active in buffer \"%s\"" (buffer-name)) >> (cond ((not arg) >> (if (member (buffer-file-name) yant/auto-tangle-list) >> (progn (setq yant/auto-tangle-list (delete >> (buffer-file-name) yant/auto-tangle-list)) >> (message "Auto tangling disabled for %s" >> (buffer-file-name))) >> (add-to-list 'yant/auto-tangle-list (buffer-file-name)) >> (message "Auto tangling enabled for %s" >> (buffer-file-name)))) >> ((or (and (not (listp arg)) (> arg 0)) >> (equal arg '(4))) >> (add-to-list 'yant/auto-tangle-list (buffer-file-name)) >> (message "Auto tangling enabled for %s" (buffer-file-name))) >> (t >> (setq yant/auto-tangle-list (delete (buffer-file-name) >> yant/auto-tangle-list)) >> (message "Auto tangling disabled for %s" >> (buffer-file-name)))))) >> >> (bind-key "C-c C-*" #'yant/toggle-buffer-auto-tangle org-mode-map)) >> >> (defun yant/org-babel-tangle-current-buffer-async () >> "Tangle current buffer asynchronously." >> (when (and (eq major-mode 'org-mode) >> (member (buffer-file-name) yant/auto-tangle-list)) >> (yant/org-babel-tangle-async (buffer-file-name)))) >> >> (add-hook 'after-save-hook #'yant/org-babel-tangle-current-buffer-async) >> #+end_src >> >> >> Diego Zamboni <diego@zzamboni.org> writes: >> >> > Hi Ihor, >> > >> > I cannot answer your question, but I am curious about using async >> together >> > with tangling, since for some of my buffers, tangling takes some time and >> > freezes Emacs in the process. Do you have some examples of this that you >> > could share? >> > >> > Thanks, >> > --Diego >> > >> > >> > On Thu, Dec 12, 2019 at 9:21 AM Ihor Radchenko <yantar92@gmail.com> >> wrote: >> > >> >> I am thinking if it is possible to implement org-agenda-redo >> >> asynchronously. >> >> >> >> Rebuilding agenda should normally not affect any buffer except agenda >> >> buffer. So, it should be sufficient to block any agenda modifying >> >> commands in the agenda buffer, redo the agenda buffer in separate >> >> thread, and replace the old agenda with the calculated one. >> >> Then, emacs should remain responsive while updating agenda (except for >> >> modifying the agenda buffer). >> >> >> >> For example, this naive code kind of works (forgetting that buffer-local >> >> variables will not be passed to the thread): >> >> >> >> (define-advice org-agenda-redo (:around (oldfun &optional all) >> make-async) >> >> (make-thread oldfun "org-agenda-redo")) >> >> >> >> The problem is that emacs does not become responsive... >> >> >> >> Another approach would be using async.el package, which allows calling >> >> arbitrary function in subordinate emacs process. Then, the main emacs >> >> instance should not be "frozen" (I use same approach for tangling and it >> >> works fine). >> >> >> >> However, the question is how to pass the .org and agenda buffers to this >> >> subordinate process. Opening the .org files there is not a good option >> >> since it would give too much overhead to this asynchronous agenda. >> >> >> >> Any suggestions? Alternative ideas? >> >> >> >> Best, >> >> Ihor >> >> >> >> >> >> >> >> -- >> Ihor Radchenko, >> PhD, >> Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) >> State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong >> University, Xi'an, China >> Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg >> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) 2019-12-15 11:56 ` Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) Diego Zamboni 2019-12-15 13:40 ` Ihor Radchenko @ 2019-12-15 13:41 ` Ihor Radchenko 1 sibling, 0 replies; 17+ messages in thread From: Ihor Radchenko @ 2019-12-15 13:41 UTC (permalink / raw) To: Diego Zamboni; +Cc: emacs-orgmode Mailinglist Thanks for the feedback. > - For me, the tangle works if I only load the org library in the > sub-process, so I disabled the loading of my config. I guess this depends > heavily and what other configuration one has loaded, but I think for > tangling, not much else is needed :) Note that your version tangling may not work as expected if your init file changes any default headers args affecting the tangling. For example, I set (:comments . "link") in my init file. Not loading init file would disallow detangling in my case. > - In you code, the process-list variable was not actually being modified > since plist-put returns the new value but does not modify the > variable. (facepalm) > I > tried a few variations wrapping the plist-put in (setq ...), and adding > corresponding code to clear the element after the tangle was finished, but > could not get it to work. Well. Things did not work because plist-put compares keys by #'eq, which returns nil on equal strings. > - I wanted my code to report the time the tangle took. This works well by > passing the time as the result of the initial lambda. Interestingly, it > works if I return the time already formatted as a string (as in my code) > but not if I return the result of (float-time ..) directly. Thanks for this neat idea. Will also grab it to my config. float-time did not work because of bug in async.el. It uses backward-sexp to find the returned value of the lambda (in the process buffer). But backwards-sexp fails on float (it moves point just after the point). P.S. I just noticed that async tangle fails when I try to tangle into "/sudo::filename". Not even sure how to fix it. Best, Ihor Diego Zamboni <diego@zzamboni.org> writes: > Hi Ihor, > > On Thu, Dec 12, 2019 at 4:00 PM Ihor Radchenko <yantar92@gmail.com> wrote: > >> See the relevant code from my config below. Let me know if you have any >> questions or suggestions. >> > > Thank you, this was extremely helpful. I am now using a modified version of > your code (see below) with the following comments/questions: > > - For me, the tangle works if I only load the org library in the > sub-process, so I disabled the loading of my config. I guess this depends > heavily and what other configuration one has loaded, but I think for > tangling, not much else is needed :) > > - In you code, the process-list variable was not actually being modified > since plist-put returns the new value but does not modify the variable. I > tried a few variations wrapping the plist-put in (setq ...), and adding > corresponding code to clear the element after the tangle was finished, but > could not get it to work. As this is not really critical for me, I just > removed the whole process-checking code, significantly simplifying the rest. > > - I wanted my code to report the time the tangle took. This works well by > passing the time as the result of the initial lambda. Interestingly, it > works if I return the time already formatted as a string (as in my code) > but not if I return the result of (float-time ..) directly. > > Here's my code: > > #+begin_src emacs-lisp > (defun zz/org-babel-tangle-async (file) > "Invoke `org-babel-tangle-file' asynchronously." > (message "Tangling %s..." (buffer-file-name)) > (async-start > (let ((args (list file))) > `(lambda () > (require 'org) > ;;(load "~/.emacs.d/init.el") > (let ((start-time (current-time))) > (apply #'org-babel-tangle-file ',args) > (format "%.2f" (float-time (time-since start-time)))))) > (let ((message-string (format "Tangling %S completed after " file))) > `(lambda (tangle-time) > (message (concat ,message-string > (format "%s seconds" tangle-time))))))) > > (defun zz/org-babel-tangle-current-buffer-async () > "Tangle current buffer asynchronously." > (zz/org-babel-tangle-async (buffer-file-name))) > #+end_src > > Thanks again for your help, and for the inspiration to finally get this > done, it had been bugging me for a while :) > > --Diego > > > >> Also, my config is written in the way that everything related to user >> interaction can be disabled if I load emacs for tangling >> (with org-tangle-flag). This is to speed up the async process. >> >> #+begin_src emacs-lisp >> (defvar yant/org-babel-tangle-async-process-list nil >> "Plist of (file . process) for all the currently running async tangle >> processes.") >> >> >> (defun yant/org-babel-tangle-async (file &optional target-file lang) >> "Invoke `org-babel-tangle-file' asynchronously." >> (require 'async) >> (let ((oldproc (plist-get yant/org-babel-tangle-async-process-list >> file))) >> (when (or (not oldproc) >> (async-wait oldproc)) >> (message "Tangling %s..." (buffer-file-name)) >> (plist-put yant/org-babel-tangle-async-process-list >> file >> (async-start >> (let ((args (list file target-file lang))) >> `(lambda () >> (require 'org) >> (setq org-tangle-flag t) >> (load "~/.emacs.d/config.el") >> (apply #'org-babel-tangle-file ',args))) >> (let ((message-string (format "Tangling (%S %S %S) >> completed." file target-file lang))) >> `(lambda (result) (message ,message-string)))))))) >> >> (defvar yant/auto-tangle-list nil >> "List of files, which can be safely tangled on save. >> The list is saved between Emacs sessions.") >> >> (when init-flag >> (use-package savehist >> :config >> (add-to-list 'savehist-additional-variables 'yant/auto-tangle-list)) >> (savehist-mode +1) >> (defun yant/toggle-buffer-auto-tangle (arg) >> "Toggle auto tangling of a buffer." >> (interactive "P") >> (if (not (eq major-mode 'org-mode)) >> (message "Org-mode is not active in buffer \"%s\"" (buffer-name)) >> (cond ((not arg) >> (if (member (buffer-file-name) yant/auto-tangle-list) >> (progn (setq yant/auto-tangle-list (delete >> (buffer-file-name) yant/auto-tangle-list)) >> (message "Auto tangling disabled for %s" >> (buffer-file-name))) >> (add-to-list 'yant/auto-tangle-list (buffer-file-name)) >> (message "Auto tangling enabled for %s" >> (buffer-file-name)))) >> ((or (and (not (listp arg)) (> arg 0)) >> (equal arg '(4))) >> (add-to-list 'yant/auto-tangle-list (buffer-file-name)) >> (message "Auto tangling enabled for %s" (buffer-file-name))) >> (t >> (setq yant/auto-tangle-list (delete (buffer-file-name) >> yant/auto-tangle-list)) >> (message "Auto tangling disabled for %s" >> (buffer-file-name)))))) >> >> (bind-key "C-c C-*" #'yant/toggle-buffer-auto-tangle org-mode-map)) >> >> (defun yant/org-babel-tangle-current-buffer-async () >> "Tangle current buffer asynchronously." >> (when (and (eq major-mode 'org-mode) >> (member (buffer-file-name) yant/auto-tangle-list)) >> (yant/org-babel-tangle-async (buffer-file-name)))) >> >> (add-hook 'after-save-hook #'yant/org-babel-tangle-current-buffer-async) >> #+end_src >> >> >> Diego Zamboni <diego@zzamboni.org> writes: >> >> > Hi Ihor, >> > >> > I cannot answer your question, but I am curious about using async >> together >> > with tangling, since for some of my buffers, tangling takes some time and >> > freezes Emacs in the process. Do you have some examples of this that you >> > could share? >> > >> > Thanks, >> > --Diego >> > >> > >> > On Thu, Dec 12, 2019 at 9:21 AM Ihor Radchenko <yantar92@gmail.com> >> wrote: >> > >> >> I am thinking if it is possible to implement org-agenda-redo >> >> asynchronously. >> >> >> >> Rebuilding agenda should normally not affect any buffer except agenda >> >> buffer. So, it should be sufficient to block any agenda modifying >> >> commands in the agenda buffer, redo the agenda buffer in separate >> >> thread, and replace the old agenda with the calculated one. >> >> Then, emacs should remain responsive while updating agenda (except for >> >> modifying the agenda buffer). >> >> >> >> For example, this naive code kind of works (forgetting that buffer-local >> >> variables will not be passed to the thread): >> >> >> >> (define-advice org-agenda-redo (:around (oldfun &optional all) >> make-async) >> >> (make-thread oldfun "org-agenda-redo")) >> >> >> >> The problem is that emacs does not become responsive... >> >> >> >> Another approach would be using async.el package, which allows calling >> >> arbitrary function in subordinate emacs process. Then, the main emacs >> >> instance should not be "frozen" (I use same approach for tangling and it >> >> works fine). >> >> >> >> However, the question is how to pass the .org and agenda buffers to this >> >> subordinate process. Opening the .org files there is not a good option >> >> since it would give too much overhead to this asynchronous agenda. >> >> >> >> Any suggestions? Alternative ideas? >> >> >> >> Best, >> >> Ihor >> >> >> >> >> >> >> >> -- >> Ihor Radchenko, >> PhD, >> Center for Advancing Materials Performance from the Nanoscale (CAMP-nano) >> State Key Laboratory for Mechanical Behavior of Materials, Xi'an Jiaotong >> University, Xi'an, China >> Email: yantar92@gmail.com, ihor_radchenko@alumni.sutd.edu.sg >> ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2019-12-24 0:36 UTC | newest] Thread overview: 17+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-12-12 8:18 Asynchronous org-agenda-redo Ihor Radchenko 2019-12-12 12:17 ` Adam Porter 2019-12-12 15:46 ` Ihor Radchenko 2019-12-13 6:49 ` Adam Porter 2019-12-13 8:35 ` Ihor Radchenko 2019-12-13 9:39 ` Ihor Radchenko 2019-12-14 4:59 ` Adam Porter 2019-12-22 6:54 ` Ihor Radchenko 2019-12-24 0:36 ` Adam Porter 2019-12-14 4:50 ` Adam Porter 2019-12-16 7:23 ` Ihor Radchenko 2019-12-16 10:32 ` Adam Porter 2019-12-12 12:51 ` Diego Zamboni 2019-12-12 14:58 ` Ihor Radchenko 2019-12-15 11:56 ` Asynchronous org-babel-tangle (was Re: Asynchronous org-agenda-redo) Diego Zamboni 2019-12-15 13:40 ` Ihor Radchenko 2019-12-15 13:41 ` Ihor Radchenko
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).