返回 DeepSeek-Reasonix
benchmark_test.go
根目录 / internal / extension / benchmark_test.go
1 package extension
2
3 import (
4 "context"
5 "slices"
6 "testing"
7 "time"
8
9 "reasonix/internal/extensioncontract"
10 )
11
12 // BenchmarkExtensionKernelStartup measures the immutable snapshot assembly
13 // portion of startup with no extensions and with a representative 64-entry
14 // interceptor catalog. Process spawn and sidecar handshake latency are
15 // intentionally excluded and should be measured by extension authors.
16 func BenchmarkExtensionKernelStartup(b *testing.B) {
17 b.Run("NoExtensions", func(b *testing.B) {
18 benchmarkBuildLatency(b, NewBuilder().WithSystemPrompt("stable prompt"))
19 })
20
21 contributions := make([]Contribution, 0, 64)
22 for i := range 64 {
23 contributions = append(contributions, Contribution{
24 Kind: KindInterceptor,
25 ID: string(PointToolBefore),
26 Source: ContributionSource{
27 Scope: ScopePlugin,
28 PluginID: "plugin-" + benchmarkIndex(i),
29 },
30 Priority: i%21 - 10,
31 })
32 }
33 builder := NewBuilder().WithSystemPrompt("stable prompt").AddContributor(ContributorFunc{
34 ContributorName: "benchmark",
35 Fn: func(context.Context) ([]Contribution, error) {
36 return contributions, nil
37 },
38 })
39 b.Run("64Interceptors", func(b *testing.B) { benchmarkBuildLatency(b, builder) })
40 }
41
42 func benchmarkIndex(i int) string {
43 const digits = "0123456789abcdef"
44 return string([]byte{digits[(i>>4)&15], digits[i&15]})
45 }
46
47 func benchmarkBuildLatency(b *testing.B, builder *Builder) {
48 b.Helper()
49 b.ReportAllocs()
50 const maxSamples = 100_000
51 samples := make([]int64, 0, maxSamples)
52 for b.Loop() {
53 start := time.Now()
54 _, runtimeSet, err := builder.Build(context.Background())
55 if err != nil {
56 b.Fatal(err)
57 }
58 if err := runtimeSet.Close(); err != nil {
59 b.Fatal(err)
60 }
61 if len(samples) < maxSamples {
62 samples = append(samples, time.Since(start).Nanoseconds())
63 }
64 }
65 b.StopTimer()
66 slices.Sort(samples)
67 if len(samples) == 0 {
68 return
69 }
70 b.ReportMetric(float64(samples[(len(samples)-1)*50/100]), "p50-ns/op")
71 b.ReportMetric(float64(samples[(len(samples)-1)*95/100]), "p95-ns/op")
72 }
73
74 // BenchmarkDependencyGraphAndPlan measures graph resolution and no-op / full
75 // plan diffs as the component count grows (performance baseline for rebuild).
76 func BenchmarkDependencyGraphAndPlan(b *testing.B) {
77 for _, n := range []int{8, 64, 256} {
78 comps := make([]ComponentDescriptor, 0, n)
79 for i := range n {
80 id := ComponentID("plugin/" + benchmarkIndex(i%256) + benchmarkIndex(i/256))
81 comps = append(comps, ComponentDescriptor{
82 ID: id,
83 Provides: []extensioncontract.Capability{{
84 Key: extensioncontract.CapabilityKey{
85 Namespace: string(id), Kind: "interceptors", ID: "default",
86 },
87 Version: "1.0.0",
88 }},
89 })
90 }
91 b.Run("graph/"+itoa(n), func(b *testing.B) {
92 b.ReportAllocs()
93 for b.Loop() {
94 if _, err := BuildDependencyGraph(comps); err != nil {
95 b.Fatal(err)
96 }
97 }
98 })
99 g, err := BuildDependencyGraph(comps)
100 if err != nil {
101 b.Fatal(err)
102 }
103 b.Run("plan-noop/"+itoa(n), func(b *testing.B) {
104 b.ReportAllocs()
105 for b.Loop() {
106 _ = DiffRuntimePlan(g, g, 1, 2)
107 }
108 })
109 // Full reload of every component identity (version bump).
110 reloaded := make([]ComponentDescriptor, len(comps))
111 copy(reloaded, comps)
112 for i := range reloaded {
113 if len(reloaded[i].Provides) > 0 {
114 reloaded[i].Provides[0].Version = "2.0.0"
115 }
116 }
117 g2, err := BuildDependencyGraph(reloaded)
118 if err != nil {
119 b.Fatal(err)
120 }
121 b.Run("plan-full/"+itoa(n), func(b *testing.B) {
122 b.ReportAllocs()
123 for b.Loop() {
124 _ = DiffRuntimePlan(g, g2, 1, 2)
125 }
126 })
127 }
128 }
129
130 func itoa(n int) string {
131 if n == 0 {
132 return "0"
133 }
134 var buf [16]byte
135 i := len(buf)
136 for n > 0 {
137 i--
138 buf[i] = byte('0' + n%10)
139 n /= 10
140 }
141 return string(buf[i:])
142 }
143
143 lines GO