libtorch mobile(pytorch 1.4) をビルドするメモ

## 背景

libtorch mobile(推論) を(Android, aarch64 IoT/Edge)向けにビルドしたい.

## 環境

* pytorch 1.4
  * `v1.4.0` などのバージョン名の branch を引くとよいです(`master` はよくコケるため). バージョンのついたブランチは, リリース後もそれなりにアップデートされている... ような?(が, tag を引いたほうがいいのかも?).

pytorch `master`(1.5 or later)はなんかうまくいかなくなったようです. もう少し成熟をまつ必要があります.

## 構成

* pytorch mobile: 今後の主流. Android, iOS 向け. IoT/Edge でも一応ビルドは通って動く模様. CPU 実装のみっぽい.
* caffe2 mobile: IoT/Edge 向け(GLSL とかで GPU が使える?). ただし deprecated のようで今後は pytorch mobile に統合されるのが予想される.

## ビルドスクリプト

`scripts/` フォルダにいくらかあります. 基本は,

```
$ ./scripts/build_mobile.sh
```

とかしてビルドすればそのままいけます.

`tools` にも python でのビルドスクリプトがありますが, 内部動作としては `scripts` とだいたい同じ様に cmake を叩いているようです.

### build_pytorch_android.sh, build_android.sh

build_android.sh :  libtorch .a まで.
build_pytorch_android.sh: Java binding で .aar(.so)まで作ります. 内部で build_android.sh を読んでいます.

詳細は以下を参照ください.

libtorch を Android でビルドするメモ
https://qiita.com/syoyo/items/360bcf15b9ee88eeeacd

### build_mobile.sh

host のコンパイラを使って libtorch をビルドします.
pytorch mobile(Aten+c10 ベース?) を PC Linux(x64, aarch64)で動かしたい, テストしたいときに役立ちます(PC x86-64 Linux, Jetson AGX(aarch64 linux) などでビルドできるのを確認しました).

caffe2 mobile は off(`BUILD_PYTORCH_MOBILE` 環境変数はセットされないときと同じ振る舞い)に設定されます.

```
$ ./scripts/build_mobile.sh
```

ビルドは Ryzen9 3950x で 2 分 30 秒くらいで終わります.

## mobile ビルド設定

mobile に関わるのは以下です.

* PYTORCH_BUILD_MOBILE(環境変数). Android では `build_pytorch_android` では有効にしている. iOS ではどちらでも. IoT など(`build_mobile.sh`)では使われていません. 環境変数がセットされているかどうかだけ見ているので, `PYTORCH_BUILD_MOBILE=0` としても on になります. 内部的には caffe2_mobile を使うかどうかの判定に使っています.
  * BUILD_CAFFE2_MOBILE(cmake option). deprecated です. PYTORCH_BUILD_MOBILE が有効のとき(`build_pytorch_android.sh` などのスクリプト)では `BUILD_CAFFE2_MOBILE` は off に設定されます. 

基本的には `PYTORCH_BUILD_MOBILE` をセットしておきます(つまり caffe2 mobile off)

### Android build

```
$ ./sctipts/build_android_pytorch.sh arm64-v8a
```

としてビルドします. .a だけほしいときは `scripts/build_android.sh` を呼びます(`PYTORCH_BUILD_MOBILE=1` は必須)

build_android_pytorch.sh.sh では, fbjni による Java binding も含んでビルドするので, C/C++ だけ使いたい場合には, build_android.sh で .a を生成したのち,
 
https://github.com/pytorch/pytorch/tree/master/android/pytorch_android 

を参照にリンクしたりする必要があります.

C++STL(`ANDROID_STL`) は, pytorch 側では `c++_static` を使っています. 
一応 `c++_shared` でも動作するのを確認しました(この場合は apk に libc++_shared.so 同梱が必要).

#### .a の循環参照

libtorch の .a のいくつかでは, シンボルの循環参照があります.
pytorch repo の `<pytorch>/android/pytorch_android` の CMakeListst.txt にあるように

```
  function(import_static_lib name)
    add_library(${name} STATIC IMPORTED)
    set_property(
        TARGET ${name}
        PROPERTY IMPORTED_LOCATION
        ${CMAKE_CURRENT_LIST_DIR}/src/main/jniLibs/${ANDROID_ABI}/${name}.a)
  endfunction(import_static_lib)

  import_static_lib(libtorch)
  import_static_lib(libtorch_cpu)
  import_static_lib(libc10)
  import_static_lib(libnnpack)
  import_static_lib(libpytorch_qnnpack)
  import_static_lib(libeigen_blas)
  import_static_lib(libcpuinfo)
  import_static_lib(libclog)

    # Link most things statically on Android.
  target_link_libraries(pytorch_jni
      fbjni
      -Wl,--gc-sections
      -Wl,--whole-archive
      libtorch
      libtorch_cpu
      -Wl,--no-whole-archive
      libc10
      libnnpack
      libpytorch_qnnpack
      libeigen_blas
      libcpuinfo
      libclog
  )
```


`-Wl,--whole-archive` のリンカフラグで, シンボル全体を libtorch.a から取り込むようにして解決するようにします.
(.a を複数回指定するという手もありますが)

静的ライブラリ (.a) の作成と利用
https://www.yunabe.jp/docs/static_library.html

## symbol の strip

Android cmake toolchain では, `-g` オプションが常に追加されるので, .a や .so が巨大になります
(libtorch.a は 1.2GB! `ANDROID_DEBUG_SYMBOLS`(env var) が用意されていますが, これが off でも 1.0 GB くらい)

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Super awesome tool! 🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🙏🙏🙏🙏🙏🙏🙏🙏 I got nice summary of 1.2 GB of libtorch.a with bloaty!!! <a href="https://t.co/qc1KdkwbVj">pic.twitter.com/qc1KdkwbVj</a></p>&mdash; Syoyo Fujita 🌸 RayWa(Ray and Peace) (@syoyo) <a href="https://twitter.com/syoyo/status/1225124096742047744?ref_src=twsrc%5Etfw">February 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

bloaty による .a などのオブジェクトバイナリ解析ツール便利ですね.

.so から .aar を作る gradle では, relese 時 symbol の strip が行われるようで, 最終的に .aar に含まれる .so は各アーキテクチャで 20 MB くらいに減ります.

必要に応じて  .a, .so からデバッグシンボルを strip しておきましょう(host の `strip` ではアーキテクチャ違って適用できないので, NDK の cross-compile 用の gcc の strip を使います)

## サンプル cmake

.a の libtorch mobile をリンクする cmake サンプルを作りました. Windows には対応していません.

https://github.com/syoyo/libtorch_mobile_build

## まとめ

スクリプトの内容は, 基本 CMake だけでビルドできるようになっているので, スクリプトを解読したのち, 自身の利用に合わせてカスタマイズしてみましょう.