極限のPi起動最適化
(kittenlabs.de)- SolarCamPiは、撮影時だけRaspberry Pi Zero 2 Wの電源を入れて切るオフグリッド太陽光カメラであるため、起動時間そのものがバッテリー寿命を左右する
- Debian 12 arm64 Liteを基準に、
/init.shを最初のユーザー空間コードとして測定したところ、初期状態の起動は約12秒、9.5Wsを消費した - HDMI、LED、HAT/PoE/LCD、カメラ・ディスプレイの自動検出、initramfsを削減し、不要な待機と検出を取り除いた結果、電流は136.7mA → 120.6mAに低下した
- Buildroot 2024.02.1とカスタムカーネルへ移行した後、ドライバ、圧縮、KASLR、一部の緩和機能を削除し、3.5秒未満でLinuxユーザー空間プログラムまで到達した
- 入力電圧を5.0Vから3.6Vまで下げると総エネルギーは1.754Ws → 1.438Wsに減ったが、仕様外動作のため安定性・信頼性の検証が残っている
SolarCamPiが起動時間を短縮する必要があった理由
- SolarCamPiは太陽光ベースのWiFiカメラで、Raspberry Pi Zero 2 Wを定期的に起動して写真を撮影し、WiFiでクラウドサービスへアップロードした後、再びシャットダウンする
- 西ヨーロッパの冬のように電力が不足する環境では、Piが電源オンになっている毎秒がそのまま電力コストになる
- ユーザー空間アプリケーションのサーバー接続と写真アップロードはすでに可能な限り最適化されており、スリープ中の電力を減らすよう電子回路も設計済みだった
- 残された削減経路は2つだった
- 消費電流を減らす
- 実行時間を減らす
- 電流だけを下げる方法が常に得になるとは限らない
- CPU turboを切って電流が減っても、実行時間が増えれば総エネルギーはむしろ大きくなり得る
- 目標は、電流と時間のグラフの下の面積を最小化すること
測定機材とテスト方法
- 組み込みの起動最適化では、変更後の実動作を素早く確認する必要があるため、SDカードの抜き差しや電源操作を減らすツールが重要になる
- 使用した機材は次のとおり
- Nordic Power Profiler Kit II
- USB-SD-Mux Fast
- USB-UART変換器
- Power Profiler Kit IIは、テスト対象デバイスへ電源を供給しながら、時間ごとの消費電力と8本のデジタル入力状態を同時に測定する
- Raspberry PiのGPIOピンをデジタル入力へ接続する
- アプリケーションの最初の動作としてGPIOをトグルし、電源投入からユーザー空間コード実行までの時間を測定する
- USB-SD-MuxはmicroSDカードとデバイスの間に入るインターポーザーで、コンピュータがカードを取り込んで内容を書き直し、デバイスへ戻せるようにする
- カードを抜いてリーダーに挿し、その後デバイスに戻す反復作業をなくす
- オンボードGPIOにより、テスト対象のリセットや電源制御も自動化できる
- USB-UARTコンソールは、起動失敗やWiFi問題のようにシステムが壊れたとき、状態を確認するために必要
Debian基準の起動測定
- クリーンなDebian 12 bookworm arm64 Liteイメージで、
/boot/firmware/cmdline.txtにinit=/init.shを追加した - この設定により、カーネルはsystemdより先に
/init.shをユーザー空間の最初のプロセスとして実行する - 例の
init.shはGPIO4をトグルした後、exec /sbin/initでsystemdの起動を続行する - 初期測定では約12秒後にデジタル入力0がlowになり、
init.shの実行が確認された - この起動プロセスは合計1.90 coulombを使用した
1.9As * 5.0V = 9.5Ws- 単3アルカリ電池1本は約13500Wsのエネルギーを供給できる
消費電流の削減
- HDMIエンコーダを完全に無効化すると、電流は136.7mAから122.6mAへ減った
- カメラデータをエンコードする必要があるため、GPU自体はオフにできなかった
- カメラやGPUを必要としないアプリケーションなら、GPUの無効化も試せる
- activity LEDを無効化するだけで2mAを節約し、電流は122.6mAから120.6mAへ下がった
- カメラLEDがある場合は無効化できる
- LEDが画像に反射する可能性も減る
- turbo設定変更のテストでは、現在の変更状態のPiは1.62Asを使用した
- 強制turboなしで実行すると1.58Asを使用した
- 不明な理由でturbo/boostモードを切るとGPIO4のデフォルト状態が反転したため、
init.shで極性を変えた
起動時間の短縮
- 電流は約13%減ったが、Linuxの最初の出力がコンソールに現れるまで、依然として約8秒かかり、この区間で約1Asを消費していた
- Raspberry Pi系ではGPUが先に初期化される
- GPUはSDカードから
bootcode.binを探す - Pi 4以降はEEPROMを使用する
- GPUはSDカードから
bootcode.binのBOOT_UARTを有効にすると、詳細なUARTログを確認できる- 元の
bootcode.binのバックアップが必要で、変更プロセスは潜在的に破壊的である
- 元の
- ブートローダーは、接続されたHDMIモニターのビデオパラメータを自動検出するためにEDID応答を待つ
- HDMIを使わないため、EDID文字列をハードコードして検出を回避した
- HAT、PoEファン、LCD、タッチスクリーン関連のI2C EEPROM検出も無効化した
- これらのオプションはI2Cバスの応答を待つため、不要ならオフにできる
- MIPIカメラとディスプレイの自動検出も時間を使う
- 使用カメラはHQ Camera IMX477に決まっているため、
camera_auto_detect=0、display_auto_detect=0でオフにし、dtoverlay=imx477を直接ロードした
- 使用カメラはHQ Camera IMX477に決まっているため、
- 上記の変更により自己報告の起動時間は5.38秒から4.75秒へ短縮され、
auto_initramfs=1の削除でinitramfsをオフにすると4.47秒まで下がった - SD周辺機器を100MHzにオーバークロックする
sdtweak方式は、測定可能な起動性能差を生まなかった- 書き込みアクセスでデータ破損のリスクがあり、リモートIoTデバイスには望ましくない
カーネル読み込みのボトルネックとBuildrootへの移行
- この時点では、カーネル読み込みが最も遅い作業の1つだった
kernel8.img9,276,375バイトの読み込みに約1.54秒かかった- 転送速度は約6MiB/s
- カーネル読み込みは、内部の独自仕様のVideoCore IVプロセッサを使うGPUが実行する
- ローダーコードが非効率、または保守的な設定を使っている可能性はあるが、ブラックボックスであるため、レジスタやパラメータを有用に調整することはできなかった
- GPUプロセッサコアのオーバークロックは理論上可能で、カーネル読み込み時間を**20%**短縮した
- 信頼性などの副作用が分からないため推奨されない
- システムをRaspbian/DebianからBuildroot 2024.02.1ベースのカスタムディストリビューションへ移行した
- native aarch64ツールチェーンを使用
- glibcとRaspberry Pi userlandツールは維持
- カスタムカーネルでは次のものを削除または削減した
- サウンド対応
- 大半のブロックデバイス・ファイルシステムドライバ。ただしSD/MMCとext4は維持
- RAID、USB、HID、DVB
- ビデオ・フレームバッファ対応
- トンネル、ブリッジング、ファイアウォールなどの高度なネットワーク機能
- Gzipカーネル圧縮とモジュール圧縮
- カーネルとモジュールの両方を圧縮しない方法は、GPUの読み込みが長くなっても、総エネルギーの観点では得だった
- Gzip展開は多くのエネルギーを使い、実質的に追加の再配置ステップを含む
- KASLRも無効化した
- KASLRはカーネルメモリのロードアドレスをランダム化してエクスプロイト作成を難しくするが、GPU読み込み後にカーネル再配置が必要になる
- このユースケースではネットワーク攻撃面が非常に限定的で、アプリケーションソフトウェアはすべてrootで実行される
- Spectreのような投機的実行脆弱性の緩和機能も無効化した
- 結果としてカーネルサイズは8.5MiB非圧縮で、比較用のGzip圧縮サイズは4.1MiBだった
- 元のRaspbianカーネルは25MiB非圧縮、8.9MiB Gzip圧縮だった
最終結果と入力電圧の最適化
- 最終的にLinuxユーザー空間プログラムまで3.5秒未満で到達した
- Linuxカーネル内部で使われた時間は約400msだった
- 総エネルギー消費は0.364As * 5.0V = 1.82Ws
- stock Debianのユーザー空間到達エネルギー9.5Wsと比べると、約5分の1の水準
- 公開後、Graham Sutherland / Polynomialは、Pi Zeroのレギュレータが5.0V入力では効率的ではないと指摘した
- テストシナリオと完成品では、入力電圧を4.0Vまで下げられた
- 5.0V:
350.94mAs * 5.0V = 1.754Ws - 4.0V:
390.77mAs * 4.0V = 1.563Ws - 3.6V:
399.60mAs * 3.6V = 1.438Ws
- 5.0V:
- 電圧を下げると、電流増加によりmC、つまりmAsは増えるが、総エネルギーは大きく減る
- 3.6V動作はスイッチングレギュレータをより理想的な動作点で使うことで、エネルギーをさらに約**20%**減らしたが、技術的には仕様外のため、安定性・信頼性の検証がさらに必要
公開されている設定とコード
- SolarCamPi config.txt: 完全な
config.txt - SolarCamPi Linux kernel defconfig: 削減したカーネル設定
- SolarCamPi-Buildroot v2 branch: Buildrootツリー全体。作業は進行中
1件のコメント
Hacker News の意見
Raspberry Pi 系列の電源管理は確かに弱点の一つです。なので新しい Pico 2 には期待しています。外部ハードウェアなしでも、比較的深い省電力状態に入りやすくなったように見えるからです。
似た用途のカメラを Google Coral mini で作ったことがあります。カメラは HQ cam ほど良くありませんが、内蔵 RTC で待機/ウェイクアップをとても簡単に扱えたので、定期的に撮影するアプリにはぴったりでした。高解像度画像を処理できるだけの性能と 2GB メモリもありました。
HQ camera を物理的に接続することはできますが、私の知る限り Coral でそれを管理するソフトウェアパイプラインはありません。
Raspberry Pi エコシステムのほうがはるかに成熟しており、Google が Coral ラインを供給し続けるかよりも、Raspberry Pi の将来的な供給可能性のほうを信頼しています。それでも、ハードウェア側の優れた電源サポートがどれほど有用かは確かに実感しました。
皮肉なことに、次期バージョンのカメラは外注先が Raspberry Pi で作り、電源問題はずっと大きなバッテリーを入れることで補いました。そのおかげで、未開封の Coral dev mini とカメラが 100 個積み上がっており、良いアイデアを探すか売るしかない状況になっています。
公式 Twitter アカウントを探そうとして「google coral twitter」で検索したら、2 番目の結果が余ったボード 100 枚を売ろうとしているあなたのツイートで、困惑しました。
外部ハードウェアで電力を管理すると言っているのを見ると、単なるソフトウェアの問題だけではなさそうです。
アプリケーションをカーネルにリンクされた initramfs としてまとめれば、単純な場合はファイルシステムのマウントが不要になり、かなり削減できます。
場合によっては BusyBox init のようなものも、最小限の起動だけを行う簡単な bash スクリプトで置き換えられます。devtmpfs、proc、sysfs などをマウントし、可能なら glibc を捨てることも検討に値します。
initramfs をカーネルに組み込む前に、chroot で必要なアプリケーション依存関係がすべて揃っているかテストするとよいです。chroot で実行できるなら、カーネルも起動中に実行でき、開発の反復がずっと速くなります。
カーネルモジュールを無効にして、必要な機能だけをカーネルにリンクして入れれば、容量と起動時間をさらに減らせます。gzip の代わりに zstd 圧縮も試す価値があります。
以前の起動段階がハードウェア性能を十分に使えていない場合や、イメージが大きく、読み込みと並行して別の作業をしたほうがよい場合がこれに当たります。
そうすれば残るのは、静的リンクされたバイナリ 1 つが入った initramfs だけです。
Pi の起動時間を短縮する良い記事がほかにも 2 つあります。
https://www.furkantokac.com/rpi3-fast-boot-less-than-2-secon...
http://himeshp.blogspot.com/2018/08/fast-boot-with-raspberry...
この 2 つの記事を参考にして Pi でデジタルフォトフレームを作り、キオスクモードブラウザまで非常に高速に起動するようにしました。要件がごく少なければ、かなり印象的な起動時間を得られます。
本当の悲劇は、bootcode.bin GPU コードがプロプライエタリなブラックボックスで、ソースコードがないことです。
ハッキングや趣味のプロジェクトが、修正できない隠された秘密のブラックボックスを抱えなければならないなんてひどい話です。
今は自由なオープンソースソフトウェアです。
https://www.theregister.com/2023/11/28/microsoft_opens_sourc...
だからといって Pi ファームウェア全体が自動的に自由なオープンソースになるわけではありません。ドライバーではないからです。ただし望めばそうすることはできます。
中に何があって、閉じておく必要があるのか気になります。
全体として記事は気に入っているが、この部分は確信が持てない
CPUターボを切って電流消費を少し節約するのは悪い選択で、遅くなった時間のせいで、早く終わらせて電源を切る場合より多くのエネルギーを使う、という部分
コンピュータサイエンスの授業で、消費電力はクロック周波数の2乗に比例すると習った。つまりクロックを2倍にすると電力は4倍になるという意味
だとすると、クロック上昇による2次の増加分が、作業時間が延びることで発生する固定消費電力の積より大きいかを確認するには、実際の電力差を測定する必要がありそう
関連して、PiのCPUがデータシートから導出できるか、レジスタでリアルタイムに公開されるような詳細な消費電力情報を提供してくれるとよいと思う
ほとんどのチップは、より高いクロック速度に到達するにはより高い電圧が必要なので、そこで2次の関係が生じる
ただ、Raspberry Piには動的電圧制御がないと理解しているので、電圧を下げずにクロックだけ下げても、全体のスイッチングエネルギー消費には影響しないと思う
もちろん最適点もある。CPUをあまりに大きくオーバークロックすると、ワット当たり性能が落ちすぎて、race to idle はもはや効かなくなる
そのため現代の組み込みシステムでは、作業を非常に速く実行してから次のイベントが来るまで大半のコンポーネントを切っておく、"race-to-sleep" や "race-to-halt" 戦略を使うことがよくある
ある計算を1秒で行うのに1Jかかる、たとえば1GHzで1Wだとすると、完全な球形の牛モデルでは、同じ計算を0.5秒で行うには2Jかかるということなのだろう。2GHzでは4Wになるから
ただしそれはCPU消費だけを見たもので、システム全体に4Wの固定消費があるなら、1GHzではCPU 1Jとシステム4Jで合計5J、2GHzではCPU 2Jとシステム2Jで合計4Jになる
正しく理解できているなら、システム全体の消費電力がターボ時のCPU消費電力と同程度ならターボを使うべきで、そうでなければ使うべきではない、ということ?
印象的。ただ、こういう記事を読むたびに、Pi ZeroでPlan 9の起動を録画したことを思い出す: https://taoofmac.com/space/blog/2020/09/02/1900#resurrecting
GIFはリアルタイム出力
ただし、元記事のプロジェクトに必要なカメラとWi-Fiドライバをロードできるかは別問題
個人的にはLinuxディストリビューションの起動時間は全般的にかなり残念で、こうした非力なハードウェアではその問題がずっと大きくなる
MQ-Pro SBCでも似たような最適化を試した。ノートPCでもこれはかなり体感する。MacBookは例外かもしれないが、いずれにせよ苛立つ部分
いったん電源が入った後にログインし直すだけなら一瞬だが、再起動はかなり時間がかかる
ただしディストリビューションは妥当な理由で非常に汎用的なカーネルとinitramfsを作っており、この組み合わせは特に起動が速いわけではない
[0]: https://blog.davidv.dev/posts/minimizing-linux-boot-times/
存在しないWi-FiをNetworkManagerが90秒待つ、といったひどい設定ミスがなければの話。私のLinuxマシンはgraphical.targetまで約4秒で、その大半はWi-Fi接続とntpdによるものだが、どちらも原理的には任意
本当に速い起動が欲しいなら、ブートローダ互換レイヤー、抽象化、initramfsのような動的な設定可能性をすべて捨てればよい。だがそうするとハードウェアベンダーに左右されるので、それだけの価値はない
たとえばLinuxを入れたMacBook Air 11はログイン画面まであまりに速く表示されるので、起動ログをほとんど見られない。systemd-analyze基準でgraphical targetまで4秒未満
要点は2つに見える。デスクトップ環境を使わず、テキストモードで起動して必要なときにstartxするか、軽量なログインマネージャ(lightdm)でXへ起動する。デスクトップ環境がないとサービス数が一桁倍減り、古いハードウェアで起動中のI/O圧迫が大きく下がる。Xを実行しても、起動済みシステムは200MB未満
もう1つは EFI stub が高速化に効く可能性がある: https://wiki.archlinux.org/title/EFISTUB
最初の直感は、別のコアを使えないのか、というものだった。写真を撮ってクラウドへ送るのに本当に Linux が必要なのか?
ハードウェア側の人間ではないので、最小限の予算でこの作業をどう完成させられるのか気になる。興味深い記事だった
いま食卓の上に ESP32-CAM が2つあるというだけだけど、ESP32-CAM 構成だとどう比較されるのか気になった。おそらく2メガピクセル画像までなら問題ないだろうが、起動時間と消費電力はどちらもほぼ1桁少ないと思う
気になるなら、詳細はここにある: https://components101.com/modules/esp32-cam-camera-module
原理的にはベアメタルでも可能だが、必要な周辺機器を動かすのは簡単ではない
カスタムカーネルがなぜこんなに遅く出てきたのか気になった。最適化するなら LFS やソースベースのディストリビューションから始めるものでは? こういうデバイスに自律的なソフトウェアアップデートが必ずしも必要とも思えない
それに、こういうデバイスで EFI/BIOS を最適化できるのかも気になる。少なくとも自分の普通の Arch Linux デスクトップでは、起動時間のかなりの部分を占めている
$ systemd-analyzeStartup finished in 10.076s (firmware) + 1.339s (loader) + 1.569s (kernel) + 2.974s (initrd) + 3.894s (userspace) = 19.854sRaspberry Pi のハードウェアは詳しく知らないが、他の多くの組み込み SoC には u-boot で動作するかなり最小限のブートローダがあり、たいてい非常に速い。ユーザー入力待ちの遅延時間を0に設定すれば特にそう
最小カーネルと busybox システムのほうがずっと苦痛が少なく、Gentoo も悪い選択ではない
> systemd-analyzeStartup finished in 3.259s (firmware) + 35.127s (loader) + 1.823s (kernel) + 2.927s (userspace) = 43.138s3.5秒は見事だが、全体のシナリオが本当に数分おきに Wi-Fi に接続して画像をアップロードすることなら、ESP32 のほうが消費電力の面ではずっと良い選択だったはず
Pi 用カメラモジュールに、ESP32-CAM 互換カメラにはない特定の機能がどうしても必要な場合を除けば、だが
マイクロコントローラならほとんど苦もなくできることをやるために、作業をしすぎているように見える