Initial commit: DV homework_2

This commit is contained in:
2026-03-14 15:24:57 +08:00
commit 3c1f431ca9
38 changed files with 1580 additions and 0 deletions

114
report.md Normal file
View File

@@ -0,0 +1,114 @@
# 第二讲大作业实验报告:海量 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