improved perfs and event management
This commit is contained in:
1 parent
667ed0c100
commit
bb99be5889
17 files changed
+626
-59
No files matched your search
+62
-7
@@ -163,6 +163,16 @@ BufferStore::BufferStore(const Partition& part, f64 span_s)
|
||||
}
|
||||
}
|
||||
|
||||
void BufferStore::reset() noexcept {
|
||||
time_->clear();
|
||||
if (abs_time_) abs_time_->clear();
|
||||
for (auto& v : values_) v->clear();
|
||||
for (auto& c : channels_) {
|
||||
c->invalidate();
|
||||
c->set_epoch(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferStore::acquire_all() {
|
||||
for (auto& c : channels_) c->acquire();
|
||||
}
|
||||
@@ -536,9 +546,32 @@ void ScopeModel::rebuild_locked() {
|
||||
return;
|
||||
}
|
||||
|
||||
part_ = plan_partition(dts, cfg_.buffer_mb * 1024.0 * 1024.0,
|
||||
target_span_.load(std::memory_order_relaxed), cfg_.relative_time);
|
||||
store_ = std::make_unique<BufferStore>(part_, part_.span_s);
|
||||
const Partition want = plan_partition(dts, cfg_.buffer_mb * 1024.0 * 1024.0,
|
||||
target_span_.load(std::memory_order_relaxed),
|
||||
cfg_.relative_time);
|
||||
// A plan that describes the storage already allocated is cleared, not rebuilt. Allocating
|
||||
// a store is what costs -- its rings are value-initialised, so it touches the whole
|
||||
// `--buffer-mb` -- and most structural changes do not move the plan at all: a calibration
|
||||
// keeps the channel set, and so does leaving an event for the live view. Startup asked for
|
||||
// sixteen of these and eight of them planned the same buffer twice. Clearing has the same
|
||||
// meaning (`BufferStore::reset()`): the history is gone either way, which is why
|
||||
// `plan_generation_` bumps above regardless.
|
||||
if (store_ && part_.same_storage_as(want)) {
|
||||
part_ = want;
|
||||
store_->reset();
|
||||
} else {
|
||||
part_ = want;
|
||||
const std::uint64_t t0 = now_ns();
|
||||
store_ = std::make_unique<BufferStore>(part_, part_.span_s);
|
||||
// Named, with what it cost: this is the one operation in the application whose duration a
|
||||
// user can see with no instrument, because it blocks the main thread for the time it takes
|
||||
// to touch the whole buffer -- about 300 ms per GB. A startup or a mode change that reads
|
||||
// more than one of these lines is a re-plan that should have been coalesced, and that is
|
||||
// the defect the line exists to name (it was fifteen of them at startup).
|
||||
std::fprintf(stderr, "[udpscope] buffer allocated: %zu channels, %.3g s span, %.0f ms\n",
|
||||
channels_.size(), part_.span_s, double(now_ns() - t0) / 1.0e6);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
if (trigger_.channel >= static_cast<int>(channels_.size())) trigger_.channel = -1;
|
||||
prepare_math_locked();
|
||||
@@ -590,9 +623,8 @@ bool ScopeModel::signal_enabled(std::size_t signal) const {
|
||||
return signal < enabled_.size() && enabled_[signal];
|
||||
}
|
||||
|
||||
void ScopeModel::set_signal_enabled(std::size_t signal, bool on) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (signal >= enabled_.size() || enabled_[signal] == on) return;
|
||||
bool ScopeModel::enable_locked(std::size_t signal, bool on) {
|
||||
if (signal >= enabled_.size() || enabled_[signal] == on) return false;
|
||||
enabled_[signal] = on;
|
||||
// A math channel's synthetic signal is enabled *here* -- it is the panel's switch -- while
|
||||
// `append_math_signals_locked()` re-states `enabled_` from `math_` on the next edit. The two have
|
||||
@@ -603,9 +635,14 @@ void ScopeModel::set_signal_enabled(std::size_t signal, bool on) {
|
||||
if (m.name == signals_[signal].name) m.enabled = on;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScopeModel::rebuild_enabled_locked() {
|
||||
// The trigger names a channel by its display name, which is stable across
|
||||
// renumbering: when its own signal is disabled the channel vanishes (the trigger
|
||||
// then waits, and comes back when the signal is re-enabled).
|
||||
// then waits, and comes back when the signal is re-enabled). Read before
|
||||
// `rebuild_channels()`, which is what renumbers.
|
||||
const int old_trigger = trigger_.channel;
|
||||
const std::string trigger_name =
|
||||
(old_trigger >= 0 && old_trigger < static_cast<int>(channels_.size()))
|
||||
@@ -622,6 +659,24 @@ void ScopeModel::set_signal_enabled(std::size_t signal, bool on) {
|
||||
generation_.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
void ScopeModel::set_signal_enabled(std::size_t signal, bool on) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!enable_locked(signal, on)) return;
|
||||
rebuild_enabled_locked();
|
||||
}
|
||||
|
||||
void ScopeModel::set_signals_enabled(const std::vector<std::size_t>& signals, bool on) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
bool any = false;
|
||||
for (const std::size_t s : signals) {
|
||||
if (enable_locked(s, on)) any = true;
|
||||
}
|
||||
// Nothing moved: no re-plan, and in particular no `generation_` bump -- a restored session
|
||||
// re-enables what is already on, and a bump there would make the UI rebind for nothing.
|
||||
if (!any) return;
|
||||
rebuild_enabled_locked();
|
||||
}
|
||||
|
||||
std::pair<double, double> ScopeModel::calibration(std::size_t signal) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (signal >= signals_.size()) return {1.0, 0.0};
|
||||
|
||||
Reference in new issue
Block a user