未来のいつか/hyoshiokの日記

hyoshiokの日々思うことをあれやこれや

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は

innobase/srv/srv0srv.c:ulong srv_n_spin_wait_rounds = 20;

というふうに定義されている。この20という数字をとりあえづ1000倍くらいしてみるといいかもしれない。(根拠は、ut_delayで50*ut_rnd_interval(0, srv_spin_wait_delay)回ループしているから。ここでsrv_spin_wait_delayは5となっている)