みなさん、nproc は使いますよね。
こんなふうに。
make -j$(nproc)
しかし、それが「適さない環境」があること、ご存知でしたか?
nproc の返す数字ってなんだ? ¶
「コア数に決まっているだろう」「スレッド数だ、バカタレ」
までは、よく議論するかと思います。
実際の挙動 ¶
もし、先の説明通りならば、システムの起動中に「ダイナミック」に値が増減するはずはないのですが…。
~$ while : ; do echo $(date) / nproc: $(nproc); done | uniq
Fri May 9 14:44:27 JST 2025 / nproc: 6
Fri May 9 14:44:28 JST 2025 / nproc: 6
Fri May 9 14:44:28 JST 2025 / nproc: 7
Fri May 9 14:44:28 JST 2025 / nproc: 8
Fri May 9 14:44:28 JST 2025 / nproc: 7
Fri May 9 14:44:28 JST 2025 / nproc: 6
Fri May 9 14:44:28 JST 2025 / nproc: 7
Fri May 9 14:44:28 JST 2025 / nproc: 6
Fri May 9 14:44:28 JST 2025 / nproc: 7
なんということでしょう。1秒のうちに何度も値が増減しています。
これは、「ベアメタル」なLinuxシステムで取ったログであり、手動でvCPUを増減させているわけではありません。
どうしてこうなってしまうのでしょうか。
man を読もう ¶
Print the number of processing units available to the current process, which may be less than the number of online processors
はい、というわけで、「オンラインプロセッサ」のなかでも特に「現在」使えるユニット数を返してくるわけですね。
ん?「現在」?
ソースコード (とコメント) を読もう ¶
GNUのnproc実装のソースコード を実際に読んでみましょう。
といっても、丁寧なドキュメントがコメントにあるため、だいたいそれを読むだけです。
On systems with a modern affinity mask system call, we have
sysconf (_SC_NPROCESSORS_CONF)
>= sysconf (_SC_NPROCESSORS_ONLN)
>= num_processors_via_affinity_mask ()
The first number is the number of CPUs configured in the system.
The second number is the number of CPUs available to the scheduler.
The third number is the number of CPUs available to the current process.
読むと、以下の3種類の値を取得している事がわかります。
- 「システム」に設定されたCPU数
getconf _NPROCESSORS_CONF
でも取れる
- 「スケジューラー」が使えるCPU数
getconf _NPROCESSORS_CONF
でも取れる
- Affinityで「使えるとマーク」されているCPU数
taskset -cp <PID>
でも取れるpthread_getaffinity_np
を使っている
このまま処理を追い、その関係を調べると、
nproc
の場合は大凡、Affinityから使えるとされている数nproc --all
の場合は大凡、オンラインプロセッサ数
ということがわかりますね。
全くの意図通りじゃないか! ¶
はい。そのとおりです。
「Affinityを誰かが裏でこっそり設定しない限り。」
あっ!省電力機能! ¶
例えば「ベアメタル」なLinuxシステムであるところの Android は!
プロセスのAffinityを
- 負荷状況に応じて
- 高頻度かつ
- ダイナミックに
設定して回ってくれているのです!
実際に見てみると:
~$ while :; do echo $(date) $(taskset -cp $BASHPID | awk '{ print $6 }') ; done | uniq
Fri May 9 14:50:37 JST 2025 0-6
Fri May 9 14:50:37 JST 2025 0-4,6
Fri May 9 14:50:37 JST 2025 0-6
Fri May 9 14:50:37 JST 2025 0-5
Fri May 9 14:50:37 JST 2025 0-7
Fri May 9 14:50:37 JST 2025 0-3,5-7
Fri May 9 14:50:37 JST 2025 0-3,5,6
Fri May 9 14:50:37 JST 2025 0-6
Fri May 9 14:50:37 JST 2025 0-5
私の環境 (8コア) では、コアスリープを活用するために、1秒のうちにも何度もスケジュールし直してくれているようです!
電池に優しいですね!
nproc が返す値もこれと同様に、1秒のうちに何度もスケジュールされ直すというわけです!
こまりますね!!
というわけで ¶
省電力環境では make -j$(nproc)
が、無負荷時のAffinityを参照してしまい、期待通りのコア数が使えないケースがある、ということです。
その様な環境では、 make -j$(nproc --all)
や、nproc
の代わりに、実際に使いたい並列数を入力すると良いでしょう。