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%
```
This commit is contained in:
Bjørn Erik Pedersen 2017-03-18 16:46:10 +01:00
parent df95383914
commit 6178238a0b
4 changed files with 42 additions and 4 deletions

View file

@ -190,6 +190,8 @@ type Page struct {
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 @@ func (p *Page) layouts(layouts ...string) []string {
}
return p.s.layoutHandler.For(
p.createLayoutDescriptor(),
p.layoutDescriptor,
layoutOverride,
output.HTMLType)
}
@ -880,6 +882,7 @@ func (p *Page) initURLs() error {
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) Hugo() *HugoInfo {
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
}

View file

@ -1656,7 +1656,7 @@ func (s *Site) kindFromSections(sections []string) string {
}
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 {

View file

@ -17,6 +17,7 @@ import (
"fmt"
"path"
"strings"
"sync"
)
// LayoutDescriptor describes how a layout should be chosen. This is
@ -32,10 +33,19 @@ type LayoutDescriptor struct {
// 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 @@ indexes/indexes.NAME.SUFFIX indexes/indexes.SUFFIX
)
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
@ -110,6 +130,10 @@ func (l *LayoutHandler) For(d LayoutDescriptor, layoutOverride string, f Format)
return layoutsWithThemeLayouts
}
l.mu.Lock()
l.cache[key] = layouts
l.mu.Unlock()
return layouts
}

View file

@ -73,4 +73,15 @@ func TestLayout(t *testing.T) {
}
})
}
}
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)
}
}