如何看 emacs-ng 项目?

https://emacs-ng.github.io/emacs-ng/

有哪位道友体验过?使用体验怎么样?性能真的好吗?

用了一小会, wayland 下窗口疯狂闪烁,就删了。。。

别想靠硬件加速绘画而提升 emacs 的运行速度(由于这根本就不是问题区域),也不要听别人胡说 Emacs 现在没有硬件加速,他们不懂 cairo 和 Xrender 是怎么实现的

说白了,emacs-ng 一点意义都没有。

5赞

cario本身就有加速,emacs主要缺乏复杂控件布局管理。

emacs慢主要体现在三个方面,elisp语言执行效率,没有多线程支持(大数据计算卡界面),第三方插件。

emacs最大优势是现有生态体系,emacs-ng破坏生态最终会因为人力不够而失败。

4赞

cairo 加速是由 xrender 实现的,不使用 cairo 时也会由 Glamor 或其他的 DDX 实现。 Emacs Lisp 执行效率其实能和 cpython 相比,只是不能利用多执行单元而已。 而 emacs-ng 也不能解决复杂控件布局或 Emacs Lisp 性能这些问题。

再说,文字编辑器为啥需要布局系统?难道你要 `XmTextField’ 也有布局系统?

1赞

就像gtk和qt的box设计,可以有但是可以大部分场景不用。

这样比如实现侧边栏,表格对齐,逻辑行和逻辑列的时候就不用每天hacking text world了。

elisp性能还没法和python以及js相比。

多线程是死穴,单靠elisp生态大计算场景都卡手。

表格对齐不能用类似 shr 的 shr-string-pixel-width' 和像素 :width’ (参考 Emacs Lisp 说明书 39.16.3 (elisp)Pixel Specification) 做到吗?我以前尝试过,效果很好。再说你 XmTextField 还做不到这种对齐呢。

关于 CPython 的性能,你有证据吗?我看 CPython 编译器 constant folding 都不会,还不如 90年代 JWZ 为当时 dynamically bound elisp 做的编译器。

多线程已经做到了,只是限制与GIL和只能在 `thread-yield’ 或进入 pselect 或等待 X11 粘贴板相应时切换而已。

当年 XEmacs 的 GC 和 runtime 已经做了对并行处理的准备,不知道现在如何,但是也证明了让 emacs lisp 支持并行处理并不是空中楼阁。

表格对齐 align-to 的技术可以实现,但是这个和控件体系还不是一样的,控件是有一个区域独立绘制的,Emacs现状主要靠脆弱的代码逻辑来维护独立绘制区域,这个软件工程复杂度太高了。

Elisp可以和Python和JavaScript做一些横向性能对比测试,确实有差异,而不仅仅是 Elisp 可以直接调用 C 函数就可以的,而是Elisp标准库很多性能还不够,可以Google一下。

Emacs现在的模型不是多线程,真正的多线程,多个子线程是可以独立运行的,这样可以让耗时操作一直在后台线程工作,只有当需要向主线程提交计算结果的时候才发送到主线程,只有真正的多线程机制才能保证真正不卡界面,这方面在Gtk和Qt都有很多年的工程实践。

Emacs现在的模型更像协程的概念,在多个协程之间来回切换,协程有自己的调度事件循环,但是还是有一个锁,无法做到 ‘图形主线程’ 和 ‘计算线程’ 真正的独立运行,如果 ‘图形主线程’ 无法独立获取足够的CPU计算时间,就会导致界面卡顿现象。

Emacs内置真正多线程模型会对现有生态进行大规模破坏,现在最佳实践是像EAF这种,先用IPC把应用进程独立出来,Emacs专注做文本编辑操作(单线程就够了),EAF来处理图形和多线程。不管是否是多进程,图形流畅性本质还是多线程能力,如果不具备多线程能力,外部子进程海量返回数据回Emacs时,Emacs本身没有多线程,一旦一个时间周期(通常是 30ms) 处理不过来,还是会在子进程返回的时候卡顿Emacs界面。

并行进程模型无法解决Emacs在大型计算卡界面的问题,只有真正的多线程模型可以解决

2赞

想起了 emacs-ng 之前想使用 rust 替换 C 的 remacs,现在状态已经在不再维护了

关于 elisp 执行效率,我比较好奇:现在的 JIT 技术不是很强吗?另外加上 native-comp 之类的技术,感觉 elisp 执行效率本身一般不会成为瓶颈?

其实有多线程模型,即使Elisp这么慢的语言,界面也不会卡顿,用户对于Emacs的慢心智是卡顿,语言本身的慢最多是计算时间长一点而已。

JIT本身涉及指令集平台的对接(如果只支持X86会简单点),真正实现JIT还是要花很大功夫的,你看JS本身就是一个性能很差的语言,但是有V8 JIT加持, JS甚至比Python都要快很多。

人眼流畅的感官是,每秒最少绘制30帧,也就是任意图形区域更新的时间不要超过 33ms, 如果图形更新周期超过这个时间,人就会感动明显的卡顿,即使执行性能快如 Rust, 也会遇到一些耗时操作(比如全盘搜索文件内容)超过 33ms 的,所以要把语言本身的执行性能和多线程模型这两个概念彻底理清楚才能构建真正高性能的图形软件。

嗯,所以关键还是异步并行。我在想是不是可以把处理绘图和输入控制的逻辑剥离出来单独搞一个线程,这个线程不做大的计算,而是将要做的任务变成异步请求交给 worker thread 完成。但是这样会遇到全局共享变量,有些难解决。可能需要一套比较复杂的同步机制。

回到这个帖子标题, emacs-ng 我的理解想改变两个事情:

  1. 通过JS异步回调或者 worker 机制来实现多线程的机制
  2. 通过Web图形来实现复杂控件界面绘制

第一步其实有很多方法,比如EAF的 IPC 出子进程以后,通过Python来实现多线程,当然他们通过 JS 的 worker 也是可行的,只是大家用的方法不同而已,只要可以实现不卡界面的目标都可以。

第二步才是我不看好 emacs-ng 的原因,给Emacs加Web能力本身是好事情,但是要建立在不破坏Emacs现有主分支的前提下,Emacs最大的资产就是那些专门针对 text world 的插件生态,这些生态是无价之宝,如果破坏Emacs现有机制(即使有点落伍)兼容性,就会导致另外一个更致命的问题,没人加入他们的开发,时间长了就荒废了。

所以,针对Emacs开发图形功能目前有两种策略,一种是Po Lu现在做的是把 xwidget 完善,尽量用操作系统底层窗口服务API, 不要在 Client 端拷贝图形数据,二是EAF这种,完全不修改Emacs的代码,在子进程去解决图形和多线程的问题。

3赞

如果你熟悉图形编程就知道,像XWindow这种模型,图形代码只能必须在主线程运行,只要你尝试在多个线程访问图形代码,X马上就会报 Bad Window 错误把主进程搞崩溃。

Gtk/Qt一般的做法是内置多线程支持,把非图形计算的代码完全隔离到子线程,子线程处理把数据量非常小的数据结果通过队列的方式传送给主线程,再由主线程在 30ms 一个绘制周期内更新界面,用户就不会觉得卡。

Emacs这边主要是并不具备真正的多线程设计(真正多线程会导致Emacs一堆 hook 和现有插件挂掉,那是真正的灾难开始),所以需要将就Emacs现状绕一下:

  1. 图形计算分离:先启动一个进程,因为进程在操作系统概念里是完全隔离的,所以这个子进程来处理图形绘制和多线程计算,只和Emacs进行微量的IPC互调用发给消息给Emacs在minibuffer显示一下,就是EAF现在的思路
  2. 后台计算分离:先启动一个进程,调用任意支持多线程的编程语言去处理耗时计算,为Emacs和外部进程之间建立一个数据隔离带,避免海量数据涌入Emacs导致Emacs一下处理不过来又卡顿了,现在 lsp-mode 就是这个问题,虽然启动子进程了,但是子进程并没有做好数据过滤的职责,把 lsp server海量的json消息返回给 Emacs, Elisp标准库根本就无法在一个周期内处理这些数据就会卡,其实Emacs只需要中间进程做好数据过滤后,Emacs只需要接受数据量非常小的补全菜单候选列表数据就可以了,这也是我春节计划编写的 lsp-bridge的思路

没有图形编程经验的人,主要认为Emacs支持异步子进程就能不卡,那只能解决计算过程中的不卡,如果返回Emacs的那一瞬间 “Emacs处理不过来” 还是会卡。

7赞

说这么多,其实就一句话 ”破坏Emacs现有生态兼容性” 的技术方案都会落后,不是新的技术不好,而是破坏Emacs现有生态就没人持续参与了,因为Emacs核心用户群体还是对 text world 保佑信念的老黑客。

真正以Web优先的都是VSCode用户。

2赞

首先,我没有把 V8 拿来与 Emacs Lisp 比较,我比较的是没有任何 JIT 的 CPython。同时,CPython也存在有 GIL,不能使用多个执行单位做到并行处理。而你要注意,Emacs 的 redisplay 是可能在任何没有 block_input 的情况下运行的(比如说,收到 SIGIO 进入某某 terminal 的 read_socket_hook 就有可能进入 redisplay)。我当年(90年初期)用没有 threads 的 Unix 做到流畅的 X 应用似乎就是这样的。

再说,内置并行处理(注意:并行处理不等于多线程,多线程也不表示并行处理,现在 Emacs 已经做到了多线程,而没有做到并行处理)也不会对现有生态系统造成很大的影响。最多会限制非主要线程进入 thread_select/redisplay/wait_reading_process_output 等等。

2赞

redisplay 反映不过来就是 bug。不管你在处理多少信息,Emacs 卡死的有多厉害,redisplay 也能运行,而且通常一次 redisplay 不会超过 10 到 15 ms(可以通过 (progn (redraw-display) (redisplay)) 来证明)。

顺便问一声,能不能通过邮件来回复帖子啊?有人回复我时,总是收到邮件,但是来自于 noreply 地址。

并行属于多进程概念,只要数据不和Emacs主线程沟通,都不会有啥影响。

Emacs主要缺乏 ‘独立运行的多线程并发’ 模型,从而完全把图形代码和非图形代码完全隔离开来,避免图形绘制周期计算量过大。

真正多线程的好处是,读取的数据不会产生线程竞争,线程间读取都是内存地址,多进程还是会涉及大量数据在两个进程间交互。

我没有看到Emacs具备真正多线程完整编程模型。

就说Emacs现状,现在最主要的问题是,即使IPC子进程以后,只要返回的数据足够多,Emacs主线程一个周期处理不过来就会卡顿,主要是还是数据处理耗时,或者线程之间切换不够即使也会卡顿。

理论上,只要GPU能够处理的数据和带宽足够,一个图形周期都会绘制任意图形,真实情况是,绘制就只需要 15ms, 但是这15ms之前的数据并不是图形数据,非图形数据转换成图形数据太耗时就也会卡顿。