Native side implementation for Performance.measure()

Summary:
Changelog: [Internal]

This implements the C++ side logic of handling `Performance.measure` calls.

Since measures may refer to earlier logged marks by name, we need to keep track of the former. I also use a fixed size (circular) buffer to prevent the marks from piling forever.

Also adds implementation of clearing marks/measures, as per standard.

Reviewed By: mdvacca

Differential Revision: D41756928

fbshipit-source-id: 19dce28d6af4c5646274e6d5db20b41869282780
This commit is contained in:
Ruslan Shestopalyuk
2022-12-06 17:34:24 -08:00
committed by Facebook GitHub Bot
parent d8bcab2be7
commit 62a28e4bdb
5 changed files with 156 additions and 5 deletions
@@ -59,6 +59,24 @@ void PerformanceEntryReporter::mark(
const std::string &name,
double startTime,
double duration) {
// Register the mark for further possible "measure" lookup, as well as add
// it to a circular buffer:
PerformanceMark &mark = marks_buffer_[marks_buffer_position_];
marks_buffer_position_ = (marks_buffer_position_ + 1) % marks_buffer_.size();
if (!mark.name.empty()) {
// Drop off the oldest mark out of the queue, but only if that's indeed the
// oldest one
auto it = marks_registry_.find(&mark);
if (it != marks_registry_.end() && *it == &mark) {
marks_registry_.erase(it);
}
}
mark.name = name;
mark.timeStamp = startTime;
marks_registry_.insert(&mark);
logEntry(
{name,
static_cast<int>(PerformanceEntryType::MARK),
@@ -68,4 +86,83 @@ void PerformanceEntryReporter::mark(
std::nullopt,
std::nullopt});
}
void PerformanceEntryReporter::clearMarks(
const std::optional<std::string> &markName) {
if (markName) {
PerformanceMark mark{{*markName, 0}};
marks_registry_.erase(&mark);
clearEntries([&markName](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MARK) &&
entry.name == markName;
});
} else {
marks_registry_.clear();
clearEntries([](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MARK);
});
}
}
void PerformanceEntryReporter::measure(
const std::string &name,
double startTime,
double endTime,
const std::optional<double> &duration,
const std::optional<std::string> &startMark,
const std::optional<std::string> &endMark) {
double startTimeVal = startMark ? getMarkTime(*startMark) : startTime;
double endTimeVal = endMark ? getMarkTime(*endMark) : endTime;
double durationVal = duration ? *duration : endTimeVal - startTimeVal;
logEntry(
{name,
static_cast<int>(PerformanceEntryType::MEASURE),
startTimeVal,
durationVal,
std::nullopt,
std::nullopt,
std::nullopt});
}
void PerformanceEntryReporter::clearMeasures(
const std::optional<std::string> &measureName) {
if (measureName) {
clearEntries([&measureName](const RawPerformanceEntry &entry) {
return entry.entryType ==
static_cast<int>(PerformanceEntryType::MEASURE) &&
entry.name == measureName;
});
} else {
marks_registry_.clear();
clearEntries([](const RawPerformanceEntry &entry) {
return entry.entryType == static_cast<int>(PerformanceEntryType::MEASURE);
});
}
}
double PerformanceEntryReporter::getMarkTime(
const std::string &markName) const {
PerformanceMark mark{{std::move(markName), 0}};
auto it = marks_registry_.find(&mark);
if (it != marks_registry_.end()) {
return (*it)->timeStamp;
} else {
return 0.0;
}
}
void PerformanceEntryReporter::clearEntries(
std::function<bool(const RawPerformanceEntry &)> predicate) {
int lastPos = entries_.size() - 1;
int pos = lastPos;
while (pos >= 0) {
if (predicate(entries_[pos])) {
entries_[pos] = entries_[lastPos];
lastPos--;
}
pos--;
}
entries_.resize(lastPos + 1);
}
} // namespace facebook::react