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

2024-07-02 17:04:22 浏览数 (2)

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 进行优化编译。如:

代码语言:javascript复制
#### 采样编译
g   -o test test.cpp -fprofile-generate=/tmp/pgo
#### 运行程序
./test
### 优化编译
>g   -o test test.cpp -fprofile-use=/tmp/pgo

使用 PGO 编译 MySQL

由于 MySQL 数据库使用 C 进行编写,显然可以使用 PGO 优化。从 MySQL 8.0.19 版本开始,MySQL 的 cmake 文件支持 PGO 优化,对 MySQL 进行采样编译可以加上参数 FPROFILE_GENERATE:

第一次编译
代码语言:javascript复制
$ cd mysql-server-8.0.33
$ mkdir build
$ cmake -DCMAKE_C_FLAGS="-g -O3 -march=native -mcpu=native -flto" -DCMAKE_CXX_FLAGS="-g -O3 -march=native -mcpu=native -flto" -DCMAKE_INSTALL_PREFIX=/mysql_data/mysql_8.0.33_gcc_11.3.0_profile -DWITH_BOOST=/build/boost_1_77_0 -DFPROFILE_GENERATE=ON ..
$ make -j $(nproc)
$ make install
运行mysqld ,并压测

接着运行采样编译的二进制程序 mysqld,并运行 sysbench 程序,测试点查询的只读性能。

此时 PGO 其实在收集 MySQL 数据库运行时的各种状态,函数,变量。所以, MySQL 的 QPS 降低了很多。如果我们在观察二进制文件所在的 xxx-profile-data 文件夹,就会发现该文件夹下已经产生了很多 gcda 结尾的 profile 文件。

mysql-server-8.0.33/build-profile-data

第二次编译

然后,停止 mysqld 程序,通过下面的命令重新编译 MySQL。

代码语言:javascript复制
$ cd mysql-server-8.0.33
$ rm rf build && mkdir build # make sure it's same build directory as in step 1, or it would fail to find the profile data under build-profile-data
$ cmake -DCMAKE_C_FLAGS="-g -O3 -march=native -mcpu=native -flto" -DCMAKE_CXX_FLAGS="-g -O3 -march=native -mcpu=native -flto" -DCMAKE_INSTALL_PREFIX=/mysql_data/mysql_8.0.33_gcc_11.3.0_pgo  -DWITH_BOOST=/build/boost_1_77_0 -DFPROFILE_USE=ON ..
$ make -j $(nproc)
$ make install

通过指定FPROFILE_USE=ON,再次进行编译时,MySQL 会自动根据 xxx-profile-data 文件夹下的各种 profile 文件进行编译器优化。这里产生的 mysqld 程序将会是对应上述 sysbench 测试后的编译优化版本。因此,在优化编译后,重新启动优化后的 mysqld 程序,再进行同样的 sysbench 性能压测,结果较之前有明显的提升,通过 PGO 优化后,性能提升超过 30%。

PGO 优化的限制

其实,从上面的实现原理来看,PGO是一种基于场景反馈的优化,比如针对 sysbench 的读写操作进行优化。但是实际业务中,这个数据库实例随着业务的发展,会有不同的TP 业务模型 ,或者会增加 AP 类的查询,此时 PGO 优化就不能完全覆盖新增的业务场景。

那么如何更好的使用的?欢迎大家来探讨。

参考

https://aijishu.com/a/1060000000433835

0 人点赞