Sentinel 错误和 errors.Is() 会使你的代码速度降低 500%

这是一篇关于在 Go 语言中处理错误的不同策略的博客文章,主要内容如下:

  • 方法论:作者编写了几个具有相似方法但不同方法签名和表示未找到值的方式的假对象存储,以测试不同的错误处理策略,并设置了基准测试来调用这些函数。
  • 基准测试结果:展示了不同错误处理策略在“未找到值”和“找到值”两种情况下的性能差异,从最快到最慢列出了各种策略,并给出了具体的基准测试输出。
  • 不同错误处理策略

    • Bool:不返回错误:处理“未找到”条件的最快方法,不将其表示为错误,而是返回一个布尔found结果。
    • ErrEqual:带有直接相等检查的哨兵错误:使用经典的 Go 哨兵错误来表示“未找到”条件,通过直接==比较来检测未找到的情况。
    • ErrEqualNilCheck:带有err!= nil检查的哨兵错误:在检查错误是否为非 nil 后,再进行==比较来处理哨兵错误。
    • ErrorsIs:更健壮的哨兵错误检测:使用errors.Is()来正确检测包装的错误,性能较差。
    • ErrorsIsNilCheck:在正常路径上有更好的性能:仅在确定错误为非 nil 后调用errors.Is(),以避免在找到值的情况下出现较大的性能损失。
    • Panic:最慢的错误处理技术:不推荐使用的错误处理技术,性能最差,可能会导致程序停止。
  • 错误包装的影响:在实际应用中,错误通常会在堆栈的多个层之间传递,作者编写了两个模拟错误在堆栈中更深层传递的对象存储实现,发现错误包装会严重降低性能。
  • 总结与讨论

    • errors.Is()==检查哨兵错误都很昂贵,应先检查错误是否为非 nil 以避免性能损失。
    • 错误包装会使使用哨兵错误更加昂贵。
    • 在正常路径上,使用哨兵错误与其他技术一样高效,但在错误路径上则会更昂贵。
    • 除了性能因素外,还有非性能方面的原因可能导致避免使用哨兵错误。
  • 结论:作者所在的团队在构建Dolt数据库时,避免使用哨兵错误,并将所有错误视为不透明的,同时很少包装错误,更喜欢使用堆栈跟踪。如果对 Go 错误处理有疑问或想了解世界上第一个版本控制的 SQL 数据库,可以加入Discord与团队交流。
阅读 13
0 条评论