编译参数
理论上说 -s -w加上后,代码段的长度会减小,理论上会提高CPU代码cache的利用率 。(还未亲自测试过)
- -X importpath.name=value 编译期设置变量的值
- -s disable symbol table 禁用符号表
- -w disable DWARF generation 禁用调试信息
——《golang编译参数ldflags》
使用runtime中的非导出函数runtime中有的底层函数是汇编实现的,性能很高,但是不是export类型 。
这时候可以用链接声明来使用这些函数:
//go:noescape//go:linkname memmove runtime.memmove//goland:noinspection GoUnusedParameterfunc memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)func memmove(to, from unsafe.Pointer, n uintptr)// 通过上面的声明后,就可以在代码中使用底层的memmove函数了 。这个函数相当于c中的memcpy()具体的细节请看这篇文章:《Go的2个黑魔法技巧》(腾讯 pedrogao)
函数内联golang的小函数默认就是内联的 。
可以通过函数前的注释
//go:noinline
来取消内联,不过似乎没有理由这么做 。关于函数内联的深层知识还是值得学习的,推荐这篇文章:《详解Go内联优化》
可以关注文章中的这个内联优化技巧:
可通过泛型golang 1.18正式发布了泛型 。-gcflags="-l"
选项全局禁用内联,与一个-l
禁用内联相反,如果传递两个或两个以上的-l
则会打开内联,并启用更激进的内联策略 。
泛型可以让之前基于反射的代码变得更加简单,很多type assert的代码可以去掉;基于interface的运行期动态分发,也可以转成编译期决定 。
由于对具体的类型产生了具体的代码,理论上指令cache命中会提高,分支预测失败会降低,
不过,对于有一定体量的golang团队而言,泛型的引入要考虑的问题比较多:如何避免滥用,如何找到与之匹配的基础库?
在整个团队的能力还没准备好迎接泛型以前,使用工具生产代码的
产生式编程
或许是更容易驾驭的方法 。API使用反射编译期决定当然是好于运行期决定的 。
我的建议是:
- 能不用就不用,可以用下面的方法代替:
- 泛型
- 代码生成(产生式编程)
- 非得要用
- 缓存反射的到的结果
原理就是创建协程每秒一次获取 time.Now(),然后一秒以内取时间戳就只是访问全局变量 。
我测试过:性能比直接使用time.Now()快三倍左右 。
fastrand,绕开rand库的锁源码请见:https://github.com/valyala/fastrand
超长字符串输出的优化:quicktemplate假设一次要输出几兆字节的JSON字符串,如何优化性能?
VictoriaMetrics中的vm-select就遇到了这个问题,当一个大查询需要返回很多的metrics数据的时候,其输出的json的体积非常可观 。
如果把数据先放到一个大数组,再使用json.Marsharl,则一方面要频繁申请释放内存,另一方面会带来内存使用量的剧烈抖动 。vm-select的解决方式是使用quicktemplate库——把json看成是字符串流的输出 。
具体代码请看:https://github.com/valyala/quicktemplate
经验总结扩展阅读
- 持续集成指南:GitLab 的 CI/CD 工具配置与使用
- 信用卡怎么查消费明细
- 消费贷款申请产生的费用高吗
- oppo账号的姓名怎么修改
- 租房可以换锁芯吗合法么 租房换锁费用谁来承担
- 特斯拉可以用家用电充电吗
- 拆线多久可以用祛疤膏?
- 卫生间和厨房用什么瓷砖?
- 肉苁蓉副作用是什么
- 计米器怎么设置参数