ROCm pytorch をコンパイルする試み(2018 年 7 月 27 日時点 実行時 seg fault)

pytorch を ROCm でコンパイルにチャレンジします.

## 2018 年 7 月 27 日時点まとめ

乱数ライブラリ(hcrng)の対応が過渡期のためか, いろいろ頑張ってコンパイルはできても, strace で確認すると乱数ライブラリを呼ぶところで seg fault することがわかりました.
しばらく様子見ですね.

https://github.com/ROCmSoftwarePlatform/pytorch/pull/41
https://github.com/ROCmSoftwarePlatform/pytorch/pull/80

以下, メモ...

Ubuntu 16.04 + ROCm 1.8.2 で環境を整えているものとします.

途中まではうまく行きますが, `caffe2/proto/caffe2.pb.h` ヘッダが見つからないというエラーがでて, うんうんと悩んで原因調べてみたところ, CMake generator expression の挙動でうまくコンパイルできないことがわかりました.

pytorch repo の `cmake/Dependency.cmake` で

```
    set(Caffe2_HIP_INCLUDES
      ${hip_INCLUDE_DIRS} ${hcc_INCLUDE_DIRS} ${hsa_INCLUDE_DIRS} ${rocrand_INCLUDE_DIRS} ${hiprand_INCLUDE_DIRS} ${rocblas_INCLUDE_DIRS} ${miopen_INCLUDE_DIRS} ${thrust_INCLUDE_DIRS} $<INSTALL_INTERFACE:include> ${Caffe2_HIP_INCLUDES})
```

というのがあります.

ここの `$<INSTALL_INTERFACE:include>` は CMake generator expression であり,

https://cmake.org/cmake/help/v3.2/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES.html

基本的には特定の cmake コマンド(e.g. `target_include_directories`)でしか値が展開できないようになっています(他の環境では値が展開されません(要検証))

pytorch rocm ポートでは, HIP でコンパイルするために, hip_include_directories() に include path を渡し, それを外部コマンドへの引数に渡しているため, ここで問題があります.

apt で入る HIP(`/opt/rocm/hip`) はいくらか古いため, `opt/rocm/hip/cmake/FindHIP.cmake` において, `hip_include_directories` の定義は以下のようになっています.

```
macro(HIP_INCLUDE_DIRECTORIES)
    foreach(dir ${ARGN})
        list(APPEND HIP_HIPCC_INCLUDE_ARGS_USER -I${dir})
    endforeach()
endmacro()
```

generator script は展開されると空文字になるため(要検証), これだと

`-I$<INSTALL_INTERFACE:include> -I/path/to/protobuf/src`

となり, これが展開されて HIP コンパイラには以下が渡ります.

`-I -I/path/to/protobuf/src`

ここで, gcc/clang では, 空の `-I` があると, 後続の `-I/path/to/protobuf/src` をサーチパスとして認識する挙動になっているため,  これにより `/path/to/protobuf/src` にパスが通らずにコンパイルが失敗します.

では, `Caffe2_HIP_INCLUDES` から `$<INSTALL_INTERFACE:include>` を除けばいいのかといういうと, 実際には gloo や gtest などの submodule でも include path に `$<INSTALL_INTERFACE:include>` を使うような cmake 記述があり, pytorch/caffe2 ではこれらの変数も参照していますから, FindHIP をやはり修正しないとダメです.

最新の FindHIP.cmake をみますと

https://github.com/ROCm-Developer-Tools/HIP/blob/master/cmake/FindHIP.cmake#L290

```
list(APPEND HIP_HIPCC_INCLUDE_ARGS_USER $<$<BOOL:${dir}>:-I${dir}>)
```

となり, この generator expression による空の `-I` 問題が解決されています.

pytorch の Docker + Jenkins ビルドスクリプトでは, そのための work around の記述があります.

https://github.com/pytorch/pytorch/blob/master/docker/caffe2/jenkins/common/install_rocm.sh#L39

build cofiguration についても修正がありますね.

https://github.com/pytorch/pytorch/blob/master/docker/caffe2/jenkins/common/install_rocm.sh#L45

(2018 年 7 月 27 日時点, ここまで)

以下, 作業メモ...

## 環境

* (mini)conda
* その他, で apt や pip パッケージを入れているとします

Docker 用のビルドスクリプト

https://github.com/pytorch/pytorch/blob/master/docker/caffe2/jenkins/common/install_rocm.sh

を参考にします.

pytorch 本家での CI サーバのビルド状態も確認しておきましょう.

py2-clang3.8-rocmnightly-ubuntu16.04-test など, rocmnightly の文字があるターゲットになります.

https://ezyang.github.io/pytorch-ci-hud/build/pytorch-master


hipblas https://github.com/ROCmSoftwarePlatform/hipBLAS を入れます.

```
$ sudo apt-get install hipblas
```

ROCm(HIP)版 Thrust をインストールします.

https://github.com/ROCmSoftwarePlatform/Thrust

apt package は無いので git clone. /opt/rocm/Thrust にインストールするものとします. Thrust はヘッダファイル集なのでビルドは不要です.
cub-hip が submodule になっているので, 忘れずに submodule も checkout しておきます.


```
$ git clone --recursive https://github.com/ROCmSoftwarePlatform/Thrust
$ sudo mv Thrust /opt/rocm/ 
```

https://github.com/ROCmSoftwarePlatform/pytorch

を clone します.

```
$ git clone --recursive https://github.com/ROCmSoftwarePlatform/pytorch pytorch-rocm
```

(pytorch オフィシャルの repo だと, hcrnd, hcsparse が見つからないとエラーがでま)

python の環境を作ります. 今回は 3.6 にしました.

```
$ conda create -n py36 python=3.6
$ source activate py36
```

前処理として, hippify(CUDA コードの HIP 化?)をします.

```
(py27) $ cd pytorch-rocm
(py27) $ python tools/amd_build/build_pytorch_amd.py
```

1 分くらいかかりますのでしばらく待ちます(プログレスが出ないので少し不安になります)

一度実行していると, すでに patch が当たっていて fail するなどのエラーが出ます(変換自体には影響無い模様) 再実行するときは一度 git checkout しておくとよいかもしれません.

ROCm を on にしてビルドします.

USE_ROCM=1 を指定しないと, caffe2 自体は ROCm(HIP) 環境が見つかれば ROCm(HIP) 有効でビルドされますが, python binding のほうが ROCm 有効にならず, `torch.cuda.is_available()` が false になってしまうので注意ください.


```
$ USE_ROCM=1 python setup.py install
```

`libcaffe2_hip.so` のリンクにめちゃめちゃ時間がかかります. Ryzen2700X で 10~20 分くらい.
(ROCm clang `opt` で最適化しているようですが, その最適化処理に時間がかかっています)

再ビルドでもなぜか hip カーネル周り再コンパイルになってつらい(hipcc を外部コマンドで呼び出すところはキャッシュされないから?)

setup_caffe2.py もありますが, これは少なくとも ROCm ビルドでは `setup.py` から呼ばれるのを想定するようで, setup_caffe2.py で caffe2 だけビルドしようとしてもうまくいきませんでした.

## 実行時セットアップ

libcaffe2_hip.so は HIP clang でコンパイルされていますが, `libcxx` がデフォルトになっているようで, `import torch` とすると,

```
ImportError: libc++.so.1: cannot open shared object file: No such file or directory
```

と出ます...

`libc++.so` は rocm パッケージ(prebult HIP パッケージ)に含まれていないので, 自前ビルドします...
(もしかしたら llvm.org で配布している prebuilt package でも動くかもしれません)

ROCm 1.8.2 の hcc では clang 7(ToT, top of trunk) ですが, 6.0.1 release でも大丈夫のようです.

http://releases.llvm.org/download.html#6.0.1

libcxx, libcxxabi をインストールしておきます.
(default の /usr/local に install すると仮定. clang++/clang は hcc の clang++(e.g. /opt/rocm/hcc/clang++) を仮定)

```
$ cd libcxx
$ mkdir build
$ cd build
$ CXX=clang++ CC=clang cmake ..
$ make
$ (sudo) make install
```

```
$ cd libcxxabi
$ mkdir build
$ cd build
$ CXX=clang++ CC=clang cmake -DLIBCXXABI_LIBCXX_INCLUDES=/usr/local/include/c++/v1 ..
$ make
$ (sudo) make install
```

インストールした先の lib ディレクトリを `LD_LIBRARY_PATH` に設定しておきます.

```
import torch
torch.cuda.is_available()
```

で True が返れば成功です!

## TODO

pytorch の ROCm ビルド手順ドキュメントが無くてかなしい.
(web とか Jenkins CI のスクリプトを見るしかない)

https://github.com/RadeonOpenCompute/hcc/tree/roc-1.8.x 

の ThinLTO でリンクが早くなるかしら?

```
export KMTHINLTO=1
```