diff options
Diffstat (limited to 'src/utils/cpu_test.cc')
-rw-r--r-- | src/utils/cpu_test.cc | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/utils/cpu_test.cc b/src/utils/cpu_test.cc new file mode 100644 index 0000000..3a01b33 --- /dev/null +++ b/src/utils/cpu_test.cc @@ -0,0 +1,248 @@ +// Copyright 2021 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/utils/cpu.h" + +#if defined(__linux__) +#include <unistd.h> + +#include <cerrno> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#endif // defined(__linux__) + +#include "gtest/gtest.h" +#include "src/utils/logging.h" + +namespace libgav1 { +namespace { + +#if defined(__linux__) + +// Sample code for getting the number of performance CPU cores. The following +// sources were consulted: +// * https://www.kernel.org/doc/html/latest/admin-guide/cputopology.html +// * cpu-hotplug.txt: CPU hotplug Support in Linux(tm) Kernel +// https://lwn.net/Articles/537570/ +// * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu +// * Android bionic source code of get_nprocs(): +// libc/bionic/sysinfo.cpp +// * glibc 2.30 source code of get_nprocs(): +// sysdeps/unix/sysv/linux/getsysstats.c +// +// Tested on: +// * Asus Nexus 7 2013: Qualcomm Snapdragon 600, 32-bit Android 6.0.1 +// (Marshmallow). Brings cores online and offline dynamically. (The tablet +// has 4 cores. "0", "0-1", "0-2", and "0-3" have all been observed in the +// /sys/devices/system/cpu/online file.) This causes the number of cores +// currently online to potentially be lower than the number of cores that can +// be brought online quickly. +// * General Mobile 4G: Qualcomm Snapdragon 410, 32-bit Android 7.1.1 (Nougat). +// * Motorola Moto G5 Plus: Qualcomm Snapdragon 625, 32-bit Android 8.1.0 +// (Oreo). +// * Motorola Moto G7 Play: Qualcomm Snapdragon 632, 32-bit Android 9 (Pie). +// All 8 cores have the same cpuinfo_max_freq (1804800), but there are two +// values of cpuinfo_min_freq: cores 0-3 have 614400 and cores 4-7 have +// 633600. We would need to check cpuinfo_min_freq to differentiate the two +// kinds of cores (Qualcomm Kryo 250 Gold and Qualcomm Kryo 250 Silver). +// * Pixel 2 XL: Qualcomm Snapdragon 835, 64-bit Android 9 (Pie). +// * Pixel 3: Qualcomm Snapdragon 845, 64-bit Android 9 (Pie). +// * Pixel 3a: Qualcomm Snapdragon 670, 64-bit Android 9 (Pie). +// * Samsung Galaxy S6: Samsung Exynos 7 Octa (7420), 64-bit Android 7.0 +// (Nougat). +// * Samsung Galaxy S8+ (SM-G955FD): Samsung Exynos 8895, 64-bit Android 8.0.0. +// +// Note: The sample code needs to use the 'long' type because it is the return +// type of the Standard C Library function strtol(). The ClangTidy warnings are +// suppressed with NOLINT(google-runtime-int) comments. + +// Returns the number of online processor cores. +int GetNumberOfProcessorsOnline() { + // See https://developer.android.com/ndk/guides/cpu-features. + long num_cpus = sysconf(_SC_NPROCESSORS_ONLN); // NOLINT(google-runtime-int) + if (num_cpus < 0) { + LIBGAV1_DLOG(ERROR, "sysconf(_SC_NPROCESSORS_ONLN) failed: %s.", + strerror(errno)); + return 0; + } + // It is safe to cast num_cpus to int. sysconf(_SC_NPROCESSORS_ONLN) returns + // the return value of get_nprocs(), which is an int. + return static_cast<int>(num_cpus); +} + +// These CPUs support heterogeneous multiprocessing. +#if defined(__arm__) || defined(__aarch64__) + +// A helper function used by GetNumberOfPerformanceCoresOnline(). +// +// Returns the cpuinfo_max_freq value (in kHz) of the given CPU. Returns 0 on +// failure. +long GetCpuinfoMaxFreq(int cpu_index) { // NOLINT(google-runtime-int) + char buffer[128]; + const int rv = snprintf( + buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu_index); + if (rv < 0 || rv >= sizeof(buffer)) { + LIBGAV1_DLOG(ERROR, "snprintf failed, or |buffer| is too small."); + return 0; + } + FILE* file = fopen(buffer, "r"); + if (file == nullptr) { + LIBGAV1_DLOG(ERROR, "fopen(\"%s\", \"r\") failed: %s.", buffer, + strerror(errno)); + return 0; + } + char* const str = fgets(buffer, sizeof(buffer), file); + fclose(file); + if (str == nullptr) { + LIBGAV1_DLOG(ERROR, "fgets failed."); + return 0; + } + const long freq = strtol(str, nullptr, 10); // NOLINT(google-runtime-int) + if (freq <= 0 || freq == LONG_MAX) { + LIBGAV1_DLOG(ERROR, + "No conversion can be performed, or the converted value is " + "invalid: %ld.", + freq); + return 0; + } + return freq; +} + +// Returns the number of performance CPU cores that are online. The number of +// efficiency CPU cores is subtracted from the total number of CPU cores. Uses +// cpuinfo_max_freq to determine whether a CPU is a performance core or an +// efficiency core. +// +// This function is not perfect. For example, the Snapdragon 632 SoC used in +// Motorola Moto G7 has performance and efficiency cores with the same +// cpuinfo_max_freq but different cpuinfo_min_freq. This function fails to +// differentiate the two kinds of cores and reports all the cores as +// performance cores. +int GetNumberOfPerformanceCoresOnline() { + // Get the online CPU list. Some examples of the online CPU list are: + // "0-7" + // "0" + // "0-1,2,3,4-7" + char online[512]; + FILE* file = fopen("/sys/devices/system/cpu/online", "r"); + if (file == nullptr) { + LIBGAV1_DLOG(ERROR, + "fopen(\"/sys/devices/system/cpu/online\", \"r\") failed: %s.", + strerror(errno)); + return 0; + } + char* const str = fgets(online, sizeof(online), file); + fclose(file); + file = nullptr; + if (str == nullptr) { + LIBGAV1_DLOG(ERROR, "fgets failed."); + return 0; + } + LIBGAV1_DLOG(INFO, "The online CPU list is %s", online); + + // Count the number of the slowest CPUs. Some SoCs such as Snapdragon 855 + // have performance cores with different max frequencies, so only the slowest + // CPUs are efficiency cores. If we count the number of the fastest CPUs, we + // will fail to count the second fastest performance cores. + long slowest_cpu_freq = LONG_MAX; // NOLINT(google-runtime-int) + int num_slowest_cpus = 0; + int num_cpus = 0; + const char* cp = online; + int range_begin = -1; + while (true) { + char* str_end; + const int cpu = static_cast<int>(strtol(cp, &str_end, 10)); + if (str_end == cp) { + break; + } + cp = str_end; + if (*cp == '-') { + range_begin = cpu; + } else { + if (range_begin == -1) { + range_begin = cpu; + } + + num_cpus += cpu - range_begin + 1; + for (int i = range_begin; i <= cpu; ++i) { + const long freq = GetCpuinfoMaxFreq(i); // NOLINT(google-runtime-int) + if (freq <= 0) { + return 0; + } + LIBGAV1_DLOG(INFO, "cpu%d max frequency is %ld kHz.", i, freq); + if (freq < slowest_cpu_freq) { + slowest_cpu_freq = freq; + num_slowest_cpus = 0; + } + if (freq == slowest_cpu_freq) { + ++num_slowest_cpus; + } + } + + range_begin = -1; + } + if (*cp == '\0') { + break; + } + ++cp; + } + + LIBGAV1_DLOG(INFO, "There are %d CPU cores.", num_cpus); + LIBGAV1_DLOG(INFO, + "%d CPU cores are the slowest, with max frequency %ld kHz.", + num_slowest_cpus, slowest_cpu_freq); + // If there are faster CPU cores than the slowest CPU cores, exclude the + // slowest CPU cores. + if (num_slowest_cpus < num_cpus) { + num_cpus -= num_slowest_cpus; + } + return num_cpus; +} + +#else + +// Assume symmetric multiprocessing. +int GetNumberOfPerformanceCoresOnline() { + return GetNumberOfProcessorsOnline(); +} + +#endif + +#endif // defined(__linux__) + +/* + Run this test with logging enabled on an Android device: + 64-bit Android: + tests/run_android_test.sh --test cpu --enable_asserts + 32-bit Android: + tests/run_android_test.sh --test cpu --arch arm \ + --enable_asserts +*/ +TEST(CpuTest, GetNumberOfPerformanceCoresOnline) { +#if defined(__linux__) + const int num_cpus = GetNumberOfProcessorsOnline(); + ASSERT_NE(num_cpus, 0); + LIBGAV1_DLOG(INFO, "There are %d cores online.", num_cpus); + const int num_performance_cpus = GetNumberOfPerformanceCoresOnline(); + ASSERT_NE(num_performance_cpus, 0); + LIBGAV1_DLOG(INFO, "There are %d performance cores online.", + num_performance_cpus); +#endif // defined(__linux__) +} + +} // namespace +} // namespace libgav1 |