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は使わなくてもいいかもしれない.
初心者なので,これじゃ正しく動かなかったり,他にもっとこんな方法があるよとか,ベンチマークの方法がおかしいとかあったら,指摘してください.泣いて喜びます.