
ロッキーが階段を駆け上がる
こんにちは、Habr。 おそらく私を覚えているでしょう。私は、BadooのシステムプログラマーであるMarko Kevatsです。 最近、初心者がGo言語ランタイムをどのように変更したかという短い話に出会いました。 この投稿は多分非常に思いがけずハブーブログBadooにあり、多くの人はそれが平凡で素朴な喜びに満ちていると言うかもしれませんが、そのような物語はGoコミュニティーがそのすべてのメンバーに対して友好的で気配りがあることを示していると信じています。 したがって、彼はそれを翻訳しました。
また、投稿には、言語の内部に関連する2つの興味深い事実があります。 素敵な読書を!
私はしばらくの間Goでオープンソースプログラムを書いてきました。 そしてつい最近になって、Goや職場で執筆する機会を得ました。 私は喜んで乗り換え、真のゴーデベロッパーになりました。
言語の開発に貢献したい人のためのマスタークラスを開催した最後のGopherConカンファレンスが開催されるまで、すべては順調でした。 そして今、これらすべての人々がメインリポジトリにコードをコミットする方法を見て、私も何かをしたかったです。 そして数日後、 Francesc CampoyはGoで密輸する方法を詳しく説明した素晴らしいビデオを録画しました。
関わりたいという欲求が私を掴みました。 何ができるかわからなかったが、ソースコードをダウンロードしてコンパイルすることにした。 そして、Goの貢献者の道に沿って私の旅を始めました。
寄稿者の指示を読み、それらを段階的に追った。 CLAへの署名は、命令が少し不器用だったため、すぐにはうまくいきませんでした。 実際、それを指摘して修正することを提案してみませんか? これが私の最初のCLかもしれません! 触発されて、私はチケットを作成しました。 しかし、結局のところ、私は標準的な初心者レーキを踏んだ。 この問題は既にTipで解決されていますが、私も見ようとは思いませんでした。
すべての準備がすでに整っているので、ときどき修正するものを探して標準ライブラリを歩き回りました。 そして、私は何ヶ月もの間Goを仕事のためにプログラミングしていたので、プロファイリング中に常にポップアップするランタイムの部分に出会いました。 そのような部分の1つがfmtパッケージです。 私は彼を注意深く見て、それについて何かできるかどうかを理解することにしました。 約1時間後、私は何か面白いことに出会いました。
fmt/format.go
のfmt_sbx
関数は、次のように開始します。
func (f *fmt) fmt_sbx(s string, b []byte, digits string) { length := len(b) if b == nil {
b
がnil
場合、 len()
が2回呼び出されることは明らかでしたが、多くの場合1つで十分です。 ただし、それをelse
に移動すると、 len()
は常に1回だけ呼び出されます。 他の人の意見を見るために、これについてCLに送ることにしました。
数分後、 イアンの評価は+2になり、その後アヴェリーノ -+1になりました。 信じられませんでした!
しかし、その後、何かがうまくいきませんでした。 デイブは-1をセットし、マーティンはした 彼はコードのバイナリダンプを取り、2つのオプションに違いがないことを確認しました。 コンパイラは、この小さな最適化を行うのに十分なほどスマートでした。 結局、私は赤字でした。 else
の場合、コードの可読性に悪影響を及ぼしますが、パフォーマンスは向上しません。
このCLは放棄されなければなりませんでした...
しかし、私は多くを学びました。 たとえば、 benchcmp
やbenchcmp
などのユーティリティ。 さらに、今ではプロセス全体に精通しており、再試行しても何もかかりませんでした。
すぐに、単純な文字列の連結がfmt.Sprintf()
よりもはるかに速いことをfmt.Sprintf()
。 この知識を持って、私は「犠牲者」を探しに行きましたが、それほど時間はかかりませんでした。 archive/tar
パッケージに決めました。 archive/tar/strconv.go
のformatPAXRecord
関数には、次のコードが含まれています。
size := len(k) + len(v) + padding size += len(strconv.Itoa(size)) record := fmt.Sprintf("%d %s=%s\n", size, k, v)
record := fmt.Sprint(size) + " " + k + "=" + v + "\n"
する最後の行を変更した後record := fmt.Sprint(size) + " " + k + "=" + v + "\n"
、大幅な加速が見られました:
name old time/op new time/op delta FormatPAXRecord 683ns ± 2% 457ns ± 1% -33.05% (p=0.000 n=10+10) name old alloc/op new alloc/op delta FormatPAXRecord 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=10+10) name old allocs/op new allocs/op delta FormatPAXRecord 8.00 ± 0% 6.00 ± 0% -25.00% (p=0.000 n=10+10)
彼らが言うように、残りは歴史です。 今回、 Joeはコードをレビューし、いくつかのマイナーな修正の後、フリーズしました! やった! Goの開発に貢献しました。 私は平凡なオープンソースの貢献者からGoプログラミング言語の貢献者になりました。
これは終わりにはほど遠い。 私は言語の微妙さをよりよく理解し始めており、何かを見つけるたびにCLを作成し続けます。 ゴーチームの紳士向けのお茶10杯は、このような複雑なプロジェクトの開発を非常にエレガントかつたゆまずに管理しているという事実に基づいています。
PSまあ、参照用: