mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-01 08:20:36 +00:00
perf(gitdiff): optimize inline diff highlighting using cache (#38706)
This pull request optimizes code diff highlighting in the PR Files Changed view, which is a major CPU bottleneck and hot path in production. Previously, Gitea executed the expensive `DiffMatchPatch` algorithm twice for every matching deleted/added line pair (once for the deleted line, and once for the added line). We now run the diff algorithm once and cache the resulting `DiffInline` on the `DiffLine` struct itself, allowing the second line to render via a 0-cost cache lookup. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -10,14 +10,8 @@ type EscapeStatus struct {
|
||||
HasAmbiguous bool
|
||||
}
|
||||
|
||||
// Or combines two EscapeStatus structs into one representing the conjunction of the two
|
||||
func (status *EscapeStatus) Or(other *EscapeStatus) *EscapeStatus {
|
||||
st := status
|
||||
if status == nil {
|
||||
st = &EscapeStatus{}
|
||||
}
|
||||
func (st *EscapeStatus) Combine(other *EscapeStatus) {
|
||||
st.Escaped = st.Escaped || other.Escaped
|
||||
st.HasAmbiguous = st.HasAmbiguous || other.HasAmbiguous
|
||||
st.HasInvisible = st.HasInvisible || other.HasInvisible
|
||||
return st
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package highlight
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
)
|
||||
|
||||
func BenchmarkDetectChromaLexerByFileName(b *testing.B) {
|
||||
for b.Loop() {
|
||||
// BenchmarkDetectChromaLexerByFileName-12 18214717 61.35 ns/op
|
||||
DetectChromaLexerByFileName("a.sql", "")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDetectChromaLexerWithAnalyze(b *testing.B) {
|
||||
code := []byte(strings.Repeat("SELECT * FROM table;\n", 1000))
|
||||
b.ResetTimer()
|
||||
for b.Loop() {
|
||||
// BenchmarkRenderCodeSlowGuess-12 87946 13310 ns/op
|
||||
detectChromaLexerWithAnalyze("a", "", code)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChromaAnalyze(b *testing.B) {
|
||||
code := strings.Repeat("SELECT * FROM table;\n", 1000)
|
||||
b.ResetTimer()
|
||||
for b.Loop() {
|
||||
// comparing to detectChromaLexerWithAnalyze (go-enry), "chroma/lexers.Analyse" is very slow
|
||||
// BenchmarkChromaAnalyze-12 519 2247104 ns/op
|
||||
lexers.Analyse(code)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRenderCodeByLexer(b *testing.B) {
|
||||
code := strings.Repeat("SELECT * FROM table;\n", 1000)
|
||||
lexer := DetectChromaLexerByFileName("a.sql", "")
|
||||
b.ResetTimer()
|
||||
for b.Loop() {
|
||||
// HINT: CODE-HIGHLIGHT-PERFORMANCE: Really slow ....... the regexp2 used by Chroma takes most of the time
|
||||
// BenchmarkRenderCodeByLexer-12 22 47159038 ns/op
|
||||
RenderCodeByLexer(lexer, code)
|
||||
}
|
||||
}
|
||||
@@ -4,53 +4,11 @@
|
||||
package highlight
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func BenchmarkDetectChromaLexerByFileName(b *testing.B) {
|
||||
for b.Loop() {
|
||||
// BenchmarkDetectChromaLexerByFileName-12 18214717 61.35 ns/op
|
||||
DetectChromaLexerByFileName("a.sql", "")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDetectChromaLexerWithAnalyze(b *testing.B) {
|
||||
b.StopTimer()
|
||||
code := []byte(strings.Repeat("SELECT * FROM table;\n", 1000))
|
||||
b.StartTimer()
|
||||
for b.Loop() {
|
||||
// BenchmarkRenderCodeSlowGuess-12 87946 13310 ns/op
|
||||
detectChromaLexerWithAnalyze("a", "", code)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChromaAnalyze(b *testing.B) {
|
||||
b.StopTimer()
|
||||
code := strings.Repeat("SELECT * FROM table;\n", 1000)
|
||||
b.StartTimer()
|
||||
for b.Loop() {
|
||||
// comparing to detectChromaLexerWithAnalyze (go-enry), "chroma/lexers.Analyse" is very slow
|
||||
// BenchmarkChromaAnalyze-12 519 2247104 ns/op
|
||||
lexers.Analyse(code)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRenderCodeByLexer(b *testing.B) {
|
||||
b.StopTimer()
|
||||
code := strings.Repeat("SELECT * FROM table;\n", 1000)
|
||||
lexer := DetectChromaLexerByFileName("a.sql", "")
|
||||
b.StartTimer()
|
||||
for b.Loop() {
|
||||
// Really slow ....... the regexp2 used by Chroma takes most of the time
|
||||
// BenchmarkRenderCodeByLexer-12 22 47159038 ns/op
|
||||
RenderCodeByLexer(lexer, code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectChromaLexer(t *testing.T) {
|
||||
globalVars().highlightMapping[".my-html"] = "HTML"
|
||||
t.Cleanup(func() { delete(globalVars().highlightMapping, ".my-html") })
|
||||
|
||||
Reference in New Issue
Block a user