xref: /aosp_15_r20/external/libgav1/src/utils/cpu_test.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/utils/cpu.h"
16 
17 #if defined(__linux__)
18 #include <unistd.h>
19 
20 #include <cerrno>
21 #include <climits>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #endif  // defined(__linux__)
26 
27 #include "gtest/gtest.h"
28 #include "src/utils/logging.h"
29 
30 namespace libgav1 {
31 namespace {
32 
33 #if defined(__linux__)
34 
35 // Sample code for getting the number of performance CPU cores. The following
36 // sources were consulted:
37 // * https://www.kernel.org/doc/html/latest/admin-guide/cputopology.html
38 // * cpu-hotplug.txt: CPU hotplug Support in Linux(tm) Kernel
39 //   https://lwn.net/Articles/537570/
40 // * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu
41 // * Android bionic source code of get_nprocs():
42 //   libc/bionic/sysinfo.cpp
43 // * glibc 2.30 source code of get_nprocs():
44 //   sysdeps/unix/sysv/linux/getsysstats.c
45 //
46 // Tested on:
47 // * Asus Nexus 7 2013: Qualcomm Snapdragon 600, 32-bit Android 6.0.1
48 //   (Marshmallow). Brings cores online and offline dynamically. (The tablet
49 //   has 4 cores. "0", "0-1", "0-2", and "0-3" have all been observed in the
50 //   /sys/devices/system/cpu/online file.) This causes the number of cores
51 //   currently online to potentially be lower than the number of cores that can
52 //   be brought online quickly.
53 // * General Mobile 4G: Qualcomm Snapdragon 410, 32-bit Android 7.1.1 (Nougat).
54 // * Motorola Moto G5 Plus: Qualcomm Snapdragon 625, 32-bit Android 8.1.0
55 //   (Oreo).
56 // * Motorola Moto G7 Play: Qualcomm Snapdragon 632, 32-bit Android 9 (Pie).
57 //   All 8 cores have the same cpuinfo_max_freq (1804800), but there are two
58 //   values of cpuinfo_min_freq: cores 0-3 have 614400 and cores 4-7 have
59 //   633600. We would need to check cpuinfo_min_freq to differentiate the two
60 //   kinds of cores (Qualcomm Kryo 250 Gold and Qualcomm Kryo 250 Silver).
61 // * Pixel 2 XL: Qualcomm Snapdragon 835, 64-bit Android 9 (Pie).
62 // * Pixel 3: Qualcomm Snapdragon 845, 64-bit Android 9 (Pie).
63 // * Pixel 3a: Qualcomm Snapdragon 670, 64-bit Android 9 (Pie).
64 // * Samsung Galaxy S6: Samsung Exynos 7 Octa (7420), 64-bit Android 7.0
65 //   (Nougat).
66 // * Samsung Galaxy S8+ (SM-G955FD): Samsung Exynos 8895, 64-bit Android 8.0.0.
67 //
68 // Note: The sample code needs to use the 'long' type because it is the return
69 // type of the Standard C Library function strtol(). The ClangTidy warnings are
70 // suppressed with NOLINT(google-runtime-int) comments.
71 
72 // Returns the number of online processor cores.
GetNumberOfProcessorsOnline()73 int GetNumberOfProcessorsOnline() {
74   // See https://developer.android.com/ndk/guides/cpu-features.
75   long num_cpus = sysconf(_SC_NPROCESSORS_ONLN);  // NOLINT(google-runtime-int)
76   if (num_cpus < 0) {
77     LIBGAV1_DLOG(ERROR, "sysconf(_SC_NPROCESSORS_ONLN) failed: %s.",
78                  strerror(errno));
79     return 0;
80   }
81   // It is safe to cast num_cpus to int. sysconf(_SC_NPROCESSORS_ONLN) returns
82   // the return value of get_nprocs(), which is an int.
83   return static_cast<int>(num_cpus);
84 }
85 
86 // These CPUs support heterogeneous multiprocessing.
87 #if defined(__arm__) || defined(__aarch64__)
88 
89 // A helper function used by GetNumberOfPerformanceCoresOnline().
90 //
91 // Returns the cpuinfo_max_freq value (in kHz) of the given CPU. Returns 0 on
92 // failure.
GetCpuinfoMaxFreq(int cpu_index)93 long GetCpuinfoMaxFreq(int cpu_index) {  // NOLINT(google-runtime-int)
94   char buffer[128];
95   const int rv = snprintf(
96       buffer, sizeof(buffer),
97       "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu_index);
98   if (rv < 0 || rv >= sizeof(buffer)) {
99     LIBGAV1_DLOG(ERROR, "snprintf failed, or |buffer| is too small.");
100     return 0;
101   }
102   FILE* file = fopen(buffer, "r");
103   if (file == nullptr) {
104     LIBGAV1_DLOG(ERROR, "fopen(\"%s\", \"r\") failed: %s.", buffer,
105                  strerror(errno));
106     return 0;
107   }
108   char* const str = fgets(buffer, sizeof(buffer), file);
109   fclose(file);
110   if (str == nullptr) {
111     LIBGAV1_DLOG(ERROR, "fgets failed.");
112     return 0;
113   }
114   const long freq = strtol(str, nullptr, 10);  // NOLINT(google-runtime-int)
115   if (freq <= 0 || freq == LONG_MAX) {
116     LIBGAV1_DLOG(ERROR,
117                  "No conversion can be performed, or the converted value is "
118                  "invalid: %ld.",
119                  freq);
120     return 0;
121   }
122   return freq;
123 }
124 
125 // Returns the number of performance CPU cores that are online. The number of
126 // efficiency CPU cores is subtracted from the total number of CPU cores. Uses
127 // cpuinfo_max_freq to determine whether a CPU is a performance core or an
128 // efficiency core.
129 //
130 // This function is not perfect. For example, the Snapdragon 632 SoC used in
131 // Motorola Moto G7 has performance and efficiency cores with the same
132 // cpuinfo_max_freq but different cpuinfo_min_freq. This function fails to
133 // differentiate the two kinds of cores and reports all the cores as
134 // performance cores.
GetNumberOfPerformanceCoresOnline()135 int GetNumberOfPerformanceCoresOnline() {
136   // Get the online CPU list. Some examples of the online CPU list are:
137   //   "0-7"
138   //   "0"
139   //   "0-1,2,3,4-7"
140   char online[512];
141   FILE* file = fopen("/sys/devices/system/cpu/online", "r");
142   if (file == nullptr) {
143     LIBGAV1_DLOG(ERROR,
144                  "fopen(\"/sys/devices/system/cpu/online\", \"r\") failed: %s.",
145                  strerror(errno));
146     return 0;
147   }
148   char* const str = fgets(online, sizeof(online), file);
149   fclose(file);
150   file = nullptr;
151   if (str == nullptr) {
152     LIBGAV1_DLOG(ERROR, "fgets failed.");
153     return 0;
154   }
155   LIBGAV1_DLOG(INFO, "The online CPU list is %s", online);
156 
157   // Count the number of the slowest CPUs. Some SoCs such as Snapdragon 855
158   // have performance cores with different max frequencies, so only the slowest
159   // CPUs are efficiency cores. If we count the number of the fastest CPUs, we
160   // will fail to count the second fastest performance cores.
161   long slowest_cpu_freq = LONG_MAX;  // NOLINT(google-runtime-int)
162   int num_slowest_cpus = 0;
163   int num_cpus = 0;
164   const char* cp = online;
165   int range_begin = -1;
166   while (true) {
167     char* str_end;
168     const int cpu = static_cast<int>(strtol(cp, &str_end, 10));
169     if (str_end == cp) {
170       break;
171     }
172     cp = str_end;
173     if (*cp == '-') {
174       range_begin = cpu;
175     } else {
176       if (range_begin == -1) {
177         range_begin = cpu;
178       }
179 
180       num_cpus += cpu - range_begin + 1;
181       for (int i = range_begin; i <= cpu; ++i) {
182         const long freq = GetCpuinfoMaxFreq(i);  // NOLINT(google-runtime-int)
183         if (freq <= 0) {
184           return 0;
185         }
186         LIBGAV1_DLOG(INFO, "cpu%d max frequency is %ld kHz.", i, freq);
187         if (freq < slowest_cpu_freq) {
188           slowest_cpu_freq = freq;
189           num_slowest_cpus = 0;
190         }
191         if (freq == slowest_cpu_freq) {
192           ++num_slowest_cpus;
193         }
194       }
195 
196       range_begin = -1;
197     }
198     if (*cp == '\0') {
199       break;
200     }
201     ++cp;
202   }
203 
204   LIBGAV1_DLOG(INFO, "There are %d CPU cores.", num_cpus);
205   LIBGAV1_DLOG(INFO,
206                "%d CPU cores are the slowest, with max frequency %ld kHz.",
207                num_slowest_cpus, slowest_cpu_freq);
208   // If there are faster CPU cores than the slowest CPU cores, exclude the
209   // slowest CPU cores.
210   if (num_slowest_cpus < num_cpus) {
211     num_cpus -= num_slowest_cpus;
212   }
213   return num_cpus;
214 }
215 
216 #else
217 
218 // Assume symmetric multiprocessing.
GetNumberOfPerformanceCoresOnline()219 int GetNumberOfPerformanceCoresOnline() {
220   return GetNumberOfProcessorsOnline();
221 }
222 
223 #endif
224 
225 #endif  // defined(__linux__)
226 
227 /*
228   Run this test with logging enabled on an Android device:
229   64-bit Android:
230     tests/run_android_test.sh --test cpu --enable_asserts
231   32-bit Android:
232     tests/run_android_test.sh --test cpu --arch arm \
233         --enable_asserts
234 */
TEST(CpuTest,GetNumberOfPerformanceCoresOnline)235 TEST(CpuTest, GetNumberOfPerformanceCoresOnline) {
236 #if defined(__linux__)
237   const int num_cpus = GetNumberOfProcessorsOnline();
238   ASSERT_NE(num_cpus, 0);
239   LIBGAV1_DLOG(INFO, "There are %d cores online.", num_cpus);
240   const int num_performance_cpus = GetNumberOfPerformanceCoresOnline();
241   ASSERT_NE(num_performance_cpus, 0);
242   LIBGAV1_DLOG(INFO, "There are %d performance cores online.",
243                num_performance_cpus);
244 #endif  // defined(__linux__)
245 }
246 
247 }  // namespace
248 }  // namespace libgav1
249