Raspberry Pi 3 B+ & Raspberry Pi OS & OpenCV 4.2.0

投稿者: | 2020年6月16日

システムに最適化してOpenCV 4.2.0の環境を構築する

Raspberry Pi 3 B+に Raspberry Pi OS (旧名 Raspbian ) を載せて OpenCV 4.2.0 をNEONとVFPV3を有効にしてコンパイルした時のcmakeのオプションと、ちょっとしたノウハウを書いておく。全体のコンパイルのやり方はあちこちに書いてあるので、そっちを参照して欲しい。

拡張命令

Raspberry Pi 3 にはSoC(CPUチップ)にARMv7が使われていて、拡張命令・拡張機能が用意されており、それを使えばCPUのみで処理するよりも速くなる。

  • モデル名 ARMv7 Processor rev 4 (v7l)
  • 拡張命令・拡張機能 half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32

OpenCV 4.2.0では cmakeの引数でFAST_MATHと NEONと VFPV3 をONにすることで、これらの拡張機能が使えるようになる。

  • VFP (Vector Floating Point) はARMアーキテクチャのコプロセッサ拡張で、VPFv3では半精度、単精度、倍精度の浮動小数点演算が計算できる。
  • NEONとはSIMD and Floating-point命令の名称で、こちらも処理の高速化に寄与する。

cmakeの引数

cmake -D CMAKE_BUILD_TYPE=RELEASE \
 -D CMAKE_INSTALL_PREFIX=/usr/local \
 -D INSTALL_C_EXAMPLES=OFF \
 -D INSTALL_PYTHON_EXAMPLES=ON \
 -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
 -D BUILD_EXAMPLES=OFF \
 -D BUILD_TEST=OFF \
 -D ENABLE_FAST_MATH=ON \
 -D ENABLE_NEON=ON \
 -D ENAVLE_VFPV3=ON ..

結果

CPUのみのものと、拡張命令を使ったものを2つ作って比較したところ、顔検出、両目検出などにおいては、拡張命令を使ったものは平均で半分程度の処理時間で終わった。

コンパイル時についてのTips

makeを使ってのコンパイルでは、最初の方は軽い処理からは始まり、最後の方はライブラリ全体を最適化するような重い処理がいくつも発生する。この時、仮想記憶容量がデフォルトのままでは足りなくなるので、スワップサイズを1024KBにしておく必要がある。

makeの後半にあるpythonのcv2ライブラリを作る段階では大量にメモリを消費する。この時、実メモリを1GBしかもたないRaspberry Pi 3では、当然ながら、ページイン・ページアウトによるスラッシングが発生し、ロードアベレージが極端に上昇する。少しでもメモリに余裕が欲しいため、ディスプレイを接続せずにブートしGUIに使うメモリ量を節約した。作業はssh経由でログインして行った。

microSDカードやUSBメモリ上でコンパイルせずに、nfsを使いファイルサーバー(M.2 SSD)に接続し、その上にソースコードを展開し、コンパイルした。ファイルサーバーとなったGNU/Linux上でネットワークのトラフィックを確認していたが30MB/sec程度のスループットが確保出来ていた。

makeは-jオプションを使ってコンパイルを並列化することが可能であり、4コアを持つarmv7lなので、4コアをフルに使うことが有効といわれている。しかし、現実にはコンパイル以外のシステムも同時に動いているので、すべてのコアをコンパイルに充てると、トータルでのシステムのスループットが悪くなってしまう。そのため3コアのみをコンパイルに使用し、1コアをシステムに割り当てた。

並列化の最大の問題点はRaspberry Pi 3の実メモリは1GBと余裕がなく、それを複数のコンパイル・プロセスが奪い合うことである。小さいサイズのモジュールをコンパイルしている時は良いが、後半になり大きなモジュールをコンパイルする際は、頻繁にページイン・ページアウトを繰り返すようになる。また、コンパイル時に大量のメモリが必要なケースはランダムに発生するわけではなく、後半に連続して発生する。スラッシングは極端にスループットを落とす。そこで、定期的にスワップ領域の利用状況をみて、長期間に渡りスワップ領域が逼迫している時は、makeを一旦killし、シングルタスクのmakeに戻して処理し、スワップ領域が使われなくなったらば、また並列処理オプションをつけたmakeに戻すといったことをした。比較をしていないので、どの程度コンパイル時間が短くなったかはわからないが、理屈としてはページイン・ページアウトのスラッシングを避けているので、トータルで効率がよくなっているはずである。