ml: use clock() to measure the CPU time.
clock() can give higher accuracy compared to
GetPlatformIndependentCPUUsage(). For example, on "eve" and
"nocturne", its resolution is 1e-6 second whereas
GetPlatformIndependentCPUUsage()'s is 1e-3 second.
BUG=chromium:1108264
TEST=on device, it can measure CPU usage less than 0.001.
Change-Id: I86dbf3312ca1648582c9d4bd3c18c99566a2824f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2315717
Reviewed-by: Andrew Moylan <amoylan@chromium.org>
Tested-by: Honglin Yu <honglinyu@chromium.org>
Commit-Queue: Honglin Yu <honglinyu@chromium.org>
diff --git a/ml/request_metrics.cc b/ml/request_metrics.cc
index 8678142..c66b06a 100644
--- a/ml/request_metrics.cc
+++ b/ml/request_metrics.cc
@@ -12,19 +12,19 @@
using chromeos::machine_learning::mojom::LoadModelResult;
-RequestMetrics::RequestMetrics(
- const std::string& model_name, const std::string& request_name)
+RequestMetrics::RequestMetrics(const std::string& model_name,
+ const std::string& request_name)
: name_base_(std::string(kGlobalMetricsPrefix) + model_name + "." +
request_name),
- process_metrics_(nullptr) {}
+ initial_cpu_clock_(0),
+ initial_memory_(0), status_(Status::kNotStarted) {}
void RequestMetrics::StartRecordingPerformanceMetrics() {
- DCHECK(process_metrics_ == nullptr);
- process_metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
- // Call GetPlatformIndependentCPUUsage in order to set the "zero" point of the
- // CPU usage counter of process_metrics_.
- process_metrics_->GetPlatformIndependentCPUUsage();
- timer_.Start();
+ DCHECK(status_ == Status::kNotStarted);
+ // Get initial CPU clock in order to set the "zero" point of the CPU usage
+ // counter.
+ initial_cpu_clock_ = std::clock();
+ DCHECK(initial_cpu_clock_ != static_cast<std::clock_t>(-1));
// Query memory usage.
size_t usage = 0;
if (!GetTotalProcessMemoryUsage(&usage)) {
@@ -32,27 +32,16 @@
return;
}
initial_memory_ = static_cast<int64_t>(usage);
+
+ status_ = Status::kRecording;
}
void RequestMetrics::FinishRecordingPerformanceMetrics() {
- DCHECK(process_metrics_ != nullptr);
- // To get CPU time, we multiply elapsed (wall) time by CPU usage percentage.
- timer_.Stop();
- base::TimeDelta elapsed_time;
- DCHECK(timer_.GetElapsedTime(&elapsed_time));
- const int64_t elapsed_time_microsec = elapsed_time.InMicroseconds();
-
- // CPU usage, 12.34 means 12.34%, and the range is 0 to 100 * numCPUCores.
- // That's to say it can exceed 100 when there're multi CPUs.
- // For example, if the device has 4 CPUs and the process fully uses 2 of
- // them, the percent will be 200%.
- const double cpu_usage_percent =
- process_metrics_->GetPlatformIndependentCPUUsage();
-
- // CPU time, as mentioned above, "100 microseconds" means "1 CPU core fully
- // utilized for 100 microseconds".
- const int64_t cpu_time_microsec =
- static_cast<int64_t>(cpu_usage_percent * elapsed_time_microsec / 100.);
+ DCHECK(status_ == Status::kRecording);
+ status_ = Status::kFinished;
+ // Get CPU time by `clock()`.
+ const int64_t cpu_time_microsec = static_cast<int64_t>(
+ (std::clock() - initial_cpu_clock_) * 1000000.0 / CLOCKS_PER_SEC);
// Memory usage
size_t usage = 0;
diff --git a/ml/request_metrics.h b/ml/request_metrics.h
index 622602e..42223e7 100644
--- a/ml/request_metrics.h
+++ b/ml/request_metrics.h
@@ -5,12 +5,11 @@
#ifndef ML_REQUEST_METRICS_H_
#define ML_REQUEST_METRICS_H_
+#include <ctime>
#include <memory>
#include <string>
#include <base/macros.h>
-#include <base/process/process_metrics.h>
-#include <base/time/time.h>
#include <metrics/metrics_library.h>
#include "metrics/timer.h"
@@ -46,13 +45,30 @@
void FinishRecordingPerformanceMetrics();
private:
+ enum class Status {
+ kNotStarted = 0,
+ kRecording = 1,
+ kEventSent = 2,
+ kFinished = 3
+ };
+
MetricsLibrary metrics_library_;
const std::string name_base_;
- std::unique_ptr<base::ProcessMetrics> process_metrics_;
- chromeos_metrics::Timer timer_;
+ std::clock_t initial_cpu_clock_;
int64_t initial_memory_;
+ // The `status_` serves as a safe guard to ensure the correct usage of the
+ // objects of this class, specifically,
+ // 1. `status_` is initialized to `kNotStarted` at construction.
+ // 2. `StartRecordingPerformanceMetrics()` must be called when
+ // `status_=kNotStarted`. And it sets `status_` to `kRecording`.
+ // 3. `RecordRequestEvent()` must be called when `status_=kRecording` or
+ // `status_=kFinished`, and it sets `status_` to `kEventSent`.
+ // 4. `FinishRecordingPerformanceMetrics()` must be called when
+ // `status_=kRecording` and it sets `status_` to `kEventFinished`.
+ Status status_;
+
DISALLOW_COPY_AND_ASSIGN(RequestMetrics);
};
@@ -72,10 +88,11 @@
template <class RequestEventEnum>
void RequestMetrics::RecordRequestEvent(RequestEventEnum event) {
+ DCHECK(status_ == Status::kRecording || status_ == Status::kFinished);
metrics_library_.SendEnumToUMA(
name_base_ + kEventSuffix, static_cast<int>(event),
static_cast<int>(RequestEventEnum::kMaxValue) + 1);
- process_metrics_.reset(nullptr);
+ status_ = Status::kEventSent;
}
// Records a generic model specification error event during a model loading