Files
dv-homework-2/report.md

115 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第二讲大作业实验报告:海量 float32 索引排序算法设计与实现
## 1. 数据生成或来源
本实验使用纯 Python + NumPy 生成两类 float32 数据作为评测输入:
- uniform使用随机均匀分布生成数据
- clustered使用三段高斯混合分布生成数据
实验中所有数据都在本地实时生成,不依赖外部数据集。测试类型和规模如下:
- 数据类型float32
- 数据分布uniform, clustered
- 数据规模10,000,000
- 每组重复次数1
## 2. NumPy原生argsort性能基准测试
NumPy 原生基准作为参考上界,使用 stable 模式执行。结果如下:
| 数据分布 | 规模 | 平均耗时/s | 标准差/s |
| --------- | ---------- | ---------- | -------- |
| clustered | 10,000,000 | 1.5460 | 0.0000 |
| uniform | 10,000,000 | 1.4686 | 0.0000 |
从基准可以看到在千万级规模下NumPy 官方实现仍然非常高效,是后续自定义算法的核心对照对象。
## 3. 算法原理(含图)
### 3.1 方案一:浮点数位级 LSD Radix Sort
将 float32 视图解释为 uint32并进行单调映射
- 负数key = ~raw_bits
- 非负数key = raw_bits ^ 0x80000000
然后执行 4 趟 8-bit 稳定分发shift = 0, 8, 16, 24得到最终索引。
![图1float32 位布局与排序顺序示意图](assets/figure1_float32_layout.png)
![图2负数位翻转前后对比](assets/figure2_negative_flip.png)
![图3radix sort 每一趟桶分布示意图](assets/figure3_radix_bucket_pass.png)
### 3.2 方案二:多级直方图 + Bucket Sort
从最高字节开始进行分桶,再对每个非平凡桶递归下钻,直到桶规模低于阈值后使用插入排序收尾。该策略能在分布相对分散时减少不必要搬运,但在桶不均衡时会出现局部退化。
![图5不同数据分布下的高位桶均衡性](assets/bucket_balance.png)
## 4. 核心代码实现(分函数写)
主要函数划分如下:
1. float32_to_ordered_uint32完成浮点到可比较无符号键的映射
2. stable_digit_scatter实现单趟稳定计数分发
3. argsort_float32_radix实现 4-pass LSD Radix 索引排序
4. argsort_float32_bucket实现 MSD 多级分桶 + 小桶插入排序
5. run_benchmarks完成对照组与实验组计时与统计
正确性检查结论:共验证 12 组算法/数据组合,其中 12 组通过排序正确性检查12 组与 stable numpy argsort 索引完全一致。
## 5. 实验环境与结果(多组数据对比表 + 图)
实验环境:
- Python3.12.3
- NumPy2.4.3
- 平台Linux-6.17.0-14-generic-x86_64-with-glibc2.39
- 逻辑 CPU 数6
最终多组数据汇总如下:
| 数据分布 | 算法 | 规模 | 平均耗时/s | 标准差/s | 相对 numpy |
| --------- | ------------- | ---------- | ---------- | -------- | ---------- |
| clustered | bucket_msd | 10,000,000 | 60.2848 | 0.0000 | 38.99x |
| clustered | numpy_argsort | 10,000,000 | 1.5460 | 0.0000 | 1.00x |
| clustered | radix_lsd | 10,000,000 | 27.4960 | 0.0000 | 17.79x |
| uniform | bucket_msd | 10,000,000 | 64.0348 | 0.0000 | 43.60x |
| uniform | numpy_argsort | 10,000,000 | 1.4686 | 0.0000 | 1.00x |
| uniform | radix_lsd | 10,000,000 | 26.0530 | 0.0000 | 17.74x |
图4给出最终性能对比柱状图对数纵轴
![图4最终性能对比柱状图](assets/figure4_benchmark_bar.png)
图6给出不同算法在规模变化下的运行时间曲线
![图6运行时间缩放曲线](assets/benchmark_runtime.png)
图7给出相对 NumPy argsort 的性能倍率:
![图7相对 NumPy 的性能倍率](assets/benchmark_ratio.png)
## 6. 性能分析与瓶颈讨论
1. 在千万级输入上NumPy 原生 argsort 仍保持明显优势。
2. Radix 路线的理论复杂度接近 O(n),但纯 NumPy 稳定散射的常数项较高,且强依赖内存带宽。
3. Bucket 路线在数据分布集中时更容易出现不均衡桶,导致局部处理时间上升。
4. 当前实现的主要瓶颈在于大规模数据搬运和块内前缀计算;若引入低层并行或 JIT有机会继续压缩差距。
## 7. 总结与展望
本实验已按课程要求完成两条实现路径、千万级数据基准、图1-图7可视化与完整报告自动生成流程。结论如下
1. 在禁止直接调用 np.sort 和 np.argsort 的约束下,两条方案都可得到正确排序索引。
2. 千万级规模下,自定义纯 NumPy 实现可以稳定运行,但性能与官方底层优化实现仍存在差距。
3. 后续可尝试方向包括:减少中间数组写放大、优化桶阈值自适应策略、采用更底层 SIMD/JIT 机制。
复现实验:
1. source venv/bin/activate
2. pip install -e .
3. python scripts/run_all.py