package annotations import ( "fmt" "strings" "testing" "pvfine/internal/pvf" ) func BenchmarkAnnotateManyRules(b *testing.B) { for _, count := range []int{100, 1000, 10000} { b.Run(fmt.Sprint(count), func(b *testing.B) { rules := make([]Rule, count) for i := range rules { rules[i] = Rule{ ID: fmt.Sprint(i), Match: MatchSpec{Extensions: []string{".equ"}}, Target: TargetSpec{Kind: "token", Section: fmt.Sprintf("section%d", i), Index: intPtr(0)}, Annotation: AnnotationSpec{Title: "说明", Type: "text"}, } } engine, err := Compile(Document{Version: 1, Rules: rules}) if err != nil { b.Fatal(err) } var text strings.Builder for i := 0; i < 100; i++ { fmt.Fprintf(&text, "[section%d]\n1 2 3 4 5\n", i) } view := pvf.ParseScriptView(text.String()) b.ResetTimer() for i := 0; i < b.N; i++ { engine.Annotate("a.equ", view, nil) } }) } } // BenchmarkAnnotatePathManyRules guards the bulk path note lookup: without the // literal glob and "/**" buckets every tree node would scan all rules. func BenchmarkAnnotatePathManyRules(b *testing.B) { const files, directories = 15000, 2000 rules := make([]Rule, 0, files+directories+4) for i := 0; i < files; i++ { rules = append(rules, Rule{ ID: fmt.Sprintf("file.%d", i), Match: MatchSpec{Glob: fmt.Sprintf("dir%02d/sub%02d/file%05d.ani", i%40, i%50, i)}, Target: TargetSpec{Kind: "path"}, Annotation: AnnotationSpec{Title: "文件说明", Type: "text"}, }) } for i := 0; i < directories; i++ { rules = append(rules, Rule{ ID: fmt.Sprintf("dir.%d", i), Match: MatchSpec{Glob: fmt.Sprintf("dir%02d/sub%02d/**", i%40, i%50)}, Target: TargetSpec{Kind: "path"}, Annotation: AnnotationSpec{Title: "目录说明", Type: "text"}, }) } rules = append(rules, Rule{ID: "star", Match: MatchSpec{Glob: "**/*.ani"}, Target: TargetSpec{Kind: "path"}, Annotation: AnnotationSpec{Title: "通配", Type: "text"}}, Rule{ID: "all", Target: TargetSpec{Kind: "path"}, Annotation: AnnotationSpec{Title: "全部", Type: "text"}}, ) engine, err := Compile(Document{Version: 1, Rules: rules}) if err != nil { b.Fatal(err) } paths := []string{ "dir01/sub01/file00001.ani", "dir01/sub01/file00001.ani.bak", "dir39/sub49/deep/deeper/1.ani", "unknown/path.dat", "dir01/sub01", } b.ResetTimer() for i := 0; i < b.N; i++ { engine.AnnotatePath(paths[i%len(paths)], false) } }