tpl: Add a partial lookup cache

````
                 │ stash.bench  │          perf-v146.bench           │
                 │    sec/op    │   sec/op     vs base               │
LookupPartial-10   248.00n ± 0%   14.75n ± 2%  -94.05% (p=0.002 n=6)

                 │ stash.bench │          perf-v146.bench          │
                 │    B/op     │   B/op     vs base                │
LookupPartial-10    48.00 ± 0%   0.00 ± 0%  -100.00% (p=0.002 n=6)

                 │ stash.bench │          perf-v146.bench           │
                 │  allocs/op  │ allocs/op   vs base                │
LookupPartial-10    3.000 ± 0%   0.000 ± 0%  -100.00% (p=0.002 n=6)
```

THe speedup above assumes reuse of the same partials over and over again, which I think is not uncommon.

This commits also adds some more lookup benchmarks. The current output of these on my MacBook looks decent:

```
BenchmarkLookupPagesLayout/Single_root-10                3031562               395.5 ns/op             0 B/op          0 allocs/op
BenchmarkLookupPagesLayout/Single_sub_folder-10          2515915               480.9 ns/op             0 B/op          0 allocs/op
BenchmarkLookupPartial-10                               84808112                14.13 ns/op            0 B/op          0 allocs/op
BenchmarkLookupShortcode/toplevelpage-10                 8111779               148.2 ns/op             0 B/op          0 allocs/op
BenchmarkLookupShortcode/nestedpage-10                   8088183               148.6 ns/op             0 B/op          0 allocs/op
```

Note that in the above the partial lookups are cahced, the others not (they are harder to cache because of the page path).

Closes #13571
This commit is contained in:
Bjørn Erik Pedersen
2025-04-10 09:22:29 +02:00
parent 18d2d2f985
commit 208a0de6c3
11 changed files with 158 additions and 73 deletions

View File

@@ -51,7 +51,7 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, err
var templateDesc tplimpl.TemplateDescriptor
var base string = ""
if ps, ok := p.(*pageState); ok {
base, templateDesc = ps.getTemplateBasePathAndDescriptor()
base, templateDesc = ps.GetInternalTemplateBasePathAndDescriptor()
}
templateDesc.Layout = ""
templateDesc.Kind = ""

View File

@@ -476,7 +476,8 @@ func (ps *pageState) initCommonProviders(pp pagePaths) error {
return nil
}
func (po *pageOutput) getTemplateBasePathAndDescriptor() (string, tplimpl.TemplateDescriptor) {
// Exported so it can be used in integration tests.
func (po *pageOutput) GetInternalTemplateBasePathAndDescriptor() (string, tplimpl.TemplateDescriptor) {
p := po.p
f := po.f
base := p.PathInfo().BaseReTyped(p.m.pageConfig.Type)
@@ -491,7 +492,7 @@ func (po *pageOutput) getTemplateBasePathAndDescriptor() (string, tplimpl.Templa
}
func (p *pageState) resolveTemplate(layouts ...string) (*tplimpl.TemplInfo, bool, error) {
dir, d := p.getTemplateBasePathAndDescriptor()
dir, d := p.GetInternalTemplateBasePathAndDescriptor()
if len(layouts) > 0 {
d.Layout = layouts[0]

View File

@@ -97,7 +97,7 @@ type pageCommon struct {
pageMenus *pageMenus
// Internal use
page.InternalDependencies
page.RelatedDocsHandlerProvider
contentConverterInit sync.Once
contentConverter converter.Converter

View File

@@ -209,11 +209,11 @@ func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
ShortcodeInfoProvider: page.NopPage,
LanguageProvider: m.s,
InternalDependencies: m.s,
init: lazy.New(),
m: m,
s: m.s,
sWrapped: page.WrapSite(m.s),
RelatedDocsHandlerProvider: m.s,
init: lazy.New(),
m: m,
s: m.s,
sWrapped: page.WrapSite(m.s),
},
}

View File

@@ -275,7 +275,7 @@ func (pco *pageContentOutput) initRenderHooks() error {
// Inherit the descriptor from the page/current output format.
// This allows for fine-grained control of the template used for
// rendering of e.g. links.
base, layoutDescriptor := pco.po.p.getTemplateBasePathAndDescriptor()
base, layoutDescriptor := pco.po.p.GetInternalTemplateBasePathAndDescriptor()
switch tp {
case hooks.LinkRendererType:

View File

@@ -397,7 +397,7 @@ func doRenderShortcode(
ofCount[match.D.OutputFormat]++
return true
}
base, layoutDescriptor := po.getTemplateBasePathAndDescriptor()
base, layoutDescriptor := po.GetInternalTemplateBasePathAndDescriptor()
q := tplimpl.TemplateQuery{
Path: base,
Name: sc.name,