I’ve been working on saving types such as int/float etc. in dgraph as binary instead of the string representation that we now have. I was investigating the various ways of converting these types to the corresponding byte arrays in Go. Go offers many solutions for this. There is encoding/binary
, encoding/gob
, saving as the string representation, or writing bytes directly.
I decided to run benchmarks on the different methods to see which works well. The results were pretty surprising to me, which is why I’m sharing them.
I compared 4 ways of encoding/decoding int
s:
Using encoding/binary
func binaryWriteInt(value int) ([]byte, error) {
bs := make([]byte, 0, 8)
buf := bytes.NewBuffer(bs)
err := binary.Write(buf, binary.LittleEndian, int64(value))
return buf.Bytes(), err
}
Using ByteOrder in encoding/binary
func encodingWriteInt(value int) []byte {
bs := make([]byte, 8)
binary.LittleEndian.PutUint64(bs, uint64(value))
return bs
}
Using strings
func strWriteInt(value int) []byte {
s := strconv.Itoa(value)
return []byte(s)
}
Using encoding/gob
func gobWriteInt(value int) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(value)
return buf.Bytes(), err
}
The read functions are similar. The benchmarks on these functions (including both encoding & decoding) show:
BenchmarkEncodeIntBinary-4 5000000 391 ns/op
BenchmarkEncodeIntEncoding 500000000 3.05 ns/op
BenchmarkEncodeIntGob-4 300000 5235 ns/op
BenchmarkEncodeIntStr-4 10000000 191 ns/op
It turns out encoding/binary
is really slow, even slower than encoding/decoding it as a string. Directly writing the bytes is 100 times faster. Both encoding/gob
and encoding/binary
use reflection, which makes it much slower.
Using this information, I updated the library that parses WKB (it was using encoding/binary
). The old benchmark was:
BenchmarkParseWKBPayne-4 100 11519626 ns/op
after my changes, the benchmark is:
BenchmarkParseWKBPayne-4 200 6085073 ns/op
which is a 45% improvement