|
35 | 35 | #endif |
36 | 36 | #endif |
37 | 37 |
|
38 | | - |
39 | | -// --------------------------------------------------------------------------- |
40 | | -// Runtime CPU feature detection |
41 | | -// --------------------------------------------------------------------------- |
42 | | -// Uses CPUID (via __cpuidex on MSVC, __cpuid_count on GCC/Clang) to detect |
43 | | -// AVX2 and AVX-512F at runtime. Results are cached in a function-local static |
44 | | -// so the CPUID query runs only once. |
45 | | -// |
46 | | -// AVX2 is the minimum x86 baseline requirement. F16C is always available on |
47 | | -// CPUs that support AVX2, so it is not tracked separately. |
48 | | -// |
49 | | -// The distance headers compile all SIMD code paths unconditionally (the |
50 | | -// intrinsic headers are always included below). At runtime, select_dist() |
51 | | -// uses deglib::cpu::has_*() to dispatch to the best variant. This means there |
52 | | -// is zero loop overhead — capability checks happen during variant selection, |
53 | | -// not inside distance calculation loops. |
54 | | -// --------------------------------------------------------------------------- |
55 | | - |
56 | | -namespace deglib::cpu { |
57 | | - |
58 | | - namespace detail { |
59 | | - |
60 | | -#if defined(DEGLIB_X86) |
61 | | - // Query CPUID leaf/subleaf into a 4-element int array. |
62 | | - // Uses __cpuidex on MSVC and __cpuid_count on GCC/Clang. |
63 | | - inline void cpuid(int leaf, int subleaf, int cpu_info[4]) { |
64 | | -#if defined(_MSC_VER) |
65 | | - __cpuidex(cpu_info, leaf, subleaf); |
66 | | -#else |
67 | | - __cpuid_count(leaf, subleaf, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); |
68 | | -#endif |
69 | | - } |
70 | | -#endif |
71 | | - |
72 | | - // Cached hardware feature flags, populated on first call via a function-local static. |
73 | | - struct CpuFeatures { |
74 | | - bool avx2{false}; |
75 | | - bool avx512f{false}; |
76 | | - |
77 | | - CpuFeatures() { |
78 | | -#if defined(DEGLIB_X86) |
79 | | - int cpu_info[4] = {0}; |
80 | | - |
81 | | - // Leaf 7, subleaf 0: extended feature flags in EBX and ECX |
82 | | - cpuid(7, 0, cpu_info); |
83 | | - avx2 = (cpu_info[1] & (1 << 5)) != 0; |
84 | | - avx512f = (cpu_info[1] & (1 << 16)) != 0; |
85 | | -#endif |
86 | | - } |
87 | | - }; |
88 | | - |
89 | | - inline const CpuFeatures& features() { |
90 | | - static CpuFeatures cached; |
91 | | - return cached; |
92 | | - } |
93 | | - |
94 | | - } // namespace detail |
95 | | - |
96 | | - enum class InstructionSet : uint8_t { |
97 | | - Auto = 0, |
98 | | - Scalar = 1, |
99 | | - AVX2 = 2, |
100 | | - AVX512 = 3 |
101 | | - }; |
102 | | - |
103 | | - inline const char* instruction_set_to_string(InstructionSet inst) { |
104 | | - switch (inst) { |
105 | | - case InstructionSet::Auto: return "Auto"; |
106 | | - case InstructionSet::Scalar: return "Scalar"; |
107 | | - case InstructionSet::AVX2: return "AVX2"; |
108 | | - case InstructionSet::AVX512: return "AVX512"; |
109 | | - } |
110 | | - return "Unknown"; |
111 | | - } |
112 | | - |
113 | | - // Runtime CPU feature detection — safe to call from any translation unit. |
114 | | - // These checks are performed once (cached) and have zero cost per call thereafter. |
115 | | - |
116 | | - inline bool has_avx2() { return detail::features().avx2; } |
117 | | - inline bool has_avx512() { return detail::features().avx512f; } |
118 | | - |
119 | | -} // namespace deglib::cpu |
120 | | - |
| 38 | +// CPU detection utilities |
| 39 | +#include "deglib/utils/cpu.h" |
0 commit comments