Add a cache for lexers.Get

```
name                            old time/op    new time/op    delta
Codeblocks/Default-10              152ms ±11%      12ms ± 1%  -92.44%  (p=0.029 n=4+4)
Codeblocks/Hook_no_higlight-10     142ms ± 0%       7ms ± 0%  -95.36%  (p=0.029 n=4+4)

name                            old alloc/op   new alloc/op   delta
Codeblocks/Default-10             11.9MB ± 0%    11.7MB ± 0%   -1.59%  (p=0.029 n=4+4)
Codeblocks/Hook_no_higlight-10    4.62MB ± 1%    4.43MB ± 0%   -4.08%  (p=0.029 n=4+4)

name                            old allocs/op  new allocs/op  delta
Codeblocks/Default-10               209k ± 0%      209k ± 0%   -0.03%  (p=0.029 n=4+4)
Codeblocks/Hook_no_higlight-10     68.4k ± 0%     68.3k ± 0%   -0.06%  (p=0.029 n=4+4)

```
This commit is contained in:
Bjørn Erik Pedersen 2022-11-24 12:13:19 +01:00
parent 34d1150d92
commit 7855b47f07
5 changed files with 58 additions and 7 deletions

View file

@ -33,10 +33,10 @@ import (
"github.com/spf13/cast"
"github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/highlight/chromalexers"
"github.com/gohugoio/hugo/markup/converter"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/gohugoio/hugo/lazy"
bp "github.com/gohugoio/hugo/bufferpool"
@ -545,7 +545,7 @@ func (p *pageContentOutput) initRenderHooks() error {
layoutDescriptor.Kind = "render-codeblock"
if id != nil {
lang := id.(string)
lexer := lexers.Get(lang)
lexer := chromalexers.Get(lang)
if lexer != nil {
layoutDescriptor.KindVariants = strings.Join(lexer.Config().Aliases, ",")
} else {

View file

@ -19,11 +19,11 @@ import (
"strings"
"sync"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/gohugoio/hugo/common/herrors"
htext "github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
"github.com/gohugoio/hugo/markup/highlight/chromalexers"
"github.com/gohugoio/hugo/markup/internal/attributes"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
@ -94,7 +94,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
}
attrtp := attributes.AttributesOwnerCodeBlockCustom
if isd, ok := renderer.(hooks.IsDefaultCodeBlockRendererProvider); (ok && isd.IsDefaultCodeBlockRenderer()) || lexers.Get(lang) != nil {
if isd, ok := renderer.(hooks.IsDefaultCodeBlockRendererProvider); (ok && isd.IsDefaultCodeBlockRenderer()) || chromalexers.Get(lang) != nil {
// We say that this is a Chroma code block if it's the default code block renderer
// or if the language is supported by Chroma.
attrtp = attributes.AttributesOwnerCodeBlockChroma

View file

@ -0,0 +1,50 @@
// Copyright 2022 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 chromalexers
import (
"sync"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/lexers"
)
type lexersMap struct {
lexers map[string]chroma.Lexer
mu sync.RWMutex
}
var lexerCache = &lexersMap{lexers: make(map[string]chroma.Lexer)}
// Get returns a lexer for the given language name, nil if not found.
// This is just a wrapper around chromalexers.Get that caches the result.
// Reasoning for this is that chromalexers.Get is slow in the case where the lexer is not found,
// which is a common case in Hugo.
func Get(name string) chroma.Lexer {
lexerCache.mu.RLock()
lexer, found := lexerCache.lexers[name]
lexerCache.mu.RUnlock()
if found {
return lexer
}
lexer = lexers.Get(name)
lexerCache.mu.Lock()
lexerCache.lexers[name] = lexer
lexerCache.mu.Unlock()
return lexer
}

View file

@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/highlight/chromalexers"
"github.com/gohugoio/hugo/markup/internal/attributes"
)
@ -167,7 +168,7 @@ func (h HightlightResult) Inner() template.HTML {
func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.Attribute, cfg Config) (int, int, error) {
var lexer chroma.Lexer
if lang != "" {
lexer = lexers.Get(lang)
lexer = chromalexers.Get(lang)
}
if lexer == nil && (cfg.GuessSyntax && !cfg.NoHl) {

View file

@ -18,10 +18,10 @@ import (
"html"
"html/template"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/gohugoio/hugo/cache/namedmemcache"
"github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/highlight"
"github.com/gohugoio/hugo/markup/highlight/chromalexers"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/deps"
@ -93,7 +93,7 @@ func (ns *Namespace) HighlightCodeBlock(ctx hooks.CodeblockContext, opts ...any)
// CanHighlight returns whether the given code language is supported by the Chroma highlighter.
func (ns *Namespace) CanHighlight(language string) bool {
return lexers.Get(language) != nil
return chromalexers.Get(language) != nil
}
// HTMLEscape returns a copy of s with reserved HTML characters escaped.