* `hashtable` — hash table page-occupancy histogram.
* `delta-counts` — event-counter change since this report last rendered.
* `delta-rates` — that change divided by the elapsed time.
- * `heatmap` — planner cost heatmaps (zeroed hourly when rendered).
+ * `heatmap` — planner cost heatmaps, showing the change since this report
+ last rendered. Like `delta-counts`, the token snapshots the live
+ (monotonic) heatmap counters and prints the difference from its previous
+ snapshot, so the picture tracks the report's own interval without ever
+ resetting the counters the planner threads keep accumulating. Each report
+ owns its own snapshot, so two reports rendering `heatmap` at different
+ intervals each see the change over their own interval.
### Built-in reports
}
};
-/// Renders the planner cost heatmaps and zeroes them hourly so the picture
-/// reflects recent activity rather than all-time totals. Planner threads
-/// accumulate the heatmaps lock-free; this token only reads and resets them.
-/// The hourly reset timer lives here, tying the reset to this report's
-/// cadence.
+/// Renders the change in the planner cost heatmaps since this token last
+/// rendered, the same way the delta-counts token handles event counters: it
+/// snapshots the live (monotonic) heatmaps and prints the difference from the
+/// previous snapshot, never resetting the counters the planner threads keep
+/// writing lock-free. Each report owns its own token, so the delta spans
+/// that report's own interval.
class HeatmapToken : public BeesReportToken {
shared_ptr<BeesContext> m_ctx;
- Timer m_reset_timer;
+ BeesHeatmap::Snapshot m_last_regions;
+ BeesHeatmap::Snapshot m_last_boundaries;
+ Timer m_timer;
public:
- explicit HeatmapToken(shared_ptr<BeesContext> ctx) : m_ctx(std::move(ctx)) {}
+ explicit HeatmapToken(shared_ptr<BeesContext> ctx) :
+ m_ctx(std::move(ctx)),
+ m_last_regions(m_ctx->plan_regions_heatmap().snapshot()),
+ m_last_boundaries(m_ctx->plan_boundaries_heatmap().snapshot())
+ {
+ }
void render(ostream &os) override {
- auto ®ions = m_ctx->plan_regions_heatmap();
- auto &boundaries = m_ctx->plan_boundaries_heatmap();
- if (regions.count() > 0) {
- os << "PLANNER HEATMAPS (last " << m_reset_timer.age() << "s):\n";
- regions.print(os);
- boundaries.print(os);
- }
- if (m_reset_timer.age() >= 3600.0) {
- m_reset_timer.reset();
+ const auto regions = m_ctx->plan_regions_heatmap().snapshot();
+ const auto boundaries = m_ctx->plan_boundaries_heatmap().snapshot();
+ const auto age = m_timer.age();
+ const auto d_regions = regions - m_last_regions;
+ const auto d_boundaries = boundaries - m_last_boundaries;
+ if (d_regions.count() > 0) {
+ os << "PLANNER HEATMAPS (last " << age << "s):\n";
+ BeesHeatmap::print(os, d_regions);
+ BeesHeatmap::print(os, d_boundaries);
}
+ m_last_regions = regions;
+ m_last_boundaries = boundaries;
+ m_timer.lap();
}
};
assert(out.find("col_total") != string::npos);
}
+static void
+test_snapshot_delta()
+{
+ BeesHeatmap hm("delta");
+ hm.add(3, 0.01);
+ const auto base = hm.snapshot();
+ assert(base.count() == 1);
+
+ hm.add(5, 0.02);
+ hm.add(5, 0.02);
+ const auto cur = hm.snapshot();
+ assert(cur.count() == 3);
+ // Snapshotting never touches the live counter: it stays monotonic.
+ assert(hm.count() == 3);
+
+ // The delta sees only the events recorded since the base snapshot.
+ const auto delta = cur - base;
+ assert(delta.count() == 2);
+
+ // A snapshot renders through the static print(os, snap) entry point.
+ ostringstream oss;
+ BeesHeatmap::print(oss, delta);
+ assert(oss.str().find("heatmap delta") != string::npos);
+
+ // An empty delta (no change between two equal snapshots) prints the
+ // single empty line, never a grid.
+ ostringstream oss_empty;
+ BeesHeatmap::print(oss_empty, cur - cur);
+ assert(oss_empty.str().find("(empty)") != string::npos);
+}
+
static void
test_clamping()
{
RUN_A_TEST(test_empty());
RUN_A_TEST(test_counting());
RUN_A_TEST(test_bucket_sharing());
+ RUN_A_TEST(test_snapshot_delta());
RUN_A_TEST(test_clamping());
return 0;
}