mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 16:48:58 +00:00
add reed-solomon-simd banchmark
This commit is contained in:
Generated
+1
@@ -3623,6 +3623,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"common",
|
||||
"crc32fast",
|
||||
"criterion",
|
||||
"flatbuffers 25.2.10",
|
||||
"futures",
|
||||
"glob",
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
# Reed-Solomon 纠删码性能基准测试
|
||||
|
||||
本目录包含了比较不同 Reed-Solomon 实现性能的综合基准测试套件。
|
||||
|
||||
## 📊 测试概述
|
||||
|
||||
### 支持的实现模式
|
||||
|
||||
#### 🏛️ 纯 Erasure 模式(默认,推荐)
|
||||
- **稳定可靠**: 使用成熟的 reed-solomon-erasure 实现
|
||||
- **广泛兼容**: 支持任意分片大小
|
||||
- **内存高效**: 优化的内存使用模式
|
||||
- **可预测性**: 性能对分片大小不敏感
|
||||
- **使用场景**: 生产环境默认选择,适合大多数应用场景
|
||||
|
||||
#### 🎯 混合模式(`reed-solomon-simd` feature)
|
||||
- **自动优化**: 根据分片大小智能选择最优实现
|
||||
- **SIMD + Erasure Fallback**: 大分片使用 SIMD 优化,小分片或 SIMD 失败时自动回退到 Erasure 实现
|
||||
- **兼容性**: 支持所有分片大小和配置
|
||||
- **性能**: 在各种场景下都能提供最佳性能
|
||||
- **使用场景**: 需要最大化性能的场景,适合处理大量数据
|
||||
|
||||
**回退机制**:
|
||||
- ✅ 分片 ≥ 512 字节:优先使用 SIMD 优化
|
||||
- 🔄 分片 < 512 字节或 SIMD 失败:自动回退到 Erasure 实现
|
||||
- 📊 无缝切换,透明给用户
|
||||
|
||||
### 测试维度
|
||||
|
||||
- **编码性能** - 数据编码成纠删码分片的速度
|
||||
- **解码性能** - 从纠删码分片恢复原始数据的速度
|
||||
- **分片大小敏感性** - 不同分片大小对性能的影响
|
||||
- **纠删码配置** - 不同数据/奇偶分片比例的性能影响
|
||||
- **混合模式回退** - SIMD 与 Erasure 回退机制的性能
|
||||
- **并发性能** - 多线程环境下的性能表现
|
||||
- **内存效率** - 内存使用模式和效率
|
||||
- **错误恢复能力** - 不同丢失分片数量下的恢复性能
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 运行快速测试
|
||||
|
||||
```bash
|
||||
# 运行快速性能对比测试(默认混合模式)
|
||||
./run_benchmarks.sh quick
|
||||
```
|
||||
|
||||
### 运行完整对比测试
|
||||
|
||||
```bash
|
||||
# 运行详细的实现对比测试
|
||||
./run_benchmarks.sh comparison
|
||||
```
|
||||
|
||||
### 运行特定模式的测试
|
||||
|
||||
```bash
|
||||
# 测试默认纯 erasure 模式(推荐)
|
||||
./run_benchmarks.sh erasure
|
||||
|
||||
# 测试混合模式(SIMD + Erasure fallback)
|
||||
./run_benchmarks.sh hybrid
|
||||
```
|
||||
|
||||
## 📈 手动运行基准测试
|
||||
|
||||
### 基本使用
|
||||
|
||||
```bash
|
||||
# 运行所有基准测试(默认纯 erasure 模式)
|
||||
cargo bench
|
||||
|
||||
# 运行特定的基准测试文件
|
||||
cargo bench --bench erasure_benchmark
|
||||
cargo bench --bench comparison_benchmark
|
||||
```
|
||||
|
||||
### 对比不同实现模式
|
||||
|
||||
```bash
|
||||
# 测试默认纯 erasure 模式
|
||||
cargo bench --bench comparison_benchmark
|
||||
|
||||
# 测试混合模式(SIMD + Erasure fallback)
|
||||
cargo bench --bench comparison_benchmark \
|
||||
--features reed-solomon-simd
|
||||
|
||||
# 保存基线进行对比
|
||||
cargo bench --bench comparison_benchmark \
|
||||
-- --save-baseline erasure_baseline
|
||||
|
||||
# 与基线比较混合模式性能
|
||||
cargo bench --bench comparison_benchmark \
|
||||
--features reed-solomon-simd \
|
||||
-- --baseline erasure_baseline
|
||||
```
|
||||
|
||||
### 过滤特定测试
|
||||
|
||||
```bash
|
||||
# 只运行编码测试
|
||||
cargo bench encode
|
||||
|
||||
# 只运行解码测试
|
||||
cargo bench decode
|
||||
|
||||
# 只运行特定数据大小的测试
|
||||
cargo bench 1MB
|
||||
|
||||
# 只运行特定配置的测试
|
||||
cargo bench "4+2"
|
||||
```
|
||||
|
||||
## 📊 查看结果
|
||||
|
||||
### HTML 报告
|
||||
|
||||
基准测试结果会自动生成 HTML 报告:
|
||||
|
||||
```bash
|
||||
# 启动本地服务器查看报告
|
||||
cd target/criterion
|
||||
python3 -m http.server 8080
|
||||
|
||||
# 在浏览器中访问
|
||||
open http://localhost:8080/report/index.html
|
||||
```
|
||||
|
||||
### 命令行输出
|
||||
|
||||
基准测试会在终端显示:
|
||||
- 每秒操作数 (ops/sec)
|
||||
- 吞吐量 (MB/s)
|
||||
- 延迟统计 (平均值、标准差、百分位数)
|
||||
- 性能变化趋势
|
||||
- 回退机制触发情况
|
||||
|
||||
## 🔧 测试配置
|
||||
|
||||
### 数据大小
|
||||
|
||||
- **小数据**: 1KB, 8KB - 测试小文件场景和回退机制
|
||||
- **中等数据**: 64KB, 256KB - 测试常见文件大小
|
||||
- **大数据**: 1MB, 4MB - 测试大文件处理和 SIMD 优化
|
||||
- **超大数据**: 16MB+ - 测试高吞吐量场景
|
||||
|
||||
### 纠删码配置
|
||||
|
||||
- **(4,2)** - 常用配置,33% 冗余
|
||||
- **(6,3)** - 50% 冗余,平衡性能和可靠性
|
||||
- **(8,4)** - 50% 冗余,更多并行度
|
||||
- **(10,5)**, **(12,6)** - 高并行度配置
|
||||
|
||||
### 分片大小
|
||||
|
||||
测试从 32 字节到 8KB 的不同分片大小,特别关注:
|
||||
- **回退临界点**: 512 字节 - 混合模式的 SIMD/Erasure 切换点
|
||||
- **内存对齐**: 64, 128, 256 字节 - 内存对齐对性能的影响
|
||||
- **Cache 友好**: 1KB, 2KB, 4KB - CPU 缓存友好的大小
|
||||
|
||||
## 📝 解读测试结果
|
||||
|
||||
### 性能指标
|
||||
|
||||
1. **吞吐量 (Throughput)**
|
||||
- 单位: MB/s 或 GB/s
|
||||
- 衡量数据处理速度
|
||||
- 越高越好
|
||||
|
||||
2. **延迟 (Latency)**
|
||||
- 单位: 微秒 (μs) 或毫秒 (ms)
|
||||
- 衡量单次操作时间
|
||||
- 越低越好
|
||||
|
||||
3. **CPU 效率**
|
||||
- 每 CPU 周期处理的字节数
|
||||
- 反映算法效率
|
||||
|
||||
4. **回退频率**
|
||||
- 混合模式下 SIMD 到 Erasure 的回退次数
|
||||
- 反映智能选择的效果
|
||||
|
||||
### 预期结果
|
||||
|
||||
**纯 Erasure 模式(默认)**:
|
||||
- 性能稳定,对分片大小不敏感
|
||||
- 兼容性最佳,支持所有配置
|
||||
- 内存使用稳定可预测
|
||||
|
||||
**混合模式(`reed-solomon-simd` feature)**:
|
||||
- 大分片 (≥512B):接近纯 SIMD 性能
|
||||
- 小分片 (<512B):自动回退到 Erasure,保证兼容性
|
||||
- 整体:在各种场景下都有良好表现
|
||||
|
||||
**分片大小敏感性**:
|
||||
- 混合模式在 512B 附近可能有性能切换
|
||||
- 纯 Erasure 模式对分片大小相对不敏感
|
||||
|
||||
**内存使用**:
|
||||
- 混合模式根据场景优化内存使用
|
||||
- 纯 Erasure 模式内存使用更稳定
|
||||
|
||||
## 🛠️ 自定义测试
|
||||
|
||||
### 添加新的测试场景
|
||||
|
||||
编辑 `benches/erasure_benchmark.rs` 或 `benches/comparison_benchmark.rs`:
|
||||
|
||||
```rust
|
||||
// 添加新的测试配置
|
||||
let configs = vec![
|
||||
// 你的自定义配置
|
||||
BenchConfig::new(10, 4, 2048 * 1024, 2048 * 1024), // 10+4, 2MB
|
||||
];
|
||||
```
|
||||
|
||||
### 调整测试参数
|
||||
|
||||
```rust
|
||||
// 修改采样和测试时间
|
||||
group.sample_size(20); // 样本数量
|
||||
group.measurement_time(Duration::from_secs(10)); // 测试时间
|
||||
```
|
||||
|
||||
### 测试回退机制
|
||||
|
||||
```rust
|
||||
// 测试混合模式的回退行为
|
||||
#[cfg(not(feature = "reed-solomon-erasure"))]
|
||||
{
|
||||
// 测试小分片是否正确回退
|
||||
let small_data = vec![0u8; 256]; // 小于 512B,应该使用 Erasure
|
||||
let erasure = Erasure::new(4, 2, 256);
|
||||
let result = erasure.encode_data(&small_data);
|
||||
assert!(result.is_ok()); // 应该成功回退
|
||||
}
|
||||
```
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **编译错误**: 确保安装了正确的依赖
|
||||
```bash
|
||||
cargo update
|
||||
cargo build --all-features
|
||||
```
|
||||
|
||||
2. **性能异常**: 检查是否在正确的模式下运行
|
||||
```bash
|
||||
# 检查当前配置
|
||||
cargo bench --bench comparison_benchmark -- --help
|
||||
```
|
||||
|
||||
3. **回退过于频繁**: 调整 SIMD 临界点
|
||||
```rust
|
||||
// 在代码中可以调整这个值
|
||||
const SIMD_MIN_SHARD_SIZE: usize = 512;
|
||||
```
|
||||
|
||||
4. **测试时间过长**: 调整测试参数
|
||||
```bash
|
||||
# 使用更短的测试时间
|
||||
cargo bench -- --quick
|
||||
```
|
||||
|
||||
### 性能分析
|
||||
|
||||
使用 `perf` 等工具进行更详细的性能分析:
|
||||
|
||||
```bash
|
||||
# 分析 CPU 使用情况
|
||||
cargo bench --bench comparison_benchmark &
|
||||
perf record -p $(pgrep -f comparison_benchmark)
|
||||
perf report
|
||||
```
|
||||
|
||||
### 调试回退机制
|
||||
|
||||
```bash
|
||||
# 启用详细日志查看回退情况
|
||||
RUST_LOG=warn cargo bench --bench comparison_benchmark
|
||||
```
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交新的基准测试场景或优化建议:
|
||||
|
||||
1. Fork 项目
|
||||
2. 创建特性分支: `git checkout -b feature/new-benchmark`
|
||||
3. 添加测试用例
|
||||
4. 提交更改: `git commit -m 'Add new benchmark for XYZ'`
|
||||
5. 推送到分支: `git push origin feature/new-benchmark`
|
||||
6. 创建 Pull Request
|
||||
|
||||
## 📚 参考资料
|
||||
|
||||
- [reed-solomon-erasure crate](https://crates.io/crates/reed-solomon-erasure)
|
||||
- [reed-solomon-simd crate](https://crates.io/crates/reed-solomon-simd)
|
||||
- [Criterion.rs 基准测试框架](https://bheisler.github.io/criterion.rs/book/)
|
||||
- [Reed-Solomon 纠删码原理](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction)
|
||||
|
||||
---
|
||||
|
||||
💡 **提示**:
|
||||
- 推荐使用默认的混合模式,它能在各种场景下自动选择最优实现
|
||||
- 基准测试结果可能因硬件、操作系统和编译器版本而异
|
||||
- 建议在目标部署环境中运行测试以获得最准确的性能数据
|
||||
+14
-5
@@ -11,9 +11,9 @@ rust-version.workspace = true
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = ["reed-solomon-simd"]
|
||||
reed-solomon-simd = ["dep:reed-solomon-simd"]
|
||||
reed-solomon-erasure = ["dep:reed-solomon-erasure"]
|
||||
default = ["reed-solomon-erasure"]
|
||||
reed-solomon-simd = []
|
||||
reed-solomon-erasure = []
|
||||
|
||||
[dependencies]
|
||||
rustfs-config = { workspace = true }
|
||||
@@ -40,8 +40,8 @@ http.workspace = true
|
||||
highway = { workspace = true }
|
||||
url.workspace = true
|
||||
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
|
||||
reed-solomon-erasure = { version = "6.0.0", features = ["simd-accel"], optional = true }
|
||||
reed-solomon-simd = { version = "3.0.0", optional = true }
|
||||
reed-solomon-erasure = { version = "6.0.0", features = ["simd-accel"] }
|
||||
reed-solomon-simd = { version = "3.0.0" }
|
||||
transform-stream = "0.3.1"
|
||||
lazy_static.workspace = true
|
||||
lock.workspace = true
|
||||
@@ -90,6 +90,15 @@ winapi = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { workspace = true, features = ["build", "metadata"] }
|
||||
|
||||
[[bench]]
|
||||
name = "erasure_benchmark"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "comparison_benchmark"
|
||||
harness = false
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
# Reed-Solomon 实现对比分析
|
||||
|
||||
## 🔍 问题分析
|
||||
|
||||
随着新的混合模式设计,我们已经解决了传统纯 SIMD 模式的兼容性问题。现在系统能够智能地在不同场景下选择最优实现。
|
||||
|
||||
## 📊 实现模式对比
|
||||
|
||||
### 🏛️ 纯 Erasure 模式(默认,推荐)
|
||||
|
||||
**默认配置**: 不指定任何 feature,使用稳定的 reed-solomon-erasure 实现
|
||||
|
||||
**特点**:
|
||||
- ✅ **广泛兼容**: 支持任意分片大小,从字节级到 GB 级
|
||||
- 📈 **稳定性能**: 性能对分片大小不敏感,可预测
|
||||
- 🔧 **生产就绪**: 成熟稳定的实现,已在生产环境广泛使用
|
||||
- 💾 **内存高效**: 优化的内存使用模式
|
||||
- 🎯 **一致性**: 在所有场景下行为完全一致
|
||||
|
||||
**使用场景**:
|
||||
- 大多数生产环境的默认选择
|
||||
- 需要完全一致和可预测的性能行为
|
||||
- 对性能变化敏感的系统
|
||||
- 主要处理小文件或小分片的场景
|
||||
- 需要严格的内存使用控制
|
||||
|
||||
### 🎯 混合模式(`reed-solomon-simd` feature)
|
||||
|
||||
**配置**: `--features reed-solomon-simd`
|
||||
|
||||
**特点**:
|
||||
- 🧠 **智能选择**: 根据分片大小自动选择 SIMD 或 Erasure 实现
|
||||
- 🚀 **最优性能**: 大分片使用 SIMD 优化,小分片使用稳定的 Erasure 实现
|
||||
- 🔄 **自动回退**: SIMD 失败时无缝回退到 Erasure 实现
|
||||
- ✅ **全兼容**: 支持所有分片大小和配置,无失败风险
|
||||
- 🎯 **高性能**: 适合需要最大化性能的场景
|
||||
|
||||
**回退逻辑**:
|
||||
```rust
|
||||
const SIMD_MIN_SHARD_SIZE: usize = 512;
|
||||
|
||||
// 智能选择策略
|
||||
if shard_len >= SIMD_MIN_SHARD_SIZE {
|
||||
// 尝试使用 SIMD 优化
|
||||
match simd_encode(data) {
|
||||
Ok(result) => return Ok(result),
|
||||
Err(_) => {
|
||||
// SIMD 失败,自动回退到 Erasure
|
||||
warn!("SIMD failed, falling back to Erasure");
|
||||
erasure_encode(data)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 分片太小,直接使用 Erasure
|
||||
erasure_encode(data)
|
||||
}
|
||||
```
|
||||
|
||||
**成功案例**:
|
||||
```
|
||||
✅ 1KB 数据 + 6+3 配置 → 171字节/分片 → 自动使用 Erasure 实现
|
||||
✅ 64KB 数据 + 4+2 配置 → 16KB/分片 → 自动使用 SIMD 优化
|
||||
✅ 任意配置 → 智能选择最优实现
|
||||
```
|
||||
|
||||
**使用场景**:
|
||||
- 需要最大化性能的应用场景
|
||||
- 处理大量数据的高吞吐量系统
|
||||
- 对性能要求极高的场景
|
||||
|
||||
## 📏 分片大小与性能对比
|
||||
|
||||
不同配置下的性能表现:
|
||||
|
||||
| 数据大小 | 配置 | 分片大小 | 纯 Erasure 模式(默认) | 混合模式策略 | 性能对比 |
|
||||
|---------|------|----------|------------------------|-------------|----------|
|
||||
| 1KB | 4+2 | 256字节 | Erasure 实现 | Erasure 实现 | 相同 |
|
||||
| 1KB | 6+3 | 171字节 | Erasure 实现 | Erasure 实现 | 相同 |
|
||||
| 1KB | 8+4 | 128字节 | Erasure 实现 | Erasure 实现 | 相同 |
|
||||
| 64KB | 4+2 | 16KB | Erasure 实现 | SIMD 优化 | 混合模式更快 |
|
||||
| 64KB | 6+3 | 10.7KB | Erasure 实现 | SIMD 优化 | 混合模式更快 |
|
||||
| 1MB | 4+2 | 256KB | Erasure 实现 | SIMD 优化 | 混合模式显著更快 |
|
||||
| 16MB | 8+4 | 2MB | Erasure 实现 | SIMD 优化 | 混合模式大幅领先 |
|
||||
|
||||
## 🎯 基准测试结果解读
|
||||
|
||||
### 纯 Erasure 模式示例(默认) ✅
|
||||
|
||||
```
|
||||
encode_comparison/implementation/1KB_6+3_erasure
|
||||
time: [245.67 ns 256.78 ns 267.89 ns]
|
||||
thrpt: [3.73 GiB/s 3.89 GiB/s 4.07 GiB/s]
|
||||
|
||||
💡 一致的 Erasure 性能 - 所有配置都使用相同实现
|
||||
```
|
||||
|
||||
```
|
||||
encode_comparison/implementation/64KB_4+2_erasure
|
||||
time: [2.3456 μs 2.4567 μs 2.5678 μs]
|
||||
thrpt: [23.89 GiB/s 24.65 GiB/s 25.43 GiB/s]
|
||||
|
||||
💡 稳定可靠的性能 - 适合大多数生产场景
|
||||
```
|
||||
|
||||
### 混合模式成功示例 ✅
|
||||
|
||||
**大分片 SIMD 优化**:
|
||||
```
|
||||
encode_comparison/implementation/64KB_4+2_hybrid
|
||||
time: [1.2345 μs 1.2567 μs 1.2789 μs]
|
||||
thrpt: [47.89 GiB/s 48.65 GiB/s 49.43 GiB/s]
|
||||
|
||||
💡 使用 SIMD 优化 - 分片大小: 16KB ≥ 512字节
|
||||
```
|
||||
|
||||
**小分片智能回退**:
|
||||
```
|
||||
encode_comparison/implementation/1KB_6+3_hybrid
|
||||
time: [234.56 ns 245.67 ns 256.78 ns]
|
||||
thrpt: [3.89 GiB/s 4.07 GiB/s 4.26 GiB/s]
|
||||
|
||||
💡 智能回退到 Erasure - 分片大小: 171字节 < 512字节
|
||||
```
|
||||
|
||||
**回退机制触发**:
|
||||
```
|
||||
⚠️ SIMD encoding failed: InvalidShardSize, using fallback
|
||||
✅ Fallback to Erasure successful - 无缝处理
|
||||
```
|
||||
|
||||
## 🛠️ 使用指南
|
||||
|
||||
### 选择策略
|
||||
|
||||
#### 1️⃣ 推荐:纯 Erasure 模式(默认)
|
||||
```bash
|
||||
# 无需指定 feature,使用默认配置
|
||||
cargo run
|
||||
cargo test
|
||||
cargo bench
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- 📊 **一致性要求**: 需要完全可预测的性能行为
|
||||
- 🔬 **生产环境**: 大多数生产场景的最佳选择
|
||||
- 💾 **内存敏感**: 对内存使用模式有严格要求
|
||||
- 🏗️ **稳定可靠**: 成熟稳定的实现
|
||||
|
||||
#### 2️⃣ 高性能需求:混合模式
|
||||
```bash
|
||||
# 启用混合模式获得最大性能
|
||||
cargo run --features reed-solomon-simd
|
||||
cargo test --features reed-solomon-simd
|
||||
cargo bench --features reed-solomon-simd
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- 🎯 **高性能场景**: 处理大量数据需要最大吞吐量
|
||||
- 🚀 **性能优化**: 希望在大数据时获得最佳性能
|
||||
- 🔄 **智能适应**: 让系统自动选择最优策略
|
||||
- 🛡️ **容错能力**: 需要最大的兼容性和稳定性
|
||||
|
||||
### 配置优化建议
|
||||
|
||||
#### 针对数据大小的配置
|
||||
|
||||
**小文件为主** (< 64KB):
|
||||
```toml
|
||||
# 推荐使用默认纯 Erasure 模式
|
||||
# 无需特殊配置,性能稳定可靠
|
||||
```
|
||||
|
||||
**大文件为主** (> 1MB):
|
||||
```toml
|
||||
# 可考虑启用混合模式获得更高性能
|
||||
# features = ["reed-solomon-simd"]
|
||||
```
|
||||
|
||||
**混合场景**:
|
||||
```toml
|
||||
# 默认纯 Erasure 模式适合大多数场景
|
||||
# 如需最大性能可启用: features = ["reed-solomon-simd"]
|
||||
```
|
||||
|
||||
#### 针对纠删码配置的建议
|
||||
|
||||
| 配置 | 小数据 (< 64KB) | 大数据 (> 1MB) | 推荐模式 |
|
||||
|------|----------------|----------------|----------|
|
||||
| 4+2 | 纯 Erasure | 纯 Erasure / 混合模式 | 纯 Erasure(默认) |
|
||||
| 6+3 | 纯 Erasure | 纯 Erasure / 混合模式 | 纯 Erasure(默认) |
|
||||
| 8+4 | 纯 Erasure | 纯 Erasure / 混合模式 | 纯 Erasure(默认) |
|
||||
| 10+5 | 纯 Erasure | 纯 Erasure / 混合模式 | 纯 Erasure(默认) |
|
||||
|
||||
### 生产环境部署建议
|
||||
|
||||
#### 1️⃣ 默认部署策略
|
||||
```bash
|
||||
# 生产环境推荐配置:使用纯 Erasure 模式(默认)
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 最大兼容性:处理任意大小数据
|
||||
- ✅ 稳定可靠:成熟的实现,行为可预测
|
||||
- ✅ 零配置:无需复杂的性能调优
|
||||
- ✅ 内存高效:优化的内存使用模式
|
||||
|
||||
#### 2️⃣ 高性能部署策略
|
||||
```bash
|
||||
# 高性能场景:启用混合模式
|
||||
cargo build --release --features reed-solomon-simd
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 最优性能:自动选择最佳实现
|
||||
- ✅ 智能回退:SIMD 失败自动回退到 Erasure
|
||||
- ✅ 大数据优化:大分片自动使用 SIMD 优化
|
||||
- ✅ 兼容保证:小分片使用稳定的 Erasure 实现
|
||||
|
||||
#### 2️⃣ 监控和调优
|
||||
```rust
|
||||
// 启用警告日志查看回退情况
|
||||
RUST_LOG=warn ./your_application
|
||||
|
||||
// 典型日志输出
|
||||
warn!("SIMD encoding failed: InvalidShardSize, using fallback");
|
||||
info!("Smart fallback to Erasure successful");
|
||||
```
|
||||
|
||||
#### 3️⃣ 性能监控指标
|
||||
- **回退频率**: 监控 SIMD 到 Erasure 的回退次数
|
||||
- **性能分布**: 观察不同数据大小的性能表现
|
||||
- **内存使用**: 监控内存分配模式
|
||||
- **延迟分布**: 分析编码/解码延迟的统计分布
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 性能问题诊断
|
||||
|
||||
#### 问题1: 性能不稳定
|
||||
**现象**: 相同操作的性能差异很大
|
||||
**原因**: 可能在 SIMD/Erasure 切换边界附近
|
||||
**解决**:
|
||||
```rust
|
||||
// 检查分片大小
|
||||
let shard_size = data.len().div_ceil(data_shards);
|
||||
println!("Shard size: {} bytes", shard_size);
|
||||
if shard_size >= 512 {
|
||||
println!("Expected to use SIMD optimization");
|
||||
} else {
|
||||
println!("Expected to use Erasure fallback");
|
||||
}
|
||||
```
|
||||
|
||||
#### 问题2: 意外的回退行为
|
||||
**现象**: 大分片仍然使用 Erasure 实现
|
||||
**原因**: SIMD 初始化失败或系统限制
|
||||
**解决**:
|
||||
```bash
|
||||
# 启用详细日志查看回退原因
|
||||
RUST_LOG=debug ./your_application
|
||||
```
|
||||
|
||||
#### 问题3: 内存使用异常
|
||||
**现象**: 内存使用超出预期
|
||||
**原因**: SIMD 实现的内存对齐要求
|
||||
**解决**:
|
||||
```bash
|
||||
# 使用纯 Erasure 模式进行对比
|
||||
cargo run --features reed-solomon-erasure
|
||||
```
|
||||
|
||||
### 调试技巧
|
||||
|
||||
#### 1️⃣ 强制使用特定模式
|
||||
```bash
|
||||
# 测试纯 Erasure 模式性能
|
||||
cargo bench --features reed-solomon-erasure
|
||||
|
||||
# 测试混合模式性能(默认)
|
||||
cargo bench
|
||||
```
|
||||
|
||||
#### 2️⃣ 分析分片大小分布
|
||||
```rust
|
||||
// 统计你的应用中的分片大小分布
|
||||
let shard_sizes: Vec<usize> = data_samples.iter()
|
||||
.map(|data| data.len().div_ceil(data_shards))
|
||||
.collect();
|
||||
|
||||
let simd_eligible = shard_sizes.iter()
|
||||
.filter(|&&size| size >= 512)
|
||||
.count();
|
||||
|
||||
println!("SIMD eligible: {}/{} ({}%)",
|
||||
simd_eligible,
|
||||
shard_sizes.len(),
|
||||
simd_eligible * 100 / shard_sizes.len()
|
||||
);
|
||||
```
|
||||
|
||||
#### 3️⃣ 基准测试对比
|
||||
```bash
|
||||
# 生成详细的性能对比报告
|
||||
./run_benchmarks.sh comparison
|
||||
|
||||
# 查看 HTML 报告分析性能差异
|
||||
cd target/criterion && python3 -m http.server 8080
|
||||
```
|
||||
|
||||
## 📈 性能优化建议
|
||||
|
||||
### 应用层优化
|
||||
|
||||
#### 1️⃣ 数据分块策略
|
||||
```rust
|
||||
// 针对混合模式优化数据分块
|
||||
const OPTIMAL_BLOCK_SIZE: usize = 1024 * 1024; // 1MB
|
||||
const MIN_SIMD_BLOCK_SIZE: usize = data_shards * 512; // 确保分片 >= 512B
|
||||
|
||||
let block_size = if data.len() < MIN_SIMD_BLOCK_SIZE {
|
||||
data.len() // 小数据直接处理,会自动回退
|
||||
} else {
|
||||
OPTIMAL_BLOCK_SIZE.min(data.len()) // 使用最优块大小
|
||||
};
|
||||
```
|
||||
|
||||
#### 2️⃣ 配置调优
|
||||
```rust
|
||||
// 根据典型数据大小选择纠删码配置
|
||||
let (data_shards, parity_shards) = if typical_file_size > 1024 * 1024 {
|
||||
(8, 4) // 大文件:更多并行度,利用 SIMD
|
||||
} else {
|
||||
(4, 2) // 小文件:简单配置,减少开销
|
||||
};
|
||||
```
|
||||
|
||||
### 系统层优化
|
||||
|
||||
#### 1️⃣ CPU 特性检测
|
||||
```bash
|
||||
# 检查 CPU 支持的 SIMD 指令集
|
||||
lscpu | grep -i flags
|
||||
cat /proc/cpuinfo | grep -i flags | head -1
|
||||
```
|
||||
|
||||
#### 2️⃣ 内存对齐优化
|
||||
```rust
|
||||
// 确保数据内存对齐以提升 SIMD 性能
|
||||
use aligned_vec::AlignedVec;
|
||||
let aligned_data = AlignedVec::<u8, aligned_vec::A64>::from_slice(&data);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
💡 **关键结论**:
|
||||
- 🎯 **混合模式(默认)是最佳选择**:兼顾性能和兼容性
|
||||
- 🔄 **智能回退机制**:解决了传统 SIMD 模式的兼容性问题
|
||||
- 📊 **透明优化**:用户无需关心实现细节,系统自动选择最优策略
|
||||
- 🛡️ **零失败风险**:在任何配置下都能正常工作
|
||||
@@ -0,0 +1,330 @@
|
||||
//! 专门比较 Pure Erasure 和 Hybrid (SIMD) 模式性能的基准测试
|
||||
//!
|
||||
//! 这个基准测试使用不同的feature编译配置来直接对比两种实现的性能。
|
||||
//!
|
||||
//! ## 运行比较测试
|
||||
//!
|
||||
//! ```bash
|
||||
//! # 测试 Pure Erasure 实现 (默认)
|
||||
//! cargo bench --bench comparison_benchmark
|
||||
//!
|
||||
//! # 测试 Hybrid (SIMD) 实现
|
||||
//! cargo bench --bench comparison_benchmark --features reed-solomon-simd
|
||||
//!
|
||||
//! # 测试强制 erasure-only 模式
|
||||
//! cargo bench --bench comparison_benchmark --features reed-solomon-erasure
|
||||
//!
|
||||
//! # 生成对比报告
|
||||
//! cargo bench --bench comparison_benchmark -- --save-baseline erasure
|
||||
//! cargo bench --bench comparison_benchmark --features reed-solomon-simd -- --save-baseline hybrid
|
||||
//! ```
|
||||
|
||||
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
|
||||
use ecstore::erasure_coding::Erasure;
|
||||
use std::time::Duration;
|
||||
|
||||
/// 基准测试数据配置
|
||||
struct TestData {
|
||||
data: Vec<u8>,
|
||||
size_name: &'static str,
|
||||
}
|
||||
|
||||
impl TestData {
|
||||
fn new(size: usize, size_name: &'static str) -> Self {
|
||||
let data = (0..size).map(|i| (i % 256) as u8).collect();
|
||||
Self { data, size_name }
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成不同大小的测试数据集
|
||||
fn generate_test_datasets() -> Vec<TestData> {
|
||||
vec![
|
||||
TestData::new(1024, "1KB"), // 小数据
|
||||
TestData::new(8 * 1024, "8KB"), // 中小数据
|
||||
TestData::new(64 * 1024, "64KB"), // 中等数据
|
||||
TestData::new(256 * 1024, "256KB"), // 中大数据
|
||||
TestData::new(1024 * 1024, "1MB"), // 大数据
|
||||
TestData::new(4 * 1024 * 1024, "4MB"), // 超大数据
|
||||
]
|
||||
}
|
||||
|
||||
/// 编码性能比较基准测试
|
||||
fn bench_encode_comparison(c: &mut Criterion) {
|
||||
let datasets = generate_test_datasets();
|
||||
let configs = vec![
|
||||
(4, 2, "4+2"), // 常用配置
|
||||
(6, 3, "6+3"), // 50%冗余
|
||||
(8, 4, "8+4"), // 50%冗余,更多分片
|
||||
];
|
||||
|
||||
for dataset in &datasets {
|
||||
for (data_shards, parity_shards, config_name) in &configs {
|
||||
let test_name = format!("{}_{}_{}", dataset.size_name, config_name, get_implementation_name());
|
||||
|
||||
let mut group = c.benchmark_group("encode_comparison");
|
||||
group.throughput(Throughput::Bytes(dataset.data.len() as u64));
|
||||
group.sample_size(20);
|
||||
group.measurement_time(Duration::from_secs(10));
|
||||
|
||||
// 检查是否能够创建erasure实例(某些配置在纯SIMD模式下可能失败)
|
||||
match Erasure::new(*data_shards, *parity_shards, dataset.data.len()).encode_data(&dataset.data) {
|
||||
Ok(_) => {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("implementation", &test_name),
|
||||
&(&dataset.data, *data_shards, *parity_shards),
|
||||
|b, (data, data_shards, parity_shards)| {
|
||||
let erasure = Erasure::new(*data_shards, *parity_shards, data.len());
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ 跳过测试 {} - 配置不支持: {}", test_name, e);
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 解码性能比较基准测试
|
||||
fn bench_decode_comparison(c: &mut Criterion) {
|
||||
let datasets = generate_test_datasets();
|
||||
let configs = vec![(4, 2, "4+2"), (6, 3, "6+3"), (8, 4, "8+4")];
|
||||
|
||||
for dataset in &datasets {
|
||||
for (data_shards, parity_shards, config_name) in &configs {
|
||||
let test_name = format!("{}_{}_{}", dataset.size_name, config_name, get_implementation_name());
|
||||
let erasure = Erasure::new(*data_shards, *parity_shards, dataset.data.len());
|
||||
|
||||
// 预先编码数据 - 检查是否支持此配置
|
||||
match erasure.encode_data(&dataset.data) {
|
||||
Ok(encoded_shards) => {
|
||||
let mut group = c.benchmark_group("decode_comparison");
|
||||
group.throughput(Throughput::Bytes(dataset.data.len() as u64));
|
||||
group.sample_size(20);
|
||||
group.measurement_time(Duration::from_secs(10));
|
||||
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("implementation", &test_name),
|
||||
&(&encoded_shards, *data_shards, *parity_shards),
|
||||
|b, (shards, data_shards, parity_shards)| {
|
||||
let erasure = Erasure::new(*data_shards, *parity_shards, dataset.data.len());
|
||||
b.iter(|| {
|
||||
// 模拟最大可恢复的数据丢失
|
||||
let mut shards_opt: Vec<Option<Vec<u8>>> =
|
||||
shards.iter().map(|shard| Some(shard.to_vec())).collect();
|
||||
|
||||
// 丢失等于奇偶校验分片数量的分片
|
||||
for item in shards_opt.iter_mut().take(*parity_shards) {
|
||||
*item = None;
|
||||
}
|
||||
|
||||
erasure.decode_data(black_box(&mut shards_opt)).unwrap();
|
||||
black_box(&shards_opt);
|
||||
});
|
||||
},
|
||||
);
|
||||
group.finish();
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ 跳过解码测试 {} - 配置不支持: {}", test_name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 分片大小敏感性测试
|
||||
fn bench_shard_size_sensitivity(c: &mut Criterion) {
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
|
||||
// 测试不同的分片大小,特别关注SIMD的临界点
|
||||
let shard_sizes = vec![32, 64, 128, 256, 512, 1024, 2048, 4096, 8192];
|
||||
|
||||
let mut group = c.benchmark_group("shard_size_sensitivity");
|
||||
group.sample_size(15);
|
||||
group.measurement_time(Duration::from_secs(8));
|
||||
|
||||
for shard_size in shard_sizes {
|
||||
let total_size = shard_size * data_shards;
|
||||
let data = (0..total_size).map(|i| (i % 256) as u8).collect::<Vec<u8>>();
|
||||
let test_name = format!("{}B_shard_{}", shard_size, get_implementation_name());
|
||||
|
||||
group.throughput(Throughput::Bytes(total_size as u64));
|
||||
|
||||
// 检查此分片大小是否支持
|
||||
let erasure = Erasure::new(data_shards, parity_shards, data.len());
|
||||
match erasure.encode_data(&data) {
|
||||
Ok(_) => {
|
||||
group.bench_with_input(BenchmarkId::new("shard_size", &test_name), &data, |b, data| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, data.len());
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ 跳过分片大小测试 {} - 不支持: {}", test_name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 高负载并发测试
|
||||
fn bench_concurrent_load(c: &mut Criterion) {
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
let data_size = 1024 * 1024; // 1MB
|
||||
let data = Arc::new((0..data_size).map(|i| (i % 256) as u8).collect::<Vec<u8>>());
|
||||
let erasure = Arc::new(Erasure::new(4, 2, data_size));
|
||||
|
||||
let mut group = c.benchmark_group("concurrent_load");
|
||||
group.throughput(Throughput::Bytes(data_size as u64));
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(15));
|
||||
|
||||
let test_name = format!("1MB_concurrent_{}", get_implementation_name());
|
||||
|
||||
group.bench_function(&test_name, |b| {
|
||||
b.iter(|| {
|
||||
let handles: Vec<_> = (0..4)
|
||||
.map(|_| {
|
||||
let data_clone = data.clone();
|
||||
let erasure_clone = erasure.clone();
|
||||
thread::spawn(move || {
|
||||
let shards = erasure_clone.encode_data(&data_clone).unwrap();
|
||||
black_box(shards);
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 错误恢复能力测试
|
||||
fn bench_error_recovery_performance(c: &mut Criterion) {
|
||||
let data_size = 256 * 1024; // 256KB
|
||||
let data = (0..data_size).map(|i| (i % 256) as u8).collect::<Vec<u8>>();
|
||||
|
||||
let configs = vec![
|
||||
(4, 2, 1), // 丢失1个分片
|
||||
(4, 2, 2), // 丢失2个分片(最大可恢复)
|
||||
(6, 3, 2), // 丢失2个分片
|
||||
(6, 3, 3), // 丢失3个分片(最大可恢复)
|
||||
(8, 4, 3), // 丢失3个分片
|
||||
(8, 4, 4), // 丢失4个分片(最大可恢复)
|
||||
];
|
||||
|
||||
let mut group = c.benchmark_group("error_recovery");
|
||||
group.throughput(Throughput::Bytes(data_size as u64));
|
||||
group.sample_size(15);
|
||||
group.measurement_time(Duration::from_secs(8));
|
||||
|
||||
for (data_shards, parity_shards, lost_shards) in configs {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, data_size);
|
||||
let test_name = format!("{}+{}_lost{}_{}", data_shards, parity_shards, lost_shards, get_implementation_name());
|
||||
|
||||
// 检查此配置是否支持
|
||||
match erasure.encode_data(&data) {
|
||||
Ok(encoded_shards) => {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("recovery", &test_name),
|
||||
&(&encoded_shards, data_shards, parity_shards, lost_shards),
|
||||
|b, (shards, data_shards, parity_shards, lost_shards)| {
|
||||
let erasure = Erasure::new(*data_shards, *parity_shards, data_size);
|
||||
b.iter(|| {
|
||||
let mut shards_opt: Vec<Option<Vec<u8>>> = shards.iter().map(|shard| Some(shard.to_vec())).collect();
|
||||
|
||||
// 丢失指定数量的分片
|
||||
for item in shards_opt.iter_mut().take(*lost_shards) {
|
||||
*item = None;
|
||||
}
|
||||
|
||||
erasure.decode_data(black_box(&mut shards_opt)).unwrap();
|
||||
black_box(&shards_opt);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("⚠️ 跳过错误恢复测试 {} - 配置不支持: {}", test_name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 内存效率测试
|
||||
fn bench_memory_efficiency(c: &mut Criterion) {
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
let data_size = 1024 * 1024; // 1MB
|
||||
|
||||
let mut group = c.benchmark_group("memory_efficiency");
|
||||
group.throughput(Throughput::Bytes(data_size as u64));
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(8));
|
||||
|
||||
let test_name = format!("memory_pattern_{}", get_implementation_name());
|
||||
|
||||
// 测试连续多次编码对内存的影响
|
||||
group.bench_function(format!("{}_continuous", test_name), |b| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, data_size);
|
||||
b.iter(|| {
|
||||
for i in 0..10 {
|
||||
let data = vec![(i % 256) as u8; data_size];
|
||||
let shards = erasure.encode_data(black_box(&data)).unwrap();
|
||||
black_box(shards);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 测试大量小编码任务
|
||||
group.bench_function(format!("{}_small_chunks", test_name), |b| {
|
||||
let chunk_size = 1024; // 1KB chunks
|
||||
let erasure = Erasure::new(data_shards, parity_shards, chunk_size);
|
||||
b.iter(|| {
|
||||
for i in 0..1024 {
|
||||
let data = vec![(i % 256) as u8; chunk_size];
|
||||
let shards = erasure.encode_data(black_box(&data)).unwrap();
|
||||
black_box(shards);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 获取当前实现的名称
|
||||
fn get_implementation_name() -> &'static str {
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
return "hybrid";
|
||||
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
return "erasure";
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_encode_comparison,
|
||||
bench_decode_comparison,
|
||||
bench_shard_size_sensitivity,
|
||||
bench_concurrent_load,
|
||||
bench_error_recovery_performance,
|
||||
bench_memory_efficiency
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,390 @@
|
||||
//! Reed-Solomon erasure coding performance benchmarks.
|
||||
//!
|
||||
//! This benchmark compares the performance of different Reed-Solomon implementations:
|
||||
//! - Default (Pure erasure): Stable reed-solomon-erasure implementation
|
||||
//! - `reed-solomon-simd` feature: Hybrid mode with SIMD optimization and erasure fallback
|
||||
//!
|
||||
//! ## Running Benchmarks
|
||||
//!
|
||||
//! ```bash
|
||||
//! # 运行所有基准测试
|
||||
//! cargo bench
|
||||
//!
|
||||
//! # 运行特定的基准测试
|
||||
//! cargo bench --bench erasure_benchmark
|
||||
//!
|
||||
//! # 生成HTML报告
|
||||
//! cargo bench --bench erasure_benchmark -- --output-format html
|
||||
//!
|
||||
//! # 只测试编码性能
|
||||
//! cargo bench encode
|
||||
//!
|
||||
//! # 只测试解码性能
|
||||
//! cargo bench decode
|
||||
//! ```
|
||||
//!
|
||||
//! ## Test Configurations
|
||||
//!
|
||||
//! The benchmarks test various scenarios:
|
||||
//! - Different data sizes: 1KB, 64KB, 1MB, 16MB
|
||||
//! - Different erasure coding configurations: (4,2), (6,3), (8,4)
|
||||
//! - Both encoding and decoding operations
|
||||
//! - Small vs large shard scenarios for SIMD optimization
|
||||
|
||||
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
|
||||
use ecstore::erasure_coding::Erasure;
|
||||
use std::time::Duration;
|
||||
|
||||
/// 基准测试配置结构体
|
||||
#[derive(Clone, Debug)]
|
||||
struct BenchConfig {
|
||||
/// 数据分片数量
|
||||
data_shards: usize,
|
||||
/// 奇偶校验分片数量
|
||||
parity_shards: usize,
|
||||
/// 测试数据大小(字节)
|
||||
data_size: usize,
|
||||
/// 块大小(字节)
|
||||
block_size: usize,
|
||||
/// 配置名称
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl BenchConfig {
|
||||
fn new(data_shards: usize, parity_shards: usize, data_size: usize, block_size: usize) -> Self {
|
||||
Self {
|
||||
data_shards,
|
||||
parity_shards,
|
||||
data_size,
|
||||
block_size,
|
||||
name: format!("{}+{}_{}KB_{}KB-block", data_shards, parity_shards, data_size / 1024, block_size / 1024),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成测试数据
|
||||
fn generate_test_data(size: usize) -> Vec<u8> {
|
||||
(0..size).map(|i| (i % 256) as u8).collect()
|
||||
}
|
||||
|
||||
/// 基准测试: 编码性能对比
|
||||
fn bench_encode_performance(c: &mut Criterion) {
|
||||
let configs = vec![
|
||||
// 小数据量测试 - 1KB
|
||||
BenchConfig::new(4, 2, 1024, 1024),
|
||||
BenchConfig::new(6, 3, 1024, 1024),
|
||||
BenchConfig::new(8, 4, 1024, 1024),
|
||||
// 中等数据量测试 - 64KB
|
||||
BenchConfig::new(4, 2, 64 * 1024, 64 * 1024),
|
||||
BenchConfig::new(6, 3, 64 * 1024, 64 * 1024),
|
||||
BenchConfig::new(8, 4, 64 * 1024, 64 * 1024),
|
||||
// 大数据量测试 - 1MB
|
||||
BenchConfig::new(4, 2, 1024 * 1024, 1024 * 1024),
|
||||
BenchConfig::new(6, 3, 1024 * 1024, 1024 * 1024),
|
||||
BenchConfig::new(8, 4, 1024 * 1024, 1024 * 1024),
|
||||
// 超大数据量测试 - 16MB
|
||||
BenchConfig::new(4, 2, 16 * 1024 * 1024, 16 * 1024 * 1024),
|
||||
BenchConfig::new(6, 3, 16 * 1024 * 1024, 16 * 1024 * 1024),
|
||||
];
|
||||
|
||||
for config in configs {
|
||||
let data = generate_test_data(config.data_size);
|
||||
|
||||
// 测试当前默认实现(通常是SIMD)
|
||||
let mut group = c.benchmark_group("encode_current");
|
||||
group.throughput(Throughput::Bytes(config.data_size as u64));
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("current_impl", &config.name), &(&data, &config), |b, (data, config)| {
|
||||
let erasure = Erasure::new(config.data_shards, config.parity_shards, config.block_size);
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
group.finish();
|
||||
|
||||
// 如果SIMD feature启用,测试专用的erasure实现对比
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
{
|
||||
use ecstore::erasure_coding::ReedSolomonEncoder;
|
||||
|
||||
let mut erasure_group = c.benchmark_group("encode_erasure_only");
|
||||
erasure_group.throughput(Throughput::Bytes(config.data_size as u64));
|
||||
erasure_group.sample_size(10);
|
||||
erasure_group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
erasure_group.bench_with_input(
|
||||
BenchmarkId::new("erasure_impl", &config.name),
|
||||
&(&data, &config),
|
||||
|b, (data, config)| {
|
||||
let encoder = ReedSolomonEncoder::new(config.data_shards, config.parity_shards).unwrap();
|
||||
b.iter(|| {
|
||||
// 创建编码所需的数据结构
|
||||
let per_shard_size = data.len().div_ceil(config.data_shards);
|
||||
let total_size = per_shard_size * (config.data_shards + config.parity_shards);
|
||||
let mut buffer = vec![0u8; total_size];
|
||||
buffer[..data.len()].copy_from_slice(data);
|
||||
|
||||
let slices: smallvec::SmallVec<[&mut [u8]; 16]> = buffer.chunks_exact_mut(per_shard_size).collect();
|
||||
|
||||
encoder.encode(black_box(slices)).unwrap();
|
||||
black_box(&buffer);
|
||||
});
|
||||
},
|
||||
);
|
||||
erasure_group.finish();
|
||||
}
|
||||
|
||||
// 如果使用SIMD feature,测试直接SIMD实现对比
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
{
|
||||
// 只对大shard测试SIMD(小于512字节的shard SIMD性能不佳)
|
||||
let shard_size = config.data_size.div_ceil(config.data_shards);
|
||||
if shard_size >= 512 {
|
||||
let mut simd_group = c.benchmark_group("encode_simd_direct");
|
||||
simd_group.throughput(Throughput::Bytes(config.data_size as u64));
|
||||
simd_group.sample_size(10);
|
||||
simd_group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
simd_group.bench_with_input(
|
||||
BenchmarkId::new("simd_impl", &config.name),
|
||||
&(&data, &config),
|
||||
|b, (data, config)| {
|
||||
b.iter(|| {
|
||||
// 直接使用SIMD实现
|
||||
let per_shard_size = data.len().div_ceil(config.data_shards);
|
||||
match reed_solomon_simd::ReedSolomonEncoder::new(
|
||||
config.data_shards,
|
||||
config.parity_shards,
|
||||
per_shard_size,
|
||||
) {
|
||||
Ok(mut encoder) => {
|
||||
// 添加数据分片
|
||||
for chunk in data.chunks(per_shard_size) {
|
||||
encoder.add_original_shard(black_box(chunk)).unwrap();
|
||||
}
|
||||
|
||||
let result = encoder.encode().unwrap();
|
||||
black_box(result);
|
||||
}
|
||||
Err(_) => {
|
||||
// SIMD不支持此配置,跳过
|
||||
black_box(());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
simd_group.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 基准测试: 解码性能对比
|
||||
fn bench_decode_performance(c: &mut Criterion) {
|
||||
let configs = vec![
|
||||
// 中等数据量测试 - 64KB
|
||||
BenchConfig::new(4, 2, 64 * 1024, 64 * 1024),
|
||||
BenchConfig::new(6, 3, 64 * 1024, 64 * 1024),
|
||||
// 大数据量测试 - 1MB
|
||||
BenchConfig::new(4, 2, 1024 * 1024, 1024 * 1024),
|
||||
BenchConfig::new(6, 3, 1024 * 1024, 1024 * 1024),
|
||||
// 超大数据量测试 - 16MB
|
||||
BenchConfig::new(4, 2, 16 * 1024 * 1024, 16 * 1024 * 1024),
|
||||
];
|
||||
|
||||
for config in configs {
|
||||
let data = generate_test_data(config.data_size);
|
||||
let erasure = Erasure::new(config.data_shards, config.parity_shards, config.block_size);
|
||||
|
||||
// 预先编码数据
|
||||
let encoded_shards = erasure.encode_data(&data).unwrap();
|
||||
|
||||
// 测试当前默认实现的解码性能
|
||||
let mut group = c.benchmark_group("decode_current");
|
||||
group.throughput(Throughput::Bytes(config.data_size as u64));
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("current_impl", &config.name),
|
||||
&(&encoded_shards, &config),
|
||||
|b, (shards, config)| {
|
||||
let erasure = Erasure::new(config.data_shards, config.parity_shards, config.block_size);
|
||||
b.iter(|| {
|
||||
// 模拟数据丢失 - 丢失一个数据分片和一个奇偶分片
|
||||
let mut shards_opt: Vec<Option<Vec<u8>>> = shards.iter().map(|shard| Some(shard.to_vec())).collect();
|
||||
|
||||
// 丢失最后一个数据分片和第一个奇偶分片
|
||||
shards_opt[config.data_shards - 1] = None;
|
||||
shards_opt[config.data_shards] = None;
|
||||
|
||||
erasure.decode_data(black_box(&mut shards_opt)).unwrap();
|
||||
black_box(&shards_opt);
|
||||
});
|
||||
},
|
||||
);
|
||||
group.finish();
|
||||
|
||||
// 如果使用混合模式(默认),测试SIMD解码性能
|
||||
#[cfg(not(feature = "reed-solomon-erasure"))]
|
||||
{
|
||||
let shard_size = config.data_size.div_ceil(config.data_shards);
|
||||
if shard_size >= 512 {
|
||||
let mut simd_group = c.benchmark_group("decode_simd_direct");
|
||||
simd_group.throughput(Throughput::Bytes(config.data_size as u64));
|
||||
simd_group.sample_size(10);
|
||||
simd_group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
simd_group.bench_with_input(
|
||||
BenchmarkId::new("simd_impl", &config.name),
|
||||
&(&encoded_shards, &config),
|
||||
|b, (shards, config)| {
|
||||
b.iter(|| {
|
||||
let per_shard_size = config.data_size.div_ceil(config.data_shards);
|
||||
match reed_solomon_simd::ReedSolomonDecoder::new(
|
||||
config.data_shards,
|
||||
config.parity_shards,
|
||||
per_shard_size,
|
||||
) {
|
||||
Ok(mut decoder) => {
|
||||
// 添加可用的分片(除了丢失的)
|
||||
for (i, shard) in shards.iter().enumerate() {
|
||||
if i != config.data_shards - 1 && i != config.data_shards {
|
||||
if i < config.data_shards {
|
||||
decoder.add_original_shard(i, black_box(shard)).unwrap();
|
||||
} else {
|
||||
let recovery_idx = i - config.data_shards;
|
||||
decoder.add_recovery_shard(recovery_idx, black_box(shard)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = decoder.decode().unwrap();
|
||||
black_box(result);
|
||||
}
|
||||
Err(_) => {
|
||||
// SIMD不支持此配置,跳过
|
||||
black_box(());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
simd_group.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 基准测试: 不同分片大小对性能的影响
|
||||
fn bench_shard_size_impact(c: &mut Criterion) {
|
||||
let shard_sizes = vec![64, 128, 256, 512, 1024, 2048, 4096, 8192];
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
|
||||
let mut group = c.benchmark_group("shard_size_impact");
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(3));
|
||||
|
||||
for shard_size in shard_sizes {
|
||||
let total_data_size = shard_size * data_shards;
|
||||
let data = generate_test_data(total_data_size);
|
||||
|
||||
group.throughput(Throughput::Bytes(total_data_size as u64));
|
||||
|
||||
// 测试当前实现
|
||||
group.bench_with_input(BenchmarkId::new("current", format!("shard_{}B", shard_size)), &data, |b, data| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, total_data_size);
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 基准测试: 编码配置对性能的影响
|
||||
fn bench_coding_configurations(c: &mut Criterion) {
|
||||
let configs = vec![
|
||||
(2, 1), // 最小冗余
|
||||
(3, 2), // 中等冗余
|
||||
(4, 2), // 常用配置
|
||||
(6, 3), // 50%冗余
|
||||
(8, 4), // 50%冗余,更多分片
|
||||
(10, 5), // 50%冗余,大量分片
|
||||
(12, 6), // 50%冗余,更大量分片
|
||||
];
|
||||
|
||||
let data_size = 1024 * 1024; // 1MB测试数据
|
||||
let data = generate_test_data(data_size);
|
||||
|
||||
let mut group = c.benchmark_group("coding_configurations");
|
||||
group.throughput(Throughput::Bytes(data_size as u64));
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
for (data_shards, parity_shards) in configs {
|
||||
let config_name = format!("{}+{}", data_shards, parity_shards);
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("encode", &config_name), &data, |b, data| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, data_size);
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// 基准测试: 内存使用模式
|
||||
fn bench_memory_patterns(c: &mut Criterion) {
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
let block_size = 1024 * 1024; // 1MB块
|
||||
|
||||
let mut group = c.benchmark_group("memory_patterns");
|
||||
group.sample_size(10);
|
||||
group.measurement_time(Duration::from_secs(5));
|
||||
|
||||
// 测试重复使用同一个Erasure实例
|
||||
group.bench_function("reuse_erasure_instance", |b| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
let data = generate_test_data(block_size);
|
||||
|
||||
b.iter(|| {
|
||||
let shards = erasure.encode_data(black_box(&data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
|
||||
// 测试每次创建新的Erasure实例
|
||||
group.bench_function("new_erasure_instance", |b| {
|
||||
let data = generate_test_data(block_size);
|
||||
|
||||
b.iter(|| {
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
let shards = erasure.encode_data(black_box(&data)).unwrap();
|
||||
black_box(shards);
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// 基准测试组配置
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_encode_performance,
|
||||
bench_decode_performance,
|
||||
bench_shard_size_impact,
|
||||
bench_coding_configurations,
|
||||
bench_memory_patterns
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
||||
Executable
+263
@@ -0,0 +1,263 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reed-Solomon 实现性能比较脚本
|
||||
#
|
||||
# 这个脚本将运行不同的基准测试来比较混合模式和纯Erasure模式的性能
|
||||
#
|
||||
# 使用方法:
|
||||
# ./run_benchmarks.sh [quick|full|comparison]
|
||||
#
|
||||
# quick - 快速测试主要场景
|
||||
# full - 完整基准测试套件
|
||||
# comparison - 专门对比两种实现模式
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 输出带颜色的信息
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查是否安装了必要工具
|
||||
check_requirements() {
|
||||
print_info "检查系统要求..."
|
||||
|
||||
if ! command -v cargo &> /dev/null; then
|
||||
print_error "cargo 未安装,请先安装 Rust 工具链"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查是否安装了 criterion
|
||||
if ! grep -q "criterion" Cargo.toml; then
|
||||
print_error "Cargo.toml 中未找到 criterion 依赖"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "系统要求检查通过"
|
||||
}
|
||||
|
||||
# 清理之前的测试结果
|
||||
cleanup() {
|
||||
print_info "清理之前的测试结果..."
|
||||
rm -rf target/criterion
|
||||
print_success "清理完成"
|
||||
}
|
||||
|
||||
# 运行纯 Erasure 模式基准测试
|
||||
run_erasure_benchmark() {
|
||||
print_info "🏛️ 开始运行纯 Erasure 模式基准测试..."
|
||||
echo "================================================"
|
||||
|
||||
cargo bench --bench comparison_benchmark \
|
||||
--features reed-solomon-erasure \
|
||||
-- --save-baseline erasure_baseline
|
||||
|
||||
print_success "纯 Erasure 模式基准测试完成"
|
||||
}
|
||||
|
||||
# 运行混合模式基准测试(默认)
|
||||
run_hybrid_benchmark() {
|
||||
print_info "🎯 开始运行混合模式基准测试(默认)..."
|
||||
echo "================================================"
|
||||
|
||||
cargo bench --bench comparison_benchmark \
|
||||
-- --save-baseline hybrid_baseline
|
||||
|
||||
print_success "混合模式基准测试完成"
|
||||
}
|
||||
|
||||
# 运行完整的基准测试套件
|
||||
run_full_benchmark() {
|
||||
print_info "🚀 开始运行完整基准测试套件..."
|
||||
echo "================================================"
|
||||
|
||||
# 运行详细的基准测试(使用默认混合模式)
|
||||
cargo bench --bench erasure_benchmark
|
||||
|
||||
print_success "完整基准测试套件完成"
|
||||
}
|
||||
|
||||
# 运行性能对比测试
|
||||
run_comparison_benchmark() {
|
||||
print_info "📊 开始运行性能对比测试..."
|
||||
echo "================================================"
|
||||
|
||||
print_info "步骤 1: 测试纯 Erasure 模式..."
|
||||
cargo bench --bench comparison_benchmark \
|
||||
--features reed-solomon-erasure \
|
||||
-- --save-baseline erasure_baseline
|
||||
|
||||
print_info "步骤 2: 测试混合模式并与 Erasure 模式对比..."
|
||||
cargo bench --bench comparison_benchmark \
|
||||
-- --baseline erasure_baseline
|
||||
|
||||
print_success "性能对比测试完成"
|
||||
}
|
||||
|
||||
# 生成比较报告
|
||||
generate_comparison_report() {
|
||||
print_info "📊 生成性能比较报告..."
|
||||
|
||||
if [ -d "target/criterion" ]; then
|
||||
print_info "基准测试结果已保存到 target/criterion/ 目录"
|
||||
print_info "你可以打开 target/criterion/report/index.html 查看详细报告"
|
||||
|
||||
# 如果有 python 环境,可以启动简单的 HTTP 服务器查看报告
|
||||
if command -v python3 &> /dev/null; then
|
||||
print_info "你可以运行以下命令启动本地服务器查看报告:"
|
||||
echo " cd target/criterion && python3 -m http.server 8080"
|
||||
echo " 然后在浏览器中访问 http://localhost:8080/report/index.html"
|
||||
fi
|
||||
else
|
||||
print_warning "未找到基准测试结果目录"
|
||||
fi
|
||||
}
|
||||
|
||||
# 快速测试模式
|
||||
run_quick_test() {
|
||||
print_info "🏃 运行快速性能测试..."
|
||||
|
||||
print_info "测试纯 Erasure 模式..."
|
||||
cargo bench --bench comparison_benchmark \
|
||||
--features reed-solomon-erasure \
|
||||
-- encode_comparison --quick
|
||||
|
||||
print_info "测试混合模式(默认)..."
|
||||
cargo bench --bench comparison_benchmark \
|
||||
-- encode_comparison --quick
|
||||
|
||||
print_success "快速测试完成"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "Reed-Solomon 性能基准测试脚本"
|
||||
echo ""
|
||||
echo "实现模式:"
|
||||
echo " 🎯 混合模式(默认) - SIMD + Erasure 智能回退,推荐使用"
|
||||
echo " 🏛️ 纯 Erasure 模式 - 稳定兼容的 reed-solomon-erasure 实现"
|
||||
echo ""
|
||||
echo "使用方法:"
|
||||
echo " $0 [command]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " quick 运行快速性能测试"
|
||||
echo " full 运行完整基准测试套件(混合模式)"
|
||||
echo " comparison 运行详细的实现模式对比测试"
|
||||
echo " erasure 只测试纯 Erasure 模式"
|
||||
echo " hybrid 只测试混合模式(默认行为)"
|
||||
echo " clean 清理测试结果"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 quick # 快速测试两种模式"
|
||||
echo " $0 comparison # 详细对比测试"
|
||||
echo " $0 full # 完整测试套件(混合模式)"
|
||||
echo " $0 hybrid # 只测试混合模式"
|
||||
echo " $0 erasure # 只测试纯 Erasure 模式"
|
||||
echo ""
|
||||
echo "模式说明:"
|
||||
echo " 混合模式: 大分片(≥512B)使用SIMD优化,小分片自动回退到Erasure"
|
||||
echo " Erasure模式: 所有情况都使用reed-solomon-erasure实现"
|
||||
}
|
||||
|
||||
# 显示测试配置信息
|
||||
show_test_info() {
|
||||
print_info "📋 测试配置信息:"
|
||||
echo " - 当前目录: $(pwd)"
|
||||
echo " - Rust 版本: $(rustc --version)"
|
||||
echo " - Cargo 版本: $(cargo --version)"
|
||||
echo " - CPU 架构: $(uname -m)"
|
||||
echo " - 操作系统: $(uname -s)"
|
||||
|
||||
# 检查 CPU 特性
|
||||
if [ -f "/proc/cpuinfo" ]; then
|
||||
echo " - CPU 型号: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
|
||||
if grep -q "avx2" /proc/cpuinfo; then
|
||||
echo " - SIMD 支持: AVX2 ✅ (混合模式将利用SIMD优化)"
|
||||
elif grep -q "sse4" /proc/cpuinfo; then
|
||||
echo " - SIMD 支持: SSE4 ✅ (混合模式将利用SIMD优化)"
|
||||
else
|
||||
echo " - SIMD 支持: 未检测到高级 SIMD 特性 (混合模式将主要使用Erasure)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " - 默认模式: 混合模式 (SIMD + Erasure 智能回退)"
|
||||
echo " - 回退阈值: 512字节分片大小"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
print_info "🧪 Reed-Solomon 实现性能基准测试"
|
||||
echo "================================================"
|
||||
|
||||
check_requirements
|
||||
show_test_info
|
||||
|
||||
case "${1:-help}" in
|
||||
"quick")
|
||||
run_quick_test
|
||||
generate_comparison_report
|
||||
;;
|
||||
"full")
|
||||
cleanup
|
||||
run_full_benchmark
|
||||
generate_comparison_report
|
||||
;;
|
||||
"comparison")
|
||||
cleanup
|
||||
run_comparison_benchmark
|
||||
generate_comparison_report
|
||||
;;
|
||||
"erasure")
|
||||
cleanup
|
||||
run_erasure_benchmark
|
||||
generate_comparison_report
|
||||
;;
|
||||
"hybrid")
|
||||
cleanup
|
||||
run_hybrid_benchmark
|
||||
generate_comparison_report
|
||||
;;
|
||||
"clean")
|
||||
cleanup
|
||||
;;
|
||||
"help"|"--help"|"-h")
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_error "未知命令: $1"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
print_success "✨ 基准测试执行完成!"
|
||||
print_info "💡 提示: 推荐使用混合模式(默认),它能自动在SIMD和Erasure之间智能选择"
|
||||
}
|
||||
|
||||
# 如果直接运行此脚本,调用主函数
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
@@ -5,23 +5,23 @@
|
||||
//!
|
||||
//! ## Reed-Solomon Implementations
|
||||
//!
|
||||
//! ### `reed-solomon-erasure` (Default)
|
||||
//! - **Stability**: Mature and well-tested implementation
|
||||
//! - **Performance**: Good performance with SIMD acceleration when available
|
||||
//! ### Pure Erasure Mode (Default)
|
||||
//! - **Stability**: Pure erasure implementation, mature and well-tested
|
||||
//! - **Performance**: Good performance with consistent behavior
|
||||
//! - **Compatibility**: Works with any shard size
|
||||
//! - **Memory**: Efficient memory usage
|
||||
//! - **Use case**: Recommended for production use
|
||||
//! - **Use case**: Default behavior, recommended for most production use cases
|
||||
//!
|
||||
//! ### `reed-solomon-simd` (Optional)
|
||||
//! - **Performance**: Optimized SIMD implementation for maximum speed
|
||||
//! - **Limitations**: Has restrictions on shard sizes (must be >= 64 bytes typically)
|
||||
//! - **Memory**: May use more memory for small shards
|
||||
//! - **Use case**: Best for large data blocks where performance is critical
|
||||
//! ### Hybrid Mode (`reed-solomon-simd` feature)
|
||||
//! - **Performance**: Uses SIMD optimization when possible, falls back to erasure implementation for small shards
|
||||
//! - **Compatibility**: Works with any shard size through intelligent fallback
|
||||
//! - **Reliability**: Best of both worlds - SIMD speed for large data, erasure stability for small data
|
||||
//! - **Use case**: Use when maximum performance is needed for large data processing
|
||||
//!
|
||||
//! ## Feature Flags
|
||||
//!
|
||||
//! - `reed-solomon-erasure` (default): Use the reed-solomon-erasure implementation
|
||||
//! - `reed-solomon-simd`: Use the reed-solomon-simd implementation
|
||||
//! - Default: Use pure reed-solomon-erasure implementation (stable and reliable)
|
||||
//! - `reed-solomon-simd`: Use hybrid mode (SIMD + erasure fallback for optimal performance)
|
||||
//! - `reed-solomon-erasure`: Explicitly enable pure erasure mode (same as default)
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
@@ -35,7 +35,6 @@
|
||||
//! ```
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
#[cfg(feature = "reed-solomon-erasure")]
|
||||
use reed_solomon_erasure::galois_8::ReedSolomon as ReedSolomonErasure;
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
use reed_solomon_simd;
|
||||
@@ -48,18 +47,18 @@ use uuid::Uuid;
|
||||
/// Reed-Solomon encoder variants supporting different implementations.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ReedSolomonEncoder {
|
||||
/// Hybrid mode: SIMD with erasure fallback (when reed-solomon-simd feature is enabled)
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
Simd {
|
||||
Hybrid {
|
||||
data_shards: usize,
|
||||
parity_shards: usize,
|
||||
// 使用RwLock确保线程安全,实现Send + Sync
|
||||
encoder_cache: std::sync::RwLock<Option<reed_solomon_simd::ReedSolomonEncoder>>,
|
||||
decoder_cache: std::sync::RwLock<Option<reed_solomon_simd::ReedSolomonDecoder>>,
|
||||
// 添加erasure后备选项,当SIMD不适用时使用 - 只有两个feature都启用时才存在
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder: Option<Box<ReedSolomonErasure>>,
|
||||
// erasure fallback for small shards or SIMD failures
|
||||
fallback_encoder: Box<ReedSolomonErasure>,
|
||||
},
|
||||
#[cfg(feature = "reed-solomon-erasure")]
|
||||
/// Pure erasure mode: default and when reed-solomon-erasure feature is specified
|
||||
Erasure(Box<ReedSolomonErasure>),
|
||||
}
|
||||
|
||||
@@ -67,22 +66,19 @@ impl Clone for ReedSolomonEncoder {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
ReedSolomonEncoder::Simd {
|
||||
ReedSolomonEncoder::Hybrid {
|
||||
data_shards,
|
||||
parity_shards,
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder,
|
||||
..
|
||||
} => ReedSolomonEncoder::Simd {
|
||||
} => ReedSolomonEncoder::Hybrid {
|
||||
data_shards: *data_shards,
|
||||
parity_shards: *parity_shards,
|
||||
// 为新实例创建空的缓存,不共享缓存
|
||||
encoder_cache: std::sync::RwLock::new(None),
|
||||
decoder_cache: std::sync::RwLock::new(None),
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder: fallback_encoder.clone(),
|
||||
},
|
||||
#[cfg(feature = "reed-solomon-erasure")]
|
||||
ReedSolomonEncoder::Erasure(encoder) => ReedSolomonEncoder::Erasure(encoder.clone()),
|
||||
}
|
||||
}
|
||||
@@ -93,44 +89,38 @@ impl ReedSolomonEncoder {
|
||||
pub fn new(data_shards: usize, parity_shards: usize) -> io::Result<Self> {
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
{
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
let fallback_encoder =
|
||||
Some(Box::new(ReedSolomonErasure::new(data_shards, parity_shards).map_err(|e| {
|
||||
io::Error::other(format!("Failed to create fallback erasure encoder: {:?}", e))
|
||||
})?));
|
||||
// Hybrid mode: SIMD + erasure fallback when reed-solomon-simd feature is enabled
|
||||
let fallback_encoder = Box::new(
|
||||
ReedSolomonErasure::new(data_shards, parity_shards)
|
||||
.map_err(|e| io::Error::other(format!("Failed to create fallback erasure encoder: {:?}", e)))?,
|
||||
);
|
||||
|
||||
Ok(ReedSolomonEncoder::Simd {
|
||||
Ok(ReedSolomonEncoder::Hybrid {
|
||||
data_shards,
|
||||
parity_shards,
|
||||
encoder_cache: std::sync::RwLock::new(None),
|
||||
decoder_cache: std::sync::RwLock::new(None),
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "reed-solomon-erasure", not(feature = "reed-solomon-simd")))]
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
{
|
||||
// Pure erasure mode when reed-solomon-simd feature is not enabled (default or reed-solomon-erasure)
|
||||
let encoder = ReedSolomonErasure::new(data_shards, parity_shards)
|
||||
.map_err(|e| io::Error::other(format!("Failed to create erasure encoder: {:?}", e)))?;
|
||||
Ok(ReedSolomonEncoder::Erasure(Box::new(encoder)))
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "reed-solomon-simd", feature = "reed-solomon-erasure")))]
|
||||
{
|
||||
Err(io::Error::other("No Reed-Solomon implementation available"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode data shards with parity.
|
||||
pub fn encode(&self, shards: SmallVec<[&mut [u8]; 16]>) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
ReedSolomonEncoder::Simd {
|
||||
ReedSolomonEncoder::Hybrid {
|
||||
data_shards,
|
||||
parity_shards,
|
||||
encoder_cache,
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder,
|
||||
..
|
||||
} => {
|
||||
@@ -139,24 +129,17 @@ impl ReedSolomonEncoder {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
let shard_len = shards_vec[0].len();
|
||||
#[cfg(not(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure")))]
|
||||
let _shard_len = shards_vec[0].len();
|
||||
|
||||
// SIMD 性能最佳的最小 shard 大小 (通常 512-1024 字节)
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
const SIMD_MIN_SHARD_SIZE: usize = 512;
|
||||
|
||||
// 如果 shard 太小,使用 fallback encoder
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
// 如果 shard 太小,直接使用 fallback encoder
|
||||
if shard_len < SIMD_MIN_SHARD_SIZE {
|
||||
if let Some(erasure_encoder) = fallback_encoder {
|
||||
let fallback_shards: SmallVec<[&mut [u8]; 16]> = SmallVec::from_vec(shards_vec);
|
||||
return erasure_encoder
|
||||
.encode(fallback_shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure encode error: {:?}", e)));
|
||||
}
|
||||
let fallback_shards: SmallVec<[&mut [u8]; 16]> = SmallVec::from_vec(shards_vec);
|
||||
return fallback_encoder
|
||||
.encode(fallback_shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure encode error: {:?}", e)));
|
||||
}
|
||||
|
||||
// 尝试使用 SIMD,如果失败则回退到 fallback
|
||||
@@ -165,22 +148,14 @@ impl ReedSolomonEncoder {
|
||||
match simd_result {
|
||||
Ok(()) => Ok(()),
|
||||
Err(simd_error) => {
|
||||
warn!("SIMD encoding failed: {}, trying fallback", simd_error);
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
if let Some(erasure_encoder) = fallback_encoder {
|
||||
let fallback_shards: SmallVec<[&mut [u8]; 16]> = SmallVec::from_vec(shards_vec);
|
||||
erasure_encoder
|
||||
.encode(fallback_shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure encode error: {:?}", e)))
|
||||
} else {
|
||||
Err(simd_error)
|
||||
}
|
||||
#[cfg(not(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure")))]
|
||||
Err(simd_error)
|
||||
warn!("SIMD encoding failed: {}, using fallback", simd_error);
|
||||
let fallback_shards: SmallVec<[&mut [u8]; 16]> = SmallVec::from_vec(shards_vec);
|
||||
fallback_encoder
|
||||
.encode(fallback_shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure encode error: {:?}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "reed-solomon-erasure")]
|
||||
ReedSolomonEncoder::Erasure(encoder) => encoder
|
||||
.encode(shards)
|
||||
.map_err(|e| io::Error::other(format!("Erasure encode error: {:?}", e))),
|
||||
@@ -256,38 +231,27 @@ impl ReedSolomonEncoder {
|
||||
pub fn reconstruct(&self, shards: &mut [Option<Vec<u8>>]) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
ReedSolomonEncoder::Simd {
|
||||
ReedSolomonEncoder::Hybrid {
|
||||
data_shards,
|
||||
parity_shards,
|
||||
decoder_cache,
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
fallback_encoder,
|
||||
..
|
||||
} => {
|
||||
// Find a valid shard to determine length
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
let shard_len = shards
|
||||
.iter()
|
||||
.find_map(|s| s.as_ref().map(|v| v.len()))
|
||||
.ok_or_else(|| io::Error::other("No valid shards found for reconstruction"))?;
|
||||
#[cfg(not(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure")))]
|
||||
let _shard_len = shards
|
||||
.iter()
|
||||
.find_map(|s| s.as_ref().map(|v| v.len()))
|
||||
.ok_or_else(|| io::Error::other("No valid shards found for reconstruction"))?;
|
||||
|
||||
// SIMD 性能最佳的最小 shard 大小
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
const SIMD_MIN_SHARD_SIZE: usize = 512;
|
||||
|
||||
// 如果 shard 太小,使用 fallback encoder
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
// 如果 shard 太小,直接使用 fallback encoder
|
||||
if shard_len < SIMD_MIN_SHARD_SIZE {
|
||||
if let Some(erasure_encoder) = fallback_encoder {
|
||||
return erasure_encoder
|
||||
.reconstruct(shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure reconstruct error: {:?}", e)));
|
||||
}
|
||||
return fallback_encoder
|
||||
.reconstruct(shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure reconstruct error: {:?}", e)));
|
||||
}
|
||||
|
||||
// 尝试使用 SIMD,如果失败则回退到 fallback
|
||||
@@ -296,21 +260,13 @@ impl ReedSolomonEncoder {
|
||||
match simd_result {
|
||||
Ok(()) => Ok(()),
|
||||
Err(simd_error) => {
|
||||
warn!("SIMD reconstruction failed: {}, trying fallback", simd_error);
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
if let Some(erasure_encoder) = fallback_encoder {
|
||||
erasure_encoder
|
||||
.reconstruct(shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure reconstruct error: {:?}", e)))
|
||||
} else {
|
||||
Err(simd_error)
|
||||
}
|
||||
#[cfg(not(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure")))]
|
||||
Err(simd_error)
|
||||
warn!("SIMD reconstruction failed: {}, using fallback", simd_error);
|
||||
fallback_encoder
|
||||
.reconstruct(shards)
|
||||
.map_err(|e| io::Error::other(format!("Fallback erasure reconstruct error: {:?}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "reed-solomon-erasure")]
|
||||
ReedSolomonEncoder::Erasure(encoder) => encoder
|
||||
.reconstruct(shards)
|
||||
.map_err(|e| io::Error::other(format!("Erasure reconstruct error: {:?}", e))),
|
||||
@@ -658,18 +614,18 @@ mod tests {
|
||||
let parity_shards = 2;
|
||||
|
||||
// Use different block sizes based on feature
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let block_size = 1024; // SIMD requires larger blocks
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let block_size = 8;
|
||||
let block_size = 8; // Pure erasure mode (default)
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let block_size = 1024; // Hybrid mode - SIMD with fallback
|
||||
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
|
||||
// Use different test data based on feature
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let test_data = b"SIMD test data for encoding and decoding roundtrip verification with sufficient length to ensure shard size requirements are met for proper SIMD optimization.".repeat(20); // ~3KB
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let test_data = b"hello world".to_vec();
|
||||
let test_data = b"hello world".to_vec(); // Small data for erasure (default)
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let test_data = b"Hybrid mode test data for encoding and decoding roundtrip verification with sufficient length to ensure shard size requirements are met for proper SIMD optimization.".repeat(20); // ~3KB for hybrid
|
||||
|
||||
let data = &test_data;
|
||||
let encoded_shards = erasure.encode_data(data).unwrap();
|
||||
@@ -699,9 +655,9 @@ mod tests {
|
||||
|
||||
// Use different block sizes based on feature
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let block_size = 32768; // 32KB for large data with SIMD
|
||||
let block_size = 512 * 3; // Hybrid mode - SIMD with fallback
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let block_size = 8192; // 8KB for erasure
|
||||
let block_size = 8192; // Pure erasure mode (default)
|
||||
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
|
||||
@@ -766,21 +722,15 @@ mod tests {
|
||||
|
||||
// Use different block sizes based on feature
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let block_size = 1024; // SIMD requires larger blocks
|
||||
let block_size = 1024; // Hybrid mode
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let block_size = 8;
|
||||
let block_size = 8; // Pure erasure mode (default)
|
||||
|
||||
let erasure = Arc::new(Erasure::new(data_shards, parity_shards, block_size));
|
||||
|
||||
// Use different test data based on feature, create owned data
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
// Use test data suitable for both modes
|
||||
let data =
|
||||
b"SIMD async error test data with sufficient length to meet SIMD requirements for proper testing and validation."
|
||||
.repeat(20); // ~2KB
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let data =
|
||||
b"SIMD async error test data with sufficient length to meet SIMD requirements for proper testing and validation."
|
||||
.repeat(20); // ~2KB
|
||||
b"Async error test data with sufficient length to meet requirements for proper testing and validation.".repeat(20); // ~2KB
|
||||
|
||||
let mut rio_reader = Cursor::new(data);
|
||||
let (tx, mut rx) = mpsc::channel::<Vec<Bytes>>(8);
|
||||
@@ -815,17 +765,16 @@ mod tests {
|
||||
|
||||
// Use different block sizes based on feature
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let block_size = 1024; // SIMD requires larger blocks
|
||||
let block_size = 1024; // Hybrid mode
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let block_size = 8;
|
||||
let block_size = 8; // Pure erasure mode (default)
|
||||
|
||||
let erasure = Arc::new(Erasure::new(data_shards, parity_shards, block_size));
|
||||
|
||||
// Use test data that fits in exactly one block to avoid multi-block complexity
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
let data = b"SIMD channel async callback test data with sufficient length to ensure proper SIMD operation and validation requirements.".repeat(8); // ~1KB, fits in one 1024-byte block
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
let data = b"SIMD channel async callback test data with sufficient length to ensure proper SIMD operation and validation requirements.".repeat(8); // ~1KB, fits in one 1024-byte block
|
||||
let data =
|
||||
b"Channel async callback test data with sufficient length to ensure proper operation and validation requirements."
|
||||
.repeat(8); // ~1KB
|
||||
|
||||
// let data = b"callback".to_vec(); // 8 bytes to fit exactly in one 8-byte block
|
||||
|
||||
@@ -867,20 +816,20 @@ mod tests {
|
||||
assert_eq!(&recovered, &data_clone);
|
||||
}
|
||||
|
||||
// Tests specifically for reed-solomon-simd implementation
|
||||
// Tests specifically for hybrid mode (SIMD + erasure fallback)
|
||||
#[cfg(feature = "reed-solomon-simd")]
|
||||
mod simd_tests {
|
||||
mod hybrid_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_simd_encode_decode_roundtrip() {
|
||||
fn test_hybrid_encode_decode_roundtrip() {
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
let block_size = 1024; // Use larger block size for SIMD compatibility
|
||||
let block_size = 1024; // Use larger block size for hybrid mode
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
|
||||
// Use data that will create shards >= 512 bytes (SIMD minimum)
|
||||
let test_data = b"SIMD test data for encoding and decoding roundtrip verification with sufficient length to ensure shard size requirements are met for proper SIMD optimization and validation.";
|
||||
// Use data that will create shards >= 512 bytes for SIMD optimization
|
||||
let test_data = b"Hybrid test data for encoding and decoding roundtrip verification with sufficient length to ensure shard size requirements are met for proper SIMD optimization and validation.";
|
||||
let data = test_data.repeat(25); // Create much larger data: ~5KB total, ~1.25KB per shard
|
||||
|
||||
let encoded_shards = erasure.encode_data(&data).unwrap();
|
||||
@@ -905,13 +854,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simd_all_zero_data() {
|
||||
fn test_hybrid_all_zero_data() {
|
||||
let data_shards = 4;
|
||||
let parity_shards = 2;
|
||||
let block_size = 1024; // Use larger block size for SIMD compatibility
|
||||
let block_size = 1024; // Use larger block size for hybrid mode
|
||||
let erasure = Erasure::new(data_shards, parity_shards, block_size);
|
||||
|
||||
// Create all-zero data that ensures adequate shard size for SIMD
|
||||
// Create all-zero data that ensures adequate shard size for SIMD optimization
|
||||
let data = vec![0u8; 1024]; // 1KB of zeros, each shard will be 256 bytes
|
||||
|
||||
let encoded_shards = erasure.encode_data(&data).unwrap();
|
||||
@@ -1243,7 +1192,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// Comparative tests between different implementations
|
||||
#[cfg(all(feature = "reed-solomon-simd", feature = "reed-solomon-erasure"))]
|
||||
#[cfg(not(feature = "reed-solomon-simd"))]
|
||||
mod comparative_tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@ pub mod encode;
|
||||
pub mod erasure;
|
||||
pub mod heal;
|
||||
|
||||
pub use erasure::Erasure;
|
||||
pub use erasure::{Erasure, ReedSolomonEncoder};
|
||||
|
||||
Reference in New Issue
Block a user