diff --git a/app/benchmark_internal_test.go b/app/benchmark_internal_test.go index afc61e63f..fa5944654 100644 --- a/app/benchmark_internal_test.go +++ b/app/benchmark_internal_test.go @@ -72,6 +72,15 @@ func BenchmarkReportMerge(b *testing.B) { } } +func BenchmarkReportFastMerge(b *testing.B) { + reports := upgradeReports(readReportFiles(b, *benchReportPath)) + merger := NewFastMerger() + b.ResetTimer() + for i := 0; i < b.N; i++ { + merger.Merge(reports) + } +} + func getReport(b *testing.B) report.Report { r := fixture.Report if *benchReportPath != "" { diff --git a/app/merger.go b/app/merger.go index 94231baf8..ac5c936f3 100644 --- a/app/merger.go +++ b/app/merger.go @@ -60,3 +60,21 @@ func (smartMerger) Merge(reports []report.Report) report.Report { } return <-c } + +type fastMerger struct{} + +// NewFastMerger makes a Merger which merges together reports, mutating the one we are building up +func NewFastMerger() Merger { + return fastMerger{} +} + +func (fastMerger) Merge(reports []report.Report) report.Report { + rpt := report.MakeReport() + id := murmur3.New64() + for _, r := range reports { + rpt.UnsafeMerge(r) + id.Write([]byte(r.ID)) + } + rpt.ID = fmt.Sprintf("%x", id.Sum64()) + return rpt +} diff --git a/app/merger_test.go b/app/merger_test.go index 5ec36c307..ec114eedb 100644 --- a/app/merger_test.go +++ b/app/merger_test.go @@ -52,6 +52,10 @@ func BenchmarkDumbMerger(b *testing.B) { benchmarkMerger(b, app.MakeDumbMerger()) } +func BenchmarkFastMerger(b *testing.B) { + benchmarkMerger(b, app.NewFastMerger()) +} + const numHosts = 15 func benchmarkMerger(b *testing.B, merger app.Merger) { diff --git a/report/report.go b/report/report.go index a217550e9..d21551c50 100644 --- a/report/report.go +++ b/report/report.go @@ -305,16 +305,21 @@ func (r Report) Copy() Report { // original is not modified. func (r Report) Merge(other Report) Report { newReport := r.Copy() - newReport.DNS = newReport.DNS.Merge(other.DNS) - newReport.Sampling = newReport.Sampling.Merge(other.Sampling) - newReport.Window = newReport.Window + other.Window - newReport.Plugins = newReport.Plugins.Merge(other.Plugins) - newReport.WalkPairedTopologies(&other, func(ourTopology, theirTopology *Topology) { - *ourTopology = ourTopology.Merge(*theirTopology) - }) + newReport.UnsafeMerge(other) return newReport } +// UnsafeMerge merges another Report into the receiver. The original is modified. +func (r *Report) UnsafeMerge(other Report) { + r.DNS = r.DNS.Merge(other.DNS) + r.Sampling = r.Sampling.Merge(other.Sampling) + r.Window = r.Window + other.Window + r.Plugins = r.Plugins.Merge(other.Plugins) + r.WalkPairedTopologies(&other, func(ourTopology, theirTopology *Topology) { + ourTopology.UnsafeMerge(*theirTopology) + }) +} + // WalkTopologies iterates through the Topologies of the report, // potentially modifying them func (r *Report) WalkTopologies(f func(*Topology)) { diff --git a/report/topology.go b/report/topology.go index 83baa0f94..49c01678c 100644 --- a/report/topology.go +++ b/report/topology.go @@ -163,6 +163,21 @@ func (t Topology) Merge(other Topology) Topology { } } +// UnsafeMerge merges the other object into this one, modifying the original. +func (t *Topology) UnsafeMerge(other Topology) { + if t.Shape == "" { + t.Shape = other.Shape + } + if t.Label == "" { + t.Label, t.LabelPlural = other.Label, other.LabelPlural + } + t.Nodes.UnsafeMerge(other.Nodes) + t.Controls = t.Controls.Merge(other.Controls) + t.MetadataTemplates = t.MetadataTemplates.Merge(other.MetadataTemplates) + t.MetricTemplates = t.MetricTemplates.Merge(other.MetricTemplates) + t.TableTemplates = t.TableTemplates.Merge(other.TableTemplates) +} + // Nodes is a collection of nodes in a topology. Keys are node IDs. // TODO(pb): type Topology map[string]Node type Nodes map[string]Node @@ -186,14 +201,19 @@ func (n Nodes) Merge(other Nodes) Nodes { return n } cp := n.Copy() + cp.UnsafeMerge(other) + return cp +} + +// UnsafeMerge merges the other object into this one, modifying the original. +func (n *Nodes) UnsafeMerge(other Nodes) { for k, v := range other { - if n, ok := cp[k]; ok { // don't overwrite - cp[k] = v.Merge(n) + if existing, ok := (*n)[k]; ok { // don't overwrite + (*n)[k] = v.Merge(existing) } else { - cp[k] = v + (*n)[k] = v } } - return cp } // Validate checks the topology for various inconsistencies.