1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
/*
* Copyright (c) 2025 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "rtc_base/cpu_info.h"
#include <cstdint>
#if defined(WEBRTC_WIN)
#include <windows.h>
#elif defined(WEBRTC_MAC)
#include <sys/sysctl.h>
#elif defined(WEBRTC_ANDROID)
#include <cpu-features.h>
#include <unistd.h>
#elif defined(WEBRTC_FUCHSIA)
#include <zircon/syscalls.h>
#elif defined(WEBRTC_LINUX)
#include <features.h>
#include <stdlib.h>
#include <string.h> // IWYU pragma: keep
#include <unistd.h>
#ifdef __GLIBC_PREREQ
#define WEBRTC_GLIBC_PREREQ(a, b) __GLIBC_PREREQ(a, b)
#else
#define WEBRTC_GLIBC_PREREQ(a, b) 0
#endif
#if WEBRTC_GLIBC_PREREQ(2, 16)
#include <sys/auxv.h> // IWYU pragma: keep
#else
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#endif
#endif // WEBRTC_LINUX
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/system/arch.h"
#include "rtc_base/system/unused.h" // IWYU pragma: keep
#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(_MSC_VER)
#include <intrin.h>
#endif
#if defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_LINUX)
#include <asm/hwcap.h>
#endif
// Parts of this file derived from Chromium's base/cpu.cc.
namespace {
uint32_t DetectNumberOfCores() {
int number_of_cores = 0;
#if defined(WEBRTC_WIN)
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
number_of_cores = static_cast<int>(si.dwNumberOfProcessors);
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
number_of_cores = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
if (number_of_cores <= 0) {
RTC_LOG(LS_ERROR) << "Failed to get number of cores";
number_of_cores = 1;
}
#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
int name[] = {CTL_HW, HW_AVAILCPU};
size_t size = sizeof(number_of_cores);
if (0 != sysctl(name, 2, &number_of_cores, &size, NULL, 0)) {
RTC_LOG(LS_ERROR) << "Failed to get number of cores";
number_of_cores = 1;
}
#elif defined(WEBRTC_FUCHSIA)
number_of_cores = zx_system_get_num_cpus();
#else
RTC_LOG(LS_ERROR) << "No function to get number of cores";
number_of_cores = 1;
#endif
RTC_LOG(LS_INFO) << "Available number of cores: " << number_of_cores;
RTC_CHECK_GT(number_of_cores, 0);
return static_cast<uint32_t>(number_of_cores);
}
#if defined(WEBRTC_ARCH_X86_FAMILY)
#if defined(WEBRTC_ENABLE_AVX2)
// xgetbv returns the value of an Intel Extended Control Register (XCR).
// Currently only XCR0 is defined by Intel so `xcr` should always be zero.
uint64_t xgetbv(uint32_t xcr) {
#if defined(_MSC_VER)
return _xgetbv(xcr);
#else
uint32_t eax, edx;
__asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
return (static_cast<uint64_t>(edx) << 32) | eax;
#endif // _MSC_VER
}
#endif // WEBRTC_ENABLE_AVX2
#ifndef _MSC_VER
// Intrinsic for "cpuid".
#if defined(__pic__) && defined(__i386__)
static inline void __cpuid(int cpu_info[4], int info_type) {
__asm__ volatile(
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
"=d"(cpu_info[3])
: "a"(info_type));
}
#else
inline void __cpuid(int cpu_info[4], int info_type) {
__asm__ volatile("cpuid\n"
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
"=d"(cpu_info[3])
: "a"(info_type), "c"(0));
}
#endif
#endif // _MSC_VER
#endif // WEBRTC_ARCH_X86_FAMILY
} // namespace
namespace webrtc {
namespace cpu_info {
uint32_t DetectNumberOfCores() {
// Statically cache the number of system cores available since if the process
// is running in a sandbox, we may only be able to read the value once (before
// the sandbox is initialized) and not thereafter.
// For more information see crbug.com/176522.
static const uint32_t logical_cpus = ::DetectNumberOfCores();
return logical_cpus;
}
bool Supports(ISA instruction_set_architecture) {
#if defined(WEBRTC_ARCH_X86_FAMILY)
int cpu_info[4];
__cpuid(cpu_info, 1);
if (instruction_set_architecture == ISA::kSSE2) {
return 0 != (cpu_info[3] & 0x04000000);
}
if (instruction_set_architecture == ISA::kSSE3) {
return 0 != (cpu_info[2] & 0x00000001);
}
#if defined(WEBRTC_ENABLE_AVX2)
if (instruction_set_architecture == ISA::kAVX2) {
int cpu_info7[4];
__cpuid(cpu_info7, 0);
int num_ids = cpu_info7[0];
if (num_ids < 7) {
return false;
}
// Interpret CPU feature information.
__cpuid(cpu_info7, 7);
// AVX instructions can be used when
// a) AVX are supported by the CPU,
// b) XSAVE is supported by the CPU,
// c) XSAVE is enabled by the kernel.
// Compiling with MSVC and /arch:AVX2 surprisingly generates BMI2
// instructions (see crbug.com/1315519).
return (cpu_info[2] & 0x10000000) != 0 /* AVX */ &&
(cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
(cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
(xgetbv(0) & 0x00000006) == 6 /* XSAVE enabled by kernel */ &&
(cpu_info7[1] & 0x00000020) != 0 /* AVX2 */ &&
(cpu_info7[1] & 0x00000100) != 0 /* BMI2 */;
}
#endif // WEBRTC_ENABLE_AVX2
if (instruction_set_architecture == ISA::kFMA3) {
return 0 != (cpu_info[2] & 0x00001000);
}
#elif defined(WEBRTC_ARCH_ARM_FAMILY)
if (instruction_set_architecture == ISA::kNeon) {
#if defined(WEBRTC_ANDROID)
return 0 != (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON);
#elif defined(WEBRTC_LINUX)
uint64_t hwcap = 0;
#if WEBRTC_GLIBC_PREREQ(2, 16)
hwcap = getauxval(AT_HWCAP);
#else
ElfW(auxv_t) auxv;
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd >= 0) {
while (hwcap == 0) {
if (read(fd, &auxv, sizeof(auxv)) < (ssize_t)sizeof(auxv)) {
if (errno == EINTR) {
continue;
}
break;
}
if (AT_HWCAP == auxv.a_type) {
hwcap = auxv.a_un.a_val;
}
}
close(fd);
}
#endif // WEBRTC_GLIBC_PREREQ(2, 16)
#if defined(__aarch64__)
if ((hwcap & HWCAP_ASIMD) != 0) {
return true;
}
#else
if ((hwcap & HWCAP_NEON) != 0) {
return true;
}
#endif
#endif // WEBRTC_LINUX
}
#else
RTC_UNUSED(instruction_set_architecture);
#endif // WEBRTC_ARCH_ARM_FAMILY
return false;
}
} // namespace cpu_info
} // namespace webrtc
|