终于有人出来解决Emacs处理外部进程输出速度慢的问题了

链接

按照作者的说法基本达到了和终端一致的速度。期待这个改进早日进入上游。

18 个赞

期待啊。

有相关的邮件列表讨论吗?

感觉 eglot 之类的包应该也是能够从这里获益的吧?

我觉得所有读外部进程输出的elisp代码都可以受益。原来的外部输出处理是全部在主线程里完成的,严重阻塞了外部进程的输出速度。现在这个改进把外部输出处理放到独立的线程里,可以允许外部进程全速运行,不被elisp的处理速度制约。

2 个赞

这个是很好的进步。

发去reddit推一推!

GitHub - tyler-dodge/emacs: Fork of emacs mirror Emacs. Has a background thread optimization for getting past the 1024 byte bottleneck on MacOS 的这个代码是在macOS中,emacs 28.0的代码。我照着他的代码在master分支上改了,可以编译运行。eshell中用top测试,能感觉出来光标闪烁比原版emacs稳定,速度更快,cpu更低。

但是可能是我patch的时候哪里有问题,emacs貌似有内存泄漏,跑一会top,内存就到300多MB,打开c的代码,color-rg grep几个关键字最高到900MB。

主要是28.0和29.0的 src/process.c 差异有点大。

1 个赞

我就是在reddit上看来的

可能代码还不成熟吧。但是

  1. 已经足够证明这是可以做到的
  2. 作者表示有意愿把它推进上游

这个patch看着主要是为提升eshell的使用体验。希望能早点推到上游。

不用等了,在同个话题里有个用户贴了他的修改patch(出处链接),macos上亲测有效(作者说他在linux上已经用了很久)。

修改:

modified   src/sysdep.c
@@ -2533,6 +2533,15 @@ emacs_intr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible)
     }
   while (result < 0 && errno == EINTR);
 
+  if (result > 0) {
+      for (;;) {
+          ssize_t r = read(fd, buf + result, nbyte - result);
+          if (r <= 0) break;
+          if (interruptible) maybe_quit ();
+          result += r;
+      }
+  }
+
   return result;
 }

外加init.el中

(setq read-process-output-max (* 1024 1024))
(setq process-adaptive-read-buffering nil)

这样修改以后真的已经快到我没啥可抱怨的了,eshell,shell,compilation-mode都快得跟终端窗口主观感觉差不多了。

3 个赞

原理大致是这样的:我们设置一个1Mb大小的进程输出缓冲区,Emacs去读进程输出,但是一次只读到了2k,然后就返回了。这时候外部进程还在继续输出,但是它最多只能再输出16k就得停下来,因为系统管道缓冲只有这么点。等Emacs处理完之前读到的2k,这次能回来能读到16k,读完以后外部进程又活过来,可以再最多输出16k.这样来回导致这1Mb大小的缓冲区完全用不上。

这个修改让Emacs每次一直读到read()返回0(外部进程已经没更多东西可输出了),或者缓冲区写满才停止。这让大缓冲区在外部进程高速输出时可以得到充分利用,而不至于堵在16k系统缓冲上。

为啥用了没感觉到有什么变化。。。终端下在emacs-mac源码里tree一下只需要0.1秒,修改后在emacs shell里tree一下还是要8秒多。

你说的对,这个C语言的修改没什么用,主要的变化是

(setq read-process-output-max (* 1024 1024))
(setq process-adaptive-read-buffering nil)

引起的,尤其是后者。

两个都设了,但还是没什么变化,也许是我编译姿势不正确。

我也试了tree一下emacs源码目录,加了(setq process-adaptive-read-buffering nil)是5.4秒,不加是21秒

我设成nil或t都是7~8秒左右,基本没变化…玄学

在我这里只加(setq process-adaptive-read-buffering nil)从21秒变成5秒。加不加 read-process-output-max 没有变化。

:sweat_smile:还是继续用Kitty吧…

这个patch合进上游了么,还是需要自己打?