feat(data): SPSC ring buffer, incremental min/max LOD pyramid, series types
This commit is contained in:
1 parent
c93c98002b
commit
138fa478c3
15 files changed
+1182
-8
No files matched your search
@@ -0,0 +1,115 @@
|
||||
#include <plot/data/stream_series.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace plot::data {
|
||||
namespace {
|
||||
|
||||
/// Samples copied per ring read on the ingest path, so rebuilding a saturated
|
||||
/// window costs no allocation.
|
||||
constexpr u64 kReadChunk = 256;
|
||||
|
||||
} // namespace
|
||||
|
||||
StreamSeries::StreamSeries(u64 capacity, f64 x0, f64 dx)
|
||||
: ring_(capacity), x0_(x0), dx_(dx) {}
|
||||
|
||||
void StreamSeries::push(f32 y) { ring_.push(y); }
|
||||
|
||||
void StreamSeries::push(const f32* ys, u64 n) { ring_.push(ys, n); }
|
||||
|
||||
u64 StreamSeries::produced() const { return ring_.produced(); }
|
||||
|
||||
void StreamSeries::acquire() {
|
||||
// One atomic read: every absolute index below is relative to this single count,
|
||||
// so the producer may keep pushing without the snapshot shifting under us.
|
||||
const u64 p = ring_.produced();
|
||||
const u64 cap = ring_.capacity();
|
||||
const u64 base = p > cap ? p - cap : 0; // oldest index the ring still holds
|
||||
const u64 n = p - base; // retained, published sample count
|
||||
|
||||
if (base != base_) {
|
||||
// The window slid: samples the pyramid covered are gone, and because bucket
|
||||
// boundaries are absolute, extending it incrementally would mis-align every
|
||||
// bucket. Rebuild from the retained samples instead.
|
||||
pyramid_.clear();
|
||||
pyramid_.reserve(n);
|
||||
f32 lo = 0.0f, hi = 0.0f;
|
||||
bool first = true;
|
||||
f32 chunk[kReadChunk];
|
||||
for (u64 i = 0; i < n;) {
|
||||
const u64 take = std::min<u64>(kReadChunk, n - i);
|
||||
ring_.copy_from(base + i, take, chunk);
|
||||
for (u64 k = 0; k < take; ++k) {
|
||||
const f32 v = chunk[k];
|
||||
pyramid_.append(v);
|
||||
if (first) {
|
||||
lo = v;
|
||||
hi = v;
|
||||
first = false;
|
||||
} else {
|
||||
lo = std::min(lo, v);
|
||||
hi = std::max(hi, v);
|
||||
}
|
||||
}
|
||||
i += take;
|
||||
}
|
||||
y_bounds_ = first ? Range{0.0, 0.0} : Range{f64(lo), f64(hi)};
|
||||
base_ = base;
|
||||
} else {
|
||||
// Contiguous: only the samples produced since the last acquire() are new.
|
||||
if (n == 0) y_bounds_ = Range{0.0, 0.0};
|
||||
f32 chunk[kReadChunk];
|
||||
for (u64 i = end_ - base_; i < n;) {
|
||||
const u64 take = std::min<u64>(kReadChunk, n - i);
|
||||
ring_.copy_from(base + i, take, chunk);
|
||||
for (u64 k = 0; k < take; ++k) {
|
||||
const f32 v = chunk[k];
|
||||
pyramid_.append(v);
|
||||
if (i + k == 0) {
|
||||
y_bounds_ = Range{f64(v), f64(v)};
|
||||
} else {
|
||||
y_bounds_.min = std::min(y_bounds_.min, f64(v));
|
||||
y_bounds_.max = std::max(y_bounds_.max, f64(v));
|
||||
}
|
||||
}
|
||||
i += take;
|
||||
}
|
||||
}
|
||||
end_ = p;
|
||||
}
|
||||
|
||||
u64 StreamSeries::size() const { return end_ - base_; }
|
||||
|
||||
f64 StreamSeries::x0() const { return x0_ + f64(base_) * dx_; }
|
||||
|
||||
f64 StreamSeries::dx() const { return dx_; }
|
||||
|
||||
Range StreamSeries::x_bounds() const {
|
||||
const f64 n = f64(size());
|
||||
return n == 0.0 ? Range{x0(), x0()} : Range{x0(), x0() + (n - 1.0) * dx_};
|
||||
}
|
||||
|
||||
Range StreamSeries::y_bounds() const { return y_bounds_; }
|
||||
|
||||
const MinMaxPyramid& StreamSeries::pyramid() const { return pyramid_; }
|
||||
|
||||
void StreamSeries::gather(u32 level, u64 begin, u64 end, std::vector<f32>& out_xy) const {
|
||||
gather_impl(level, begin, end, out_xy);
|
||||
}
|
||||
|
||||
void StreamSeries::copy_raw(u64 begin, u64 end, f32* out) const {
|
||||
const u64 want = end > begin ? end - begin : 0;
|
||||
if (want == 0) return;
|
||||
const u64 n = size();
|
||||
const u64 b = std::min(begin, n);
|
||||
const u64 e = std::min(std::max(end, b), n);
|
||||
// Retained index b is absolute base_ + b: fixed for the whole copy, so a
|
||||
// pushing producer cannot shift the window midway.
|
||||
ring_.copy_from(base_ + b, e - b, out);
|
||||
std::fill(out + static_cast<std::ptrdiff_t>(e - b),
|
||||
out + static_cast<std::ptrdiff_t>(want), 0.0f);
|
||||
}
|
||||
|
||||
} // namespace plot::data
|
||||
Reference in new issue
Block a user