1
0
mirror of https://github.com/golang/go synced 2024-09-30 00:14:36 -06:00

errors: optimize Is and As by reusing reflection of target

goos: darwin
goarch: amd64
pkg: errors
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
        │     old     │                 new                 │
        │   sec/op    │   sec/op     vs base                │
Is-12     133.4n ± 0%   126.8n ± 3%   -4.91% (p=0.001 n=10)
As-12     464.1n ± 1%   307.2n ± 0%  -33.80% (p=0.000 n=10)
geomean   248.8n        197.4n       -20.66%

        │    old     │                 new                 │
        │    B/op    │    B/op     vs base                 │
Is-12     24.00 ± 0%   24.00 ± 0%       ~ (p=1.000 n=10) ¹
As-12     40.00 ± 0%   40.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean   30.98        30.98       +0.00%
¹ all samples are equal

        │    old     │                 new                 │
        │ allocs/op  │ allocs/op   vs base                 │
Is-12     1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=10) ¹
As-12     2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean   1.414        1.414       +0.00%
¹ all samples are equal

Change-Id: I0844f3ab77e63b5f773594157dcffaffffd5e70d
Reviewed-on: https://go-review.googlesource.com/c/go/+/520756
Run-TryBot: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
j178 2023-08-18 12:04:35 +08:00 committed by Gopher Robot
parent 8789b5d72f
commit ba7ba9bcc0
2 changed files with 36 additions and 4 deletions

View File

@ -47,8 +47,12 @@ func Is(err, target error) bool {
}
isComparable := reflectlite.TypeOf(target).Comparable()
return is(err, target, isComparable)
}
func is(err, target error, targetComparable bool) bool {
for {
if isComparable && err == target {
if targetComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
@ -62,7 +66,7 @@ func Is(err, target error) bool {
}
case interface{ Unwrap() []error }:
for _, err := range x.Unwrap() {
if Is(err, target) {
if is(err, target, targetComparable) {
return true
}
}
@ -106,9 +110,13 @@ func As(err error, target any) bool {
if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
panic("errors: *target must be interface or implement error")
}
return as(err, target, val, targetType)
}
func as(err error, target any, targetVal reflectlite.Value, targetType reflectlite.Type) bool {
for {
if reflectlite.TypeOf(err).AssignableTo(targetType) {
val.Elem().Set(reflectlite.ValueOf(err))
targetVal.Elem().Set(reflectlite.ValueOf(err))
return true
}
if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
@ -122,7 +130,10 @@ func As(err error, target any) bool {
}
case interface{ Unwrap() []error }:
for _, err := range x.Unwrap() {
if As(err, target) {
if err == nil {
continue
}
if as(err, target, targetVal, targetType) {
return true
}
}

View File

@ -238,6 +238,27 @@ func TestAsValidation(t *testing.T) {
}
}
func BenchmarkIs(b *testing.B) {
err1 := errors.New("1")
err2 := multiErr{multiErr{multiErr{err1, errorT{"a"}}, errorT{"b"}}}
for i := 0; i < b.N; i++ {
if !errors.Is(err2, err1) {
b.Fatal("Is failed")
}
}
}
func BenchmarkAs(b *testing.B) {
err := multiErr{multiErr{multiErr{errors.New("a"), errorT{"a"}}, errorT{"b"}}}
for i := 0; i < b.N; i++ {
var target errorT
if !errors.As(err, &target) {
b.Fatal("As failed")
}
}
}
func TestUnwrap(t *testing.T) {
err1 := errors.New("1")
erra := wrapped{"wrap 2", err1}