【第三届青训营-后端专场】课后作业 -《高性能 Go 语言发行版优化与落地实践》

1,188 阅读4分钟

大家直接在评论区发布答案就可以哦~

课后作业- 重点内容 Review

  1. 从业务层和语言运行时层进行优化分别有什么特点?
  1. 从软件工程的角度出发,为了保证语言SDK的可维护性和可拓展性,在进行运行时优化时需要注意什么?
  1. 自动内存管理技术从大类上分为哪两种,每一种技术的特点以及优缺点有哪些?
  1. 什么是分代假说?分代 GC 的初衷是为了解决什么样的问题?
  1. Go 是如何管理和组织内存的?
  1. 为什么采用 bump-pointer 的方式分配内存会很快?
  1. 为什么我们需要在编译器优化中进行静态代码分析?
  1. 函数内联是什么,这项优化的优缺点是什么?
  1. 什么是逃逸分析?逃逸分析是如何提升代码性能的?

    < 作业提交截止时间:5月12日 10:00前 >

正确答案:

  1. 业务层的优化需要针对具体问题具体分析;而语言运行时层的优化针对的是更通用的问题,需要在多方面进行 tradeoff。两者都需要自动化性能分析工具的支持。
  1. 因为语言 SDK 会被大量广泛使用,保证接口的稳定性和一致性至关重要。对已有接口的改动,需要保证兼容性;对新增的接口,需要提供明确的文档说明。对于功能的改动,最好通过选项进行隔离,保证新增改动在不打开的情况下不影响原本的功能。
  1. 主要分为追踪垃圾回收和引用计数:

    a. 追踪垃圾回收

    1️⃣ 回收内存的条件:回收不可达的对象;
    2️⃣ 回收时,首先会扫描 GC roots,例如栈上的对象、全局变量等;
    3️⃣ 从 GC roots 出发,沿着指针指向,追踪所有的可达对象;
    4️⃣ 追踪完成后,回收所有不可达对象的内存。
    

    b. 引用计数

    1️⃣ 对象有一个与之关联的引用数目。当且仅当引用数大于 0 时对象存活;
    2️⃣ 当对象的引用数目为 0 时,内存可以被回收;
    3️⃣ 优点:内存管理的开销被平摊到程序运行中,且内存管理不需要了解 runtime 的实现细节;
    4️⃣ 缺点:维护引用计数的开销较大;额外的内存开销存储引用计数;无法回收带有环的数据结构;回收大数据结构时依然会造成程序暂停。
    
  1. 大量的对象会很快死去。分代 GC 的思想是:针对不同生命周期的对象采取不同策略的内存管理机制。
  1. Go 使用 TCMalloc 风格的内存管理方式。

    a. TC 是 thread caching 的简写。每个线程都绑定 cache 方便线程快速分配内存;

    b. 内存被划分为特定大小的块。根据对象是否包含指针,将内存块分为 scan 和 noscan 两种;

    c. 根据内存分配请求的大小,选择合适的内存块返回,完成一次内存分配操作;

    d. 回收的内存不会立刻还给操作系统,而是在 Go 内部缓存起来,方便下次分配。

  1. 每个线程都持有用于对象分配的 buffer,因此指针碰撞方式的内存分配无需加锁或使用 CAS 操作;对象分配的操作非常简单。
  1. 通过静态分析,我们可以获取更多关于程序的非平凡特性 (non-trivial properties);这些关于程序的知识可以指导编译器优化。例如通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。
  1. 函数内联:将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定。

    a. 优点

    ① 消除函数调用;
    ② 由于没有了函数调用,过程间分析转化为过程内分析;
    

    b. 缺点

    ① 函数体变大;
    ② 编译生成的 Go 镜像变大。
    
  1. 逃逸分析:分析代码中指针的动态作用域,即指针在何处可以被访问。

    通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。