MySQL 编译器优化PGO(Profile Guided Optimization)
MySQL 在不修改代码的情况下,通过编译器优化,可以提高特定场景下的运行性能,通常性能可以有 30% ~ 50% 的性能提升。这种特性在某些特定场景的 POC 测试中可以使用,以便更容易的通过测试。
在 PGO 编译器优化前,我们做的比较多的编译器优化是分支预测优化。这种奇技淫巧通过告诉编译器代码更倾向于走哪个分支,从而提升程序的运行效率。一旦预测是正确的,那么程序的性能就能得到有效的提升。但分支预测优化存在以下几个问题:
- 需要修改源码,而且需要非常有经验的程序员方能编写可靠的代码
- 编写的分支预测可能与实际业务运行的不一样,从而失去性能提升的可能性,反而导致灾难的发生
为了解决上面这两个问题,可以使用 PGO,更为通用的编译器优化,从而提升程序的运行效率。简单来说,PGO 会根据程序真实的运行情况,生成一份 profile 文件。下次编译时,通过上次运行产生的 profile 文件,以产生更加贴近真实运行情况、性能更好的代码。可以看到,PGO 优化需要两次编译,三个阶段:
- 采样编译(Instrument):第1次编译,并向代码中插入指令,以便下一阶段可以收集数据;
- 训练程序(Train):运行程序,收集数据,产生 profile 文件;
- 优化编译(Optimization);第2次编译,利用程序实际运行产生的 profile 文件,对程序进行编译优化;
可以发现 PGO 优化不仅仅对数据库程序优化有效率,其实他是一种通用的优化手段,可以用于任何程序的优化。常见的 C/C++、Rust语言都已支持 PGO 优化。在 C++ 代码中使用 PGO 优化其实也比较简单,并不复杂。加上编译参数 -fprofile-generate 用户采样编译,参数 -fprofile-use 表示用 profile 进行优化编译。如:
# 采样编译 g++ -o test test.cpp -fprofile-generate=/tmp/pgo # 运行程序 ./test # 优化编译 g++ -o test test.cpp -fprofile-use=/tmp/pgo
由于 MySQL 数据库使用 C++ 进行编写,显然可以使用 PGO 优化。从 MySQL 8.0.19 版本开始,MySQL 的 cmake 文件支持 PGO 优化,感兴趣小伙伴可以阅读源码:fprofile.cmake。所以,对 MySQL 进行采样编译可以加上参数 FPROFILE_GENERATE:
cmake .. -DFPROFILE_GENERATE=ON
接着运行采样编译的二进制程序 mysqld,并运行 sysbench 程序,测试点查询的只读性能。
这时,你会发现 MySQL 的 QPS 降低了很多。这是因为此时是在对 MySQL 数据库运行时的各种状态进行收集。所以,若这时观察二进制文件所在的 xxx-profile-data 文件夹,你会发现该文件夹下已经产生了很多 gcda 结尾的 profile 文件。
接着,停止 mysqld 程序,通过下面的命令重新编译 MySQL。
cmake .. -DFPROFILE_USE=ON
通过指定FPROFILE_USE=ON,这时再次进行编译时,MySQL 会自动根据 xxx-profile-data 文件夹下的各种 profile 文件进行编译器优化。这里产生的 mysqld 程序将会是对应上述 sysbench 测试后的编译优化版本。因此,在优化编译后,重新启动优化后的 mysqld 程序,再进行同样的 sysbench 性能压测,结果较之前有明显的提升,通过 PGO 优化后,性能提升超过 40%。