課題: 任意の文字列が与えられたとき、ヌル文字まで取得したい
え、普通にSplit使えばいいじゃんて?それに気づいたのは遥か先だった。。
- 方法1: 直接ゴリゴリ
- 方法2: バッファに入れてNextで読み出し
- 方法2: バッファに入れてReadで読み出し
- 方法3: バッファに入れてencoding/binaryのReadで1バイトずつ読み出し
- 方法4: splitsで分けちゃえ
試したもの
package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"io"
"testing"
)
func generateRandomBytes() []byte {
token := make([]byte, 1000000)
rand.Read(token)
return token
}
func BenchmarkSplitByNullByte_Direct(b *testing.B) {
token := generateRandomBytes()
b.ResetTimer()
var bytes [][]byte
bytes = append(bytes, []byte{})
for _, v := range token{
bytes[len(bytes)-1] = append(bytes[len(bytes)-1], v)
if v == 0x00 {
bytes = append(bytes, []byte{})
}
}
}
func BenchmarkSplitByNullByte_BytesNext(b *testing.B) {
token := generateRandomBytes()
b.ResetTimer()
buf := bytes.NewBuffer(token)
var bytes [][]byte
bytes = append(bytes, []byte{})
v := buf.Next(1)
for ; ; v = buf.Next(1) {
if len(v) == 0 {
break
}
bytes[len(bytes)-1] = append(bytes[len(bytes)-1], v[0])
if v[0] == 0x00 {
bytes = append(bytes, []byte{})
}
}
}
func BenchmarkSplitByNullByte_BytesRead(b *testing.B) {
token := generateRandomBytes()
b.ResetTimer()
buf := bytes.NewBuffer(token)
var bytes [][]byte
bytes = append(bytes, []byte{})
var n int
v := make([]byte, 1)
n, _ = buf.Read(v)
for ; ; n, _ = buf.Read(v) {
if n == 0 {
break
}
bytes[len(bytes)-1] = append(bytes[len(bytes)-1], v[0])
if v[0] == 0x00 {
bytes = append(bytes, []byte{})
}
}
}
func BenchmarkSplitByNullByte_EncodingBinaryRead(b *testing.B) {
token := generateRandomBytes()
b.ResetTimer()
buf := bytes.NewBuffer(token)
var bytes [][]byte
bytes = append(bytes, []byte{})
var err error
v := make([]byte, 1)
binary.Read(buf, binary.BigEndian, v)
for ; ; err = binary.Read(buf, binary.BigEndian, v) {
if err == io.EOF {
break
}
bytes[len(bytes)-1] = append(bytes[len(bytes)-1], v[0])
if v[0] == 0x00 {
bytes = append(bytes, []byte{})
}
}
}
func BenchmarkSplitByNullByte_Split(b *testing.B) {
token := generateRandomBytes()
b.ResetTimer()
bytes.Split(token, []byte("\x00"))
}
結果
goos: linux
goarch: amd64
pkg: github.com/gpioblink/sandbox
BenchmarkSplitByNullByte_Direct-8 1000000000 0.00876 ns/op
BenchmarkSplitByNullByte_BytesNext-8 1000000000 0.0106 ns/op
BenchmarkSplitByNullByte_BytesRead-8 1000000000 0.0142 ns/op
BenchmarkSplitByNullByte_EncodingBinaryRead-8 1000000000 0.101 ns/op
BenchmarkSplitByNullByte_Split-8 1000000000 0.000196 ns/op
PASS
うん、やっぱり普通にSplit使うのが一番早いよね。。
NextよりReadの方が遅いのはちょっと意外だったかな。コード見ると内部でコピーとかやってるからかな?
ゴリ押しのコードも、頑張れば絶対もっと早く出来ると思います。何か知っていたら教えてください!
結論
普通にSplit使え