Skip to content

Commit

Permalink
refactor: extract performance observers to native layer and push entr…
Browse files Browse the repository at this point in the history
…ies to each observer
  • Loading branch information
robik committed Jul 10, 2024
1 parent 9580eca commit 5de7d48
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,11 @@ class NativePerformanceObserver
public:
NativePerformanceObserver(std::shared_ptr<CallInvoker> jsInvoker);

void startReporting(jsi::Runtime& rt, PerformanceEntryType entryType);

void stopReporting(jsi::Runtime& rt, PerformanceEntryType entryType);

void setIsBuffered(
jsi::Runtime& rt,
const std::vector<PerformanceEntryType> entryTypes,
bool isBuffered);

PerformanceEntryReporter::PopPendingEntriesResult popPendingEntries(
jsi::Runtime& rt);

void setOnPerformanceEntryCallback(
jsi::Runtime& rt,
std::optional<AsyncCallback<>> callback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ class BoundedConsumableBuffer {
* operation, which will depend on whether the buffer reached the max allowed
* size and how many are there unconsumed elements.
*/
PushStatus add(const T&& el) {
PushStatus add(const T& el) {
if (entries_.size() < maxSize_) {
// Haven't reached max buffer size yet, just add and grow the buffer
entries_.emplace_back(el);
entries_.push_back(el);
cursorEnd_++;
numToConsume_++;
return PushStatus::OK;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#Copyright(c) Meta Platforms, Inc.and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#This source code is licensed under the MIT license found in the
#LICENSE file in the root directory of this source tree.

cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ struct PerformanceEntry {
std::optional<PerformanceEntryInteractionId> interactionId;
};

constexpr size_t NUM_PERFORMANCE_ENTRY_TYPES =
(size_t)PerformanceEntryType::_NEXT - 1; // Valid types start from 1.

/**
* Status of the add/push operation for the `BoundedConsumableBuffer`
* container
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "PerformanceEntryBuffer.h"

namespace facebook::react {

PerformanceEntryPushStatus PerformanceEntryCircularBuffer::add(const facebook::react::PerformanceEntry&& entry) {
return entries.add(std::move(entry));
}

void PerformanceEntryCircularBuffer::consume(std::vector<PerformanceEntry>& target) {
entries.consume(target);
}

size_t PerformanceEntryCircularBuffer::pendingMessagesCount() const {
return entries.getNumToConsume();
}

void PerformanceEntryCircularBuffer::getEntries(std::optional<std::string_view> name, std::vector<PerformanceEntry>& target) const {
entries.getEntries(
target, [&](const PerformanceEntry& e) { return e.name == name; });
}

void PerformanceEntryCircularBuffer::clear() {
entries.clear();
}


void PerformanceEntryCircularBuffer::clear(std::string_view name) {
entries.clear([&](const PerformanceEntry& e) { return e.name == name; });
}

PerformanceEntryPushStatus PerformanceEntryKeyedBuffer::add(const facebook::react::PerformanceEntry&& entry) {
entries.add(entry);
return PerformanceEntryPushStatus::OK;
}

void PerformanceEntryKeyedBuffer::consume(std::vector<PerformanceEntry>& target) {
entries.consume(target);
}

size_t PerformanceEntryKeyedBuffer::pendingMessagesCount() const {
return entries.getNumToConsume();
}

void PerformanceEntryKeyedBuffer::getEntries(std::optional<std::string_view> name, std::vector<PerformanceEntry>& target) const {
if (name.has_value()) {
std::string nameStr{name.value()};
entries.getEntries(nameStr, target);
} else {
entries.getEntries(target);
}
}

void PerformanceEntryKeyedBuffer::clear() {
entries.clear();
}

void PerformanceEntryKeyedBuffer::clear(std::string_view name) {
std::string nameStr{name};
entries.clear(nameStr);
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ namespace facebook::react {
// all")
constexpr double DEFAULT_DURATION_THRESHOLD = 0.0;

// Abstract performance entry buffer with reporting flags
// Default buffer size limit, per entry type
constexpr size_t DEFAULT_MAX_BUFFER_SIZE = 1024;

/**
* Abstract performance entry buffer with reporting flags.
* Subtypes differ on how entries are stored.
*/
struct PerformanceEntryBuffer {
bool isReporting{false};
bool isAlwaysLogged{false};
double durationThreshold{DEFAULT_DURATION_THRESHOLD};

Expand All @@ -42,32 +47,17 @@ struct PerformanceEntryCircularBuffer : public PerformanceEntryBuffer {
explicit PerformanceEntryCircularBuffer(size_t size) : entries(size) {}
~PerformanceEntryCircularBuffer() override = default;

PerformanceEntryPushStatus add(const PerformanceEntry&& entry) override {
return entries.add(std::move(entry));
}

void consume(std::vector<PerformanceEntry>& target) override {
entries.consume(target);
}
PerformanceEntryPushStatus add(const PerformanceEntry&& entry) override;
void consume(std::vector<PerformanceEntry>& target) override;

size_t pendingMessagesCount() const override {
return entries.getNumToConsume();
}
size_t pendingMessagesCount() const override;

void getEntries(
std::optional<std::string_view> name,
std::vector<PerformanceEntry>& target) const override {
entries.getEntries(
target, [&](const PerformanceEntry& e) { return e.name == name; });
}

void clear() override {
entries.clear();
}

void clear(std::string_view name) override {
entries.clear([&](const PerformanceEntry& e) { return e.name == name; });
}
std::vector<PerformanceEntry>& target) const override;

void clear() override;
void clear(std::string_view name) override;
};

struct PerformanceEntryKeyedBuffer : public PerformanceEntryBuffer {
Expand All @@ -76,38 +66,17 @@ struct PerformanceEntryKeyedBuffer : public PerformanceEntryBuffer {
explicit PerformanceEntryKeyedBuffer() = default;
~PerformanceEntryKeyedBuffer() override = default;

PerformanceEntryPushStatus add(const PerformanceEntry&& entry) override {
entries.add(entry);
return PerformanceEntryPushStatus::OK;
}

void consume(std::vector<PerformanceEntry>& target) override {
entries.consume(target);
}
PerformanceEntryPushStatus add(const PerformanceEntry&& entry) override;
void consume(std::vector<PerformanceEntry>& target) override;

size_t pendingMessagesCount() const override {
return entries.getNumToConsume();
}
size_t pendingMessagesCount() const override;

void getEntries(
std::optional<std::string_view> name,
std::vector<PerformanceEntry>& target) const override {
if (name.has_value()) {
std::string nameStr{name.value()};
entries.getEntries(nameStr, target);
} else {
entries.getEntries(target);
}
}

void clear() override {
entries.clear();
}

void clear(std::string_view name) override {
std::string nameStr{name};
entries.clear(nameStr);
}
std::vector<PerformanceEntry>& target) const override;

void clear() override;
void clear(std::string_view name) override;
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -17,98 +17,44 @@ PerformanceEntryReporter::getInstance() {
return instance;
}

PerformanceEntryReporter::PerformanceEntryReporter() = default;

void PerformanceEntryReporter::setReportingCallback(
std::function<void()> callback) {
callback_ = std::move(callback);
}
PerformanceEntryReporter::PerformanceEntryReporter(): observerRegistry_(std::make_unique<PerformanceObserverRegistry>()) {}

DOMHighResTimeStamp PerformanceEntryReporter::getCurrentTimeStamp() const {
return timeStampProvider_ != nullptr ? timeStampProvider_()
: JSExecutor::performanceNow();
}

void PerformanceEntryReporter::startReporting(PerformanceEntryType entryType) {
auto& buffer = getBuffer(entryType);
buffer.isReporting = true;
buffer.durationThreshold = DEFAULT_DURATION_THRESHOLD;
}

void PerformanceEntryReporter::setAlwaysLogged(
PerformanceEntryType entryType,
bool isAlwaysLogged) {
auto& buffer = getBuffer(entryType);
buffer.isAlwaysLogged = isAlwaysLogged;
}

void PerformanceEntryReporter::setDurationThreshold(
PerformanceEntryType entryType,
DOMHighResTimeStamp durationThreshold) {
getBuffer(entryType).durationThreshold = durationThreshold;
}

void PerformanceEntryReporter::stopReporting(PerformanceEntryType entryType) {
getBuffer(entryType).isReporting = false;
}

void PerformanceEntryReporter::stopReporting() {
eventBuffer_.isReporting = false;
markBuffer_.isReporting = false;
measureBuffer_.isReporting = false;
}

PerformanceEntryReporter::PopPendingEntriesResult
PerformanceEntryReporter::popPendingEntries() {
std::lock_guard lock(entriesMutex_);
PopPendingEntriesResult res = {
.entries = std::vector<PerformanceEntry>(),
.droppedEntriesCount = droppedEntriesCount_};

eventBuffer_.consume(res.entries);
markBuffer_.consume(res.entries);
measureBuffer_.consume(res.entries);

// Sort by starting time (or ending time, if starting times are equal)
std::stable_sort(
res.entries.begin(), res.entries.end(), PerformanceEntrySorter{});

droppedEntriesCount_ = 0;
return res;
}

void PerformanceEntryReporter::logEntry(const PerformanceEntry& entry) {
if (entry.entryType == PerformanceEntryType::EVENT) {
eventCounts_[entry.name]++;
}
{
std::lock_guard lock(entriesMutex_);
auto& buffer = getBuffer(entry.entryType);

if (!isReporting(entry.entryType) && !isAlwaysLogged(entry.entryType)) {
return;
}

std::lock_guard lock(entriesMutex_);

auto& buffer = getBuffer(entry.entryType);

if (entry.duration < buffer.durationThreshold) {
// The entries duration is lower than the desired reporting threshold, skip
return;
}
if (entry.duration < buffer.durationThreshold) {
// The entries duration is lower than the desired reporting threshold, skip
return;
}

auto pushResult = buffer.add(std::move(entry));
if (pushResult ==
BoundedConsumableBuffer<PerformanceEntry>::PushStatus::DROP) {
// Start dropping entries once reached maximum buffer size.
// The number of dropped entries will be reported back to the corresponding
// PerformanceObserver callback.
droppedEntriesCount_ += 1;
auto pushResult = buffer.add(std::move(entry));
if (pushResult ==
BoundedConsumableBuffer<PerformanceEntry>::PushStatus::DROP) {
// Start dropping entries once reached maximum buffer size.
// The number of dropped entries will be reported back to the corresponding
// PerformanceObserver callback.
droppedEntriesCount_ += 1;
}
}

if (buffer.pendingMessagesCount() == 1) {
// If the buffer was empty, it signals that JS side just has possibly
// consumed it and is ready to get more
scheduleFlushBuffer();
}
observerRegistry_.emit(entry);
}

void PerformanceEntryReporter::mark(
Expand Down Expand Up @@ -226,10 +172,4 @@ void PerformanceEntryReporter::logEventEntry(
.interactionId = interactionId});
}

void PerformanceEntryReporter::scheduleFlushBuffer() {
if (callback_) {
callback_();
}
}

} // namespace facebook::react
Loading

0 comments on commit 5de7d48

Please sign in to comment.