mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
125 lines
2.9 KiB
Go
125 lines
2.9 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
|
|
"github.com/bluele/gcache"
|
|
"github.com/spaolacci/murmur3"
|
|
|
|
"github.com/weaveworks/scope/report"
|
|
)
|
|
|
|
// Merger is the type for a thing that can merge reports.
|
|
type Merger interface {
|
|
Merge([]report.Report) report.Report
|
|
}
|
|
|
|
type dumbMerger struct{}
|
|
|
|
// MakeDumbMerger makes a Merger which merges together reports in the simplest possible way.
|
|
func MakeDumbMerger() Merger {
|
|
return dumbMerger{}
|
|
}
|
|
|
|
func (dumbMerger) Merge(reports []report.Report) report.Report {
|
|
rpt := report.MakeReport()
|
|
id := murmur3.New64()
|
|
for _, r := range reports {
|
|
rpt = rpt.Merge(r)
|
|
id.Write([]byte(r.ID))
|
|
}
|
|
rpt.ID = fmt.Sprintf("%x", id.Sum64())
|
|
return rpt
|
|
}
|
|
|
|
type smartMerger struct {
|
|
cache gcache.Cache
|
|
}
|
|
|
|
// NewSmartMerger makes a Merger which merges together reports as
|
|
// a binary tree of reports. Speed up comes from the fact that
|
|
// most merges are between small reports.
|
|
func NewSmartMerger() Merger {
|
|
return smartMerger{}
|
|
}
|
|
|
|
type node struct {
|
|
id uint64
|
|
rpt report.Report
|
|
}
|
|
|
|
type byID []*node
|
|
|
|
func (ns byID) Len() int { return len(ns) }
|
|
func (ns byID) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
|
|
func (ns byID) Less(i, j int) bool { return ns[i].id < ns[j].id }
|
|
|
|
func hash(ids ...string) uint64 {
|
|
id := murmur3.New64()
|
|
for _, i := range ids {
|
|
id.Write([]byte(i))
|
|
}
|
|
return id.Sum64()
|
|
}
|
|
|
|
func (s smartMerger) Merge(reports []report.Report) report.Report {
|
|
// Start with a sorted list of leaves.
|
|
// Note we must dedupe reports with the same ID to ensure the
|
|
// algorithm below doesn't go into an infinite loop. This is
|
|
// fine as reports with the same ID are assumed to be the same.
|
|
nodes := []*node{}
|
|
seen := map[uint64]struct{}{}
|
|
for _, r := range reports {
|
|
id := hash(r.ID)
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
nodes = append(nodes, &node{
|
|
id: id,
|
|
rpt: r,
|
|
})
|
|
}
|
|
sort.Sort(byID(nodes))
|
|
|
|
// Define how to merge two nodes together. The result of merging
|
|
// two reports is cached.
|
|
merge := func(left, right *node) *node {
|
|
return &node{
|
|
id: hash(left.rpt.ID, right.rpt.ID),
|
|
rpt: report.MakeReport().Merge(left.rpt).Merge(right.rpt),
|
|
}
|
|
}
|
|
|
|
// Define how to reduce n nodes to 1.
|
|
// Min and max are both inclusive!
|
|
var reduce func(min, max uint64, nodes []*node) *node
|
|
reduce = func(min, max uint64, nodes []*node) *node {
|
|
switch len(nodes) {
|
|
case 0:
|
|
return &node{rpt: report.MakeReport()}
|
|
case 1:
|
|
return nodes[0]
|
|
case 2:
|
|
return merge(nodes[0], nodes[1])
|
|
}
|
|
|
|
partition := min + ((max - min) / 2)
|
|
index := sort.Search(len(nodes), func(i int) bool {
|
|
return nodes[i].id > partition
|
|
})
|
|
if index == len(nodes) {
|
|
return reduce(min, partition, nodes)
|
|
} else if index == 0 {
|
|
return reduce(partition+1, max, nodes)
|
|
}
|
|
left := reduce(min, partition, nodes[:index])
|
|
right := reduce(partition+1, max, nodes[index:])
|
|
return merge(left, right)
|
|
}
|
|
|
|
return reduce(0, math.MaxUint64, nodes).rpt
|
|
}
|