(本文的pdf版本)
众所周知,golang非常适合用于开发后台应用,但也通常是各种各样的应用层软件 。
开发系统软件,目前的首选还是C++, C, rust等语言 。相比应用软件,系统软件需要更加稳定,更加高效 。其维持自身运行的资源消耗要尽可能小,然后才可以把更多CPU、内存等资源用于业务处理上 。简单来说,系统软件在CPU、内存、磁盘、带宽等计算机资源的使用上要做到平衡且极致 。
golang代码经过写法上的优化,是可以达到接近C的性能的 。现在早已出现了很多用golang完成的系统软件,例如很优秀的etcd, VictoriaMetrics等 。VictoriaMetrics是Metric处理领域优秀的TSDB存储系统,在阅读其源码后,结合其他一些golang代码优化的知识,我将golang开发系统软件的知识总结如下:
golang的第一性能杀手:GC个人认为GC扫描对象、及其GC引起的STW,是golang最大的性能杀手 。本小节讨论优化golang GC的各种技巧 。
压舱物ballast下面一段神奇的代码,能够减少GC的频率,从而提升程序性能:
func main(){ ballast := make([]byte, 10*1024*1024*1024) runtime.KeepAlive(ballast) // do other things}其原理是扩大golang runtime的堆内存,使得实际分配的内存不容易超过堆内存的一定比例,进而减少GC的频率 。GC的频率低了,STW的次数和时间也就更少,从而程序的性能也提升了 。
具体的细节请参考文章:
- 一个神奇的golang技巧:扩大heap内存来降低gc频率 (本人)
- Go Ballast 让内存控制更加丝滑
也需要注意,这里有个坑:如果使用mmap去映射一个文件,则某个虚拟地址没有对应的物理地址时,操作系统会产生缺页终端,并转到内核态执行,把磁盘的内容load到page cache 。如果此时磁盘IO高,可能会长时间的阻塞……进一步地,导致了golang调度器的阻塞 。对象复用对象太多会导致GC压力,但又不可能不分配对象 。因此对象复用就是减少分配消耗和减少GC的释放消耗的好办法 。
下面分别通过不同的场景来讨论如何复用对象 。
海量微型对象的情况假设有很多几个字节或者几十个字节的,数以万计的对象 。那么最好不要一个个的new出来,会有两个坏处:
- 对象的管理会需要额外的内存,考虑内存对齐等因素又会造成额外的内存浪费 。因此海量微型对象需要的总内存远远大于其自身真实使用的字节数;
- GC的压力源于对象的个数,而不是总字节数 。海量微型对象必然增大GC压力 。
因此,海量微型对象的场景,这样解决:
- 分配一大块数组,在数组中索引微型对象
- 考虑fastcache这样的组件,通过堆外内存绕过GC
大量小型对象的情况对于大量的小型对象,sync.Pool是个好选择 。
推荐阅读这篇文章:《Go sync.Pool 保姆级教程》
经验总结扩展阅读
- 持续集成指南:GitLab 的 CI/CD 工具配置与使用
- 信用卡怎么查消费明细
- 消费贷款申请产生的费用高吗
- oppo账号的姓名怎么修改
- 租房可以换锁芯吗合法么 租房换锁费用谁来承担
- 特斯拉可以用家用电充电吗
- 拆线多久可以用祛疤膏?
- 卫生间和厨房用什么瓷砖?
- 肉苁蓉副作用是什么
- 计米器怎么设置参数