より詳しい説明や経緯は応援サイト
で有料会員向けに公開しているのですが、グラフだけ共有します。
どうも小手先のヒンティングでは回避できない様相です。
原因を特定するためにはLLVMの深淵に向かわなければいけなくなってきました…
より詳しい説明や経緯は応援サイト
で有料会員向けに公開しているのですが、グラフだけ共有します。
どうも小手先のヒンティングでは回避できない様相です。
原因を特定するためにはLLVMの深淵に向かわなければいけなくなってきました…
MySQL RP (Restore Performance) として、成果をgithubで公開しながら(利用はMySQL Community Editionと同様のGPLライセンス) 個人でMySQLの性能回復を研究しています。
これまでは、第1段階として、OLTP処理で競合しそうな要素を狙ったSQLでのCPU性能スケールテストを行って、MySQL-8.0で問題のある箇所を修正してきました。RP008で目立った問題は解決したと思われるので、
第2段階として、PGOビルドなしでもシングルスレッド性能で、5.7のノーマルビルド(公平のため -DDISABLE_PSI_MEMORY=ON オプション付きビルド)を超えることを目指します。そうすれば限定句が少し取れて「世界で一番CPU性能が高いMySQL」となります。
(※MySQL-5.7のPSI_MEMORYのコードはperformance_schema=OFFでも非常に重く、5.7の性能を不当に下げています。)
現状の比較結果を簡単に紹介しますが、その前にこの活動の持続のためのご協力のお願いです。
一つの手段は、Patreonのサポートサイトの有料会員になっていただくことです。今回の結果のもっと詳しい説明もありますし、活動の中間報告なども随時しています。RPxxxの各変更も、どの様に分析したかも説明してます。
もう一つの手段は、、、
「よくある事情」により、この活動は求職活動の一環と言う側面も暫くあるので、この活動(とういか生活…)の持続性のために、採用選考・採用面接などの実績もあると助かります。このRP開発活動との折り合いが良ければ実際副業にしますので。 buildup.db@gmail.com まで一報をお願いします。
あとは、結果が出始めたので、この活動について機を見て話題に上げて宣伝していただけると有り難いです。
よろしくお願いいたします。
では現状(第1段階完了)のベンチマーク結果を簡単に。
まずTPC-Cですが、ほんの少ししかベースの8.0と差がないので割愛します。
TPC-CはMySQLのOLTP性能を測る指標としては今はもう緩すぎるようです。個別の要素を調べなければなりません。
(※この20年弱、TPC-C同等処理のベンチマークをOLTP性能の指標にして性能改善を進めてきましたが、既に8.0では十分なスケールが得られているため、より厳しい指標でなければ、もう緩いTPC-Cでは性能が下がっても分からないようです。そのせいで、遅いコードを平気で書く人達が気づかなかった。これは我々開発に関わった人達のミスです。反省しています…)
というわけで、例によって Dimitriさんの BMK-kit の中から各個別要素に一番負荷をかけるSQLを選んでテストしていたわけです。
今までと同様 "Xeon(R) CPU E5-2699 v4 (22cores;44threads)" で 2ノードNUMA、
単純化してできるだけ純粋なCPUスケールを見るために、ストレージは ramfs、
REDOログの書き込み待ち設定 innodb_flush_log_at_trx_commit=0 で io wait を極力排除しています。
(まず、CPU性能を優先してるのは、誤魔化しようがないからです。IOは多少 Durability を犠牲にすれば可能なので後回し。)
比べたのは、これらのバイナリ、
あとは、私がノーマルビルド(但し -DDISABLE_PSI_MEMORY=ON)した、"MySQL-5.7.44"、"MySQL-8.0.42"、"MySQL RP-8.0[RP008]"。そして、MySQL RP-8.0[RP008] は PGOビルドも比較しています。
セカンダリ索引の範囲スキャンで、索引に無い値を取得するクエリは、1クエリで多数の主キーランダム検索を発生することができます。クライアント通信のラグなどを抑えて、より純粋な性能です。
BMK-kit でいうと、sb_exec/sb11-OLTP_RO_10M_8tab-uniform-s_ranges1-Rsize100-SecIDX-notrx-socket.sh 相当。
上記と同様のクエリに "LOCK IN SHARE MODE" 句を足せば、競合しないロック処理のみで lock_sys に負荷をかけられます。
REDO log バッファへの書き込みは mini-transaction 単位で、それは1レコード毎です。
1クエリで多数のレコードを更新すれば、多数のmini-transactionを発生させて、
log_sys に負荷をかけられます。
BMK-kit でいうと、sb_exec/sb11-OLTP_RW_10M_8tab-uniform-upd_noidx1-upd_range100-notrx-socket.sh 相当。
参照整合性取る必要のなくなった古いUNDOレコードを消すのがバックグラウンドのPurge処理です。innodb_max_purge_lag_delay を使えば、フォアグラウンドの処理がPurgeを置いていかなくなり、Purgeの処理性能が現れます。1クエリで多数のセカンダリ索引キー値を更新すれば、主キーレコードの更新やセカンダリキーレコードの削除・追加などで、大量のUNDOレコードを生成することになりPurge処理が増やせます。このテストは、purge_threads を変更して平衡するスループットを調べます。
BMK-kit でいうと、sb_exec/sb11-OLTP_RW_10M_8tab-uniform-upd_idx1-upd_range100-notrx-socket.sh 相当の最後の値です。
2.と3.は、1.を直したことで性能が高くなっているようです。
というわけで、
1.と4.の問題は、他のMySQLコードでは何も直していないようですので、
現状、世界で一番と言っても良いでしょう。:)
しかし低並列では、RP008ノーマルビルドは5.7ノーマルビルドの真の性能(-DDISABLE_PSI_MEMORY=ON)にまだ負けるので、それが次の課題です。
(PGOビルドはバイナリの再現性が少し良くないので、維持管理上不便なので。不便だとバグを取りこぼしたりビルドによって挙動が違う可能性があったり少し不安要素ですので。処理によっては遅くなるものもあるかも知れない。安全のためにはコード側でコントロールしたいところ。)
うまく行けば、PGOビルドよりも速くなるかも。
そして、ここからがこの活動の本番なのです。何か良い方法をこれから考えます。時間は掛かるかも知れませんが、応援してください。
第2段階のその後はIO性能とか何か劣化を取りこぼしてる処理など(有料会員優先で)募集したりします。
では、ご期待ください。
私の個人活動、MySQL RP (Restore Performance) で、1つベンチマーク結果を十分改善しました。区切りが良いので簡単に報告だけ。
ベンチマークは、DimitriKさん ( http://dimitrik.free.fr/blog/ ) がメンテして公開している sysbench バイナリ & スクリプト BMK-kit ( http://dimitrik.free.fr/blog/posts/mysql-perf-bmk-kit.html ) でいうところの、sb11-OLTP_RO_10M_8tab-uniform-s_ranges1-Rsize100-SecIDX-notrx-socket.sh 相当で、
セカンダリ索引の100行スキャンで、索引カラム以外のデータを返すクエリ
で、内部的に主キー検索が100回起こる、純粋主キー検索に近い性能が出る処理です。特殊なクエリですが、実は基本的な処理のベンチマークになってるみたいです。(クライアント通信1回で100回主キー検索が発生するので) Adaptive Hash Index も結構効きます。
マシンは、Xeon(R) CPU E5-2699 v4 (22cores;44threads) の 2NUMA ノード構成 というレトロなのにコアが多い構成で、性能スケールは厳しい構成です。(7,8年前の超最高スペックサーバーが個人でもPC相当の価格で購入可能になりましたね) 厳しいので、ここでスケールすれば、何処に持っていってもスケール問題は出ないはず。
で、現状こんな結果です。 (X軸がスレッド数、Y軸がTPSです)
(AHI=off)
(AHI=on)
他に特別遅くなってるベンチマークがあれば見ていきます。でも参照系処理のスケールはこれでかなり直ってると思います。
より詳しい説明や、解析・修正の経緯は応援サイト
Patreonの私のページ
で有料会員向けに公開しています。
このような感じでこれからも続けていきますので宜しくおねがいします。
ブログでは、このように大きな案件の簡単な結果のみにします。
ようやく、MySQL性能向上開発に専念する体制が整いました。やっと開発作業に戻れます。
実質無所属なのは人生初なので、各種社会保障関係の手続きとか、どのサイトを使って組み合わせるか、どういう設定でアカウントをつくるかなどなど、開発環境も含めて全部自分で決めなければならず、結構時間を要してしまいました。すみません。
結果、本家在籍時と同様の使い勝手で自由に開発できるようになりました。
名前は、『MySQL RP (Restore Performance)』 としました。 ソースコードはMySQLのオープンソースライセンス(GPLv2ベース)で提供します。最初は劣化がもう増えない、劣化が少ないと思われる 8.0 からforkして直していきます。まだ空っぽですが、数日中に更新が増えていきます。
バイナリ提供は余裕が出てから考えます。まだ無理です。既存の互換バイナリ提供者がpullしてくれるようになれば、それもいいかなと思います。
フルタイムで独自活動を続けていくために、支援者も募集します。
buildup-db と言う名前で Patreon に登録しました。(※)
MySQL RP の性能改善修正毎に、ベンチマーク結果、問題解析、ソース変更点の説明を有料会員向けに行っていきます。内容に興味のある方、最新コードで性能向上を体験できた方は、ご支援ください。
長年、影に隠れてきましたが遂に一人で細々と矢面に立つ羽目になりました。
ご期待に応えられるよう善処します。
よろしくおねがいします。
※6月4日現在、Patreonのアカウントにトラブルがあったようです。問い合わせ中です。Patreon側がアカウントの内容をレビュー中なのかも知れません。しばらくお待ちください。
※6月17日現在、Patreonのアカウントが復活しました。github側で進んだ分の解説をしていきます。
前回の在籍も含めると、累計9年半、本家MySQLチームでInnoDBの性能改善をターゲットに開発の仕事してきました。5.7でのb-tree index scaleや、8.0の初期の新機能で入ってしまった性能問題の修正など貢献できましたが、ここ数年は開発が進む度に導入される性能劣化に追いつけなくなってしまいました。悪いコードを見つけて直そうとしても抵抗が大きいのも大きな要因です。(遅くしたいのでしょうか?遅いことが認識できないのでしょうか?)
この度、体制変更で退職を勧められたのを機に、別の営みでMySQL/InnoDBの性能を改善していくことにしました。どうせ、性能劣化に(私よりも)無頓着な現体制では私が性能を改善することは困難なので、成果は出ないでしょう。(直しても、同時に導入される新機能・修正による劣化に改善分を喰いつぶされることもしばしばあった、と考えています。キリがありません。そもそも、性能に悪い新機能を通すための性能改善回復ではないのに…)
8.0以降の性能に最も満足していないのは私自身です。このままでは終われません。
というわけで、
これは「終わり」ではなく「始まり」です。
これからは、8.0の性能回復を個人で、できればフルタイムでコツコツやっていきます。支援が生活持続上十分に集まるようなら、そのままセキュリティ問題修正なども続けて、延長サポート終了後も8.0性能修正版を使えるようにメンテ・性能改善していきます。(8.0の修正が終了して且つ8.4も相当性能に直せたら8.4への移行も検討します)
会社やチームに気を遣って表現・説明を抑えることも、
理解できない人に際限なく説明して許可を求める必要も、
悪いコードを認めたくない作者に邪魔されることも、
公開の別ブランチならばもうないのです!(あったとは明言はしない。一応。)
なので、本家在籍中よりもスムーズに直していけると思います。(思えば、会社には性能改善に役立つものは何もなく、もう障害・妨害しかないと感じていました。)
体制(各種アカウント開設等)は近日中に整えます。本編は日本語英語併記でやろうと思ってます。
当面は、
github上にブランチを作成して公開。(GPL MySQLなので GPL)
支援者のみに、
ベンチマーク解析内容・修正内容解説を随時公開(これが本編に当たる)
していきます。
改善点がある程度落ち着いてきてから(当初は何も受け付けられませんが)、
いずれ個別ベンチマークの処理改善要望も募っていこうと思います。
乞うご期待です。
* なんで8.0かというと、「性能改善」が多く「汚染」はまだ少なく、EOL間際なので今後も「汚染」は増えづらいから、です。まずは、ここから直して、徐々に進んでいきます。
* 量子コンピュータとか技術革新で暗号通信が安全でなくなったら基幹はオンプレ回帰するはずで、そのときにクラウドでしかまともに動かないというのは不味いと思います。
* そもそも既存の古いオンプレでスケールしなくなったら、クラウドでもスケールしなくなると思うのですが、何を見て開発してるのか。。。
念を押しておきますが、このブログの「内容は個人の考えであって、所属組織とは方針が異なる」と考えてください。
前のエントリでは、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版バイナリより速いかも。)
前のエントリの続きです。
念を押しておきますが、このブログの「内容は個人の考えであって、所属組織とは方針が異なる」と考えてください。
さて、MySQL 8.0.xの単スレッド性能がどんどん遅くなってきた要因は幾つかありそうなので切り分けていきたいと思います。
まずは、数年前のエントリ「やはりC++はCよりも遅い?」の影響をできるだけ正確に見積もりたいところです。実行バイナリの最適化レベルを合わせて比較して初めて、ロジックの劣化が判るわけです。コンパイラのオプションの範疇でできるだけ最大の最適化を行って計測したいところです。いくつか試した結果、clangのPGO+LTO が手軽な中では最も効果があったのでそれで同じ計測をしてみましょう。(GCCのPGO+LTO と clangのPGOのみ はこれよりも少し劣ったのでとりあえず。)
(補足)
PGO は、一旦ターゲットとなる処理をプロファイリング用のビルドで実行してから、その結果を基に本ビルドする方法です。ソースコードが構造化すればするほど、どのようにCPUネイティブのバイナリにするかの意図が伝えづらく、プログラムの流れ(メインパスのアセンブラコードはアドレス順に真っ直ぐでコンパクトな方がいい)が曖昧で、本筋はプロファイリングして与えなくては最適解とならなくなっていきます。
只PGOを用いただけでは、オブジェクトファイル(*.o)単位でしか最適化されません。ホットなコード・領域はできるだけコンパクトに順番にしたほうがいい(CPUキャッシュの効率化等のため)のですが、オブジェクトファイル単位でバラけては効果半減です。オブジェクトファイルを纏めて実行ファイルにリンクするときにも配置の最適化を行うのがLTOだと思っておいてください。
8.0.18くらいからでしょうか、cmake/fprofile.cmake というファイルが存在して、コメントにやりかたが記述されています。なので、以下の手順がわからなくても最新の 8.0.x ではcmakeのオプションだけでPGO+LTOビルドができます。どの程度動作サポートされているかはまだ不明なので今の所、自己責任(ちゃんと自分で十分テストして)でお願いします。
今回も 5.6 からやります。
以前のバージョンにはPGOビルドのcmakeオプションは無いので、自力でやります。
clang で PGO+LTOをするには、コンパイラのオプション
-fprofile-generate=[output dir]
を付けたビルドでターゲットの処理を実行して、
> llvm-profdata merge -output=default.profdata *.profraw
みたいにして、profdata 形式に纏めてから
-flto -fprofile-use=[output dir]
を付けてビルドします。
clangでPGOは動くのに、LTOできない場合は、llvm*-gold というパッケージが足りないのかも知れません。
で、計測してみました。プロファイリングありの実行は結構重いので、プロファイリング用の処理だけは本処理の800万件程度ではなく、200万件程度に減らしてます。
結構綺麗な結果が得られました。(5.7の一部がビルドできませんでしたが主旨に影響ないのでそのまま)
あくまで、今の所このINSERT INTO SELECT文に特化した最適化だけですが、現状と比較すれば5.6とも遜色ない結果が得られることがわかりました。(まだ数%遅いですが)
なので、数年前のエントリ「やはりC++はCよりも遅い?」の影響は非常に大きく、しかも、実行ファイルのその性能劣化はC++の標準規格が新しいほど劣化が大きいと疑われます。
標準規格の変遷もグラフに記述しました。各標準規格に変更後、GCCの普通のビルドの性能が5、6バージョンかけて遅くなって安定する様子がなんとなく見て取れると思います。
要するに8.0.xの開発では、ソースコードの反最適化が随時行われていたわけです。リポジトリのヒストリから解ると思いますが、バグフィクス時にも積極的に新しい方のC++規格にご丁寧に書き換えたがる人も多いようで、それも、5.7との差が広がっていくのを後押ししているのではないでしょうか。逆に5.7の性能が維持されていたのはやはり、C++標準規格を変えずに新機能追加も少なかったからではないでしょうか。
参考に8.0.37のコミュニティー版バイナリの計測も緑丸で示しています。コミュニティー版はGCCでの普通のビルドであると思われます。(少し遅いのは-DDISABLE_PSI_MEMORY=ONの差でしょう。)
性能劣化の説明としては、
8.0.xではソースコードの反最適化を進めているにも関わらず、提供されるバイナリの最適化レベルは据え置きなのでどんどん遅くなってきた。
と言えると思います。
まだ数%遅いぶんは別の原因(多分ソースコードの変更ロジック自体による)と予想しますが、今回の要素よりもかなり小さいのでこの最適化問題が解決してから踏み込むことにします。
…それにしても、もっと早く綺麗に証明できていれば…。悔やまれます。
私がMySQLをチューニング・ベンチマークし始めてからもうすぐ20年経とうとしています。すべてのユーザーが高い性能のMySQLを使えるように色々足掻いてきて、本家の開発者までやらせてもらっています。以前はソースコードを改善する本家の開発者になることがベストの手段だと考えていましたが、直した性能を維持するフェーズに入ってきてソースコードの問題だけではなくなって、MySQLの性能のためのベストの役割は最早違う立場にあるのかも知れません。
すべてのユーザーが性能最適なバイナリを使えるように何ができるか今の立場から模索をスタートしていきます。