はじめに
- 本記事はOpenCV Advent Calendar 2023 12日目の記事です。
- その他の記事は目次をご覧ください。
- 最近は仕事に追われてOpenCVのpull requestをちゃんと追ってなかったのだが、このカレンダーの主催者熊太郎さんのツイートで気づいた。
ぬぉおおおん!!!とても悲しい悲しいissueが発行されました。。。これはもう、政府は熊太郎さんにRaspi5を支給する以外の選択肢はないですね(何を言い出したのか本人もわかっていない)https://t.co/LKXPqtu2dZ
— 恋する熊太郎@AdvCal_OpenCV2023 (@hon_no_mushi) November 24, 2023
-DCPU_BASELINE="NEON_FP16;NEON_DOTPROD;NEON_BF16"
- OpenCVには、拡張命令を安全に利用するための
Dispatch
機能が存在しているそれらは筆者の過去のアドベントカレンダー記事で何度か紹介した。
bfloat16とは
-
float
、double
、half
に合わせて、bfloat16
もしくはbf16
という浮動小数点数方式が世間一般で使われているが、このbit数の内訳は以下のように各フォーマットで違う
型 | bit幅 | 符号 | 指数部 | 仮数部 |
---|---|---|---|---|
float |
32bit | 1bit | 8bit | 23(+1)bit |
double |
64bit | 1bit | 11bit | 52(+1)bit |
half |
16bit | 1bit | 5bit | 10(+1)bit |
bf16 |
16bit | 1bit | 8bit | 7(+1)bit |
- 仮数部の
(+1)
はケチ表現を表す部分である。符号bit、指数部、仮数部(ケチ表現を除く)を合計するとbit幅と等しくなるのが確認できる。 -
bf16
は全体として16bitであるが、指数部はfloat
と同じく8bit割り当てられており、数の絶対値としてはfloat
とほぼ同じ範囲の実数が表現できる。ダイナミックレンジが広いDNNのパラメータ型として使うことでオーバーフロー/アンダーフローを抑えつつメモリ使用量を抑えたり計算のスループットを増大できたりする - なお、
float
、double
、half
はそれぞれIEEE754で制定された、いわゆる「標準」規格であるが、bfloat16
はGoogle Brainのチームが提唱し始めたフォーマットでありIEEE754では制定されてない。にも関わらずHWで命令を実装するアーキテクチャが複数でてくるあたり、Deeeeeeeepの流行を体感する限りである。
で再びissue
- で、issueでは、
NEON_FP16
、NEON_BF16
、NEON_DOTPROD
の3つを同時に指定するとコンパイルエラーになるというものである。 - なお、
NEON_FP16
とNEON_BF16
はそれぞれ名前が紛らわしいが、NEON_FP16
はfp16
つまりhalf
のまま演算する拡張命令であり、NEON_BF16
はbfloat16
のまま四則演算がサポートされる拡張命令である。 - ここでCMakeの
CPU_BASELINE
に下記オプションを指定すると、最終的に対応するコンパイルオプションがGCC/Clangに渡される仕組みとなっている
OpenCVのオプション | GCC/Clangのオプション |
---|---|
NEON_FP16 |
-march=armv8.2-a+fp16 |
NEON_BF16 |
-march=armv8.2-a+bf16+fp16 |
NEON_DOTPROD |
-march=armv8.2-a+dotprod |
- これらのオプションのうち、2つ以上指定すると、最後に指定したやつ以外無視されるというGCC/Clangの挙動につき、
- コードとしては拡張命令が埋め込まれている
- コンパイラとしては一部の拡張命令が無効になってコンパイルされる
- 結果コンパイルエラーが発生するというissueである
- 本来GCC/Clangとしては
arch
オプションで拡張命令を複数指定する場合はmarch=armv8.2a-+fp16+bf16+dotprod
という具合に拡張命令だけをarmv8.2-a
の後ろに連結し、arch
オプションは一つだけ渡すことを想定している。が、現状ではCMakeで指定したオプション分だけarch
オプションが渡されてしまう。 - という訳で筆者が複数ある
arch
オプションを束ねる機能を追加するPRを発行したのだが、記事執筆時点ではまだマージされていない - また、
NEON_BF16
に対応するコンパイラオプションは-march=armv8.2-a+bf16
なのだが、現時点では何故か-march=armv8.2-a+bf16+fp16
(+fp16
もセットにされてる)であり、原因は不明である。筆者が誤解している可能性もあるのでPR上で問い合わせたのだが、記事執筆時点では特に回答はない
後日談というか今回のオチ
-
bfloat16
命令を有効にするコンパイラオプションが追加されたものの、じつはbfloat16命令を使った実装はまだ追加されていない。 - cmakeを使った際の出力には、各オプションが有効にして個別にコンパイルされるファイルがどれぐらいあるか表示される。
-- CPU/HW features:
-- Baseline: NEON FP16
-- Dispatched code generation: NEON_DOTPROD NEON_FP16 NEON_BF16
-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD
-- NEON_DOTPROD (1 files): + NEON_DOTPROD
-- NEON_FP16 (2 files): + NEON_FP16
-- NEON_BF16 (0 files): + NEON_BF16
--
- 記事執筆時点の4.x系列のブランチでは、
NEON
とFP16
がCPU_BASELINE
、つまり必須要件としてビルドされ、NEON_DOTPROD
、NEON_FP16
、NEON_BF16
の3つのフラグはそれぞれCPU_DISPATCH
、つまり実行時にCPUの対応命令を把握して実行するかどうかを判断する命令としてビルドされる -
NEON_BF16 (0 files)
は、フラグは有効になったものの、このオプションを使うファイルは存在しないとなっており、bfloat16
命令を使った実装はまだ存在しない - 参考までに、コンパイラがこのオプションに対応していない場合も考えられる。筆者が調べた限りだと、GCC 10系列から
+bf16
オプションが有効になった模様で、それ以前のコンパイラでOpenCVをビルドすると、そもそもNEON_BF16がこの一覧に現れない - 過去のdispatchを使った記事でも触れているが、OpenCVのSIMD命令を使った実装は、intrinsicなどを使った実装のPRは基本的にコアメンバに断られる。これはメンテナンスの観点からの理由である。記事執筆時点ではx86系アーキテクチャ(
AVX
、AVX512
など)Armv7とv8(NEON
)、MIPS(MSA
)、PowerPC(VSX
)に加えてRISC-V(RVV
)など多岐にわたるSIMD命令が用意されている。たしかにメンテナンスが大変そうである - ただし、メンテナンスが大変になっても生のSIMD intrinsicを受け付けてるモジュールがあり、それがdnnモジュールである。今回もdnnモジュールに
half
およびbfloat16
に対応した命令をdnnモジュールに追加するための前準備だった模様で、そのうち追加されるかもしれない。
その他
-
本記事は以下の環境で検証しました
- 主にDockerの
arm64v8/gcc:13.2
を使用 - コンパイラ GCC 13.2.0
- CMake 3.25.1
- OpenCV (git commit
e9f35610a54479eb170c101745cbd6bcc8e1d122
: 4.8.1と4.9.0の間) - OS Debian 12.2
- 主にDockerの
-
以下、記事執筆時点での話
- OpenCV内で命名されている拡張命令一覧は
NEON
、FP16
、NEON_FP16
と紛らわしいものがあるが、NEON
は、ArmアーキテクチャのSIMD命令のことであり、FP16
はfloat
とhalf
を変換/逆変換する2命令だけのことである。Armv8からは両者とも基本命令セットに含まれたので、特段指定する必要はない。FP16
は、全く同じ命令セットがAVXのサブセットにも含まれていたので、FP16
という一般的な名前になったが、NEON_FP16
はhalf
のまま算術演算できる命令のことであり、これはArm V8の拡張命令なので、NEON_FP16
とNEONの名を冠している。 - 本記事で紹介したビルドエラー以外にも、4.xブランチとARM64マシン上では
NEON_FP16
命令を有効にしなくてもリンク時点でDNNモジュールがNEON_FP16
実装を参照してしまい、リンカエラーが発生することが公式のビルドマシンで確認されている - メジャーに使われてるコンパイラとしてはGCCの他、ClangとVisual Studioが挙げられるが、現状のOpenCVのコードは、Aarch64用のコードをVisual Studioでビルドする想定に全くなってない
- x86_64の
AVX512
のサブセットにもAVX512_FP16
およびAVX512_ BF16
と呼ばれる、それぞれhalf
とbfloat16
での処理に対応した命令群があるが、現時点ではOpenCVの対応命令一覧に追加されていない(とは言えAVX512の命令体系は複雑っぽいので筆者が見落としている可能性もゼロではない)
- OpenCV内で命名されている拡張命令一覧は
その他詳細
# g++ --version
g++ (GCC) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# cat /etc/debian_version
12.2
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9b5da3fad9f arm64v8/gcc:13.2 "bash" 8 days ago Up 8 days laughing_mendeleev
# cmake --version
cmake version 3.25.1
$ git log
commit e9f35610a54479eb170c101745cbd6bcc8e1d122 (upstream/4.x, 159)
Merge: d296d29a1c 2830551e89
Author: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com>
Date: Fri Nov 24 14:16:43 2023 +0300
おわりに
- OpenCVに
bfloat16
命令をAarch64で使うための下地が整いつつあるが、実装はまだ追加されてないことを紹介した - 明日の発表はあのテクい説明で有名なdandelion先生である。みんな!正座して待機だよ!