spin-waitの実装とHyperThreading (MySQL) その2
id:hyoshiok:20060301#c1141252713のコメントでsaoshimaさんからいいコメントをいただいた。
『mysqlのmutex_spin_wait()は、SYNC_SPIN_ROUNDS回spin wait loopしてから、os_thread_yield()する実装になっていますね。しかも1回のloop毎にudelay()してるんですが、これまた単なる空回りとして実装されてますし。SYNC_SPIN_ROUNDの回数がいくつくらいか解りませんが、この実装はHyperThreadingにとってはつらいと思います。spin wait loopしているsiblingが、lockを握っているsibligの時間を食べている状態だと思います。
spin wait loopの中、特にudelay()の中にpause命令を入れるべきでしょう。
MySQLのコーディングにinline assemblerが使えるのかどうかわかりませんが。』
仰る通りなのである。
実は、一昨日、御指摘のとおりのパッチを仕込んで評価をしていた。(下記参照)
$ diff -pu ut0ut.c.orig ut0ut.c --- ut0ut.c.orig 2005-10-17 10:27:43.000000000 +0900 +++ ut0ut.c 2006-02-28 11:59:16.777840496 +0900 @@ -290,6 +290,13 @@ ut_delay( j = 0; for (i = 0; i < delay * 50; i++) { + /* When executing a spin-wait loop on the Hyper-Threading + processor, the processor can suffer a severe performance + penalty. The pause instruction provides a hint to the + processor. Please refer IA-32 Intel Architecture + Software Developers Manual, Vol 3. */ + __asm__ __volatile__( + "pause; \n"); j += i; }
しかし、残念ながらHT/ON時の性能劣化問題の解決にはいたらなかった。
よくわかっていないのだが、
while (mutex_get_lock_word(mutex) != 0 && i < SYNC_SPIN_ROUNDS) { if (srv_spin_wait_delay) { ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); } i++; }
ut_delay()を呼ぶのを取り合えづやめにして、
$ diff -up sync0sync.c.orig sync0sync.c --- sync0sync.c.orig 2005-10-17 10:27:43.000000000 +0900 +++ sync0sync.c 2006-03-01 13:04:18.607875680 +0900 @@ -402,11 +402,13 @@ spin_loop: while (mutex_get_lock_word(mutex) != 0 && i < SYNC_SPIN_ROUNDS) { - if (srv_spin_wait_delay) - { - ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); - } - + /* When executing a spin-wait loop on the Hyper-Threading + processor, the processor can suffer a severe performance + penalty. The pause instruction provides a hint to the + processor. Please refer IA-32 Intel Architecture + Software Developers Manual, Vol 3. */ + __asm__ __volatile__( + "pause; \n"); i++; }
みたいな実装にしたらどうかと思うのだが、未評価です。
マニュアルを読んで出直します。
おまけ:
SYNC_SPIN_ROUNDの回数は、
innobase/include/sync0sync.h:#define SYNC_SPIN_ROUNDS srv_n_spin_wait_rounds
と定義されていて、srv_n_spin_wait_roundsは
というふうに定義されている。この20という数字をとりあえづ1000倍くらいしてみるといいかもしれない。(根拠は、ut_delayで50*ut_rnd_interval(0, srv_spin_wait_delay)回ループしているから。ここでsrv_spin_wait_delayは5となっている)