1212
1313#include < unordered_map>
1414
15- // All the unflushed entries beyond this amount will get discarded, with
16- // the amount of discarded ones sent back to the observers' callbacks as
17- // "droppedEntryCount" value
18- static constexpr size_t MAX_ENTRY_BUFFER_SIZE = 1024 ;
19-
2015namespace facebook ::react {
2116EventTag PerformanceEntryReporter::sCurrentEventTag_ {0 };
2217
23- RawPerformanceEntry PerformanceMark::toRawPerformanceEntry () const {
24- return {
25- name,
26- static_cast <int >(PerformanceEntryType::MARK),
27- timeStamp,
28- 0.0 ,
29- std::nullopt ,
30- std::nullopt ,
31- std::nullopt };
32- }
33-
34- RawPerformanceEntry PerformanceMeasure::toRawPerformanceEntry () const {
35- return {
36- name,
37- static_cast <int >(PerformanceEntryType::MEASURE),
38- timeStamp,
39- duration,
40- std::nullopt ,
41- std::nullopt ,
42- std::nullopt };
43- }
44-
4518PerformanceEntryReporter &PerformanceEntryReporter::getInstance () {
4619 static PerformanceEntryReporter instance;
4720 return instance;
4821}
4922
23+ PerformanceEntryReporter::PerformanceEntryReporter () {
24+ // For mark entry types we also want to keep the lookup by name, to make
25+ // sure that marks can be referenced by measures
26+ getBuffer (PerformanceEntryType::MARK).hasNameLookup = true ;
27+ }
5028void PerformanceEntryReporter::setReportingCallback (
5129 std::optional<AsyncCallback<>> callback) {
5230 callback_ = callback;
5331}
5432
5533void PerformanceEntryReporter::startReporting (PerformanceEntryType entryType) {
56- int entryTypeIdx = static_cast < int > (entryType);
57- reportingType_[entryTypeIdx] = true ;
58- durationThreshold_[entryTypeIdx] = DEFAULT_DURATION_THRESHOLD;
34+ auto &buffer = getBuffer (entryType);
35+ buffer. isReporting = true ;
36+ buffer. durationThreshold = DEFAULT_DURATION_THRESHOLD;
5937}
6038
6139void PerformanceEntryReporter::setDurationThreshold (
6240 PerformanceEntryType entryType,
6341 double durationThreshold) {
64- durationThreshold_[ static_cast < int > (entryType)] = durationThreshold;
42+ getBuffer (entryType). durationThreshold = durationThreshold;
6543}
6644
6745void PerformanceEntryReporter::stopReporting (PerformanceEntryType entryType) {
68- reportingType_[ static_cast < int > (entryType)] = false ;
46+ getBuffer (entryType). isReporting = false ;
6947}
7048
7149void PerformanceEntryReporter::stopReporting () {
72- reportingType_.fill (false );
50+ for (auto &buffer : buffers_) {
51+ buffer.isReporting = false ;
52+ }
7353}
7454
7555GetPendingEntriesResult PerformanceEntryReporter::popPendingEntries () {
7656 std::lock_guard<std::mutex> lock (entriesMutex_);
57+ GetPendingEntriesResult res = {
58+ std::vector<RawPerformanceEntry>(), droppedEntryCount_};
59+ for (auto &buffer : buffers_) {
60+ buffer.entries .consume (res.entries );
61+ }
62+
63+ // Sort by starting time (or ending time, if starting times are equal)
64+ std::sort (
65+ res.entries .begin (),
66+ res.entries .end (),
67+ [](const RawPerformanceEntry &lhs, const RawPerformanceEntry &rhs) {
68+ if (lhs.startTime != rhs.startTime ) {
69+ return lhs.startTime < rhs.startTime ;
70+ } else {
71+ return lhs.duration < rhs.duration ;
72+ }
73+ });
7774
78- GetPendingEntriesResult res = {std::move (entries_), droppedEntryCount_};
79- entries_ = {};
8075 droppedEntryCount_ = 0 ;
8176 return res;
8277}
@@ -91,24 +86,39 @@ void PerformanceEntryReporter::logEntry(const RawPerformanceEntry &entry) {
9186 return ;
9287 }
9388
94- if (entry.duration < durationThreshold_[entry.entryType ]) {
89+ std::lock_guard<std::mutex> lock (entriesMutex_);
90+
91+ auto &buffer = buffers_[entry.entryType ];
92+
93+ if (entry.duration < buffer.durationThreshold ) {
9594 // The entries duration is lower than the desired reporting threshold, skip
96- // return;
95+ return ;
9796 }
9897
99- std::lock_guard<std::mutex> lock (entriesMutex_);
98+ if (buffer.hasNameLookup ) {
99+ auto overwriteCandidate = buffer.entries .getNextOverwriteCandidate ();
100+ if (overwriteCandidate != nullptr ) {
101+ auto it = buffer.nameLookup .find (overwriteCandidate);
102+ if (it != buffer.nameLookup .end () && *it == overwriteCandidate) {
103+ buffer.nameLookup .erase (it);
104+ }
105+ }
106+ }
100107
101- if (entries_.size () == MAX_ENTRY_BUFFER_SIZE) {
108+ auto pushResult = buffer.entries .add (std::move (entry));
109+ if (pushResult ==
110+ BoundedConsumableBuffer<RawPerformanceEntry>::PushStatus::DROP) {
102111 // Start dropping entries once reached maximum buffer size.
103112 // The number of dropped entries will be reported back to the corresponding
104113 // PerformanceObserver callback.
105114 droppedEntryCount_ += 1 ;
106- return ;
107115 }
108116
109- entries_.emplace_back (entry);
117+ if (buffer.hasNameLookup ) {
118+ buffer.nameLookup .insert (&buffer.entries .back ());
119+ }
110120
111- if (entries_. size () == 1 ) {
121+ if (buffer. entries . getNumToConsume () == 1 ) {
112122 // If the buffer was empty, it signals that JS side just has possibly
113123 // consumed it and is ready to get more
114124 scheduleFlushBuffer ();
@@ -119,95 +129,75 @@ void PerformanceEntryReporter::mark(
119129 const std::string &name,
120130 double startTime,
121131 double duration) {
122- // Register the mark for further possible "measure" lookup, as well as add
123- // it to a circular buffer:
124- PerformanceMark &mark = marksBuffer_[marksBufferPosition_];
125- marksBufferPosition_ = (marksBufferPosition_ + 1 ) % marksBuffer_.size ();
126- marksCount_ = std::min (marksBuffer_.size (), marksCount_ + 1 );
127-
128- if (!mark.name .empty ()) {
129- // Drop off the oldest mark out of the queue, but only if that's indeed the
130- // oldest one
131- auto it = marksRegistry_.find (&mark);
132- if (it != marksRegistry_.end () && *it == &mark) {
133- marksRegistry_.erase (it);
134- }
135- }
136-
137- mark.name = name;
138- mark.timeStamp = startTime;
139- marksRegistry_.insert (&mark);
140-
141- logEntry (
142- {name,
143- static_cast <int >(PerformanceEntryType::MARK),
144- startTime,
145- duration,
146- std::nullopt ,
147- std::nullopt ,
148- std::nullopt });
132+ logEntry (RawPerformanceEntry{
133+ name,
134+ static_cast <int >(PerformanceEntryType::MARK),
135+ startTime,
136+ duration,
137+ std::nullopt ,
138+ std::nullopt ,
139+ std::nullopt });
149140}
150141
151142void PerformanceEntryReporter::clearEntries (
152143 PerformanceEntryType entryType,
153144 const char *entryName) {
154- if (entryType == PerformanceEntryType::MARK ||
155- entryType == PerformanceEntryType::UNDEFINED) {
156- if (entryName != nullptr ) {
157- // remove a named mark from the mark/measure registry
158- PerformanceMark mark{{entryName, 0 }};
159- marksRegistry_.erase (&mark);
160-
161- clearCircularBuffer (
162- marksBuffer_, marksCount_, marksBufferPosition_, entryName);
163- } else {
164- marksCount_ = 0 ;
165- marksRegistry_.clear ();
145+ if (entryType == PerformanceEntryType::UNDEFINED) {
146+ // Clear all entry types
147+ for (int i = 1 ; i < NUM_PERFORMANCE_ENTRY_TYPES; i++) {
148+ clearEntries (static_cast <PerformanceEntryType>(i), entryName);
166149 }
167- }
168-
169- if (entryType == PerformanceEntryType::MEASURE ||
170- entryType == PerformanceEntryType::UNDEFINED) {
150+ } else {
151+ auto &buffer = getBuffer (entryType);
171152 if (entryName != nullptr ) {
172- clearCircularBuffer (
173- measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
153+ if (buffer.hasNameLookup ) {
154+ RawPerformanceEntry entry{
155+ entryName,
156+ static_cast <int >(entryType),
157+ 0.0 ,
158+ 0.0 ,
159+ std::nullopt ,
160+ std::nullopt ,
161+ std::nullopt };
162+ buffer.nameLookup .erase (&entry);
163+ }
164+ buffer.entries .clear ([entryName](const RawPerformanceEntry &entry) {
165+ return std::strcmp (entry.name .c_str (), entryName) == 0 ;
166+ });
174167 } else {
175- measuresCount_ = 0 ;
168+ buffer.entries .clear ();
169+ buffer.nameLookup .clear ();
176170 }
177171 }
172+ }
178173
179- int lastPos = entries_.size () - 1 ;
180- int pos = lastPos;
181- while (pos >= 0 ) {
182- const RawPerformanceEntry &entry = entries_[pos];
183- if (entry.entryType == static_cast <int32_t >(entryType) &&
184- (entryName == nullptr || entry.name == entryName)) {
185- entries_[pos] = entries_[lastPos];
186- lastPos--;
174+ void PerformanceEntryReporter::getEntries (
175+ PerformanceEntryType entryType,
176+ const char *entryName,
177+ std::vector<RawPerformanceEntry> &res) const {
178+ if (entryType == PerformanceEntryType::UNDEFINED) {
179+ // Collect all entry types
180+ for (int i = 1 ; i < NUM_PERFORMANCE_ENTRY_TYPES; i++) {
181+ getEntries (static_cast <PerformanceEntryType>(i), entryName, res);
182+ }
183+ } else {
184+ const auto &entries = getBuffer (entryType).entries ;
185+ if (entryName == nullptr ) {
186+ entries.getEntries (res);
187+ } else {
188+ entries.getEntries (res, [entryName](const RawPerformanceEntry &entry) {
189+ return std::strcmp (entry.name .c_str (), entryName) == 0 ;
190+ });
187191 }
188- pos--;
189192 }
190- entries_.resize (lastPos + 1 );
191193}
192194
193195std::vector<RawPerformanceEntry> PerformanceEntryReporter::getEntries (
194196 PerformanceEntryType entryType,
195197 const char *entryName) const {
196- if (entryType == PerformanceEntryType::MARK) {
197- return getCircularBufferContents (
198- marksBuffer_, marksCount_, marksBufferPosition_, entryName);
199- } else if (entryType == PerformanceEntryType::MEASURE) {
200- return getCircularBufferContents (
201- measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
202- } else if (entryType == PerformanceEntryType::UNDEFINED) {
203- auto marks = getCircularBufferContents (
204- marksBuffer_, marksCount_, marksBufferPosition_, entryName);
205- auto measures = getCircularBufferContents (
206- measuresBuffer_, measuresCount_, measuresBufferPosition_, entryName);
207- marks.insert (marks.end (), measures.begin (), measures.end ());
208- return marks;
209- }
210- return {};
198+ std::vector<RawPerformanceEntry> res;
199+ getEntries (entryType, entryName, res);
200+ return res;
211201}
212202
213203void PerformanceEntryReporter::measure (
@@ -220,13 +210,6 @@ void PerformanceEntryReporter::measure(
220210 double startTimeVal = startMark ? getMarkTime (*startMark) : startTime;
221211 double endTimeVal = endMark ? getMarkTime (*endMark) : endTime;
222212 double durationVal = duration ? *duration : endTimeVal - startTimeVal;
223-
224- measuresBuffer_[measuresBufferPosition_] =
225- PerformanceMeasure{name, startTime, endTime};
226- measuresBufferPosition_ =
227- (measuresBufferPosition_ + 1 ) % measuresBuffer_.size ();
228- measuresCount_ = std::min (measuresBuffer_.size (), measuresCount_ + 1 );
229-
230213 logEntry (
231214 {name,
232215 static_cast <int >(PerformanceEntryType::MEASURE),
@@ -239,10 +222,19 @@ void PerformanceEntryReporter::measure(
239222
240223double PerformanceEntryReporter::getMarkTime (
241224 const std::string &markName) const {
242- PerformanceMark mark{{std::move (markName), 0 }};
243- auto it = marksRegistry_.find (&mark);
244- if (it != marksRegistry_.end ()) {
245- return (*it)->timeStamp ;
225+ RawPerformanceEntry mark{
226+ markName,
227+ static_cast <int >(PerformanceEntryType::MARK),
228+ 0.0 ,
229+ 0.0 ,
230+ std::nullopt ,
231+ std::nullopt ,
232+ std::nullopt };
233+
234+ const auto &marksBuffer = getBuffer (PerformanceEntryType::MARK);
235+ auto it = marksBuffer.nameLookup .find (&mark);
236+ if (it != marksBuffer.nameLookup .end ()) {
237+ return (*it)->startTime ;
246238 } else {
247239 return 0.0 ;
248240 }
0 commit comments