xref: /aosp_15_r20/external/cronet/base/power_monitor/cpu_frequency_utils.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/power_monitor/cpu_frequency_utils.h"
6 
7 #include "base/system/sys_info.h"
8 #include "base/time/time.h"
9 #include "base/timer/elapsed_timer.h"
10 #include "build/build_config.h"
11 
12 #if BUILDFLAG(IS_WIN)
13 #include <windows.h>
14 
15 #include <powerbase.h>
16 #include <winternl.h>
17 #endif
18 
19 namespace base {
20 namespace {
21 
22 #if BUILDFLAG(IS_WIN)
23 // From
24 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa373184(v=vs.85).aspx.
25 // Note that this structure definition was accidentally omitted from WinNT.h.
26 typedef struct _PROCESSOR_POWER_INFORMATION {
27   ULONG Number;
28   ULONG MaxMhz;
29   ULONG CurrentMhz;
30   ULONG MhzLimit;
31   ULONG MaxIdleState;
32   ULONG CurrentIdleState;
33 } PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
34 #endif
35 
36 }  // namespace
37 
EstimateCpuFrequency()38 double EstimateCpuFrequency() {
39 #if defined(ARCH_CPU_X86_FAMILY)
40   // The heuristic to estimate CPU frequency is based on UIforETW code.
41   // see: https://github.com/google/UIforETW/blob/main/UIforETW/CPUFrequency.cpp
42   //      https://github.com/google/UIforETW/blob/main/UIforETW/SpinALot64.asm
43   base::ElapsedTimer timer;
44   const int kAmountOfIterations = 50000;
45   const int kAmountOfInstructions = 10;
46   for (int i = 0; i < kAmountOfIterations; ++i) {
47     __asm__ __volatile__(
48         "addl  %%eax, %%eax\n"
49         "addl  %%eax, %%eax\n"
50         "addl  %%eax, %%eax\n"
51         "addl  %%eax, %%eax\n"
52         "addl  %%eax, %%eax\n"
53         "addl  %%eax, %%eax\n"
54         "addl  %%eax, %%eax\n"
55         "addl  %%eax, %%eax\n"
56         "addl  %%eax, %%eax\n"
57         "addl  %%eax, %%eax\n"
58         :
59         :
60         : "eax");
61   }
62 
63   const base::TimeDelta elapsed = timer.Elapsed();
64   const double estimated_frequency =
65       (kAmountOfIterations * kAmountOfInstructions) / elapsed.InSecondsF();
66   return estimated_frequency;
67 #else
68   return 0.0;
69 #endif
70 }
71 
GetCpuMaxMhz()72 unsigned long GetCpuMaxMhz() {
73 #if BUILDFLAG(IS_WIN)
74   size_t num_cpu = static_cast<size_t>(base::SysInfo::NumberOfProcessors());
75   std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpu);
76   if (!NT_SUCCESS(CallNtPowerInformation(
77           ProcessorInformation, nullptr, 0, &info[0],
78           static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) * num_cpu)))) {
79     return 0;
80   }
81 
82   unsigned long max_mhz = 0;
83   for (const auto& i : info) {
84     max_mhz = std::max(max_mhz, i.MaxMhz);
85   }
86 
87   return max_mhz;
88 #else
89   return 0;
90 #endif
91 }
92 
GetCpuMhzLimit()93 unsigned long GetCpuMhzLimit() {
94 #if BUILDFLAG(IS_WIN)
95   size_t num_cpu = static_cast<size_t>(base::SysInfo::NumberOfProcessors());
96   std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpu);
97   if (!NT_SUCCESS(CallNtPowerInformation(
98           ProcessorInformation, nullptr, 0, &info[0],
99           static_cast<ULONG>(sizeof(PROCESSOR_POWER_INFORMATION) * num_cpu)))) {
100     return 0;
101   }
102 
103   unsigned long mhz_limit = 0;
104   for (const auto& i : info) {
105     mhz_limit = std::max(mhz_limit, i.MhzLimit);
106   }
107 
108   return mhz_limit;
109 #else
110   return 0;
111 #endif
112 }
113 
114 }  // namespace base
115