Linux: 動的ローダーへの問い合わせ

glibc 2.33 以降 (ldd --version でバージョンがわかります) では、動的ローダーが、どの x86-64 レベル向けに最適化されたライブラリを読み込むかを報告します。

$ /lib64/ld-linux-x86-64.so.2 --help | grep -A4 'glibc-hwcaps directories'
Subdirectories of glibc-hwcaps directories, in priority order:
  x86-64-v4
  x86-64-v3
  x86-64-v2 (supported, searched)

supported と表示された最も高いレベルが、そのマシンのレベルです。上の出力は、x86-64-v2 にあたる QEMU の Nehalem CPU モデルで Debian 13 のローダーを実行して記録したものです。どのレベルにも印が付いていなければ、QEMU の qemu64 モデルのときのように、マシンは x86-64-v1 のベースラインです。

C ライブラリがプロセッサーの持つレベルより高いレベルを必要とするシステムでは、ローダーは Fatal glibc error: CPU does not support x86-64-v2 のようなメッセージを出してすぐに停止します。このメッセージや似たメッセージについてはエラーメッセージで説明しています。

Linux: x86-64-level スクリプトの実行

このサイトの x86-64-level スクリプトは glibc が古い環境でも動き、次のレベルに何が足りないかも示します。/proc/cpuinfo を読み、POSIX シェルだけを必要とします。ダウンロードし、中身を読んでから実行してください。

curl -fsSLO https://x86-64.net/x86-64-level
less x86-64-level
sh x86-64-level -v
x86-64-v2
missing for x86-64-v3: avx avx2 bmi1 bmi2 f16c fma abm movbe xsave

これは、x86-64-v2 の機能をすべて備え、x86-64-v3 の機能を 1 つも持たないプロセッサーでの出力で、サイトのテストで確認しています。

-q は数字だけを表示するので、スクリプトの中で使えます。出力に含まれる名前は Linux のフラグ名で、各レベルのページでそれらを機能に対応付けています。

x86-64-level のソースコード
#!/bin/sh
# x86-64-level: print the x86-64 microarchitecture level (x86-64-v1 to
# x86-64-v4) of this machine's processor, as Linux reports it.
#
# Documentation: https://x86-64.net/check/
# SPDX-License-Identifier: MIT
#
# Usage: x86-64-level [-q] [-v]
#   -q  print only the level number (1 to 4)
#   -v  also list the features missing for the next level
#
# X86_64_LEVEL_CPUINFO names a file to read instead of /proc/cpuinfo.
set -eu

# Linux flag names of the features each level adds, following the x86-64
# psABI. OSFXSR and OSXSAVE have no flags; Linux clears the AVX family
# flags when the OS has not enabled XSAVE, so the lists below cover them.
LEVEL1="cmov cx8 fpu fxsr mmx syscall sse sse2"
LEVEL2="cx16 lahf_lm popcnt pni sse4_1 sse4_2 ssse3"
LEVEL3="avx avx2 bmi1 bmi2 f16c fma abm movbe xsave"
LEVEL4="avx512f avx512bw avx512cd avx512dq avx512vl"

quiet=0
verbose=0
for arg in "$@"; do
	case $arg in
	-q) quiet=1 ;;
	-v) verbose=1 ;;
	-h | --help)
		sed -n '2,13s/^# \{0,1\}//p' "$0"
		exit 0
		;;
	*)
		echo "x86-64-level: unknown option: $arg" >&2
		exit 2
		;;
	esac
done

cpuinfo=${X86_64_LEVEL_CPUINFO:-/proc/cpuinfo}
if [ ! -r "$cpuinfo" ]; then
	echo "x86-64-level: cannot read $cpuinfo (this script needs Linux)" >&2
	exit 1
fi

# All CPUs report the same flags; the first "flags" line is enough.
flags=$(sed -n 's/^flags[[:space:]]*:[[:space:]]*//p' "$cpuinfo" | head -n 1)
if [ -z "$flags" ]; then
	echo "x86-64-level: no x86 flags line in $cpuinfo" >&2
	exit 1
fi

missing() {
	for flag in $1; do
		case " $flags " in
		*" $flag "*) ;;
		*) printf ' %s' "$flag" ;;
		esac
	done
}

level=0
lack=
for n in 1 2 3 4; do
	eval "wanted=\$LEVEL$n"
	lack=$(missing "$wanted")
	[ -n "$lack" ] && break
	level=$n
done

if [ "$level" -eq 0 ]; then
	echo "x86-64-level: not even the x86-64 baseline; missing:$lack" >&2
	exit 1
fi

if [ "$quiet" -eq 1 ]; then
	echo "$level"
else
	echo "x86-64-v$level"
fi
if [ "$verbose" -eq 1 ] && [ "$level" -lt 4 ]; then
	echo "missing for x86-64-v$((level + 1)):$lack"
fi

Linux: フラグの直接確認

すべての機能には /proc/cpuinfo のフラグがあり、各レベルのページに載っています。たとえば、プロセッサーが x86-64-v3 のフラグのうちどれを持っているかを見るには次のようにします。

grep -m1 -o -w -E 'avx|avx2|bmi1|bmi2|f16c|fma|abm|movbe|xsave' /proc/cpuinfo | sort -u

いくつかのフラグは機能と異なる名前を持ちます。SSE3 は pni、LZCNT は abm、CMPXCHG16B は cx16、LAHF/SAHF は lahf_lm です。

コンテナーと仮想マシン

コンテナーは自分が動いているマシンのカーネルを共有するので、このページのどの方法も、コンテナーの中からでも外からでも、そのマシンのプロセッサーを報告します。

仮想マシンは違います。ゲストに見えるのはハイパーバイザーが提示するプロセッサーモデルで、多くの場合、新しい機能は隠されています。VM の中で確認すると VM 内のソフトウェアが使えるレベルがわかり、それはホストのレベルより低いことがあります。QEMU、libvirt、Proxmox、VMware、Hyper-V でモデルを変える方法は仮想マシンでのレベルで説明しています。

Windows

Microsoft の Coreinfo はプロセッサーの機能を一覧表示します。coreinfo64 -f を実行し、* が付いた行をレベルの表と比べてください。Coreinfo は、LAHF-SAHF、CX16、MOVBE、F16C、AVX-512 の各サブセットを含め、レベルに必要なすべての機能を報告します。

PowerShell 7.4 以降では .NET に問い合わせることもできます。.NET は、プロセッサーと Windows の両方がサポートしているものを報告します。.NET には MOVBE、F16C、CMPXCHG16B、LAHF/SAHF のプロパティがないので、結果はかなり近い近似になります。次の内容を level.ps1 として保存し、pwsh ./level.ps1 を実行してください。

using namespace System.Runtime.Intrinsics.X86
# PowerShell 7.4 or later. .NET does not report MOVBE, F16C,
# CMPXCHG16B or LAHF/SAHF, so this is a close approximation.
$v2 = [Sse3]::IsSupported -and [Ssse3]::IsSupported -and
      [Sse41]::IsSupported -and [Sse42]::IsSupported -and [Popcnt]::IsSupported
$v3 = $v2 -and [Avx2]::IsSupported -and [Bmi1]::IsSupported -and
      [Bmi2]::IsSupported -and [Fma]::IsSupported -and [Lzcnt]::IsSupported
$v4 = $v3 -and [Avx512F]::IsSupported -and [Avx512BW]::IsSupported -and
      [Avx512CD]::IsSupported -and [Avx512DQ]::IsSupported -and [Avx512F+VL]::IsSupported
if ($v4) { 'x86-64-v4' } elseif ($v3) { 'x86-64-v3' } elseif ($v2) { 'x86-64-v2' } else { 'x86-64-v1' }

macOS

Intel プロセッサーを搭載した Mac では、カーネルがプロセッサーの機能を一覧表示します。

sysctl -n machdep.cpu.features machdep.cpu.leaf7_features machdep.cpu.extfeatures

名前をレベルの表と比べてください。macOS はいくつかの名前を独自に綴ります。たとえば AVX は AVX1.0、CMPXCHG16B は CX16、LAHF-SAHF は LAHF です。

C と C++ からの確認

GCC 12 と Clang 18 は __builtin_cpu_supports でレベル名を受け付けます。この関数は、実行中のプロセッサーとオペレーティングシステムを確認します。

#include <stdio.h>

int main(void)
{
	/* GCC 12 or later, Clang 18 or later */
	if (__builtin_cpu_supports("x86-64-v4"))
		puts("x86-64-v4");
	else if (__builtin_cpu_supports("x86-64-v3"))
		puts("x86-64-v3");
	else if (__builtin_cpu_supports("x86-64-v2"))
		puts("x86-64-v2");
	else
		puts("x86-64-v1");
	return 0;
}

これは、ベースライン向けにビルドしたプログラムが、実行時により速いコードパスを選ぶための方法でもあります。

出典

  1. glibc 2.33 release notes (glibc-hwcaps)
  2. GCC 12 release notes
  3. LLVM change D158811: __builtin_cpu_supports x86-64 levels
  4. Coreinfo (Microsoft Sysinternals)
  5. System.Runtime.Intrinsics.X86 namespace (.NET)
  6. XNU xnu-12377.121.6: osfmk/i386/cpuid.c (the feature names of machdep.cpu)