拙作 gorak/errs パッケージの性能評価をしてもらった

no extension

こんなマイナーなパッケージの性能評価をしていただいてありがとうございます 🙇 いや,マジで。

pkg/errors は昔から人気の高いエラーハンドリング・パッケージで,私も随分お世話になった。 このパッケージの更新が止まって read-only になったのに伴い代替となるパッケージが望まれていたのは知っている。 で,登場したのが cockroachdb/errors パッケージなわけだ。

cockroachdb/errors パッケージは,おそらく CockroachDB などのデータベース操作に向いたエラーハンドリング・パッケージと思われ, pkg/errors との互換性を維持したまま PII (Personally Identifiable Information) のマスキングもできる優れものである。 pkg/errors からの乗り換えを考えるなら cockroachdb/errors パッケージはアリな選択だと思うし個人的にもお勧めである。

一方で拙作の goark/errs はもう少し違うところを目指していて

  • 任意の error インスタンスをラッピングすることに主眼を置く
    • 任意の error インスタンスを原因エラーとして埋め込み可能
  • 任意のコンテキスト情報を埋め込み可能
    • 既定でエラーが発生した関数名をコンテキスト情報として保持する
  • 構造化されたエラー情報を JSON 形式で出力可能
    • MarshalJSON() メソッド完備
    • 書式 %+v を使って JSON 形式で出力
    • 任意の error インスタンスで(Unwrap メソッドの挙動に従い)可能な限り構造を辿って出力

といった機能を有している。 もちろんこれは pkg/errors パッケージに対するささやかな不満から来ている。

私は「スタック情報は9割以上がノイズ」「藁束の中から金の針を探すようなもの」という危険思想の持ち主なので,どういう形であれスタック情報を丸ごとどーんと出力することはしないようにしている。 他人様が書いた Java コードのデバッグでカジュアルにスタックトレースを吐き出しやがる(しかもそれを見ても結局分からずデバッガを動かす羽目になる)のに辟易してたというのもある。

それならスタック情報はエラーを吐き出した関数名を保持するのみとし,あとはエラーに至る「文脈(context)」をできる限りかき集めてエラー・インスタンスに突っ込むという戦略のほうが幾らかマシだろう,と考えたのだ。 どうしても関数呼び出しの構造が欲しければ,エラーを検出した時点で都度ラッピングしていけばいいという考え方である。

ところで最初に挙げた記事では

出力の処理は json.Marshalfmt.Sprintf を使っています。 json.Marshal を高速化するために goccy/go-json に変えるのもありかもしれません。

と評価をいただいていて, JSON の marshalling については(一瞬心が揺らいだが)性能がよくてもサードパーティのパッケージには依存したくないというのがあるので,パスさせていただくが, fmt.Sprintf に関しては正直に言って実装をサボっているだけなので,少し改善してみることにした。

まずは miyataka/benchmark_pkg_errors_alternatives を拝借して改めてベンチマークをとってみる。 他のパッケージと比べても仕方がないので goark/errs を使った結果のみ示すと

benchmark ns/op B/op allocs/op
BenchmarkErrors/goark/errs-stack-10-12 2746 648 7
BenchmarkErrors/goark/errs-stack-100-12 3278 648 7
BenchmarkErrors/goark/errs-stack-1000-12 6810 648 7
BenchmarkStackFormatting/goark/errs-%s-stack-10-12 167.3 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-10-12 185.0 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-10-12 8680 1401 33
BenchmarkStackFormatting/goark/errs-%s-stack-30-12 174.8 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-30-12 180.4 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-30-12 8826 1401 33
BenchmarkStackFormatting/goark/errs-%s-stack-60-12 170.0 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-60-12 160.5 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-60-12 8636 1401 33

うっ,アロケート回数が33回とか orz

凹みつつも JSON データ生成部分でなるべく fmt.Sprintf を使わないようにした v1.2.3 をリリースした。

これを使って同じ条件でベンチマークをとってみたのだが

benchmark ns/op B/op allocs/op
BenchmarkErrors/goark/errs-stack-10-12 2850 648 7
BenchmarkErrors/goark/errs-stack-100-12 3344 648 7
BenchmarkErrors/goark/errs-stack-1000-12 6365 648 7
BenchmarkStackFormatting/goark/errs-%s-stack-10-12 167.7 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-10-12 164.6 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-10-12 7098 1385 31
BenchmarkStackFormatting/goark/errs-%s-stack-30-12 171.8 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-30-12 171.5 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-30-12 6974 1385 31
BenchmarkStackFormatting/goark/errs-%s-stack-60-12 173.9 8 1
BenchmarkStackFormatting/goark/errs-%v-stack-60-12 164.8 8 1
BenchmarkStackFormatting/goark/errs-%+v-stack-60-12 7097 1385 31

ちょっとしか変わらん orz やっぱ json.Marshal を使ってるのがあかんのか? そもそも改善になってない?

…というわけで諦めました。 こんなのでよろしければ使ってやってください。

そうそう errors.Join 互換の関数ってあったほうがいいのかなぁ。 それをするにはマルチエラー用の型を作らないといけないのだが… これはちょっと考えてみてもいいかも。

ブックマーク

参考図書

photo
プログラミング言語Go
アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)
丸善出版 2016-06-20 (Release 2021-07-13)
Kindle版
B099928SJD (ASIN)
評価     

Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想はこちら

reviewed by Spiegel on 2021-05-22 (powered by PA-APIv5)

photo
初めてのGo言語 ―他言語プログラマーのためのイディオマティックGo実践ガイド
Jon Bodner (著), 武舎 広幸 (翻訳)
オライリージャパン 2022-09-26
単行本(ソフトカバー)
4814400047 (ASIN), 9784814400041 (EAN), 4814400047 (ISBN)
評価     

2021年に出た “Learning Go” の邦訳版。私は版元で PDF 版を購入。 Go 特有の語法(idiom)を切り口として Go の機能やパッケージを解説している。 Go 1.19 対応。

reviewed by Spiegel on 2022-10-11 (powered by PA-APIv5)

photo
デベロッパーゴースーパーゴラン Tシャツ
Geek Go Super Golang Tees
ウェア&シューズ
B09C2XBC2F (ASIN)
評価     

ついカッとなってポチった。反省はしない

reviewed by Spiegel on 2022-04-10 (powered by PA-APIv5)