mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
performance: Misc allocation improvements
```
│ stash.bench │ perf-allloc20251118.bench │
│ sec/op │ sec/op vs base │
CreateShortcodePlaceholders-10 54.16n ± ∞ ¹ 39.06n ± ∞ ¹ -27.87% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ stash.bench │ perf-allloc20251118.bench │
│ B/op │ B/op vs base │
CreateShortcodePlaceholders-10 52.00 ± ∞ ¹ 48.00 ± ∞ ¹ -7.69% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
│ stash.bench │ perf-allloc20251118.bench │
│ allocs/op │ allocs/op vs base │
CreateShortcodePlaceholders-10 2.000 ± ∞ ¹ 1.000 ± ∞ ¹ -50.00% (p=0.029 n=4)
¹ need >= 6 samples for confidence interval at level 0.95
````
This commit is contained in:
@@ -86,7 +86,7 @@ type HugoSites struct {
|
|||||||
// Cache for page listings.
|
// Cache for page listings.
|
||||||
cachePages *dynacache.Partition[string, page.Pages]
|
cachePages *dynacache.Partition[string, page.Pages]
|
||||||
// Cache for content sources.
|
// Cache for content sources.
|
||||||
cacheContentSource *dynacache.Partition[string, *resources.StaleValue[[]byte]]
|
cacheContentSource *dynacache.Partition[uint64, *resources.StaleValue[[]byte]]
|
||||||
|
|
||||||
// Before Hugo 0.122.0 we managed all translations in a map using a translationKey
|
// Before Hugo 0.122.0 we managed all translations in a map using a translationKey
|
||||||
// that could be overridden in front matter.
|
// that could be overridden in front matter.
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -69,12 +68,6 @@ func (m *pageMetaSource) parseFrontMatter(
|
|||||||
h *HugoSites,
|
h *HugoSites,
|
||||||
sid uint64,
|
sid uint64,
|
||||||
) error {
|
) error {
|
||||||
var sourceKey string
|
|
||||||
|
|
||||||
if sourceKey == "" {
|
|
||||||
sourceKey = strconv.FormatUint(sid, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
var filename string
|
var filename string
|
||||||
if m.f != nil {
|
if m.f != nil {
|
||||||
filename = m.f.Filename()
|
filename = m.f.Filename()
|
||||||
@@ -83,7 +76,6 @@ func (m *pageMetaSource) parseFrontMatter(
|
|||||||
m.pi = &contentParseInfo{
|
m.pi = &contentParseInfo{
|
||||||
h: h,
|
h: h,
|
||||||
sid: sid,
|
sid: sid,
|
||||||
sourceKey: sourceKey,
|
|
||||||
openSource: m.openSource,
|
openSource: m.openSource,
|
||||||
shortcodeParseInfo: newShortcodeHandler(filename, h.Deps),
|
shortcodeParseInfo: newShortcodeHandler(filename, h.Deps),
|
||||||
}
|
}
|
||||||
@@ -163,8 +155,7 @@ func (c *cachedContent) getOrCreateScope(scope string, pco *pageContentOutput) *
|
|||||||
type contentParseInfo struct {
|
type contentParseInfo struct {
|
||||||
h *HugoSites
|
h *HugoSites
|
||||||
|
|
||||||
sid uint64
|
sid uint64
|
||||||
sourceKey string
|
|
||||||
|
|
||||||
// The source bytes.
|
// The source bytes.
|
||||||
openSource hugio.OpenReadSeekCloser
|
openSource hugio.OpenReadSeekCloser
|
||||||
@@ -323,7 +314,7 @@ Loop:
|
|||||||
currShortcode.pos = it.Pos()
|
currShortcode.pos = it.Pos()
|
||||||
currShortcode.length = iter.Current().Pos() - it.Pos()
|
currShortcode.length = iter.Current().Pos() - it.Pos()
|
||||||
if currShortcode.placeholder == "" {
|
if currShortcode.placeholder == "" {
|
||||||
currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, currShortcode.ordinal)
|
currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, uint64(currShortcode.ordinal))
|
||||||
}
|
}
|
||||||
|
|
||||||
if currShortcode.name != "" {
|
if currShortcode.name != "" {
|
||||||
@@ -335,7 +326,7 @@ Loop:
|
|||||||
currShortcode.params = s
|
currShortcode.params = s
|
||||||
}
|
}
|
||||||
|
|
||||||
currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, ordinal)
|
currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, uint64(ordinal))
|
||||||
ordinal++
|
ordinal++
|
||||||
s.shortcodes = append(s.shortcodes, currShortcode)
|
s.shortcodes = append(s.shortcodes, currShortcode)
|
||||||
|
|
||||||
@@ -390,10 +381,10 @@ func (c *cachedContent) mustSource() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pi *contentParseInfo) contentSource(s resource.StaleInfo) ([]byte, error) {
|
func (pi *contentParseInfo) contentSource(s resource.StaleInfo) ([]byte, error) {
|
||||||
key := pi.sourceKey
|
key := pi.sid
|
||||||
versionv := s.StaleVersion()
|
versionv := s.StaleVersion()
|
||||||
|
|
||||||
v, err := pi.h.cacheContentSource.GetOrCreate(key, func(string) (*resources.StaleValue[[]byte], error) {
|
v, err := pi.h.cacheContentSource.GetOrCreate(key, func(uint64) (*resources.StaleValue[[]byte], error) {
|
||||||
b, err := pi.readSourceAll()
|
b, err := pi.readSourceAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -457,13 +448,13 @@ type contentPlainPlainWords struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *cachedContentScope) keyScope(ctx context.Context) string {
|
func (c *cachedContentScope) keyScope(ctx context.Context) string {
|
||||||
return hugo.GetMarkupScope(ctx) + c.pco.po.f.Name
|
return hugo.GetMarkupScope(ctx) + c.pco.po.f.Name + fmt.Sprintf("_%d", c.pi.sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cachedContentScope) contentRendered(ctx context.Context) (contentSummary, error) {
|
func (c *cachedContentScope) contentRendered(ctx context.Context) (contentSummary, error) {
|
||||||
cp := c.pco
|
cp := c.pco
|
||||||
ctx = tpl.Context.DependencyScope.Set(ctx, pageDependencyScopeGlobal)
|
ctx = tpl.Context.DependencyScope.Set(ctx, pageDependencyScopeGlobal)
|
||||||
key := c.pi.sourceKey + "/" + c.keyScope(ctx)
|
key := c.keyScope(ctx)
|
||||||
|
|
||||||
versionv := c.version(cp)
|
versionv := c.version(cp)
|
||||||
|
|
||||||
@@ -616,7 +607,7 @@ var setGetContentCallbackInContext = contexthelpers.NewContextDispatcher[func(*p
|
|||||||
|
|
||||||
func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfContents, error) {
|
func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfContents, error) {
|
||||||
cp := c.pco
|
cp := c.pco
|
||||||
key := c.pi.sourceKey + "/" + c.keyScope(ctx)
|
key := c.keyScope(ctx)
|
||||||
versionv := c.version(cp)
|
versionv := c.version(cp)
|
||||||
|
|
||||||
v, err := c.pm.contentTableOfContents.GetOrCreate(key, func(string) (*resources.StaleValue[contentTableOfContents], error) {
|
v, err := c.pm.contentTableOfContents.GetOrCreate(key, func(string) (*resources.StaleValue[contentTableOfContents], error) {
|
||||||
@@ -737,7 +728,7 @@ func (c *cachedContent) version(cp *pageContentOutput) uint32 {
|
|||||||
|
|
||||||
func (c *cachedContentScope) contentPlain(ctx context.Context) (contentPlainPlainWords, error) {
|
func (c *cachedContentScope) contentPlain(ctx context.Context) (contentPlainPlainWords, error) {
|
||||||
cp := c.pco
|
cp := c.pco
|
||||||
key := c.pi.sourceKey + "/" + c.keyScope(ctx)
|
key := c.keyScope(ctx)
|
||||||
|
|
||||||
versionv := c.version(cp)
|
versionv := c.version(cp)
|
||||||
|
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ func (scp *ShortcodeWithPage) Unwrapv() any {
|
|||||||
// Note - this value must not contain any markup syntax
|
// Note - this value must not contain any markup syntax
|
||||||
const shortcodePlaceholderPrefix = "HAHAHUGOSHORTCODE"
|
const shortcodePlaceholderPrefix = "HAHAHUGOSHORTCODE"
|
||||||
|
|
||||||
func createShortcodePlaceholder(sid string, id uint64, ordinal int) string {
|
func createShortcodePlaceholder(sid string, id, ordinal uint64) string {
|
||||||
return shortcodePlaceholderPrefix + strconv.FormatUint(id, 10) + sid + strconv.Itoa(ordinal) + "HBHB"
|
return shortcodePlaceholderPrefix + strconv.FormatUint(id, 10) + sid + strconv.FormatUint(ordinal, 10) + "HBHB"
|
||||||
}
|
}
|
||||||
|
|
||||||
type shortcode struct {
|
type shortcode struct {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2025 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package hugolib
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func BenchmarkCreateShortcodePlaceholders(b *testing.B) {
|
||||||
|
var ordinal int = 42
|
||||||
|
for b.Loop() {
|
||||||
|
createShortcodePlaceholder("shortcodeName", 32, uint64(ordinal))
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -451,7 +451,7 @@ func newHugoSites(
|
|||||||
page.Pages](d.MemCache, "/pags/all",
|
page.Pages](d.MemCache, "/pags/all",
|
||||||
dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
|
dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
|
||||||
),
|
),
|
||||||
cacheContentSource: dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
cacheContentSource: dynacache.GetOrCreatePartition[uint64, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||||
translationKeyPages: maps.NewSliceCache[page.Page](),
|
translationKeyPages: maps.NewSliceCache[page.Page](),
|
||||||
currentSite: first[0],
|
currentSite: first[0],
|
||||||
skipRebuildForFilenames: make(map[string]bool),
|
skipRebuildForFilenames: make(map[string]bool),
|
||||||
|
|||||||
@@ -398,11 +398,22 @@ func BenchmarkSets(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("HasAnyVector", func(b *testing.B) {
|
b.Run("HasAnyVector(Sets)", func(b *testing.B) {
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
_ = sets1.HasAnyVector(sets2)
|
_ = sets1.HasAnyVector(sets2)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
b.Run("HasAnyVector(Vector)", func(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
_ = sets1.HasAnyVector(v1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("HasAnyVector(&Vector)", func(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
_ = sets1.HasAnyVector(&v1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
b.Run("FirstVector", func(b *testing.B) {
|
b.Run("FirstVector", func(b *testing.B) {
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
|
|||||||
Reference in New Issue
Block a user