jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
cpuid.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <cstring>
26#include <string>
27#include <cstdlib>
28#include <thread>
29
30#include <jau/cpuid.hpp>
31#include <jau/byte_util.hpp>
32#include <jau/debug.hpp>
33#include <jau/os/os_support.hpp>
34
35#if defined(_WIN32)
36 #include <windows.h>
37#else /* assume POSIX sysconf() availability */
38 #include <unistd.h>
39 #ifdef __linux__
40 extern "C" {
41 #include <sys/auxv.h>
42 }
43 #endif
44#endif
45
46using namespace jau;
47using namespace jau::cpu;
48
49static bool get_cache_line_size(size_t& l1_share_max, size_t& l1_apart_min) noexcept {
50 #ifdef __cpp_lib_hardware_interference_size
51 l1_share_max = std::hardware_constructive_interference_size;
52 l1_apart_min = std::hardware_destructive_interference_size;
53 return true;
54 #else
55 l1_share_max = 0;
56 l1_apart_min = 0;
57 return false;
58 #endif
59}
60
61static size_t get_page_size() noexcept {
62 #if defined(_WIN32)
63 SYSTEM_INFO si;
64 GetSystemInfo(&si);
65 return (jlong) si.dwPageSize;
66 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_PAGESIZE)
67 return sysconf(_SC_PAGESIZE);
68 #else
69 return 0;
70 #endif
71}
72static size_t get_concurrent_thread_count() noexcept {
73 return std::thread::hardware_concurrency();
74}
75
76static size_t get_sys_online_core_count() noexcept {
77 #if defined(_WIN32)
78 SYSTEM_INFO sysinfo;
79 GetSystemInfo(&sysinfo);
80 return static_cast<size_t>( sysinfo.dwNumberOfProcessors );
81 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_NPROCESSORS_ONLN)
82 return static_cast<size_t>( sysconf(_SC_NPROCESSORS_ONLN) );
83 #elif defined(JAU_OS_TYPE_UNIX) && defined(HW_NCPU)
84 int mib[] { CTL_HW, HW_NCPU }; // set the mib for hw.ncpu
85 int numCPU;
86 size_t len = sizeof(numCPU);
87 if( sysctl(mib, 2, &numCPU, &len, NULL, 0) || 0 >= numCPU ) {
88 return 1; // minimum ;-)
89 } else {
90 return static_cast<size_t>(numCPU);
91 }
92 #else
93 return 1; // minimum ;-)
94 #endif
95}
96
97static size_t get_sys_max_core_count() noexcept {
98 #if defined(_WIN32)
99 SYSTEM_INFO sysinfo;
100 GetSystemInfo(&sysinfo);
101 return static_cast<size_t>( sysinfo.dwNumberOfProcessors ); // FIXME?
102 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_NPROCESSORS_CONF)
103 return static_cast<size_t>( sysconf(_SC_NPROCESSORS_CONF) );
104 #elif defined(JAU_OS_TYPE_UNIX) && defined(HW_NCPU)
105 int mib[] { CTL_HW, HW_AVAILCPU }; // set the mib for hw.availcpu FIXME?
106 int numCPU;
107 size_t len = sizeof(numCPU);
108 if( sysctl(mib, 2, &numCPU, &len, NULL, 0) || 0 >= numCPU ) {
109 return 1; // minimum ;-)
110 } else {
111 return static_cast<size_t>(numCPU);
112 }
113 #else
114 return 1; // minimum ;-)
115 #endif
116}
117
118template<typename T>
119static void append_bitstr(std::string& out, T mask, T bit, const std::string& bitstr, bool& comma) {
120 if( bit == ( mask & bit ) ) {
121 if( comma ) { out.append(", "); }
122 out.append(bitstr); comma = true;
123 }
124}
125
126/** Returns cpu_family derived from [Architectures](https://sourceforge.net/p/predef/wiki/Architectures/) predefined compiler macros. Consider using singleton CpuInfo. */
127static cpu_family_t get_cpu_family() noexcept {
128 #if defined(__EMSCRIPTEN__)
129 if( 32 == pointer_bit_size() ) {
131 } else if( 64 == pointer_bit_size() ) {
133 } else {
134 return cpu_family_t::wasm64; // FIXME?
135 }
136 #elif defined(__aarch64__)
137 static_assert( 64 == pointer_bit_size() );
138 return cpu_family_t::arm64;
139 #elif defined(__arm__)
140 static_assert( 32 == pointer_bit_size() );
141 return cpu_family_t::arm32;
142 #elif defined(__x86_64__)
143 static_assert( 64 == pointer_bit_size() );
145 #elif defined(__ia64__)
146 static_assert( 64 == pointer_bit_size() );
147 return cpu_family_t::ia64;
148 #elif defined(__i386__)
149 static_assert( 32 == pointer_bit_size() );
150 return cpu_family_t::X86_32;
151 #elif defined(__powerpc__)
152 #if defined(__LP64__)
153 static_assert( 64 == pointer_bit_size() );
154 return cpu_family_t::ppc64;
155 #else
156 static_assert( 32 == pointer_bit_size() );
157 return cpu_family_t::ppc32;
158 #endif
159 #elif defined(__sparc__)
160 #if defined(__LP64__)
161 static_assert( 64 == pointer_bit_size() );
163 #else
164 static_assert( 32 == pointer_bit_size() );
166 #endif
167 #elif defined(__mips__)
168 #if defined(__LP64__)
169 static_assert( 64 == pointer_bit_size() );
171 #else
172 static_assert( 32 == pointer_bit_size() );
174 #endif
175 #elif defined(__sh__)
176 #if defined(__LP64__)
177 static_assert( 64 == pointer_bit_size() );
179 #else
180 static_assert( 32 == pointer_bit_size() );
182 #endif
183 #else
184 return cpu_family_t::UNDEF;
185 #endif
186}
187
188#if 0
189#if defined(__i386__) || defined(__x86_64__)
190
191#if defined(_MSC_VER)
192 #include <intrin.h>
193#elif defined(__INTEL__)
194 #include <ia32intrin.h>
195#elif defined(__GNUC__) || defined(__clang__)
196 #include <cpuid.h>
197#endif
198
199static void invoke_cpuid(uint32_t type, uint32_t out[4]) {
200#if defined(_MSC_VER) || defined(__INTEL__)
201 __cpuid((int*)out, type);
202#elif defined(__GNUC__) || defined(__clang__)
203 __get_cpuid(type, out, out+1, out+2, out+3);
204#else
205 #warning "No x86 cpuid supported for this compiler"
206 ::bzero(out, sizeof(uint32_t)*4);
207#endif
208}
209
210static void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4]) {
211#if defined(_MSC_VER)
212 __cpuidex((int*)out, type, level);
213#elif defined(__GNUC__) || defined(__clang__)
214 __cpuid_count(type, level, out[0], out[1], out[2], out[3]);
215#else
216 #warning "No x86 cpuid sublevel supported for this compiler"
217 ::bzero(out, sizeof(uint32_t)*4);
218#endif
219}
220
221#endif // #if defined(__i386__) || defined(__x86_64__)
222#endif // disabled
223
224static bool get_arm32_hwcap(arm32_hwcap1_t& hwcap1, arm32_hwcap2_t& hwcap2) noexcept {
225#ifdef __linux__
227 hwcap1 = (arm32_hwcap1_t) ::getauxval(number(arm32_hwcap1_t::at_hwcap_1));
228 if( arm32_hwcap1_t::neon == ( hwcap1 & arm32_hwcap1_t::neon ) ) {
229 hwcap2 = (arm32_hwcap2_t) ::getauxval(number(arm32_hwcap2_t::at_hwcap_2));
230 } else {
231 hwcap2 = arm32_hwcap2_t::none;
232 }
233 return true;
234 } else {
235 hwcap1 = arm32_hwcap1_t::none;
236 hwcap2 = arm32_hwcap2_t::none;
237 return false;
238 }
239#else
240 hwcap1 = arm32_hwcap1_t::none;
241 hwcap2 = arm32_hwcap2_t::none;
242 return false;
243#endif
244}
245
246static bool get_arm64_hwcap(arm64_hwcap_t& hwcap) noexcept {
247#ifdef __linux__
249 hwcap = (arm64_hwcap_t) ::getauxval(number(arm64_hwcap_t::at_hwcap));
250 return true;
251 } else {
252 hwcap = arm64_hwcap_t::none;
253 return false;
254 }
255#else
256 hwcap = arm64_hwcap_t::none;
257 return false;
258#endif
259}
260
261jau::cpu::CpuInfo::CpuInfo() noexcept
262: pointer_bits(pointer_bit_size()),
263 page_size(get_page_size()),
264 has_l1_minmax(false), l1_share_max(0), l1_apart_min(0),
265 concurrent_threads(get_concurrent_thread_count()),
266 sys_online_cores(get_sys_online_core_count()),
267 sys_max_cores(get_sys_max_core_count()),
268 family(get_cpu_family()), byte_order(jau::endian_t::native),
269 has_arm32_hwcap(cpu_family_t::arm32 == family ? get_arm32_hwcap(arm32_hwcap1, arm32_hwcap2) : false),
270 has_arm64_hwcap(cpu_family_t::arm64 == family ? get_arm64_hwcap(arm64_hwcap) : false)
271{
273}
274
275std::string jau::cpu::CpuInfo::toString(std::string& sb, bool details_only) const noexcept {
276 if( !details_only ) {
277 sb.append(to_string(family)).append(" (");
278 }
279 sb.append( jau::format_string("%s endian, %zu bits, %zu cores, page-sz %zu",
281 if( has_l1_minmax ) {
282 sb.append( jau::format_string(", l1[shared-max %zu, apart-min %zu]", l1_share_max, l1_apart_min) );
283 }
284 if( has_arm32_hwcap ) {
286 sb.append( jau::format_string(", hwcap1 0x%" PRIx64 ": '%s'", number(arm32_hwcap1), to_string(arm32_hwcap1).c_str()) );
287 }
289 sb.append( jau::format_string(", hwcap2 0x%" PRIx64 ": '%s'", number(arm32_hwcap2), to_string(arm32_hwcap2).c_str()) );
290 }
291 } else if( has_arm64_hwcap ) {
293 sb.append( jau::format_string(", hwcap 0x%" PRIx64 ": '%s'", number(arm64_hwcap), to_string(arm64_hwcap).c_str()) );
294 }
295 }
296 if( !details_only ) {
297 sb.append(")");
298 }
299 return sb;
300}
301
size_t pointer_bits
See pointer_bit_size()
Definition cpuid.hpp:182
std::string toString() const noexcept
Definition cpuid.hpp:230
size_t l1_apart_min
Minimum offset between two objects to avoid false sharing if has_l1_minmax, or zero.
Definition cpuid.hpp:190
size_t online_core_count() const noexcept
Returns maximum number of available/online cores, i.e.
Definition cpuid.hpp:225
cpu_family_t family
cpu_family_t derived from Architectures predefined compiler macros.
Definition cpuid.hpp:198
bool has_l1_minmax
True if successfully queried l1_share_max and l1_apart_min.
Definition cpuid.hpp:186
arm32_hwcap1_t arm32_hwcap1
arm32_hwcap1_t info if available, i.e.
Definition cpuid.hpp:203
bool has_arm32_hwcap
True if successfully queried arm32_hwcap1 and arm32_hwcap1 on cpu_family_t::arm32.
Definition cpuid.hpp:201
size_t page_size
Size of a page in bytes or zero if not available.
Definition cpuid.hpp:184
jau::endian_t byte_order
Definition cpuid.hpp:199
arm64_hwcap_t arm64_hwcap
arm64_hwcap_t info if available, i.e.
Definition cpuid.hpp:209
size_t l1_share_max
Maximum size of contiguous memory to promote true sharing if has_l1_minmax, or zero.
Definition cpuid.hpp:188
bool has_arm64_hwcap
True if successfully queried arm64_hwcap on cpu_family_t::arm64.
Definition cpuid.hpp:207
arm32_hwcap2_t arm32_hwcap2
arm32_hwcap2_t info if available, i.e.
Definition cpuid.hpp:205
static bool get_arm32_hwcap(arm32_hwcap1_t &hwcap1, arm32_hwcap2_t &hwcap2) noexcept
Definition cpuid.cpp:224
static size_t get_concurrent_thread_count() noexcept
Definition cpuid.cpp:72
static size_t get_sys_online_core_count() noexcept
Definition cpuid.cpp:76
static size_t get_sys_max_core_count() noexcept
Definition cpuid.cpp:97
static size_t get_page_size() noexcept
Definition cpuid.cpp:61
static cpu_family_t get_cpu_family() noexcept
Returns cpu_family derived from Architectures predefined compiler macros.
Definition cpuid.cpp:127
static bool get_arm64_hwcap(arm64_hwcap_t &hwcap) noexcept
Definition cpuid.cpp:246
static bool get_cache_line_size(size_t &l1_share_max, size_t &l1_apart_min) noexcept
Definition cpuid.cpp:49
endian_t
Endian identifier, indicating endianess of all scalar types.
std::string to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
@ native
Identifier for native platform type, one of the above.
constexpr std::underlying_type_t< E > number(const E v) noexcept
constexpr void append_bitstr(std::string &out, E mask, E bit, const std::string &bitstr, bool &comma)
std::string format_string(const char *format,...)
Returns a string according to printf() formatting rules and variable number of arguments following th...
arm32_hwcap2_t
Definition cpuid.hpp:125
arm64_hwcap_t
Definition cpuid.hpp:137
constexpr size_t pointer_bit_size() noexcept
Returns the compile time pointer architecture size in bits.
Definition cpuid.hpp:48
cpu_family_t
Definition cpuid.hpp:50
arm32_hwcap1_t
Definition cpuid.hpp:94
@ x86_64
AMD/Intel 64-bit.
Definition cpuid.hpp:62
@ arm64
ARM 64bit.
Definition cpuid.hpp:57
@ sparc32
SPARC 32bit.
Definition cpuid.hpp:72
@ wasm64
WebAssembly 64-bit.
Definition cpuid.hpp:89
@ superh32
Hitachi SuperH 32bit.
Definition cpuid.hpp:82
@ superh64
Hitachi SuperH 64bit.
Definition cpuid.hpp:84
@ arm32
ARM 32bit.
Definition cpuid.hpp:55
@ wasm32
WebAssembly 32-bit.
Definition cpuid.hpp:87
@ ppc64
Power PC 32bit.
Definition cpuid.hpp:69
@ mips64
Mips 64bit.
Definition cpuid.hpp:79
@ sparc64
SPARC 32bit.
Definition cpuid.hpp:74
@ mips32
Mips 32bit.
Definition cpuid.hpp:77
@ ppc32
Power PC 32bit.
Definition cpuid.hpp:67
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32