NuttX for Raspberry Pi PicoのSMP対応


Raspberry Pi PicoでNuttXを動かす で動かしたNuttXをSMPに対応させました。

【追記】hackster.io で紹介されました。
The Raspberry Pi Pico, and RP2040, Get Full Dual-Core SMP Support in Apache's NuttX RTOS Port

PicoはCortex-M0+を2つ搭載していますが、通常の動作ではそのうち1つのCPUしか使われず、もう1つのCPUはPico SDKのpico_multicore APIを使って明示的に動かしてやる必要がありました。
NuttX SMPは一般のPC用OSのようにスケジューラが複数コアを管理するので、複数タスク、スレッドの動作時にOSがその実行を各コアに割り振ってくれます。

コード取得とビルド

Raspberry Pi Pico SMP対応も公式リポジトリマージされました。

環境準備やビルドの手順は以前と同じです。唯一、ビルド時のコンフィグレーションがraspberrypi-pico:nshからraspberrypi-pico:smpに変わります。

$ git clone https://github.com/apache/incubator-nuttx.git nuttx
$ git clone https://github.com/apache/incubator-nuttx-apps.git apps
$ cd nuttx
$ ./tools/configure.sh raspberrypi-pico:smp
$ make

実行

起動してpsを見て見ると、以下のように表示内容に"CPU"の項目が増えて、2つのCPUがそれぞれアイドルタスクを実行していることが分かります。

NuttShell (NSH) NuttX-10.0.1
nsh>
nsh> ps
  PID CPU PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK   STACK COMMAND
    0   0   0 FIFO     Kthread N-- Assigned           00000000 001024 CPU0 IDLE
    1   1   0 FIFO     Kthread N-- Running            00000000 002052 CPU1 IDLE
    3   0 100 RR       Task    --- Running            00000000 002036 init
nsh>

smpコマンドで、多数のスレッドを作ってSMP動作を確認するテストが走ります。

nsh> smp
  Main[0]: Running on CPU0
  Main[0]: Initializing barrier
Thread[1]: Started
  Main[0]: Thread 1 created
Thread[1]: Running on CPU0
  Main[0]: Now running on CPU1
Thread[2]: Started
Thread[2]: Running on CPU1
  Main[0]: Thread 2 created
  Main[0]: Now running on CPU0
Thread[3]: Started
...
Thread[2]: Now running on CPU0
Thread[6]: Done
  Main[0]: Thread 1 completed with result=0
Thread[2]: Done
Thread[3]: Now running on CPU0
Thread[7]: Now running on CPU0
Thread[6]: Now running on CPU0
Thread[2]: Now running on CPU1
  Main[0]: Now running on CPU1
  Main[0]: Thread 2 completed with result=0
  Main[0]: Thread 3 completed with result=0
  Main[0]: Thread 4 completed with result=0
  Main[0]: Thread 5 completed with result=0
  Main[0]: Thread 6 completed with result=0
  Main[0]: Thread 7 completed with result=0
  Main[0]: Thread 8 completed with result=0
nsh>

getprimeで、素数を求めるベンチマークを引数で指定したスレッド数同時に走らせます。

nsh> getprime 2
Set thread priority to 10
Set thread policy to SCHED_RR
Start thread #0
thread #0 started, looking for primes < 10000, doing 10 run(s)
Start thread #1
thread #1 started, looking for primes < 10000, doing 10 run(s)
thread #0 finished, found 1230 primes, last one was 9973
thread #1 finished, found 1230 primes, last one was 9973
Done
getprime took 14440 msec
nsh>

SMPを有効にしなかった場合は、各スレッドを同じCPUで処理するため倍近くの時間がかかります。

nsh> getprime 2
Set thread priority to 10
Set thread policy to SCHED_RR
Start thread #0
Start thread #1
thread #2 started, looking for primes < 10000, doing 10 run(s)
thread #2 started, looking for primes < 10000, doing 10 run(s)
thread #2 finished, found 1230 primes, last one was 9973
thread #2 finished, found 1230 primes, last one was 9973
Done
getprime took 25610 msec
nsh>

RP2040にはCPUキャッシュがないので、2つのCPUが同時に走るとメモリアクセスが競合しまくるはずですが、この状況で 25610/14440 = 約1.8倍程度のパフォーマンスが出るのは、SMPシステムとしてはかなり優秀ではないでしょうか。