summaryrefslogtreecommitdiffstats
path: root/runtime/base
diff options
context:
space:
mode:
authorMathieu Chartier <mathieuc@google.com>2014-06-20 10:37:54 -0700
committerMathieu Chartier <mathieuc@google.com>2014-06-24 13:45:05 -0700
commitf5997b4d3f889569d5a2b724d83d764bfbb8d106 (patch)
treef0d3b3890a604a8b3c4c87c0f408f86df041690e /runtime/base
parent9658d24deaa9bd07781bfac860d2a6dd89066d55 (diff)
downloadandroid_art-f5997b4d3f889569d5a2b724d83d764bfbb8d106.tar.gz
android_art-f5997b4d3f889569d5a2b724d83d764bfbb8d106.tar.bz2
android_art-f5997b4d3f889569d5a2b724d83d764bfbb8d106.zip
More advanced timing loggers.
The new timing loggers have lower overhead since they only push into a vector. The new format has two types, a start timing and a stop timing. You can thing of these as brackets associated with a timestamp. It uses these to construct various statistics when needed, such as: Total time, exclusive time, and nesting depth. Changed PrettyDuration to have a default of 3 digits after the decimal point. Exaple of a GC dump with exclusive / total times and indenting: I/art (23546): GC iteration timing logger [Exclusive time] [Total time] I/art (23546): 0ms InitializePhase I/art (23546): 0.305ms/167.746ms MarkingPhase I/art (23546): 0ms BindBitmaps I/art (23546): 0ms FindDefaultSpaceBitmap I/art (23546): 0ms/1.709ms ProcessCards I/art (23546): 0.183ms ImageModUnionClearCards I/art (23546): 0.916ms ZygoteModUnionClearCards I/art (23546): 0.610ms AllocSpaceClearCards I/art (23546): 1.373ms AllocSpaceClearCards I/art (23546): 0.305ms/6.318ms MarkRoots I/art (23546): 2.106ms MarkRootsCheckpoint I/art (23546): 0.153ms MarkNonThreadRoots I/art (23546): 4.287ms MarkConcurrentRoots I/art (23546): 43.461ms UpdateAndMarkImageModUnionTable I/art (23546): 0ms/112.712ms RecursiveMark I/art (23546): 112.712ms ProcessMarkStack I/art (23546): 0.610ms/2.777ms PreCleanCards I/art (23546): 0.305ms/0.855ms ProcessCards I/art (23546): 0.153ms ImageModUnionClearCards I/art (23546): 0.610ms ZygoteModUnionClearCards I/art (23546): 0.610ms AllocSpaceClearCards I/art (23546): 0.549ms AllocSpaceClearCards I/art (23546): 0.549ms MarkRootsCheckpoint I/art (23546): 0.610ms MarkNonThreadRoots I/art (23546): 0ms MarkConcurrentRoots I/art (23546): 0.610ms ScanGrayImageSpaceObjects I/art (23546): 0.305ms ScanGrayZygoteSpaceObjects I/art (23546): 0.305ms ScanGrayAllocSpaceObjects I/art (23546): 1.129ms ScanGrayAllocSpaceObjects I/art (23546): 0ms ProcessMarkStack I/art (23546): 0ms/0.977ms (Paused)PausePhase I/art (23546): 0.244ms ReMarkRoots I/art (23546): 0.672ms (Paused)ScanGrayObjects I/art (23546): 0ms (Paused)ProcessMarkStack I/art (23546): 0ms/0.610ms SwapStacks I/art (23546): 0.610ms RevokeAllThreadLocalAllocationStacks I/art (23546): 0ms PreSweepingGcVerification I/art (23546): 0ms/10.621ms ReclaimPhase I/art (23546): 0.610ms/0.702ms ProcessReferences I/art (23546): 0.214ms/0.641ms EnqueueFinalizerReferences I/art (23546): 0.427ms ProcessMarkStack I/art (23546): 0.488ms SweepSystemWeaks I/art (23546): 0.824ms/9.400ms Sweep I/art (23546): 0ms SweepMallocSpace I/art (23546): 0.214ms SweepZygoteSpace I/art (23546): 0.122ms SweepMallocSpace I/art (23546): 6.226ms SweepMallocSpace I/art (23546): 0ms SweepMallocSpace I/art (23546): 2.144ms SweepLargeObjects I/art (23546): 0.305ms SwapBitmaps I/art (23546): 0ms UnBindBitmaps I/art (23546): 0.275ms FinishPhase I/art (23546): GC iteration timing logger: end, 178.971ms Change-Id: Ia55b65609468f212b3cd65cda66b843da42be645
Diffstat (limited to 'runtime/base')
-rw-r--r--runtime/base/histogram-inl.h20
-rw-r--r--runtime/base/timing_logger.cc233
-rw-r--r--runtime/base/timing_logger.h156
-rw-r--r--runtime/base/timing_logger_test.cc197
4 files changed, 298 insertions, 308 deletions
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index 7c0999992e..4c18ce405c 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -164,18 +164,18 @@ inline void Histogram<Value>::PrintBins(std::ostream& os, const CumulativeData&
template <class Value>
inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double interval,
const CumulativeData& data) const {
+ static constexpr size_t kFractionalDigits = 3;
DCHECK_GT(interval, 0);
DCHECK_LT(interval, 1.0);
-
- double per_0 = (1.0 - interval) / 2.0;
- double per_1 = per_0 + interval;
- TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust);
- os << Name() << ":\tSum: ";
- os << PrettyDuration(Sum() * kAdjust) << " ";
- os << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit);
- os << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit) << " ";
- os << "Avg: " << FormatDuration(Mean() * kAdjust, unit) << " Max: ";
- os << FormatDuration(Max() * kAdjust, unit) << "\n";
+ const double per_0 = (1.0 - interval) / 2.0;
+ const double per_1 = per_0 + interval;
+ const TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust);
+ os << Name() << ":\tSum: " << PrettyDuration(Sum() * kAdjust) << " "
+ << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit,
+ kFractionalDigits)
+ << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit, kFractionalDigits) << " "
+ << "Avg: " << FormatDuration(Mean() * kAdjust, unit, kFractionalDigits) << " Max: "
+ << FormatDuration(Max() * kAdjust, unit, kFractionalDigits) << "\n";
}
template <class Value>
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index a1550028e9..b6a2aaf33b 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -33,6 +33,8 @@ namespace art {
constexpr size_t CumulativeLogger::kLowMemoryBucketCount;
constexpr size_t CumulativeLogger::kDefaultBucketCount;
+constexpr size_t TimingLogger::kIndexNotFound;
+
CumulativeLogger::CumulativeLogger(const std::string& name)
: name_(name),
lock_name_("CumulativeLoggerLock" + name),
@@ -66,10 +68,12 @@ void CumulativeLogger::Reset() {
void CumulativeLogger::AddLogger(const TimingLogger &logger) {
MutexLock mu(Thread::Current(), lock_);
- for (const TimingLogger::SplitTiming& split : logger.GetSplits()) {
- uint64_t split_time = split.first;
- const char* split_name = split.second;
- AddPair(split_name, split_time);
+ TimingLogger::TimingData timing_data(logger.CalculateTimingData());
+ const std::vector<TimingLogger::Timing>& timings = logger.GetTimings();
+ for (size_t i = 0; i < timings.size(); ++i) {
+ if (timings[i].IsStartTiming()) {
+ AddPair(timings[i].GetName(), timing_data.GetExclusiveTime(i));
+ }
}
++iterations_;
}
@@ -124,166 +128,125 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) const {
}
TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
- : name_(name), precise_(precise), verbose_(verbose), current_split_(NULL) {
+ : name_(name), precise_(precise), verbose_(verbose) {
}
void TimingLogger::Reset() {
- current_split_ = NULL;
- splits_.clear();
+ timings_.clear();
}
-void TimingLogger::StartSplit(const char* new_split_label) {
- DCHECK(new_split_label != nullptr) << "Starting split with null label.";
- TimingLogger::ScopedSplit* explicit_scoped_split =
- new TimingLogger::ScopedSplit(new_split_label, this);
- explicit_scoped_split->explicit_ = true;
+void TimingLogger::StartTiming(const char* label) {
+ DCHECK(label != nullptr);
+ timings_.push_back(Timing(NanoTime(), label));
+ ATRACE_BEGIN(label);
}
-void TimingLogger::EndSplit() {
- CHECK(current_split_ != nullptr) << "Ending a non-existent split.";
- DCHECK(current_split_->label_ != nullptr);
- DCHECK(current_split_->explicit_ == true)
- << "Explicitly ending scoped split: " << current_split_->label_;
- delete current_split_;
- // TODO: current_split_ = nullptr;
+void TimingLogger::EndTiming() {
+ timings_.push_back(Timing(NanoTime(), nullptr));
+ ATRACE_END();
}
-// Ends the current split and starts the one given by the label.
-void TimingLogger::NewSplit(const char* new_split_label) {
- if (current_split_ == nullptr) {
- StartSplit(new_split_label);
- } else {
- DCHECK(new_split_label != nullptr) << "New split (" << new_split_label << ") with null label.";
- current_split_->TailInsertSplit(new_split_label);
+uint64_t TimingLogger::GetTotalNs() const {
+ if (timings_.size() < 2) {
+ return 0;
}
+ return timings_.back().GetTime() - timings_.front().GetTime();
}
-uint64_t TimingLogger::GetTotalNs() const {
- uint64_t total_ns = 0;
- for (const TimingLogger::SplitTiming& split : splits_) {
- total_ns += split.first;
+size_t TimingLogger::FindTimingIndex(const char* name, size_t start_idx) const {
+ DCHECK_LT(start_idx, timings_.size());
+ for (size_t i = start_idx; i < timings_.size(); ++i) {
+ if (timings_[i].IsStartTiming() && strcmp(timings_[i].GetName(), name) == 0) {
+ return i;
+ }
}
- return total_ns;
+ return kIndexNotFound;
+}
+
+TimingLogger::TimingData TimingLogger::CalculateTimingData() const {
+ TimingLogger::TimingData ret;
+ ret.data_.resize(timings_.size());
+ std::vector<size_t> open_stack;
+ for (size_t i = 0; i < timings_.size(); ++i) {
+ if (timings_[i].IsEndTiming()) {
+ CHECK(!open_stack.empty()) << "No starting split for ending split at index " << i;
+ size_t open_idx = open_stack.back();
+ uint64_t time = timings_[i].GetTime() - timings_[open_idx].GetTime();
+ ret.data_[open_idx].exclusive_time += time;
+ DCHECK_EQ(ret.data_[open_idx].total_time, 0U);
+ ret.data_[open_idx].total_time += time;
+ // Each open split has exactly one end.
+ open_stack.pop_back();
+ // If there is a parent node, subtract from the exclusive time.
+ if (!open_stack.empty()) {
+ // Note this may go negative, but will work due to 2s complement when we add the value
+ // total time value later.
+ ret.data_[open_stack.back()].exclusive_time -= time;
+ }
+ } else {
+ open_stack.push_back(i);
+ }
+ }
+ CHECK(open_stack.empty()) << "Missing ending for timing "
+ << timings_[open_stack.back()].GetName() << " at index " << open_stack.back();
+ return ret; // No need to fear, C++11 move semantics are here.
}
-void TimingLogger::Dump(std::ostream &os) const {
+void TimingLogger::Dump(std::ostream &os, const char* indent_string) const {
+ static constexpr size_t kFractionalDigits = 3;
+ TimingLogger::TimingData timing_data(CalculateTimingData());
uint64_t longest_split = 0;
- uint64_t total_ns = 0;
- for (const SplitTiming& split : splits_) {
- uint64_t split_time = split.first;
- longest_split = std::max(longest_split, split_time);
- total_ns += split_time;
+ for (size_t i = 0; i < timings_.size(); ++i) {
+ longest_split = std::max(longest_split, timing_data.GetTotalTime(i));
}
// Compute which type of unit we will use for printing the timings.
TimeUnit tu = GetAppropriateTimeUnit(longest_split);
uint64_t divisor = GetNsToTimeUnitDivisor(tu);
+ uint64_t mod_fraction = divisor >= 1000 ? divisor / 1000 : 1;
// Print formatted splits.
- for (const SplitTiming& split : splits_) {
- uint64_t split_time = split.first;
- if (!precise_ && divisor >= 1000) {
- // Make the fractional part 0.
- split_time -= split_time % (divisor / 1000);
+ size_t tab_count = 1;
+ os << name_ << " [Exclusive time] [Total time]\n";
+ for (size_t i = 0; i < timings_.size(); ++i) {
+ if (timings_[i].IsStartTiming()) {
+ uint64_t exclusive_time = timing_data.GetExclusiveTime(i);
+ uint64_t total_time = timing_data.GetTotalTime(i);
+ if (!precise_) {
+ // Make the fractional part 0.
+ exclusive_time -= exclusive_time % mod_fraction;
+ total_time -= total_time % mod_fraction;
+ }
+ for (size_t j = 0; j < tab_count; ++j) {
+ os << indent_string;
+ }
+ os << FormatDuration(exclusive_time, tu, kFractionalDigits);
+ // If they are the same, just print one value to prevent spam.
+ if (exclusive_time != total_time) {
+ os << "/" << FormatDuration(total_time, tu, kFractionalDigits);
+ }
+ os << " " << timings_[i].GetName() << "\n";
+ ++tab_count;
+ } else {
+ --tab_count;
}
- os << name_ << ": " << std::setw(8) << FormatDuration(split_time, tu) << " "
- << split.second << "\n";
}
- os << name_ << ": end, " << NsToMs(total_ns) << " ms\n";
+ os << name_ << ": end, " << PrettyDuration(GetTotalNs()) << "\n";
}
-TimingLogger::ScopedSplit::ScopedSplit(const char* label, TimingLogger* timing_logger) {
- DCHECK(label != NULL) << "New scoped split (" << label << ") with null label.";
- CHECK(timing_logger != NULL) << "New scoped split (" << label << ") without TimingLogger.";
- timing_logger_ = timing_logger;
- label_ = label;
- running_ns_ = 0;
- explicit_ = false;
-
- // Stash away the current split and pause it.
- enclosing_split_ = timing_logger->current_split_;
- if (enclosing_split_ != NULL) {
- enclosing_split_->Pause();
- }
-
- timing_logger_->current_split_ = this;
-
- ATRACE_BEGIN(label_);
-
- start_ns_ = NanoTime();
- if (timing_logger_->verbose_) {
- LOG(INFO) << "Begin: " << label_;
- }
-}
-
-TimingLogger::ScopedSplit::~ScopedSplit() {
- uint64_t current_time = NanoTime();
- uint64_t split_time = current_time - start_ns_;
- running_ns_ += split_time;
- ATRACE_END();
-
- if (timing_logger_->verbose_) {
- LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time);
- }
-
- // If one or more enclosed explicitly started splits are not terminated we can
- // either fail or "unwind" the stack of splits in the timing logger to 'this'
- // (by deleting the intervening scoped splits). This implements the latter.
- TimingLogger::ScopedSplit* current = timing_logger_->current_split_;
- while ((current != NULL) && (current != this)) {
- delete current;
- current = timing_logger_->current_split_;
- }
-
- CHECK(current != NULL) << "Missing scoped split (" << this->label_
- << ") in timing logger (" << timing_logger_->name_ << ").";
- CHECK(timing_logger_->current_split_ == this);
-
- timing_logger_->splits_.push_back(SplitTiming(running_ns_, label_));
-
- timing_logger_->current_split_ = enclosing_split_;
- if (enclosing_split_ != NULL) {
- enclosing_split_->Resume();
+void TimingLogger::Verify() {
+ size_t counts[2] = { 0 };
+ for (size_t i = 0; i < timings_.size(); ++i) {
+ if (i > 0) {
+ CHECK_LE(timings_[i - 1].GetTime(), timings_[i].GetTime());
+ }
+ ++counts[timings_[i].IsStartTiming() ? 0 : 1];
}
+ CHECK_EQ(counts[0], counts[1]) << "Number of StartTiming and EndTiming doesn't match";
}
-
-void TimingLogger::ScopedSplit::TailInsertSplit(const char* label) {
- // Sleight of hand here: Rather than embedding a new scoped split, we're updating the current
- // scoped split in place. Basically, it's one way to make explicit and scoped splits compose
- // well while maintaining the current semantics of NewSplit. An alternative is to push a new split
- // since we unwind the stack of scoped splits in the scoped split destructor. However, this implies
- // that the current split is not ended by NewSplit (which calls TailInsertSplit), which would
- // be different from what we had before.
-
- uint64_t current_time = NanoTime();
- uint64_t split_time = current_time - start_ns_;
- ATRACE_END();
- timing_logger_->splits_.push_back(std::pair<uint64_t, const char*>(split_time, label_));
-
- if (timing_logger_->verbose_) {
- LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time) << "\n"
- << "Begin: " << label;
+TimingLogger::~TimingLogger() {
+ if (kIsDebugBuild) {
+ Verify();
}
-
- label_ = label;
- start_ns_ = current_time;
- running_ns_ = 0;
-
- ATRACE_BEGIN(label);
-}
-
-void TimingLogger::ScopedSplit::Pause() {
- uint64_t current_time = NanoTime();
- uint64_t split_time = current_time - start_ns_;
- running_ns_ += split_time;
- ATRACE_END();
-}
-
-
-void TimingLogger::ScopedSplit::Resume() {
- uint64_t current_time = NanoTime();
-
- start_ns_ = current_time;
- ATRACE_BEGIN(label_);
}
} // namespace art
diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h
index 9b558980c5..b300109e31 100644
--- a/runtime/base/timing_logger.h
+++ b/runtime/base/timing_logger.h
@@ -77,93 +77,119 @@ class CumulativeLogger {
// A timing logger that knows when a split starts for the purposes of logging tools, like systrace.
class TimingLogger {
public:
- // Splits are nanosecond times and split names.
- typedef std::pair<uint64_t, const char*> SplitTiming;
- typedef std::vector<SplitTiming> SplitTimings;
+ static constexpr size_t kIndexNotFound = static_cast<size_t>(-1);
- explicit TimingLogger(const char* name, bool precise, bool verbose);
- ~TimingLogger() {
- // TODO: DCHECK(current_split_ == nullptr) << "Forgot to end split: " << current_split_->label_;
- }
- // Clears current splits and labels.
- void Reset();
+ class Timing {
+ public:
+ Timing(uint64_t time, const char* name) : time_(time), name_(name) {
+ }
+ bool IsStartTiming() const {
+ return !IsEndTiming();
+ }
+ bool IsEndTiming() const {
+ return name_ == nullptr;
+ }
+ uint64_t GetTime() const {
+ return time_;
+ }
+ const char* GetName() const {
+ return name_;
+ }
- // Starts a split
- void StartSplit(const char* new_split_label);
+ private:
+ uint64_t time_;
+ const char* name_;
+ };
- // Ends the current split and starts the one given by the label.
- void NewSplit(const char* new_split_label);
+ // Extra data that is only calculated when you call dump to prevent excess allocation.
+ class TimingData {
+ public:
+ TimingData() = default;
+ TimingData(TimingData&& other) {
+ std::swap(data_, other.data_);
+ }
+ TimingData& operator=(TimingData&& other) {
+ std::swap(data_, other.data_);
+ return *this;
+ }
+ uint64_t GetTotalTime(size_t idx) {
+ return data_[idx].total_time;
+ }
+ uint64_t GetExclusiveTime(size_t idx) {
+ return data_[idx].exclusive_time;
+ }
- // Ends the current split and records the end time.
- void EndSplit();
+ private:
+ // Each begin split has a total time and exclusive time. Exclusive time is total time - total
+ // time of children nodes.
+ struct CalculatedDataPoint {
+ CalculatedDataPoint() : total_time(0), exclusive_time(0) {}
+ uint64_t total_time;
+ uint64_t exclusive_time;
+ };
+ std::vector<CalculatedDataPoint> data_;
+ friend class TimingLogger;
+ };
+ explicit TimingLogger(const char* name, bool precise, bool verbose);
+ ~TimingLogger();
+ // Verify that all open timings have related closed timings.
+ void Verify();
+ // Clears current timings and labels.
+ void Reset();
+ // Starts a timing.
+ void StartTiming(const char* new_split_label);
+ // Ends the current timing.
+ void EndTiming();
+ // End the current timing and start a new timing. Usage not recommended.
+ void NewTiming(const char* new_split_label) {
+ EndTiming();
+ StartTiming(new_split_label);
+ }
+ // Returns the total duration of the timings (sum of total times).
uint64_t GetTotalNs() const;
-
- void Dump(std::ostream& os) const;
+ // Find the index of a timing by name.
+ size_t FindTimingIndex(const char* name, size_t start_idx) const;
+ void Dump(std::ostream& os, const char* indent_string = " ") const;
// Scoped timing splits that can be nested and composed with the explicit split
// starts and ends.
- class ScopedSplit {
- public:
- explicit ScopedSplit(const char* label, TimingLogger* timing_logger);
-
- ~ScopedSplit();
-
- friend class TimingLogger;
-
- private:
- // Pauses timing of the split, usually due to nesting of another split.
- void Pause();
-
- // Resumes timing of the split, usually because a nested split has ended.
- void Resume();
-
- // Used by new split to swap splits in place in a ScopedSplit instance.
- void TailInsertSplit(const char* label);
-
- // The scoped split immediately enclosing this split. Essentially, we get a
- // stack of nested splits through this field.
- ScopedSplit* enclosing_split_;
-
- // Was this created via TimingLogger's StartSplit?
- bool explicit_;
-
- // The split's name.
- const char* label_;
-
- // The current split's latest start time. (It may have been paused and restarted.)
- uint64_t start_ns_;
-
- // The running time, outside of pauses.
- uint64_t running_ns_;
-
- // The timing logger holding this split.
- TimingLogger* timing_logger_;
+ class ScopedTiming {
+ public:
+ explicit ScopedTiming(const char* label, TimingLogger* logger) : logger_(logger) {
+ logger_->StartTiming(label);
+ }
+ ~ScopedTiming() {
+ logger_->EndTiming();
+ }
+ // Closes the current timing and opens a new timing.
+ void NewTiming(const char* label) {
+ logger_->NewTiming(label);
+ }
- DISALLOW_COPY_AND_ASSIGN(ScopedSplit);
+ private:
+ TimingLogger* const logger_; // The timing logger which the scoped timing is associated with.
+ DISALLOW_COPY_AND_ASSIGN(ScopedTiming);
};
- const SplitTimings& GetSplits() const {
- return splits_;
+ // Return the time points of when each start / end timings start and finish.
+ const std::vector<Timing>& GetTimings() const {
+ return timings_;
}
- friend class ScopedSplit;
+ TimingData CalculateTimingData() const;
+
protected:
// The name of the timing logger.
const char* const name_;
-
// Do we want to print the exactly recorded split (true) or round down to the time unit being
// used (false).
const bool precise_;
-
// Verbose logging.
const bool verbose_;
-
- // The current scoped split is also the 'top' of the stack of splits in progress.
- ScopedSplit* current_split_;
-
- // Splits that have ended.
- SplitTimings splits_;
+ // Timing points that are either start or end points. For each starting point ret[i] = location
+ // of end split associated with i. If it is and end split ret[i] = i.
+ std::vector<Timing> timings_;
private:
DISALLOW_COPY_AND_ASSIGN(TimingLogger);
diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc
index 0757751822..35a73d0a76 100644
--- a/runtime/base/timing_logger_test.cc
+++ b/runtime/base/timing_logger_test.cc
@@ -26,16 +26,14 @@ class TimingLoggerTest : public CommonRuntimeTest {};
TEST_F(TimingLoggerTest, StartEnd) {
const char* split1name = "First Split";
- TimingLogger timings("StartEnd", true, false);
-
- timings.StartSplit(split1name);
-
- timings.EndSplit(); // Ends split1.
-
- const TimingLogger::SplitTimings& splits = timings.GetSplits();
-
- EXPECT_EQ(1U, splits.size());
- EXPECT_STREQ(splits[0].second, split1name);
+ TimingLogger logger("StartEnd", true, false);
+ logger.StartTiming(split1name);
+ logger.EndTiming(); // Ends split1.
+ const auto& timings = logger.GetTimings();
+ EXPECT_EQ(2U, timings.size()); // Start, End splits
+ EXPECT_TRUE(timings[0].IsStartTiming());
+ EXPECT_STREQ(timings[0].GetName(), split1name);
+ EXPECT_TRUE(timings[1].IsEndTiming());
}
@@ -43,56 +41,61 @@ TEST_F(TimingLoggerTest, StartNewEnd) {
const char* split1name = "First Split";
const char* split2name = "Second Split";
const char* split3name = "Third Split";
- TimingLogger timings("StartNewEnd", true, false);
-
- timings.StartSplit(split1name);
-
- timings.NewSplit(split2name); // Ends split1.
-
- timings.NewSplit(split3name); // Ends split2.
-
- timings.EndSplit(); // Ends split3.
-
- const TimingLogger::SplitTimings& splits = timings.GetSplits();
-
- EXPECT_EQ(3U, splits.size());
- EXPECT_STREQ(splits[0].second, split1name);
- EXPECT_STREQ(splits[1].second, split2name);
- EXPECT_STREQ(splits[2].second, split3name);
+ TimingLogger logger("StartNewEnd", true, false);
+ logger.StartTiming(split1name);
+ logger.NewTiming(split2name);
+ logger.NewTiming(split3name);
+ logger.EndTiming();
+ // Get the timings and verify that they are sane.
+ const auto& timings = logger.GetTimings();
+ // 6 timings in the timing logger at this point.
+ EXPECT_EQ(6U, timings.size());
+ EXPECT_TRUE(timings[0].IsStartTiming());
+ EXPECT_STREQ(timings[0].GetName(), split1name);
+ EXPECT_TRUE(timings[1].IsEndTiming());
+ EXPECT_TRUE(timings[2].IsStartTiming());
+ EXPECT_STREQ(timings[2].GetName(), split2name);
+ EXPECT_TRUE(timings[3].IsEndTiming());
+ EXPECT_TRUE(timings[4].IsStartTiming());
+ EXPECT_STREQ(timings[4].GetName(), split3name);
+ EXPECT_TRUE(timings[5].IsEndTiming());
}
TEST_F(TimingLoggerTest, StartNewEndNested) {
- const char* split1name = "First Split";
- const char* split2name = "Second Split";
- const char* split3name = "Third Split";
- const char* split4name = "Fourth Split";
- const char* split5name = "Fifth Split";
- TimingLogger timings("StartNewEndNested", true, false);
-
- timings.StartSplit(split1name);
-
- timings.NewSplit(split2name); // Ends split1.
-
- timings.StartSplit(split3name);
-
- timings.StartSplit(split4name);
-
- timings.NewSplit(split5name); // Ends split4.
-
- timings.EndSplit(); // Ends split5.
-
- timings.EndSplit(); // Ends split3.
-
- timings.EndSplit(); // Ends split2.
-
- const TimingLogger::SplitTimings& splits = timings.GetSplits();
-
- EXPECT_EQ(5U, splits.size());
- EXPECT_STREQ(splits[0].second, split1name);
- EXPECT_STREQ(splits[1].second, split4name);
- EXPECT_STREQ(splits[2].second, split5name);
- EXPECT_STREQ(splits[3].second, split3name);
- EXPECT_STREQ(splits[4].second, split2name);
+ const char* name1 = "First Split";
+ const char* name2 = "Second Split";
+ const char* name3 = "Third Split";
+ const char* name4 = "Fourth Split";
+ const char* name5 = "Fifth Split";
+ TimingLogger logger("StartNewEndNested", true, false);
+ logger.StartTiming(name1);
+ logger.NewTiming(name2); // Ends timing1.
+ logger.StartTiming(name3);
+ logger.StartTiming(name4);
+ logger.NewTiming(name5); // Ends timing4.
+ logger.EndTiming(); // Ends timing5.
+ logger.EndTiming(); // Ends timing3.
+ logger.EndTiming(); // Ends timing2.
+ const auto& timings = logger.GetTimings();
+ EXPECT_EQ(10U, timings.size());
+ size_t idx_1 = logger.FindTimingIndex(name1, 0);
+ size_t idx_2 = logger.FindTimingIndex(name2, 0);
+ size_t idx_3 = logger.FindTimingIndex(name3, 0);
+ size_t idx_4 = logger.FindTimingIndex(name4, 0);
+ size_t idx_5 = logger.FindTimingIndex(name5, 0);
+ size_t idx_6 = logger.FindTimingIndex("Not found", 0);
+ EXPECT_NE(idx_1, TimingLogger::kIndexNotFound);
+ EXPECT_NE(idx_2, TimingLogger::kIndexNotFound);
+ EXPECT_NE(idx_3, TimingLogger::kIndexNotFound);
+ EXPECT_NE(idx_4, TimingLogger::kIndexNotFound);
+ EXPECT_NE(idx_5, TimingLogger::kIndexNotFound);
+ EXPECT_EQ(idx_6, TimingLogger::kIndexNotFound);
+ TimingLogger::TimingData data = logger.CalculateTimingData();
+ EXPECT_STREQ(timings[idx_1].GetName(), name1);
+ EXPECT_STREQ(timings[idx_2].GetName(), name2);
+ EXPECT_STREQ(timings[idx_3].GetName(), name3);
+ EXPECT_STREQ(timings[idx_4].GetName(), name4);
+ EXPECT_STREQ(timings[idx_5].GetName(), name5);
}
@@ -101,31 +104,32 @@ TEST_F(TimingLoggerTest, Scoped) {
const char* innersplit1 = "Inner Split 1";
const char* innerinnersplit1 = "Inner Inner Split 1";
const char* innersplit2 = "Inner Split 2";
- TimingLogger timings("Scoped", true, false);
-
+ TimingLogger logger("Scoped", true, false);
{
- TimingLogger::ScopedSplit outer(outersplit, &timings);
-
- {
- TimingLogger::ScopedSplit inner1(innersplit1, &timings);
-
- {
- TimingLogger::ScopedSplit innerinner1(innerinnersplit1, &timings);
- } // Ends innerinnersplit1.
- } // Ends innersplit1.
-
+ TimingLogger::ScopedTiming outer(outersplit, &logger);
+ {
+ TimingLogger::ScopedTiming inner1(innersplit1, &logger);
{
- TimingLogger::ScopedSplit inner2(innersplit2, &timings);
- } // Ends innersplit2.
+ TimingLogger::ScopedTiming innerinner1(innerinnersplit1, &logger);
+ } // Ends innerinnersplit1.
+ } // Ends innersplit1.
+ {
+ TimingLogger::ScopedTiming inner2(innersplit2, &logger);
+ } // Ends innersplit2.
} // Ends outersplit.
-
- const TimingLogger::SplitTimings& splits = timings.GetSplits();
-
- EXPECT_EQ(4U, splits.size());
- EXPECT_STREQ(splits[0].second, innerinnersplit1);
- EXPECT_STREQ(splits[1].second, innersplit1);
- EXPECT_STREQ(splits[2].second, innersplit2);
- EXPECT_STREQ(splits[3].second, outersplit);
+ const size_t idx_outersplit = logger.FindTimingIndex(outersplit, 0);
+ const size_t idx_innersplit1 = logger.FindTimingIndex(innersplit1, 0);
+ const size_t idx_innerinnersplit1 = logger.FindTimingIndex(innerinnersplit1, 0);
+ const size_t idx_innersplit2 = logger.FindTimingIndex(innersplit2, 0);
+ const auto& timings = logger.GetTimings();
+ EXPECT_EQ(8U, timings.size()); // 4 start timings and 4 end timings.
+ EXPECT_GE(timings[idx_innerinnersplit1].GetTime(), timings[idx_innersplit1].GetTime());
+ EXPECT_GE(timings[idx_innersplit2].GetTime(), timings[idx_innersplit1].GetTime());
+ TimingLogger::TimingData data(logger.CalculateTimingData());
+ EXPECT_GE(data.GetTotalTime(idx_outersplit), data.GetTotalTime(idx_innerinnersplit1));
+ EXPECT_GE(data.GetTotalTime(idx_outersplit),
+ data.GetTotalTime(idx_innersplit1) + data.GetTotalTime(idx_innersplit2));
+ EXPECT_GE(data.GetTotalTime(idx_innersplit1), data.GetTotalTime(idx_innerinnersplit1));
}
@@ -134,27 +138,24 @@ TEST_F(TimingLoggerTest, ScopedAndExplicit) {
const char* innersplit = "Inner Split";
const char* innerinnersplit1 = "Inner Inner Split 1";
const char* innerinnersplit2 = "Inner Inner Split 2";
- TimingLogger timings("Scoped", true, false);
-
- timings.StartSplit(outersplit);
-
+ TimingLogger logger("Scoped", true, false);
+ logger.StartTiming(outersplit);
{
- TimingLogger::ScopedSplit inner(innersplit, &timings);
-
- timings.StartSplit(innerinnersplit1);
-
- timings.NewSplit(innerinnersplit2); // Ends innerinnersplit1.
+ TimingLogger::ScopedTiming inner(innersplit, &logger);
+ logger.StartTiming(innerinnersplit1);
+ logger.NewTiming(innerinnersplit2); // Ends innerinnersplit1.
+ logger.EndTiming();
} // Ends innerinnersplit2, then innersplit.
-
- timings.EndSplit(); // Ends outersplit.
-
- const TimingLogger::SplitTimings& splits = timings.GetSplits();
-
- EXPECT_EQ(4U, splits.size());
- EXPECT_STREQ(splits[0].second, innerinnersplit1);
- EXPECT_STREQ(splits[1].second, innerinnersplit2);
- EXPECT_STREQ(splits[2].second, innersplit);
- EXPECT_STREQ(splits[3].second, outersplit);
+ logger.EndTiming(); // Ends outersplit.
+ const size_t idx_outersplit = logger.FindTimingIndex(outersplit, 0);
+ const size_t idx_innersplit = logger.FindTimingIndex(innersplit, 0);
+ const size_t idx_innerinnersplit1 = logger.FindTimingIndex(innerinnersplit1, 0);
+ const size_t idx_innerinnersplit2 = logger.FindTimingIndex(innerinnersplit2, 0);
+ const auto& timings = logger.GetTimings();
+ EXPECT_EQ(8U, timings.size());
+ EXPECT_LE(timings[idx_outersplit].GetTime(), timings[idx_innersplit].GetTime());
+ EXPECT_LE(timings[idx_innersplit].GetTime(), timings[idx_innerinnersplit1].GetTime());
+ EXPECT_LE(timings[idx_innerinnersplit1].GetTime(), timings[idx_innerinnersplit2].GetTime());
}
} // namespace art