Go 的错误处理性能测试(panic 和 recover,直接返回 err 和 errors.As)

panic 和 recover 在 Go 官方的 JSON 包中也有使用。但是我一直这种直接 panic 并 recover 的方式性能如何,所以有了这次测试。

先说结论:

goos: darwin
goarch: arm64
pkg: go-recover-test
cpu: Apple M1
BenchmarkErrorReturn-8                  1000000000               0.3197 ns/op          0 B/op          0 allocs/op
BenchmarkErrorReturnWithDefer-8         576616402                2.110 ns/op           0 B/op          0 allocs/op
BenchmarkPanicRecover-8                 16081614                74.77 ns/op            0 B/op          0 allocs/op
BenchmarkErrorsAs-8                     33246062                37.72 ns/op            0 B/op          0 allocs/op
BenchmarkErrorsAsWrapped-8              23144929                49.71 ns/op            0 B/op          0 allocs/op
BenchmarkTypeAssertion-8                1000000000               0.3166 ns/op          0 B/op          0 allocs/op
BenchmarkReturnBusinessError-8          1000000000               0.4713 ns/op          0 B/op          0 allocs/op
BenchmarkPanicRecoverBusinessError-8    16641386                96.30 ns/op            0 B/op          0 allocs/op
PASS
ok      go-recover-test 8.533s

基础错误处理性能

  1. 错误返回性能:
    • 普通 error 返回:~0.32 纳秒/操作
    • 带 defer 的错误返回:~2.07 纳秒/操作
    • panic/recover:~74.10 纳秒/操作
  2. 错误类型检查性能:
    • 普通类型断言:~0.32 纳秒/操作
    • errors.As(直接错误):~36.32 纳秒/操作
    • errors.As(包装错误):~48.62 纳秒/操作
  3. 自定义错误处理性能:
    • 返回自定义业务错误:~0.48 纳秒/操作
    • panic/recover 自定义业务错误:~73.39 纳秒/操作

主要结论

错误返回方式的性能差异:

  • 直接返回 error 是最快的(亚纳秒级)
  • defer 会带来约 6 倍的性能开销
  • panic/recover 的开销最大,比直接返回慢约 230 倍

错误类型检查的性能差异:

  • 直接类型断言最快(与普通错误返回相当)
  • errors.As 有显著开销(约 36-49 纳秒)
  • 错误包装会进一步增加 errors.As 的开销(约 34% 的额外开销)

自定义错误的影响:

  • 使用自定义错误类型的开销很小(比简单错误多约 0.15 纳秒)
  • 自定义错误的复杂度(字段数量)对性能影响很小
  • panic/recover 的性能开销主要来自机制本身,与错误类型关系不大

实践建议

  1. 日常错误处理:
    • 优先使用返回 error 的方式
    • 不用担心自定义错误类型带来的性能影响
    • 需要时可以放心使用 defer,其开销在大多数场景下可以接受
  2. 错误类型检查:
    • 如果知道具体错误类型,优先使用类型断言
    • 需要处理错误包装链时才使用 errors.As
    • 避免在热点代码路径中过度使用 errors.As
  3. panic/recover 使用:
    • 仅用于真正的异常情况
    • 不适合用作常规错误处理机制
    • 适用于程序初始化或不可恢复的错误场景

性能优化建议

  • 在热点代码路径中避免使用 panic/recover
  • 如果需要频繁检查错误类型,优先使用类型断言
  • 在错误处理的性能要求不高的场景,可以优先考虑代码的清晰度和可维护性

panic 和 recover 不能随便用,但是 errors.As 虽然也有性能开销,但是那一点也无所谓,因为这对于错误的传播非常有帮助。比如我就自定义了个 HTTPError,我可以直接返回这个错误,然后再进行统一的错误处理。比如直接返回一个 HTTPError,在错误处理时,这个错误肯定是可以被用户知道的。但是如果你返回了其他类型的错误,比如数据库错误,那么这个错误肯定是要被遮蔽的,需要通过日志进行输出,而不是告知用户。

测试代码

package main

import (
	"errors"
	"fmt"
	"testing"
)

// 自定义错误类型
type CustomError struct {
	msg string
}

func (e *CustomError) Error() string {
	return e.msg
}

// 另一个自定义错误类型,用于测试性能
type BusinessError struct {
	Code    int
	Message string
}

func (e *BusinessError) Error() string {
	return fmt.Sprintf("error code: %d, message: %s", e.Code, e.Message)
}

var (
	errTest     = errors.New("test error")
	customErr   = &CustomError{msg: "custom error"}
	wrappedErr  = fmt.Errorf("wrapped: %w", customErr)
	businessErr = &BusinessError{Code: 500, Message: "internal server error"}
)

// 使用传统错误返回的函数
func returnError() error {
	return errTest
}

// 返回自定义错误
func returnCustomError() error {
	return customErr
}

// 返回业务错误
func returnBusinessError() error {
	return businessErr
}

// 返回包装的错误
func returnWrappedError() error {
	return wrappedErr
}

// panic 业务错误
func panicBusinessError() {
	panic(businessErr)
}

// 使用 defer 的传统错误返回函数
func returnErrorWithDefer() error {
	var err error
	defer func() {
		// 空的 defer,用于测试 defer 的开销
	}()
	err = errTest
	return err
}

// 不使用 defer 直接调用 recover(错误示范)
func returnErrorWithoutDefer() error {
	// recover 在非 defer 函数中无效,总是返回 nil
	if r := recover(); r != nil {
		return r.(error)
	}
	panic(errTest)
}

// 使用 panic 的函数
func doPanic() {
	panic(errTest)
}

// 包装 panic/recover 的函数
func returnErrorWithRecover() error {
	var err error
	func() {
		defer func() {
			if r := recover(); r != nil {
				err = r.(error)
			}
		}()
		doPanic()
	}()
	return err
}

// 包装 panic/recover 业务错误的函数
func returnBusinessErrorWithRecover() error {
	var err error
	func() {
		defer func() {
			if r := recover(); r != nil {
				err = r.(error)
			}
		}()
		panicBusinessError()
	}()
	return err
}

// 测试传统错误返回的性能
func BenchmarkErrorReturn(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnError()
		if err != nil {
			_ = err
		}
	}
}

// 测试带 defer 的错误返回性能
func BenchmarkErrorReturnWithDefer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnErrorWithDefer()
		if err != nil {
			_ = err
		}
	}
}

// 测试 panic/recover 的性能
func BenchmarkPanicRecover(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnErrorWithRecover()
		if err != nil {
			_ = err
		}
	}
}

// 测试没有 defer 的 recover(会导致 panic)
func BenchmarkRecoverWithoutDefer(b *testing.B) {
	// 这个测试会失败,因为 recover 必须在 defer 中
	b.Skip("这个测试会导致程序崩溃,因为 recover 必须在 defer 中使用")
	for i := 0; i < b.N; i++ {
		err := returnErrorWithoutDefer()
		if err != nil {
			_ = err
		}
	}
}

// 测试简单的 errors.As 性能
func BenchmarkErrorsAs(b *testing.B) {
	var customErr *CustomError
	for i := 0; i < b.N; i++ {
		err := returnCustomError()
		if errors.As(err, &customErr) {
			_ = customErr
		}
	}
}

// 测试带包装的 errors.As 性能
func BenchmarkErrorsAsWrapped(b *testing.B) {
	var customErr *CustomError
	for i := 0; i < b.N; i++ {
		err := returnWrappedError()
		if errors.As(err, &customErr) {
			_ = customErr
		}
	}
}

// 测试普通的类型断言性能(作为对照)
func BenchmarkTypeAssertion(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnCustomError()
		if ce, ok := err.(*CustomError); ok {
			_ = ce
		}
	}
}

// 测试返回自定义业务错误的性能
func BenchmarkReturnBusinessError(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnBusinessError()
		if err != nil {
			if be, ok := err.(*BusinessError); ok {
				_ = be.Code
				_ = be.Message
			}
		}
	}
}

// 测试 panic/recover 自定义业务错误的性能
func BenchmarkPanicRecoverBusinessError(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := returnBusinessErrorWithRecover()
		if err != nil {
			if be, ok := err.(*BusinessError); ok {
				_ = be.Code
				_ = be.Message
			}
		}
	}
}

执行测试:

go test -bench=. -benchmem
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇