ref: 6178238a0b069ae8ce65a23e3dd60c091de0cfef
parent: df953839143c15e147d35f8908ed7f02fb62a10a
author: Bjørn Erik Pedersen <[email protected]>
date: Sat Mar 18 12:46:10 EDT 2017
output: Speed up layout calculations ``` BenchmarkLayout-4 4883 497 -89.82% benchmark old allocs new allocs delta BenchmarkLayout-4 18 1 -94.44% benchmark old bytes new bytes delta BenchmarkLayout-4 1624 32 -98.03% ```
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -190,6 +190,8 @@
permalink string
relPermalink string
+ layoutDescriptor output.LayoutDescriptor
+
scratch *Scratch
// It would be tempting to use the language set on the Site, but in they way we do
@@ -666,7 +668,7 @@
}
return p.s.layoutHandler.For(
- p.createLayoutDescriptor(),
+ p.layoutDescriptor,
layoutOverride,
output.HTMLType)
}
@@ -880,6 +882,7 @@
p.permalink = p.s.permalink(rel)
rel = p.s.PathSpec.PrependBasePath(rel)
p.relPermalink = rel
+ p.layoutDescriptor = p.createLayoutDescriptor()
return nil
}
@@ -1558,7 +1561,7 @@
func (p *Page) RSSlink() template.URL {
// TODO(bep) we cannot have two of these
// Remove in Hugo 0.20
- helpers.Deprecated(".Page", "Use RSSlink", "RSSLink", true)
+ helpers.Deprecated(".Page", "RSSlink", "Use RSSLink", true)
return p.RSSLink
}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1656,7 +1656,7 @@
}
func (s *Site) layouts(p *PageOutput) []string {
- return s.layoutHandler.For(p.createLayoutDescriptor(), "", p.outputFormat)
+ return s.layoutHandler.For(p.layoutDescriptor, "", p.outputFormat)
}
func (s *Site) preparePages() error {
--- a/output/layout.go
+++ b/output/layout.go
@@ -17,6 +17,7 @@
"fmt"
"path"
"strings"
+ "sync"
)
// LayoutDescriptor describes how a layout should be chosen. This is
@@ -32,10 +33,19 @@
// TODO(bep) output improve names
type LayoutHandler struct {
hasTheme bool
+
+ mu sync.RWMutex
+ cache map[layoutCacheKey][]string
}
+type layoutCacheKey struct {
+ d LayoutDescriptor
+ layoutOverride string
+ f Format
+}
+
func NewLayoutHandler(hasTheme bool) *LayoutHandler {
- return &LayoutHandler{hasTheme: hasTheme}
+ return &LayoutHandler{hasTheme: hasTheme, cache: make(map[layoutCacheKey][]string)}
}
const (
@@ -62,6 +72,16 @@
)
func (l *LayoutHandler) For(d LayoutDescriptor, layoutOverride string, f Format) []string {
+
+ // We will get lots of requests for the same layouts, so avoid recalculations.
+ key := layoutCacheKey{d, layoutOverride, f}
+ l.mu.RLock()
+ if cacheVal, found := l.cache[key]; found {
+ l.mu.RUnlock()
+ return cacheVal
+ }
+ l.mu.RUnlock()
+
var layouts []string
layout := d.Layout
@@ -109,6 +129,10 @@
return layoutsWithThemeLayouts
}
+
+ l.mu.Lock()
+ l.cache[key] = layouts
+ l.mu.Unlock()
return layouts
}
--- a/output/layout_test.go
+++ b/output/layout_test.go
@@ -73,4 +73,15 @@
}
})
}
+
+}
+
+func BenchmarkLayout(b *testing.B) {
+ descriptor := LayoutDescriptor{Kind: "taxonomyTerm", Section: "categories"}
+ l := NewLayoutHandler(false)
+
+ for i := 0; i < b.N; i++ {
+ layouts := l.For(descriptor, "", HTMLType)
+ require.NotEmpty(b, layouts)
+ }
}