zoukankan      html  css  js  c++  java
  • Go优化浅谈

    Go 优化之路

    来自 GOCN.VIP 的 2020 深圳 gopher meet up,演讲者陈一枭, GitHub ,观后笔记。

    前言

    优化是有成本的,需要权衡优化成本和优化价值。而且随着优化的推进,收益递减,所以我们必须知道 何时停止,并且 目标明确

    比如:优化目标是改进CPU,那么什么是可接受的速度,你想要将当前的性能提高多少?2倍还是10倍?如果目标是减少内存使用量,那么我们可以接受的速度有多慢,换句话说,你打算用多少速度去换取较低的需求。

    停止优化的信号是什么?例如优化网络I/O服务,当我们发现Read/Write操作均消耗在syscall.Syscall则无需继续优化。

    How to

    1588685130857

    在尝试改善一段代码性能前,我们需要先了解 当前性能

    优化是一种重构形式,我们以提高性能为目标,那么就意味这要以代码可读性为代价。

    既然是重构,为了确保没有破坏任何内容,所以我们需要一套全面的单元测试,以及一套很好的基准测试,用来记录你的改变对性能产生的影响。

    基准测试

    Go 的标准库 testing,提供了 benchmark功能,通过执行 go test -bench=. 来进行基准测试。

    分析

    关注不同部分的优化影响,如果将仅占 5%的例程速度提高一倍,那么整个项目的速度只提高了 2.5%,相反,你将占 80%的部分加速10%,那么整个项目将提高 8%

    使用工具: GODEBUG,go tool pprof,go tool trace

    校验

    使用 benchstat 来统计测试,

    先安装 benchstat:

    go get golang.org/x/pref/cmd/benchstat
    

    这里举个例子,看以下代码:

    func MyItoa(i int) string {
    	return fmt.Sprint(i) //版本一
    	//return strconv.Itoa(i) //版本二
    }
    
    var r string
    
    func BenchmarkMyItoa(b *testing.B) {
    	for i := 0; i < b.N; i++ {
    		r = MyItoa(i)
    	}
    }
    
    

    分别对版本一和版本二执行:

    go test -bench=. -count=10 > ver1.txt
    go test -bench=. -count=10 > ver1.txt
    

    然后进行对比:

    benchstat ver1.txt ver2.txt
    name      old time/op  new time/op  delta
    MyItoa-4   148ns ± 2%    47ns ± 2%  -68.40%  (p=0.000 n=10+9)
    
    

    可以看到,性能损耗降低了68%。

    实践

    使用 syncPool复用对象,复用已分配对象,减少分配数量,降低GC压力,注意重置复用对象,避免取到脏数据。

    使用成员变量复用对象。

    写时复制代替互斥锁。

    分区:减少加锁粒度

    避免包含指针结构体作为map的key,因为在GC时,运行时扫描包含指针对象并且进行追踪。解决:在插入map之前将字符串散列为整数(以下为例)。

    优化前

    func timeGC() {
    	t := time.Now()
    	runtime.GC()
    	fmt.Printf("gc took: %s
    ", time.Since(t))
    }
    
    // go中string是指针
    var pointers = map[string]int{}
    
    func main() {
    	for i := 0; i < 10000000; i++ {
    		pointers[strconv.Itoa(i)] = i
    	}
    
    	for {
    		timeGC()
    		time.Sleep(1 * time.Second)
    	}
    }
    

    优化为:

    type Entity struct {
    	A int
    	B float64
    }
    
    var entities = map[Entity]int{}
    
    func main() {
    	for i := 0; i < 10000000; i++ {
    		entities[Entity{
    			A: i,
    			B: float64(i),
    		}] = i
    	}
    
    	for {
    		timeGC()
    		time.Sleep(1 * time.Second)
    	}
    }
    
    

    使用 strings.Builder拼接字符串

    避免[]bytestring的转换

    多读少写,使用 sync.RWMutex代替 sync.Mutex

  • 相关阅读:
    LINUX-----管道流及重定向
    转:vim----复制粘贴
    linux ----虚拟机无法与本地机通信
    C语言---注释
    C语言---翻译过程
    DROP--删除表
    ALTER---删除字段
    Oracle中用一条Sql实现任意的行转列拼接 多行拼接
    Oracle列操作(增加列,修改列,删除列)
    Java 把long 转换成 日期 再转换成String类型
  • 原文地址:https://www.cnblogs.com/Jun10ng/p/12833686.html
Copyright © 2011-2022 走看看