]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
performance: Misc allocation improvements
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 18 Nov 2025 12:58:19 +0000 (13:58 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 18 Nov 2025 18:17:15 +0000 (19:17 +0100)
```
                               │ 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
````

hugolib/hugo_sites.go
hugolib/page__content.go
hugolib/shortcode.go
hugolib/shortcode_test.go [new file with mode: 0644]
hugolib/site.go
hugolib/sitesmatrix/vectorstores_test.go

index a01f93b75f5d7887cd56337a9ddd910b3bc449b8..e89ad222a9107dc65a57045cd94d3ec2c69fe1a2 100644 (file)
@@ -86,7 +86,7 @@ type HugoSites struct {
        // Cache for page listings.
        cachePages *dynacache.Partition[string, page.Pages]
        // 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
        // that could be overridden in front matter.
index ba164e54932602c65aa2d81046078783f189f102..f1ed435e97cf67a313a6e51542d3c33b149be98b 100644 (file)
@@ -19,7 +19,6 @@ import (
        "fmt"
        "html/template"
        "io"
-       "strconv"
        "strings"
        "sync/atomic"
        "unicode/utf8"
@@ -69,12 +68,6 @@ func (m *pageMetaSource) parseFrontMatter(
        h *HugoSites,
        sid uint64,
 ) error {
-       var sourceKey string
-
-       if sourceKey == "" {
-               sourceKey = strconv.FormatUint(sid, 10)
-       }
-
        var filename string
        if m.f != nil {
                filename = m.f.Filename()
@@ -83,7 +76,6 @@ func (m *pageMetaSource) parseFrontMatter(
        m.pi = &contentParseInfo{
                h:                  h,
                sid:                sid,
-               sourceKey:          sourceKey,
                openSource:         m.openSource,
                shortcodeParseInfo: newShortcodeHandler(filename, h.Deps),
        }
@@ -163,8 +155,7 @@ func (c *cachedContent) getOrCreateScope(scope string, pco *pageContentOutput) *
 type contentParseInfo struct {
        h *HugoSites
 
-       sid       uint64
-       sourceKey string
+       sid uint64
 
        // The source bytes.
        openSource hugio.OpenReadSeekCloser
@@ -323,7 +314,7 @@ Loop:
                        currShortcode.pos = it.Pos()
                        currShortcode.length = iter.Current().Pos() - it.Pos()
                        if currShortcode.placeholder == "" {
-                               currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, currShortcode.ordinal)
+                               currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, uint64(currShortcode.ordinal))
                        }
 
                        if currShortcode.name != "" {
@@ -335,7 +326,7 @@ Loop:
                                currShortcode.params = s
                        }
 
-                       currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, ordinal)
+                       currShortcode.placeholder = createShortcodePlaceholder("s", pi.sid, uint64(ordinal))
                        ordinal++
                        s.shortcodes = append(s.shortcodes, currShortcode)
 
@@ -390,10 +381,10 @@ func (c *cachedContent) mustSource() []byte {
 }
 
 func (pi *contentParseInfo) contentSource(s resource.StaleInfo) ([]byte, error) {
-       key := pi.sourceKey
+       key := pi.sid
        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()
                if err != nil {
                        return nil, err
@@ -457,13 +448,13 @@ type contentPlainPlainWords struct {
 }
 
 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) {
        cp := c.pco
        ctx = tpl.Context.DependencyScope.Set(ctx, pageDependencyScopeGlobal)
-       key := c.pi.sourceKey + "/" + c.keyScope(ctx)
+       key := c.keyScope(ctx)
 
        versionv := c.version(cp)
 
@@ -616,7 +607,7 @@ var setGetContentCallbackInContext = contexthelpers.NewContextDispatcher[func(*p
 
 func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfContents, error) {
        cp := c.pco
-       key := c.pi.sourceKey + "/" + c.keyScope(ctx)
+       key := c.keyScope(ctx)
        versionv := c.version(cp)
 
        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) {
        cp := c.pco
-       key := c.pi.sourceKey + "/" + c.keyScope(ctx)
+       key := c.keyScope(ctx)
 
        versionv := c.version(cp)
 
index ec6e25cd675c54de4f8b20472287a87c7ac3649d..78edd8135a4025f0521866c30332df258606182c 100644 (file)
@@ -192,8 +192,8 @@ func (scp *ShortcodeWithPage) Unwrapv() any {
 // Note - this value must not contain any markup syntax
 const shortcodePlaceholderPrefix = "HAHAHUGOSHORTCODE"
 
-func createShortcodePlaceholder(sid string, id uint64, ordinal int) string {
-       return shortcodePlaceholderPrefix + strconv.FormatUint(id, 10) + sid + strconv.Itoa(ordinal) + "HBHB"
+func createShortcodePlaceholder(sid string, id, ordinal uint64) string {
+       return shortcodePlaceholderPrefix + strconv.FormatUint(id, 10) + sid + strconv.FormatUint(ordinal, 10) + "HBHB"
 }
 
 type shortcode struct {
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
new file mode 100644 (file)
index 0000000..dceb93b
--- /dev/null
@@ -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))
+       }
+}
index ce0331200ed648e1aa44aac16cbe8477d6e40514..edcf2548a93081050080694a3ad2ab2547fe2fb2 100644 (file)
@@ -451,7 +451,7 @@ func newHugoSites(
                        page.Pages](d.MemCache, "/pags/all",
                        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](),
                currentSite:             first[0],
                skipRebuildForFilenames: make(map[string]bool),
index 45015afdd8ad1fb4ac7a7083c9f8c19d588c559a..3f5bd5905e0bd19b0fe2c32495a497fc73e9ab78 100644 (file)
@@ -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() {
                        _ = 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) {
                for b.Loop() {