オープンソース情報データベース
ミラクル・リナックス株式会社
クアッドコア(quad core)Intel Xeonプロセッサ搭載マシン(x86_64)上での性能評価をおこなった。DBT-1を利用したハードリソース変更のパフォーマンスへの影響について考察する。
------------------------------------------------------------------------------- ==> cpu=4-mysql=5.0.24-gcc3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <== CPU: Core Solo / Duo, speed 2666.77 MHz (estimated) Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000 samples % app name symbol name 49661094 20.3662 libpthread-2.3.4.so pthread_mutex_trylock 24660214 10.1132 libpthread-2.3.4.so pthread_mutex_unlock 16691946 6.8454 mysqld btr_search_guess_on_hash 12933540 5.3041 mysqld rec_get_offsets_func 10784991 4.4230 mysqld mutex_spin_wait -------------------------------------------------------------------------------一方、8コアの場合、下記のとおりである。
------------------------------------------------------------------------------- ==> cpu=8-mysql=5.0.24-gcc=3.4/cpu=8-mysql=5.0.24-gcc=3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <== CPU: Core Solo / Duo, speed 2666.7 MHz (estimated) Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) cou nt 100000 samples % app name symbol name 180708060 38.8878 libpthread-2.3.4.so pthread_mutex_trylock 105501875 22.7037 mysqld mutex_spin_wait 49569939 10.6673 libpthread-2.3.4.so pthread_mutex_unlock 11292438 2.4301 mysqld btr_search_guess_on_hash 9457097 2.0351 mysqld rec_get_offsets_func -------------------------------------------------------------------------------ここで明らかのようにpthread_mutex_trylock、mutex_spin_wait、pthread_mutex_unlockが実行コストの約7割となり、ロックの競合が多発している。特にpthread_mutex_trylockおよび、mutex_spin_waitの増加が著しい。 これはあきらかにmutex_spin_lockを中心としたロック競合であると言える。
------------------------------------------------------------------------------- 299373 4.1407 mysqld mysqld buf_page_get_known_nowait 342988 4.7439 mysqld mysqld row_search_for_mysql 919809 12.7220 mysqld mysqld buf_page_get_gen 4638092 64.1499 mysqld mysqld btr_search_guess_on_hash 7230090 9.1619 mysqld mysqld mutex_spin_wait 7230090 72.1552 mysqld mysqld mutex_spin_wait [self] 1812007 18.0836 mysqld mysqld pthread_mutex_trylock 817859 8.1621 mysqld mysqld ut_delay -------------------------------------------------------------------------------
btr_search_guess_on_hash, buf_page_get_gen, row_search_for_mysql, ...から呼ばれていることが分かる。
mutex_spin_waitのコードからは字面の上でpthread_mutex_trylockは見えないが、 ./innobase/include/os0sync.ic のos_fast_mutex_trylockで利用されている。 ./innobase/include/sync0sync.icのmutex_test_and_setでos_fast_mutex_trylock は呼ばれている。このmutex_test_and_setはmutex_spin_waitで呼ばれている。
このようにしてソースコードを分析していってボトルネックとなる部分を発見する。
さて、5.0.24ではpthread_mutexだけではなく独自のmutexを実装している。独自にreserver_cellというデータ構造を持ちそれでロックを管理しているのだが、ロック取得のコストが高い。
ロック競合は、(1)ロック対象にアクセスする確率、(2)ロック取得にかかるコスト、(3)ロックを取得している時間などにより発生する。
同時にロック対象にアクセスする確率が高ければ競合する確率も当然高くなる。通常は、この確率を減らす目的でロック対象の粒度をより小さくする。データベース全体へのロックよりもファイル、テーブル、ページ、レコードへのロックの方が小さくなるので、より粒度の小さいロックへと変換する必要がある。
ロック取得にかかるコストは、システムコールの場合、コンテキストスイッチが発生するので、spin loopに比較してコスト高になる。一方、spin loopの場合、CPUを100%占有するため、他のスレッドないしプロセスが動けないので、全体としてのスループットに影響がある。従って、すぐにロックを取得できると考えられる対象に関してはspin loop長い事待つことが予測される対象に関してはシステムコールを利用するなどのバランスが重要になってくる。
spin loopも通常はCPUバスをロックし、アトミックにtest and set(IA-32の場合cmpxchg命令)をおこなうため、コストが高い。ナイーブに実装するとspin loopの最中にバスロックがかかってしまい、ロック対象へのアクセス以外もブロックする結果になるので注意が必要である。
-------------------------------------------------------------------------------
./innobase/sync/sync0sync.c
mutex_spin_waitの実装
spin_loop:
/* 下記はコストが高い*/
if (mutex_test_and_set(mutex) == 0)
{
/* Succeeded! */
goto finish_timing;
}
i++;
/* SYNC_SPIN_ROUNDS 回数ループする */
if (i < SYNC_SPIN_ROUNDS)
{
goto spin_loop;
}
-------------------------------------------------------------------------------
innodbの実装ではinnodb_sync_spin_loops(SYNC_SPIN_ROUNDS)回数スピンしてその間にロックを取得できなかったらsleepして待つ実装になっている。
従って、負荷が増大し、ロック対象にアクセスする確率があがればあがるほど、ロック競合が発生する。単位時間あたり処理できる量を越えると、スループットは頭うちとなり、それ以上、ロック競合等により処理ができなくなる。
EU=2200で性能上の問題が解消した5.0.32についてCPU数4と8のoprofileのデータを下記にしめす。
------------------------------------------------------------------------------- ==> cpu=4-mysql=5.0.32-gcc=3.4/oprofile061211-eu=2200-op=default-none/opreport-l.txt <== CPU: Core Solo / Duo, speed 2666.74 MHz (estimated) Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000 samples % app name symbol name 27607749 12.4331 libpthread-2.3.4.so pthread_mutex_trylock 16409288 7.3899 mysqld btr_search_guess_on_hash 15570000 7.0119 mysqld rec_get_offsets_func 13151738 5.9228 libpthread-2.3.4.so pthread_mutex_unlock 9934356 4.4739 libc-2.3.4.so memcpy -------------------------------------------------------------------------------どちらのデータもmutex_spin_waitが発生しなくなっている。
------------------------------------------------------------------------------- ==> cpu=8-mysql=5.0.32-gcc=3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <== CPU: Core Solo / Duo, speed 2666.76 MHz (estimated) Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000 samples % app name symbol name 47097502 16.8391 libpthread-2.3.4.so pthread_mutex_trylock 19636300 7.0207 libpthread-2.3.4.so pthread_mutex_unlock 18600010 6.6502 mysqld rec_get_offsets_func 18121328 6.4790 mysqld btr_search_guess_on_hash 11453095 4.0949 mysqld row_search_for_mysql -------------------------------------------------------------------------------
これは実装が、ロックの対象の粒度をより小さくしている、待ち行列(reserve_cell)の無駄な処理をやめているなどの変更を行った結果、ロック対象にアクセスする確率が減少したため、競合の頻度が減ったためと考えられる。
今後の方向としては、(1)粒度が大きいロックについては、より小さいロックへの変更、(2)ロックコストがより安いロックアルゴリズムの適用、(3)ロック期間(ロックを取得してから解放するまでの時間)のさらなる削減などを組み合わせることが必要と考える。 現時点では(1)の効果が大きいと考えられるので、ロック競合のさらなる分析検討が必要である。