1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

golangでバイナリ操作 - ヌル文字が出てくるまで取得したい

Posted at

課題: 任意の文字列が与えられたとき、ヌル文字まで取得したい

え、普通に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使え

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?