在C/C++里,自己动手实现内存分配器是很常见的事情,写过几年C/C++程序的人可能都做过这样的事情。这其中很重要的一个原因是C/C++不支持垃圾回收。但是既然go语言已经支持垃圾回收,还有必要自己去写一个内存分配器吗?我们做一个简单的测试看看结果怎么样。
测试平台:
OS: ubuntu 12.04 x86_64
CPU: i5 2.27G
MEMORY: 8G
// ben1.go 自己实现内存分配器
package main
type Pool struct {
buf []byte
}
func (p *Pool) alloc(size int) []byte {
if len(p.buf) < size {
l := 1024 * 1024 for l < size {
l += l if l <= 0 {
panic("out of memory")
} }
p.buf = make([]byte, l)
} buff := p.buf[:size] p.buf = p.buf[size:] return buff
}
func main() {
var p Pool for i := 0; i < 10000000; i++ {
_ = p.alloc(100)
}
}
// ben2.go 系统内存分配器
package main
func main() {
for i := 0; i < 10000000; i++ {
_ = make([]byte, 100)
}
}
编译测试:
go build ben1.go
time ./ben1
go build ben2.go
time ./ben2
测试结果:
次数 | ben1(s) | ben2(s) |
---|---|---|
1 | 0.308 | 2.057 |
2 | 0.304 | 2.048 |
3 | 0.308 | 2.093 |
平均 | 0.307 | 2.066 |
结论:
可以看到,自己实现的内存分配器的执行时间大约是系统内存分配器的十分之一,差不多是一个数量级的差距。因此对于一些特定应用场景,比如网络库等,使用自定义内存分配器还是很有必要的。由于go语言提供了垃圾收集功能,所以实现自定义内存分配器相比较在C/C++里简单很多。但是对于自定义内存分配器,还需要注意多goroutine下的同步问题。