From b66850cc8c584cc00bcd796eb3aa238bcf87394a Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 17 Sep 2026 14:17:54 +0200 Subject: a file's name is folded once, not once per name test Folding is the expensive half of a name test on a name with diacritics, and every name test of every rule folded the same name again: on Polish names it was most of the matching work. The per-file facts memoise it, which is where one file's work belongs - the object is per file and per goroutine, so no lock. 4000 Polish names, twelve rules with name tests: 0.33s -> 0.13s --- internal/cond/eval.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'internal/cond/eval.go') diff --git a/internal/cond/eval.go b/internal/cond/eval.go index 2ae31fb..7d8e00d 100644 --- a/internal/cond/eval.go +++ b/internal/cond/eval.go @@ -28,6 +28,12 @@ type Facts interface { // an earlier rule could not be decided (its condition was unknown): with // no match and an undecided rule, (matched) is unknown. Matched() (matched, undecided bool) + // Folded is norm.FoldMapped(subj), memoised per file. Folding is the + // expensive half of a name test on a name with diacritics, and the + // same name is folded again by every name test of every rule; the + // implementation holds one file at a time, so a small memo there + // removes the repetition entirely. + Folded(subj string) norm.Folded } // Result is the outcome of evaluating a Cond against one file's Facts. @@ -243,7 +249,7 @@ func (c *Cond) evalLeaf(n *node, f Facts) (ok bool, reason, warn string, caps [] // back from the original, so {1} keeps the name's diacritics. folded := norm.Folded{Text: subj} if c.opt.Fold { - folded = norm.FoldMapped(subj) + folded = f.Folded(subj) } for _, p := range n.patterns { loc := p.re.FindStringSubmatchIndex(folded.Text) -- cgit v1.3