不花钱提升 20-30% 的性能?看看MySQL编译器优化PGO


MySQL 在不修改代码的情况下,通过编译器优化,可以提高特定场景下的运行性能,通常性能可以有 20% ~ 40% 的性能提升。

这种特性在某些特定场景的 POC 测试中可以使用,以便更容易的通过测试, 部分云厂商已经提供针对sysbench的 PGO 版本。

在 PGO 编译器优化前

我们做的比较多的编译器优化是分支预测优化。这种奇技淫巧通过告诉编译器代码更倾向于走哪个分支,从而提升程序的运行效率。一旦预测是正确的,那么程序的性能就能得到有效的提升。但分支预测优化存在以下几个问题:

  1. 需要修改源码,而且需要非常有经验的程序员方能编写可靠的代码
  2. 编写的分支预测可能与实际业务运行的不一样,从而失去性能提升的可能性,反而导致灾难的发生

PGO 编译器优化

为了解决上面这两个问题,可以使用 PGO,更为通用的编译器优化,从而提升程序的运行效率。简单来说,PGO 会根据程序真实的运行情况,生成一份 profile 文件。下次编译时,通过上次运行产生的 profile 文件,以产生更加贴近真实运行情况、性能更好的代码。可以看到,PGO 优化需要两次编译,三个阶段:

  1. 采样编译(Instrument):在 instrument 阶段,先对应用做一次编译,在此编译中,编译器会向代码中插入指令,以便下一阶段可以收集数据。这些指令分为三种类型,分别用于统计每个函数被执行了多少次,每个分支被执行了多少次(例如 if-else 的场景)以及某些变量的值。

  2. 训练程序(Train):在 train 阶段,用户需要使用最常用的输入来运行上一阶段编译生成后的应用。由于上一阶段已经做好了收集数据的准备,所以在经过 train 阶段之后,该应用最常见的使用场景对应的数据就会被收集下来。

  3. 优化编译(Optimization):在 optimization 阶段中,编译器会利用上一阶段收集到的数据,对应用进行重新编译。由于上一阶段的数据来自于用户输入的最常见的用户场景,所以最后优化得到的结果就能在该场景下有更好的优化。

可以发现 PGO 优化不仅仅对数据库程序优化有效率,其实他是一种通用的优化手段,可以用于任何程序的优化。常见的 C/C++、Rust语言都已支持 PGO 优化。在 C++ 代码中使用 PGO 优化其实也比较简单,并不复杂。加上编译参数 -fprofile-generate 用户采样编译,参数 -fprofile-use 表示用 profile 进行优化编译。如:

#### 采样编译<br>g++ -o test test.cpp -fprofile-generate=/tmp/pgo<br>#### 运行程序<br>./test<br>### 优化编译<br>>g++ -o test test.cpp -fprofile-use=/tmp/pgo<br>