Skip to content

Commit 35263d8

Browse files
committed
benchmark: add a benchmark for the errors package
This is a benchmark created by @rafi. It demonstrates `errors.Is` is very inefficient when the reference error does not match the input error.
1 parent 2008f7c commit 35263d8

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

benchmark_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package errors_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"testing"
8+
9+
"github.com/cockroachdb/errors"
10+
)
11+
12+
func BenchmarkErrorsIs(b *testing.B) {
13+
b.Run("NilError", func(b *testing.B) {
14+
var err error
15+
for range b.N {
16+
errors.Is(err, context.Canceled)
17+
}
18+
})
19+
20+
b.Run("SimpleError", func(b *testing.B) {
21+
err := errors.New("test")
22+
for range b.N {
23+
errors.Is(err, context.Canceled)
24+
}
25+
})
26+
27+
b.Run("WrappedError", func(b *testing.B) {
28+
baseErr := errors.New("test")
29+
err := errors.Wrap(baseErr, "wrapped error")
30+
for range b.N {
31+
errors.Is(err, context.Canceled)
32+
}
33+
})
34+
35+
b.Run("WrappedWithStack", func(b *testing.B) {
36+
baseErr := errors.New("test")
37+
err := errors.WithStack(baseErr)
38+
for range b.N {
39+
errors.Is(err, context.Canceled)
40+
}
41+
})
42+
43+
b.Run("NetworkError", func(b *testing.B) {
44+
netErr := &net.OpError{
45+
Op: "dial",
46+
Net: "tcp",
47+
Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 26257},
48+
Err: fmt.Errorf("connection refused"),
49+
}
50+
err := errors.Wrap(netErr, "network connection failed")
51+
for range b.N {
52+
errors.Is(err, context.Canceled)
53+
}
54+
})
55+
56+
b.Run("DeeplyWrappedNetworkError", func(b *testing.B) {
57+
netErr := &net.OpError{
58+
Op: "dial",
59+
Net: "tcp",
60+
Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 26257},
61+
Err: fmt.Errorf("connection refused"),
62+
}
63+
err := errors.WithStack(netErr)
64+
err = errors.Wrap(err, "failed to connect to database")
65+
err = errors.Wrap(err, "unable to establish connection")
66+
err = errors.WithStack(err)
67+
for range b.N {
68+
errors.Is(err, context.Canceled)
69+
}
70+
})
71+
72+
b.Run("MultipleWrappedErrors", func(b *testing.B) {
73+
baseErr := errors.New("internal error")
74+
err := errors.WithStack(baseErr)
75+
err = errors.Wrap(err, "operation failed")
76+
err = errors.WithStack(err)
77+
err = errors.Wrap(err, "transaction failed")
78+
err = errors.WithStack(err)
79+
for range b.N {
80+
errors.Is(err, context.Canceled)
81+
}
82+
})
83+
84+
b.Run("NetworkErrorWithLongAddress", func(b *testing.B) {
85+
netErr := &net.OpError{
86+
Op: "read",
87+
Net: "tcp",
88+
Addr: &net.TCPAddr{
89+
IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
90+
Port: 26257,
91+
},
92+
Err: fmt.Errorf("i/o timeout"),
93+
}
94+
err := errors.WithStack(netErr)
95+
err = errors.Wrap(err, "failed to read from connection")
96+
for range b.N {
97+
errors.Is(err, context.Canceled)
98+
}
99+
})
100+
101+
b.Run("WithMessage", func(b *testing.B) {
102+
baseErr := errors.New("test")
103+
err := errors.WithMessage(baseErr, "additional context")
104+
for range b.N {
105+
errors.Is(err, context.Canceled)
106+
}
107+
})
108+
109+
b.Run("MultipleWithMessage", func(b *testing.B) {
110+
baseErr := errors.New("internal error")
111+
err := errors.WithMessage(baseErr, "first message")
112+
err = errors.WithMessage(err, "second message")
113+
err = errors.WithMessage(err, "third message")
114+
for range b.N {
115+
errors.Is(err, context.Canceled)
116+
}
117+
})
118+
119+
b.Run("WithMessageAndStack", func(b *testing.B) {
120+
baseErr := errors.New("test")
121+
err := errors.WithStack(baseErr)
122+
err = errors.WithMessage(err, "operation context")
123+
err = errors.WithStack(err)
124+
for range b.N {
125+
errors.Is(err, context.Canceled)
126+
}
127+
})
128+
129+
b.Run("NetworkErrorWithMessage", func(b *testing.B) {
130+
netErr := &net.OpError{
131+
Op: "dial",
132+
Net: "tcp",
133+
Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 26257},
134+
Err: fmt.Errorf("connection refused"),
135+
}
136+
err := errors.WithMessage(netErr, "database connection failed")
137+
err = errors.WithMessage(err, "unable to reach server")
138+
for range b.N {
139+
errors.Is(err, context.Canceled)
140+
}
141+
})
142+
143+
b.Run("NetworkErrorWithEverything", func(b *testing.B) {
144+
netErr := &net.OpError{
145+
Op: "dial",
146+
Net: "tcp",
147+
Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 26257},
148+
Err: fmt.Errorf("connection refused"),
149+
}
150+
err := errors.WithStack(netErr)
151+
err = errors.WithMessage(err, "database connection failed")
152+
err = errors.Wrap(err, "failed to establish TCP connection")
153+
err = errors.WithStack(err)
154+
err = errors.WithMessage(err, "unable to reach CockroachDB server")
155+
err = errors.Wrap(err, "connection attempt failed")
156+
for range b.N {
157+
errors.Is(err, context.Canceled)
158+
}
159+
})
160+
161+
b.Run("DeeplyNested100Levels", func(b *testing.B) {
162+
baseErr := errors.New("base error")
163+
err := baseErr
164+
165+
// Create a 100-level deep error chain
166+
for i := 0; i < 100; i++ {
167+
switch i % 3 {
168+
case 0:
169+
err = errors.Wrap(err, fmt.Sprintf("wrap level %d", i))
170+
case 1:
171+
err = errors.WithMessage(err, fmt.Sprintf("message level %d", i))
172+
case 2:
173+
err = errors.WithStack(err)
174+
}
175+
}
176+
177+
for range b.N {
178+
errors.Is(err, context.Canceled)
179+
}
180+
})
181+
}

0 commit comments

Comments
 (0)