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