Go 编译器优化 & Go 框架初识 | 青训营笔记

320 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天

Go 编译器优化

编译器优化带来的好处:

  1. 用户无感知,重新编译就可以获得性能收益
  2. 通用性优化

而编译器现状是:

  • 采用的优化少
  • 编译时间短,没有进行较复杂的代码分析和优化

编译优化的思路

  • 常见:面向后端长期执行任务
  • Tradeoff:用编译时间换取更高效的机器码

Beast Mode(字节自研)

  • 函数内联
  • 逃逸分析
  • 默认栈大小调整
  • 边界检查消除
  • 循环展开
  • ......

函数内联

内联指的就是将被调用函数的函数体(callee)的副本替换到调用位置(caller)上,同时重写代码以反映参数的绑定。

内联的优点如下:

  • 消除函数调用开销,例如传递参数、保存寄存器等
  • 将过程间分析转换为过程内分析,帮助其他优化,例如逃逸分析

有优点必然也有缺点,其缺点如下:

  • 函数体变大,对于instruction cache不友好
  • 编译生成的Go镜像变大

不过在大多数情况下,内联都是正向优化

在Golang函数中内联受到的限制比较多

  • 语言特性,例如interface、defer等,限制了函数的内联
  • 内联的策略非常保守

可以使用 micro-benchmark 快速验证和对比性能优化结果。

逃逸分析

逃逸分析指的是分析代码中指针的动态作用域,以确定指针在何处可以被访问。

其大致思路如下:

  • 从对象分配处出发,沿着控制流,观察对象的数据流
  • 若发现指针p在当前作用域s下有以下行为:
    • 作为参数传递给其他参数
    • 传递给全局变量
    • 传递给其他的goroutine
    • 传递给已逃逸的指针指向的对象
  • 那么就说指针p指向的对象逃逸出了s,反之则没有逃逸出s

Beast Mode

其为字节自研的编译优化方法。

主要介绍了函数内联+逃逸分析。

函数内联

对于函数内联,其调整了函数内联的策略,从而使得更多函数被内联。

其好处就是:

  • 降低函数调用的开销
  • 增加了其他优化的机会:逃逸分析

不过有些许的开销

  • Go镜像增加了10%左右
  • 编译时间增加

逃逸分析

对于逃逸分析,由于函数内联拓展了函数边界,因此更多的对象都不会逃逸出来。

其所做的优化是将未逃逸的对象在栈上分配,因为对象在栈上分配和回收很快,只需要移动sp指针即可。而减少heap上的内存分配,可以降低GC的负担。

Go 的框架三件套

GKH 框架三件套

  1. Gorm

Gorm 是一个已经迭代了10年+的功能强大的ORM框架,在字节内部被广泛使用并且拥有非常丰富的开源扩展。

  1. Kitex

Kietx 是字节内部的 Golang 微服务 RPC 框架,具有高性能,强可扩展的主要特点,支持多协议并且拥有丰富的开源扩展。

  1. Hertz

Hertz 是字节内部的 HTTP 框架,参考了其他开源框架的优势,结合字节跳动内部的需求,具有高易用性、高性能、高扩展特点。