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,82 @@
|
||||
#pragma once
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <plot/core/types.hpp>
|
||||
|
||||
namespace plot::data {
|
||||
|
||||
/// Aggregate of one bucket: the extremes of the samples it covers.
|
||||
struct MinMax {
|
||||
f32 min = 0.0f;
|
||||
f32 max = 0.0f;
|
||||
};
|
||||
|
||||
/// Samples per level-0 bucket. Fixed so the LOD geometry is identical for every
|
||||
/// series and the level maths is a shift.
|
||||
inline constexpr u32 kBucket = 64;
|
||||
|
||||
/// Incremental min/max LOD pyramid.
|
||||
///
|
||||
/// Level 0 buckets kBucket consecutive samples and keeps one (min, max) per
|
||||
/// bucket; level l buckets 2^l level-0 buckets. Memory is n/4 bytes -- about 6%
|
||||
/// of 4-byte raw samples. append() merges into the last bucket at each level and
|
||||
/// walks up the O(log n) chain above it, so building is amortised O(1) per sample
|
||||
/// and nothing is ever recomputed from raw samples.
|
||||
///
|
||||
/// A pyramid stores *aggregates*, so it can only answer exactly for whole
|
||||
/// buckets; a range with partial edge buckets would silently pull in samples
|
||||
/// outside the range. Every query here is therefore bucket-grained and named
|
||||
/// *_buckets, and exact range queries live on Series, which owns the raw samples.
|
||||
/// reduce() is the one non-*_buckets query: it is the *covering* aggregate over
|
||||
/// the level-0 buckets that [begin, end) touches. Use it to ask which buckets a
|
||||
/// range spans, never to measure a range.
|
||||
class MinMaxPyramid {
|
||||
public:
|
||||
MinMaxPyramid() { add_level(); }
|
||||
|
||||
/// Sizes every level (buckets and prefix sums) for `n` samples so a following
|
||||
/// run of appends performs no reallocation. The pyramid itself stays empty.
|
||||
void reserve(u64 n);
|
||||
void append(f32 v);
|
||||
void clear();
|
||||
|
||||
u64 size() const noexcept { return size_; }
|
||||
/// At least 1, even when empty, so level_count() - 1 is always valid.
|
||||
u32 level_count() const noexcept { return live_levels_; }
|
||||
/// Level `l`'s buckets; empty span when `l` is beyond level_count().
|
||||
std::span<const MinMax> level(u32 l) const;
|
||||
|
||||
/// Min/max over the whole level-0 buckets fully contained in [begin, end).
|
||||
/// Partial edge buckets are excluded, never approximated; an empty result is a
|
||||
/// degenerate Range with span() == 0.
|
||||
Range reduce_buckets(u64 begin, u64 end) const;
|
||||
/// Sum of the samples in those same whole buckets, accumulated in double.
|
||||
f64 sum_buckets(u64 begin, u64 end) const;
|
||||
/// Sum of squares of the samples in those same whole buckets.
|
||||
f64 sumsq_buckets(u64 begin, u64 end) const;
|
||||
|
||||
/// Min/max over the covering level-0 buckets: every bucket that holds a sample
|
||||
/// of [begin, end). Exact only when the range is bucket-aligned.
|
||||
Range reduce(u64 begin, u64 end) const;
|
||||
|
||||
/// Pyramid level whose buckets hold about `samples_per_pixel` samples, for a
|
||||
/// window of `n` samples: 0 when a pixel covers at most one bucket. Clamped to
|
||||
/// the levels that exist, to levels with more than one bucket, and to levels
|
||||
/// that give the window at least two buckets.
|
||||
u32 level_for(u64 n, f64 samples_per_pixel) const;
|
||||
/// Index of the sample nearest x on the uniform grid (x0 + i*dx), clamped to
|
||||
/// [0, size() - 1]. Returns 0 for an empty pyramid or a non-positive dx.
|
||||
u64 snap(f64 x, f64 x0, f64 dx) const;
|
||||
|
||||
private:
|
||||
void add_level();
|
||||
|
||||
u64 size_ = 0;
|
||||
u64 reserved_n_ = 0;
|
||||
u32 live_levels_ = 1;
|
||||
std::vector<std::vector<MinMax>> levels_; // levels_[0] = kBucket granularity
|
||||
std::vector<std::vector<f64>> sums_, sumsqs_; // inclusive prefix sums per level
|
||||
};
|
||||
|
||||
} // namespace plot::data
|
||||
Reference in new issue
Block a user