1// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_TIMER_H_
6#define RUNTIME_VM_TIMER_H_
7
8#include "platform/atomic.h"
9#include "platform/utils.h"
10#include "vm/allocation.h"
11#include "vm/flags.h"
12#include "vm/os.h"
13
14namespace dart {
15
16struct MeasureMonotonic {
17 static inline int64_t Now() { return OS::GetCurrentMonotonicMicros(); }
18};
19
20struct MeasureCpu {
21 static inline int64_t Now() { return OS::GetCurrentThreadCPUMicros(); }
22};
23
24// Timer class allows timing of specific operations in the VM.
25template <typename Measure>
26class TimerImpl : public ValueObject {
27 public:
28 TimerImpl() { Reset(); }
29 ~TimerImpl() {}
30
31 // Start timer.
32 void Start() {
33 start_ = Measure::Now();
34 running_ = true;
35 }
36
37 // Stop timer.
38 void Stop() {
39 ASSERT(start_ != 0);
40 ASSERT(running());
41 stop_ = Measure::Now();
42 int64_t elapsed = ElapsedMicros();
43 max_contiguous_ = Utils::Maximum(x: max_contiguous_.load(), y: elapsed);
44 // Make increment atomic in case it occurs in parallel with aggregation.
45 total_.fetch_add(arg: elapsed);
46 running_ = false;
47 }
48
49 // Get total cumulative elapsed time in micros.
50 int64_t TotalElapsedTime() const {
51 int64_t result = total_;
52 if (running_) {
53 int64_t now = Measure::Now();
54 result += (now - start_);
55 }
56 return result;
57 }
58
59 int64_t MaxContiguous() const {
60 int64_t result = max_contiguous_;
61 if (running_) {
62 int64_t now = Measure::Now();
63 result = Utils::Maximum(x: result, y: now - start_);
64 }
65 return result;
66 }
67
68 void Reset() {
69 start_ = 0;
70 stop_ = 0;
71 total_ = 0;
72 max_contiguous_ = 0;
73 running_ = false;
74 }
75
76 bool IsReset() const {
77 return (start_ == 0) && (stop_ == 0) && (total_ == 0) &&
78 (max_contiguous_ == 0) && !running_;
79 }
80
81 void AddTotal(const TimerImpl& other) { total_.fetch_add(arg: other.total_); }
82
83 // Accessors.
84 bool running() const { return running_; }
85
86 private:
87 friend class Timer;
88
89 explicit TimerImpl(int64_t elapsed)
90 : total_(elapsed), max_contiguous_(elapsed) {}
91
92 int64_t ElapsedMicros() const {
93 ASSERT(start_ != 0);
94 ASSERT(stop_ != 0);
95 return stop_ - start_;
96 }
97
98 RelaxedAtomic<int64_t> start_;
99 RelaxedAtomic<int64_t> stop_;
100 RelaxedAtomic<int64_t> total_;
101 RelaxedAtomic<int64_t> max_contiguous_;
102
103 bool running_ = false;
104
105 DISALLOW_COPY_AND_ASSIGN(TimerImpl);
106};
107
108class Timer : public ValueObject {
109 public:
110 Timer(int64_t elapsed, int64_t elapsed_cpu)
111 : monotonic_(elapsed), cpu_(elapsed) {}
112 Timer() { Reset(); }
113 ~Timer() {}
114
115 // Start timer.
116 void Start() {
117 cpu_.Start();
118 monotonic_.Start();
119 }
120
121 // Stop timer.
122 void Stop() {
123 cpu_.Stop();
124 monotonic_.Stop();
125 }
126
127 // Get total cumulative elapsed time in micros.
128 int64_t TotalElapsedTime() const { return monotonic_.TotalElapsedTime(); }
129 int64_t TotalElapsedTimeCpu() const { return cpu_.TotalElapsedTime(); }
130
131 int64_t MaxContiguous() const { return monotonic_.MaxContiguous(); }
132
133 void Reset() {
134 monotonic_.Reset();
135 cpu_.Reset();
136 }
137
138 bool IsReset() const { return monotonic_.IsReset(); }
139
140 void AddTotal(const Timer& other) {
141 monotonic_.AddTotal(other: other.monotonic_);
142 cpu_.AddTotal(other: other.cpu_);
143 }
144
145 const char* FormatElapsedHumanReadable(Zone* zone) const {
146 return FormatElapsedHumanReadable(zone, total_elapsed: TotalElapsedTime(),
147 total_elapsed_cpu: TotalElapsedTimeCpu());
148 }
149
150 static const char* FormatTime(Zone* zone, int64_t total) {
151 if (total > kMicrosecondsPerSecond) {
152 return OS::SCreate(zone, format: "%6.2f s", MicrosecondsToSeconds(micros: total));
153 } else if (total > kMicrosecondsPerMillisecond) {
154 return OS::SCreate(zone, format: "%6.2f ms", MicrosecondsToMilliseconds(micros: total));
155 } else {
156 return OS::SCreate(zone, format: "%6" Pd64 " \u00B5s", total);
157 }
158 }
159
160 static constexpr double kCpuTimeReportingThreshold = 0.05;
161
162 // Formats the given monotonic and CPU times as a human readable string.
163 //
164 // CPU time is included into the formated string only if
165 // it is |kCpuTimeReportingThreshold| percent different from the monotonic
166 // time.
167 static const char* FormatElapsedHumanReadable(Zone* zone,
168 int64_t total_elapsed,
169 int64_t total_elapsed_cpu) {
170 if ((total_elapsed == 0) ||
171 static_cast<double>(Utils::Abs(x: total_elapsed - total_elapsed_cpu) /
172 total_elapsed) < kCpuTimeReportingThreshold) {
173 return FormatTime(zone, total: total_elapsed);
174 } else {
175 return OS::SCreate(zone, format: "%s (cpu %s)", FormatTime(zone, total: total_elapsed),
176 FormatTime(zone, total: total_elapsed_cpu));
177 }
178 }
179
180 private:
181 TimerImpl<MeasureMonotonic> monotonic_;
182 TimerImpl<MeasureCpu> cpu_;
183
184 DISALLOW_COPY_AND_ASSIGN(Timer);
185};
186
187class TimerScope : public StackResource {
188 public:
189 TimerScope(ThreadState* thread, Timer* timer)
190 : StackResource(thread), timer_(timer) {
191 if (timer_ != nullptr) timer_->Start();
192 }
193 ~TimerScope() {
194 if (timer_ != nullptr) timer_->Stop();
195 }
196
197 private:
198 Timer* const timer_;
199};
200
201class PrintTimeScope : public ValueObject {
202 public:
203 explicit PrintTimeScope(const char* name) : name_(name) { timer_.Start(); }
204 ~PrintTimeScope();
205
206 private:
207 Timer timer_;
208 const char* name_;
209};
210
211} // namespace dart
212
213#endif // RUNTIME_VM_TIMER_H_
214

source code of flutter_engine/third_party/dart/runtime/vm/timer.h