2011-10-05 05:20:07 +00:00
|
|
|
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
|
2016-10-26 07:26:03 +00:00
|
|
|
#ifndef RUNTIME_VM_TIMER_H_
|
|
|
|
#define RUNTIME_VM_TIMER_H_
|
2011-10-05 05:20:07 +00:00
|
|
|
|
2018-03-28 23:16:09 +00:00
|
|
|
#include "platform/atomic.h"
|
2014-04-04 22:13:59 +00:00
|
|
|
#include "platform/utils.h"
|
2011-10-05 05:20:07 +00:00
|
|
|
#include "vm/allocation.h"
|
|
|
|
#include "vm/flags.h"
|
|
|
|
#include "vm/os.h"
|
|
|
|
|
|
|
|
namespace dart {
|
|
|
|
|
2021-05-11 11:15:53 +00:00
|
|
|
struct MeasureMonotonic {
|
|
|
|
static inline int64_t Now() { return OS::GetCurrentMonotonicMicros(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MeasureCpu {
|
|
|
|
static inline int64_t Now() { return OS::GetCurrentThreadCPUMicros(); }
|
|
|
|
};
|
|
|
|
|
2011-10-05 05:20:07 +00:00
|
|
|
// Timer class allows timing of specific operations in the VM.
|
2021-05-11 11:15:53 +00:00
|
|
|
template <typename Measure>
|
|
|
|
class TimerImpl : public ValueObject {
|
2011-10-05 05:20:07 +00:00
|
|
|
public:
|
2021-05-11 11:15:53 +00:00
|
|
|
TimerImpl() { Reset(); }
|
|
|
|
~TimerImpl() {}
|
2011-10-05 05:20:07 +00:00
|
|
|
|
|
|
|
// Start timer.
|
|
|
|
void Start() {
|
2021-05-11 11:15:53 +00:00
|
|
|
start_ = Measure::Now();
|
2014-01-29 18:40:12 +00:00
|
|
|
running_ = true;
|
2011-10-05 05:20:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stop timer.
|
|
|
|
void Stop() {
|
2014-01-29 18:40:12 +00:00
|
|
|
ASSERT(start_ != 0);
|
|
|
|
ASSERT(running());
|
2021-05-11 11:15:53 +00:00
|
|
|
stop_ = Measure::Now();
|
2014-04-04 22:13:59 +00:00
|
|
|
int64_t elapsed = ElapsedMicros();
|
2019-10-09 21:48:20 +00:00
|
|
|
max_contiguous_ = Utils::Maximum(max_contiguous_.load(), elapsed);
|
2016-03-30 20:38:32 +00:00
|
|
|
// Make increment atomic in case it occurs in parallel with aggregation.
|
2019-10-09 21:48:20 +00:00
|
|
|
total_.fetch_add(elapsed);
|
2014-01-29 18:40:12 +00:00
|
|
|
running_ = false;
|
2011-10-05 05:20:07 +00:00
|
|
|
}
|
|
|
|
|
2017-05-01 06:28:01 +00:00
|
|
|
// Get total cumulative elapsed time in micros.
|
2012-05-09 00:03:25 +00:00
|
|
|
int64_t TotalElapsedTime() const {
|
2014-01-29 18:40:12 +00:00
|
|
|
int64_t result = total_;
|
|
|
|
if (running_) {
|
2021-05-11 11:15:53 +00:00
|
|
|
int64_t now = Measure::Now();
|
2014-01-29 18:40:12 +00:00
|
|
|
result += (now - start_);
|
2011-10-05 05:20:07 +00:00
|
|
|
}
|
2014-01-29 18:40:12 +00:00
|
|
|
return result;
|
2011-10-05 05:20:07 +00:00
|
|
|
}
|
|
|
|
|
2014-04-04 22:13:59 +00:00
|
|
|
int64_t MaxContiguous() const {
|
|
|
|
int64_t result = max_contiguous_;
|
|
|
|
if (running_) {
|
2021-05-11 11:15:53 +00:00
|
|
|
int64_t now = Measure::Now();
|
2014-04-04 22:13:59 +00:00
|
|
|
result = Utils::Maximum(result, now - start_);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-05-09 00:03:25 +00:00
|
|
|
void Reset() {
|
2014-01-29 18:40:12 +00:00
|
|
|
start_ = 0;
|
|
|
|
stop_ = 0;
|
|
|
|
total_ = 0;
|
2014-04-04 22:13:59 +00:00
|
|
|
max_contiguous_ = 0;
|
2014-01-29 18:40:12 +00:00
|
|
|
running_ = false;
|
2012-05-09 00:03:25 +00:00
|
|
|
}
|
|
|
|
|
2016-03-30 20:38:32 +00:00
|
|
|
bool IsReset() const {
|
|
|
|
return (start_ == 0) && (stop_ == 0) && (total_ == 0) &&
|
2016-11-08 21:54:47 +00:00
|
|
|
(max_contiguous_ == 0) && !running_;
|
2016-03-30 20:38:32 +00:00
|
|
|
}
|
|
|
|
|
2021-05-11 11:15:53 +00:00
|
|
|
void AddTotal(const TimerImpl& other) { total_.fetch_add(other.total_); }
|
2016-03-30 20:38:32 +00:00
|
|
|
|
2011-10-05 05:20:07 +00:00
|
|
|
// Accessors.
|
2012-01-17 18:29:54 +00:00
|
|
|
bool running() const { return running_; }
|
2011-10-05 05:20:07 +00:00
|
|
|
|
|
|
|
private:
|
2021-05-11 11:15:53 +00:00
|
|
|
friend class Timer;
|
|
|
|
|
|
|
|
explicit TimerImpl(int64_t elapsed)
|
|
|
|
: total_(elapsed), max_contiguous_(elapsed) {}
|
|
|
|
|
2011-10-05 05:20:07 +00:00
|
|
|
int64_t ElapsedMicros() const {
|
2014-01-29 18:40:12 +00:00
|
|
|
ASSERT(start_ != 0);
|
|
|
|
ASSERT(stop_ != 0);
|
|
|
|
return stop_ - start_;
|
2011-10-05 05:20:07 +00:00
|
|
|
}
|
|
|
|
|
2019-10-09 21:48:20 +00:00
|
|
|
RelaxedAtomic<int64_t> start_;
|
|
|
|
RelaxedAtomic<int64_t> stop_;
|
|
|
|
RelaxedAtomic<int64_t> total_;
|
|
|
|
RelaxedAtomic<int64_t> max_contiguous_;
|
2021-05-11 11:15:53 +00:00
|
|
|
|
|
|
|
bool running_ = false;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(TimerImpl);
|
|
|
|
};
|
|
|
|
|
|
|
|
class Timer : public ValueObject {
|
|
|
|
public:
|
|
|
|
Timer(int64_t elapsed, int64_t elapsed_cpu)
|
|
|
|
: monotonic_(elapsed), cpu_(elapsed) {}
|
|
|
|
Timer() { Reset(); }
|
|
|
|
~Timer() {}
|
|
|
|
|
|
|
|
// Start timer.
|
|
|
|
void Start() {
|
|
|
|
cpu_.Start();
|
|
|
|
monotonic_.Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop timer.
|
|
|
|
void Stop() {
|
|
|
|
cpu_.Stop();
|
|
|
|
monotonic_.Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get total cumulative elapsed time in micros.
|
|
|
|
int64_t TotalElapsedTime() const { return monotonic_.TotalElapsedTime(); }
|
|
|
|
int64_t TotalElapsedTimeCpu() const { return cpu_.TotalElapsedTime(); }
|
|
|
|
|
|
|
|
int64_t MaxContiguous() const { return monotonic_.MaxContiguous(); }
|
|
|
|
|
|
|
|
void Reset() {
|
|
|
|
monotonic_.Reset();
|
|
|
|
cpu_.Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsReset() const { return monotonic_.IsReset(); }
|
|
|
|
|
|
|
|
void AddTotal(const Timer& other) {
|
|
|
|
monotonic_.AddTotal(other.monotonic_);
|
|
|
|
cpu_.AddTotal(other.cpu_);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* FormatElapsedHumanReadable(Zone* zone) const {
|
|
|
|
return FormatElapsedHumanReadable(zone, TotalElapsedTime(),
|
|
|
|
TotalElapsedTimeCpu());
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* FormatTime(Zone* zone, int64_t total) {
|
|
|
|
if (total > kMicrosecondsPerSecond) {
|
|
|
|
return OS::SCreate(zone, "%6.2f s", MicrosecondsToSeconds(total));
|
|
|
|
} else if (total > kMicrosecondsPerMillisecond) {
|
|
|
|
return OS::SCreate(zone, "%6.2f ms", MicrosecondsToMilliseconds(total));
|
|
|
|
} else {
|
|
|
|
return OS::SCreate(zone, "%6" Pd64 " \u00B5s", total);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr double kCpuTimeReportingThreshold = 0.05;
|
|
|
|
|
|
|
|
// Formats the given monotonic and CPU times as a human readable string.
|
|
|
|
//
|
|
|
|
// CPU time is included into the formated string only if
|
|
|
|
// it is |kCpuTimeReportingThreshold| percent different from the monotonic
|
|
|
|
// time.
|
|
|
|
static const char* FormatElapsedHumanReadable(Zone* zone,
|
|
|
|
int64_t total_elapsed,
|
|
|
|
int64_t total_elapsed_cpu) {
|
|
|
|
if ((total_elapsed == 0) ||
|
|
|
|
static_cast<double>(Utils::Abs(total_elapsed - total_elapsed_cpu) /
|
|
|
|
total_elapsed) < kCpuTimeReportingThreshold) {
|
|
|
|
return FormatTime(zone, total_elapsed);
|
|
|
|
} else {
|
|
|
|
return OS::SCreate(zone, "%s (cpu %s)", FormatTime(zone, total_elapsed),
|
|
|
|
FormatTime(zone, total_elapsed_cpu));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
TimerImpl<MeasureMonotonic> monotonic_;
|
|
|
|
TimerImpl<MeasureCpu> cpu_;
|
2011-10-05 05:20:07 +00:00
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Timer);
|
|
|
|
};
|
|
|
|
|
2021-05-11 11:15:53 +00:00
|
|
|
class TimerScope : public StackResource {
|
|
|
|
public:
|
|
|
|
TimerScope(ThreadState* thread, Timer* timer)
|
|
|
|
: StackResource(thread), timer_(timer) {
|
|
|
|
if (timer_ != nullptr) timer_->Start();
|
|
|
|
}
|
|
|
|
~TimerScope() {
|
|
|
|
if (timer_ != nullptr) timer_->Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Timer* const timer_;
|
|
|
|
};
|
|
|
|
|
2021-10-05 16:32:40 +00:00
|
|
|
class PrintTimeScope : public ValueObject {
|
|
|
|
public:
|
|
|
|
explicit PrintTimeScope(const char* name) : name_(name) { timer_.Start(); }
|
|
|
|
~PrintTimeScope();
|
|
|
|
|
|
|
|
private:
|
|
|
|
Timer timer_;
|
|
|
|
const char* name_;
|
|
|
|
};
|
|
|
|
|
2011-10-05 05:20:07 +00:00
|
|
|
} // namespace dart
|
|
|
|
|
2016-10-26 07:26:03 +00:00
|
|
|
#endif // RUNTIME_VM_TIMER_H_
|