Skip to content

Latest commit

 

History

History
95 lines (68 loc) · 2.35 KB

binary_read_write.md

File metadata and controls

95 lines (68 loc) · 2.35 KB

概述

encoding/binary 包用于数字和字节序列之间的简单转换以及 varints 的编码和解码。

varints 是一种使用可变字节表示整数的方法,其中数值本身越小,其所占用的字节数越少。

标准库中的 binary.Read 方法和 binary.Write 方法内部使用 反射 实现,会对性能有一定影响。如果相关代码在 hot path 上面, 那么应该考虑是否可以手动实现相关功能,避免直接使用这两个函数。

直接使用 binary.read

测试代码

package performance

import (
	"bytes"
	"encoding/binary"
	"testing"
)

// 将网络字节序解析到 uint32
func convert(bys []byte) uint32 {
	var num uint32
	buf := bytes.NewReader(bys)
	_ = binary.Read(buf, binary.BigEndian, &num)
	return num
}

func Benchmark_Convert(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = convert([]byte{0x7f, 0, 0, 0x1})
	}
}

运行测试,并将基准测试结果写入文件:

# 运行 100000 次, 统计内存分配
$ go test -run='^$' -bench=. -count=1 -benchtime=100000x -benchmem > slow.txt

手动实现函数

测试代码

package performance

import (
	"testing"
)

// 将网络字节序解析到 uint32
func convert(bys []byte) uint32 {
	return uint32(bys[3]) | uint32(bys[2])<<8 | uint32(bys[1])<<16 | uint32(bys[0])<<24
}

func Benchmark_Convert(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = convert([]byte{0x7f, 0, 0, 0x1})
	}
}

运行测试,并将基准测试结果写入文件:

# 运行 100000 次, 统计内存分配
$ go test -run='^$' -bench=. -count=1 -benchtime=100000x -benchmem > fast.txt

使用 benchstat 比较差异

$ benchstat -alpha=100 fast.txt slow.txt

# 输出如下
name          old time/op    new time/op    delta
_Convert-8    0.26ns ± 0%   83.85ns ± 0%  +31782.13%  (p=1.000 n=1+1)

name          old alloc/op   new alloc/op   delta
_Convert-8     0.00B         60.00B ± 0%       +Inf%  (p=1.000 n=1+1)

name          old allocs/op  new allocs/op  delta
_Convert-8      0.00           4.00 ± 0%       +Inf%  (p=1.000 n=1+1)

从输出的结果中可以看到,通过手动实现函数功能的方式,避免了 反射 的开销,运行时间提升了 300 多倍, 内存分配量和内存分配次数降为 0

小结

任何出现在 hot path 上面的反射代码都应该尽可能地优化。