Rust in Everything
基于 rayon 1.12.0

常见坑

每一页对着一个报错或一个现象:先给最小复现和实测输出,再到源码里找它从哪来,最后给出改法。机制在前面各章讲过的,这里只链过去;能复用前面实验的直接引用它们的数字和页面,不重复测。新做的两组复现在 exp-rayon/pitfalls/(blocking、tls 两个程序),其余在前面各章的实验目录里。本机 M2 Max(8 性能核 + 4 能效核),rustc 1.99.1,rayon 1.12.0 / rayon-core 1.13.0。

进程退出或报错

  1. Rayon: detected unexpected panic; aborting:spawn 的闭包 panic,没有 panic_handler 就 abort,退出码 134(E11)。
  2. The global thread pool has already been initialized:任何一次 rayon 调用都会用默认配置建好全局池,之后 build_global 失败(E10)。
  3. scope 里两个任务都 panic,只看到一个:只保留第一个存进 AtomicPtr 的;join 两边都 panic 传 A 的(E11)。

卡住

  1. 持着 Mutex 调 par_iter,偶尔卡死:等待时同一线程执行偷来的任务,再加同一把锁(E7:确定性 20/20,普通写法 10 次 7 次)。
  2. 任务里阻塞等另一个 rayon 任务,整个池卡住:池大小固定,阻塞的任务数达到线程数就没人执行被等的任务(E12,本部分新测)。
  3. 在 tokio 任务里调 par_iter,运行时停住:tokio worker 阻塞在 LockLatch 上(E5:心跳停 245 ms)。

变慢

  1. 几十个元素的 par_iter 比串行慢几万倍:从池外进池一次 17–21 µs,16 个元素串行 1 ns(E13、E1、E4)。
  2. par_sort 比 sort 还慢:2000 以下不进池,比的是 std 旧版算法和新版算法(E9)。
  3. filter 之后 collect 慢了 8 倍:没有长度就走 LinkedList<Vec<T>>(E8)。
  4. par_bridge 打乱了顺序,还比顺序执行慢:一把锁后面逐个抢元素。

结果或环境和预期不同

  1. 同一个数组,par_iter().sum::<f32>() 每次结果不同:树形随窃取变化,浮点加法不满足结合律(E8)。
  2. RAYON_NUM_THREADS=0 不是「不并行」:0 是默认,写错的值被静默忽略。
  3. install 等待时,本池的其他任务插进来执行:跨池 install 边等边干活(E6:200/200 次 one one two two)。
  4. thread_local 在池里读不到,或者被别的任务改掉:闭包跑在 worker 上;等待时同一线程执行别的任务(本部分新测:200 个元素里 9–18 个被改掉)。
flowchart TB
  wait["worker 的等待 = 在当前栈上执行别的任务"] --> p4["4 持锁重入"]
  wait --> p13["13 install 插队"]
  wait --> p14["14 thread_local 被改"]
  fixed["池大小固定、阻塞不补线程"] --> p5["5 池内阻塞"]
  cold["池外进池:注入 + LockLatch"] --> p6["6 async 里调用"]
  cold --> p7["7 小任务"]
  split["切分树随窃取变化"] --> p11["11 浮点求和"]
  split --> p9["9 filter 后 collect"]
  detach["detached 任务没有调用方"] --> p1["1 spawn abort"]
  once["全局池只建一次"] --> p2["2 build_global 太晚"]
  once --> p12["12 线程数变量"]
  other["实现选择"] --> p3["3 只传一个 panic"]
  other --> p8["8 排序算法旧"]
  other --> p10["10 par_bridge 一把锁"]

按根源分:第 4、13、14 条是同一个机制——worker 等待时不阻塞,而是在当前栈帧之上执行别的任务,换来的是嵌套多深都不耗尽线程(08 设计取舍);第 5 条是它的反面,用户代码自己阻塞时这层保护就没了。第 6、7 条都是从池外进池的固定成本。按后果分:第 1 条杀进程;第 4、5、6 条卡住,其中 4、5 依赖时序,测试里不一定出现;第 7–10 条只是慢,结果正确;第 11、14 条结果或上下文不对,最难发现。

规划时没有实测的三条

卷计划里有三条坑规划时没有数据,约定「先测,测不出就删」:

  • filter 后 collect:第 3 批的 collect 到 Vec 已测出几千次分配、慢 8 倍,保留为第 9 条。
  • 浮点求和不确定:第 3 批的 fold、reduce 与 sum 已测出 100 次 4–5 个结果,保留为第 11 条。
  • 池内阻塞死锁:本部分新做 E12,确定性复现 1 / 2 / 4 / 12 线程都 3/3 卡死,par_iter 写法 2 / 4 线程 10/10、12 线程 5/10 卡死,保留为第 5 条。

三条都复现了,没有删掉的候选。另外加了一条规划里没有实测的第 14 条(thread_local),复现程序和数字在该页。

本站对 rayon 只有间接依赖;这些坑在本站的 tantivy 用法里碰不碰得到,见 10 在本站中。

💬 关联讨论

发起讨论
加载中...