uutils coreutils sort 性能基准测试完全指南:从 hyperfine 实战到源码级优化
uutils coreutils sort 性能基准测试完全指南从 hyperfine 实战到源码级优化【免费下载链接】coreutilsCross-platform Rust rewrite of the GNU coreutils项目地址: https://gitcode.com/GitHub_Trending/co/coreutils在 uutils coreutils 项目中sort是性能最敏感的核心命令之一其绝大多数运行时间花在逐行比较上而比较函数的开销会随-f、-n、-g、-h、locale 等参数的不同产生数量级的差异。因此官方在 src/uu/sort/BENCHMARKING.md 中沉淀了一套系统的基准测试方法论覆盖词表、数值、SI 前缀、外部排序、归并、检查等多个典型负载。本文以该文档为骨架结合 sort 源码 与 bench 目录 中的 divan 基准实现完整讲解如何复现这些基准、如何与 GNU sort 对比以及背后对应的实现机制帮助你建立可量化、可回归的 sort 性能评估能力。为什么要对多个场景分别基准测试sort的比较函数并非唯一。当传入不同的命令行参数时排序模式会切换到完全不同的实现路径默认按字节字典序比较、-f需要大小写折叠、-n走数值解析、-g走通用浮点解析、-h需要解析 SI 单位后缀、locale-aware 排序还需要调用 ICU collator。从源码可以直观印证这一点src/uu/sort/src/sort.rs 中的SortMode枚举定义了Numeric、HumanNumeric、GeneralNumeric、Month、Version、Random、Default七种模式而GlobalSettings::init_precomputed会在排序开始前一次性计算Precomputed结构中的各种快速路径标志如fast_lexicographic、fast_locale_collation、fast_ascii_insensitive并统计每行需要预解析的数值信息数量num_infos_per_line、浮点数量floats_per_line等。正因如此单独一个负载的基准结果无法代表sort的整体性能。任何对sort的改动都应按下文列出的多组负载分别测量确保没有引入回归也欢迎补充新的负载到清单中。官方要求修改sort之后基准测试前务必先执行cargo build --release。基准测试准备构建 release 版本所有基准都针对 release 构建产物cargo build --release之后通过target/release/coreutils sort ...调用multicall 二进制或target/release/sort ...如果单独构建了 sort 二进制见 src/uu/sort/Cargo.toml 中的[[bin]]配置。安装 hyperfine文档中的时间测量统一使用 hyperfine外部工具按官方安装方式获取。它的优势在于自动执行多次采样、剔除异常值、给出中位数与置信区间并支持同时对比多个命令。负载一词表排序默认字典序这是最基础的负载用于衡量默认字节比较路径的吞吐。准备一份词表例如 Linux 上的/usr/share/dict/american-english。具体是哪份词表并不重要——性能对比看的是相对变化。将词表打乱避免已排序输入带来的捷径效应sort -R /usr/share/dict/american-english shuffled_wordlist.txt用 hyperfine 基准排序hyperfine target/release/coreutils sort shuffled_wordlist.txt -o output.txt注意-o output.txt把输出写入文件而非 stdout避免终端 I/O 干扰计时。这一步对应仓库 benches/sort_bench.rs 中的sort_ascii_only基准500,000 行 ASCII 数据通过-o输出到临时文件。负载二词表排序 忽略大小写-f-f会折叠大小写再比较比较函数与纯字节比较不同hyperfine target/release/coreutils sort shuffled_wordlist.txt -f -o output.txt源码层面该路径受Precomputed::fast_ascii_insensitive标志驱动当数据可以安全按 ASCII 折叠比较时走快速路径否则退回通用实现。仓库基准中对应 sort_bench.rs 的sort_case_insensitive对混合大小写数据传入-f参数。负载三数值排序-n-n按数值而非字典序比较每行都需要先解析出数值 token开销显著高于字节比较。先生成 100 万行随机数值shuf -i 1-1000000 -n 1000000 shuffled_numbers.txt # 或者 seq 1 1000000 | sort -R shuffled_numbers.txt然后基准文档给出的示例同时对比 GNU sort 与改动前后的两个 uutils 版本/tmp/uu_before与/tmp/uu_after为预先放置的两个版本二进制hyperfine --warmup 3 \ /tmp/gnu-sort -n /tmp/shuffled_numbers.txt \ /tmp/uu_before sort -n /tmp/shuffled_numbers.txt \ /tmp/uu_after sort -n /tmp/shuffled_numbers.txt--warmup 3表示正式采样前先运行 3 次预热。对应仓库基准 sort_bench.rs 中的sort_numeric生成带value_文本前缀的伪随机数值行传入-n。数值解析的核心实现位于 numeric_str_cmp.rs它同时处理千位分隔符与小数点的 locale 差异NumericLocaleSettings。负载四通用数值排序-g-g支持科学计数法如1.23e5内部走bigdecimal/ExtendedBigDecimal的通用浮点解析比-n更慢但更通用hyperfine target/release/coreutils sort shuffled_numbers.txt -g -o output.txt复用上面生成的shuffled_numbers.txt。仓库对应 sort_bench.rs 的sort_general_numeric其生成的数据特意包含小数点与e±2指数如-123.456e2格式用于压测通用数值解析路径。负载五SI 前缀数值排序-h-h识别k、K、M、G等 SI 后缀如123K、45G。基准前需要先构造带后缀的数据集文档提供了一个用 Rust 编写的小生成器约 10 万行。先建一个临时 Cargo 项目Cargo.toml 仅需添加一个依赖[dependencies] rand 0.10.0src/main.rs内容如下use rand::prelude::*; fn main() { let suffixes [k, K, M, G, T, P, E, Z, Y, R, Q]; let mut rng rand::rng(); for _ in 0..100000 { println!( {}{}, rng.random_range(0..1000000), suffixes[rng.random_range(..suffixes.len())], ) } }运行生成数据cargo run shuffled_numbers_si.txt然后基准hyperfine target/release/coreutils sort shuffled_numbers_si.txt -h -o output.txt这些后缀k K M G T P E Z Y R Q与-S缓冲区大小解析共用同一套单位体系——sort.rs 的parse_byte_count通过uucore的Parser带 allow-listb k K m M g G t T P E Z Y R Q %解析大小字符串默认单位是K1024 字节。负载六外部排序-S超出内存容量的数据当数据量超出内存缓冲时sort会退化为外部排序将输入切成多个 chunk分别排序后写入临时文件再归并回输出。-S指定可用于排序的内存上限例如1M。用-S 1M甚至更小的值强制触发外部排序路径。也可以用巨型文件理想情况数 GB分别测-S与默认自动缓冲两种情形。制造大文件的一个技巧反复把文件内容追加到自己身上cat shuffled_wordlist.txt | sort -R shuffled_wordlist.txt重复执行多次文件大小指数增长。示例对比 uutils 与 GNU sorthyperfine ./target/release/coreutils sort shuffled_wordlist.txt -S 1M sort shuffled_wordlist.txt -S 1M实现层面外部排序与缓冲策略集中在前者位于 ext_sort 目录含threaded.rs、wasi.rs两个平台变体后者在 buffer_hint.rs 的automatic_buffer_size。不指定-S时自动缓冲启发式会把大小钳制在 512 KiB1 GiB 之间见 sort.rs 顶部的MIN_AUTOMATIC_BUF_SIZE、FALLBACK_AUTOMATIC_BUF_SIZE、MAX_AUTOMATIC_BUF_SIZE常量。负载七合并已排序文件-m-m直接把多个已排序文件归并为一个有序流不做全量排序是外部排序的收尾子步骤值得单独基准把词表切成多个分片split shuffled_wordlist.txt shuffled_wordlist_slice_ --additional-suffix.txt每个分片先各自排序for f in shuffled_wordlist_slice_*; do sort $f -o $f; done基准归并hyperfine target/release/coreutils sort -m shuffled_wordlist_slice_*仓库 benches/sort_bench_merge.rs 的merge_pre_sorted_files正是把这一流程自动化生成 50 万行 ASCII 数据 → 切成 8 份 → 每份独立排序 → 用-m归并。locale 对归并路径的关键影响归并比较器是惰性的因此 locale 在这里影响极大在 UTF-8 localelocale-aware collation下归并不能为每行预先计算完整的 collation 排序键——k-way 归并总共只执行 O(n log k) 次比较单文件归并时一次比较都没有为每行都算完整排序键纯属浪费。务必同时基准 C locale 与 UTF-8 locale尤其要测单文件情形这是急切计算排序键的最坏情况# 单个已排序文件急切排序键计算的最坏场景 LC_ALLen_US.UTF-8 hyperfine --warmup 3 \ target/release/coreutils sort -m /usr/share/dict/words \ sort -m /usr/share/dict/words # 多个已排序分片 LC_ALLen_US.UTF-8 hyperfine --warmup 3 \ target/release/coreutils sort -m shuffled_wordlist_slice_* \ sort -m shuffled_wordlist_slice_*上述两个场景正好对应 benches/sort_locale_utf8_bench.rs 中的merge_single_file_utf8_locale与merge_pre_sorted_files_utf8_locale两个基准。该文件顶部还特别注释locale 必须在任何基准运行前设置std::env::set_var(LC_ALL, en_US.UTF-8)因为 locale 通过OnceLock在首次访问时缓存之后无法更改。归并的底层实现在 merge.rs读取与排序/写入分处两个线程通过 channel 传递 chunk 并复用内存分配effective_merge_batch_size还会参考文件描述符软限制fd_soft_limit动态收缩批大小预留 stdio 输出、Ctrl-C 处理等 fd 余量。排序侧compare_by见 sort.rs在fast_lexicographic时走字节快路径否则在 UTF-8 locale 下调用uucore::i18n::collator::locale_cmp按需 collate依赖i18n-collatorfeature默认开启。负载八检查是否已有序-c-c只检查输入是否已有序不输出排序结果因此基准输入应该用已排序的文件hyperfine target/release/coreutils sort -c sorted_wordlist.txt对应实现位于 check.rs检查逻辑逐行与前一行的compare_by结果比对。仓库基准 sort_locale_utf8_bench.rs 的check_sorted_utf8_locale覆盖了 UTF-8 locale 下的检查场景250 万行混合数据先排序再-c并解释了逐行按需 collate 优于逐行计算排序键。负载九stdin/stdout 管道性能文件 I/O 与管道 I/O 的性能特征不同上述所有负载都建议再以管道方式跑一遍去掉输入文件参数在命令前加cat [input_file] |去掉-o output.txt在命令末尾加 output.txt。示例把hyperfine target/release/coreutils sort shuffled_numbers.txt -n -o output.txt改成hyperfine cat shuffled_numbers.txt | target/release/coreutils sort -n output.txt然后确认性能与原基准相近。管道路径会影响sort的缓冲策略与并行度决策值得单独验证。与 GNU sort 对比hyperfine 天然支持多命令对比把命令字符串复制一份、去掉其中的target/release/coreutils前缀即可假设系统sort就是 GNU sorthyperfine target/release/coreutils sort shuffled_numbers_si.txt -h -o output.txt sort shuffled_numbers_si.txt -h -o output.txthyperfine 会输出两条命令各自的均值、中位数、标准差与相对比值是追踪回归和验证优化的标准做法。内存与 CPU 使用测量速度之外资源占用同样重要。可用 GNUtime注意与 bash 内建time区分可能需要先安装再用/bin/time -v强制调用外部版本/bin/time -v target/release/coreutils sort shuffled_numbers.txt输出示例Command being timed: target/release/coreutils sort shuffled_numbers.txt User time (seconds): 0.10 System time (seconds): 0.00 Percent of CPU this job got: 365% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.02 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 25360 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 5802 Voluntary context switches: 462 Involuntary context switches: 73 Swaps: 0 File system inputs: 1184 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Swaps: 0 Page size (bytes): 4096 Exit status: 0值得重点关注的指标User time纯 CPU 计算耗时反映排序算法本身的成本Percent of CPU this job got超过 100% 说明用上了多核并行sort通过 rayon 并行排序且支持--parallel控制线程数见 sort.rs 中PARALLEL选项与#[cfg(not(target_os wasi))]条件下的rayon::slice::ParallelSliceMutMaximum resident set size峰值内存直接关系到-S缓冲大小的合理性也是外部排序触发与否的观察窗口。仓库内置的 divan 基准如何运行除了 hyperfine 命令行方案仓库还维护了一套可直接运行的 divan 基准覆盖上述大部分负载便于在 CI 或本地快速回归。相关文件benches/sort_bench.rsASCII、重音字符、混合数据、大小写敏感/不敏感、字典序-d、数值-n、通用数值-g、反向-r、键字段-k 2、去重-u、超长行等 11 个基准benches/sort_bench_merge.rs-m归并 8 个预排序分片benches/sort_locale_utf8_bench.rsUTF-8 locale 下的 ASCII/混合/数值/反向/去重/超长行/长公共前缀/归并/外部排序/检查等场景其中sort_very_long_lines_utf8_locale专门复现超长行1 MB 单字符行导致完整 collation 排序键计算退化的病理场景benches/sort_locale_c_bench.rs 与 benches/sort_locale_de_bench.rsC locale 与德语 locale 变体。基准通过[[bench]]声明在 Cargo.toml 中harness false运行方式cargo bench -p uu_sort这些基准直接调用uu_sort::uumainrun_util_function/get_bench_args测试数据由 uucore 的 benchmark 模块setup_test_file创建临时文件并泄漏临时目录以保证基准期间存活text_data::generate_ascii_data等生成不同特征的文本数据提供。结语建立可持续的性能回归习惯本文从 BENCHMARKING.md 出发完整还原了 uutils coreutilssort的九类基准负载——词表、忽略大小写、数值、通用数值、SI 前缀、外部排序、归并、检查、管道 I/O——并扩展到 GNU sort 对比与内存/CPU 测量。要点可以归结为三条纪律多负载并行验证比较函数随参数变化单一负载的结论不可外推release 构建 hyperfine 定量任何改动先cargo build --release用--warmup消除冷启动噪声locale 场景单独覆盖UTF-8 locale 下的归并与检查路径采用惰性 collate 策略其性能特征与 C locale 截然不同。若你正在修改sort建议在改动前后分别运行上述 hyperfine 命令与仓库内置的cargo bench -p uu_sort以这份清单作为回归基线同时欢迎为清单补充新的负载让sort的性能评估覆盖更真实的场景。【免费下载链接】coreutilsCross-platform Rust rewrite of the GNU coreutils项目地址: https://gitcode.com/GitHub_Trending/co/coreutils创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →