惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
美团技术团队
NISL@THU
NISL@THU
C
Cisco Blogs
SecWiki News
SecWiki News
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
Cloudbric
Cloudbric
雷峰网
雷峰网
T
Tailwind CSS Blog
博客园 - 司徒正美
The Register - Security
The Register - Security
L
LangChain Blog
S
Security Affairs
Hacker News - Newest:
Hacker News - Newest: "LLM"
B
Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Threat Research - Cisco Blogs
I
InfoQ
S
Schneier on Security
L
Lohrmann on Cybersecurity
量子位
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Martin Fowler
Martin Fowler
Schneier on Security
Schneier on Security
F
Fortinet All Blogs
TaoSecurity Blog
TaoSecurity Blog
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
Cisco Talos Blog
Cisco Talos Blog
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
Project Zero
Project Zero
The GitHub Blog
The GitHub Blog
D
Docker
N
News | PayPal Newsroom
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Hacker News: Front Page
云风的 BLOG
云风的 BLOG
Microsoft Security Blog
Microsoft Security Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 聂微东
Webroot Blog
Webroot Blog
MongoDB | Blog
MongoDB | Blog

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 【Linux 网络子系统深度拆解】内核网络调优方法论:从基准测试到生产验证
排序基准测试:用数据说话
2025-07-15 · via 土法炼钢兴趣小组的算法知识备份

“不要猜,去测。” ——每一个严肃的性能工程师

排序是计算机科学里最经典的问题之一,但“哪种排序最快”从来没有一个脱离上下文的答案。数据规模、数据分布、元素类型、缓存行为、标准库实现、编译器版本,都会影响最后结果。

本文只做一件事:把仓库里的排序 benchmark 变成可以直接执行、可以重新出 CSV、可以重新画图的版本,然后在当前环境下重跑一次,用真实结果回答“什么时候该用什么排序”。

先说结论:这篇文章的旧版本并不是当前仓库里可直接复现的实测稿。 当时仓库只有文章中的代码片段和手工绘制的 SVG,没有独立的 benchmark 源码、没有原始 CSV,也没有一条能直接执行的构建命令。所以旧稿里的具体数字,不能算当前仓库内可验证的真实数据。

这次我把可执行代码补到了 examples/sort-bench/,并在当前环境重跑了一遍:x86-64 Linux,12th Gen Intel Core i9-12900K,GCC 15.2.1,单线程 taskset -c 4 绑核。文中的表格和图都来自这次重新生成的 CSV。

排序基准测试结果总览

代码与原始结果:bench.cpp | Makefile | README.md | results-current-env.csv | plot.py

一、基准测试方法论

1.1 micro-benchmark 的几个基本陷阱

陷阱一:编译器把你的工作优化掉了

如果排序结果没有被观测到,编译器可能直接把整个调用删掉。常见做法是加汇编栅栏:

inline void do_not_optimize(const void* ptr) {
    asm volatile("" : : "g"(ptr) : "memory");
}

inline void clobber_memory() {
    asm volatile("" : : : "memory");
}

陷阱二:小数组太快,计时噪声大于算法本身

几十个元素的排序可能只有几十到几百纳秒,单次测量几乎没有意义。正确做法是 batch 化,把一批相同规模的排序放到同一个计时窗口里,再除以 batch。

陷阱三:只测一次,拿偶然值当结论

这次套件对每个点都做多次采样,并记录中位数、p5、p95。文章里主要引用中位数,因为它比单次最小值更抗调度抖动。

1.2 这次重跑的测试口径

这次套件覆盖的是:

  • 数据类型:int32_t
  • 分布:uniformnormalsortedreversesawtoothpipe_organhigh_dup
  • 规模:641024655361000000
  • 算法:insertionheapmergequickintrosortpdqsortTimSortradix_lsdradix_msdska_sortstd::sortstd::stable_sort

套件使用固定随机种子 42,小数组走 batch 测量,大数组直接单次计时并重复采样。每轮排序结束后都会检查:

  1. 输出是否有序。
  2. 输出是否和输入保持同一个 multiset。

二、这次补齐了什么

为了把文章从“描述性 benchmark”变成“可复现 benchmark”,仓库里新增了这些文件:

也就是说,现在不是“文章里贴了点代码”,而是仓库里真的有一套可以执行的实验。

三、怎么构建与重跑

真正可执行的命令如下:

cd examples/sort-bench
make

正式跑数:

taskset -c 4 ./sort_bench --csv results-current-env.csv

用当前 CSV 重画文章里的图:

python3 plot.py \
    --csv results-current-env.csv \
    --output ../../post/algorithms/06-sort-bench/benchmark-comparison.svg

如果想换规模:

./sort_bench --sizes 64,4096,262144 --csv custom.csv

四、当前环境与边界

这次重跑环境固定如下:

项目 本次设置
CPU 12th Gen Intel Core i9-12900K
架构 x86-64 Linux
编译器 GCC 15.2.1
Python 3.14.3
绑核方式 taskset -c 4 单线程
数据类型 int32_t
原始结果 results-current-env.csv

这里有两个边界必须说清楚:

  1. 这次图和表是真实数据,但它们比较的是这套仓库内实现。std::sort / std::stable_sort 是标准库实现;pdqsortTimSortradixska_sort 是本文配套的参考实现。所以这些结果应理解为“这套实现的表现”,而不是整个算法家族在所有实现下的绝对排名。
  2. 当前环境没有 perf,所以旧稿里那些 cache miss、branch miss、IPC 表格全部不保留。没有重测的数据,就不应该继续出现在正文里。

五、当前环境的真实结果

5.1 uniform 随机输入下,不同规模的前三名

n 第一名 第二名 第三名
64 std::sort 159.9 ns quick 212.9 ns introsort 215.0 ns
1024 std::sort 4.458 us std::stable_sort 5.584 us radix_lsd 5.831 us
65536 radix_lsd 0.327 ms radix_msd 0.994 ms ska_sort 2.029 ms
1000000 radix_lsd 6.019 ms radix_msd 15.428 ms ska_sort 31.125 ms

这张表足够说明两件事:

  • 小规模时,libstdc++ 的 std::sort 是最强 baseline。
  • 当数据真的是大规模 int32_t 且分布接近随机时,基数排序会把比较排序甩开一个量级。

5.2 n=1,000,000 时,各分布前三名

分布 第一名 第二名 第三名
uniform radix_lsd 6.019 ms radix_msd 15.428 ms ska_sort 31.125 ms
normal radix_lsd 5.839 ms radix_msd 9.878 ms ska_sort 18.810 ms
sorted TimSort 0.210 ms pdqsort 0.329 ms std::sort 4.609 ms
reverse TimSort 0.518 ms std::sort 3.416 ms pdqsort 5.139 ms
sawtooth TimSort 4.120 ms std::stable_sort 5.304 ms radix_msd 9.142 ms
pipe_organ TimSort 1.107 ms std::stable_sort 6.654 ms radix_lsd 9.676 ms
high_dup radix_lsd 4.371 ms radix_msd 6.945 ms ska_sort 11.568 ms

这里最值得注意的是:这次真实重跑里,TimSort 对结构化输入非常强,而当前仓库这版 pdqsort 并没有出现“全分布通吃”的表现。

5.3 几个最值得看的对照

下面只挑最常见的几类输入,看几个代表性算法:

分布,n=1,000,000 std::sort pdqsort TimSort radix_lsd 观察
uniform 47.153 ms 76.158 ms 73.883 ms 6.019 ms 随机 int32_t 上基数排序碾压比较排序
sorted 4.609 ms 0.329 ms 0.210 ms 11.772 ms 有结构输入时自适应排序巨大受益
reverse 3.416 ms 5.139 ms 0.518 ms 12.138 ms TimSort 能有效利用 run 和反转结构
high_dup 26.954 ms 43.675 ms 52.429 ms 4.371 ms 高重复整数上基数排序仍然最稳

另外,heap_sort 在 uniform 1e6 上是 88.122 ms,在 sorted 上是 37.808 ms,在 reverse 上是 42.824 ms,几乎在所有大规模输入里都靠后。这个方向判断和旧稿一致:堆排序更像保底策略,而不是默认首选。

六、这次重跑后,哪些结论还能站住

6.1 还站得住的结论

  1. 没有一个算法在所有分布上都最快。 这次环境里,random/high-dup 是 radix 家族赢,sorted/reverse/pipe_organ 是 TimSort 赢。
  2. 对大规模 int32_tradix_lsd 的优势非常实。 在 uniform 1e6 上,它比 std::sort 快约 \(47.153 / 6.019 \approx 7.8\) 倍。
  3. TimSort 对有结构的数据极其敏感。 在 sorted 1e6 上,TimSort 0.210 ms,std::sort 4.609 ms,差了约 22 倍。
  4. std::sort 仍然是最可靠的比较排序 baseline。 在 uniform 的 64 和 1024 上,它都是第一;在 uniform 1e6 上,它也仍然是比较排序里最快的一档。
  5. heapsort 依旧不适合作为默认选择。 它存在的主要工程价值还是给 introsort 兜底最坏情况。

6.2 站不住,或者不能这么写的结论

  1. “pdqsort 是通用场景最佳选择” 这句话不能继续保留。 这次真实重跑里,当前仓库这版 pdqsort 在 uniform / normal / high_dup 上都不占优,只在 sorted 上表现很好。
  2. 旧稿里的 perf / cache / branch 数字必须删。 当前环境没有 perf,所以这些数字不是这次 bench 产物。
  3. 旧稿里关于大对象、并行排序、1e8 规模的具体数字也不该保留。 这次并没有按那个口径重跑,就不要伪装成实测结果。

七、怎么在你的机器上复现

最短路径就是这三条命令:

cd examples/sort-bench
make
taskset -c 4 ./sort_bench --csv results-current-env.csv

重画文章里的 SVG:

python3 plot.py \
    --csv results-current-env.csv \
    --output ../../post/algorithms/06-sort-bench/benchmark-comparison.svg

如果你要修改规模:

./sort_bench --sizes 64,4096,262144 --csv custom.csv

八、当前环境下的选型建议

如果你的问题和这次 bench 的口径接近,也就是单线程、int32_t、内存内排序,那我会给出更保守、也更诚实的一版建议:

  1. 随机或接近随机的大规模整数:优先看 radix_lsd
  2. 明显有序、部分有序、run 很长的输入:优先看 TimSort
  3. 只是想要一个稳妥的通用 baseline:先用 std::sort,不要先入为主切到手写 pdqsort
  4. 必须稳定排序,但输入又不是强结构化:先用 std::stable_sort;如果数据本身就带 run,再测 TimSort
  5. heapsort 不要直接选。 让 introsort 在需要时退回堆排序就够了。

最后还是那句老话:永远实测。 但这次至少不再是“文章里说能跑”,而是仓库里真的有代码、真的能跑、真的能产出 CSV 和 SVG 了。

参考文献

  1. Musser, D.R. “Introspective Sorting and Selection Algorithms.” Software: Practice and Experience, 27(8), 1997.
  2. Peters, O. “Pattern-defeating Quicksort.” arXiv:2106.05123, 2021.
  3. McIlroy, P.M. “Optimistic Sorting and Information Theoretic Complexity.” SODA, 1993.
  4. Skarupke, M. “ska_sort: A Radix Sort Implementation.” 2016.
  5. Peters, T. “TimSort.” CPython source, 2002.
  6. Drepper, U. “What Every Programmer Should Know About Memory.” 2007.
  7. Fog, A. “Optimizing Software in C++.” 2023.
  8. Edelkamp, S., Weiss, A. “BlockQuicksort.” ESA 2016.

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-10 · algorithms

排序算法专题:从 TimSort 到并行排序

把 TimSort、pdqsort、radix sort、external sort、parallel sort 与 benchmark 串成一条阅读路径。先读哪篇、什么时候选哪种排序,这一页讲清。

2025-07-15 · algorithms

基数排序:打破比较下界的正确姿势

比较排序有 O(n log n) 的理论下界,基数排序如何绕过这个限制?它在什么场景下真正有优势,又为什么没有成为通用排序的首选?