From: Tanaka Akira Date: 2009-02-21T12:24:10+09:00 Subject: [ruby-dev:38060] [Bug:1.9] thread switch when heavy load マシンの負荷が高いときに、マルチスレッドプログラムの処理が進 まないことがあります。 以下のプログラムはパイプに 1秒毎に "a" を書き込んで、それを 他のスレッドから sysread で読み出して、読み出しにかかった時 間を表示するものです。 % cat /etc/debian_version 4.0 % uname -a Linux nute 2.6.18-6-486 #1 Fri Dec 12 16:18:30 UTC 2008 i686 GNU/Linux % ruby -ve ' p $$ r, w = IO.pipe Thread.new { begin t1 = Time.now loop { s = r.sysread(10) t2 = Time.now p [t2-t1, s] t1 = t2 } ensure p $! end } loop { w.write "a"; sleep 1 } ' ruby 1.9.2dev (2009-02-15 trunk 22328) [i686-linux] 16259 [1.5086e-05, "a"] [1.001370774, "a"] [1.004141531, "a"] [1.004539908, "a"] [1.003810203, "a"] [1.003755167, "a"] [1.004224783, "a"] [1.003889822, "a"] [1.004372847, "a"] [2.163765927, "a"] [15.54612696, "a"] [1.009049164, "a"] [1.002022536, "a"] ... ここで、だいたいは読み出しは 1秒で行われます。これは 1秒毎に 書き込むので期待される挙動です。 しかし、ひとつ 15秒以上かかっているところがあります。 これは、このタイミングで他の端末から以下のように負荷をかけた 結果です。 % ruby -e 'loop {}' この負荷を ^C で終わらせた後はふつうに進み出します。 負荷をかけた結果、少なくとも書き込み側のスレッドの処理が進ま なくなっています。 (読み出し側が止まっているのでないことは、負荷を取り除いた後 に表示される "a" の数がひとつであることからわかります。もし 読みだし側だけが進まなくなっているのであれば、15個とかもっと たくさん表示されるはずです。) それならなにをしてんのか、ということで strace すると、 くりかえし sched_yield() してるようです。 % strace -p 16259 Process 16259 attached - interrupt to quit sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 ... 少なくとも、OS が ruby に処理を進める機会をまったく与えてく れないというわけではないようです。 しかし実際には処理が進まないのでこの挙動は嬉しくありません。 で、以下の疑問があるんですが、どうなんでしょうか。 * この挙動はどの OS で起こるのか * ruby と GNU/Linux のどちらが問題なのか (あるいは両方か) あと、ここでテストしたマシンはシングルプロセッサです。マルチ プロセッサ (マルチコア) だと、再現するのに負荷を増やす必要が あったりするかもしれません。 なお、容易に予測されることですが、1.8 では問題ありません。 -- [田中 哲][たなか あきら][Tanaka Akira]