2023/01/06

x86-64-v2, x86-64-v3, x86-64-v4のコンパイラ(GCC,Clang,rustc,Cargo)からの最適化指定

世代の新しいx86_64のCPUには新しい拡張命令セットが追加されており、その拡張命令セットの集まりを段階的に上乗せしたレベル(x86-64-v[234])が仕様として後に定義された。

あるレベルに対して最適化した機械語の実行ファイルをビルドすることにより、そのレベル以上に対応するプロセッサ環境上でのプログラム実行速度が若干改善できるようになった。

一方で、新しい世代のプロセッサでの実行速度改善と引き換えに古い世代のプロセッサのサポートを切り捨てる動きも少しずつ出始めている。

  1. x86-64-v2からx86-64-v4までの/proc/cpuinfo上のフラグ名との対応
  2. GCCやClangでの実装と指定
  3. rustcでの指定

x86-64-v2からx86-64-v4までの/proc/cpuinfo上のフラグ名との対応

アーキテクチャのレベル拡張命令セット群のフラグ名(全部が必要)
x86-64cmov, cx8, fpu, fxsr, syscall, sse, sse2
x86-64-v2 (Level A)cx16, lahf_lm, popcnt, pni, sse4_1, sse4_2, ssse3
x86-64-v3 (Level C)avx, avx2, bmi1, bmi2, f16c, fma, abm, movbe, xsave
x86-64-v4 (Level D)avx512f, avx512bw, avx512cd, avx512dq, avx512vl
  • Red Hat Enterprise Linux (RHEL) 9の要件として知られているレベルはx86-64-v2で、公開された2022年5月からみて10年程度が発売から経過したプロセッサでも動作するものとなっている
  • “Level B” (Level A + AVX)はGCCでもClangでもrustcでも指定可能なレベルとしては存在しない
    • GCCやClangでは-march=x86-64-v2 -mavxのようにできる
    • rustcでは-C target-cpu=x86-64-v2 -C target-feature=+avxとして指定できると思われるが、意図した通りに最適化されているかは未確認
  • 仕様としては、x86-64-v3はAVX2などを含むHaswell世代の拡張命令セット群までとなっている: https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/77566eb03bc6a326811cb7e9a6b9396884b67c7c

手元のAthlon 220GEで/proc/cpuinfoを参照したものとカーネルソース(arch/x86/include/asm/cpufeatures.harch/x86/boot/cpustr.h)から調べたものから作成したが、中身が100%正しいという保証はできない。

Intel CPUではフラグと機能の対応に細かい違いがあるかもしれないが、本記事作成時点ではIntel CPUが動作する環境がないので確認できない。

GCCやClangでの実装と指定

  • GCC(11系以上で対応)の実装について: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97250
  • Clang(12系以上で対応)の実装について: https://github.com/llvm/llvm-project/issues/47030
  • Clangのマニュアルにてレベルごとの拡張命令セットの対応が具体的に参照できる: https://clang.llvm.org/docs/UsersManual.html#x86

-march=オプションにおける指定値はいずれもx86-64-v[234]形式となる(例:-march=x86-64-v3)。

rustcでの指定

Ubuntu 22.10上のrustcの1.64系の時点では-C target-cpu=x86-64-v[234]形式での指定が可能。

Rustの過去のリリースの更新点などを確認してみたが、いつから対応したのかは不明で、機械語生成を担うLLVMライブラリが12系(Clang 12が対応したのと同時期)以上なら対応しているという可能性は高そうだが、その辺りには詳しくないので、断定は避ける。

(rustcのバージョンを表示)
$ rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)

(LLVMのバージョンを表示)
$ llvm-config --version
15.0.2

(x86 LinuxのターゲットCPU一覧から "x86-64" を含むものを表示)
$ rustc --target=i686-unknown-linux-gnu --print target-cpus | grep x86-64
    x86-64
    x86-64-v2
    x86-64-v3
    x86-64-v4

(x86_64 LinuxのターゲットCPU一覧から "x86-64" を含むものを表示)
$ rustc --target=x86_64-unknown-linux-gnu --print target-cpus | grep x86-64
    x86-64
    x86-64-v2
    x86-64-v3
    x86-64-v4

実際にはCargoでビルドが行われる場合が多いので[ホームディレクトリ]/.cargo/configを書き換える。

下はx86-64-v3を指定する例。

[一部]ファイル名:~/.cargo/config

[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-cpu=x86-64-v3"]