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
26using namespace std;
27
28Metrics* g_metrics = NULL;
29
30namespace {
31
32/// Compute a platform-specific high-res timer value that fits into an int64.
33int64_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
40constexpr 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
49int64_t TimerToMicros(int64_t dt) {
50 // dt is in ticks. We want microseconds.
51 return (dt * 1000000) / GetFrequency();
52}
53
54int64_t TimerToMicros(double dt) {
55 // dt is in ticks. We want microseconds.
56 return (dt * 1000000) / GetFrequency();
57}
58
59} // anonymous namespace
60
61ScopedMetric::ScopedMetric(Metric* metric) {
62 metric_ = metric;
63 if (!metric_)
64 return;
65 start_ = HighResTimer();
66}
67ScopedMetric::~ScopedMetric() {
68 if (!metric_)
69 return;
70 metric_->count++;
71 // Leave in the timer's natural frequency to avoid paying the conversion cost
72 // on every measurement.
73 int64_t dt = HighResTimer() - start_;
74 metric_->sum += dt;
75}
76
77Metric* Metrics::NewMetric(const string& name) {
78 Metric* metric = new Metric;
79 metric->name = name;
80 metric->count = 0;
81 metric->sum = 0;
82 metrics_.push_back(metric);
83 return metric;
84}
85
86void Metrics::Report() {
87 int width = 0;
88 for (vector<Metric*>::iterator i = metrics_.begin();
89 i != metrics_.end(); ++i) {
90 width = max((int)(*i)->name.size(), width);
91 }
92
93 printf("%-*s\t%-6s\t%-9s\t%s\n", width,
94 "metric", "count", "avg (us)", "total (ms)");
95 for (vector<Metric*>::iterator i = metrics_.begin();
96 i != metrics_.end(); ++i) {
97 Metric* metric = *i;
98 uint64_t micros = TimerToMicros(metric->sum);
99 double total = micros / (double)1000;
100 double avg = micros / (double)metric->count;
101 printf("%-*s\t%-6d\t%-8.1f\t%.1f\n", width, metric->name.c_str(),
102 metric->count, avg, total);
103 }
104}
105
106double Stopwatch::Elapsed() const {
107 // Convert to micros after converting to double to minimize error.
108 return 1e-6 * TimerToMicros(static_cast<double>(NowRaw() - started_));
109}
110
111uint64_t Stopwatch::NowRaw() const {
112 return HighResTimer();
113}
114
115int64_t GetTimeMillis() {
116 return TimerToMicros(HighResTimer()) / 1000;
117}
118