0

[base] Precise CPU usage by default on Windows.

Now that we're confident that QueryProcessCycleTime provides more
accurate CPU measurements than GetProcessTimes, use it by default in
ProcessMetrics::GetCumulativeCPUUsage and remove
ProcessMetrics::GetPreciseCumulativeCPUUsage.

Bug: 1313216
Change-Id: I5e22f1c18069bacd1199a14b4ab1ba6b811166b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4943814
Commit-Queue: Francois Pierre Doray <fdoray@chromium.org>
Reviewed-by: Patrick Monette <pmonette@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Joe Mason <joenotcharles@google.com>
Auto-Submit: Francois Pierre Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1218355}
This commit is contained in:
François Doray
2023-11-01 18:59:06 +00:00
committed by Chromium LUCI CQ
parent eb473c5951
commit 0017e24cb3
8 changed files with 27 additions and 136 deletions
base/process
chrome/browser
metrics
performance_manager
components/performance_manager/resource_attribution
content/browser

@ -128,34 +128,6 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
}
#endif
#if BUILDFLAG(IS_WIN)
double ProcessMetrics::GetPreciseCPUUsage(TimeDelta cumulative_cpu) {
TimeTicks time = TimeTicks::Now();
if (last_precise_cumulative_cpu_.is_zero()) {
// First call, just set the last values.
last_precise_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_for_precise_cpu_usage_ = time;
return 0;
}
TimeDelta cpu_time_delta = cumulative_cpu - last_precise_cumulative_cpu_;
TimeDelta time_delta = time - last_cpu_time_for_precise_cpu_usage_;
DCHECK(!time_delta.is_zero());
if (time_delta.is_zero())
return 0;
last_precise_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_for_precise_cpu_usage_ = time;
return 100.0 * cpu_time_delta / time_delta;
}
double ProcessMetrics::GetPreciseCPUUsage() {
return GetPreciseCPUUsage(GetPreciseCumulativeCPUUsage());
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_AIX)
int ProcessMetrics::CalculateIdleWakeupsPerSecond(

@ -136,32 +136,6 @@ class BASE_EXPORT ProcessMetrics {
// will result in a time delta of 2 seconds/per 1 wall-clock second.
[[nodiscard]] TimeDelta GetCumulativeCPUUsage();
#if BUILDFLAG(IS_WIN)
// TODO(pmonette): Remove the precise version of the CPU usage functions once
// we're validated that they are indeed better than the regular version above
// and that they can replace the old implementation.
// Returns the percentage of time spent executing, across all threads of the
// process, in the interval since the last time the method was called, using
// the current |cumulative_cpu|.
//
// Same as GetPlatformIndependentCPUUSage() but implemented using
// `QueryProcessCycleTime` for higher precision.
[[nodiscard]] double GetPreciseCPUUsage(TimeDelta cumulative_cpu);
// Same as the above, but automatically calls GetPreciseCumulativeCPUUsage()
// to determine the current cumulative CPU.
[[nodiscard]] double GetPreciseCPUUsage();
// Returns the cumulative CPU usage across all threads of the process since
// process start. In case of multi-core processors, a process can consume CPU
// at a rate higher than wall-clock time, e.g. two cores at full utilization
// will result in a time delta of 2 seconds/per 1 wall-clock second.
//
// This is implemented using `QueryProcessCycleTime` for higher precision.
[[nodiscard]] TimeDelta GetPreciseCumulativeCPUUsage();
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
BUILDFLAG(IS_AIX)
// Emits the cumulative CPU usage for all currently active threads since they
@ -266,11 +240,6 @@ class BASE_EXPORT ProcessMetrics {
TimeDelta last_cumulative_cpu_;
#endif
#if BUILDFLAG(IS_WIN)
TimeTicks last_cpu_time_for_precise_cpu_usage_;
TimeDelta last_precise_cumulative_cpu_;
#endif
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_AIX)
// Same thing for idle wakeups.

@ -68,19 +68,6 @@ TimeDelta TestCumulativeCPU(ProcessMetrics* metrics, TimeDelta prev_cpu_usage) {
return current_cpu_usage;
}
TimeDelta TestPreciseCumulativeCPU(ProcessMetrics* metrics,
TimeDelta prev_cpu_usage) {
#if BUILDFLAG(IS_WIN)
const TimeDelta current_cpu_usage = metrics->GetPreciseCumulativeCPUUsage();
EXPECT_GE(current_cpu_usage, prev_cpu_usage);
EXPECT_GE(metrics->GetPreciseCPUUsage(), 0.0);
return current_cpu_usage;
#else
// Do nothing. Not supported on this platform.
return base::TimeDelta();
#endif
}
#endif // ENABLE_CPU_TESTS
std::unique_ptr<ProcessMetrics> CreateProcessMetricsForTest(
@ -386,9 +373,6 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
std::unique_ptr<ProcessMetrics> metrics(CreateProcessMetricsForTest(handle));
EXPECT_GE(metrics->GetPlatformIndependentCPUUsage(), 0.0);
#if BUILDFLAG(IS_WIN)
EXPECT_GE(metrics->GetPreciseCPUUsage(), 0.0);
#endif
Thread thread1("thread1");
Thread thread2("thread2");
@ -411,23 +395,15 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
thread3.task_runner()->PostTask(FROM_HERE, BindOnce(&BusyWork, &vec3));
TimeDelta prev_cpu_usage = TestCumulativeCPU(metrics.get(), TimeDelta());
TimeDelta prev_precise_cpu_usage =
TestPreciseCumulativeCPU(metrics.get(), TimeDelta());
thread1.Stop();
prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
prev_precise_cpu_usage =
TestPreciseCumulativeCPU(metrics.get(), prev_precise_cpu_usage);
thread2.Stop();
prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
prev_precise_cpu_usage =
TestPreciseCumulativeCPU(metrics.get(), prev_precise_cpu_usage);
thread3.Stop();
prev_cpu_usage = TestCumulativeCPU(metrics.get(), prev_cpu_usage);
prev_precise_cpu_usage =
TestPreciseCumulativeCPU(metrics.get(), prev_precise_cpu_usage);
}
#endif // ENABLE_CPU_TESTS

@ -120,6 +120,28 @@ struct SYSTEM_PERFORMANCE_INFORMATION {
ULONG SystemCalls;
};
TimeDelta GetImpreciseCumulativeCPUUsage(const win::ScopedHandle& process) {
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (!process.is_valid()) {
return TimeDelta();
}
if (!GetProcessTimes(process.get(), &creation_time, &exit_time, &kernel_time,
&user_time)) {
// This should never fail because we duplicate the handle to guarantee it
// will remain valid.
DCHECK(false);
return TimeDelta();
}
return TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time);
}
} // namespace
size_t GetMaxFds() {
@ -140,31 +162,10 @@ std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (!process_.is_valid())
return TimeDelta();
if (!GetProcessTimes(process_.get(), &creation_time, &exit_time, &kernel_time,
&user_time)) {
// This should never fail because we duplicate the handle to guarantee it
// will remain valid.
DCHECK(false);
return TimeDelta();
}
return TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time);
}
TimeDelta ProcessMetrics::GetPreciseCumulativeCPUUsage() {
#if defined(ARCH_CPU_ARM64)
// Precise CPU usage is not available on Arm CPUs because they don't support
// constant rate TSC.
return GetCumulativeCPUUsage();
return GetImpreciseCumulativeCPUUsage(process_);
#else // !defined(ARCH_CPU_ARM64)
if (!time_internal::HasConstantRateTSC())
return GetCumulativeCPUUsage();
@ -172,10 +173,10 @@ TimeDelta ProcessMetrics::GetPreciseCumulativeCPUUsage() {
const double tsc_ticks_per_second = time_internal::TSCTicksPerSecond();
if (tsc_ticks_per_second == 0) {
// TSC is only initialized once TSCTicksPerSecond() is called twice 50 ms
// apart on the same thread to get a baseline. This often doesn't happen in
// unit tests, and theoretically may happen in production if
// GetPreciseCumulativeCPUUsage() is called before any uses of ThreadTicks.
return GetCumulativeCPUUsage();
// apart on the same thread to get a baseline. In unit tests, it is frequent
// for the initialization not to be complete. In production, it can also
// theoretically happen.
return GetImpreciseCumulativeCPUUsage(process_);
}
ULONG64 process_cycle_time = 0;

@ -58,11 +58,7 @@ std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
ProcessMonitor::Metrics SampleMetrics(base::ProcessMetrics& process_metrics) {
ProcessMonitor::Metrics metrics;
#if BUILDFLAG(IS_WIN)
metrics.cpu_usage = process_metrics.GetPreciseCPUUsage();
#else
metrics.cpu_usage = process_metrics.GetPlatformIndependentCPUUsage();
#endif
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
BUILDFLAG(IS_AIX)
@ -94,16 +90,9 @@ ProcessMonitor::Metrics GetLastIntervalMetrics(
base::ProcessMetrics& process_metrics,
base::TimeDelta cumulative_cpu_usage) {
ProcessMonitor::Metrics metrics;
#if BUILDFLAG(IS_WIN)
metrics.cpu_usage = process_metrics.GetPreciseCPUUsage(cumulative_cpu_usage);
#else
metrics.cpu_usage =
process_metrics.GetPlatformIndependentCPUUsage(cumulative_cpu_usage);
#endif
// TODO: Add other values in ProcessMonitor::Metrics.
return metrics;
}

@ -67,11 +67,7 @@ CPUMeasurementDelegateImpl::CPUMeasurementDelegateImpl(
}
base::TimeDelta CPUMeasurementDelegateImpl::GetCumulativeCPUUsage() {
#if BUILDFLAG(IS_WIN)
return process_metrics_->GetPreciseCumulativeCPUUsage();
#else
return process_metrics_->GetCumulativeCPUUsage();
#endif
}
} // namespace

@ -68,11 +68,7 @@ CPUMeasurementDelegateImpl::CPUMeasurementDelegateImpl(
}
base::TimeDelta CPUMeasurementDelegateImpl::GetCumulativeCPUUsage() {
#if BUILDFLAG(IS_WIN)
return process_metrics_->GetPreciseCumulativeCPUUsage();
#else
return process_metrics_->GetCumulativeCPUUsage();
#endif
}
// Returns true if `result` is in the default-initialized state.

@ -45,15 +45,7 @@ base::TimeDelta GetCPUUsage(base::ProcessHandle process_handle) {
std::unique_ptr<base::ProcessMetrics> process_metrics =
base::ProcessMetrics::CreateProcessMetrics(process_handle);
#endif
#if BUILDFLAG(IS_WIN)
// Use the precise version which is Windows specific.
// TODO(pmonette): Clean up this code when the precise version becomes the
// default.
return process_metrics->GetPreciseCumulativeCPUUsage();
#else
return process_metrics->GetCumulativeCPUUsage();
#endif
}
#endif // !BUILDFLAG(IS_ANDROID)