jaulib v1.3.0
Jau Support Library (C++, Java, ..)
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 <cstdint>
28#include <cstdlib>
29#include <thread>
30#include <new>
31
32#include <jau/cpuid.hpp>
33#include <jau/byte_util.hpp>
34#include <jau/debug.hpp>
35#include <jau/os/os_support.hpp>
36
37#if defined(_WIN32)
38 #include <windows.h>
39#else /* assume POSIX sysconf() availability */
40 #include <unistd.h>
41 #ifdef __linux__
42 extern "C" {
43 #include <sys/auxv.h>
44 }
45 #endif
46#endif
47
48using namespace jau;
49using namespace jau::cpu;
50
51static bool get_cache_line_size(size_t& l1_share_max, size_t& l1_apart_min) noexcept {
52 #ifdef __cpp_lib_hardware_interference_size
53 l1_share_max = std::hardware_constructive_interference_size;
54 l1_apart_min = std::hardware_destructive_interference_size;
55 return true;
56 #else
57 l1_share_max = 0;
58 l1_apart_min = 0;
59 return false;
60 #endif
61}
62
63static size_t get_page_size() noexcept {
64 #if defined(_WIN32)
65 SYSTEM_INFO si;
66 GetSystemInfo(&si);
67 return (jlong) si.dwPageSize;
68 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_PAGESIZE)
69 return sysconf(_SC_PAGESIZE);
70 #else
71 return 0;
72 #endif
73}
74static size_t get_concurrent_thread_count() noexcept {
75 return std::thread::hardware_concurrency();
76}
77
78static size_t get_sys_online_core_count() noexcept {
79 #if defined(_WIN32)
80 SYSTEM_INFO sysinfo;
81 GetSystemInfo(&sysinfo);
82 return static_cast<size_t>( sysinfo.dwNumberOfProcessors );
83 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_NPROCESSORS_ONLN)
84 return static_cast<size_t>( sysconf(_SC_NPROCESSORS_ONLN) );
85 #elif defined(JAU_OS_TYPE_UNIX) && defined(HW_NCPU)
86 int mib[] { CTL_HW, HW_NCPU }; // set the mib for hw.ncpu
87 int numCPU;
88 size_t len = sizeof(numCPU);
89 if( sysctl(mib, 2, &numCPU, &len, NULL, 0) || 0 >= numCPU ) {
90 return 1; // minimum ;-)
91 } else {
92 return static_cast<size_t>(numCPU);
93 }
94 #else
95 return 1; // minimum ;-)
96 #endif
97}
98
99static size_t get_sys_max_core_count() noexcept {
100 #if defined(_WIN32)
101 SYSTEM_INFO sysinfo;
102 GetSystemInfo(&sysinfo);
103 return static_cast<size_t>( sysinfo.dwNumberOfProcessors ); // FIXME?
104 #elif defined(JAU_OS_TYPE_UNIX) && defined(_SC_NPROCESSORS_CONF)
105 return static_cast<size_t>( sysconf(_SC_NPROCESSORS_CONF) );
106 #elif defined(JAU_OS_TYPE_UNIX) && defined(HW_NCPU)
107 int mib[] { CTL_HW, HW_AVAILCPU }; // set the mib for hw.availcpu FIXME?
108 int numCPU;
109 size_t len = sizeof(numCPU);
110 if( sysctl(mib, 2, &numCPU, &len, NULL, 0) || 0 >= numCPU ) {
111 return 1; // minimum ;-)
112 } else {
113 return static_cast<size_t>(numCPU);
114 }
115 #else
116 return 1; // minimum ;-)
117 #endif
118}
119
120template<typename T>
121static void append_bitstr(std::string& out, T mask, T bit, const std::string& bitstr, bool& comma) {
122 if( bit == ( mask & bit ) ) {
123 if( comma ) { out.append(", "); }
124 out.append(bitstr); comma = true;
125 }
126}
127#define APPEND_BITSTR(U,V,M) append_bitstr(out, M, U::V, #V, comma);
128
129#define CASE_TO_STRING(U,V) case U::V: return #V;
130
131#define CPUFAMILY_ENUM(X) \
132 X(cpu_family_t,none) \
133 X(cpu_family_t,arm32) \
134 X(cpu_family_t,arm64) \
135 X(cpu_family_t,x86_32) \
136 X(cpu_family_t,x86_64) \
137 X(cpu_family_t,ia64) \
138 X(cpu_family_t,ppc32) \
139 X(cpu_family_t,ppc64) \
140 X(cpu_family_t,sparc32) \
141 X(cpu_family_t,sparc64) \
142 X(cpu_family_t,mips32) \
143 X(cpu_family_t,mips64) \
144 X(cpu_family_t,superh32) \
145 X(cpu_family_t,superh64) \
146 X(cpu_family_t,wasm32) \
147 X(cpu_family_t,wasm64)
148
149std::string jau::cpu::to_string(const cpu_family_t v) noexcept {
150 switch(v) {
152 default: ; // fall through intended
153 }
154 return "none";
155}
156
157
158#define ARM32HWCAP1_ENUM(X,M) \
159 X(arm32_hwcap1_t,swp,M) \
160 X(arm32_hwcap1_t,half,M) \
161 X(arm32_hwcap1_t,thumb,M) \
162 X(arm32_hwcap1_t,bits26,M) \
163 X(arm32_hwcap1_t,fmult,M) \
164 X(arm32_hwcap1_t,fpa,M) \
165 X(arm32_hwcap1_t,vfp,M) \
166 X(arm32_hwcap1_t,edsp,M) \
167 X(arm32_hwcap1_t,java,M) \
168 X(arm32_hwcap1_t,iwmmxt,M) \
169 X(arm32_hwcap1_t,crunch,M) \
170 X(arm32_hwcap1_t,thumbee,M) \
171 X(arm32_hwcap1_t,neon,M) \
172 X(arm32_hwcap1_t,vfp_v3,M) \
173 X(arm32_hwcap1_t,vfp_v3_d16,M) \
174 X(arm32_hwcap1_t,tls,M) \
175 X(arm32_hwcap1_t,vfp_v4,M) \
176 X(arm32_hwcap1_t,idiva,M) \
177 X(arm32_hwcap1_t,idivt,M) \
178 X(arm32_hwcap1_t,vfp_d32,M) \
179 X(arm32_hwcap1_t,lpae,M) \
180 X(arm32_hwcap1_t,evtstrm,M)
181
182std::string jau::cpu::to_string(const arm32_hwcap1_t hwcaps) noexcept {
183 std::string out;
184 bool comma = false;
186 return out;
187}
188
189#define ARM32HWCAP2_ENUM(X,M) \
190 X(arm32_hwcap2_t,aes,M) \
191 X(arm32_hwcap2_t,pmull,M) \
192 X(arm32_hwcap2_t,sha1,M) \
193 X(arm32_hwcap2_t,sha2,M) \
194 X(arm32_hwcap2_t,crc32,M)
195
196std::string jau::cpu::to_string(const arm32_hwcap2_t hwcaps) noexcept {
197 std::string out;
198 bool comma = false;
200 return out;
201}
202
203#define ARM64HWCAP_ENUM(X,M) \
204 X(arm64_hwcap_t,fp,M) \
205 X(arm64_hwcap_t,asimd,M) \
206 X(arm64_hwcap_t,evtstrm,M) \
207 X(arm64_hwcap_t,aes,M) \
208 X(arm64_hwcap_t,pmull,M) \
209 X(arm64_hwcap_t,sha1,M) \
210 X(arm64_hwcap_t,sha2,M) \
211 X(arm64_hwcap_t,crc32,M) \
212 X(arm64_hwcap_t,atomics,M) \
213 X(arm64_hwcap_t,fphp,M) \
214 X(arm64_hwcap_t,asimdhp,M) \
215 X(arm64_hwcap_t,cpuid,M) \
216 X(arm64_hwcap_t,asimdrdm,M) \
217 X(arm64_hwcap_t,jscvt,M) \
218 X(arm64_hwcap_t,fcma,M) \
219 X(arm64_hwcap_t,lrcpc,M) \
220 X(arm64_hwcap_t,dcpop,M) \
221 X(arm64_hwcap_t,sha3,M) \
222 X(arm64_hwcap_t,sm3,M) \
223 X(arm64_hwcap_t,sm4,M) \
224 X(arm64_hwcap_t,asimddp,M) \
225 X(arm64_hwcap_t,sha512,M) \
226 X(arm64_hwcap_t,sve,M) \
227 X(arm64_hwcap_t,asimdfhm,M) \
228 X(arm64_hwcap_t,dit,M) \
229 X(arm64_hwcap_t,uscat,M) \
230 X(arm64_hwcap_t,ilrcpc,M) \
231 X(arm64_hwcap_t,flagm,M) \
232 X(arm64_hwcap_t,ssbs,M) \
233 X(arm64_hwcap_t,sb,M) \
234 X(arm64_hwcap_t,paca,M) \
235 X(arm64_hwcap_t,pacg,M)
236
237std::string jau::cpu::to_string(const arm64_hwcap_t hwcaps) noexcept {
238 std::string out;
239 bool comma = false;
241 return out;
242}
243
244/** Returns cpu_family derived from [Architectures](https://sourceforge.net/p/predef/wiki/Architectures/) predefined compiler macros. Consider using singleton CpuInfo. */
245static cpu_family_t get_cpu_family() noexcept {
246 #if defined(__EMSCRIPTEN__)
247 if( 32 == pointer_bit_size() ) {
248 return cpu_family_t::wasm32;
249 } else if( 64 == pointer_bit_size() ) {
250 return cpu_family_t::wasm64;
251 } else {
252 return cpu_family_t::wasm64; // FIXME?
253 }
254 #elif defined(__aarch64__)
255 static_assert( 64 == pointer_bit_size() );
256 return cpu_family_t::arm64;
257 #elif defined(__arm__)
258 static_assert( 32 == pointer_bit_size() );
259 return cpu_family_t::arm32;
260 #elif defined(__x86_64__)
261 static_assert( 64 == pointer_bit_size() );
262 return cpu_family_t::x86_64;
263 #elif defined(__ia64__)
264 static_assert( 64 == pointer_bit_size() );
265 return cpu_family_t::ia64;
266 #elif defined(__i386__)
267 static_assert( 32 == pointer_bit_size() );
268 return cpu_family_t::X86_32;
269 #elif defined(__powerpc__)
270 #if defined(__LP64__)
271 static_assert( 64 == pointer_bit_size() );
272 return cpu_family_t::ppc64;
273 #else
274 static_assert( 32 == pointer_bit_size() );
275 return cpu_family_t::ppc32;
276 #endif
277 #elif defined(__sparc__)
278 #if defined(__LP64__)
279 static_assert( 64 == pointer_bit_size() );
280 return cpu_family_t::sparc64;
281 #else
282 static_assert( 32 == pointer_bit_size() );
283 return cpu_family_t::sparc32;
284 #endif
285 #elif defined(__mips__)
286 #if defined(__LP64__)
287 static_assert( 64 == pointer_bit_size() );
288 return cpu_family_t::mips64;
289 #else
290 static_assert( 32 == pointer_bit_size() );
291 return cpu_family_t::mips32;
292 #endif
293 #elif defined(__sh__)
294 #if defined(__LP64__)
295 static_assert( 64 == pointer_bit_size() );
296 return cpu_family_t::superh64;
297 #else
298 static_assert( 32 == pointer_bit_size() );
299 return cpu_family_t::superh32;
300 #endif
301 #else
302 return cpu_family_t::UNDEF;
303 #endif
304}
305
306#if 0
307#if defined(__i386__) || defined(__x86_64__)
308
309#if defined(_MSC_VER)
310 #include <intrin.h>
311#elif defined(__INTEL__)
312 #include <ia32intrin.h>
313#elif defined(__GNUC__) || defined(__clang__)
314 #include <cpuid.h>
315#endif
316
317static void invoke_cpuid(uint32_t type, uint32_t out[4]) {
318#if defined(_MSC_VER) || defined(__INTEL__)
319 __cpuid((int*)out, type);
320#elif defined(__GNUC__) || defined(__clang__)
321 __get_cpuid(type, out, out+1, out+2, out+3);
322#else
323 #warning "No x86 cpuid supported for this compiler"
324 ::bzero(out, sizeof(uint32_t)*4);
325#endif
326}
327
328static void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4]) {
329#if defined(_MSC_VER)
330 __cpuidex((int*)out, type, level);
331#elif defined(__GNUC__) || defined(__clang__)
332 __cpuid_count(type, level, out[0], out[1], out[2], out[3]);
333#else
334 #warning "No x86 cpuid sublevel supported for this compiler"
335 ::bzero(out, sizeof(uint32_t)*4);
336#endif
337}
338
339#endif // #if defined(__i386__) || defined(__x86_64__)
340#endif // disabled
341
342static bool get_arm32_hwcap(arm32_hwcap1_t& hwcap1, arm32_hwcap2_t& hwcap2) noexcept {
343#ifdef __linux__
344 if( cpu_family_t::arm32 == get_cpu_family() ) {
345 hwcap1 = (arm32_hwcap1_t) ::getauxval(number(arm32_hwcap1_t::at_hwcap_1));
346 if( arm32_hwcap1_t::neon == ( hwcap1 & arm32_hwcap1_t::neon ) ) {
347 hwcap2 = (arm32_hwcap2_t) ::getauxval(number(arm32_hwcap2_t::at_hwcap_2));
348 } else {
349 hwcap2 = arm32_hwcap2_t::none;
350 }
351 return true;
352 } else {
353 hwcap1 = arm32_hwcap1_t::none;
354 hwcap2 = arm32_hwcap2_t::none;
355 return false;
356 }
357#else
358 hwcap1 = arm32_hwcap1_t::none;
359 hwcap2 = arm32_hwcap2_t::none;
360 return false;
361#endif
362}
363
364static bool get_arm64_hwcap(arm64_hwcap_t& hwcap) noexcept {
365#ifdef __linux__
366 if( cpu_family_t::arm64 == get_cpu_family() ) {
367 hwcap = (arm64_hwcap_t) ::getauxval(number(arm64_hwcap_t::at_hwcap));
368 return true;
369 } else {
370 hwcap = arm64_hwcap_t::none;
371 return false;
372 }
373#else
374 hwcap = arm64_hwcap_t::none;
375 return false;
376#endif
377}
378
379jau::cpu::CpuInfo::CpuInfo() noexcept
380: pointer_bits(pointer_bit_size()),
381 page_size(get_page_size()),
382 has_l1_minmax(false), l1_share_max(0), l1_apart_min(0),
383 concurrent_threads(get_concurrent_thread_count()),
384 sys_online_cores(get_sys_online_core_count()),
385 sys_max_cores(get_sys_max_core_count()),
386 family(get_cpu_family()), byte_order(jau::endian_t::native),
387 has_arm32_hwcap(cpu_family_t::arm32 == family ? get_arm32_hwcap(arm32_hwcap1, arm32_hwcap2) : false),
388 has_arm64_hwcap(cpu_family_t::arm64 == family ? get_arm64_hwcap(arm64_hwcap) : false)
389{
391}
392
393std::string jau::cpu::CpuInfo::toString(std::string& sb, bool details_only) const noexcept {
394 if( !details_only ) {
395 sb.append(to_string(family)).append(" (");
396 }
397 sb.append( jau::format_string("%s endian, %zu bits, %zu cores, page-sz %zu",
398 to_string(byte_order).c_str(), pointer_bits, online_core_count(), page_size) );
399 if( has_l1_minmax ) {
400 sb.append( jau::format_string(", l1[shared-max %zu, apart-min %zu]", l1_share_max, l1_apart_min) );
401 }
402 if( has_arm32_hwcap ) {
403 if( arm32_hwcap1_t::none != arm32_hwcap1 ) {
404 sb.append( jau::format_string(", hwcap1 0x%" PRIx64 ": '%s'", number(arm32_hwcap1), to_string(arm32_hwcap1).c_str()) );
405 }
406 if( arm32_hwcap2_t::none != arm32_hwcap2 ) {
407 sb.append( jau::format_string(", hwcap2 0x%" PRIx64 ": '%s'", number(arm32_hwcap2), to_string(arm32_hwcap2).c_str()) );
408 }
409 } else if( has_arm64_hwcap ) {
410 if( arm64_hwcap_t::none != arm64_hwcap ) {
411 sb.append( jau::format_string(", hwcap 0x%" PRIx64 ": '%s'", number(arm64_hwcap), to_string(arm64_hwcap).c_str()) );
412 }
413 }
414 if( !details_only ) {
415 sb.append(")");
416 }
417 return sb;
418}
419
std::string toString() const noexcept
Definition: cpuid.hpp:367
size_t l1_apart_min
Minimum offset between two objects to avoid false sharing if has_l1_minmax, or zero.
Definition: cpuid.hpp:327
bool has_l1_minmax
True if successfully queried l1_share_max and l1_apart_min.
Definition: cpuid.hpp:323
size_t l1_share_max
Maximum size of contiguous memory to promote true sharing if has_l1_minmax, or zero.
Definition: cpuid.hpp:325
static bool get_arm32_hwcap(arm32_hwcap1_t &hwcap1, arm32_hwcap2_t &hwcap2) noexcept
Definition: cpuid.cpp:342
static size_t get_concurrent_thread_count() noexcept
Definition: cpuid.cpp:74
static void append_bitstr(std::string &out, T mask, T bit, const std::string &bitstr, bool &comma)
Definition: cpuid.cpp:121
#define ARM32HWCAP2_ENUM(X, M)
Definition: cpuid.cpp:189
static size_t get_sys_online_core_count() noexcept
Definition: cpuid.cpp:78
#define ARM32HWCAP1_ENUM(X, M)
Definition: cpuid.cpp:158
#define ARM64HWCAP_ENUM(X, M)
Definition: cpuid.cpp:203
static size_t get_sys_max_core_count() noexcept
Definition: cpuid.cpp:99
static size_t get_page_size() noexcept
Definition: cpuid.cpp:63
#define CASE_TO_STRING(U, V)
Definition: cpuid.cpp:129
#define CPUFAMILY_ENUM(X)
Definition: cpuid.cpp:131
static cpu_family_t get_cpu_family() noexcept
Returns cpu_family derived from Architectures predefined compiler macros.
Definition: cpuid.cpp:245
static bool get_arm64_hwcap(arm64_hwcap_t &hwcap) noexcept
Definition: cpuid.cpp:364
static bool get_cache_line_size(size_t &l1_share_max, size_t &l1_apart_min) noexcept
Definition: cpuid.cpp:51
#define APPEND_BITSTR(U, V, M)
Definition: cpuid.cpp:127
endian_t
Endian identifier, indicating endianess of all scalar types.
Definition: byte_util.hpp:203
constexpr uint32_t number(const iostate rhs) noexcept
Definition: byte_stream.hpp:72
std::string format_string(const char *format,...) noexcept
Returns a string according to printf() formatting rules and variable number of arguments following th...
arm32_hwcap2_t
Definition: cpuid.hpp:192
arm64_hwcap_t
Definition: cpuid.hpp:240
std::string to_string(const cpu_family_t v) noexcept
Definition: cpuid.cpp:149
constexpr size_t pointer_bit_size() noexcept
Returns the compile time pointer architecture size in bits.
Definition: cpuid.hpp:45
constexpr uint16_t number(const cpu_family_t rhs) noexcept
Definition: cpuid.hpp:89
cpu_family_t
Definition: cpuid.hpp:47
arm32_hwcap1_t
Definition: cpuid.hpp:127
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32