Linuxのプロセススケジューラから見たSocionext SC2A11

はじめに

本記事はSocionext SC2A11のキャッシュメモリ構成の続きです。今回はLinuxのプロセススケジューラにはこのCPUがどのように見えているかについて調べました。

まずは前提知識としてLinuxカーネルのプロセススケジューラに存在するロードバランサという機能について説明します。その後に、ロードバランサからSC2A11がどのように見えているのかについて記載します。

ロードバランサ

システムに複数の論理CPU(1つのコア、あるいはSMT(1)が有効なら1つのスレッド)が存在する場合、Linuxのプロセススケジューラの中のロードバランサという機能がそれぞれの論理CPU上のプロセス数を平均化しようとします2

たとえば1ソケット4コア、つまり4論理CPUのシステムにおいて実行可能プロセスが4つ存在する場合、1つの論理CPUに4つのプロセスが偏るようなことはなく、それぞれの論理CPUにプロセスが1つづつ割り振られます。一時的にそのようなことになる可能性はありますが、すぐに解消されます。

f:id:satoru_takeuchi:20200329053857p:plain

ロードバランサが単純に全論理CPUを平等に扱って負荷分散すると問題が発生します。たとえば2コア2プロセスの4論理CPUシステムについて考えてみましょう。論理CPU0,1と2と3がそれぞれ同じコア内の2スレッドとします。このとき実行可能プロセスが2つあるとすると、論理CPU0と1にプロセスが割り振られて、2と3がアイドルという状況が考えられます。

ペアになっているスレッドはほとんどのハードウェア資源を共有するため、現実的なワークロードの場合、2つのスレッド上に両方プロセスが動作している場合の性能は片方のスレッドにだけプロセスが存在する場合の高々1.5倍程度です。したがって、この場合は論理CPU0と1のいずれかに1つのプロセスを、論理CPU1と2のいずれかに1つのプロセスを割り振るほうがCPU資源を有効に使えます。

f:id:satoru_takeuchi:20200329053908p:plain

ロードバランサはCPU資源を有効に使うためにハードウェアから与えられた情報をもとにして論理CPUの階層構造を作り出し、上の階層から下の階層に順番に負荷分散します。それぞれの階層のことをスケジューラドメインと呼びます。たとえば前述の2コア2スレッドのシステムの場合は

  1. まずは上層のスケジューラドメインにおいて2コア間のプロセス数を平均化する
  2. 続いて下層のスケジューラドメインにおいて1コア内の2スレッド間でプロセス数を平均化する

という処理論理になっています。スケジューラドメインの階層構造は/proc/schedstatを見ればわかります。詳細については次節において述べます。

SC2A11の場合

SC2A11の/proc/schedstatの中身は次のようになっていました。

$ cat /proc/schedstat
cpu0 0 0 0 0 0 0 40778266780070 212490834550 257123
domain0 000003 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cpu1 0 0 0 0 0 0 6518877752440 6168684830 155606
domain0 000003 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cpu2 0 0 0 0 0 0 2042515035170 7070977990 189374
domain0 00000c 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cpu22 0 0 0 0 0 0 777618304410 8670056290 155964
domain0 c00000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
cpu23 0 0 0 0 0 0 803551513420 8844408870 165495
domain0 c00000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
domain1 ffffff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
$ 

ここでは論理CPUごとに自身が属するスケジューラドメインについての情報が出力されます。各ドメインはdomain<階層番号>で表現されます。階層番号が大きいほど上の階層であることを示します。domainの右にある数値が、当該スケジューラドメインに属する論理CPUの一覧を示すビットマスクです。

上記の出力によって次のことがわかります。

  • スケジューラドメインは2階層ある
  • 階層0のスケジューラドメインは2つの論理CPU間で負荷分散する
  • 階層1のスケジューラドメインは12個ある階層0のスケジューラドメイン間で負荷分散する

続いてこの階層はどこから得られたものなのかを調査しました。arm64アーキテクチャにおけるスケジューラドメインの階層構造は最大3階層です。

  • CPUコアを複数まとめたクラスタ間で負荷分散
  • クラスタ内のコア(最大4個)間で負荷分散
  • コア内のスレッド間で負荷分散

この階層構造を作っているのがarch/arm64/kernel/topology.c内のコードです。ここで作成した情報は/sys/firmware/devicetree/base/cpus/cpu-map以下を見ればわかります。SC2A11の場合はcpu-mapディレクトリの下に12個のcluster<クラスタ番号>ファイル、そのまた下にcore<クラスタ内のコア番号>というディレクトリが存在します。つまりSC2A11は24個のコアが2個づつのコアから成る12個のクラスタによって構成されていることがわかりました。ちなみに1つのクラスタを構成する2つのコアは前回の記事においてL2キャッシュを共有していることがわかっています。

なお、このマシンは該当しませんが、SMT機能があるCPUの場合はcoreディレクトリの下に、さらにthread<コア内のスレッド番号>というディレクトリができるようです。

おわりに

次の記事はSocionext SC0FQAA-BはNUMAか否かです。


  1. Intelの場合はハイパースレッドという機能名です

  2. nice値やスケジューリングクラス、CPU affinityが絡んでくると話はこの限りではありませんが、ここではそれらについれは割愛