あっきぃ日誌

ラズピッピブログのようなオタクブログのようななにか

Raspberry Pi OSのNUMA導入を試す

Raspberry Pi Advent Calendar 2024 11日目です。9日目は武藤さんのPicoでFuzix OSを動かすお話、10日目は北神さんのPico SDK入門でした。どちらもPicoネタでしたね。ありがとうございます!

adventar.org

Raspberry Pi OSのFake NUMA導入

直近のRaspberry Pi OSでは、Raspberry Pi 8GB SDRAMモデルのメモリパフォーマンス改善の一環として、Fake NUMA(エミュレートNUMA)が導入されました。導入すると、Pi 5の場合はGeekbenchのシングルコアの結果で11.8%、同じくマルチコアの結果で32.5%の改善が見られるようです(冒頭のフォーラムURLの投稿から引用)。また、Pi 4の8GB RAMもGeekbenchで7%ほどの改善が見込めるようです。

お仕事でもサーバーを扱う時にNUMAの話題はよく出ますが、仕組みがいまだに理解ができていなかったりするので、説明は略……。以下の記事が参考になると思います。マルチプロセッサーな環境(サーバーとか)で、CPUが隣のCPUのメモリにアクセスしないようにいい感じにするやつという認識です。

qiita.com

Raspberry Piから見れば1CPU1メモリなので、どうやってもローカルメモリで済みますが、メモリの中にはバンクという単位があるらしく、特定バンクに対して別々のCPUコアが同時にアクセスはできないので、遅くなる可能性があるので採用した、ということみたいです。

NUMA Testing - Raspberry Pi Forums

カーネル側のPRはこちら。すでにapt upgradeすれば適用済みのカーネルが降ってきます。

Support emulated NUMA for BCM2711 and BCM2712 by popcornmix · Pull Request #6273 · raspberrypi/linux · GitHub

また、EEPROMのアップデートもlatestチャネルでリリースされています。設定パラメーターが追加されているので、こちらもアップデートが必要です。

github.com

導入する

先述の通り、OS側は単にアップデートでOKです。この時に新しいEEPROMイメージも取得されます。

$ sudo apt update;sudo apt full-upgrade

EEPROMは、この執筆時点ではraspi-configでリリースチャネルをlatestに変更してからアップデートをします。2024/12/7以降のリリースがdefaultチャネルに来ていれば、リリースチャネルを変更せずにアップデートしてもかまいません。

以下はCM4でアップデートした様子。

$ sudo rpi-eeprom-update -a
*** PREPARING EEPROM UPDATES ***

BOOTLOADER: update available
   CURRENT: Mon 21 Oct 14:24:54 UTC 2024 (1729520694)
    LATEST: Sat  7 Dec 12:39:28 UTC 2024 (1733575168)
   RELEASE: latest (/lib/firmware/raspberrypi/bootloader-2711/latest)
            Use raspi-config to change the release.

  VL805_FW: Using bootloader EEPROM
     VL805: up to date
   CURRENT: 
    LATEST: 
   CURRENT: Mon 21 Oct 14:24:54 UTC 2024 (1729520694)
    UPDATE: Sat  7 Dec 12:39:28 UTC 2024 (1733575168)
    BOOTFS: /boot/firmware
'/tmp/tmp.tZJayhkhC0' -> '/boot/firmware/pieeprom.upd'

UPDATING bootloader. This could take up to a minute. Please wait

*** Do not disconnect the power until the update is complete ***

If a problem occurs then the Raspberry Pi Imager may be used to create
a bootloader rescue SD card image which restores the default bootloader image.

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=16000 -w /boot/firmware/pieeprom.upd
UPDATE SUCCESSFUL

適用してOSを再起動したら、今度は設定を追加します。SDRAM_BANKLOWパラメーターは、Pi5なら1、Pi4なら3が良いとのこと。保存したらもう一度OSを再起動します。

$ sudo rpi-eeprom-config -e

# Pi 5の場合はこれを追記
SDRAM_BANKLOW=1

# Pi 4の場合はこれを追記
SDRAM_BANKLOW=3

確認してみる

設定後は/proc/cmdlineを見ると確認できます。「numa=fake=2」みたいなパラメータがあればOK。

$ cat /proc/cmdline 
coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_hdmi=0 snd_bcm2835.enable_headphones=1  numa=fake=2 system_heap.max_order=0 smsc95xx.macaddr=E4:5F:01:XX:XX:XX vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  console=tty1 root=PARTUUID=1e0f6b5f-02 rootfstype=ext4 fsck.repair=yes rootwait

(参考: 設定前はない)
$ cat /proc/cmdline 
coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_hdmi=0  smsc95xx.macaddr=E4:5F:01:XX:XX:XX vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  console=tty1 root=PARTUUID=1e0f6b5f-02 rootfstype=ext4 fsck.repair=yes rootwait

NUMAの割り当てを見るにはnumactlコマンドが使えます。aptでパッケージを導入して、numastatコマンドを実行してみます。ノードが2つ作られていますね。単位はページ数なので、メモリのサイズとは違いますが、なんとなく半分のようです。

$ sudo apt install -y numactl

$ numastat
                           node0           node1
numa_hit                  145239          131243
numa_miss                      0               0
numa_foreign                   0               0
interleave_hit            138038          125738
local_node                145239               0
other_node                     0          131243
  • mオプションを付けるとメモリのサイズで見られます。
$ numastat -m

Per-node system memory usage (in MBs):
Token SwapCached not in hash table.
Token SecPageTables not in hash table.
Token SwapCached not in hash table.
Token SecPageTables not in hash table.
                          Node 0          Node 1           Total
                 --------------- --------------- ---------------
MemTotal                 3806.77         4015.10         7821.86
MemFree                  3619.60         3850.21         7469.80
MemUsed                   187.17          164.89          352.06
(以下略)

numactlコマンドでも様子を見てみます。

qiita.com

numactl -H(ハードウェア)はこんな感じ。Raspberry Piはマルチコアだけど、マルチプロセッサーではないからなのか、ノード0にCPUコアが寄るみたいです。これって分散もできるのかしら。

$ sudo numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3
node 0 size: 3806 MB
node 0 free: 3619 MB
node 1 cpus:
node 1 size: 4015 MB
node 1 free: 3849 MB
node distances:
node   0   1 
  0:  10  20 
  1:  20  10

参考までに、2CPU構成のラックサーバーではこんな感じになります。7の次が16だったり15の次が24だったりしているのは、0-7と8-16が物理コアで、17-23と24-31はハイパースレッディングコアだからです。

# numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 32055 MB
node 0 free: 28680 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 32208 MB
node 1 free: 30129 MB
node distances:
node   0   1 
  0:  10  21 
  1:  21  10 

ベンチマークは略

他の人がやっているのでもういいかなと思って略します。たぶん、体感できるほどの効果を頻繁に享受できることは、そこまでないんじゃないかな……?と思っていますが、どうでしょう。私も、この記事を書きながらメダカメラ環境のCM4(8GB RAM)に導入してみましたが、パフォーマンスは不満がない(気にしたことがない)ので、よほどガッツリリソースを使うとかしないと気づかないかも?と思っています。しかし、改善はあればあっただけ嬉しいので、ありがたいことです。

元々はPi 5のベンチマーク中に見つけられた問題で、Pi 5ではファームウェアでの調整も重ねられていましたが、NUMAの導入はPi 4にも恩恵があるので、いずれかの8GB RAMモデルを使用しているのであれば、導入の価値はありそうです。

追記 Pi 5の8GB RAMモデルだとどうなるの

Pi 5の8GB RAMモデルは、やらなくてもたいして変わらんやろと思って書いてなかったのですが、やってみたらNUMAが8ノードに分かれてさすがにびっくりしたので追記。RAMが1GBずつで分割されていますね。

numastat  -m
(中略)
                          Node 0          Node 1          Node 2          Node 3
                 --------------- --------------- --------------- ---------------
MemTotal                  993.47         1019.98         1019.98          955.98
MemFree                   665.89          806.25          805.81          717.94
MemUsed                   327.58          213.73          214.17          238.05
(中略)
                          Node 4          Node 5          Node 6          Node 7
                 --------------- --------------- --------------- ---------------
MemTotal                 1019.98         1019.98         1019.98         1014.77
MemFree                   782.12          809.12          806.61          791.14
MemUsed                   237.86          210.86          213.38          223.62

$ numactl -H
available: 8 nodes (0-7)
node 0 cpus: 0 1 2 3
node 0 size: 993 MB
node 0 free: 666 MB
node 1 cpus:
node 1 size: 1019 MB
node 1 free: 807 MB
node 2 cpus:
node 2 size: 1019 MB
node 2 free: 805 MB
node 3 cpus:
node 3 size: 955 MB
node 3 free: 718 MB
node 4 cpus:
node 4 size: 1019 MB
node 4 free: 782 MB
node 5 cpus:
node 5 size: 1019 MB
node 5 free: 809 MB
node 6 cpus:
node 6 size: 1019 MB
node 6 free: 806 MB
node 7 cpus:
node 7 size: 1014 MB
node 7 free: 791 MB
node distances:
node   0   1   2   3   4   5   6   7 
  0:  10  20  20  20  20  20  20  20 
  1:  20  10  20  20  20  20  20  20 
  2:  20  20  10  20  20  20  20  20 
  3:  20  20  20  10  20  20  20  20 
  4:  20  20  20  20  10  20  20  20 
  5:  20  20  20  20  20  10  20  20 
  6:  20  20  20  20  20  20  10  20 
  7:  20  20  20  20  20  20  20  10