如果你是第一次来到这组排序文章,不建议按发布时间乱翻。排序问题的关键从来不是“哪种算法理论上更强”,而是:你的数据是什么分布、稳定性要不要、数据是否装得进内存、瓶颈在 CPU 还是 I/O、单线程还是多核。
这页的目标不是重复每篇文章的全部内容,而是把它们串成一条清晰的阅读路径。
从这里开始
如果你只想先读一篇,再决定要不要继续往下看,按这个顺序选:
- TimSort 深度解剖:适合想理解“为什么标准库默认排序会这样设计”的读者。
- pdqsort:适合想知道“如果不要求稳定性,现代默认排序为什么常常选它”的读者。
- 基数排序:适合想判断“什么时候真的值得用 O(n) 排序”的读者。
- 排序基准测试:适合已经知道算法名,但想直接看数据、做工程选型的人。
推荐阅读顺序
- TimSort 先建立“真实数据往往部分有序”的直觉,理解 run、稳定性与工程常数因子的意义。
- pdqsort 再看不稳定默认排序的另一条主线:模式检测、坏分区回退、对抗性输入处理。
- 基数排序 把“比较排序”和“非比较排序”的边界真正分清楚,理解缓存与数据类型的约束。
- 外部排序 当数据装不进内存时,CPU 复杂度不再是唯一问题,I/O 模型开始主导性能。
- 并行排序 进入多核与 GPU 时代,同一个排序问题需要重新看待同步、分片和吞吐。
- 排序基准测试 最后回到统一测试框架,用数据校验前面的直觉,而不是靠经验拍脑袋。
按问题找文章
| 你的问题 | 最先看哪篇 | 为什么 |
|---|---|---|
| 为什么标准库默认排序不直接用快排? | TimSort | 默认排序首先服务真实数据,而不是随机输入下的教科书模型 |
| 不要求稳定性,通用场景谁最强? | pdqsort | 它代表了现代不稳定默认排序的主流工程答案 |
| O(n) 排序到底什么时候真能赢? | 基数排序 | 关键在键类型、缓存、位宽和常数因子 |
| 数据已经大到内存装不下怎么办? | 外部排序 | 此时核心矛盾从 CPU 变成磁盘与归并策略 |
| 多核或 GPU 值不值得上? | 并行排序 | 并行化会带来新的同步、分片和负载均衡问题 |
| 我只想看实测,不想先看理论 | 排序基准测试 | 统一基准能快速告诉你不同数据分布下的现实表现 |
一页结论
- 如果你要的是稳定排序,而且数据常常部分有序,优先看 TimSort。
- 如果你要的是通用内存内排序吞吐,且不要求稳定性,优先看 pdqsort。
- 如果键提取便宜、位宽固定、数据规模足够大,再考虑 基数排序。
- 如果数据已经不在内存里,直接跳去 外部排序。
- 如果瓶颈已经从单核 CPU 变成并行吞吐,再看 并行排序。
- 如果你要把选型说服给别人,最后拿 排序基准测试 的实测数据收尾。
延伸阅读
- SIMD 算法设计模式:排序优化里绕不开的向量化直觉。
- 缓存无关算法:理解内存层级为什么会改变算法优先级。
- B-tree 深度解剖:当排序与外存、索引结构联系起来时,这篇很有帮助。
文章导航
分类入口
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
TimSort:自然 run、galloping 与从栈不变量到 Powersort 的合并策略
对照 CPython 与 OpenJDK 源码拆解 TimSort 的 run 检测、minrun、galloping 与合并,梳理 2015 年栈不变量 bug 和改用 Powersort 的原因;比较次数来自与 CPython 逐次一致的 C 移植。
pdqsort:坏分区计数、重复键分区与块分区如何改造 introsort
对照 orlp/pdqsort 源码与 Peters 论文,拆解 pdqsort 在 introsort 上的四处改动;用与参考实现比较次数逐次一致的 C 移植和 McIlroy 对抗输入实测,并梳理 Boost、Rust、Go、libc++ 各自采用了哪些部分。
并行排序:排序网络、并行归并、样本排序与 GPU 基数排序
用 work/span 计数解释并行排序为何难以线性加速:串行归并与串行划分把并行度压在个位数,并行归并、Merge Path 与样本排序各自如何突破;再对照 libstdc++、oneTBB、Rayon、CUB 源码看生产实现的真实选择。
排序基准测试:比较次数、分支预测与输入分布
在 GCC 16 上对 9 种 int32 排序做精确比较计数与绑核计时(8 种输入分布、3 个进程取中位数):比较次数预测不了耗时,分支预测与输入结构决定排名;反复排序同一数组会把小数组耗时低估 2 到 6 倍。