😁

[Go] Go公式にコミットしてみた

2024/12/22に公開

発端

先日Go本体のbytesパッケージのコードを見ていたら、こんな感じのコードがありました。

go/src/bytes/buffer.go
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:])

これはbyte.BufferのStringメソッドのコードです。この"return string(b.buf[b.off:])"
の部分を変えられるのではないかと思い、
何ならGo公式にもコミットしてみようと思いました。

こんな風に変更

func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return unsafe.String(unsafe.SliceData(b.buf[b.off:]), len(b.buf[b.off:]))
}

unsafeパッケージを使ってます。
以前のと比較しベンチマークを取ったところこんな感じになりました。

o test -bench=. -benchmem
goos: windows
goarch: amd64
pkg: sand
cpu: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
BenchmarkBytesBufferStringOriginal-8       25308            103032 ns/op          484949 B/op          1 allocs/op
BenchmarkBytesBufferStringImproved-8    18792693                74.42 ns/op          114 B/op          0 allocs/op
PASS
ok      sand    5.053s

BenchmarkBytesBufferStringOriginalが以前のコードで
BenchmarkBytesBufferStringImprovedが私が変更したコードです。
103032 ns/op と 74.42 ns/op 約1400倍速くなった??

これをGo公式に出してみましょう。

実際にやる

やり方はGoのContribution Guideに詳細なやり方が載っていますが本記事の詳細から外れるので紹介はしません。
https://go.dev/doc/contribute

いろいろ設定したのちGerritという自分の変更したGo公式のコードを評価して
実際にマージするか検討してもらうサイトがあるのでそこに変更したコードをこのように提出してみました。

bytes: improved bytes.Buffer method String

The String method of buffer.go in the bytes package has been improved, making it about 1400 times faster than before.
The following code is an actual benchmark I used.

The buf package in the benchmark is the directory containing a copy of the official Go bytes/buffer.go code.

package main

import (
	"bytes"
	"testing"
	"sand/buf"
)

var ss string

func BenchmarkBytesBufferStringOriginal(b *testing.B) {
	var originalOut bytes.Buffer
	var str string = "asdfghjklqwertyuiopzxcvbnm,.1234567890"
	var bs []byte = []byte(str)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		originalOut.Write(bs)

		ss = originalOut.String()

	}

}

func BenchmarkBytesBufferStringImproved(b *testing.B) {
	var usingUnsafeOut buf.Buffer
	var str string = "asdfghjklqwertyuiopzxcvbnm,.1234567890"
	var bs []byte = []byte(str)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		usingUnsafeOut.Write(bs)

		ss = usingUnsafeOut.String()

	}
}

Below are the benchmark results

go test -bench=. -benchmem
goos: windows
goarch: amd64
pkg: sand
cpu: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz
BenchmarkBytesBufferStringOriginal-8       25308            103032 ns/op          484949 B/op          1 allocs/op
BenchmarkBytesBufferStringImproved-8    18792693                74.42 ns/op          114 B/op          0 allocs/op
PASS
ok      sand    5.053s

具体的には、

  • どんな変更をしたか
  • どんなベンチマークを取ったか
  • ベンチマークの結果
    といった感じで出しました。

これで変更点に問題がなかったら晴れてGoの公式のコードに私の変更がマージされるらしいのですがどうなるのでしょう...

と思ったらレビュアーの方からこんな返事が来ました。

うーん。どうやらそもそもbytes.Bufferは非推奨で同じような操作をしたいならstrings.Builderを使うべきだと、
そんな感じのことが書いてありますね...つまりは私の変更は棄却されました、ざんねん。

さいごに

結果は失敗でしたね、コミッターになれるかもと思ったんだけどなあ、
しかし変更はどうやら標準パッケージだけでなく
ドキュメントやらコメントの変更だけでもいいらしいのでGoの公式で不満点や違和感があり且つそれを直せるのならどんどん出してみるのもいいのではないかと思いました。
ぜひともやってみてください。

Discussion