From 0e3e3b74f8559ef475b422bf070cf2f20d73900e Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 25 Jan 2016 15:05:48 -0800 Subject: [PATCH] Add benchmark for list topologies API. --- app/api_topologies.go | 28 +++++++++-------- app/benchmark_internal_test.go | 55 ++++++++++++++++++++++++++++++++++ render/render.go | 12 +------- 3 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 app/benchmark_internal_test.go diff --git a/app/api_topologies.go b/app/api_topologies.go index 15b094044..1d8594be7 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -189,23 +189,25 @@ func (r *registry) walk(f func(APITopologyDesc)) { // makeTopologyList returns a handler that yields an APITopologyList. func (r *registry) makeTopologyList(rep Reporter) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, req *http.Request) { - var ( - rpt = rep.Report() - topologies = []APITopologyDesc{} - ) - r.walk(func(desc APITopologyDesc) { - renderer := renderedForRequest(req, desc) - desc.Stats = decorateWithStats(rpt, renderer) - for i := range desc.SubTopologies { - renderer := renderedForRequest(req, desc.SubTopologies[i]) - desc.SubTopologies[i].Stats = decorateWithStats(rpt, renderer) - } - topologies = append(topologies, desc) - }) + topologies := r.renderTopologies(rep.Report(), req) respondWith(w, http.StatusOK, topologies) } } +func (r *registry) renderTopologies(rpt report.Report, req *http.Request) []APITopologyDesc { + topologies := []APITopologyDesc{} + r.walk(func(desc APITopologyDesc) { + renderer := renderedForRequest(req, desc) + desc.Stats = decorateWithStats(rpt, renderer) + for i := range desc.SubTopologies { + renderer := renderedForRequest(req, desc.SubTopologies[i]) + desc.SubTopologies[i].Stats = decorateWithStats(rpt, renderer) + } + topologies = append(topologies, desc) + }) + return topologies +} + func decorateWithStats(rpt report.Report, renderer render.Renderer) topologyStats { var ( nodes int diff --git a/app/benchmark_internal_test.go b/app/benchmark_internal_test.go new file mode 100644 index 000000000..ccc5ff4a8 --- /dev/null +++ b/app/benchmark_internal_test.go @@ -0,0 +1,55 @@ +package app + +import ( + "encoding/json" + "flag" + "io/ioutil" + "net/http" + "net/url" + "testing" + + "github.com/weaveworks/scope/render" + "github.com/weaveworks/scope/report" + "github.com/weaveworks/scope/test/fixture" +) + +// StaticReport is used as a fixture in tests. It emulates an xfer.Collector. +type StaticReporter struct{ r report.Report } + +func (s StaticReporter) Report() report.Report { return s.r } +func (s StaticReporter) WaitOn(chan struct{}) {} +func (s StaticReporter) UnWait(chan struct{}) {} + +var ( + benchReportFile = flag.String("bench-report-file", "", "json report file to use for benchmarking (relative to this package)") +) + +func loadReport() (report.Report, error) { + if *benchReportFile == "" { + return fixture.Report, nil + } + + b, err := ioutil.ReadFile(*benchReportFile) + if err != nil { + return fixture.Report, err + } + rpt := report.MakeReport() + err = json.Unmarshal(b, &rpt) + return rpt, err +} + +func BenchmarkTopologyList(b *testing.B) { + report, err := loadReport() + if err != nil { + b.Fatal(err) + } + b.ReportAllocs() + b.ResetTimer() + request := &http.Request{ + Form: url.Values{}, + } + for i := 0; i < b.N; i++ { + render.ResetCache() + topologyRegistry.renderTopologies(report, request) + } +} diff --git a/render/render.go b/render/render.go index a273a4664..f2e3ec6da 100644 --- a/render/render.go +++ b/render/render.go @@ -3,7 +3,6 @@ package render import ( "fmt" "reflect" - "strings" "github.com/bluele/gcache" @@ -11,34 +10,25 @@ import ( ) var renderCache = gcache.New(100).LRU().Build() -var depth = 0 func memoisedRender(r Renderer, rpt report.Report) RenderableNodes { - fmt.Printf("%sRendering: %#v\n", strings.Repeat(" ", depth), r) - key := "" v := reflect.ValueOf(r) switch v.Kind() { case reflect.Ptr, reflect.Func: key = fmt.Sprintf("%s-%x", rpt.ID, v.Pointer()) default: - fmt.Printf("%s- Cannot memoise: %v\n", strings.Repeat(" ", depth), r) return r.Render(rpt) } if result, err := renderCache.Get(key); err == nil { - fmt.Printf("%s- Hit %s\n", strings.Repeat(" ", depth), key) return result.(RenderableNodes) } - fmt.Printf("%s- Miss %s\n", strings.Repeat(" ", depth), key) - - depth += 1 output := r.Render(rpt) - depth -= 1 - renderCache.Set(key, output) return output } +// ResetCache blows away the rendered node cache. func ResetCache() { renderCache.Purge() }