Posted at

Homebrew on Linuxでターゲットアーキテクチャを指定する


はじめに

Linux環境でHomebrewを使うとき、世代の異なるCPUのマシン同士でhomeを共有していたりするとHomebrewでインストールしたコマンドがうまく動かないことがあります。

例えば、Skylake世代のマシンでbrew install python3として、Nehalem世代のマシンでpython3とすると

[1]    106302 illegal hardware instruction (core dumped)  python3

となります。

Nehalemは10年前なので多少はしょうがない感じもしますが、Skylake vs Ryzen Threadripperでも同様なのでさすがに困ります。

macOSでも原理的には同じことが起こるはずですが、homeを共有したりするような使い方がされないということか、あまり問題になっていないようです。


原因

現在のHomebrewはLinux環境下では、バイナリ生成時にコンパイラに渡す-marchオプションが-march=nativeとなっています。-marchオプションはターゲットのCPUアーキテクチャを指定するオプションで、これによってアーキテクチャ固有の命令が使われたりします。

-march=nativeを指定した場合、コンパイルを実行したマシンのアーキテクチャが選択されます。そのためSkylake世代のマシンでbrew installすると-march=skylake相当でコンパイルされ、最近のIntel CPU固有の命令が入ったバイナリが生成されて古いマシンでエラーになる、ということです。


対策

アーキテクチャを指定する環境変数HOMEBREW_ARCHを設定した場合に-marchの値を上書きできるようなPRを出して取り込んでもらいました。

(実は同様のPRがLinuxbrew時代に一度マージされているのですが、Homebrew本家に合流するタイミングでrevertされたまま放置されていたようです)

「はじめに」で挙げた環境ならbrew updateで最新版を取り込んで

export HOMEBREW_ARCH=nehalem

とすれば、以降はコンパイル時に-march=nehalemとなってSkylakeでもNehalemでも実行可能なバイナリが生成されます。(もちろんSkylakeに最適化されたものよりは遅くなるはずですが)

指定可能なmarchの一覧は例えばx86系なら以下にあります。

https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-march-14