Rust in Everything
基于 rayon-core 1.13.0

RAYON_NUM_THREADS=0 不是「不并行」

主线位置:线程池的创建。解析代码逐行在 ThreadPoolBuilder。

flowchart TB
  n{"num_threads(n),n > 0"} -->|是| use["用 n,不看环境变量"]
  n -->|否| e{"RAYON_NUM_THREADS 按 usize 解析"}
  e -->|"正数"| usee["用它"]
  e -->|"0"| def["available_parallelism()"]
  e -->|"未设或解析失败(abc、空格、-1)"| old{"RAYON_RS_NUM_CPUS 正数?"}
  old -->|是| useo["用它"]
  old -->|否| def

现象

几种常见的误会:

  • 想关掉并行,设了 RAYON_NUM_THREADS=0,结果线程数是核数。
  • 部署脚本里写成 RAYON_NUM_THREADS=" 4" 或 RAYON_NUM_THREADS=4x,没有任何报错,线程数还是核数。
  • 设了环境变量,程序里某个 ThreadPoolBuilder::new().num_threads(8).build() 的池仍是 8 个线程。
  • 在 pool.install 里用 rayon::current_num_threads() 算分块数,得到的是 pool 的线程数,不是全局池的。

复现

exp-rayon/global-init 的 env_threads(本机 available_parallelism() = 12)打印全局池大小和一个 num_threads(2) 池的大小(ThreadPoolBuilder):

RAYON_NUM_THREADSRAYON_RS_NUM_CPUS全局池num_threads(2) 的池
未设未设122
4未设42
0未设122
abc / " 4" / -1 / ""未设122
未设332
5352
03122
abc332

从哪来

get_num_threads:

  1. num_threads(n) 且 n > 0:用 n,环境变量不看。
  2. RAYON_NUM_THREADS 用 usize::from_str 解析:正数就用;0 直接返回默认值;解析失败(非数字、带空格、负数、空串)什么也不说,往下走。
  3. 废弃的 RAYON_RS_NUM_CPUS,只认正数。
  4. available_parallelism(),取不到是 1。

所以 0 的含义是「用默认」,和 num_threads(0) 一致(builder 的默认值就是 0);而写错的值被当成没设,还会让旧变量意外生效(最后一行)。之后 Registry::new 再截到 max_num_threads()(64 位 65535),超出也是静默截断。

current_num_threads() 读的是「调用线程所在的池」,不在任何池里才是全局池(Registry::current_num_threads)。第一次从非 worker 线程调用它,还会用默认配置把全局池建起来(The global thread pool has already been initialized)。

环境变量只在全局池被创建的那一刻和每次 ThreadPoolBuilder::build() 时读取,进程运行中改 std::env::set_var 对已建好的池没有作用。

怎么改

  • 要串行:RAYON_NUM_THREADS=1,不是 0。注意 1 线程时 par_iter 仍然要进池(从调用线程注入到那一个 worker),从池外调用的固定开销还在(几十个元素的 par_iter 比串行慢几万倍)。
  • 校验配置:启动时打印一次 rayon::current_num_threads(),和期望值对比;部署脚本里只写纯数字。
  • 库代码别写死 num_threads:私有池写死线程数会让用户的环境变量失效。需要可配置时,自己读环境变量或接收参数。
  • 其他读同一个变量的库:candle 的 get_num_threads() 也读 RAYON_NUM_THREADS,它和 rayon 全局池大小一起变(candle 卷 CPU 后端)。

小结

  • RAYON_NUM_THREADS=0 = 默认(核数);非数字、带空格、负数被静默忽略,并可能让废弃的 RAYON_RS_NUM_CPUS 生效。
  • num_threads(n > 0) 优先于环境变量;current_num_threads() 是当前池的线程数。
  • 要串行设 1;启动时打印实际线程数做校验。

💬 关联讨论

发起讨论
加载中...