func TypeSwitch(v interface{}) {
switch v.(type) {
case InterfaceA:
v.(InterfaceA).AA()
case InterfaceB:
v.(InterfaceB).BB()
}
}
func NormalSwitch(a *A) {
a.AA()
}
func InterfaceSwitch(v interface{}) {
v.(InterfaceA).AA()
} Benchmark_TypeSwitch 50000000 33.0 ns/op
Benchmark_NormalSwitch 2000000000 1.99 ns/op
Benchmark_InterfaceSwitch 100000000 18.4 ns/op
2.指针传参比值传参效率高很多。
指针传参 Benchmark_Invoke1 2000000000 0.52 ns/op
值传参 Benchmark_Invoke2 100000000 12.8 ns/op
3.对象创建的效率。用new一个指针或者直接初始化更快.
func NewStruct1() *BigStruct {
return new(BigStruct)
}
func NewStruct2() *BigStruct {
return &BigStruct{}
}
func NewStruct3() *BigStruct {
var r = new(BigStruct)
*r = BigStruct{}
return r
}
func NewStruct4(r *BigStruct) {
*r = BigStruct{}
}
func NewStruct5(r *BigStruct) {
r.C01 = 0
r.C02 = 0
r.C03 = 0
r.C04 = 0
}
Benchmark_NewStruct1 100000000 13.0 ns/op
Benchmark_NewStruct2 100000000 24.5 ns/op
Benchmark_NewStruct3 50000000 37.8 ns/op
Benchmark_NewStruct4 100000000 25.1 ns/op
Benchmark_NewStruct5 200000000 8.65 ns/op
4.对结构体列表的range循环最消耗性能,因为数据要重复复制,Loop2是因为复制指针比较快。
func Loop1(a []*BigStruct) int {
var n = 0
for i := 0; i < len(a); i++ {
n += a[i].C30
}
return n
}
func Loop2(a []*BigStruct) int {
var n = 0
for _, item := range a {
n += item.C30
}
return n
}
func Loop3(a []BigStruct) int {
var n = 0
for i := 0; i < len(a); i++ {
n += a[i].C30
}
return n
}
func Loop4(a []BigStruct) int {
var n = 0
for _, item := range a {
n += item.C30
}
return n
}
Benchmark_Loop1 2000000 923 ns/op
Benchmark_Loop2 2000000 819 ns/op
Benchmark_Loop3 2000000 825 ns/op
Benchmark_Loop4 100000 26230 ns/op
5.测试中浮点数除法比乘法慢,但是我测试的时候,发现是一样的,可能是新版本的Golang在这方面优化了.
6.硬编码 < 指针slice的range循环 < for循环,但是量级是一样的,看情况用。但是map差了一个量级,小数据量尽量少用map。
7.测试几种链表遍历查找的性能差异.
8.用slice替代链表结构,简单循环slice明显更快,但是加入查询后, slice的优势就可以忽略不计了。但是slice可以优化GC.
9.测试中用的是一个很简单的函数,所以很容易被新版的go做内联优化,禁用优化前后结果差别明显,如果不考虑优化,匿名函数和普通函数调用是一个量级的消耗,差别甚微。但是普通函数比较有可能被优化。在没有优化的情况下,闭包函数消耗又要再高一个量级。
func NormalFunc(i int) int {
return i + 1
}
//普通函数调用
func Benchmark_NormalFuncCall(b *testing.B) {
for i := 0; i < b.N; i++ {
NormalFunc(i)
}
}
//匿名函数
func Benchmark_VarFuncCall1(b *testing.B) {
for i := 0; i < b.N; i++ {
VarFunc := func(i int) int {
return i + 1
}
VarFunc(i)
}
}
//闭包函数
func Benchmark_VarFuncCall2(b *testing.B) {
for i := 0; i < b.N; i++ {
VarFunc := func() int {
return i + 1
}
VarFunc()
}
}
禁用优化前:
dada-imac:labs dada$ go test -test.bench="." labs09
Benchmark_NormalFuncCall-4 2000000000 0.33 ns/op
Benchmark_VarFuncCall1-4 2000000000 0.33 ns/op
Benchmark_VarFuncCall2-4 1000000000 2.31 ns/op
PASS
ok labs09 3.949s
禁用优化后:
dada-imac:labs dada$ go test -test.bench="." -gcflags '-N' labs09
testing: warning:
no tests to run
PASS
Benchmark_NormalFuncCall-4 1000000000 2.63 ns/op
Benchmark_VarFuncCall1-4 1000000000 2.64 ns/op
Benchmark_VarFuncCall2-4 500000000 3.98 ns/op
PASS
ok labs09 8.216s
10.尝试绕过gc扫描,实际用到项目里发现会导致在其他地方出现内存分配死锁的错误,所以不敢实际使用,并且经过测试发现mallocgc还是不能绕过gc扫描,反而C.malloc更简单有效。
11.测试json序列化支持的类型
12.测试jemalloc和malloc在go程序中是否有性能差别。
var sizeof_BigStruct = C.size_t(unsafe.Sizeof(BigStruct{}))
func NewBigStruct() *BigStruct {
return (*BigStruct)(C.calloc(1, sizeof_BigStruct))
}
func AllocAndFree() {
var ptr = C.malloc(sizeof_BigStruct)
C.free(ptr)
}
func JeAllocAndFree() {
var ptr = C.je_malloc(sizeof_BigStruct)
C.je_free(ptr)
}
func Benchmark_AllocAndFree(b *testing.B) {
for i := 0; i < b.N; i++ {
AllocAndFree()
}
}
func Benchmark_JeAllocAndFree(b *testing.B) {
for i := 0; i < b.N; i++ {
JeAllocAndFree()
}
}
实验结果:
dada-imac:labs dada$ go test -test.bench=".*" labs12
PASS
Benchmark_AllocAndFree 10000000 190 ns/op
Benchmark_JeAllocAndFree 20000000 135 ns/op
ok labs12 4.971s
实验结论:性能还是有差别的
13.
a. 存对象指针的map和存结构体的map,每条数据都占用一个对象数量,两种数据结构无差异.
b. 存对象的slice和存结构体的slice有明显差异,存结构体的slice不重复占用对象数量.
c. 未初始化的对象类型字段是不占用对象数量的
14.测试用cgo复制go的内存数据和还原数据
15.看不懂,略
16.测试平方根算法的效率,总结就是多用math库
17.
a.把正则编译提取到外部,避免反复初始化
b.把map的key换成rune,避免反复生成字符串
18.测试switch和回调函数效率差异,回调函数快了一点
func New() *MyObject {
return &MyObject{
n: 4,
head: make([]byte, 4),
bo: binary.LittleEndian,
}
}
func NewCallback() *MyObject {
return &MyObject{
n: 4,
head: make([]byte, 4),
callback: func(head []byte) int {
return int(binary.LittleEndian.Uint32(head)) + 10
},
}
}
func (obj *MyObject) UseSwitch() int {
size := 0
switch obj.n {
case 1:
size = int(obj.head[0])
case 2:
size = int(obj.bo.Uint16(obj.head))
case 4:
size = int(obj.bo.Uint32(obj.head))
case 8:
size = int(obj.bo.Uint64(obj.head))
default:
panic("unsupported packet head size")
}
return size + 10
}
func (obj *MyObject) UseCallback() int {
return obj.callback(obj.head)
}
dada-imac:labs18 dada$ go test -bench=".*"
testing: warning: no tests to run
PASS
Benchmark_UseSwitch 200000000 9.58 ns/op
Benchmark_UseCallback 200000000 8.05 ns/op
ok github.com/idada/go-labs/labs18 5.323s
19.测试缓存反射信息对效率的影响
```
dada-imac:src dada$ go test labs19 --bench=".*" -v
=== RUN Test_Verify
--- PASS: Test_Verify (0.00 seconds)
=== RUN Test_FastVerify
--- PASS: Test_FastVerify (0.00 seconds)
=== RUN Test_VeryFastVerify
--- PASS: Test_VeryFastVerify (0.00 seconds)
PASS
Benchmark_________Verify 1000000 1399 ns/op
Benchmark_____FastVerify 10000000 178 ns/op
Benchmark_VeryFastVerify 20000000 93.9 ns/op
ok labs19 5.364s
```
第一种跟第三种差了十倍左右的速度
------------------------------------------------------
转达达的代码链接:https://github.com/bg5sbk/go-labs