output: Speed up layout calculations - hugo - [fork] hugo port for 9front
HTML git clone git@git.drkhsh.at/hugo.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
DIR commit 6178238a0b069ae8ce65a23e3dd60c091de0cfef
DIR parent df953839143c15e147d35f8908ed7f02fb62a10a
HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date: Sat, 18 Mar 2017 16:46:10 +0100
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%
```
Diffstat:
M hugolib/page.go | 7 +++++--
M hugolib/site.go | 2 +-
M output/layout.go | 26 +++++++++++++++++++++++++-
M output/layout_test.go | 11 +++++++++++
4 files changed, 42 insertions(+), 4 deletions(-)
---
DIR diff --git a/hugolib/page.go b/hugolib/page.go
@@ -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
}
DIR diff --git a/hugolib/site.go b/hugolib/site.go
@@ -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 {
DIR diff --git a/output/layout.go b/output/layout.go
@@ -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
}
DIR diff --git a/output/layout_test.go b/output/layout_test.go
@@ -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)
+ }
}