pstats: Performance improvements for time-based strip charts

Remove the use of set and list, which are allocator-heavy and insertion was a bottleneck.  Since each sample occurs only once on the linked list, we can more efficiently roll our own linked list with next and prev pointers, so no allocation needed.  Instead of the set, we can just store a per-collector flag.
This commit is contained in:
rdb 2022-11-26 19:23:36 +01:00
parent d00d4a44a0
commit 5247446500

View File

@ -15,13 +15,9 @@
#include "pStatFrameData.h"
#include "pStatCollectorDef.h"
#include "vector_int.h"
#include "plist.h"
#include "pset.h"
#include <algorithm>
#include <vector>
/**
* This class is used within this module only--in fact, within
@ -30,15 +26,7 @@
*/
class FrameSample {
public:
typedef plist<FrameSample *> Started;
FrameSample() {
_touched = false;
_is_started = false;
_pushed = false;
_net_time = 0.0;
}
void data_point(double time, bool is_start, Started &started) {
void data_point(double time, bool is_start, FrameSample *started) {
_touched = true;
// We only consider events that change the startstop state. With two
@ -58,20 +46,25 @@ public:
if (_pushed) {
nassertv(!_is_started);
Started::iterator si = find(started.begin(), started.end(), this);
nassertv(si != started.end());
started.erase(si);
nassertv(_next != nullptr && _prev != nullptr);
_prev->_next = _next;
_next->_prev = _prev;
_next = _prev = nullptr;
} else {
if (_is_started) {
_net_time -= time;
push_all(time, started);
started.push_back(this);
nassertv(_next == nullptr && _prev == nullptr);
_prev = started->_prev;
_next = started;
_prev->_next = this;
started->_prev = this;
} else {
_net_time += time;
Started::iterator si = find(started.begin(), started.end(), this);
nassertv(si != started.end());
started.erase(si);
nassertv(_next != nullptr && _prev != nullptr);
_prev->_next = _next;
_next->_prev = _prev;
_next = _prev = nullptr;
pop_one(time, started);
}
}
@ -93,31 +86,32 @@ public:
}
}
void push_all(double time, Started &started) {
Started::iterator si;
for (si = started.begin(); si != started.end(); ++si) {
(*si)->push(time);
void push_all(double time, FrameSample *started) {
for (FrameSample *sample = started->_next;
sample != started; sample = sample->_next) {
sample->push(time);
}
}
void pop_one(double time, Started &started) {
Started::reverse_iterator si;
for (si = started.rbegin(); si != started.rend(); ++si) {
if ((*si)->_pushed) {
(*si)->pop(time);
void pop_one(double time, FrameSample *started) {
for (FrameSample *sample = started->_prev;
sample != started; sample = sample->_prev) {
if (sample->_pushed) {
sample->pop(time);
return;
}
}
}
bool _touched;
bool _is_started;
bool _pushed;
double _net_time;
FrameSample *_next = nullptr;
FrameSample *_prev = nullptr;
bool _touched = false;
bool _is_started = false;
bool _pushed = false;
bool _is_new = false;
double _net_time = 0.0;
};
/**
*
*/
@ -279,19 +273,19 @@ get_level(int collector) {
void PStatView::
update_time_data(const PStatFrameData &frame_data) {
int num_events = frame_data.get_num_events();
int num_collectors = _client_data->get_num_collectors();
typedef pvector<FrameSample> Samples;
Samples samples(_client_data->get_num_collectors());
typedef std::vector<FrameSample> Samples;
Samples samples(num_collectors);
FrameSample::Started started;
// Keep a linked list of started samples.
FrameSample started;
started._next = &started;
started._prev = &started;
_all_collectors_known = true;
// This tracks the set of samples we actually care about.
typedef pset<int> GotSamples;
GotSamples got_samples;
int new_collectors = 0;
int i;
for (i = 0; i < num_events; i++) {
int collector_index = frame_data.get_time_collector(i);
@ -301,7 +295,7 @@ update_time_data(const PStatFrameData &frame_data) {
_all_collectors_known = false;
} else {
nassertv(collector_index >= 0 && collector_index < (int)samples.size());
nassertv(collector_index >= 0 && collector_index < num_collectors);
if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
// Here's a data point we care about: anything at constraint level or
@ -310,8 +304,8 @@ update_time_data(const PStatFrameData &frame_data) {
if (!is_start) {
// A "stop" in the middle of a frame implies a "start" since time
// 0 (that is, since the first data point in the frame).
samples[collector_index].data_point(frame_data.get_time(0), true, started);
samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
samples[collector_index].data_point(frame_data.get_time(0), true, &started);
samples[collector_index].data_point(frame_data.get_time(i), is_start, &started);
} else {
// An extra "start" for a collector that's already started is an
// error.
@ -320,23 +314,25 @@ update_time_data(const PStatFrameData &frame_data) {
<< "\n";
}
} else {
samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
got_samples.insert(collector_index);
samples[collector_index].data_point(frame_data.get_time(i), is_start, &started);
if (!samples[collector_index]._is_new) {
samples[collector_index]._is_new = true;
++new_collectors;
}
}
}
}
}
// Make sure everything is stopped.
Samples::iterator si;
for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
if ((*si)._is_started) {
(*si).data_point(frame_data.get_end(), false, started);
(*si).data_point(frame_data.get_end(), false, &started);
}
}
nassertv(started.empty());
nassertv(started._next == &started && started._prev == &started);
bool any_new_levels = false;
@ -356,11 +352,10 @@ update_time_data(const PStatFrameData &frame_data) {
}
int collector_index = level->_collector;
GotSamples::iterator gi;
gi = got_samples.find(collector_index);
if (gi != got_samples.end()) {
if (samples[collector_index]._is_new) {
level->_value_alone = samples[collector_index]._net_time;
got_samples.erase(gi);
samples[collector_index]._is_new = false;
--new_collectors;
}
li = lnext;
@ -368,14 +363,14 @@ update_time_data(const PStatFrameData &frame_data) {
// Finally, any samples left over in the got_samples set are new collectors
// that we need to add to the Levels list.
if (!got_samples.empty()) {
if (new_collectors > 0) {
any_new_levels = true;
GotSamples::const_iterator gi;
for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
int collector_index = (*gi);
PStatViewLevel *level = get_level(collector_index);
level->_value_alone = samples[*gi]._net_time;
for (int collector_index = 0; collector_index < num_collectors; ++collector_index) {
if (samples[collector_index]._is_new) {
PStatViewLevel *level = get_level(collector_index);
level->_value_alone = samples[collector_index]._net_time;
}
}
}