mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
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:
parent
df95383914
commit
6178238a0b
4 changed files with 42 additions and 4 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue