总结自己遇过的各种问题
一些优化
循环内捕获变量
1 | package main |
这里不是输出0到1,可能是10个10,因为捕获的i在后面被修改了,改成加个复制就好了
1 | package main |
make slice 的两种写法
1 | sliceA := make([]int, 10) |
第一种是创建一个slice,容量和长度都是10,可以直接 sliceA[index]
下标访问,初始值是该类型的默认值,比如bool是false,int是0,指针是nil
第二种是创建一个slice,容量是10,长度是0,不能直接下标访问,必须要先 sliceB=append(sliceB, 1)
后,有数据了才能下标访问
slice进行append时不是协程安全的,多协程同时append需要加锁。
time.Since 优化
Since在1.16后做了一次优化,使用了runtime的纳秒计数器,少了一次调用 now(),优化了一次syscall。调用的 runtimeNano()
返回的是runtime的毫秒计数器,不需要系统调用。
1 | // runtimeNano returns the current value of the runtime clock in nanoseconds. |
所以既然有内部计时器,我们可以自己整一个优化的耗时计数。
1 | // runtimeNano returns the current value of the runtime clock in nanoseconds. |
下面是 benchmark 的代码
1 | // runtimeNano returns the current value of the runtime clock in nanoseconds. |
benchmark 结果如下:
1 | cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz |
差不多快了一倍
fmt 和 strconv
strconv使用的是查表,甚至还有32位系统的取模优化,性能很不错。
fmt使用的是反射,还有各种边界检验,计算量比strconv高很多
benchmark 代码:
1 | func BenchmarkUseFmt(b *testing.B) { |
benchmark 结果:
1 | cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz |
快了差不多3倍
字符串拼接 和 strings.Builder 和 内存预申请
很常见的优化了,go的string是不可修改的。append时需要拷贝,少量拼接的话无伤大雅,但是量太大的话,建议使用strings.Builder,如果知道最终字符串会有多大,预先申请会带来更高的性能优化。
benchmark 代码:
1 | func BenchmarkSimpleConcat(b *testing.B) { |
结果:
1 | cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz |
明显快了非常多。同样的,还有bytes.Buffer
,同样的使用方法,可以优化byte的拼接性能。
内存预申请从而实现优化的,标准库里还有 url.PathEscape
和 url.QueryEscape
,两个都是调用内部方法 escape
1 | func escape(s string, mode encoding) string { |
sync.pool
- 使用lock-free的双向队列作为缓存
- 使用victim作为待gc缓存,每次gc开头,把victim队列的东西清空,然后把local队列转到victim队列
- 在获取时,优先在local,然后victim,然后去别的p的pool拿,最后使用New。local中优先使用无锁的private(pin了,不会有竞争),没有再从shared拿,可以pushHead/popHead,别的p只能popTail
- 每个P享有一个pool。G操作pool时,需要锁P
编译相关
race detector
1 | go run -race |
所有内联tag
1 | go tool link --help |
所有gc tag
1 | go tool compile --help |
减小二进制文件大小
1 | -ldflags="-s -w" |
方便 dlv 调试(关闭优化、关闭内联)
1 | -gcflags '-N -l' |
某些技巧
runtime.LockOSThread
runtime.LockOSThread()可以把当前goroutine绑定在物理线程上,但是如果在goroutine绑定后没有UnlockOSThread,就会导致goroutine结束时,这条线程被杀掉