URI: 
       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)
       +        }
        }