rustfs-erasure-codec 7.0.0

Rust implementation of Reed-Solomon erasure coding
Documentation
# P3-2: 基于分片大小的自动并行度调优 — 子任务详细文档

> **状态: ✅ 已完成** — cache_detect 模块(Linux sysfs + macOS sysctl),cache-aware chunk sizing
> 文档日期: 2026-05-31
> 预估总工作量: 3-5 天
> 前置依赖: 无

---

## 概述

在并行决策中考虑 CPU 缓存大小,根据分片大小自动计算最优并行度,避免缓存抖动。

---

## P3-2a: 缓存感知并行度计算

### P3-2a-1: 算法设计

**当前算法** (parallel.rs:56-109):
```
max_jobs = min(max_jobs or available_parallelism, available_parallelism)
chunk_count = shard_size / min_bytes_per_job
jobs = min(max_jobs, chunk_count)
```

**问题**: 未考虑 L2/L3 缓存。当 shard_size 很大时,每个 chunk 可能超出 L2 缓存。

**改进算法**:
```
// 估算每个核心的理想工作集大小
l2_per_core = 256KB (可配置)
shard_elements = data_shards + output_shards
ideal_chunk = l2_per_core / shard_elements
chunk_count = shard_size / ideal_chunk
jobs = min(max_jobs, chunk_count)
```

**添加字段**:
```rust
pub struct ParallelPolicy {
    pub min_parallel_shard_bytes: usize,
    pub min_bytes_per_job: usize,
    pub max_jobs: usize,
    pub l2_cache_bytes: usize,  // 新增: L2 缓存大小估算
}
```

**预估**: 1 天

### P3-2a-2: 实现

**文件**: `src/core/parallel.rs`

```rust
impl ParallelPolicy {
    pub fn with_l2_cache_bytes(mut self, bytes: usize) -> Self {
        self.l2_cache_bytes = bytes;
        self
    }
}

// decide() 中:
if self.l2_cache_bytes > 0 {
    let ideal_chunk = self.l2_cache_bytes / (data_shards + output_shards).max(1);
    let cache_chunk_count = shard_size.div_ceil(ideal_chunk.max(1));
    chunk_count = chunk_count.min(cache_chunk_count);
}
```

**预估**: 1 天

### P3-2a-3: 测试

```rust
#[test]
fn test_cache_aware_reduces_chunks() {
    let policy = ParallelPolicy {
        l2_cache_bytes: 256 * 1024,
        ..Default::default()
    };
    let decision = policy.decide(1024 * 1024, 10, 4);
    // 验证 chunk 数量受缓存约束
}
```

**预估**: 0.5 天

---

## P3-2b: 缓存大小自动检测

### P3-2b-1: Linux 检测

**新建文件**: `src/core/cache_detect.rs`

```rust
#[cfg(target_os = "linux")]
pub fn detect_l2_cache_bytes() -> Option<usize> {
    // 读取 /sys/devices/system/cpu/cpu0/cache/index2/size
    let path = "/sys/devices/system/cpu/cpu0/cache/index2/size";
    let content = std::fs::read_to_string(path).ok()?;
    parse_cache_size(&content)
}

fn parse_cache_size(s: &str) -> Option<usize> {
    let s = s.trim();
    if let Some(k) = s.strip_suffix('K') {
        k.parse::<usize>().ok().map(|v| v * 1024)
    } else if let Some(m) = s.strip_suffix('M') {
        m.parse::<usize>().ok().map(|v| v * 1024 * 1024)
    } else {
        s.parse().ok()
    }
}
```

**预估**: 1 天

### P3-2b-2: macOS 检测

```rust
#[cfg(target_os = "macos")]
pub fn detect_l2_cache_bytes() -> Option<usize> {
    // 使用 sysctl hw.l2cachesize
    let output = std::process::Command::new("sysctl")
        .arg("-n")
        .arg("hw.l2cachesize")
        .output().ok()?;
    let s = String::from_utf8(output.stdout).ok()?;
    s.trim().parse().ok()
}
```

**预估**: 0.5 天

### P3-2b-3: 回退默认值

```rust
pub fn detect_l2_cache_bytes() -> Option<usize> {
    #[cfg(target_os = "linux")]
    { detect_l2_cache_bytes_linux().or(Some(256 * 1024)) }

    #[cfg(target_os = "macos")]
    { detect_l2_cache_bytes_macos().or(Some(256 * 1024)) }

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    { Some(256 * 1024) } // 默认 256KB
}
```

**集成**: 在 `ParallelPolicy::default()` 中调用检测:
```rust
impl Default for ParallelPolicy {
    fn default() -> Self {
        Self {
            min_parallel_shard_bytes: PARALLEL_MIN_SHARD_BYTES,
            min_bytes_per_job: CODE_SLICE_LARGE_CHUNK_BYTES,
            max_jobs: 0,
            l2_cache_bytes: detect_l2_cache_bytes().unwrap_or(256 * 1024),
        }
    }
}
```

**预估**: 0.5 天

---

## 依赖关系

```
P3-2a-1 → P3-2a-2 → P3-2a-3
P3-2b-1 + P3-2b-2 + P3-2b-3 → P3-2a-2 (集成到 default)
```