Vertex Pipelines で lightfm が「時々」status 132 で exit して困った
(Vertex Pipelines とタイトルに入れているものの VertexPipelines に固有の問題ではないです)
状況
Vertex Pipelines 上で実行される機械学習パイプラインを作成していた。
機械学習パイプラインでは lightfm
を使用して機械学習モデルの訓練を行っていた。
Vertex Pipelines 上で機械学習モデルの訓練を行う際(=LightFM#fit_partial
をコールした際)に時々以下のようなエラーを吐いて落ちてしまっていた。
The replica workerpool0-0 exited with a non-zero status of 132.
Vertex Pipelines 用のコンテナイメージは Github Action で作成していた。
コンテナイメージを作成する際に使用した Dockerfile
は以下のようになっていた。
# Dockerfile
FROM python:3.7
WORKDIR /app
COPY . /app
RUN pip install lightfm==1.16
(この場合の)解決策
ほぼ同じ状況で困っている人が Issue を立ててくれていて、しかも解決策について議論されていた。lightfm
を pip install
でインストールする前に環境変数に LIGHTFM_NO_CFLAGS=1
を設定しておけば良いとのこと。
上記の Dockerfile
の場合、以下のように書き換えれば良いです。
# Dockerfile (修正版)
FROM python:3.7
WORKDIR /app
COPY . /app
RUN LIGHTFM_NO_CFLAGS=1\
pip install -r requirements.txt
これでめでたく lightfm
が想定通り動いてくれるようになりました。
よかった😊。
解決したけど...🤔
とりあえず解決はしたものの、何故コレが起きたのか、何故上記の方法で解決できたのかがいまいち分かりませんでした。
今まであまり遭遇したことがないタイプのエラーだったので、勉強になりそうだな〜と思いこの事象について少しだけ詳しく調べてみることにしました。
以降では今時点で自分が理解した内容をまとめてあります (あまりこのあたりの内容に明るくないので間違っていたら教えていただけると嬉しいです🙇♂️)。
exit status 132
とはなんぞや
exit status
は以下のようにマッピングされるらしい。
exit status number: 128+n
→ Fatal error signal: n
ref:https://tldp.org/LDP/abs/html/exitcodes.html
今回の場合、132
は 128 + 4
なので、エラーシグナル 4
ということだと思われる。
またエラーシグナル 4
は Wikipedia 曰く SIGILL
を意味するらしい。ref:https://ja.wikipedia.org/wiki/シグナル_(Unix)
じゃあ SIGILL
は何かというとこちらも Wikipedia 曰く、
通常、命令でないメモリ領域にジャンプしたときに発生(コールスタックのリターンアドレスが破壊されたときなど)。他に特権レベルが高くないと実行できない命令を実行しようとしたときなどにも発生する。
とのこと。
ひとまず、コンピュータが処理できないおかしな命令を出しちゃった時に 132
が吐かれる、くらいの理解で先に進んでみる。
Issue のコメント
次は Issue のコメントを見ていこうと思います。
Hi,
if you clone and install via pip install . -vv you can see the flags in the end of a line that usually starts with "gcc". For example, mine looks like thisgcc -pthread -B /home/simon/miniconda3/envs/test2/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/simon/miniconda3/envs/test2/include/python3.7m -c lightfm/_lightfm_fast_openmp.c -o build/temp.linux-x86_64-3.7/lightfm/_lightfm_fast_openmp.o -ffast-math -march=native -fopenmp
There, you see that
-ffast-math -march=native -fopenmp
were used. In my experienceffast-math
is often problematic. You can avoid it by setting an environment variable, i.e.export LIGHTFM_NO_CFLAGS=1 && python -m pip install . -vv
This is the relevant line in setup.py
Alternatively, you can install the pre-compiled package from conda-forge where we also compile withoutffast-math
.
gcc
、-ffast-math
etc. 耳馴染みのない単語たちだ...
一つずつ見ていきます。
gcc
ソースコードをコンパイルする際に使えるやつ、くらいの理解で一旦先に進んでみます。
-ffast-math
-march=native
-fopenmp
どれもコンパイル時の最適化オプションらしい。
LIGHTFM_NO_CFLAGS
Issue のコメントにある通り setup.py
内で環境変数 LIGHTFM_NO_CFLAGS
を参照している。LIGHTFM_NO_CFLAGS
が設定されている場合のみ -ffast-math
と -march=native
(Anaconda でない場合)をコンパイルオプションに設定しているみたい。
pip install
についている -vv
というオプション
(今回の問題とはあまり関係ないです。)
このオプション実は初めて見ました。
インストール中のログをどの程度詳しく表示するか指定するためのオプションなんですね。
ちなみに additive
なオプションとのことで 3 つまで増やせる (-vvv
) らしい。
additive
なオプションというのも実は初めて見ました。そういうオプションもあるんだね。
デバッグの際に便利だ。
ということは多分...
以下のような流れで今回の問題が発生したのかもしれない。
GithubActions で pip install
する際に lightfm
が GithubActions のマシンの CPU アーキテクチャに対して最適化される。
GithubActions で使用したマシンと実際にプログラムが実行されるマシンの CPU アーキテクチャが異なっている場合がある。
このような場合、 GithubActions のマシンに対して最適化された lightfm
は実際にプログラムが実行されるマシンでうまく動かない場合がある。
感想
最初にこの現象に遭遇した時、132
で exit
したよ、という見慣れないエラーメッセージだけしかログに残っておらず、これじゃあ何もわからんよ、と思った。
けど落ち着いてググってみると(いつも遭遇するその他の問題と同じように)ちゃんと理解できる(気になれる)ことが分かって良かった。
Discussion