From 56892addfc9faa438c9c3dc20e793791f82b84d6 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 6 Mar 2017 10:33:03 +0000 Subject: [PATCH] quantise reports Merge all reports received within a specified interval, discarding the originals. This improves performance of Report() on repeated invocation since it ends up merging fewer reports. For example, if reports are received every second (e.g. 3 probes reporting at the default probe.publishInterval of 3s), and the app.windows is 15s (the default) and the report generation interval is 5s (e.g. one UI accessing every TOPOLOGY_INTERVAL; the default), then the original code would have to merge 15 reports per UI access, whereas this code will only need to merge 8 to 9 reports (3-4 merged reports from previous invocation plus 5 recently received reports). --- app/collector.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/app/collector.go b/app/collector.go index 2a57ba10f..b17bacbab 100644 --- a/app/collector.go +++ b/app/collector.go @@ -16,6 +16,16 @@ import ( "github.com/weaveworks/scope/report" ) +// We merge all reports received within the specified interval, and +// discard the orignals. Higher figures improve the performance of +// Report(), but at the expense of lower time resolution, since time +// is effectively advancing in quantiles. +// +// The current figure is identical to the default +// probe.publishInterval, which results in performance improvements +// as soon as there is more than one probe. +const reportQuantisationInterval = 3 * time.Second + // Reporter is something that can produce reports on demand. It's a convenient // interface for parts of the app, and several experimental components. type Reporter interface { @@ -123,11 +133,14 @@ func (c *collector) Report(_ context.Context) (report.Report, error) { } c.clean() + c.quantise() + rpt := c.merger.Merge(c.reports).Upgrade() c.cached = &rpt return rpt, nil } +// remove reports older than the app.window func (c *collector) clean() { var ( cleanedReports = make([]report.Report, 0, len(c.reports)) @@ -144,6 +157,36 @@ func (c *collector) clean() { c.timestamps = cleanedTimestamps } +// Merge reports received within the same reportQuantisationInterval. +// +// Quantisation is relative to the time of the first report in a given +// interval, rather than absolute time. So, for example, with a +// reportQuantisationInterval of 3s and reports with timestamps [0, 1, +// 2, 5, 6, 7], the result contains merged reports with +// timestamps/content of [0:{0,1,2}, 5:{5,6,7}]. +func (c *collector) quantise() { + if len(c.reports) == 0 { + return + } + var ( + quantisedReports = make([]report.Report, 0, len(c.reports)) + quantisedTimestamps = make([]time.Time, 0, len(c.timestamps)) + ) + quantumStartIdx := 0 + quantumStartTimestamp := c.timestamps[0] + for i, t := range c.timestamps { + if t.Sub(quantumStartTimestamp) < reportQuantisationInterval { + continue + } + quantisedReports = append(quantisedReports, c.merger.Merge(c.reports[quantumStartIdx:i])) + quantisedTimestamps = append(quantisedTimestamps, quantumStartTimestamp) + quantumStartIdx = i + quantumStartTimestamp = t + } + c.reports = append(quantisedReports, c.merger.Merge(c.reports[quantumStartIdx:])) + c.timestamps = append(quantisedTimestamps, c.timestamps[quantumStartIdx]) +} + // StaticCollector always returns the given report. type StaticCollector report.Report