| /* |
| * 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 |
| static 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 |