bigywater.tech
Rust · 69

sort 与 sort_unstable

稳定排序和不稳定排序的区别不只是「相同元素会不会换位置」。这篇用相同 key 的例子证明稳定性,用分配器计数看内存,用 100 万个元素测速度,再看 key 函数的调用次数和 f64 排序的出路。

513 个
sort 开始产生堆分配的 u64 元素个数
20898 对 1000
sort_by_key 与 sort_by_cached_key 的 key 函数调用次数(1000 个元素)
E0277
f64 直接 sort

稳定性

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    200 个元素、3 种 key:sort_by_key 保持了相同 key 的原顺序: true  sort_unstable_by_key 保持了: false
    sort_unstable 打乱的一个例子:相邻两个 key 相同的元素 (0, 113) 排在 (0, 6) 前面(原始序号反了)
    两种排序的 key 序列相同: true

内存

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    n=    10: sort 分配 0 次 0 字节   sort_unstable 分配 0 次 0 字节
    n=    20: sort 分配 0 次 0 字节   sort_unstable 分配 0 次 0 字节
    n=   100: sort 分配 0 次 0 字节   sort_unstable 分配 0 次 0 字节
    第一个让 sort 发生堆分配的元素个数(u64,在 1..=5000 里扫描): Some(513);sort_unstable 在 1..=5000 里的第一次分配: None
    n= 10000: sort 分配 1 次 80000 字节   sort_unstable 分配 0 次 0 字节

速度

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    随机         sort   14.313 ms   sort_unstable   11.198 ms
    已有序        sort    0.296 ms   sort_unstable    0.295 ms
    逆序         sort    0.435 ms   sort_unstable    0.436 ms
    大量重复       sort    2.281 ms   sort_unstable    2.054 ms

耗时是本机 release 实测,元素是 u64;更大的元素或昂贵的比较函数会改变结论。

sort_by_key 与缓存

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    1000 个元素:sort_by_key 的 key 函数被调用 20898 次;sort_by_cached_key 调用 1000 次;结果相同: true

浮点数排序

实测 · 本机执行输出
  源码:
      1 | fn main() { let mut v = vec![2.5, 1.0]; v.sort(); }
  编译:失败
    error[E0277]: the trait bound `{float}: Ord` is not satisfied
       --> <实验目录>/src/u69e.rs:1:43
        |
      1 | fn main() { let mut v = vec![2.5, 1.0]; v.sort(); }
        |                                           ^^^^ the trait `Ord` is not implemented for `{float}`
        |
        = help: the following other types implement trait `Ord`:
                  i128
                  i16
                  i32
                  i64
                  i8
                  isize
                  u128
                  u16
                and 4 others
    note: required by a bound in `slice::<impl [T]>::sort`
       --> <rust 标准库源码>/library/alloc/src/slice.rs:133:12
        |
    131 |     pub fn sort(&mut self)
        |            ---- required by a bound in this associated function
    132 |     where
    133 |         T: Ord,
        |            ^^^ required by this bound in `slice::<impl [T]>::sort`
    
    …(共 28 行)
  编译:成功
  运行(退出码 0):
    total_cmp: [-1.0, -0.0, 0.0, 1.0, 2.5, NaN]
    partial_cmp.unwrap(): [-1.0, 1.0, 2.5]
    total_cmp 把 -0.0 排在 0.0 前面,NaN 排在最后(正 NaN)

f64 没有 Ord 的原因与 NaN 有关,这里只验证了编译错误与 total_cmp 的输出。

分步演示

sort 对 sort_unstable
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。计时 release + black_box,取最小值
    • 没有验证:sort 与 sort_unstable 的具体算法(没读源码)、元素很大时的差别、比较函数不满足全序时的行为(可能 panic)、partial_cmp 遇到 NaN 的 panic、select_nth_unstable
    京ICP备2026067502号-1京公网安备11010502063487号