念を押しておきますが、このブログの「内容は個人の考えであって、所属組織とは方針が異なる」と考えてください。
前のエントリでは、MySQL 8.0は、clangのPGO+LTOでビルドしないと本来の性能が出ない。ということを証明しました。その後、PGO+LTOといってもプロファイリングをどうしたらいいのかと、デスクトップマシンの空き時間でひたすらビルドとtpcc(ramfs)を繰り返した結果、興味深いことがわかりました。
tpccのようなある程度複雑なベンチマークは、
ベンチマークそのもの(この場合tpcc)をプロファイリングするよりも、
mysql-testのスクリプトを組み合わせて工夫したほうが性能が出る
ということです。(少なくとも私の環境で、ではですが)
つまり、
ビルドしてテストスクリプトが流せる環境であれば、総合的に最適に近いバイナリが生成できるということです。誰でもビルドできます。多分。しかも、公開ソースツリーだけでです。
(何故本家がそういう最適化バイナリを配布しないかもよくわからないですね。配布版が遅いビルドだとMySQL自体のプレゼンスが下がると思うのですが…)
このまま放って置いても8.0のCommunity Editionの最速バイナリが公式提供されるか怪しいので、それに近いものを作るための手順を公開しようと思います。大人の事情で自分ビルドを直接使えない人も話題に上げることで公式提供が早まるかもしれないので、色々試して皆で話題にしてみたり、要望してみたりしても…
※
clang PGO+LTO ビルドのテストもしてみましたが(私の環境での)唯一の違いは、ソース中の"__FILE__"シンボルの展開にパスが含まれない(ファイル名のみ)ことです。何が起こったかというと、performance_schema.error_log の subsystem列 で"Repl"となるべきものが"__FILE__"のパース(コンパイル時)違いで"Server"になってしまうエラー出力がある。という程度でした。本質的な問題にはならなそうです。(エラー出力のsubsystem判断の一部はパスの区切り文字が無いと、__FILE__からのbasename抽出ができないみたい。バグとして報告済みなのでいつか治るでしょう…)
最適に近いビルドの手順を説明する前に、練習としてまず、clangで普通のビルドをどうするかの説明をします。その中で、どのようなオプションを使うか決めてください。それを踏まえて、(現状私の環境で暫定ベストの)最適化ビルドの説明をします。今回はLinux(x86_64) clang環境だけで、それ以外の環境では事情が異なるかもしれませんが似た結論になると予想します。
練習: clangでノーマルビルド
まず練習として、用途に必要な機能を含むように普通のビルドをclangでできるようにしましょう。汎用的にするために、できるだけ Community Edition の配布バイナリと機能同等なビルド想定からスタートします。
githubの8.0ブランチのルートディレクトリをカレントに始めます。
#clang でビルドできるようにします。一応私の環境は clang13 です。
export CC=clang
export CXX=clang++
#とりあえず、64bitアーキテクチャ汎用で。お好みでアーキテクチャ限定してみても速くなるかも。
export CFLAGS="-O2 -g -pipe -m64 -mtune=generic"
export CXXFLAGS="-O2 -g -pipe -m64 -mtune=generic"
#私の場合です。git cleanとかでどうせ全部消せるので、ビルドを整理して分けたりしません。
#次の本番のやり方にも多少影響するのでとりあえすこれで。
cmake . -DFORCE_INSOURCE_BUILD=1 \
-DBUILD_CONFIG=mysql_release \
-DINSTALL_LAYOUT=STANDALONE \
-DFEATURE_SET=community \
-DPLATFORM=linux-custom \
-DWITH_ROUTER=OFF \
-DWITH_AUTHENTICATION_LDAP=ON \
-DWITH_AUTHENTICATION_FIDO=ON \
-DWITH_AUTHENTICATION_KERBEROS=ON \
-DWITH_CURL=system \
-DWITH_TIRPC=bundled \
-DWITH_NUMA=ON \
-DWITH_BOOST=~/boost_1_77_0 \
-DCMAKE_INSTALL_PREFIX=/opt/mysql-8.0 \
-DMYSQL_UNIX_ADDR=/opt/mysql-8.0/mysql.sock
必要なライブラリがインストールされていない場合はエラーで止まりますので、
入れるか、オプションを外すかしていきます。
このcmakeオプションが次の本番のベースになります。続けて…
#WITH_TIRPC=bundled でビルド途中で怒られないように、シンボリックリンクを作っておきます。
#(私の環境では必要ですが、要らない環境もあると思います。)
(cd tirpc; ln -s lib64 lib)
#ここまでエラーなく来ればビルドはできるはずです。
#※エラーが起きたら確認できるように一応 VERBOSE=1 を付けておきますが無くてもいいです。
#※"8"は私の環境でのCPU数です。並列数は環境に合わせて。
make -j8 VERBOSE=1
途中でなにかあったら解決してください。。。
因みに、100%まで終わった状態で
make install すれば使えますし、
make package で .tar.gz にパッケージできます。
最後に、次で必要となるので、mysql-testが動くようにしてください。
とりあえず、
(cd mysql-test; ./mtr innodb.innodb)
が動くようならOK。本番の準備はできています。プロファイリングで使います。
perlが入っていれば動くはずですが、何か必要なモジュールがあったかもしれません。
次は、この環境で最適化バイナリをビルドしてみましょう。
ちなみに、clangが利用するllvmXXに対応する、llvmXX-goldと言うパッケージが必要になるので、環境に入れておいてください。
本番: clang で PGO+LTO ビルドして最適に近いバイナリを作る
cmakeのオプションは練習のものベースで。指定の追加ぶん以外は変えないようにしましょう。
※幾つかある"8"は私の環境でのCPU数です。並列数指定は環境に合わせて。
#練習と同様
export CC=clang
export CXX=clang++
export CFLAGS="-O2 -g -pipe -m64 -mtune=generic"
export CXXFLAGS="-O2 -g -pipe -m64 -mtune=generic"
#一応一旦、練習時のファイルは全部消したほうがいいかも。
(git clean -xfd)
#練習と同様のものに -DFPROFILE_GENERATE=1 を足します。
cmake . -DFORCE_INSOURCE_BUILD=1 \
-DBUILD_CONFIG=mysql_release \
-DINSTALL_LAYOUT=STANDALONE \
-DFEATURE_SET=community \
-DPLATFORM=linux-custom \
-DWITH_ROUTER=OFF \
-DWITH_AUTHENTICATION_LDAP=ON \
-DWITH_AUTHENTICATION_FIDO=ON \
-DWITH_AUTHENTICATION_KERBEROS=ON \
-DWITH_CURL=system \
-DWITH_TIRPC=bundled \
-DWITH_NUMA=ON \
-DWITH_BOOST=~/boost_1_77_0 \
-DCMAKE_INSTALL_PREFIX=/opt/mysql-8.0 \
-DMYSQL_UNIX_ADDR=/opt/mysql-8.0/mysql.sock \
-DFPROFILE_GENERATE=1 -DDISABLE_PSI_MEMORY=ON -DWITH_UNIT_TESTS=OFF
先ほどと同様、プロファイル用のビルドをします。
#WITH_TIRPC=bundled でビルド途中で怒られないように、シンボリックリンクを作っておきます。
#(私の環境では必要ですが、要らない環境もあると思います。)
(cd tirpc; ln -s lib64 lib)
#ここまでエラーなく来ればビルドはできるはずです。
#※エラーが起きたら確認できるように一応 VERBOSE=1 を付けておきますが無くてもいいです。
#※"8"は私の環境でのCPU数です。並列数は環境に合わせて。
make -j8 VERBOSE=1
ちゃんとビルドできたら mysql-test の処理を流してプロファイリングします。
#../profile-data/ に ビルド中の実行のものもできてしまうので、気になるので一旦消します。
rm ../profile-data/*.profraw
#テスト自体の結果は関係ありません。プロファイリング処理があるせいで幾つか失敗しますが、強制で全部流してます。
#suite はこの組み合わせが(私の環境ですが)現時点での汎用暫定ベストです。
(cd mysql-test ; ./mtr --accept-test-fail --clean-vardir --force --max-test-fail=0 --mem --mysqld=--binlog-format=row --parallel=8 --retry=0 --skip-rpl --suite=binlog,collations,connection_control,encryption,gcol,gis,innodb,innodb_fts,innodb_gis,innodb_undo,innodb_zip,jp,json,main,sysschema,x)
#できた ../profile-data/*.profraw を利用できるように纏めます。
#偶にエラーが出る .profraw が混ざりますが、それを消すか、全部消してmysql-testをやり直すかします。
(cd ../profile-data ;llvm-profdata merge -output=default.profdata .)
PGO+LTO でビルドします。まずはcmake。
# 中途半端に前の設定が残らないようにキャッシュを消す。(重要)
rm CMakeCache.txt
#練習と同様のものに今度は -DFPROFILE_USE=1 を足します。
cmake . -DFORCE_INSOURCE_BUILD=1 \
-DBUILD_CONFIG=mysql_release \
-DINSTALL_LAYOUT=STANDALONE \
-DFEATURE_SET=community \
-DPLATFORM=linux-custom \
-DWITH_ROUTER=OFF \
-DWITH_AUTHENTICATION_LDAP=ON \
-DWITH_AUTHENTICATION_FIDO=ON \
-DWITH_AUTHENTICATION_KERBEROS=ON \
-DWITH_CURL=system \
-DWITH_TIRPC=bundled \
-DWITH_NUMA=ON \
-DWITH_BOOST=~/boost_1_77_0 \
-DCMAKE_INSTALL_PREFIX=/opt/mysql-8.0 \
-DMYSQL_UNIX_ADDR=/opt/mysql-8.0/mysql.sock \
-DFPROFILE_USE=1 -DDISABLE_PSI_MEMORY=ON -DWITH_UNIT_TESTS=OFF
cmake の結果、LTOがちゃんと使われるか確認します。駄目な場合は、なんとか解決してください。(私の場合は llvmXX-goldパッケージが無いことでLTOがcmake中に変なエラーで落ちてました)
grep WITH_LTO CMakeCache.txt
#こんな出力になるはず
# WITH_LTO:BOOL=ON
# WITH_LTO_DEFAULT:INTERNAL=ON
先ほどと同様、最適バイナリをビルドします。
#WITH_TIRPC=bundled でビルド途中で怒られないように、シンボリックリンクを作っておきます。
#(私の環境では必要ですが、要らない環境もあると思います。)
(cd tirpc; ln -s lib64 lib)
#ここまでエラーなく来ればビルドはできるはずです。
#※エラーが起きたら確認できるように一応 VERBOSE=1 を付けておきますが無くてもいいです。
#※"8"は私の環境でのCPU数です。並列数は環境に合わせて。
make -j8 VERBOSE=1
因みに、100%まで終わった状態で
make -j8 install すれば使えますし、
make -j8 package で .tar.gz にパッケージできます。
それでは、8.0 本来の性能をぜひ享受してください!
(もしかしたら、5.7配布バイナリや、8.0EE版バイナリより速いかも。)
(追記1)
返信削除なんか、プロファイリング絡みのシンボルがリンク時に解決できなくて失敗している場合は、lldをインストールしておくと良いかも。
8.0ならcmakeで多分勝手に使ってくれるか、明示的には、-DUSE_LD_LLD=ON。
8.4なら、-DWITH_LD=lld。
をcmakeのオプションに足す。
(追記2)
プロファイリング用のテストの中で、"io_setup() failed with EAGAIN." とかたくさん出たら、多分 fs.aio-max-nr (/proc/sys/fs/aio-max-nr) が足りない。
1048576 みたいに大きめにしましょう。
(追記1の追記)
返信削除lldが-DFPROFILE*と一緒に使えないようになっているかもので、update-alternatives で、ld が lld を指すようにするしか無いかも。 あくまで、通常のの GNU ld がエラーで使えない場合ですが。