TL;DR
io.Reader
から[]byte
に変換する際はio.Copy
を使おう.
buf := new(bytes.Buffer)
io.Copy(buf, reader)
ret := buf.Bytes()
io.Reader
は値を一度読むと二回目以降はデータが無くなるようで,ベンチマークのコードが正しくなかったので修正しました.
背景
httpの通信結果を取得した際など,res.Body
から値を読み取ると思う.
res.Body
はio.ReadCloser
インターフェースを実装しているので,io.Reader
から何らかの方法で取得する.
その時に幾つか方法がありそうだったので,どれがパフォーマンスが良いのか調べてみた.
方法
調べた範囲で見つけられたのは,ioutil.ReadAll
,bytes.Buffer
のReadFrom
メソッド,io.Copy
メソッドの3つだった.
それぞれのベンチマークを示す.
const N int = 1000
func BenchmarkReadAll(b *testing.B) {
data := make([]byte, N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
reader := bytes.NewBuffer(data)
ioutil.ReadAll(reader)
}
}
func BenchmarkReadFrom(b *testing.B) {
data := make([]byte, N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
reader := bytes.NewBuffer(data)
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
buf.Bytes()
}
}
func BenchmarkReadCopy(b *testing.B) {
data := make([]byte, N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
reader := bytes.NewBuffer(data)
buf := new(bytes.Buffer)
io.Copy(buf, reader)
buf.Bytes()
}
}
ベンチマークの実行コマンドを以下に示す.
go test -benchmem -bench .
結果
ベンチマーク結果を示す.
BenchmarkReadAll-4 2000000 944 ns/op 2160 B/op 3 allocs/op
BenchmarkReadFrom-4 1000000 1049 ns/op 2160 B/op 3 allocs/op
BenchmarkReadCopy-4 2000000 598 ns/op 1248 B/op 3 allocs/op
PASS
ok _/Users/taisuke/manga 5.770s
ReadAll
とReadFrom
がほぼ同じ性能で,io.Copy
が倍以上の性能となっていることがわかる.
まとめ
ReadAll
とReadFrom
がほぼ同じ性能だったので,ソースを見てみると,ReadAll
の内部でReadFrom
が呼ばれていた.
bytes.Buffer
から[]byte
もstring
も取得できるので,ささっと書きたい時以外ioutil.ReadAll
は使わなくてもいいかもしれない.
初心者なので,これじゃ正しく動かなかったり,他にもっとこんな方法があるよとか,ベンチマークの方法がおかしいとかあったら,指摘してください.泣いて喜びます.