Last active
June 4, 2024 06:30
-
-
Save zachmu/00d0410431f9d8d314155361836eecdd to your computer and use it in GitHub Desktop.
Go error handling benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2024 Dolthub, Inc. | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
package main | |
import ( | |
"errors" | |
"fmt" | |
"testing" | |
) | |
var notFoundErr = errors.New("not found") | |
type resultType struct {} | |
type boolStore struct {} | |
//go:noinline | |
func (b *boolStore) GetValue(found bool) (*resultType, bool, error) { | |
if found { | |
return &resultType{}, true, nil | |
} else { | |
return nil, false, nil | |
} | |
} | |
type errStore struct {} | |
//go:noinline | |
func (b *errStore) GetValue(found bool) (*resultType, error) { | |
if found { | |
return &resultType{}, nil | |
} else { | |
return nil, notFoundErr | |
} | |
} | |
type panicStore struct{} | |
//go:noinline | |
func (b *panicStore) GetValue(found bool) *resultType { | |
if found { | |
return &resultType{} | |
} else { | |
panic(notFoundErr) | |
} | |
} | |
type wrappedErrStore struct{} | |
//go:noinline | |
func (b *wrappedErrStore) GetValue(found bool) (*resultType, error) { | |
result, err := b.queryValueStore(found) | |
if err != nil { | |
return nil, fmt.Errorf("GetValue couldn't get a value: %w", err) | |
} | |
return result, nil | |
} | |
//go:noinline | |
func (b *wrappedErrStore) queryValueStore(found bool) (*resultType, error) { | |
result, err := b.queryDisk(found) | |
if err != nil { | |
return nil, fmt.Errorf("queryValueStore couldn't get a value: %w", err) | |
} | |
return result, nil | |
} | |
//go:noinline | |
func (b *wrappedErrStore) queryDisk(found bool) (*resultType, error) { | |
result, err := b.readValueFromDiskFake(found) | |
if err != nil { | |
return nil, fmt.Errorf("queryDisk couldn't get a value: %w", err) | |
} | |
return result, nil | |
} | |
//go:noinline | |
func (b *wrappedErrStore) readValueFromDiskFake(found bool) (*resultType, error) { | |
if found { | |
return &resultType{}, nil | |
} else { | |
return nil, notFoundErr | |
} | |
} | |
type wrappedBoolStore struct {} | |
//go:noinline | |
func (b *wrappedBoolStore) GetValue(found bool) (*resultType, bool, error) { | |
result, found, err := b.queryValueStore(found) | |
if err != nil { | |
return nil, false, fmt.Errorf("GetValue couldn't get a value: %w", err) | |
} | |
return result, found, nil | |
} | |
//go:noinline | |
func (b *wrappedBoolStore) queryValueStore(found bool) (*resultType, bool, error) { | |
result, found, err := b.queryDisk(found) | |
if err != nil { | |
return nil, false, fmt.Errorf("queryValueStore couldn't get a value: %w", err) | |
} | |
return result, found, nil | |
} | |
//go:noinline | |
func (b *wrappedBoolStore) queryDisk(found bool) (*resultType, bool, error) { | |
result, found, err := b.readValueFromDiskFake(found) | |
if err != nil { | |
return nil, false, fmt.Errorf("queryDisk couldn't get a value: %w", err) | |
} | |
return result, found, nil | |
} | |
//go:noinline | |
func (b *wrappedBoolStore) readValueFromDiskFake(found bool) (*resultType, bool, error) { | |
if found { | |
return &resultType{}, true, nil | |
} else { | |
return nil, false, nil | |
} | |
} | |
func BenchmarkNotFoundBool(b *testing.B) { | |
var bs boolStore | |
for i := 0; i < b.N; i++ { | |
val, found, err := bs.GetValue(i < 0) | |
if err != nil { | |
b.Fatal(err) | |
} else if found { | |
b.Fatal("expected not found") | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundErrorsIs(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if errors.Is(err, notFoundErr) { | |
// nothing to do | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundErrorsIsNilCheck(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if err != nil { | |
if errors.Is(err, notFoundErr) { | |
// nothing to do | |
} else { | |
b.Fatal(err) | |
} | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundErrEqual(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if err == notFoundErr { | |
// nothing to do | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundErrEqualNilCheck(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if err != nil { | |
if err == notFoundErr { | |
// nothing to do | |
} else { | |
b.Fatal(err) | |
} | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundWrappedErr(b *testing.B) { | |
var es wrappedErrStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if errors.Is(err, notFoundErr) { | |
// nothing to do | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundWrappedErrNilCheck(b *testing.B) { | |
var es wrappedErrStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i < 0) | |
if err != nil { | |
if errors.Is(err, notFoundErr) { | |
// nothing to do | |
} else { | |
b.Fatal(err) | |
} | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundWrappedBool(b *testing.B) { | |
var bs wrappedBoolStore | |
for i := 0; i < b.N; i++ { | |
val, found, err := bs.GetValue(i < 0) | |
if err != nil { | |
b.Fatal(err) | |
} else if found { | |
b.Fatal("expected not found") | |
} | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkNotFoundPanic(b *testing.B) { | |
var es panicStore | |
for i := 0; i < b.N; i++ { | |
var val *resultType | |
func() { | |
defer func() { | |
// recover panic | |
err := recover() | |
if err == nil { | |
b.Fatal("expected panic") | |
} | |
}() | |
val = es.GetValue(i < 0) | |
}() | |
if val != nil { | |
b.Fatal("expected nil") | |
} | |
} | |
} | |
func BenchmarkFoundBool(b *testing.B) { | |
var bs boolStore | |
for i := 0; i < b.N; i++ { | |
val, found, err := bs.GetValue(i >= 0) | |
if err != nil { | |
b.Fatal(err) | |
} else if !found { | |
b.Fatal("expected found") | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundErrorsIs(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if errors.Is(err, notFoundErr) { | |
b.Fatal("expected found") | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundErrorsIsNilCheck(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if err != nil { | |
if errors.Is(err, notFoundErr) { | |
b.Fatal("expected found") | |
} else { | |
b.Fatal(err) | |
} | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundErrEqual(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if err == notFoundErr { | |
b.Fatal("expected found") | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundErrEqualNilCheck(b *testing.B) { | |
var es errStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if err != nil { | |
if err == notFoundErr { | |
b.Fatal("expected found") | |
} else { | |
b.Fatal(err) | |
} | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundWrappedErr(b *testing.B) { | |
var es wrappedErrStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if errors.Is(err, notFoundErr) { | |
b.Fatal(err) | |
} else if err != nil { | |
b.Fatal(err) | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundWrappedErrNilCheck(b *testing.B) { | |
var es wrappedErrStore | |
for i := 0; i < b.N; i++ { | |
val, err := es.GetValue(i >= 0) | |
if err != nil { | |
if errors.Is(err, notFoundErr) { | |
b.Fatal(err) | |
} else { | |
// nothing to do | |
} | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundWrappedBool(b *testing.B) { | |
var bs wrappedBoolStore | |
for i := 0; i < b.N; i++ { | |
val, found, err := bs.GetValue(i >= 0) | |
if err != nil { | |
b.Fatal(err) | |
} else if !found { | |
b.Fatal("expected found") | |
} | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} | |
func BenchmarkFoundPanic(b *testing.B) { | |
var es panicStore | |
for i := 0; i < b.N; i++ { | |
var val *resultType | |
func() { | |
defer func() { | |
// recover panic | |
err := recover() | |
if err != nil { | |
b.Fatal("unexpected panic") | |
} | |
}() | |
val = es.GetValue(i >= 0) | |
}() | |
if val == nil { | |
b.Fatal("expected not nil") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment