Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/tests_archdetect.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
strategy:
matrix:
proc_cpuinfo:
- x86_64/intel/graniterapids/AWS-Rocky9-6975PC
- x86_64/intel/graniterapids/Azure-Ubuntu24-6973P-C
- x86_64/intel/haswell/archspec-linux-E5-2680-v3
- x86_64/intel/sapphirerapids/AWS-Rocky8-8488C
- x86_64/intel/skylake_avx512/archspec-linux-6132
Expand Down Expand Up @@ -50,6 +52,9 @@ jobs:
export EESSI_MACHINE_TYPE=${{matrix.proc_cpuinfo}}
export EESSI_MACHINE_TYPE=${EESSI_MACHINE_TYPE%%/*}
export EESSI_PROC_CPUINFO=./tests/archdetect/${{matrix.proc_cpuinfo}}.cpuinfo
if [[ -f ./tests/archdetect/${{matrix.proc_cpuinfo}}.cpu_features ]]; then
export EESSI_CPU_FEATURES_FILE=./tests/archdetect/${{matrix.proc_cpuinfo}}.cpu_features
fi
# check that printing of best match works correctly
CPU_ARCH=$(./init/eessi_archdetect.sh cpupath)
if [[ $CPU_ARCH == "$( cat ./tests/archdetect/${{matrix.proc_cpuinfo}}.output )" ]]; then
Expand Down
19 changes: 10 additions & 9 deletions init/arch_specs/eessi_arch_x86.spec
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# x86_64 CPU architecture specifications
# The overview at https://github.com/InstLatx64/InstLatX64_Misc/tree/main/SIMD_Euler may be helpful in defining this list
# Software path in EESSI | Vendor ID | List of defining CPU features
"x86_64/intel/haswell" "GenuineIntel" "avx2 fma" # Intel Haswell, Broadwell
"x86_64/intel/skylake_avx512" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl" # Intel Skylake
"x86_64/intel/cascadelake" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_vnni" # Intel Cascade Lake
"x86_64/intel/icelake" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_vnni avx512_vbmi2" # Intel Icelake Lake
"x86_64/intel/sapphirerapids" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_bf16 amx_tile" # Intel Sapphire/Emerald Rapids
"x86_64/amd/zen2" "AuthenticAMD" "avx2 fma" # AMD Rome
"x86_64/amd/zen3" "AuthenticAMD" "avx2 fma vaes" # AMD Milan, Milan-X
"x86_64/amd/zen4" "AuthenticAMD" "avx2 fma vaes avx512f avx512ifma" # AMD Genoa, Genoa-X
"x86_64/amd/zen5" "AuthenticAMD" "avx2 fma vaes avx512f avx512ifma avx512_vp2intersect avx_vnni movdiri" # AMD Turin
"x86_64/intel/haswell" "GenuineIntel" "avx2 fma" # Intel Haswell, Broadwell
"x86_64/intel/skylake_avx512" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl" # Intel Skylake
"x86_64/intel/cascadelake" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_vnni" # Intel Cascade Lake
"x86_64/intel/icelake" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_vnni avx512_vbmi2" # Intel Icelake Lake
"x86_64/intel/sapphirerapids" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_bf16 amx_tile" # Intel Sapphire/Emerald Rapids
"x86_64/intel/graniterapids" "GenuineIntel" "avx2 fma avx512f avx512bw avx512cd avx512dq avx512vl avx512_bf16 amx_tile amx_fp16" # Granite
"x86_64/amd/zen2" "AuthenticAMD" "avx2 fma" # AMD Rome
"x86_64/amd/zen3" "AuthenticAMD" "avx2 fma vaes" # AMD Milan, Milan-X
"x86_64/amd/zen4" "AuthenticAMD" "avx2 fma vaes avx512f avx512ifma" # AMD Genoa, Genoa-X
"x86_64/amd/zen5" "AuthenticAMD" "avx2 fma vaes avx512f avx512ifma avx512_vp2intersect avx_vnni movdiri" # AMD Turin
109 changes: 90 additions & 19 deletions init/eessi_archdetect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ LOG_LEVEL="WARN"
# Default result type is a best match
CPUPATH_RESULT="best"

# map CPU flag names from cpu_features to the ones used in /proc/cpuinfo
declare -A cpu_flags_name_map=(
[fma3]=fma
[avx512bitalg]=avx512_bitalg
[avx512vbmi2]=avx512_vbmi2
[avx512vnni]=avx512_vnni
[avx512vpopcntdq]=avx512_vpopcntdq
[sha]=sha_ni
)

timestamp () {
date "+%Y-%m-%d %H:%M:%S"
}
Expand Down Expand Up @@ -70,6 +80,50 @@ get_cpuinfo(){
grep -i "$cpuinfo_pattern" ${EESSI_PROC_CPUINFO:-/proc/cpuinfo} | tail -n 1 | sed "s/$cpuinfo_pattern//i"
}

# CPU specification of host system as reported by cpu_features
get_cpu_features(){
# Return the value from list_cpu_features for the matching key
# 1: string with key pattern

[ -z "$1" ] && log "ERROR" "get_cpu_features: missing key pattern in argument list"
cpu_features_pattern="^${1}\s*:\s*"

if [ ! -z ${EESSI_CPU_FEATURES_FILE} ]; then
cpu_features_output=$(cat ${EESSI_CPU_FEATURES_FILE})
elif command -v "list_cpu_features" >/dev/null 2>&1; then
cpu_features_output=$(list_cpu_features)
else
log "DEBUG" "cpu_features cannot be found"
return 1
fi

# /proc/cpuinfo uses space-separated flags and uses different names for some flags,
# so we reformat and rename things a bit here to make it easier to do the comparisons
cpu_features_reformatted=$(
while IFS= read -r line; do
if [[ $line =~ ^(flags[[:space:]]*:[[:space:]]*)(.*)$ ]]; then
prefix=${BASH_REMATCH[1]}
flags=${BASH_REMATCH[2]}

new_flags=""
IFS=',' read -ra flag_array <<< "$flags"

for flag in "${flag_array[@]}"; do
new_flags+=" ${cpu_flags_name_map[$flag]:-$flag}"
done

printf '%s%s\n' "$prefix" "${new_flags# }"
else
printf '%s\n' "$line"
fi
done <<< "${cpu_features_output}"
)

# case insensitive match of key pattern and delete key pattern from result
echo "${cpu_features_reformatted}" | grep -i "$cpu_features_pattern" | tail -n 1 | sed "s/$cpu_features_pattern//i"
return 0
}

check_allinfirst(){
# Return true if all given arguments after the first are found in the first one
# 1: reference string of space separated values
Expand All @@ -85,6 +139,30 @@ check_allinfirst(){
return 0
}

# Iterate over the supported CPU specifications to find which ones match and
# which one is the best match for the host CPU.
# Order of the specifications matters, the last one to match will be selected as best match.
# Uses machine_type, cpu_vendor and cpu_arch_spec from the calling context, and sets
# best_arch_match and all_arch_matches in the caller's scope.
find_arch_matches(){
# 1: space separated list of flags of the host system
local flags="$1"

# Default to generic CPU
best_arch_match="${machine_type}/generic"
all_arch_matches=$best_arch_match

for arch in "${cpu_arch_spec[@]}"; do
eval "arch_spec=$arch"
if [ "${cpu_vendor}x" == "${arch_spec[1]}x" ]; then
# each flag in this CPU specification must be found in the list of flags of the host
check_allinfirst "$flags" ${arch_spec[2]} && best_arch_match=${arch_spec[0]} && \
all_arch_matches="$best_arch_match:$all_arch_matches" && \
log "DEBUG" "cpupath: host CPU best match updated to $best_arch_match"
fi
done
}

cpupath(){
# If EESSI_SOFTWARE_SUBDIR_OVERRIDE is set, use it
log "DEBUG" "cpupath: Override variable set as '$EESSI_SOFTWARE_SUBDIR_OVERRIDE' "
Expand Down Expand Up @@ -156,38 +234,31 @@ cpupath(){
local cpu_flags=$(get_cpuinfo "$cpu_flag_tag")
log "DEBUG" "cpupath: CPU flags of host system: '$cpu_flags'"

# Default to generic CPU
local best_arch_match="$machine_type/generic"
local all_arch_matches=$best_arch_match

# Iterate over the supported CPU specifications to find the best match for host CPU
# Order of the specifications matters, the last one to match will be selected
for arch in "${cpu_arch_spec[@]}"; do
eval "arch_spec=$arch"
if [ "${cpu_vendor}x" == "${arch_spec[1]}x" ]; then
# each flag in this CPU specification must be found in the list of flags of the host
check_allinfirst "${cpu_flags[*]}" ${arch_spec[2]} && best_arch_match=${arch_spec[0]} && \
all_arch_matches="$best_arch_match:$all_arch_matches" && \
log "DEBUG" "cpupath: host CPU best match updated to $best_arch_match"
fi
done
# Find the best match for host CPU, based on its flags
# (find_best_arch_match resets best_arch_match and all_arch_matches, which are set in this scope)
local best_arch_match all_arch_matches
find_arch_matches "$cpu_flags"

# Some Intel microarchitectures are flag-indistinguishable from an older one because their
# new features are not exposed in /proc/cpuinfo. Granite Rapids (Xeon 6) shows the exact same
# visible flags as Sapphire/Emerald Rapids (its extras like amx_fp16 are hidden by the kernel),
# so the flag match above lands on 'sapphirerapids'. Refine using the CPU model number - the
# only reliable discriminator on Linux. If no dedicated graniterapids subdir is shipped yet,
# downstream subdir resolution falls back to the next entry in the chain, so prepending is safe.
# so the flag match above lands on 'sapphirerapids'.
# Refine using the list_cpu_features tool from Google's cpu_features if available,
# and otherwise use the CPU model number.
if [ "${best_arch_match}" == "x86_64/intel/sapphirerapids" ]; then
local cpu_family=$(get_cpuinfo "cpu[ _]family")
local cpu_model=$(get_cpuinfo "model")
log "DEBUG" "cpupath: refining Sapphire Rapids match (family='$cpu_family', model='$cpu_model')"
if cpu_features_flags=$(get_cpu_features "$cpu_flag_tag"); then
log "DEBUG" "cpupath: flags reported by list_cpu_features: ${cpu_features_flags}"
# Now that we have a (possibly) changed set of flags, find the best match for host CPU again
find_arch_matches "$cpu_features_flags"
# Intel family 6 model numbers below come from the kernel's authoritative table
# arch/x86/include/asm/intel-family.h (what the kernel itself uses for model dispatch):
# INTEL_GRANITERAPIDS_X = IFM(6, 0xAD) -> family 6, model 173 (Granite Rapids-SP/AP)
# INTEL_GRANITERAPIDS_D = IFM(6, 0xAE) -> family 6, model 174 (Granite Rapids-D)
# (cf. INTEL_SAPPHIRERAPIDS_X = 0x8F/143, INTEL_EMERALDRAPIDS_X = 0xCF/207)
if [ "${cpu_family}" == "6" ] && { [ "${cpu_model}" == "173" ] || [ "${cpu_model}" == "174" ]; }; then
elif [ "${cpu_family}" == "6" ] && { [ "${cpu_model}" == "173" ] || [ "${cpu_model}" == "174" ]; }; then
best_arch_match="x86_64/intel/graniterapids"
all_arch_matches="$best_arch_match:$all_arch_matches"
log "DEBUG" "cpupath: model $cpu_model identifies Granite Rapids; best match upgraded to $best_arch_match"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x86_64/intel/graniterapids:x86_64/intel/sapphirerapids:x86_64/intel/icelake:x86_64/intel/cascadelake:x86_64/intel/skylake_avx512:x86_64/intel/haswell:x86_64/generic
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
arch : x86
brand : Intel(R) Xeon(R) 6975P-C
family : 6 (0x06)
model : 173 (0xAD)
stepping : 1 (0x01)
uarch : X86_UNKNOWN
flags : adx,aes,amx_bf16,amx_fp16,amx_int8,amx_tile,avx,avx2,avx512_bf16,avx512_fp16,avx512_second_fma,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avx_vnni,bmi1,bmi2,clflushopt,clfsh,clwb,cx16,cx8,erms,f16c,fma3,fpu,fs_rep_cmpsb_scasb,fs_rep_stosb,fz_rep_movsb,gfni,lzcnt,mmx,movbe,movdir64b,movdiri,pclmulqdq,popcnt,rdrnd,rdseed,sha,ss,sse,sse2,sse3,sse4_1,sse4_2,ssse3,tsc,vaes,vpclmulqdq
cache_info : {"level":1,"cache_type":"data","cache_size":49152,"ways":12,"line_size":64,"tlb_entries":64,"partitioning":1},{"level":1,"cache_type":"instruction","cache_size":65536,"ways":16,"line_size":64,"tlb_entries":64,"partitioning":1},{"level":2,"cache_type":"unified","cache_size":2097152,"ways":16,"line_size":64,"tlb_entries":2048,"partitioning":1},{"level":3,"cache_type":"unified","cache_size":503316480,"ways":16,"line_size":64,"tlb_entries":491520,"partitioning":1}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 173
model name : Intel(R) Xeon(R) 6975P-C
stepping : 1
microcode : 0x1000434
cpu MHz : 2700.000
cache size : 491520 KB
physical id : 0
siblings : 16
core id : 0
cpu cores : 8
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 36
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx_vnni avx512_bf16 wbnoinvd ida arat avx512vbmi umip pku ospke waitpkg avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg tme avx512_vpopcntdq rdpid bus_lock_detect cldemote movdiri movdir64b md_clear serialize amx_bf16 avx512_fp16 amx_tile amx_int8 flush_l1d arch_capabilities
bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs bhi spectre_v2_user
bogomips : 5400.00
clflush size : 64
cache_alignment : 64
address sizes : 48 bits physical, 48 bits virtual
power management:
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x86_64/intel/graniterapids
Loading