1 // Copyright 2011 Google Inc. All Rights Reserved.
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 "metrics.h"
16
17 #include <errno.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include <algorithm>
22 #include <chrono>
23
24 #include "util.h"
25
26 using namespace std;
27
28 Metrics* g_metrics = NULL;
29
30 namespace {
31
32 /// Compute a platform-specific high-res timer value that fits into an int64.
HighResTimer()33 int64_t HighResTimer() {
34 auto now = chrono::steady_clock::now();
35 return chrono::duration_cast<chrono::steady_clock::duration>(
36 now.time_since_epoch())
37 .count();
38 }
39
GetFrequency()40 constexpr int64_t GetFrequency() {
41 // If numerator isn't 1 then we lose precision and that will need to be
42 // assessed.
43 static_assert(std::chrono::steady_clock::period::num == 1,
44 "Numerator must be 1");
45 return std::chrono::steady_clock::period::den /
46 std::chrono::steady_clock::period::num;
47 }
48
TimerToMicros(int64_t dt)49 int64_t TimerToMicros(int64_t dt) {
50 // dt is in ticks. We want microseconds.
51 return chrono::duration_cast<chrono::microseconds>(
52 std::chrono::steady_clock::duration{ dt })
53 .count();
54 }
55
TimerToMicros(double dt)56 int64_t TimerToMicros(double dt) {
57 // dt is in ticks. We want microseconds.
58 using DoubleSteadyClock =
59 std::chrono::duration<double, std::chrono::steady_clock::period>;
60 return chrono::duration_cast<chrono::microseconds>(DoubleSteadyClock{ dt })
61 .count();
62 }
63
64 } // anonymous namespace
65
ScopedMetric(Metric* metric)66 ScopedMetric::ScopedMetric(Metric* metric) {
67 metric_ = metric;
68 if (!metric_)
69 return;
70 start_ = HighResTimer();
71 }
~ScopedMetric()72 ScopedMetric::~ScopedMetric() {
73 if (!metric_)
74 return;
75 metric_->count++;
76 // Leave in the timer's natural frequency to avoid paying the conversion cost
77 // on every measurement.
78 int64_t dt = HighResTimer() - start_;
79 metric_->sum += dt;
80 }
81
NewMetric(const string& name)82 Metric* Metrics::NewMetric(const string& name) {
83 Metric* metric = new Metric;
84 metric->name = name;
85 metric->count = 0;
86 metric->sum = 0;
87 metrics_.push_back(metric);
88 return metric;
89 }
90
Report()91 void Metrics::Report() {
92 int width = 0;
93 for (vector<Metric*>::iterator i = metrics_.begin();
94 i != metrics_.end(); ++i) {
95 width = max((int)(*i)->name.size(), width);
96 }
97
98 printf("%-*s\t%-6s\t%-9s\t%s\n", width,
99 "metric", "count", "avg (us)", "total (ms)");
100 for (vector<Metric*>::iterator i = metrics_.begin();
101 i != metrics_.end(); ++i) {
102 Metric* metric = *i;
103 uint64_t micros = TimerToMicros(metric->sum);
104 double total = micros / (double)1000;
105 double avg = micros / (double)metric->count;
106 printf("%-*s\t%-6d\t%-8.1f\t%.1f\n", width, metric->name.c_str(),
107 metric->count, avg, total);
108 }
109 }
110
Elapsed() const111 double Stopwatch::Elapsed() const {
112 // Convert to micros after converting to double to minimize error.
113 return 1e-6 * TimerToMicros(static_cast<double>(NowRaw() - started_));
114 }
115
NowRaw() const116 uint64_t Stopwatch::NowRaw() const {
117 return HighResTimer();
118 }
119
GetTimeMillis()120 int64_t GetTimeMillis() {
121 return TimerToMicros(HighResTimer()) / 1000;
122 }
123