Pystd 标准库:功能类似,编译时间大幅缩减
摘要
Pystd 是一个从头编写的 C++ 标准库,旨在大幅缩短编译时间,与标准库耗时数分钟的编译相比,可在数秒内完成完整构建,同时提供类似的功能。
<p><a href="https://lobste.rs/s/xfftl1/pystd_standard_library_similar_ish">评论</a></p>
查看缓存全文
缓存时间: 2026/06/29 16:30
# Pystd 标准库:功能相近,编译时间却仅有几分之一
来源:https://nibblestew.blogspot.com/2026/06/pystd-standard-library-similar-ish.html
我向多个会议提交了关于 Pystd 的演讲提案——这是一个从零编写的 C++ 标准库(自定义设计,并非 ISO 规范的实现)。不幸的是,所有提案都被拒绝了,所以现在是发博客的时候了。
## 一个有争议的看法
几乎所有人都同意编译 C++ 很慢,但我个人觉得它*慢得让人无法忍受*。别人可能不同意,那也没关系,但让我们看看一些数据。
用 C 编译一个 helloworld 可执行文件大约需要 0.02 秒。用 C++ 编译一个实现相同功能的可执行文件(使用了 `#include <iostream>`),在禁用优化时需要 1 秒,开启优化后则需 2.3 秒。这大约是 100 倍的减速。我使用的是 Ryzen 7 3700X 处理器,虽然不是最新的,但也不算差。
我曾和一些人在线下讨论过这个减速问题,奇怪的是,他们的回答往往是“这不是问题,两秒钟微不足道”。即使你接受了这一点(我个人不接受),更大的问题来自扩展性,因为减速因子大致是线性的。假设在某个不那么极端的情况下,减速只有 20 倍而不是 100 倍。那么,一个本应在 0.1 秒内完成编译的程序将需要 2 秒,而一个本应在一分钟内编译完成的程序大约需要 20 分钟。
## 为什么编译这么慢?
C++ 本身*非常*快,减速主要来自于标准库的实现方式。实际上,你可以通过运行下面的 shell 命令来轻松测试这一点。
> `echo '#include <vector>' | g++ -x c++ -E - -std=c++23 | wc -l`
`-E` 标志告诉编译器在预处理后停止。输出是喂给编译器前端的源代码。我们将输出传给 `wc`,结果发现仅仅包含 `<vector>` 就扩展出了 29,000 行代码。这个数字不能直接与"人类编写的代码"相比,但不管怎样,仅仅为了得到一个可增长的动态数组就产生近 30k 行代码,似乎有点过分。而且 `vector` 其实算是比较轻量的头文件了。`<memory>` 有 55,000 行(尤其糟糕,因为 99% 的时候人们只需要 `unique_ptr`),`<print>` 有 65,000 行,`<filesystem>` 有 80,000 行。不幸的现实是,只要你包含了一个标准库头文件,你的编译时间就会暴跌,而你对此无能为力。
## 干脆说不
Pystd 最初只是我为乐趣从零实现底层原语(如哈希映射等)的一个项目。很快我就确立了三个设计优先级:
1. 编译时间
2. 实现简单性
3. 性能
据我所知,目前还没有任何一个现有的标准库将构建时间最小化作为设计优先级。那些编译快的库(如 C 和 Go 的标准库)似乎主要得益于它们语言本身的简单性。
截至撰写本文时,用单核从头构建 Pystd 及其所有测试需要 4 秒。这包括 45 个独立的进程(主要是编译,少量链接)。开启优化后,构建时间膨胀到 9 秒。使用全部 16 个核心则将其降低到 1.9 秒。
## 目前的进展
如果忽略测试代码,Pystd 共有 6500 行头文件和 5600 行源文件。即使把这两者相加,行数也大约只有 `std::vector`(预处理后)实现的三分之一。
Pystd 提供的功能包括:
- vector, string, 带验证的 u8string, string views, span
- 哈希映射,有序映射(使用 B-树)
- sort(速度大约与 stdlibc++ 相当),stable_sort(比 stdlibc++ 更快)
- ISO 算法头文件中随机选取的部分
- Optional, expected, variant, unique_ptr
- 功能大致相当于 Python 模块 argparse, pathlib(包括 `**` 运算符),正则表达式(使用 pcre)和 tempfile
请注意,所有这些功能都是"完整"的,但通常只包含最常用的功能子集。这可能相当有限。
## 性能
关于性能,有一篇[早期博客文章](https://nibblestew.blogspot.com/2025/06/a-custom-c-standard-library-part-4.html)。转换 CapyPDF 库的数据如下:
- 编译时间下降了大约 80%
- 未剥离的二进制大小减少了大约 75%
- 剥离后的二进制大小减少了大约 30%
- 运行时性能提高了约 25%(是的,更快了,而不是更慢)
## 回归是可以避免的
当人们听到需要"编译快"时,通常会提出两个典型问题:
1. 什么是"快"?这是高度主观的,取决于每个人和他们使用的计算机。
2. 即使现在快了,也不能一直保持快。新功能不断被添加,代码注定会越来越慢,最终变得和默认的标准库一样慢。
这两个问题的无聊解决方案是一样的:预定义的时间预算。Pystd 有一个要求:编译一个包含任何单个公共头文件的源文件,最多需要 0.15 秒。这个限制最初是 0.1 秒,在 GCC 上运行完美,但 Clang 的进程启动时间比这更长。
负责验证性能的测试脚本在[这里](https://github.com/jpakkane/pystd/blob/master/test/timingtest.py)。它即使在树莓派上也必须通过。有趣的是,测试脚本最初并不是单线程的。我将其并行化仅仅是因为它是 Pystd 编译-测试周期中最慢的部分,耗时超过一秒。
这是 Pystd 中所有新功能都必须满足的要求。如果你想添加的代码编译太慢,那么要么你重写整个包使其编译更快,要么向上游编译器提交补丁让它运行更快。
## 亲自试试
Pystd 的代码可以在[通常的地方](https://github.com/jpakkane/pystd)获取。初学者可能想尝试使用[示例项目](https://github.com/jpakkane/pystddemo)。代码可在 Linux 和 macOS 上运行。不支持 MSVC,因为该实现使用了 pack indexing,而 MSVC 尚未实现此功能。
相似文章
Sp.h 是 C 语言应得的标准库
sp.h 是一个 15000 行的单头文件 C99 标准库,它绕过 libc 以提供可移植、显式且无堆的原始接口。其旨在用现代的系统调用级抽象取代传统的 libc。
C++ 标准库在过去十五年间一直在自我撤步,证据公开
一份详细的目录,列出了从 C++11 到 C++26 期间被正式弃用、非正式不推荐或由于 ABI 约束实际上已损坏但无法修复的 C++ 标准库特性。文章指出,C++ 委员会推出一系列替代品来替换其自身特性的模式始终如一,其中包含一个基准测试,显示 Rust 和 C++ 标准库容器之间的 P99 延迟差异高达 58 倍。
正统 C++ (2016)
正统C++是C++的一个最小子集,避免使用现代特性,倡导更简单、类似C的风格,以提高可读性和兼容性。
C++26:标准库强化
C++26 引入了标准化的库强化机制,用于在运行时捕获常见的未定义行为(如越界访问)。基于 Google 的生产经验,此举仅带来 0.30% 的性能开销,同时将段错误减少了 30%。
hubert.cpp,一个 distilHuBERT 的 C++ 实现 [P]
一个没有运行时依赖的 distilHuBERT C++ 实现,权重编译入库,支持动态大小,性能与 ONNX Runtime 相当,便于集成到 CMake 项目中。