emacs有哪些可用的异步计算解决方案

我目前在做一个文档阅读器,它需要把文档的每一页渲染成图片然后在emacs里显示出来。目前支持平滑翻页,就是能在一个窗口里同时显示连续的多个页面。因为它只渲染当前页面和相邻的几个页面,所滚动还算流畅。

但是我遇到个问题,就是每当窗口里出现一个未曾渲染过的新页面,emacs会卡顿一下,时间在0.1到0.2秒之间。这个时间接近dynamic module里的c函数渲染页面的时间。 为此我在c代码里加上了缓存,卡顿时间缩短到0.01到0.02秒之间。翻页的时候,体验明显改善,但是仍然能感受到短暂的的卡顿。

我猜想仍然存在卡顿的原因是emacs处理新图片的过程有点慢(或许跟image cache有关)。它见过这个图片之后,再显示起来会快很多。

我想如果能在显示一个页面的同时,预先加载后续的几页,卡顿现象应该能完全消除。这个加载过程必须用elisp做,让emacs把图片放到image cache里。

我尝试了 run-with-idle-timer 和 emacs-async。run-with-idle-timer执行我写的elisp函数时会crash,查看core文件可以看到c代码中出现了空指针。我不清楚run-with-idle-timer到底是怎么实现的,crash或许跟它的实现有关。emacs-async则完全不能识别我写的elisp函数,说符号未定义,估计也跟它的实现有关。

在c代码里执行elisp代码恐怕不行,我之前做其他事的时候发现在c代码里开线程使用emacs传过来的env和args也会导致崩溃。

我不知道现在还有什么可用的实现异步计算方法。

我对elisp了解不多,希望有热心道友能指点一下。谢谢!

1 个赞

正好我前几天在Emacs-devel上看过类似的问题。

https://lists.gnu.org/archive/html/emacs-devel/2020-10/msg01746.html

简单来说,按传统的方法,不能。emacs_env这个结构不是线程安全的,这和Emacs的一些设计缺陷有关系,所以不能在多线程环境下从module执行lisp code。

Emacs目前提供的唯一异步信号处理工具就是process-filter. 因此,可以利用unix socket/web socket/或者28新加入的具名管道支持。通过这个信号通道发送一个byte给Emacs。同时在module里暴露类似(module-get-next-async-state)的函数,让filter执行这个函数。从而在多线程环境下异步通知Emacs。

3 个赞

如果是 dynamic module 的话,里面可以开线程去做其它的事吗?

可以,但是不能跟emacs交互。

是 PDF 的文档阅读器吗?

首先,先不讨论Emacs, 只说PDF加载图片有两种方法:

  1. 像Gnome PDF 阅读器那样,所有图片一次性全部用多线程读出来,边打开边读取,速度最快,因为都在内存,但是内存占用大;
  2. 像EAF PDF Viewer那样,只读取当前页上下两页放进缓存,这样会兼顾速度和内存占用

你的问题是,因为Emacs没有真正的多线程,没法让主线程(渲染图片)和子线程(读取图片缓存)来回快速切换以避免卡顿的问题,因为Emacs一个时刻只能运行一个线程,当切换到子线程后就会卡住主线程,反之亦然。

解决方法:

  1. 在Linux平台,直接用EAF吧,你想要的缓存算法和PDF阅读器已经实现了;
  2. 采用多进程的方式,Emacs启动子进程,子进程在后面读取图片缓存,Emacs通过进程间通讯,Emacs通过接受子进程传过来的文件名来加载图片,这种方法可以避免Emacs进程自己读取图片的卡顿。

如果你用第二个方法,在你写多进程通讯之前,我强烈建议你像写Demo程序,就是让Emacs读取一些列图片,看看翻页时Emacs读取新图片的时候卡不卡?

如果读取新图片的时候卡,你即使用多进程的方法依然无法解决你翻页的时候短暂卡端的问题,因为瓶颈就在于Emacs读取一个图片并渲染的周期超过 30ms 。

Emacs真正的瓶颈在于不是真正的多线程和Emacs自身的渲染引擎太慢导致的。

在Linux就用EAF吧,不但浏览器、 PDF阅读器异常流畅,即使看视频都毫无卡顿。

我折腾Linux下各种图形库的编程很多年了,放弃折腾Emacs的多线程吧,这也是我创建EAF的原因。

5 个赞

是的,pdf阅读器。

  1. EAF在我的机器上无法使用,因为我用了i3wm。EAF的issue列表现在有三个问题,其中一个是EAF在i3wm等桌面环境下不能响应按键操作。虽然这是i3wm的锅,但是我不打算为了EAF换掉i3wm。

  2. 我在dynamic module里做了多线程渲染图片(从pdf到png)。这一步在之前是瓶颈,不过现在不是了。emacs加载 全新的 图片确实慢,虽然只有几十毫秒,但是仍然有卡顿感。为了解决这个问题,我把大图片切成许多小长条,分段加载。现在已经感受不到卡顿了。

牛逼,黑科技在手,天下我有

求项目地址,观摩一下。

其实我也一直想写个 pdf-mode,就是懒只创建了 repo 一直没行动 :sob:

我现在还在试验阶段,代码结构经常大变。这几天被老师捉去干活,几乎没什么进展。等基本功能完善了我再推到github上。

楼主有没有考虑过像 EAF 类似的 reparent 方案?