bigywater.tech
Rust · 95

Rayon 并行迭代

rayon 让「把 iter 改成 par_iter」成为可能,但到底能快多少?什么时候反而更慢?线程数怎么影响?闭包里能不能随便改共享状态?这篇每个问题都在本机跑出数字。

6.4×
10 线程下,2000 万个 sin 求和的加速比
慢于串行
数据量 100 万以下时 par_iter 的表现
110.97 对 0.65 ms
par_iter 用 Mutex 共享累加与用 reduce 的耗时

par_iter 的基本行为

实测 · 本机执行输出
  (含下载编译 rayon,用时 2 s)
  可用并行度 10  rayon 线程数 10
  par_iter().map().collect() 保持原顺序: true
  par_iter().sum() = 210  iter().sum() = 210
  for_each 里的执行顺序按 0..16 排好: false(并行执行,顺序不保证);内容齐全: true

rayon 1.x 从 crates.io 下载(需要联网)。线程的基础见线程创建与 Join。

CPU 密集任务

实测 · 本机执行输出
  串行 iter        57.2 ms  结果 186.509301
  并行 par_iter     8.9 ms  结果 186.509301  加速 6.4×
  浮点求和的结果在小数点后 9 位一致(并行的加法顺序不同)

任务太小

实测 · 本机执行输出
  n=       100: 串行       0.01 µs   并行      20.84 µs   串行更快
  n=     10000: 串行       0.73 µs   并行      41.80 µs   串行更快
  n=   1000000: 串行      88.15 µs   并行      90.22 µs   串行更快
  n= 100000000: 串行   25725.89 µs   并行    6468.01 µs   并行更快

线程数与加速

实测 · 本机执行输出
  线程数  1:     28.5 ms  相对 1 线程 1.0×
  线程数  2:     14.3 ms  相对 1 线程 2.0×
  线程数  4:      7.5 ms  相对 1 线程 3.8×
  线程数  8:      4.8 ms  相对 1 线程 5.9×
  线程数 10:      4.6 ms  相对 1 线程 6.2×

共享可变状态

实测 · 本机执行输出
  par_iter + Mutex    110.97 ms  结果 12499997500000
  par_iter + reduce     0.65 ms  结果 12499997500000
  串行 sum              0.76 ms  结果 12499997500000

Mutex 的开销见Mutex 与 RwLock。

Send / Sync 约束

实测 · 本机执行输出
  error[E0277]: `RefCell<i32>` cannot be shared between threads safely
     --> src/main.rs:2:70
      |
    2 | fn main() { let c = RefCell::new(0); (0..4).into_par_iter().for_each(|i| { *c.borrow_mut() += i; }); }
      |                                                             -------- ---^^^^^^^^^^^^^^^^^^^^^^^^^^
      |                                                             |        |
      |                                                             |        `RefCell<i32>` cannot be shared between threads safely
      |                                                             |        within this `{closure@src/main.rs:2:70: 2:73}`
      |                                                             required by a bound introduced by this call

分步演示

rayon
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。10 核机器,rayon 1.x;耗时 release 构建
    • 没有验证:work-stealing 的具体调度、par_bridge、嵌套并行与死锁风险、par_sort 的加速、不同核数的机器上的曲线、线程池创建开销、内存带宽受限任务的加速比
    京ICP备2026067502号-1京公网安备11010502063487号