Raspberry Pi Advent Calendar 2024 11日目です。9日目は武藤さんのPicoでFuzix OSを動かすお話、10日目は北神さんのPico SDK入門でした。どちらもPicoネタでしたね。ありがとうございます!
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のメモリにアクセスしないようにいい感じにするやつという認識です。
Raspberry Piから見れば1CPU1メモリなので、どうやってもローカルメモリで済みますが、メモリの中にはバンクという単位があるらしく、特定バンクに対して別々のCPUコアが同時にアクセスはできないので、遅くなる可能性があるので採用した、ということみたいです。
NUMA Testing - Raspberry Pi Forums
カーネル側のPRはこちら。すでにapt upgradeすれば適用済みのカーネルが降ってきます。
また、EEPROMのアップデートもlatestチャネルでリリースされています。設定パラメーターが追加されているので、こちらもアップデートが必要です。
導入する
先述の通り、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コマンドでも様子を見てみます。
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