LoginSignup
6
3

OpenCV bfloat16のサポート始めるってよ

Last updated at Posted at 2023-12-11

はじめに

  • 本記事はOpenCV Advent Calendar 2023 12日目の記事です。
  • その他の記事は目次をご覧ください。
  • 最近は仕事に追われてOpenCVのpull requestをちゃんと追ってなかったのだが、このカレンダーの主催者熊太郎さんのツイートで気づいた。
  • このissueOpenCVをAarch64上で -DCPU_BASELINE="NEON_FP16;NEON_DOTPROD;NEON_BF16"と指定してビルドするとコンパイルが通らないというissueである。

  • 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_FP16NEON_BF16NEON_DOTPRODの3つを同時に指定するとコンパイルエラーになるというものである。
  • なお、NEON_FP16NEON_BF16はそれぞれ名前が紛らわしいが、NEON_FP16fp16つまり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系列のブランチでは、NEONFP16CPU_BASELINE、つまり必須要件としてビルドされ、NEON_DOTPRODNEON_FP16NEON_BF16の3つのフラグはそれぞれCPU_DISPATCH、つまり実行時にCPUの対応命令を把握して実行するかどうかを判断する命令としてビルドされる
  • NEON_BF16 (0 files) は、フラグは有効になったものの、このオプションを使うファイルは存在しないとなっており、bfloat16命令を使った実装はまだ存在しない
  • 参考までに、コンパイラがこのオプションに対応していない場合も考えられる。筆者が調べた限りだと、GCC 10系列から+bf16オプションが有効になった模様で、それ以前のコンパイラでOpenCVをビルドすると、そもそもNEON_BF16がこの一覧に現れない
  • 過去のdispatchを使った記事でも触れているが、OpenCVのSIMD命令を使った実装は、intrinsicなどを使った実装のPRは基本的にコアメンバに断られる。これはメンテナンスの観点からの理由である。記事執筆時点ではx86系アーキテクチャ(AVXAVX512など)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
  • 以下、記事執筆時点での話

    • OpenCV内で命名されている拡張命令一覧はNEONFP16NEON_FP16と紛らわしいものがあるが、NEONは、ArmアーキテクチャのSIMD命令のことであり、FP16はfloatとhalfを変換/逆変換する2命令だけのことである。Armv8からは両者とも基本命令セットに含まれたので、特段指定する必要はない。FP16は、全く同じ命令セットがAVXのサブセットにも含まれていたので、FP16という一般的な名前になったが、NEON_FP16halfのまま算術演算できる命令のことであり、これは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の命令体系は複雑っぽいので筆者が見落としている可能性もゼロではない)
その他詳細
# 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先生である。みんな!正座して待機だよ!
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3