在 Linux 和 Unix 系统上编译 Emacs 以提升性能的技术指南

Lobsters Hottest 工具

摘要

本技术指南提供了在各类 Linux 发行版上从源码编译 Emacs 的详细步骤,旨在通过 CPU 特定指令集和 Wayland 等现代显示协议来优化性能。文中还涵盖了依赖项配置以及微调原生 Lisp 编译器以提升执行速度的相关内容。

<p><a href="https://lobste.rs/s/zihixw/technical_guide_compiling_emacs_for">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/12 11:18

# 在 Linux 和 Unix 系统上为性能编译 Emacs 的技术指南 Source: https://www.jamescherti.com/compiling-emacs/ 大多数 Linux 发行版提供的二进制文件均为通用编译,旨在兼容广泛的旧硬件配置。虽然这确保了广泛的兼容性,但却牺牲了利用处理器特定现代指令集所带来的速度优势。直接从源码编译 Emacs 可以指示编译器生成针对你 CPU 架构的机器码,从而打造更快、更高效的运行环境。 在深入了解之前,**请考虑在你的网站/博客、Mastodon、Reddit、X 或你偏好的社交媒体平台上分享本文**。你的分享将帮助其他 Emacs 用户发现更高效管理代码折叠的方法。 除了纯硬件层面的优化外,从源码构建还允许我们摒弃数十年的遗留兼容性层,转而拥抱现代桌面技术。例如,Wayland 用户可以配置构建过程以绕过旧的 X11 显示协议,采用 Wayland 环境,从而确保更流畅的渲染效果与更好的系统集成。 此外,本文详细介绍了如何优化内部的原生 Lisp 编译器(Native Lisp Compiler),该编译器可将 Lisp 包转换为机器码,以确保你配置中的每一个 Emacs 包都能以最大潜在速度运行。 以下是编译高度优化的 Emacs 二进制文件及包的逐步指南。 ## 安装依赖项 ### Arch Linux 在 Arch Linux 上,解决构建依赖项最干净、最原生的方法是使用官方的 Emacs`PKGBUILD`,它会自动读取官方依赖列表并进行安装: `` mkdir -p ~/emacs-deps cd ~/emacs-deps wget https://gitlab.archlinux.org/archlinux/packaging/packages/emacs/-/raw/main/PKGBUILD makepkg --syncdeps --nobuildCode language: plaintext (plaintext) `` 注意:当`makepkg`完成依赖项安装后,你可以安全地删除`~/emacs-deps`目录。 ### Debian、Mint 和 Ubuntu 在基于 Debian 的系统上,收集依赖项最高效的方法是让包管理器根据发行版的源码包自动获取它们。首先你必须确保已启用源码仓库: - 以 root 权限用文本编辑器打开`/etc/apt/sources.list`。 - 找到以`deb-src`开头的行(参考 Debian Wiki 上的 “sources.list format (https://wiki.debian.org/SourcesList#sources.list_format)”),并移除行首的`\#`以取消注释。 - 更新软件包列表并下载安装 Emacs 的依赖项: `` sudo apt update sudo apt build-dep emacs `` ### 其他 Linux 发行版(通用) 如果你使用的是上述未列出的发行版,则需要使用该发行版特定的包管理器安装所需的开发库。寻找包含库头文件的软件包,通常后缀为 `-dev` 或 `-devel`。 以下是编译此特定 Wayland 和 Native-Comp 优化版本所需的上游库名清单: - **核心构建工具:**gcc, make, autoconf, automake, pkg-config(或 pkgconf)、git、texinfo - **原生编译引擎:**libgccjit(请确保它与你的 GCC 版本匹配) - **系统与核心库:**glib2, gnutls, zlib, ncurses, sqlite3, tree-sitter, gmp - **图形与 Wayland 工具包(PGTK):**gtk3, cairo, harfbuzz, pango, dbus - **图像格式支持:**libjpeg(或 libjpeg-turbo)、libpng、libtiff、giflib、libwebp、librsvg2、lcms2 - **字体与文字排版:**fontconfig, freetype2, libotf, m17n-lib, libxml2 ## 优化原生 Lisp 编译器 ### 微调原生编译 在构建带有原生编译支持的 Emacs 之前,我们将优化 Emacs 将 Lisp 包转换为机器码的方式。这通过`native-comp-compiler-options`和`native-comp-driver-options`变量进行管理。虽然主 Emacs 构建会使用你全局的`CFLAGS`,但这些变量专门指示`libgccjit`如何处理其遇到的每个`.el`文件。 将以下内容添加到你的`early-init.el`文件中: `` ;; 使用以下命令查看架构: ;; gcc -march=native -Q --help=target | grep march ;; ;; 上述命令要求编译器为你当前的 CPU 解析 `native` 选项 ;; 并显示目标架构标识。例如,如果输出显示 ;; `-march=skylake`,则说明 skylake 就是你应该传递给 ;; -mtune 和 -march 的参数。 (defvar my-cpu-architecture "CHANGE_THIS_TO_YOUR_ARCHITECTURE") ;; `native-comp-compiler-options` 指定了在原生编译过程中产生 Lisp-to-C 输出时 ;; 直接传递给 C 编译器(例如 GCC)的标志位。这些标志位会影响 ;; 代码生成、优化级别和调试信息的处理。 (setq native-comp-compiler-options '("-O2" "-g0" "-fno-omit-frame-pointer" "-fno-finite-math-only")) ;; `native-comp-driver-options` 指定了传递给原生编译驱动进程的额外标志位, ;; 该进程可能会以特定参数调用编译器和链接器。 (setq native-comp-driver-options `(,(format "-mtune=%s" my-cpu-architecture) ,(format "-march=%s" my-cpu-architecture)))Code language: Lisp (lisp) `` **重要提示:**将`CHANGE_THIS_TO_YOUR_ARCHITECTURE`替换为你的具体架构标识。运行以下命令来查看 CPU 架构: `` gcc -march=native -Q --help=target | grep march `` **上述命令要求编译器为你当前的 CPU 解析`native`并显示对应的目标标识。例如,若输出显示`-march=skylake`,则将`CHANGE_THIS_TO_YOUR_ARCHITECTURE`替换为`skylake`。** **注意:***避免在`native-comp-compiler-options`和/或`native-comp-driver-options`变量中添加`-mtune=native`和/或*-`march=native`*。尽管这些标志位在全局`CFLAGS`中有效,但负责原生编译的 libgccjit 库通常无法正确解析`native`关键字。这是因为 JIT 接口并不总是能触发与独立 GCC 驱动程序相同的宿主探测逻辑。* 选择这些标志位的理由如下: - 选择`-O2`是出于有意平衡。虽然`-O3`可能在某些工作负载下带来微小的性能提升,但它也会增加二进制文件体积,并偶尔降低响应速度。通常不建议使用`-Ofast`,因为它会启用激进的浮点优化。 - 使用`-g0`禁用`.eln`文件的调试符号生成,这会减小它们在磁盘上的体积,并加快编译过程本身的速度。 - `-fno-omit-frame-pointer`:Emacs 开发者建议使用此标志位来禁止省略帧指针。尽管启用省略帧指针可释放一个通用 CPU 寄存器,但在现代架构上并不能带来显著的性能提升,反而可能导致难以调试的 bug。正如 Emacs 开发者 Eli Zaretskii 所述:“请参阅 bug #76180。这一点在 igc 分支的背景下尤为突出,`omit-frame-pointer` 会带来特别多的麻烦,尽管在其他情况下它也可能引发问题。更多详情请参阅 Emacs 源码树中的 etc/DEBUG。” - `-fno-finite-math-only`标志位可防止编译器假设浮点运算绝不会产生 NaN(非数字)或无穷大值。设置此标志位主要是出于防御性考虑,因为 GCC 默认在 `-O2` 下并不会启用 `-ffinite-math-only`,但如果后续引入其他激进优化标志位,它能帮助避免不安全的假设。 ### 清除原生缓存以强制 JIT 重新编译 修改`native-comp-compiler-options`和`native-comp-driver-options`变量后,你必须强制 Emacs 重新构建你的包。**确保 Emacs 已关闭,并使用以下命令删除所有现有的`.eln`二进制文件**: `` find ~/.emacs.d/ -name '*.eln' -deleteCode language: plaintext (plaintext) `` 由于 Emacs 使用了延迟编译引擎(原生 JIT 编译器),你无需手动触发构建脚本。只需启动 Emacs 并进入正常工作流即可。当 Emacs 加载你的包并检测到缺失的`.eln`文件时,它会自动启动后台线程,使用你新的优化标志位对它们进行重新编译。 ### 步骤 1:克隆并准备源码 要构建 Emacs,你首先需要从官方镜像获取源代码,并切换到稳定的发布分支: `` git clone --depth 1 https://github.com/emacs-mirror/emacs cd emacs git checkout "emacs-30.2"Code language: plaintext (plaintext) `` **注意:**位于*https://git.savannah.gnu.org/git/emacs.git*的官方 Savannah Git 仓库有时会缓慢或不稳定,因此本指南改用官方 GitHub 镜像。 我们锁定具体的发布标签如`emacs-30.2`(若想安装其他版本,可使用`git tag`命令查看可用标签),以确保有一个稳固的基础。基于打标签的发布版本而非像`master`分支这样的前沿开发分支进行构建,可最大程度降低构建失败或实验性功能回退影响日常生产力的风险。 进入该仓库后,后续所有的配置和编译命令均在此目录下执行。 ### 步骤 2:生成配置脚本 Emacs 使用 Autotools 构建系统,在使用前需要先生成最终的配置脚本: `` ./autogen.shCode language: Bash (bash) `` 该 Shell 脚本会读取仓库中提供的开发者配置文件,并生成最终的`./configure`脚本。它会搭建必要的底层设施,用于检查你的具体环境、定位系统库,并准备好 Makefile。 ### 步骤 3:设置编译器与链接器标志位 为了提升运行时性能,我们必须导出特定的环境变量,指示 C 编译器和链接器针对主机 CPU 优化生成的二进制文件: `` export CFLAGS="-O2 -pipe -march=native -mtune=native -fno-omit-frame-pointer -flto=auto" export LDFLAGS="-Wl,-O2 -Wl,--sort-common -Wl,--as-needed -Wl,-z,pack-relative-relocs -flto=auto"Code language: Bash (bash) `` `CFLAGS`变量告诉编译器应用安全、通用的优化(`-O2`),针对编译代码的具体 CPU 优化指令调度(`-march=native`和`-mtune=native`),存储链接器后续进行跨模块优化所需的元数据(`-flto=auto`),禁用省略帧指针(`-fno-omit-frame-pointer`),并使用内存管道代替临时文件以加速构建(`-pipe`)。 `LDFLAGS`变量指示链接器对最终可执行文件应用额外的优化阶段(`-Wl,-O2`),归组常见符号以减少重复存储(`-Wl,--sort-common`),避免链接未使用的共享库(`-Wl,--as-needed`),通过执行全局分析来内联函数并移除整个程序中的死代码,从而减小二进制体积(`-flto=auto`),压缩并打包相对重定位项以降低重定位开销和二进制大小(`-Wl,-z,pack-relative-relocs`)。这些标志位共同作用,有助于生成体积更小、内存效率更高且启动与运行时性能更优的可执行文件。 ### 步骤 4:配置构建选项 我们运行 configure 脚本来精确选择需要包含哪些功能特性。 针对 X11 用户: `` ./configure \ --with-x \ --with-x-toolkit=gtk3 \ --without-toolkit-scroll-bars \ --with-cairo \ --without-xft \ --with-harfbuzz \ --without-libotf \ --with-gnutls \ --without-xdbe \ --without-xim \ --without-gpm \ --disable-gc-mark-trace \ --enable-link-time-optimization \ --with-gsettings \ --with-modules \ --with-threads \ --with-libgmp \ --with-xml2 \ --with-tree-sitter \ --with-zlib \ --without-included-regex \ --with-native-compilation \ --with-file-notification=inotify \ --without-compress-installCode language: plaintext (plaintext) `` 针对 Wayland 用户: `` ./configure \ --without-x \ --with-pgtk \ --with-toolkit-scroll-bars \ --with-cairo \ --without-xft \ --with-harfbuzz \ --without-libotf \ --with-gnutls \ --without-xdbe \ --without-xim \ --without-gpm \ --disable-gc-mark-trace \ --enable-link-time-optimization \ --with-gsettings \ --with-modules \ --with-threads \ --with-libgmp \ --with-xml2 \ --with-tree-sitter \ --with-zlib \ --without-included-regex \ --with-native-compilation \ --with-file-notification=inotify \ --without-compress-installCode language: plaintext (plaintext) `` 选择这些标志位的理由如下: - `\-\-enable-link-time-optimization`:指示构建系统原生地应用链接时优化。这确保 Emacs 的构建脚本能够协调优化所有已编译的对象文件,从而生成尽可能快的可执行文件。 - `\-\-disable-gc-mark-trace`:禁用 GC 标记跟踪缓冲区,可使垃圾回收性能提升约 5%。它会从垃圾回收器的标记阶段中移除调试代码。这在 GC 循环期间降低了开销,使内存密集型任务的处理更加响应灵敏。 - `\-\-with-native-compilation`:启用原生编译。(此处省略了 `aot` 标志位,以指示构建系统在 `make` 阶段跳过内置 Lisp 文件的编译。相反,Emacs 会将此工作推迟到运行时,在首次启动 Emacs 编译所有 `.el` 文件时,它将应用`native-comp-compiler-options`和`native-comp-driver-options`中的优化。) - `\-\-with-cairo`和`\-\-with-harfbuzz`:启用现代文本渲染。 - `\-\-without-compress-install`:默认情况下,Emacs 会压缩已安装的 Lisp 文件以节省磁盘空间。使用此标志位可保持文件不压缩。其好处是当 Emacs 需要从磁盘读取这些文件时,加载速度更快,因为它完全跳过了解压步骤。 - `\-\-without-xft`:禁用 xft,因为现代 Emacs 的渲染机制已不再需要它。现代 GTK 版本的 Emacs 使用 Cairo + HarfBuzz 来处理渲染。 - `\-\-without-included-regex`:使 Emacs 使用系统 libc 的正则表达式实现。在使用 glibc 的现代 Linux 系统上,系统正则引擎通常比 Emacs 捆绑的 GNU 正则实现优化得更好且维护得更及时。 - `\-\-with-libgmp`:启用 GMP(GNU 多精度算术库),这是一个高度优化的引擎,Emacs 依靠它以接近硬件极限的速度计算任意精度整数(bignums)。现代 Emacs 工作流经常在后台处理海量数字。如果禁用此标志位或未安装系统库,Emacs 将被迫回退到 mini-gmp,这是一个仅面向移植性而非速度的通用纯软件 C 实现。 - **`\-\-without-xim`**:禁用 XIM(X 输入法协议),这是一种主要用于在旧 X11 系统上输入复杂语言(如中文、日文或韩文)的传统协议。如果你使用 GTK3/Wayland,现代输入法会处理此类输入。建议 Wayland/PGTK 用户禁用此项,因为它移除了不必要的 X11 特定代码。 - `\-\-with-file-notification=inotify`:确保构建版本将使用 Linux 内核高效的事件监听 API,而不是在检测失败时回退到缓慢且消耗资源轮询机制。 - `\-\-with-gsettings`:启用与 GTK 配置存储系统的通信。这使得 Emacs 能够继承系统级别的偏好设置,如主题、字体、抗锯齿提示等…… - `\-\-without-libotf`:禁用传统的 OpenType 字体库。对于使用 HarfBuzz(`\-\-with-harfbuzz`)的现代构建版本推荐此操作,因为

相似文章

推荐一下……理解 Emacs 的模式

Lobsters Hottest

文章解释了 Emacs 的架构模式,重点介绍了通用缓冲区数据模型和增量补全读取(ICR),并将其比作玫瑰的根与花瓣。文章强调了 Emacs 如何统一界面并通过 Elisp 实现可扩展性。

热接线Lisp机器

Lobsters Hottest

一位开发者分享了他们使用Emacs和Org-mode构建零依赖静态网站生成器的经验,讨论了现有工具(如org-publish)的局限性,以及他们创建发布方案以保留工作流程的过程。

Emacs 与 Bzr 的坎坷往事

Lobsters Hottest

文章回顾了 2008 年 GNU Emacs 开发者决定采用 Bazaar 而非 Git 作为版本控制系统的决策过程,重点强调了性能方面的顾虑,以及 Richard Stallman 坚持使用 GNU 软件包的立场,尽管技术基准测试显示 Git 更具优势。

用 x86_64 汇编写成的 Linux 桌面

Lobsters Hottest

一位开发者借助 Claude Code,用纯 x86_64 汇编重建了完整的 Linux 桌面栈——从 shell、终端、窗口管理器到各种工具,实现微秒级启动,并延长数小时续航。

软件界的Emacs化

Hacker News Top

作者讲述了在终端中阅读 Markdown 的烦恼,并描述了如何使用 Claude 快速构建一个自定义的 macOS Markdown 查看器(MDV.app),展示了 AI 如何让人能够迅速创建个人软件工具。